Skip to content

Instantly share code, notes, and snippets.

@bbaster
Created September 24, 2024 21:05
Show Gist options
  • Save bbaster/67d43265234a891260b564112e902094 to your computer and use it in GitHub Desktop.
Save bbaster/67d43265234a891260b564112e902094 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
#A master.m3u8, playlist.m3u8 and index-v1-a1 downloader
#Licensed under the GNU AGPLv3
#Warning: selecting a specific *.m3u8 from a master or playlist file is unsupported! This downloader will pick the first one, you can manually supplement it with another one if preferred.
#The slashes there make no sense, I know
#Usage: escapeURL "url"
escapeURL()
{
printf ${1//\//\\/}
}
#Checks for ffmpeg and aria2
if ! [ $(command -v ffmpeg) ]; then
echo "Error: FFmpeg not found!"
echo "Please install it with \"sudo pacman -S ffmpeg\" on Arch(-based) distros (Manjaro) or with \"sudo apt install ffmpeg\" on Debian(-based) distros (Ubuntu, Pop!_OS, Linux Mint)"
exit 1
fi
if ! [ $(command -v aria2c) ]; then
echo "Error: Aria2 not found!"
echo "Please install it with \"sudo pacman -S aria2\" on Arch(-based) distros (Manjaro) or with \"sudo apt install aria2\" on Debian(-based) distros (Ubuntu, Pop!_OS, Linux Mint)"
exit 1
fi
#Checks for a "link.txt" and "movie.m3u8" file, else reads user input
if [ -s "$(dirname ${BASH_SOURCE})/link.txt" ]; then
echo "Reading link from \"link.txt\" file"
url="$(cat link.txt)"
elif [ -s "$(dirname ${BASH_SOURCE})/movie.m3u8" ]; then
echo "Reading from \"movie.m3u8\" file"
url=file
else
echo -n "Input master.m3u8 or index*.m3u8 URL (try a full curl command with browser headers if a website gives you HTTP 403 errors): "
read -r input
fi
#Checks if input is a curl command, or just an URL
if [[ $input =~ ^curl\ \'(https?.+index.*\.m3u8)\'\s?(.*) ]]; then
echo "curl index"
#Sets some variables
url="$(echo ${BASH_REMATCH[1]})"
url_escaped_canonical="$([[ "$(escapeURL "$url")" =~ (.*)\.m3u8.* ]] && echo "${BASH_REMATCH[1]}")"
url_index="$url"
curl_params="$(echo ${BASH_REMATCH[2]})"
aria2_params="${curl_params//--compressed/}"
aria2_params="${aria2_params//-H/--header}"
elif [[ $input =~ ^curl\ \'(https?.+(master|playlist)\.m3u8)\'\s?(.*) ]]; then
echo "curl master"
#Sets some variables
url="$(echo ${BASH_REMATCH[1]})"
url_escaped_canonical="$([[ "$(escapeURL "$url")" =~ (.*)\\/(master|playlist)\.m3u8.* ]] && echo "${BASH_REMATCH[1]}")"
curl_params="$(echo ${BASH_REMATCH[3]})"
aria2_params="${curl_params//--compressed/}"
aria2_params="${aria2_params//-H/--header}"
#Downloads the master.m3u8 file
eval curl -o master.m3u8 '$url' ${curl_params}
#Adds the canonical URL if missing to the master.m3u8 file
sed -i -E "s/(^[^#h].*$)/${url_escaped_canonical}\/\1/g" master.m3u8
# #Searches for the index*.m3u8 URL in the master.m3u8 file
while read -r line; do
if [[ $line == *index*.m3u8* ]]; then
url_index="$line"
fi
done < master.m3u8
elif [ -n "$input" ]; then
url=$input
fi
#Checks if the URL points to a master.m3u8, playlist.m3u8 or index*.m3u8 file, else exits with an error
if [[ $url =~ https?.*(master|playlist)\.m3u8.* ]]; then
#Sets some variables
url_escaped_canonical="$([[ "$(escapeURL "$url")" =~ (.*)\\/(master|playlist)\.m3u8.* ]] && echo "${BASH_REMATCH[1]}")"
#Downloads the master.m3u8 file
eval curl -o master.m3u8 '$url' ${curl_params}
#Adds the canonical URL if missing to the master.m3u8 file
sed -i -E "s/(^[^#h].*$)/${url_escaped_canonical}\/\1/g" master.m3u8
#Searches for the index-v1-a1.m3u8 URL in the master.m3u8 file
while read -r line; do
if [[ $line == *index*.m3u8* ]]; then
url_index="$line"
fi
done < master.m3u8
elif [[ $url == http*index*.m3u8* ]]; then
echo "index"
#Sets some variables
url_escaped_canonical="$([[ "$(escapeURL "$url")" =~ (.*)\\/index.*\.m3u8.* ]] && echo "${BASH_REMATCH[1]}")"
url_index="$url"
elif [ -n "$input" ]; then
echo "Error: Not a curl command, master.m3u8, playlist.m3u8 nor an index*.m3u8 URL!"
exit 1
fi
#Downloads the index-v1-a1.m3u8 file
if [ "$url" != file ]; then eval curl -o index-v1-a1.m3u8 '$url' ${curl_params}; fi
#Renames movie.m3u8 file
if [ "$url" == file ]; then mv movie.m3u8 index-v1-a1.m3u8; fi
#Adds the canonical URL if missing to the index-v1-a1.m3u8 file
sed -i -E "s/(^[^#h].*$)/${url_escaped_canonical}\/\1/g" index-v1-a1.m3u8
#The actual downloading part, feel free to adjust the parameters "-x" (streams for downloading one file) and "-j" (files to download at once)
eval aria2c --continue=true -x 12 -j 12 -i index-v1-a1.m3u8 ${aria2_params}
#Sorts and concatenates all *.ts segments into one movie.ts file
cat $(ls seg-*.ts video*.ts 2>/dev/null| sort -V) > movie.ts
#Converts the movie.ts to movie.mkv with GPU acceleration, around 9x faster than the video duration at best (unnecessary, you can just watch the movie.ts file)
#ffmpeg -i movie.ts movie.mkv -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' -c:v h264_vaapi -qp 18
#Removes unneded files
rm master.m3u8 index-v1-a1.m3u8 seg-*.ts video*.ts 2>/dev/null
#rm movie.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment