feat: trusted-domains OAuth popups, og:image bookmark posters, periodic updater
- main.js: trusted-domains list with default Google/Yandex/GitHub/etc.; cross-domain confirmation skipped for trusted; setWindowOpenHandler returns action:'allow' for trusted so OAuth popups work (postMessage back to opener, popup self-closes). Fixes YouTube/Google login reset. - main.js: get-page-meta IPC extracts og:image / twitter:image / JSON-LD image from current view; HDRezka also tries .b-sidecover img for hi-res. - Header: bookmark button pulls og:image as poster and the page's title; duplicate detection switched from hostname to full URL so multiple movies from same site can coexist. - BookmarksBar: site icon rendered next to source domain when distinct from poster; img onerror falls back to placeholder. - Settings: trusted domains chip list with add/remove/reset. - Updater: proper semver compare (only show if latest > current), direct installer URL detection per platform, hourly re-check. Bookmark schema gains optional siteIcon; existing bookmarks remain valid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,9 @@ export interface AppEntry {
|
||||
export interface Bookmark {
|
||||
title: string
|
||||
url: string
|
||||
poster?: string
|
||||
source?: 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 {
|
||||
@@ -31,6 +32,7 @@ interface SettingsData {
|
||||
movieSites: MovieSite[]
|
||||
tmdbApiKey: string
|
||||
bookmarks: Bookmark[]
|
||||
trustedDomains?: string[]
|
||||
}
|
||||
|
||||
interface SettingsProps {
|
||||
@@ -38,7 +40,17 @@ interface SettingsProps {
|
||||
onAppsChange: (apps: AppEntry[]) => void
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: SettingsData = { apps: [], proxy: { host: '127.0.0.1', port: '7890' }, movieSites: [], tmdbApiKey: '', bookmarks: [] }
|
||||
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'
|
||||
@@ -96,6 +108,26 @@ const Settings: React.FC<SettingsProps> = ({ onClose, onAppsChange }) => {
|
||||
}
|
||||
|
||||
const [newSite, setNewSite] = useState<MovieSite>({ 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
|
||||
@@ -161,6 +193,36 @@ const Settings: React.FC<SettingsProps> = ({ onClose, onAppsChange }) => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<div className="settings-section-head-row">
|
||||
<h3>Доверенные домены</h3>
|
||||
<button className="settings-reset-btn" onClick={resetTrustedDomains} title="Сбросить к стандартному списку">Сбросить</button>
|
||||
</div>
|
||||
<p className="proxy-hint">
|
||||
Переходы и popup'ы на эти домены открываются без подтверждения — нужно для входа через Google, Яндекс, GitHub и т.п.
|
||||
Совпадение по суффиксу: <code>google.com</code> разрешит и <code>accounts.google.com</code>, и <code>www.google.com</code>.
|
||||
</p>
|
||||
<div className="trusted-domains-list">
|
||||
{trustedDomains.map((d, i) => (
|
||||
<span key={i} className="trusted-domain-chip">
|
||||
{d}
|
||||
<button className="trusted-domain-remove" onClick={() => removeTrustedDomain(i)} title="Удалить">✕</button>
|
||||
</span>
|
||||
))}
|
||||
{trustedDomains.length === 0 && <p className="settings-empty">Список пуст.</p>}
|
||||
</div>
|
||||
<div className="trusted-domain-add">
|
||||
<input
|
||||
className="settings-input"
|
||||
placeholder="example.com"
|
||||
value={newTrusted}
|
||||
onChange={e => setNewTrusted(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && addTrustedDomain()}
|
||||
/>
|
||||
<button className="settings-add-btn" onClick={addTrustedDomain}>Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h3>Приложения</h3>
|
||||
<div className="settings-apps-list">
|
||||
|
||||
Reference in New Issue
Block a user