v0.14.0-beta.1
Pre-releaseFirst beta for v0.14.0, cut from master. 61 PRs since v0.13.2.
Two things drive this release. Media import is now an asynchronous, resumable run instead of a wizard that needed your browser tab held open, and device pairing is end to end encrypted, always on. Player traffic has always been end to end encrypted. Pairing was the part that was not, and that is what this fixes.
The import path is about a week old. It is a rewrite of how a library gets into Mydia, and while it has been exercised against a real library and the full suite on both database adapters, it will still have rough edges. That is what a beta is for. If it goes wrong for you, open an issue or say something in Discussions. I want the feedback on this one.
Read the upgrade notes at the bottom before you pull this. The music, books and adult verticals are gone, remote access turns on for existing installs, and there are 22 migrations.
Import
- The import run moved out of the browser. It lives in Oban on its own
importsqueue and works in two phases: a scan commits amedia_filesrow per file with no HTTP at all, then a match phase caches a candidate per file and links confident matches. Closing the tab, a disconnect or a deploy no longer loses progress, stopping keeps everything processed so far, and the database is the cursor, so Continue is just a fresh run over the same path. Matching also groups a batch by normalized title and warms the cache on one file before fanning out, so twelve episodes of a season cost one relay search where they used to cost ten (#472) - Review works on folders. A few thousand unresolved files collapse to a few dozen folder groups arranged as series, seasons and episodes, and actions apply to the whole group: change the match, reversible ignore, create a show from the folder for media no provider carries, and bulk accept. Selection is a filter predicate, so "select all" no longer blows past the adapter's bind-parameter cap. Match policy is one number, the 0.85 link threshold shared by the review bands and the ingest path, a failed match is retryable where it used to be parked forever, and year evidence can overturn a title tie (#482, #493, #495)
- Smaller import fixes: library icons restored, scan progress shown, completion feedback made transient, clearing scan results actually clears them, and a run can no longer be stranded (#480, #494, #497)
Remote access and pairing
- Pairing is end to end encrypted. The server generates the claim code locally and derives an Argon2id seed from it. The relay receives a blinded lookup key and a ChaCha20-Poly1305 sealed blob holding the node address and instance id, and can read neither. Before this it held both halves of the secret, so it could pair itself as a device or substitute an address. Derivation and sealing live in one Rust module in
mydia_p2p_core, reached from Elixir through Rustler and from Dart through flutter_rust_bridge, with a pinned known-answer vector guarding the parameters. QR pairing drops out of the relay path entirely, so an outage degrades pairing to QR alone. There is no opt-out on the server, since sealing is the only claim-code path an updated server has, though the player keeps an unencrypted fallback for older servers that comes out one minor from now. The p2p path also had no guess limiting, so codes could be ground against ~30 bits at whatever rate the server answered; the rate limiter runs on it now (#513) - Connecting a player is a user action, and the Remote Access toggle is real.
/devicesis user-scoped and carries pairing, that user's own paired players with their real online status, and download links for every platform, with/admin/devicesredirecting to it. The toggle defaults on and enforces. It was read in exactly one template and nowhere else, so switching it off still left a node dialing the relay, still allowed pairing, and still served media to every paired device. It now gates claim codes, p2p pairing, media reads and GraphQL, and the p2p keypair path defaults underMYDIA_DATA_DIR(#510, #496) - Server and player each declare the oldest counterpart version they work with, published over a public
serverCompatibilityGraphQL field, with a banner in the app shell on a mismatch. Fails open to silence (#470) - Pre-iroh leftovers removed from remote access, and iroh-relay held at v1.0.0 with image verification against real UDP traffic (#509, #498)
TV and metadata
- Oversized TV seasons can be split using TVDB's alternate season orderings, turning a single 170-episode season into 51, 51, 52 and 16 under DVD ordering. Switching is lossless: file links, downloads, watch history and per-episode monitored flags all survive. Episodes carry
absolute_numberandprovider_episode_id, and the upsert keys on provider identity so a reordering moves rows and stops stranding the originals with their file links. Seasons above 50 episodes render in collapsible labelled ranges, which also helps the season-0 specials pile. Anime classification now recognises TVDB'sjpncountry and language codes and re-runs on every metadata refresh, where it used to ride entirely on an explicit genre tag;mix mydia.reclassifyhandles the backlog (#486, #499, #504) - Metadata refreshes reach episodes. The scheduled pass was skipping the episode leg outright, and its throttle applied to the manual refresh button too (#454)
- TV monitoring stays in sync between presets and manual season toggles (#447)
- TMDB recommendations on the detail page and in the Discover modal, ranked and merged with the Collection strip, and Discover stores every provider id so Add stops failing on a duplicate tvdb_id (#453, #457, #456)
Downloads and search
- Manual search survives a bad indexer. Results stream, so one dead indexer can no longer stall or blank the whole search, the indexer bar shows that a search is still running, and a nil field in a result no longer crashes and blanks the list (#474, #479, #500)
- A deleted download stops taking things with it. Cancelling a job no longer deletes source media, and a download deleted mid-flight no longer crashes jobs and LiveViews (#450, #505, #508)
- Trackerless
.torrentfiles get public trackers added, which was killing real downloads, andmin_seedersfor automatic search is operator-configurable (#440, #441)
Player
- The playback chrome refracts where it used to only blur, and the up-next prompt is rebuilt on the same OSD foundation (#455, #514)
- Watched state is coherent across the app. Infuse-style indicators including on the home rails, watched titles no longer draw a progress bar, and every watch-state screen refreshes when watched changes (#443, #444, #491)
- Subtitles when casting to a Chromecast, over both direct and p2p routes, and the media proxy survives a next-episode handoff (#462, #488)
- Horizontal rails scroll with a plain mouse, and the show page's similar-titles rail is collapsed by default (#492, #475)
- The login and connect screen is simplified, and sign out actually signs out on macOS (#481, #507)
- The web player is built in CI and deployed on release, and browsers stop pinning an old bundle (#446, #512)
Web UI
- Dense pages got controls: season count, a compact grid density and view mode toggle, a Recently Added dashboard rail, and collapsible seasons and recommendations rail on the TV show page (#469, #484, #476, #483)
- Remove a title from Continue Watching, and pick the audio track by language preference with the choice remembered (#501, #449)
- Smaller fixes: the dashboard counts TV streams and sources recent activity from real plays, the subtitle search modal no longer crashes on every file, the Discover library picker no longer renders clipped inside the poster card, in-flight adds are tracked per rail, and two crash regressions from 0.13.x turned up in the relay crash reports (#439, #467, #468, #485, #461, #445)
Database
- Every
varcharcolumn widened to TEXT and int4 byte-size columns widened to bigint on PostgreSQL, and SQLiteDatabase busyon events andoban_jobswrites (#448, #451, #502)
Removed
- The music, books and adult library verticals are gone. Movies and TV Shows are unaffected. Twelve tables are archived to NDJSON before anything is dropped, and the migration raises without dropping if archival fails (#471)
Upgrade notes
Back up first. There are 22 migrations.
- Music, books and adult are removed.
library_pathsrows of those types are converted tomixedand left unmonitored, never deleted, because deleting them would cascade into real movie and TV media. Leave monitoring off on those paths. Amixedscan collects video extensions only and would classify everything else as deleted, which moves the bytes to trash. Remove the path or repoint it at video content (#471). - Remote access turns on for every install that already has a config row. The old flag was never enforced, so a stored
falserecords a click rather than how the server behaved, and honoring it now would cut off working remote players.ENABLE_REMOTE_ACCESS=falseis unchanged and remains the hard off switch (#510). - Manual claim-code entry breaks against players that have not updated. An older app typing a code at an updated server gets "invalid or expired code". Use the QR on the same screen, which works across versions. In the other direction, an updated player pairing with a server older than this release falls back to the old unencrypted lookup, since the old server never published a sealed entry. The relay serves both route sets through the transition, and the fallback is removed one minor from now (#513).
- The import wizard is gone and
import_sessionsis dropped, which discards any in-flight wizard session. Those expired after 24 hours anyway and rescanning rebuilds the state (#472). - The scheduled library scan now writes a match candidate for every file it cannot link, which it never did before. On your first scan after upgrading, orphaned files that have been invisible for months appear in review. This is intended, and the scan's linking decision is unchanged (#472).
- An interrupted import no longer auto-resumes. A run interrupted by a restart is reconciled at boot and its job retired, so restarting is an explicit Start click. No work is lost (#472).
monitoring_presetis dropped frommedia_items. TV monitoring is derived from episodes, with onlymonitor_new_seasonsstored (#447).- PostgreSQL installs get column widening across the schema,
varcharto TEXT and int4 to bigint. Expect the migration to take longer on a large database (#448, #451).
Docker is ghcr.io/getmydia/mydia:0.14.0-beta.1, or :beta. Add -pg for the Postgres image. If you are already on :master you have all of this.
Full Changelog: v0.13.2...v0.14.0-beta.1