Library / SDK
nonbili/NouTube avatar
nonbili/NouTube

NouTube: a YouTube client built on a wrapped web view

YouTube and YouTube Music in a single app. No ads, plays in the background.

1,443 stars106 forksTypeScriptAGPL-3.0

At a glance

What is it?
NouTube is an open source client for YouTube and YouTube Music that runs on Android, iOS, and desktop, and it is implemented by wrapping m.youtube.com and music.youtube.com in a web view, injecting code to block ads, and hooking the playback controls so audio keeps playing in the background. The whole approach is three lines long and everything else follows from it.
Who is it for?
Adopt NouTube if you want an open source YouTube client with background playback and no ad slots, and you are comfortable with an app that depends on the upstream web interface. Do not adopt it if you need a guaranteed API, since a web view based client breaks whenever the site changes.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three lines of architecture

The How it Works section is the shortest and most revealing part of the README.

You wrap https://m.youtube.com and https://music.youtube.com in an Android web view. You inject code to block ads. And you hook playback controls and support playing in the background.

That is the entire implementation strategy, and it explains every other decision in the project. Because the app is a web view rather than a native client, it does not reimplement YouTube's API, does not maintain its own model of the site, and does not have to reverse engineer a private endpoint. It renders the site the site expects, and then it changes a few behaviours from the inside.

The background playback feature is the clearest example of that approach. The reason a normal YouTube app stops when you leave it is a mobile web restriction, and since the content is a web view, hooking the playback controls is the way to keep audio going. The same reasoning applies to the ad blocking, which is done by injecting code that suppresses the ad elements rather than by negotiating with an ad server.

The consequence is that this class of client inherits the fragility of the web interface. A change to the mobile site can break the injection, and there is no server side to coordinate with. The project is explicit about not being affiliated with any .org websites, which is the disclaimer that belongs to a client built this way.

What the app actually does

The feature list is short and each item is a specific behaviour rather than a category.

There is no ads, and it plays in the background. You can manage the video and music library without logging in, and manage the watch history without logging in. You can download video. There is an RSS feed reader for YouTube channels, which is the feature that makes the app useful for tracking subscriptions without an account. You can hide shorts. There is live chat. You can play the original audio, which is the YouTube Music catalogue as opposed to the video audio, and you can customise with CSS.

The without login features are the interesting ones, because they are what a web view can do that a logged-out session normally cannot. The library and the history are tied to the account on Google's side, and the app exposes them without requiring the user to sign in, which is a different arrangement from the official client.

The CSS customisation is the feature that most directly follows from the web view approach. Since the app is rendering web pages, a stylesheet can be applied to them, and that turns the client into something that can be themed to your own preferences in a way a native app with a fixed design system cannot.

The download feature is listed plainly, and it is worth noting that the project does not frame it as a workaround or hide it among the other items.

One repository, four shipping targets

The distribution story is unusual, and it is split across more than one place.

The mobile app is on F-Droid as jp.nonbili.noutube, with a GitHub releases badge for downloading the APK directly, and there is a TestFlight link for the iOS beta. The Linux, macOS, and Windows versions are in a separate repository, NouTube-Desktop. And there is a browser extension for Firefox and Chrome, listed in the Firefox Add-ons and the Chrome Web Store.

So the main repository here is the mobile app and the shared core, and the desktop builds live in their own repository. That is a deliberate arrangement, since a desktop build of a web view client and a browser extension are different products with different toolchains, even though they share the injected script.

The repository layout makes the shared part visible. There is a content/ directory whose main.ts is the entry point of the injected script, an assets/scripts/ output path for the bundled result, a desktop/ and an extension/ directory, and a modules/, lib/, components/, and plugins/ set for the app code. There is also a flatpak/ directory, a fastlane/ directory with fastlane-config/ beside it for release automation, and a Gemfile with a Gemfile.lock, which is the standard fastlane setup. The locales/ directory and a metadata/ directory holding per-locale screenshots and descriptions sit alongside, and translation is handled through Weblate.

The build is a web script, not a native bundle

The package manifest explains the build in a way that is unusual for a mobile app, and it is worth reading because it shows where the real logic lives.

The root package.json declares two workspaces, desktop and extension, and the app itself is private with the main entry point at index.js. The scripts are the interesting part:

json
"bundle": "bun build --target browser content/main.ts --outfile assets/scripts/main.bjs",
"bundle:es": "esbuild --bundle content/main.ts --minify --outfile=assets/scripts/main.bjs",
"dev": "bun bundle --watch",
"start": "expo start",
"android": "expo run:android --variant fullDebug",
"ios": "expo run:ios",
"web": "expo start --web",
"lint": "expo lint"

So the injected code, which is the part that does the ad blocking and the playback hooking, is bundled as a browser target script and written into assets/scripts/main.bjs. The two bundlers are Bun and esbuild, and the esbuild variant is the minified one. The dev script watches and rebuilds that script, which is the edit and refresh loop for anyone working on the injection.

