EDIT 2016-11-02: Updated to version 0.0.4α. Also, if anyone uses Notepad++, I have a fairly good definition of the Picard Tagger Script language, posted below.
Here’s my first attempt at a naming script. You just maybe might be able to guess that I’m a software professional. But I’m new to this level of detail in this language. (I don’t even know what the language is called or where it’s documented, although I’ve specified file and folder names with it in other tagging apps.)
I want to learn how to create functions to get rid of all the repeated code and to create my own unique functions. And if there’s documentation beyond the Scripting page on the Picard site I’d love to know where it is.
Anyway, if you see anything you like or you think could be improved, don’t hesitate to comment.
Pardon the formatting - it’s copied from the text editor I’m using, with tabs replaced by spaces for display here. This was written for Picard v1.3.2, my first Picard. No guarantees that it works with earlier versions.
$noop(MusicBrainz Picard file naming script.
Author: Michael Piz [Your intrepid author {YIA}; @Piz on MusicBrainz]
Version 0.0.4α [2016-11-01, edit history at end]
Copyright 2016 by Michael Pizolato. This work is licensed under a Creative
Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Details are available at http://creativecommons.org/licenses/by-nc-sa/4.0/
THIS IS AN ALPHA RELEASE!
USE AT YOUR OWN RISK!
YIA has a very specific naming convention for files and folders because,
while he's a genial fellow, he does have minor OCD tendencies. Here's what
this script generates for the examples in Picard's options [as of v1.3.2;
the line breaks are for display formatting only]:
ROOT/Beatles, The/Help! [soundtrack]/Beatles, The • Help! [soundtrack] •
07 · Ticket to Ride.mp3
ROOT/Various Artists/Coup d'État, Volume 1 - Ku De
Ta - Prologue/Snowboy · Various Artists • Coup d'État, Volume 1 - Ku De
Ta - Prologue • 02.05 · I've Got to Learn the Mambo.mp3
Here are some from YIA's own library:
ROOT/Wings/Wings over America/Wings • Wings over America • 02.11 · Band on
the Run [live].mp3
ROOT/Saint-Saëns, Camille/Saint-Saëns - Piano Concertos 1-5; Wedding Cake
Caprice-Valse/Saint-Saëns, Camille • Saint-Saëns - Piano Concertos 1-5;
Wedding Cake Caprice-Valse • 02.08 · 'Africa' Fantaisie, Op. 89
Broken down, they look like this:
ARTIST FOLDER: Beatles, The
ALBUM FOLDER: Help! [soundtrack]
TRACK FILE: Beatles, The • Help! [soundtrack] • Track 07 · Ticket to
Ride.mp3
ARTIST FOLDER: Various Artists
ALBUM FOLDER: Coup d'État, Volume 1 - Ku De Ta - Prologue
TRACK FILE: Snowboy · Various Artists • Coup d'État, Volume 1 - Ku De
Ta - Prologue • Disc 02 · Track 05 · I've Got to Learn the
Mambo.mp3
ARTIST FOLDER: Wings
ALBUM FOLDER: Wings over America
TRACK FILE: Wings • Wings over America • 02.11 · Band on the Run [live]
ARTIST FOLDER: Saint-Saëns, Camille
ALBUM FOLDER: Saint-Saëns - Piano Concertos 1-5; Wedding Cake Caprice-Valse
TRACK FILE: Saint-Saëns, Camille • Saint-Saëns - Piano Concertos 1-5;
Wedding Cake Caprice-Valse • 02.08 · 'Africa' Fantaisie, Op.
89.mp3
Track filenames duplicate all the folder information because in most UI
file search r only the filename of a match is displayed. This way,
all the infomation will be in the search results, resulting in unique
matches. YIA tends to be impatient, especially with looking things up more
than once.
Here are examples of artist folders:
[Various Artists]
[unknown artist]
10,000 Maniacs
Beatles, The
Davis, Sammy, Jr
In addition to individual album folders within those, there can be
"[unknown album]" and "[singles]" folders. Within any of these, individual
track files are stored. In the "[Various Artists]" folder there are
individual album folders [and possibly "[unknown album]"]. Track names in
the "[Various Artists]" album folders include the form "Artist · Various
Artists" so the individual artist is included. Tracks missing a title have
"unknown title" assigned as the title. Thus, should all fail, the
following is possible [though somewhat unlikely]:
[unknown artist]/[unknown album]/[unknown artist] • [unknown album] •
[unknown title].mp3
The brackets are added so those folders sort to the top of an ascending
alphabetical list [in Windows, anyway] instead of being mixed in with the
individual artist or album folders.
Final note: You may wonder why YIA has created all these verbose constants
instead of just using their [shorter, duh] corresponding characters or
strings. That's because he doesn't trust himself not to drop extraneous
characters in this language. This language works by building a string from
the results of all the functions used plus, importantly, any explicit
characters in the script. For example, if you put "xyzzy" at the end of
your script, just literally like that, whatever final string you generate
will end in "xyzzy" even though it's not generated by a function call, and
no error will be displayed because that's how the language works. [If you
are insane and want to name all your files "foobar", your script can
consist solely of that string.] Stray spaces or punctuation or whatnot
strewn hither and yon could affect the final string in very difficult to
debug ways. Therefore, YIA, being completely untrusting of himself in such
matters, doesn't allow himself the possibility of strays. By using
variables as constants and never including whitespace in function calls
[except at the beginning of lines, where it is ignored], the possibility of
stray character gremlins is all but eliminated. [In addition, using
"_kNull" makes it explicit where null strings are used, also making
debugging, and reading the code, easier.]
)
$noop(Basic constants.)
$set(_kNull,)
$set(_kSpace, )
$set(_kComma,\,)
$set(_kSingleQuote,')
$set(_kEllipsis,…)
$set(_kPeriod,.)
$set(_kAmpersand, & )
$set(_kNoteBracketLeft,[)
$set(_kNoteBracketRight,])
$noop(End basic constants.)
$noop(Aticles.)
$set(_kA,A)
$set(_kAn,An)
$set(_kThe,The)
$noop(End articles.)
$noop(Folder and track name constants.)
$set(_kFolderSep,/)
$noop(
There are three levels of separators within track names. The top levels
are "Artist • Album • Track", corresponding to "Artist/Album/Track" in
the file system. In track names, for example when the album artist is
"Various Artists", one of the possible second levels is "Artist ·
Various Artists" [see the Snowboy and Saint-Saëns examples above; also
note that the first and second level separators are different
characters, i.e. U+2022: Bullet and U+00B7: Middle Dot; the third level
separartor is U+002D: Hyphen-Minus, the simple minus sign]. The third
level separator replaces any characters in artist/album/track names that
are illegal filename characters, e.g. "Some Band: Greatest Hits" becomes
"Some Band - Greatest Hits" [again, see the Snowboy and Saint-Saëns
examples].
)
$set(_kTrackSep1, • )
$set(_kTrackSep2, · )
$set(_kTrackSep3, - )
$noop(End folder and track name constants.)
$noop(Filename substitution regex constants.)
$noop(All bracket types.)
$set(_kReOpenBrackets,[\\%_kNoteBracketLeft%\({])
$set(_kReCloseBrackets,[\\%_kNoteBracketRight%\){])
$noop(For replacing three consecutive periods with one ellipsis.)
$set(_kRe3Dots,\\%_kPeriod%\\%_kPeriod%\\%_kPeriod%)
$noop(Trim all multiple spaces down to single spaces.)
$set(_kMultiSpace,\\s+)
$noop(
Remove characters not permitted in file or folder names. These are
treated as separators and replaced with _kTrackSep2. Underscore isn't
really an illegal char but having it here gets it replaced by a
separator.
)
$set(_kReIllegalFileChars,\\s*[/\\><*:_|]+\\s*)
$noop(
Replace all quote marks with single quotes. The double quote isn't part
of the illegal chars because those get dropped, not replaced.
)
$set(_kReQuoteMarks,["“”‘’]+)
$noop(
Remove question marks entirely. E.g., "What Kind of Fool Am I?" becomes
"What Kind of Fool Am I". This isn't part of the illegal chars because
those get dropped, not replaced. The inverted question mark is dropped
for aesthetic reasons - it won't have its mate.
)
$set(_kReQuestionMarks,[¿?]+)
$noop(
Windows doesn't allow periods as the final char of folder names. It
automatically drops them in Windows Explorer, but YIA doesn't know what
happens programatically so it's best to drop them here.
)
$set(_kReEndPeriod,\\.\$)
$noop(Case insensitivity.)
$set(_kReCaseInsensitive,\(?i\))
$noop(And. Always replace wth '&'.)
$set(_kReAnd,%_kReCaseInsensitive%\\sand\\s)
$noop(
Remove "feat." or "ft." [with or without the periods] plus everything
that follows [and precedes, if inside brackets].
)
$set(_kReFeat,
\\s+\(%_kReOpenBrackets%.*\)?f\(ea\)?t\\.?.*\(%_kReCloseBrackets%.*\)?
)
$noop(End filename substitution regex constants.)
$noop(Artist/album/title/media/track constants.)
$noop(
Release types. Comment shows whether value goes in album or track
notes. Album and single determine the others. "Other" isn't used
because it provides no information.
)
$set(_kAlbum,album) $noop(N/A)
$set(_kSingle,single) $noop(N/A)
$set(_kEP,ep) $noop(album)
$set(_kBroadcast,broadcast) $noop(track)
$set(_kOther,other) $noop(N/A)
$noop(
Secondary release types. Comment shows whether value goes in album
or track notes.
)
$set(_kCompilation,compilation) $noop(album)
$set(_kSoundtrack,soundtrack) $noop(album)
$set(_kAudiobook,audiobook) $noop(album)
$set(_kMixtape,mixtape/street) $noop(album)
$set(_kSpokenword,spokenword) $noop(track)
$set(_kInterview,interview) $noop(track)
$set(_kLive,live) $noop(track)
$set(_kRemix,remix) $noop(track)
$set(_kDJMix,dj-mix) $noop(track)
$noop(Default artist/album/title.)
$set(_kUnknownArtist,unknown artist)
$set(_kUnknownAlbum,unknown album)
$set(_kNoTitle,unknown title)
$noop(
NOTE: The next two must duplicate the "Various artists" and "Non-album
tracks" option settings. I've submitted a Picard feature request to
have those options be made available as tags, to avoid having to
maintain them in two places. That ticket can be found at
https://tickets.musicbrainz.org/browse/PICARD-849
)
$set(_kVariousArtists,Various Artists)
$set(_kNonAlbumTracks,%_kNoteBracketLeft%singles%_kNoteBracketRight%)
$noop(Media and track number.)
$set(_kDefaultMedia,Disc)
$set(_kTrackTxt,Track )
$noop(End artist/album/title/media/track constants.)
$noop(Copies of existing tags.
After this, only the copies are used. In some instances, the tag is later
replaced by the processed copy.
)
$set(_media,%media%)
$set(_totaldiscs,%totaldiscs%)
$set(_discNum,%discnumber%)
$set(_trackNum,%tracknumber%)
$noop(Album, artist, album artist, and title are more complex.)
$set(_artist,
$if2(
%artistsort%,
%artist%,
%albumartistsort%,
%albumartist%,
%_kUnknownArtist%
)
)
$set(_albumArtist,
$noop(albumartistsort already handles "A" and The")
$swapprefix(
$if2(%albumartistsort%,
%albumartist%,
%artistsort%,
%artist%,
%_kUnknownArtist%
),
%_kAn%
)
)
$set(_album,
$swapprefix(
$if2(%album%,%_kUnknownAlbum%),
%_kA%,%_kAn%,%_kThe%
)
)
$set(_title,
$swapprefix(
$if2(%title%,%_kNoTitle%),
%_kA%,%_kAn%,%_kThe%
)
)
$noop(End copies of existing tags.)
$noop(Album/artist/track characteristics.)
$set(_isVariousArtists,$eq(%albumartist%,%_kVariousArtists%))
$set(_differentAlbumArtist,$ne(%artistsort%,%albumartistsort%))
$set(_isUnknownArtist,
$or($not(%albumartist%),$eq(%albumartist%,%_kUnknownArtist%))
)
$set(_isUnknownAlbum,
$or($not(%album%),$eq(%album%,%_kUnknownAlbum%))
)
$set(_isSingle,$in(%releasetype%,%_kSingle%))
$noop(End track characteristics.)
$noop(Album/track notes.)
$set(_relType,%_kSoundtrack%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_albumNotes,
%_albumNotes%$if(%_albumNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kAudiobook%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_albumNotes,
%_albumNotes%$if(%_albumNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kMixtape%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_albumNotes,
%_albumNotes%$if(%_albumNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kCompilation%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($not(%_isVariousArtists%)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_albumNotes,
%_albumNotes%$if(%_albumNotes%,%_kComma%%_kSpace%)%_relType%
)
),
%_kNull%
)
$set(_relType,%_kEP%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%releasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_albumNotes,
%_albumNotes%$if(%_albumNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kLive%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_trackNotes,
%_trackNotes%$if(%_trackNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kBroadcast%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%releasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_trackNotes,
%_trackNotes%$if(%_trackNotes%,%_kComma%%_kSpace%)%_relType%
),
)
$set(_relType,%_kSpokenword%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_trackNotes,
%_trackNotes%$if(%_trackNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kInterview%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_trackNotes,
%_trackNotes%$if(%_trackNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kRemix%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_trackNotes,
%_trackNotes%$if(%_trackNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$set(_relType,%_kDJMix%)
$set(_pattern,
%_kReCaseInsensitive%\(%_kReOpenBrackets%?%_relType%%_kReCloseBrackets%?\)
)
$if($rsearch(%_secondaryreleasetype%,%_pattern%),
$set(_artist,$rreplace(%_artist%,%_pattern%,%_kNull%))
$set(_album,$rreplace(%_album%,%_pattern%,%_kNull%))
$set(_title,$rreplace(%_title%,%_pattern%,%_kNull%))
$set(_trackNotes,
%_trackNotes%$if(%_trackNotes%,%_kComma%%_kSpace%)%_relType%
)
)
$if(%_albumNotes%,
$set(_albumNotes,
%_kSpace%%_kNoteBracketLeft%%_albumNotes%%_kNoteBracketRight%
)
)
$if(%_trackNotes%,
$set(_trackNotes,
%_kSpace%%_kNoteBracketLeft%%_trackNotes%%_kNoteBracketRight%
)
)
$noop(End album/track notes.)
$noop(Set up file/folder names.)
$noop(Artist. Only used for the track filename.)
$set(_artist,$rreplace($rreplace($rreplace($rreplace(%_artist%,
%_kReFeat%,%_kNull%),
%_kMultiSpace%,%_kSpace%),
%_kRe3Dots%,%_kEllipsis%),
%_kReAnd%,%_kAmpersand%)
)
$noop(
At this point the artist to be used for tagging is "clean", so set
the actual tag value. Similarly for album artist and album, below.
)
$set(artist,%_artist%)
$noop(Scrub _artist for file names.)
$set(_artist,$rreplace($rreplace($rreplace(%_artist%,
%_kReQuestionMarks%,%_kNull%),
%_kReQuoteMarks%,%_kSingleQuote%),
%_kReIllegalFileChars%,%_kTrackSep3%)
)
$noop(Make the filename part.)
$set(_artistTrack,
%_artist%
$if(%_differentAlbumArtist%,
%_kTrackSep2%,
%_kTrackSep1%
)
)
$noop(End artist.)
$noop(Album artist.)
$set(_albumArtist,$rreplace($rreplace($rreplace($rreplace(%_albumArtist%,
%_kReFeat%,%_kNull%),
%_kMultiSpace%,%_kSpace%),
%_kRe3Dots%,%_kEllipsis%),
%_kReAnd%,%_kAmpersand%)
)
$set(albumartist,%_albumArtist%)
$set(_albumArtist,$rreplace($rreplace($rreplace(%_albumArtist%,
%_kReQuestionMarks%,%_kNull%),
%_kReQuoteMarks%,%_kSingleQuote%),
%_kReIllegalFileChars%,%_kTrackSep3%)
)
$set(_albumArtist,$rreplace(%_albumArtist%,%_kReEndPeriod%,%_kNull%))
$if(%_differentAlbumArtist%,
$noop(_albumArtistTrack is unnecesasry when they match.)
$set(_albumArtistTrack,
$if(%_isUnknownArtist%,
%_kNoteBracketLeft%%_albumArtist%%_kNoteBracketRight%,
%_albumArtist%
)
%_kTrackSep1%
)
)
$set(_albumArtistFolder,
$if($or(%_isVariousArtists%,%_isUnknownArtist%),
%_kNoteBracketLeft%%_albumArtist%%_kNoteBracketRight%,
%_albumArtist%
)
%_kFolderSep%
)
$noop(End album artist.)
$noop(Album.)
$set(_album,$rreplace($rreplace($rreplace($rreplace(%_album%,
%_kReFeat%,%_kNull%),
%_kMultiSpace%,%_kSpace%),
%_kRe3Dots%,%_kEllipsis%),
%_kReAnd%,%_kAmpersand%)
)
$set(_album,%_album%%_albumNotes%)
$set(album,%_album%)
$set(_album,$rreplace($rreplace($rreplace(%_album%,
%_kReQuestionMarks%,%_kNull%),
%_kReQuoteMarks%,%_kSingleQuote%),
%_kReIllegalFileChars%,%_kTrackSep3%)
)
$set(_album,$rreplace(%_album%,%_kReEndPeriod%,%_kNull%))
$set(_albumTrack,$if($not(%_isSingle%),%_album%%_kTrackSep1%))
$set(_albumFolder,
$if(%_isUnknownAlbum%,
%_kNoteBracketLeft%%_album%%_kNoteBracketRight%,
%_album%
)
%_kFolderSep%
)
$noop(End album.)
$noop(Media type.)
$set(_media,$if2(%_media%,%_kDefaultMedia%)%_kSpace%)
$noop(End media type.)
$noop(Disc number, if necessary.)
$set(_discNum,
$if($gt(%_totaldiscs%,1),
$num(%_discNum%,2)%_kPeriod%,
%_kNull%
)
)
$noop(End end disc number. )
$noop(Track number.)
$set(_trackNum,
$if($and(
%_trackNum%,
$or($not(%_isSingle%),$ne(%_album%,%_kNonAlbumTracks%))
),
$num(%_trackNum%,2)%_kTrackSep2%
)
)
$noop(End track number.)
$noop(Title.)
$set(_title,$rreplace($rreplace($rreplace($rreplace(%_title%,
%_kReFeat%,%_kNull%),
%_kMultiSpace%,%_kSpace%),
%_kRe3Dots%,%_kEllipsis%),
%_kReAnd%,%_kAmpersand%)
)
$set(_title,%_title%%_trackNotes%)
$set(title,%_title%)
$set(_title,$rreplace($rreplace($rreplace(%_title%,
%_kReQuestionMarks%,%_kNull%),
%_kReQuoteMarks%,%_kSingleQuote%),
%_kReIllegalFileChars%,%_kTrackSep3%)
)
$noop(???)
$set(_trackFile,
%_artistTrack%%_albumArtistTrack%%_albumTrack%%_discNum%%_trackNum%
%_title%
)
$noop(End title.)
$noop(End set up file/folder names.)
$noop(
Finally, apply it all to the actual folders and file.
)
%_albumArtistFolder%%_albumFolder%%_trackFile%
$noop(EDIT HISTORY
2016-11-01 0.0.4α
1. Revamped naming convention. Now "Various Artists" is an artist and
the album artist goes in the track filename."[Various Artists]" will
be a folder at the same level as other artists. Same for "[unknown
artist]". "[unknown album]" will get its own folder under each
artist, if necessary. "[unknown artist]", "[unknown album]", and
"[unknown title]" will be included in track filenames, if necessary.
2. Updated comments to reflect all that.
3. Removed a bunch of unnecessary "_kNull"s.
4. Bug fixes.
2016-10-28 0.0.3α
1. Added replacing all occurrances of " and " with " & ".
2. Added moving articles to the end of title and album.
3. Tweaked handling for primary/secondary release types.
4. Bug fixes.
2016-10-27 0.0.2α
1. Added handling for all primary/secondary release types.
2. Tweaked regex for "feat." handling.
3. Bug fixes.
2016-10-26 0.0.1α
1. Initial version.
)