Skip to content

2.95.3

Latest

Choose a tag to compare

@ttlequals0 ttlequals0 released this 05 Sep 19:21
7cc7867

2.95.3 is the new stable release. It covers the 23 edge releases since 2.89.5.

Pull ttlequals0/minuspod:stable for the GPU image (amd64) or ttlequals0/minuspod:stable-cpu for the CPU image (amd64 and arm64).

Breaking changes

  • The /quick-search endpoint is gone. Use GET /search instead. GET /search also rejects the old type parameter with a 400 rather than ignoring it; use groups to pick which result groups are computed. (#717)
  • Storage and audio-processing paths now resolve DATA_DIR before DATA_PATH and MINUSPOD_DATA_DIR, matching every other path resolver in the app. If you have DATA_DIR set alongside a different DATA_PATH, storage and audio follow DATA_DIR after upgrading. (#717)

New features

  • Local feeds: build a podcast feed from your own audio files, with no upstream RSS behind it. Episodes go in by single upload or bulk archive import, with a dry-run preview, optional JSON sidecar metadata, and editable Podcasting 2.0 fields. Each one runs the same ad-removal, transcript, and chapter pipeline as a subscribed feed. Local feeds are exempt from retention and cleanup, since their audio is the only copy. See docs/local-feeds.md. (#689)
  • Rate-limit queue hold: when the LLM provider answers 429 with a reset more than five minutes out, the queue pauses until the reset instead of burning retries on a throttled provider. Off by default, with a give-up window of 1 to 720 hours, under Settings > AI & Processing > Queue Control. Play and Reprocess still run mid-pause. (#696)
  • Queue control: per-episode priority with -/+ buttons in the Processing Queue panel, a waiting list paginated at 25 rows per page, and the queue boost sizes exposed as settings. A play request or manual reprocess now always outranks backlog work, and a queued episode's priority can rise but never fall. (#696, #688)
  • Batch recuts: confirming, rejecting, or recategorizing a detection records the decision and marks the episode, then one Apply recuts button rebuilds every waiting episode. An episode edited five times is rebuilt once instead of five times. Available on Ad Review, Detected Ads, and a feed's own page. (#696)
  • Unified search across shows, episodes, and transcripts: type on any page, or press / or Ctrl+K, with the same field built into the Dashboard so it works on mobile. A dedicated page adds type filters plus pattern and sponsor matches. The index now covers every episode whatever its status, so discovered, queued, and failed episodes are findable by title and description too. (#717)
  • Webhook and email alerts for queue holds: Queue Held, Queue Resumed, Service Offline, Service Reachable. Notifications also carry timestamp_local and hold_until_local next to their UTC counterparts, in a timezone you set under Settings > Notifications. (#717)
  • Per-feed overrides for the retention window, for keeping the pre-cut original audio, for the splice check, and for free-text detection notes appended to the LLM prompt. The retention override includes archive-forever, meant for shows that stopped publishing, where a swept episode cannot be fetched again. (#688, #714, #709)
  • Configurable outbound User-Agent strings, one for audio, artwork, and chapters and one for RSS, under Settings > Data & Security > Outbound Requests. Hosts disagree: bot mitigation on some CDNs refuses browser identifiers below a version floor that moves as new browsers ship, while some feed hosts answer only a declared podcast client. Working around a host that starts refusing yours no longer takes a new image. (#714)
  • GET /api/v1/status and the SSE stream carry a hold block: whether a rate-limit pause is stopping new claims, the provider's reset time, and how many episodes each hold keeps. The status bar now appears when the queue is holding work with nothing running, which previously looked identical to an idle queue. (#714)
  • Detection experiments: seed-sponsor toggles per prompt, cross-episode text recurrence hints (off by default), and segment-ID and random addressing modes, with per-mode contract compliance and ad yield on the Stats page. (#683, #685, #688)
  • Opt-in JSON schema response format for OpenAI-compatible providers, probed and remembered per model rather than per endpoint, since one URL can serve models that differ on it. (#693, #694)
  • Per-category segment actions (keep, beep, remove) now apply in the verification pass, not just pass 1. DAI review safeguards keep human decisions from being undone by automatic boundary mutation, and VAD gap extension guardrails hold an oversized untranscribed gap for review instead of extending into it. (#677, #679, #680)

Fixes

  • API keys saved after the container starts take effect without a restart. A worker that booted before a key was configured used to cache the empty value and keep skipping ad detection and chapter generation. (#686)
  • A full or LLM-mode reprocess no longer wipes the episode's transcript and ad markers up front, so an OOM kill or container restart mid-run leaves the prior results intact instead of emptying the episode. (#692)
  • Same-sponsor merging no longer swallows the show content between two mentions. Two detections naming the same sponsor used to merge on that alone; the gap between them must now read as filler, and a zero-length detection is never a merge partner. (#714)
  • Pattern learning no longer throws away most of what it sees on ad-heavy feeds. A span over the duration ceiling used to be dropped whole, so every episode re-detected it from scratch; it is now split at its ad transitions and each piece learned separately. Confirming an ad runs the same gates as auto-learning, so a confirm can no longer mint a pattern keyed on show content. (#696)
  • A 403 on the availability check now fails the episode immediately, instead of being retried as a transient CDN error against a host that answers the same way every time. A download 403 is probed once more with the feed User-Agent, so a host gating on the browser identifier is named in the log. (#714, #696)
  • The auto-process poller no longer loses 30 seconds at every episode handoff. It waits for the queue lock to drop before claiming the next row. (#714)
  • Rate-limited API requests no longer return a sporadic 500. Set RATE_LIMIT_STORAGE_URI to a Redis URL to share limiter counters across workers. (#717)
  • Uptime on the Settings page no longer flips between two values on refresh. Each worker used to stamp its own start time, so a respawn left the two disagreeing. (#717)
  • The verification pass no longer stores a second marker for a span pass 1 already recorded, which put one span in two marker buckets and counted a single pending review twice. (#717)
  • Ollama reasoning effort is now sent as reasoning_effort through the OpenAI-compatible API. The previous form was silently ignored, so the configured effort never reached the model. (#678)
  • A permanently broken artwork URL is no longer refetched every few hours despite the failure cache. (#688)
  • Episode lookups behind the just-in-time serve path are cached for 15 minutes, so a podcast client's HEAD for every unprocessed episode no longer reparses the whole upstream feed each time. (#688)
  • fpcalc fingerprinting survives recoverable decode hiccups, so an episode with one bad frame no longer loses all cue scanning. (#690)
  • Editing a feed setting no longer loses an unsaved edit to a background refetch, and failures from refresh, rename, reprocess-all, and the bulk actions are shown instead of swallowed. (#717, #696)
  • The Ad Review queue holds only what needs a decision. A segment left in by its category is settled by the feed's segment actions, so it now appears under its cut status and the queue can be emptied. (#696)
  • Focusing a field on a phone no longer makes iOS Safari zoom the page. Mobile layout fixes also cover the feed detail action row, the queue pager, switches, detection cards, the Ad Patterns header, and 3- and 4-digit queue positions. (#717, #696)
  • SQLite logs any statement that waits five seconds or longer on the lock, and any write transaction held open that long, with the thread name and the statement that opened it. A "database is locked" burst used to leave no record of which connection was holding it. (#714)

Upgrade notes

  • Clients running 2.95.2 need one manual reload to pick this up. From 2.95.3 on, a deploy reloads the app once the new service worker is ready and the tab is next hidden. Before, the tab kept running its cached bundle until the next visit. (#717)
  • The search index reindexes automatically on first boot after upgrading. There is no manual step. (#717)
  • Episodes holding duplicate verification markers are cleaned up once on upgrade. (#717)
  • The default download User-Agent moves off a Chrome 120 string that hosts had begun refusing. If you were relying on the old value, set it explicitly under Settings > Data & Security > Outbound Requests. (#714)
  • Installs still running the shipped default ad reviewer prompt get its corrected examples on upgrade. A customized prompt is left alone. (#695)
  • Community patterns contributed before segment categories existed now declare category: "sponsor". Installs read the category to decide whether a match is cut, beeped, or kept, and to filter which categories they accept on sync. (#714)

Security

  • Episode artwork reads, stale artwork cleanup, and feed-directory deletion join paths through the containment helper instead of raw path concatenation. That closes the open CodeQL path-injection findings on storage.py. All three sites were already guarded by input validation; this makes the containment provable. (#681)
  • Lockfile bumps for fast-uri and browserslist covering six advisories. Both are build-time dependencies and never reach the browser. (#714)

Thanks

Work ported or adapted from @kristofferR (#677, #679, #680), @leboff (#686), @tylermiranda (#717), @d-portero (#688), and @stansz (#688).