Skip to content

YouTube Channel

Austin Nunn edited this page Nov 9, 2026 · 1 revision

YouTube Channel

This adds a channel that is YouTube's TV app. You cast to it from the YouTube app on your phone, the same way you'd cast to a smart TV. Whatever you cast plays on the channel, in Emby or Jellyfin and on a remote player.

It works like the weather channel. A container called ytcast runs YouTube's TV page (youtube.com/tv) in Firefox on a hidden screen, and records the picture and sound as a video stream. A small streaming server, MediaMTX, hands that stream to the wrapper, which adds it as a channel.

graph TD
  phone[Phone: YouTube app, Cast]
  ytcast[ytcast: YouTube TV app in Firefox, recorded]
  mediamtx[MediaMTX: serves the stream]
  wrapper[Station Wrapper 43: Sources page]
  emby[Emby or Jellyfin]
  phone --> ytcast --> mediamtx --> wrapper --> emby
Loading

Good to know before you start:

  • It runs all the time. Expect about a quarter of a CPU core when nothing is playing and about one core while a video plays, plus 600–800 MB of RAM. An Intel GPU can take over the encoding (see below).
  • YouTube only shows its TV app to TVs. A tiny Firefox extension that comes with ytcast tells the page it's talking to a TV. Google can change this at any time. If the channel suddenly shows YouTube's normal website or an error, that's why.
  • Normal videos play. Paid movies and other DRM videos don't.
  • Without a YouTube Premium account you get YouTube's ads, just like on a TV.

These steps use 192.168.1.20 for the server, like the other guides.

1. Get the files

ytcast isn't a ready-made image: Docker builds it on your server from a folder in this project. Copy that folder next to your compose file:

cd ~
git clone --depth 1 https://github.com/austincnunn/station-wrapper-43.git sw43
cp -r sw43/extras/ytcast ~/fieldstation42/ytcast
rm -rf sw43

2. Add MediaMTX and ytcast to the compose file

Add these two services under services: in ~/fieldstation42/docker-compose.yml, next to the wrapper. (The same text is in ytcast/compose-snippet.yml.)

  mediamtx:
    image: bluenviron/mediamtx:latest
    container_name: mediamtx
    restart: unless-stopped
    ports:
      - "8888:8888"   # the stream, for the wrapper and players
    environment:
      MTX_HLSVARIANT: mpegts
      MTX_HLSALWAYSREMUX: "yes"
      MTX_RTSPTRANSPORTS: tcp

  ytcast:
    build: ./ytcast
    container_name: ytcast
    restart: unless-stopped
    shm_size: 1g        # needed: Firefox crashes with Docker's small default
    mem_limit: 3g
    volumes:
      - ./data/ytcast/profile:/profile   # keeps your phone pairing and sign-in
    environment:
      PUBLISH_URL: rtsp://mediamtx:8554/youtube
    depends_on:
      - mediamtx

Build and start them. The first build takes a few minutes:

cd ~/fieldstation42
docker compose up -d --build

Check that the stream is there. After about half a minute this should print HTTP/1.1 200 OK:

curl -sI http://192.168.1.20:8888/youtube/index.m3u8 | head -1

3. Add it as a channel

  1. In the dashboard, open Sources and click Add a source.
  2. Type: a single stream.
  3. Address: http://192.168.1.20:8888/youtube/index.m3u8
  4. Mode: Convert.
  5. First channel number: pick one, like 107.
  6. Click Test the address if you like, then save.

Tune to the channel. You should see YouTube's welcome screen: Watch the latest videos, just for you.

4. Link your phone

There's no remote, so you press the TV app's buttons from the server with xdotool. Each command below sends key presses into the hidden screen. Keep the channel on so you can watch what happens.

First, get past the welcome screen. Press Get started:

docker exec -e DISPLAY=:99 ytcast xdotool key Return

Now you choose who watches:

  • To sign in with your YouTube account (your subscriptions, and no ads if you have Premium): the screen shows a code and yt.be/activate. On your phone, go to that address, type the code and pick your account.

  • To watch without an account: press Down twice, then Enter, to pick Watch as guest:

    docker exec -e DISPLAY=:99 ytcast xdotool key --delay 300 Down Down Return

Then open Settings → Link with TV code (left menu, bottom item, then the 7th line):

docker exec -e DISPLAY=:99 ytcast xdotool key --delay 300 Left Down Down Down Down Down Down Down Down Down Down Down Down Down Right Down Down Down Down Down Down Return

A 12-digit code appears. (If the menu moved and you land somewhere else, take a picture of the screen as shown below, and use the arrow keys Left, Up, Down, Right, Return and Escape to find Settings → Link with TV code yourself.) In the YouTube app on your phone, tap the Cast button, then Link with TV code, and type it in. The code only lasts a few minutes. If it runs out, press Up, Down and Enter for a fresh one:

docker exec -e DISPLAY=:99 ytcast xdotool key --delay 400 Up Down Return

That's it. Next time, open YouTube on your phone, tap Cast and pick the TV. Pairing and sign-in are saved in data/ytcast/profile, so they survive restarts and updates. Several phones can link the same way.

Can't see the channel while you do this? Take a picture of the hidden screen instead:

docker exec -e DISPLAY=:99 ytcast ffmpeg -loglevel error -y -f x11grab -video_size 1280x720 -i :99 -frames:v 1 /tmp/screen.png
docker cp ytcast:/tmp/screen.png .

Optional: use an Intel GPU

On a PC with Intel graphics, the video encoding can run on the GPU instead of the CPU. Only x86 PCs, not a Raspberry Pi. First find the GPU's "render node". If you have more than one GPU, ls -l /dev/dri/by-path/ shows which is which (00:02.0 is the one built into the CPU):

ls -l /dev/dri/by-path/

Then add this to the ytcast service and run docker compose up -d again:

    devices:
      - /dev/dri/renderD128
    environment:
      PUBLISH_URL: rtsp://mediamtx:8554/youtube
      VAAPI_DEVICE: /dev/dri/renderD128

Pass in only the GPU you want ytcast to use.

Settings

All optional, under the ytcast service's environment::

Setting Default What it does
SCREEN 1280x720 The size Firefox draws at
OUT_SIZE 854:480 The size that gets streamed
FPS 60 Frames per second. 60 looks smoother on an old CRT
VIDEO_KBPS 2500 Video bitrate
VAAPI_DEVICE (empty) An Intel GPU to encode on, like /dev/dri/renderD128

The picture is widescreen (16:9), like your other channels. A remote player on a 4:3 TV crops the sides the same way it does for everything else.

Updating

Firefox gets updates when the image is rebuilt:

cd ~/fieldstation42
docker compose build --pull ytcast && docker compose up -d ytcast

To update ytcast's own files, copy extras/ytcast again (step 1), then run the same two commands.

If something's wrong

  • The channel doesn't play: run docker logs ytcast. Warnings about dbus, a11y and Sandbox are normal. Then check the stream with the curl command from step 2.

  • The picture is jerky: this command shows how many frames ffmpeg had to copy because the screen recording fell behind. Run it twice, a minute apart. The number should barely change:

    docker exec ytcast grep dup_frames /tmp/ffmpeg-progress

    If it jumps by thousands, the server is too busy or something else is wrong. Check docker logs ytcast.

  • "Firefox is already running": the profile folder isn't writable. ytcast fixes its owner on every start, so restart it: docker restart ytcast.

  • The TV app shows the profile picker: press Enter to pick the highlighted profile: docker exec -e DISPLAY=:99 ytcast xdotool key Return.

  • Typing while a phone is casting can open the TV app's "Ask about this video" panel or pause the video. Escape closes the panel, and Return starts the video again.

Clone this wiki locally