Files
par/par-core/src/main/resources/scripts/default-nexusphp.groovy

179 lines
7.9 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// NexusPHP 站点默认脚本
// 适用: PThome / HDSky / OurBits / HDFans / 聆音 等
// 覆盖: 搜索 / 详情 / 用户信息 / 签到
Map parse(String content, String url) {
def doc = jsoup.parse(content)
if (url.contains('/torrents.php')) return parseSearch(doc)
if (url.contains('/details.php')) return parseDetail(doc)
if (url.contains('/attendance.php')) return parseSign(doc)
if (url.contains('/index.php') && !url.contains('?')) return parseUserInfo(doc)
if (url.contains('/usercp.php')) return parseUserPanel(doc)
return [pageName: '未知页面', message: "未配置: ${url}"]
}
private Map parseUserInfo(doc) {
def fields = [:]
def el = utils.selectFirst(doc, '#info_block, td.bottom.clearfix, .userbar')
if (el) {
def t = utils.text(el)
// 通用正则提取
def um = t =~ /上传[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
def dm = t =~ /下载[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
def rm = t =~ /分享率[::]\s*([\d.,]+)/
def bm = t =~ /魔力[值::]*[^0-9]*([\d.,]+)/
if (um) fields.upload = um[0][1] + (um[0][2] ?: '')
if (dm) fields.download = dm[0][1] + (dm[0][2] ?: '')
if (rm) fields.ratio = rm[0][1]
if (bm) fields.bonus = bm[0][1]
// PTTIME 特殊:做种/下载计数
def sm = t =~ /⬆\s*(\d+)/
def lm = t =~ /⬇\s*(\d+)/
if (sm) fields.seeding = sm[0][1]
if (lm) fields.leeching = lm[0][1]
}
return [pageName: '用户信息', fields: fields]
}
private Map parseSearch(doc) {
def rows = []
// 兼容有/无 tbody 的 NexusPHP 变体
doc.select('table.torrents tr:has(td.rowfollow):not(:has(.colhead))').each { row ->
def titleEl = utils.selectFirst(row, 'td.embedded a[href*=details]')
if (!titleEl) return
def catEl = utils.selectFirst(row, 'td.rowfollow a[href*=cat] img, td.rowfollow a[href*=torrents] img, span.category')
def freeEl = utils.selectFirst(row, 'font.promotion')
def tds = row.select('td.rowfollow')
// 按列索引查找:size(含br)、seeders、leechers、completed、time
def size = '', seeders = '', leechers = '', completed = '', time = '', publisher = ''
for (int i = 0; i < tds.size(); i++) {
def t = utils.text(tds.get(i))
if (!t) continue
if (t.contains('GB') || t.contains('MB') || t.contains('TB') || t.contains('KB')) size = t
else if (t ==~ /^\d+$/ && !seeders) { seeders = t }
else if (t ==~ /^\d+$/ && seeders) { leechers = t }
else if (t ==~ /^\d+$/ && seeders && leechers) { completed = t }
else if (t ==~ /^\d+年|^\d+月|^\d+天|^\d+时/) time = t
else if (seeders && !leechers) publisher = t
}
rows << [
title: utils.text(titleEl),
detailUrl: utils.attr(titleEl, 'href'),
category: utils.attr(catEl, 'alt') ?: utils.attr(catEl, 'title') ?: '',
free: utils.text(freeEl),
size: size,
seeders: seeders,
leechers: leechers,
completed: completed,
publishDate: time,
publisher: publisher,
]
}
return [pageName: '搜索', extracted: rows, message: "共 ${rows.size()} 条"]
}
private Map parseDetail(doc) {
return [pageName: '种子详情', fields: [
title: utils.text(utils.selectFirst(doc, '#top h1, h1')),
subtitle: utils.text(utils.selectFirst(doc, 'td.embedded b')),
size: utils.text(utils.selectFirst(doc, 'td:has(b:contains(大小)) + td, td.rowhead:contains(大小) + td')),
seeders: utils.text(utils.selectFirst(doc, 'td:contains(做种者) + td')),
leechers: utils.text(utils.selectFirst(doc, 'td:contains(下载者) + td')),
]]
}
private Map parseSign(doc) {
def t = utils.text(doc.body())
def matched = t ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
return [pageName: '签到', fields: [result: t.take(200)], message: matched ? '成功' : '未知']
}
private Map parseUserPanel(doc) {
def fields = [:]
doc.select('td.text').each { td ->
def t = utils.text(td)
if (t.contains('分享率')) fields.shareRate = t.replaceAll(/[^0-9.]/, '')
if (t.contains('魔力')) fields.bonus = t.replaceAll(/[^0-9.,]/, '')
}
return [pageName: '控制面板', fields: fields]
}
Map sync(PluginContext ctx) {
if (!ctx.site.auth) return [message: "未配置认证", loginRequired: true]
try {
def conn = ctx.httpGet('/index.php')
def doc = jsoup.parse(conn.body())
def el = utils.selectFirst(doc, '#info_block, td.bottom.clearfix, .userbar')
if (!el) return [message: "未找到用户信息"]
def t = utils.text(el)
def result = [:]
def um = t =~ /上传[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
def dm = t =~ /下载[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
def rm = t =~ /分享率[::]\s*([\d.,]+)/
def bm = t =~ /魔力[值::]*[^0-9]*([\d.,]+)/
if (um) result.upload = parseTraffic(um[0][1].replace(',','') as double, um[0][2])
if (dm) result.download = parseTraffic(dm[0][1].replace(',','') as double, dm[0][2])
if (rm) result.shareRate = rm[0][1].replace(',','') as double
if (bm) result.bonus = bm[0][1].replace(',','') as double
// 做种数
def sm = t =~ /⬆\s*(\d+)/
if (sm) result.seeding = sm[0][1] as int
return result
} catch (e) { return [message: "同步失败: ${e.message}"] }
}
Map sign(PluginContext ctx) {
if (!ctx.site.auth) return [message: "未配置认证", loginRequired: true]
try {
def conn = ctx.httpGet('/attendance.php')
def matched = conn.body() ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
return [success: matched, message: matched ? '已签到' : '失败']
} catch (e) { return [message: "签到异常: ${e.message}"] }
}
List search(PluginContext ctx, Map params) {
if (!ctx.site.auth) return []
try {
def keyword = params.keyword ?: ''
def page = (params.page ?: 0) as int
def conn = ctx.httpGet("/torrents.php?search=${URLEncoder.encode(keyword,'UTF-8')}&page=${page}")
def doc = jsoup.parse(conn.body())
def rows = []
doc.select('table.torrents tr:has(td.rowfollow):not(:has(.colhead))').each { row ->
def titleEl = utils.selectFirst(row, 'td.embedded a[href*=details]')
if (!titleEl) return
def tds = row.select('td.rowfollow')
def size = 0, seeders = 0, leechers = 0, grabs = 0
boolean hasSize = false, hasSeed = false, hasLeech = false
for (int i = 0; i < tds.size(); i++) {
def t = utils.text(tds.get(i))
if (!t) continue
if (!hasSize && (t.contains('GB')||t.contains('MB')||t.contains('TB')||t.contains('KB'))) {size=parseHtmlSize(t);hasSize=true}
else if (hasSize && !hasSeed && t ==~ /^\d+$/) {seeders=t as int;hasSeed=true}
else if (hasSeed && !hasLeech && t ==~ /^\d+$/) {leechers=t as int;hasLeech=true}
else if (hasSeed && hasLeech && t ==~ /^\d+$/) {grabs=t as int}
}
rows << [
title: utils.text(titleEl),
detailUrl: "${ctx.siteUrl}${utils.attr(titleEl,'href')}",
size: size,
seeders: seeders,
leechers: leechers,
grabs: grabs,
]
}
return rows
} catch (e) { return [] }
}
private long parseTraffic(double val, String unit) {
def m = [B:1,KB:1024,MB:1048576,GB:1073741824,TB:1099511627776]
return (val * (m[unit?.toUpperCase()] ?: 1)) as long
}
private long parseHtmlSize(String s) {
def m = s =~ /([\d.]+)\s*(TB|GB|MB|KB|B)?/
return m ? parseTraffic(m[0][1] as double, m[0][2]) : 0
}