{"id":15515896,"url":"https://github.com/marcolucidi01/ytcast","last_synced_at":"2025-04-04T10:06:07.088Z","repository":{"id":44356429,"uuid":"428363460","full_name":"MarcoLucidi01/ytcast","owner":"MarcoLucidi01","description":"cast YouTube videos to your smart TV from command-line","archived":false,"fork":false,"pushed_at":"2024-01-28T20:31:58.000Z","size":118,"stargazers_count":768,"open_issues_count":1,"forks_count":24,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-04T09:39:14.427Z","etag":null,"topics":["cast","dial","smart-tv","ssdp","wake-on-lan","youtube","youtube-tv"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MarcoLucidi01.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-11-15T17:43:06.000Z","updated_at":"2025-04-01T23:39:14.000Z","dependencies_parsed_at":"2024-02-08T19:03:57.754Z","dependency_job_id":"ff7ee630-63ed-45cb-9611-48a5bb662752","html_url":"https://github.com/MarcoLucidi01/ytcast","commit_stats":{"total_commits":120,"total_committers":3,"mean_commits":40.0,"dds":0.01666666666666672,"last_synced_commit":"72c271afa89a7b9a081a878d91b055053306095b"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcoLucidi01%2Fytcast","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcoLucidi01%2Fytcast/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcoLucidi01%2Fytcast/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcoLucidi01%2Fytcast/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MarcoLucidi01","download_url":"https://codeload.github.com/MarcoLucidi01/ytcast/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247157281,"owners_count":20893220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cast","dial","smart-tv","ssdp","wake-on-lan","youtube","youtube-tv"],"created_at":"2024-10-02T10:04:46.531Z","updated_at":"2025-04-04T10:06:07.059Z","avatar_url":"https://github.com/MarcoLucidi01.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"ytcast\n======\n\ncast YouTube videos to your smart TV from command-line.\n\nthis program does roughly the same thing as the \"Play on TV\" button that appears\non the player bar when you visit youtube.com with Chrome or when you use the\nYouTube smartphone app:\n\n![Play on TV button][0]\n\n([the feature is also described here][1]).\n\nI don't use Chrome as my daily driver because of *reasons* and I tend to use my\nsmartphone the least as possible when I'm at home... but still I want the \"Play\non TV\" functionality to watch videos on the big television screen without having\nto search them with the remote! this is why I wrote this tool. also my computing\nworkflow is \"command-line centric\" and `ytcast` fits well in my toolbox (see\n[other tools][2]).\n\nhttps://user-images.githubusercontent.com/23704923/147848611-0d20563e-f656-487a-9774-9eb6feca1f58.mp4\n\n([video demo on YouTube if above doesn't play][3]).\n\n[0]: play-on-tv.png\n[1]: https://support.google.com/youtube/answer/7640706\n[2]: #other-tools\n[3]: https://www.youtube.com/watch?v=07aWOpi8DVk\n\ncontents\n--------\n\n- [usage](#usage)\n- [install](#install)\n- [how it works](#how-it-works)\n- [BUGS](#bugs)\n- [workarounds](#workarounds)\n- [THANKS](#thanks)\n- [other tools](#other-tools)\n\nusage\n-----\n\n- the computer running `ytcast` and the target device must be on the **same network**.\n- the target device must support the **DIAL protocol** (see [how it works][14]).\n- the target device must have the **YouTube on TV app already installed**.\n\nrun `ytcast -h` for the full usage, here I'll show the basic options.\n\nthe `-d` (device) option selects the target device matching by name, hostname\n(ip), or unique service name:\n\n    $ ytcast -d fire https://www.youtube.com/watch?v=dQw4w9WgXcQ\n\nto see the already discovered (cached) devices use the `-l` (list) option:\n\n    $ ytcast -l\n    28bc7426 192.168.1.35    \"FireTVStick di Marco\"         cached lastused\n    d0881fbe 192.168.1.227   \"[LG] webOS TV UM7100PLB\"      cached\n\nto update the devices cache use the `-s` (search) option (it's implicit when the\ncache is empty or when `-d` doesn't match anything in the cache):\n\n    $ ytcast -s\n    28bc7426 192.168.1.35    \"FireTVStick di Marco\"         lastused\n    d0881fbe 192.168.1.227   \"[LG] webOS TV UM7100PLB\"      cached\n\nif your target device doesn't show up, you can try increasing the search timeout\nwith the `-t` (timeout) option to give the device more time to respond to the\nquery:\n\n    $ ytcast -s -t 10s\n    28bc7426 192.168.1.35    \"FireTVStick di Marco\"         lastused\n    d0881fbe 192.168.1.227   \"[LG] webOS TV UM7100PLB\"      cached\n\nremember that the computer and the target device must be on the same network.\nif it doesn't show up after several tries, you may consider using the `-pair`\noption to skip the discovery process altogether. this adds some limitations\nthough, see [workarounds][15].\n\nto cast to the last used device use the `-p` option:\n\n    $ ytcast -p https://www.youtube.com/watch?v=dQw4w9WgXcQ\n\nwhen no url is passed in the arguments, `ytcast` reads video urls (or ids) from\n`stdin` one per line:\n\n    $ ytcast -d lg \u003c watchlist\n\nthis makes it easy to combine `ytcast` with other tools like [`ytfzf`][11] or my\n`ytfzf` clone [`ytsearch`][12].\n\nto see what's going on under the hood use the `-verbose` option:\n\n    $ ytsearch fireplace 10 hours | ytcast -d lg -verbose\n    21:13:08 ytcast.go:82: ytcast v0.1.0-6-g8e6daeb\n    21:13:08 ytcast.go:168: mkdir -p /home/marco/.cache/ytcast\n    21:13:08 ytcast.go:177: loading cache /home/marco/.cache/ytcast/ytcast.json\n    21:13:08 ytcast.go:319: reading videos from stdin\n    21:13:15 dial.go:153: GET http://192.168.1.227:1754/\n    21:13:15 dial.go:153: GET http://192.168.1.227:36866/apps/YouTube\n    21:13:15 ytcast.go:293: \"YouTube\" is stopped on \"[LG] webOS TV UM7100PLB\"\n    21:13:15 ytcast.go:306: launching \"YouTube\" on \"[LG] webOS TV UM7100PLB\"\n    21:13:15 dial.go:153: POST http://192.168.1.227:36866/apps/YouTube\n    21:13:18 dial.go:153: GET http://192.168.1.227:36866/apps/YouTube\n    21:13:18 ytcast.go:293: \"YouTube\" is running on \"[LG] webOS TV UM7100PLB\"\n    21:13:18 ytcast.go:358: requesting YouTube Lounge to play [cdKop6aixVE] on \"[LG] webOS TV UM7100PLB\"\n    21:13:18 remote.go:233: POST https://www.youtube.com/api/lounge/bc/bind\n    21:13:18 remote.go:233: POST https://www.youtube.com/api/lounge/bc/bind\n    21:13:18 ytcast.go:197: saving cache /home/marco/.cache/ytcast/ytcast.json\n\n(please run with `-verbose` and **attach the log** when reporting an [issue][13]).\n\n[11]: https://github.com/pystardust/ytfzf\n[12]: https://github.com/MarcoLucidi01/bin/blob/master/ytsearch\n[13]: https://github.com/MarcoLucidi01/ytcast/issues\n[14]: #how-it-works\n[15]: #workarounds\n\ninstall\n-------\n\nyou can get a pre-compiled binary from the [latest release][20] assets and copy\nit somewhere in your `$PATH`.\n\nhere a quick and dirty one-liner script to do it fast on unix-like systems\n(adjust `target` and `dir` to your needs, lookup available targets in the\n[latest release][20] assets):\n\n    (target=\"linux-amd64\"; dir=\"$HOME/bin\"; \\\n      wget -O - https://api.github.com/repos/MarcoLucidi01/ytcast/releases/latest \\\n        | jq -r --arg target \"$target\" '.assets[] | select(.name | match(\"checksums|\"+$target)) | .browser_download_url' \\\n        | wget -i - \\\n       \u0026\u0026 sha256sum -c --ignore-missing ytcast-v*-checksums.txt \\\n       \u0026\u0026 tar -vxf ytcast-v*\"$target.tar.gz\" \\\n       \u0026\u0026 install -m 755 ytcast-v*\"$target/ytcast\" \"$dir\")\n\nif you run Arch Linux (btw I don't) you can get [`ytcast-bin` from the AUR][21]\n(many thanks to the maintainer)!\n\nif your os or architecture are not available, or you want to get the latest\nchanges from `master`, you can compile from source. a `go` compiler and `make`\nare required for building and installing:\n\n    $ git clone https://github.com/MarcoLucidi01/ytcast.git\n    ...\n    $ cd ytcast\n    $ make install\n    ...\n\n`make install` installs in `/usr/local/bin` by default, you can change `PREFIX`\nif you want, for example I like to keep my binaries inside `$HOME/bin` so I\nusually install with:\n\n    $ make install PREFIX=$HOME\n    ...\n    go build -trimpath -tags netgo,osusergo -ldflags=\"-w -s -X main.progVersion=v0.5.0-3-gd513b8e\" -o ytcast\n    mkdir -p /home/marco/bin\n    install -m 755 ytcast /home/marco/bin\n\nto uninstall run `make uninstall` (with the same `PREFIX` used for `install`).\n\n[20]: https://github.com/MarcoLucidi01/ytcast/releases/latest\n[21]: https://aur.archlinux.org/packages/ytcast-bin\n\nhow it works\n------------\n\nI've always been curious to know how my phone can find my TV on my home network\nand instruct it to start the YouTube on TV app and play a video right away\nwithout basically any manual pairing.\n\nI did some research and found about this nice little protocol called [DIAL\n(DIscovery And Launch)][30] developed by Netflix and Google which does the\ninitial part i.e. allows second-screen devices (phone, laptop, etc..) to\ndiscover and launch apps on first-screen devices (TV, set-top, blu-ray, etc..).\nthere is a 40 pages [specification][31] and a [reference implementation][32] for\nthis protocol.\n\nthe discovery part of DIAL is actually performed using another protocol, [SSDP\n(Simple Service Discovery Protocol)][33], which in turn is part of [UPnP][34].\n\nall this is not enough to play videos. once the YouTube on TV app is started by\nDIAL, we need some other way to \"tell\" the app which video we want to play\n(actually DIAL allows to pass parameters to an app you want to launch, but this\nmechanism is not used by the YouTube on TV app anymore).\n\nafter a little more research, I found about the YouTube Lounge api which is used\nby Chrome and the YouTube smartphone app to remotely control the YouTube on TV\napp. it allows to start playing videos, pause, unpause, skip, add videos to the\nqueue and more. the api is **not documented** and understanding how it works\nit's not an easy and fun job. luckily lots of people have already reverse\nengineered the thing (see [THANKS][35]) so all I had to do was taking the bits I\nneeded to build `ytcast`.\n\nthe bridge between DIAL and YouTube Lounge api is the `screenId` which as you\ncan imagine is an identifier for your \"screen\" (TV app). DIAL allows to get\ninformation about the current \"state\" of an app on a particular device. some\nfields of this state are required by DIAL, other fields are app specific (called\nadditional data). `screenId` is a YouTube specific field that can be used to get\na token from the YouTube Lounge api: with that token we can control the YouTube\non TV app via api calls.\n\nputting all together, what `ytcast` does is:\n\n1. search DIAL enabled devices on the local network (SSDP)\n2. get the state of the YouTube on TV app on the target device (DIAL)\n3. if the app is stopped, start it (DIAL)\n4. get the `screenId` of the app (DIAL)\n5. get a token for that `screenId` (Lounge)\n6. call the api's \"play video endpoint\" passing the token and the video urls to\n   play (Lounge)\n\n(there is a \"devices cache\" involved so `ytcast` won't necessarily do all these\nsteps every time, also if the target device is turned off, `ytcast` tries to\nwake it up with [Wake-on-Lan][36]).\n\nas you may have already guessed, all this **can stop working at any time!** the\nweakest point is the YouTube Lounge api since it's **not documented** and\n`ytcast` depends heavily on it. moreover, **`ytcast` may not work at all on your\nsetup!** I use and test `ytcast` with 2 devices:\n\n- Amazon Fire TV Stick\n- LG Smart TV running WebOS\n\nthat's all I have. `ytcast` works great with both these devices but I don't know\nif it will work well on setups different than mine (it should, but I don't know\nfor sure). if it doesn't work on your setup please [open an issue][37]\ndescribing your setup and attach a `-verbose` log so we can investigate what's\nwrong and hopefully fix it.\n\nalso **Chromecast**. I don't own a Chromecast and you'll probably need to use\nthe `-pair` option (see [workarounds][38]) to make `ytcast` work with Chromecast\nbecause it [doesn't use the DIAL protocol anymore, but switched to mDNS for\ndiscovery][39].\n\n[30]: http://www.dial-multiscreen.org\n[31]: http://www.dial-multiscreen.org/dial-protocol-specification/DIAL-2ndScreenProtocol-2.2.1.pdf\n[32]: https://github.com/Netflix/dial-reference\n[33]: https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol\n[34]: https://en.wikipedia.org/wiki/Universal_Plug_and_Play\n[35]: #thanks\n[36]: https://en.wikipedia.org/wiki/Wake-on-LAN\n[37]: https://github.com/MarcoLucidi01/ytcast/issues\n[38]: #workarounds\n[39]: https://en.wikipedia.org/wiki/Chromecast#Device_discovery_protocols\n\nBUGS\n----\n\n- sometimes the playing queue gets \"messed up\" i.e. some videos are added\n  between others, some videos don't get added at all or even an old queue might\n  be \"reused\" so you could see videos from an old session after the ones you\n  requested to play. unfortunately, using (or misusing) an undocumented api may\n  lead to these kinds of problems and I haven't bothered too much trying to fix\n  them.\n\n- the `-a` (add) option is slower because it does an api call for each video you\n  want to add and adds a random \"sleep delay\" *before* each call. without this\n  delay, the queue gets messed up more easily and videos get lost i.e. they\n  don't get added to the queue.\n\n- playing a video from a specific starting time (`t` parameter in urls) works\n  only for the *first* video and only if you are *not* using the `-a` (add)\n  option.\n\n- `ytcast` doesn't appear in `Settings \u003e Linked devices` menu. it used to show\n  up there and there was a button to \"unlink all devices\" which caused the\n  `screenId` to change, but a YouTube update \"broke\" this feature.\n\nworkarounds\n-----------\n\n- some devices don't support the DIAL protocol ([notably Chromecast][60]) so\n  they can't be discovered by `ytcast`. the YouTube on TV app has a [\"link with\n  code\" functionality][61] which can be used as workaround to pair `ytcast` with\n  these devices. the pairing code can be found in `Settings \u003e Link with TV code`\n  and then you can use the `-pair` option to do the pairing:\n\n      $ ytcast -pair 123456789101\n      8a59f138 unknown         \"YouTube on TV\"\n\n  once paired you can cast videos in the usual way:\n\n      $ ytcast -d 8a59 https://www.youtube.com/watch?v=t0Q2otsqC4I\n\n  when using this method, `ytcast` and the target device do *not* need to be on\n  the same network, but it adds many manual steps i.e. the YouTube on TV app\n  must be already open because `ytcast` won't be able to start it nor to\n  Wake-On-Lan the TV and it won't automatically \"re-pair\" when the `screenId`\n  changes (I don't know how often that happens).\n\n- playlist urls don't work with `ytcast`, I haven't found a reliable way to pass\n  playlist ids to the api. `youtube-dl` comes to the rescue (see also [other\n  tools][62]) since it can extract all video urls of YouTube playlists:\n\n      $ youtube-dl -j --flat-playlist https://www.youtube.com/playlist?list=PLrOv9FMX8xJHqMvSGB_9G9nZZ_4IgteYf | jq -r '.url' | ytcast -p\n\n  you can of course filter the pipeline as you like and this makes it so\n  flexible that I actually don't feel the need for `ytcast` to support playlist\n  urls: less is more!\n\n- this might sound obvious, but if you are tired of typing the device name (even\n  a substring of it) every time you want to cast something or if you have\n  multiple devices with the same name, you can define shell aliases and benefit\n  from shell tab-completion feature:\n\n      $ alias ytcbed=\"ytcast -d 'LG 32'\"\n      $ ytcbed https://www.youtube.com/watch?v=dQw4w9WgXcQ\n\n  (see your shell documentation to make aliases persistent, usually you have to\n  add them in the shell's `rc` file).\n\n[60]: https://en.wikipedia.org/wiki/Chromecast#Device_discovery_protocols\n[61]: https://support.google.com/youtube/answer/3230451\n[62]: #other-tools\n\nTHANKS\n------\n\nI would like to thank all the people whose work has helped me tremendously in\nbuilding `ytcast`, especially the following projects/posts:\n\n- https://0x41.cf/automation/2021/03/02/google-assistant-youtube-smart-tvs.html\n- https://github.com/thedroidgeek/youtube-cast-automation-api\n- https://github.com/mutantmonkey/youtube-remote\n- https://bugs.xdavidhu.me/google/2021/04/05/i-built-a-tv-that-plays-all-of-your-private-youtube-videos\n- https://github.com/aykevl/plaincast\n- https://github.com/ur1katz/casttube\n\nother tools\n-----------\n\nas I said earlier, my computing environment is very command-line centric and I'd\nlike to showcase the other tools I use to enjoy a \"no frills\" YouTube experience\nfrom the terminal!\n\n- [`youtube-dl`][40] (actually [`yt-dlp`][41] these days) doesn't need\n  introduction, it's an awesome tool and it's well integrated with [`mpv`][42]\n  so I can watch videos with my favorite player without having my laptop fan\n  spin like an airplane engine thanks to this `mpv` config:\n\n      ytdl-format=bestvideo[height\u003c=?1080][vcodec!=?vp9]+bestaudio/best\n\n- [`ytsearch`][43] is my clone of the initial version of [`ytfzf`][44]. it\n  allows to search and select video urls from the command-line using the\n  wonderful [`fzf`][45] (fun fact: it's implemented basically as a single big\n  pipe ahah). you have already seen it in action in `ytcast` examples, but it\n  works great with `mpv` too:\n\n      $ ytsearch matrix 4 | xargs mpv\n      $ ytsearch 9 symphony | xargs mpv --no-video\n\n- [`ytxrss`][46] allows to extract the rss feed url of a YouTube channel\n  starting from a video or channel url. I use rss feeds ([`newsboat`][47]) to\n  keep up-to-date with *things* and I'm really glad YouTube still supports them\n  for channel uploads. if I'm interested in a channel's future uploads, what I\n  usually do is:\n\n      $ ytxrss https://www.youtube.com/user/Computerphile \u003e\u003e ~/.newsboat/urls\n\n[40]: https://github.com/ytdl-org/youtube-dl\n[41]: https://github.com/yt-dlp/yt-dlp\n[42]: https://github.com/mpv-player/mpv\n[43]: https://github.com/MarcoLucidi01/bin/blob/master/ytsearch\n[44]: https://github.com/pystardust/ytfzf\n[45]: https://github.com/junegunn/fzf\n[46]: https://github.com/MarcoLucidi01/bin/blob/master/ytxrss\n[47]: https://github.com/newsboat/newsboat\n\n---\n\nsee [license file][50] for copyright and license details.\n\n[50]: license\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcolucidi01%2Fytcast","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcolucidi01%2Fytcast","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcolucidi01%2Fytcast/lists"}