Skip to content

Fix/crashes history jackett - #1

Merged
Langegen merged 13 commits into
mainfrom
fix/crashes-history-jackett
Oct 29, 2026
Merged

Langegen merged 13 commits into
mainfrom
fix/crashes-history-jackett

Conversation

@Langegen

Copy link
Copy Markdown
Owner

No description provided.

Langegen and others added 13 commits October 26, 2026 16:35
Crashes
- History tab: rebuilding the list freed the focused row (after leaving the
  player, Remove or Clear) while borealis still pointed at it, both as the
  current focus and in its focus stack. Rebuild now waits until the tab is
  the top activity, parks the focus and hands it back to the same position.
  The deferred rebuild no longer captures a raw `this`.
- Jackett tab: Retry / Configure Jackett freed themselves through refresh()
  while focused. Focus is parked on the search bar first.

Jackett
- Every result was downloaded to one shared cache.bin_jackett.torrent, so
  History resumed whichever torrent played last. Each link now gets its own
  file under /jackett/ (FNV-1a of the link).
- Indexer links that redirect to a magnet: URI failed with "Protocol not
  supported". New http::downloadTorrent() reads the Location header and
  returns the magnet, which is then resolved and played (or saved).
- The search log line no longer includes the API key.
- JackettView was never freed (one leak per tab visit); the tab owns it now,
  and async callbacks check the alive flag.

