Skip to content
Domekologe edited this page Aug 8, 2026 · 20 revisions

Web UI

🌐 English · Deutsch

The interface is built as a Progressive Web App (PWA) — it can be "installed" from the browser and is available in German and English (language switcher at the bottom of the sidebar, stored per user — the same choice, with flags, sits under Your profile → Language). Which language a newly created account starts in is an instance default an admin sets under Settings → Design → Default appearance for new accounts → Language; it never changes accounts that already exist. The same block sets the theme, the accent colour and — under Advanced Settings — the eight extra design options a new account starts with (glow effect, header colour, faded header, skeleton loader, selection highlight, download highlight, button-press animation, icon animations). Everyone can still change all of them afterwards on their own profile page, and accounts that already picked their own are left alone.

Screenshot: WebUI overview

Navigation (sidebar)

Discover

Page Path Description
Home / Search across all sources, browse categories (new anime, popular anime, new series, popular series, new movies), random anime. With a TMDB key configured, cards are enriched with posters, ratings, age ratings, genres and more (Integrations). The order, visibility and per-section layout of the source blocks are configurable (Configuration → Sources)
Favourites /favourites Per-user series bookmarks
Library /library Manage and play downloaded files (Library)
Queues modal One window for all three queues, switched with the segmented control at the top: Downloads (live progress, Download System), Encoding (deferred after-download encodes, Encoding) and Upscaling (Anime4K upscaler, Anime4K Upscaling). Plus Everything, the merged view across all three. The sidebar has one Queues entry that opens this window straight on Everything; its badge is the sum of all three queues. See below
Advanced search /advanced-search TMDB Discover: genres, ratings, years, runtime, keywords, original language, series status, networks and streaming providers, grouped in a floating filter menu with quick presets (only visible when enabled in CineInfo) — see below
Calendar /calendar Upcoming episode air dates & movie releases for AutoSync, Seerr and Library titles (only visible when enabled in CineInfo) (Calendar)

Management

Page Path Description
Seerr requests /seerr View, approve, decline Overseerr/Jellyseerr requests and download them directly
AutoSync /autosync Automatic series synchronisation (AutoSync)
Statistics /stats Download, queue and sync statistics
Download History /history Searchable per-episode log with start/end time and details (Download History)
UpTime /uptime Uptime-Kuma-style live monitoring of the source sites — appears when enabled under Integrations → UpTime (Integrations)

System

Page Path Description
Notifications /notifications Your notification services and events (Notifications)
Integrations /integrations Jellyfin/Plex, MediaScan, Seerr, CineInfo/TMDB (Integrations)
Encoding /encoding Re-encoding settings, admin only (Encoding)
Settings /settings Global settings + user management, admin only (Configuration)

Menu entries contributed by Modules carry a small "M" pill next to their name, so you can tell built-in pages and module pages apart at a glance.

The new home page (opt-in)

The new home page replaces the classic discovery block with rows grouped by question instead of by source. Every account picks its own layout — trying it out changes nothing for anybody else on the instance.

Three ways in, all the same setting:

  • the "Try the new home page" banner at the top of the classic page. It appears once per account and never comes back after you answer it, whether you switch over or click Don't show again;
  • "Your home page" → Layout on the home page itself (the button under the source chips on the classic layout, the sliders icon next to the filter dropdowns on the new one). Follow the instance default is the third option there, and the one you start on;
  • Settings → Start Page for an admin, which additionally sets the default for accounts that never picked one.

Your rows come first. Each one appears only when it has something to say, so a fresh install shows none of them:

  • Continue watching — every file you stopped in the middle of, with the remaining runtime and a progress bar. Clicking a tile resumes it in the player, at the second you left off.
  • New in your library — what the library scan picked up most recently.

Below the discovery rows, two more that are really other pages in miniature:

  • Your watchlist — your favourites.
  • Airing next — the next two weeks from the calendar (needs the CineInfo calendar enabled and a TMDB key).

Every row's heading names where its content comes from — from your favourites, from the calendar, from your playback progress — and that label links to the page that owns the data.

Then the discovery rows. New this week and Popular right now each mix every enabled source into one row, interleaved so a row never opens with twenty cards from the same provider; Movies collects whatever did not already appear above. A title is only ever shown once on the page — the topmost row it qualifies for wins.

  • Every poster carries a small label naming its source, since the heading no longer does. A title offered by two sources becomes one card whose label reads e.g. FilmPalast +1click that label to pick which source to open.
  • Three dropdowns above them — Sources, Type and Status, side by side, with the page-settings button next to them — say what is on and filter at the same time. They are the same controls the phone layout already used, now at every width: the row of clickable pills is gone. On an instance with nine sources that row was a twelve-pill ribbon that wrapped onto a second line before a single poster was visible, which is a lot of screen spent on a filter most visits never touch. Both are multi-select. A source switched off in Settings → Sources is not listed at all — not greyed out, not as a disabled entry: a list of things you cannot have is not a filter, and the place that explains the absence is the switch itself. An unreachable source is still listed, marked offline. Type (series, movies, 18+ when hanime is enabled) works the same way. Your choice is stored on your account, so the filters you set on the desktop are the ones your phone gets.
  • Status answers a different question from Type: not what is this but have I already got it. Two entries, Already downloaded and On Auto-Sync, both on by default. Switch one off and every card carrying that badge disappears from the discovery rows, and — because those rows hold a reserve — the next cards move up to fill the gap, so the row keeps its length instead of going short. The two answers are the same ones behind the badges on the card, so what the filter removes is exactly what was marked. Unlike Sources and Type this one may be switched fully off: "show me only what is new to me" is the point of it, and it cannot empty the page on its own.
  • The Status filter applies to the discovery rows (New this week, Popular right now, Movies) and deliberately not to the personal ones. New in your library is a list of things you have and Continue watching a list of things you started — filtering "already downloaded" out of those would not narrow them, it would empty them.
  • A source that is not answering is named, with a Try again button — an empty row and a dead site no longer look the same.
  • Each row is its own request (/api/home-feed/row/<row>), and rows below the fold are only fetched once you scroll to them. One unresponsive site therefore delays one row instead of the whole page, and a home page you never scroll does not scrape five sites. The 18+ source is only ever fetched when it is selected. Each row also fetches a reserve of cards beyond the number it shows, so deselecting a source or a type no longer shortens it: the next-best cards move up and the row keeps its configured cards-per-row length (Settings → Start Page) for as long as there is anything left to show.
  • The reserve is not blank. The CineInfo/TMDB details behind the cards a row is holding back — genres, rating, age rating, streaming pills — are resolved in the background, in batches, while you are looking at the row. Changing the Sources or Type filter therefore promotes finished cards instead of empty ones that fill in a moment later. An answer already fetched for a title is also kept for the rest of your visit, so the same title appearing in two rows costs one lookup, not two.
  • Posters, the "already downloaded" and Auto-Sync badges, TMDB enrichment and the click-through are the same components as on the classic page. The "✓ Downloaded" badge no longer depends on which source you arrived from: the title is matched against the folder on disk ignoring punctuation, spacing and case, and in both directions, so a show one site spells <Title>: <romaji subtitle> is still recognised in a folder named after the site that downloaded it first. With a TMDB key configured it also recognises a folder whose name shares nothing at all with the card's title — see Recognising what is already there.

Fill the gaps sits right behind Popular right now by default. It lists series with holes in them — 3 missing, S2E4, S2E7 — and clicking one hands that title to the ordinary search, so a season that is one episode short is one click from being finished rather than a number on the statistics page. Slots you marked "never mind" on Statistics → Media stay hidden here too.

Not every gap is one you intend to close. Each card carries a × in its top-right corner that removes that series from the row for good. It writes the same ignore entry the statistics page writes — for the whole series, not the single episode — so a series dismissed here also disappears from the incomplete series list under Statistics → Media. That modal is also where you take it back: open Incomplete and switch to its Ignored tab — restoring the series there brings it back to both places.

Density and modes live next to the filter dropdowns:

  • Comfortable / Compact / List. Compact fits about twice the cards on a wide screen; List stops the rows scrolling sideways and lays them out as a dense text grid instead. The choice is stored on your account.
  • Standard / Kids. The switch only appears once an admin has both ticked Offer kids mode on the home page and set a PIN under Settings → Start Page → Kids mode — both, because a mode nobody can leave would lock the account out of its own home page. The age limit is enforced by the server while the rows are built, so it cannot be undone by editing a URL, and the 18+ entry becomes a stated fact rather than a switch. Entering the mode never asks for anything; leaving it opens a PIN dialog. A note above the rows says the page is limited, because a limit with nothing to remove otherwise looks like the button did nothing.

Keyboard. Arrow keys walk a row, / move between rows and keep your column, Home/End jump to the ends of a row, Enter opens. Each row is a single tab stop, so Tab moves between rows rather than through forty posters.

The setup checklist appears on a fresh instance — sources, TMDB key, library scan, modules, media-server profile — and disappears by itself once everything is done. Hide this removes it for good; steps only an admin can act on are not shown to a normal account at all.

Review is a button in the panel bar, next to System: hours watched, most-watched title, top genre, downloads, volume, busiest source, biggest single download, for the month that just ended. It is a panel rather than a card on the page because a recap is something you go and look at, not something that interrupts the page you opened to search on. Its watched half comes from your linked Jellyfin/Plex profile and is simply absent without one — it is never estimated from MediaForge's own playback positions, which only know about files MediaForge itself downloaded.

Your Jellyfin/Plex profile

If an admin has connected a media server (Settings → Integrations → MediaPlayer), Your home page → Your Jellyfin/Plex profile lets you pick your user on that server. Then:

  • Continue watching shows what the media server knows — including everything you started in the Jellyfin or Plex app and MediaForge never saw. Those cards open the media server rather than MediaForge's own player, since there is no local file behind them, and they say which server they came from. The local row is replaced, not merged: two rows of half-truths in different orders is worse than one.
  • The monthly recap counts what you watched there.

Artwork is fetched through MediaForge, so your browser never needs the media server's address or token — which also means this works from outside that network.

The search box (on both home pages) remembers what you searched for and now previews as you type: matching titles in your library, entries on your watchlist (which open that series directly instead of searching for its name), and your recent searches. Arrow keys pick an entry, Enter takes the highlighted one — or, with nothing highlighted, searches exactly what you typed. Press / anywhere on the page to jump into it.

The panel bar

Under the search field sits a row of buttons, and below it one panel whose content depends on the button you pressed. It answers the question the poster rows cannot: what is this instance doing right now.

  • Queue — what is running and what is waiting, with progress.
  • Activity — what finished (or failed) recently.
  • Library — how much you have, and what arrived last.
  • Storage (admins) — how full each download path is; the bar turns amber at 90 % and red at 95 %.
  • System (admins) — version, encoding and upscaling queues, whether the downloader is paused, and every failed queue item.

Click the button that is already open to close the panel again. Which panel you had open is remembered on your account, so the page comes back the way you left it — and a button with something worth knowing behind it carries a small number.

Only the open panel refreshes (every 20 seconds, and only while the tab is in front), so the bar costs one request per visit rather than one per box. On a phone the buttons scroll sideways and the panel is full width.

Admin-only panels are not merely hidden for a normal account: the data is not sent either.

For module authors: register_home_panel() adds a button and a panel of your own — see Modules.

Settings → Start Page

Which rows you get, in which order, and how many cards each holds is a setting, not a decision someone else made for you. Settings → Start Page has it — and the same panel opens from the sliders button next to the chip row on the home page itself, which is how a non-admin account reaches it (/settings is admin-only).

  • Rows — switch any row off and drag (or arrow) the rest into the order you want. A row you switched off is not collected at all, so hiding Airing next on an install without a TMDB key also stops the work behind it.
  • Cards per row — 10, 20, 30, 40 or 60.
  • Reset to the instance default — hands the page back to whatever the admin configured.

An admin sees a second half: Default for new accounts, with the same row list, the cards-per-row setting, the default chip filters (which of the enabled sources and types are off the first time someone opens the page — a source switched off under Sources has no chip to begin with) and the default layout switch. Changing the default never overwrites what a user already chose — each part of a user's layout, the layout itself included, only stops following the default once they change that part themselves.

The classic layout stays the default, keeps one block per source with its own rows, and remains what dashboard-widget modules extend. Switching takes effect on the next load of / — no restart.

