12 Commits

Author SHA1 Message Date
1cdd48b225 Publish mteam v1.0.4 2026-09-20 17:20:51 +00:00
c408126e89 Publish mteam v1.0.4 2026-09-20 17:20:48 +00:00
3c476b977b Publish mteam v1.0.4 2026-09-20 17:20:46 +00:00
4e69263ab3 Publish mteam v1.0.3 2026-09-20 16:37:53 +00:00
f2c54ec458 Publish mteam v1.0.3 2026-09-20 16:37:50 +00:00
09a85f06b5 Publish mteam v1.0.3 2026-09-20 16:37:48 +00:00
c841952cc9 Publish nexusphp v1.0.2 2026-09-20 16:37:10 +00:00
04f535177a Publish nexusphp v1.0.2 2026-09-20 16:37:07 +00:00
b472ecfc5b Publish nexusphp v1.0.2 2026-09-20 16:37:05 +00:00
ae4e2d1311 Publish mteam v1.0.2 2026-09-20 16:01:15 +00:00
f661ed5b27 Publish mteam v1.0.2 2026-09-20 16:01:10 +00:00
9844bc4d78 Publish mteam v1.0.2 2026-09-20 16:01:06 +00:00
5 changed files with 231 additions and 11 deletions

View File

@@ -168,7 +168,7 @@ function parseSearch(items) {
var row = {
id: it.id !== undefined && it.id !== null ? String(it.id) : '',
title: it.name || '',
subTitle: it.smallDescr || '',
subtitle: it.smallDescr || '',
size: size,
seeders: __toInt(st.seeders),
leechers: __toInt(st.leechers),
@@ -355,4 +355,92 @@ function download(input) {
} catch (e) {
return { error: String((e && e.message) || e), url: url };
}
}
}
// ===================== 能力: rss(RSS/Atom 解析)=====================
// ctx: { baseUrl, feedUrl }
// mteam 没有通用 RSS 端点 (签名算法不可预知), 用户需从 /rss 页面复制完整 RSS URL,
// 填到 RssSubscription.Url 字段 (→ ctx.feedUrl).
// NexusPHP 类站点: getrss.php / torrentrss.php 两种命名, 脚本 rss() 里兼容.
// 返回: [{ title, detailUrl, downloadUrl, description, pubDate, guid }] 或 { error }
function rss(ctx) {
var base = String(ctx.baseUrl || '').replace(/\/+$/, '');
var apiUrl = String(ctx.apiUrl || (base ? base + '/api' : '')).replace(/\/+$/, '');
var url = ctx.feedUrl ? String(ctx.feedUrl) : '';
// 用户没手动填 feedUrl → 尝试 POST /rss/genlink 自动生成
if (!url && apiUrl) {
var apiKey = ctx.apiKey || ctx.xApiKey;
var genResult = __apiPost(apiUrl, '/rss/genlink', {}, apiKey);
if (genResult && genResult.code === 0 && genResult.data) {
var d = genResult.data;
if (typeof d === 'string' && d.length > 0) {
url = d;
} else if (typeof d === 'object' && d !== null) {
url = d.url || d.link || d.rssUrl || d.feedUrl || d.linkUrl || '';
}
}
}
if (!url) return { error: 'rss_url_required', hint: '请从 /rss 页面复制完整 RSS URL 到 RssSubscription, 或确认 Cookie/apiKey 有效' };
var xml;
try {
xml = http.get(url);
} catch (e) {
return { error: String((e && e.message) || e), url: url };
}
if (!xml) return { error: 'empty_feed', url: url };
var items = [];
function tag(block, name) {
var m = block.match(new RegExp('<' + name + '[^>]*>([\\s\\S]*?)<\\/' + name + '>', 'i'));
return m ? m[1].replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, '$1').trim() : '';
}
// RSS 2.0: <item>
var blocks = xml.match(/<item[\s\S]*?<\/item>/gi);
if (blocks) {
for (var i = 0; i < blocks.length; i++) {
var b = blocks[i];
var encMatch = b.match(/<enclosure[^>]*>/i);
var encUrl = '';
if (encMatch) {
var u = encMatch[0].match(/url=["'']([^"'']*)["'']/i);
if (u) encUrl = u[1];
}
items.push({
title: tag(b, 'title'),
detailUrl: tag(b, 'link'),
downloadUrl: encUrl || tag(b, 'link'),
description: tag(b, 'description'),
pubDate: tag(b, 'pubDate'),
guid: tag(b, 'guid') || tag(b, 'link')
});
}
}
// Atom: <entry>
var atomBlocks = xml.match(/<entry[\s\S]*?<\/entry>/gi);
if (atomBlocks) {
for (var j = 0; j < atomBlocks.length; j++) {
var a = atomBlocks[j];
var linkMatch = a.match(/<link[^>]*href=["'']([^"'']*)["''][^>]*>/i);
var link = linkMatch ? linkMatch[1] : tag(a, 'link');
items.push({
title: tag(a, 'title'),
detailUrl: link,
downloadUrl: link,
description: tag(a, 'summary') || tag(a, 'content'),
pubDate: tag(a, 'updated') || tag(a, 'published'),
guid: tag(a, 'id') || link
});
}
}
if (items.length === 0) {
return { error: 'no_items_parsed', url: url, hint: 'RSS/Atom 格式未识别' };
}
return items;
}

View File

@@ -1,7 +1,7 @@
{
"AdapterCode": "mteam",
"DisplayName": "mteam",
"Version": "1.0.1",
"Version": "1.0.4",
"Status": "published",
"Runtime": "javascript",
"Description": "",

View File

@@ -9,7 +9,7 @@
// download(ctx) —— 下载 .torrent 种子文件字节(能力: download)
// sync(ctx) —— 站点信息同步(mediabot2 Host 调用:首页统计 + 账号资料)
// parse(content, url) —— 解析用户统计页(校验沙箱 /validate 使用,http 走 mock)
// rss(ctx) —— RSS/Atom 刷流(供 RssDiscoveryJob,非标准能力)
// rss(ctx) —— RSS 刷流(供 RssDiscoveryJob,非标准能力)
// ==================== 运行环境兼容层 ====================
// mediabot2 宿主注入: http / html / utils / json;
@@ -555,6 +555,21 @@ function search(input) {
var title = titleEl.text();
if (!title) continue;
// torrentnameHtml: 只定位、不解析. 把标题 <a> 所在 td 的 innerHTML 片段原样传给 C#,
// 由 TorrentNameHtmlParser 用 AngleSharp 精拆 subtitle + tags.
var torrentnameHtml = null;
var titleTd = titleEl.closest ? titleEl.closest('td') : null;
if (!titleTd) {
var p = titleEl.parentElement;
while (p && p.tagName && p.tagName.toLowerCase() !== 'td') p = p.parentElement;
titleTd = p;
}
if (titleTd && typeof titleTd.html === 'function') {
torrentnameHtml = titleTd.html();
} else if (titleTd && typeof titleTd.innerHTML !== 'undefined') {
torrentnameHtml = titleTd.innerHTML;
}
var href = titleEl.attr('href');
var detailUrl = null;
if (href) {
@@ -624,7 +639,7 @@ function search(input) {
var encoding = parseMediaTag(title, /\b(x264|x265|H\.?265|H\.?264|AVC|HEVC|VC-?1|XviD|AV1)\b/i);
results.push({
title: title,
torrentnameHtml: torrentnameHtml,
detailUrl: detailUrl,
downloadUrl: downloadUrl,
size: size,
@@ -659,6 +674,38 @@ function search(input) {
return results;
}
// 从详情页 DOM 中提取外部 ID (doubanId / imdbId / tmdbId / tvdbId).
// NexusPHP 详情页的外部链接通常放在 #kdescr 描述块、#info_block 标签区或页面底部,
// 通过遍历所有 <a> 的 href 正则命中即可覆盖多数站点布局。
function extractExternalIds(doc) {
var ids = {};
try {
var links = doc.queryList('a');
for (var i = 0; i < links.length; i++) {
var href = links[i].attr('href') || '';
if (!ids.doubanId) {
var dbm = href.match(/douban\.com\/subject\/(\d+)/i);
if (dbm) ids.doubanId = dbm[1];
}
if (!ids.imdbId) {
var imm = href.match(/imdb\.com\/title\/(tt\d+)/i);
if (imm) ids.imdbId = imm[1];
}
if (!ids.tmdbId) {
var tmm = href.match(/themoviedb\.org\/(?:movie|tv)\/(\d+)/i);
if (tmm) ids.tmdbId = parseInt(tmm[1], 10);
}
if (!ids.tvdbId) {
var tvm = href.match(/thetvdb\.com\/series\/(\d+)/i) || href.match(/tvdbid=(\d+)/i);
if (tvm) ids.tvdbId = parseInt(tvm[1], 10);
}
// 四个全齐就提前退出
if (ids.doubanId && ids.imdbId && ids.tmdbId && ids.tvdbId) break;
}
} catch (e) { /* 任何异常都静默降级, 返回部分结果 */ }
return ids;
}
// ================= 能力: detail(种子详情)=================
// ctx: { baseUrl, id (或 detailId), detailUrl?(可选, 直接指定详情页地址) }
// 输出字段与能力模板 detail 一致(size/seeders 等保留页面原始文本)。
@@ -722,9 +769,94 @@ function detail(input) {
if (mhref.indexOf('magnet:') === 0) { out.magnet = mhref; break; }
}
// 提取外部 ID (doubanId / imdbId / tmdbId / tvdbId)
var extIds = extractExternalIds(doc);
for (var k in extIds) if (extIds[k] !== undefined) out[k] = extIds[k];
return out;
}
// fetchDetail: C# DetailVerifyJob 调用入口, 别名到 detail 函数.
// C# 端 Invoke("fetchDetail") 是外部约定, 这里保持兼容.
function fetchDetail(input) { return detail(input); }
// ================= 能力: hr(H&R 列表)=================
// 访问 myhr.php, 解析当前账号 H&R 考察中的种子列表.
// C# SiteHrSyncJob 约定入口名: "hr".
// ctx: { baseUrl, hrPath?(可选, 默认 /myhr.php) }
// 返回 { items: [...] } — 见 SiteScriptTorrentFetcher.ParseHrEntries 契约.
//
// #hr-table 表格列:
// H&R ID | 种子名称 | 上传量 | 下载量 | 分享率 | 还需做种时间 | 下载完成时间 | 剩余考察时间 | 备注 | 操作
//
// hrState 值: reviewing (考察中, 默认列表) | satisfied (已达标) | failed (未达标) | pardoned (已免罪)
// myhr.php 默认页 = status=1 考察中; 其他状态通过 ?status=2/3/4 切换.
function parseMyhr(htmlStr) {
var doc = html.parse(htmlStr);
var items = [];
var table = doc.querySelector('#hr-table');
if (!table) return { items: items };
var rows = table.queryList('tr');
for (var i = 0; i < rows.length; i++) {
var tds = rows[i].queryList('td');
if (tds.length < 5) continue;
if ((tds[0].attr('class') || '').indexOf('colhead') !== -1) continue;
var hrId = (tds[0].text() || '').trim();
if (!/^\d+$/.test(hrId)) continue;
// 种子详情链接 → detailId
var nameTd = tds[1];
var detailId = '';
if (nameTd) {
var a = nameTd.querySelector('a[href*="details.php"]');
if (a) {
var idM = (a.attr('href') || '').match(/details\.php\?.*?id=(\d+)/i);
if (idM) detailId = idM[1];
}
}
if (!detailId) continue; // C# ParseHrEntries 跳过 detailId 为空的条目
// 剩余考察时间 → remainingSeconds
// 格式: "9天09:10:50" 或 "10:19:39" (不足一天时可能只有 HH:mm:ss)
var remain = tds[7] ? (tds[7].text() || '').trim() : '';
var remainingSeconds = 0;
var daysMatch = remain.match(/(\d+)\s*天/);
var days = daysMatch ? parseInt(daysMatch[1], 10) : 0;
var hms = remain.match(/(\d{1,2}):(\d{2}):(\d{2})/);
if (hms) {
remainingSeconds = days * 86400
+ parseInt(hms[1], 10) * 3600
+ parseInt(hms[2], 10) * 60
+ parseInt(hms[3], 10);
} else if (days > 0) {
remainingSeconds = days * 86400;
}
items.push({
detailId: detailId,
remainingSeconds: remainingSeconds,
hrState: remainingSeconds > 0 ? 'reviewing' : 'satisfied',
hrId: hrId
});
}
return { items: items };
}
function hr(input) {
var ctx = __asCtx(input);
var base = __resolveBase(ctx);
if (!base || base === 'http://mock.local') return { items: [] };
var path = ctx && ctx.hrPath ? ctx.hrPath : '/myhr.php';
var url = base + path;
var htmlStr = http.get(url);
if (!htmlStr) return { items: [] };
return parseMyhr(htmlStr);
}
// ================= 能力: download(下载种子)=================
// 优先直接使用 ctx.downloadUrl / ctx.detailUrl (搜索解析时抓取的行内下载链接, 已含 passkey/uid 等完整参数, 如 pttime);
// 无行内链接时由 ctx.id (或 detailId) 构造, 可带 ctx.passkey (仅需 Cookie 的站点如 carpt 不传即可)。

View File

@@ -1,7 +1,7 @@
{
"AdapterCode": "nexusphp",
"DisplayName": "nexusphp",
"Version": "1.0.1",
"Version": "1.0.2",
"Status": "published",
"Runtime": "javascript",
"Description": "",

View File

@@ -1,13 +1,13 @@
{
"version": "1.0",
"minClientVersion": "0.1.0",
"generatedAt": "2026-09-20T16:00:31.814Z",
"generatedAt": "2026-09-20T17:20:46.287Z",
"adapters": {
"nexusphp": {
"adapterCode": "nexusphp",
"displayName": "nexusphp",
"version": "1.0.1",
"checksum": "sha256:8380d6b89dc46d48f3b150d0b4285138958f031b3fb08f624bbfc304b9ea4820",
"version": "1.0.2",
"checksum": "sha256:8e64b4650695b75f79784f0251b66bc249fb23a1bd1fb337f4a7e3e972b8b0f0",
"scriptPath": "adapters/nexusphp/adapter.js",
"manifestPath": "adapters/nexusphp/manifest.json",
"capabilities": [
@@ -25,8 +25,8 @@
"mteam": {
"adapterCode": "mteam",
"displayName": "mteam",
"version": "1.0.0",
"checksum": "sha256:6bb1361631d010dda916e03ac2ca138e4adb9f97b75d43f9b38ba27ff1550413",
"version": "1.0.3",
"checksum": "sha256:c679c547b2af424c8cb4809133edb1f1cdde97d37d83390bcb277a76cdeed0ab",
"scriptPath": "adapters/mteam/adapter.js",
"manifestPath": "adapters/mteam/manifest.json",
"capabilities": [],