2025-05-30 15:52:49 +00:00
#!/bin/bash
2025-05-30 16:09:56 +00:00
#Parameters:
#1st parameter: Channel you want to turn into a playlist. Leave blank to save your subscriptions (cookie file required)
2025-05-30 15:52:49 +00:00
channel = ${ 1 :- "subscriptions" }
2025-05-30 16:09:56 +00:00
#2nd parameter: Time limit for the download. Leave blank to save all videos from the last month.
2025-05-30 15:52:49 +00:00
breaktime = ${ 2 :- "today-1month" }
2025-05-30 16:09:56 +00:00
#3rd parameter: Seconds between data requests. Decrease to make downloads faster, but your account may be temporarily blocked if you use a number too low.
2025-05-30 15:52:49 +00:00
sleeptime = ${ 3 :- "1.0" }
2025-05-30 16:09:56 +00:00
#Internal variables:
2025-05-30 15:52:49 +00:00
#Via https://stackoverflow.com/questions/59895/how-do-i-get-the-directory-where-a-bash-script-is-located-from-within-the-script
2025-05-30 16:09:56 +00:00
folder = $( cd -- " $( dirname -- " ${ BASH_SOURCE [0] } " ) " & >/dev/null && pwd )
2025-05-30 15:52:49 +00:00
#Required to download your own subscriptions.
#Obtain this file through the procedure listed at
# https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp
#and place it next to your script.
2025-05-30 16:09:56 +00:00
cookies = " ${ folder } /yt-cookies.txt "
subfolder = " ${ folder } / ${ channel } "
archive = " ${ subfolder } / ${ channel } .txt "
sortcsv = " ${ subfolder } / ${ channel } -sort.csv "
csv = " ${ subfolder } / ${ channel } .csv "
json = " ${ subfolder } / ${ channel } .json "
2025-05-30 15:52:49 +00:00
python = "python"
if [ [ -f "/opt/venv/bin/python" ] ] ; then
2025-05-30 16:09:56 +00:00
python = "/opt/venv/bin/python"
2025-05-30 15:52:49 +00:00
fi
ytdl = "/usr/bin/yt-dlp"
if [ [ -f "/opt/venv/bin/yt-dlp" ] ] ; then
2025-05-30 16:09:56 +00:00
ytdl = "/opt/venv/bin/yt-dlp"
2025-05-30 15:52:49 +00:00
fi
2025-05-30 16:09:56 +00:00
if [ [ -z " ${ subfolder } " ] ] ; then
mkdir " ${ subfolder } "
2025-05-30 15:52:49 +00:00
fi
2025-05-30 16:09:56 +00:00
cd " ${ subfolder } " || exit
2025-05-30 15:52:49 +00:00
#If available, you can use the cookies from your browser directly:
# --cookies-from-browser "firefox"
2025-05-30 16:09:56 +00:00
url = " https://www.youtube.com/@ ${ channel } "
if [ [ " ${ channel } " = "subscriptions" ] ] ; then
url = "https://www.youtube.com/feed/subscriptions"
2025-05-30 15:52:49 +00:00
fi
2025-05-30 16:09:56 +00:00
if [ [ -z " ${ cookies } " ] ] ; then
" ${ python } " " ${ ytdl } " " ${ url } " \
--skip-download --download-archive " ${ archive } " \
--dateafter " ${ breaktime } " \
--extractor-args youtubetab:approximate_date \
--break-on-reject --lazy-playlist --write-info-json \
--sleep-requests " ${ sleeptime } "
2025-05-30 15:52:49 +00:00
else
2025-05-30 16:09:56 +00:00
" ${ python } " " ${ ytdl } " " ${ url } " \
--cookies " ${ cookies } " \
--skip-download --download-archive " ${ archive } " \
--dateafter " ${ breaktime } " \
--extractor-args youtubetab:approximate_date \
--break-on-reject --lazy-playlist --write-info-json \
--sleep-requests " ${ sleeptime } "
2025-05-30 15:52:49 +00:00
fi
2025-05-30 16:09:56 +00:00
rm -rf " ${ csv } "
2025-05-30 15:52:49 +00:00
ls -t | grep -e ".info.json" | while read -r x; do
2025-05-30 16:09:56 +00:00
echo youtube $( jq -c '.id' " ${ x } " | sed -e "s/\"//g" ) | tee -a " ${ archive } " &
jq -c '[.upload_date, .timestamp, .uploader , .title, .webpage_url]' " ${ subfolder } / ${ x } " | while read -r i; do
echo " ${ i } " | sed -e "s/^\[//g" -e " s/\] $//g " -e "s/\\\\\"/" /g" | tee -a " ${ csv } " &
done
jq -c '[.upload_date, .timestamp]' " ${ subfolder } / ${ x } " | while read -r i; do
echo " ${ i } , ${ x } " | sed -e "s/^\[//g" -e "s/\],/,/g" -e "s/\\\\\"/" /g" | tee -a " ${ sortcsv } " &
done
if [ [ $( jobs -r -p | wc -l) -ge $(( $( getconf _NPROCESSORS_ONLN) * 3 * 2 )) ] ] ; then
wait -n
fi
2025-05-30 15:52:49 +00:00
done
wait
2025-05-30 16:09:56 +00:00
sort " ${ sortcsv } " | uniq >" /tmp/ ${ channel } -sort-ordered.csv "
echo " {\"playlistName\":\" ${ channel } \",\"protected\":false,\"description\":\"Videos to watch later\",\"videos\":[ " >" /tmp/ ${ channel } .db "
cat " /tmp/ ${ channel } -sort-ordered.csv " | while read -r line; do
file = $( echo " ${ line } " | cut -d ',' -f3-)
echo " ${ file } "
jq -c " {\"videoId\": .id, \"title\": .title, \"author\": .uploader, \"authorId\": .channel_id, \"lengthSeconds\": .duration, \"published\": .epoch, \"timeAdded\": $( date +%s) , \"playlistItemId\": \" $( cat /proc/sys/kernel/random/uuid) \", \"type\": \"video\"} " " ${ subfolder } / ${ file } " | tee -a " /tmp/ ${ channel } .db "
echo "," >>" /tmp/ ${ channel } .db "
2025-05-30 15:52:49 +00:00
done
2025-05-30 16:09:56 +00:00
echo " ],\"_id\":\" ${ channel } \",\"createdAt\": $( date +%s) ,\"lastUpdatedAt\": $( date +%s) } " >>" /tmp/ ${ channel } .db "
rm " ${ json } "
cat " /tmp/ ${ channel } .db " | grep -v -e ":[ ]*null" | tr '\n' '\r' | sed -e "s/,\r\]/\]/g" | tr '\r' '\n' | jq -c "." >" ${ json } " && rm " /tmp/ ${ channel } .db "
rm " /tmp/ ${ channel } -sort-ordered.csv " " ${ sortcsv } "
sort " ${ csv } " | uniq >" /tmp/ ${ channel } -without-header.csv "
echo '"Upload Date", "Timestamp", "Uploader", "Title", "Webpage URL"' >" /tmp/ ${ channel } .csv "
cat " /tmp/ ${ channel } -without-header.csv " >>" /tmp/ ${ channel } .csv "
mv " /tmp/ ${ channel } .csv " " ${ csv } "
rm " /tmp/ ${ channel } -without-header.csv "
sort " ${ archive } " | uniq >" /tmp/ ${ channel } .txt "
mv " /tmp/ ${ channel } .txt " " ${ archive } "