mirror of
https://github.com/CoolnsX/repos_scripts.git
synced 2025-12-20 07:15:20 +05:30
Feat: continue watching feature in jellyfin
This commit is contained in:
114
jellyfin
114
jellyfin
@@ -24,17 +24,17 @@ err() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
save_config() {
|
save_config() {
|
||||||
if [ -f "$config_file" ] && grep -q "$1" "$config_file";then
|
if [ -f "$config_file" ] && grep -q "$1" "$config_file"; then
|
||||||
sed -i "s|$1=.*|$1=$2|g" "$config_file" >/dev/null 2>&1
|
sed -i "s|$1=.*|$1=$2|g" "$config_file" >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
#shellcheck disable=SC2059
|
#shellcheck disable=SC2059
|
||||||
printf "$1=$2\n" >> "$config_file"
|
printf "$1=$2\n" >>"$config_file"
|
||||||
sed '/^$/d' "$config_file" >/dev/null 2>&1
|
sed '/^$/d' "$config_file" >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
[ -z "$3" ] && success "$1 Saved in config, to override config values use it as envs\n"
|
[ -z "$3" ] && success "$1 Saved in config, to override config values use it as envs\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
auth_quick_connect () {
|
auth_quick_connect() {
|
||||||
info "Generating Quick Connect Code.."
|
info "Generating Quick Connect Code.."
|
||||||
DEVICE_ID="$(uuidgen)"
|
DEVICE_ID="$(uuidgen)"
|
||||||
custom_auth='Authorization: MediaBrowser Client="jellyfin-cli", Device="jellyfin-cli", DeviceId="'"$DEVICE_ID"'", Version="'"$VERSION"'"'
|
custom_auth='Authorization: MediaBrowser Client="jellyfin-cli", Device="jellyfin-cli", DeviceId="'"$DEVICE_ID"'", Version="'"$VERSION"'"'
|
||||||
@@ -42,8 +42,7 @@ auth_quick_connect () {
|
|||||||
info "Your Quick Connect Code: "
|
info "Your Quick Connect Code: "
|
||||||
printf '%s\n' "$CODE"
|
printf '%s\n' "$CODE"
|
||||||
info "Waiting for you to authorized."
|
info "Waiting for you to authorized."
|
||||||
while curl -s "$JF_URL/QuickConnect/Connect?Secret=$SECRET" -H "$custom_auth" | grep -q '"Authenticated":false';
|
while curl -s "$JF_URL/QuickConnect/Connect?Secret=$SECRET" -H "$custom_auth" | grep -q '"Authenticated":false'; do
|
||||||
do
|
|
||||||
printf '.'
|
printf '.'
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
@@ -59,8 +58,8 @@ auth_quick_connect () {
|
|||||||
check_config_auth() {
|
check_config_auth() {
|
||||||
info "Checking Config.."
|
info "Checking Config.."
|
||||||
[ -f "$config_file" ] || return 1
|
[ -f "$config_file" ] || return 1
|
||||||
for i in JF_USER_ID JF_TOKEN JF_URL;do
|
for i in JF_USER_ID JF_TOKEN JF_URL; do
|
||||||
if ! grep -qE "$i=(.+)" "$config_file";then
|
if ! grep -qE "$i=(.+)" "$config_file"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -74,15 +73,15 @@ check_config_auth() {
|
|||||||
configure() {
|
configure() {
|
||||||
info "Welcome to Jellyfin CLI script, we will go through some configuration.\n"
|
info "Welcome to Jellyfin CLI script, we will go through some configuration.\n"
|
||||||
[ -f "$config_file" ] && . "$config_file"
|
[ -f "$config_file" ] && . "$config_file"
|
||||||
if ! [ -f "$config_file" ] || ! grep -qE "JF_URL=(.+)" "$config_file";then
|
if ! [ -f "$config_file" ] || ! grep -qE "JF_URL=(.+)" "$config_file"; then
|
||||||
ask "First, Where is Jellyfin hosted? : "
|
ask "First, Where is Jellyfin hosted? : "
|
||||||
read -r JF_URL
|
read -r JF_URL
|
||||||
save_config "JF_URL" "$JF_URL"
|
save_config "JF_URL" "$JF_URL"
|
||||||
fi
|
fi
|
||||||
if ! [ -f "$config_file" ] || ! grep -qE "JF_TOKEN=(.+)" "$config_file";then
|
if ! [ -f "$config_file" ] || ! grep -qE "JF_TOKEN=(.+)" "$config_file"; then
|
||||||
auth_quick_connect
|
auth_quick_connect
|
||||||
fi
|
fi
|
||||||
if ! [ -f "$config_file" ] || ! grep -qE "JF_USER_ID=(.+)" "$config_file";then
|
if ! [ -f "$config_file" ] || ! grep -qE "JF_USER_ID=(.+)" "$config_file"; then
|
||||||
eval "$(curl -s "$JF_URL/Users/Me" -H 'Authorization: MediaBrowser Token="'"$JF_TOKEN"'"' | sed -nE 's|.*"Id":"([^"]*)".*|JF_USER_ID=\1|p')"
|
eval "$(curl -s "$JF_URL/Users/Me" -H 'Authorization: MediaBrowser Token="'"$JF_TOKEN"'"' | sed -nE 's|.*"Id":"([^"]*)".*|JF_USER_ID=\1|p')"
|
||||||
save_config "JF_USER_ID" "$JF_USER_ID"
|
save_config "JF_USER_ID" "$JF_USER_ID"
|
||||||
fi
|
fi
|
||||||
@@ -91,20 +90,24 @@ configure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mpv_jellyfin() {
|
mpv_jellyfin() {
|
||||||
|
[ -z "$1" ] && return 0
|
||||||
|
[ -z "$2" ] && return 0
|
||||||
|
|
||||||
success "Playing $2 on mpv"
|
success "Playing $2 on mpv"
|
||||||
url="$JF_URL/Items/$1/Download?api_key=$JF_TOKEN"
|
url="$JF_URL/Items/$1/Download?api_key=$JF_TOKEN"
|
||||||
sub="$JF_URL/Videos/$(printf '%s' "$1" | sed -E 's/(.{8})(.{4})(.{4})(.{4})(.{12})/\1-\2-\3-\4-\5/')/$1/Subtitles/0/0/Stream.ass?api_key=$JF_TOKEN"
|
sub="$JF_URL/Videos/$(printf '%s' "$1" | sed -E 's/(.{8})(.{4})(.{4})(.{4})(.{12})/\1-\2-\3-\4-\5/')/$1/Subtitles/0/0/Stream.ass?api_key=$JF_TOKEN"
|
||||||
! curl -s "$sub" | grep -q "Error processing request" && sub_arg="--sub-file=$sub"
|
! curl -s "$sub" | grep -q "Error processing request" && sub_arg="--sub-file=$sub"
|
||||||
#shellcheck disable=SC2086
|
#shellcheck disable=SC2086
|
||||||
nohup mpv --input-ipc-server="$socket" --force-media-title="$2" "$url" $sub_arg >/dev/null 2>&1 &
|
nohup mpv --input-ipc-server="$socket" --start="$((playbackPositionTicks / 10000000))" --force-media-title="$2" "$url" $sub_arg >/dev/null 2>&1 &
|
||||||
track_progress "$(printf '%s' "$1" | sed -E 's/(.{8})(.{4})(.{4})(.{4})(.{12})/\1-\2-\3-\4-\5/')"
|
track_progress "$(printf '%s' "$1" | sed -E 's/(.{8})(.{4})(.{4})(.{4})(.{12})/\1-\2-\3-\4-\5/')"
|
||||||
}
|
}
|
||||||
|
|
||||||
track_progress() {
|
track_progress() {
|
||||||
ITEM_ID=$1
|
ITEM_ID=$1
|
||||||
\cat <<EOF >"$progress_track_file"
|
\cat <<EOF >"$progress_track_file"
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
positionTicks=$playbackPositionTicks
|
||||||
while sleep 5;do
|
while sleep 5;do
|
||||||
position=\$(echo '{"command" :["get_property","playback-time"]}' | socat - "$socket" 2>/dev/null | sed -nE 's_.*data":([^,]*).*_\1_p' | tr -d '.' | sed 's|$|0|g')
|
position=\$(echo '{"command" :["get_property","playback-time"]}' | socat - "$socket" 2>/dev/null | sed -nE 's_.*data":([^,]*).*_\1_p' | tr -d '.' | sed 's|$|0|g')
|
||||||
[ -z "\$position" ] && break
|
[ -z "\$position" ] && break
|
||||||
@@ -115,16 +118,65 @@ rm "$socket"
|
|||||||
rm "\$0"
|
rm "\$0"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$progress_track_file"
|
chmod +x "$progress_track_file"
|
||||||
setsid -f "$progress_track_file"
|
setsid -f "$progress_track_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_data() {
|
get_data() {
|
||||||
curl -s "${JF_URL}/$1" -H 'Authorization: MediaBrowser Token="'"$JF_TOKEN"'"' -H "Accept: application/json" | sed 's|\[{|\n|g;s|},{|\n|g' | sed -nE 's|^"Name":"([^"]*)",.*,"Id":"([^"]*)".*Primary":\{?"([^"]*)".*|\2\t\3\t\1|p' | menu "$2"
|
curl -s "${JF_URL}/$1" -H 'Authorization: MediaBrowser Token="'"$JF_TOKEN"'"' -H "Accept: application/json" | sed 's|\[{|\n|g;s|},{|\n|g' | sed -nE 's|^"Name":"([^"]*)",.*,"Id":"([^"]*)".*"PlaybackPositionTicks":([^,]*),.*Primary":\{?"([^"]*)".*|\2\t\4\t\1\t\3|p' | menu "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
shows() {
|
||||||
|
season=$(get_data "Shows/$id/Seasons?userId=$JF_USER_ID" "Select Season >")
|
||||||
|
[ -z "$season" ] && exit 1
|
||||||
|
season_title=$(printf "%s" "$season" | cut -f3)
|
||||||
|
season_id=$(printf "%s" "$season" | cut -f1)
|
||||||
|
episode=$(get_data "Shows/$id/Episodes?seasonId=$season_id&userId=$JF_USER_ID" "Select Episode >")
|
||||||
|
[ -z "$episode" ] && exit 1
|
||||||
|
episode_title=$(printf "%s" "$episode" | cut -f3)
|
||||||
|
episode_id=$(printf "%s" "$episode" | cut -f1)
|
||||||
|
title="$title $season_title ep: $episode_title"
|
||||||
|
id=$episode_id
|
||||||
|
playbackPositionTicks=$(printf '%s' "$episode" | cut -f4)
|
||||||
|
}
|
||||||
|
|
||||||
|
collection() {
|
||||||
|
collection=$(get_data "UserViews?userId=$JF_USER_ID" "Select Collection >")
|
||||||
|
|
||||||
|
[ -z "$collection" ] && exit 1
|
||||||
|
collection_id=$(printf '%s' "$collection" | cut -f1)
|
||||||
|
collection_title=$(printf '%s' "$collection" | cut -f3 | sed 's|.$||g')
|
||||||
|
|
||||||
|
data=$(get_data "Items?IncludeItemTypes=$collection_title&Recursive=false&ParentId=$collection_id" "Select $collection_title >")
|
||||||
|
[ -z "$data" ] && exit 1
|
||||||
|
id=$(printf "%s" "$data" | cut -f1)
|
||||||
|
title=$(printf "%s" "$data" | cut -f3)
|
||||||
|
playbackPositionTicks=$(printf '%s' "$data" | cut -f4)
|
||||||
|
|
||||||
|
case "$collection_title" in
|
||||||
|
Show) shows ;;
|
||||||
|
Movie) ;; # the id and title already belongs to Movie.
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
resume() {
|
||||||
|
resume=$(get_data "UserItems/Resume?enableImages=true" "Resume >")
|
||||||
|
[ -z "$resume" ] && exit 1
|
||||||
|
id=$(printf "%s" "$resume" | cut -f1)
|
||||||
|
title=$(printf "%s" "$resume" | cut -f3)
|
||||||
|
playbackPositionTicks=$(printf '%s' "$resume" | cut -f4)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextUp() {
|
||||||
|
nextup=$(get_data "Shows/NextUp?enableTotalRecordCount=true&disableFirstEpisode=false&enableResumable=true" "Next UP >")
|
||||||
|
[ -z "$nextup" ] && exit 1
|
||||||
|
id=$(printf "%s" "$nextup" | cut -f1)
|
||||||
|
title=$(printf "%s" "$nextup" | cut -f3)
|
||||||
|
playbackPositionTicks=$(printf '%s' "$nextup" | cut -f4)
|
||||||
}
|
}
|
||||||
|
|
||||||
menu() {
|
menu() {
|
||||||
fzf --prompt="$1" --layout=reverse --border --with-nth=3.. --preview="img2sixel '$JF_URL/items/{1}/Images/Primary?fillHeight=450&quality=96'" --preview-window=right,70%
|
fzf --prompt="$1" --layout=reverse --border -d'\t' --with-nth=3 --preview="img2sixel '$JF_URL/items/{1}/Images/Primary?fillHeight=450&quality=96'" --preview-window=right,70%
|
||||||
}
|
}
|
||||||
|
|
||||||
check_config_auth || configure
|
check_config_auth || configure
|
||||||
@@ -136,26 +188,12 @@ info ""
|
|||||||
socket="/tmp/${0##*/}-mpvsocket"
|
socket="/tmp/${0##*/}-mpvsocket"
|
||||||
progress_track_file="/tmp/${0##*/}-progress"
|
progress_track_file="/tmp/${0##*/}-progress"
|
||||||
|
|
||||||
|
what_to_watch=$(printf "My Media\nResume\nNext Up" | fzf --prompt="Select >" --layout=reverse --border)
|
||||||
|
|
||||||
what_to_watch=$(get_data "UserViews?userId=$JF_USER_ID" "What To Watch? >")
|
case "$what_to_watch" in
|
||||||
[ -z "$what_to_watch" ] && exit 1
|
'My Media') collection ;;
|
||||||
what_to_watch_id=$(printf '%s' "$what_to_watch" | cut -f1)
|
Resume) resume ;;
|
||||||
what_to_watch_title=$(printf '%s' "$what_to_watch" | cut -f3 | sed 's|.$||g')
|
'Next Up') nextUp ;;
|
||||||
|
esac
|
||||||
|
|
||||||
data=$(get_data "Items?IncludeItemTypes=$what_to_watch_title&Recursive=false&ParentId=$what_to_watch_id" "Select $what_to_watch_title >")
|
mpv_jellyfin "$id" "$title"
|
||||||
[ -z "$data" ] && exit 1
|
|
||||||
id=$(printf "%s" "$data" | cut -f1)
|
|
||||||
title=$(printf "%s" "$data" | cut -f3)
|
|
||||||
|
|
||||||
[ "$what_to_watch_title" = "Movie" ] && mpv_jellyfin "$id" "$title" && exit 0
|
|
||||||
|
|
||||||
season=$(get_data "Shows/$id/Seasons?userId=$JF_USER_ID" "Select Season >")
|
|
||||||
[ -z "$season" ] && exit 1
|
|
||||||
season_title=$(printf "%s" "$season" | cut -f3)
|
|
||||||
season_id=$(printf "%s" "$season" | cut -f1)
|
|
||||||
episode=$(get_data "Shows/$id/Episodes?seasonId=$season_id&userId=$JF_USER_ID" "Select Episode >")
|
|
||||||
[ -z "$episode" ] && exit 1
|
|
||||||
episode_title=$(printf "%s" "$episode" | cut -f3)
|
|
||||||
episode_id=$(printf "%s" "$episode" | cut -f1)
|
|
||||||
|
|
||||||
mpv_jellyfin "$episode_id" "$title $season_title ep: $episode_title"
|
|
||||||
|
|||||||
17
yt-music
17
yt-music
@@ -78,7 +78,8 @@ get_cookies() {
|
|||||||
get_data() {
|
get_data() {
|
||||||
lol=$(date +%s)
|
lol=$(date +%s)
|
||||||
grep -q "SAPISID" "$logdir/cookies" 2>/dev/null && sapisid_hash=$(printf '%s_%s' "$lol" "$(printf '%s %s %s' "$lol" "$(sed -nE 's|.*SAPISID=([^;]*);.*|\1|p' "$logdir/cookies")" "$base_url" | sha1sum | cut -d' ' -f1)") && sapisid_header="Authorization: SAPISIDHASH $sapisid_hash" || sapisid_header=""
|
grep -q "SAPISID" "$logdir/cookies" 2>/dev/null && sapisid_hash=$(printf '%s_%s' "$lol" "$(printf '%s %s %s' "$lol" "$(sed -nE 's|.*SAPISID=([^;]*);.*|\1|p' "$logdir/cookies")" "$base_url" | sha1sum | cut -d' ' -f1)") && sapisid_header="Authorization: SAPISIDHASH $sapisid_hash" || sapisid_header=""
|
||||||
curl -X POST -A "${3:-$agent}" -s "$base_url/youtubei/v1/$1?prettyPrint=false" -H "content-type:application/json" -d "$2" -e "$base_url" -b "$(cat "$cookie")" -H "$sapisid_header"
|
output=$(curl -X POST -A "${3:-$agent}" -s "$base_url/youtubei/v1/$1?prettyPrint=false" -H "content-type:application/json" -d "$2" -e "$base_url" -b "$(cat "$cookie")" -H "$sapisid_header")
|
||||||
|
printf '%s\n' "$output" | tee -a "$logdir/logs"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_music_list() {
|
get_music_list() {
|
||||||
@@ -181,7 +182,7 @@ play() {
|
|||||||
\"racyCheckOk\": true
|
\"racyCheckOk\": true
|
||||||
}"
|
}"
|
||||||
|
|
||||||
audio_url=$(get_data "player" "$json" "$yt_agent" | sed -nE 's_.*itag":251,"url":"([^"]*)".*_\1_p')
|
audio_url=$(get_data "player" "$json" "$yt_agent" | sed -nE 's_.*itag":240,"url":"([^"]*)".*_\1_p')
|
||||||
[ -z "$audio_url" ] && return 0
|
[ -z "$audio_url" ] && return 0
|
||||||
if [ -n "$2" ]; then
|
if [ -n "$2" ]; then
|
||||||
printf "Name >> %s\n" "$title"
|
printf "Name >> %s\n" "$title"
|
||||||
@@ -190,7 +191,7 @@ play() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
curl -s "https://i.ytimg.com/vi/$id/hqdefault.jpg" -o - | magick convert - -crop 270x270+105+45 "$logdir/default.jpg" && notify-send -e -h "string:x-canonical-private-synchronous:${0##*/}" -i "$logdir/default.jpg" "Now Playing" "$title" -t 5000
|
curl -s "https://i.ytimg.com/vi/$id/hqdefault.jpg" -o - | magick convert - -crop 270x270+105+45 "$logdir/default.jpg" && notify-send -e -h "string:x-canonical-private-synchronous:${0##*/}" -i "$logdir/default.jpg" "Now Playing" "$title" -t 5000
|
||||||
pgrep -f "$socket" >/dev/null || (setsid -f mpv --really-quiet --input-ipc-server="$socket" --idle --quiet >/dev/null && sleep 1)
|
pgrep -f "$socket" >/dev/null || (setsid -f mpv --really-quiet --input-ipc-server="$socket" --idle --quiet --user-agent="$yt_agent" >/dev/null && sleep 1)
|
||||||
printf '{"command":["loadfile","%s","replace"]}\n' "$audio_url" | socat - "$socket"
|
printf '{"command":["loadfile","%s","replace"]}\n' "$audio_url" | socat - "$socket"
|
||||||
printf 'SONG="%s"\nARTIST="%s"\nID="%s"' "$(printf '%s' "$title" | sed 's|[^-]*$||g;s|-$||g;s| $||g;s|^ ||g;s/\\//g;s|"||g')" "$(printf '%s' "$title" | sed 's_.* - __;s| $||;s|"||g')" "$id" >"$logdir/current"
|
printf 'SONG="%s"\nARTIST="%s"\nID="%s"' "$(printf '%s' "$title" | sed 's|[^-]*$||g;s|-$||g;s| $||g;s|^ ||g;s/\\//g;s|"||g')" "$(printf '%s' "$title" | sed 's_.* - __;s| $||;s|"||g')" "$id" >"$logdir/current"
|
||||||
|
|
||||||
@@ -210,9 +211,9 @@ search_play() {
|
|||||||
[ -z "$query" ] && notify-send -e "Err.. Search query empty" -u critical -h "string:x-canonical-private-synchronous:${0##*/}" && exit 1
|
[ -z "$query" ] && notify-send -e "Err.. Search query empty" -u critical -h "string:x-canonical-private-synchronous:${0##*/}" && exit 1
|
||||||
|
|
||||||
#storing context
|
#storing context
|
||||||
printf '{"client":{"clientName":"WEB_REMIX","clientVersion":"1.20250305.01.00"}}' >"$logdir/context"
|
printf '{"client":{"clientName":"WEB_REMIX","clientVersion":"1.20250310.01.00"}}' >"$logdir/context"
|
||||||
#extracting your cookies so that the song list are according to your taste
|
#extracting your cookies so that the song list are according to your taste
|
||||||
get_cookies
|
#get_cookies
|
||||||
|
|
||||||
if ! (printf '%s' "$query" | grep -q 'https:' || printf '%s' "$query" | grep -q 'ID:'); then
|
if ! (printf '%s' "$query" | grep -q 'https:' || printf '%s' "$query" | grep -q 'ID:'); then
|
||||||
#json for song search
|
#json for song search
|
||||||
@@ -275,16 +276,16 @@ base_url="https://music.youtube.com"
|
|||||||
cookie="$logdir/cookies"
|
cookie="$logdir/cookies"
|
||||||
|
|
||||||
# user agents, used by script
|
# user agents, used by script
|
||||||
random_no=$(head /dev/urandom | tr -dc '0-4' | cut -c1)
|
random_no=$(head /dev/urandom | tr -dc '1-5' | cut -c1)
|
||||||
# web
|
# web
|
||||||
agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/13$random_no.0.0.0 Safari/537.36"
|
agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/13$random_no.0.0.0 Safari/537.36"
|
||||||
# android
|
# android
|
||||||
extra_up=$((random_no > 1))
|
extra_up=$((random_no > 1))
|
||||||
# youtube
|
# youtube
|
||||||
yt_ver="19.42.41"
|
yt_ver="20.10.40"
|
||||||
yt_agent="com.google.android.youtube/$yt_ver (Linux; U; Android 1$random_no) gzip"
|
yt_agent="com.google.android.youtube/$yt_ver (Linux; U; Android 1$random_no) gzip"
|
||||||
# youtube music
|
# youtube music
|
||||||
yt_music_ver="7.24.51"
|
yt_music_ver="8.10.51"
|
||||||
yt_music_agent="com.google.android.apps.youtube.music/$yt_music_ver (Linux; U; Android 1$random_no) gzip"
|
yt_music_agent="com.google.android.apps.youtube.music/$yt_music_ver (Linux; U; Android 1$random_no) gzip"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user