Watch YouTube videos in terminal

You can download videos and/or just the audio and then watch/listen to them using youtube-dl. The script is written in Python and makes use of ffmpeg I believe.

$ youtube-dl --help
Usage: youtube-dl [options] url [url...]

Options:
  General Options:
    -h, --help                       print this help text and exit
    --version                        print program version and exit
    -U, --update                     update this program to latest version.
                                     Make sure that you have sufficient 
                                     permissions (run with sudo if needed)
...
...

To download videos you simply give it the URL from the page you want the video on and the script does the rest:

$ youtube-dl https://www.youtube.com/watch?v=OwvZemXJhF4
[youtube] Setting language
[youtube] OwvZemXJhF4: Downloading webpage
[youtube] OwvZemXJhF4: Downloading video info webpage
[youtube] OwvZemXJhF4: Extracting video information
[youtube] OwvZemXJhF4: Encrypted signatures detected.
[youtube] OwvZemXJhF4: Downloading js player 7N
[youtube] OwvZemXJhF4: Downloading js player 7N
[download] Destination: Joe Nichols - Yeah (Audio)-OwvZemXJhF4.mp4
[download] 100% of 21.74MiB in 00:16

You can then use vlc or mplayer to watch these locally:

$ vlc "Joe Nichols - Yeah (Audio)-OwvZemXJhF4.mp4"
VLC media player 2.1.5 Rincewind (revision 2.1.4-49-gdab6cb5)
[0x1cd1118] main libvlc: Running vlc with the default interface. Use 'cvlc' to use vlc without interface.
Fontconfig warning: FcPattern object size does not accept value "0"
Fontconfig warning: FcPattern object size does not accept value "0"
Fontconfig warning: FcPattern object size does not accept value "0"
Fontconfig warning: FcPattern object size does not accept value "0"

OK but I want to watch these videos as they're streamed & in ASCII

I found this blog article titled: On ascii, youtube and letting go that demonstrates the method that I discussed in the chatroom, mainly using youtube-dl as the "backend" which could do the downloading of the YouTube stream and then redirecting it to some other app.

This article shows it being done with mplayer:

$ youtube-dl http://www.youtube.com/watch?v=OC83NA5tAGE -o - | \
    mplayer -vo aa -monitorpixelaspect 0.5 - 

The video being downloaded by youtube-dl is redirected via STDOUT above, -o -. There's a demo of the effect here.

          ss#1

With the installation of additional libraries the ASCII video can be enhanced further.

               ss#2

OK but I want the video in my actual terminal?

I found this trick which allows video to be played in an xterm in the O'Reilly articled titled: Watch Videos in ASCII Art.

$ xterm -fn 5x7 -geometry 250x80 -e "mplayer -vo aa:driver=curses j.mp4

The above results in a xterm window being opened where the video plays.

   ss#3

So I thought, why not put the peanut butter and the chocolate together like this:

$ xterm -fn 5x7 -geometry 250x80 -e \
    "youtube-dl http://www.youtube.com/watch?v=OC83NA5tAGE -o - | \
    mplayer -vo aa:driver=curses -"

This almost works! I'm not sure why the video cannot play in the window, but it would seem like it should be able to. The window comes up and starts to play but then closes. I see video for a brief few seconds and then nothing.

Perhaps the above will get you closer to your ultimate solution, or perhaps it just needs to be tweaked a bit on the options.

Additional libraries

If you have libcaca installed (the colorized version of aalib) and you reduce the font size in your gnome-terminal to something really small, like say 3, the following command will display a much better looking ASCII video directly within the terminal:

$ CACA_DRIVER=ncurses mplayer -vo caca video.mp4

   ss#4

Terminals

It would seem that the choice of terminal can make a big deal as to whether mplayer can play directly inside the terminal or whether it opens a separate window. Caching too on mplayer made a dramatic difference in being able to play directly in ones terminals.

Using this command I was able to play in terminator, at least for the first 1/4 of the video before it cut out:

$ youtube-dl http://www.youtube.com/watch?v=OC83NA5tAGE -o - | \
    mplayer -cache 32767 -vo aa:driver=curses -

The colored version used this command:

$ youtube-dl http://www.youtube.com/watch?v=OC83NA5tAGE -o - | \
    CACA_DRIVER=ncurses mplayer -cache 64000 -vo caca -

These same commands could play in gnome-terminal & xterm too.

    ss montage

    NOTE: That's (from Left to Right) xterm, terminator, gnome-terminal, and terminology.


So, with Terminology (probably the very best thing that ever happened to a terminal emulator, by the way) at the time of this writing the following works:

