fix(sites): sync user parsers with MoviePilot v3.2.4

参考:docs/porting/moviepilot-downloader-and-sites.md。按GPLv3公开行为维护TS,补齐UUID身份、专站做种入口与汇总、用户资料回退及刷新边界;不引入上游源码或加密站点数据。
This commit is contained in:
root committed 2026-11-06 00:06:46 +08:00
1 parent c38e7131f1
commit 7e4cdbf5ba
34 files changed
+603 -103

No files matched your search

@@ -7,7 +7,7 @@
* - `IndexerModule.__init__` 里 `ModuleHelper.load('app.modules.indexer.parser', filter=有 schema)` → `USER_PARSER_CLASSES`
* (按模块文件名字母序,与 pkgutil 遍历顺序一致;回退时按此顺序尝试)
* - `IndexerModule.refresh_userdata` → `refreshSiteUserData`:按站点声明的 schema 取解析器;解析后 userid 为空且非公开站时,
* 依次尝试其他 schema(兼容 schema 标注错误/变种站),其他 schema 的异常只记录不抛出。
* 仅尝试 Gazelle/NexusPhp/Unit3d 通用模型,避免探测专站 API;回退异常不抛出。
*
* 与 MP 一致:声明 schema 解析器的异常原样抛出(由调用方记录),只有回退尝试的异常被吞掉。
* 差异:结果不含凭据;`fetchMessages` 默认 false(MP 由全局 SITE_MESSAGE 控制)。
@@ -31,7 +31,7 @@ import { SmallHorseSiteUserInfo } from "./small-horse.ts";
import { SunnyPTSiteUserInfo } from "./sunnypt.ts";
import { TNodeSiteUserInfo } from "./tnode.ts";
import { TorrentLeechSiteUserInfo } from "./torrent-leech.ts";
import type { SiteSchemaValue, SiteUserInfo, SiteUserParserOptions } from "./types.ts";
import { SiteSchema, type SiteSchemaValue, type SiteUserInfo, type SiteUserParserOptions } from "./types.ts";
import { Unit3dSiteUserInfo } from "./unit3d.ts";
import { YemaSiteUserInfo } from "./yema.ts";
import { ZhixingSiteUserInfo } from "./zhixing.ts";
@@ -72,15 +72,27 @@ export interface SiteUserDataRequest extends Omit<SiteUserParserOptions, "siteNa
schema?: string | null;
/** 公开站:不报"未找到解析器",也不做 schema 回退 */
public?: boolean;
/** 用户资料入口;相对路径附加在站点URL之后。 */
userdataPath?: string | null;
/** 全局UA,由调用方从站点设置注入。 */
defaultUa?: string | null;
}
const FALLBACK_SCHEMAS = new Set<SiteSchemaValue>([SiteSchema.Gazelle, SiteSchema.NexusPhp, SiteSchema.Unit3d]);
/** MP `refresh_userdata`:未找到解析器返回 null */
export async function refreshSiteUserData(site: SiteUserDataRequest): Promise<SiteUserInfo | null> {
const make = (schema: string | null | undefined): SiteParserBase | null => {
const cls = getUserParserClass(schema);
if (!cls) return null;
const { name, schema: _s, public: _p, ...rest } = site;
return new cls({ ...rest, siteName: name });
const { name, schema: _s, public: _p, userdataPath, defaultUa, ...rest } = site;
const path = userdataPath?.trim();
const url = path
? /^https?:\/\//.test(path)
? path
: new URL(path.replace(/^\/+/, ""), `${site.url.replace(/\/+$/, "")}/`).toString()
: site.url;
return new cls({ ...rest, siteName: name, url, ua: site.ua || defaultUa });
};
let obj = make(site.schema);
if (!obj) return null;
@@ -90,7 +102,7 @@ export async function refreshSiteUserData(site: SiteUserDataRequest): Promise<Si
const tried = new Set<string>([String(site.schema)]);
for (const cls of USER_PARSER_CLASSES) {
const value = cls.schema as SiteSchemaValue | null;
if (!value || tried.has(value)) continue;
if (!value || tried.has(value) || !FALLBACK_SCHEMAS.has(value)) continue;
tried.add(value);
const alt = make(value);
if (!alt) continue;
@@ -105,6 +117,9 @@ export async function refreshSiteUserData(site: SiteUserDataRequest): Promise<Si
}
}
}
if (!obj.userid && !site.public && !obj.errMsg) {
obj.errMsg = "未获取到站点用户信息,请检查 Cookie 是否有效";
}
const info = obj.toUserInfo();
info.domain = extractDomain(site.url) || null;
return info;