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. :-)

slm
  • 369,824

4 Answers4

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.

cocomac
  • 505
slm
  • 369,824
  • 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 '14 at 03: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 '14 at 03: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 '14 at 04:12
  • 3
    What are the said additional ASCII libs? – PythonNut Oct 10 '14 at 04:20
  • @PythonNut - see updates. – slm Oct 10 '14 at 21:12
  • @Mew - I have 3 small children that are nuts for that movie, so I used it as source material. Is that really worth a -1? – slm Oct 11 '14 at 07:41
  • Badly specified joke. – Kaz Wolfe Oct 11 '14 at 08:09
  • I use the printf thing here. – mikeserv Oct 12 '14 at 22:38
  • if you use mpv instead of mplayer, you can play the youtube URL directly; no need for youtube-dl. – derobert Oct 14 '14 at 23:57
  • @derobert - thanks, I saw your A on the other Q that's related to this one. Unfortunately all the distros I have (Fedora 20 & Ubuntu 14.04) do not appear to have caca suport compiled into their packaged versions of mpv. – slm Oct 15 '14 at 00:38
  • 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 '14 at 00:55
  • With ncurses driver, mplayer keeps updating the status text after the frame is displayed which clears the screen and cause flickering. The solution is to turn off the console status text with -quiet. – cbliard Oct 15 '14 at 09:21
  • This is cool, but I've seen programs that can display images in the virtual terminal using the framebuffer. I wonder if the same can be done for videos... Then you'd have the actually color, not ascii. – gsgx Oct 15 '14 at 20:56
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

mikeserv
  • 58,310
  • 1
    Instead of /tmp/vid.mp4 maybe you should use mktemp --suffix=.mp4, no? – Braiam Oct 12 '14 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 '14 at 23:49
  • @Braiam - reworked it - have a look. Probably a dedicated tmp dir/mktemp is next. – mikeserv Oct 13 '14 at 04:18
  • @Ramesh - look again. If you used the last version, this is better. – mikeserv Oct 13 '14 at 04:19
  • This is pretty sick, but it seems everything to do with terminology's fun features (tybg, tyls, etc) won't work out if launched within a tmux session. No idea if screen gives the same hard time. Interestingly you can spawn a tmux session -on top- of the terminology window playing a video with this method, though. – Dmitri DB Dec 21 '14 at 20: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 '14 at 20:43
  • I couldn't get this to work. I tried a number of different YouTube links - I either got "ERROR RETRIEVING VIDEO!" or it just silently failed. – emacsomancer Feb 17 '15 at 17:19
  • @emacsomancer - well, it did work at the time I wrote it - but I expected that it wouldn't forever (which is why I was careful to emphasize at the time of this writing* at top)*. It maybe I could update it later today if I find the time. – mikeserv Feb 17 '15 at 19:06
  • @mikeserv: Sorry, I didn't craft my comment very well, I'm afraid. I did notice the "at the time of this writing" caveat and so wasn't surprised that it didn't work. I was curious if anyone had a modified version which was working. – emacsomancer Feb 18 '15 at 18:12
  • @emacsomancer - no worries, I took no offense, but neither have I found the time to fix it either. Hopefully soon... – mikeserv Feb 18 '15 at 19:47
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")
}
jimmij
  • 47,140
  • This is what I get on my machine: http://paste.ubuntu.com/8527219/ – Ionică Bizău Oct 09 '14 at 14:47
  • 2
    @IonicăBizău try: mplayer <(youtube-dl -o - "https://www.youtube.com/watch?v=QCuq0_nY3Xk") – Martin von Wittich Oct 09 '14 at 15:13
  • @MartinvonWittich It's working, but it's not ASCII video, in terminal but in another window... :-/ – Ionică Bizău Oct 09 '14 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. – Millie Smith Oct 09 '14 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. – Martin von Wittich Oct 09 '14 at 15:23
  • @IonicăBizău Do you want to play this video as a background in the current terminal? As far as I know only terminology terminal is capable to do that. – jimmij Oct 09 '14 at 15:25
  • Hmm, even with -vo caca mplayer will still open a new X window. I don't really know how to force it to play in the terminal. – Martin von Wittich Oct 09 '14 at 15:27
  • CACA_DRIVER=ncurses mplayer -vo caca <(youtube-dl -o - "https://www.youtube.com/watch?v=x3BY72RF8vc") works, but it runs pretty slow. – Martin von Wittich Oct 09 '14 at 15:31
  • xterm -fn 5x7 -geometry 250x80 -e 'CACA_DRIVER=ncurses mplayer -vo caca <(youtube-dl -o - "https://www.youtube.com/watch?v=x3BY72RF8vc")' works best for me. – Martin von Wittich Oct 09 '14 at 15:33
1

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

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