import React, { useState, useEffect } from 'react' export interface AppEntry { name: string imageUrl: string url: string useProxy: boolean } export interface Bookmark { title: string url: string poster?: string // movie poster (og:image, or fallback site icon) source?: string // domain shown under title siteIcon?: string // small site icon shown alongside source } export interface MovieSite { domain: string type: 'dle' | 'hdrezka' | 'filmix' enabled: boolean } interface ProxyConfig { host: string port: string } interface SettingsData { apps: AppEntry[] proxy: ProxyConfig movieSites: MovieSite[] tmdbApiKey: string bookmarks: Bookmark[] trustedDomains?: string[] } interface SettingsProps { onClose: () => void onAppsChange: (apps: AppEntry[]) => void } const DEFAULT_TRUSTED_DOMAINS = [ 'google.com', 'accounts.google.com', 'googleapis.com', 'googleusercontent.com', 'gstatic.com', 'youtube.com', 'ytimg.com', 'googlevideo.com', 'yandex.ru', 'yandex.com', 'passport.yandex.ru', 'passport.yandex.com', 'yastatic.net', 'github.com', 'github.io', 'githubassets.com', 'githubusercontent.com', 'vk.com', 'vk.ru', 'vkuser.net', 'mail.ru', 'my.mail.ru', 'live.com', 'microsoft.com', 'microsoftonline.com', 'office.com', 'apple.com', 'icloud.com', 'facebook.com', 'fb.com', ] const DEFAULT_SETTINGS: SettingsData = { apps: [], proxy: { host: '127.0.0.1', port: '7890' }, movieSites: [], tmdbApiKey: '', bookmarks: [], trustedDomains: DEFAULT_TRUSTED_DOMAINS } function guessMovieSiteType(domain: string): MovieSite['type'] { if (/rezka/.test(domain)) return 'hdrezka' if (/filmix/.test(domain)) return 'filmix' return 'dle' } function saveSettings(data: SettingsData) { window.electron?.writeConfig(data) } const Settings: React.FC = ({ onClose, onAppsChange }) => { const [settings, setSettings] = useState(DEFAULT_SETTINGS) const [newApp, setNewApp] = useState({ name: '', imageUrl: '', url: '', useProxy: false }) useEffect(() => { window.electron?.readConfig().then((cfg: SettingsData | null) => { if (cfg?.apps) setSettings(cfg) }) }, []) const updateProxy = (field: keyof ProxyConfig, value: string) => { const updated = { ...settings, proxy: { ...settings.proxy, [field]: value } } setSettings(updated) saveSettings(updated) window.electron?.setProxy(updated.proxy.host, updated.proxy.port) } const toggleAppProxy = (index: number) => { const apps = settings.apps.map((app, i) => i === index ? { ...app, useProxy: !app.useProxy } : app ) const updated = { ...settings, apps } setSettings(updated) saveSettings(updated) onAppsChange(apps) } const addApp = () => { if (!newApp.name || !newApp.url) return const apps = [...settings.apps, newApp] const updated = { ...settings, apps } setSettings(updated) saveSettings(updated) onAppsChange(apps) setNewApp({ name: '', imageUrl: '', url: '', useProxy: false }) } const removeApp = (index: number) => { const apps = settings.apps.filter((_, i) => i !== index) const updated = { ...settings, apps } setSettings(updated) saveSettings(updated) onAppsChange(apps) } const [newSite, setNewSite] = useState({ domain: '', type: 'dle', enabled: true }) const [newTrusted, setNewTrusted] = useState('') const trustedDomains = settings.trustedDomains ?? DEFAULT_TRUSTED_DOMAINS const addTrustedDomain = () => { const d = newTrusted.trim().toLowerCase().replace(/^https?:\/\//, '').replace(/\/.*$/, '').replace(/^\./, '') if (!d || trustedDomains.includes(d)) { setNewTrusted(''); return } const updated = { ...settings, trustedDomains: [...trustedDomains, d] } setSettings(updated); saveSettings(updated); setNewTrusted('') } const removeTrustedDomain = (index: number) => { const updated = { ...settings, trustedDomains: trustedDomains.filter((_, i) => i !== index) } setSettings(updated); saveSettings(updated) } const resetTrustedDomains = () => { const updated = { ...settings, trustedDomains: DEFAULT_TRUSTED_DOMAINS } setSettings(updated); saveSettings(updated) } const addMovieSite = () => { if (!newSite.domain.trim()) return let domain = newSite.domain.trim() if (domain.startsWith('https://')) domain = domain.slice(8) else if (domain.startsWith('http://')) domain = domain.slice(7) if (domain.endsWith('/')) domain = domain.slice(0, -1) const movieSites = [...(settings.movieSites ?? []), { ...newSite, domain }] const updated = { ...settings, movieSites } setSettings(updated) saveSettings(updated) setNewSite({ domain: '', type: 'dle', enabled: true }) } const removeMovieSite = (index: number) => { const movieSites = (settings.movieSites ?? []).filter((_, i) => i !== index) const updated = { ...settings, movieSites } setSettings(updated) saveSettings(updated) } const toggleMovieSite = (index: number) => { const movieSites = (settings.movieSites ?? []).map((s, i) => i === index ? { ...s, enabled: !s.enabled } : s ) const updated = { ...settings, movieSites } setSettings(updated) saveSettings(updated) } return (
e.stopPropagation()}>

Настройки

Прокси

updateProxy('host', e.target.value)} /> : updateProxy('port', e.target.value)} />
http_proxy=http://{settings.proxy.host}:{settings.proxy.port} https_proxy=http://{settings.proxy.host}:{settings.proxy.port} socks5://{settings.proxy.host}:{settings.proxy.port}

Прокси применяется к каждому сайту индивидуально — переключатель рядом с каждым приложением.

Доверенные домены

Переходы и popup'ы на эти домены открываются без подтверждения — нужно для входа через Google, Яндекс, GitHub и т.п. Совпадение по суффиксу: google.com разрешит и accounts.google.com, и www.google.com.

{trustedDomains.map((d, i) => ( {d} ))} {trustedDomains.length === 0 &&

Список пуст.

}
setNewTrusted(e.target.value)} onKeyDown={e => e.key === 'Enter' && addTrustedDomain()} />

Приложения

{settings.apps.map((app, i) => (
{app.imageUrl && ( {app.name} )}
{app.name} {app.url}
))} {settings.apps.length === 0 && (

Нет приложений. Добавьте ниже.

)}

Добавить приложение

setNewApp({ ...newApp, name: e.target.value })} /> setNewApp({ ...newApp, url: e.target.value })} /> setNewApp({ ...newApp, imageUrl: e.target.value })} />

Поиск фильмов

TMDB API ключ

{ const updated = { ...settings, tmdbApiKey: e.target.value } setSettings(updated) saveSettings(updated) }} />

Нужен для постеров и метаданных. Без ключа поиск работает напрямую по сайтам.

Сайты

Тип определяется автоматически. Поддерживаются: kinogo, lordfilm, gidonline, hdrezka, filmix и их зеркала. Домен без https://

{(settings.movieSites ?? []).map((site, i) => (
{site.domain} {site.type}
))} {!(settings.movieSites ?? []).length && (

Нет сайтов. Добавьте ниже.

)}

Добавить сайт

{ const domain = e.target.value setNewSite({ ...newSite, domain, type: guessMovieSiteType(domain) }) }} />
) } export default Settings