Self-hosted multi-user YouTube subscription feed
Find a file
2026-06-11 01:57:19 +02:00
backend feat: M3 — automatic channel tagging (language + topic system tags) 2026-06-11 01:57:19 +02:00
scripts feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
.dockerignore feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
.env.example feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
.gitattributes chore: enforce LF line endings via .gitattributes (CRLF for .ps1) 2026-06-11 01:02:06 +02:00
.gitignore feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
docker-compose.yml feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
README.md feat: M3 — automatic channel tagging (language + topic system tags) 2026-06-11 01:57:19 +02:00

Subfeed

Self-hosted, multi-user web app for browsing your own YouTube subscriptions the way you actually want: precise filtering and sorting (by language, topic, length, age, watch state…), a fast local-first feed, and one-click playback that opens the real youtube.com — so your browser's ad blocking and SponsorBlock keep working exactly as before.

Each user signs in with their own Google account (invite-only) and sees only their own subscriptions. All the expensive data (channels, videos, metadata) is fetched from YouTube once and stored locally, so filtering/searching/sorting are instant and don't burn API quota.

Status: early development.

  • M1 (foundation): docker-compose stack, FastAPI backend, Google OAuth login with an email invite-list, encrypted token storage.
  • M2 (ingest core): subscription import, free RSS detection, recent-first + deep backfill from the uploads playlist, enrichment (duration/stats/category, Shorts & livestream classification), a shared daily quota guard, and a background scheduler.
  • M3 (auto-tagging): system tags for channel language (offline detection) and topic (from YouTube topics + dominant category), regenerated automatically; user tags are never overwritten.

Requirements

  • Docker + Docker Compose
  • A Google Cloud project with an OAuth client (see below)

Quick start

  1. Copy the env template and generate secrets:

    cp .env.example .env
    python -c "import secrets;print('SECRET_KEY=',secrets.token_urlsafe(48))"
    python -c "import base64,os;print('TOKEN_ENCRYPTION_KEY=',base64.urlsafe_b64encode(os.urandom(32)).decode())"
    

    Paste the generated values into .env.

  2. Create a Google OAuth client (Google Cloud Console → APIs & Services):

    • Enable the YouTube Data API v3.
    • OAuth consent screen: External, publishing status Testing, and add every invited Google account as a Test user (up to 100 — no app verification needed at this scale).
    • Create credentials → OAuth client ID → type Web application.
    • Authorized redirect URI: http://localhost:8080/auth/callback (must match OAUTH_REDIRECT_URL in .env).
    • Put the client ID/secret into .env, and list invited emails in ALLOWED_EMAILS.
  3. Start it:

    docker compose up --build
    

    Open http://localhost:8080 and sign in. Database migrations run automatically on startup.

Backup & moving to another machine

All data lives in the pgdata Postgres volume — moving to another host (e.g. a Proxmox Linux server) does not require re-fetching from YouTube. Copy your .env (keep the same TOKEN_ENCRYPTION_KEY and Google client so stored tokens stay valid), then:

./scripts/backup.sh                 # -> backups/subfeed-<timestamp>.dump   (Windows: scripts\backup.ps1)
./scripts/restore.sh backups/<file> # on the new host after `docker compose up`

Tech

FastAPI + PostgreSQL backend, React + Vite frontend (added in a later milestone), packaged with Docker Compose.

Note

This project is developed with AI assistance.