github getmydia/mydia v0.13.0-beta.4

latest releases: v0.13.0-beta.6, v0.13.0-beta.5
pre-release6 hours ago

Fourth beta for v0.13.0, cut from master. 14 PRs since beta.3. No migrations.

Playback quality

  • The player's quality switcher works on every platform. It previously rendered only on web, where it set a state field and showed a snackbar without changing the stream.
  • The rung ladder is derived from the source height, so a 720p file offers no rung above 720p. The chosen rung persists and restarts the session at the current position.
  • startStreamingSession accepts maxHeight alongside maxBitrate. StreamingSessionResult echoes the maxBitrate and maxHeight the server actually applied, which can be lower than requested on a relay connection.
  • Transcodes preserve aspect ratio and never upscale. Forced transcodes previously emitted -s 1280x720 with no aspect handling, so 2.39:1 and 4:3 sources came out geometrically wrong.
  • Output height is rounded to even on both the capped and uncapped paths. libx264 with yuv420p refuses an odd frame height.
  • Session matching compares position plus both caps. It previously compared position only, so a rung change reused the session already running at the old cap.
  • New MAX_TRANSCODE_HEIGHT operator ceiling, settable through env, config.yml, the DB overlay and the admin UI.
  • A chosen rung vetoes direct play. Original still direct-plays.
  • The playback transport cluster went from 256px to 192px, secondary buttons from 40px to 32px, padding from 20/12 to 12/8, so four discrete buttons fit down to 360px width.

File selection

  • The player streams the file you picked. It previously fetched streaming candidates keyed on the media id, so the selection could not be expressed, and the direct-play branch overwrote it with the server's pick. Reproduced on desktop only, because kIsWeb forces web onto HLS.
  • New Mydia.Library.FileRanking ranks by resolution, then bitrate, then id, with unanalyzed files last. Wired into Mydia.Streaming.Candidates, StreamController and the subtitle controller, which previously took the head of an unordered preload.
  • StreamController and the subtitle controller now filter trashed files. Only Candidates did before, so the candidates endpoint and the stream route could name different files.
  • Interlaced resolutions are handled in file ranking.
  • The streaming by-id path honors the trashed-file filter.
  • A downloaded item whose local file is missing falls back to asking by media item instead of sending the offline sentinel where a UUID was expected.

Playback reliability

  • Replacing an episode's file no longer breaks playback of that episode permanently. The streaming candidates query ran cacheFirst against an on-disk Hive store, so a cached entry kept naming a deleted file across restarts, rendering a black screen with an advancing timeline and no error. The query is now networkOnly, and poisoned entries correct themselves on the first play after updating.
  • This also affected every automatic quality upgrade, which writes a new media_files row and deletes the old one.
  • The player subscribes to Player.stream.error, so a failure after open reaches the error UI instead of a black screen. The subscription is gated on playback never having advanced, so recoverable decoder and network log lines do not replace playing video with an error page.
  • Audio tracks are re-detected as mpv finishes probing. The track list was sampled once, 500ms after open, so a file whose non-default audio stream resolved later offered no way to reach it.
  • The cast button is back on the playback screen. ChromeTopBar has a castAction slot that player_screen.dart never filled, so the pill rendered empty for the whole of playback. The pill is now the tap target rather than the 18px glyph inside it.

Recently added

  • Recently added means when content arrived, not when the record was created. A show added two years ago that received an episode last night was previously invisible to the rail.
  • Files group into slots, one per episode or movie. A slot's timestamp is its earliest file; an item's is the newest of its slots. Quality upgrades therefore cannot bump an item back to the top.
  • Computed at query time in a new Mydia.Media.RecentlyAdded. No migration and no denormalized column.
  • recentlyAdded gains three nullable fields: newEpisodeCount, latestSeasonNumber, latestEpisodeNumber. Cards show S04E02 or 3 new episodes.
  • favorites, unwatched and collectionItems report the corrected addedAt. Their episode-context fields stay null.
  • The /tv and /movies "Added (Newest)" sorts read the same value, so the web UI and the player agree. Fileless wanted items fall back to their own inserted_at.
  • The rail passes an explicit limit. The resolver previously materialized the whole 30-day window to return 20 items, which crosses SQLite's bind-parameter ceiling past roughly 32k items.
  • A GraphQL relay client could bypass the pagination ceiling by passing a zero cap. Fixed.

Linux Flatpak

The Flatpak channels are not serving this build yet. The build itself succeeded, but publishing to the OSTree repositories requires one-time operator setup that has not been completed, so flatpak.mydia.dev has nothing to install from. Use the mydia-player-linux-v0.13.0-beta.4.tar.gz asset for now. The commands below will work once the channels are populated.

  • The Linux player builds as a Flatpak targeting two OSTree repositories: mydia for stable, mydia-beta for prereleases. Both ship dev.mydia.player, distinguished by OSTree branch.

    flatpak remote-add --if-not-exists --from mydia-beta https://flatpak.mydia.dev/mydia-beta.flatpakrepo
    flatpak install mydia-beta dev.mydia.player
    
  • Built on org.gnome.Platform 50 with libass, libplacebo and mpv as manifest modules, so libmpv ships inside the app. The existing tarball links the system libmpv and dies at load time without it.

  • Desktop entry and AppStream metadata are added, so the player appears in GNOME Software and KDE Discover.

  • Signing runs in a separate job from building, so the GPG key is absent from the job that runs build code from the dependency tree.

  • The mydia-player-linux-*.tar.gz asset stays.

