Files
ESH-Media/src/main.tsx
eshmeshek 1c7bb75a05 ESH-Media v1.0.11 — kiosk media browser for elderly users
Electron-based kiosk desktop app: large-tile launcher for YouTube, RuTube,
movie sites and Google services, designed for low-tech grandparent use.

Features:
  - WebContentsView-per-app tabbed browsing with session persistence
  - per-app proxy routing (Clash/V2Ray friendly, useProxy flag)
  - cliqz-electron adblocker with whitelist for OAuth/integrity domains
  - TMDB-backed movie search across kinogo / hdrezka / filmix
  - bookmark posters auto-fetched from og:image / JSON-LD
  - electron-updater wired to Gitea releases API (latest.yml + .blockmap)
  - cross-domain navigation confirms via custom WebContentsView dialogs
  - kiosk window with hidden menu, Ctrl+Shift+I devtools shortcut
  - Trusted Types disabled engine-wide so adblocker scriptlets work on YouTube

Google OAuth handling (the hard-won part):
  Google's anti-abuse JS rejects WebContentsView + custom session settings
  as "embedded browser". So accounts.google.com opens in a top-level
  BrowserWindow popup in a dedicated persist:google-login partition that
  we never call setProxy/setUserAgent on — it inherits Windows system
  proxy and the default Electron-tagged UA, both of which Google accepts.
  After login, .google.com/.youtube.com cookies migrate into the parent
  view's session and the view reloads to pick up the logged-in state.

Session restore: only the last-active tab attaches to the window; other
tabs load silently in the background and become instantly visible when
the user clicks them in the sidebar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 00:46:02 +03:00

42 lines
1.6 KiB
TypeScript

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
declare global {
interface Window {
electron?: {
createView: (name: string, url: string, imageUrl: string, zoom: number, useProxy: boolean) => void
confirm: (text: string, funcName: string) => void
removeView: (name?: string) => void
hideView: () => void
showView: (name: string) => void
adjustView: (expanded: boolean) => void
on: (channel: string, func: (...args: any[]) => void) => () => void
getCurrentPage: () => Promise<{ name: string; url: string; imageUrl: string } | null>
handleAction: (action: string) => void
setProxy: (host: string, port: string) => void
expandWithHeader: () => void
collapseWithHeader: () => void
backwardPage: () => void
forwardPage: () => void
refreshPage: () => void
readConfig: () => Promise<any>
writeConfig: (data: any) => void
searchMovies: (query: string, sites: any[]) => Promise<any[]>
searchTmdb: (query: string, apiKey: string) => Promise<{ results: any[]; error?: string }>
discoverTmdb: (params: any) => Promise<{ results: any[]; totalPages: number; error?: string }>
getPageMeta: () => Promise<{ poster: string; title: string; url: string } | null>
installUpdate: () => Promise<boolean>
checkUpdateNow: () => Promise<boolean>
toggleKiosk: () => Promise<boolean>
isKiosk: () => Promise<boolean>
}
}
}
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)