palmarci's blog

a random blog mostly for myself, so i can remember stuff

Fixing the YouTube TV interface on PC


The source code is available on my GitHub.

You probably know that lots of smart TVs use YouTube's HTML TV interface, found at youtube.com/tv. You could easily open this up on your PC web browser until September 2019, when YouTube deprecated it. After a few weeks, some people on Reddit discovered that you could get around this restriction by tweaking the User-Agent HTTP Header to act like a smart TV. Even though there are already plenty of web browser extensions out there that work like this, most of them use a hardcoded user agent string from an older TV that usually locks the video resolution to 720p.

Looking around on the internet will suggest that changing the old TV's user agent to mimic a newer video game console, like a PS4 or Xbox could enable us to have extra features, like HQ video streaming. Testing this idea made me download and inspect a random extension from the Addon Store.

Extracting the code

First step is to download the extension's package. We can just right click and copy the link for the extension on Mozilla's webstore.

  1. wget https://addons.mozilla.org/Firefox/downloads/file/3420768/youtube_for_tv-0.0.3.xpi
  2. Running file will tell us that it's in a simple ZIP format: Zip archive data, at least v2.0 to extract, compression method=deflate
  3. Simply extracting it with 7-zip:
    7z x youtube_for_tv-0.0.3.xpi -oaddon
  4. Opening up the extracted background.js file will reveal a very simple function:
function rewriteUserAgent(x) {
    for (var header of x.requestHeaders) {
        if (header.name.toLowerCase() === "user-agent") {
            header.value = "Mozilla/5.0 (SMART-TV; Linux; Tizen 5.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/5.0 NativeTVAds Safari/538.1";
        }
    }
    return {
        requestHeaders: x.requestHeaders
    };
}
browser.webRequest.onBeforeSendHeaders.addListener(rewriteUserAgent, {
    urls: ["*://*.youtube.com/*tv*"]
}, ["blocking", "requestHeaders"]);

60 FPS problems

Changing the User-Agent string will result in a slightly different, more modern looking web UI, but upon opening a 4K test video, the resolution was still 720p. After a lot of searching around, I read somewhere that zooming out of the webpage will fix this problem. Sure enough, after zooming to 50% with Ctrl + - and opening a new tab fixed the issue. Interestingly, while you change the zoom level, the website more or less stays the same, unlike other, static websites. This tells me that maybe the the system's DPI setting can influence the calculation of the allowed quality settings.

Interestingly, checking out another test video resulted in the same 720p limit, but after a bit of confusion it occured to me: 60 fps videos will always cause the player to output low-definition. This may be due to the player trying to use hardware-accelerated video codecs to gain performance on the TVs and failing back to a safe quality.

New extension

After this discovery, I still wanted to bump up the resolution to at least to 1080p, so I started to add code to the old extension. Since this rabbit-hole could have been easily prevented if I could simply just change the user agent string without editing the source code, I implemented that feature too. Also, we can use the web browser APIs to influence the zoom level:

function applyZoom() {
    chrome.tabs.query({
        active: true,
        currentWindow: true
    }, (tabs) => {
        const activeTab = tabs[0];
        const url = new URL(activeTab.url);
        if (!(url.host.includes("youtube.com") && url.pathname.startsWith("/tv"))) {
            return;
        }
        if (currentConfig.overrideZoom) {
            chrome.tabs.setZoom(activeTab.id, (currentConfig.zoomLevel / 100));
        }
    })
}

Don't worry about the chrome namespace in the code, it also works fine on Firefox and is an intended, compatibility function of the API. Read more about this here.

After removing the now invalid signatures in the META-INF directory, we can just rebuild the extension:

7z a ./temp_addon.zip ./addon/*

After a bit of research the mainline version of Firefox will not allow you to install unsigned extension permanently, but there is an option to load and "debug" extensions until the browser is not restarted. This allows me to create a working extension what I will be able to submit into the Mozzila Extension Store in the future, hopefully.