LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Software (https://www.linuxquestions.org/questions/linux-software-2/)
-   -   youtube-dl: can I download only a portion of the video? (https://www.linuxquestions.org/questions/linux-software-2/youtube-dl-can-i-download-only-a-portion-of-the-video-4175711173/)

newbiesforever 04-23-2022 02:09 PM

youtube-dl: can I download only a portion of the video?
 
I would like to download only part of a video with youtube-dl, but can't figure out how from the man page. There is that section "Video Selection," but it seems to concern only downloading entire videos within a playlist. I want or more sections within one video. (If I mayy download only one at a time, no problem.) It sounds like something that should be possible...

I habitually download the audio from Youtube videos of services at my church (that is, I youtube-dl -x them); and the only way I know how to separate parts of the audio is to tediously work on it in Audacity. It would save a good lot of time if I could download only the parts I wanted.

teckk 04-23-2022 03:35 PM

You can do that in multiple ways.

Let me get an example, dogs barking. (You did say audio.)
Code:

yt-dlp -F https://m.youtube.com/watch?v=muAiqLbblVQ
I'll get parts of format 140, which is aac audio.

From 00:01:00 to 00:01:20 in the audio
Code:

url=$(yt-dlp -g -f 140 https://m.youtube.com/watch?v=muAiqLbblVQ)

ffmpeg -ss 00:01:00 -t 00:00:20 -i "$url" -c:a copy -c:v copy DogsBark.m4a


teckk 04-23-2022 03:57 PM

And of course, I would script that.

Basic example:
Code:

#!/usr/bin/bash

while :; do
    read -p "Enter/Paste Utube video url: " url
    read -p "Enter Outfile name: " ofile
    read -p "Enter start time: " stime
    read -p "Enter Duration: " dur
   
    yturl=$(yt-dlp -g -f 140 "$url")

    ffmpeg -ss "$stime" -t "$dur" -i "$yturl" -c:a copy -c:v copy "$ofile"
   
    read -p "Get another segment? Enter y or n: " yn
   
    case $yn in
        [Yy]* ) echo "" ;;
       
        [Nn]* ) exit ;;
           
        *) echo -e "You did not enter y or n, try again.\n" ;;
    esac
done

You probably want to heep that url in a variable so that you don't have to enter it over and over. Maybe change where the loop starts.

boughtonp 04-23-2022 04:44 PM


 
The key thing to point out in the above is that "-g" is short for "--get-url" - i.e. the above is using youtube-dl (or yt-dlp) to retrieve the URL of the video/audio file, then using ffmpeg to do the actual downloading.

Also, if the order of the arguments is correct (i.e. the offset and duration come before the URL), ffmpeg will only download the relevant section, but with incorrect argument ordering it will download the full thing first.


According to the youtube-dl help, it should be possible to do all this with a single command via "--external-downloader=ffmpeg" and "--external-downloader-args='-ss 00:01:00 -t 00:00:20'", but in the quick test I did, youtube-dl seems to ignore them and download too much (but is still parsing the args because it errors if they're incorrect).


ondoho 04-24-2022 02:23 AM

Just a quick thought (no research done): can youtube-dl or yt-dlp be configured to respect the &t= part of a youtube url?
E.g. https://youtube.com/watch?v=muAiqLbblVQ&t=60 would be 1 minute into the video.

teckk 04-24-2022 08:00 AM

I never thought of that.

Lets see...
Code:

yt-dlp -f 140 "https://youtube.com/watch?v=muAiqLbblVQ&t=10" -o test1.m4a
mplayer test1.m4a

url=$(yt-dlp -g -f 140 https://m.youtube.com/watch?v=muAiqLbblVQ)
mplayer "$url"

Nope, it grabbed the whole thing.

It's hard to beat ffmpeg for video/audio manipulation. ffmpeg is my first goto for that.

The problem that the @OP will have is, you'll have to know where the breaks that you want are before you start to download the segments. So you'll have to get the whole audio to start with anyway. Unless you have some kind of notes on the video because you were there when it was made. Otherwise, I don't see how one could know where the segments should start/stop.

And if you are playing it over and over to determine that, you'll use more bandwidth than if you downloaded it once, and then worked on that local file.

You can make a script easy enough to get time stamps for where it should be cut. Play the local video once and mark it while you play it.

Example:
There was a sitcom that I caught from TV tuner to file, overnight. My machine awakened, caught the video, went back to sleep. And that gave me a 34 min long transport stream. mpeg2 video, ac3 audio.

It had 4 segments that I wanted to keep, and get rid of the commercial breaks. First thing that I wanted was to run it through ffmpeg to make a mp4 out of the transport stream so that it had an accurate time base for ffmpeg to work with. A transport stream does not, there is no way to cut a video on time stamps with a file.ts, because it doesn't have one.

I wanted to be able to play the video quickly and mark the start and stop of the segments that I wanted to keep, cut them out with ffmpeg into separate files, then put all of them together in to one video. Done.

Here are a few snippets of it.

Make a mp4 out of it if wanted.
Code:

read -p "Enter/Paste file.ts for editing :" vid

#Make a .mp4 out of it so it has correct time index
read -p "Make .mp4 file to start? y or n :" yn
case $yn in
    [Yy]* ) ffmpeg -i "$vid" -c:a copy -c:v copy "${vid%.*}".mp4
            vid=""${vid%.*}".mp4" ;;
   
    [Nn]* ) ;;
esac

Mark the video segments start/stop with the space bar while playing the video with mplayer. I'll use a utube video for source.
Code:

vid="MyVideo.mp4"

#Get video index times from mplayer. Press space bar to
#capture index times to array for start/stop times of segments
tstamp=($(while read line; do
grep -oP 'A:\K[^V]+' <<< "$line" | tail -n 1 | cut -d "." -f1
done < <(mplayer -osdlevel 3 "$vid" -vf scale=768:432 2>&1)))

For a test, I marked the video with the space bar for 6-13 seconds, then 20-28 seconds.
Code:

echo "${tstamp[@]}"
6 13 20 28

You can then use that info for ffmpeg.
Code:

#Loop through array and use ffmpeg to make video segments.
arr_num=0
mp4_num=1
for i in "${tstamp[@]}"; do
    #Stop on last segment
    if [ "$arr_num" -eq "${#tstamp[@]}" ]; then
        break
    fi
   
    #Start/Stop time of segments
    Start="${tstamp[arr_num]}"
    arr_num=$(($arr_num + 1))
    Stop="${tstamp[arr_num]}"
    arr_num=$(($arr_num + 1))
    Diff=$(($Stop - $Start))
   
    #ffmpeg string to make video segments
    ffmpeg -ss "$Start" -t "$Diff" -i "$vid" -c:a aac -b:a 192k \
    -af volume=1.2 -c:v libx264 -crf 18 -preset slow -s 768x432 \
    file"$mp4_num".mp4 &&
   
    #echo file name created to ConFile for ffmpeg
    echo "file 'file"$mp4_num".mp4'" >> ConFile
   
    mp4_num=$(($mp4_num + 1))
done

And that gives me file1.mp4, file2.mp4 and Confile.

Code:

#Put the segments together.
ffmpeg -f concat -i ConFile -c:a copy -c:v copy Tv_finished.mp4

Code:

ffprobe Tv_finished.mp4
ffprobe version n5.0 Copyright (c) 2007-2022 the FFmpeg developers
  built with gcc 11.2.0 (GCC)
...
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Tv_finished.mp4':
  Metadata:
    major_brand    : isom
    minor_version  : 512
    compatible_brands: isomiso2avc1mp41
    encoder        : Lavf59.16.100
  Duration: 00:00:15.07, start: 0.000000, bitrate: 1597 kb/s
  Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 768x432 [SAR 1:1 DAR 16:9], 1412 kb/s, 29.97 fps, 29.97 tbr, 30k tbn (default)
    Metadata:
      handler_name    : ISO Media file produced by Google Inc.
      vendor_id      : [0][0][0][0]
  Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 179 kb/s (default)
    Metadata:
      handler_name    : ISO Media file produced by Google Inc.
      vendor_id      : [0][0][0][0

If you have a bash shell, youtube-dl and ffmpeg, There isn't much that can't be done with utube vids.

ondoho 04-24-2022 11:52 PM

Yeah I guess it always downloads the whole video first, no matter what.
Analog to what boughtonp suggested, you could also try --postprocessor-args (this from 'man yt-dlp').


All times are GMT -5. The time now is 05:05 PM.