openlist离线下载增加额外源选择
This commit is contained in:
@@ -3155,6 +3155,11 @@ const OpenListConfigComponent = ({
|
|||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [rootPaths, setRootPaths] = useState<string[]>(['/']);
|
const [rootPaths, setRootPaths] = useState<string[]>(['/']);
|
||||||
const [offlineDownloadPath, setOfflineDownloadPath] = useState('/');
|
const [offlineDownloadPath, setOfflineDownloadPath] = useState('/');
|
||||||
|
const [offlineDownloadUseCustomSource, setOfflineDownloadUseCustomSource] =
|
||||||
|
useState(false);
|
||||||
|
const [offlineDownloadUrl, setOfflineDownloadUrl] = useState('');
|
||||||
|
const [offlineDownloadUsername, setOfflineDownloadUsername] = useState('');
|
||||||
|
const [offlineDownloadPassword, setOfflineDownloadPassword] = useState('');
|
||||||
const [scanInterval, setScanInterval] = useState(0);
|
const [scanInterval, setScanInterval] = useState(0);
|
||||||
const [scanMode, setScanMode] = useState<'torrent' | 'name' | 'hybrid'>(
|
const [scanMode, setScanMode] = useState<'torrent' | 'name' | 'hybrid'>(
|
||||||
'hybrid'
|
'hybrid'
|
||||||
@@ -3183,6 +3188,16 @@ const OpenListConfigComponent = ({
|
|||||||
: ['/'])
|
: ['/'])
|
||||||
);
|
);
|
||||||
setOfflineDownloadPath(config.OpenListConfig.OfflineDownloadPath || '/');
|
setOfflineDownloadPath(config.OpenListConfig.OfflineDownloadPath || '/');
|
||||||
|
setOfflineDownloadUseCustomSource(
|
||||||
|
config.OpenListConfig.OfflineDownloadUseCustomSource || false
|
||||||
|
);
|
||||||
|
setOfflineDownloadUrl(config.OpenListConfig.OfflineDownloadURL || '');
|
||||||
|
setOfflineDownloadUsername(
|
||||||
|
config.OpenListConfig.OfflineDownloadUsername || ''
|
||||||
|
);
|
||||||
|
setOfflineDownloadPassword(
|
||||||
|
config.OpenListConfig.OfflineDownloadPassword || ''
|
||||||
|
);
|
||||||
setScanInterval(config.OpenListConfig.ScanInterval || 0);
|
setScanInterval(config.OpenListConfig.ScanInterval || 0);
|
||||||
setScanMode(config.OpenListConfig.ScanMode || 'hybrid');
|
setScanMode(config.OpenListConfig.ScanMode || 'hybrid');
|
||||||
setDisableVideoPreview(
|
setDisableVideoPreview(
|
||||||
@@ -3233,6 +3248,10 @@ const OpenListConfigComponent = ({
|
|||||||
Password: password,
|
Password: password,
|
||||||
RootPaths: rootPaths,
|
RootPaths: rootPaths,
|
||||||
OfflineDownloadPath: offlineDownloadPath,
|
OfflineDownloadPath: offlineDownloadPath,
|
||||||
|
OfflineDownloadUseCustomSource: offlineDownloadUseCustomSource,
|
||||||
|
OfflineDownloadURL: offlineDownloadUrl,
|
||||||
|
OfflineDownloadUsername: offlineDownloadUsername,
|
||||||
|
OfflineDownloadPassword: offlineDownloadPassword,
|
||||||
ScanInterval: scanInterval,
|
ScanInterval: scanInterval,
|
||||||
ScanMode: scanMode,
|
ScanMode: scanMode,
|
||||||
DisableVideoPreview: disableVideoPreview,
|
DisableVideoPreview: disableVideoPreview,
|
||||||
@@ -3592,6 +3611,92 @@ const OpenListConfigComponent = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='space-y-4 rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<div>
|
||||||
|
<h3 className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||||
|
离线下载使用独立 OpenList 源
|
||||||
|
</h3>
|
||||||
|
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||||
|
开启后,存到私人影库和追番订阅会把任务提交到下方 OpenList,扫描和播放仍使用上方主 OpenList
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={() =>
|
||||||
|
setOfflineDownloadUseCustomSource(
|
||||||
|
!offlineDownloadUseCustomSource
|
||||||
|
)
|
||||||
|
}
|
||||||
|
disabled={!enabled}
|
||||||
|
className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors ${
|
||||||
|
offlineDownloadUseCustomSource
|
||||||
|
? 'bg-blue-600'
|
||||||
|
: 'bg-gray-200 dark:bg-gray-700'
|
||||||
|
} ${!enabled ? 'opacity-50 cursor-not-allowed' : ''}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||||
|
offlineDownloadUseCustomSource
|
||||||
|
? 'translate-x-6'
|
||||||
|
: 'translate-x-1'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{offlineDownloadUseCustomSource && (
|
||||||
|
<div className='space-y-4'>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
离线下载 OpenList URL
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={offlineDownloadUrl}
|
||||||
|
onChange={(e) => setOfflineDownloadUrl(e.target.value)}
|
||||||
|
disabled={!enabled}
|
||||||
|
placeholder='https://download-openlist-server.com'
|
||||||
|
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-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='grid grid-cols-2 gap-4'>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
离线下载账号
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={offlineDownloadUsername}
|
||||||
|
onChange={(e) =>
|
||||||
|
setOfflineDownloadUsername(e.target.value)
|
||||||
|
}
|
||||||
|
disabled={!enabled}
|
||||||
|
placeholder='admin'
|
||||||
|
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-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
离线下载密码
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='password'
|
||||||
|
value={offlineDownloadPassword}
|
||||||
|
onChange={(e) =>
|
||||||
|
setOfflineDownloadPassword(e.target.value)
|
||||||
|
}
|
||||||
|
disabled={!enabled}
|
||||||
|
placeholder='password'
|
||||||
|
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-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
定时扫描间隔(分钟)
|
定时扫描间隔(分钟)
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||||||
|
|
||||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||||
import { getConfig } from '@/lib/config';
|
import { getConfig } from '@/lib/config';
|
||||||
import { OpenListClient } from '@/lib/openlist.client';
|
import {
|
||||||
|
addOpenListOfflineDownload,
|
||||||
|
getOfflineDownloadBasePath,
|
||||||
|
joinOpenListPath,
|
||||||
|
} from '@/lib/openlist-offline-download';
|
||||||
import { hasFeaturePermission } from '@/lib/permissions';
|
import { hasFeaturePermission } from '@/lib/permissions';
|
||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
@@ -55,56 +59,13 @@ export async function POST(req: NextRequest) {
|
|||||||
|
|
||||||
// 获取 OpenList 配置
|
// 获取 OpenList 配置
|
||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
const openlistConfig = config.OpenListConfig;
|
|
||||||
|
|
||||||
if (!openlistConfig?.Enabled) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: '私人影库功能未启用' },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!openlistConfig.URL || !openlistConfig.Username || !openlistConfig.Password) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'OpenList 配置不完整' },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建下载路径(使用离线下载目录)
|
// 构建下载路径(使用离线下载目录)
|
||||||
const offlineDownloadPath = openlistConfig.OfflineDownloadPath || '/';
|
const downloadPath = joinOpenListPath(
|
||||||
const downloadPath = `${offlineDownloadPath.replace(/\/$/, '')}/${name}`;
|
getOfflineDownloadBasePath(config),
|
||||||
|
name
|
||||||
// 使用 OpenListClient 添加离线下载任务
|
|
||||||
const client = new OpenListClient(
|
|
||||||
openlistConfig.URL,
|
|
||||||
openlistConfig.Username,
|
|
||||||
openlistConfig.Password
|
|
||||||
);
|
);
|
||||||
|
await addOpenListOfflineDownload(config, downloadPath, url, tool);
|
||||||
// 获取 Token 并调用 API
|
|
||||||
const token = await (client as any).getToken();
|
|
||||||
const openlistUrl = `${openlistConfig.URL.replace(/\/$/, '')}/api/fs/add_offline_download`;
|
|
||||||
|
|
||||||
const response = await fetch(openlistUrl, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': token,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
path: downloadPath,
|
|
||||||
urls: [url],
|
|
||||||
tool,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
// 检查响应状态
|
|
||||||
if (!response.ok || data.code !== 200) {
|
|
||||||
throw new Error(data.message || '添加离线下载任务失败');
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -45,7 +45,22 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const { action, Enabled, URL, Username, Password, RootPaths, OfflineDownloadPath, ScanInterval, ScanMode, DisableVideoPreview } = body;
|
const {
|
||||||
|
action,
|
||||||
|
Enabled,
|
||||||
|
URL,
|
||||||
|
Username,
|
||||||
|
Password,
|
||||||
|
RootPaths,
|
||||||
|
OfflineDownloadPath,
|
||||||
|
OfflineDownloadUseCustomSource,
|
||||||
|
OfflineDownloadURL,
|
||||||
|
OfflineDownloadUsername,
|
||||||
|
OfflineDownloadPassword,
|
||||||
|
ScanInterval,
|
||||||
|
ScanMode,
|
||||||
|
DisableVideoPreview,
|
||||||
|
} = body;
|
||||||
|
|
||||||
const authInfo = getAuthInfoFromCookie(request);
|
const authInfo = getAuthInfoFromCookie(request);
|
||||||
if (!authInfo || !authInfo.username) {
|
if (!authInfo || !authInfo.username) {
|
||||||
@@ -74,6 +89,10 @@ export async function POST(request: NextRequest) {
|
|||||||
Password: Password || '',
|
Password: Password || '',
|
||||||
RootPaths: RootPaths || ['/'],
|
RootPaths: RootPaths || ['/'],
|
||||||
OfflineDownloadPath: OfflineDownloadPath || '/',
|
OfflineDownloadPath: OfflineDownloadPath || '/',
|
||||||
|
OfflineDownloadUseCustomSource: OfflineDownloadUseCustomSource || false,
|
||||||
|
OfflineDownloadURL: OfflineDownloadURL || '',
|
||||||
|
OfflineDownloadUsername: OfflineDownloadUsername || '',
|
||||||
|
OfflineDownloadPassword: OfflineDownloadPassword || '',
|
||||||
LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime,
|
LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime,
|
||||||
ResourceCount: adminConfig.OpenListConfig?.ResourceCount,
|
ResourceCount: adminConfig.OpenListConfig?.ResourceCount,
|
||||||
ScanInterval: 0,
|
ScanInterval: 0,
|
||||||
@@ -97,6 +116,16 @@ export async function POST(request: NextRequest) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
OfflineDownloadUseCustomSource &&
|
||||||
|
(!OfflineDownloadURL || !OfflineDownloadUsername || !OfflineDownloadPassword)
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '请提供离线下载 OpenList URL、账号和密码' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 验证 RootPaths
|
// 验证 RootPaths
|
||||||
if (!Array.isArray(RootPaths) || RootPaths.length === 0) {
|
if (!Array.isArray(RootPaths) || RootPaths.length === 0) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
@@ -130,6 +159,24 @@ export async function POST(request: NextRequest) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OfflineDownloadUseCustomSource) {
|
||||||
|
try {
|
||||||
|
console.log('[OpenList Config] 验证离线下载 OpenList 账号密码');
|
||||||
|
await OpenListClient.login(
|
||||||
|
OfflineDownloadURL,
|
||||||
|
OfflineDownloadUsername,
|
||||||
|
OfflineDownloadPassword
|
||||||
|
);
|
||||||
|
console.log('[OpenList Config] 离线下载 OpenList 账号密码验证成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[OpenList Config] 离线下载 OpenList 账号密码验证失败:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '离线下载 OpenList 账号密码验证失败: ' + (error as Error).message },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adminConfig.OpenListConfig = {
|
adminConfig.OpenListConfig = {
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
URL,
|
URL,
|
||||||
@@ -137,6 +184,10 @@ export async function POST(request: NextRequest) {
|
|||||||
Password,
|
Password,
|
||||||
RootPaths: cleanedRootPaths,
|
RootPaths: cleanedRootPaths,
|
||||||
OfflineDownloadPath: OfflineDownloadPath || '/',
|
OfflineDownloadPath: OfflineDownloadPath || '/',
|
||||||
|
OfflineDownloadUseCustomSource: OfflineDownloadUseCustomSource || false,
|
||||||
|
OfflineDownloadURL: OfflineDownloadURL || '',
|
||||||
|
OfflineDownloadUsername: OfflineDownloadUsername || '',
|
||||||
|
OfflineDownloadPassword: OfflineDownloadPassword || '',
|
||||||
LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime,
|
LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime,
|
||||||
ResourceCount: adminConfig.OpenListConfig?.ResourceCount,
|
ResourceCount: adminConfig.OpenListConfig?.ResourceCount,
|
||||||
ScanInterval: scanInterval,
|
ScanInterval: scanInterval,
|
||||||
|
|||||||
@@ -141,6 +141,10 @@ export interface AdminConfig {
|
|||||||
RootPath?: string; // 旧字段:根目录路径(向后兼容,迁移后删除)
|
RootPath?: string; // 旧字段:根目录路径(向后兼容,迁移后删除)
|
||||||
RootPaths?: string[]; // 新字段:多根目录路径列表
|
RootPaths?: string[]; // 新字段:多根目录路径列表
|
||||||
OfflineDownloadPath: string; // 离线下载目录,默认 "/"
|
OfflineDownloadPath: string; // 离线下载目录,默认 "/"
|
||||||
|
OfflineDownloadUseCustomSource?: boolean; // 离线下载是否使用独立 OpenList 源
|
||||||
|
OfflineDownloadURL?: string; // 独立离线下载 OpenList 服务器地址
|
||||||
|
OfflineDownloadUsername?: string; // 独立离线下载 OpenList 账号
|
||||||
|
OfflineDownloadPassword?: string; // 独立离线下载 OpenList 密码
|
||||||
LastRefreshTime?: number; // 上次刷新时间戳
|
LastRefreshTime?: number; // 上次刷新时间戳
|
||||||
ResourceCount?: number; // 资源数量
|
ResourceCount?: number; // 资源数量
|
||||||
ScanInterval?: number; // 定时扫描间隔(分钟),0表示关闭,最低60分钟
|
ScanInterval?: number; // 定时扫描间隔(分钟),0表示关闭,最低60分钟
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import { parseStringPromise } from 'xml2js';
|
|||||||
import { getConfig, setCachedConfig } from '@/lib/config';
|
import { getConfig, setCachedConfig } from '@/lib/config';
|
||||||
import { db, getStorage } from '@/lib/db';
|
import { db, getStorage } from '@/lib/db';
|
||||||
import { EmailService } from '@/lib/email.service';
|
import { EmailService } from '@/lib/email.service';
|
||||||
import { OpenListClient } from '@/lib/openlist.client';
|
import {
|
||||||
|
addOpenListOfflineDownload,
|
||||||
|
getOfflineDownloadBasePath,
|
||||||
|
joinOpenListPath,
|
||||||
|
} from '@/lib/openlist-offline-download';
|
||||||
import { AnimeSubscription, AnimeSubscriptionDownloadTool } from '@/types/anime-subscription';
|
import { AnimeSubscription, AnimeSubscriptionDownloadTool } from '@/types/anime-subscription';
|
||||||
|
|
||||||
const downloadTools: AnimeSubscriptionDownloadTool[] = ['aria2', 'qBittorrent', 'Transmission'];
|
const downloadTools: AnimeSubscriptionDownloadTool[] = ['aria2', 'qBittorrent', 'Transmission'];
|
||||||
@@ -128,50 +132,11 @@ export async function addOfflineDownload(
|
|||||||
downloadPath: string
|
downloadPath: string
|
||||||
) {
|
) {
|
||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
const openlistConfig = config.OpenListConfig;
|
|
||||||
const downloadTool = getAnimeSubscriptionDownloadTool(
|
const downloadTool = getAnimeSubscriptionDownloadTool(
|
||||||
config.AnimeSubscriptionConfig?.DownloadTool
|
config.AnimeSubscriptionConfig?.DownloadTool
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!openlistConfig?.Enabled) {
|
await addOpenListOfflineDownload(config, downloadPath, torrentUrl, downloadTool);
|
||||||
throw new Error('私人影库功能未启用');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!openlistConfig.URL ||
|
|
||||||
!openlistConfig.Username ||
|
|
||||||
!openlistConfig.Password
|
|
||||||
) {
|
|
||||||
throw new Error('OpenList 配置不完整');
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new OpenListClient(
|
|
||||||
openlistConfig.URL,
|
|
||||||
openlistConfig.Username,
|
|
||||||
openlistConfig.Password
|
|
||||||
);
|
|
||||||
|
|
||||||
const token = await (client as any).getToken();
|
|
||||||
const openlistUrl = `${openlistConfig.URL.replace(/\/$/, '')}/api/fs/add_offline_download`;
|
|
||||||
|
|
||||||
const response = await fetch(openlistUrl, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Authorization: token,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
path: downloadPath,
|
|
||||||
urls: [torrentUrl],
|
|
||||||
tool: downloadTool,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok || data.code !== 200) {
|
|
||||||
throw new Error(data.message || '添加离线下载任务失败');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -306,9 +271,7 @@ async function sendAnimeUpdateNotifications(
|
|||||||
*/
|
*/
|
||||||
export async function checkSubscription(subscription: AnimeSubscription) {
|
export async function checkSubscription(subscription: AnimeSubscription) {
|
||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
const openlistConfig = config.OpenListConfig;
|
if (!config.OpenListConfig?.OfflineDownloadPath) {
|
||||||
|
|
||||||
if (!openlistConfig?.OfflineDownloadPath) {
|
|
||||||
throw new Error('OpenList 离线下载路径未配置');
|
throw new Error('OpenList 离线下载路径未配置');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +292,10 @@ export async function checkSubscription(subscription: AnimeSubscription) {
|
|||||||
const downloaded = [];
|
const downloaded = [];
|
||||||
for (const item of newEpisodes) {
|
for (const item of newEpisodes) {
|
||||||
try {
|
try {
|
||||||
const downloadPath = `${openlistConfig.OfflineDownloadPath.replace(/\/$/, '')}/${subscription.title}`;
|
const downloadPath = joinOpenListPath(
|
||||||
|
getOfflineDownloadBasePath(config),
|
||||||
|
subscription.title
|
||||||
|
);
|
||||||
await addOfflineDownload(item.torrentUrl, downloadPath);
|
await addOfflineDownload(item.torrentUrl, downloadPath);
|
||||||
|
|
||||||
// 成功后更新 lastEpisode
|
// 成功后更新 lastEpisode
|
||||||
|
|||||||
@@ -558,6 +558,29 @@ export function configSelfCheck(adminConfig: AdminConfig): AdminConfig {
|
|||||||
}
|
}
|
||||||
adminConfig.LiveRefreshIntervalHours = normalizeLiveRefreshIntervalHours(adminConfig.LiveRefreshIntervalHours);
|
adminConfig.LiveRefreshIntervalHours = normalizeLiveRefreshIntervalHours(adminConfig.LiveRefreshIntervalHours);
|
||||||
|
|
||||||
|
if (adminConfig.OpenListConfig) {
|
||||||
|
if (!adminConfig.OpenListConfig.RootPaths) {
|
||||||
|
adminConfig.OpenListConfig.RootPaths = adminConfig.OpenListConfig.RootPath
|
||||||
|
? [adminConfig.OpenListConfig.RootPath]
|
||||||
|
: ['/'];
|
||||||
|
}
|
||||||
|
if (!adminConfig.OpenListConfig.OfflineDownloadPath) {
|
||||||
|
adminConfig.OpenListConfig.OfflineDownloadPath = '/';
|
||||||
|
}
|
||||||
|
if (adminConfig.OpenListConfig.OfflineDownloadUseCustomSource === undefined) {
|
||||||
|
adminConfig.OpenListConfig.OfflineDownloadUseCustomSource = false;
|
||||||
|
}
|
||||||
|
if (adminConfig.OpenListConfig.OfflineDownloadURL === undefined) {
|
||||||
|
adminConfig.OpenListConfig.OfflineDownloadURL = '';
|
||||||
|
}
|
||||||
|
if (adminConfig.OpenListConfig.OfflineDownloadUsername === undefined) {
|
||||||
|
adminConfig.OpenListConfig.OfflineDownloadUsername = '';
|
||||||
|
}
|
||||||
|
if (adminConfig.OpenListConfig.OfflineDownloadPassword === undefined) {
|
||||||
|
adminConfig.OpenListConfig.OfflineDownloadPassword = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 用户信息已迁移到新版数据库
|
// 用户信息已迁移到新版数据库
|
||||||
// 这里只保留站长用户用于兼容性,其他用户从数据库读取
|
// 这里只保留站长用户用于兼容性,其他用户从数据库读取
|
||||||
const ownerUser = process.env.USERNAME;
|
const ownerUser = process.env.USERNAME;
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
import { AdminConfig } from '@/lib/admin.types';
|
||||||
|
import { OpenListClient } from '@/lib/openlist.client';
|
||||||
|
|
||||||
|
type OpenListOfflineDownloadSource = {
|
||||||
|
url: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getOfflineDownloadSource(config: AdminConfig): OpenListOfflineDownloadSource {
|
||||||
|
const openlistConfig = config.OpenListConfig;
|
||||||
|
|
||||||
|
if (!openlistConfig?.Enabled) {
|
||||||
|
throw new Error('私人影库功能未启用');
|
||||||
|
}
|
||||||
|
|
||||||
|
const useCustomSource = openlistConfig.OfflineDownloadUseCustomSource === true;
|
||||||
|
const source = useCustomSource
|
||||||
|
? {
|
||||||
|
url: openlistConfig.OfflineDownloadURL || '',
|
||||||
|
username: openlistConfig.OfflineDownloadUsername || '',
|
||||||
|
password: openlistConfig.OfflineDownloadPassword || '',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
url: openlistConfig.URL,
|
||||||
|
username: openlistConfig.Username,
|
||||||
|
password: openlistConfig.Password,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!source.url || !source.username || !source.password) {
|
||||||
|
throw new Error(
|
||||||
|
useCustomSource
|
||||||
|
? '离线下载 OpenList 配置不完整'
|
||||||
|
: 'OpenList 配置不完整'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOfflineDownloadBasePath(config: AdminConfig): string {
|
||||||
|
const path = config.OpenListConfig?.OfflineDownloadPath || '/';
|
||||||
|
const normalizedPath = path.replace(/\/$/, '');
|
||||||
|
return normalizedPath || '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function joinOpenListPath(basePath: string, name: string): string {
|
||||||
|
return basePath === '/' ? `/${name}` : `${basePath}/${name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addOpenListOfflineDownload(
|
||||||
|
config: AdminConfig,
|
||||||
|
downloadPath: string,
|
||||||
|
url: string,
|
||||||
|
tool: string
|
||||||
|
) {
|
||||||
|
const source = getOfflineDownloadSource(config);
|
||||||
|
const client = new OpenListClient(source.url, source.username, source.password);
|
||||||
|
const token = await (client as any).getToken();
|
||||||
|
const openlistUrl = `${source.url.replace(/\/$/, '')}/api/fs/add_offline_download`;
|
||||||
|
|
||||||
|
const response = await fetch(openlistUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: token,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
path: downloadPath,
|
||||||
|
urls: [url],
|
||||||
|
tool,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok || data.code !== 200) {
|
||||||
|
throw new Error(data.message || '添加离线下载任务失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user