# my-yt: a local YouTube frontend that scrapes subscriptions and stores videos on your own disk

> my-yt is a minimal JavaScript application that turns your YouTube subscriptions into a chronological feed, downloads videos with yt-dlp and keeps them in JSON-backed local storage, with optional transcript summaries from a local or hosted LLM. It suits a self-hoster who wants control over their feed, and it depends on yt-dlp and ffmpeg rather than any YouTube API key.

**christian-fei/my-yt** — A clean and minimal youtube frontend, without all the ads and whistles

- Repository: https://github.com/christian-fei/my-yt
- Website: https://cri.dev
- Stars: 1,071 · Forks: 79
- Language: JavaScript
- License: Unlicense
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/christian-fei-my-yt

## A subscription feed that is scraped, not queried

my-yt presents itself as a clean and minimal YouTube frontend. The mechanism behind the feed is direct: subscriptions are scraped from YouTube every 30 minutes, and the README states that no API key is needed. The scraped channel list feeds a chronological display of the latest videos, so the result is a reverse-chronological list rather than an algorithmically ranked one. The author explains the motivation plainly in the Why section, wanting to get back a chronological feed instead of an algorithmically curated one, with no distractions, no comments, no related videos and no sponsors. The author also notes paying for YouTube Premium anyway, which frames the project as a matter of control over attention rather than a way to avoid paying for a subscription.

## The server is a bare HTTP server with SSE for live updates

The project lists its own components, and they are deliberately small. The server is a bare HTTP server that handles Server-sent events for client updates and implements HTTP Ranged requests for video playback, which is what makes seeking work in a browser without serving the whole file. The client is dependency-free bare HTML5, CSS3 and JavaScript, and the README states there is no JavaScript framework on the client or the server side. The whole dependency list in package.json is a single runtime package, nano-spawn, which the README says itself has no transient dependencies. The npm script uses node --env-file-if-exists=.env, so the .env file is optional at the process level, and the application serves on http://localhost:3000. The SSE choice is stated as a deliberate personal experiment in the Why section.

## yt-dlp is the engine, and ffmpeg is the transcoder

Nothing about YouTube is implemented in this project. The lib/youtube.js module is described as a yt-dlp wrapper handling video downloads, channel video listing, video information and transcripts, and the README's feature list credits yt-dlp behind the scenes for downloads. That makes yt-dlp the single point of dependency on the outside world, and it also means the project inherits the fragility of scraping a site that changes its internals. The Docker image makes this explicit. It starts from node:25-alpine, installs python3 and ffmpeg, and then downloads the latest yt-dlp release directly from its GitHub releases URL into /usr/local/bin/yt-dlp, making it executable with chmod a+rx. Transcoding to h264 is automatic for device compatibility, especially iOS, and the README says you can switch it off in the settings if the system is under stress during downloads.

## The AI summary is optional and talks to any chat completions endpoint

The video summary feature routes through lib/llm/index.js, which the README says uses the chat completions API of a provider that can be local, such as LMStudio or Ollama, or hosted, such as OpenAI or Anthropic. The transcript is the input, handled by lib/subtitles-summary.js, and the whole thing is configured through environment variables with sensible defaults, where the model defaults to meta-llama-3.1-8b-instruct, the host to http://127.0.0.1:1234, the endpoint to /v1/chat/completions and the temperature to zero.

```bash
AI_MODEL=gpt-4o-mini AI_HOST=https://api.openai.com AI_APIKEY=sk-proj-123 npm start
```

The hosted variants work the same way, with the Anthropic example swapping the endpoint to /v1/messages.

```bash
docker run -e AI_MODEL=claude-xyz -e AI_HOST=https://api.anthropic.com -e AI_ENDPOINT=/v1/messages -e AI_APIKEY=your-key --rm -it -p 3000:3000 -v $HOME/my-yt-data:/app/data christianfei/my-yt:latest
```

Because the defaults point at a local address, the feature works with no configuration at all if you happen to run a compatible model server on port 1234.

## Persistence is JSON files, which makes backup trivial and migration hard

