mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-05 21:06:38 +08:00
perf(strm): 优化 OpenList 元数据下载速度 (#15)
* Add favourite buttons to library list and series detail pages Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix favourites list for remote Emby mounted media Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Route favourites to library series detail when appropriate Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix favourites link to library series for remote anime paths Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix remote Emby playback history and continue watching - Preserve duration_ms when web player reports duration=0 - Add GET /playback/:id/resume for reliable per-item resume - Switch home continue watching to /watch-history/continue API - Set library_id on remote media for play-profile visibility - Keep continue-watching rows when remote metadata hydration fails Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix mobile sort dropdown overflowing off-screen Align dropdown to button left edge on small screens and cap width to viewport so sort options stay fully visible on mobile. Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Remove stale Windows binary backup files Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix STRM account edit form not echoing saved config Return config_preview from account API for server/url/username and secret flags. Merge partial config updates so empty password/token fields do not wipe stored credentials. Co-authored-by: truewhile <truewhile@users.noreply.github.com> * perf(strm): speed up OpenList metadata downloads with provider-aware concurrency - Split download semaphore: 115 stays capped at 3 for WAF safety; OpenList/CloudDrive2 WebDAV metadata uses strm.download_threads (default raised to 6, max 16) - Scope 115 WAF cooldown to 115 tasks only so OpenList downloads are not paused - Requeue claimed tasks back to pending when slot acquisition fails or 115 is cooling - Add tests for separate semaphores and requeue behavior Co-authored-by: truewhile <truewhile@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: truewhile <truewhile@users.noreply.github.com>
This commit is contained in:
@@ -95,6 +95,26 @@ function Field({ label, hint, children }: { label: string; hint?: string; childr
|
||||
|
||||
const inputCls = 'input-base w-full'
|
||||
|
||||
const SECRET_PLACEHOLDER = '已配置,留空则不修改'
|
||||
|
||||
function accountDialogDefaults(existing: StrmAccount | null) {
|
||||
const preview = existing?.config_preview
|
||||
return {
|
||||
provider: (existing?.provider ?? 'cloud115') as StrmProvider,
|
||||
name: existing?.name ?? '',
|
||||
enabled: existing?.enabled ?? true,
|
||||
url: preview?.url ?? '',
|
||||
server: preview?.server ?? '',
|
||||
username: preview?.username ?? '',
|
||||
password: '',
|
||||
token: '',
|
||||
proxyPlay: existing?.proxy_play ?? false,
|
||||
embyLines: defaultEmbyRemoteLines(existing?.provider === 'emby_remote' ? existing?.emby_lines : undefined),
|
||||
hasPassword: Boolean(preview?.has_password),
|
||||
hasToken: Boolean(preview?.has_token || preview?.has_api_key),
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 添加/编辑网盘账号 ────────────────────────────────────────────────────────
|
||||
|
||||
const PROVIDER_OPTIONS: { provider: StrmProvider; label: string; desc: string }[] = [
|
||||
@@ -112,18 +132,19 @@ export function StrmAccountDialog({
|
||||
onClose: () => void
|
||||
onSaved: () => void
|
||||
}) {
|
||||
const [provider, setProvider] = useState<StrmProvider>(existing?.provider ?? 'cloud115')
|
||||
const [name, setName] = useState(existing?.name ?? '')
|
||||
const [enabled, setEnabled] = useState(existing?.enabled ?? true)
|
||||
const [url, setUrl] = useState('')
|
||||
const [embyLines, setEmbyLines] = useState<EmbyRemoteLine[]>(() =>
|
||||
defaultEmbyRemoteLines(existing?.provider === 'emby_remote' ? existing?.emby_lines : undefined),
|
||||
)
|
||||
const [server, setServer] = useState('')
|
||||
const [username, setUsername] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [token, setToken] = useState('')
|
||||
const [proxyPlay, setProxyPlay] = useState(existing?.proxy_play ?? false)
|
||||
const defaults = accountDialogDefaults(existing)
|
||||
const [provider, setProvider] = useState<StrmProvider>(defaults.provider)
|
||||
const [name, setName] = useState(defaults.name)
|
||||
const [enabled, setEnabled] = useState(defaults.enabled)
|
||||
const [url, setUrl] = useState(defaults.url)
|
||||
const [embyLines, setEmbyLines] = useState<EmbyRemoteLine[]>(defaults.embyLines)
|
||||
const [server, setServer] = useState(defaults.server)
|
||||
const [username, setUsername] = useState(defaults.username)
|
||||
const [password, setPassword] = useState(defaults.password)
|
||||
const [token, setToken] = useState(defaults.token)
|
||||
const [proxyPlay, setProxyPlay] = useState(defaults.proxyPlay)
|
||||
const [hasPassword, setHasPassword] = useState(defaults.hasPassword)
|
||||
const [hasToken, setHasToken] = useState(defaults.hasToken)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
const buildConfig = (): Record<string, string> => {
|
||||
@@ -178,7 +199,7 @@ export function StrmAccountDialog({
|
||||
name,
|
||||
provider: existing.provider,
|
||||
enabled,
|
||||
config: existing.has_credential && !Object.keys(buildConfig()).length ? {} : buildConfig(),
|
||||
config: buildConfig(),
|
||||
})
|
||||
toast.success('网盘账号已更新')
|
||||
} else {
|
||||
@@ -364,7 +385,16 @@ export function StrmAccountDialog({
|
||||
<input className={inputCls} value={username} onChange={(e) => setUsername(e.target.value)} />
|
||||
</Field>
|
||||
<Field label="密码">
|
||||
<input className={inputCls} type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
|
||||
<input
|
||||
className={inputCls}
|
||||
type="password"
|
||||
value={password}
|
||||
placeholder={existing && hasPassword ? SECRET_PLACEHOLDER : undefined}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value)
|
||||
if (e.target.value) setHasPassword(false)
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
@@ -375,20 +405,45 @@ export function StrmAccountDialog({
|
||||
<input className={inputCls} value={username} onChange={(e) => setUsername(e.target.value)} />
|
||||
</Field>
|
||||
<Field label="密码">
|
||||
<input className={inputCls} type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
|
||||
<input
|
||||
className={inputCls}
|
||||
type="password"
|
||||
value={password}
|
||||
placeholder={existing && hasPassword ? SECRET_PLACEHOLDER : undefined}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value)
|
||||
if (e.target.value) setHasPassword(false)
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{provider === 'emby_remote' && (
|
||||
<Field label="API Key(可选)" hint="留空则用下方用户名/密码自动认证获取">
|
||||
<input className={inputCls} value={token} placeholder="留空自动认证" onChange={(e) => setToken(e.target.value)} />
|
||||
<input
|
||||
className={inputCls}
|
||||
value={token}
|
||||
placeholder={existing && hasToken ? SECRET_PLACEHOLDER : '留空自动认证'}
|
||||
onChange={(e) => {
|
||||
setToken(e.target.value)
|
||||
if (e.target.value) setHasToken(false)
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{provider === 'openlist' && (
|
||||
<Field label="Token(可选,优先于密码)">
|
||||
<input className={inputCls} value={token} onChange={(e) => setToken(e.target.value)} />
|
||||
<input
|
||||
className={inputCls}
|
||||
value={token}
|
||||
placeholder={existing && hasToken ? SECRET_PLACEHOLDER : undefined}
|
||||
onChange={(e) => {
|
||||
setToken(e.target.value)
|
||||
if (e.target.value) setHasToken(false)
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user