114 lines
4.8 KiB
Groovy
114 lines
4.8 KiB
Groovy
// UNIT3D 站点默认脚本
|
||
// 适用: BLU / TL / ACM 等 UNIT3D 架构站点(HTML + 可选 API)
|
||
// 覆盖: 搜索 / 种子详情 / 首页
|
||
|
||
Map parse(String content, String url) {
|
||
def doc = jsoup.parse(content)
|
||
|
||
if (url.contains('/torrents') && !url.contains('/torrents/')) return parseSearch(doc)
|
||
if (url.contains('/torrents/') && url.contains('/')) return parseDetail(doc)
|
||
|
||
// 尝试 JSON API
|
||
if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
|
||
try {
|
||
def root = json.readValue(content, Map)
|
||
if (root?.data) return parseApiData(root?.data, url)
|
||
} catch (e) {}
|
||
}
|
||
|
||
if (url.contains('/torrentrss.php') || url.contains('/rss')) return parseRss()
|
||
|
||
return [pageName: '未知页面', message: "未配置: ${url}"]
|
||
}
|
||
|
||
private Map parseRss() {
|
||
return [pageName: 'RSS', message: 'RSS 内容由 Java 层解析']
|
||
}
|
||
|
||
private Map parseApiData(d, url) {
|
||
if (url.contains('/user') || url.contains('/profile'))
|
||
return [pageName: '用户', fields: [username: d?.username, uploaded: d?.uploaded?.toString(), downloaded: d?.downloaded?.toString()]]
|
||
return [pageName: 'API数据', fields: d?.subMap(d.keySet().take(10))]
|
||
}
|
||
|
||
private Map parseSearch(doc) {
|
||
def rows = []
|
||
doc.select('table.table tbody tr, .torrent-row, div.torrent').each { row ->
|
||
def titleEl = utils.selectFirst(row, 'a[href*=torrents]')
|
||
def tds = row.select('td')
|
||
def size = ''
|
||
for (int i = 0; i < tds.size(); i++) {
|
||
def t = utils.text(tds.get(i))
|
||
if (t =~ /[\d.]+\s*(GB|MB|TB)/) { size = t; break }
|
||
}
|
||
rows << [
|
||
title: utils.text(titleEl),
|
||
detailUrl: utils.attr(titleEl, 'href'),
|
||
size: size,
|
||
seeders: utils.text(utils.selectFirst(row, 'td:nth-child(5), td:has(.text-success)')),
|
||
leechers: utils.text(utils.selectFirst(row, 'td:nth-child(6), td:has(.text-danger)')),
|
||
uploadDate: utils.text(utils.selectFirst(row, 'td:nth-child(4), time')),
|
||
]
|
||
}
|
||
return [pageName: '搜索', extracted: rows, message: "共 ${rows.size()} 条"]
|
||
}
|
||
|
||
private Map parseDetail(doc) {
|
||
return [pageName: '种子详情', fields: [
|
||
title: utils.text(utils.selectFirst(doc, 'h1, .torrent-title, .meta-title')),
|
||
size: utils.text(utils.selectFirst(doc, 'td:contains(Size) + td, .torrent-size')),
|
||
seeders: utils.text(utils.selectFirst(doc, 'td:contains(Seeders) + td, .seeders-count')),
|
||
leechers: utils.text(utils.selectFirst(doc, 'td:contains(Leechers) + td, .leechers-count')),
|
||
description:utils.text(utils.selectFirst(doc, '.torrent-description, #description')).take(200),
|
||
]]
|
||
}
|
||
|
||
Map sync(PluginContext ctx) {
|
||
if (!ctx.site.auth) return [message: "未配置认证", loginRequired: true]
|
||
try {
|
||
def conn = ctx.httpGet('/')
|
||
def doc = jsoup.parse(conn.body())
|
||
def upEl = utils.selectFirst(doc, '.user-uploaded, span:contains(Upload), dt:contains(Upload) + dd')
|
||
def dlEl = utils.selectFirst(doc, '.user-downloaded, span:contains(Download), dt:contains(Download) + dd')
|
||
def ratioEl = utils.selectFirst(doc, '.user-ratio, span:contains(Ratio), dt:contains(Ratio) + dd')
|
||
def result = [:]
|
||
if (upEl) result.upload = parseHtmlSize(utils.text(upEl))
|
||
if (dlEl) result.download = parseHtmlSize(utils.text(dlEl))
|
||
if (ratioEl) result.shareRate = (utils.text(ratioEl) =~ /([\d.]+)/)[0][1] as double
|
||
return result
|
||
} catch (e) { return [message: "同步失败: ${e.message}"] }
|
||
}
|
||
|
||
Map sign(PluginContext ctx) {
|
||
return [message: "UNIT3D 不支持签到"]
|
||
}
|
||
|
||
List search(PluginContext ctx, Map params) {
|
||
if (!ctx.site.auth) return []
|
||
try {
|
||
def keyword = params.keyword ?: ''
|
||
def conn = ctx.httpGet("/torrents?search=${URLEncoder.encode(keyword,'UTF-8')}")
|
||
def doc = jsoup.parse(conn.body())
|
||
def rows = []
|
||
doc.select('table.table tbody tr, .torrent-row').each { row ->
|
||
def titleEl = utils.selectFirst(row, 'a[href*=torrents]')
|
||
rows << [
|
||
title: utils.text(titleEl),
|
||
detailUrl: "${ctx.siteUrl}${utils.attr(titleEl,'href')}",
|
||
size: parseHtmlSize(utils.text(utils.selectFirst(row, 'td:nth-child(4)'))) ?: 0,
|
||
seeders: utils.toInt(utils.text(utils.selectFirst(row, 'td:nth-child(5)'))) ?: 0,
|
||
leechers: utils.toInt(utils.text(utils.selectFirst(row, 'td:nth-child(6)'))) ?: 0,
|
||
]
|
||
}
|
||
return rows
|
||
} catch (e) { return [] }
|
||
}
|
||
|
||
private long parseHtmlSize(String s) {
|
||
if (!s) return 0
|
||
def m = s =~ /([\d.]+)\s*(TB|GB|MB|KB|B)?/
|
||
if (!m) return 0
|
||
def m2 = [B:1,KB:1024,MB:1048576,GB:1073741824,TB:1099511627776]
|
||
return ((m[0][1] as double) * (m2[m[0][2]?.toUpperCase()] ?: 1)) as long
|
||
}
|