Engine
- Magnet metadata: when trackers returned peers but none served the
  metadata, the fetch failed without asking the DHT ("peer 80/80 0%",
  upstream issue #3). A DHT round with untried peers now follows.
- torrent_announce() stops the remaining announces once the peer list is
  full instead of waiting for every slow tracker.

Other
- History: periodic saves run off the UI thread; find() returns a copy
  instead of a pointer into the shared list; load() falls back to the .tmp
  left by an interrupted save.
- Updater points at Langegen/NX-torrent-player (it would replace the fork
  with an upstream build); the release-by-tag URL is configurable too.
- File pickers prefix the folder when two videos share a name (season
  packs: "Season 1/E01.mkv" vs "Season 2/E01.mkv").

Builds clean; offline pctest cases pass. Not yet tested on a console.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… cleanup

CI
- .github/workflows/build.yml: offline engine tests, then the .nro in the
  devkitpro/devkita64 image, on every pull request. A vX.Y.Z tag also opens a
  draft release with the .nro attached; publishing stays manual because the
  in-app updater offers the latest published release to everyone. The tag
  must match VERSION_* in CMakeLists.txt, or a build would keep offering
  itself as an update (or never update again).
- Version bumped to 0.5.1.

Tests
- pctest/test_suite.c no longer touches the network: udp_announce() and
  dht_find_peers() are mocked at link time, and a mock peer on loopback serves
  the metadata over BEP 10/9, so peer.c runs as on the console. Six tests,
  including the DHT round after stale tracker peers (upstream #3) and the
  announce stopping once the peer list is full. Disabling the DHT fallback
  makes test 5 fail.
- pctest/run_tests.sh builds and runs them (gcc + libcurl only).

App / engine
- Jackett: the per-link .torrent files under /jackett/ are pruned at startup
  when no history entry points at them; nothing else ever removed them.
- Default trackers: dropped openbittorrent, exodus.desync, explodie and
  moeking (gone from ngosang/trackerslist), added open.tracker.cl. The
  engine's list and the two magnet lists in the app now match.
- History dates: no change needed. libnx sets TZ from the console's time
  zone at startup, so localtime() is local; noted where the date is
  formatted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ussian UI

Direct links
- Stremio streams that are plain http(s) URLs (debrid and the like) now
  play instead of being marked "unsupported".
- The Switch's FFmpeg is built without the https protocol (only http, tls,
  tcp, ...), so mpv opens http:// and HLS over it itself, and an https://
  URL is played as nxhttps:// through engine/httpsrc.c: curl with the app's
  CA bundle, 4 MiB Range requests, redirect shortcut, retries, cancel.
- Player: a no-engine path (loading screen, open errors shown with their
  reason, ZR panel, watch-state sync); buffering and seeking already went
  through mpv. HLS over https is not supported yet.

TorrServer (Options -> Network)
- With a server address set, magnets, .torrent files from the SD card
  (turned into a magnet) and Jackett results are streamed by TorrServer and
  played as HTTP. Without a file index the server's file list picks the
  largest video. History keeps the torrent as the source.

Proxy (Options -> Network)
- HTTP or SOCKS proxy for the app's requests and https playback; hosts on
  the local network (RFC 1918, .local, bare names) always go direct.
  FFmpeg's http:// path takes an http proxy only.

Russian UI
- app/i18n_ru.cpp: all 332 strings (every tr() literal, the language and
  colour labels, engine errors). Space Grotesk has no Cyrillic, so in
  Russian the UI uses the system font throughout: new borealis local patch
  SwitchFontLoader::useBundledFont (VENDORED.md).

Tests
- pctest/test_httpsrc.c: range reads against a mock HTTP server (chunk
  boundaries, seeks, no-range servers, redirect, retry, cancel).
- pctest/test_net.cpp: TorrServer URLs and file choice, local-host bypass.
- 17 offline tests in all; run_tests.sh builds the three suites.

Also: http::get takes a timeout; http.hpp includes <cstdint>; CLAUDE.md
notes the missing https protocol and the font constraint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck-ups

Home tab (app/home.cpp), first in the header and the default on startup
- A hero banner for the top trending title: backdrop darkened from the
  left, title, meta line, synopsis, "Watch" and "Details".
- "Continue watching" from the local history as 16:9 cards with progress.
- Poster rows: popular now, popular films / shows, in cinemas, top rated
  films / shows; each row loads on its own.
- "-" searches TMDB (films and shows on a results page).
- Without a TMDB key: a note on where to get and enter one.

Title card (app/card.cpp)
- Full-screen backdrop shaded left and bottom, poster, title, chips (TMDB
  rating, year, genres, runtime or seasons/episodes), synopsis, cast,
  "Watch" and "Torrents"; for a series a season switcher (buttons and L/R,
  focus parked before the strip is rebuilt) and the season's episodes.
- Opens at once from the list data, fills in from the details.
- Sources: the Stremio addons by IMDb id when signed in (existing film and
  episode screens), otherwise a Jackett search by title (+ year / SxxEyy).

TMDB client (app/tmdb.cpp, app/content.hpp)
- Trending, popular, top rated, now playing, multi search, details with
  external_ids and credits, seasons; in the UI language.
- v3 key or v4 token, from Options -> Network or the build
  (-DTMDB_API_KEY, CI secret TMDB_API_KEY); the API address can be a
  mirror; goes through the proxy.
- Answers cached on the SD card (lists 6 h, details 3 days); offline, any
  cached copy is used.
- json::topLevel / json::object: TMDB nests "name" (created_by, genres,
  crew) ahead of the title's own.

Shared widgets (app/widgets.*)
- FadeStrip and TopRoundedImage moved out of stremio.cpp unchanged; new
  shade box, poster and wide cards, chips, action buttons.
- Jackett: openJackettSearch() opens the search screen on a query.
- Poster memory: borealis' refcounted LRU texture cache already bounds it;
  rows use TMDB's w185 posters.

Also: Russian strings for all of it; Options -> Network -> TMDB (key,
address, test, clear cache); pctest/test_tmdb.cpp (6 tests on trimmed real
answers, 23 offline tests in all).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uild

Sources screen (roadmap phase 3: app/sources.cpp, app/release.cpp)
- A film's or an episode's releases from Jackett (localized and original
  title, the year for a film) and the Stremio account's stream addons (by
  IMDb id), deduplicated, in one list.
- Release names parsed into quality, source, codec, HDR / Dolby Vision,
  10-bit, voice-overs (dub, MVO, DVO, AVO, studios), group, and the seasons /
  episodes held (S01E06, 1x06, "Сезон: 1, Серии: 1-8 из 8", ...). Other
  seasons / episodes and cam rips are hidden; the rest ranked for a Switch
  (1080p H.264 over 4K HEVC HDR and remuxes, seeds, size); "best choice".
- Filters: quality, voice-over, source, picture (HDR), tracker; X cycles the
  order.
- A season pack opens on the episode's file (release::matchEpisode); the
  voice and group picked for a show rank first next time.
- app/torrent_play.cpp: the Jackett tab's play flow, shared and extended
  with the episode match, artwork and watch context.

Library (roadmap phase 4: app/library.cpp, app/bookmarks.cpp)
- Bookmarks: Watching / Planned / Completed / Dropped, from the card or Y on
  a poster; a Bookmarks tab; export / import of library-export.json.
- Progress per film and per episode in library.json, tied to the title, not
  the file. "Continue watching" on Home resumes in one press (X removes);
  the card's main button says "Continue S1 E5" / "Watch S1 E6".
