mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
docs(portals): record the behaviour the review rounds changed
The architecture doc and CLAUDE.md described the feature as first written, not as it now behaves: pins were documented as a stored preference without saying they decide playback, failover was described as stopping at the first unresolvable candidate, the probe as HEAD-only, and discovery as pure FTS with no mention that short titles cannot be tokenized at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5514def368
commit
a715e05a09
2 files changed
+26
-5
No files matched your search
@@ -823,10 +823,10 @@ engine` (restart required) or
|
||||
- Finds the same movie in the user's other imported playlists and adds a "Sources N" chip to the Xtream VOD action row (only when ≥1 alternative exists), plus a `.source-caption` line reporting where playback is coming from. The chip opens a 460px anchored `MatMenu` popover (`libs/ui/components/src/lib/vod-sources/`), reused unchanged on the playback-error screen.
|
||||
- Scope v1 is **Xtream ↔ Xtream, movies only, Electron only**. Stalker never reaches the `content` table and M3U is a JSON blob whose search forces `content_type:'live'`; both are additive later since `VodSourceCandidate.portalType` already carries all three. In the PWA every entry point is gated off by a bridge `typeof` check and the chip renders nothing.
|
||||
- **Metadata provenance is the core contract.** Every field is `{value, provenance}` where `api`/`probe` are facts (plain tag), `parsed` is a title-regex guess (tag prefixed `~`, warn colour), and absent renders **no tag at all** plus a `check` chip. `factualOnly()` in `vod-source-metadata.util.ts` is the only accessor allowed for ranking/failover, so guesses are structurally unable to influence a decision. `VodSourceProbeStatus` separates `fail` (contacted and refused) from `unknown` (timed out / blocked / no capability) — an unchecked source is never shown as offline. Quality is derived from pixel **width** because letterboxing crops height.
|
||||
- Discovery (`DB_FIND_TITLE_SOURCES`, trigram FTS over `content_title_fts`) is lazy and returns only what the `content` table can prove. Resolution is deferred to click/pin/check because `content` stores no `container_extension` and `constructVodUrl` returns `''` without one — each alternative costs a live `get_vod_info` against the foreign playlist's credentials.
|
||||
- Discovery (`DB_FIND_TITLE_SOURCES`, trigram FTS over `content_title_fts`) is lazy and returns only what the `content` table can prove; titles whose tokens are all shorter than three characters ("Up", "It") fall back to a bounded LIKE scan, since the trigram tokenizer cannot index them at all. Resolution is deferred to click/pin/check because `content` stores no `container_extension` and `constructVodUrl` returns `''` without one — each alternative costs a live `get_vod_info` against the foreign playlist's credentials.
|
||||
- Switching = one `inlinePlayback.set({...next, startTime})`, never null-then-set, so the player and engine survive and re-seek. The carried position is read *before* the 15s persistence throttle, and `VodDetailsPlaybackService` uses a one-shot `resumeSettled` latch so a resuming engine's `timeupdate` at ~0 cannot overwrite the resume point.
|
||||
- Pins are keyed portal-agnostically (`tmdb:{id}` else `title:{base}:{year}`, `vod_source_pins` table); lookups pass every alias most-trusted-first so a late TMDB id does not orphan a title-keyed pin.
|
||||
- Auto-failover is `Settings.vodAutoFailover`, **opt-in and off by default**, web engines only. Each source is tried at most once per session (`triedSourceIds` only grows), so it terminates structurally. The switch is never silent: the toast names the new playlist, offers Undo, and warns "dub may differ" only when both sides state an audio track as fact.
|
||||
- Pins are keyed portal-agnostically (`tmdb:{id}` else `title:{base}:{year}`, `vod_source_pins` table); lookups pass every alias most-trusted-first so a late TMDB id does not orphan a title-keyed pin. A pin is not decoration: the primary Play action starts from the pinned source, and it outranks everything else in failover ranking. The movie-identity key covers title, year and tmdbId, so TMDB enrichment re-triggers discovery and rebuilds the pin keys.
|
||||
- Auto-failover is `Settings.vodAutoFailover`, **opt-in and off by default**, web engines only. Each source is tried at most once per session (`triedSourceIds` only grows), so it terminates structurally, and it continues past candidates that fail to resolve rather than stopping at the first one — `switchTo` reports whether it was unresolvable (keep going) or superseded (stop), since only the former marks the candidate tried. The switch is never silent: the toast names the new playlist, offers Undo, and warns "dub may differ" only when both sides state an audio track as fact.
|
||||
- HEAD probe reuses the main-process handler extracted to `apps/electron-backend/src/app/events/stream-probe.ts` (`STREAM_PROBE_URL`; `XTREAM_PROBE_URL` still delegates there for catchup). No ffprobe — the binary is not bundled.
|
||||
- See `docs/architecture/vod-multi-source.md`
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ Every metadata value carries **where it came from**:
|
||||
|---|---|---|
|
||||
| `api` | `get_vod_info` — container, codec, audio, dimensions | plain tag |
|
||||
| `parsed` | regex over the title/filename | tag prefixed `~`, warn colour |
|
||||
| `probe` | HEAD → reachable + latency | `ok` / `fail` status tag |
|
||||
| `probe` | HEAD → reachable + latency (retried as a ranged GET when the server answers 405/501, since plenty serve media over GET while refusing HEAD) | `ok` / `fail` status tag |
|
||||
| *absent* | — | **no tag at all** + a `check` chip |
|
||||
|
||||
Three rules follow, and each is enforced in code rather than by convention:
|
||||
@@ -110,6 +110,16 @@ landed is stored under its title key and prefers a `tmdb:` key afterwards;
|
||||
reading both means the id arriving later does not orphan the pin, and unpinning
|
||||
clears every alias so a stale row cannot resurrect it.
|
||||
|
||||
## Short titles
|
||||
|
||||
The trigram tokenizer cannot index tokens under three characters, so a title
|
||||
like "Up", "It" or "Us" produces an empty `MATCH` expression. Discovery falls
|
||||
back to a bounded `LIKE` scan for exactly those titles rather than returning
|
||||
nothing — the two-tier normalized confirmation still runs afterwards, so the
|
||||
looser query does not admit "Upgrade" for "Up". The scan is slower and cannot
|
||||
use the title index, which is acceptable because it is only reachable for the
|
||||
handful of titles FTS structurally cannot serve.
|
||||
|
||||
## Why resolution is lazy
|
||||
|
||||
The `content` table stores no `container_extension`, and
|
||||
@@ -161,7 +171,18 @@ an N-source movie fails over at most N−1 times and then shows the honest error
|
||||
screen. Returning to an earlier source by hand does not clear the set.
|
||||
|
||||
A source that cannot even be resolved is marked tried without becoming active,
|
||||
so it is not re-picked.
|
||||
and failover **continues to the next candidate** rather than giving up —
|
||||
production calls `failover()` only once, on the original failure, so stopping at
|
||||
a dead top-ranked source would strand every healthy one below it. `switchTo`
|
||||
therefore reports which of two things happened: `unresolvable` (marked tried,
|
||||
keep going) or `superseded` (something newer owns the screen, stop). Without
|
||||
that distinction the loop would spin on a superseded target forever, because
|
||||
only the first outcome marks the candidate tried.
|
||||
|
||||
**A pin is not decoration.** The primary Play action starts from the pinned
|
||||
source when one is set, and the pin outranks every other signal in the ranking
|
||||
above. Loading a pin that only decorated its row would mean "make this the main
|
||||
source" survived a restart as an icon and nothing else.
|
||||
|
||||
The switch is **always announced** — another source can carry a different dub or
|
||||
cut. The toast offers Undo, and adds a dub warning when
|
||||
|
||||
Reference in new issue
Block a user