Download.sh hinzugefügt

This commit is contained in:
2026-09-12 21:00:16 +02:00
commit badcdc44cb
+201
View File
@@ -0,0 +1,201 @@
#!/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"