- "Next episode" at the end of an episode works without Stremio: from the
  same season pack when the last one came from it, else its sources.

Catalog
- Layout after the roadmap mock-ups: header Home / Movies / Shows /
  Bookmarks / History, Stremio / Local / Jackett behind one "Sources" button,
  "Search" on the right; compact hero; pill buttons; smaller cards; A "Open",
  Y bookmark, X remove hints. The L/R "View" keys exist only on Stremio's tab.
- Collections: every row's heading and its "See all" tile open a paged grid
  (app/collection.cpp).
- Search screen (app/search.cpp): search bar, kind / genre / year / order
  filters, genre tiles; TMDB discover without text, search with it.
- IMDb rating (from Cinemeta) on the card and the hero.
- borealis' footer words in the app's language: Platform::APP_LOCALE_DEFAULT,
  plus i18n/ru|fr/hints.json (upstream only had an unread brls.json).

PC build (for checking the UI without a console)
- build-pc.bat / run-pc.bat, compat/ headers, CMake and source guards for a
  desktop (MinGW) build with libmpv from pc_deps/.

Tests: pctest/test_release.cpp, test_library.cpp, TMDB queries; 34 offline
tests in all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Card: a "More like this" row of TMDB recommendations under the seasons
  (fetched with the details, no extra request; A opens that card, Y
  bookmarks). The poster with those two actions is shared now
  (catalogPoster) by the home rows, collections and the card.
- Player: when the history has a file as finished but the Stremio account
  has a position, ask "Resume playback?" with it instead of seeking there
  silently; with nothing to offer, start from the beginning.
- Translated: the loading screen's metadata / peers / pieces / speed line,
  the "unsupported" and "link" source labels. The ZR panel stays English on
  purpose (it is diagnostics for bug reports).
- The "no sources" hint names Jackett first.
- Version 0.6.0.

Tests: a film's recommendations parsed without leaking into its own fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jackett is the app's main source, but the sources screen waited for every
request -- each Stremio addon included -- before it listed anything, so one
slow addon held back results Jackett had already returned.

- The list is built as soon as all Jackett queries are in; the addons' results
  are folded in as each one answers ("searching addons..." in the subtitle
  meanwhile). Without Jackett it waits for the addons, as before.
- A rebuild keeps the focus on the same release (by its dedup key) instead of
  dropping it on the filter row -- X on a row no longer loses the place either.
- Results that land while the player or a dialog is on top wait until the
  screen is on top again: freeing the row that opened it would leave a
  dangling pointer in borealis' focus stack (the History crash).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setup (app/setup.cpp)
- On a start with no Jackett address, once: a screen with the server URL,
  the API key and "Check and save", which also counts the indexers; "Skip"
  or B is an answer too (config.json: jackettSetupAsked). Later changes are
  in Options -> Jackett as before. TMDB (the key ships in the build) and the
  proxy (optional) are not asked.

Errors (app/jackett_diag.cpp, pure, tested)
- curl's and Jackett's failures sorted into what to do about them: a bad
  URL, a name that does not resolve, nothing listening (Jackett not running,
  wrong port / network), a timeout, a certificate, a rejected API key (401 /
  403 / "Invalid API key"), something that is not Jackett's API (a login
  page, a 404), no indexers, every indexer failing. Each is one sentence
  with the next step, in the UI language; the sources screen, the Jackett
  tab and the connection test all show it.
- The connection test now reports "Jackett works. Indexers: N" and how many
  failed, instead of a bare "Connection successful!".
- A typed address is made usable: "192.168.1.5" becomes
  http://192.168.1.5:9117; spaces and a trailing "/" go.
- http::get can report the HTTP status (optional argument).

