Wieder einmal: Wie ich Video-Streams von News-Sendungen herunterlade

Mittels eines Cron-Scripts lade ich jede Stunde alle YouTube-Links in meinen Remember The Milk-Taskliste auf mein NAS, damit ich diese dann zu Hause mit Air Video auf meinen Apple TV (und somit 55″ Plasma) streamen kann.

Manchmal komme ich aber auch an News-Beiträgen von Fernsehanstalten vorbei, welche nicht auf YouTube angeboten werden und mit youtube-dl nicht heruntergeladen werden können.

Um dieser Videos trotzdem habhaft zu werden ist (leider) noch einiges an Handarbeit nötig. In der Regel streamen Fernsehanstalten solche Inhalte als f4f („F4F is Adobe’s fragmented MP4 or FLV file format for HTTP Dynamic Streaming“).

Als Beispiel nehmen wir ABC’s David Muir Exclusive Backstage Interview With Apple CEO Tim Cook.

Nachdem man die Web-Site im Browser der Wahl geöffnet hat, startet man die Web Developer-Konsole, schaltet auf die Ansicht der HTTP-Requests und startet anschliessend das Video. Im Chaos der aufgerufenen Assets suchen wir das sogenannte f4m-Manifest — eine XML-Datei, die dem Videoplayer sagt, wo er die Fragmente des Video-Streams findet.

Obwowhl ich Firefox längst totgeschrieben habe — ich habe die Erfahrung gemacht, dass dieser Browser die Netzwerkrequests am intuitivsten aufbereitet, um am schnellsten zum Ziel zu kommen. Nicht zuletzt, weil es eine Spalte mit dem Typ des Requests gibt, die es äusserst einfach macht, nach dem Typ f4m Ausschau zu halten.

Hier die Datei für obiges Video vom September 2014:

