kaimi.cc

203: Non-Authoritative Information

Youtube im Offline-Modus

Ich sehe nicht gerne fern. Das war schon immer so und das wurde mit den Jahren immer stärker. Den Mist kann sich doch echt keiner mehr reinziehen …

Nichtsdestotrotz hänge ich abends nach einem langen Tag des Öfteren mal in meinem Stuhl und kann nicht mehr viele Dinge tun, außer mich sinnlos berieseln zu lassen. Ich zocke in meiner Freizeit auch immer noch gerne das eine oder andere Spiel, also war es naheliegend, das doch beides mal zu verbinden und sich über Youtube passende Kanäle reinzuziehen.

Was ich allerdings auch nicht wirklich lieber mag als Fernsehen sind komische Weboberflächen und, noch ungleich schlimmer, FLASH! Also habe ich nach einem Weg gesucht, mir die Videos – am besten vollautomatisiert – auf die heimische Festplatte liefern zu lassen. Abends kann ich sie mir dann in Ruhe mit dem Player meiner Wahl zu Gemüte führen. Ohne mich durch das Webinterface von Youtube klicken zu müssen, und ohne ein nerviges Flash-Browser-Plugin.

Grundsätzlich ist das Setup ganz einfach. Man braucht ausschließlich flexget und youtube-dl. Beide gibt es sogar für Windoof.

flexget

Flexget kann man eigentlich ganz gut als flexiblen Downloadmanager beschreiben. Organisiert ist das Programm über drei Arten von Plugins:

  • Input: bietet eine Quelle für Einträge, die zum Download angeboten werden; z.B. RSS-Feeds
  • Filter: sortiert und filtert die vom Input gelieferten Einträge
  • Output: tut mit den akzeptierten Einträgen Dinge™; das reicht von einfachem „lad mal runter“ über Aufruf von Skripten bis zum Durchreichen an den laufenden Bittorrent-Client per RPC

Schauen wir uns doch einfach mal die für das Thema heute relevanten Auszüge aus meiner flexget-Konfigurationsdatei an. Setzt man beide hintereinander, kann man das so benutzen. Ich habe bei mir nur noch etwas mehr drin, z.B. Popupbenachrichtigungen bei neuen Downloads und Mailbenachrichtigungen bei Fehlern.

Hier sind mal beispielhaft 3 “tasks” für meine Downloads:

config.yml (config-tasks.yml) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
tasks:
  postillon24:
    rss: https://www.youtube.com/feeds/videos.xml?channel_id=UCIIVvAp6DP3a2MmoIuIjvQA
    template: youtube
  smitepro:
    rss: https://www.youtube.com/feeds/videos.xml?channel_id=UCDBofwsppusJeitUyKpkB5g
    regexp:
      accept:
        - "Top 5 Plays"
      rest: reject
      from: title
    template: youtube-filtered
  scott-manley:
    rss: https://www.youtube.com/feeds/videos.xml?channel_id=UCxzC4EngIsMrPmbm6Nxvb-A
    regexp:
      reject:
        - "^Deep Space Hangout"
        - "Strike Suit Zero"
      rest: accept
      from: title
    template: youtube-filtered

Jeder dieser Tasks bekommt als Input den RSS-Feed eines Youtube-Kanals gefüttert. postillon24 läd einfach alle Einträge über das Template youtube herunter. smitepro akzeptiert über den Filter regexp nur Einträge, die “Top 5 Plays” im Titel enthalten – alle anderen werden abgewiesen und nicht heruntergeladen (rest: reject). scott-manley wiederum verwendet eine Blacklist; was mit “Deep Space Hangout” im Titel beginnt oder “Strike Suit Zero” enthält, wird verworfen – alle anderen heruntergeladen (rest: accept)

Die URL zum Feed eines Kanals findet man übrigens leider nicht ohne weiteres über das Webinterface. Man muss vielmehr die Channel-ID (nicht den Username!) des gewünschten Kanals in https://www.youtube.com/feeds/videos.xml?channel_id=<ID> einsetzen. Ähnlich wie beispielsweise auch Twitter möchte Google halt nach Möglichkeit alle Interaktionen der Nutzer mit Ihrer Plattform über die Weboberfläche oder die eigenen Apps abwickeln. Schade eigentlich, aber dann muss man eben etwas suchen, bis man an die Daten kommt, die man so braucht. Neuere Channels haben die ID direkt in der URL stehen, bei älteren Accounts aus der Zeit vor UIDs, die noch mit ihrem Usernamen arbeiten, muss man sich die ID erst aus dem Quelltext pfriemeln. Dort steht auch die gesamte URL zum Feed noch einmal im Header (unter link rel="alternate" type="application/rss+xml").

Natürlich müssen die verwendeten Templates youtube und youtube-filtered auch definiert sein:

config.yml (config-templates.yml) download
1
2
3
4
5
6
7
8
templates:
  youtube-filtered:
    exec:
      on_output:
        for_accepted: ~/bin/youtube-dl "{{url}}"
  youtube:
    accept_all: yes
    template: youtube-filtered

Beide Templates tun grundsätzlich dasselbe: sie rufen für alle akzeptierten Einträge mein youtube-dl-Skript auf und übergeben ihm die URL des Eintrags. Der einzige Unterschied zwischen den beiden Templates ist daß youtube alle Einträge akzeptiert, während man bei youtube-filtered noch nach diversen Kriterien filtern kann.

Hier kann man auch sehr schön sehen, daß ein Template auf einem anderen Template basieren kann. youtube übernimmt alle Einstellungen von youtube-filtered und setzt zusätzlich noch accept_all: yes.

cron

Jetzt muss flexget nur noch regelmäßig ausgeführt werden. Dazu genügt eine einzelne Zeile in der crontab:

1
0 */2 * * * /usr/bin/flexget --cron execute

Alle zwei Stunden sucht es bei mir also nach neuem Input. Alternativ kann man flexget auch im daemon mode starten und im Hintergrund laufen lassen.

youtube-dl

Um nicht in der flexget-Konfiguration eine ellenlange Zeile stehen zu haben und da ich youtube-dl auch gerne mal von Hand anwerfe, habe ich ein kleines Wrapper-Skript geschrieben, das automatisch die beste verfügbare Qualität auswählt und den Dateinamen des Downloads ordentlich formatiert:

~/bin/youtube-dl
1
2
#!/usr/bin/env bash
/usr/bin/youtube-dl -f best --restrict-filenames -o "/share/videos/inbox/%(uploader)s-%(upload_date)s-%(title)s-%(id)s.%(ext)s" "$@"

Zusammen mit meinem kleinen alias yt=youtube-dl muss ich jetzt nur noch minimal viel tippen, wenn ich mal eben was runterladen will. Mag ich.

1080p

Sagte ich „beste verfügbare Qualität“? Weit gefehlt! Mit -f best läd youtube-dl leider maximal 720p (1280×720 Pixel) runter. Das ist irgendwie doof, wenn der eigene Monitor doch 1080p (1920×1080 Pixel) kann. Die Ursache ist kein Bug in youtube-dl, sondern daß bei Youtube das 1080p-Formal als zwei getrennte Streams ausgeliefert (einmal Video, einmal Audio) und vom Player beim Abspielen wieder zusammengesetzt wird.

Glücklicherweise bin ich nicht der einzige, den das nervt, und jemand hat einen Fix dafür geschrieben. Mittlerweile kann man youtube-dl zwei getrennte Formate für Video und Audio mitgeben. Die werden dann getrennt runtergeladen, mittels ffmpeg gemixt und die temporären Einzeldateien wieder gelöscht. Voilà, 1080p mit Ton.

Damit das so funktioniert, gibt man als Qualität -f "bestvideo+bestaudio" an. Möchte man Videos von anderen Seiten ziehen, stolpert youtube-dl dann aber wieder über diese Option. Also habe ich flugs eine Weiche implementiert, die die übergebene URL auf ein Vorkommen von “youtube.com/” prüft und die Qualität entsprechend setzt.

~/bin/youtube-dl
1
2
3
4
5
6
7
#!/usr/bin/env bash
quality="best"
if [[ "$@" =~ youtube\.com\/ ]] ; then
 quality+="video+bestaudio"
fi

/usr/bin/youtube-dl -f "${quality}" --restrict-filenames -o "/share/videos/inbox/%(uploader)s-%(upload_date)s-%(title)s-%(id)s.%(ext)s" "$@"

Fertig!

Fazit

Ich bin zufrieden. Wenn ich abends nach Hause komme, warten die Veröffentlichungen des Tages schon auf mich. Ich muss mich nur noch zurücklehnen und “play” drücken. Das Zusammenspiel der beiden Tools funktioniert ausgezeichnet. Und beide Programme können noch mehr. Youtube-dl kann nicht nur Videos von Youtube laden, sondern noch von vielen anderen Seiten. Flexget kann nicht nur youtube-dl aufrufen, sondern noch ganz viele andere Dinge. Ich nutze es z.B. auch, um meine diversen abonnierten Podcasts automatisch einzusammeln.

Man muss auch nicht unbedingt einen Rechner zur Verfügung haben, der rund um die Uhr eingeschaltet ist. Statt flexget regelmäßig über einen cron job aufzurufen, kann man natürlich auch einfach beispielsweise einmal am Tag von Hand einen Durchlauf anstoßen.

Youtube hat die RSS-API geändert. Feeds finden sich jetzt nicht mehr unter https://gdata.youtube.com/feeds/api/users/<ID>/uploads, sondern https://www.youtube.com/feeds/videos.xml?channel_id=<ID>. Habe das mal aktualisiert. Außerdem heißt es seit einiger Zeit nicht mehr flexget execute --cron, sondern flexget --cron execute. Auch gefixt.

2 Comments

Tim Knaack – 2014-10-06 06:39

Hallo,
eine schöne Idee die für dich wohl gut funktioniert. Ich möchte ebenfalls Youtube Kanäle offline zur Verfügung haben.
Wie schaffe ich es, das ich verschiedene Youtube Kanäle in unterschiedlichen Verzeichnissen bekomme_
Also 1x täglich meine bestimmten Kanäle scannen und bei neuen Videos diese in den zugehörigen Verzeichnissen speichern

ka’imi2014-10-06 08:45

Der Pfad steht bei mir jetzt fest im Script für youtube-dl mit drin. Du kannst natürlich auch einfach im Template direkt die Kommandozeile für youtube-dl einsetzen. Dabei hast Du Zugriff auf verschiedene Variablen, die Du in doppelten geschweiften Klammern verwenden kannst, unter anderem `task` für den Namen des Tasks, der gerade läuft: http://flexget.com/wiki/Entry
Ich verwende da ja z.B. `{{url}}`. Du könntest sowas machen wie `for_accepted: /usr/bin/youtube-dl -f best -o "~/Downloads/youtube/{{task}}/%(upload_date)s-%(title)s-%(id)s.%(ext)s" "{{url}}"`. Das schiebt dann alles in einen Unterordner von ~/Downloads/youtube, der nach dem Namen des Tasks benannt ist. In meinem Beispiel gäbe es also die Ordner „postillon24“, „smitepro“ und „scott-manley“.

Post a comment

Your email address will not be published. The form doesn’t spit out an email? Go get a real browser and/or mail client.

Name:
Website: