Stop streaming music from YouTube with this one weird trick

Having grown up on the internet long before the average connection speed made music streaming services viable, streaming has always struck me as wasteful. And I know that doesn't make much sense—it's not like there's a limited amount of bandwidth to go around! But if I'm going to listen to the same audio file five times, why not just download it once and listen to it forever? Particularly if I want to listen to it while airborne and avoid the horrors of plane wifi. Or if I want to remove NSFW graphics that seem to frequently accompany mixes I enjoy.

youtube-dl to the rescue

Luckily, at least as far as YouTube audio is concerned, there is plenty of free software available to help with this! I like to fetch and process music from YouTube using youtube-dl and ffmpeg. Both are packaged and available in Debian if you like to apt install things:

Saving audio files from YouTube

Well, let's suppose you want to download some eurobeat. youtube-dl can help. The -x flag tells youtube-dl to skip downloading video and to only fetch audio.

$ youtube-dl -x https://www.youtube.com/watch?v=8B4guKLlbVU
[youtube] 8B4guKLlbVU: Downloading webpage
[youtube] 8B4guKLlbVU: Downloading video info webpage
[youtube] 8B4guKLlbVU: Extracting video information
[youtube] 8B4guKLlbVU: Downloading js player vflGUPF-i
[download] Destination: SUPER EUROBEAT MIX-8B4guKLlbVU.webm
[download] 100% of 60.68MiB in 20:57
Deleting original file SUPER EUROBEAT MIX-8B4guKLlbVU.webm (pass -k to keep)

YouTube sometimes throttles connections to approximate real-time buffer rates, so the download could take a while. If you need to interrupt the download for some reason, you can use SIGINT (Ctrl+C) to stop it. If you run youtube-dl again, it's smart enough to resume the download where you left off.

Once the download is complete, there's not much more to do. It will be saved with the appropriate file extension so you can determine what audio codec it uses. You might want to rename the file:

$ mv SUPER\ EUROBEAT\ MIX-8B4guKLlbVU.ogg super_eurobeat_mix.ogg

Now we can enjoy many plays of our super_eurobeat_mix.ogg file! 🚗🎶

Re-encoding audio to another format

Suppose that I have a really old MP3 player I'd like to put this file on, and it doesn't support the OGG Vorbis format. That's not a problem; we can use ffmpeg to re-encode the audio.

The -i flag to ffmpeg specifies an input file. -acodec mp3 says that we want to use the mp3 codec to re-encode our audio. The last positional argument, super_eurobeat_mix.mp3, is the name of the file we want to output.

$ ffmpeg -i super_eurobeat_mix.ogg -acodec mp3 super_eurobeat_mix.mp3
ffmpeg version 3.4.2-1+b1 Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 7 (Debian 7.3.0-4)
  configuration: --prefix=/usr --extra-version=1+b1 --toolchain=hardened
 --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu
 --enable-gpl --disable-stripping --enable-avresample --enable-avisynth
 --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray
 --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite
 --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme
 --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg
 --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband
 --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr
 --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame
 --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp
 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq
 --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2
 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint
 --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
  libavutil      55. 78.100 / 55. 78.100
  libavcodec     57.107.100 / 57.107.100
  libavformat    57. 83.100 / 57. 83.100
  libavdevice    57. 10.100 / 57. 10.100
  libavfilter     6.107.100 /  6.107.100
  libavresample   3.  7.  0 /  3.  7.  0
  libswscale      4.  8.100 /  4.  8.100
  libswresample   2.  9.100 /  2.  9.100
  libpostproc    54.  7.100 / 54.  7.100
Input #0, ogg, from 'super_eurobeat_mix.ogg':
  Duration: 01:06:35.57, start: 0.000000, bitrate: 124 kb/s
    Stream #0:0(eng): Audio: vorbis, 44100 Hz, stereo, fltp, 128 kb/s
    Metadata:
      LANGUAGE        : eng
      ENCODER         : Lavf57.83.100
Stream mapping:
  Stream #0:0 -> #0:0 (vorbis (native) -> mp3 (libmp3lame))
Press [q] to stop, [?] for help
Output #0, mp3, to 'super_eurobeat_mix.mp3':
  Metadata:
    TSSE            : Lavf57.83.100
    Stream #0:0(eng): Audio: mp3 (libmp3lame), 44100 Hz, stereo, fltp
    Metadata:
      LANGUAGE        : eng
      encoder         : Lavc57.107.100 libmp3lame
size=   62432kB time=01:06:35.58 bitrate= 128.0kbits/s speed=22.4x    
video:0kB audio:62431kB subtitle:0kB other streams:0kB global headers:0kB
muxing overhead: 0.000396%

Voila! We now have a super_eurobeat_mix.mp3 file we can copy to our janky old MP3 player.

Extracting audio from an existing video file

Sometimes I accidentally forget to pass the -x flag to youtube-dl, and get a video file instead of an audio track. Oops.

But that's okay! Extracting the audio track from the video file with ffmpeg is just a couple of commands away.

First, we should determine the encoding of the audio track. The combined video file is a .webm file, but we can peek inside using ffmpeg.

$ ffmpeg -i SUPER\ EUROBEAT\ MIX-8B4guKLlbVU.webm 
ffmpeg version 3.4.2-1+b1 Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 7 (Debian 7.3.0-4)
  configuration: --prefix=/usr --extra-version=1+b1 --toolchain=hardened
 --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu
 --enable-gpl --disable-stripping --enable-avresample --enable-avisynth
 --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray
 --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite
 --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme
 --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg
 --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband
 --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr
 --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame
 --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp
 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq
 --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2
 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint
 --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
  libavutil      55. 78.100 / 55. 78.100
  libavcodec     57.107.100 / 57.107.100
  libavformat    57. 83.100 / 57. 83.100
  libavdevice    57. 10.100 / 57. 10.100
  libavfilter     6.107.100 /  6.107.100
  libavresample   3.  7.  0 /  3.  7.  0
  libswscale      4.  8.100 /  4.  8.100
  libswresample   2.  9.100 /  2.  9.100
  libpostproc    54.  7.100 / 54.  7.100
Input #0, matroska,webm, from 'SUPER EUROBEAT MIX-8B4guKLlbVU.webm':
  Metadata:
    ENCODER         : Lavf57.83.100
  Duration: 01:06:35.57, start: 0.000000, bitrate: 490 kb/s
    Stream #0:0(eng): Video: vp9 (Profile 0), yuv420p(tv,
bt709/unknown/unknown), 1920x1080, SAR 1:1 DAR 16:9, 29.97 fps, 29.97 tbr, 1k
tbn, 1k tbc (default)
    Metadata:
      DURATION        : 01:06:35.558000000
    Stream #0:1(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)
    Metadata:
      DURATION        : 01:06:35.572000000
At least one output file must be specified

The important line is here:

Stream #0:1(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)

The audio uses the "vorbis" codec. Hence, we should probably use the .ogg extension for our output file, to ensure we specify a compatible audio container format. If it were mp3-encoded, we'd use .mp3, and so on.

Let's extract the audio track from our video file. We need a couple new flags for ffmpeg. The first is -vn, which tells ffmpeg to not include a video track. The second is -acodec copy, which says we want to copy the existing audio track, rather than re-encode it.

$ ffmpeg -i SUPER\ EUROBEAT\ MIX-8B4guKLlbVU.webm -vn -acodec copy super_eurobeat_mix.ogg
ffmpeg version 3.4.2-1+b1 Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 7 (Debian 7.3.0-4)
  configuration: --prefix=/usr --extra-version=1+b1 --toolchain=hardened
 --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu
 --enable-gpl --disable-stripping --enable-avresample --enable-avisynth
 --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray
 --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite
 --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme
 --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg
 --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband
 --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr
 --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame
 --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp
 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq
 --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2
 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint
 --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
  libavutil      55. 78.100 / 55. 78.100
  libavcodec     57.107.100 / 57.107.100
  libavformat    57. 83.100 / 57. 83.100
  libavdevice    57. 10.100 / 57. 10.100
  libavfilter     6.107.100 /  6.107.100
  libavresample   3.  7.  0 /  3.  7.  0
  libswscale      4.  8.100 /  4.  8.100
  libswresample   2.  9.100 /  2.  9.100
  libpostproc    54.  7.100 / 54.  7.100
Input #0, matroska,webm, from 'SUPER EUROBEAT MIX-8B4guKLlbVU.webm':
  Metadata:
    ENCODER         : Lavf57.83.100
  Duration: 01:06:35.57, start: 0.000000, bitrate: 490 kb/s
    Stream #0:0(eng): Video: vp9 (Profile 0), yuv420p(tv,
bt709/unknown/unknown), 1920x1080, SAR 1:1 DAR 16:9, 29.97 fps, 29.97 tbr, 1k
tbn, 1k tbc (default)
    Metadata:
      DURATION        : 01:06:35.558000000
    Stream #0:1(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)
    Metadata:
      DURATION        : 01:06:35.572000000
Output #0, ogg, to 'super_eurobeat_mix.ogg':
  Metadata:
    encoder         : Lavf57.83.100
    Stream #0:0(eng): Audio: vorbis, 44100 Hz, stereo, fltp (default)
    Metadata:
      DURATION        : 01:06:35.572000000
      encoder         : Lavf57.83.100
Stream mapping:
  Stream #0:1 -> #0:0 (copy)
Press [q] to stop, [?] for help
size=   60863kB time=01:06:35.54 bitrate= 124.8kbits/s speed=1.03e+03x    
video:0kB audio:60330kB subtitle:0kB other streams:0kB global headers:4kB
muxing overhead: 0.884396%

We've successfully extracted super_eurobeat_mix.ogg from our video file! Go us!

Happy listening, and drive safely while you eurobeat. 🎵