jellyfin show manager
I'm back with an overengineered solution for myself. I have a complete show, but I want to have Jellyfin show only up to 2 new episodes at a time. I'll set a cron job to check and add new episodes from the real location as necessary.
So, the overcomplicated script:
files/2024/listings/show-manager.sh (Source)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
#!/bin/sh # File: show-manager.sh # Location: /mnt/public/Support/Programs/jellyfin/scripts/ # Author: bgstack15 # Startdate: 2024-02-18-1 17:24 # SPDX-License-Identifier: GPL-3.0-only # Title: Slowly add new episodes of a completed show to jellyfin # Purpose: Check jellyfin for if the last season of a given show is greater than (x-2)/x episodes, and if so, then add that many new episodes. # History: # Usage: # . ./jellystack-autocomplete.bash ; manage_show stsnw # Improve: # hook in to jellyfin deeper, by checking file paths of the episodes where watched=True, to know which episodes to skip trying to add # learn how to completely clear the "watched" information about the show in jellyfin. # Documentation: # Assumptions: # 1. media files are sorted in "Season 01/" type directories. # 2. Media files are named "*s01e07 *" style which will be sorted alphabetically in episode number order. # 3. watched episodes are in exact order! # 4. input files are all mkv (this is to simplify filtering out non-episode files like images; it could be made more complex as needed) # Dependencies: # jellystack_lib with watched_episodes_for_show() # flow: # get episodes-watched for last season of show (value is, e.g., "8/10" # depends on content being in "Season 01/" directories. get_episodes_watched_count() { # input env vars: library, show, DEBUG # output: prints watched= and total= numbers # usage: $( library="TV" show="Star Trek: Strange New Worlds" get_episodes_watched_count ) _pyverbose=False test -n "${DEBUG}" && _pyverbose=True cd /mnt/public/Support/Programs/jellyfin/scripts . ~/.config/jellystack.viewing.user { python3 <<-EOF import jellystack_lib a = jellystack_lib.get_authenticated_client(url="${server}",username="${username}",password="${password}") b = jellystack_lib.watched_episodes_for_show(a,"${library}","${show}","sum",${_pyverbose}) c = b.split("/") print(f"watched={c[0]}") print(f"total={c[1]}") EOF } } symlink_file() { # input vars: infile SOURCE_DIR LINK_DIR LINK_PREFIX VERBOSE APPLY # reference: printf '%s\n' *s01e0* | while read line ; do ln -sf "../../../off.movies/Star Trek - Strange New Worlds (2021/Season 01/${line}" "/mnt/public/Video/TV/Star Trek - Strange New Worlds (2021/Season 01/" ; done # We now calculate season number based on file name #season_num_str="$( \printf '%02d' "${season_num}" )" base="$( basename "${infile}" )" season_num_str="$( \printf '%02d' "$( echo "${base}" | grep -oE '\<s[0-9]+e[0-9]+' | awk -F'e' '{print $1}' | tr -dc '0-9' )" )" test -n "${VERBOSE}" && echo ln -s "${LINK_PREFIX}/Season ${season_num_str}/${base}" "${LINK_DIR}/Season ${season_num_str}/" test -n "${APPLY}" && ln -s "${LINK_PREFIX}/Season ${season_num_str}/${base}" "${LINK_DIR}/Season ${season_num_str}/" } list_sorted_episode_files() { # input env vars: SOURCE_DIR # if changing from %P, probably do a cd in a sub-shell. find "${SOURCE_DIR}" -name '*mkv' -iregex '.*\<s[0-9]+e[0-9]+\>.*' -printf '%P\n' | sort -n } # Logic flow: # diff=total-watched # if diff < 2, # find the smallest-number episode files and add them in. manage_show() { # input vars: MAX_NEW_EPISODES SOURCE_DIR VERBOSE LIBRARY SHOW # You may also run: manage_show "TV/Star Trek: Strange New Worlds" conf="${1}" test -f "/mnt/public/Support/Programs/jellyfin/scripts/input/${conf}.conf" && . "/mnt/public/Support/Programs/jellyfin/scripts/input/${conf}.conf" # Not necessary with the conf file method #test -z "${SHOW}" && test -z "${LIBRARY}" && { # LIBRARY="$( echo "${1}" | awk -F'/' '{print $1}' )" # SHOW="$( echo "${1}" | awk -F'/' '{print $2}' )" #} test -z "${MAX_NEW_EPISODES}" && MAX_NEW_EPISODES=2 # this next function sets vars: watched, total eval $( library="${LIBRARY}" show="${SHOW}" get_episodes_watched_count ) # hardcode these to test max_new_episodes #watched=6 total=8 diff=$((total-watched)) test -n "${VERBOSE}" && echo "Got watched=${watched} total=${total} diff=${diff}" 1>&2 if test ${diff:-0} -lt ${MAX_NEW_EPISODES} ; then need=$((MAX_NEW_EPISODES-diff)) echo "Must prepare next ${need} episodes." 1>&2 w1=$((watched+1)) end=$((watched+need)) list_sorted_episode_files | sed -n -r -e "${w1},${end}p" | while read infile ; do # VERBOSE and APPLY are used in this function: infile="${infile}" symlink_file done fi } # Before the config file redesign, use one of these: # These will still get "/Season 01" prepended to the file basename: # SOURCE_DIR="/mnt/public/Video/off.movies/Star Trek - Strange New Worlds (2021)" LINK_DIR="/mnt/public/Video/TV/Star Trek - Strange New Worlds (2021)" LINK_PREFIX="../../../off.movies/Star Trek - Strange New Worlds (2021)" LIBRARY="TV" SHOW="Star Trek - Strange New Worlds (2021)" DIR="${SHOW}" manage_show |
So I'm pretty sure this uses yet a new password file, this time as a shell snippet with export statements of the interesting variables, ~/.config/jellystack.viewing.user
. (I don't use the admin user for actually watching, what am I, ridiculous?!)
export password="KEEPASS" username="jellyfin" server="http://vm4:8096"
And then with this autocomplete:
files/2024/listings/jellystack-autocomplete.bash (Source)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/bin/bash # trimmed for blog _show_manager_confs() { # reference: vm4:/etc/bash_completion.d/docker-nfs-check local cur prev words cword; _init_completion || return _tmpfile1="$( mktemp )" # populate list find /mnt/public/Support/Programs/jellyfin/scripts/input -mindepth 1 -maxdepth 1 ! -type d -name '*.conf' -printf '%P\n' | sed -r -e 's/\.conf$//;' > "${_tmpfile1}" COMPREPLY=($( compgen -W "$( cat ${_tmpfile1} )" -- "$cur" | sed -r -e "/^${prev}/d;" )) command rm -rf "${_tmpfile1:-NOTHINGTODEL}" 1>/dev/null 2>&1 return 0 } && complete -F _show_manager_confs manage-show . /mnt/public/Support/Programs/jellyfin/scripts/show-manager.sh alias manage-show="manage_show" |
And any number of files in that input/
directory:
files/2024/listings/strangenewworlds.conf.example (Source)
1 2 3 4 5 6 7 8 9 10 11 12 |
# Where all the episode mkv files are SOURCE_DIR="/mnt/public/Video/off.movies/Star Trek - Strange New Worlds (2021)" # Where they should be placed for Jellyfin to pick them up LINK_DIR="/mnt/public/Video/TV/Star Trek - Strange New Worlds (2021)" # Symlink prefix, so probably the relative path from LINK_DIR to SOURCE_DIR LINK_PREFIX="../../../off.movies/Star Trek - Strange New Worlds (2021)" # Jellyfin library name LIBRARY="TV" # Jellyfin show name SHOW="Star Trek: Strange New Worlds" # Show directory name. Not necessarily the same as SHOW. DIR="Star Trek - Strange New Worlds (2021)" |
So if I've watched 6/7 episodes, and MAX_NEW_EPISODES=2
, then it should symlink in one new episode.
This process counts incorrectly sometimes (because I originally had all the episode files available to it and the counting got messed up), so I just keep increasing MAX_NEW_EPISODES
until it puts the correct number of new episodes in the path for Jellyfin.
So I just run:
. jellystack-autocomplete.bash VERBOSE=1 MAX_NEW_EPISODES=3 manage-show <tab>
And list the show confs, and pick one, and then run it. Once it looks good, I re-run with APPLY=1
.
Comments