Skip to content

Commit 21f6ada

Browse files
committed
Setup readme, GCR action
1 parent 17b3e98 commit 21f6ada

3 files changed

Lines changed: 254 additions & 2 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Publish Docker image
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- "v*"
9+
10+
env:
11+
REGISTRY: ghcr.io
12+
IMAGE_NAME: spaggel/tubechecker
13+
14+
jobs:
15+
build-and-push:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read
19+
packages: write
20+
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Set up Docker Buildx
26+
uses: docker/setup-buildx-action@v3
27+
28+
- name: Log in to GitHub Container Registry
29+
uses: docker/login-action@v3
30+
with:
31+
registry: ${{ env.REGISTRY }}
32+
username: ${{ github.actor }}
33+
password: ${{ secrets.GITHUB_TOKEN }}
34+
35+
- name: Extract metadata (tags and labels)
36+
id: meta
37+
uses: docker/metadata-action@v5
38+
with:
39+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
40+
tags: |
41+
type=ref,event=branch
42+
type=semver,pattern={{version}}
43+
type=semver,pattern={{major}}.{{minor}}
44+
type=raw,value=latest,enable={{is_default_branch}}
45+
46+
- name: Build and push
47+
uses: docker/build-push-action@v5
48+
with:
49+
context: .
50+
push: true
51+
tags: ${{ steps.meta.outputs.tags }}
52+
labels: ${{ steps.meta.outputs.labels }}
53+
cache-from: type=gha
54+
cache-to: type=gha,mode=max

README.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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) |

docker-compose.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
services:
22
channel-watcher:
3-
build: .
4-
container_name: tube-checker
3+
image: ghcr.io/spaggel/tubechecker:latest
4+
# To build from source instead, replace the image line above with:
5+
# build: .
6+
container_name: tubechecker
57
restart: unless-stopped
68
ports:
79
- "8083:8083"

0 commit comments

Comments
 (0)