Watch Later Manager

Technologies Used

Vue 3TypeScriptPiniaNode.js 20ExpressSQLiteDockerGitLab CI/CDFluxKubernetes

Project Overview

Watch Later Manager is a self-hosted web application I built so I can keep my own Watch Later playlist outside YouTube and sort it in ways the native client does not support—especially by duration and as a deliberate queue I work through. At a few hundred videos, YouTube’s playlist becomes something I dump into, not something I finish.

The app syncs with YouTube through OAuth (and can also import via a userscript JSON export), runs on my homelab Kubernetes cluster behind the same GitOps workflow I use for other personal services, and treats the backlog as a queue I control rather than a feed YouTube surfaces.

Evolution

The project did not start as a split frontend and backend. The first version was a monolith—enough to prove the workflow and validate sync against a real playlist. Once the core loop worked, I split it into separate frontend and backend services:

  • Frontend — Vue 3, TypeScript, and Pinia for reactive state and a responsive UI
  • Backend — Node 20 with Express and SQLite for persistence (a deliberate choice for a single-user, self-hosted deployment)

That separation made testing, container builds, and independent deployment straightforward without over-engineering the data layer.

The Challenge

YouTube’s Watch Later playlist breaks down at scale:

  • No practical way to sort by duration or otherwise prioritise what to watch next
  • Restore and backup of local state is not part of the product
  • Native clients are not built for a deliberate “work through the backlog” workflow

The Solution

A focused self-hosted manager that treats Watch Later as a queue I control:

  • YouTube OAuth sync keeps the local database aligned with the remote playlist (with userscript JSON import as an alternate path)
  • Duration-based sorting and queue operations make large lists workable
  • Like and subscribe controls reduce context switching back to YouTube
  • Queue Manager search filters the local queue by title or channel when the list is long
  • Responsive UI works on desktop and mobile browsers
  • Kubernetes / Flux deployment on the homelab cluster, with images built in GitLab CI

Technical Implementation

Frontend

  • Vue 3 with the Composition API
  • TypeScript throughout
  • Pinia for centralized application state
  • Component-based layout tuned for queue browsing and playback flow

Backend

  • Node 20 and Express
  • SQLite for durable, low-overhead storage
  • REST endpoints for sync, queue mutations, YouTube actions, and metadata queries
  • Container image published through GitLab CI

Deployment

  • Docker images for frontend and backend
  • Deployed to the homelab cluster via Flux
  • Changes flow through Git merge requests like other platform services

Results

The application runs in production and is my main way of watching YouTube. Automated tests cover frontend and backend behaviour; CI validates changes before they reach the cluster.

Lessons Learned

  • Monolith first, split when boundaries are clear — the first version validated sync; the split came when API and UI concerns were obvious.
  • SQLite fits single-tenant self-hosting — no operational cost of a separate database server for one user.
  • Same GitOps path as everything else — treating this like any other homelab service keeps updates boring in a good way.