funkwlmpv/funkwhale-cli.sh

511 lines
15 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
instance=$(jj -i config.json instance)
instance_hist='instance.hist'
ordering='-creation_date'
default_player_command='mpv --no-vid --no-ytdl --network-timeout=30'
instance_point="https://$instance/api/v1"
download_selected_track=$(jj -i config.json download_selected_track)
local_content_only=$(jj -i config.json local_content_only)
touch .auth.json
chmod 600 .auth.json
make_login()
{
auth="$(jj -i .auth.json "$(echo "$instance" | sed 's/\./\\\./g')")"
if [ -n "$auth" ]; then
default_curl_opt()
{
curl --retry 3 -s --compressed -H "Authorization: Bearer $auth" "$@"
}
listen_token=$(default_curl_opt "$instance_point/users/me" | jj tokens.listen)
echo '+Authorized account+'
export listen_token
else
listen_token=''
export listen_token
default_curl_opt()
{
curl --retry 3 -s --compressed "$@"
}
fi
export default_curl_opt
}
make_login
delq()
{
sed 's/"//g'
}
funkwhale_api_check_api_limits()
{
default_curl_opt "$instance_point/rate-limit/" | jj -p
}
funkwhale_api_tags()
{
mkdir -p tags_db
if [ ! -f "tags_db/tags_$instance.gz" ]; then
tagsload=$(default_curl_opt "$instance_point/tags?playable=true")
pageindex=$(echo "$tagsload" | jj next)
echo "$tagsload" | jj -l 'results.#.name' | delq | gzip > tags_db/tags_"$instance".gz
clear
while [ -n "$pageindex" ]; do
echo "Loading... +$(echo "$tagsload" | jj 'results.#') tags"
echo "$tagsload" | jj -l 'results.#.name' | delq | gzip >> tags_db/tags_"$instance".gz
pageindex=$(echo "$tagsload" | jj next)
tagsload=$(default_curl_opt "$pageindex")
sleep 1
done
fi
tag=$(cat tags_db/tags_"$instance".gz | gzip -d | fzy)
}
funkwhale_api_get_tracks()
{
if [ "$local_content_only" = 'true' ]; then
default_scope="domain:$instance"
else
default_scope='all'
fi
default_curl_opt \
"$instance_point/tracks?ordering=$ordering&playable=true&scope=$default_scope&page_size=50&page=$1&tag=$tag&artist=$2" \
2>&1 | tee preload
}
play_or_download_selected()
{
mkdir -p music_dl
if [ "$download_selected_track" = 'true' ]; then
default_curl_opt --tcp-fastopen --output - --url "https://$instance$download_track&token=$listen_token" | \
tee "music_dl/$artist_name - $title.$download_ext" | $default_player_command -
else
$default_player_command "https://$instance$play_track?token=$listen_token"
fi
}
funkwhale_menu_tracks()
{
sub2_menu=1
level=1
echo 'Loading tracks...'
funkwhale_api_get_tracks "$1" "$2" 1>> /dev/null
count_tracks_results=$(jj -i preload 'results.#')
echo '#EXTM3U\n' > playlist.m3u8
chmod 600 playlist.m3u8
counter_titles=0
while [ $sub2_menu -eq $level ]; do
titles=$(jj -i preload -l 'results.#.title' | nl -s: -v0 -w1)
playlist=$(jj -i preload -l 'results.#.listen_url' | delq)
menu_album_tracks_choice=$(echo "Back\nMain menu\nListen all\n$titles" | fzy)
case $menu_album_tracks_choice in
"Back")
if [ $sub2_menu -eq 2 ]; then
sub2_menu=1
else
sub2_menu=0
fi ;;
"Main menu") sub2_menu=0 && sub_menu=0 ;;
"Listen all")
echo > playlist.m3u8
chmod 600 playlist.m3u8
tracks_for_playlist=$(jj -i preload -l 'results.#.uploads.0.listen_url' | delq)
for i in $tracks_for_playlist; do
title=$(jj -i preload results."$counter_titles".title)
artist_name=$(jj -i preload results."$counter_titles".artist.name)
duration=$(jj -i preload results."$counter_titles".uploads.0.duration)
echo "#EXTINF:$duration, $artist_name - $title" >> playlist.m3u8
counter_titles=$(expr $counter_titles + 1)
echo "https://$instance$i&token=$listen_token\n" >> playlist.m3u8
done
$default_player_command playlist.m3u8 ;;
*)
index=$(echo "$menu_album_tracks_choice" | cut -f 1 -d:)
play_track=$(jj -i preload results."$index".listen_url)
download_track=$(jj -i preload results."$index".uploads.0.listen_url)
download_ext=$(jj -i preload results."$index".uploads.0.extension)
title=$(jj -i preload results."$index".title)
artist_name=$(jj -i preload results."$index".artist.name)
play_or_download_selected
;;
esac
done
}
funkwhale_api_get_albums()
{
get_json=$(default_curl_opt "$instance_point/albums?ordering=$ordering&playable=true&page=$1" 2>&1 | tee preload)
}
funkwhale_menu_albums()
{
echo 'Loading albums...'
funkwhale_api_get_albums "$1"
echo "Avalaible $(jj -i preload count) albums"
sub_menu=1
while [ $sub_menu -eq 1 ]; do
titles=$(jj -i preload -l 'results.#.title' | nl -s: -v0 -w1)
menu_albums_choice=$(echo "Main menu\n$titles\nNext page" | fzy)
case $menu_albums_choice in
"Main menu") sub_menu=0 ;;
"Next page")
page=$(expr "$page" + 1)
get_json=$(funkwhale_api_get_albums "$page")
;;
*)
index=$(echo "$menu_albums_choice" | cut -f 1 -d:)
album_id=$(jj -i preload results."$index".id)
funkwhale_menu_albums_tracks "$album_id"
esac
done
}
funkwhale_menu_albums_tracks()
{
if [ $sub2_menu -eq 1 ]; then
sub2_menu=2
level=2
else
sub2_menu=1
level=1
fi
tracks_a=$(default_curl_opt "$instance_point/tracks?ordering=$ordering&playable=true&album=$1&page_size=50")
while [ $sub2_menu -eq $level ]; do
titles=$(echo "$tracks_a" | jj -l 'results.#.title' | nl -s: -v0 -w1)
playlist=$(echo "$tracks_a" | jj -l 'results.#.listen_url' | delq)
menu_album_tracks_choice=$(echo "Back\nMain menu\nListen all\n$titles" | fzy)
case $menu_album_tracks_choice in
"Back")
if [ $sub2_menu -eq 2 ]; then
sub2_menu=1
else
sub2_menu=0
fi ;;
"Main menu") sub2_menu=0 && sub_menu=0 ;;
"Listen all")
echo > playlist.m3u8
chmod 600 playlist.m3u8
for i in $playlist; do
if [ -n "$auth" ]; then
echo "https://$instance$i?token=$listen_token\n" >> playlist.m3u8
else
echo "https://$instance$i\n" >> playlist.m3u8
fi
done
$default_player_command playlist.m3u8 ;;
*)
index=$(echo "$menu_album_tracks_choice" | cut -f 1 -d:)
play_track=$(echo "$tracks_a" | jj results."$index".listen_url)
if [ -n "$auth" ]; then
$default_player_command "https://$instance$play_track?token=$listen_token"
else
$default_player_command "https://$instance$play_track"
fi ;;
esac
done
}
funkwhale_api_get_artists()
{
default_curl_opt "$instance_point/artists?ordering=-creation_date&playable=true&page=$1"
}
funkwhale_api_get_artist()
{
default_curl_opt "$instance_point/artists/$1"
}
funkwhale_menu_artists()
{
echo 'Loading artists...'
get_json=$(funkwhale_api_get_artists "$1")
echo "Avalaible $(echo "$get_json" | jj count) artists"
sub_menu=1
while [ $sub_menu -eq 1 ]; do
names=$(echo "$get_json" | jj -l 'results.#.name' | nl -s: -v0 -w1)
menu_artists_choice=$(echo "Main menu\n$names\nNext page" | fzy)
case $menu_artists_choice in
"Main menu") sub_menu=0 ;;
"Next page")
page=$(expr "$page" + 1)
get_json=$(funkwhale_api_get_artists "$page")
;;
*)
index=$(echo "$menu_artists_choice" | cut -f 1 -d:)
artist_id=$(echo "$get_json" | jj results."$index".id)
funkwhale_menu_albums_artist "$artist_id" ;;
esac
done
}
funkwhale_menu_albums_artist()
{
echo 'Loading albums...'
albums_a=$(funkwhale_api_get_artist "$1")
standart_albums='albums.#.title'
count_albums=$(echo "$albums_a" | jj albums.\#)
if [ "$count_albums" -eq 0 ]; then
echo 'failed: no albums, getting tracks...'
sub2_menu=1
funkwhale_menu_tracks 1 "$1"
return 1
fi
index_type='albums'
echo "Avalaible $count_albums albums"
sub2_menu=1
while [ $sub2_menu -eq 1 ]; do
titles=$(echo "$albums_a" | jj -l $standart_albums | nl -s: -v0 -w1)
menu_albums_choice=$(echo "Back\nMain menu\n$titles" | fzy)
case $menu_albums_choice in
"Main menu") sub2_menu=0 && sub_menu=0 ;;
"Back") sub2_menu=0 ;;
*)
index=$(echo "$menu_albums_choice" | cut -f 1 -d:)
album_id=$(echo "$albums_a" | jj $index_type."$index".id)
funkwhale_menu_albums_tracks "$album_id"
esac
done
}
funkwhale_api_get_tracks_from_channel()
{
sub2_menu=1
track_list=$(default_curl_opt "$instance_point/tracks?channel=$1&playable=true&include_channels=true")
echo "Loaded $(echo "$track_list" | jj count) podcasts"
while [ $sub2_menu -eq 1 ]; do
titles=$(echo "$track_list" | jj -l 'results.#.title' | nl -s: -v0 -w1)
menu_podcast_choice=$(echo "Back\nMain menu\n$titles" | fzy)
case $menu_podcast_choice in
"Back") sub2_menu=0 ;;
"Main menu") sub2_menu=0 && sub_menu=0 ;;
*)
index=$(echo "$menu_podcast_choice" | cut -f 1 -d:)
play_track=$(echo "$track_list" | jj results."$index".listen_url)
$default_player_command "https://$instance$play_track&token=$listen_token"
;;
esac
done
}
funkwhale_get_podcasts_artists()
{
sub_menu=1
echo 'Loading podcast artists...'
default_curl_opt "$instance_point/artists?ordering=-creation_date&playable=true&include_channels=true&content_category=podcast&page=$1" | tee preload 1>> /dev/null
while [ $sub_menu -eq 1 ]; do
count_artists=$(jj -i preload count)
echo "$count_artists avalaible"
podcast_names=$(jj -l -i preload 'results.#.name' | nl -s: -v0 -w1)
menu_podcast_choice=$(echo "Main menu\n$podcast_names" | fzy)
case $menu_podcast_choice in
"Main menu") sub_menu=0 ;;
*)
index=$(echo "$menu_podcast_choice" | cut -f 1 -d:)
channel_uuid=$(jj -i preload results."$index".channel.uuid)
if [ -z "$channel_uuid" ]; then
echo 'Error when loading podcast'
else
funkwhale_api_get_tracks_from_channel "$channel_uuid"
fi ;;
esac
done
}
get_all_avalaible_count_tracks()
{
funkwhale_api_get_tracks 1 1>> /dev/null
export count_all_tracks=$(jj -i preload -l count)
if [ -z "$count_all_tracks" ]; then
echo 'Error: Connection error or API limit expired'
elif [ "$count_all_tracks" -eq 0 ]; then
echo 'Error: No tracks'
else
echo "Tracks avalaible on $instance: $count_all_tracks\n"
fi
}
get_all_avalaible_count_tracks
funkwhale_pseudofs_load()
{
# Создаёт дерево каталогов из id исполнителей
# В каждый каталог складывает плейлист с URL треков для прослушивания\закачки
# tree:
# [fwfs_instance.url]
# |
# | -- [id исполнителя]
# | |
# | | -- плейлист с треками
# | ...
echo "Processing..."
mkdir -p "fwfs_$instance"
counter_page=1
tracks_counter_watch=0
skip_count=0
while true; do
tracks=$(funkwhale_api_get_tracks $counter_page)
next_check=$(echo "$tracks" | jj next)
for idartist in $(echo "$tracks" | jj -l 'results.#.artist.id' | delq | sort -u); do
mkdir -p "fwfs_$instance/$idartist"
if [ ! -f "fwfs_$instance/$idartist/playlist.m3u8" ] && [ ! -f "fwfs_$instance/$idartist/playlist.m3u8.noplay" ]; then
echo '#EXTM3U' > "fwfs_$instance/$idartist/playlist.m3u8"
fi
get_tracks_uuid_by_artist_id=$(echo "$tracks" | jj -l "results.#[artist.id=$idartist]#.uploads.0.uuid" | delq)
for track in $get_tracks_uuid_by_artist_id; do
fid_url=$(echo "$tracks" | jj "results.#[uploads.0.uuid="$track"].fid")
artist_name=$(echo "$tracks" | jj "results.#[uploads.0.uuid="$track"].artist.name")
album_name=$(echo "$tracks" | jj "results.#[uploads.0.uuid="$track"].album.title")
track_name=$(echo "$tracks" | jj "results.#[uploads.0.uuid="$track"].title")
extension=$(echo "$tracks" | jj "results.#[uploads.0.uuid="$track"].uploads.0.extension")
duration=$(echo "$tracks" | jj "results.#[uploads.0.uuid="$track"].uploads.0.duration")
track_filename=$(echo "$artist_name - $album_name - $track_name.$extension")
listen_url=$(echo "$tracks" | jj "results.#[uploads.0.uuid="$track"].uploads.0.listen_url")
full_url=$(echo $listen_url | delq | sed "s/^/https:\/\/$instance/ ; s/$/\&token=$listen_token\&to=ogg/")
echo "adding "$idartist"_$track_filename"
exists_test=$(grep -F "$fid_url" "fwfs_$instance/$idartist/playlist.m3u8")
if [ -n "$exists_test" ]; then
skip_count=$(expr $skip_count + 1)
else
echo "#EXTINF:$duration, "$idartist"_$track_filename\n#$fid_url\n$full_url\n" >> "fwfs_$instance/$idartist/playlist.m3u8"
fi
tracks_counter_watch=$(expr $tracks_counter_watch + 1)
echo "current: $tracks_counter_watch skipped: $skip_count total: $count_all_tracks"
done
chmod 600 "fwfs_$instance/$idartist/playlist.m3u8"
done
if [ -z "$next_check" ]; then
break
fi
counter_page=$(expr $counter_page + 1)
echo "page $counter_page"
done
}
funkwhale_pseudofs_transcode_switch()
{
echo "Transcode to"
transcode_ch=$(echo "none\nogg\nmp3" | fzy)
if [ "$transcode_ch" = 'none' ]; then
sed -i 's/to=.*/to=/' ./fwfs_$instance/*/*.m3u8
else
sed -i "s/to=.*/to=$transcode_ch/" ./fwfs_$instance/*/*.m3u8
fi
}
funkwhale_pseudofs_update_listen_token()
{
echo "Updating listen token for all tracks $instance ..."
sed -i "s/token=.*\&/token=$listen_token\&/" ./fwfs_$instance/*/*.m3u8
echo "Done"
}
trackspls='Tracks'
albumsmenu='Albums'
artistmenu='Artists'
podcasts='Podcasts'
changepod='Switch instance'
load_pseudofs='Create artists local playlist'
checkapilimits='Check API limits (debug)'
Exit='Exit'
while true; do
choice=$(echo "$trackspls\n$albumsmenu\n$artistmenu\n$podcasts\n$changepod\n$load_pseudofs\n$checkapilimits\n$Exit" | fzy)
case "$choice" in
"$trackspls")
echo 'Order by (prefix - is DESC ordering):'
ordering=$(echo 'title\n-title\ncreation_date\n-creation_date\nrelease_date\n-release_date\nrandom' | fzy)
export ordering
echo 'Tags:'
tag=$(echo "TagOFF\nTags from instance\n$(jj -l -i config.json tags | delq)" | fzy)
case $tag in
"TagOFF") tag= ;;
"Tags from instance") funkwhale_api_tags ;;
esac
export tag
echo 'Enter page number: '
read page
funkwhale_menu_tracks "$page"
tag=
;;
"$albumsmenu")
echo 'Enter page number: '
read page
funkwhale_menu_albums "$page" ;;
"$artistmenu")
echo 'Enter page number: '
read page
funkwhale_menu_artists "$page" ;;
"$podcasts")
echo 'Enter page number: '
read page
funkwhale_get_podcasts_artists "$page" ;;
"$changepod")
empty=0
case $(echo 'Recently used\nChoice from list\nManual input' | fzy) in
"Recently used")
if [ -s $instance_hist ]; then
touch $instance_hist && instance=$(cat $instance_hist | fzy)
else
echo 'No recently used instances...'
empty=1
fi ;;
"Choice from list") instance=$(jj -l -i config.json public_list_instances | delq | fzy) ;;
"Manual input") echo "Type instance (ex. $instance):" && read instance ;;
esac
if [ $empty -eq 0 ]; then
echo "$instance" >> $instance_hist
cat $instance_hist | sort | uniq | tee $instance_hist 1>>/dev/null
instance_point="https://$instance/api/v1"
conf_instance_state=$(echo 'Permanent\nTemporaly' | fzy)
if [ "$conf_instance_state" = 'Permanent' ]; then
jj -i config.json instance -v "$instance" -o config.json
else
echo ''
fi
clear
make_login
get_all_avalaible_count_tracks
fi ;;
"$load_pseudofs")
menu_pseudofs=$(echo "Start\nSwitch transcode\nUpdate listen token" | fzy)
case "$menu_pseudofs" in
'Start') funkwhale_pseudofs_load ;; # beta
'Switch transcode') funkwhale_pseudofs_transcode_switch ;;
'Update listen token') funkwhale_pseudofs_update_listen_token ;;
esac
;;
"$checkapilimits")
funkwhale_api_check_api_limits | more ;;
"$Exit")
exit 0 ;;
esac
done