Tests: pctest/test_jackett.cpp (4 groups); 7 offline suites in all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A scripted run of the PC build (app/autotest.cpp, NX_AUTOTEST=script):
the app presses buttons, opens screens, plays a link and saves screenshots
and view-tree dumps itself; input goes straight into borealis, never through
the desktop. A frame-end hook in borealis (VENDORED.md #7) is where the
frame is read back. Not compiled for the console.

What it found, fixed
- Sources: a search for a show also finds other titles ("Fallout" ->
  Mission.Impossible.Fallout.2018 ranked "best choice"). release::titleMatches
  (the name or one of its " / " parts starts with the local or original
  title) and release::yearsIn hide other titles, a film's other years and,
  for a show, same-named films from before it began -- only once some
  release does match, so an odd spelling never empties the list.
- Ranking: "H 264" / "x 265" with spaces read as codecs; Ukrainian-only
  audio ranks below the rest in a Russian UI. The first listing puts the
  focus on the best choice, so A plays it.
- Sizes over 2 GB wrapped (json::integer was `long`, 32-bit on Windows) and
  went missing: 64-bit now.
- Selected pills (quality filters, seasons, search filters) never showed
  as selected: setPillActive painted over the accent.
- Card: the title from TMDB's details (it kept whatever opened it); poster
  and backdrop filled in when the opener had none; when the details fail,
  say so and let Watch / Torrents search by title (it said "Still loading"
  forever); L/R season hints only for a show.
- Focus: empty strips no longer take it (a 0x0 frame held it on the card);
  a collection opens with the first poster focused; a genre tile in search
  hands it to the first result.
- No empty title bar over the card, sources, collection and setup screens;
  Options values and list pickers in the accent instead of borealis' cyan;
  accent text readable on the dark field (accentText).
- Sources menu: Jackett first, nothing marked when none is open.
- Jacred mirrors (ru.jac.black): no "Indexers" list is a working server,
  not "no indexers"; a typed public name keeps port 80; nginx's own 403 is
  "the server refused", not "wrong key". The setup says Jacred works too.
- TMDB: one retry on a failed request.
- Engine errors translated in the metadata dialog; more of them in ru.

Anime
- "Anime" in the header next to Movies and Shows: popular / new / top
  anime series, anime films, and genre rows (TMDB discover: animation,
  original language ja).
- Search: Movies / Shows / Anime; anime text search keeps anime results only.

Tests: release title matching, years, codecs, language order; json 64-bit
sizes; Jacred answers. 7 offline suites pass; .nro and PC build clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- app/workers.cpp: four threads for the network, High before Low priority.
  brls::async ran every task on one thread with a half-second nap between
  batches: a card's details waited behind the home screen's posters, and the
  two Jackett queries of a sources search ran one after the other. TMDB,
  Jackett, TorrServer, magnet metadata and the Stremio reads are on it now;
  images go Low; history / library writes and Stremio pushes stay ordered on
  brls::async. The same poster asked for twice downloads once, and every
  cache file is written whole and then renamed.
- Release sizes: checked against ru.jac.black's own sizeName -- right after
  the 64-bit json fix (5.3 GB had shown as 922 MB, 2-4 GB as nothing).
  Shown with a decimal under 100 GB and in the UI language ("14.3 ГБ").
- Ranking: seeders count up to ~30 (they stopped at ~13: 19 seeds ranked
  like 286).
- History: the source badge in the UI language.
- autotest: the D-pad itself (DLEFT/DRIGHT...), for the player's seek.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ui::paintField / ui::FieldFrame: the Stremio tab's gradient (the accent's
  tint at the top right to black, or to white in the light theme) behind
  every tab and screen -- home, catalog, search, sources, collections,
  setup, Options -- instead of borealis' flat grey. The strips' edge fades
  already blended into this gradient (tabBgAt) and now match it.
- Card: the backdrop and its shades run on under the footer, no grey bar
  (dark theme; the light one keeps its light footer for the dark hints).
- Light theme: buttons on artwork keep white text and their fill
  (setTextColor resets the fill, so it goes first); success text and
  "best choice" in a green that reads on white (theme::textGood); the
  Options sidebar a veil over the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Settings: the Jackett, TorrServer, TMDB and update-check buttons are
  relabelled from a background answer that can come half a minute later;
  switching the Options sidebar meanwhile freed the button first. They
  check an alive flag now (AsyncButton).
- Watch on a card: without a Stremio account the IMDb id is not needed
  (Jackett searches by title), so the sources open straight away instead
  of after the details request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Torrents" on a show's card opens the season's releases with no episode.
Picking a file from a pack there recorded the progress against the show as
if it were a film: no episode marked watched, no "next episode".

- release::episodeOfFile: which episode a torrent file is, by its name or
  folders (S01E06, "Сезон 1/Серия 2", "10. ..."), the pack's season when the
  name gives none. Tested.
- The file picker uses it for a catalog show: the player gets that
  episode's watch context (sources' episodeHint), so progress, the episode
  mark, Continue and "next episode" follow.
- The picker stays under the player: the next episode closes it too.
- Version 0.6.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Langegen
Langegen merged commit aa7ab8a into main Oct 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant