Create a centralized media library with a service:
I keep very little data on my small SSD that I use strictly for the OS. I have 6 other drives in this computer not including my mounted network shares. I tend to keep stuff split up between different drives and I've often thought it would be nice to be able to have my movies, music, pictures from different sources symlinked to a central library. This is normally not doable using symlinks as a single symlink cannot link to multiple sources.
I found a simple python script today that will symlink multiple sources into a single destination directory. It allows all sources to be viewed and accessed together in one single library. The script auto generates all the symlinks very quickly when launched and it also removes old dead links at the same time. I have only used it briefly but it has really impressed me. I have been looking for a way to centralize thousands of movies into one all encompassing movie library. This script seems to accomplish that perfectly.
My python skills are pretty much non-existent. I did not write this script I was merely lucky enough to stumble across it on the internet. This script seemed a natural for a systemd timer to run on a schedule hourly. You could also use a bash alias to run the script and update the library on demand.
These are my notes on how this is best accomplished.
Create the following python script (choose any name you wish):
/home/$USER/.local/bin/movie_library.py
Substitute your username for "$USER" in the above example.
Add the following contents to the script:
#!/usr/bin/env python3
import os
import sys
if len(sys.argv)<3:
print("target and source not found")
exit(1)
target = sys.argv[1]
sources = sys.argv[2:]
currlinks = os.listdir(target)
compare = []
for dr in sources:
for f in os.listdir(dr):
compare.append(f)
if not f in currlinks:
# create link
os.symlink(dr+"/"+f, target+"/"+f)
# clean up possible broken links
for link in currlinks:
if not link in compare:
os.remove(target+"/"+link)
Information regarding customizing and executing the script:
Substitute your username for "$USER" in any examples here.
The script is called via its directory path and then additional arguments for the source directories are passed sequentially.
Script path:
/home/$USER/.local/bin/movie_library.py
"sys.argv[1]" is passed to the script first (which is the destination directory) where you want all source directories symlinked.
/home/$USER/Movies
The following line in the script adds the directories you wish to link together.
sources = sys.argv[2:]
"target = sys.argv[1]" is the destination path, and it is the first argument. The "sources = sys.argv[2:] line reflects how many sources you wish to symlink into the destination directory. I am symlinking four directories, so the fields will be "2, 3, 4, 5". This is more simply expressed as [2:] which includes all source paths from 2 to the end of the last path specified. Simply add on any path you wish linked into your library directory.
My destination directory for the movie library is:
/home/$USER/Movies
These are my video sources:
/run/media/$USER/6TB_SG18/Movies
/run/media/$USER/6TB_SG18/Recent_Movies
/run/media/$USER/1TB_WD_ext4/Transfers/Movies-Scraped
/run/media/$USER/1TB_WD_ext4/Transfers/Movies-Unscraped
To call the python script via arguments to link four separate locations into one directory:
/python3 /home/$USER/.local/bin/movie_library.py /home/$USER/Movies /run/media/$USER/6TB_SG18/Movies /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Scraped /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Unscraped
The python script can be started as above from the terminal, or from the "ExecStart=" line of a systemd service.
Substitute your username for "$USER" in any of the above examples.
This can be called by a bash alias on demand, or by a systemd timer to update the library contents on a regular schedule.
The script will automatically symlink any new contents added since the last time it was run to the destination directory. It will also automatically delete the symlinks to any files that have been removed since its last execution. The script will display an error on duplicate files but the symlink will still be created and functional.
Create a systemd service and timer to execute the python script at a predetermined interval to update your library contents.
I have chosen to update the Library on an hourly basis. You may alter the time interval if you would prefer this done more or less frequently.
Substitute your username for "$USER" in any of the below examples.
Create the service file:
/home/$USER/.config/systemd/user/library-update.service
With the following contents:
#~/.config/systemd/user/library-update.service
#systemctl --user enable library-update.service
#systemctl --user disable library-update.service
#systemctl --user start library-update.service
#systemctl --user stop library-update.service
#systemctl --user status library-update.service
#systemctl --user daemon-reload
[Unit]
Description=Library service
StopWhenUnneeded=yes
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/python3 /home/$USER/.local/bin/movie_library.py /home/$USER/Movies /run/media/$USER/6TB_SG18/Movies /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Scraped /run/media/$USER/1TB_WD_ext4/Transfers/Movies-Unscraped
[Install]
WantedBy=multi-user.target
Create the timer file:
/home/$USER/.config/systemd/user/library-update.timer
With the following contents:
#~/.config/systemd/user/library-update.timer
#systemctl --user enable library-update.timer
#systemctl --user disable library-update.timer
#systemctl --user start library-update.timer
#systemctl --user stop library-update.timer
#systemctl --user status library-update.timer
#systemctl --user daemon-reload
#systemctl --user list-timers
[Unit]
Description=Runs library update hourly
[Timer]
OnUnitActiveSec=1h
Unit=library-update.service
OnBootSec=180
[Install]
WantedBy=timers.target
Once you have finished creating the service and timer, enable them both:
systemctl --user enable library-update.timer && systemctl --user enable library-update.service
Then restart.
(edit)
This is a modified version of the above service that also includes a combined library for my network shares mounted in /media.
I have written this service so that it should not fail if the network shares are offline when the service starts.
Service to include mounted network shares:
#~/.config/systemd/user/library-update.service
#systemctl --user enable library-update.service
#systemctl --user disable library-update.service
#systemctl --user start library-update.service
#systemctl --user stop library-update.service
#systemctl --user status library-update.service
#systemctl --user daemon-reload
[Unit]
Description=Library service
StopWhenUnneeded=yes
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=sleep 20
ExecStart=/home/htpc/.local/bin/movie_library.py /home/htpc/Videos/Movies-Local /run/media/htpc/6TB_SG18/Movies /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Scraped /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Unscraped /run/media/htpc/1TB_WD_ext4/Transfers/Movies-New /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Newer /run/media/htpc/1TB_WD_ext4/Transfers/Movies-Newest
ExecStartPost=-/home/htpc/.local/bin/movie_library.py /home/htpc/Videos/Movies-Remote /media/nfs/3tb/Movies_A-M /media/nfs/4tb/Movies /media/nfs/4tb/Movies-New /media/nfs/4tb/Movies-Newer /media/nfs/4tb/Movies-Newest
[Install]
WantedBy=multi-user.target
Be sure to substitute in your username in any path statements that require modification.