feat(plum): add release/quality switching commands

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-06-07 20:41:24 -07:00
parent 6680cc6e7f
commit 71bed108a9

View file

@ -166,6 +166,43 @@ build_resume_playlist() { # <showdir> <resume-path> -> writes $PLAYLIST from tha
rm -f "$PLAYLIST.all"
}
# --- release/quality switching ---------------------------------------------
json_str() { python3 -c "import json,sys; print(json.dumps(sys.argv[1]))" "$1"; }
cur_path() { getprop path | sed 's/^"//; s/"$//'; } # strip the JSON quotes
se_of() { printf '%s' "$1" | grep -oiE 's[0-9]+e[0-9]+' | head -1; }
showdir_of() { # <path> -> $MEDIA_ROOT/<category>/<show>
local p="$1" rel cat show
case "$p" in "$MEDIA_ROOT"/*) rel="${p#"$MEDIA_ROOT"/}" ;; *) return 1 ;; esac
cat="${rel%%/*}"; rel="${rel#*/}"; show="${rel%%/*}"
[ -n "$cat" ] && [ -n "$show" ] || return 1
printf '%s/%s/%s\n' "$MEDIA_ROOT" "$cat" "$show"
}
rel_label() { # <a path within the release> -> "720p x264"
local q c
q=$(printf '%s' "$1" | grep -oiE '2160p|1080p|720p|480p' | head -1)
c=$(printf '%s' "$1" | grep -oiE 'x265|hevc|x264|av1' | head -1)
printf '%s%s' "${q:-SD}" "${c:+ $c}"
}
# JSON list of release subtrees of the current show that contain the current
# episode (so movies / partial sets are excluded). [{id,label,current}].
releases_json() {
local cp sd sxe out="[" first=1 d mf id label curflag
cp=$(cur_path); [ -n "$cp" ] || { echo "[]"; return; }
sd=$(showdir_of "$cp") || { echo "[]"; return; }
sxe=$(se_of "$cp")
while IFS= read -r d; do
[ -n "$d" ] || continue
if [ -n "$sxe" ]; then mf=$(find "$d" -type f -iregex "$VIDEO_RE" -iname "*$sxe*" 2>/dev/null | head -1)
else mf=$(find "$d" -type f -iregex "$VIDEO_RE" 2>/dev/null | head -1); fi
[ -n "$mf" ] || continue
id=$(basename "$d"); label=$(rel_label "$mf")
case "$cp" in "$d"/*) curflag=true ;; *) curflag=false ;; esac
[ $first -eq 1 ] || out="$out,"; first=0
out="$out{\"id\":$(json_str "$id"),\"label\":$(json_str "$label"),\"current\":$curflag}"
done < <(find "$sd" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
echo "$out]"
}
status_json() {
if [ ! -S "$SOCK" ]; then echo '{"playing":false}'; return; fi
printf '{"playing":true,"paused":%s,"title":%s,"volume":%s,"position":%s,"duration":%s,"playlist_pos":%s,"playlist_count":%s}\n' \
@ -225,6 +262,29 @@ case "$cmd" in
goto-ep)
[ $# -ge 1 ] || die "usage: black-tv goto-ep <index 0-based>"
setprop playlist-pos "$1"; echo "playlist-pos=$(getprop playlist-pos)" ;;
releases)
releases_json ;;
switch)
# switch the CURRENT episode to another release at the same timestamp, then
# continue the rest of the series in that release.
[ $# -ge 1 ] || die "usage: black-tv switch <release-id>"
[ -S "$SOCK" ] || die "nothing playing"
cp=$(cur_path); [ -n "$cp" ] || die "nothing playing"
pos=$(getprop time-pos); pos="${pos%.*}"; [ -n "$pos" ] || pos=0
sxe=$(se_of "$cp"); [ -n "$sxe" ] || die "current item has no SxxEyy to map"
sd=$(showdir_of "$cp") || die "cannot locate show dir for current item"
td="$sd/$1"; [ -d "$td" ] || die "release not found: $1"
tep=$(find "$td" -type f -iregex "$VIDEO_RE" -iname "*$sxe*" 2>/dev/null | head -1)
[ -n "$tep" ] || die "release '$1' has no $sxe"
# seamless: open the target file already positioned at the saved second
ipc "{\"command\":[\"loadfile\",$(json_str "$tep"),\"replace\",\"start=$pos\"]}" >/dev/null
# queue the rest of the series in the target release after this episode
find "$td" -type f -iregex "$VIDEO_RE" ! -ipath '*/Specials/*' ! -ipath '*/Extras/*' | sort > "$PLAYLIST.all"
start=$(grep -nF "$tep" "$PLAYLIST.all" | head -1 | cut -d: -f1)
if [ -n "$start" ]; then tail -n +"$((start + 1))" "$PLAYLIST.all" > "$PLAYLIST"; else : > "$PLAYLIST"; fi
[ -s "$PLAYLIST" ] && ipc "{\"command\":[\"loadlist\",$(json_str "$PLAYLIST"),\"append\"]}" >/dev/null
rm -f "$PLAYLIST.all"
echo "switched to $(rel_label "$tep") at ${pos}s" ;;
watched)
[ -f "$WATCHLOG" ] || { echo "[]"; exit 0; }
if [ $# -ge 1 ]; then grep -iF "$1" "$WATCHLOG" | tail -100; else tail -100 "$WATCHLOG"; fi ;;
@ -239,5 +299,5 @@ case "$cmd" in
stop) sudo systemctl stop "$UNIT" 2>/dev/null || true; sudo pkill -x mpv 2>/dev/null || true; rm -f "$SOCK"; echo stopped ;;
status) status_json ;;
ensure-display) ensure_display; echo "display ready: $(cat /sys/class/drm/card0-${CONNECTOR}/status 2>/dev/null)" ;;
*) die "usage: black-tv {play <path>|play-show <q> [S] [E]|resume-show <q>|enqueue <x>|goto-ep N|pause|resume|toggle|vol N|seek S|next|prev|stop|status|watched [q]|ensure-display}" ;;
*) die "usage: black-tv {play <path>|play-show <q> [S] [E]|resume-show <q>|enqueue <x>|goto-ep N|releases|switch <rel>|pause|resume|toggle|vol N|seek S|next|prev|stop|status|watched [q]|ensure-display}" ;;
esac