202 lines
6.3 KiB
Bash
202 lines
6.3 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# song_downloader.sh — Songs aus segments.json suchen und als Video- oder
|
|
# Audio-Segment mit yt-dlp herunterladen.
|
|
#
|
|
# Abhaengigkeiten: jq, yt-dlp, ffmpeg (fuer --force-keyframes-at-cuts / mp3-Extraktion)
|
|
# Arch: sudo pacman -S jq yt-dlp ffmpeg
|
|
#
|
|
# Nutzung:
|
|
# ./song_downloader.sh # interaktiver Suchmodus
|
|
# ./song_downloader.sh <suchbegriff> # direkt mit Suchbegriff starten
|
|
# ./song_downloader.sh -a <suchbegriff> # Audio (mp3) statt Video
|
|
# ./song_downloader.sh -f meine.json # andere segments.json verwenden
|
|
#
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
|
JSON_FILE="${SCRIPT_DIR}/segments.json"
|
|
OUT_DIR="${SCRIPT_DIR}/downloads"
|
|
MODE="video" # video | audio
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Argumente parsen
|
|
# ---------------------------------------------------------------------------
|
|
QUERY=""
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-a|--audio)
|
|
MODE="audio"
|
|
shift
|
|
;;
|
|
-v|--video)
|
|
MODE="video"
|
|
shift
|
|
;;
|
|
-f|--file)
|
|
JSON_FILE="$2"
|
|
shift 2
|
|
;;
|
|
-o|--out)
|
|
OUT_DIR="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
grep '^#' "$0" | sed 's/^#//'
|
|
exit 0
|
|
;;
|
|
*)
|
|
QUERY="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Vorbedingungen pruefen
|
|
# ---------------------------------------------------------------------------
|
|
for bin in jq yt-dlp; do
|
|
if ! command -v "$bin" &>/dev/null; then
|
|
echo "Fehler: '$bin' ist nicht installiert (siehe Kopf des Scripts)." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
if [[ ! -f "$JSON_FILE" ]]; then
|
|
echo "Fehler: Segment-Datei nicht gefunden: $JSON_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# JSON in eine flache, mit Tabs getrennte Liste umwandeln:
|
|
# songname \t videoId \t start \t end \t herkunfts-videotitel
|
|
# ---------------------------------------------------------------------------
|
|
build_index() {
|
|
jq -r '
|
|
.[] as $stream
|
|
| $stream.songs[]
|
|
| [ .name, $stream.videoId, (.range[0]|tostring), (.range[1]|tostring), $stream.name ]
|
|
| @tsv
|
|
' "$JSON_FILE"
|
|
}
|
|
|
|
sanitize_filename() {
|
|
# Entfernt Zeichen, die auf den meisten Dateisystemen Probleme machen
|
|
echo "$1" | sed -e 's#[/\\:*?"<>|]#_#g' -e 's/ */ /g' -e 's/^ *//;s/ *$//'
|
|
}
|
|
|
|
format_time() {
|
|
local secs="$1"
|
|
printf '%02d:%02d:%02d' $((secs/3600)) $(((secs%3600)/60)) $((secs%60))
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Song(s) anhand des Suchbegriffs finden (case-insensitive, Substring)
|
|
# ---------------------------------------------------------------------------
|
|
search_songs() {
|
|
local term="$1"
|
|
build_index | awk -F'\t' -v term="$term" '
|
|
BEGIN { IGNORECASE = 1 }
|
|
index($1, term) > 0 { print }
|
|
'
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Einen Treffer herunterladen
|
|
# song_name, video_id, start, end, source_title
|
|
# ---------------------------------------------------------------------------
|
|
download_segment() {
|
|
local song_name="$1" video_id="$2" start="$3" end="$4" source_title="$5"
|
|
local clean_name url start_ts end_ts outfile
|
|
|
|
clean_name="$(sanitize_filename "$song_name")"
|
|
url="https://www.youtube.com/watch?v=${video_id}"
|
|
start_ts="$(format_time "$start")"
|
|
end_ts="$(format_time "$end")"
|
|
|
|
echo "-> Lade '${song_name}' (${start_ts}-${end_ts}) aus '${source_title}' [${video_id}] ..."
|
|
|
|
if [[ "$MODE" == "audio" ]]; then
|
|
outfile="${OUT_DIR}/${clean_name}.%(ext)s"
|
|
yt-dlp \
|
|
--download-sections "*${start}-${end}" \
|
|
--force-keyframes-at-cuts \
|
|
-x --audio-format mp3 \
|
|
--embed-thumbnail --embed-metadata \
|
|
-o "$outfile" \
|
|
"$url"
|
|
else
|
|
outfile="${OUT_DIR}/${clean_name}.%(ext)s"
|
|
yt-dlp \
|
|
--download-sections "*${start}-${end}" \
|
|
--force-keyframes-at-cuts \
|
|
-f "bv*+ba/b" --merge-output-format mp4 \
|
|
--embed-thumbnail --embed-metadata \
|
|
-o "$outfile" \
|
|
"$url"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Auswahl aus Treffern anzeigen und Download anstossen
|
|
# ---------------------------------------------------------------------------
|
|
select_and_download() {
|
|
local matches="$1"
|
|
local count
|
|
count="$(echo "$matches" | grep -c . || true)"
|
|
|
|
if [[ "$count" -eq 0 ]]; then
|
|
echo "Keine Songs gefunden."
|
|
return
|
|
fi
|
|
|
|
echo
|
|
echo "Gefundene Songs (Modus: $MODE):"
|
|
local i=1
|
|
while IFS=$'\t' read -r song_name video_id start end source_title; do
|
|
printf ' [%2d] %-45s (%s-%s, aus "%s")\n' \
|
|
"$i" "$song_name" "$(format_time "$start")" "$(format_time "$end")" "$source_title"
|
|
i=$((i+1))
|
|
done <<< "$matches"
|
|
echo " [ a] alle herunterladen"
|
|
echo " [ q] abbrechen"
|
|
echo
|
|
read -rp "Auswahl (Nummer, 'a' oder Komma-getrennte Liste): " choice
|
|
|
|
if [[ "$choice" == "q" ]]; then
|
|
echo "Abgebrochen."
|
|
return
|
|
fi
|
|
|
|
if [[ "$choice" == "a" ]]; then
|
|
while IFS=$'\t' read -r song_name video_id start end source_title; do
|
|
download_segment "$song_name" "$video_id" "$start" "$end" "$source_title"
|
|
done <<< "$matches"
|
|
return
|
|
fi
|
|
|
|
IFS=',' read -ra picks <<< "$choice"
|
|
for pick in "${picks[@]}"; do
|
|
pick="$(echo "$pick" | tr -d ' ')"
|
|
line="$(echo "$matches" | sed -n "${pick}p")"
|
|
if [[ -z "$line" ]]; then
|
|
echo "Ungueltige Auswahl: $pick" >&2
|
|
continue
|
|
fi
|
|
IFS=$'\t' read -r song_name video_id start end source_title <<< "$line"
|
|
download_segment "$song_name" "$video_id" "$start" "$end" "$source_title"
|
|
done
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main
|
|
# ---------------------------------------------------------------------------
|
|
if [[ -z "$QUERY" ]]; then
|
|
read -rp "Suchbegriff (Songname): " QUERY
|
|
fi
|
|
|
|
matches="$(search_songs "$QUERY")"
|
|
select_and_download "$matches"
|