'use strict'; const cheerio = require('cheerio'); const animegarden = require('animegarden'); function splitOnce(text, separator) { const found = text.indexOf(separator); if (found === -1) { return [text, ""]; } const first = text.slice(0, found); const second = text.slice(found); return [first, second]; } async function fetchDmhyPage(ofetch, options = {}) { const { page = 1, retry = 5 } = options; const resp = await animegarden.retryFn(async () => { const resp2 = await ofetch(`https://share.dmhy.org/topics/list/page/${page}`); if (!resp2.ok) { throw new Error(resp2.statusText, { cause: resp2 }); } return resp2; }, retry); if (!resp.ok) { throw new Error("Failed connecting https://share.dmhy.org"); } const html = await resp.text(); const $ = cheerio.load(html); const errorNode = $(".ui-state-error"); if (errorNode.length !== 0) { throw new Error("dmhy server is down"); } const res = []; $("#topic_list tbody tr").each((_idx, row) => { const tds = $(row).find("td"); const createdAt = tds.eq(0).find("span").text().trim(); const type = tds.eq(1).text().trim(); const title = tds.eq(2).children("a").text().trim(); const href = "https://share.dmhy.org" + tds.eq(2).children("a").attr("href").trim(); const fansub = tds.eq(2).find("span.tag a"); const fansubName = fansub.text().trim(); const fansubId = fansub.attr("href")?.split("/").at(-1); const magnetFull = tds.eq(3).find("a").attr("href"); const [magnet, tracker] = splitOnce(magnetFull, "&"); const size = tds.eq(4).text().trim(); const publisher = tds.eq(8).find("a"); const publisherName = publisher.text(); const publisherId = publisher.attr("href").split("/").at(-1); const lastHref = href.split("/").at(-1); if (!lastHref) return; const matchId = /^(\d+)/.exec(lastHref); if (!matchId) return; res.push({ provider: "dmhy", providerId: matchId[1], title, href, type, magnet, tracker, size, fansub: fansubId ? { id: fansubId, name: fansubName } : void 0, publisher: { id: publisherId, name: publisherName }, createdAt }); }); return res; } async function fetchDmhyDetail(ofetch, href, options = {}) { const url = new URL(href, `https://share.dmhy.org/topics/view/`); const lastHref = url.href.split("/").at(-1); if (!lastHref) return void 0; const matchId = /^(\d+)/.exec(lastHref); if (!matchId) return void 0; const { retry = 5 } = options; const resp = await animegarden.retryFn(() => ofetch(url.href), retry); if (!resp.ok) { throw new Error("Failed connecting https://share.dmhy.org"); } const html = await resp.text(); const $ = cheerio.load(html); const errorNode = $(".ui-state-error"); if (errorNode.length !== 0) { throw new Error("dmhy server is down"); } const title = $(".topic-main>.topic-title>h3").text().trim(); if (!title) { return void 0; } const type = $(".topic-main>.topic-title li:first-child a:last-of-type").text().trim(); const size = $(".topic-main>.topic-title li:nth-child(4) span").text().trim(); const createdAt = $(".topic-main>.topic-title li:nth-child(2) span").text().trim(); const publisherAvatar = $(".topics_bk .avatar:first-child img").attr("src")?.trim() ?? "https://share.dmhy.org/images/defaultUser.png"; const publisherName = $(".topics_bk .avatar:first-child p:nth-child(2) a").text().trim(); const publisherId = $(".topics_bk .avatar:first-child p:nth-child(2) a").attr("href")?.trim().split("/").at(-1); const fansubAvatar = $(".topics_bk .avatar:nth-child(2) img").attr("src")?.trim() ?? "https://share.dmhy.org/images/defaultTeam.gif"; const fansubName = $(".topics_bk .avatar:nth-child(2) p:nth-child(2) a").text().trim(); const fansubId = $(".topics_bk .avatar:nth-child(2) p:nth-child(2) a").attr("href")?.trim().split("/").at(-1); const description = $(".topic-nfo").html()?.trim() ?? ""; const magnetUser = $("#resource-tabs #tabs-1 p:nth-child(1) a").attr("href")?.trim() ?? ""; const magnetHref = $("#a_magnet").attr("href")?.trim() ?? ""; const magnetHref2 = $("#magnet2").attr("href")?.trim() ?? ""; const magnetDdplay = $("#ddplay").attr("href")?.trim() ?? ""; let hasMoreFiles = false; const files = $(".file_list li").map((_idx, el) => { const size2 = $(el).children("span").text().trim(); const name = $(el).text().trim().replace(new RegExp(`${size2}$`), "").trim(); return { size: size2, name }; }).toArray().filter((f) => f.size !== "\u7A2E\u5B50\u53EF\u80FD\u4E0D\u5B58\u5728" && f.size !== "Bytes").filter((f) => { if (/More Than \d+ Files/.test(f.name)) { hasMoreFiles = true; } return !!f.size; }); return { provider: "dmhy", providerId: matchId[1], title, href: url.href, type, size, createdAt, publisher: { id: publisherId, name: publisherName, avatar: publisherAvatar.replace( "/images/defaultUser.png", "https://share.dmhy.org/images/defaultUser.png" ) }, fansub: fansubId !== void 0 ? { id: fansubId, name: fansubName, avatar: fansubAvatar.replace( "/images/defaultTeam.gif", "https://share.dmhy.org/images/defaultTeam.gif" ) } : void 0, description, magnet: { user: magnetUser.startsWith("https://") ? magnetUser : `https:${magnetUser}`, href: magnetHref, href2: magnetHref2, ddplay: magnetDdplay, files, hasMoreFiles } }; } const Anime = "549ef207fe682f7549f1ea90"; const Collection = "54967e14ff43b99e284d0bf7"; const Music = "549eef6ffe682f7549f1ea8b"; const Manga = "549eefebfe682f7549f1ea8c"; const TV = "549ff1db30bcfc225bf9e607"; const Other = "549ef250fe682f7549f1ea91"; const Game = "549ef015fe682f7549f1ea8d"; function getType(tags) { for (const tag of tags) { switch (tag) { case Anime: return "\u52D5\u756B"; case Collection: return "\u5B63\u5EA6\u5168\u96C6"; case Manga: return "\u6F2B\u756B"; case Music: return "\u97F3\u6A02"; case TV: return "\u65E5\u5287"; case Game: return "\u904A\u6232"; case Other: return "\u5176\u4ED6"; } } return "\u5176\u4ED6"; } const Users = /* @__PURE__ */ new Map(); const Teams = /* @__PURE__ */ new Map(); async function fetchUser(ofetch, id) { if (Users.get(id)) { return Users.get(id); } const resp = await animegarden.retryFn(async () => { const resp2 = await ofetch(`https://bangumi.moe/api/user/fetch`, { method: "POST", body: JSON.stringify({ _ids: [id] }), headers: new Headers([ [ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" ] ]) }); if (!resp2.ok) { throw new Error(resp2.statusText, { cause: resp2 }); } return resp2; }, 10); if (!resp.ok) { throw new Error("Failed connecting https://bangumi.moe/"); } const data = await resp.json(); const user = { provider: "moe", providerId: id, name: data[0].username }; Users.set(id, user); return user; } async function fetchTeam(ofetch, id) { if (Teams.get(id)) { return Teams.get(id); } const resp = await animegarden.retryFn(async () => { const resp2 = await ofetch(`https://bangumi.moe/api/team/fetch`, { method: "POST", body: JSON.stringify({ _ids: [id] }), headers: new Headers([ [ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" ] ]) }); if (!resp2.ok) { throw new Error(resp2.statusText, { cause: resp2 }); } return resp2; }, 10); if (!resp.ok) { throw new Error("Failed connecting https://bangumi.moe/"); } const data = await resp.json(); const team = { provider: "moe", providerId: id, name: data[0].name, avatar: data[0].icon }; Teams.set(id, team); return team; } const TRACKER = `&tr=https%3A%2F%2Ftr.bangumi.moe%3A9696%2Fannounce&tr=http%3A%2F%2Ftr.bangumi.moe%3A6969%2Fannounce&tr=udp%3A%2F%2Ftr.bangumi.moe%3A6969%2Fannounce&tr=http%3A%2F%2Fopen.acgtracker.com%3A1096%2Fannounce&tr=http%3A%2F%2F208.67.16.113%3A8000%2Fannounce&tr=udp%3A%2F%2F208.67.16.113%3A8000%2Fannounce&tr=http%3A%2F%2Ftracker.ktxp.com%3A6868%2Fannounce&tr=http%3A%2F%2Ftracker.ktxp.com%3A7070%2Fannounce&tr=http%3A%2F%2Ft2.popgo.org%3A7456%2Fannonce&tr=http%3A%2F%2Fbt.sc-ol.com%3A2710%2Fannounce&tr=http%3A%2F%2Fshare.camoe.cn%3A8080%2Fannounce&tr=http%3A%2F%2F61.154.116.205%3A8000%2Fannounce&tr=http%3A%2F%2Fbt.rghost.net%3A80%2Fannounce&tr=http%3A%2F%2Ftracker.openbittorrent.com%3A80%2Fannounce&tr=http%3A%2F%2Ftracker.publicbt.com%3A80%2Fannounce&tr=http%3A%2F%2Ftracker.prq.to%2Fannounce&tr=http%3A%2F%2Fopen.nyaatorrents.info%3A6544%2Fannounce`; async function fetchMoePage(ofetch, options = {}) { const { page = 1, retry = 5 } = options; const resp = await animegarden.retryFn(async () => { const resp2 = await ofetch(`https://bangumi.moe/api/torrent/page/${page}`, { headers: new Headers([ [ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" ] ]) }); if (!resp2.ok) { throw new Error(resp2.statusText, { cause: resp2 }); } return resp2; }, retry); if (!resp.ok) { throw new Error("Failed connecting https://bangumi.moe/"); } const data = await resp.json(); const result = []; for (const torrent of data?.torrents ?? []) { const user = await fetchUser(ofetch, torrent.uploader_id); const team = torrent.team_id ? await fetchTeam(ofetch, torrent.team_id) : void 0; result.push({ provider: "moe", providerId: torrent._id, title: torrent.title, href: torrent._id, magnet: torrent.magnet, tracker: TRACKER, type: getType(torrent.tag_ids), size: torrent.size, publisher: { id: torrent.uploader_id, name: user.name, avatar: team?.avatar }, fansub: team ? { id: torrent.team_id, name: team.name, avatar: team.avatar } : void 0, createdAt: torrent.publish_time }); } return result; } async function fetchMoeDetail(ofetch, id, options = {}) { const { retry = 5 } = options; const resp = await animegarden.retryFn(async () => { const resp2 = await ofetch(`https://bangumi.moe/api/torrent/fetch`, { method: "POST", body: JSON.stringify({ _id: id }) }); if (!resp2.ok) { throw new Error(resp2.statusText, { cause: resp2 }); } return resp2; }, retry); if (!resp.ok) { throw new Error("Failed connecting https://bangumi.moe/"); } const torrent = await resp.json(); const user = await fetchUser(ofetch, torrent.uploader_id); const team = await fetchTeam(ofetch, torrent.team_id); return { provider: "moe", providerId: torrent._id, title: torrent.title, href: torrent._id, description: torrent.introduction, magnet: { user: "", href: torrent.magnet + TRACKER, href2: torrent.magnet, ddplay: "", files: torrent.content.map((t) => ({ name: t[0], size: t[1] })), hasMoreFiles: false }, type: getType(torrent.tag_ids), size: torrent.size, publisher: { id: torrent.uploader_id, name: user.name, avatar: team.avatar }, fansub: { id: torrent.team_id, name: team.name, avatar: team.avatar }, createdAt: torrent.publish_time }; } exports.fetchDmhyDetail = fetchDmhyDetail; exports.fetchDmhyPage = fetchDmhyPage; exports.fetchMoeDetail = fetchMoeDetail; exports.fetchMoePage = fetchMoePage;