commit badcdc44cb50d66f98eaca37cfd3e6c249177b48 Author: m2x2p Date: Sat Sep 12 21:00:16 2026 +0200 Download.sh hinzugefügt diff --git a/Download.sh b/Download.sh new file mode 100644 index 0000000..86278bf --- /dev/null +++ b/Download.sh @@ -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 # direkt mit Suchbegriff starten +# ./song_downloader.sh -a # 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"