Pinchflat¶
Pinchflat is a YouTube media downloader that indexes channels and playlists, downloads content on a schedule, and serves podcast RSS feeds. It uses yt-dlp under the hood. Available at pinchflat.hdhomelab.com.
Deployment¶
Pinchflat runs in Kubernetes (media namespace) as a single-replica Deployment pinned to worker-2a. Config is stored on a local-path PVC; downloads go to a 200Gi NFS volume on the Synology NAS.
graph LR
Client -->|HTTPS + Basic Auth| Gateway[Cilium Gateway]
Gateway --> Pinchflat[Pinchflat :8945]
Pinchflat -->|yt-dlp| YouTube[(YouTube)]
Pinchflat -->|RSS feed| ABS[Audiobookshelf]
Pinchflat -->|config| ConfigPVC[local-path PVC]
Pinchflat -->|downloads| DownloadPVC[NFS 200Gi]
Vault -->|ExternalSecret| K8sSecret[K8s Secret]
K8sSecret -->|username/password| Pinchflat
-
URL
-
Namespace
media -
Storage
Config:
local-path(1Gi) · Downloads:syno-nfs-hdd-retain(200Gi) -
Image
ghcr.io/kieraneglin/pinchflat -
Config
flux/apps/noah/media/pinchflat/ -
RSS pattern
https://pinchflat.hdhomelab.com/sources/<uuid>/feed.xml
Media Profiles¶
Media profiles define what to download and how. Each source is assigned one profile.
| Profile | Resolution | Shorts | Livestreams | Use case |
|---|---|---|---|---|
| Podcasts | Audio Only | Exclude | Exclude | Regular video audio |
| Podcast Livestreams | Audio Only | Exclude | Only | Past livestreams audio |
| TV Shows | 2160p | Include | Include | General video downloads |
RSS Feeds¶
Pinchflat exposes RSS feeds per source (UUID-based URL). Feed endpoints are public (no auth required) because EXPOSE_FEED_ENDPOINTS=true is set.
Feed URL format:
Find a source's UUID in the Pinchflat UI under Actions → Copy RSS Feed, or by querying the database:
Audiobookshelf Integration¶
Add the feed URL directly in ABS as a podcast RSS feed. Pinchflat is whitelisted in ABS's SSRF filter via SSRF_REQUEST_FILTER_WHITELIST.
Secrets¶
| Vault path | Key | Used as |
|---|---|---|
pinchflat |
username |
Basic auth username |
pinchflat |
password |
Basic auth password |
Troubleshooting¶
Check indexed vs downloaded per source¶
# Copy DB from pod
kubectl cp media/$(kubectl get pod -n media -l app=pinchflat -o jsonpath='{.items[0].metadata.name}'):/config/db/pinchflat.db /tmp/pinchflat.db
# Summary per source
sqlite3 /tmp/pinchflat.db "
SELECT s.id, s.custom_name,
COUNT(m.id) as total,
SUM(CASE WHEN m.media_filepath IS NOT NULL THEN 1 ELSE 0 END) as downloaded,
SUM(CASE WHEN m.media_filepath IS NULL THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN m.last_error IS NOT NULL THEN 1 ELSE 0 END) as errors
FROM sources s
LEFT JOIN media_items m ON m.source_id = s.id
GROUP BY s.id;"
Check source URLs¶
/featured URL bug
If a source URL ends in /featured (e.g. /@channelname/featured), Pinchflat will index fewer items than expected. Edit the source and change the URL to the bare channel URL (https://www.youtube.com/@channelname).
Logs¶
# Info/warn/error only, no SQL noise
kubectl logs -n media deploy/pinchflat | grep -E "\[info\]|\[warn\]|\[error\]" | grep -v "QUERY\|oban.*plugin" | tail -80