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