Skip to content

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
Hold "Alt" / "Option" to enable pan & zoom
  • URL


    pinchflat.hdhomelab.com

  • 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:

https://pinchflat.hdhomelab.com/sources/<uuid>/feed.xml

Find a source's UUID in the Pinchflat UI under Actions → Copy RSS Feed, or by querying the database:

sqlite3 /tmp/pinchflat.db "SELECT custom_name, uuid FROM sources;"

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

sqlite3 /tmp/pinchflat.db "SELECT id, custom_name, original_url, last_indexed_at FROM sources;"

/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