Releases: o51r15/pinchfork
Release list
0.3.6 - more ARM bugs
Full Changelog: v0.3.5...v0.3.6
0.3.5 - pi bug fixes
Full Changelog: v0.3.4...v0.3.5
v0.3.4 — Bug Fixes
Fixed: Source metadata failing on large channels
yt-dlp's playlist:%()j template dumped the full playlist info dict including per-video format data, producing multi-megabyte JSON for large channels. This caused Jason.DecodeError on truncated writes, silently crashing the metadata worker and leaving sources without descriptions, posters, or banners. The template is now scoped to only the fields actually used (description, thumbnails, title, id, etc.), and --flat-playlist is applied so yt-dlp never processes individual entry format data. JSON output is now a few KB regardless of channel size.
Fixed: Source metadata worker crashing with unguarded match error
A bare {:ok, metadata} = pattern match in fetch_source_metadata_and_images caused a MatchError on any metadata fetch failure, bypassing the Oban rescue block and producing noisy retries/discards. The match is now handled with a proper case — fetch failures log a warning and the job completes gracefully rather than crashing.
Fixed: PO token test timing out on ARM hardware
The token server health check used --max-time 15, which was too short for the bgutil Innertube challenge on ARM (Pi 4). The timeout is now 30 seconds, confirmed working on Pi hardware.
Full Changelog: v0.3.3...v0.3.4
0.3.3
Pinchfork v0.3.3
Sonarr-style UX overhaul + system foundation
This release replaces most of the inherited Pinchflat UI with a Sonarr-inspired interface and adds core system management capabilities.
Sources
Poster card grid — sources index rebuilt as a card grid with per-card progress bars showing download completion percentage
Per-card monitored toggle — eye icon directly on each card to enable/disable downloading without opening the source
Filter and sort toolbar — filter by name or monitored state, sort by name or download count
Source show page — fanart banner with poster overlay, stats bar (Downloaded / Failed / Prevented / Skipped with tooltips), horizontal action bar replacing the old Edit button and Actions dropdown
Episode list
Year-grouped episode list — episodes grouped and collapsed by year with per-year progress bars and download counts
Three-way year toggle — Monitored / Mixed / Unmonitored state per year, click to enable or disable the whole year at once
Per-item eye toggle — enable or disable individual episodes inline
Granular status badges — Downloaded, Downloading, Pending, Retrying, Failed, Prevented, and five Skipped variants (Short, Live, Members, Manual, Cutoff)
Show excluded toggle — optionally surface items hidden by profile preference, cutoff date, or availability policy
Adjustable page size — 50 / 100 / 200 / 300 / All per year section
Navigation
/ now redirects to /sources as the landing page
/stats — dedicated stats page
/activity — renamed from History; tabs: Active Tasks / Pending / Retry / Failed / Downloaded
Retry tab shows live countdown to next attempt per item with a Retry Now button
Failed tab has a Force Retry button that clears error state and re-queues
System section
Status page — application health overview, PO token server health monitor with live test button (curl-based for Docker DNS compatibility)
Logs page — tail application logs from the UI
Backup page — create, download, and delete pg_dump backups with gzip compression and configurable retention
Updates page — check for and apply container updates
Download engine
SABR bypass — client override dropdown (iOS / Android / TV embedded) for sources where YouTube bot detection blocks standard downloads; enforces mutual exclusivity with cookie auth
LOCALTEMP staging — each download's intermediate files are isolated under /downloads-staging/<media_id>/ and cleaned up on completion, failure, or retry
Branding
Pinchflat ? Pinchfork throughout (logo, sidebar, header, onboarding)
Discord and donate links removed
0.3.2
v0.3.2 fixes two critical issues that prevented Pinchfork from working on any fresh deployment (Pi4, Windows, clean server):
Bug fixes:
Downloads silently never start on fresh installs — Oban's oban_jobs table was created with a priority_range constraint of 0–3 (Oban schema v11). MediaDownloadWorker uses priority: 5, which violated this constraint. Every Oban.insert for download jobs failed silently with a constraint error that Enum.each swallowed. Fix: new migration 20260618000001_bump_oban_to_v12.exs runs Oban.Migration.up(version: 12), widening the constraint to 0–9.
Docker image CI build failed — The bgutil-ytdlp-pot-provider v1.3.1 zip changed its internal structure: files now extract directly to yt_dlp_plugins/extractor/ with no top-level subdirectory. The Dockerfile test -d check expected the old bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor path, causing every CI build to fail. Fix: corrected the path in docker/selfhosted.Dockerfile.
Full Changelog: v0.3.1...v0.3.2
v0.3.1 — PO Token plugin + fresh-install fix
This release fixes a critical bug that made the app unusable on a fresh install, and completes the one-and-done PO Token setup story.
Bug fixes
Fixed a fresh-install regression where every yt-dlp call (indexing, downloading, version checks) would abort with Invalid plugin directory: /config/yt-dlp-plugins if the plugin directory didn't exist. The app now fails open — if the plugin directory is absent or empty, yt-dlp runs normally without the flag.
Improvements
The bgutil POT provider yt-dlp plugin is now baked into the image. Adding the bgutil sidecar container to your compose is all that's required for PO tokens to work — no manual plugin download or volume mount needed.
Added download_prevented_reason field on media items, surfaced on the media item page. Distinguishes between manually prevented, policy-blocked, and error-stopped downloads.
Added docker-compose.sample.yml to the repo — a complete annotated three-container compose file for new installs.
Notes
The baked-in plugin is pinned to bgutil 1.3.1. Run the matching sidecar tag (brainicism/bgutil-ytdlp-pot-provider:1.3.1).
If you were previously running :dev, this release is what :latest now points to.
v0.3.0
Per-item staging cleanup
- Each download now stages its intermediate files under a per-item directory keyed on the video's unique id (
/downloads-staging/<id>), isolating every file (video, thumbnail, fragments) for that item. - Failed, abandoned, or interrupted attempts have their staging files cleaned up automatically — before the next attempt, on failure, and after success — so stale intermediates can't accumulate or interfere with a later retry.
- Requires
LOCALTEMP=true; a no-op otherwise.
Home page failure-state tabs
The single "Pending" tab is now split into three so you can tell apart queued, retrying, and permanently failed items:
- Pending — queued and waiting, no errors.
- Retry — failed but will retry automatically, with a live countdown to the next attempt and a Retry Now button to skip the backoff.
- Failed — permanently failed, showing the last error and a Force Retry button that clears the error state and restarts the download from scratch (with a confirmation prompt).
Other
- Sidebar now shows the Pinchfork version dynamically (no more manual drift); the Pinchflat line reflects the upstream base it forks from.
- Docker image:
ghcr.io/o51r15/pinchflat:v0.3.0and:latest.
v0.2.0
SABR bypass / PO Token provider
Works around YouTube SABR-corrupted downloads that fail with Invalid data found when processing input.
- Per-source Video Client override (Source → Edit → Downloading Options) to route downloads around the SABR code path
- Optional bgutil PO Token sidecar support via a mounted yt-dlp plugin that survives yt-dlp self-updates
- Cookie ↔ bypass-client mutual exclusivity enforced in code and blocked at save time
Local temp staging (LOCALTEMP)
For setups where the downloads directory is a network mount (SMB/CIFS, NFS).
- Set
LOCALTEMP=trueto stage all yt-dlp intermediate work (fragments, stream merge, post-processing) on a local disk, moving finished files to the downloads directory in a single operation - Keeps network-fragile read/write/rename activity off the mount, preventing mid-pipeline file corruption
- Requires a local
/downloads-stagingvolume mount (see README)
Notes
- Docker image:
ghcr.io/o51r15/pinchflat:v0.2.0 - Built on the PostgreSQL backend introduced in v0.1.0
v0.1.0 - PostgreSQL Backend
Functional PostgreSQL backend replacing the original SQLite implementation.
What changed:
- SQLite replaced with PostgreSQL — resolves Oban write contention and crash loops under load
- Oban engine switched from
LitetoBasic(Postgres native) - All SQLite-specific SQL rewritten for Postgres (
IFNULL→COALESCE,DATETIME()→ interval arithmetic,regexp_like→~,COLLATE NOCASE→LOWER()) - Full-text search migrated from SQLite FTS5 to Postgres
tsvectorwith GIN index and trigger-maintained updates uploaded_atcolumn type corrected totimestamptz- Oban unique job conflict handling fixed for Postgres advisory lock behavior
Requires: Postgres 16 sidecar container. See README for compose setup.
Upstream base: kieraneglin/pinchflat @ 2025.9.26
Full Changelog: https://github.com/o51r15/pinchflat/commits/v0.1.0