For module authors: a source registered with register_home_feed_source() appears in these rows and in the chip row on its own — see Modules. The classic home page still has its rows in the template, so it cannot show module sources.

The queue window

All three queues live in one window since July 2026. It is opened from Queues in the sidebar (or the queue icon in the mobile top bar); the segmented control at the top switches between Everything, Downloads, Encoding and Upscaling, each with a count of what is still moving. Everything is the merged view — one list across all three queues. Each entry in the sidebar keeps its own badge, and Queues itself shows the sum.

  • One running job at the top, large. Poster, title, episode, where it comes from, and a meta line saying what it is doing right now — speed, remaining time, size, language. Underneath it the stations a download passes through, Download → Encoding → Upscaling → Library, so the percentage is not the only thing that says where it stands. Encoding and upscaling jobs light up their own station.
  • Everything else as slim numbered rows in three groups: Needs you for anything waiting on a decision (a captcha, a failed or partly finished download), Up next in the order it will be worked through, and Finished today with the time each one landed. The coloured edge on the left names the station rather than repeating the status: violet for downloads, yellow for encoding, blue for upscaling, green for finished, red when it needs you.
  • A row that failed can be clicked open. A red chevron in front of the title marks it; clicking the row (or focusing it and pressing Enter/Space) unfolds the error underneath. Downloads list one line per failed episode with its episode code, encoding and upscaling show their single message. The panel stays open while the list refreshes, so a long ffmpeg message can be read in peace. Jobs that finished but had individual files fail carry the note too, even though their status is completed.
  • Hovering a row swaps its status word for the buttons that apply to it — move up/down, remove, cancel, restart. On a touch screen they are always visible. Clicking those buttons does not unfold the error.
  • Clear finished removes completed, failed and cancelled entries from whatever the current segment shows. Pause all appears when there is something to pause and the big card is not already offering it.

On a phone the window fills the screen, the four switch buttons share the width, and the episode column is dropped so title and status keep their room.

Search

The search bar on the home page queries, depending on the selected source:

  • AniWorld — anime search via the aniworld.to API
  • serienstream.to — series search via serienstream.to
  • FilmPalast — movie search via the autocomplete API; result URLs are validated automatically (umlauts in titles are handled)
  • MegaKino — movie & series search via the megakino.to JSON API
  • filmo.to — movie search via the server-rendered results page (posters included)
  • 9anime / Aniwaves — anime series search, English-only catalogues; only queried when the source is enabled (they ship off, no age confirmation needed)
  • hanime — adult (18+) search; only queried when the source is enabled

A source a module registers with register_search_source() joins this list on its own — nothing has to be configured for it, and it is queried under the same rules as a built-in source.

The stream-lookup modal — the Search / Search streams button on Advanced Search, in the Calendar's entry details and in the shared detail modal a module can embed — fans out over every enabled source in your own source order, module sources included. It used to query four hardcoded sites (AniWorld, serienstream.to, FilmPalast, MegaKino), so filmo.to, 9anime and Aniwaves were missing from it. The "Also hide disabled sources from search results" preference is honoured, and adult sources stay opt-in.

Catalogue

/catalogue shows the complete A–Z list a source site publishes — AniWorld's ~2,400 anime and SerienStream's ~10,800 series — so you can mark any number of titles and hand them all over at once. It sits in the sidebar under Home, next to Advanced Search and Favorites.

Control What it does
Source chips The list is one merged A–Z list; the chips narrow it to a source. A title both sites carry is a single row with one pill per site, and pressing a pill picks where it is downloaded from. A source switched off in Settings is shown but not selectable.
Status chips Narrow the list to what is in your library, already queued or already on Auto-Sync. Each one carries a live count.
Filter Instant search over the visible title and the site's own alternate titles — "Shingeki no Kyojin" finds "Attack on Titan".
Select all shown Marks everything the current filter left, not the whole catalogue.
Details Cover, description, genres, year and the episode list for one title, with a check on every episode already on disk. Season 1 opens straight away; the others load when you open them, because each one is a live request to the source site. Opening the dialog never changes your selection.
Only missing episodes On by default: episodes already on disk are left out instead of queued and skipped later.

Marked titles are remembered across filtering, switching source, opening details and reloading the page.

