BETA: Chrome Capture for Channels

Nice work! As far as your question goes, I don't think it's possible... yet?

1 Like

No matter what I try I get this message after chrome capture listening on Port 5589 if anyone has any ideas:


Error: spawn UNKNOWN
at ChildProcess.spawn (node:internal/child_process:420:11)
at Object.spawn (node:child_process:757:9)
at new Process (C:\Users\chrome-capture-for-channels\node_modules\puppeteer-stream\node_modules@puppeteer\browsers\lib\cjs\launch.js:143:87)
at launch (C:\Users\chrome-capture-for-channels\node_modules\puppeteer-stream\node_modules@puppeteer\browsers\lib\cjs\launch.js:80:12)
at ChromeLauncher.launch (C:\Users\chrome-capture-for-channels\node_modules\puppeteer-stream\node_modules\puppeteer-core\lib\cjs\puppeteer\node\ProductLauncher.js:90:54)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
errno: -4094,
code: 'UNKNOWN',
syscall: 'spawn'

Node.js v20.3.0

1 Like

@tmm1 any chance of deploying an ARM version for the M1?

Screen Shot 2023-06-20 at 6.27.16 PM

This is quite slick. It was very easy to get it working on the Mac, and within minutes I was tuned into the Weatherscan channel on a client.

I then tried to add a YouTube TV channel, thinking I could manually walk through the prompts by sitting in front of the machine I'm running this on (same machine I'm running the Channels DVR on). But when I try to enter my YouTube TV password, I get a "This browser or app may not be secure" message, and it won't let me log in.

I'll have to re-test, but I think nothing at all happens if the YouTube TV channel is the first channel I tune into, and the above message appears if I first go to the Weatherscan channel, and then try go to the YouTube TV channel.

So that was puzzling. If I close Chrome completely, and then try to run the channel, I get the spinning wheel for about 30 seconds, and then it loads . . . but if I look at the server machine, it is my HDMI for Channels version of the same channel loading (I can see the message in that app's Terminal window), not the one through Chrome Capture for Channels. But if I first launch the Weatherscan channel and go back to the guide, and then launch the YouTube TV channel, it tries to take me to the sign-in page as mentioned above (the window that Weatherscan was open in never closes, and then tries to load the YouTube TV channel in a tab, and asks me to sign in, but won't let me because it says the browser may not be secure).

EDIT: I should mention my Chrome version is Version 114.0.5735.133. This is on an older MacBook Air, so that might be the newest version it supports.


Simplified Installation Instructions (as of 2023-10-18 at 11:45am ET | v0.1.10)

Chrome Capture for Channels will open a Chrome browser and literally capture what is being shown on that tab and project it to Channels DVR. It works based on the browser's built-in capture functionality and other programs that have taken advantage of that in the past. It requires no interaction after initial setup and only will display a specific tab to Channels DVR. It does not take over your system as it is basically a background process, even when it is visible. This can be used as a workaround for DRM content, web-only content, or other similar uses. If it has a video or something you want to display on a webpage, it can work. It will only be valuable if you have legal access to the content.

(1) Install Chrome Capture for Channels
  1. Upgrade your Channels DVR server to the latest pre-release.
  1. Visit the Github repo to familiarize yourself with the programs and, potentially, to get the latest version of the Chrome Capture for Channels program: GitHub - fancybits/chrome-capture-for-channels: Capture video and audio from a Chrome tab

  2. Install the program.

  • NOTE #1: Windows/Mac executable is the preferred methodology, followed by Node if you need to make adjustments to the program file (see details below). Docker is not the best installation method due to performance issues, although some of these can be overcome as also discussed below.

  • NOTE #2: You should attempt your first installation using the default/easy methodologies. However, you may run into a situation where you need to use one of the advanced methods in order to overcome issues, such as not being able to get full-screen for a certain website or desiring to modify other settings like the video and audio bitrate. In these situations, there are changes you could make in the base files to customize your experience.

Example of advanced/optional editing...

An an example, a number of stations are already written in the code. If you go to the directory with the files and edit the one named "main.js", you will find something like this:

These are all of the default stations. If you have more you would like to add, you can do so here by adding in the same format {Station ID}: {Link to Screen to Display},. However, to be clear, this is unnecessary as there are simpler ways to add stations as detailed below. Further, this is an active development, so any changes you make to base code may be overwritten with an update. It is recommended to only modify the main.js file to resolve performance issues or try advanced settings. If you do so, you will have to follow the "Node" method for Windows/Mac or one of the advanced methods for Docker.



Follow the link to "latest release" and download the appropriate compiled (executable) program. Be sure to move it to a directory where you would like it to live permanently.

NOTE: If you are installing on a machine that is not your Channels DVR Server or if you may want to access the links/program directly from another machine without going through Channels, then you may need to open ports on your local machine. In order to do so, follow these directions [ Windows | Mac ] and open port 5589 for both TCP and UDP.

WINDOWS & MAC - NODE (Advanced):

NOTE: Once again, it is unnecessary to edit base files to add additional stations. See directions below.

You will want to follow the directions under the "development" section on the Git repo.

  • Open a command prompt and navigate to the parent directory above where you would like Chrome Capture for Channels to live.

  • Enter the following commands:


winget install -e --id Git.Git
winget install -e --id OpenJS.NodeJS
git clone
cd chrome-capture-for-channels
npm install


brew install nodejs git
git clone
cd chrome-capture-for-channels
npm install
  • Now, edit the main.js for any modifications you desire.

  • Once you have made changes to the main.js file, it will need to be launched without the benefit of an executable. This will be discussed in the next step of creating a launcher.


There are two options...


Enter the Docker command as shown in the directions at the Git repo or the version here:

docker run -d --restart=unless-stopped --name cc4c -p 5589:5589 fancybits/chrome-capture-for-channels:latest
  • STEP 1: Confirm Docker Swarm is not Active.

  • --- On a command line, enter docker info

  • --- Scroll down to "Swarm" and confirm it looks like this: image

  • --- If it says "Active" and has other information, enter the command docker swarm leave --force

  • STEP 2: Install Portainer (Docker Desktop | Docker Standalone)

  • STEP 3: In Portainer, navigate to Stacks and click + Add Stack. In the screen that comes up, give your Stack a name like cc4c, choose Web Editor, and paste the following code:

version: '3.9'
    image: fancybits/chrome-capture-for-channels:latest
    container_name: cc4channels
      - sh 
      - -c 
      - |
        Xvfb :99 -screen 0 1920x1080x16 &
        x11vnc -display :99 -forever &
        sed -i '/videoBitsPerSecond/c\        videoBitsPerSecond: process.env.VIDEO,' main.js;
        sed -i '/audioBitsPerSecond/c\        audioBitsPerSecond: process.env.AUDIO,' main.js;
        exec node main.js
      #- /dev/dri:/dev/dri # Uncomment for Intel Quick Sync (GPU) access
      - 5589:5589 # cc4channels proxy port
      - 5900:5900 # VNC port for entering credentials
      - VIDEO=${VIDEO}
      - AUDIO=${AUDIO}
      - TZ=${TZ} # Add your timezone in the Environment variables section with "name" set to TZ and "value" to your local timezone
      - cc4channels:/home/chrome # Creates persistent Docker Volume in /var/lib/docker/volumes for Chrome data and main.js
    restart: unless-stopped

It should now look something like this:

Scroll down most of the way and find the section called Environment variables. Here, click + Add an environment variable and create ones named VIDEO, AUDIO, and TZ. These variables allow you modify settings that are normally hardcoded in the program to impact your video and audio bitrate as well as timezone (link to list of values). The default settings are shown here:

However, you can modify them to test your own situation. For instance:

Scroll to the bottom and click Deploy the stack. This might take a couple of minutes, so be patient.

DOCKER (Advanced)

There are multiple ways to modify the base files in order to override the default settings and test if another one would work best for you. One option is that after you pull the repo that you can edit the main.js file directly. On the other hand, if you are using the Portainer Stack installation method, you could also create variables for any line. Just add another line like sed -i '/videoBitsPerSecond/c\ videoBitsPerSecond: process.env.VIDEO,' main.js;, replacing the setting you want to make variable and a name for the environmental variable. Similarly, be sure to add the environment variable to that section and then add the variable in the Stack as detailed above.

In either case, after these modifications, you can then just follow the easy directions again.

  1. You may need to revisit this section several times until you find the right configuration that works for you
(2) Create a Launcher and Confirm it is Running
  1. This is automatic; there is nothing to do.
  1. In the same directory you have the executable, create a new file called chrome-capture-for-channels-win-x64.ps1

  2. Edit the file and add this code:


Start-Process -WindowStyle hidden "C:\{PATH_TO_CHROME_CAPTURE}\chrome-capture-for-channels-win-x64.exe"


Start-Process -WindowStyle hidden "powershell" -ArgumentList "node 'C:\{PATH_TO_CHROME_CAPTURE}\main.js'"
  1. Be sure to edit the {PATH_TO_CHROME_CAPTURE} to your directory!

  2. Open Task Scheduler:


  1. Create a new task and follow the directions from the link below on how to setup a PowerShell task:

How to run PowerShell scripts from Task Scheduler

  1. Make sure you set it to:
  • Run only if the user is logged in (critical for now in order to not hide windows)
  • Run with the highest privileges
  • Triggered When user logs on and make sure it is the account you will run this under
  • Start the program powershell with these arguments: -ExecutionPolicy Bypass -File "C:{PATH_TO_CHROME_CAPTURE}\chrome-capture-for-channels-win-x64.ps1", but replacing the directory path with your own.
  1. Right click on the newly created task and click Run to execute it immediately and confirm it is working. If it is, you'll see it in Task Manager:


NOTE: If you are running the Node method, you will only see an additional Powershell and Node session running, not the CC4C executable.


Option 1

  1. Go to ~/Library/LaunchAgents on your Mac and create a file called

  2. Edit the file and add the following code:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

Substitute your own username for {YOUR_USERNAME} and the right path to Chrome Capture for Channels for {PATH_TO_CHROME_CAPTURE} in a format like /path/to/chrome-capture-for-channels in each of the places these are used.

  1. Execute it by running:
launchctl load ~/Library/LaunchAgents/
  1. You can remove / stop the service with (but should not need to):
launchctl remove
  1. Logs will be in chromecapture.log in the main Chrome Capture for Channels directory.

Option 2

  1. See: Platypus - Create Mac apps from command line scripts | for a third-party tool that can assist in this.
  1. If you use a different bootstrapping methodology, you should do that.


  1. Open your web browser and go to http://localhost:5589, replacing localhost with the IP address of the machine you installed everything on if it is not the one you are currently using.

  2. Confirm that you see this:


  1. If you do so, then the capture program is up and running.
(3) Create Default and Add Additional Stations in Channels DVR
  1. Create a new Custom Channels: Channels Support - Add Custom Channels with M3U Playlists

  2. You have many options, but you will end up with something like this:

  • Nickname: Whatever you want to call it, but you cannot change it
  • Stream Format: MPEG-TS is recommended as it appears to be a bit faster with less hiccups, but HLS is fine too. You should test what works best for your situation.
  • Source: Text, see next sub-step on what to put there
  • Options: Here you can decide how you want station numbers to be handled. You could even put in code to put a channel number in the source above/below if you want to match to the old TVE stations. Either way, you should probably Prefer your logos from Guide Data. Again, you don't have to; you can provide your own.
  • XMLTV Guide Data: Don't worry about this, you can ignore it.
  1. In the text field you will create the Stations as they will appear in Channels. You can copy and paste this for all of the built-in stations:

#EXTINF:-1 channel-id="Windy",Windy

#EXTINF:-1 channel-id="Weatherscan",Weatherscan

#EXTINF:-1 channel-id="NBC News NOW",NBC News NOW

#EXTINF:-1 channel-id="NBC Local",NBC Local ({YOUR STATION ID HERE})

#EXTINF:-1 channel-id="Bravo (East)",Bravo (East)

#EXTINF:-1 channel-id="Bravo (West)",Bravo (West)

#EXTINF:-1 channel-id="CNBC",CNBC

#EXTINF:-1 channel-id="E! (East)",E! (East)

#EXTINF:-1 channel-id="E! (West)",E! (West)

#EXTINF:-1 channel-id="Golf",Golf

#EXTINF:-1 channel-id="MSNBC",MSNBC

#EXTINF:-1 channel-id="Oxygen (East)",Oxygen (East)

#EXTINF:-1 channel-id="Oxygen (West)",Oxygen (West)

#EXTINF:-1 channel-id="SyFy (East)",SyFy (East)

#EXTINF:-1 channel-id="SyFy (West)",SyFy (West)

#EXTINF:-1 channel-id="USA (East)",USA (East)

#EXTINF:-1 channel-id="USA (West)",USA (West)

#EXTINF:-1 channel-id="Universo (East)",Universo (East)

#EXTINF:-1 channel-id="Universo (West)",Universo (West)

#EXTINF:-1 channel-id="NECN",NECN

#EXTINF:-1 channel-id="NBC Sports Bay Area",NBC Sports Bay Area

#EXTINF:-1 channel-id="NBC Sports Boston",NBC Sports Boston

#EXTINF:-1 channel-id="NBC Sports California",NBC Sports California

#EXTINF:-1 channel-id="NBC Sports Chicago",NBC Sports Chicago

#EXTINF:-1 channel-id="NBC Sports Philadelphia",NBC Sports Philadelphia

#EXTINF:-1 channel-id="NBC Sports Washington",NBC Sports Washington

Be sure to change things like {YOUR STATION ID HERE} to its ID (i.e., WJAR) If you don't want one, you can just remove it with no issue. If you want to leave them all and manage through the the favorite/hide functionality, you can do that, too. If you created your own station in the main.js file from before, you can add it here with the same {Station ID}. If you want this program running on a machine that is different than your Channels server, then you can change "localhost" to the IP address of that machine. If you want to add any of the other options discussed above or other similar things, you are free to do so.

NOTE: Some users are unable to use the built in stations. For those people, use the URL Method as shown next. If you are having issues with the URL Method, see Troubleshooting / Common Issues below.

More so, you can add any station you want that is not built in. Just add another record that looks like this:


Replace the {YOUR STATION NAME} and {LINK} fields to the ones you would like, so something like this:


There is no limit to how many of these you could to add! NOTE: Each Custom Channels m3u is limited to 750 stations, but there is no limit to the number of Custom Channels you can create.

  1. Click Save to finish.
(4) Map Stations

Depending upon a number of factors, the stations may already be mapped correctly or not. If they are not...

  1. Click the gear icon next to your new Custom Channel and select Manage Lineup.


  1. For each station, click the + icon the right and in the dropdown change the search to Search All Lineups.


  1. Search for what you are looking for and select the most appropriate one. This can be tricky with some stations, so don't worry if you have to try a few different options to get it right.


  1. You will now end up with something like this:

  1. Close out with X and either let the guide data update naturally or force an update.
(5) First Time Launch and Services Login

You now should be able to launch any station, though I recommend doing so on a client and not in the Web UX.

  1. For your first test, you should do so by selecting Windy and/or Weatherscan to confirm functionality and familiarize how it looks when a station is launching. There will be a delay as Chrome launches, the page loads, and everything maximizes. NOTE: If nothing happens or you see errors, review "Troubleshooting / Common Issues" below before proceeding.

  2. Launch a DRM station from the NBC family, like CNBC, MSNBC, Golf, or the like.

  3. When you do so, you will see it asking you to log in to your provider. Quickly return to your server.

  4. On your server, select the newly opened Chrome browser and you can interact with it to log in and authenticate with your provider.

  • If you are running a Docker, your first option is to open a new Chromium-based browser and go to http://localhost:5589/debug?url=, replacing "localhost" with your IP address if they are different servers. This will give you a slow but partially interactive window to work in to do the login steps the same as Windows or Mac below.

To reiterate, the debug mode is slow and not very responsive. It may require launching it several times in a row in order to get it to work. That said, even if you do get it to work, there is a current bug where the following characters do not work: . / \ # % ? DEL BACKSPACE SPACE. If you need a more interactive method and you have deployed via the Portainer Stack method, you can do so with VNC.

