|
| 1 | +# TubeChecker |
| 2 | + |
| 3 | +A lightweight, self-hosted tool that watches YouTube channels via RSS, detects new videos, and automatically dispatches them to a [MeTube](https://github.com/alexta69/metube) instance for downloading. Optionally triggers a Jellyfin library refresh after new videos are queued. |
| 4 | + |
| 5 | +Runs as a single Docker container with a built-in web UI. No external services, no message queues — just a SQLite database and a scheduler. |
| 6 | + |
| 7 | +## Features |
| 8 | + |
| 9 | +- Polls YouTube channel RSS feeds on a configurable interval |
| 10 | +- Resolves channels from a `@handle`, full URL, or bare `UC…` channel ID |
| 11 | +- Filters by start date — only downloads videos published on or after a given date |
| 12 | +- Per-channel download folder configuration |
| 13 | +- Download history with status tracking (`sent` / `failed`) |
| 14 | +- Retry failed downloads individually or in bulk |
| 15 | +- Optional Jellyfin library refresh after new videos are queued |
| 16 | + |
| 17 | +## Requirements |
| 18 | + |
| 19 | +- Docker + Docker Compose |
| 20 | +- A running [MeTube](https://github.com/alexta69/metube) instance |
| 21 | +- (Optional) A running [Jellyfin](https://jellyfin.org) instance |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## Quick Start (published image) |
| 26 | + |
| 27 | +No clone required. Create a `docker-compose.yml`: |
| 28 | + |
| 29 | +```yaml |
| 30 | +services: |
| 31 | + tubechecker: |
| 32 | + image: ghcr.io/spaggel/tubechecker:latest |
| 33 | + container_name: tubechecker |
| 34 | + restart: unless-stopped |
| 35 | + ports: |
| 36 | + - "8083:8083" |
| 37 | + volumes: |
| 38 | + - ./data:/data |
| 39 | + environment: |
| 40 | + - METUBE_URL=http://your-metube-host:8081 |
| 41 | + - CHECK_INTERVAL=60 |
| 42 | + # - JELLYFIN_URL=http://your-jellyfin-host:8096 |
| 43 | + # - JELLYFIN_API_KEY=your_key_here |
| 44 | +``` |
| 45 | + |
| 46 | +Then run: |
| 47 | + |
| 48 | +```bash |
| 49 | +docker compose up -d |
| 50 | +``` |
| 51 | + |
| 52 | +The UI is available at `http://localhost:8083`. |
| 53 | + |
| 54 | +Open **Settings** and set your MeTube URL, then add channels from the **Channels** view. |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +## Integration into an Existing Docker Compose Stack |
| 59 | + |
| 60 | +If you already run MeTube (and optionally Jellyfin) in a shared `docker-compose.yml`, add TubeChecker as another service. Because all containers share the same Compose network by default, you can reference MeTube by its service name. |
| 61 | + |
| 62 | +### Without a reverse proxy |
| 63 | + |
| 64 | +```yaml |
| 65 | +tubechecker: |
| 66 | + image: ghcr.io/spaggel/tubechecker:latest |
| 67 | + container_name: tubechecker |
| 68 | + restart: unless-stopped |
| 69 | + ports: |
| 70 | + - "8083:8083" |
| 71 | + volumes: |
| 72 | + - ${DATA_DIR}/tubechecker:/data |
| 73 | + environment: |
| 74 | + - METUBE_URL=http://metube:8081 # internal Docker service name |
| 75 | + - CHECK_INTERVAL=60 |
| 76 | + # - JELLYFIN_URL=http://jellyfin:8096 |
| 77 | + # - JELLYFIN_API_KEY=your_key_here |
| 78 | +``` |
| 79 | + |
| 80 | +### With Traefik |
| 81 | + |
| 82 | +```yaml |
| 83 | +tubechecker: |
| 84 | + image: ghcr.io/spaggel/tubechecker:latest |
| 85 | + container_name: tubechecker |
| 86 | + restart: unless-stopped |
| 87 | + volumes: |
| 88 | + - ${DATA_DIR}/tubechecker:/data |
| 89 | + environment: |
| 90 | + - METUBE_URL=http://metube:8081 |
| 91 | + - CHECK_INTERVAL=60 |
| 92 | + # - JELLYFIN_URL=http://jellyfin:8096 |
| 93 | + # - JELLYFIN_API_KEY=your_key_here |
| 94 | + labels: |
| 95 | + - "traefik.enable=true" |
| 96 | + - "traefik.http.routers.tubechecker.rule=Host(`${URL_TUBECHECKER}`)" |
| 97 | + - "traefik.http.routers.tubechecker.entrypoints=websecure" |
| 98 | + - "traefik.http.routers.tubechecker.tls.certresolver=myresolver" |
| 99 | + - "traefik.http.services.tubechecker.loadbalancer.server.port=8083" |
| 100 | +``` |
| 101 | +
|
| 102 | +Add `URL_TUBECHECKER=channels.yourdomain.com` to your `.env` file. |
| 103 | + |
| 104 | +> **Note:** The web UI has no built-in authentication. If it is publicly accessible, protect it with a Traefik `basicauth` middleware or equivalent. |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +## Build from Source |
| 109 | + |
| 110 | +```bash |
| 111 | +git clone https://github.com/Spaggel/TubeChecker.git |
| 112 | +cd TubeChecker |
| 113 | +docker compose up -d --build |
| 114 | +``` |
| 115 | + |
| 116 | +--- |
| 117 | + |
| 118 | +## Configuration |
| 119 | + |
| 120 | +All settings can be configured from the **Settings** view in the UI and are persisted to the database. Environment variables seed the database on first run and act as defaults — subsequent UI changes take precedence. |
| 121 | + |
| 122 | +| Environment Variable | Default | Description | |
| 123 | +|----------------------|----------------------------|----------------------------------------------------------| |
| 124 | +| `DATA_DIR` | `/data` | Directory where the SQLite database is stored | |
| 125 | +| `METUBE_URL` | `http://localhost:8081` | Base URL of your MeTube instance (no trailing slash) | |
| 126 | +| `CHECK_INTERVAL` | `60` | How often to poll RSS feeds, in minutes | |
| 127 | +| `JELLYFIN_URL` | *(empty)* | Base URL of your Jellyfin instance — leave empty to disable | |
| 128 | +| `JELLYFIN_API_KEY` | *(empty)* | Jellyfin API key (Dashboard → API Keys) | |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +## Usage |
| 133 | + |
| 134 | +### Adding a channel |
| 135 | + |
| 136 | +1. Go to **Channels** and click **Add Channel**. |
| 137 | +2. Enter any of the following in the channel field: |
| 138 | + - A `@handle` (e.g. `@mkbhd`) |
| 139 | + - A full channel URL (e.g. `https://www.youtube.com/@mkbhd`) |
| 140 | + - A bare channel ID starting with `UC…` |
| 141 | +3. Optionally set a **Start Date** to limit downloads to videos published on or after that date. Leave empty to download all available videos. |
| 142 | +4. Optionally set a **Download Folder** — a subfolder inside MeTube's download directory. Defaults to the channel name. |
| 143 | +5. Click **Add Channel**. The channel name is auto-fetched from YouTube if you leave it blank. |
| 144 | + |
| 145 | +### Checking for new videos |
| 146 | + |
| 147 | +- Channels are checked automatically on the configured interval (default: every 60 minutes). |
| 148 | +- Click **Check Now** on any row to trigger an immediate check for a single channel. |
| 149 | +- Click **Check All Now** to trigger an immediate check for all channels. |
| 150 | + |
| 151 | +### Viewing download history |
| 152 | + |
| 153 | +- **History** shows the most recent 200 dispatched videos across all channels, with status (`sent` / `failed`). |
| 154 | +- Hover over a `failed` badge to see the error detail. |
| 155 | +- Use the retry button on any row, or **Retry All Failed**, to re-dispatch failed downloads. |
| 156 | + |
| 157 | +### Per-channel video history |
| 158 | + |
| 159 | +- Click the playlist icon on any channel row to open that channel's video history. |
| 160 | +- The same retry controls are available scoped to just that channel. |
| 161 | + |
| 162 | +### Jellyfin integration |
| 163 | + |
| 164 | +1. In **Settings**, enter your Jellyfin URL and API key. |
| 165 | +2. Click **Save Settings**. |
| 166 | +3. After each scheduled check that dispatches at least one new video, a library refresh is triggered automatically. |
| 167 | +4. The **Refresh Jellyfin** button in the Channels view (and the test button in Settings) let you trigger a refresh manually. |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +## Updating |
| 172 | + |
| 173 | +**Published image:** |
| 174 | +```bash |
| 175 | +docker compose pull tubechecker |
| 176 | +docker compose up -d tubechecker |
| 177 | +``` |
| 178 | + |
| 179 | +**Built from source:** |
| 180 | +```bash |
| 181 | +git -C /path/to/TubeChecker pull |
| 182 | +docker compose up -d --build tubechecker |
| 183 | +``` |
| 184 | + |
| 185 | +The SQLite database is stored in the mounted volume and is preserved across updates. |
| 186 | + |
| 187 | +--- |
| 188 | + |
| 189 | +## Stack |
| 190 | + |
| 191 | +| Layer | Technology | |
| 192 | +|-----------|-------------------------------------| |
| 193 | +| Backend | Python 3.12, FastAPI, APScheduler | |
| 194 | +| Database | SQLite via SQLAlchemy | |
| 195 | +| Frontend | Vue 3 (CDN), Bootstrap 5 (CDN) | |
| 196 | +| Container | Docker (single image, no build step for frontend) | |
0 commit comments