Activity feed

  • All 32 recorded event types are labelled from one registry, Mydia.Events.Presentation. Presentation previously lived in three maps that had drifted: 13 types were unhandled by the feed and 9 had no presentation anywhere, so download.stalled rendered as its raw key.
  • Event.changeset/2 validates :type against the registry, so an unregistered type cannot be recorded.
  • Filter chips added for the playback and plugin categories, which were reachable only under "All".
  • plugin.http_request is excluded from the global feed, where it fired once per plugin egress request. It keeps its dedicated viewer in the admin plugins logs modal.
  • Whole hours render without a trailing decimal.

Admin and settings

  • Every typed setting in Admin → Configuration raised a FunctionClauseError on blur, so the DB overlay worked for toggles but not for anything you type.
  • A setting whose value has not changed no longer writes a config row.
  • Adding or removing a Direct URL on the Remote Access page persists. The handler called a stub left behind when the WebSocket relay was replaced by P2P, whose entire body returned its input, and flashed "Direct URL added successfully" regardless.
  • The Updates section is hidden on iOS, where PlatformUpdater.forCurrentPlatform() returns null and the tile's actions were no-ops.

Player interface

  • Poster cards that only navigate no longer show a hover play button. Affects the library, favorites, recently-added, unwatched and collection grids, the home rails and search results. EpisodeRailCard, whose tap does start playback, is unchanged.
  • Search posters use the library's depth treatment. They previously had no resting shadow, a purple hover glow, a 1.03 hover scale, a 12px radius against the library's 8px, and ignored reduced-motion.
  • The Movies and TV Shows grids no longer render 128px below the app bar. _getTopPadding added the app bar height to a MediaQuery.padding.top that Flutter had already rewritten to mean the same thing under extendBodyBehindAppBar. The measured gap goes from 128px to 8px at devicePixelRatio 1.0 with a 47px status bar.
  • The freshness header overlays the grid instead of sitting beside it in a Column, where every background refetch pushed the grid down another 161px.
  • The ambient backdrop stays in step with the hovered poster, resets on the search screen, and its deferred writes are guarded against races on dispose and didUpdateWidget.
  • Movie and show cards compose from a shared PosterFrame that owns the poster depth contract.
  • The Chromecast glyph is drawn from the rounded icon family.

Filename parsing

  • Standalone anime absolute-episode numbering and (US)-style parentheses in titles are fixed. Corpus pass rate goes from 75.8% to 76.4%.
  • Both bugs were found by porting a dead parser's test suite onto the live one.

Removed code

  • Mydia.Library.FileParser, 589 lines with no production callers. Both call sites reached the live parser through alias Mydia.Library.ReleaseParser, as: FileParser, so edits to it changed nothing at runtime while a 795-line test suite reported confidence about it.
  • Mydia.Settings.QualityMatcher, its coverage audited against ReleaseRanker first.
  • The WebSocket relay stubs, the orphaned UserFavorite schema and the old home screen.
  • The min_quality_score config key. Its only reader was QualityMatcher, so setting it had no effect.
  • New mix mydia.dead_code task, advisory rather than a CI gate. A compiler tracer records module reference edges so alias renames resolve, and liveness is forward reachability from exempt roots grown to a fixpoint.

CI

  • The E2E stack runs the shipped production image. Dockerfile.e2e is deleted; it was a 351-line near-duplicate of the production Dockerfile. Seeding moved to scripts/e2e/ and asserts its own postconditions instead of ending each step in || echo "Warning: ...".
  • player/Dockerfile.test goes from 291 lines to 66 and no longer bakes player source, so editing player/lib/ stops invalidating the image.
  • Player E2E coverage goes from 1 test file to 3, and 8 executing tests. p2p_streaming_test.dart passes 6/6 unmodified, having never run in CI before.
  • Rolling :master tags are published for the E2E toolbox and the relay.
  • flutter test runs at --concurrency=1 in CI. At the default it silently drops files while exiting 0.

Upgrade notes

  1. No migrations in this release.
  2. min_quality_score is removed from config. It was read only by a module with no callers, so removing it changes no behavior. A leftover value is ignored.
  3. GraphQL additions are additive. startStreamingSession gains an optional maxHeight, StreamingSessionResult gains maxBitrate and maxHeight, and recentlyAdded gains three nullable fields. A player build older than this server keeps working.
  4. A player build newer than the server retries a rejected document once with a frozen legacy version, so a new player against an older server keeps its Recently Added rail rather than losing it to one unknown field.
  5. Until you update the player app, it still asks for streaming candidates by media id. That previously returned an arbitrary file and now returns the highest-resolution one, so a user who picked a lower resolution gets a consistent wrong answer instead of an intermittent one until the app updates. The web player ships with the server and is unaffected.
  6. Playback pinned to a deleted file corrects itself on the first play after updating the player. No app data needs clearing.
  7. The Linux Flatpak channels are not live for this build. Install the Linux tarball asset, which still requires libmpv on the system.

Docker is ghcr.io/getmydia/mydia:0.13.0-beta.4, or :beta. Add -pg for the Postgres image.

Full Changelog: v0.13.0-beta.3...v0.13.0-beta.4

Don't miss a new mydia release

NewReleases is sending notifications on new releases.