mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-01 12:06:38 +08:00
refactor: split modules and harden scraping workflows
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
import type { FormEvent } from 'react'
|
||||
import { Loader2, Save, Wand2 } from 'lucide-react'
|
||||
|
||||
import type { GenerateSTRMResult } from '../api/strm'
|
||||
import type { Library } from '../types'
|
||||
import { currentOrigin, type CloudPlaybackMode } from './strmPageModel'
|
||||
|
||||
type StrmGenerateSectionProps = {
|
||||
libraries: Library[]
|
||||
generateLibraryID: string
|
||||
baseURL: string
|
||||
outputDir: string
|
||||
cloudPlaybackMode: CloudPlaybackMode
|
||||
strmPlaybackEnabled: boolean
|
||||
redirectProxyEnabled: boolean
|
||||
autoGenerate: boolean
|
||||
savingSettings: boolean
|
||||
overwrite: boolean
|
||||
generating: boolean
|
||||
generateResult: GenerateSTRMResult | null
|
||||
playbackStatus: string
|
||||
onGenerate: (event: FormEvent) => void
|
||||
saveSTRMSettings: () => void
|
||||
setGenerateLibraryID: (value: string) => void
|
||||
setBaseURL: (value: string) => void
|
||||
setOutputDir: (value: string) => void
|
||||
setCloudPlaybackMode: (value: CloudPlaybackMode) => void
|
||||
setStrmPlaybackEnabled: (value: boolean) => void
|
||||
setRedirectProxyEnabled: (value: boolean) => void
|
||||
setAutoGenerate: (value: boolean) => void
|
||||
setOverwrite: (value: boolean) => void
|
||||
}
|
||||
|
||||
export function StrmGenerateSection({
|
||||
libraries,
|
||||
generateLibraryID,
|
||||
baseURL,
|
||||
outputDir,
|
||||
cloudPlaybackMode,
|
||||
strmPlaybackEnabled,
|
||||
redirectProxyEnabled,
|
||||
autoGenerate,
|
||||
savingSettings,
|
||||
overwrite,
|
||||
generating,
|
||||
generateResult,
|
||||
playbackStatus,
|
||||
onGenerate,
|
||||
saveSTRMSettings,
|
||||
setGenerateLibraryID,
|
||||
setBaseURL,
|
||||
setOutputDir,
|
||||
setCloudPlaybackMode,
|
||||
setStrmPlaybackEnabled,
|
||||
setRedirectProxyEnabled,
|
||||
setAutoGenerate,
|
||||
setOverwrite,
|
||||
}: StrmGenerateSectionProps) {
|
||||
return (
|
||||
<section className="glass-panel space-y-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 className="font-display text-lg font-semibold text-ink-600">自动生成 STRM 文件</h2>
|
||||
<p className="text-sm text-ink-50">
|
||||
只需要填写自己的访问域名,系统会按媒体库内每个媒体批量生成可播放的 .strm 文件。
|
||||
</p>
|
||||
</div>
|
||||
<span className={`rounded-full border px-3 py-1 text-xs font-semibold ${
|
||||
strmPlaybackEnabled || redirectProxyEnabled
|
||||
? 'border-emerald-300/40 bg-emerald-400/10 text-emerald-500'
|
||||
: 'border-red-300/40 bg-red-400/10 text-red-500'
|
||||
}`}>
|
||||
{playbackStatus}
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid gap-3 rounded-2xl border border-gray-200 bg-white/70 p-4 md:grid-cols-2">
|
||||
<label className="flex items-start gap-3 text-sm text-ink-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-1 h-4 w-4 accent-primary-400"
|
||||
checked={strmPlaybackEnabled}
|
||||
onChange={(e) => setStrmPlaybackEnabled(e.target.checked)}
|
||||
/>
|
||||
<span>
|
||||
<span className="block font-medium text-ink-600">启用 STRMURL 播放</span>
|
||||
<span className="text-xs text-ink-50">第三方客户端可拿到 /api/stream/媒体ID 入口,适合 STRM 管理和自动生成方案。</span>
|
||||
</span>
|
||||
</label>
|
||||
<label className="flex items-start gap-3 text-sm text-ink-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-1 h-4 w-4 accent-primary-400"
|
||||
checked={redirectProxyEnabled}
|
||||
onChange={(e) => setRedirectProxyEnabled(e.target.checked)}
|
||||
/>
|
||||
<span>
|
||||
<span className="block font-medium text-ink-600">启用 302/反代播放</span>
|
||||
<span className="text-xs text-ink-50">第三方客户端可拿到 /Videos/媒体ID/stream 入口,由服务端解析后 302 或必要时反代。</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid gap-3 rounded-2xl border border-gray-200 bg-white/70 p-4 md:grid-cols-[1fr_1fr_auto]">
|
||||
<label className="text-sm text-ink-100">
|
||||
<span className="mb-1 block font-medium text-ink-600">两者都开启时优先</span>
|
||||
<select
|
||||
className="input-base"
|
||||
value={cloudPlaybackMode}
|
||||
onChange={(e) => setCloudPlaybackMode(e.target.value as CloudPlaybackMode)}
|
||||
disabled={!strmPlaybackEnabled || !redirectProxyEnabled}
|
||||
>
|
||||
<option value="strm">优先 STRMURL</option>
|
||||
<option value="redirect_proxy">优先 302/反代</option>
|
||||
</select>
|
||||
<span className="mt-1 block text-xs text-ink-50">只开启一个时自动使用已开启的播放方式;两个都关闭时云盘媒体不向第三方提供播放。</span>
|
||||
</label>
|
||||
<label className="flex items-start gap-3 text-sm text-ink-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="mt-1 h-4 w-4 accent-primary-400"
|
||||
checked={autoGenerate}
|
||||
onChange={(e) => setAutoGenerate(e.target.checked)}
|
||||
/>
|
||||
<span>
|
||||
<span className="block font-medium text-ink-600">扫描后自动刷新 STRM 文件</span>
|
||||
<span className="text-xs text-ink-50">默认关闭,避免扫描大型网盘库时重复写文件。</span>
|
||||
</span>
|
||||
</label>
|
||||
<button type="button" className="neon-button self-center" disabled={savingSettings} onClick={saveSTRMSettings}>
|
||||
{savingSettings ? <Loader2 size={16} className="animate-spin" /> : <Save size={16} />}
|
||||
保存开关
|
||||
</button>
|
||||
</div>
|
||||
<form onSubmit={onGenerate} className="grid gap-3 md:grid-cols-4">
|
||||
<select
|
||||
required
|
||||
className="input-base"
|
||||
value={generateLibraryID}
|
||||
onChange={(e) => setGenerateLibraryID(e.target.value)}
|
||||
>
|
||||
<option value="" disabled>
|
||||
选择媒体库
|
||||
</option>
|
||||
<option value="*">全部媒体库</option>
|
||||
{libraries.map((library) => (
|
||||
<option key={library.id} value={library.id}>
|
||||
{library.name} ({library.type})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<input
|
||||
required
|
||||
className="input-base md:col-span-2"
|
||||
placeholder="http://NAS-IP:18080 或 https://media.example.com"
|
||||
value={baseURL}
|
||||
onChange={(e) => setBaseURL(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-2xl border border-primary-400/40 px-3 py-2 text-sm text-brand-500 transition hover:bg-primary-400/10"
|
||||
onClick={() => setBaseURL(currentOrigin())}
|
||||
>
|
||||
使用当前访问地址
|
||||
</button>
|
||||
<label className="flex items-center gap-2 rounded-2xl border border-gray-200 bg-white/70 px-3 py-2 text-sm text-ink-50">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={overwrite}
|
||||
onChange={(e) => setOverwrite(e.target.checked)}
|
||||
/>
|
||||
覆盖已存在
|
||||
</label>
|
||||
<input
|
||||
className="input-base md:col-span-3"
|
||||
placeholder="输出目录可留空,默认写入 data/strm/分类/子分类"
|
||||
value={outputDir}
|
||||
onChange={(e) => setOutputDir(e.target.value)}
|
||||
/>
|
||||
<button type="submit" disabled={generating || !generateLibraryID || !baseURL.trim()} className="neon-button md:col-span-4">
|
||||
{generating ? <Loader2 size={16} className="animate-spin" /> : <Wand2 size={16} />}
|
||||
{generating ? '生成中…' : '批量生成 STRM'}
|
||||
</button>
|
||||
</form>
|
||||
<p className="text-xs text-sand-500">
|
||||
生成内容为 <code>域名 + /api/stream/媒体ID?token=...</code>;第三方客户端播放优先方式由上方「STRMURL / 302反代」模式决定。域名会同步保存到系统设置中的「公开访问域名 / STRM 域名」。
|
||||
</p>
|
||||
{generateResult && (
|
||||
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 text-sm text-ink-50">
|
||||
<div className="font-semibold text-ink-600">
|
||||
输出目录:{generateResult.output_dir}
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
新增 {generateResult.generated} · 更新 {generateResult.updated} · 跳过 {generateResult.skipped} · 清理 {generateResult.cleaned || 0}
|
||||
</div>
|
||||
{generateResult.errors && generateResult.errors.length > 0 && (
|
||||
<div className="mt-2 text-red-500">
|
||||
失败 {generateResult.errors.length} 条:{generateResult.errors.slice(0, 3).join(';')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user