The README describes persistence as just JSON files for persistence, stupid simple management and backup, and lib/repository.js is the module that handles it, storing video information such as whether a video has been downloaded, its summary, whether it is ignored, and upserting videos. The data directory is the unit of persistence in both deployment paths: the compose file mounts ./data to /app/data, and the standalone docker run example mounts a host directory to the same path. The upside is that your entire library is a directory of JSON you can copy, sync or check into version control. The downside follows from the same property: there is no database, no migration path and no schema version described anywhere in the README, so a format change in a later version is something you would have to handle by hand. The compose file also declares a named volume for data even though the service binds a host directory to it, which is an inconsistency in the sample configuration worth noticing before you rely on it.

## Two install routes, and the Docker path needs host media libraries

The node installation is a clone, an npm install, and the presence of yt-dlp and ffmpeg on the machine, followed by npm start. The Docker route builds an image that already contains both tools, and the compose file starts a single service on port 3000 with the data directory bound. The README offers a copy-paste docker run line as well.

```bash
git clone https://github.com/christian-fei/my-yt.git
cd my-yt
docker compose up --build -d
```

The media tools exist in the container, but the videos still land on the host through the mounted data volume, so your playback depends on the container being able to read and write that directory. The image installs ffmpeg through apk on Alpine rather than a static build, which is a detail that tends to matter on Raspberry Pi and other ARM hosts where the Alpine packages differ from the desktop ones.

## A personal project with a licence discrepancy worth resolving

The repository metadata classifies the licence as Unlicense, while the package.json declares an ISC licence, and the LICENSE file in the root is the third place to look. That is a genuine ambiguity rather than a formality, because the Unlicense and ISC place very different obligations on a redistributor, even though both are permissive. If you plan to fork or ship a modified version, read the LICENSE file and reconcile it with the package.json before you rely on either statement. The licence is one of several signals that this is a personal project. The author describes it as wanting to dabble with Server-sent Events, to try integrating AI in a personal project, to try out yt-dlp and to experiment with the HTML5 track element and the WebVTT API. The README points to a todo list issue for further work, and the repository publishes no GitHub releases, though the last push was on 2026-10-18.

## Conclusion

my-yt is worth running if your goal is a chronological, ad-free feed with offline playback that you control, and if you are comfortable running yt-dlp and ffmpeg on a machine that talks to YouTube without an API key. It does not suit a shared or multi-tenant deployment, since there is no mention of accounts or per-user isolation, and the Docker image fetches the latest yt-dlp release at build time, so builds are not reproducible without pinning. Verify first that your network provider allows the scraping the README describes, which happens every 30 minutes, and that the JSON files under the data directory are the persistence layer you are willing to back up before you download a large library.

## FAQ

### Does my-yt need a YouTube API key?

No. The README states that subscriptions are scraped from YouTube every 30 minutes and that no API key is needed. The scraping is done through yt-dlp, which the project wraps in lib/youtube.js.

### What does my-yt need installed besides Node.js?

yt-dlp and ffmpeg. The node installation instructions list both, and the Docker image installs ffmpeg through apk on Alpine and downloads the latest yt-dlp release into /usr/local/bin/yt-dlp.

### How do I add AI summaries to my-yt?

Set the AI environment variables, either in the .env file or on the command line. The defaults point at a local model server at http://127.0.0.1:1234 with endpoint /v1/chat/completions, and you can point AI_HOST and AI_ENDPOINT at a hosted provider instead.

### Where is my-yt's data stored?

In JSON files under the data directory, which is mounted into the container at /app/data. The compose file binds ./data to that path, and the standalone docker run example mounts a host directory the same way, which makes backup a directory copy.

## Sources

- [christian-fei/my-yt on GitHub](https://github.com/christian-fei/my-yt)
- [Issues](https://github.com/christian-fei/my-yt/issues)
- [License: Unlicense](https://github.com/christian-fei/my-yt/blob/main/LICENSE)
- [Project website](https://cri.dev)
- [README](https://github.com/christian-fei/my-yt/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/christian-fei-my-yt