ytplay() ( 
    init() if     [ "${#1}" -gt 0 ] && i=$? du= f=
           then   durl \! \" \# \$ \% \& \' \( \) \* \
                       \+ \, \/ \: \; \= \? \@ \[ \]
                  : >"${f:=${2:-/tmp/vid}.$(
                      durl "$1" 's/.*mime=[^/]*.\([^&]*\).*/\1/'
                  )}"
                  init() { loop; }
           else   ! echo 'NO LINK SPECIFIED!' >&3
           fi
    durl() if    [ "${#du}" -eq 0 ]
           then  du=$(for c do printf 's/%%%X/\\%s/g;' "'$c" "$c"; done)
           else  curl -s "$1" | { shift
                 sed '/.*url_encoded_fmt_stream_map[^,]*url=/!d
                      s///;s/,.*//;s/\\u0026/\&/g;'"$du$*"; }
           fi
    loop() if    [ "$((i=$i+1))" -le 5 ] &&
                 sleep "$(($i*2))" 
           then  play || kill "$pid" || :
           else  ! echo 'ERROR RETRIEVING VIDEO!' >&3
           fi
    play() if    [ -s "$f" ]
           then  printf '\033}bt%s\0' "$f"; exit
           fi
    while init "$@" || exit
    do    curl -s "$(durl "$1")" >"$f" & pid=$!
    done  3>&2 2>/dev/null
)

The terminology specific bit is the then block in play() - the printf \033}... line. terminology accepts extended terminal escapes for printing media to the screen - kinda like prompt colors but also hi-def video and basically whatever else you want.

The rest is a little bit of curl | sed script that:

  1. Accepts a regular youtube link as its first argument...
    • ytplay 'https://www.youtube.com/watch?v=${id}' and so on...
  2. Pulls the HTML and finds the relevant javascript code for alternate download streams...
    • do curl "$yturl" | grep url_encoded_fmt_stream_map to see the whole block.
  3. From within that block it selects the first offered alternate stream.
    • many are offered - if you did the grep above you'll find the list in the "quote-delimited ...stream_map: " field.
    • while at first I thought I would only get mp4s more and more I get webms, too. So I've edited it for flow and to assign the file extension by mime type.
    • so far - whether webm or mp4 - the first alternate stream is the highest quality offered per video - usually 720p .mp4 - which is what I'm looking for anyway and so there's no logic here for selecting others. My latest edit added some modularity/explicit tests to make this possible, though. See this for the perl script I referenced when originally writing the function.
  4. Parses the %[[:hexdigit:]]\{2\} encoding into working links.
    • See this for my working reference.
  5. Tries to download the file up to 5 times with a bit of a backoff.
    • By default the file is /tmp/vid.${mime-type} but if ytplay is called with a second argument it will interpret that as the targeted download filename - while still appending the extension according to mime type.
  6. If the download target is at a size greater than zero following any of its tries it allows the download to continue in the background and prints terminology's place media in background now terminal escape, else it just gives up and leaves you with an error message all in caps.
    • \033}bt[LINK/FILE]\0 for full-window play. pn would pop it out to a new window. There are others for geometrically limiting the play area - such as only for a portion of the window, for example.

This has undergone only a little more than very minimal testing, but has so far worked for every link without fail but one - and because I wasn't actually interested in watching the one that didn't play I didn't try to find out why.

While it worked for all of them, it didn't necessarily play each - and this is because at first I blindly appended the .mp4 extension to the target file without checking if it was correct. It is possible that the one I didn't look into before was only this as well. In any case, I changed it to handle that.

As I mentioned before, the only terminology specific bit is in the very last function - play() - and so you could easily alter that to do anything you want with the downloading/downloaded video file - such as using it with mplayer's CACA libs in another terminal, or else pop it out into a vlc window or whatever. But if you like yourself you'll watch it in hi-def in terminology.

Oh, and last, this is typically very fast - my speeds have maxed my bandwidth for the duration so far on every attempt, but terminology doesn't need the whole file to begin playing it anyway.

Here it is working:

terminology rocks


There is tutorial for this on youtube:

https://www.youtube.com/watch?v=QCuq0_nY3Xk

According to that video the following should work:

mplayer -cookies -cookies-file /tmp/cook.txt $(youtube-dl -g --cookies /tmp/cook.txt "https://www.youtube.com/watch?v=QCuq0_nY3Xk")

You can create a simple function for this purpose:

playtube () {
mplayer -cookies -cookies-file /tmp/cook.txt $(youtube-dl -g --cookies /tmp/cook.txt "$1")
}