mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 12:36:15 -08:00
Locks are per category (Xtream category ids, Stalker genre ids, M3U group titles) and kept in one renderer lock store persisted to app_state / localStorage; `categories.locked` is the SQLite index re-stamped from it. While the lock is active the DB worker filters every content read, the PWA data source, the Stalker store and the M3U channel list filter in memory, and the enforcement service reloads the stores and steps off withheld selections. Settings → Parental lock sets the PIN (PBKDF2, never in Settings), the relock timeout and Lock now; lock toggles live in the Xtream/M3U management dialogs and a new Stalker lock dialog, all behind the PIN. Backups carry the locks per playlist entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
114 lines
3.5 KiB
TypeScript
114 lines
3.5 KiB
TypeScript
import {
|
|
DEFAULT_DASHBOARD_RAILS_SETTINGS,
|
|
DEFAULT_PARENTAL_LOCK_RELOCK_MINUTES,
|
|
DEFAULT_TMDB_SETTINGS,
|
|
Language,
|
|
Settings,
|
|
StartupBehavior,
|
|
StreamFormat,
|
|
Theme,
|
|
VideoPlayer,
|
|
} from '@iptvnator/shared/interfaces';
|
|
|
|
/** Defaults and boot-time helpers of `SettingsStore`, split out for size. */
|
|
export const DEFAULT_SETTINGS: Settings = {
|
|
player: VideoPlayer.VideoJs,
|
|
webPlayerSharedControls: true,
|
|
playerAmbientMode: false,
|
|
playerUpNextRail: true,
|
|
fullscreenChannelPanel: true,
|
|
vodAutoFailover: false,
|
|
m3uVodDetails: true,
|
|
streamFormat: StreamFormat.AutoStreamFormat,
|
|
openStreamOnDoubleClick: false,
|
|
language: Language.ENGLISH,
|
|
showCaptions: false,
|
|
showDashboard: true,
|
|
startupBehavior: StartupBehavior.FirstView,
|
|
startupWindowMode: 'normal',
|
|
showExternalPlaybackBar: true,
|
|
stripCountryPrefix: false,
|
|
theme: Theme.SystemTheme,
|
|
mpvPlayerPath: '',
|
|
mpvPlayerArguments: '',
|
|
mpvReuseInstance: false,
|
|
vlcPlayerPath: '',
|
|
vlcPlayerArguments: '',
|
|
vlcReuseInstance: false,
|
|
remoteControl: false,
|
|
remoteControlPort: 8765,
|
|
epgUrl: [],
|
|
downloadFolder: '',
|
|
recordingFolder: '',
|
|
embeddedMpvFrameCopy: false,
|
|
embeddedMpvExtraOptions: '',
|
|
embeddedMpvAutoReconnect: true,
|
|
portalConnectivityGuard: true,
|
|
coverSize: 'medium',
|
|
epgViewMode: 'timeline',
|
|
epgOffsetMinutes: 0,
|
|
dashboardRails: DEFAULT_DASHBOARD_RAILS_SETTINGS,
|
|
preferUploadedEpgOverXtream: false,
|
|
trustedPrivateNetworkEpgUrls: [],
|
|
trustedInsecureTlsHosts: [],
|
|
tmdb: DEFAULT_TMDB_SETTINGS,
|
|
parentalLockEnabled: false,
|
|
parentalLockRelockMinutes: DEFAULT_PARENTAL_LOCK_RELOCK_MINUTES,
|
|
};
|
|
|
|
/**
|
|
* Which half of the settings persistence round-trip failed, if any.
|
|
*
|
|
* Settings live in the renderer's IndexedDB, which can be unavailable for
|
|
* reasons the app cannot control (a second instance holding the Chromium
|
|
* storage lock, a corrupted profile, storage blocked by security software).
|
|
* Both failures used to be swallowed: `updateSettings` patches the in-memory
|
|
* state before persisting, so a failed write still looked applied until the
|
|
* next restart (issue #1156). Recording the failure lets the settings UI say
|
|
* so instead of pretending the change stuck.
|
|
*/
|
|
export type SettingsStorageFailure = 'load' | 'save';
|
|
|
|
export interface SettingsStorageState {
|
|
storageFailure: SettingsStorageFailure | null;
|
|
}
|
|
|
|
let embeddedMpvPrepareScheduled = false;
|
|
|
|
export function scheduleEmbeddedMpvPrepare(): void {
|
|
if (
|
|
embeddedMpvPrepareScheduled ||
|
|
typeof window === 'undefined' ||
|
|
!window.electron?.prepareEmbeddedMpv
|
|
) {
|
|
return;
|
|
}
|
|
|
|
embeddedMpvPrepareScheduled = true;
|
|
const prepare = () => {
|
|
void window.electron
|
|
.prepareEmbeddedMpv?.()
|
|
.then((support) => {
|
|
if (!support?.supported) {
|
|
embeddedMpvPrepareScheduled = false;
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
embeddedMpvPrepareScheduled = false;
|
|
console.warn('Failed to prepare embedded MPV.', error);
|
|
});
|
|
};
|
|
const idleWindow = window as typeof window & {
|
|
requestIdleCallback?: (
|
|
callback: IdleRequestCallback,
|
|
options?: IdleRequestOptions
|
|
) => number;
|
|
};
|
|
|
|
if (idleWindow.requestIdleCallback) {
|
|
idleWindow.requestIdleCallback(prepare, { timeout: 5000 });
|
|
} else {
|
|
window.setTimeout(prepare, 2000);
|
|
}
|
|
}
|