From 30b448c920d3b2bcd8e753bd457a0a4b374610eb Mon Sep 17 00:00:00 2001 From: mtvpls Date: Mon, 27 Apr 2026 09:43:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=A9=E7=BF=BC=E7=BD=91?= =?UTF-8?q?=E7=9B=98=E5=9C=A8=E7=BA=BF=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/page.tsx | 116 ++++ src/app/api/admin/netdisk/route.ts | 23 +- .../api/netdisk/tianyi/instant-play/route.ts | 64 +++ src/app/api/netdisk/tianyi/play/route.ts | 82 +++ src/app/api/source-detail/route.ts | 86 ++- src/components/PansouSearch.tsx | 8 +- src/components/Toast.tsx | 4 +- src/lib/admin.types.ts | 5 + src/lib/config.ts | 13 + src/lib/netdisk/source.ts | 10 +- src/lib/netdisk/tianyi-session-cache.ts | 105 ++++ src/lib/netdisk/tianyi-session-resolver.ts | 48 ++ src/lib/netdisk/tianyi.client.ts | 522 ++++++++++++++++++ 13 files changed, 1077 insertions(+), 9 deletions(-) create mode 100644 src/app/api/netdisk/tianyi/instant-play/route.ts create mode 100644 src/app/api/netdisk/tianyi/play/route.ts create mode 100644 src/lib/netdisk/tianyi-session-cache.ts create mode 100644 src/lib/netdisk/tianyi-session-resolver.ts create mode 100644 src/lib/netdisk/tianyi.client.ts diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 2354008..3067f9f 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -3708,6 +3708,9 @@ const NetDiskConfigComponent = ({ const [mobileAuthorization, setMobileAuthorization] = useState(''); const [baiduEnabled, setBaiduEnabled] = useState(false); const [baiduCookie, setBaiduCookie] = useState(''); + const [tianyiEnabled, setTianyiEnabled] = useState(false); + const [tianyiAccount, setTianyiAccount] = useState(''); + const [tianyiPassword, setTianyiPassword] = useState(''); useEffect(() => { const quark = config?.NetDiskConfig?.Quark; @@ -3719,6 +3722,9 @@ const NetDiskConfigComponent = ({ setMobileAuthorization(mobile?.Authorization || ''); setBaiduEnabled(config?.NetDiskConfig?.Baidu?.Enabled || false); setBaiduCookie(config?.NetDiskConfig?.Baidu?.Cookie || ''); + setTianyiEnabled(config?.NetDiskConfig?.Tianyi?.Enabled || false); + setTianyiAccount(config?.NetDiskConfig?.Tianyi?.Account || ''); + setTianyiPassword(config?.NetDiskConfig?.Tianyi?.Password || ''); }, [config]); const handleSave = async () => { @@ -3741,6 +3747,11 @@ const NetDiskConfigComponent = ({ Enabled: baiduEnabled, Cookie: baiduCookie, }, + Tianyi: { + Enabled: tianyiEnabled, + Account: tianyiAccount, + Password: tianyiPassword, + }, }), }); @@ -3838,6 +3849,35 @@ const NetDiskConfigComponent = ({ }); }; + const handleValidateTianyi = async () => { + await withLoading('validateTianyiNetDisk', async () => { + try { + const response = await fetch('/api/admin/netdisk', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'validate', + provider: 'tianyi', + Tianyi: { + Account: tianyiAccount, + Password: tianyiPassword, + }, + }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.error || '校验失败'); + } + + showSuccess(data.message || '天翼云盘账号密码可用', showAlert); + } catch (error) { + showError(error instanceof Error ? error.message : '校验失败', showAlert); + throw error; + } + }); + }; + return (
@@ -4028,6 +4068,82 @@ const NetDiskConfigComponent = ({
+
+ + 天翼云盘 + +
+
+ 使用天翼云盘前,请先关闭账号的设备锁,否则可能无法登录。 +
+ +
+
+

+ 启用天翼云盘 +

+

+ 开启后,网盘搜索中的天翼云盘资源会显示“立即播放”按钮 +

+
+ +
+ +
+ + setTianyiAccount(e.target.value)} + disabled={!tianyiEnabled} + placeholder='手机号 / 邮箱 / 天翼账号' + className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-red-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed' + /> +
+ +
+ + setTianyiPassword(e.target.value)} + disabled={!tianyiEnabled} + placeholder='输入天翼云盘密码' + className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-red-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed' + /> +
+ +
+ + +
+
+
+ { + const parsed = parseVideoFileName(file.name); + return { + ...file, + originalIndex: index, + sortEpisode: parsed.episode || index + 1, + isOVA: parsed.isOVA, + displayTitle: + parsed.title || (parsed.episode ? `第${parsed.episode}集` : file.name), + }; + }) + .sort((a, b) => { + if (a.isOVA && !b.isOVA) return 1; + if (!a.isOVA && b.isOVA) return -1; + return a.sortEpisode !== b.sortEpisode + ? a.sortEpisode - b.sortEpisode + : a.name.localeCompare(b.name, 'zh-Hans-CN', { numeric: true, sensitivity: 'base' }); + }); + + return NextResponse.json({ + source: NETDISK_TIANYI_SOURCE, + source_name: '天翼云盘', + id: tianyiSession.id, + title: title || tianyiSession.title, + poster: '', + year: '', + douban_id: 0, + desc: `天翼云盘分享:${tianyiSession.shareUrl}`, + episodes: parsedFiles.map((file) => ( + `/api/netdisk/tianyi/play?id=${encodeURIComponent(tianyiSession.id)}&episodeIndex=${file.originalIndex}` + )), + episodes_titles: parsedFiles.map((file) => file.displayTitle), + proxyMode: false, + }); + } catch (error) { + return NextResponse.json( + { error: (error as Error).message }, + { status: 500 } + ); + } + } + if (sourceCode === NETDISK_QUARK_SOURCE || sourceCode === LEGACY_QUARK_TEMP_SOURCE) { try { const config = await getConfig(); diff --git a/src/components/PansouSearch.tsx b/src/components/PansouSearch.tsx index 39197c5..8bcda57 100644 --- a/src/components/PansouSearch.tsx +++ b/src/components/PansouSearch.tsx @@ -168,7 +168,9 @@ export default function PansouSearch({ ? '/api/netdisk/mobile/instant-play' : cloudType === 'baidu' ? '/api/netdisk/baidu/instant-play' - : '/api/netdisk/quark/instant-play'; + : cloudType === 'tianyi' + ? '/api/netdisk/tianyi/instant-play' + : '/api/netdisk/quark/instant-play'; const response = await fetch(instantPlayApi, { method: 'POST', headers: { @@ -187,7 +189,7 @@ export default function PansouSearch({ } router.push( - `/play?source=${encodeURIComponent(data.source || (cloudType === 'mobile' ? 'netdisk-mobile' : cloudType === 'baidu' ? 'netdisk-baidu' : 'netdisk-quark'))}&id=${encodeURIComponent(data.id)}&title=${encodeURIComponent(data.title || keyword)}` + `/play?source=${encodeURIComponent(data.source || (cloudType === 'mobile' ? 'netdisk-mobile' : cloudType === 'baidu' ? 'netdisk-baidu' : cloudType === 'tianyi' ? 'netdisk-tianyi' : 'netdisk-quark'))}&id=${encodeURIComponent(data.id)}&title=${encodeURIComponent(data.title || keyword)}` ); } catch (err: any) { setToast({ @@ -345,7 +347,7 @@ export default function PansouSearch({ {/* 操作按钮 */}
- {(cloudType === 'quark' || cloudType === 'mobile' || cloudType === 'baidu') && ( + {(cloudType === 'quark' || cloudType === 'mobile' || cloudType === 'baidu' || cloudType === 'tianyi') && ( <>