Last active
April 9, 2023 11:40
-
-
Save elundmark/a3c1cc9b438ff58fbed584e803431a9c to your computer and use it in GitHub Desktop.
Make m3u playlist of a Youtube Playlist (youtube-dl & jq must be installed)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# $ ./make_m3u_youtube_playlist.sh [-d OUTPUT-DIR] URL [URL ...] | |
if ! which jq &>/dev/null || ! which youtube-dl &>/dev/null; then | |
echo "'youtube-dl' and 'jq' must be installed in \$PATH, exiting." 1>&2 | |
exit 1 | |
fi | |
declare log_file="$HOME/.make_m3u_youtube_playlist.log" | |
declare prefix_id='https://www.youtube.com/watch?v=' | |
declare prefix_pls='https://www.youtube.com/playlist?list=' | |
declare pls_url | |
declare pls_re='^https?://www\.youtube\.com/(watch|playlist)\?.*?list=([^&]+)' | |
declare pls_n | |
declare pls_json | |
declare pls_u | |
declare pls_ids | |
declare pls_titles | |
declare -i replace_tmp=0 | |
declare -a pls_i_arr=() | |
declare -a pls_t_arr=() | |
declare top_str='#EXTM3U'$'\n' | |
declare str | |
declare file_name | |
declare desired_filename | |
declare -i pls_len=0 | |
declare output_dir="$PWD" # where to write the m3u file | |
declare json_re='\{.+\}' | |
declare -a args=() | |
function log_echo () | |
{ | |
echo "${*}" 1>&2 | |
echo "${*}" >> "$log_file" | |
return $? | |
} | |
while getopts ":d:" options ; do | |
case "$options" in | |
d) output_dir="$(sed -r 's#/+$##' <<< "$OPTARG")";; # override directory | |
*) log_echo "Not a valid option, exiting:" "${*}" | |
exit 1;; | |
esac | |
done | |
args+=("${@:OPTIND}") | |
# enable support for multiple playlists at once | |
for i in "${!args[@]}" ; do | |
[[ ${args[i]} = '' ]] && continue | |
pls_url="${args[i]}" | |
# reset loop variables | |
replace_tmp=0 | |
pls_len=0 | |
file_name='' | |
str="$top_str" | |
pls_i_arr=() | |
pls_t_arr=() | |
if [[ ! $pls_url =~ $pls_re ]] ; then | |
log_echo "No valid playlist url given, exiting:" "$pls_url" | |
exit 1 | |
fi | |
pls_url="${BASH_REMATCH[2]}" | |
pls_u="$( | |
# Get the playlist's owner / username - lazy way | |
curl -s -L -A "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:51.0) Gecko/20100101 Firefox/51.0" "${prefix_pls}${pls_url}" \ | |
| grep -oP 'channel-title.+?<a [^>]+>[^<]+</a>' \ | |
| sed -r 's#.+>([^<]+)</a>#\1#' | |
)" | |
if [[ $pls_u = '' ]]; then | |
log_echo "No user found, exiting:" "$pls_u" | |
exit 1 | |
fi | |
pls_json="$(youtube-dl --ignore-config --flat-playlist --dump-single-json "${prefix_pls}${pls_url}")" | |
if [[ $? -ne 0 || ! "$pls_json" =~ $json_re ]] ; then | |
log_echo "Not a playlist or youtube-dl returned an error, exiting:" "$pls_json" | |
exit 1 | |
fi | |
# verify it's valid json first | |
if ! jq . <<< "$pls_json" &>/dev/null; then | |
log_echo "jq could not parse string, exiting:" "$pls_json" | |
exit 1 | |
fi | |
pls_ids="$(jq -rM '.entries[].id' <<< "$pls_json")" || exit 3 | |
pls_titles="$(jq -rM '.entries[].title' <<< "$pls_json")" || exit 4 | |
pls_n="$(jq -rM '.title' <<< "$pls_json")" || exit 5 | |
if [[ $pls_n = '' ]] ; then | |
# Allow empty name | |
pls_n="Untitled" | |
fi | |
pls_len=$(wc -l <<< "$pls_ids") | |
if [[ $pls_len -eq 0 ]] ; then | |
# Don't write empty playlists | |
log_echo "No videos in playlist, exiting:" "$pls_len" | |
exit 1 | |
fi | |
# Turn lines into arrays | |
while read -r LINE; do | |
[[ $LINE = '' ]] && continue | |
# Full url | |
pls_i_arr+=("${prefix_id}${LINE}") | |
done <<< "$pls_ids" | |
while read -r LINE; do | |
[[ $LINE = '' ]] && continue | |
pls_t_arr+=("$LINE") | |
done <<< "$pls_titles" | |
# Build m3u string | |
# https://en.wikipedia.org/wiki/M3U | |
# Playlist will be missing the length property but it should work (does in mpv) | |
for x in "${!pls_i_arr[@]}" ; do | |
[[ ${pls_i_arr[x]} = '' ]] && continue | |
# #EXTM3U | |
# #EXTINF:,User - Title - YT-PL-ID i/size | |
# https://www.youtube.com/watch?v=AAAAAAAAAAA | |
# | |
str="${str}#EXTINF:,$pls_u - $pls_n - ${pls_t_arr[x]} - $pls_url $((x+1))/$pls_len"$'\n'"${pls_i_arr[x]}"$'\n'$'\n' | |
done | |
desired_filename="$output_dir/$pls_u - $pls_n ($pls_len).m3u" | |
file_name="$desired_filename" | |
if [[ -e $file_name ]] ; then | |
log_echo "creating tmp file and backing up existing file:" "$file_name" | |
file_name="$(mktemp -p "$output_dir")" | |
replace_tmp=1 | |
fi | |
echo -n "$str" > "$file_name" | |
if [[ $replace_tmp -eq 1 ]] ; then | |
# depend on mv's backup functionallity to create a bak file | |
mv -v --backup=t "$file_name" "$desired_filename" | tee -a "$log_file" 1>&2 | |
file_name="$desired_filename" | |
fi | |
if [[ $? -ne 0 || ! -f "$file_name" ]] ; then | |
log_echo "$file_name" | |
notify-send -i 'error' "M3U playlist" "Failed to create playlist" | |
else | |
echo "$file_name" | |
notify-send -i 'gtk-ok' "M3U playlist created" "$(basename "$file_name" .m3u)" | |
fi | |
done | |
exit $? |
I get
./make_m3u_youtube_playlist.sh: line 66: unexpected EOF while looking for matching
)'
./make_m3u_youtube_playlist.sh: line 143: syntax error: unexpected end of file`
This worked for me - https://superuser.com/a/1359132
youtube-dl -j --flat-playlist <PLAYLIST_LINK> | jq -r '.id' | sed 's_^_https://youtu.be/_' > playlist.m3u
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
why does it ask for the user?!...