107

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

  • 2
    Do you mean like this? google.com/… – chaos Oct 9 '14 at 13:33
  • 1
    @chaos Exactly. :-) I'm looking for this for months, but nothing found yet. Decided to ask here... – Ionică Bizău Oct 9 '14 at 13:34
  • VLC can play Youtube videos, not sure if it has terminal output capabilities though. – user67289 Oct 9 '14 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. – Gerardo Marset Oct 10 '14 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? ;-) – Digital Trauma Oct 10 '14 at 21:36
132

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.

  • 2
    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 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 '14 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 '14 at 4:12
  • 3
    What are the said additional ASCII libs? – PythonNut Oct 10 '14 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 '14 at 0:55
14

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

  • 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 4:18
  • @Ramesh - look again. If you used the last version, this is better. – mikeserv Oct 13 '14 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 '14 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")
}
  • This is what I get on my machine: paste.ubuntu.com/8527219 – Ionică Bizău Oct 9 '14 at 14:47
  • 1
    @IonicăBizău try: mplayer <(youtube-dl -o - "https://www.youtube.com/watch?v=QCuq0_nY3Xk") – Martin von Wittich Oct 9 '14 at 15:13
  • @MartinvonWittich It's working, but it's not ASCII video, in terminal but in another window... :-/ – Ionică Bizău Oct 9 '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 9 '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 9 '14 at 15:23
1

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.