Queue or Auto-Sync

  • Add to queue — one queue entry per series, holding every episode that exists today.
  • Create Auto-Sync jobs — one Auto-Sync job per series. Admin only, the same as creating one on the Auto-Sync page: a recurring job keeps downloading long after the click. Nothing is downloaded by that click; the Auto-Sync worker picks the series up and keeps picking up new episodes later. For a running series this is usually what you actually want.

Because /api/download needs a list of episodes, the selection has to be expanded first: each series page and each of its seasons is fetched. That happens on the server, one series at a time with a short pause, as a background job you can watch and stop — closing the tab or navigating away does not stop it, and coming back to the page picks the progress display back up. The job also appears in Settings → Operations like every other worker.

The language you pick applies to the whole selection, so a language group (Settings → Downloads) is usually the better choice: every series has different languages, and a single fixed one fails on a good share of a large selection.

There is deliberately no ceiling on how many series one action may carry: marking the whole catalogue is a legitimate thing to want, and the cost of a large selection is handled where it belongs — one series at a time, in a background job you can stop — rather than by refusing the request.

Advanced search (TMDB Discover)

/advanced-search is a browse-by-filter page on top of TMDB's Discover API — useful when you do not have a title in mind yet. It only appears in the sidebar when Advanced search is enabled under Integrations → CineInfo.

Filters are grouped into four sections, switched from the floating menu on the left (on a phone it opens as a drawer from the button in the top-right corner). The number badge on a menu entry shows how many filters are active in that group.

Group Filters
Basics Media type (series/movies), sorting, genres, exclude genres
Rating & Year Minimum rating, minimum number of votes, release year from/to, runtime from/to
Streaming Watch region, include providers, exclude providers
Details Keywords, original language, series status, TV network

Below the menu entries sit six Quick Presets (Top Anime, Popular Series, Popular Movies, New this year, Action & Thriller, Hidden Gems) — one click applies a complete filter set and searches immediately.

Notes:

  • Minimum number of votes is the filter that makes "sort by rating" useful: without it the top of the list is filled with titles rated 10.0 by three people.
  • Series status and TV network only apply to series and are hidden when Movies is selected. Genres are also reset when you switch the media type, because TMDB uses different genre ids for series and movies.
  • Provider filters need a watch region — TMDB only knows what is streaming where once a region is set.
  • Active filters are shown as chips above the results; the ✕ on a chip removes that one filter and searches again.
  • Results are paginated with page numbers and a jump box. TMDB serves at most 10,000 results per query, so the page count stops there — narrow the filters if you hit that limit.
  • Filters are remembered for 24 hours and the last result page for 5 minutes, so leaving the page and coming back does not throw your selection away.
  • Hovering a result card (or, on a phone, just looking at it) shows two buttons: Search runs the stream search for that title across every enabled source in your own source order — module sources included, disabled ones skipped, adult sources only when opted in (Search). Details opens the full TMDB description together with rating, vote count, year, season count, genres and original language — the old page only showed a truncated version of that text in a hover overlay, which a touch device could never reach. The Details dialog has its own Search streams button, so you can read first and search after.
  • Cards for titles you already downloaded or that are in AutoSync are marked accordingly.

Languages & providers in the picker

Key aniworld.to serienstream.to
1 German Dub German Dub
2 English Sub English Dub
3 German Sub English Dub (German Sub)
4 English Dub

Providers offered are the active hosts (currently VOE, Vidmoly, Vidoza, VeeV) — the app probes at startup which extractors are actually functional.

Statistics

The statistics page (/stats) aggregates download, queue, media and sync metrics and visualises them as charts. A time-range switcher in the page header (7 / 30 / 90 / 365 days) applies to every trend chart; the range is served by /api/stats/trends alone, so switching it does not re-scan the library.

KPI row. Downloads, data volume, average speed and success rate for the selected range. Each card carries an inline sparkline and a trend badge comparing the newer half of the range against the older half. The speed card is clickable and opens the speed-details modal.

Trend charts.

  • Downloads per day — successful vs. failed as a two-series area chart.
  • Volume per day — downloaded data amount as bars.
  • Speed over time — daily average in MB/s.
  • Activity by weekday & hour — a heatmap showing when your downloads actually run.

