Download.sh hinzugefügt
This commit is contained in:
+201
@@ -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"
|
||||
Reference in New Issue
Block a user