Scripting

This page describes the simple scripting language implemented in MusicBrainz Picard.

Syntax

The syntax is derived from Foobar2000's titleformat. There are three base elements: text, variable and function. Variables consist of alpha-numeric characters enclosed in percent signs (e.g. %artist%). Functions start with a dollar sign and end with an argument list enclosed in parentheses (e.g. $lower(...)).

To use parentheses or commas as-is inside a function call, you must escape them with a backslash.

Metadata Variables

See Picard tags for list of standard variables provided by Picard.

Picard's standard variables can be either simple variables containing a single text string, or multi-value variables containing multiple text strings. In scripts, multi-value variables are automatically converted to a single text string by joining the values with ; , except when used with special multi-value functions.

Functions

$if(if,then,else)

  • If if is not empty, it returns then, otherwise it returns else.

$if2(a1,a2,a3,...)

  • Returns first non empty argument.

$lower(text)

  • Returns text in lower case.

$upper(text)

  • Returns text in upper case.

$reverse(text)

  • Returns text> in reverse order.

$left(text,num)

  • Returns the first num characters from text.

$right(text,num)

  • Returns the last num characters from text.

$substr(text,start,end)

  • Returns the substring beginning with the character at the start index, up to (but not including) the character at the end index. Indexes are zero-based. Negative numbers will be counted back from the end of the string. If the start or end indexes are left blank, they will default to the start and end of the string respectively.

$find(haystack,needle)

  • Finds the location of one string within another. Returns the index of the first occurrance of needle in haystack, or "" if needle was not found.
  • Added in version 2.3
  • Note that prior to Picard 2.3.2 $find returned "-1" if needle was not found.

$matchedtracks()

  • Returns the number of matched tracks within a release.
  • Only works in File Naming scripts.
  • Added in version 0.12

$is_complete()

  • Returns true if every track in the album is matched to a single file.
  • Only works in File Naming scripts.

$num(num,len)

  • Returns num formatted to len digits, where len cannot be greater than 20.

$replace(text,search,replace)

  • Replaces occurrences of search in text with value of replace and returns the resulting string.

$rsearch(text,pattern)

$rreplace(text,pattern,replace)

$in(x,y)

  • Returns true, if x contains y.

$inmulti(%x%,y)

  • Returns true if multi-value variable x contains exactly y as one of its values.
  • Since Picard 1.0

$set(name,value)

  • Sets the variable name to value.
  • Note: To create a variable which can be used for the file naming string, but which will not be written as a tag in the file, prefix the variable name with an underscore. %something% will create a "something" tag; %_something% will not.

$setmulti(name,value,separator="; ")

  • Sets the variable name to value, using the separator (or "; " if not passed) to coerce the value back into a proper multi-valued tag. This can be used to operate on multi-valued tags as a string, and then set them back as proper multi-valued tags, e.g
  • $setmulti(genre,$lower(%genre%))
  • Since Picard 1.0

$unset(name)

  • Unsets the variable name.
  • Allows for wildcards to unset certain tags (works with 'performer:*', 'comment:*', and 'lyrics:*').
    i.e. $unset(performer:*) would unset all performer tags.

$delete(name)

  • Unsets the variable name and marks the tag for deletion.
  • This is similar to $unset(name) but also marks the tag for deletion. E.g. running $delete(genre) will actually remove the genre tag from a file when saving.
  • Since Picard 2.1

$get(name)

  • Returns the variable name (equivalent to %name%).

$getmulti(name,index,separator="; ")

  • Gets the element at index from the multi-value tag name. A literal value representing a multi-value can be substituted for name, using the separator (or "; " if not passed) to coerce the value into a proper multi-valued tag.

$slice(name,start,end,separator="; ")

  • Returns a multi-value variable containing the elements between the start and end indexes from the multi-value tag name. A literal value representing a multi-value can be substituted for name, using the separator (or "; " if not passed) to coerce the value into a proper multi-valued tag. Indexes are zero based. Negative numbers will be counted back from the end of the list. If the start or end indexes are left blank, they will default to the start and end of the list respectively.
  • For example, the following will create a multi-value variable with all artists in %artists% except the first, which can be used to create a "feat." list.
  • $setmulti(supporting_artists,$slice(%artists%,1,))

$join(name,text,separator="; ")

  • Joins all elements in name, placing text between each element, and returns the result as a string.

$map(name,code,separator="; ")

  • Iterates over each element found in the multi-value tag name and updates the value of the element to the value returned by code, returning the updated multi-value tag. For each loop, the element value is first stored in the tag _loop_value and the count is stored in the tag _loop_count. This allows the element or count value to be accessed within the code script.

$sortmulti(name,separator="; ")

  • Returns a copy of the multi-value tag name with the elements sorted in ascending order.

$reversemulti(name,separator="; ")

  • Returns a copy of the multi-value tag name with the elements in reverse order. This can be used in conjunction with the $sortmulti function to sort in descending order.

$foreach(name,code,separator="; ")

  • Iterates over each element found in the multi-value tag name, executing code. For each loop, the element value is first stored in the tag _loop_value and the count is stored in the tag _loop_count. This allows the element or count value to be accessed within the code script. A literal value representing a multi-value can be substituted for name, using the separator (or "; " if not passed) to coerce the value into a proper multi-valued tag.

$while(condition,code)

  • Standard 'while' loop. Executes code repeatedly until condition no longer evaluates to True. For each loop, the count is stored in the tag _loop_count. This allows the count value to be accessed within the code script. The function limites the maximum number of iterations to 1000 as a safeguard against accidentally creating an infinite loop.

$copy(new,old)

  • Copies metadata from variable old to new. The difference between $set(new,%old%) is that $copy(new,old) copies multi-value variables without flattening them.
  • Since Picard 0.9

$copymerge(new,old)

  • Merges metadata from variable old into new, removing duplicates and appending to the end, so retaining the original ordering. Like $copy, this will also copy multi-valued variables without flattening them.
  • Since Picard 1.0

$pad(text,length,char)

  • Pads the text to the length provided by adding as many copies of char as needed to the beginning of the string.

$strip(text)

  • Replaces all whitespace in text with a single space, and removes leading and trailing spaces. Whitespace characters include multiple consecutive spaces, and various other unicode characters.

$trim(text[,char])

  • Trims all leading and trailing whitespaces from text. The optional second parameter specifies the character to trim.

$add(x,y,*args)

  • Add y to x.
  • Can be used with an arbitrary number of arguments.
    i.e. $add(x,y,z) = ((x + y) + z)

$sub(x,y,*args)

  • Subtracts y from x.
  • Can be used with an arbitrary number of arguments.
    i.e. $sub(x,y,z) = ((x - y) - z)

$div(x,y,*args)

  • Divides x by y.
  • Can be used with an arbitrary number of arguments.
    i.e. $div(x,y,z) = ((x / y) / z)

$mod(x,y,*args)

  • Returns the remainder of x divided by y.
  • Can be used with an arbitrary number of arguments.
    i.e. $mod(x,y,z) = ((x % y) % z)

$mul(x,y,*args)

  • Multiplies x by y.
  • Can be used with an arbitrary number of arguments.
    i.e. $mul(x,y,z) = ((x * y) * z)

$or(x,y,*args)

  • Returns true if either x or y not empty.
  • Can be used with an arbitrary number of arguments. The result is true if ANY of the arguments is not empty.

$and(x,y,*args)

  • Returns true if both x and y are not empty.
  • Can be used with an arbitrary number of arguments. The result is true if ALL of the arguments are not empty.

$not(x)

  • Returns true if x is empty.

$eq(x,y)

  • Returns true if x equals y.

$ne(x,y)

  • Returns true if x does not equal y.

$lt(x,y)

  • Returns true if x is less than y.

$lte(x,y)

  • Returns true if x is less than or equal to y.

$gt(x,y)

  • Returns true if x is greater than y.

$gte(x,y)

  • Returns true if x is greater than or equal to y.

$eq_any(x,a1,a2...)

  • Returns true if x equals a1 or a2 or ...
  • Functionally equivalent to $or($eq(x,a1),$eq(x,a2) ...).
  • Functionally equivalent to the eq2 plugin.

$eq_all(x,a1,a2...)

  • Returns true if x equals a1 and a2 and ...
  • Functionally equivalent to $and($eq(x,a1),$eq(x,a2) ...).

$ne_all(x,a1,a2...)

  • Returns true if x does not equal a1 and a2 and ...
  • Functionally equivalent to $and($ne(x,a1),$ne(x,a2) ...).
  • Functionally equivalent to the ne2 plugin.

$ne_any(x,a1,a2...)

  • Returns true if x does not equal a1 or a2 or ...
  • Functionally equivalent to $or($ne(x,a1),$ne(x,a2) ...).

$noop(...)

  • Does nothing (useful for comments or disabling a block of code).

$len(text)

  • Returns the number of characters in text.

$lenmulti(name,separator="; ")

  • Returns the number of elements in the multi-value tag name. A literal value representing a multi-value can be substituted for name, using the separator (or "; " if not passed) to coerce the value into a proper multi-valued tag.
  • For example, the following will return the value "3".
  • $lenmulti(One; Two; Three)

$performer(pattern="",join=", ")

  • Returns the performers where the performance type (e.g. "vocal") matches pattern, joined by join.
  • Since Picard 0.10

$title(text)

  • Returns text in title case (first character in every word capitalized).
  • Since Picard 2.1

$firstalphachar(text,nonalpha="#")

  • Returns the first character of text. If text is not an alphabetic character nonalpha is returned instead.
  • Since Picard 0.12

$initials(text)

  • Returns the first character of each word in text, if it is an alphabetic character.
  • Since Picard 0.12

$truncate(text,length)

  • Truncate text to length.
  • Since Picard 0.12

$firstwords(text,length)

  • Like $truncate except that it will only return the complete words from text which fit within length characters.
  • Since Picard 0.12

$swapprefix(text,*prefixes="a","the")

  • Moves the specified prefixes from the beginning to the end of text. If no prefix is specified 'A' and 'The' are used by default.
  • $swapprefix(%albumartist%,A,An,The,Le)
  • Integrated since Picard 1.3, previously as a plugin since Picard 0.13

$delprefix(text,*prefixes="a","the")

  • Deletes the specified prefixes from the beginning of text. If no prefix is specified 'A' and 'The' are used by default.
  • Since Picard 1.3

$startswith(text,prefix)

  • Returns true if text starts with prefix.
  • Since Picard 1.4

$endswith(text,suffix)

  • Returns true if text ends with suffix.
  • Since Picard 1.4

$is_audio()

  • Returns true, if the file processed is an audio file.
  • Since Picard 2.2

$is_video()

  • Returns true, if the file processed is an video file.
  • Since Picard 2.2

$datetime(format="%Y-%m-%d %H:%M:%S")

  • Returns the current date and time in the specified format, which is based on the standard Python strftime format codes. If no format is specified the date/time will be returned in the form '2020-02-05 14:26:32'.
  • Since Picard 2.3
  • Note: Platform-specific formatting codes should be avoided to help ensure the portability of scripts across the different platforms. These codes include: remove zero-padding (e.g. %-d and %-m on Linux or macOS, and their equivalent %#d and %#m on Windows); element length specifiers (e.g. %3Y); and hanging '%' at the end of the format string.

Tagger Script Examples

OptionsOptions...AdvancedScripting

Tagger Script usually creates, modifies, or deletes metadata variables.

Artist names

$if($search(%album%,(feat. conductor)), $set(artist,%orchestra%))
  • Stupid assumption that all classical albums have "feat. conductor" in the title, but it shows the idea. :)

Live tracks on live albums

$if($and($eq(%releasetype%,live),$not($in(%title%,\(live\)))),$set(title,%title% \(live\)))

Remove "feat." from track titles

$set(title,$rreplace(%title%,\\s\\\(feat. [^\)]+\\\),))

Convert triple-dot to ellipsis

$set(title,$replace(%title%,...,…))
$set(album,$replace(%album%,...,…))

