/* * @File : 影视大全.js * @Author : opencode * @Date : 2026-07-09 * @Desc : 影视大全 (www.iysdq.tv) TVBox T4(JS) 源 —— 自包含,MacCMS shoutu45 模板 * * 站点结构: * - 服务端渲染 MacCMS(shoutu45),首页/分类/搜索/详情 HTML 直出,无防护盾。 * - 分类:/vodtype/{tid}.html * - 搜索:/vodsearch/{词}-------------.html (关键词在开头,共 13 个 '-') * - 详情:/voddetail/{id}.html * 线路 tab: .anthology-tab a.swiper-slide(按 DOM 顺序) * 集数列表: 并列的多个 .anthology-list-play,按 DOM 顺序与 tab 一一对应 * 集数链接: /vodplay/{id}-{sid}-{nid}.html * - 播放:播放页 player_aaaa.url 为明文 m3u8/mp4 直链。 * - 卡片:div.public-list-box > a.public-list-exp[href=/voddetail/xx.html][title] * 封面 img data-src;备注 span.public-list-prb。 */ var IYSDQ_VERSION = "20260709-js-1"; var HOST = "https://www.iysdq.tv"; var UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"; var CLASSES = [ { type_id: "1", type_name: "电影" }, { type_id: "2", type_name: "电视剧" }, { type_id: "3", type_name: "综艺" }, { type_id: "4", type_name: "动漫" }, { type_id: "5", type_name: "短剧" } ]; function log(msg) { try { console.log("[IYSDQ_JS " + IYSDQ_VERSION + "] " + msg); } catch (e) {} } /* ---------------- 通用工具 ---------------- */ function toStr(v) { return v === undefined || v === null ? "" : String(v); } function encode(s) { return encodeURIComponent(toStr(s)); } function fixUrl(u) { u = toStr(u).trim(); if (!u) return ""; if (u.indexOf("//") === 0) return "https:" + u; if (u.charAt(0) === "/") return HOST + u; return u; } function textClean(s) { return toStr(s) .replace(/<[^>]+>/g, "") .replace(/ /g, " ") .replace(/&/g, "&") .replace(/"/g, '"') .replace(/'/g, "'") .replace(/&#\d+;/g, "") .replace(/\s+/g, " ") .trim(); } function attr(tag, name) { var r = new RegExp(name + "\\s*=\\s*([\"'])([\\s\\S]*?)\\1", "i"); var m = r.exec(tag || ""); return m ? m[2] : ""; } function requestHtml(url) { try { log("fetch " + url); var res = req(url, { method: "GET", headers: { "User-Agent": UA, "Referer": HOST + "/", "Accept": "text/html" }, timeout: 10000 }); var body = (typeof res === "string") ? res : (res && (res.content || res.body || res.data)) || ""; return toStr(body); } catch (e) { log("fetch_error " + e); return ""; } } /* ---------------- 封面提取 ---------------- */ function pickPic(tag) { var p = attr(tag, "data-src") || attr(tag, "data-original"); if (!p || /img-bj-k\.png/i.test(p)) { var m = /]+data-src="([^"]+)"/i.exec(tag); if (m) p = m[1]; } return fixUrl(p); } /* ---------------- 列表解析 ---------------- */ // 首页/分类/搜索卡片:div.public-list-box function parseList(html) { var list = []; var seen = {}; // 每个卡片块:从 public-list-box 起,到下一个 public-list-box 或结束 var re = /
([\s\S]*?)(?=
]*title="([^"]*)"/.exec(block); if (tm) name = textClean(tm[1]); if (!name) { var tm2 = /class="time-title[^"]*"[^>]*title="([^"]*)"/.exec(block); if (tm2) name = textClean(tm2[1]); } if (!name) { var am = /alt="([^"]+?)封面图"/.exec(block) || /alt="([^"]+)"/.exec(block); if (am) name = textClean(am[1]); } var note = ""; var nm = /class="public-list-prb[^"]*"[^>]*>([\s\S]*?)<\/span>/.exec(block); if (nm) note = textClean(nm[1]); list.push({ vod_id: id, vod_name: name, vod_pic: pickPic(block), vod_remarks: note }); } return list; } /* ---------------- 接口实现 ---------------- */ function init(ext) { if (typeof ext === "string" && /^https?:\/\//.test(ext.trim())) { HOST = ext.trim().replace(/\/+$/, ""); } log("init host=" + HOST); } function home(filter) { log("home"); return JSON.stringify({ class: CLASSES }); } function homeVod() { log("homeVod"); var html = requestHtml(HOST + "/"); var list = parseList(html).slice(0, 40); return JSON.stringify({ list: list }); } function category(tid, pg, filter, extend) { tid = toStr(tid || "1"); pg = parseInt(pg || 1); if (!pg || pg < 1) pg = 1; // 站点分类页无分页参数,首页即全量;仅第 1 页返回数据 if (pg > 1) return JSON.stringify({ page: pg, pagecount: pg, limit: 40, total: 0, list: [] }); var url = HOST + "/vodtype/" + tid + ".html"; var html = requestHtml(url); var list = parseList(html); log("category tid=" + tid + " count=" + list.length); return JSON.stringify({ page: 1, pagecount: 1, limit: list.length || 40, total: list.length, list: list }); } function detail(id) { var vid = toStr(id).split(",")[0].replace(/[^\d]/g, ""); log("detail id=" + vid); var html = requestHtml(HOST + "/voddetail/" + vid + ".html"); if (!html) return JSON.stringify({ list: [] }); // 标题 var name = ""; var nm = /]*>([\s\S]*?)<\/h1>/.exec(html); if (nm) name = textClean(nm[1]); if (!name) { var tt = /([^<_\-]+)/.exec(html); if (tt) name = tt[1].trim(); } // 封面 var pic = ""; var pm = /property="og:image"\s+content="([^"]+)"/.exec(html); if (pm) pic = pm[1]; if (!pic) { var pm2 = /<img[^>]*class="[^"]*lazy[^"]*"[^>]*data-src="([^"]+)"/.exec(html); if (pm2) pic = pm2[1]; } var vod = { vod_id: vid, vod_name: name, vod_pic: fixUrl(pic) }; // 线路名(.anthology-tab 内的 a.swiper-slide,按 DOM 顺序) var froms = []; var tabStart = html.indexOf('anthology-tab'); var tabBlock = ""; if (tabStart >= 0) { var tabEnd = html.indexOf('anthology-list', tabStart); tabBlock = html.substring(tabStart, tabEnd > tabStart ? tabEnd : tabStart + 3000); } var fRe = /class="swiper-slide"[^>]*>([\s\S]*?)<\/a>/g; var fm; while ((fm = fRe.exec(tabBlock)) !== null) { var fn = textClean(fm[1]).replace(/\d+$/, "").trim(); if (fn) froms.push(fn); } // 集数 panel:并列的 .anthology-list-play var playFrom = []; var playUrl = []; var panelRe = /class="anthology-list-play[^"]*"[^>]*>([\s\S]*?)<\/ul>/g; var pm3; var idx = 0; while ((pm3 = panelRe.exec(html)) !== null) { var block = pm3[1]; var eps = []; var epRe = /<a[^>]*href="(\/vodplay\/[^"]+\.html)"[^>]*>([\s\S]*?)<\/a>/g; var em; while ((em = epRe.exec(block)) !== null) { var epUrl = em[1]; var epName = textClean(em[2]) || (eps.length + 1); eps.push(epName + "$" + epUrl); } if (eps.length) { playFrom.push(froms[idx] || ("线路" + (idx + 1))); playUrl.push(eps.join("#")); } idx++; } if (playFrom.length) { vod.vod_play_from = playFrom.join("$$$"); vod.vod_play_url = playUrl.join("$$$"); } else { vod.vod_play_from = "提示"; vod.vod_play_url = "暂无播放源$"; } return JSON.stringify({ list: [vod] }); } function play(flag, id, flags) { var pageUrl = fixUrl(id); log("play flag=" + flag + " url=" + pageUrl); var html = requestHtml(pageUrl); var url = ""; // player_aaaa = {...,"url":"https://...m3u8",...} var m = /player_\w+\s*=\s*(\{[\s\S]*?\})\s*<\/script>/.exec(html); if (m) { try { var info = JSON.parse(m[1]); url = info.url || ""; } catch (e) { log("player parse error " + e); } } // 兜底:直接搜 url 字段 / m3u8 / mp4 if (!url) { var m2 = /"url"\s*:\s*"(https?:[^"]+?\.(?:m3u8|mp4)[^"]*)"/.exec(html); if (m2) url = m2[1]; } if (!url) { var m3 = html.match(/https?:\/\/[^"'\s\\]+?\.m3u8[^"'\s\\]*/); if (m3) url = m3[0]; } if (!url) { var m4 = html.match(/https?:\/\/[^"'\s\\]+?\.mp4[^"'\s\\]*/); if (m4) url = m4[0]; } url = toStr(url).replace(/\\\//g, "/").trim(); if (url && /^https?:\/\//.test(url)) { var header = { "User-Agent": UA, "Referer": HOST + "/" }; return JSON.stringify({ parse: 0, url: url, header: header }); } return JSON.stringify({ parse: 1, url: pageUrl }); } function search(wd, quick, pg) { pg = parseInt(pg || 1); if (!pg || pg < 1) pg = 1; if (pg > 1) return JSON.stringify({ list: [], page: pg, pagecount: pg }); log("search wd=" + wd); // 关键词在开头,后接 13 个 '-' var url = HOST + "/vodsearch/" + encode(wd) + "-------------.html"; var html = requestHtml(url); var list = parseList(html); return JSON.stringify({ list: list, page: 1, pagecount: 1, limit: list.length, total: list.length }); } /* ---------------- 导出 ---------------- */ __JS_SPIDER__ = { init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search };