Breakdowns. Donut charts by provider and by trigger (manual, Auto-Sync, …), plus horizontal bars for languages and your top titles.

Queue & Auto-Sync. Queue status as a donut over all six states, and an Auto-Sync ring showing how many of the configured jobs are enabled, with episodes found and the last check time.

All-time totals. A compact fact strip with the lifetime numbers (total downloads, episodes, movies, failures, last 24 h, average duration, total volume) — independent of the selected range.

The trend charts read from the per-episode download history table. On a fresh install, or after the history retention window has pruned everything, the charts show an empty state while the KPI cards fall back to the lifetime totals.

All charts are rendered as inline SVG by MediaForge's own chart module — no external chart library, no CDN. They follow the active theme (including Theme Packs) and are fully responsive; hovering or tapping any series, bar, slice or heatmap cell shows a tooltip with the exact value.

On phones every chart starts collapsed — tap its title to expand it — so the numbers and tables are not buried under a stack of full-height charts. The modal tables also switch to one labelled card per row instead of scrolling sideways.

Media category (optional)

When Media statistics is enabled in Settings → Library and Statistics, an extra Media category appears, sourced from the Library and kept current by its file watcher:

  • Movies, Series, Episodes, Disk usage (with the total file count), Incomplete and Duplicates as KPI cards.
  • Charts for completeness (share of complete series), resolutions, codecs, the largest series by disk usage, and — when more than one library location is configured — disk usage per storage location.
  • Incomplete is clickable and opens a modal listing every series with gaps — missing episodes inside a season or a missing season — together with the location and the missing slots (e.g. S1E3, S2).
  • Duplicates is clickable and opens a modal listing media that exists more than once with the same identity — same series/movie title, season and episode (and, in language-separation mode, the same language) — but with differing files. The most common case is the same episode kept in two resolutions (e.g. 480p and 720p): resolution and codec are deliberately not part of the match, so differing quality is exactly what gets flagged. Movies are matched by their filename so distinct films in one folder are not grouped together. For each duplicate the modal shows every copy's resolution, codec, size and full file path, and marks the largest copy in green so it is obvious which one to keep. A summary strip at the top reports how many groups and files are affected and how much disk space you would reclaim by keeping only the largest copy of each, plus a donut of the resolution mix. A search box filters the table by title.

The language is part of the identity, so the German and the English version of the same episode are not duplicates of each other. With language separation off, the language is not a folder MediaForge knows about — it sits inside the file name via the {language} placeholder of the naming template, so it is read back from the file name (and from the season/folder names as a fallback).

Only different files count as copies: the same file reached through two overlapping library locations is counted once, matched by its real path (case-insensitively on Windows). Locations that are no longer configured are ignored entirely, and their leftover scan data is removed on the next library scan — an old cache entry from a deleted custom path used to make every episode look duplicated.

The duplicate and incomplete-series lists are loaded on demand, one page at a time, and are not part of the stats page payload. On a large library that matters: 90 000 files produce tens of thousands of duplicate groups, which used to be serialised into the page response in full — around 28 MB on every load, whether or not you ever opened the modal. The results are also cached server-side and only recomputed after a library scan finishes.

All three modals (speed details, incomplete series, duplicates) share the same layout: an icon header, a summary strip of key figures, a chart where one helps, and a searchable, paginated table — 20 entries per page, with the range shown above the pager. Searching filters the whole list, not just the visible page, and jumps back to page 1. In the incomplete-series modal your selection is kept while you page and filter, so you can tick series across several pages and ignore them in one go; the counter next to the button shows how many are selected. On phones each table row becomes its own labelled card.

Completeness is determined purely from the local library by detecting numbering gaps; a series that is only missing its newest, not-yet-released/-downloaded episodes (no gap) counts as complete.

Favourites

The Favourites page (/favourites) holds your per-user series bookmarks. Add a series to your favourites from a series page (the ♥ button in the detail view); each user has their own list. Use it as a quick shortlist of series you follow or want to start.

Each favourite is shown as a card with metadata badges for its type (movie / series), its source (AniWorld, SerienStream, Megakino, …) and the language that was selected when you saved it. Once you have favourites, a control bar lets you search by name, sort (recently added, oldest first, name A–Z / Z–A) and group the list by source, type or language.

Each card has three actions: Open jumps to the series' detail view, Auto-Sync (series only) opens the Auto-Sync setup for that series directly, and Remove deletes the bookmark.

Playback & streaming

Anime and series can be watched directly in the browser — either from a downloaded file (Library) or streamed straight from the provider without downloading.

Screenshot: in-browser player

Direct streaming (play without downloading)

Each episode in the search/series view has a play (▶) button. Clicking it streams the episode immediately:

  1. The app resolves the provider stream and, for HLS sources, proxies the provider's native playlist/segments through the server (passing only the required headers) — no FFmpeg, no re-encode, smooth playback and instant seeking.
  2. If the source is not HLS, it automatically falls back to a server-side HLS transcode (FFmpeg).
  3. The source can be changed live from the badge in the player's title bar — see The player below.

The player

The controls sit on top of the picture and fade out after a few seconds of inactivity, so nothing steals height from the video.

Transport. Play/pause plus four jump buttons: 10 s and 30 s in each direction. Repeated taps add up and are applied once, so four taps forward jump 40 s in a single move rather than restarting the stream four times.

Tracks and quality. Subtitles, audio track, quality and playback speed are picked from the rail (or from the settings sheet on a phone):

Picker Library file Direct Play
Subtitles every embedded text track, converted to WebVTT on demand; picture-based tracks (PGS, VobSub) are drawn into the frame instead whatever the provider's HLS carries
Audio track every embedded track, by language and channel layout the provider's alternative audio renditions, if any
Quality Original plus 1080p / 720p / 480p / 360p downscales the provider's own renditions when it offers several, otherwise the same downscales
Speed 0.5x – 2x 0.5x – 2x

Changing the audio track, the quality or a picture-based subtitle restarts the server-side transcode — FFmpeg cannot switch them inside a running output. The position is kept, so playback resumes where it was after a short pause.

Chapters, intro and preview. Chapters in the file are drawn as notches in the seek bar and named in the hover preview. For downloaded files the server builds a sprite of preview frames in the background; until it is ready the preview shows the time only. Anime episodes with aniskip data get a Skip intro button that appears only inside the marked range.

Up next. Towards the end of an episode the next one is offered with a countdown. Autoplay can be switched off in the settings menu; the setting is remembered per browser.

Touch. Double tap the left or right third to jump 10 s, swipe up/down on the right half for volume and on the left half to dim the picture, press and hold for 2x speed, swipe down to close. Dragging the seek bar works with a finger — in the previous player it was mouse-only and did nothing on a phone.

Keyboard.

Key Action
Space / K play / pause
/ 10 s back / forward
Shift + / 30 s back / forward
J / L 10 s back / forward
/ volume
09 jump to 0 % – 90 %
C subtitles on / off
M mute
F fullscreen
P picture in picture
< / > slower / faster
N next episode
Esc close (or close the open menu)

The source picker (Direct Play only)

When an episode is streamed rather than played from disk, the title bar carries a badge with the running hoster, language and resolution. It is also the button that opens the source picker.

The picker lists every hoster the site offers for every language, not just for the one selected on the series page. Each entry is measured: the server resolves the link and times the first response, and the result is shown as signal bars plus the time — so a hoster that is listed but dead is visible as such before you switch to it. Measurements are cached for a few minutes and Re-check forces a fresh round.

Switching keeps the playback position. If the running source dies mid-episode, the player does not show a dead error screen: it offers the next best source with a short countdown and switches on its own unless you pick something else.

Inside a SyncPlay room the picker is locked, because switching the source would desynchronise everyone else in the room.

Resume per user

The playback position is stored per user. Reopening a partly-watched title (downloaded or streamed) offers Resume (at the saved time) or Start over.

Watch Together (Syncplay)

The player has a Watch Together button that joins a Syncplay room. Play/pause/seek are synchronised with everyone else in the room — including people using the Syncplay desktop app with mpv/VLC, because the server speaks the real Syncplay protocol. The Syncplay server is configured under Settings → Syncplay.

Web Console

Admins can enable a read-only Web Console under Settings → General that mirrors the live application/FFmpeg console output directly in the browser — handy for diagnosing a stream or download without shell access.

