Skip to content

shmoinkle/musiclinker4plex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

musiclinker4plex

Python 3.11 License

Maintain a Plex friendly directory of artists and albums with symlinks

Why ?

There's no one-size-fits-all way to organize music. But, Plex expects your music only one way.

With this script you can leave your music files where they are while simultaneously maintaining a directory of symlinks that Plex reads from instead.

How ?

A small json file tracks every configured path's mtime. On each run, the script checks the modified timestamp of each configured path and all the subdirs for "Category" paths. When you add or remove an "Artist" or "VA Album" from those paths, the script notices the parent directory's mtime change and queues it for processing.

Unless --force is set, only directories where mtime differs are going to get their contents processed into symlinks.

All broken symlinks found are automatically removed beforehand.

When no changes are detected, the script exits without attempting to process anything further, making it safe to run at a high frequency.

Setup

Requires Python 3.11+

# make your config.
cp m4p.config{.example,}.toml

# update your config.
vim m4p.config.toml

Usage

usage: musiclinker4plex.py [-h] [-c DIR] [-f] [-j] [-l CSV] [-v] [--allow-root]

Maintain a Plex friendly directory of artists and albums with symlinks

options:
  -h, --help           show this help message and exit
  -c DIR, --confs DIR  path to configs
  -f, --force          clear existing symlinks and recreate all links
  -j, --junctions      [Windows] use NTFS junctions instead of symlinks
  -l CSV, --libs CSV   CSV list of libraries to exclusively scan (in order)
  -v, --verbose        print every action being taken and the results
  --allow-root         allow running as root/admin

Examples

alias m4p="/bin/python3 /usr/local/bin/musiclinker4plex.py"

# a regular scan of all configured libraries
m4p

# on windows, only process two libraries in D:\Configs\m4p.config.toml
# use NTFS Junctions instead of symlinks 
m4p --confs "D:\Configs" --libs "library3,library1" --junctions

# as root, read config file at /root/m4p.config.toml
# process all configured libraries and directories
# and overwrite any existing symlinks in the process 
m4p -c "/root" -f --allow-root 

# example cron to run run every minute.
* * * * * /bin/python3 /usr/local/bin/musiclinker4plex.py

Config

Files

m4p.config.toml - What folders to scan and create links from

m4p.cache.libraryname.json - The modified timestamps of all scanned folders per library

Settings

  • Each [library] section defines one output directory.
  • Each library name is case-sensitive.
  • Only use absolute paths.
Setting Description
plex_root The directory where the symlinks will be created
ignore * List of dir names to skip when scanning
artist_dirs Array of dirs containing an Artist / Album layout
artist_cat_dirs Array of dirs containing a Category / Artist / Album layout
special_artists * Matching dirs within artist_dirs ** become individual artist dirs
va_album_dirs Array of dirs containing Various Artists Album
va_album_cat_dirs Array of dirs containing a Category / Various Artists Album layout
  • * wildcards supported
  • ** applies to both artist_dirs and artist_cat_dirs

A note about plex_root

Each library's plex_root is assumed to never share the same tree of any other library's and is not recommended.

  • The script will process the libraries and directories in the configured order (or the order they're specified in when called with --libs).
  • If the plex_root of multiple libraries share the same tree, the first library will get the symlinks-
  • -AND, if you specify the force flag in that setup the last library scanned gets the symlinks. fun.

Example

Directory layout

/home/you/MUSIC/
|__ Artists/
|   |__ Aaron Funk/
|   |   |__ Last Step/
|   |   |__ Venetian Snares/
|   |__ Wesley Willis/
|__ Comedy/
|   |__ Matt Besser/
|   |__ misc/
|__ Games/
|   |__ FEZ/
|   |__ The Tomorrow Children/
|__ Genres/
|   |__ Dabke/
|       |__ Omar Souleyman/
|__ Labels/
|   |__ Childisc/
|   |   |__ Childisc Vol. 1/
|   |__ misc/
|   |__ Planet Mu/
|       |__ µ Allstars Criminal/
|__ Movies/
    |__ A Clockwork Orange/
    |__ Return To Silent Hill/

Config

[mylibrary]
plex_root = "/home/you/MUSIC_Plex"
ignore = ["misc"]
artist_dirs = [
    "/home/you/MUSIC/Artists",
    "/home/you/MUSIC/Comedy",
]
artist_cat_dirs = ["/home/you/MUSIC/Genres"]
special_artists = ["Aaron Funk"]
va_album_dirs = [
    "/home/you/MUSIC/Games",
    "/home/you/MUSIC/Movies",
]
va_album_cat_dirs = ["/home/you/MUSIC/Labels"]

Results

/home/you/MUSIC_Plex/
|__ Last Step       -> ../MUSIC/Artists/Aaron Funk/Last Step
|__ Matt Besser     -> ../MUSIC/Comedy/Matt Besser
|__ Omar Souleyman  -> ../MUSIC/Genres/Dabke/Omar Souleyman
|__ Venetian Snares -> ../MUSIC/Artists/Aaron Funk/Venetian Snares
|__ Wesley Willis   -> ../MUSIC/Artists/Wesley Willis
|__ Various Artists/
    |__ A Clockwork Orange    -> ../../MUSIC/Movies/A Clockwork Orange
    |__ Childisc Vol. 1       -> ../../MUSIC/Labels/Childisc/Childisc Vol. 1
    |__ FEZ                   -> ../../MUSIC/Games/FEZ
    |__ Return To Silent Hill -> ../../MUSIC/Movies/Return To Silent Hill
    |__ The Tomorrow Children -> ../../MUSIC/Games/The Tomorrow Children
    |__ µ Allstars Criminal   -> ../../MUSIC/Labels/Planet Mu/µ Allstars Criminal

Windows Notes

  • Junctions & symlinks are only possible on NTFS within Windows.
  • symlinks are the default and are supported in Windows 10+ with Developer Mode enabled.
  • You can specify the --junctions flag to force the use of NTFS Junctions, which does not require additional permissions.
  • If you run this script within a WSL environment, the symlinks created are only usable if-
    • Plex Media Server runs natively on Windows and has Dev Mode enabled, or
    • Plex Media Server also runs from WSL.

Other Notes

  • If a soundtrack only has one artist, that's fine. Plex's ability to correctly match the album seems unaffected. YMMV
  • The script needs to be run from the same system as Plex.
    • It might be worth adding optional remote path links.
  • The script assumes all "Artist" and "VA Album" dirs are unique across an entire library.
    • If there are duplicates, only the first one scanned will get the symlink created for it (unless --force is set).
    • It might be worth trying to create suffixed (Artist$N) symlinks. Plex's matcher might be cool with it?
  • Plex integration to queue up a library scan upon changes being made would definitely be nice.
    • A clean, dependency free way to do that is a requirement.
  • This is made to be multi-platform but has not had much testing. Should definitely add pytest
    • Haven't had a chance to test it much on Windows, in particular.

About

Creates a Plex compatible directory hierarchy of symlinks based on your music library sorting preferences.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages