128

I am using Ubuntu, and the youtube-dl command is working absolutely fine.

However, now I want to download only a portion a video that is too long. So I want to download only a few minutes of that video, e.g. from minute 13 to minute 17.

Is there any way to do that?

13 Answers 13

105

I don't believe youtube-dl alone will do what you want. However you can combine it with a command line utility like ffmpeg.

First acquire the actual URL using youtube-dl:

youtube-dl -g "https://www.youtube.com/watch?v=V_f2QkBdbRI"

Copy the output of the command and paste it as part of the -i parameter of the next command:

ffmpeg -ss 00:00:15.00 -i "OUTPUT-OF-FIRST URL" -t 00:00:10.00 -c copy out.mp4

The -ss parameter in this position states to discard all input up until 15 seconds into the video. The -t option states to capture for 10 seconds. The rest of the command tells it to store as an mp4.

ffmpeg is a popular tool and should be in any of the popular OS repositories/package managers.

  • 14
    So essentially, you still have to download the whole video and crop it yourself – Antony Jun 17 '16 at 0:08
  • 25
    You can use the -to option instead of -t if you want to specify the time for ending the video slice. ffmpeg -i "OUTPUT-OF-FIRST URL" -ss 00:13:00.00 -to 00:17:00.00 -c copy out.mp4 – Roy Apr 7 '17 at 14:41
  • 11
    @Antony ffmpeg will actually only download the part you specify. – akaihola Dec 21 '18 at 20:29
  • 2
    @Antony As long as you put the -ss parameter before the -i parameter, then it will start downloading from the time you specified with -ss. However, if you place -ss after the -i parameter, then ffmpeg will download from the very beginning of the video and start encoding from where -ss specifies. See my edit of Johnnie's answer for more info. – programmar Dec 15 '19 at 7:35
  • 2
    using the link provided by youtube-dl with the -g option will always return 403 forbidden for me. Using youtube-dl alone to download the video, will always work like a charm. – Fabián Aug 5 '20 at 19:27
56

Adding to Johnnie's answer:

Use youtube-dl --youtube-skip-dash-manifest -g "URL" to get the video and audio streams.

Now use:

ffmpeg -ss 12:15 -i "1st-URL" -ss 12:15 -i "2nd-URL" -t 5:15 -map 0:v -map 1:a -c:v libx264 -c:a aac output.mkv

You'll need to use the -ss option for each stream. I also recommend doing it about 30 seconds earlier and then using another -ss 30 to avoid losing any key frames. Here's a real example using one of my youtube videos.

Video

youtube-dl --youtube-skip-dash-manifest -g https://www.youtube.com/watch?v=gESHIrvIQQo

Output:

https://r3---sn-mv-cvne.googlevideo.com/videoplayback/id/80448722bbc8410a/itag/298/source/youtube/requiressl/yes/mn/sn-mv-cvne/ei/BgifWfmmL4iE8wSlv47oCA/mm/31/pl/23/mv/m/ms/au/initcwndbps/11447500/ratebypass/yes/mime/video%2Fmp4/otfp/1/gir/yes/clen/5231968228/lmt/1502479662079137/dur/18575.164/key/dg_yt0/signature/4FFB9B0B7E1703B31F5D07DAD579B55F17EF7BAA.0CB63905C89DD4D33F90CF3AAD728F1ECDFCB9B3/mt/1503594423/ip/206.34.122.70/ipbits/0/expire/1503616102/sparams/ip,ipbits,expire,id,itag,source,requiressl,mn,ei,mm,pl,mv,ms,initcwndbps,ratebypass,mime,otfp,gir,clen,lmt,dur/
https://r3---sn-mv-cvne.googlevideo.com/videoplayback/id/80448722bbc8410a/itag/140/source/youtube/requiressl/yes/mn/sn-mv-cvne/ei/BgifWfmmL4iE8wSlv47oCA/mm/31/pl/23/mv/m/ms/au/initcwndbps/11447500/ratebypass/yes/mime/audio%2Fmp4/otfp/1/gir/yes/clen/295235970/lmt/1502480001536214/dur/18575.243/key/dg_yt0/signature/4CD42047D1D5C714377350905C1CC5CBA37C0009.6EED1FC92D17A096235C32E48F4B15DEF7DD99B0/mt/1503594423/ip/206.34.122.70/ipbits/0/expire/1503616102/sparams/ip,ipbits,expire,id,itag,source,requiressl,mn,ei,mm,pl,mv,ms,initcwndbps,ratebypass,mime,otfp,gir,clen,lmt,dur/

I wanted to cut from 43:00 to 50:10 so I'm going to do -ss 42:30 (giving me a few seconds to catch a good keyframe) on both inputs and then do a -ss 30 after the inputs to start at 43:00.

I'll then use map to map the video 0:v and audio 1:a (0 means first input, which is the video and 1 means the second input, which is the audio) and then choose my encoding options.

# The first URL
video_url="https://r3---sn-mv-cvne.googlevideo.com/videoplayback/id/80448722bbc8410a/itag/298/source/youtube/requiressl/yes/pl/23/ei/5wCfWY6dBeOj8gSSxZaACQ/mv/m/initcwndbps/5055000/ms/au/mm/31/mn/sn-mv-cvne/ratebypass/yes/mime/video%2Fmp4/otfp/1/gir/yes/clen/5231968228/lmt/1502479662079137/dur/18575.164/key/dg_yt0/beids/%5B9466591%5D/mt/1503592562/signature/8CCFBF5CAB97341D0CB1F34E85AB6EE20FC7A03E.7679F39A8603CF41A95F10232E2A921EB0774101/ip/206.34.122.70/ipbits/0/expire/1503614279/sparams/ip,ipbits,expire,id,itag,source,requiressl,pl,ei,mv,initcwndbps,ms,mm,mn,ratebypass,mime,otfp,gir,clen,lmt,dur/"
# The second URL
audio_url="https://r3---sn-mv-cvne.googlevideo.com/videoplayback/id/80448722bbc8410a/itag/140/source/youtube/requiressl/yes/pl/23/ei/5wCfWY6dBeOj8gSSxZaACQ/mv/m/initcwndbps/5055000/ms/au/mm/31/mn/sn-mv-cvne/ratebypass/yes/mime/audio%2Fmp4/otfp/1/gir/yes/clen/295235970/lmt/1502480001536214/dur/18575.243/key/dg_yt0/beids/%5B9466591%5D/mt/1503592562/signature/4AACC8E27F9036D36D4D623A771A9F2BAC4674BA.7E4F4FB4DC023E3FE491A991F0F9F2329648DE9D/ip/206.34.122.70/ipbits/0/expire/1503614279/sparams/ip,ipbits,expire,id,itag,source,requiressl,pl,ei,mv,initcwndbps,ms,mm,mn,ratebypass,mime,otfp,gir,clen,lmt,dur/"
ffmpeg -ss 42:30 -i "$video_url" -ss 42:30 -i "$audio_url" -map 0:v -map 1:a -ss 30 -t 7:10 -c:v libx264 -c:a aac gog-vs-triv.mkv

Credit to Jakub Vrána for the --youtube-skip-dash-manifest solution.

  • 2
    Thanks. I was not sure how to get sound, for @Johnnie post. Your post answered it. – Manny_Mar Jun 27 '19 at 3:27
  • 1
    How does it differ from using --postprocessor-args directly in youtube-dl? – daGo Nov 2 '19 at 13:15
  • 3
    @daGo - I believe youtube-dl --postprocessor-args will download the whole video and then edit it down to the selected range, whereas this answer uses youtube-dl just to get some special URLs. ffmpeg then uses those URLs to download only the required range. So if you want to extract 2 minutes from a 2 hour lecture video, this answer will save you a lot of downloading. – Nick Chammas Feb 3 '20 at 7:20
  • 1
    This works with most YT videos, but I am trying this with a 9 hour video that was previously streamed live (and now completed) and I get "Invalid data found when processing input". The main difference is that the output of youtube-dl -g is different. With most videos it has the struct. "https://r3---sn-mv-cvne.googlevideo.com/videoplayback/id/8..." but for this video it has the struct. "https://manifest.googlevideo.com/api/manifest/dash/expire/15956..." (truncated for space). Ideas on how to make this work? – Leftover Salad Jul 24 '20 at 23:26
  • 1
    @LeftoverSalad Found one solution. – ynn Aug 18 '20 at 9:01
38
ffmpeg $(youtube-dl -g 'https://www.youtube.com/watch?v=oHg5SJYRHA0' | sed 's/.*/-ss 00:05 -i &/') -t 01:00 -c copy out.mkv

Edit the URL and the -ss, -t times to use this command. It creates the arguments, using both video and audio URLs, similar to this answer by @godofgrunts. No quotes are used for $() because this produces two separated arguments for ffmpeg.

  • 7
    Could you explain, what it does, how it works? – peterh - Reinstate Monica Sep 15 '17 at 4:49
  • 4
    It's just a more terse version of @godofgrunts answer, using sed to save some time manually grabbing the URLs and copying/pasting in the start times (-ss 00:05). @godofgrunts has some extra flags -map 0:v -map 1:a -c:v libx264 -c:a aac that might improve quality. I also like to add youtube-dl -f bestvideo+bestaudio to be sure I'm getting the highest quality version. – Steve Goranson Sep 16 '17 at 7:13
  • The "extra flags" deal with the two streams (video and audio) which I think you are mishandling - IIUC they have nothing to do with improving quality (well, having sound is probably a mark of improved quality :-) ) The answer by @godofgrunts below seems to be correct. – NickD Dec 9 '20 at 22:23
30

youtube-dl supports passing arguments to the underlying postprocessor (tested with version 2017.08.27.1):

youtube-dl -x --postprocessor-args "-ss 00:13:00.00 -t 00:04:00.00" https://youtu.be/...

This is basically the same as doing the postprocessing with ffmpeg yourself after downloading the file first.

18

This feature request is not yet implemented in youtube-dl. See #622 issue and many duplicates of it on github.

12

Use the --postprocessor-args parameter to pass the audio/video output to ffmpeg to be edited (the processor). The postprocessor needs to be ffmpeg installed.

--postprocessor-args takes 3 arguments & values (for example, theres is more, check manual page of ffmpeg):

  • -ss HH:MM:SS : start time to take
  • -to HH:MM:SS : end time
  • -t HH:MM:SS : time length to take

Examples:

  • Start encoding at 15 seconds and stop at 1 minutes 20 seconds
    $ youtube-dl --postprocessor-args "-ss 0:0:15 -to 0:1:20" '[video_URL]'
    
  • Start encoding at 15 seconds and take only the next 3 minutes 5 seconds
    $ youtube-dl --postprocessor-args "-ss 0:0:15 -t 0:3:5" '[video_URL]'
    

PS: youtube-dl will download the entire media before processing it, and remove it after.

8

This doesn't completely answer OP's question but there is way to download a stream from beginning to a specific duration without having to download the complete stream. Since YouTube provides resume support, we could request for partial content using the Range header.

We first fetch the stream URLs:

$ youtube-dl -g https://www.youtube.com/watch?v=yysk8s2vih8
https://r1---sn-npoeenee.googlevideo.com/videoplayback?source=youtu...
https://r1---sn-npoeenee.googlevideo.com/videoplayback?source=youtu...

This should output two URLs (each for video and audio streams).

Now send a head request to the first URL (which links to the video stream) to fetch the total content length of this stream:

$ curl "1st-URL" -sI | grep Content-Length
Content-Length: 64380504

Now, we divide this total content length by total seconds in video (the YouTube video has a duration of 4 min and 7 secs which is 247 seconds.) to approximately get the content length of 1 second:

64380504 / 247 ≈ 260650

We multiply this value with (number of seconds we want to fetch from the beginning + 1)

(we add one to also roughly account for extra space taken by metadata which is placed at the beginning of the stream)

For example to fetch approximately the first 10 seconds, you will need to fetch the first 260650 * 11 = 2867150 bytes, so we make a request with the Range header:

$ curl "1st-URL" -H "Range: bytes=0-2867150" -o ten_secs.mp4

This should only download the first 10 secs. The downloaded file should be able to play but best let FFmpeg fix the incorrect metadata:

$ ffmpeg -i ten_secs.mp4 -c copy corrected_ten_secs.mp4

We can also download only the initial part of the audio (2nd-URL) in a similar fashion (content-length would differ but total seconds would remain same).

Downloading any middle portion from the video should also be possible in this way but is going probably way much trickier because YouTube places the metadata at the beginning of the stream (in the first few bytes) and without it being present in the downloaded media, the stream won't play at all.

EDIT: This will only work on websites with resume support, say YouTube. It won't work for other websites.

3

You can download from the start up to a point without downloading the whole thing and editing. That's half of what this question asks:

interrupt the download with ^C

  • I've only tried this with mp4
  • won't work with separate video and audio, you need a format with both. By default ytdl often gets separate video and audio, then merges them. Use -F to see the formats available, and choose an mp4 that has both. e.g. -f18 or -f22 are usually there.
  • you'll have to estimate from percentage downloaded (which isn't linear; the compression rate varies over time).

Yes. It's a hack.

Further question: if you do want the separate video and audio formats, could you download part of each separately (using ^c as here), and then merge them manually? I don't think it willl work for some formats.

1

I made a script implementing @godofgrunts answer here

#!/bin/bash
#taken from https://unix.stackexchange.com/a/388148/48971

if [ $# -lt 4 ]; then
        echo "Usage: $0 <youtube's URL> <HH:mm:ss from time> <HH:mm:ss to time> <output_file_name>"
        echo "e.g.:"
        echo "$0 https://www.youtube.com/watch?v=T1n5gXIPyws 00:00:25 00:00:42 intro.mp4"
        exit 1
fi

echo "processing..."

from=$(date "+%s" -d "UTC 01/01/1970 $2")
to=$(date "+%s" -d "UTC 01/01/1970 $3")

from_pre=$(($from - 30))

if [ $from_pre -lt 0 ]
then
        from_pre=0
fi

from_pre_command_print=$(date -u "+%T" -d @$from_pre)
from_command_print=$(date -u "+%T" -d @$(($from - $from_pre)))$(grep -o "\..*" <<< $2)
to_command_print=$(date -u "+%T" -d @$(($to - $from_pre)))$(grep -o "\..*" <<< $3)

command="ffmpeg "

for uri in $(youtube-dl -g $1)
do
        command+="-ss $from_pre_command_print -i $uri "
done

command+="-ss $from_command_print -to $to_command_print $4"
echo "downloading with the following command:"
echo "$command" 
$command

I also uploaded it to Gitlab's snippets

1

I made a flask app that can to what you want. I know the post is old, but the problem persists.

You can find it here

It can parse text for YouTube links as well. You just supply e.g.:

YouTube.com/blahblah start-1:20

This will download a video from 0-1:22 (slightly differences can arise because of keyframes). It can also download whole videos, just omit the time interval in that case.

1

For all the lazy guys who don't want to use more than one command, do the following:

youtube-dl.exe https://www.youtube.com/watch?v=MJD39Aja1Is --external-downloader ffmpeg --external-downloader-args "-ss 00:00:10.00 -t 00:01:00.00"

You can now combine the options of youtube-dl.exe and ffmpeg like choosing the format, removing the video and cutting the audio file etc. (maybe cutting the video file and than converting to an audio file, whatever happens first)

youtube-dl.exe -f 251 https://www.youtube.com/watch?v=MJD39Aja1Is -x --external-downloader ffmpeg --external-downloader-args "-ss 00:00:10.00 -t 00:01:00.00"
  • your command does not work on GNU/Linux: we use youtube-dl instead of youtube-dl.exe. And ffmpeg told me Invalid duration specification for ss: 00.00.10.00, it should be -ss 00:00:30.00 with colons instead of periods except for the last one. – ewen-goisot Sep 8 '20 at 21:33
  • 1
    @ewin-goisot I am sorry, this happened because i didn't copy-pasted the whole command after testing it. Now it should work. the problem was the time definition. it has to be 00:00:00.00 2 times double point, than only 1 point. like hh:mm:ss.ms – Yuki Sep 9 '20 at 13:56
  • with 00:00:00.0, it works better, I can download a portion of the video. I still have an issue (with your command and most of other ones) : it downloads 5 or 10 seconds too much. The solution is given on @godofgrunts ' s answer: -ss 42:30 -i "$video_url" -ss 42:30 -i "$audio_url" -map 0:v -map 1:a -ss 30 starting 30 seconds before the portion start and adding a -ss 30 at the end. So I need to use several times the -ss option on the same command. Is there a way to do the same with --external-downloader-args ? – ewen-goisot Sep 9 '20 at 16:21
  • ah sorry, i am not that familiar with mixing different streams. but if you want to take just the best audio and video material of the same video, than you can do it with youtube-dl instead – Yuki Sep 10 '20 at 18:29
0

Someone made a batch script (for Windows) that downloads the file and extracts the desired portion:

@ECHO off

:Input
ECHO.
SET url=
ECHO Enter Youtube-url:
SET /P url=
IF "%url%" EQU "" GOTO End
IF "%url: =%" NEQ "%url%" GOTO Input
ECHO Enter start time (in seconds, or in hh:mm:ss[.xxx] form):
SET /P start=
ECHO Enter duration (in seconds, or in hh:mm:ss[.xxx] form):
SET /P dur=
ECHO.
FOR /F "delims==" %%A IN ('youtube-dl.exe --no-warnings --get-filename "%url%"') DO SET filename=%%A
FOR /F %%B IN ('youtube-dl.exe -g "%url%"') DO (
ffmpeg.exe -hide_banner -ss "%start%" -i "%%B" -c copy -t "%dur%" "%filename%"
)
GOTO Input

:End
0

To add on to @hyperpallium answer: OP did not mention the video he was downloading had to be from YouTube. YouTube is ridiculously restrictive with their videos, they split the audio and video streams into different files, and they track you/sell your data; therefore, one could have difficulties with the method of downloading it partly then hitting ^C. With youtube-dl version 2020.11.17 in Microsoft Windows version 10.0.17134.1304 I downloaded part of a video from archive.org:

E:\cs>youtube-dl --abort-on-unavailable-fragment https://archive.org/download/beastars-season-1-1080p/%5BHCS%5D%20Beastars%20-%2003%20%5B1080p%5D.mkv
[generic] [HCS] Beastars - 03 [1080p]: Requesting header
[redirect] Following redirect to https://ia601504.us.archive.org/14/items/beastars-season-1-1080p/%5BHCS%5D%20Beastars%20-%2003%20%5B1080p%5D.mkv
[generic] [HCS] Beastars - 03 [1080p]: Requesting header
WARNING: Falling back on generic information extractor.
[generic] [HCS] Beastars - 03 [1080p]: Downloading webpage
WARNING: URL could be a direct video link, returning it as such.
[download] Destination: [HCS] Beastars - 03 [1080p]-[HCS] Beastars - 03 [1080p].mkv
[download]   2.3% of 513.77MiB at 839.68KiB/s ETA 10:12
ERROR: Interrupted by user

E:\cs>ren "[HCS] Beastars - 03 [1080p]-[HCS] Beastars - 03 [1080p].mkv.part" "[HCS] Beastars - 03 [1080p]-[HCS] Beastars - 03 [1080p].mkv"

E:\cs>ffmpeg -i "[HCS] Beastars - 03 [1080p]-[HCS] Beastars - 03 [1080p].mkv" "[HCS] Beastars - 03 [1080p].mp4"
ffmpeg version N-91990-g49c67e79ca Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 8.2.1 (GCC) 20180813
[stdout text]
    Stream #0:1(jpn): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      encoder         : Lavc58.30.100 aac
[matroska,webm @ 0000020c8f28a2c0] Read errorme=00:00:33.96 bitrate=2099.5kbits/s speed=0.0185x
frame=  823 fps=0.3 q=-1.0 Lsize=    9677kB time=00:00:34.56 bitrate=2293.8kbits/s speed=0.0147x
video:9134kB audio:519kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.248960%
[libx264 @ 0000020c8f3423c0] frame I:25    Avg QP:14.71  size: 56865
[stdout]

E:\cs>youtube-dl -v
[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: ['-v']
[debug] Encodings: locale cp1252, fs mbcs, out cp437, pref cp1252
[debug] youtube-dl version 2020.11.17
[debug] Python version 3.4.4 (CPython) - Windows-10-10.0.17134
[debug] exe versions: ffmpeg 3.4, ffprobe N-91990-g49c67e79ca
[debug] Proxy map: {}
Usage: youtube-dl [OPTIONS] URL [URL...]

The MKV file I ended up with was "00:22:57" in "total duration", but it ended after 34 seconds. The MP4 video I ended up with was was 00:00:34 seconds in total duration, ended after 34 seconds, and the video and audio played completely fine/as intended. Full cmd.exe log: https://pastebin.com/wiY5EQXK

So in summary, run youtube-dl --abort-on-unavailable-fragment [URL to video file] (option --abort-on-unavailable-fragment might be important as you end up interrupting the download with control+c), let it download for a bit, then press ctrl+c. Optional: rename the "[file].mkv.part" file to "[file].mkv". Converting the MKV to MP4 is probably optional too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.