Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Я выступил с докладом на WordCamp US 2023 и захотел скачать видео с ним. Я всегда стараюсь хранить собственные копии таких видео, потому что некоторые конференции в прошлом со временем закрывали к ним доступ.
Моя презентация в прямом эфире транслировалась через YouTube, однако получившееся видео длится 9,5 часа!
В обычной ситуации я бы скачал видео полностью и обрезал его локально, но тогда я был подключен к WiFi конференции и не хотел ждать всего файла, поэтому решил проверить, можно ли скачать его часть.
Я нашёл этот рецепт пользователя StackExchange godofgrunts, который походил на то, что мне нужно:
#!/bin/bash
#Arguments: URL, Time stamp -5 seconds, length of clip, video file name
readarray -t urls <<< """ class="formula inline">1")"
ffmpeg -ss "" class="formula inline">{urls[0]}" -ss "" class="formula inline">{urls[1]}" -ss 5 -map 0:v -map 1:a -c:v libx264 -c:a aac -t " class="formula inline">4
Но когда я запустил его, то получил ошибку, потому что на моём Mac не было readarray
.
Поэтому я вставил команды в GPT-4 со следующим промтом:
Port this to macOS - I do not have the readarray command
«Портируй это на macOS - у меня нет команды readarray»
Он выдал мне такое:
#!/bin/bash
# Arguments: URL, Time stamp -5 seconds, length of clip, video file name
IFS=$'\n' urls=($(yt-dlp --youtube-skip-dash-manifest -g "$1"))
ffmpeg -ss $2 -i "${urls[0]}" -ss $2 -i "${urls[1]}" -ss 5 -map 0:v -map 1:a -c:v libx264 -c:a aac -t $3 $4
1. Переменной IFS присвоен символ новой строки, чтобы возвращаемые yt-dlp URL разделялись по новым строкам.
2. Массив urls заполняется подстановкой команды и присвоением массива.
3. Остальная часть скрипта осталась неизменной.
Я сохранил команды как clip-youtube.sh
, выполнил chmod 755 clip-youtube.sh
и... это сработало!
Я вызвал код следующим образом:
./clip-youtube.sh 'https://www.youtube.com/watch?v=LpDulftMkew' 8:18:25 3600 simon-wordcamp.mp4
Моя часть видео начинается примерно с 8 ч 18 мин и 25 с. Я указал 3600, чтобы скачать час контента (раздел Q&A длился немного больше, нужно было указать большее значение).
Для выполнения скрипта потребовался почти целый час! В конечном итоге, возможно, было бы быстрее скачать файл целиком, а потом нарезать его.
Но... в результате я получил файл simon-wordcamp.mp4
на 308 МБ.
Как всё это работает
Немного объясню код. Первая часть выглядит так:
yt-dlp --youtube-skip-dash-manifest -g "$1"
Выполнив её, мы получим такой результат:
yt-dlp --youtube-skip-dash-manifest -g 'https://www.youtube.com/watch?v=LpDulftMkew'
Вывод:
https://rr2---sn-8xgp1vo-p5qs.googlevideo.com/videoplayback?expire=1693085635&ei=YxvqZJ2RJb6t_9EPwuSgkAk&ip=65.201.185.136&id=o-AAyFKyoRtQrnNL3zrfdNULpm39lectAVHkcbeoAF2jLI&itag=137&source=youtube&requiressl=yes&mh=Vv&mm=31%2C26&mn=sn-8xgp1vo-p5qs%2Csn-ab5l6nkd&ms=au%2Conr&mv=m&mvi=2&pcm2cms=yes&pl=21&initcwndbps=1816250&vprv=1&svpuc=1&mime=video%2Fmp4&gir=yes&clen=2674985800&dur=34334.996&lmt=1693003267530862&mt=1693063539&fvip=1&keepalive=yes&fexp=24007246&c=IOS&txp=7209224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRAIgDzk4tc_9bCUvwN0Wg_73hJdAJHl2CDJW8ntEGmIu5HoCIH6u9T8EZSnITAln8Ebh6Vt_P17x3sKbecFfwdkH7AKP&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpcm2cms%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgFZ9mdd4hTdBnDCn0DhbbOkgCLsKQbaoI8eFU3SDBiAYCIHIwpPwy8LfmGv1sNezegCuIQ8f5OZV-J1pBYYZ6Spxi
https://rr2---sn-8xgp1vo-p5qs.googlevideo.com/videoplayback?expire=1693085635&ei=YxvqZMPFMYmi_9EP1YOsqA4&ip=65.201.185.136&id=o-AJrteXKDNXZVv-8ohwCNtkgRusvA7tjSCrK-Yhvj13FZ&itag=251&source=youtube&requiressl=yes&mh=Vv&mm=31%2C26&mn=sn-8xgp1vo-p5qs%2Csn-ab5sznzk&ms=au%2Conr&mv=m&mvi=2&pl=21&initcwndbps=1816250&spc=UWF9f2Mp7IK_PwGaM7XhKZtnkS6X3I4&vprv=1&svpuc=1&mime=audio%2Fwebm&gir=yes&clen=318551384&dur=34335.041&lmt=1693003946161041&mt=1693063539&fvip=1&keepalive=yes&fexp=24007246%2C51000023&beids=24350017&c=ANDROID&txp=7208224&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&sig=AOq0QJ8wRQIhAK--nt8-f1qPpaG1ioc5I2gQLEVD5sCFCUh6fjruOHPUAiAWz0o0kOhS-M--vPkNWD0ZqdSguD5lxFxwLu46Zgb5jw%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIgWmu0j9lq5uhkxVkOfUK4cctQbhgwMcU1stpCvLNYBB4CIQCMt0PzKQ5T9ofUxoIYywGN_fE72dMvPuoEvJZr4jOwJg%3D%3D
Я немного поискал и выяснил, что --youtube-skip-dash-manifest
больше не нужен. То есть на самом деле команда будет такой:
yt-dlp -g 'https://www.youtube.com/watch?v=LpDulftMkew'
Опция -g
заставляет yt-dlp
создать URL стримов, а не скачивать видео.
Команда возвращает два URL, потому что один относится к видеопотоку, а другой — к аудиопотоку. Мы можем переформатировать эти URL, чтобы это стало очевиднее — подсказка заключается в том, что в первом случае используется &mime=video%2Fmp4
, а во втором — &mime=audio%2Fwebm
.
Затем скрипт вызывает ffmpeg
. Вот очищенная версия этого вызова:
ffmpeg \
-ss '8:18:25' -i "${VIDEO_STREAM_URL}" \
-ss '8:18:25' -i "${AUDIO_STREAM_URL}" \
-ss 5 \
-map 0:v \
-map 1:a \
-c:v libx264 \
-c:a aac \
-t 3600 simon-wordcamp.mp4
Две строки -ss
выполняют поиск отметки 8:18:25
в видеопотоке и в аудиопотоке.
--s 5
выполняет поиск на 5 секунд внутрь скомбинированного вывода. В инструкциях на StackExchange говорится, что нужно начинать как минимум за 5 с до той части видео, которую вы хотите сохранить. В рецепте с StackExchange указано, что это нужно «для того, чтобы дать несколько секунд на поиск хорошего ключевого кадра».
-map 0:v
и -map 1:a
задают первый поток как видео, а второй — как аудио.
-c:v libx264
и -c:a aac
конфигурируют выходные кодеки для видео (H.264) и аудио (AAC).
-t 3600 simon-wordcamp.mp4
задаёт длительность (час, или 60 * 60 секунд) и имя выходного файла.
Прим. пер.: вообще для решения задачи можно было использовать встроенную в yt-dlp
функцию --download-sections
, но текст всё равно интересен необычностью изложенной идеи.
Дополнительные ссылки
Exporting and editing a Twitter Spaces recording - 23.03.2022
Syncing slide images and audio in iMovie - 15.06.2023
Reading thermometer temperatures over time from a video - 02.04.2023
Trick Apple Photos into letting you access your video files - 12.04.2022
Running nanoGPT on a MacBook M2 to generate terrible Shakespeare - 01.02.2023
sips: Scriptable image processing system - 18.02.2023
Set a GIF to loop using ImageMagick - 03.08.2021
Rewriting a repo to contain the history of just specific files - 22.03.2022
Compiling to WASM with llvm on macOS - 28.03.2022
Using curl to run GraphQL queries from the command line - 21.02.2022