Skip to content

Auto-switch source when AllManga lacks the requested episode#90

Open
nevatas wants to merge 1 commit into
truelockmc:mainfrom
nevatas:feat/auto-failover-allmanga
Open

Auto-switch source when AllManga lacks the requested episode#90
nevatas wants to merge 1 commit into
truelockmc:mainfrom
nevatas:feat/auto-failover-allmanga

Conversation

@nevatas
Copy link
Copy Markdown

@nevatas nevatas commented May 19, 2026

What you'll see today

Pick an anime title in the app and try to play an episode that AllManga doesn't have (e.g. Naruto Shippuden S1E1 currently fails for me). The player overlay says:

⚠️ Episode not found on AllManga
No playable link found
Try a different source, or switch sub/dub.

…and the user has to manually open the source picker and re-pick a non-anime source. Then do it again the next time they visit the same episode, because the choice isn't remembered per title.

What this PR does

Adds a tiny per-episode failover cache so the app does the manual step for the user, once:

  • When resolveAllManga returns { ok: false } for an episode, the resolve effect now switches playerSource to the next non-async source in PLAYER_SOURCES order (Videasy → VidSrc → 2Embed) instead of surfacing the error overlay, and writes the choice into storage.SOURCE_FAILOVER_CACHE keyed by tv_<tmdbId>_s<season>_e<ep>_<dub> (or movie_<tmdbId>_<dub> for movies).
  • On subsequent visits to the same episode, the resolve effect checks the cache before hitting the IPC and switches sources immediately, so AllManga isn't re-queried for a title we already know it lacks.
  • On resolveAllManga success the cache entry is cleared, so once AllManga gets the title we naturally fall back to the user's normal preference.
  • The source picker dropdown now clears the cache entry for the current title before applying the user's selection, so manual picks can't get clobbered by the failover effect on the next render.

Only the explicit "no playable link" branch triggers the switch — network/timeout errors still go through the existing setResolveError path so a flaky connection doesn't silently change sources.

Why this design

I considered two other approaches and rejected them:

  • Probe sources upfront before letting the user click play. Adds latency on every episode load — even the ~95% of cases where AllManga has the title. The failover-on-error path only pays the cost when the title is actually missing.
  • Heartbeat timer on the iframe sources (Videasy/VidSrc/2Embed) to detect silent failures. Doable but it's a separate problem (those sources rarely 404 on a title — they usually show their own "not available" UI inside the iframe). Kept out of scope to keep this PR small; happy to follow up.

Files changed

  • src/utils/storage.js — adds STORAGE_KEYS.SOURCE_FAILOVER_CACHE and three helpers: getFailoverSource(epKey), setFailoverSource(epKey, sourceId), clearFailoverSource(epKey). The shape is a flat { [epKey]: sourceId } object kept in localStorage.
  • src/utils/api.js — adds getNextNonAsyncSource(currentId) helper that walks PLAYER_SOURCES and returns the next entry whose async flag is falsy. Async sources are skipped because they share AllManga's "missing episode" failure mode and we'd just loop.
  • src/pages/TVPage.jsx — wires the cache into the existing AllManga resolve useEffect: read the cached source up front, write it on !res.ok, clear it on success. The source-picker onClick also clears the entry for the current episode before applying the user's manual choice.
  • src/pages/MoviePage.jsx — same wiring for the single-resolve movie path with movie_<tmdbId>_<dub> as the cache key.

How to verify

  1. Open a series where AllManga is missing an episode (Naruto Shippuden S1E1 is the easy reproducer for me right now).
  2. Click that episode and let the resolve attempt finish.
  3. Before this PR: "Episode not found on AllManga" overlay. User must open the source picker manually.
  4. After this PR: the player switches to Videasy automatically and plays the episode. No overlay flash; just slightly delayed playback start while the next source loads.
  5. Navigate away and back to the same episode — second visit goes straight to Videasy without an AllManga IPC roundtrip.
  6. Manually pick AllManga from the source dropdown — cache entry for this episode is cleared, so the resolve effect tries AllManga again (and fails again, re-caching to the next source). The failover doesn't fight the user's manual override on the same click.

AllManga doesn't always have every episode (especially newer or less
popular titles). The default source for anime content is AllManga, so
the user currently gets an "Episode not found on AllManga · Try a
different source" overlay and has to manually switch sources each visit.

Add per-episode failover:

  * On AllManga resolve failure (res.ok === false), automatically switch
    to the next non-async source (Videasy → VidSrc → 2Embed) and remember
    the choice in localStorage keyed by (tmdbId, season, episode, dub).
  * On subsequent visits to the same episode, skip the AllManga IPC call
    and load the cached fallback directly.
  * On AllManga resolve success, clear the cache entry so we don't keep
    overriding once the title becomes available upstream.
  * Manual source picks clear the entry for the current episode, so the
    auto-switch can't fight the user's explicit choice.

Network/timeout errors still surface the existing error overlay — only
explicit "episode missing" responses trigger the failover, so a flaky
connection won't silently change sources.

Covers both episodic content (TVPage) and movies (MoviePage). Cache
keys: tv_<tmdbId>_s<season>_e<ep>_<sub|dub> and movie_<tmdbId>_<sub|dub>.
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