A lot of fixes (see description)

- fix indentation (I used a broken vim config)
- add a -d (dry run) option
- add guarding double quotes at a few places
- use `python3` if no `python` executable exists
- explicitly use bash (instead of posix sh) where it is required
This commit is contained in:
r4 2022-06-11 12:39:16 +02:00
parent 2655890dd4
commit 6ede9a651f
2 changed files with 185 additions and 164 deletions

31
init.sh
View File

@ -1,15 +1,30 @@
#!/usr/bin/env sh #!/usr/bin/env sh
CUSTOM_PYTHON=
if ! which python > /dev/null; then
if which python3 > /dev/null; then
echo ">>> python not found, using python3 instead"
CUSTOM_PYTHON=python3
else
echo ">>> No python executable found, please install python or python3"
exit 1
fi
fi
if [ -e yt-dlc/ ]; then if [ -e yt-dlc/ ]; then
echo ">>> youtube-dlc already downloaded, delete and redownload? [y/N]" printf ">>> youtube-dlc already downloaded, delete and redownload? [y/N]: "
read RES read RES
[ "$RES" = "y" ] || [ "$RES" = "Y" ] && rm -rf yt-dlc/ [ "$RES" = "y" ] || [ "$RES" = "Y" ] && rm -rf yt-dlc/
fi fi
if [ ! -e yt-dlc/ ]; then if [ ! -e yt-dlc/ ]; then
echo ">>> Cloning youtube-dlc repo" && echo ">>> Cloning youtube-dlc repo" &&
git clone --depth 1 --branch "2020.11.11-3" "https://github.com/blackjack4494/yt-dlc.git" git clone --depth 1 --branch "2020.11.11-3" "https://github.com/blackjack4494/yt-dlc.git"
echo ">>> Applying patches" echo ">>> Applying patches"
# Allows for downloading English content from the German website # Allows for downloading English content from the German website
patch -p0 < fix-southpark-de-en.diff patch -p0 < fix-southpark-de-en.diff
fi fi
echo ">>> Building youtube-dlc" echo ">>> Building youtube-dlc"
make -C yt-dlc/ youtube-dlc make -C yt-dlc/ youtube-dlc
[ -n "$CUSTOM_PYTHON" ] &&
sed -i "1c\\#!/usr/bin/env $CUSTOM_PYTHON" yt-dlc/youtube-dlc

View File

