This script uses timers and log file size changes to determine actions, rather than responses from tMM's HTTP API because the necessary calls and data do not exist. This means the script is not perfect but it works very well in my environment for my use cases. YMMV. It also helps to create scheduled tasks or cron jobs that run periodically to pick up anything that this script may have missed, or to add new metadata as it becomes available since new shows don't always have all of the metadata and/or artwork available. I've uploaded additional scripts that can be modified for your environment to cover these tasks: Scan for and download missing artwork, scan for and update show and episode ratings, scan for and scrape new and unscraped shows and episodes. Just update the scripts to use your tMM API key and IP:port. If you use the scripts as files, don't forget to make them executable!
Feel free to use these and let me know your results, problems, or improvements that could be made.
I prefer to use tinyMediaManager (tMM) to manage my media metadata because it provides more flexibility than Sonarr's basic metadata system, which exclusively pulls from TheTVDB. Nothing wrong with this for many people, but I find that sometimes things are missing from one metadata source that you can find at another (e.g., cast/actors might be missing from TheTVDB but can be found by scraping with TMDB or IMDB).
tMM has an HTTP API that allows you to send commands to a running instance of the application. This is ideal for self-hosted setups where Sonarr and/or tMM are running as services or in containers. The tMM HTTP API has endpoints for updating and scraping, as well as some other things (some of which were added based on my personal requests).
For a long time, I was using the basic tMM HTTP API scripts that everyone around the web is probably using:
curl -d '[{"action":"update", "scope":{"name":"all"}},{"action":"scrape", "scope":{"name":"new"}}]' -H "Content-Type: application/json" -H "api-key: redacted" -X POST http://redacted:redacted/api/tvshow
This mostly works, but there are some issues that bothered me and caused me to have to do a lot of manual work for something I was attempting to automate.
- Inefficiency: It's not always necessary (or desired) to update all libraries, or even a library index, when it would suffice to update just the show in tMM.
- Queue Management: tMM's queue system is a bit wonky; updates may execute while another action is being processed, causing things to get missed.
- Conditional Logic: Reduce the need for multiple custom scripts in Sonarr for different events.
- Sonarr Series Delete: Sonarr struggles to delete entire series in a timely fashion (sometimes fails to delete the directory at all).
I started out thinking I could create a script to check if a show exists in tMM when sending commands from Sonarr, which seems like it should be a simple thing but tMM's HTTP API options are limited. This was challenging as there is no straighforward way to request the needed information from tMM's API. Ultimately, I ended up checking for the existence of the tvshow.nfo
file. If it doesn't exist, tMM hasn't scraped the show yet, so the script should update the library and scrape new items which picks up the show and scrapes the show level metadata. After the first episode, only the "show" needs to be updated in tMM, which is much more efficient. As I was working on the script, I discovered more and more scenarios that could be cared for, so it was adapted, and I also needed to log everything for debugging. The script ended up MUCH more complex than I set out to create, but I'm very happy with the result, even though some of the solutions involve less-than-ideal techniques.
This script automates the interaction between Sonarr and tinyMediaManager (tMM). It handles various Sonarr events related to TV shows and episodes and ensures that tMM updates its database accordingly. The script manages a queue of commands for tMM, checks if tMM is idle (based on tmm.log
file size) before executing these commands, and logs all activities.
- User-configurable variables (e.g., file paths, tMM API details, log rotation size).
- Rotates the log file when it reaches a certain size to prevent it from becoming too large.
- Logs script execution details, including the Sonarr event type and relevant information.
- Creates a lock file to prevent multiple instances of the script from running simultaneously.
- Removes the lock file upon script completion.
- Checks if tMM is idle based on the size of its log file to ensure commands are only sent when tMM is not busy.
- The script checks every 20 seconds for
tmm.log
file size changes. This may need adjustment based on your system.
- Adds commands to a queue file and processes the queue.
- Waits until tMM is idle before executing commands from the queue.
- Handles different Sonarr events (SeriesDelete, EpisodeFileDelete, Rename, and Download) and queues appropriate tMM commands based on these events.
- SeriesDelete: Delete the Series directory. tMM update "show" only
- Sonarr's ability to delete an entire series is slow, at best, and often fails to remove the directory (posibly because tMM or other external applications have added files). This script will force removal of the directory immediately and confirm it has been deleted before sending any commands to tMM.
- EpisodeFileDelete:
- tMM updates "show" only
- Rename:
- Update "show" and scrape both "new" and "unscraped" items (fallback)
- tMM sees renamed files as "New" files, appropriately. So, we need to rediscover and rescrape the files when a Rename event is detected.
- Check for, and download, missing artwork at the show level (by path)
- Update "show" and scrape both "new" and "unscraped" items (fallback)
- Download:
- If show exists:
- Update "show" and scrape both "new" and "unscraped" items (fallback)
- Check for, and download, missing artwork at the show level (by path)
- If show does not exist:
- Update "library" and scrape both "new" and "unscraped" (fallback)
- If unknown:
- Update "all" libraries and scrape both "new" and "unscraped" (fallback)
- If show exists:
- SeriesDelete: Delete the Series directory. tMM update "show" only
- Logs unsupported or unknown event types and exits without performing any actions.
- Sonarr and tMM must be installed and working.
- tMM must be configured to use its HTTP API: tMM HTTP API Documentation.
- Sonarr must have access to the tMM log directory (read-only at minimum).
- If using Docker for Sonarr, add a volume mapping to your Compose/Run config (e.g.,
/path/to/tmm/logs:/tmm-logs:ro
).
- If using Docker for Sonarr, add a volume mapping to your Compose/Run config (e.g.,
- Sonarr must not be allowed to create
tvshow.nfo
(Metadata settings). - The script must be executable.
- Patience
- The script checks the
tmm.log
file to determine if tMM is idle before sending any commands. Don't assume you're going to see activity in tMM immediately when events occur in Sonarr. The script will continually add commands to, and pull commands from, the queue file, and then send them to tMM when it's ready. Adjust the$delay
variable if you notice overlapping commands in tMM.
- The script checks the
- Download kitchen-sink.sh from the repository
- Update the User-defined variables and save the script
- Make the script executable:
chmod +x /path/to/kitchen_sink.sh
- Open Sonarr web interface
- Navigate to Connect settings:
- Click on Settings in the left-hand menu.
- Click on the Connect tab.
- Add a new Custom Script:
- Click the + button to add a new notification.
- Select Custom Script from the list of available options.
- Configure the Custom Script:
- Name: Give your script a name for easy identification (e.g. tMM - Kitchen Sink)
- Path: Enter the full path to your script file (e.g. /path/to/kitchen_sink.sh).
- Select the events you want the script to trigger on:
- Recommended:
- On Import Complete
- On Rename
- On Series Delete
- On Episode File Delete
- On Episode File Delete for Upgrade
- Tags: Optionally, add tags if you want the script to run only for series with specific tags.
- Recommended:
- Click the Save button
- Navigate to Metadata settings
- Click on Seeings in the left-hand menu.
- Click on the Metadata tab.
- Disable tvshow.nfo creation (if it's enabled)
- Click on Kodi (XBMC) / Emby
- Uncheck "Series Metadata (tvshow.nfo with full series metadata)"
- If you have Plex/Jellyfin configured to scrape metadata, you may need/want to disable those as well!
2024-07-29 14:11:58 [INFO] Event type: Test
2024-07-29 14:11:58 [INFO] Unsupported event type: Test. No action taken...
2024-07-29 14:13:31 [INFO] Event type: EpisodeFileDelete
2024-07-29 14:13:31 [INFO] Series title: Heroes
2024-07-29 14:13:31 [INFO] Series path: /share/Shows/Heroes (2006)
2024-07-29 14:13:31 [INFO] Episode file deleted: Season 01/Heroes - S01E06 - Better Halves.mkv.
2024-07-29 14:13:31 [INFO] Updating Heroes.
2024-07-29 14:13:31 [INFO] Added command(s) to queue: [{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}}]
2024-07-29 14:13:31 [INFO] Checking if tMM is idle...
2024-07-29 14:13:51 [INFO] tMM is idle.
2024-07-29 14:13:51 [INFO] Executing command(s) from queue: curl -d "[{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}}]" -H "Content-Type: application/json" -H "api-key: redacted" -X POST http://redacted:redacted/api/tvshow
2024-07-29 14:13:51 [INFO] Response from tMM: {"message":"commands prepared"}
2024-07-29 14:13:51 [INFO] Queue is empty, ending script.
2024-07-29 14:14:35 [INFO] Event type: Download
2024-07-29 14:14:35 [INFO] Series title: Heroes
2024-07-29 14:14:35 [INFO] Series path: /share/Shows/Heroes (2006)
2024-07-29 14:14:35 [INFO] Episode file: Season 01/Heroes - S01E06 - Better Halves.mkv
2024-07-29 14:14:35 [INFO] Heroes already exists in tMM.
2024-07-29 14:14:35 [INFO] Updating Heroes and scraping new items.
2024-07-29 14:14:35 [INFO] Added command(s) to queue: [{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}},{"action":"scrape", "scope":{"name":"new"}}]
2024-07-29 14:14:35 [INFO] Checking if tMM is idle...
2024-07-29 14:14:55 [INFO] tMM is idle.
2024-07-29 14:14:55 [INFO] Executing command(s) from queue: curl -d "[{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}},{"action":"scrape", "scope":{"name":"new"}}]" -H "Content-Type: application/json" -H "api-key: redacted" -X POST http://redacted:redacted/api/tvshow
2024-07-29 14:14:55 [INFO] Response from tMM: {"message":"commands prepared"}
2024-07-29 14:14:55 [INFO] Queue is empty, ending script.
2024-07-29 14:18:01 [INFO] Event type: EpisodeFileDelete
2024-07-29 14:18:01 [INFO] Series title: Heroes
2024-07-29 14:18:01 [INFO] Series path: /share/Shows/Heroes (2006)
2024-07-29 14:18:01 [INFO] Episode file deleted: Season 01/Heroes - S01E07 - Nothing to Hide.mkv.
2024-07-29 14:18:01 [INFO] Updating Heroes.
2024-07-29 14:18:01 [INFO] Added command(s) to queue: [{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}}]
2024-07-29 14:18:01 [INFO] Checking if tMM is idle...
2024-07-29 14:18:21 [INFO] tMM is busy. Initial log size: 937864, New log size: 955992
2024-07-29 14:18:21 [INFO] Waiting for tMM to become idle. Delay cycle: 1
2024-07-29 14:18:41 [INFO] tMM is busy. Initial log size: 955992, New log size: 976833
2024-07-29 14:18:41 [INFO] Waiting for tMM to become idle. Delay cycle: 2
2024-07-29 14:19:01 [INFO] tMM is busy. Initial log size: 976833, New log size: 997672
2024-07-29 14:19:01 [INFO] Waiting for tMM to become idle. Delay cycle: 3
2024-07-29 14:19:21 [INFO] tMM is busy. Initial log size: 997672, New log size: 1014399
2024-07-29 14:19:21 [INFO] Waiting for tMM to become idle. Delay cycle: 4
2024-07-29 14:19:41 [INFO] tMM is busy. Initial log size: 1014399, New log size: 1035236
2024-07-29 14:19:41 [INFO] Waiting for tMM to become idle. Delay cycle: 5
2024-07-29 14:20:01 [INFO] tMM is busy. Initial log size: 1035236, New log size: 1054500
2024-07-29 14:20:01 [INFO] Waiting for tMM to become idle. Delay cycle: 6
2024-07-29 14:20:21 [INFO] tMM is busy. Initial log size: 1054500, New log size: 1073709
2024-07-29 14:20:21 [INFO] Waiting for tMM to become idle. Delay cycle: 7
2024-07-29 14:20:41 [INFO] tMM is busy. Initial log size: 1073709, New log size: 1095870
2024-07-29 14:20:41 [INFO] Waiting for tMM to become idle. Delay cycle: 8
2024-07-29 14:21:01 [INFO] tMM is busy. Initial log size: 1095870, New log size: 1111943
2024-07-29 14:21:01 [INFO] Waiting for tMM to become idle. Delay cycle: 9
2024-07-29 14:21:21 [INFO] tMM is idle.
2024-07-29 14:21:21 [INFO] Executing command(s) from queue: curl -d "[{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}}]" -H "Content-Type: application/json" -H "api-key: redacted" -X POST http://redacted:redacted/api/tvshow
2024-07-29 14:21:21 [INFO] Response from tMM: {"message":"commands prepared"}
2024-07-29 14:21:21 [INFO] Queue is empty, ending script.
2024-07-29 14:22:04 [INFO] Event type: Download
2024-07-29 14:22:04 [INFO] Series title: Heroes
2024-07-29 14:22:04 [INFO] Series path: /share/Shows/Heroes (2006)
2024-07-29 14:22:04 [INFO] Episode file: Season 01/Heroes - S01E07 - Nothing to Hide.mkv
2024-07-29 14:22:04 [INFO] Heroes already exists in tMM.
2024-07-29 14:22:04 [INFO] Updating Heroes and scraping new items.
2024-07-29 14:22:04 [INFO] Added command(s) to queue: [{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}},{"action":"scrape", "scope":{"name":"new"}}]
2024-07-29 14:22:04 [INFO] Checking if tMM is idle...
2024-07-29 14:22:24 [INFO] tMM is idle.
2024-07-29 14:22:24 [INFO] Executing command(s) from queue: curl -d "[{"action":"update", "scope":{"name":"show", "args":["/share/Shows/Heroes (2006)"]}},{"action":"scrape", "scope":{"name":"new"}}]" -H "Content-Type: application/json" -H "api-key: redacted" -X POST http://redacted:redacted/api/tvshow
2024-07-29 14:22:24 [INFO] Response from tMM: {"message":"commands prepared"}
2024-07-29 14:22:24 [INFO] Queue is empty, ending script.