This one is useful for people concerned about correct typography and also fixes one problem on Linux: if an album (assuming it's also a directory) is called something like "...for you!", it is considered hidden and therefore might be not accessible from some applications.

Remove "various artists" on compilations

By default, Picard marks various-artist compilations with an album artist called "Various Artists". Even if you don't have anything by Torsten Pröfrock, it still means that your player will sort your comps between Vangelis and VCR instead of down at the end, which you may not want.

This is easy to fix:

$if($and($eq(%compilation%,1), $eq(%albumartist%,Various Artists)), $unset(albumartist) $unset(albumartistsort))

Merge writers into both composer and lyricist tags

Supported from version 1.0

Suppose you want to add anyone involved in writing to both the composer and lyricist tags.

$copymerge(composer,writer)
$copymerge(lyricist,writer)
$unset(writer)

Check for some type of Rock Genre

If you have populated Genres using folksonomy tags or a plugin such as LastFmPlus, you might get a variety of entries containing the word "Rock" such as "Rock", "Prog Rock", "Glam Rock", "Heavy Rock" etc.

You may want to check whether your track has any Rock genre, or whether it contains the specific genre "Rock".

To check whether your track has any Rock genre:

$if($in(%genre%,Rock),Let's rock,Peace at last)

To check for the specific genre "Rock":

$if($inmulti(%genre%,Rock),Let's rock,Peace at last)

File Naming Examples

OptionsOptions...File naming

Disc number only on multi-disc releases

Use the following snippet to insert the disc number in the new path if the release contains more than 1 medium.

$if($gt(%totaldiscs%,1),Disc $num(%discnumber%,2))

Lower case filenames with underscores

$lower($replace(%albumartist%/%album%/$num(%tracknumber%,2) %title%, ,_))

(Year) only if available

$if(%date%,\($left(%date%,4)\))

Use a different naming pattern for NATs

$if($eq([non-album tracks],%album%),[non-album tracks]/%tracknumber%. %artist% - ,%artist% - $if(%date%, $left(%date%,4) )- %album%/%tracknumber%.)%title%

Result:

  • Non-Album Tracks: [non-album tracks]/Band Name - Track Name.ext
  • Tracks in releases: Band Name - year - Release Name/##. Track Name.ext

Organize by alphabetical folders excluding leading The

To keep music organized by initial letter of artist's name you must first set the directory where saved files are to be stored in Options --> Moving Files. Then under Options --> File Naming check Rename files when saving. This script will then move saved files to your Moving Files location using the following hierarchy:
Result:

  • A/Artist Name, The/Year - Album Name (type-status)/Album (type-status)-disc#-Track-Song Title

for Various Artists-Albums

  • Various Artist/Year - Album Name (type-status)/Album (type-status)-disc#-Track-Song Title
$if($eq(%musicbrainz_albumartistid%,89ad4ac3-39f7-470e-963a-56509c546377),

$left($if2(%albumartistsort%, %artistsort%),30)/
$if(%date%,$left(%date%,4)) - $left(%album%,40) \(%releasestatus%-%releasetype%\)/
$left(%album%,30) \(%releasestatus%-%releasetype%\)-$if($gt(%totaldiscs%,1),$if2(%media%,CD)%discnumber%-,)$num(%tracknumber%,2)-$left(%title%,30)

,
$upper($firstalphachar($if2(%albumartistsort%, %artistsort%),#))
/$left($if2(%albumartistsort%, %artistsort%),18)/
$if(%date%,$left(%date%,4)) - $left(%album%,40) \(%releasestatus%-%releasetype%\)/
$left(%album%,30) \(%releasestatus%-%releasetype%\)-$if($gt(%totaldiscs%,1),$if2(%media%,CD)%discnumber%-,)$num(%tracknumber%,2)-$left(%title%,30)

)

Cover Art Naming Examples

OptionsOptions...Cover Art

Tagger Script can be used to change the file names of saved cover art images.

Use album title for cover art file names

%album%

Use main type for images that are not front images

This will name front images "AlbumArt" and all other images according to their maintype (e.g. "medium" or "booklet").

$if($eq(%coverart_maintype%,front),AlbumArt,%coverart_maintype%)