github getmydia/mydia v0.10.0
Mydia v0.10.0

4 hours ago

Mydia v0.10.0

v0.10.0 is here. After three betas across the past two days, this release graduates the long-running Usenet feature-parity push, the Release Parser v3 rewrite, the AdminConfig split, the type-safety overhaul, and the iroh 1.0 transport upgrade into a single stable cut.

If you've been running :beta, this is what you've been testing — minus a couple of last-mile fixes folded in below.

🎬 The big stories

Usenet, finally feature-complete

When the Usenet pipeline shipped a few months back, it worked — but it wasn't a peer of the torrent path. v0.10.0 closes the gap. Mydia now:

  • Parses usenetdate and nzb_grabs from Newznab/Prowlarr/NZBHydra2 responses, so the ranker can reason about NZB freshness and popularity instead of pretending NZB results were torrents with zero seeders.
  • Filters releases by minimum post age (per-indexer setting), so you can avoid grabbing brand-new posts that haven't propagated yet.
  • Ranks NZB results properly with a dedicated scoring branch instead of torrent heuristics.
  • Routes downloads by content type — separate SABnzbd/NZBGet categories for movies vs TV vs anime, instead of the old one-category-per-client setup.
  • Maps quality to a 5-tier priority profile per client, so you can push 4K to high priority and SD to low without touching the client.
  • Detects stalled downloads at 99% and surfaces them with a badge in the Downloads view rather than polling forever.
  • Blacklists failed releases so the next search doesn't pick the same broken NZB again.
  • Accepts SABnzbd + NZBGet completion webhooks as a fast-path signal on top of the existing poll loop. MediaImport is now idempotent so duplicate triggers from webhook + poll are safe.

Internally, SABnzbd (665 LOC) and NZBGet (517 LOC) now share a unified DownloadStatus struct and adapter helpers, with table-driven state mapping tests against frozen JSON fixtures from real API responses.

Closes #119, #120, #121, #122, #123, #124, #125, #126, #127, #128, #129.

Release Parser v3

The old FileParser.V2 was a regex-soup with edge cases that bit us in real-world torrent titles. v3 is a clean tokenizer → classifier → resolver pipeline with a regression corpus harvested from Sonarr and Radarr. All 14 call sites have been migrated, V2 has been deleted, and the parser is now 245-case parity tested against the corpus.

The tokenizer is byte-safe (the Unicode bugs in the old parser that caused the file-parser-unicode-byte-offset fix), and the quality extractor finally handles weird release groups, parenthesized sub-expressions, and word-form Season 1 Episode 5 titles that V2 missed.

Iroh 1.0.0-rc.0 for P2P transport

The P2P core (mydia_p2p_core) now runs against the first release-candidate of iroh 1.0, up from 0.96.1. Client and server stay protocol-compatible because both wrap the same Rust crate. If you run your own relay, note that n0computer dropped the v prefix at 1.0+ — the matching image is n0computer/iroh-relay:1.0.0-rc.0.

Auto-rename on import (TRaSH Guides format)

You can now opt into automatic file renaming on import using the TRaSH Guides naming format. Per-library toggle, exposed in library settings and through GraphQL.

Jellyfin NFO export

NFO writer module that emits Jellyfin-compatible .nfo files on import and refresh. Per-library opt-in plus a bulk export mix task for back-filling existing media. Handy if you're running Mydia alongside Jellyfin and want metadata to follow your files.

Issues tab revamp

The Downloads → Issues tab is now a proper three-section layout: unmatched, partial pack, failed. Manual match, manual resolution, and dismiss are all first-class actions instead of buried buttons. Season packs with unresolved files now do partial imports — the matched episodes land, the rest stay flagged.

Player CTA in the sidebar

The sidebar now surfaces the bundled web player with a prominent label and a CTA banner — so first-time users actually discover that /player is a thing.

🛠 Reliability & correctness

  • qBittorrent: multipart uploads, post-add verification, 5.x compatibility, and a less-aggressive "missing" detector that doesn't flap on slow refreshes.
  • Season pack redownload loop: fixed — packs no longer requeue every hour. Trackerless torrents are now detected structurally instead of by string matching.
  • Stuck season-pack imports self-heal instead of retrying forever after a partial-state crash.
  • Title mismatch filter: search results where the parsed title doesn't match the target are rejected upfront. Unicode normalization (NFD) is consistent across the search path so accents don't cause false negatives.
  • Crash reporting: Logger backend installs cleanly on Elixir 1.15+, and the admin toggle actually works.
  • Configurable metadata language for TMDB/TVDB requests, per install.
  • Test suite is ~42% faster (221s → 127s) and Docker CI builds dropped from 2h+ to ~15min.

🧱 Architectural cleanup

A lot of this release is invisible to users but matters for everyone contributing or reading the code:

  • AdminConfigLive split into eight focused LiveViews (AdminSettingsLive, AdminIndexersLive, AdminQualityProfilesLive, AdminLibraryPathsLive, AdminMediaServersLive, AdminDownloadClientsLive, AdminSystemLive, AdminRemoteAccessLive). The monolith is gone.
  • Settings and Downloads contexts split into facade + sub-modules.
  • MediaLive.Show extracted into focused event modules (SearchEvents, FileEvents, SubtitleEvents, plus five more).
  • Type safety overhaul: @spec and @type t annotations across all 43 Ecto schemas, all context module public functions, and 14 Oban workers (with typed Args structs). MediaFile.metadata is now a FileMetadata struct, not a plain map.
  • QueryHelpers.Filterable macro for declarative query filters, rolled out across collections, accounts, and downloads.
  • Component organization conventions documented (three-tier system: core / domain / feature-local).

⚙️ Infra

  • Iroh upgraded to 1.0.0-rc.0 for the P2P core (relay image: n0computer/iroh-relay:1.0.0-rc.0).
  • Docker CI: replaced Blacksmith with standard GitHub Actions for layer caching.
  • CI postgres containers no longer leak between precommit runs.

🐛 Notable fixes

  • Series rescan no longer imports wrong files / deletes metadata on refresh.
  • SQLite FK corruption during table recreation: fixed.
  • Word-form Season Two Episode Five filenames now import correctly.
  • File parser uses byte offsets for regex slicing, fixing Unicode title mismatches.
  • Req.TransportError from FlareSolverr and indexer connections no longer crashes the page.
  • Paused torrents at 100% are correctly classified as completed.

📦 Try it

docker pull ghcr.io/getmydia/mydia:0.10.0
# Postgres variant
docker pull ghcr.io/getmydia/mydia:0.10.0-pg
# Or the rolling stable tag
docker pull ghcr.io/getmydia/mydia:latest

Player builds for Android, macOS, Windows, and Linux are attached below.

Feedback welcome — open issues at getmydia/mydia.

Full Changelog: v0.9.0...v0.10.0

Don't miss a new mydia release

NewReleases is sending notifications on new releases.