- sites/spiders: Haidan/HDDolby/RousiPro/SunnyPT/TNode/TorrentLeech/Yema (+mTorrent wrapper), specialized SiteAdapter, indirect download resolved at download time - sites/user-parsers: SiteParserBase + all 20 MP SiteSchema parsers (lxml XPath via xpath+xmldom), refreshSiteUserData with schema fallback - sites/definitions: zod SiteDefinition schemas, 23 MovieClaw v0.27.0 (MIT) definitions via scripts/mp-sync/movieclaw-import.mjs, registry getSiteDefinition/listSiteDefinitions, NOTICE - scripts/mp-sync: ledger.json (upstream sha256), check.mjs, build-ledger.mjs; docs/porting/mp-sync-ledger.md - license-and-provenance: add copied-mit kind, re-anchor MovieClaw rows to v0.27.0 Co-Authored-By: Claude Opus 5.5 <[email protected]>
134 lines
5.3 KiB
TypeScript
134 lines
5.3 KiB
TypeScript
/**
|
|
* SPDX-License-Identifier: GPL-3.0-only
|
|
* 移植自 MoviePilot(https://github.com/jxxghp/MoviePilot,GPLv3),本文件按 GPLv3 条款提供。
|
|
* @mp-source app/modules/indexer/parser/hddolby.py @ e6820a8
|
|
*
|
|
* HDDolby 用户数据解析器(MP `HDDolbySiteUserInfo`,apikey 模式)。
|
|
* 函数对照:`HDDolby_sysRoleList` → `HDDOLBY_SYS_ROLE_LIST`、`_parse_site_page` → `_parseSitePage`(api.<域>)、
|
|
* `_parse_logged_in`(恒 True)、`_parse_user_base_info` → `_parseUserBaseInfo`(api/v1/user/data)、
|
|
* `_parse_user_torrent_seeding_info` → `_parseUserTorrentSeedingInfo`(api/v1/user/peers)、
|
|
* `_pase_unread_msgs` → `_paseUnreadMsgs`(API 只给未读数,正文借 NexusPHP 解析器用 Cookie 读网页)。
|
|
* 注:`_user_basic_params = {}` 在 Python 中为假值,故 profile/peers 两个接口实际为 GET(与 MP 一致)。
|
|
*/
|
|
import { type NextPage, SiteParserBase } from "./base.ts";
|
|
import { extractDomain, pyFloat, pyInt } from "./common.ts";
|
|
import { NexusPhpSiteUserInfo } from "./nexus-php.ts";
|
|
import { SiteSchema, type SiteSchemaValue } from "./types.ts";
|
|
|
|
type Json = Record<string, unknown>;
|
|
|
|
/** 用户级别字典(MP `HDDolby_sysRoleList`) */
|
|
export const HDDOLBY_SYS_ROLE_LIST: Record<string, string> = {
|
|
"0": "Peasant",
|
|
"1": "User",
|
|
"2": "Power User",
|
|
"3": "Elite User",
|
|
"4": "Crazy User",
|
|
"5": "Insane User",
|
|
"6": "Veteran User",
|
|
"7": "Extreme User",
|
|
"8": "Ultimate User",
|
|
"9": "Nexus Master",
|
|
"10": "VIP",
|
|
"11": "Retiree",
|
|
"12": "Helper",
|
|
"13": "Seeder",
|
|
"14": "Transferrer",
|
|
"15": "Uploader",
|
|
"16": "Torrent Manager",
|
|
"17": "Forum Moderator",
|
|
"18": "Coder",
|
|
"19": "Moderator",
|
|
"20": "Administrator",
|
|
"21": "Sysop",
|
|
"22": "Staff Leader",
|
|
};
|
|
|
|
export class HDDolbySiteUserInfo extends SiteParserBase {
|
|
static override schema: SiteSchemaValue | null = SiteSchema.HDDolby;
|
|
override requestMode = "apikey" as const;
|
|
|
|
protected _parseSitePage(_htmlText: string): void {
|
|
this._baseUrl = `https://api.${extractDomain(this._baseUrl)}`;
|
|
this._userTrafficPage = null;
|
|
this._userDetailPage = null;
|
|
this._userBasicPage = "api/v1/user/data";
|
|
this._userBasicParams = {};
|
|
this._userBasicHeaders = { "Content-Type": "application/json", Accept: "application/json, text/plain, */*" };
|
|
this._sysMailUnreadPage = null;
|
|
this._userMailUnreadPage = null;
|
|
this._mailUnreadParams = {};
|
|
this._torrentSeedingPage = "api/v1/user/peers";
|
|
this._torrentSeedingParams = {};
|
|
this._torrentSeedingHeaders = { "Content-Type": "application/json", Accept: "application/json, text/plain, */*" };
|
|
this._additionHeaders = { "x-api-key": this.apikey ?? "" };
|
|
}
|
|
|
|
protected override _parseLoggedIn(_htmlText: unknown): boolean {
|
|
return true;
|
|
}
|
|
|
|
protected _parseUserBaseInfo(htmlText: string): void {
|
|
if (!htmlText) return;
|
|
const detail = JSON.parse(htmlText) as Json | null;
|
|
if (detail?.status !== 0) return;
|
|
const infos = detail.data as Json[] | null;
|
|
if (!infos?.length) return;
|
|
const u = infos[0]!;
|
|
this.userid = u.id == null ? null : String(u.id);
|
|
this.username = (u.username as string) ?? null;
|
|
this.userLevel = HDDOLBY_SYS_ROLE_LIST[String(u.class || "1")] ?? null;
|
|
this.joinAt = (u.added as string) ?? null;
|
|
this.upload = pyInt(u.uploaded || "0");
|
|
this.download = pyInt(u.downloaded || "0");
|
|
this.ratio = this.download ? Math.round((this.upload / this.download) * 100) / 100 : 0;
|
|
this.bonus = pyFloat(u.seedbonus || "0");
|
|
this.messageUnread = pyInt(u.unread_messages || "0");
|
|
}
|
|
|
|
protected _parseUserTrafficInfo(_htmlText: string): void {}
|
|
|
|
protected _parseUserDetailInfo(_htmlText: string): void {}
|
|
|
|
protected _parseUserTorrentSeedingInfo(htmlText: string, _multiPage = false): NextPage {
|
|
if (!htmlText) return null;
|
|
const seedingInfo = JSON.parse(htmlText) as Json | null;
|
|
if (seedingInfo?.status !== 0) return null;
|
|
const torrents = (seedingInfo.data ?? []) as Json[];
|
|
let pageSeedingSize = 0;
|
|
const pageSeedingInfo: Array<[number, number]> = [];
|
|
for (const info of torrents) {
|
|
const size = Number(info.size ?? 0);
|
|
const seeder = Number(info.seeders || 1);
|
|
pageSeedingSize += size;
|
|
pageSeedingInfo.push([seeder, size]);
|
|
}
|
|
this.seeding += torrents.length;
|
|
this.seedingSize += pageSeedingSize;
|
|
this.seedingInfo.push(...pageSeedingInfo);
|
|
return null;
|
|
}
|
|
|
|
protected _parseMessageUnreadLinks(_htmlText: string, _msgLinks: string[]): NextPage {
|
|
return null;
|
|
}
|
|
|
|
protected _parseMessageContent(_htmlText: string): [string | null, string | null, string | null] {
|
|
return [null, null, null];
|
|
}
|
|
|
|
/** API 仅返回未读数量,正文需通过 NexusPHP 网页接口读取(需 Cookie) */
|
|
protected override async _paseUnreadMsgs(): Promise<void> {
|
|
if (!this._siteCookie) return;
|
|
const nexus = new NexusPhpSiteUserInfo({ ...this._opts, cookie: this._siteCookie });
|
|
nexus.messageUnread = this.messageUnread;
|
|
nexus.messageReadForce = this.messageUnread > 0;
|
|
await (nexus as unknown as { _paseUnreadMsgs(): Promise<void> })._paseUnreadMsgs();
|
|
this.messageUnreadContents = [...nexus.messageUnreadContents];
|
|
this.httpErrors.push(...nexus.httpErrors);
|
|
if (this.messageUnreadContents.length && !this.messageUnread) {
|
|
this.messageUnread = this.messageUnreadContents.length;
|
|
}
|
|
}
|
|
}
|