@ -1,233 +1,239 @@
#!/usr/bin/env sh #!/usr/bin/env bash
source "$(dirname "$0")/config.sh" source "$(dirname "$0")/config.sh"
# Turn paths into absolute ones, if they aren't already. Will be necessary, since we'll change directories later. # Turn paths into absolute ones, if they aren't already. Will be necessary, since we'll change directories later.
[ ! "${CACHEDIR::1}" = "/" ] && [ ! "${CACHEDIR::1}" = "/" ] &&
CACHEDIR="$(readlink -f "$(dirname "$0")/$CACHEDIR")" CACHEDIR="$(readlink -f "$(dirname "$0")/$CACHEDIR")"
[ ! "${OUTDIR::1}" = "/" ] && [ ! "${OUTDIR::1}" = "/" ] &&
OUTDIR="$(readlink -f "$(dirname "$0")/$OUTDIR")" OUTDIR="$(readlink -f "$(dirname "$0")/$OUTDIR")"
[ ! "${YOUTUBE_DL::1}" = "/" ] && [ ! "${YOUTUBE_DL::1}" = "/" ] &&
YOUTUBE_DL="$(readlink -f "$(dirname "$0")/$YOUTUBE_DL")" YOUTUBE_DL="$(readlink -f "$(dirname "$0")/$YOUTUBE_DL")"
[ ! -e "$OUTDIR" ] && mkdir -p "$OUTDIR" [ ! -e "$OUTDIR" ] && mkdir -p "$OUTDIR"
[ ! -e "$CACHEDIR" ] && mkdir -p "$CACHEDIR" [ ! -e "$CACHEDIR" ] && mkdir -p "$CACHEDIR"
p_info() { p_info() {
echo -e "\e[32m>>> $@\e[m" echo -e "\e[32m>>> $@\e[m"
} }
p_error() { p_error() {
echo -e "\e[1;31m>>> $@\e[m" echo -e "\e[1;31m>>> $@\e[m"
} }
usage() { usage() {
echo "Usage:" echo "Usage:"
echo " $(basename "$0") [OPTIONS] -a - Download all episodes" echo " $(basename "$0") [OPTIONS] -a - Download all episodes"
echo " $(basename "$0") [OPTIONS] -s <season> - Download all episodes of the specified season" echo " $(basename "$0") [OPTIONS] -s <season> - Download all episodes of the specified season"
echo " $(basename "$0") [OPTIONS] -s <season> -e <episode> - Download the specified episode" echo " $(basename "$0") [OPTIONS] -s <season> -e <episode> - Download the specified episode"
echo " $(basename "$0") -h - Show help page" echo " $(basename "$0") -h - Show help page"
echo "Options:" echo "Options:"
echo " -p - Show progress (default)" echo " -p - Show progress (default)"
echo " -P - Hide progress" echo " -P - Hide progress"
echo " -E - Download episodes in English (default)" echo " -E - Download episodes in English (default)"
echo " -D - Download episodes in German" echo " -D - Download episodes in German"
echo " -u - Update episode index (default)" echo " -u - Update episode index (default)"
echo " -U - Skip episode index update" echo " -U - Skip episode index update"
echo " -d - Dry run: don't download, just print out URLs"
} }
unset OPT_SEASON OPT_EPISODE OPT_ALL OPT_EN OPT_LANG OPT_PROGRESS OPT_UPDATE_INDEX unset OPT_SEASON OPT_EPISODE OPT_ALL OPT_EN OPT_LANG OPT_PROGRESS OPT_UPDATE_INDEX OPT_DRY
OPT_LANG="EN" OPT_LANG="EN"
OPT_PROGRESS=true OPT_PROGRESS=true
OPT_UPDATE_INDEX=true OPT_UPDATE_INDEX=true
while getopts "pPEDuUas:e:h" arg; do while getopts "pPEDuUdas:e:h" arg; do
case "$arg" in case "$arg" in
h) h)
usage usage
exit 0 exit 0
;; ;;
s) s)
OPT_SEASON="$OPTARG" OPT_SEASON="$OPTARG"
;; ;;
e) e)
OPT_EPISODE="$OPTARG" OPT_EPISODE="$OPTARG"
;; ;;
a) a)
OPT_ALL=true OPT_ALL=true
;; ;;
E) E)
OPT_LANG="EN" OPT_LANG="EN"
;; ;;
D) D)
OPT_LANG="DE" OPT_LANG="DE"
;; ;;
p) p)
OPT_PROGRESS=true OPT_PROGRESS=true
;; ;;
P) P)
unset OPT_PROGRESS unset OPT_PROGRESS
echo hi echo hi
;; ;;
u) u)
OPT_UPDATE_INDEX=true OPT_UPDATE_INDEX=true
;; ;;
U) U)
unset OPT_UPDATE_INDEX unset OPT_UPDATE_INDEX
;; ;;
?) d)
usage OPT_DRY=true
exit 1 ;;
;; ?)
esac usage
exit 1
;;
esac
done done
if [ "$OPT_LANG" = "DE" ]; then if [ "$OPT_LANG" = "DE" ]; then
INDEX_FILENAME="$CACHEDIR/_episode_index_DE_" INDEX_FILENAME="$CACHEDIR/_episode_index_DE_"
INDEX_INITIAL_URL="https://www.southpark.de/folgen/940f8z/south-park-cartman-und-die-analsonde-staffel-1-ep-1" INDEX_INITIAL_URL="https://www.southpark.de/folgen/940f8z/south-park-cartman-und-die-analsonde-staffel-1-ep-1"
REGEX_EPISODE_URL="\"/folgen/[0-9a-z]\+/south-park-[0-9a-z-]\+-staffel-[0-9]\+-ep-[0-9]\+\"" REGEX_EPISODE_URL="\"/folgen/[0-9a-z]\+/south-park-[0-9a-z-]\+-staffel-[0-9]\+-ep-[0-9]\+\""
elif [ "$OPT_LANG" = "EN" ]; then elif [ "$OPT_LANG" = "EN" ]; then
INDEX_FILENAME="$CACHEDIR/_episode_index_EN_" INDEX_FILENAME="$CACHEDIR/_episode_index_EN_"
INDEX_INITIAL_URL="https://www.southpark.de/en/episodes/940f8z/south-park-cartman-gets-an-anal-probe-season-1-ep-1" INDEX_INITIAL_URL="https://www.southpark.de/en/episodes/940f8z/south-park-cartman-gets-an-anal-probe-season-1-ep-1"
REGEX_EPISODE_URL="\"/en/episodes/[0-9a-z]\+/south-park-[0-9a-z-]\+-season-[0-9]\+-ep-[0-9]\+\"" REGEX_EPISODE_URL="\"/en/episodes/[0-9a-z]\+/south-park-[0-9a-z-]\+-season-[0-9]\+-ep-[0-9]\+\""
fi fi
update_index() { update_index() {
[ ! -e "$INDEX_FILENAME" ] && echo "$INDEX_INITIAL_URL" > "$INDEX_FILENAME" [ ! -e "$INDEX_FILENAME" ] && echo "$INDEX_INITIAL_URL" > "$INDEX_FILENAME"
echo -ne "\e[32m>>> Updating episode index\e[m" echo -ne "\e[32m>>> Updating episode index\e[m"
while true; do while true; do
local URL=$(tail -n1 "$INDEX_FILENAME") local URL="$(tail -n1 "$INDEX_FILENAME")"
local NEWURLS=$(curl -s "$URL" | grep -o "$REGEX_EPISODE_URL" | tr -d "\"" | sed -E "s/^/https:\/\/www.southpark.de/g") local NEWURLS="$(curl -s "$URL" | grep -o "$REGEX_EPISODE_URL" | tr -d "\"" | sed -E "s/^/https:\/\/www.southpark.de/g")"
[ "$URL" = $(printf "$NEWURLS" | tail -n1) ] && break [ "$URL" = "$(printf "$NEWURLS" | tail -n1)" ] && break
echo "$NEWURLS" >> "$INDEX_FILENAME" echo "$NEWURLS" >> "$INDEX_FILENAME"
echo -ne "\e[32m.\e[m" echo -ne "\e[32m.\e[m"
done done
# The awk command removes duplicate lines # The awk command removes duplicate lines
local NEW_INDEX=$(awk '!x[$0]++' "$INDEX_FILENAME") local NEW_INDEX="$(awk '!x[$0]++' "$INDEX_FILENAME")"
printf "$NEW_INDEX" > "$INDEX_FILENAME" printf "$NEW_INDEX" > "$INDEX_FILENAME"
echo echo
} }
# Returns all episode URLs in the specified season # Returns all episode URLs in the specified season
get_season() { get_season() {
local SEASON_NUMBER="$1" local SEASON_NUMBER="$1"
grep "\-${SEASON_NUMBER}-ep-[0-9]\+$" "$INDEX_FILENAME" grep "\-${SEASON_NUMBER}-ep-[0-9]\+$" "$INDEX_FILENAME"
} }
# Returns the URL of the specified episode # Returns the URL of the specified episode
get_episode() { get_episode() {
local SEASON_NUMBER="$1" local SEASON_NUMBER="$1"
local EPISODE_NUMBER="$2" local EPISODE_NUMBER="$2"
grep "\-${SEASON_NUMBER}-ep-${EPISODE_NUMBER}$" "$INDEX_FILENAME" grep "\-${SEASON_NUMBER}-ep-${EPISODE_NUMBER}$" "$INDEX_FILENAME"
} }
get_num_seasons() { get_num_seasons() {
# Effectively searches, how many "episode 1s" there are in the index # Effectively searches, how many "episode 1s" there are in the index
grep "\-[0-9]\+-ep-1$" "$INDEX_FILENAME" | wc -l grep "\-[0-9]\+-ep-1$" "$INDEX_FILENAME" | wc -l
} }
# Returns the number of episodes in the specified season # Returns the number of episodes in the specified season
get_num_episodes() { get_num_episodes() {
local SEASON_NUMBER="$1" local SEASON_NUMBER="$1"
get_season "$SEASON_NUMBER" | wc -l get_season "$SEASON_NUMBER" | wc -l
} }
tmp_cleanup() { tmp_cleanup() {
p_info "Cleaning up temporary files" p_info "Cleaning up temporary files"
rm -rf "$TMPDIR" rm -rf "$TMPDIR"
} }
# Monitors size of downloaded video files; takes temp folder as arg # Monitors size of downloaded video files; takes temp folder as arg
monitor_progress() { monitor_progress() {
local TMP_DIR="$1" local TMP_DIR="$1"
while true; do while true; do
[ ! -e "$TMP_DIR" ] && break [ ! -e "$TMP_DIR" ] && break
printf " Downloaded: %s\r" $(du -bB M "$TMP_DIR" | cut -f1) printf " Downloaded: %s\r" "$(du -bB M "$TMP_DIR" | cut -f1)"
sleep 0.5 sleep 0.5
done done
} }
download_interrupt() { download_interrupt() {
p_info "User interrupt received" p_info "User interrupt received"
tmp_cleanup tmp_cleanup
exit 0 exit 0
} }
merge_interrupt() { merge_interrupt() {
p_info "User interrupt received" p_info "User interrupt received"
tmp_cleanup tmp_cleanup
p_info "Cleaning up corrupted output file" p_info "Cleaning up corrupted output file"
rm -rf "$1" rm -rf "$1"
exit 0 exit 0
} }
# Takes season and episode number as arguments # Takes season and episode number as arguments
download_episode() { download_episode() {
local SEASON_NUMBER="$1" local SEASON_NUMBER="$1"
local EPISODE_NUMBER="$2" local EPISODE_NUMBER="$2"
local OUTFILE="${OUTDIR}/South_Park_S${SEASON_NUMBER}_E${EPISODE_NUMBER}_${OPT_LANG}.mp4" local OUTFILE="${OUTDIR}/South_Park_S${SEASON_NUMBER}_E${EPISODE_NUMBER}_${OPT_LANG}.mp4"
[ -e "$OUTFILE" ] && echo "Already downloaded Season ${SEASON_NUMBER} Episode ${EPISODE_NUMBER}" && return [ -e "$OUTFILE" ] && echo "Already downloaded Season ${SEASON_NUMBER} Episode ${EPISODE_NUMBER}" && return
local URL=$(get_episode "$SEASON_NUMBER" "$EPISODE_NUMBER") local URL="$(get_episode "$SEASON_NUMBER" "$EPISODE_NUMBER")"
[ -z "$URL" ] && echo "Unable to download Season ${SEASON_NUMBER} Episode ${EPISODE_NUMBER}; skipping" && return [ -z "$URL" ] && echo "Unable to download Season ${SEASON_NUMBER} Episode ${EPISODE_NUMBER}; skipping" && return
p_info "Downloading Season $SEASON_NUMBER Episode $EPISODE_NUMBER ($URL)" p_info "Downloading Season $SEASON_NUMBER Episode $EPISODE_NUMBER ($URL)"
trap download_interrupt SIGINT if [ -z "$OPT_DRY" ]; then
TMPDIR=$(mktemp -d "/tmp/southparkdownloader.XXXXXXXXXX") trap download_interrupt SIGINT
[ -n "$OPT_PROGRESS" ] && monitor_progress "$TMPDIR"& TMPDIR="$(mktemp -d "/tmp/southparkdownloader.XXXXXXXXXX")"
pushd "$TMPDIR" > /dev/null [ -n "$OPT_PROGRESS" ] && monitor_progress "$TMPDIR"&
if ! "$YOUTUBE_DL" "$URL" 2>/dev/null | grep --line-buffered "^\[download\]" | grep -v --line-buffered "^\[download\] Destination:"; then pushd "$TMPDIR" > /dev/null
p_info "possible youtube-dl \e[1;31mERROR\e[m" if ! "$YOUTUBE_DL" "$URL" 2>/dev/null | grep --line-buffered "^\[download\]" | grep -v --line-buffered "^\[download\] Destination:"; then
p_info "possible youtube-dl \e[1;31mERROR\e[m"
tmp_cleanup
exit 1
fi
echo "[download] Merging video files"
trap "merge_interrupt \"$OUTFILE\"" SIGINT
# Remove all single quotes and dashes from video files, as they cause problems
for i in ./*.mp4; do mv -n "$i" "$(echo $i | tr -d \'-)"; done
# Find all video files and write them into the list
printf "file '%s'\n" ./*.mp4 > list.txt
# Merge video files
ffmpeg -safe 0 -f concat -i "list.txt" -c copy "$OUTFILE" 2>/dev/null
popd > /dev/null
trap - SIGINT
fi
tmp_cleanup tmp_cleanup
exit 1
fi
echo "[download] Merging video files"
trap "merge_interrupt \"$OUTFILE\"" SIGINT
# Remove all single quotes and dashes from video files, as they cause problems
for i in ./*.mp4; do mv -n "$i" "$(echo $i | tr -d \'-)"; done
# Find all video files and write them into the list
printf "file '%s'\n" ./*.mp4 > list.txt
# Merge video files
ffmpeg -safe 0 -f concat -i "list.txt" -c copy "$OUTFILE" 2>/dev/null
popd > /dev/null
trap - SIGINT
tmp_cleanup
} }
# Takes season number as an argument # Takes season number as an argument
download_season() { download_season() {
local SEASON_NUMBER="$1" local SEASON_NUMBER="$1"
local NUM_EPISODES=$(get_num_episodes "$SEASON_NUMBER") local NUM_EPISODES="$(get_num_episodes "$SEASON_NUMBER")"
for i in $(seq "$NUM_EPISODES"); do for i in $(seq "$NUM_EPISODES"); do
download_episode "$SEASON_NUMBER" "$i" download_episode "$SEASON_NUMBER" "$i"
done done
} }
download_all() { download_all() {
local NUM_SEASONS=$(get_num_seasons) local NUM_SEASONS="$(get_num_seasons)"
for i in $(seq "$NUM_SEASONS"); do for i in $(seq "$NUM_SEASONS"); do
download_season "$i" download_season "$i"
done done
} }
if [ -n "$OPT_SEASON" ]; then if [ -n "$OPT_SEASON" ]; then
[ -n "$OPT_UPDATE_INDEX" ] && update_index [ -n "$OPT_UPDATE_INDEX" ] && update_index
[ -z "$(get_season $OPT_SEASON)" ] && [ -z "$(get_season $OPT_SEASON)" ] &&
p_error "Unable to find Season $OPT_SEASON" && p_error "Unable to find Season $OPT_SEASON" &&
exit 1 exit 1
if [ -n "$OPT_EPISODE" ]; then if [ -n "$OPT_EPISODE" ]; then
[ -z "$(get_episode $OPT_SEASON $OPT_EPISODE)" ] && [ -z "$(get_episode $OPT_SEASON $OPT_EPISODE)" ] &&
p_error "Unable to find Season $OPT_SEASON Episode $OPT_EPISODE" && p_error "Unable to find Season $OPT_SEASON Episode $OPT_EPISODE" &&
exit 1 exit 1
p_info "Going to download Season $OPT_SEASON Episode $OPT_EPISODE" p_info "Going to download Season $OPT_SEASON Episode $OPT_EPISODE"
download_episode "$OPT_SEASON" "$OPT_EPISODE" download_episode "$OPT_SEASON" "$OPT_EPISODE"
else else
p_info "Going to download Season $OPT_SEASON" p_info "Going to download Season $OPT_SEASON"
download_season "$OPT_SEASON" download_season "$OPT_SEASON"
fi fi
elif [ -n "$OPT_ALL" ]; then elif [ -n "$OPT_ALL" ]; then
[ -n "$OPT_UPDATE_INDEX" ] && update_index [ -n "$OPT_UPDATE_INDEX" ] && update_index
p_info "Going to download ALL episodes" p_info "Going to download ALL episodes"
download_all download_all
else else
usage usage
exit 1 exit 1
fi fi