Привет, юнга!
Сегодня я расскажу тебе как правильно брать на абордаж галеоны с контентом. (все id изменены для пущей секретности)
Прелюдия.
Пришел как-то ко мне хороший человек и сказал: “Посмотрел я вебинар интересный, можешь ты его скачать, а то вдруг потом видео закроют”. И кинул в меня ссылочкой на этот вебинар. Достал я подзорную трубу свою, осмотрел неприятельский флот. Вижу, контент мне нужный везет галеон системы kinescope.io. Попробовал я его взять прямым наскоком, посмотрев в developer tools код исходный. Но контента там не обнаружил. Нашел только то, что, когда конопчку play нажимаешь, браузер скачивает какой-то mpd файл, а потом начинает тягать mp4 и показывать их.
Скачал я парочку таких файлов, а они без звука и обрываются на середине.
Акт 1. Облом
Пошел я тогда на Тортугу, в таверну гугловскую, народ расспросить, как такие галеоны грабить. А никто там толком этого и не знает. Максимум слухи всякие ходят, что кто-то когда-то это делал с помощью абордажного крюка youtube-dl. Такой у меня как раз был.
Расчехлил его я, сунул ему ссылку и он радостно захрюкав, начал грабить галеон. Сказал, что сундук с часовым контентом будет весить 36 тонн. “Как-то многовато,” – подумал я, но дождался конца работы этого зверя. Открыл сундук, а там все дукаты поломанные. Выкинул я его на дно морское и решил, что будем грабить по науке. А для этого надо галеон этот изучить всячески.
Акт 2. Наблюдение
В руках у меня были только координаты какой-то неизвестной фигни с окончанием mpd. Надо посмотреть, что это такое.
1 |
curl https://kinescope.io/c90eca44-cc34-48b4-89c0-89eccfca03b6/master.mpd |
Сказал я. И увидел нифига. То есть письмо в формате html с надписью “Access forbidden. Video is not available on this domain”. Владельцы контента не хотели всяким бородатым пиратам отдавать важные документы. Почесал я бороду, выкурил трубку и подумал: “Раз они говорят, что в этом домене они никому не доверяют, то как они узнают своих?”. Посмотрел ещё разок в developer tools и увидел в исходящих заголовках слово referrer. “А что если?”
1 |
curl --referer https://kinescope.io/ https://kinescope.io/c90eca44-cc34-48b4-89c0-89eccfca03b6/master.mpd |
И увидел в ответ громадную депешу на языке xml.
Сохраним ее в файл, чтобы рассмотреть получше
1 |
curl --referer https://kinescope.io/ https://kinescope.io/c90eca44-cc34-48b4-89c0-89eccfca03b6/master.mpd > master.mpd |
Вот это сокровище! Да тут же все написано что мне нужно. Есть несколько тэгов AdaptationSet, которые помечены mimeType. mimeType=’video/mp4′ — это без сомнения видеопотоки, а mimeType=’audio/mp4′ — аудиодорожка. Тут стало совершенно понятно, как украсть ценный контент. Для этого мне будут нужны C#, curl, ffmpeg и немного bash-магии (я старый пират пингвинячьего флота).
Акт 3. Грабеж
Запускаем VS code, Создаем проект на .net core 6 и начинаем потихоньку кодить. Для начала набросаем вспомогательную функцию, которая будет нам из xml список координат интересных нам дукатов и дублонов доставать. Нам же нужен контент только самой высокой, 1080 пробы )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
IEnumerable<string> GetFiles( XDocument doc, Func<XAttribute, bool> adaptationAttrFilter, Func<XAttribute, bool> representationAttrFilter) => doc.Root.Descendants() .Where(x => x.Name.LocalName == "AdaptationSet" && x.Attributes().Any(adaptationAttrFilter)) .Descendants() .Where(x => x.Name.LocalName == "Representation" && x.Attributes().Any(representationAttrFilter)) .Descendants() .Where(x => x.Name.LocalName == "SegmentList") .Descendants() .Where(x => x.Name.LocalName == "SegmentURL") .Select(x => x.Attributes().First(a => a.Name.LocalName == "media").Value) ; |
Эта функция идет по xml, находит тэги “AdaptationSet”, удовлетворяющие условию adaptationAttrFilter, в них находит тэги “Representation” по условию representationAttrFilter и вытаскиваем из их потомков по пути SegmentList/SegmentURL значение атрибута media.
Начнем действовать.
Для начала определимся с количеством параметров и покажем сообщение, если не хватает, а так же загрузим наш xml
1 2 3 4 5 6 7 |
if(args.Count() < 2) { System.Console.WriteLine("Usage: kinescode-dl <mpd playlist file path> <output name>"); return; } var doc = XDocument.Load(args[0]); |
Получил самый жирный видео контент
1 2 3 4 5 6 7 8 |
var videoFiles = GetFiles( doc, a => a.Name.LocalName == "mimeType" && a.Value == "video/mp4", a => a.Name.LocalName == "width" && a.Value == "1920" ) .Distinct() .ToList(); |
В консоль будем писать команды шелла, чтобы потом их собрать в скрипт. Качать будем в каталог tmp, поэтому для начала приберем там
1 2 |
System.Console.WriteLine("mkdir tmp"); System.Console.WriteLine("rm tmp/*"); |
Сформируем команды скачивания всех кусков
1 2 3 4 5 |
for (int i = 0; i < videoFiles.Count; i++) { var f = videoFiles[i]; System.Console.WriteLine($"curl --referer https://kinescope.io/ '{f}' > ./tmp/master_video_{i:0000}.mp4"); } |
Чем хорош формат mp4 — мы можем просто склеить все куски вместе и получить поток целиком.
1 |
System.Console.WriteLine("cat ./tmp/master_video_*.mp4 >> ./tmp/master_video.mp4"); |
Повторим то же самое для звука (Пока не встрчал, чтобы аудио было порезано на куски, но на всякий случай поддержку сделал)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var audiofiles = GetFiles( doc, a => a.Name.LocalName == "mimeType" && a.Value == "audio/mp4", _ => true ) .Distinct() .ToList(); for (int i = 0; i < audiofiles.Count; i++) { var f = audiofiles[i]; System.Console.WriteLine($"curl --referer https://kinescope.io/ '{f}' > ./tmp/master_audio_{i:0000}.mp4"); } System.Console.WriteLine("cat ./tmp/master_audio_*.mp4 >> ./tmp/master_audio.mp4"); |
И финальный аккорд — собрать это все добро в один сундук
1 |
System.Console.WriteLine($"ffmpeg -i ./tmp/master_video.mp4 -i ./tmp/master_audio.mp4 -c copy {args[1]}.mkv"); |
Вот так. Всего в 60 строк мы автоматизировали грабеж галеонов с контентом компании kinescope.
Компилируем с помощью команды dotnet build и запускаем наше творение
1 |
./kinescode-dl master.mpd master_1_1 > master_1_1.sh |
На выходе получаем shell файл примерно следующего содержания
Запустив его, понаблюдаем за скачиванием контента и на выходе увидим сундук с надписью master_1_1.mkv где будет лежать ценный образовательный контент.