/tmp/temp_textmate.DgGpNM:2: warning: variable $KCODE is no longer effective; ignored
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns="http://ns.adobe.com/f4m/1.0" xmlns:akamai="uri:akamai.com/f4m/1.0">
<akamai:version>2.0</akamai:version>
<akamai:bw>5000</akamai:bw>
<id>/abcnews/2014/09/140909_wn_muir7_1837_,500,700,900,1200,1600,2000,2500,.mp4.csmil_0</id>
<streamType>recorded</streamType>
<akamai:streamType>vod</akamai:streamType>
<duration>200.968</duration>
<streamBaseTime>0.000</streamBaseTime>
<bootstrapInfo profile="named" id="bootstrap_0">AAAAi2Fic3QAAAAAAAAAAQAAAAPoAAAAAAADEQgAAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAACEBAAAARmFmcnQAAAAAAAAD6AAAAAADAAAAAQAAAAAAAAAAAAAXcAAAACEAAAAAAALuAAAAIwgAAAAAAAAAAAAAAAAAAAAAAA==</bootstrapInfo>
<bootstrapInfo profile="named" id="bootstrap_1">AAAAi2Fic3QAAAAAAAAAAQAAAAPoAAAAAAADEQgAAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAACEBAAAARmFmcnQAAAAAAAAD6AAAAAADAAAAAQAAAAAAAAAAAAAXcAAAACEAAAAAAALuAAAAIwgAAAAAAAAAAAAAAAAAAAAAAA==</bootstrapInfo>
<bootstrapInfo profile="named" id="bootstrap_2">AAAAi2Fic3QAAAAAAAAAAQAAAAPoAAAAAAADEQgAAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAACEBAAAARmFmcnQAAAAAAAAD6AAAAAADAAAAAQAAAAAAAAAAAAAXcAAAACEAAAAAAALuAAAAIwgAAAAAAAAAAAAAAAAAAAAAAA==</bootstrapInfo>
<bootstrapInfo profile="named" id="bootstrap_3">AAAAi2Fic3QAAAAAAAAAAQAAAAPoAAAAAAADEQgAAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAACEBAAAARmFmcnQAAAAAAAAD6AAAAAADAAAAAQAAAAAAAAAAAAAXcAAAACEAAAAAAALuAAAAIwgAAAAAAAAAAAAAAAAAAAAAAA==</bootstrapInfo>
<bootstrapInfo profile="named" id="bootstrap_4">AAAAi2Fic3QAAAAAAAAAAQAAAAPoAAAAAAADEQgAAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAACEBAAAARmFmcnQAAAAAAAAD6AAAAAADAAAAAQAAAAAAAAAAAAAXcAAAACEAAAAAAALuAAAAIwgAAAAAAAAAAAAAAAAAAAAAAA==</bootstrapInfo>
<bootstrapInfo profile="named" id="bootstrap_5">AAAAi2Fic3QAAAAAAAAAAQAAAAPoAAAAAAADEQgAAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAACEBAAAARmFmcnQAAAAAAAAD6AAAAAADAAAAAQAAAAAAAAAAAAAXcAAAACEAAAAAAALuAAAAIwgAAAAAAAAAAAAAAAAAAAAAAA==</bootstrapInfo>
<bootstrapInfo profile="named" id="bootstrap_6">AAAAi2Fic3QAAAAAAAAAAQAAAAPoAAAAAAADEQgAAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAACEBAAAARmFmcnQAAAAAAAAD6AAAAAADAAAAAQAAAAAAAAAAAAAXcAAAACEAAAAAAALuAAAAIwgAAAAAAAAAAAAAAAAAAAAAAA==</bootstrapInfo>
<media bitrate="495" url="0_a3ed2f8d8e5b9694_" bootstrapInfoId="bootstrap_0">
<metadata>AgAKb25NZXRhRGF0YQgAAAAMAAhkdXJhdGlvbgBAaR752yLQ5QAFd2lkdGgAQIAAAAAAAAAABmhlaWdodABAcgAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAeSO4tL3p2AAJZnJhbWVyYXRlAEA99wg+h62+AAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQFdnYcJYreUAD2F1ZGlvc2FtcGxlcmF0ZQBA5YiAAAAAAAAPYXVkaW9zYW1wbGVzaXplAEAwAAAAAAAAAAZzdGVyZW8BAQAMYXVkaW9jb2RlY2lkAEAkAAAAAAAAAAhmaWxlc2l6ZQBBZ8IggAAAAAAACQ==</metadata>
</media>
<media bitrate="694" url="1_a3ed2f8d8e5b9694_" bootstrapInfoId="bootstrap_1">
<metadata>AgAKb25NZXRhRGF0YQgAAAAMAAhkdXJhdGlvbgBAaR752yLQ5QAFd2lkdGgAQIQAAAAAAAAABmhlaWdodABAdoAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAgs/By82odgAJZnJhbWVyYXRlAEA99wg+h62+AAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQFdnYcJYreUAD2F1ZGlvc2FtcGxlcmF0ZQBA5YiAAAAAAAAPYXVkaW9zYW1wbGVzaXplAEAwAAAAAAAAAAZzdGVyZW8BAQAMYXVkaW9jb2RlY2lkAEAkAAAAAAAAAAhmaWxlc2l6ZQBBcKoP4AAAAAAACQ==</metadata>
</media>
<media bitrate="894" url="2_a3ed2f8d8e5b9694_" bootstrapInfoId="bootstrap_2">
<metadata>AgAKb25NZXRhRGF0YQgAAAAMAAhkdXJhdGlvbgBAaR752yLQ5QAFd2lkdGgAQIQAAAAAAAAABmhlaWdodABAdoAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAiQ24xGUM/gAJZnJhbWVyYXRlAEA99wg+h62+AAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQFdnYcJYreUAD2F1ZGlvc2FtcGxlcmF0ZQBA5YiAAAAAAAAPYXVkaW9zYW1wbGVzaXplAEAwAAAAAAAAAAZzdGVyZW8BAQAMYXVkaW9jb2RlY2lkAEAkAAAAAAAAAAhmaWxlc2l6ZQBBdXMc8AAAAAAACQ==</metadata>
</media>
<media bitrate="1194" url="3_a3ed2f8d8e5b9694_" bootstrapInfoId="bootstrap_3">
<metadata>AgAKb25NZXRhRGF0YQgAAAAMAAhkdXJhdGlvbgBAaR752yLQ5QAFd2lkdGgAQIQAAAAAAAAABmhlaWdodABAdoAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAkTU4pmS0SwAJZnJhbWVyYXRlAEA99wg+h62+AAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQFdnYcJYreUAD2F1ZGlvc2FtcGxlcmF0ZQBA5YiAAAAAAAAPYXVkaW9zYW1wbGVzaXplAEAwAAAAAAAAAAZzdGVyZW8BAQAMYXVkaW9jb2RlY2lkAEAkAAAAAAAAAAhmaWxlc2l6ZQBBfKCEIAAAAAAACQ==</metadata>
</media>
<media bitrate="1593" url="4_a3ed2f8d8e5b9694_" bootstrapInfoId="bootstrap_4">
<metadata>AgAKb25NZXRhRGF0YQgAAAAMAAhkdXJhdGlvbgBAaR752yLQ5QAFd2lkdGgAQIQAAAAAAAAABmhlaWdodABAdoAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAl3KVnpj2SgAJZnJhbWVyYXRlAEA99wg+h62+AAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQFdnYcJYreUAD2F1ZGlvc2FtcGxlcmF0ZQBA5YiAAAAAAAAPYXVkaW9zYW1wbGVzaXplAEAwAAAAAAAAAAZzdGVyZW8BAQAMYXVkaW9jb2RlY2lkAEAkAAAAAAAAAAhmaWxlc2l6ZQBBgxjZEAAAAAAACQ==</metadata>
</media>
<media bitrate="1993" url="5_a3ed2f8d8e5b9694_" bootstrapInfoId="bootstrap_5">
<metadata>AgAKb25NZXRhRGF0YQgAAAAMAAhkdXJhdGlvbgBAaR752yLQ5QAFd2lkdGgAQJAAAAAAAAAABmhlaWdodABAggAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAnbA4DHkwAgAJZnJhbWVyYXRlAEA99wg+h62+AAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQFdnYcJYreUAD2F1ZGlvc2FtcGxlcmF0ZQBA5YiAAAAAAAAPYXVkaW9zYW1wbGVzaXplAEAwAAAAAAAAAAZzdGVyZW8BAQAMYXVkaW9jb2RlY2lkAEAkAAAAAAAAAAhmaWxlc2l6ZQBBh+GlUAAAAAAACQ==</metadata>
</media>
<media bitrate="2491" url="6_a3ed2f8d8e5b9694_" bootstrapInfoId="bootstrap_6">
<metadata>AgAKb25NZXRhRGF0YQgAAAAMAAhkdXJhdGlvbgBAaR752yLQ5QAFd2lkdGgAQJQAAAAAAAAABmhlaWdodABAhoAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAor16mqIiOAAJZnJhbWVyYXRlAEA99wg+h62+AAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQFdnYcJYreUAD2F1ZGlvc2FtcGxlcmF0ZQBA5YiAAAAAAAAPYXVkaW9zYW1wbGVzaXplAEAwAAAAAAAAAAZzdGVyZW8BAQAMYXVkaW9jb2RlY2lkAEAkAAAAAAAAAAhmaWxlc2l6ZQBBjdrgaAAAAAAACQ==</metadata>
</media>
</manifest>

Hat man die URL dieses Manifests ergattert (in diesem Fall ist es abcvod.abcnews.com/z/abcnews/2014/09/140909_wn_muir7_1837_,500,700,900,1200,1600,2000,2500,.mp4.csmil/manifest.f4m?g=SFHWHPOOGNQB&hdcore=3.5.0&plugin=aasp-3.5.0.151.81), verwendet man AdobeHDS.php, um die Stream-Fragmente automatisiert auf den lokalen Rechner herunterzuladen und danach zu einer MP4 oder FLV-Datei zusammenzuschustern:

$ php AdobeHDS.php --manifest "http://abcvod.abcnews.com/z/abcnews/2014/09/140909_wn_muir7_1837_,500,700,900,1200,1600,2000,2500,.mp4.csmil/manifest.f4m?g=SFHWHPOOGNQB&hdcore=3.5.0&plugin=aasp-3.5.0.151.81"