openlist离线下载增加额外源选择
This commit is contained in:
@@ -141,6 +141,10 @@ export interface AdminConfig {
|
||||
RootPath?: string; // 旧字段:根目录路径(向后兼容,迁移后删除)
|
||||
RootPaths?: string[]; // 新字段:多根目录路径列表
|
||||
OfflineDownloadPath: string; // 离线下载目录,默认 "/"
|
||||
OfflineDownloadUseCustomSource?: boolean; // 离线下载是否使用独立 OpenList 源
|
||||
OfflineDownloadURL?: string; // 独立离线下载 OpenList 服务器地址
|
||||
OfflineDownloadUsername?: string; // 独立离线下载 OpenList 账号
|
||||
OfflineDownloadPassword?: string; // 独立离线下载 OpenList 密码
|
||||
LastRefreshTime?: number; // 上次刷新时间戳
|
||||
ResourceCount?: number; // 资源数量
|
||||
ScanInterval?: number; // 定时扫描间隔(分钟),0表示关闭,最低60分钟
|
||||
|
||||
@@ -5,7 +5,11 @@ import { parseStringPromise } from 'xml2js';
|
||||
import { getConfig, setCachedConfig } from '@/lib/config';
|
||||
import { db, getStorage } from '@/lib/db';
|
||||
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';
|
||||
|
||||
const downloadTools: AnimeSubscriptionDownloadTool[] = ['aria2', 'qBittorrent', 'Transmission'];
|
||||
@@ -128,50 +132,11 @@ export async function addOfflineDownload(
|
||||
downloadPath: string
|
||||
) {
|
||||
const config = await getConfig();
|
||||
const openlistConfig = config.OpenListConfig;
|
||||
const downloadTool = getAnimeSubscriptionDownloadTool(
|
||||
config.AnimeSubscriptionConfig?.DownloadTool
|
||||
);
|
||||
|
||||
if (!openlistConfig?.Enabled) {
|
||||
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 || '添加离线下载任务失败');
|
||||
}
|
||||
await addOpenListOfflineDownload(config, downloadPath, torrentUrl, downloadTool);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,9 +271,7 @@ async function sendAnimeUpdateNotifications(
|
||||
*/
|
||||
export async function checkSubscription(subscription: AnimeSubscription) {
|
||||
const config = await getConfig();
|
||||
const openlistConfig = config.OpenListConfig;
|
||||
|
||||
if (!openlistConfig?.OfflineDownloadPath) {
|
||||
if (!config.OpenListConfig?.OfflineDownloadPath) {
|
||||
throw new Error('OpenList 离线下载路径未配置');
|
||||
}
|
||||
|
||||
@@ -329,7 +292,10 @@ export async function checkSubscription(subscription: AnimeSubscription) {
|
||||
const downloaded = [];
|
||||
for (const item of newEpisodes) {
|
||||
try {
|
||||
const downloadPath = `${openlistConfig.OfflineDownloadPath.replace(/\/$/, '')}/${subscription.title}`;
|
||||
const downloadPath = joinOpenListPath(
|
||||
getOfflineDownloadBasePath(config),
|
||||
subscription.title
|
||||
);
|
||||
await addOfflineDownload(item.torrentUrl, downloadPath);
|
||||
|
||||
// 成功后更新 lastEpisode
|
||||
|
||||
@@ -558,6 +558,29 @@ export function configSelfCheck(adminConfig: AdminConfig): AdminConfig {
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -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