import type { AppUpdateChannel } from './app-update-channel.util'; import type { SourceProbeContext, SourceHealthResult } from './source-health'; import type { XtreamConnectionFailure } from './xtream-connection-test'; import type { ZoomLevelAction } from './zoom-level.util'; import type { CatchupDownloadMetadata, DownloadRecoveryResult, DownloadContentType, } from './catchup-download.interface'; import { EmbeddedMpvBounds, EmbeddedMpvRecordingStartOptions, EmbeddedMpvSession, EmbeddedMpvSubtitleStyle, EmbeddedMpvSupport, } from './embedded-mpv-session.interface'; import { ContentMetadataPatch } from './content-metadata.interface'; import { DownloadMetadataSnapshot } from './download-metadata.interface'; import { EpgChannelMetadata } from './epg-channel-metadata.model'; import { EpgProgram } from './epg-program.model'; import { ExternalPlayerSession } from './external-player-session.interface'; import { GlobalSearchPaginationOptions, GlobalSearchResult, GlobalSearchResultSource, } from './global-search-result.interface'; import { M3uFavoriteChannel } from './m3u-favorite-channel.interface'; import { RecordingProgramSnapshot, RecordingSourceType, RecordingStatus, } from './recording-metadata.interface'; import { PlaybackPositionData } from './playback-position.interface'; import { AutoUpdatePlaylistsResult } from './playlist-auto-update.interface'; import { XtreamBackupFavoriteItem, XtreamBackupHiddenCategory, XtreamBackupRecentlyViewedItem, } from './playlist-backup.interface'; import { PlaylistRefreshCancelledResult, PlaylistRefreshEvent, PlaylistRefreshPayload, } from './playlist-refresh.interface'; import { Playlist } from './playlist.interface'; import { PlayerContentInfo, ResolvedPortalPlayback, } from './portal-playback.interface'; import { PortalDebugEvent } from './portal-debug.interface'; import { CatalogTitleMatch } from './catalog-title-match.interface'; import { StreamProbeHeaders, VodSourceCandidateRow, VodSourcePin, } from './vod-source.interface'; import { Settings } from './settings.interface'; import { TmdbCacheEntry, TmdbCacheMediaType, TmdbCacheStats, } from './tmdb.interface'; import { XtreamCategory } from './xtream-category.interface'; export const ELECTRON_BRIDGE_CONTENT_TYPES = { Episode: 'episode', Live: 'live', Movie: 'movie', Series: 'series', Vod: 'vod', } as const; export type ElectronBridgeContentType = (typeof ELECTRON_BRIDGE_CONTENT_TYPES)[keyof typeof ELECTRON_BRIDGE_CONTENT_TYPES]; export type ElectronBridgePortalContentType = | typeof ELECTRON_BRIDGE_CONTENT_TYPES.Live | typeof ELECTRON_BRIDGE_CONTENT_TYPES.Movie | typeof ELECTRON_BRIDGE_CONTENT_TYPES.Series; export type ElectronBridgePlaybackContentType = | typeof ELECTRON_BRIDGE_CONTENT_TYPES.Vod | typeof ELECTRON_BRIDGE_CONTENT_TYPES.Episode; export const ELECTRON_BRIDGE_PLAYLIST_TYPES = { M3uFile: 'm3u-file', M3uText: 'm3u-text', M3uUrl: 'm3u-url', Stalker: 'stalker', Xtream: 'xtream', } as const; export type ElectronBridgePlaylistType = (typeof ELECTRON_BRIDGE_PLAYLIST_TYPES)[keyof typeof ELECTRON_BRIDGE_PLAYLIST_TYPES]; export const ELECTRON_BRIDGE_EPG_PROGRESS_STATUSES = { Cancelled: 'cancelled', Complete: 'complete', Error: 'error', Loading: 'loading', Queued: 'queued', } as const; export type ElectronBridgeEpgProgressStatus = (typeof ELECTRON_BRIDGE_EPG_PROGRESS_STATUSES)[keyof typeof ELECTRON_BRIDGE_EPG_PROGRESS_STATUSES]; export const ELECTRON_BRIDGE_SECURITY_ERROR_CODES = { EpgPrivateNetworkBlocked: 'epg-private-network-blocked', InvalidTlsCertificate: 'invalid-tls-certificate', } as const; export type ElectronBridgeSecurityErrorCode = (typeof ELECTRON_BRIDGE_SECURITY_ERROR_CODES)[keyof typeof ELECTRON_BRIDGE_SECURITY_ERROR_CODES]; export const ELECTRON_BRIDGE_DB_OPERATION_STATUSES = { Cancelled: 'cancelled', Completed: 'completed', Error: 'error', Progress: 'progress', Started: 'started', } as const; export type ElectronBridgeDbOperationStatus = (typeof ELECTRON_BRIDGE_DB_OPERATION_STATUSES)[keyof typeof ELECTRON_BRIDGE_DB_OPERATION_STATUSES]; export const ELECTRON_BRIDGE_REMOTE_CONTROL_COMMAND_TYPES = { ChannelSelectNumber: 'channel-select-number', VolumeDown: 'volume-down', VolumeToggleMute: 'volume-toggle-mute', VolumeUp: 'volume-up', } as const; export type ElectronBridgeRemoteControlCommandType = (typeof ELECTRON_BRIDGE_REMOTE_CONTROL_COMMAND_TYPES)[keyof typeof ELECTRON_BRIDGE_REMOTE_CONTROL_COMMAND_TYPES]; export const ELECTRON_BRIDGE_REMOTE_PORTALS = { M3u: 'm3u', Stalker: 'stalker', Unknown: 'unknown', Xtream: 'xtream', } as const; export type ElectronBridgeRemotePortal = (typeof ELECTRON_BRIDGE_REMOTE_PORTALS)[keyof typeof ELECTRON_BRIDGE_REMOTE_PORTALS]; export const ELECTRON_BRIDGE_DOWNLOAD_STATUSES = { Canceled: 'canceled', Completed: 'completed', Downloading: 'downloading', Failed: 'failed', Paused: 'paused', Queued: 'queued', } as const; export type ElectronBridgeDownloadStatus = (typeof ELECTRON_BRIDGE_DOWNLOAD_STATUSES)[keyof typeof ELECTRON_BRIDGE_DOWNLOAD_STATUSES]; export const ELECTRON_BRIDGE_DOWNLOAD_START_REASONS = { AlreadyDownloaded: 'already-downloaded', AlreadyInProgress: 'already-in-progress', } as const; export type ElectronBridgeDownloadStartReason = (typeof ELECTRON_BRIDGE_DOWNLOAD_START_REASONS)[keyof typeof ELECTRON_BRIDGE_DOWNLOAD_START_REASONS]; export const ELECTRON_BRIDGE_EPISODE_IDENTITY_SCOPES = { StalkerEmbeddedVod: 'stalker-embedded-vod', StalkerLazyVod: 'stalker-lazy-vod', StalkerRegularSeries: 'stalker-regular-series', } as const; export type ElectronBridgeEpisodeIdentityScope = (typeof ELECTRON_BRIDGE_EPISODE_IDENTITY_SCOPES)[keyof typeof ELECTRON_BRIDGE_EPISODE_IDENTITY_SCOPES]; export type ElectronDownloadFileAvailability = 'available' | 'missing' | 'not-applicable'; export const ELECTRON_BRIDGE_APP_UPDATE_STATUSES = { Unsupported: 'unsupported', Idle: 'idle', Checking: 'checking', Available: 'available', NotAvailable: 'not-available', Downloading: 'downloading', Downloaded: 'downloaded', Error: 'error', } as const; export type ElectronBridgeAppUpdateStatusValue = (typeof ELECTRON_BRIDGE_APP_UPDATE_STATUSES)[keyof typeof ELECTRON_BRIDGE_APP_UPDATE_STATUSES]; export const ELECTRON_BRIDGE_GLOBAL_RECENTLY_ADDED_KINDS = { All: 'all', Series: 'series', Vod: 'vod', } as const; export type ElectronBridgeGlobalRecentlyAddedKind = (typeof ELECTRON_BRIDGE_GLOBAL_RECENTLY_ADDED_KINDS)[keyof typeof ELECTRON_BRIDGE_GLOBAL_RECENTLY_ADDED_KINDS]; export interface ElectronBridgeResult { success: boolean; } export interface ElectronBridgeErrorResult extends ElectronBridgeResult { error?: string; } export interface ElectronBridgeCountResult extends ElectronBridgeResult { count: number; } export interface ElectronBridgeDialogFilter { name: string; extensions: string[]; } export interface ElectronBridgeWindowState { isMaximized: boolean; isFullScreen: boolean; } /** * Portal credentials for an auth-gated stream, passed alongside the scoped * header override. The main process attaches them only to requests going to * the exact origin of the override's `scopeUrl`, keeps them in memory only, * and drops them when the scoped override is cleared or replaced. */ export interface ElectronBridgeStreamCredentials { authorization?: string | null; cookie?: string | null; } /** * A playlist file the operating system asked the app to open — a command line * argument, a file association double-click, or macOS' `open-file` event. The * main process resolves the path to an absolute one before handing it over. */ export interface ElectronBridgePlaylistOpenRequest { fileName: string; filePath: string; /** Pass back to `acknowledgePlaylistOpenRequest` once received. */ requestId: string; } export interface ElectronBridgeAppUpdateRelease { version: string; releaseDate?: string; releaseName?: string | null; releaseNotes?: string | null; } export interface ElectronBridgeAppUpdateProgress { bytesPerSecond?: number; percent: number; total?: number; transferred?: number; } export interface ElectronBridgeAppUpdateStatus { status: ElectronBridgeAppUpdateStatusValue; currentVersion: string; latestVersion?: string; release?: ElectronBridgeAppUpdateRelease; progress?: ElectronBridgeAppUpdateProgress; error?: string; supportedSelfUpdate: boolean; /** Release page for the configured channel; opened for manual installs. */ manualDownloadUrl: string; /** Channel the updater checks (`Settings.updateChannel`, mirrored). */ channel: AppUpdateChannel; /** * Channel the current verdict — `latestVersion`, `release`, a download * in flight or finished — was produced on. Absent until the first * check. Differs from `channel` after a channel change saved while a * download was running or done: the download is kept, the new channel * has not been checked yet. */ verdictChannel?: AppUpdateChannel; /** Channel the running build came from, read off its version string. */ installedChannel: AppUpdateChannel; } export type ElectronBridgeAppUpdateReleaseNotesDirection = 'previous' | 'next'; export interface ElectronBridgeAppUpdateReleaseNotesRequest { version?: string; direction?: ElectronBridgeAppUpdateReleaseNotesDirection; fallbackToLatest?: boolean; } export interface ElectronBridgeAppUpdateReleaseNotes { version: string; tagName: string; releaseName?: string | null; publishedAt?: string | null; bodyMarkdown: string; htmlUrl: string; hasPrevious: boolean; hasNext: boolean; } export interface ElectronBridgeAiSettings { aiProvider: string; aiModelName: string; aiApiKey: string; } export interface ElectronBridgeStalkerRequestPayload { probe?: SourceProbeContext; url: string; macAddress: string; params: Record; token?: string; serialNumber?: string; requestId?: string; /** * Endpoint-discovery probes only: exempt this request from the main * process' per-host connectivity guard. Discovery walks several candidate * paths on one host and expects most of them to fail, so its failures must * neither count towards the guard nor be fast-failed by it. */ skipConnectionGuard?: boolean; } export interface ElectronBridgeXtreamRequestPayload { probe?: SourceProbeContext; connectionTest?: boolean; url: string; params: Record; requestId?: string; sessionId?: string; suppressErrorLog?: boolean; } export interface ElectronBridgeXtreamResponse { connectionFailure?: XtreamConnectionFailure; payload: unknown; action: string; } export interface ElectronBridgeXtreamCancelResult extends ElectronBridgeResult { cancelled: number; } 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 { message?: string; skipped?: string[]; } export interface ElectronBridgeTrustOptions { trustedPrivateNetworkEpgUrls?: string[]; trustedInsecureTlsHosts?: string[]; } export interface ElectronBridgePlaylistFetchOptions extends ElectronBridgeTrustOptions { userAgent?: string; } export interface ElectronBridgeEpgFreshnessResult { staleUrls: string[]; freshUrls: string[]; } export interface ElectronBridgeEpgMapping { id: number; channelKey: string; epgChannelId: string; playlistId: string | null; } /** A programme-search hit: the programme plus the XMLTV channel's display name. */ export interface ElectronBridgeEpgSearchProgram extends EpgProgram { channelName: string | null; } export interface ElectronBridgeEpgSearchResult { id: string; displayName: string; iconUrl: string | null; } export interface ElectronBridgeEpgLookupOptions { sourceUrls?: string[]; } /** * Options for the batched "currently airing" lookup. `nowMs` is the instant * the lookup is evaluated at: the renderer passes its wall clock shifted into * the provider's EPG clock (`epgProviderClockMs`, see * `epg-display-offset.util.ts`) so the row the backend picks is the one the * renderer will also present as "now". Absent, the main process uses its own * clock. */ export interface ElectronBridgeCurrentProgramsOptions extends ElectronBridgeEpgLookupOptions { nowMs?: number; } export interface ElectronBridgeEpgGuideWindow extends ElectronBridgeEpgLookupOptions { /** * Playlist channel lookup keys (tvg-id, else name), ≤100 keys for * programmes, ≤2000 for coverage; larger batches are truncated and the * cut keys are absent from the answer. */ channelIds: string[]; /** Provider-clock window bounds in epoch ms. */ fromMs: number; toMs: number; } export interface ElectronBridgeEpgProgressStats { totalChannels: number; totalPrograms: number; } export interface ElectronBridgeEpgProgress { url: string; generation?: number; status: ElectronBridgeEpgProgressStatus; stats?: ElectronBridgeEpgProgressStats; error?: string; errorCode?: ElectronBridgeSecurityErrorCode; errorHost?: string; queuePosition?: number; } export interface ElectronBridgeEpgChannelSummary { id: string; displayName: string; } export interface ElectronBridgeEpgChannelListResult { channels: ElectronBridgeEpgChannelSummary[]; /** Always empty for this endpoint; retained for wire-format compatibility. */ programs: []; } export interface ElectronBridgeDbOperationEvent { operationId?: string; operation: string; playlistId?: string; status: ElectronBridgeDbOperationStatus; phase?: string; current?: number; total?: number; increment?: number; error?: string; } export interface ElectronBridgePlaylistInput { id?: string; _id?: string; name?: string; title?: string; serverUrl?: string; username?: string; password?: string; macAddress?: string; url?: string; type?: ElectronBridgePlaylistType | string; lastUpdated?: string; } export interface ElectronBridgePlaylistRow { id: string; name: string; serverUrl: string; username: string; password: string; type: string; macAddress?: string; url?: string; lastUpdated?: string; } export type ElectronBridgePlaylistUpsertInput = Playlist | ElectronBridgePlaylistInput; export interface ElectronBridgeCategoryRow { id: number; name: string; playlist_id: string; type: 'movies' | 'live' | 'series'; xtream_id: number; hidden: boolean; /** Parental lock index, stamped from the renderer's lock store. */ locked: boolean; } export interface ElectronBridgeXtreamContent { id: number; category_id: number; title: string; rating: string; added: string; poster_url: string; backdrop_url?: string | null; epg_channel_id?: string | null; tv_archive?: number | null; tv_archive_duration?: number | null; direct_source?: string | null; xtream_id: number; type: string; added_at?: string; viewed_at?: string; position?: number | null; } export type ElectronBridgeXtreamContentStream = | { category_id: string | number; rating?: string | number; rating_imdb?: string; last_modified?: string; added?: string; stream_icon?: string; poster?: string; cover?: string; name?: string; title?: string; epg_channel_id?: string; tv_archive?: string | number; tv_archive_duration?: string | number; direct_source?: string; series_id?: string | number; stream_id?: string | number; } | Record; export type ElectronBridgeGlobalSearchResult = GlobalSearchResult; export interface ElectronBridgeGlobalRecentItem extends ElectronBridgeXtreamContent { playlist_id: string; playlist_name: string; viewed_at: string; } export interface ElectronBridgeGlobalFavoriteItem extends ElectronBridgeXtreamContent { playlist_id: string; playlist_name: string; added_at: string; } export interface ElectronBridgeGlobalRecentlyAddedItem extends ElectronBridgeXtreamContent { playlist_id: string; playlist_name: string; added_at: string; } export interface ElectronBridgeFavoriteReorderUpdate { content_id: number; /** Favorites are playlist-scoped — scope the position write per playlist */ playlist_id: string; position: number; } export interface ElectronBridgeRecentItemsBatchItem { contentId: number; playlistId: string; } export interface ElectronBridgeRemoteControlCommand { type: ElectronBridgeRemoteControlCommandType; number?: number; } export interface ElectronBridgeRemoteControlStatus { portal: ElectronBridgeRemotePortal; isLiveView: boolean; channelName?: string; channelNumber?: number; epgTitle?: string; epgStart?: string; epgEnd?: string; supportsVolume?: boolean; volume?: number; muted?: boolean; } export interface ElectronBridgePlayerError { player: string; error: string; originalError: string; } export interface ElectronBridgePlaybackPositionInput extends Omit< PlaybackPositionData, 'playlistId' | 'updatedAt' > { playlistType?: ElectronBridgePlaylistType; } export interface ElectronBridgeDownloadHeaders { userAgent?: string; referer?: string; origin?: string; } export interface ElectronBridgeDownloadStartPayload { playlistId: string; xtreamId: number; contentType: DownloadContentType; catchup?: CatchupDownloadMetadata | null; title: string; url: string; posterUrl?: string; metadataSnapshot?: DownloadMetadataSnapshot; downloadFolder: string; headers?: ElectronBridgeDownloadHeaders; seriesXtreamId?: number; seasonNumber?: number; episodeNumber?: number; episodeIdentityScope?: ElectronBridgeEpisodeIdentityScope; playlistName?: string; playlistType?: ElectronBridgePlaylistType; serverUrl?: string; portalUrl?: string; macAddress?: string; } export interface ElectronBridgeDownloadStartResult extends ElectronBridgeErrorResult { id?: number; reason?: ElectronBridgeDownloadStartReason; } export interface ElectronBridgeDownloadRedownloadResult extends ElectronBridgeErrorResult { recovered?: boolean; } export interface ElectronDownloadItem { id: number; playlistId: string; xtreamId: number; contentType: DownloadContentType; catchup?: CatchupDownloadMetadata | null; seriesXtreamId?: number; seasonNumber?: number; episodeNumber?: number; episodeIdentityScope?: ElectronBridgeEpisodeIdentityScope | null; title: string; url: string; fileName?: string; filePath?: string; posterUrl?: string; metadataSnapshot?: DownloadMetadataSnapshot; status: ElectronBridgeDownloadStatus; fileAvailability: ElectronDownloadFileAvailability; bytesDownloaded?: number; totalBytes?: number; errorMessage?: string; createdAt?: string; updatedAt?: string; } /** * Renderer-facing live-TV recording row. `fileAvailability` is derived at * read time (never persisted) and `programs` is the decoded `programs_json` * column. Recordings reuse the download availability vocabulary so the * manager UI can share its missing-file affordances — extended by * `'unknown'` for an inconclusive probe (timeout, permission or I/O error): * only proven absence may move a recording to Needs attention or hide its * file actions, so consumers gate on `=== 'missing'`, never on * `!== 'available'`. */ export interface ElectronRecordingItem { id: number; sessionId?: string; status: RecordingStatus; filePath: string; fileSizeBytes?: number; channelName: string; channelLogoUrl?: string; playlistId?: string; playlistName?: string; sourceType?: RecordingSourceType; epgChannelId?: string; programTitle?: string; programDescription?: string; programStart?: string; programStop?: string; programs?: RecordingProgramSnapshot[]; errorMessage?: string; startedAt: string; endedAt?: string; createdAt?: string; updatedAt?: string; fileAvailability: ElectronDownloadFileAvailability | 'unknown'; } export interface ElectronBridgeApi { onPortalDebugEvent?: ( callback: (data: PortalDebugEvent) => void ) => () => void; onPlaylistRefreshEvent?: ( callback: (data: PlaylistRefreshEvent) => void ) => () => void; getAppVersion: () => Promise; platform: string; getAppUpdateStatus: () => Promise; checkForAppUpdate: () => Promise; downloadAppUpdate: () => Promise; installAppUpdate: () => Promise; getAppUpdateReleaseNotes: ( request?: ElectronBridgeAppUpdateReleaseNotesRequest ) => Promise; onAppUpdateStatusChange: ( callback: (status: ElectronBridgeAppUpdateStatus) => void ) => () => void; minimizeWindow: () => Promise; toggleMaximizeWindow: () => Promise; /** * Toggles OS-level window fullscreen (F11). Reports the requested state; * the `onWindowStateChange` push stays authoritative once the window * manager has acted. */ toggleFullScreenWindow: () => Promise; closeWindow: () => Promise; /** * Zoom shortcuts (Cmd/Ctrl and +/−/0). Synchronous and preload-local: * steps the FRAME-BOUND temporary zoom level through `webFrame` (never a * main-process `webContents.setZoomLevel`, whose per-URL entry the app's * `file://` path routing resets — issue #1109) by `stepZoomLevel`'s * rules — one `ZOOM_LEVEL_STEP` inside `ZOOM_LEVEL_MIN..ZOOM_LEVEL_MAX`, * a stored level already outside them never moved against the request — * and returns the level now applied, which is therefore not itself * guaranteed to be within the limits. Persistence needs nothing from the * caller: the main process reads the live level back on close, quit and * reload. */ adjustZoomLevel: (action: ZoomLevelAction) => number; getWindowState: () => Promise; onWindowStateChange: ( callback: (state: ElectronBridgeWindowState) => void ) => () => void; /** * While active, the main process intercepts window close / app quit and * pushes a close request to the renderer instead of closing. Used by the * settings page while its form holds unsaved edits. */ setWindowCloseGuard: (active: boolean) => Promise; /** * Completes a close the guard intercepted: the main process re-runs the * original intent (window close or app quit) with the guard bypassed. */ confirmWindowClose: () => Promise; /** * Abandons a close the guard intercepted (the user stays), so the * remembered close-vs-quit intent cannot leak into a later attempt. * Carries the id of the request being abandoned: a cancellation that * arrives after a NEWER interception must not wipe that newer intent. */ cancelWindowClose: (requestId?: number) => Promise; onWindowCloseRequested: ( callback: (requestId: number) => void ) => () => void; /** * While active, the main process holds a * `powerSaveBlocker('prevent-display-sleep')` so the screen stays awake * during built-in video playback. The flag is cleared automatically when * the renderer navigates, reloads, or dies, so a crashed page can never * leave the display pinned awake. */ setPlaybackKeepAwake: (active: boolean) => Promise; /** * Parental lock enforcement for the SQLite worker: `true` withholds * every row in a locked category from content reads, `false` returns * them. Reset to the mirrored `parentalLockEnabled` setting when the * renderer reloads or dies, so a crashed page can never leave the * library unlocked. */ setParentalLockState: (active: boolean) => Promise; fetchPlaylistByUrl: ( url: string, title?: string, options?: ElectronBridgePlaylistFetchOptions ) => Promise; updatePlaylistFromFilePath: ( filePath: string, title: string ) => Promise; openPlaylistFromFile: () => Promise; onPlaylistOpenRequest: ( callback: (request: ElectronBridgePlaylistOpenRequest) => void ) => () => void; /** * Tells the main process that an `onPlaylistOpenRequest` listener is * attached, so it can push the playlist files the OS handed over — both * the ones queued before this renderer existed and any that arrive later. * Call it *after* subscribing. */ announcePlaylistOpenListener: () => Promise; /** * Confirms a pushed request reached this renderer. Until it does, the main * process keeps the request and replays it to the next renderer. */ acknowledgePlaylistOpenRequest: (requestId: string) => Promise; getPathForFile: (file: File) => string; saveFileDialog: ( defaultPath: string, filters?: ElectronBridgeDialogFilter[] ) => Promise; writeFile: ( filePath: string, content: string ) => Promise; setUserAgent: ( userAgent?: string | null, referer?: string | null, scopeUrl?: string | null, credentials?: ElectronBridgeStreamCredentials | null ) => Promise; openInMpv: ( url: string, title: string, thumbnail: string, userAgent: string | undefined, referer?: string, origin?: string, contentInfo?: PlayerContentInfo, startTime?: number, headers?: Record ) => Promise; openInVlc: ( url: string, title: string, thumbnail: string, userAgent: string | undefined, referer?: string, origin?: string, contentInfo?: PlayerContentInfo, startTime?: number, headers?: Record ) => Promise; autoUpdatePlaylists: ( playlists: Playlist[], options?: ElectronBridgeTrustOptions ) => Promise; fetchEpg: ( urls: string[], options?: ElectronBridgeTrustOptions ) => Promise; getChannelPrograms: ( channelId: string, options?: ElectronBridgeEpgLookupOptions ) => Promise; getCurrentProgramsBatch: ( channelIds: string[], options?: ElectronBridgeCurrentProgramsOptions ) => Promise>; getEpgChannelMetadata: ( channelIds: string[], options?: ElectronBridgeEpgLookupOptions ) => Promise>; getEpgChannels: () => Promise; getEpgProgramsForChannels: ( window: ElectronBridgeEpgGuideWindow ) => Promise>; getEpgProgramCoverage: ( window: ElectronBridgeEpgGuideWindow ) => Promise; forceFetchEpg: ( url: string, options?: ElectronBridgeTrustOptions ) => Promise; /** Native picker for a local XMLTV file; resolves null when cancelled. */ openEpgFileDialog: () => Promise; clearEpgData: () => Promise; reconcileEpgSources: (urls: string[]) => Promise; clearEpgDataForSource: (sourceUrl: string) => Promise; checkEpgFreshness: ( urls: string[], maxAgeHours?: number ) => Promise; searchEpgPrograms: ( searchTerm: string, limit?: number ) => Promise; // EPG channel mapping (manual user overrides) getEpgMapping: ( channelKey: string ) => Promise; getEpgMappingsBatch: ( channelKeys: string[] ) => Promise>; setEpgMapping: ( channelKey: string, epgChannelId: string, playlistId?: string ) => Promise; deleteEpgMapping: (channelKey: string) => Promise; searchEpgChannels: ( searchTerm: string, limit?: number ) => Promise; updateSettings: (settings: Partial) => Promise; getAiSettings: () => Promise; setMpvPlayerPath: (mpvPlayerPath: string) => Promise; setVlcPlayerPath: (vlcPlayerPath: string) => Promise; stalkerRequest: ( payload: ElectronBridgeStalkerRequestPayload ) => Promise>; /** * Forgets the connection failures recorded for the host `url` points at, so * the next request contacts it for real instead of being fast-failed. */ resetHostConnectivityGuard: (url: string) => Promise; xtreamRequest: ( payload: ElectronBridgeXtreamRequestPayload ) => Promise; xtreamCancelSession: ( sessionId: string ) => Promise; xtreamProbeUrl: ( url: string, method?: 'GET' | 'HEAD' ) => Promise; /** Generic stream reachability probe (VOD multi-source availability) */ cancelSourceProbe: (id: string) => Promise; probeM3uSource: (payload: { url: string; userAgent?: string; trustedInsecureTlsHosts?: string[]; probe: SourceProbeContext; }) => Promise; probeStreamUrl: ( url: string, method?: 'GET' | 'HEAD', /** Playback headers the owning playlist requires, when it has any. */ headers?: StreamProbeHeaders ) => Promise; refreshPlaylist: ( payload: PlaylistRefreshPayload ) => Promise; cancelPlaylistRefresh: ( operationId: string ) => Promise; dbCreatePlaylist: ( playlist: ElectronBridgePlaylistUpsertInput ) => Promise; dbGetPlaylist: ( playlistId: string ) => Promise; /** * Records the panel clock learned from account info as one conditional * UPDATE on the row payload; a no-op unless the row still points at the * given connection (issue #1562). */ dbSetPlaylistServerTimezone: ( playlistId: string, connection: { serverUrl: string; username: string; password: string }, serverTimezone: string ) => Promise<{ updated: boolean }>; dbUpsertAppPlaylist: ( playlist: Playlist, /** * Instrumentation-only; stripped before the DB invoke. * Never enters the DB worker payload or persisted playlist data. */ operationId?: string ) => Promise; dbMigrateAppPlaylists: ( playlists: Playlist[] ) => Promise<{ success: boolean; count: number }>; dbRecoverLegacyPlaylists: () => Promise; dbUpsertAppPlaylists: ( playlists: Playlist[] ) => Promise; dbGetAppPlaylists: () => Promise; dbGetAppPlaylistMetas: () => Promise; dbGetAppPlaylist: ( playlistId: string, /** * Instrumentation-only; stripped before the DB invoke. * Never enters the DB worker payload or persisted playlist data. */ operationId?: string ) => Promise; dbGetAppPlaylistFavoriteChannels: ( playlistId: string ) => Promise; dbUpdatePlaylist: ( playlistId: string, updates: Partial | ElectronBridgePlaylistInput ) => Promise; dbDeletePlaylist: ( playlistId: string, operationId?: string ) => Promise; dbDeleteXtreamContent: ( playlistId: string, operationId?: string ) => Promise<{ success: boolean; favorites: XtreamBackupFavoriteItem[]; recentlyViewed: XtreamBackupRecentlyViewedItem[]; hiddenCategories: XtreamBackupHiddenCategory[]; }>; dbRestoreXtreamUserData: ( playlistId: string, favorites: XtreamBackupFavoriteItem[], recentlyViewed: XtreamBackupRecentlyViewedItem[], operationId?: string ) => Promise; dbHasCategories: (playlistId: string, type: string) => Promise; dbGetCategories: ( playlistId: string, type: string ) => Promise; dbSaveCategories: ( playlistId: string, categories: XtreamCategory[], type: string, hiddenCategoryXtreamIds?: number[], lockedCategoryXtreamIds?: number[] ) => Promise; dbGetAllCategories: ( playlistId: string, type: string ) => Promise; dbUpdateCategoryVisibility: ( categoryIds: number[], hidden: boolean ) => Promise; /** * Re-stamps `categories.locked` for one playlist and type from the given * provider category ids: rows in the list become locked, every other row * of that playlist/type unlocked. Idempotent, so the renderer can replay * its lock store after a refresh or a backup restore. */ dbSetCategoryLocks: ( playlistId: string, type: string, lockedXtreamIds: number[] ) => Promise; dbHasContent: (playlistId: string, type: string) => Promise; dbGetContent: ( playlistId: string, type: string ) => Promise; dbSaveContent: ( playlistId: string, streams: ElectronBridgeXtreamContentStream[], type: string, operationId?: string ) => Promise; dbClearXtreamImportCache: ( playlistId: string, type: ElectronBridgePortalContentType ) => Promise; dbSearchContent: ( playlistId: string, searchTerm: string, types: string[], excludeHidden?: boolean ) => Promise; dbGlobalSearch: ( searchTerm: string, types: string[], excludeHidden?: boolean, sources?: GlobalSearchResultSource[], options?: GlobalSearchPaginationOptions ) => Promise; dbGetGlobalRecentlyAdded: ( kind: ElectronBridgeGlobalRecentlyAddedKind, limit?: number, playlistType?: ElectronBridgePlaylistType ) => Promise; dbGetRecentlyViewed: () => Promise; dbClearRecentlyViewed: () => Promise; dbAddFavorite: ( contentId: number, playlistId: string, backdropUrl?: string ) => Promise; dbRemoveFavorite: ( contentId: number, playlistId: string ) => Promise; dbIsFavorite: (contentId: number, playlistId: string) => Promise; dbGetFavorites: ( playlistId: string ) => Promise; dbGetGlobalFavorites: () => Promise; dbGetAllGlobalFavorites: () => Promise; dbReorderGlobalFavorites: ( updates: ElectronBridgeFavoriteReorderUpdate[] ) => Promise; dbGetRecentItems: ( playlistId: string ) => Promise; dbAddRecentItem: ( contentId: number, playlistId: string, backdropUrl?: string ) => Promise; dbClearPlaylistRecentItems: ( playlistId: string ) => Promise; dbRemoveRecentItem: ( contentId: number, playlistId: string ) => Promise; dbRemoveRecentItemsBatch: ( items: ElectronBridgeRecentItemsBatchItem[] ) => Promise; dbGetContentByXtreamId: ( xtreamId: number, playlistId: string, contentType?: ElectronBridgePortalContentType ) => Promise; dbSetContentMetadataIfMissing: ( contentId: number, patch?: ContentMetadataPatch ) => Promise; dbGetAppState: (key: string) => Promise; dbSetAppState: ( key: string, value: string ) => Promise; // TMDB metadata cache dbGetTmdbMetadata: ( mediaType: TmdbCacheMediaType, lookupKey: string, language: string ) => Promise; dbSetTmdbMetadata: (entry: TmdbCacheEntry) => Promise; /** Row count + payload bytes for the settings cache panel */ dbGetTmdbCacheStats: () => Promise; dbClearTmdbMetadata: () => Promise< ElectronBridgeResult & { deleted: number } >; /** Cross-playlist title matching (actor page "All portals" scope) */ dbMatchTitles: (titles: string[]) => Promise; /** VOD multi-source: the same movie in the user's other Xtream playlists */ dbFindTitleSources: (request: { title: string; year?: number | null; excludePlaylistId?: string | null; /** A stream id inside the excluded playlist to keep anyway (a pin). */ keepContentId?: number | null; }) => Promise; /** Per-movie pinned source; keys are passed most-trusted first */ dbGetVodSourcePin: (matchKeys: string[]) => Promise; /** Every pin pointing at this playlist — used by playlist backup. */ dbListVodSourcePins: (playlistId: string) => Promise; /** Bulk clear: not keyed, so no `MAX_KEYS_PER_LOOKUP` truncation. */ dbClearVodSourcePinsForPlaylist: ( playlistId: string ) => Promise; /** * `aliasKeys` receive the same pin, and `retireKeys` are removed, all in * the SAME transaction as the write. Aliases keep a pin readable under the * poorer key forms the movie had before enrichment. */ dbSetVodSourcePin: ( pin: VodSourcePin, retireKeys?: string[], aliasKeys?: string[] ) => Promise; /** Makes the playlist's pins exactly `pins`, in one transaction. */ dbReplaceVodSourcePins: ( playlistId: string, pins: VodSourcePin[] ) => Promise; dbClearVodSourcePin: (matchKeys: string[]) => Promise; onChannelChange?: ( callback: (data: { direction: 'up' | 'down' }) => void ) => () => void; onRemoteControlCommand?: ( callback: (data: ElectronBridgeRemoteControlCommand) => void ) => () => void; updateRemoteControlStatus?: ( status: ElectronBridgeRemoteControlStatus ) => void; onPlayerError?: ( callback: (data: ElectronBridgePlayerError) => void ) => void; getLocalIpAddresses: () => Promise; onEpgProgress?: ( callback: (data: ElectronBridgeEpgProgress) => void ) => void; onDbSaveContentProgress: (callback: (count: number) => void) => void; removeDbSaveContentProgress: () => void; onDbOperationEvent?: ( callback: (data: ElectronBridgeDbOperationEvent) => void ) => () => void; dbDeleteAllPlaylists: ( operationId?: string ) => Promise; dbCancelOperation: (operationId: string) => Promise; dbSavePlaybackPosition: ( playlistId: string, data: ElectronBridgePlaybackPositionInput ) => Promise; dbGetPlaybackPosition: ( playlistId: string, contentXtreamId: number, contentType: ElectronBridgePlaybackContentType ) => Promise; dbGetSeriesPlaybackPositions: ( playlistId: string, seriesXtreamId: number ) => Promise; dbGetRecentPlaybackPositions: ( playlistId: string, limit?: number ) => Promise; dbGetAllPlaybackPositions: ( playlistId: string ) => Promise; dbClearAllPlaybackPositions: ( playlistId: string ) => Promise; dbClearPlaybackPosition: ( playlistId: string, contentXtreamId: number, contentType: ElectronBridgePlaybackContentType ) => Promise; dbSavePlaybackPositionsBatch: ( playlistId: string, items: ElectronBridgePlaybackPositionInput[] ) => Promise; dbClearPlaybackPositionsBatch: ( playlistId: string, items: { contentXtreamId: number; contentType: ElectronBridgePlaybackContentType; }[] ) => Promise; onPlaybackPositionUpdate: ( callback: (data: PlaybackPositionData) => void ) => () => void; onExternalPlayerSessionUpdate?: ( callback: (data: ExternalPlayerSession) => void ) => () => void; onEmbeddedMpvSessionUpdate?: ( callback: (data: EmbeddedMpvSession) => void ) => () => void; closeExternalPlayerSession: ( sessionId: string ) => Promise; getEmbeddedMpvSupport: () => Promise; prepareEmbeddedMpv?: () => Promise; createEmbeddedMpvSession: ( bounds: EmbeddedMpvBounds, title?: string, initialVolume?: number ) => Promise; loadEmbeddedMpvPlayback: ( sessionId: string, playback: ResolvedPortalPlayback ) => Promise; setEmbeddedMpvBounds: ( sessionId: string, bounds: EmbeddedMpvBounds ) => Promise; setEmbeddedMpvPaused: ( sessionId: string, paused: boolean ) => Promise; seekEmbeddedMpv: ( sessionId: string, seconds: number ) => Promise; /** * Relative seek by `deltaSeconds` (negative = backwards), resolved by mpv * against its own playback position. Keyboard and button steps must use * this instead of `seekEmbeddedMpv(position + delta)`: the renderer's * `positionSeconds` is a whole-second snapshot refreshed at most every * 500 ms and a seek reply does not carry the new position yet, so rapid * presses computed from it collapse onto one target. mpv merges queued * relative seeks instead, so presses accumulate. */ seekEmbeddedMpvBy?: ( sessionId: string, deltaSeconds: number ) => Promise; setEmbeddedMpvVolume: ( sessionId: string, volume: number ) => Promise; setEmbeddedMpvAudioTrack: ( sessionId: string, trackId: number ) => Promise; setEmbeddedMpvSubtitleTrack?: ( sessionId: string, trackId: number ) => Promise; /** Loads an external subtitle file (absolute path) via mpv `sub-add`. */ addEmbeddedMpvSubtitle?: ( sessionId: string, filePath: string ) => Promise; setEmbeddedMpvSubtitleDelay?: ( sessionId: string, seconds: number ) => Promise; setEmbeddedMpvSubtitleStyle?: ( sessionId: string, style: EmbeddedMpvSubtitleStyle ) => Promise; /** Opens the main-process subtitle file dialog; null when cancelled. */ selectEmbeddedMpvSubtitleFile?: () => Promise; setEmbeddedMpvSpeed?: ( sessionId: string, speed: number ) => Promise; setEmbeddedMpvAspect?: ( sessionId: string, aspect: string ) => Promise; startEmbeddedMpvRecording?: ( sessionId: string, options: EmbeddedMpvRecordingStartOptions ) => Promise; stopEmbeddedMpvRecording?: ( sessionId: string ) => Promise; getEmbeddedMpvDefaultRecordingFolder?: () => Promise; selectEmbeddedMpvRecordingFolder?: () => Promise; disposeEmbeddedMpvSession: ( sessionId: string ) => Promise; /** * Frame-copy engine only: start/stop the preload frame pump that * uploads helper frames onto the renderer's * `` element. Optional because older * preload builds do not ship the pump. */ attachEmbeddedMpvFrameView?: (sessionId: string) => Promise; detachEmbeddedMpvFrameView?: () => void; downloadsStart: ( data: ElectronBridgeDownloadStartPayload ) => Promise; downloadsCancel: (downloadId: number) => Promise; downloadsPause: (downloadId: number) => Promise; downloadsResume: ( downloadId: number, downloadFolder: string ) => Promise; downloadsRetry: ( downloadId: number, downloadFolder: string ) => Promise; downloadsRedownloadMissing: ( downloadId: number ) => Promise; downloadsRemove: (downloadId: number) => Promise; downloadsGetList: (playlistId?: string) => Promise; downloadsGet: (downloadId: number) => Promise; downloadsUpdateMetadata: ( downloadId: number, metadataSnapshot: DownloadMetadataSnapshot ) => Promise; downloadsGetDefaultFolder: () => Promise; downloadsSelectFolder: () => Promise; downloadsRevealFile: ( filePath: string ) => Promise; downloadsPlayFile: (filePath: string) => Promise; downloadsClearCompleted: ( playlistId?: string ) => Promise; onDownloadsUpdate: (callback: () => void) => () => void; // Live-TV recordings surface. Optional: older Electron builds have no // recordings bridge, and `supportsRecordings` (not `supportsDownloads`) // gates every consumer. recordingsGetList?: ( playlistId?: string ) => Promise; recordingsGet?: ( recordingId: number ) => Promise; recordingsStop?: ( recordingId: number ) => Promise; recordingsRemove?: ( recordingId: number ) => Promise; recordingsUpdatePrograms?: ( targetPath: string, programs: RecordingProgramSnapshot[] ) => Promise; recordingsRevealFile?: ( filePath: string ) => Promise; recordingsPlayFile?: ( filePath: string ) => Promise; onRecordingsUpdate?: (callback: () => void) => () => void; }