mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
feat(portals): find the same movie in your other playlists
A movie that exists in several imported Xtream playlists now shows a "Sources N" chip on its detail page and in the player. Switching playlist mid-film keeps the timecode, a preferred source can be pinned per movie, and a failed stream offers the alternatives instead of a dead end. The governing rule is that a guess is never presented as a fact. Every metadata value carries where it came from — `api` (the provider said so), `parsed` (inferred from the title) or `probe` (we contacted the stream). Facts render as plain tags, guesses are prefixed `~` in a warning colour, and an unknown value renders no tag at all plus a "check" affordance. Ranking and failover read through `factualOnly()`, so a filename claiming 4K is structurally unable to outrank a source that was actually reached. A probe that could not complete reports "unknown", never "unavailable". Scope is deliberately narrow: Xtream to Xtream, movies only, Electron only. Stalker never reaches the `content` table and M3U is a JSON blob whose search forces live content; both are additive later, since the candidate type already carries all three portal kinds. In the PWA every entry point is gated off and the chip renders nothing. Auto-failover is opt-in and off by default. Each source is tried at most once per session, so it terminates structurally, and the switch is never silent — the toast names the new playlist, offers an undo, and warns that the dub may differ only when both sides state an audio track as fact. Notable details: - Playlist names are routinely the pasted URL, credentials included. They are never rendered raw; a short host-only label is derived instead. - Quality is derived from pixel width, not height: a 2.39:1 1080p master is 1920x800, and bucketing that by height would publish "720p" as a fact. - Switching is a single `inlinePlayback.set()` so the player and engine survive and re-seek; the carried position is read before the 15s persistence throttle so it does not rewind. - Sources from one playlist collapse into a group, since the same film often appears there several times under different stream ids. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e2300bea11
commit
004cb65fe6
101 files changed
+7183
-192
No files matched your search
@@ -32,6 +32,10 @@ import {
|
||||
} from './portal-playback.interface';
|
||||
import { PortalDebugEvent } from './portal-debug.interface';
|
||||
import { CatalogTitleMatch } from './catalog-title-match.interface';
|
||||
import {
|
||||
VodSourceCandidateRow,
|
||||
VodSourcePin,
|
||||
} from './vod-source.interface';
|
||||
import { Settings } from './settings.interface';
|
||||
import {
|
||||
TmdbCacheEntry,
|
||||
@@ -254,8 +258,17 @@ export interface ElectronBridgeXtreamCancelResult extends ElectronBridgeResult {
|
||||
}
|
||||
|
||||
export interface ElectronBridgeXtreamProbeResult {
|
||||
/**
|
||||
* HTTP status, or 0 when no response was obtained.
|
||||
*
|
||||
* 0 covers a timeout and a URL rejected by the redirect-safety policy as
|
||||
* well as a dead host, so it means "could not check" — never "offline".
|
||||
*/
|
||||
status: number;
|
||||
url: string;
|
||||
/** Round-trip time; present whenever the request completed either way. */
|
||||
latencyMs?: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface ElectronBridgeEpgFetchResult extends ElectronBridgeResult {
|
||||
@@ -672,6 +685,11 @@ export interface ElectronBridgeApi {
|
||||
url: string,
|
||||
method?: 'GET' | 'HEAD'
|
||||
) => Promise<ElectronBridgeXtreamProbeResult>;
|
||||
/** Generic stream reachability probe (VOD multi-source availability) */
|
||||
probeStreamUrl: (
|
||||
url: string,
|
||||
method?: 'GET' | 'HEAD'
|
||||
) => Promise<ElectronBridgeXtreamProbeResult>;
|
||||
refreshPlaylist: (
|
||||
payload: PlaylistRefreshPayload
|
||||
) => Promise<Playlist | PlaylistRefreshCancelledResult>;
|
||||
@@ -849,6 +867,18 @@ export interface ElectronBridgeApi {
|
||||
>;
|
||||
/** Cross-playlist title matching (actor page "All portals" scope) */
|
||||
dbMatchTitles: (titles: string[]) => Promise<CatalogTitleMatch[]>;
|
||||
/** VOD multi-source: the same movie in the user's other Xtream playlists */
|
||||
dbFindTitleSources: (request: {
|
||||
title: string;
|
||||
year?: number | null;
|
||||
excludePlaylistId?: string | null;
|
||||
}) => Promise<VodSourceCandidateRow[]>;
|
||||
/** Per-movie pinned source; keys are passed most-trusted first */
|
||||
dbGetVodSourcePin: (matchKeys: string[]) => Promise<VodSourcePin | null>;
|
||||
dbSetVodSourcePin: (pin: VodSourcePin) => Promise<ElectronBridgeResult>;
|
||||
dbClearVodSourcePin: (
|
||||
matchKeys: string[]
|
||||
) => Promise<ElectronBridgeResult>;
|
||||
onChannelChange?: (
|
||||
callback: (data: { direction: 'up' | 'down' }) => void
|
||||
) => () => void;
|
||||
|
||||
Reference in new issue
Block a user