Skip to content

Releases: LumoraPlayer/Lumora

Lumora v6.2

Choose a tag to compare

@github-actions github-actions released this 26 Oct 21:04

Lumora v6.2

Catalogue

  • Six-figure providers no longer run the app out of memory. The merged catalogue used to be one resident Channel list - a 300k-item Xtream panel measured ~260 MB, plus a copy during the merge and another while the disk cache was parsed, which killed a TV stick as soon as the provider finished loading. Films and series now live in the Room catalogue (catalog_channels) and are read back as capped poster shelves and 120-card grid pages; only the live list stays resident (~40 MB for 55k channels). Search, detail, version chips, Continue Watching, Trakt/Simkl matching and Discover ownership query the database instead of scanning a list.
  • The first films/series build is a database pass, not a parse. Category rows come from the existing grouping pipeline fed by a keyset-paged cursor; one card per title is picked in SQL (MAX(rank) per duplicate group: own library, then quality, then poster, then provider order), and backup copies sort behind visible ones without leaving the group.
  • A failed refresh no longer destroys the catalogue or postpones the retry. Each ingest writes under a generation token and only deletes the rows it did not see once the fetch completes; a dead provider leaves the previous rows in place, and a failed live fetch keeps the live list already on screen. A load where no source answered no longer stamps the refresh time, and a VOD-only panel (no live channels) stamps it on success instead of refetching its whole catalogue every launch. Provider removal, content-type switches and backup demotion update the database explicitly.
  • The catalogue has its own database file. lumora.db stays the small user-data database (guide, recordings, watch history) and remains eligible for Auto Backup. The rebuildable catalogue - whose stream URLs carry the panel credentials iptv_prefs.xml is excluded for, and which can exceed Auto Backup's 25 MB per-app quota - lives in lumora_catalog.db and is excluded from Auto Backup and device transfer.
  • The old channel cache file is gone. channels_cache.txt / channels_cache_v2.txt are never read again and are deleted on the first load; the one-time refetch after updating replaces them with the database.
  • A cold start paints what is stored, however old it is, and refreshes behind it. The live table is read as soon as the Activity exists so the query and Channel mapping overlap with layout inflation, the storage-invariant prune runs after the first paint instead of before it, and a background refresh no longer re-derives the live grouping and rebuilds the sidebar for every provider that answers - it swaps in once when the whole load is done. A 16k-channel panel went from 5.7 s (11.8 s whenever a cache missed) to ~4 s, and a catalogue past its 24 h TTL opens just as fast: the stored one is on screen while the fetch runs, and only a load where at least one source answered stamps the TTL.
  • The derived cache is actually hit now. The live grouping key hashed the whole resident list, so the anime catalogue appended after the first paint (it rides along as SERIES) gave the same output a second key and overwrote the entry the next cold start needed; sidebar rows built with anime sections did the same to the row cache. The live key now covers only the LIVE rows that pass consumes, and rows built for a plugin-supplied list get their own cache slot, so both variants stay warm. A miss is no longer silent: LumoraPerf logs the stored key beside the computed one.
  • Adding a provider says how big it is, and shows its progress. A banner floats over the app while a catalogue streams in - provider, phase, items and megabytes, with a progress bar - so a six-figure panel is a visible wait instead of a blank screen. Past 50 MB the app says so plainly ("this playlist is extremely large - ask your provider to disable content you do not need"), the size is remembered on the provider (Settings shows "147 MB · very large"), and the end-of-load summary repeats it. The banner carries a Hide button, and the warning line keeps saying what is importing and how far it has got. The banner is deliberately not the status row: that one only owns the screen when nothing else does, which is exactly not the case when a provider is added over a catalogue that is already up.
  • Films and Series open without waiting for each other. The VOD pass used to build Films, then Series, then hand both back - so opening Series after any catalogue change waited for the entire Films build first (29k films measured 23 s before Series even started). It now builds the tab you are looking at first, publishes each tab the moment its category rows are ready, and fills that tab's poster shelves afterwards, so the sidebar and the tab itself appear while the rest is still building. A tab switch no longer blocks behind the other tab either.
  • The previous VOD content stays on screen while a new one builds. The cache is now stale-while-revalidate: a catalogue change or a shaping pref invalidates the key, but the old content is painted first and the rebuild replaces it when it lands, instead of blanking Films and Series for the length of the pass.
  • An import writes in bigger batches and counts rows as they arrive. The catalogue writer commits 2,000 rows per transaction instead of 500 (the table's fifteen indexes make the commit the expensive part: a 300k-item panel used to pay for 600 of them), and the banner's item count now tracks rows received rather than rows a batch happened to commit, so it no longer steps by 500. The catalogue database also opens in WAL mode explicitly, so the UI's reads stay on their own snapshot while an ingest writes.
  • Switching a provider off keeps its catalogue, and switching it back on reuses it. Disabling a provider used to delete its rows, so re-enabling it downloaded the entire catalogue again even minutes later. The rows are parked instead - hidden from every VOD read by a rank sentinel and from the live list by the enabled-source filter - and put back in place when the provider is switched back on, with no network call at all. Only a source with nothing stored is fetched, and that fetch is scoped to it alone.
  • Switching one provider back on no longer rescans the others. Enabling a provider (or flipping one of its TV/Movies/Series gates) called a full catalogue refresh, so an IPTV panel that had nothing to do with the toggle was downloaded again from scratch. The load now takes the source ids it is meant to fetch: only that provider is contacted, and every other source keeps the rows and resident live channels it already had.
  • Removing a provider no longer re-scans the others. Deleting one source forced a full refresh, so the remaining providers' entire catalogues were downloaded again - minutes of scanning and a fresh ingest banner for a removal that touched none of them. A removal now takes the same local path as switching a provider off: its rows leave the database, the resident live list and any playback/preview/Multiview pane from it are torn down, and the screen repaints from what is stored.
  • The stored catalogue is never wiped wholesale. Pruning used to delete every row when no source was enabled - which is what a momentary empty provider list, or switching the last provider off, turned into: a full re-download of everything. An empty keep-list now deletes nothing, and removal still deletes explicitly.
  • Switching a provider off stops what it was playing. A disabled provider's stream URL keeps working - the panel does not revoke it - so the channel on screen, the Live preview and every Multiview pane carried on playing from a source Settings said was off. They are now taken down with it.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v6.1

Choose a tag to compare

@github-actions github-actions released this 25 Oct 12:51

Lumora v6.1

Live TV regions

  • The Live tab can be narrowed to the regions your providers actually carry. Settings has a new Regions pane: every country the catalogue knows, plus the two groupings the panels use as a locality of their own (Arabic, Latin America), multi-select, with nothing ticked meaning All regions. It reads the only signal providers give - the category name (Xtream category_name / M3U group-title) and, when that says nothing, the channel name ("UK: BBC ONE") - so it works on the catalogue already on disk, with no provider refresh. Detect from device seeds the selection from the device's own locale, and the selection travels in a backup with the rest of the settings.
  • A two-letter code is only trusted when the content proves what it means. HR| is horse racing, not Croatia; TS| is tennis, not Tunisia; AR| is Arabic-language news rather than Argentina. The handful of genuinely ambiguous codes are settled from the text beside them before the table is consulted, a bare two-letter word ("NO EVENTS", "IN THE NEWS") only counts when the panel separated it with a pipe or colon, and a category the provider tagged as a brand, league, quality tier or language resolves to no region at all.
  • Untagged channels stay visible unless you say otherwise. Rows a provider shipped with no locality - brands, league channels, world feeds - are a state of their own, governed by Show channels with no region tag (on by default) rather than by a country tick, so filtering to the UK cannot quietly drop half a sports catalogue. A selection that would hide everything says which regions came up empty instead of leaving a blank tab.
  • Every list agrees with the filter. The drop happens inside the Live derive, in the same pass as the adult filter, so the category rows, the grid, Search, the guide, catch-up, Multiview and the Sports board all see the same catalogue by construction. The selection is part of the derived cache key (sorted, so a stored set's iteration order cannot miss the cache on every launch), and the session log records what was filtered and how many rows fell out.

Live player guide

  • What's on now and next stays on screen while you watch. The player's controls bar carries the channel's listings as a strip of four cards: OK opens the selected programme's details, a long press arms the same reminder the guide offers, and the rail refills from the stored guide as it runs out while its now/next labels advance once a minute. The old single-line "now playing" subtitle is gone - it showed one programme and hid the rest. The bar itself is about half its previous height: channel identity, the transport and a two-row option grid share the space, so REC, Subtitles and the rest stay inside the panel instead of scrolling off its edge, with the D-pad walking the two rows column by column. A timeline filter drops overlapping or duplicate provider rows, so a programme that already started cannot reappear behind Next.

Diagnostics

  • Diagnostics can be saved to the TV itself. Save diagnostics to this device writes the crash reports and the session log into one text file under Download/Lumora - through MediaStore on Android 10+, so no storage permission is asked, and the app's own external files folder on older releases. The QR page still serves the same evidence to a phone; this is the path for a set with no browser nearby. The report carries the build header and is written off the interface thread, then the dialog names the file it landed on rather than just saying it worked.

Posters & shelves

  • Posters fill their tile instead of sitting in a card frame. The grid, shelf, search, filmography and cast tiles each drew a surface frame around the artwork - 8dp on a poster, 6dp on a face - so every picture was inset in a border of card colour. The frame is gone: the artwork runs to the tile's edge, the focus ring is drawn over the picture while the tile is focused, and each image height grew by exactly the padding that was removed, so the rows keep their previous size.

Backup

  • Exporting or importing a backup no longer crashes on a release build. The settings section built its JSON list type as an anonymous Gson TypeToken subclass, and R8 rewrites the generic signature of those subclasses even when the class is kept - so Gson's constructor threw "TypeToken must be created with a type argument" from the backup code's static initialiser, and every backup operation died with an ExceptionInInitializerError before it could read a setting. The type is built with TypeToken.getParameterized now, which carries the type in the token object instead of a class-file attribute. A build check now rejects any future anonymous TypeToken in production sources, because that failure only exists in release builds and every test passes without it.

Discover

  • Scraper catalogues load on release builds again. The A-Z index for AniWorld/SerienStream and the StreamingCommunity title parser used the same anonymous TypeToken form. Their constructors threw the same exception, the surrounding "start empty" handling swallowed it, and those catalogues came back empty in release builds only. Both use TypeToken.getParameterized now.

Stremio

  • Long addon stream URLs are no longer cut short. parseStreams capped every direct URL with take(2048), so a signed CDN link from an aggregator (AIOStreams/TorBox answered with URLs of 3-4 KB) reached the server truncated, and the server answered with its "Link not recognised" error slate. That slate is itself playable video, so Find & Play treated the attempt as a success and stayed on it instead of trying the next source. The sanity cap is 16 KB now, and a URL over it is dropped rather than shortened so the queue moves on; subtitle URLs follow the same rule.
  • An addon's direct streams are no longer one entry in Versions. Find & Play still picks one automatically, but the Versions list collapsed a Stremio addon's direct URLs to a single entry per addon - so an aggregator such as AIOStreams, with 40-50 cached results for an episode, left no way to choose a different release. An addon that answers with more than one direct stream now gets its own submenu in Versions, listing every stream by the addon's own line (release name, size, seeders, cache state, indexer) and marking the one playing. Two streams that share a release title are distinct choices again, while the last-good-source memory still keys on the title, so a re-signed URL remains the same remembered source.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v6.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 13:08

Lumora v6.0

Live TV guide

  • A programme that has already finished does not take the guide's "now" slot. A row's blocks are laid out back to back from its left edge, with the airing one truncated so every row starts flush at the current minute - and the guide handed the row every listing it had, including ones that had already ended (a provider that returns the whole day, or a window fetched earlier). Laid out like any other block, a finished listing took the front of the row: an overnight MLB game (01:00-05:00) ahead of a 13:00 Premier League listing filled the first four hours of TNT Sports 1's row and pushed the programme actually airing off the right-hand edge, so the row read as though the old game were on. The rows are built from the live listings only now, and a row with nothing but finished listings falls back to "No programme info" rather than an empty timeline.

Sports

  • A game is not on a channel that is showing something else. The board's channel list came from any listing that named the two sides - or, worse, one whose blurb did - so TNT Sports 2 was offered for Aston Villa v Manchester City on the strength of "Xtra Time" two hours after full time, and TNT Sports 4 for Sheffield United v Derby County because "Championships" in "UCI Mountain Bike Enduro World Championships" answered to the fixture's "Championship" under the competition tier's prefix tolerance. A blurb now only counts when the listing is on air through the kick-off, a listing about a sport the board has no chip for (cycling, athletics, swimming, skiing, equestrian, rowing, martial arts...) is never the carrier, and a news channel is never a place to watch anything. The guide's own team names work against a match as well as for one: a title that reads as a fixture and does not name both of your sides is that other game's coverage, so "Championship: Sheffield United v Rotherham" can no longer stand in for Sheffield United v Derby County.
  • ON NOW is what is on now. A listing that had not started yet sat on the live board: "Live US F1 GP" at 22:30 was under ON NOW at 13:14 beside the afternoon's football, because the filter only asked whether the listing had ended. It asks both ends now, so tonight's race appears when it is on rather than all afternoon.
  • The order and the failover follow the broadcaster. Channels with a name of their own (Sky Sports, TNT Sports) lead the "Which channel?" list and the player's other-broadcast failover; slots named after the fixture ("NEXT ASTON VILLA ... PPV 12", "AU STAN 04 Aston Villa v ...") sit below them. A source that just failed is not offered while a working one exists, and a live game whose channel dies now fails over to the next broadcast by itself - each tried once, the dead ones remembered - instead of landing on the error screen.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v5.9

Choose a tag to compare

@github-actions github-actions released this 24 Oct 11:41

Lumora v5.9

Continue Watching

  • A second TV picks up where the first one left off. Watching on one device and swapping to another left the second one with nothing to offer, because the Trakt sync only ever read what was finished - Continue Watching is built from what was stopped halfway, and that list lives on Trakt (/sync/playback) where it was read only so a removal could delete from it. It is pulled now: each paused entry is matched back to a copy of the same title in this device's catalogue (by TMDB id, then by the same title normalisation the watched marks use), and recorded as an in-progress position - so the tile appears on Home, the series page's Play button points at the episode that was actually in progress rather than the first one, and a film's page says Resume. Trakt states a percentage rather than a position and no duration, so the percentage travels as a percentage and is resolved against the local stream's own runtime when the title is opened; a position this device measured itself always wins when it is newer, and an entry for something finished here never resurrects it. Removing the tile still deletes Trakt's own entry, so it does not come straight back on the next pull.
  • A show spelled differently by the tracker and the panel still lands on the right episode. A watched mark is keyed by the show's title, so a mark from Trakt ("SAS Rogue Heroes") only reached an episode a panel spells differently ("SAS: Rogue Heroes") by luck - and when it missed, the Up Next row offered episode one instead of the one after the last thing watched. The pulled marks are now also written under the catalogue's own spelling of the show, matched by TMDB id where the panel carries one and by title where it does not. Additive only: nothing is ever un-marked by it, and the mark under the tracker's spelling stays so an un-tick still clears it.
  • Simkl feeds Up Next rather than Continue Watching, by design. Simkl's API has no account-wide "paused" list - a scrobble's stop only records a watched mark - so there is no resume point to fetch from it. Its watched history already answers "what comes next", which is what the Up Next row is for. The Trakt pane's toggle is now Sync watch history and Continue Watching, since it governs both.

Up Next

  • A show taken off Up Next leaves Trakt's Up Next as well. The removal used to be local only, so a show dropped from the row here was still the first thing trakt.tv and every other Trakt client offered - which read as the removal having been ignored. Trakt's own drop is the same idea (off Up Next, Progress and Calendars, watched history untouched), so removing a tile now drops the show there too, matched by TMDB id and only while signed in - independent of the sync toggles, because a removal is an instruction rather than a report. Restore removed shows undrops them again. A show that cannot be matched to a TMDB id, or a Trakt account that refuses the call, still comes off the row locally: the local removal lands first and never waits on the network. Watching the show again undrops it on Trakt by itself, and Trakt's settings page can always put it back.

Simkl

  • Simkl connects again. Simkl's original PIN sign-in has been retired for OAuth 2.0 clients, and the app still called it - so the code screen appeared, the code was typed on the phone, and no token ever came back (Simkl answered 400 unauthorized_client, pointing at the device endpoint). Sign-in now uses the OAuth 2.0 device flow: the same short code and QR, the same place to type it, and a token pair at the end. AUTH V2 access tokens last seven days, so the refresh token is stored alongside and used automatically a day before the access token lapses - a refresh Simkl rejects (the connection was revoked on its site) signs the session out instead of retrying a dead grant. Signing out revokes the grant on Simkl's side too, and every request now carries the client id, app name and app version the API requires, plus a User-Agent.

Live TV guide

  • A programme block opens its details instead of starting the channel. OK on a block now expands a panel under its own row - title, slot, status and the listing's description - filled from the guide already on screen, so opening it costs no request and cannot disagree with the blocks above it. One panel at a time (opening another collapses the previous row), updated in place rather than rebuilt, so D-pad focus stays on the block. The now line under a channel name is a touch target for the same panel, future listings surface the long-press reminder gesture, and the channel column is the way to start playback.

Toolbar

  • The settings gear is not printed on top of the clock any more. The tab strip is a scroll view, and a scroll view does not clip its own content - it draws the child through a translate, past its own edge - so when the row grew past the space the clock leaves, the settings gear and the refresh button were painted straight over the time and off the right-hand side of the screen, where the gear could not even be tapped (the clock was in front of it). The overflow is clipped at the strip's edge now, so nothing can reach the clock, and the row is trimmed to fit as well: on a 1080p set the six tabs, Search, the settings/refresh pair and the clock all sit on one line again, with the clock reading as the second step under the tab labels. A set with the width to spare (a 4K panel) keeps the roomier spacing the chrome had before.
  • Settings can be closed from the toolbar again. The close button is the toolbar's settings gear, and opening Settings hid the pills around it - but a pill that was already hidden (the Downloads tab is phone-only) was switched to INVISIBLE, which still takes up room in the row's layout, so the close button was handed a whole pill's width and pushed off the end of the screen, where nobody could see it. A hidden pill now stays hidden.

Settings

  • The settings screen uses the TV's width again. The content column carried a 200dp inset, which on a 1080p panel left a ~500dp-wide phone-shaped strip down the middle of the screen and a quarter of the width as dead space on the right - and every provider row squeezed into it, so a provider's name and URL were crushed to a few characters under the row's own buttons. The inset is 40dp now, still enough to keep text off the bezel: the panes fill the panel, and a provider's name and full URL read on one line again.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v5.8

Choose a tag to compare

@github-actions github-actions released this 21 Oct 12:53

Lumora v5.8

Fixes

  • An update no longer plays over the top of itself. Pressing Update downloads the new build in the background - the app stays up while the APK comes down, and the system package installer only takes over at the very end. Whatever was playing kept playing through all of it: the fullscreen player, every Multiview pane and the guide's inline preview, all of them decoding while the download had the bandwidth and the decoder. Everything is paused the moment Update is pressed now - the player as the user's own pause, so a cancelled install does not start playing again by itself - and it stays paused until the installer takes over.
  • The app starts faster. Two things were being brought up on the main thread before the first frame, for features most sessions never touch: the Google Cast framework (a Play Services bind - 100-300ms on a cheap stick) and the plugin engine's native library. The Cast framework is now brought up the first time the Cast button is used, and the plugin library is loaded on the thread that runs the plugin, so neither sits in front of the content. The XMLTV guide refresh also waits half a minute before it starts, so it no longer competes with the catalogue load and the first playback for the same CPU, disk and bandwidth.
  • The Live guide scrolls without stuttering. Scrolling the guide asked every visible row where it was on screen, once per scroll event - and a scroll event arrives twice a frame on a fast fling. Each of those questions walked the view hierarchy and composed transforms. The rows' positions now come from where they sit inside the list, which is the same answer for a fraction of the work. The guide's minute tick also no longer rebuilds every visible row: it updated the now/next line and the width of the airing block, and now updates exactly those in place instead of rebuilding every programme block of every row. The rows whose guide has expired - the only ones that needed a fresh fetch - are still refreshed.
  • Home stops walking the whole catalogue for a handful of rows. "Recently Played" looked each of its (at most twenty) ids up by scanning the entire live list, and the favourites row joined the films and series lists together first - a copy of tens of thousands of entries - to filter them by a few ids. Both are index lookups now, and neither allocates a copy of the catalogue.
  • Search's guide pass no longer holds the interface. Searching also looks through the guide, and that pass walked every live channel and every programme title in the guide - a lowercased copy of each - on the interface thread, right after the media results were shown. It runs off the interface thread now, and the guide fetches it asks for are unchanged.
  • Posters are clipped by the platform, not by a mask per poster per frame. Every poster was clipped twice: once by the frame around it and once by a hand-drawn rounded-rectangle clip in the image view itself, which the renderer has to mask the drawing with. The image now clips itself through the platform's own rounded-rectangle outline, which is the cheap path, and the hand-drawn clip stays as a fallback for a renderer that ignores it.
  • A big M3U show's episode list no longer freezes the app. The detail screen asked "which show is this episode from?" for every episode row it bound - watched state, in other words - and answered it by copying the catalogue's entire series list, then walking that copy three more times. An m3u_plus playlist keeps every episode as its own series row, so on a large panel that was hundreds of thousands of allocations per scroll with the heap to match: the list hung, stuttered, or took the app down. The show is now read out of the episode's own name - which is where most providers bake it in, and which yields the same title the catalogue would have answered with - and the catalogue is only consulted for the rows whose name says nothing about the show (a Jellyfin episode is titled "Episode 4"), through an index built once per catalogue. That index also carries the show cards the app derives from an M3U panel: the catalogue itself holds only episodes, which is why those rows fell through to the slow path on every bind. The watched marks themselves are the same ones as before.
  • Watching a film no longer stutters every few seconds. The VOD quality watchdog asks which duplicate group the playing copy belongs to on every tick (2.5s), and for a copy that is not its group's first - a Continue Watching resume, a search hit, the version a chip switched to - and for a film with no duplicate at all, it walked every duplicate group and every member on the main thread each time. The answer is now remembered for the title being played, so it costs one pass per title instead of one per tick.
  • The Sports tab stops sitting on "Loading today's games" for two minutes. The board waits for every sport before it paints, and the football feed (SportAPI.ai) is asked for two dates - one after the other - with the app's own read timeout, which is a minute. When that host stopped answering, the tab sat on its spinner for a minute per date while the other feeds' games were already in hand. The feeds now have their own, much shorter timeouts (a feed that has not said anything in ten seconds is not going to), and the two dates are asked for at once, so a dead host costs one timeout instead of two.
  • Exporting a backup no longer needs room for three copies of it. The export serialised the whole document once with a blank checksum to hash it and then again with the checksum filled in. The document is now serialised once and written out with the digest substituted into the member it belongs to, so a second copy of it is never held - which is what the TV sticks this app targets were running out of, and a failed export there surfaced as an OutOfMemoryError, which the export path's catch (Exception) cannot see. The bytes written are the same ones the old path wrote, so backups stay interchangeable in both directions, and the checksum member is found structurally (the file's own), so a nested member of the same name is never rewritten. Import only re-serialises the document for its fallback checksum when the file's own checksum does not verify, instead of on every import.
  • An import still runs if the app was rebuilt while the file picker was up. The result handler depended on the backup object the tap had stashed on the Activity, which does not survive a process restore or a configuration change behind the system picker - so the pick came back and nothing happened. The handler now builds one for the result when the stashed one is gone, and drops the stashed one when the picker is cancelled.
  • A refresh that fails after a restore can't take the app down. Re-applying the restored settings to the running app (UI language, provider list, Streaming sites pane, DNS, catalogue reload) used to run unguarded after the import had already succeeded, so a failure there crashed on top of a perfectly good restore. Each step is isolated now: the data is on disk either way, and the worst case is a screen that catches up on the next launch.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v5.7

Choose a tag to compare

@github-actions github-actions released this 20 Oct 13:44

Lumora v5.7

Multiview

  • Multiview: watch up to four live channels at once. The Multiview button beside the transport controls opens a grid with the channel already playing; the action bar adds, changes, closes and fullscreens screens, and the D-pad moves between them - the highlighted screen is the audible one, the rest are muted. Each screen is its own player with its own quality-version fallback, dead-feed skip and offline badge, so one bad stream never takes the grid down - and offline screens retry themselves (first try after ~10s, backing off to every 2 min) and pick the feed up the moment it returns, no poking required. A retry in flight shows "Connecting…" on the tile, so an attempt never looks like a dead screen.
  • Sized to the hardware and the subscription. Auto (the default) opens as many screens as your providers' simultaneously-connection limits allow - set a provider's Max connections when adding or editing it - and the app remembers the largest count this device has actually sustained, so a stick that cannot decode a fourth stream stops being offered one. Adding a screen that would exceed its provider's limit warns before the stream is refused. Settings → Filters holds the master switch and the Auto/2/3/4 screen count.
  • Adding a screen browses Live TV itself. Add screen (or Multiview in the Live TV tab's dropdown, above Catch Up) drops out of the grid into the normal Live TV browse - categories, guide and preview all work as usual - and OK on a channel adds it as the next screen and returns to the grid; Back returns without adding, and picking Live TV or Catch Up from the dropdown leaves multiview. The waiting grid's panes are stopped while browsing (their decoders go back to the system for the preview) and re-tune when it returns.

Sports

  • The sport row carries only the sports that have games on. A sport with nothing in the board's current mode - no game in play, no kick-off coming, nothing from the guide - has no chip at all, instead of sitting in the row greyed out and unfocusable: a chip that can only ever open an empty board was never a real target, and the row now reads as the games that are on rather than as a fixed list of five sports with four of them off today. The sport you have filtered to is the one exception - it stays (greyed, at zero) when a mode switch empties that sport's half of the day, so the board's own filter stays visible instead of the empty board reading as "nothing on today".
  • A game with no channel is not on the board. A fixture the guide could not place on any of your channels used to sit there as a row with an empty channel chip - pressing OK on it could only ever say "no guide entry matched a channel you have". Those games are off the board now, the header says how many were left out (so a board smaller than the feeds' own count reads as what it is), and the sport chips count the same games the board shows rather than the ones it hides.
  • A live NBA game is basketball, not golf. A sport keyword was matched as a substring of whatever text it sat in, so the golf championship's own name - "the open" - answered to "from the opening round of 2026-27 NBA fixtures" and the game was filed under the golf chip. Keywords are matched as whole words now, and a listing's title (what the programme is called) decides the sport before its blurb does.
  • Tonight's games are not on today's board. A listing for a later day sat under "On now" beside the games of the morning, its clock time alone saying which of the two it was - a 01:00 NBA game tonight read as the one that finished at 04:30 this morning. A listing is on the board while it belongs to the day, and a row that crosses midnight now says so ("23:00 - ends Wed 00:00"), so a game that kicked off at 23:30 and runs until 01:30 cannot be read as this morning's either.
  • A football match is not on Sky Sports F1. The competition tier took a league's name as evidence even when the name was a bare number: "Liga 1" leaves the "1" and nothing else, and every listing with a number in it answered to that - "Formula 1 Motor Racing" carried an Indonesian football match, and "Live Tennis" an Uzbek one. A number needs a word to belong to, a competition is read from the listing's title rather than its blurb, and the guard that keeps one sport's listing off another sport's fixture now knows every sport the board can show. The channel's own name has the last word too: a football fixture is not on Sky Sports F1, whatever the listing on it says.

Live TV guide

  • The guide stops asking the provider the same question every minute. A channel with no schedule - or a provider that was down - was re-asked for every visible row, once a minute, for as long as the guide was on screen; a channel with several quality variants turned one row into a dozen requests. "No guide here" is now an answer worth remembering (for a quarter of an hour, so a channel that gains a schedule later still picks it up), and a request that failed is remembered as a failure with a backoff rather than as "no guide" - which used to blank channels that do have a schedule.
  • Programme names are not garbled any more. Some panels base64-encode their guide text and most do not, and the app guessed per title - so on a plain-text panel a title that happened to be valid base64 was decoded into nonsense ("Live" became ".+\uFFFD", "ETV1" became "5u", "Sky" became "JL"). Whether a panel encodes is now decided once, from the panel's own titles, and a title that is not base64 is left exactly as it arrived.
  • A guide that was fetched hours ago is not asked for again. The Catch Up screen asked the provider for a whole day's listings, and the answer it got was used to work out how far the panel's clock was off - but a whole day's listing starts days ago, so that measurement always read "no offset" and was remembered for the whole provider: a panel running on its own local time had its guide hours out on every channel. The offset is now measured only from a short listing, whose first entry is the programme the panel believes is on air.
  • One provider's schedule is no longer shown for another's channel. Two panels both number their streams from 1, so "42" is two different channels - and the guide stored them under the same key, so switching provider left the new provider's channel reading the old provider's schedule straight off disk, and kept reading it. The guide is now filed under the channel, the provider that owns it and the source it came from, so the panel's own guide and an XMLTV feed's guide no longer overwrite each other either (the feed's blurbs, which the Sports board reads, survive a refresh from the panel).
  • An XMLTV guide no longer goes blank for hours. The feed was re-downloaded on exactly the period the reader's window allows, leaving no margin: there was always a stretch where the stored guide had aged out and the new copy had not arrived yet. The feed is now fetched more often than that window, and a source that has never been fetched is asked for straight away instead of waiting a whole period for the first run.
  • A fetched guide is no longer lost on the way to disk. The write is now one transaction (a crash between the delete and the insert used to leave a channel with no guide at all) and it is no longer cancelled when the screen closes - the fetch was already paid for.

Start screen

  • The app can open on Home, Series or Movies instead of Live TV. Settings → General has a new Open the app on row - Live TV (the default), Home, Series or Movies - and the choice is read once per launch, before the first paint, so it decides where the app opens and nothing else: it never moves you around while the app is running, and a provider reload half a minute later cannot send you back to where it opened. The labels are the tab bar's own, so the choice reads as the same thing the tab bar shows. Series and Movies fall back to Live TV when VOD is off (simple mode hides those tabs outright, and the VOD toggle leaves them there but empty), and a setup with no provider still lands on Discover, which is the only thing there is to show.

Lists

  • Single items can be taken off Recently Played. Holding OK on the Home row now offers Remove from Recently Played / Clear Recently Played - the same hold-to-remove contract the Continue Watching grid already had - instead of only favouriting the channel. Nothing outside this device's list is touched, and focus stays on the row when a channel is removed.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v5.6

Choose a tag to compare

@github-actions github-actions released this 17 Oct 18:18

Lumora v5.6

Playback & DRM

  • ClearKey DRM: protected M3U streams play with the key from the playlist. Entries carrying inputstream.adaptive.license_type=clearkey / org.w3.clearkey with a static license_key (kid:key, comma-separated pairs, or multi-key JSON) play with the key applied instead of failing; hosted ClearKey license servers work too, including url|headers|body|response wrappings, plus stream_headers / per-request manifest_headers, the manifest_type MIME hint, #EXTVLCOPT companions and URL |Header=value suffixes. Widevine/PlayReady/unknown schemes are reported, not silently failed; a device with no ClearKey plugin says so instead of spinning; protected streams refuse external-player hand-off and downloads with a clear message. See clearkeydrmsupport.md.
  • The guide refreshes the moment playback closes. Returning from fullscreen used to leave the guide frozen until the next minute tick - up to a full minute stale after a long film. Closing the player now triggers that refresh immediately (ruler rebuilt to now, visible rows revalidated disk-first), and only when the Live guide is actually showing - nothing ticks in the background while fullscreen.

Continue Watching

  • The Films tab has its own Continue Watching. Sidebar row with count, browsable content grid (a poster opens the film's detail with its Resume button), and a lead shelf on the Films poster - all following the same merge as Series: server resume entries lead, cross-provider duplicates merge by title, switched-off sources hide without deleting, adult items stay off, and removing the last tile moves the pane off the row.
  • Episode tiles open their series instead of playing outright. A tile whose parent id no longer resolves (Stalker episodes now stamp theirs like every other provider) falls back to an episode-id-prefix and cleaned-name match before giving up to direct play, and one episode never tiles twice - not across copies, and not across season-split catalogue entries ("Show" vs "Show S01").
  • Removing a provider or account takes its watch progress with it; switching one off just hides its tiles. The dead-panel health check already pruned on removal; manual removal now does the same, and the toggle only filters the rows until it is switched back on.
  • QR pairing takes an M3U file from the phone, not just a URL. The phone page has a file picker (up to 50 MB, pre-checked before sending); the TV stores the upload app-privately and registers it as a local file:// provider, which the existing local-playlist path reads. Error pages don't burn the one-shot token, so a retry stays possible.

Detail screen & D-pad

  • No more getting stuck on Play. UP/DOWN across the actions, cast faces, version chips, seasons and episode rows is one explicit chain now: UP from below returns the page to the poster/title top, DOWN never dead-ends on the faces while the strips below are still loading, and every action carries a static UP link to Back as backup.
  • The header fits a TV line. Play, Find and Trailer share one compact 40dp row with an overscan-safe start margin, and the favourite star moved up beside the series title (its DOWN lands back on Play).
  • Posters are visibly rounded everywhere. Shelf, grid, filmography, cast, episode, download, EPG-search and detail artwork all clip through a self-clipping RoundedImageView with an 18dp poster radius, after some firmware ignored the rounded parent frames and left every photo square.
  • Cast cards line up. Uniform two-line name slots, breathing room between faces, and smaller names; the season chips and cast faces focus with their white ring instead of a zoom that clipped at the scroller edge, and full-width rows use a subtle 1.03x pop with list padding so the highlight is never sliced at the screen edge.
  • The settings rail no longer paints over its "Settings" header while scrolling.

Player & shelves

  • The title sits above the transport controls. Playback identity (logo, name, live badge) gets its own full-width row, the transport stays centered beneath it, and the EPG note spans the width below - long titles are no longer squeezed beside the buttons. D-pad order is unchanged.
  • Slimmer chrome, more room for cards. Shelf titles step down (22 to 19sp), SEE ALL to 15sp on a 36dp touch floor, tighter shelf gaps, and the category sidebar narrows 200 to 180dp.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v5.5

Choose a tag to compare

@github-actions github-actions released this 16 Oct 14:47

Lumora v5.5

Cast

  • The cast section is faces, and each face opens the person. TMDB's credits become a row of headshots with the role each played (a person TMDB has no shot of shows a silhouette rather than a gap), and picking one opens their own page: portrait, role in the title it was opened from, birthday and birthplace, biography, and a "Known for" shelf of the films and series they have been in - Films and Series chips swap between the two, opening on the half matching the title the face was picked from. Every tile opens that title's detail screen through the same path Discover uses, so a title the user's own sources carry opens the copy they can play (version chips and all) and one they don't opens with Find & Play; Back walks straight back to the person page and then to the title the round started from, with the D-pad landing on the face that was picked. Providers that ship cast as a name-only string are unchanged, and without a TMDB key the row stays hidden and that line shows exactly as before.
  • The detail screen puts the episode list first. The poster and title sit a size down, the synopsis moves into the space beside them with genre and director under it, and the cast closes that column as a narrow strip of portraits; the play row lines up with the cast heading, and the episode rows get the full width they need to show a title and its plot rather than a half-page column.

Ratings

  • IMDb's own score on a title's detail screen. The detail line carried whatever the panel or TMDB shipped as a bare star; it now also carries IMDb's number, labelled (IMDb 9.3) so a 0-10 score is never read as a percentage of something else. It comes through Simkl, whose API indexes IMDb's ratings directly - so the score needs no extra credential and no scraping - and TMDB's external ids supply the tt… id the lookup is keyed by; both are cached per title, nothing is prefetched, and a title with no score anywhere simply shows the line it always did. A panel that ships the IMDb number itself (Stalker portals' rating_imdb) shows one score, not two.

Translations

  • French gains the new screenfuls. The local files pane, the addon store and phone-QR flow, the Live-category order row and the preview toggle are translated, along with the new messages above.

Fixes

  • A removed dead panel now takes its watch progress with it. The launch health check deletes scanner-found providers that stopped answering, but their Continue Watching snapshots stayed behind - a snapshot points straight at the stream its panel served, so its tile just spun on a dead host until the player gave up. Removal now drops those entries (by provider id, or by URL host for snapshots saved before provenance existed) and says how many in the session log.
  • Removing an episode from Continue Watching really does clear every copy. The sibling sweep searched the catalogue, where an episode never lives - the catalogue holds its parent series, and the episode itself exists only as a saved snapshot - so for a keyed episode it cleared nothing beyond the tile's own entry and the next provider's copy took the tile straight back on the next rebuild. It now sweeps the position store, which is where copies actually live, and the same sweep on marking an episode watched (or unwatched) drops the other copies' entries as it always intended. A media-server resume entry on a second server is pruned by the same shared keys instead of only the tile's own id.
  • Media-server episodes key on their real season. Jellyfin and Plex state an episode's season outright, but the internal item had nowhere to keep it, so seasons 2 and up keyed as "no season": a Jellyfin S02E05 never matched the same episode on a panel (no one-tile de-dup, no shared watched mark, no cross-clear on removal), and only season 1 matched by luck of the old alias. The season now travels with the episode, the unknown-season spelling became its own key ("s0" had meant both "specials" and "no season stated", so marking a special watched marked S01E05 and vice versa), and one-time marks recorded by an older build may read as unwatched once before re-marking on the next play.
  • The Up Next countdown pauses with the picture. It kept ticking while the viewer had paused - or while the app sat behind Home - so the next episode could start, audibly, fifteen seconds after a pause. The countdown now runs only while the video is playing and picks up where it stopped.
  • A credits marker that doesn't cover the end no longer silences the end-of-episode offer. IntroDB can answer with an empty start or, after a wrong-title match, a start past the end; either suppressed Up Next for the whole episode although no Skip Credits pill would ever appear, and the viewer got only the late fifteen-second ask. The countdown now stands down only for a credits segment that actually owns the last seconds.
  • Back out of a person page then a filmography title no longer ends on a blank screen. Opening another cast face from a title that was itself opened from a person's filmography, then backing out twice, hid everything and left the following Back to exit the app; opening a person page now clears the "opened from a person" return flag along with the page it referred to.
  • The guide's ruler keeps :00/:30 in every timezone. The half-hour boundaries were floored from epoch milliseconds, which are :00/:30 in UTC only - half-hour-offset zones (Kathmandu, Colombo, Adelaide) got :15/:45 labels again, and a DST jump moved the labels off the blocks beneath them. The next mark now comes from the wall clock, offset and DST included.
  • Live TV's adult categories sink below the rest again. The provider-order change dropped the old sort that pushed them to the bottom, and outside the grouped layout they sat wherever the panel listed them; they are now partitioned last after whichever order is active, and A-Z sorting no longer misorders "I..." names on a Turkish device.
  • A sports row scrolled under the mode pills can no longer hold invisible focus. Focus arriving from the chip row could land on a row hidden behind the header and OK would activate a row the viewer could not see; a row now pulls itself back into view when it takes focus. The half-width live card also measures its channel name against the space the number actually leaves it, so a long name wraps instead of being cut a line early.
  • The person biography is shown in full. It was cut at twelve lines with an ellipsis and no way to read the rest, inside a page that already scrolls; the cap is gone.
  • A USB drive unplugged mid-browse is reported instead of misread. Its folders showed as empty and its files handed the player a dead path; a folder that is gone now says so, a file that is gone says so, and Back from a vanished volume returns to the volume list instead of climbing into unreadable /storage paths. Denying the storage permission on older devices also says what is missing instead of silently doing nothing, and symlinks inside a volume can no longer walk the browser out of the drive.
  • The Stremio store survives a bad catalogue and says when it is offline. One malformed entry used to silently drop every entry after it; both catalogues are now parsed per entry, cached for half an hour, and a failed fetch reads as a connection problem rather than an empty store. Installs are written atomically (a crash mid-write used to make the addon vanish on the next launch), a QR link is single-use for its whole five-minute window, and the pairing server stops with its dialog instead of running on behind it.
  • A backup import now keeps every other section when one settings value is malformed. A hand-edited entry that isn't a {"type":…,"value":…} object failed the parse and rejected the entire restore; it now costs only its own key. The export screen also says the file carries provider logins and media-server session tokens as readable text.
  • The cast row takes the D-pad predictably. UP reaches the Back button, DOWN reaches Play, and Play's UP returns to the faces, instead of focus search wandering the whole scrolling page; the faces wear the same card chrome as the filmography tiles they lead to, and each filmography tile reads as one TalkBack stop.
  • Toggling the Live channel preview on no longer plays a stream behind the Settings dialog. The pane re-arms, and the preview starts on the way out of Settings.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v5.4

Choose a tag to compare

@github-actions github-actions released this 15 Oct 10:58

Lumora v5.4

Sports

  • A Sports tab lists the day's games, each with the channel carrying it. Fixtures come from API-Sports (one product per sport: football, basketball, baseball, ice hockey, American football, handball, volleyball, rugby and AFL) and from SportAPI.ai, which leads for football; keys are build-time credentials like TMDB's and Trakt's, and a per-provider daily request budget keeps a free tier alive across a day of opening the tab. Every fixture is matched out of the user's own EPG - a programme naming both teams is the evidence, with tiers for team-named channels (US regional sports networks) and competition-named listings ("NHL", "Top 14"), aliases and three-character prefixes for the way guides write sides ("Man Utd" for Manchester United), and the listing's description as a second chance - and OK watches the matched channel, with a picker when more than one carries the game. With no feed configured the tab still fills itself in from the guide's own live sport, and a media-server or plugin-only setup has no channels to match against, so the tab stays hidden there (the Settings pane says why). Settings > Sports: show/hide the tab, pick which sports to list, and add the curated M3U guide (epg6) as an EPG source in one tap.
  • The board reads as a scoreboard, not a list of sentences. Kick-off and state sit in a fixed left column, the sides stack with their scores in matching boxes, the competition sits beneath them, and the channel carrying the game is a chip with its logo and number on the right; the board leads with what is in play, then what is still to come, then results, and a header band gives the day, the game count and how many are live. A mode row switches between Live now (the games in play plus the guide's own "On now" listings, shown with the listing's blurb) and Upcoming, sport chips filter the board with a count each, and live games sit two to a line as half-width cards. Tennis, Darts, Cricket, Snooker and Boxing join Formula 1 and Golf as guide-only sports, and the live classifier reads every supported language, accent-folding and squashed or spaced phrase matching included.
  • Guide text, end to end, behind the tab. epg_programs gains the listing's description (DB v5, migrated in place) filled by both XMLTV <desc> and Xtream, a channel's timeline comes from the panel's whole stored table rather than the short EPG (four entries against 140-380 on the panel this was measured against), XMLTV feeds may arrive gzip-compressed, a sync filters programmes to the device's own tvg-ids, and a portal without them is met by the channel's name. Starting a game hands over every channel the guide matched to it, so the player's live version picker offers the other broadcasts beside the channel's own quality variants.

Network shares

  • SMB and WebDAV join the provider types, with browse and play on the TV. SMB goes over smbj (SMB2/3) and WebDAV over PROPFIND with Basic auth; entries stay out of the catalogue and open a browser overlay in the settings slot, with Back walking up one folder. An smb:// URL is served by a Media3 data source that reads the share directly (seeking included) and WebDAV plays as an ordinary HTTP URL carrying its Authorization header, so resume, subtitles-in-container and the external-player hand-off all work. The phone-pairing QR form learns both types, and the TV verifies a QR-added share immediately and reports what it found.

Player

  • Versions lists everything a search found - torrents and debrid included. The resolver publishes what it found and keeps collecting after playback starts, so Versions offers one entry per site (its mirrors are interchangeable), a Torrent entry opening the full release list (quality, seeders, indexer) and the debrid service a torrent will be fetched through when an account is configured; picking one re-opens that source alone, and the auto-pick (web first, torrents when nothing web resolves) is unchanged.
  • One Find & Play button on a title with no source. The toolbar's Find button hides while the primary Play relabels itself and returns when Play means play again.

Providers and plugins

  • Dead Reddit-scanner providers are dropped on launch. Entries proposed by the scanner are re-probed the way the app would use them (the Xtream login call, the Stalker handshake, or an HTTP status for a playlist, under a 20s per-request cap) once per app open, and the ones that no longer answer are removed in one write, named in a toast and the session log; an offline launch removes nothing. A candidate added mid-session is re-checked there and then and, if it does not answer, added switched off rather than deleted - and a panel that starts failing during playback is probed after the third failed play and reported as stopped responding, once per session.
  • Cloudflare challenges stay off the player. A site resolve still in flight when playback starts can no longer promote its challenge WebView over the stream: the bypass asks one gate before showing any UI, and a challenge raised moments before playback begins is dismissed, failing its caller so the queue moves on.

Translations

  • Every locale gains the missing screenfuls. The sports section plus debrid, diagnostics, network shares, Simkl, skip segments and subtitles - around 190 keys per language, each with its own plural categories, brand names left untranslated, and every format specifier audited against the English source.

Fixes

  • The guide's times sit on the clock's half-hours again. The ruler was labelled from the current minute - "Now", 12:17, 12:47 for a guide opened at 11:47 - so its times never matched the usual :00/:30 marks or the programme blocks beneath them; it is now anchored to the half-hour, with a "Now" lead cell running to the next mark and the blocks lining up under it.
  • The Live TV rail lists categories in the provider's own order. The sidebar used to float sport/UK names to the top and alphabetise the rest, scrambling the order the panel itself lists them in.
  • Cards scrolled off the top of the Sports board hide behind the mode pills and sport chips instead of painting over them.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.

Lumora v5.3

Choose a tag to compare

@github-actions github-actions released this 13 Oct 08:51

Lumora v5.3

Updates

  • The update notice is dismissable, and automatic checks can be turned off. Dismiss sits beside Update on the launch notice and unticks the new Automatically check for updates switch (Settings → About), so the notice stops returning - until a build falls more than 10 releases behind, which switches the check back on by itself (the box ticks again) and shows the notice regardless. The mandatory update screen - unrecognised builds, or 50 releases behind - is untouched and still has no dismiss.

Installation

Download the APK below and install on your Android TV, Fire TV Stick, or phone.