Other UI features

  • Update notice: The app checks for new versions every 24 h (GitHub releases, or the models branch for dev installs) and shows a badge in the sidebar.
  • Version display: Bottom of the sidebar (vX.Y.Z, with commit hash for dev installs).
  • Light/dark theme and a responsive layout for mobile devices.
  • Prefetch: A background worker warms browse lists, posters and TMDB data every 15 minutes so the home page loads instantly.
  • Dev infos: Posts in the dev infos view can be marked as read individually; the sidebar badge shows the unread count and updates immediately. The read state is instance-wide, not per user. Posts carry a type: feature, fix, announcement, warning, important or release. Warning posts additionally appear as a dismissible banner at the top of the home page. Important posts show that banner and open a modal on the home page that cannot be dismissed — confirming it marks the post as read and opens the dev infos page. The modal keeps reappearing until the post is marked as read (it is not a per-browser dismissal). Release posts announce a version: the home page banner shows the headline, the version number and a Go to updates button that jumps straight to Settings → Updates; on the dev infos page the post shows the text plus a collapsible Changelog box with that version's release notes. No modal.

"Because you watched X"

A personal home row built from what is already on disk — no ratings, no collaborative signal, no user graph. MediaForge has none of those, and inventing one from a single household's viewing would be astrology with a progress bar.

What it does have is reliable: what you finished, what is in the library, and what the TMDB cache already knows about both. The most recently finished title becomes the seed (recency beats "most watched" — what somebody finished last night predicts tonight better than a binge two years ago), and library titles sharing its genres are offered, ordered by how much they overlap.

Three deliberate limits:

  • The row names its seed in the heading. It is the only row on the home page that guesses, and a suggestion nobody can trace back to a reason is a suggestion nobody trusts — naming X makes a bad card read as "wrong guess" rather than "why is this here at all".
  • Titles you already watched are excluded, and so is the seed itself.
  • If there is no seed, or the seed has no genre information, the row does not appear. A section header with nothing under it reads as a bug.

Everything comes from the caches — never the disk, never the network. The home page is the first thing that renders after login, and a row that blocks on a filesystem walk is how an overview page ends up feeling worse than the list it replaced.

It sits behind "Continue watching" in the default order: the thing you already started beats anything this can infer. Kids accounts do not get the row at all, since the seed would come from the shared progress bucket.

Skeletons and empty states

Every page used to invent its own waiting state — a spinner here, "Loading…" there, a blank area somewhere else. Three problems, in increasing order of cost: it looked like three applications; a blank area cannot be told apart from "there is nothing here", so you cannot tell whether to wait or leave; and content arriving into an empty box makes the page jump.

There is now one set of blocks (mf_components.css + window.MFSkeleton), shaped like the thing that is coming, so the real cards drop into exactly the space the placeholder held. The shimmer is a gradient sweep rather than an opacity pulse — dozens of boxes pulsing in step read as a fault light, a sweep reads as progress — and it stops entirely under prefers-reduced-motion.

Empty states are the other half: they say what is missing and, where there is one, what to do about it.

Offline behaviour (PWA)

MediaForge installs to a home screen and receives push notifications, but until now it was a PWA in name only: the service worker cached two files and never served them — there was no fetch handler at all — so the app went blank the moment the network hiccupped.

It now handles fetches, with three deliberately different rules:

Request Strategy
/static/… Cache first, refresh in the background. A stylesheet from disk is the difference between rendering instantly and flashing unstyled
Page navigations Network first, cache as fallback, offline page as a last resort
Everything else, including /api/ Network only, never cached

That last row is the important one. Caching API responses so the app "works offline" is tempting and would be actively harmful: a queue claiming three downloads are running while the server is unreachable, or a library listing full of files deleted this morning, is worse than an honest "you are offline". Stale operational data reads as truth.

The offline page therefore shows no data at all and says so. Range requests (video) pass through untouched — the player relies on the server's own 206 handling, and a service worker "helping" there breaks seeking. Non-GET requests are never intercepted: a POST replayed from a cache would be a download queued twice.

Offline playback — downloading episodes for a flight — is not implemented. It needs Background Fetch (Chromium only), range-request handling and a storage-quota story, and a half-built version that silently keeps the first 40 MB of a file is worse than not offering it.

Clone this wiki locally