119

I'm looking for a way to watch YouTube videos in terminal (not in a browser or another window, but right there, in any bash session).

Is there a simple way to do this?

I imagine something like this:

$ youtube <video-url>     

I already know how to play a video using mplayer:

$ mplayer -vo caca local-file.avi

However, this opens a new window. It would be cool to play it in terminal.

Also, it should be compatible with tmux sessions.


I asked another question for how to prevent opening a new window.


For those that wonder where I need such a functionality, I started an experimental project named TmuxOS -- with the concept that everything should run inside of a tmux session.

So, indeed I need a video player for local and remote videos. :-)

7
  • 2
    Do you mean like this? google.com/…
    – chaos
    Oct 9, 2014 at 13:33
  • 1
    @chaos Exactly. :-) I'm looking for this for months, but nothing found yet. Decided to ask here... Oct 9, 2014 at 13:34
  • VLC can play Youtube videos, not sure if it has terminal output capabilities though.
    – user67289
    Oct 9, 2014 at 17:22
  • With mpv you can do mpv <youtube url>. It also works for a bunch of other video sites. But this opens a new window. Oct 10, 2014 at 0:43
  • 1
    This is a fascinating question, and I've learned a lot from the answers. But out of curiosity - why on earth do you need to do this? ;-) Oct 10, 2014 at 21:36

4 Answers 4

145

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 (Wayback Machine) 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.

13
  • 3
    You can play it in terminology - and not just w/ the caca libs. You can use terminal escapes to specify a mapped portion of the terninal window and play it right there in high-def. You can even set the video to your background and work over it. terminology understands URIs too - get a webm link to your youtube video and just printf it to the screen like a terminal prompt.
    – mikeserv
    Oct 10, 2014 at 3:17
  • @mikeserv - that's cool, never heard of that terminal emulator. I tried it and it's cool that you can play videos in the background, tybg my.avi does it.
    – slm
    Oct 10, 2014 at 3:48
  • Yeah - I prefer the printf method. Do tybg somefile | sed -n l to see how it works - it just uses extended terminal escapes. They're documented in the man page. You can do the same with ty{ls,cat}. But those little apps are just old demo wrappers of the real thing and maybe not as capable. Anyway, if your version has been built in the last couple months you might like the miniview as much as I do - do ctrl+shift+h. All of enlightenment is pretty much as good - it's why I suggested you try it recently. It's also crazy fast.
    – mikeserv
    Oct 10, 2014 at 4:12
  • 3
    What are the said additional ASCII libs?
    – PythonNut
    Oct 10, 2014 at 4:20
  • 1
    @slm too bad, I guess just the deb-multimedia builds have it enabled. Wonder why the others don't, guess they probably consider libcaca a joke and bloat.
    – derobert
    Oct 15, 2014 at 0:55
17

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

10
  • 1
    Instead of /tmp/vid.mp4 maybe you should use mktemp --suffix=.mp4, no?
    – Braiam
    Oct 12, 2014 at 23:26
  • @Braiam - that's a very good idea, but then I'd probably have to add logic to clean up. In this way at least it always overwrites itself. The file is never read by anything that attempts to execute it and so I don't consider it a vector - unless terminology's gstreamer backend can accidentally run executable code in a video file... Maybe. I doubt it, but I don't enough to say for sure. Anyway, I prefer it always to write over the same file unless I say otherwise, but if I get around to making it better - like size picking and such - I will definitely fix that too.
    – mikeserv
    Oct 12, 2014 at 23:49
  • @Braiam - reworked it - have a look. Probably a dedicated tmp dir/mktemp is next.
    – mikeserv
    Oct 13, 2014 at 4:18
  • @Ramesh - look again. If you used the last version, this is better.
    – mikeserv
    Oct 13, 2014 at 4:19
  • 1
    @DmitriDB - the issue is that you're not printing those escapes to terminology at all - those are going to tmux's pty because tmux owns the master side of those intervening ptys and terminology owns the master side of tmux's pty. To handle that you can do like... PTTY=$(tty) tmux ... then in the tmux session like... tybg >"$PTTY". I actually just answered the same problem you're having here only yesterday. The script above should just work like ytplay ... >"$PTTY" within a multiplexer in that way.
    – mikeserv
    Dec 21, 2014 at 20:43
6

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")
}
9
  • This is what I get on my machine: paste.ubuntu.com/8527219 Oct 9, 2014 at 14:47
  • 2
    @IonicăBizău try: mplayer <(youtube-dl -o - "https://www.youtube.com/watch?v=QCuq0_nY3Xk") Oct 9, 2014 at 15:13
  • @MartinvonWittich It's working, but it's not ASCII video, in terminal but in another window... :-/ Oct 9, 2014 at 15:19
  • 2
    Someone needs to wrap this in a program that allows searching for youtube videos in the terminal and marking them for batch download. and then wrap it one more time to strip the mp3 and delete the mp4. I might do it at some point if nobody else does, but it won't be in the next few days. Oct 9, 2014 at 15:19
  • @IonicăBizău mplayer automatically chooses the best output driver, and if you have Xorg running, it will probably choose the xv driver. Try adding the following option to mplayer to choose the console ASCII art driver: -vo caca. Oct 9, 2014 at 15:23
1

From https://github.com/mps-youtube/mps-youtube

sudo docker run --device /dev/snd -it --rm --name mpsyt rothgar/mpsyt

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .