I spoke at WordCamp US 2023, and wanted to grab a copy of the video of my talk. I always try to keep my own copies of these because I've seen some conferences eventually take these offline in the past.
My presentation was livestreamed through YouTube, but the resulting livestream video is 9.5 hours long!
Normally I would download the full video and trim it locally, but I was on conference WiFi and didn't want to wait for the whole file - so I decided to see if it was possible to download just a subset.
I found this recipe by godofgrunts on StackExchange which looked like what I wanted:
#!/bin/bash
#Arguments: URL, Time stamp -5 seconds, length of clip, video file name
readarray -t 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
But when I ran it I got an error, because readarray
wasn't available on my Mac.
So I pasted it into GPT-4 with the prompt:
Port this to macOS - I do not have the readarray command
It gave me back this:
#!/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
- The IFS variable is set to a newline character, ensuring that the URLs returned by yt-dlp are split on new lines.
- The urls array is populated using command substitution and array assignment.
- The rest of the script remains unchanged.
I saved that as clip-youtube.sh
and ran chmod 755 clip-youtube.sh
and... it worked!
I called it like this:
./clip-youtube.sh 'https://www.youtube.com/watch?v=LpDulftMkew' 8:18:25 3600 simon-wordcamp.mp4
My section of the video starts at around 8h 18m and 25s. I specified 3600 to grab on hour of content (the Q&A went a bit over that, I should have specified more).
It took nearly a full hour for the script to run! It may have been faster to download the whole file and cut it up after all.
But... the result was a 308MB simon-wordcamp.mp4
file.
Breaking this down a bit. The first part is this:
yt-dlp --youtube-skip-dash-manifest -g "$1"
Running that to see the output:
yt-dlp --youtube-skip-dash-manifest -g 'https://www.youtube.com/watch?v=LpDulftMkew'
Output:
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
I dug around a bit and it turned out --youtube-skip-dash-manifest
is no longer necessary. So this is really:
yt-dlp -g 'https://www.youtube.com/watch?v=LpDulftMkew'
The -g
option causes yt-dlp
to produce the URL to the streams rather than downloading the video.
It returns two URLs because the first one is the video stream and the secand is the audio stream. We can reformat those URLs to spot that more clearly - the clue is the &mime=video%2Fmp4
in the first compared to &mime=audio%2Fwebm
in the second.
Then it calls ffmpeg
. Here's a tidied version of that call:
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
The two -ss
lines seek the video stream and the audio stream to the 8:18:25
mark.
-ss 5
seeks 5 seconds into the combined output. The instructions on StackExchange said to start at least 5s before the section of the video that you want to capture. The StackExchange recipe says this is for "giving me a few seconds to catch a good keyframe".
-map 0:v
and -map 1:a
sets the first stream up as video and the second stream as audio.
-c:v libx264
and -c:a aac
configure the output codecs for video (H.264 video) and audio (AAC).
-t 3600 simon-wordcamp.mp4
sets the duration to an hour (60 * 60 seconds) and specifies the output file.
Created 2023-08-26T11:52:14-04:00, updated 2023-10-27T13:51:22-07:00 · History · Edit