First, make sure you have downloaded a VNC Client. Open the client and go to the location of your server and port 5900, i.e. localhost:5900:


Once connected, you will be in a virtual desktop with a regular Chrome browser that you can interact with:


There may be a slight delay on keystrokes, but all will be accepted.

NOTE: Either of these can be used for logging in or interacting with any any service, this is just the core example.

  • Go through the login process as normal. After this, your credentials will be remembered.

  • If you receive any messages such as "Error 15" during login and it does not allow you to reach a login page, then follow these steps:

  • --- In the same open browser (or in the interactive debug method shown above), open a new tab and go to the direct login page of your provider (i.e.,

  • --- Login as normal. Although you will end up back at the error page, your credentials will now be saved.

  • --- Close that tab and return to the tab with the content. Try to authenticate again. It should pass through.

  1. Return to your client and close the station. Open another one and now you should receive it with no issue.

  2. Repeat if you are using stations from another provider (i.e., Disney/ABC) or have something else you have added.

(6) Upgrade | Stay up to Date

Set up a watch on the Github repo. This way, you'll get an email whenever a new version is released, plus you can see what has changed.


When a new version becomes available, you can easily upgrade. If you are running on a Windows/Mac, just end the process for the executable (in Windows, you can do this in Task Manager), go to the Git repo, download the latest executable, overwrite the file you already have, and start it back up again. If you are running the Docker, do a re-pull.

(7) Notes and Other Considerations
  1. This is a work in progress with updates coming regularly to fix issues and improve performance.

  2. Due to the various coded wait periods and the general lag, you will need a buffer on any recording. I would recommend a couple of minutes on each end.

  3. There is no reason you cannot open multiple stations at the same time. However, system performance may be severely impacted with the more tabs that are opened.

  4. Despite warnings above, you can use this in the Web UX. It just isn't quite as responsive as the clients and may cause performance issues.

  5. On Windows and Mac machines, the Chrome browser window will stay open even after the stream disconnects. You can close it with no issue once nothing else is displayed there anymore.

(8) Troubleshooting / Common Issues
  • If you are seeing black bars border or cropped windowed video: Adjust your display settings to be at least 1920x1080 or install an HDMI Dummy Plug to do the same. On some Macs, it appears necessary to be more than 1920x1080. WARNING: on a Mac, higher resolutions takes slightly more memory consumption.

  • I am still not getting full screen for specific or all stations: Certain providers/websites appear a bit more difficult to get full-screen to work automatically. For instance, if you use YouTubeTV or go to the NFL Network's website, it may not work as expected. If this is your situation, you will have to follow the ADVANCED/OPTIONAL directions above and edit the main.js file to include this code:

// Trigger fullscreen mode using the Fullscreen API
document.documentElement.requestFullscreen(); // Pink highlight here

It should be placed at this location in the file:

  • My provider does not provide a direct link to a station that I want to use: A direct link is necessary at this time.

  • The built in stations do not work for me: Switch to the direct link method as detailed above.

  • My link works in my browser but not in CC4C: You may need to replace special characters with the correct URL reference. For instance, & would be replaced with %26. For a complete list of replacement codes, see: HTML URL Encoding Reference

  • Chrome launches, but the tab does not open a station: A bug exists in Chrome 115 that has since been resolved. Although the bug is specific to Channels, it is easily overcome by making sure Chrome is at version 116 or above. If you cannot upgrade to version 116 or above, you must downgrade Chrome to 114.0.5735.199 and keep it at that version to prevent any issues...

  • -- How to Downgrade:

  • -- Version to Downgrade to: Download Google Chrome 114.0.5735.199 for Windows |

  • I cannot pause, rewind, fast forward, etc... only the stations from CC4C: This issues is also related to the version of Chrome. Upgrade or Downgrade per the directions above.


Thank you and the CHDVR team for a great addition and for such detailed and easy to follow instructions. This is a great community.


The YouTube/Google message regarding an insecure browser doesn't appear to be due to the age of the machine/Chrome version. I just installed Chrome Capture for Channels on my m1 Mac, and I get the same message about an insecure browser. This also happened when I tried to authenticate CNBC using my YouTube TV credentials.


Seems maybe related to certain antivirus programs.

Thanks for the install writeup (and the code, thanks devs!!) but where is the main.js kept in a Win10 Docker install if you want to add channels? I've seen various explanations of where Docker containers are kept on Windows.

Strange, I tried on my M1 mac and w/ chrome 114.0.5735.133 it works fine on YTTV.


What do you mean by crashes? Does it stop running and go back to the prompt? And you can't access localhost:5589 anymore?

The application stops running completely.


Yep. Exactly. Have to restart the app.

1 Like

Maybe you need to run it in the cmd.exe console so you can see the messages when it dies. It sounds like you're double clicking right now, and when it crashes the window disappears so you can't see what the crash message was?


The docker i'm running on my Synology is working great.
I'm thinking of deploying the docker desktop on the Windows machine.
I want to see if things are different.


@cyberskier can you try the latest main.js to see if there is any improvement with YTTV


I’m also running the Docker on MacOS, however how did you authenticate DRM channels?

Hi there,
At this point, the docker can't log into websites.
I've been giving this some thought.
How about setting it up like we do in ESPN Plus where it is authorized from the website.
The page presents a series of numbers, the instance of Chrome can retain the login through the use of a token.
As far as Youtube tv or any other service is concerned, the device is either a television or a browser.

Ran the chrome-capture-for-channels-win-x64.exe in a CMD window.
throw er; // Unhandled 'error' event

Error: listen EADDRINUSE: address already in use :::5589
at Server.setupListenHandle [as _listen2] (node:net:1422:16)
at listenInCluster (node:net:1470:12)
at Server.listen (node:net:1558:7)
at Function.listen (C:\snapshot\chrome-capture-for-channels\node_modules\express\lib\application.js:635:24)
at main (C:\snapshot\chrome-capture-for-channels\main.js)
at Object. (C:\snapshot\chrome-capture-for-channels\main.js)
at Module._compile (pkg/prelude/bootstrap.js:1926:22)
at Module._extensions..js (node:internal/modules/cjs/loader:1166:10)
at Module.load (node:internal/modules/cjs/loader:988:32)
at Module._load (node:internal/modules/cjs/loader:834:12)
Emitted 'error' event on Server instance at:
at emitErrorNT (node:net:1449:8)
at processTicksAndRejections (node:internal/process/task_queues:82:21)
at process.runNextTicks [as _tickCallback] (node:internal/process/task_queues:64:3)
at Function.runMain (pkg/prelude/bootstrap.js:1980:13)
at node:internal/main/run_main_module:17:47 {
errno: -4091,
syscall: 'listen',
address: '::',
port: 5589

Node.js v18.5.0

then when attempting to stream it quit or exited w/o any other error messages in the cmd window.

the the channels error log displayed:
2023/06/20 21:21:17.945670 [TNR] Opened connection to M3U-ChromeCaptureforChannels for ch12565 Windy
2023/06/20 21:21:17.954013 [HLS] Starting live stream for channel 12565 from
2023/06/20 21:21:23.394857 [HLS] ffmpeg: chrome-Windy: [tcp @ 0000000001293b80] Connection to tcp:// failed: Error number -138 occurred
2023/06/20 21:21:23.394857 [HLS] ffmpeg: chrome-Windy: Unknown error
2023/06/20 21:21:23.410217 [ERR] Error during stream M3U-ChromeCaptureforChannels ch12565 Windy: exit status 1
2023/06/20 21:21:23.410217 [TNR] Closed connection to M3U-ChromeCaptureforChannels for ch12565 Windy
2023/06/20 21:21:23.410726 [HLS] ffmpeg: ch12565-dANY-ip127.0.0.1-remux: Output file #0 does not contain any stream
2023/06/20 21:21:23.465047 [HLS] Couldn't generate stream playlist for ch12565-dANY-ip127.0.0.1: Stream stopped
2023/06/20 21:21:23.465047 [HLS] Stopping transcoder session ch12565-dANY-ip127.0.0.1
2023/06/20 21:21:23.686616 [ERR] Probe failed for live stream after 5.7326023s and 0 bytes

1 Like