The app around it is Expo, and the platform commands are the standard Expo ones, with an android variant of fullDebug. The version is 0.7.3 with a versionCode of 83 and a buildNumber of 5, which is the triple versioning scheme mobile app stores expect, where the version is human readable, the version code is the integer that must increase, and the build number distinguishes rebuilds of the same version.

The dependency list is also telling. There is cheerio, an HTML parser, used to parse the pages the client works with, and @tanstack/react-query for data fetching state, and @supabase/supabase-js, and @legendapp/state and react-query for the state layer, and dayjs and es-toolkit as utility libraries, and a long list of expo modules covering everything from notifications and background tasks to file system, sharing, and localisation.

Licence, and what the AGPL implies for the extension

The project is licensed under AGPL-3.0, and that choice has consequences for the way the pieces are distributed.

The AGPL is the copyleft licence for network software. Its distinguishing feature is the network clause, which means that if you modify the program and run it over a network, you must offer the corresponding source to the users of that modified version. For a client application that most people run against their own copy of the service, the practical effect is that the source of the whole client, including the injection and the web view wrapper, has to stay available.

That matters here because the project ships several separate artefacts from the same codebase. The mobile app, the desktop builds, and the browser extension each carry the same injection, and the AGPL is the mechanism that keeps them all honestly licensed. A permissive licence would have allowed closed forks of the web view wrapper, which is the part that does the actual work of blocking ads and keeping playback alive.

The project also states in the README that it is not affiliated with any .org websites, which is the standard disclaimer for a third party client of a large platform and an acknowledgement that the trademarks and the site belong to someone else. The licence and the disclaimer together are the two things to read before deciding whether you can deploy this, for example inside a company, where redistribution rules and trademark questions have a different weight than they do for an individual installing from F-Droid.

The failure mode to plan for

The obvious weakness of this design is that the client is coupled to a web interface that can change without notice. Everything the app does beyond showing the site is implemented as an injection into pages that Google controls, and the injected code is written against the current shape of those pages.

That is not a hypothetical risk for this category of software, it is the normal lifecycle of it. A site redesign or a change to the ad serving path can break the injection, and the symptom is a client that shows ads again or that stops playing in the background. There is no version negotiation and no server side to coordinate with, because there is no server side.

The mitigation is the release cadence, and the release history shows one. The recent versions are v0.7.3 on 2026-10-12, v0.7.2 on 2026-10-11, and v0.7.1 on 2026-10-07, and the last push to the repository was on 2026-10-12. Patch releases within days of each other is the signature of a project that has to follow the upstream site, and the version staying below 1.0 says the same thing about the API surface being expected to change.

The second limitation is the one the web view implies about features. The library and history management without login works because the app holds its own session state, and that is a different security and privacy arrangement from the official client. An application that reads your browsing data through a web view it controls is asking for a level of trust, and the reason to accept it is that the source is open and the injection is auditable, which is exactly what the AGPL and the public repository are for.

Editorial conclusion

Adopt NouTube if you want an open source YouTube client with background playback and no ad slots, and you are comfortable with an app that depends on the upstream web interface. Do not adopt it if you need a guaranteed API, since a web view based client breaks whenever the site changes. Verify first that the platform you want is actually shipped, since the desktop builds live in a separate repository and the browser extension is a different deliverable from the app.

Frequently asked questions

How does NouTube work?

It wraps https://m.youtube.com and https://music.youtube.com in an Android web view, injects code to block ads, and hooks the playback controls to support playing in the background. The project states it is not affiliated with any .org websites.

Where can I get NouTube?

The mobile app is on F-Droid as jp.nonbili.noutube, with an APK on the GitHub releases page and an iOS beta on TestFlight. The Linux, macOS, and Windows versions are in the separate NouTube-Desktop repository, and there is a browser extension for Firefox and Chrome.

Which features does NouTube provide beyond the web interface?

Background playback, no ads, managing the video and music library and the watch history without login, video download, an RSS feed reader for YouTube channels, hiding shorts, live chat, playing the original audio, and customisation with CSS.

How is the NouTube injection script built?

With Bun or esbuild, targeting the browser, from content/main.ts into assets/scripts/main.bjs. The dev script bundles in watch mode so the injected script rebuilds as you edit it, and the app around it is an Expo project with the usual platform commands.

What licence does NouTube use and what does it mean?

AGPL-3.0, the copyleft licence for network software. Its network clause means that a modified version run over a network must offer the corresponding source to its users, which is what keeps the app, the desktop builds, and the browser extension honestly licensed given they share the same injection.

Official sources

  1. License: AGPL-3.0
  2. nonbili/NouTube on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nonbili-noutube.svg)](https://hysenlabs.com/projects/nonbili-noutube)

Was this useful?