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') && ( <>