-
-
Save marco79cgn/b09e26beaaf466cb04f9d74122866048 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
curlBin=$(which curl) | |
# use snap curl version if your OS is outdated | |
#curlBin=/snap/bin/curl | |
FILE=ard-plus-token | |
# parse input parameter | |
ardPlusUrl=$1 | |
username=$2 | |
password=$3 | |
skip=$4 | |
movieId='' | |
token='' | |
showPath=$(echo $ardPlusUrl | rev | cut -d "/" -f1 | rev) | |
showId=$(echo $showPath | cut -d "-" -f1) | |
if [[ -z "$username" || -z "$password" ]] | |
then | |
echo "Credentials missing! Please start the script with 3 parameters: " | |
echo "./ard-plus-dl <ard-plus-url> <username> <password>" | |
exit 1 | |
fi | |
if [[ -z "$skip" ]] | |
then | |
skip=1 | |
fi | |
# login only if necessary | |
login() { | |
token=$("$curlBin" -is 'https://auth.ardplus.de/auth/login?plainRedirect=true&redirectURL=https%3A%2F%2Fwww.ardplus.de%2Flogin%2Fcallback&errorRedirectURL=https%3A%2F%2Fwww.ardplus.de%2Fanmeldung%3Ferror%3Dtrue' \ | |
-H 'authority: auth.ardplus.de' \ | |
-H 'content-type: application/x-www-form-urlencoded' \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' \ | |
--data-raw "username=${username}&password=${password}" | grep -i authorization | awk '{print $3}') | |
tokenType=$(echo $token | cut -f1 -d "." | base64 -d | jq -r '.typ') | |
if [[ "$tokenType" == "JWT" ]]; then | |
echo $token > $FILE | |
else | |
echo "Login not possible! Please check credentials and subscription for user $username." | |
exit 1 | |
fi | |
} | |
# cleanup after each episode and at the end | |
cleanup() { | |
deleteToken=$("$curlBin" -s 'https://token.ardplus.de/token/session/playback/delete' \ | |
-H 'authority: token.ardplus.de' \ | |
-H 'content-type: application/json' \ | |
-H "cookie: sid=$token" \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' \ | |
--data-raw "{\"contentId\":\"$movieId\",\"contentType\":\"CmsMovie\"}" \ | |
--compressed) | |
} | |
# get authorization for content | |
auth() { | |
auth=$("$curlBin" -s 'https://token.ardplus.de/token/session' \ | |
-H 'authority: token.ardplus.de' \ | |
-H 'content-type: application/json' \ | |
-H "cookie: sid=$token" \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' \ | |
--data-raw "{\"contentId\":\"$movieId\",\"contentType\":\"CmsEpisode\",\"download\":false,\"appInfo\":{\"platform\":\"web\",\"appVersion\":\"1.0.0\",\"build\":\"web\",\"bundleIdentifier\":\"web\"},\"deviceInfo\":{\"isTouchDevice\":false,\"isTablet\":false,\"isFireOS\":false,\"appPlatform\":\"web\",\"isIOS\":false,\"isCastReceiver\":false,\"isSafari\":false,\"isFirefox\":false}}" \ | |
--compressed) | |
urlParam=$(echo ${auth} | jq -r '.authorizationParams') | |
echo "$urlParam" | |
} | |
# intercept CTRL+C click to clean up before exit | |
term() { | |
echo "CTRL+C pressed. Cleanup and exit!" | |
cleanup | |
exit 0 | |
} | |
trap term SIGINT | |
# perform login | |
if [ -f "$FILE" ]; then | |
# Using cached token | |
token=$(<$FILE) | |
else | |
# Log in once | |
login $username $password | |
fi | |
# check if token is valid | |
movieId="a0S010000007GcX" | |
urlParam=$( auth ) | |
if [[ "$urlParam" == null ]]; then | |
login $username $password | |
token=$(<$FILE) | |
if [[ -z "$token" ]]; then | |
echo "Login not possible! Please check credentials and subscription for user $username." | |
exit 0 | |
fi | |
fi | |
cleanup | |
# get requested content | |
contentUrl="https://data.ardplus.de/ard/graphql?extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%2240d7cbfb79e6675c80aae2d44da2a7f74e4a4ee913b5c31b37cf9522fa64d63b%22%7D%7D&variables=%7B%22movieId%22%3A%22$showId%22%2C%22externalId%22%3A%22%22%2C%22slug%22%3A%22%22%2C%22potentialMovieId%22%3A%22%22%7D" | |
seasonsStatus=$("$curlBin" -s -o content-result.txt -w "%{http_code}" "${contentUrl}" \ | |
-H 'authority: data.ardplus.de' \ | |
-H 'content-type: application/json' \ | |
-H "cookie: sid=$token" \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36') | |
if [[ $seasonsStatus != "200" ]]; then | |
#retry once | |
echo "Couldn't get season details. Trying again!" | |
sleep 2 | |
seasonsStatus=$("$curlBin" -s -o content-result.txt -w "%{http_code}" "${contentUrl}" \ | |
-H 'authority: data.ardplus.de' \ | |
-H 'content-type: application/json' \ | |
-H "cookie: sid=$token" \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36') | |
contentResult=$(cat content-result.txt) | |
else | |
contentResult=$(cat content-result.txt) | |
fi | |
# check whether content is movie or series | |
movie=$(echo "$contentResult" | jq '.data.movie') | |
tvshow=$(echo "$contentResult" | jq '.data.series') | |
if [[ "$movie" != null ]]; then | |
movieId=$(echo "$movie" | jq -r '.id') | |
name=$(echo "$movie" | jq -r '.title') | |
videoUrl=$(echo "$movie" | jq -r '.videoSource.dashUrl') | |
year=$(echo "$movie" | jq -r '.productionYear') | |
filename="${name} (${year})" | |
urlParam=$( auth ) | |
downloadUrl=${videoUrl}?${urlParam} | |
echo "Lade Film ${filename}..." | |
yt-dlp --quiet --progress --no-warnings --audio-multistreams -f "bv+mergeall[vcodec=none]" --sub-langs "en.*,de.*" --embed-subs --merge-output-format mp4 ${downloadUrl} -o "$filename" | |
cleanup | |
elif [[ "$tvshow" != null ]]; then | |
requestedShow=$(echo "$contentResult" | jq '.data.series.title') | |
seasonIds=$(echo "$contentResult" | jq '[.data.series.seasons.nodes[] | { season: .seasonInSeries, seasonId: .id, title: .title }]') | |
seasonOutput=$(echo "$seasonIds" | jq '[.[] | { Option: .season, Titel: .title }]' | jq -r '(.[0]|keys_unsorted|(.,map(length*"-"))),.[]|map(.)|@tsv'|column -ts $'\t') | |
echo -e "\nGewünschte Serie: $requestedShow\n" | |
echo -e "$seasonOutput\n" | |
echo -n "Welche Staffel möchtest du runterladen? " | |
read -r selectedSeason | |
selectedSeasonId=$(echo "$seasonIds" | jq -r --argjson index 1 ".[$((selectedSeason - 1))].seasonId") | |
seasonData=$("$curlBin" -s "https://data.ardplus.de/ard/graphql?extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22134d75e1e68a9599d1cdccf790839d9d71d2e7d7dca57d96f95285fcfd02b2ae%22%7D%7D&variables=%7B%22seasonId%22%3A%22$selectedSeasonId%22%7D&operationName=EpisodesInSeasonData" \ | |
-H 'authority: data.ardplus.de' \ | |
-H 'content-type: application/json' \ | |
-H "cookie: sid=$token" \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36') | |
episodes=$(echo $seasonData | jq '[.data.episodes.nodes[] | { id: .id, episodeNo: .episodeInSeason, title: .title, videoUrl: .videoSource.dashUrl }]') | |
amount=$(echo $episodes | jq '. | length') | |
echo -e "\nStaffel $selectedSeason hat $amount Folgen." | |
selectedSeasonFormatted=$(printf '%02d\n' "$selectedSeason") | |
if [[ $skip != "1" ]]; then | |
echo "Überspringe $skip Episode(n)." | |
skip=$((skip + 1)) | |
fi | |
# loop over all episodes and download each | |
while read episode | |
do | |
movieId=$(echo "$episode" | jq -r '.id') | |
name=$(echo "$episode" | jq -r '.title') | |
videoUrl=$(echo "$episode" | jq -r '.videoUrl') | |
episode=$(echo "$episode" | jq -r '.episodeNo') | |
filename="S${selectedSeasonFormatted}E$(printf '%02d\n' $episode) - ${name}" | |
urlParam=$( auth ) | |
downloadUrl=${videoUrl}?${urlParam} | |
echo "Lade ${filename}..." | |
yt-dlp --quiet --progress --no-warnings --audio-multistreams -f "bv+mergeall[vcodec=none]" --sub-langs "en.*,de.*" --embed-subs --merge-output-format mp4 ${downloadUrl} -o "$filename" | |
cleanup | |
done < <(echo "$episodes" | sed 's/\\"//g' | jq -c '.[]' | tail -n +$skip) | |
elif [[ "$ardPlusUrl" == *"tatort"* ]]; then | |
tatortCity=$(echo $showPath | cut -d "-" -f2) | |
# get all episodes per city | |
tatortCityEpisodes=$("$curlBin" -s "https://data.ardplus.de/ard/graphql?operationName=CategoryDataBySlug&variables=%7B%22slug%22%3A%22tatort-$tatortCity%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%221bf6e600fa91aa72bba35ee95a53677cf21b994a4c2afbcd01127259c7e88612%22%7D%7D" \ | |
--header 'authority: data.ardplus.de' \ | |
--header 'content-type: application/json' \ | |
--header "cookie: sid=$token" \ | |
--header 'origin: https://www.ardplus.de' \ | |
--header 'referer: https://www.ardplus.de/' \ | |
--header 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36') | |
episodeIds=$(echo "$tatortCityEpisodes" | jq '[.data.category.content.nodes[] | { id: .contentMovie.id }]') | |
amount=$(echo $episodeIds | jq '. | length') | |
cityCapitalized=$(echo ${tatortCity} | awk '{$1=toupper(substr($1,0,1))substr($1,2)}1') | |
echo "Der Tatort ${cityCapitalized} hat $amount Episoden." | |
echo -n "Wie viele Episoden möchtest du überspringen? (0=alle laden) " | |
read -r skip | |
echo "Überspringe $skip Episode(n)." | |
skip=$((skip + 1)) | |
# loop over all episodes and download each | |
while read episode | |
do | |
movieId=$(echo "$episode" | jq -r '.id') | |
episodeUrl="https://data.ardplus.de/ard/graphql?operationName=MovieDetails&variables=%7B%22movieId%22%3A%22$movieId%22%2C%22externalId%22%3A%22%22%2C%22slug%22%3A%22%22%2C%22potentialMovieId%22%3A%22%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%226a791c24fd9716b154a3d68f9b5213eb0bf25828a5e633cdbbd2e35aa5b9a984%22%7D%7D" | |
episodeDetailsStatus=$("$curlBin" -s -o current-tatort-episode.txt -w "%{http_code}" "${episodeUrl}" \ | |
-H 'authority: data.ardplus.de' \ | |
-H 'content-type: application/json' \ | |
-H "cookie: sid=$token" \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36') | |
if [[ $episodeDetailsStatus != "200" ]]; then | |
#retry once | |
echo "Couldn't get episode details. Trying again!" | |
sleep 2 | |
episodeDetailsStatus=$("$curlBin" -s -o current-tatort-episode.txt -w "%{http_code}" $episodeUrl \ | |
-H 'authority: data.ardplus.de' \ | |
-H 'content-type: application/json' \ | |
-H "cookie: sid=$token" \ | |
-H 'origin: https://www.ardplus.de' \ | |
-H 'referer: https://www.ardplus.de/' \ | |
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' \ | |
--compressed) | |
episodeDetails=$(cat current-tatort-episode.txt) | |
else | |
episodeDetails=$(cat current-tatort-episode.txt) | |
fi | |
name=$(echo "$episodeDetails" | jq -r '.data.movie.title') | |
videoUrl=$(echo "$episodeDetails" | jq -r '.data.movie.videoSource.dashUrl') | |
year=$(echo "$episodeDetails" | jq -r '.data.movie.productionYear') | |
customData=$(echo "$episodeDetails" | jq -r '.data.movie.customData') | |
episode=$(echo "$customData" | jq -r '.episodeProductionNumber') | |
team=$(echo "$customData" | jq -r '.team') | |
city=$(echo "$customData" | jq -r '.location') | |
filename="Tatort ${city}" | |
if [[ -n "$team" ]]; | |
then | |
filename="$filename (${team})" | |
fi | |
if [[ "$episode" != null ]]; | |
then | |
filename="$filename - Folge ${episode}" | |
fi | |
filename="$filename - ${name} (${year})" | |
urlParam=$( auth ) | |
downloadUrl=${videoUrl}?${urlParam} | |
echo "Lade ${filename}..." | |
yt-dlp --quiet --progress --no-warnings --audio-multistreams -f "bv+mergeall[vcodec=none]" --sub-langs "en.*,de.*" --embed-subs --merge-output-format mp4 ${downloadUrl} -o "$filename" | |
cleanup | |
sleep 1 | |
done < <(echo "$episodeIds" | jq -c '.[]' | tail -n +$skip ) | |
else | |
echo "invalid content" | |
fi | |
cleanup |
Hallo!
Hast du eine Idee, ob sich in der ARDplus Mediathek etwas geändert hat?
Ich bekomme oft 'invalid content' für Filme und Serien werden auch nicht runtergeladen...
folgendes Beispiel - dl.sh:
~/git/ard-plus-dl/ard-plus-dl.sh https://www.ardplus.de/details/a0S01000000zF11-halleluja-amigo <user> <password>
~/git/ard-plus-dl/ard-plus-dl.sh https://www.ardplus.de/details/a0T01000000s7sX-don-matteo <user> <password>
~/git/ard-plus-dl/ard-plus-dl.sh https://www.ardplus.de/details/a0S010000015IyU-alfred-die-knallerbse <user> <password>
~/git/ard-plus-dl/ard-plus-dl.sh https://www.ardplus.de/details/a0S010000015IyY-der-zerstreute <user> <password>
~/git/ard-plus-dl/ard-plus-dl.sh https://www.ardplus.de/details/a0S010000015Iyf-der-sanfte-mit-den-schnellen-beinen <user> <password>
~/git/ard-plus-dl/ard-plus-dl.sh https://www.ardplus.de/details/a0S010000015RYQ-ein-tolpatsch-auf-abwegen <user> <password>
ergibt folgenden Output:
> ./dl.sh
invalid content
Gewünschte Serie: "Don Matteo"
Option Titel
------ -----
1 Don Matteo - Staffel 1
2 Don Matteo - Staffel 2
Welche Staffel möchtest du runterladen? 2
jq: error (at <stdin>:1): Cannot iterate over null (null)
Staffel 2 hat Folgen.
Lade Film Alfred, die Knallerbse (1972)...
invalid content
invalid content
Lade Film Ein Tolpatsch auf Abwegen (1978)...
Lade Film Eine Wolke zwischen den Zähnen (1974)...
Vielen Dank für das ansonsten tolle Script!
Update 24.10.2024: fixed API calls
@gh01mojmnet
Sorry, hatte dein Feedback komplett übersehen. Das Skript sollte jetzt wieder einwandfrei funktionieren, auch mit deinen Beispielen.
Update 08.01.2025: multi-language support (automatic)
Das Skript lädt automatisch mehrere Sprachen und Untertitel (falls verfügbar).
Was mache ich falsch, wenn mir das Skript z. B. die Serie „Sherlock“ (https://www.ardplus.de/details/a0T01000002PRMu-sherlock) nur in englischer Sprachausgabe und ohne Untertitel herunterlädt? Aufgerufen wie oben beschrieben, mit den Zugangsdaten, und anschließend die Staffel bzw. Option ausgewählt. VLC media player gibt mir das Heruntergeladene englisch wieder, und hat augenscheinlich keine zweite Tonspur und auch keine Untertitel zur Auswahl. Auf ardplus.de lassen sich aber Audio-Ausgabe und Untertitel einstellen.
Bei folgendem Inhalt ist mir außerdem eine Ungereimtheit (Option bzw. Staffel 5, doch nur mit Eingabe 1 läuft das Skript ordnungsgemäß – ist hier die Zeilennummer relevant?) sowie eine Fehlermeldung aufgefallen:
~/Downloads/ard-plus-dl.sh https://www.ardplus.de/details/a0T010000001io5-es-war-einmal <user> <password>
Gewünschte Serie: "Es war einmal..."
Option Titel
------ -----
5 Es war einmal... Forscher und Erfinder
Welche Staffel möchtest du runterladen? 1
Staffel 1 hat 26 Folgen.
Lade S01E01 - Die Chinesen – die ersten Erfinder...
Lade S01E02 - Archimedes – Griechenland im Auftrieb...
Lade S01E03 - Ptolemäus und Heron – Zwei Genies aus Alexandria...
Lade S01E04 - Die Zeitmessung – Der weite Weg zur Quarzuhr...
Lade S01E05 - Heinrich der Seefahrer – Auf der Suche nach dem Ende der Welt...
Lade S01E06 - Gutenberg – Die Erfindung der Druckkunst...
Lade S01E07 - Leonardo da Vinci – Das Multitalent aus Italien...
Lade S01E08 - Die Medizin – die Väter der Heilkunst...
jq: parse error: Invalid numeric literal at line 1, column 64
jq: parse error: Invalid numeric literal at line 1, column 64
jq: parse error: Invalid numeric literal at line 1, column 64
jq: parse error: Invalid numeric literal at line 1, column 64
Lade S01E00 - ...
ERROR: [generic] '?' is not a valid URL. Set --default-search "ytsearch" (or run yt-dlp "ytsearch:?" ) to search YouTube
Lade S01E10 - Newton – Das Gesetz der Schwerkraft...
Lade S01E11 - Buffon – Die Geschichte der Natur...
Lade S01E12 - Lavoisier – Die Wunderwelt der Chemie...
Lade S01E13 - Stephenson – Mit Volldampf voraus...
Lade S01E14 - Faraday – Die Entdeckung des Elektromagnetismus...
Lade S01E15 - Darwin – Die Entstehung der Arten...
Lade S01E16 - Mendel – Die Regeln der Vererbung...
Lade S01E17 - Pasteur – Die Welt der Mikroorganismen...
Lade S01E18 - Edison – Ein Licht geht auf...
Lade S01E19 - Marconi – Ein neues Medium wird geboren...
Lade S01E20 - Ford – Automobile vom laufenden Band...
Lade S01E21 - Lindbergh – Der Traum vom Fliegen...
Lade S01E22 - Madame Curie – Das Geheimnis der Radioaktivität...
Lade S01E23 - Einstein – Alles ist relativ...
Lade S01E24 - Lorenz – Der Vater der Gänse...
Lade S01E25 - Armstrong – Der erste Mensch auf dem Mond...
Lade S01E26 - Die Zukunft – Der Aufbruch ins 21. Jahrhundert...
Ich habe den Aufruf zweimal wiederholt, mit gleichem Ergebnis. Die „S01E09“ lässt sich nicht herunterladen, obwohl auf ardplus.de enthalten & anschaubar.
Dein Skript funktioniert ansonsten sehr gut! Vielen Dank dafür!
Was mache ich falsch, wenn mir das Skript z. B. die Serie „Sherlock“ (https://www.ardplus.de/details/a0T01000002PRMu-sherlock) nur in englischer Sprachausgabe und ohne Untertitel herunterlädt? Aufgerufen wie oben beschrieben, mit den Zugangsdaten, und anschließend die Staffel bzw. Option ausgewählt. VLC media player gibt mir das Heruntergeladene englisch wieder, und hat augenscheinlich keine zweite Tonspur und auch keine Untertitel zur Auswahl. Auf ardplus.de lassen sich aber Audio-Ausgabe und Untertitel einstellen.
Du hast höchstwahrscheinlich eine alte Version des Skripts. Ich habe eben gesehen, dass in der Installationsanleitung noch eine alte Version verlinkt war. Ist jetzt angepasst. Bitte neu downloaden und darauf achten, dass der Parameter --audio-multistreams
im Skript vorkommt - dann ist es die neueste Version.
//EDIT: Gerade mit einer Folge getestet. Es werden beide Audiospuren bei Sherlock geladen und auch beide Untertitel (deutsch/englisch).
Bei folgendem Inhalt ist mir außerdem eine Ungereimtheit (Option bzw. Staffel 5, doch nur mit Eingabe 1 läuft das Skript ordnungsgemäß – ist hier die Zeilennummer relevant?) ...
Korrekt, hier werden die verschiedenen Staffeln normalerweise durchnummeriert und ich nehme die Zeile. als Index für die Staffel. Das funktionierte bisher immer, weil es normalerweise nur gesamte Serien mit allen Staffeln gab und Staffel 1 immer Index 1 war usw. Hier ist es ein Sonderfall, es gibt nur eine Staffel und die hat aber Nummer 5. Schönheitsfehler, schau ich mir an.
jq: parse error: Invalid numeric literal at line 1, column 64
jq: parse error: Invalid numeric literal at line 1, column 64
jq: parse error: Invalid numeric literal at line 1, column 64
jq: parse error: Invalid numeric literal at line 1, column 64
Lade S01E00 - ...
ERROR: [generic] '?' is not a valid URL. Set --default-search "ytsearch" (or run yt-dlp "ytsearch:?" ) to search YouTube
Ich habe den Aufruf zweimal wiederholt, mit gleichem Ergebnis. Die „S01E09“ lässt sich nicht herunterladen, obwohl auf ardplus.de enthalten & anschaubar.
Da war tatsächlich ein Fehler beim parsen des JSON. Verantwortlich dafür waren zusätzliche Quotes im Dateinamen "Galilei – \"und sie dreht sich doch\""
, deren Escape Zeichen \
beim parsen verloren gingen. Das ist jetzt gefixt in der neuesten Version des Skripts. Bitte nochmal aktualisieren.
Dein Skript funktioniert ansonsten sehr gut! Vielen Dank dafür!
Danke schön.
Update 24.05.2025: Bugfix
Spezielle Zeichen im Dateinamen werden jetzt gefiltert (z.B. "Galilei – \"und sie dreht sich doch\""
). Bitte auf die neueste Version aktualisieren.
Habe die aktuelle Version des Skripts geladen. Bekomme allerdings bei jedem Versuch nur folgende Ausgabe:
Login not possible! Please check credentials and subscription for user [email protected].
Die ard-plus-token Datei wird angelegt, ist jedoch leer (bzw. nur 1 Zeilenumbruch drin)
In der Geräteliste bei ardplus.de taucht allerdings bei jedem Versuch ein neues angemeldetes Gerät (Chrome) auf.
Der Login scheint also zu klappen, nur landet das auth-token irgendwie nicht in der Datei. Eine Idee, wo es haken könnte?
edit:
habe den login-curl manuell mit verbose ausgeführt und den auth-token (authorization: Bearer) dann manuell in die Datei ard-plus-token gepackt.
Damit gehts jetzt. Wieso das automatisch nicht geklappt hat, versteh ich jetzt aber nicht so wirklich
@kobeegh
Das könnte eventuell an Sonderzeichen im Password gelegen haben. Ich habe das Skript eben nochmal angepasst. Könntest du bitte bei dir einmal die Datei ard-plus-token
löschen und es dann nochmal mit der neuen Version des Skripts versuchen? Thx.
Nein hat leider nichts geändert. Hatte auch schon alle Sonderzeichen aus dem Kennwort rausgeworfen zum Testen. Das einzige Sonderzeichen ist das @ in der E-Mail-Adresse als Loginname
edit:
Der Vollständigkeit halber:
Ubuntu 22.04.05
curl 7.81.0
jq 1.6
yt-dlp 2025.05.22
@marco79cgn
Vielen Dank für die Anpassungen – hat alles geklappt, konnte „Sherlock“ als auch die andere Serie entsprechend laden! :)
Das einzige Problem ist, dass es mir die .vtt-Dateien nebendran legt – oder ist das so gedacht/richtig? Gleichzeitig kommt eine Postprocessing-Fehlermeldung und „encoder not found“ o. ä., obwohl z. B. ffmpeg (7.1.1) installiert ist.
@kobeegh
Ich habe das Problem bislang nicht, hatte nur einmal eine Fehlermeldung, ab dem 2. oder 3. Versuch lief alles wie erwartet. Sonderzeichen habe ich nicht im Passwort. Fedora 42 (curl 8.11.1, jq-1.7.1, yt-dlp 2025.03.31).
Nein hat leider nichts geändert. Hatte auch schon alle Sonderzeichen aus dem Kennwort rausgeworfen zum Testen. Das einzige Sonderzeichen ist das @ in der E-Mail-Adresse als Loginname
edit: Der Vollständigkeit halber: Ubuntu 22.04.05 curl 7.81.0 jq 1.6 yt-dlp 2025.05.22
Es liegt an deiner curl Version, wie ich eben rausgefunden habe. Der Parameter -w '%header{authorization}'
in curl dient dazu, den Wert des Authorization-Headers aus der HTTP-Antwort auszugeben. Das wurde leider erst mit curl Version 7.84.0
eingeführt. Schade, denn das ist tatsächlich sehr praktisch, um an das Token zu kommen (ohne -v
und fehleranfälliges sed/awk/grep). Gibt es keine Möglichkeit, die Version zu aktualisieren? Am Mac habe ich bereits curl 8.7.1
.
Tatsächlich, mit aktuellerer Version von curl läufts direkt.
Aktuell kann ich noch nicht vom 22.04 hochmigrieren, und dort ist Schluss bei curl 7.81.0. Habe mir via snap parallel noch curl 8.13.0 installiert.
Dein Skript habe ich entsprechend angepasst, oben ergänzt:
#curlVersion=/usr/bin/curl # system default
curlVersion=/snap/bin/curl # snap version 8.13.0
Und dann unten die ganzen einzelnen Aufrufe von curl angepasst:
z.B.
loginResult=$(curl -s -o /dev/null ...
→ loginResult=$("$curlVersion" -s -o /dev/null ...
Geht bestimmt auch noch eleganter, aber so läufts erstmal.
Danke für deine Unterstüztung!
@kobeegh
Ich habe gerade das parsen des Tokens angepasst im Skript. Es funktioniert jetzt auch mit alten Versionen von curl
. Zudem prüft das Skript jetzt, ob überhaupt ein Token zurück kommt beim Login und ob er vom Typ JWT
ist. Falls nicht, beendet sich das Skript und es wird keine leere Datei gespeichert geschweigedenn versucht, ohne Token mit deren API zu kommunizieren.
Hallo, bin gerade auf dein Skript gestoßen.
Leider schaffe ich es nicht, Videos herunter zu laden, ich bekomme immer folgende Meldung:
$ ./ard-plus-dl.sh 'https://www.ardplus.de/details/a0S010000037hjZ-kommissar-dupin-bretonischer-ruhm' 'meinuser' 'meinpw'
Couldn't get season details. Trying again!
cat: content-result.txt: Datei oder Verzeichnis nicht gefunden
Lade Film ()...
ERROR: [generic] '?' is not a valid URL. Set --default-search "ytsearch" (or run yt-dlp "ytsearch:?" ) to search YouTube
Ich habe das einmal weiter analysiert und festgestellt, dass der HTTP-Request per curl immer den Code 000
(anstatt 200
) zurückliefert.
Mache ich etwas falsch oder hat ARD schon wieder Änderungen vorgenommen?
P. S. Ich habe folgende Änderungen an deinem Skript vorgenommen:
- Beim wiederholten Versuch, die Filmdaten per curl zu holen, fehlt das Fehler-Handling, weshalb die seltsame Meldung
content-result.txt: Datei oder Verzeichnis nicht gefunden
auftaucht. - Ausgabe des HTTP-Status-Codes in der Meldung.
--- ard-plus-dl.sh 2025-06-02 19:07:34.513796353 +0200
+++ ard-plus-dl.sh-patched 2025-06-02 19:10:02.053509897 +0200
@@ -109,7 +109,7 @@
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36')
if [[ $seasonsStatus != "200" ]]; then
#retry once
- echo "Couldn't get season details. Trying again!"
+ echo "Couldn't get season details (HTTP-Status $seasonsStatus). Trying again!"
sleep 2
seasonsStatus=$(curl -s -o content-result.txt -w "%{http_code}" "${contentUrl}" \
-H 'authority: data.ardplus.de' \
@@ -118,6 +118,10 @@
-H 'origin: https://www.ardplus.de' \
-H 'referer: https://www.ardplus.de/' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36')
+ if [[ $seasonsStatus != "200" ]]; then
+ echo "Couldn't get season details (HTTP-Status $seasonsStatus). Giving up."
+ exit 1
+ fi
contentResult=$(cat content-result.txt)
else
contentResult=$(cat content-result.txt)
Schöne Grüße
Raimund
Ich habe das einmal weiter analysiert und festgestellt, dass der HTTP-Request per curl immer den Code
000
(anstatt200
)zurückliefert.
Das Skript bzw. der curl
Befehl versucht, die Metadaten über das ARD Plus API zu bekommen und speichert diese temporär in einer Datei namens content-result.txt
.
curl -s -o content-result.txt -w "%{http_code}" "${contentUrl}"
Der Grund hierfür ist, dass ich primär an den http status code kommen will, um zu prüfen, dass der 200
(=success) ist. Denn dann kann man davon ausgehen, dass der Aufruf auch erfolgreich war. Der Grund hierfür ist das Fehlerhandling. Wenn da bei dir stattdessen 000
zurück kommt, dann ist genau das das Problem. Welche curl Version benutzt du denn?
@profhccaesar
Habe es eben auf einem alten Raspberry Pi OS bullseye
nachgestellt. Die neueste curl-Version via apt ist dort 7.74.0
und die hat auch diesen Bug, der zu 000
führt. Scheint ein Problem zu sein mit https
im Zusammenhang mit -w "%{http_code}"
in alten Curl Versionen. (Das Problem ist seit langem gefixt.)
Workaround:
neue curl
Version per snap installieren. Beim Raspberry Pi klappt das wie hier beschrieben. Damit konnte ich das Problem direkt lösen!
Ich habe gerade das Skript angepasst und in der ersten Zeile die Möglichkeit ergänzt, einen alternativen Pfad für das curl
binary anzugeben. Man muss es entsprechend nur an dieser einen Stelle ändern.
Vielleicht sollte ich das Ganze als Docker Container anbieten. Eigentlich habe ich gedacht, bei den wenigen Abhängigkeiten kann nicht viel schief gehen. Aber offensichtlich sind viele unterschiedliche curl
Versionen im Umlauf, die oft veraltet sind und/oder Bugs enthalten.
@marco79cgn
Vielen Dank für den Typ - obwohl ich täglich mit curl arbeite, ist mir dieser Bug noch nicht in die Quere gekommen.
Leider gestaltete es sich etwas schwierig, eine aktuelle curl-Version auf meinem System zum Laufen zu bekommen - snapd ist aus diversen Gründen deaktiviert. Ich habe deshalb deine Idee des Docker-Containers aufgegriffen und einen gebaut, liegt hier auf Github im Projekt docker-ard-plus-dl. Bitte nach Belieben verwenden und anpassen - das Ding unterliegt der "Unlicense"-Lizenz.
Die Verwendung ist - eine lauffähige Docker-Installation vorausgesetzt - sehr einfach:
# Projekt herunterladen:
git clone https://github.com/profhccaesar/docker-ard-plus-dl.git ./docker-ard-plus-dl
# Video herunterladen:
cd docker-ard-plus-dl
./ard-plus-dl-docker.sh --dir ~/Downloads 'https://www.ardplus.de/details/a0S010000037hjZ-kommissar-dupin-bretonischer-ruhm' 'myuser' 'mypassword'
Wenn nötig, wird das Image gebaut, yt-dlp
und ard-plus-dl.sh
werden in der jeweils aktuellsten Version heruntergeladen. Anschliessend wird ard-plus-dl.sh
im Container gestartet. Die heruntergeladenen Dateien landen im angegebenen Verzeichnis (--dir
, im Beispiel also ~/Downloads
).
Werden Änderungen am Image vorgenommen (Anpassung Dockerfile / Startskript) oder gibt es eine neue Version von yt-dlp
oder ard-plus-dl.sh
, muss lediglich der Parameter --force
angegeben werden, dann wird das vorhandene Image gelöscht und mit den neuesten Versionen der Skripte neu gebaut:
./ard-plus-dl-docker.sh --force --dir ~/Downloads 'https://www.ardplus.de/details/a0S010000037hjZ-kommissar-dupin-bretonischer-ruhm' 'myuser' 'mypassword'
@profhccaesar
Super, vielen Dank! Ich hatte die gleiche Idee und bin beim über-optimieren an Alpine gescheitert (wollte das Image so klein wie möglich haben, aber ist eigentlich auch nicht so extrem wichtig).
Ich werde es zeitnah ausprobieren. Was ich bei einem anderen Projekt festgestellt habe: die mwader/static-ffmpeg
binaries beschleunigen nicht nur das initiale bauen des Containers, sie machen ihn vor allem wesentlich kleiner, was die Größe anbelangt.
P. S. Fehler im Docker-Image behoben, bitte von Github aktualisieren (z. B. git pull
) und einmal mit Parameter --force
aufrufen.
* (Bugfix) Missing dependency bsdextrautils.
* (Bugfix) Container now interactive - ard-plus-dl.sh sometimes requires user interaction, e. g. for season selection.
@marco79cgn
Um ehrlich zu sein, habe ich mich um die Größe des Images herzlich wenig gekümmert. Es ist 970 MB groß, der Container benötigt nur ein paar Bytes. Jedes heruntergeladene Video ist um ein Mehrfaches größer - eine Optimierung der Größe bringt also in Relation kaum etwas.
$ docker system df -v
Images space usage:
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
ard-plus-dl latest 423ff031ddb0 13 hours ago 971MB 0B 970.9MB 1
...
Containers space usage:
CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED STATUS NAMES
629bbfd5db5e ard-plus-dl "bash" 0 0B About a minute ago Up About a minute ard-plus-dl
...
Die Zeit zum Bauen des Images hat mich auch nicht viel mehr interessiert, das es nur ein einziges Mal stattfindet; außerdem muss man auch hier die Verhältnismäßigkeit sehen: jeder Video-Download benötigt mehrere Minuten; da fällt die einmalige "Investition" von etwa 20 Sekunden für den Bau des Image kaum ins Gewicht.
Zudem wird der größte Zeitanteil beim Bauen des Images sowieso durch das Herunterladen der benötigten Programme per apt-get und das Klonen des yt-dlp-Repositories beansprucht; da bringen eine geschicktere Auswahl des Images und der verwendeten Pakete in der Relation nicht viel.
@profhccaesar
Ja, da hast du natürlich vollkommen Recht. Es ist der innere Monk. ;) Ich habe die Größe jetzt dennoch runter bekommen auf 223 MB. Deine Variante ist robuster, speziell was die ganzen Berechtigungen angeht. Ich habe jetzt dennoch alles mal in ein richtiges Github Repository geworfen. Das macht Änderungen am Skript einfacher und du kannst auch leichter die aktuelle Version verlinken.
https://github.com/marco79cgn/ard-plus-dl/tree/main
Dort wird auch automatisch ein Docker image gebaut via Github Actions zur direkten Verwendung (optional).
Anforderungen
Benutzung
Skript downloaden und ausführbar machen:
chmod 755 ard-plus-dl.sh
Anschließend das Skript aufrufen und drei Parameter mitgeben:
./ard-plus-dl.sh <url> <username> <password>
Die URL ist die Übersichtsseite eines Films oder einer Serie bei ARD Plus, zum Beispiel
Gegen den Wind (Serie):
https://www.ardplus.de/details/a0T0100000064DB-gegen-den-wind
Lola rennt (Film):
https://www.ardplus.de/details/a0S01000000EWYi-lola-rennt
Das Skript erkennt automatisch, ob es sich um einen Film oder eine Serie handelt. Filme werden unmittelbar geladen. Im Falle einer Serie werden alle gefundenen Staffeln aufgelistet und zur Auswahl angeboten.
Filme und Serien werden automatisch mit mehreren Tonspuren geladen (z.B. deutsch & englisch), sofern verfügbar. Auch die Untertitel werden berücksichtigt.
Es können zusätzlich zu Filmen und Serien auch ganze Tatort Ausgaben pro Stadt geladen werden, z.B. alle Folgen aus Bremen mit der URL:
https://www.ardplus.de/kategorie/tatort-bremen
Die Zieldateien werden sinnvoll benannt, z.B.:
S01E01 - Schönes Wochenende.mp4
oder
Lola rennt (1998).mp4