Package: beets-filetote
Filetoteplugin for beetsA plugin that moves non-music extra files, attachments, and artifacts during imports and
CLI file manipulation actions (move,modify, reimport, etc.) forbeets, a music
library manager (and much more!).This plugin is supported/runs in beetsv1.6.0.InstallingStableThe stable version of the plugin is available from PyPI and can be installed usingpip3:pip3installbeets-filetoteImportant Note:Python versions <3.7 will need to also install thedataclassesmodule.ConfigurationYou will need to enable the plugin in beets'config.yaml:plugins:filetoteIt can copy files by file extension:filetote:extensions:.cue .logOr copy all non-music files:filetote:extensions:.*Or copy files by filename:filetote:filenames:song.logOr match based on a "pattern" (glob pattern):filetote:patterns:artworkdir:-"[aA]rtwork/"It can look for and target "pairs" (files having the same name as a matching or "paired"
media item/track):filetote:pairing:enabled:trueYou can specify pairing to happen to certain extensions, and even target/include only
paired files:filetote:pairing:enabled:truepairing_only:trueextensions:".lrc"It can also exclude files by name:filetote:exclude:song_lyrics.nfoAnd print what got left:filetote:print_ignored:trueexclude-d files take precedence over other matching, meaning exclude will override
other matches by eitherextensionsorfilenames.File Handling & RenamingIn order to collect extra files and artifacts, Filetote needs to be told which types of
files it should care about. This can be done using the following:Extensions (ext:): Specify individual extensions like.cueor.log, or catch all
non-music files with.*.Filenames (filename:): Match specific filenames likecover.jpgor organize artwork
with[aA]rtwork/*.Patterns (pattern:): Use flexible glob patterns for more control, like matching all
logs in a subfolder:CD1/*.log.Pairing: Move files with the same name as imported music items, like.lrclyrics or
album logs.Filetote Renaming BasicsUnless otherwise specified, the default name for artifacts and extra files is:$albumpath/$old_filename. This means that by default, the file is essentially
moved/copied into destination directory of the music item it gets grabbed with. This
also means that the album folder is flattened and any subdirectory is removed by
default. To preserve subdirectories, see$subpathusagehere.Configuration for renaming works in much the same way as beetsPath Formats, including
the standard metadata values provided by beets. Filetote provides the below new path
queries, which each takes a single corresponding value. These can be defined in either
the top-levelpathssection of Beet's config or in thepathssection of Filetote's
config. Both of the following are equivalent:paths:ext:.log:$albumpath/$artist - $albumfiletote:paths:ext:.log:$albumpath/$artist - $albumNew path queriesThese are the new path queries added by Filetote, frommosttoleastspecific:filename:paired_ext:pattern:ext:This means that thefilename:path query will take precedence overpaired_ext:,pattern:, andext:if a given file qualifies for them. This also means that
the value inpaired_ext:will take precedence overpattern:andext:, andpattern:is higher priority thanext:.Renaming considerationsRenaming has the following considerations:The fields available includethe standard metadata valuesof the imported item
($albumartist,$album,$title, etc.), along with Filetote-specific values of:$albumpath: the entire path of the new destination of the item/track (a useful
shorthand for when the extra/artifact file will be moved allongside  the item/track).Note: Beets doesn't have a strict "album" path concept. All references are
relative to Items (the actual media files). This is especially relevant for
multi-disc files/albums, but usually isn't a problem. Check the section on
multi-discsherefor more details.$subpath: Represents any subdirectories under the base album path where an
extra/artifact file resides. For use when it is desirable to preserve the directory
hierarchy in the albums. This respects the original capitalization of directory names.
Defaults to an empty string when no subdirectories exist.Example:If an extra file is located in a subdirectory named "Extras" under
the album path,$subpathwould be set to "Extras/" (with the same casing).$old_filename: the filename of the extra/artifact file before its renamed.$medianame_old: the filename of the item/track triggering it,beforeit's renamed.$medianame_new: the filename of the item/track triggering it,afterit's renamed.The full set ofbuilt in functionsare also supported, with the exception of%aunique- which will return an empty string.Note that the fields mentioned above are not usable within other plugins likeinline.
Butinlineand other plugins should be fine otherwise.Important Note:if the rename is set and there are multiple files that qualify,
only the first will be added to the library (new folder); other files that
subsequently match will not be saved/renamed. To work around this,$old_filenamecan be used to help with adding uniqueness to the name.Subpath Renaming ExampleThe following configuration or template string will be applied to.logfiles by using
the$subpathand will rename log file to:~/Music/Artist/2014 - Album/Extras/Artist - Album.logThis assumes that the original file is in the subdirectory (subpath) ofExtras/. Any
other.logfiles in other subdirectories or in the root of the album will be moved
accordingly. If a more targeted approach is needed, this can be combined with thepattern:query.Note:$subpathautomatically adds in path separators including the end one if there
are subdirectories.paths:ext:.log:$albumpath/$subpath$artist - $albumExtension (ext:)Filename can match on the extension of the file, in a space-delimited list (i.e., a
string sequence). Use.*to match all file extensions.Extension Example ConfigurationThis example will match any file which has an extension of either.lrcor.log,
across all subfolders.filetote:ext:.lrc .logExtension Renaming ExampleThe following configuration or template string will be applied to.logfiles by using
theext:query and will rename log file to:~/Music/Artist/2014 - Album/Artist - Album.logpaths:ext:.log:$albumpath/$artist - $albumFilename (filename:)Filetote can match on the actual name (including extension) of the file, in a
space-delimited list (string sequence).filename:will match across any subdirectories,
meaning targeting a filename in a specific subdirectory will not work (this functionalitycanbe achieved using apattern, however).Filename Example ConfigurationThis example will match if the filename of the given artifact or extra file matches the name
exactly as specified, eithercover.jpgorartifact.nfo.filetote:filenames:cover.jpg artifact.nfoFilename Renaming ExampleThe following configuration will rename the specificartifact.nfofile to:~/Music/Artist/2014 - Album/Artist - Album.nfofiletote:paths:filename:artifact.nfo:$albumpath/$artist - $albumfilenames:cover.jpg artifact.nfoPattern (pattern:)Filetote can match on a givenpatternas specified usingglob patterns. This allows
for more specific matching, like grabbing only PNG artwork files. Paths in the pattern
are relative to the root of the importing album. Hence, if there are subdirectories in
the album's folder (for multidisc setups, for instance, e.g.,albumpath/CD1), the
album's path would be the base/root for the pattern (ex:CD1/*.jpg). Patterns will
work with or without the proceeding slash (/). Note: Windows users will need to
use the appropriate slash (\).Patterns specifying folders with a trailing slash will (ex:albumpath/) will match
every file in that subdirectory irrespective of name or extension (it is equivalent toalbumpath/*.*).Patterns are defined by anameso that any customization for renaming can apply to the
pattern when specifying the path (ex:pattern:artworkdir; see the section on renaming
below).Pattern Example ConfigurationThis example will match if the filename of the given artifact or extra file matches the name
exactly as specified, eithercover.jpgorartifact.nfo.This example will match all files within the given subdirectory of eitherartwork/orArtwork/. Since it's not otherwise specified,[aA]rtwork/will grab all non-media
files in that subdirectory irrespective of name or extension.filetote:patterns:artworkdir:-"[aA]rtwork/"Pattern Renaming ExampleThe following pattern configuration will rename the fileartwork/cover.jpegto:~/Music/Artist/2014 - Album/artwork/cover.jpegfiletote:paths:pattern:artworkdir:$albumpath/artwork/$old_filenamepatterns:artworkdir:-"[aA]rtwork/"PairingFiletote can specially target related files like lyrics or logs with the same name as
music files ("paired" files). This keeps related files together, making your library
even more organized. When enabled, it will match and move those files having the same
name as a matching music file. Pairing can be configured to target only certain
extensions, such as.lrc.Note:Pairing takes precedence over other Filetote rules like filename or patterns.Pairing Example ConfigurationThis example configuration will grab paired.lrcfiles, along with any artwork files:filetote:pairing:enabled:trueextensions:".lrc"patterns:artworkdir:-"[aA]rtwork/"Filetote can also be configured toonlytarget paired files, which will ignore other
Filetote configurations such as filename or patterns as described above. The following
configuration wouldonlytarget.lrcfiles:filetote:pairing:enabled:truepairing_only:trueextensions:".lrc"Pairing RenamingTo mainting the concept of "pairs" after importing, it is strongly encouraged to set
thepathfor the paired files to use the media files new name. This will ensure thet
the file remains paired even after moving. E.g.:paths:paired_ext:.lrc:$albumpath/$medianame_newImport OperationsThis plugin supports the same operations as beets:copymovelink(symlink)harklinkreflinkThese options are mutually exclusive, and there are nuances to how beets (and thus this
plugin) behave when there multiple set. See thebeets import documentationand#36for more details.Reimporting has an additional nuance when copying of linking files that are already in
the library, in which files will be moved rather than duplicated. This behavior in
Filetote is identical to that of beets. See thebeets reimport documentationfor more
details.Other CLI OperationsAdditional commands such such asmove,modify,update, etc. will also trigger
Filetote to handle files. These commands typically work withqueries, targeting
specific files that match the supplied query. Please note that the operation executed
by beets for these commands do not use the value set in the config file underimport,
they instead are specified as part of the CLI command.Examples ofconfig.yamlplugins:filetotepaths:default:$albumartist/$year - $album/$track - $titlesingleton:Singletons/$artist - $titleext:.log:$albumpath/$artist - $albumext:.cue:$albumpath/$artist - $albumpaired_ext:.lrc:$albumpath/$medianame_newfilename:cover.jpg:$albumpath/coverfiletote:extensions:.cue .log .jpgfilename:"cover.jpg"pairing:enabled:trueextensions:".lrc"print_ignored:trueOr:plugins:filetotepaths:default:$albumartist/$year - $album/$track - $titlesingleton:Singletons/$artist - $titlefiletote:extensions:.cuepatterns:artworkdir:-"[sS]cans/"-"[aA]rtwork/"pairing:enabled:trueextensions:".lrc"paths:pattern:artworkdir:$albumpath/artworkpaired_ext:.lrc:$albumpath/$medianame_oldfilename:cover.jpg:$albumpath/coverMulti-Disc and Nested Import Directoriesbeets imports multi-disc albums as a single unit (see beets documentation). By
default, this results in the media importing to a single directory in the library.
Artifacts and extra files in the initial subdirectories will brought by Filetote to the
destination of the file's they're near, resulting in them landing where one would expect.
Because of this, the files will also be moved by Filetote to any specified subdirectory
in the library if the path definition creates "Disc N" subfoldersas described in the beets documentation.In short, artifacts and extra files in these scenarios should simply just move/copy as
expected.Advanced renaming for multi-disc albumsThe value for$albumpathis actually based on the path for the Item (music file) the
lead to the artifact to be moved. Since it's common to store multi-disc albums with
subfolders, this means that by default the artifact or extra file in question will also
be in a subfolder.For macOS and Linux, to achieve a different location (say the media is inArtist/Disc 01but artifacts are intended to be inArtist/Extras),..can be used
to navigate to the parent directory of the$albumpathso that the entirety of the
media's path does not have to be recreated. For Windows, the entire media path would
need to be recreated as Windows sees..as an attempt to create a directory with the
name..within path instead of it being a path component representing the parent.macOS & LinuxThe following example will have the following results on macOS & Linux:Music:~/Music/Artist/2014 - Album/Disc 1/media.mp3Artifact:~/Music/Artist/2014 - Album/Extras/example.logplugins:filetotepaths:default:$albumartist/$year - $album/$track - $titlecomp:$albumartist/$year - $album/Disc $disc/$track - $titlefiletote:extensions:.logpaths:ext:log:$albumpath/../Extras/$old_filenameWindowsThe following example will have the following results on Windows:Music:~/Music/Artist/2014 - Album/Disc 1/media.mp3Artifact:~/Music/Artist/2014 - Album/Extras/example.logplugins:filetotepaths:default:$albumartist/$year - $album/$track - $titlecomp:$albumartist/$year - $album/Disc $disc/$track - $titlefiletote:extensions:.logpaths:ext:log:$albumartist/$year - $album/Extras/$old_filenameWhy Filetote and Not Other Plugins?Filetote serves the same core purpose as thecopyfilertifactspluginand theextrafilesplugin, however both have lacked in maintenance over the last few years.
There are outstanding bugs in each (thoughcopyfilertifactshas seen some recent
activity resolving some). In addition, each are lacking in certain features and
abilities, such as hardlink/reflink support, "paired" file handling, and extending
renaming options. What's more, significant focus has been provided to Filetote around
Python3 conventions, linting, and typing in order to promote healthier code and easier
maintenance.Filetote strives to encompass all functionality thatbothcopyfilertifactsandextrafilesprovide, and then some!Migrating fromcopyfilertifactsFiletote can be configured using nearly identical configuration ascopyfilertifacts,
simply replacing the name of the plugin in its configuration settings.There is one
change that's needed if all extensions are desired, as Filetote does not grab all
extensions by default (ascopyfilertifactsdoes).To accommodate, simply explicitly
state all extension using.*:filetote:extensions:.*Otherwise, simply replacing the name in the config section will work. For example:copyartifacts:extensions:.cue .logWould become:filetote:extensions:.cue .logPath definitions can also be specified in the way thatcopyfileartifactsdoes,
alongside other path definitions for beets. E.g.:paths:ext:log:$albumpath/$artist - $albumMigrating fromextrafilesFiletote can be configured using nearly identical configuration asextrafiles, simply
replacing the name of the plugin in its configuration settings. For example:extrafiles:patterns:all:"*.*"Would become:filetote:patterns:all:"*.*"Path definitions can also be specified in the way thatextrafilesdoes, e.g.:filetote:patterns:artworkdir:-'[sS]cans/'-'[aA]rtwork/'paths:artworkdir:$albumpath/artworkVersion Upgrade InstructionsCertain versoins require changes to configurations as upgrades occur. Please see below
for specific steps for each version.0.4.0Default forextensionsis nowNoneAs of version0.4.0, Filetote no longer set the default forextensionsto.*.
Instead, setting Filetote to collect all extensions needs to be explicitly defined, e.g.:filetote:extensions:.*Pairing Config Changespairinghas been converted from a boolean to an object with other like-config. Take
the following config:filetote:pairing:truepairing_only:falseThese will both now be represented as individual settings withinpairing:filetote:pairing:enabled:truepairing_only:falseextensions:".lrc"Both remain optional and both default tofalse.DevelopmentThe development version can be installed withPoetry, a Python dependency manager
that provides dependency isolation, reproducibility, and streamlined packaging to PyPI.Testing and linting is performed withTox.Filetote currently supports Python 3.6+, which aligns with the most recent version of
beets (v1.6.0).1. Install Poetry & Tox:python3-mpipinstallpoetrytox2. Clone the repository and install the plugin:gitclonehttps://github.com/gtronset/beets-filetote.gitcdbeets-filetote
poetryinstall3. Update the config.yaml to utilize the plugin:pluginpath:-/path/to.../beets-filetote/beetsplug4. Run or test with Poetry (and Tox):Run beets with the following to locally develop:poetryrunbeetTesting can be run with Tox, ex.:poetryruntox-epy312For other linting environments, seetox.ini. Ex:black:poetryruntox-eblackDocker:A Docker Compose configuration is available for running the plugin in a controlled
environment. Running thedocker-compose.ymlfile for details.ThanksThis plugin originated as a hard fork frombeets-copyartifacts (copyartifacts3).Thank you to the original work done by Sami Barakat and Adrian Sampson, along with the
largerbeetscommunity.Please report any issues you may have and feel free to contribute.LicenseCopyright (c) 2022 Gavin TronsetLicensed under theMIT license.