▶ OmoFun 动漫(发布页多线路)

https://fb.omoo.tv/

nuo-an (15232)11/05 21:03

发布页 https://fb.omoo.tv/ 动态取 4 条线路;登录面板可换发布页/一键取线/并发测速/逐线路切换/检查状态。站点免登录;搜索/分类页偶发 MACCMS「系统安全验证」,loginCheckJs 自动 POST verify_check 过盾(本地时间戳 XOR 生成 token)。正文现场请求 art.v2player.top 播放器页,AES-CBC/PKCS7 解密出 mp4 直链,bookSourceType=4 直接播放。搜索链路 3 秒节流,不设全局限速;兼容 Legado Team / legado-E。
二维码导入
{
    "bookSourceComment": "发布页 https:\/\/fb.omoo.tv\/ 动态取 4 条线路;登录面板可换发布页\/一键取线\/并发测速\/逐线路切换\/检查状态。站点免登录;搜索\/分类页偶发 MACCMS「系统安全验证」,loginCheckJs 自动 POST verify_check 过盾(本地时间戳 XOR 生成 token)。正文现场请求 art.v2player.top 播放器页,AES-CBC\/PKCS7 解密出 mp4 直链,bookSourceType=4 直接播放。搜索链路 3 秒节流,不设全局限速;兼容 Legado Team \/ legado-E。",
    "bookSourceGroup": "AI,动漫",
    "bookSourceName": "▶ OmoFun 动漫(发布页多线路)",
    "bookSourceType": 4,
    "bookSourceUrl": "https:\/\/fb.omoo.tv\/",
    "bookUrlPattern": "https?:\/\/[^\/]+\/anime\/[0-9a-f]{24}\\.html",
    "customButton": false,
    "customOrder": 0,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "eventListener": false,
    "exploreUrl": "@js:(function(){var line=OM_line(source);var rows=[{title:'📺 首页推荐',url:line+'\/'},{title:'🗓 周更表',url:line+'\/week\/'},{title:'🔥 今日热播',url:line+'\/dayhot\/'},{title:'🌸 新番表',url:line+'\/topic\/'},{title:'🇯🇵 日漫',url:line+'\/type\/1-{{page}}.html'},{title:'🇨🇳 国漫',url:line+'\/type\/2-{{page}}.html'},{title:'🎬 动画',url:line+'\/type\/5-{{page}}.html'},{title:'🎞 剧场',url:line+'\/type\/24-{{page}}.html'},{title:'🦸 特摄',url:line+'\/type\/4-{{page}}.html'},{title:'🇺🇸 美漫',url:line+'\/type\/3-{{page}}.html'}];return JSON.stringify(rows);})()",
    "header": "{\"User-Agent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/131.0.0.0 Safari\/537.36\", \"Referer\": \"https:\/\/www.omofuns.com\/\"}",
    "jsLib": "\n\/\/ ===================== OmoFun 公用工具 =====================\nvar OM_UA = \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/131.0.0.0 Safari\/537.36\";\nvar OM_PUB = \"https:\/\/fb.omoo.tv\/\";\nvar OM_DEFAULT = \"https:\/\/www.omofuns.com\";\nvar OM_PLAYER = \"https:\/\/art.v2player.top:8989\/player\/?\";\nvar OM_AESKEY = \"ABABEF777999CCCD\";\nvar OM_LINES = [\n  \"https:\/\/www.omofuns.com\", \"https:\/\/cn.233dm.com\",\n  \"https:\/\/cn.211dm.com\", \"https:\/\/cn.agekkkk.com\"\n];\nvar OM_LINE_NAMES = [\"omofuns.com\", \"cn.233dm.com\", \"cn.211dm.com\", \"cn.agekkkk.com\"];\n\n\/\/ JsNull 三态归一:E 版 source.get 未设置返回 truthy 对象\nfunction OM_g(v) {\n  var s = String(v === undefined || v === null ? '' : v);\n  if (s === '' || s === 'null' || s === 'undefined' || s === 'JsNull' || s === '[object Object]') return '';\n  return s;\n}\nfunction OM_line(sv) {\n  var v = '';\n  try { v = OM_g(sv.get('om_line')); } catch (e) { }\n  if (v.indexOf('http') !== 0) v = OM_DEFAULT;\n  return v.replace(\/\\\/+$\/, '');\n}\nfunction OM_pub(sv) {\n  var v = '';\n  try { v = OM_g(sv.get('om_pub')); } catch (e) { }\n  if (v.indexOf('http') !== 0) v = OM_PUB;\n  return v.replace(\/\\\/+$\/, '');\n}\nfunction OM_toast(jv, msg) {\n  try { jv.toast(msg); return; } catch (e) { }\n  try { jv.longToast(msg); } catch (e2) { }\n}\n\/\/ 纯 JS Latin-1 base64(Rhino 里 java.base64Encode(byte[]) 会被字符串化成 [B@xxx)\nfunction OM_b64(s) {\n  var t = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\/=', o = '';\n  for (var i = 0; i < s.length; i += 3) {\n    var a = s.charCodeAt(i) & 255;\n    var b = i + 1 < s.length ? s.charCodeAt(i + 1) & 255 : null;\n    var c = i + 2 < s.length ? s.charCodeAt(i + 2) & 255 : null;\n    var n = (a << 16) | ((b || 0) << 8) | (c || 0);\n    o += t.charAt((n >> 18) & 63) + t.charAt((n >> 12) & 63)\n      + (b === null ? '=' : t.charAt((n >> 6) & 63))\n      + (c === null ? '=' : t.charAt(n & 63));\n  }\n  return o;\n}\n\/\/ MACCMS「系统安全验证」token:时间戳 XOR 16字节固定密钥 → Latin-1 → base64\nfunction OM_token() {\n  var key = [0x4e, 0x3f, 0xa9, 0xc2, 0x12, 0x7d, 0x88, 0xef,\n             0x55, 0xaa, 0x0b, 0xcd, 0xde, 0xad, 0xbe, 0xef];\n  var ts = String(Date.now()), p = '';\n  for (var i = 0; i < ts.length; i++) p += String.fromCharCode(ts.charCodeAt(i) ^ key[i % 16]);\n  return OM_b64(p);\n}\n\/\/ 过盾:POST verify_check 种 Cookie。type 取 search\/show;成功后同域全站放行。\nfunction OM_pass(jv, line, type) {\n  var t = type || 'search';\n  var u = line.replace(\/\\\/+$\/, '') + '\/index.php\/ajax\/verify_check?type=' + t;\n  var tok = OM_token();\n  var hd = {\n    'Content-Type': 'application\/x-www-form-urlencoded',\n    'Referer': line.replace(\/\\\/+$\/, '') + '\/',\n    'X-Requested-With': 'XMLHttpRequest',\n    'User-Agent': OM_UA,\n    'Origin': line.replace(\/\\\/+$\/, '')\n  };\n  try {\n    var r = jv.post(u, 'i=' + encodeURIComponent(tok), hd);\n    var b = String(r.body());\n    return b.indexOf('\"code\":1') >= 0 || b.indexOf('\"code\": 1') >= 0 || \/\"msg\"\\s*:\\s*\"ok\"\/.test(b);\n  } catch (e) { return false; }\n}\n\/\/ 发布页线路解析:eval 混淆脚本取 encodedLinks → base64 解码\nfunction OM_fetchLines(jv, pubUrl) {\n  var h = String(jv.ajax(pubUrl));\n  var re = \/<script>([\\w\\W]*?)<\\\/script>\/g, m, code = '';\n  while ((m = re.exec(h)) !== null) code = m[1];\n  if (!code || code.indexOf('encodedLinks') < 0) return { err: '发布页结构变化,未找到线路数组', html: h.slice(0, 200) };\n  var window = this, document = {};\n  var arr;\n  try { eval(code); arr = encodedLinks; } catch (e) { return { err: '发布页脚本执行失败:' + e }; }\n  if (!arr || !arr.length) return { err: '发布页线路数组为空' };\n  var out = [], seen = {};\n  for (var i = 0; i < arr.length; i++) {\n    var parts = String(arr[i]).split('|'), u = '';\n    try { u = String(jv.base64Decode(parts[0])); } catch (e2) { }\n    u = u.replace(\/\\\/+$\/, '');\n    if (u.indexOf('http') !== 0 || seen[u]) continue;\n    seen[u] = 1;\n    out.push({ u: u, n: parts[1] || u.replace(\/^https?:\\\/\\\/\/, '') });\n  }\n  return { lines: out };\n}\n\/\/ 线路探活三判据:code 200-399 + body 足够长 + 非异常堆栈串\nfunction OM_probeOne(jv, u) {\n  var t = Date.now();\n  try {\n    var r = jv.ajax(u + '\/');\n    var raw = r && r.raw && typeof r.raw === 'function' ? r.raw() : null;\n    var code = 0;\n    try { code = raw ? raw.code() : 0; } catch (e0) { }\n    var b = String(r.body ? r.body() : r);\n    var ok = (code >= 200 && code < 400 || code === 0) && b.length > 20000\n      && !(b.length < 3000 && \/Exception|Error|堆栈\/.test(b));\n    return { u: u, ms: Date.now() - t, ok: ok, len: b.length };\n  } catch (e) { return { u: u, ms: Date.now() - t, ok: false, err: String(e) }; }\n}\n\/\/ 保存线路池:每个按钮一个键 om_s1..om_s8 + 池数量 om_sn\nfunction OM_saveLines(sv, lines) {\n  var n = Math.min(lines.length, 8);\n  try {\n    sv.put('om_sn', String(n));\n    for (var i = 0; i < 8; i++) sv.put('om_s' + (i + 1), i < n ? lines[i].u : '');\n    sv.put('om_lines', JSON.stringify(lines.slice(0, n)));\n    sv.put('om_line', lines[0].u);\n  } catch (e) { }\n  return n;\n}\nfunction OM_loadLines(sv) {\n  var arr = [];\n  try {\n    var raw = OM_g(sv.get('om_lines'));\n    if (raw) { var p = JSON.parse(raw); if (p && p.length) arr = p; }\n  } catch (e) { }\n  if (!arr.length) {\n    var n = 0;\n    try { n = parseInt(OM_g(sv.get('om_sn')), 10) || 0; } catch (e2) { }\n    for (var i = 1; i <= n; i++) {\n      var u = ''; try { u = OM_g(sv.get('om_s' + i)); } catch (e3) { }\n      if (u.indexOf('http') === 0) arr.push({ u: u, n: u.replace(\/^https?:\\\/\\\/\/, '') });\n    }\n  }\n  if (!arr.length) for (var k = 0; k < OM_LINES.length; k++) arr.push({ u: OM_LINES[k], n: OM_LINE_NAMES[k] });\n  return arr;\n}\nfunction OM_speedKey(i) { return 'om_sp' + (i + 1); }\nfunction OM_lineLabel(sv, i) {\n  var arr = OM_loadLines(sv);\n  if (i >= arr.length) return '(空位 ' + (i + 1) + ')';\n  var sp = ''; try { sp = OM_g(sv.get(OM_speedKey(i))); } catch (e) { }\n  var mark = sp.indexOf('⚡') === 0 ? '⚡' : '';\n  return (mark ? '⚡ ' : '') + arr[i].n + ' ' + (sp || '未测速');\n}\n\/\/ 站点的书详情相对 URL 列表规则公共选择器\nvar OM_LIST = 'ul.BIaC_ACJijja > li';\n",
    "lastUpdateTime": "1793883835574",
    "loginCheckJs": "(function(){try{var b='';try{b=String(result&&result.body?result.body():result);}catch(e){b=String(result||'');}if(b.length<8000&&(b.indexOf('系统安全验证')>=0||b.indexOf('verify_check')>=0)){OM_toast(java,'⏳ 站点要求安全验证,正在自动过盾…');var ok=OM_pass(java,OM_line(source),'search');OM_toast(java,ok?'✅ 过盾成功,请重试刚才的操作':'⚠ 过盾未成功,可在书源登录面板切换线路');}}catch(err){}return result;})()",
    "loginUi": "[{\"key\": \"om_pub\", \"type\": \"text\", \"name\": \"发布页网址\", \"hint\": \"默认 https:\/\/fb.omoo.tv\/ ,可改成镜像发布页\", \"defaultValue\": \"https:\/\/fb.omoo.tv\/\"}, {\"key\": \"om_status\", \"type\": \"text\", \"name\": \"当前状态\", \"viewName\": \"OM_status(source)\", \"hint\": \"当前线路 \/ 发布页 \/ 线路数\"}, {\"key\": \"om_btn_get\", \"type\": \"button\", \"name\": \"🔄 一键从发布页获取网址路线\", \"action\": \"OM_actGetLines(java,source)\"}, {\"key\": \"om_btn_speed\", \"type\": \"button\", \"name\": \"⚡ 测试延时(测得 ms 写入各线路按钮)\", \"action\": \"OM_actSpeed(java,source)\"}, {\"key\": \"om_btn_line1\", \"type\": \"button\", \"name\": \"线路 1\", \"viewName\": \"OM_lineLabel(source,0)\", \"action\": \"OM_actSetLine(java,source,0)\"}, {\"key\": \"om_btn_line2\", \"type\": \"button\", \"name\": \"线路 2\", \"viewName\": \"OM_lineLabel(source,1)\", \"action\": \"OM_actSetLine(java,source,1)\"}, {\"key\": \"om_btn_line3\", \"type\": \"button\", \"name\": \"线路 3\", \"viewName\": \"OM_lineLabel(source,2)\", \"action\": \"OM_actSetLine(java,source,2)\"}, {\"key\": \"om_btn_line4\", \"type\": \"button\", \"name\": \"线路 4\", \"viewName\": \"OM_lineLabel(source,3)\", \"action\": \"OM_actSetLine(java,source,3)\"}, {\"key\": \"om_btn_line5\", \"type\": \"button\", \"name\": \"线路 5\", \"viewName\": \"OM_lineLabel(source,4)\", \"action\": \"OM_actSetLine(java,source,4)\"}, {\"key\": \"om_btn_line6\", \"type\": \"button\", \"name\": \"线路 6\", \"viewName\": \"OM_lineLabel(source,5)\", \"action\": \"OM_actSetLine(java,source,5)\"}, {\"key\": \"om_btn_line7\", \"type\": \"button\", \"name\": \"线路 7\", \"viewName\": \"OM_lineLabel(source,6)\", \"action\": \"OM_actSetLine(java,source,6)\"}, {\"key\": \"om_btn_line8\", \"type\": \"button\", \"name\": \"线路 8\", \"viewName\": \"OM_lineLabel(source,7)\", \"action\": \"OM_actSetLine(java,source,7)\"}, {\"key\": \"om_btn_check\", \"type\": \"button\", \"name\": \"✅ 检查登录状态(免登录\/过盾状态)\", \"action\": \"OM_actCheck(java,source)\"}]",
    "loginUrl": "<js>\n\n\/\/ ============ 登录面板(线路运维 + 检查登录状态) ============\n\/\/ 本面板仅做线路管理,站点本身无需登录即可搜索\/观看。\n\/\/ login() 必须存在,否则点右上角 √ 会抛 Function login not implements。\nfunction login() { }\n\n\/\/ 当前状态概览(viewName 动态文本)\nfunction OM_status(sv) {\n  var arr = OM_loadLines(sv);\n  var cur = OM_line(sv), pub = OM_pub(sv);\n  var idx = -1;\n  for (var i = 0; i < arr.length; i++) if (arr[i].u === cur) idx = i;\n  return '当前线路:' + cur.replace(\/^https?:\\\/\\\/\/, '')\n    + (idx >= 0 ? '(第' + (idx + 1) + '条)' : '(自定义)')\n    + ' | 发布页:' + pub.replace(\/^https?:\\\/\\\/\/, '')\n    + ' | 共 ' + arr.length + ' 条线路';\n}\n\n\/\/ 按钮 1:一键从发布页获取网址路线\nfunction OM_actGetLines(jv, sv) {\n  OM_toast(jv, '⏳ 正在读取发布页,请稍候…');\n  var res = OM_fetchLines(jv, OM_pub(sv));\n  if (res.err) { OM_toast(jv, '❌ 获取线路失败:' + res.err); return; }\n  var n = OM_saveLines(sv, res.lines);\n  for (var i = 0; i < 8; i++) { try { sv.put(OM_speedKey(i), ''); } catch (e) { } }\n  OM_toast(jv, '✅ 已从发布页获取 ' + n + ' 条线路,默认切到第 1 条;请点「测试延时」测速');\n  try { jv.reLoginView(); } catch (e2) { }\n}\n\n\/\/ 按钮 2:测试延时(并发 ajaxAll,结果写回各线路按钮)\nfunction OM_actSpeed(jv, sv) {\n  var arr = OM_loadLines(sv);\n  if (!arr.length) { OM_toast(jv, '⚠ 线路池为空,请先点「一键从发布页获取网址路线」'); return; }\n  OM_toast(jv, '⏳ 正在并发测速 ' + arr.length + ' 条线路,请稍候…');\n  var urls = [];\n  for (var i = 0; i < arr.length; i++) urls.push(arr[i].u + '\/');\n  var results = null;\n  try {\n    \/\/ 双参 ajaxAll(urls,true) 两版通吃;单参重载兜底\n    try { results = jv.ajaxAll(urls, true); } catch (e0) { results = jv.ajaxAll(urls); }\n  } catch (e1) { results = null; }\n  var out = [];\n  for (var k = 0; k < arr.length; k++) {\n    var ms = -1, ok = false;\n    if (results && results.length > k) {\n      var one = results[k];\n      try {\n        var b = String(one.body ? one.body() : one);\n        var raw = one && one.raw && typeof one.raw === 'function' ? one.raw() : null;\n        var code = 0; try { code = raw ? raw.code() : 0; } catch (ex) { }\n        ms = -1;\n        ok = (code >= 200 && code < 400 || code === 0) && b.length > 20000\n          && !(b.length < 3000 && \/Exception|Error\/.test(b));\n      } catch (e2) { }\n    }\n    if (ms < 0 || !ok) { var p = OM_probeOne(jv, arr[k].u); ms = p.ms; ok = p.ok; }\n    out.push({ u: arr[k].u, ms: ms, ok: ok });\n  }\n  var best = -1, bestMs = 1e12;\n  for (var q = 0; q < out.length; q++) {\n    var txt = out[q].ok ? (out[q].ms + 'ms') : '超时\/异常';\n    if (out[q].ok && out[q].ms < bestMs) { bestMs = out[q].ms; best = q; }\n    try { sv.put(OM_speedKey(q), txt); } catch (e3) { }\n  }\n  if (best >= 0) {\n    try { sv.put(OM_speedKey(best), '⚡' + bestMs + 'ms'); } catch (e4) { }\n    try { sv.put('om_line', out[best].u); } catch (e5) { }\n    OM_toast(jv, '✅ 测速完成!最快 ' + arr[best].n + ' ' + bestMs + 'ms,已自动切换;延时已写入各线路按钮');\n  } else {\n    OM_toast(jv, '⚠ 测速完成但无可用线路,请检查网络或点「一键从发布页获取网址路线」更新');\n  }\n  try { jv.reLoginView(); } catch (e6) { OM_toast(jv, 'ℹ 若按钮文字未刷新,请关闭后重新打开本面板'); }\n}\n\n\/\/ 线路切换按钮(各网站一个)\nfunction OM_actSetLine(jv, sv, idx) {\n  var arr = OM_loadLines(sv);\n  if (idx >= arr.length) { OM_toast(jv, '⚠ 该线路位为空,请先获取线路'); return; }\n  OM_toast(jv, '⏳ 正在切换到 ' + arr[idx].n + ' …');\n  try { sv.put('om_line', arr[idx].u); } catch (e) { }\n  var p = OM_probeOne(jv, arr[idx].u);\n  if (p.ok) OM_toast(jv, '✅ 已切换到 ' + arr[idx].n + '(' + p.ms + 'ms),线路可用');\n  else OM_toast(jv, '⚠ 已切换到 ' + arr[idx].n + ',但探活未通过(' + p.ms + 'ms);可再点测速或换其他线路');\n  try { jv.reLoginView(); } catch (e2) { }\n}\n\n\/\/ 按钮:检查登录状态(站点免登录,这里检查过盾 Cookie 与线路可用性)\nfunction OM_actCheck(jv, sv) {\n  OM_toast(jv, '⏳ 正在检查线路与过盾状态…');\n  var line = OM_line(sv);\n  var h = '';\n  try { h = String(jv.ajax(line + '\/')); } catch (e) { }\n  var blocked = h.indexOf('系统安全验证') >= 0 || h.indexOf('verify_check') >= 0;\n  if (blocked) {\n    OM_toast(jv, '⚠ 检测到安全验证页,正在自动过盾…');\n    var ok = OM_pass(jv, line, 'search');\n    OM_toast(jv, ok ? '✅ 过盾成功,Cookie 已刷新,可正常搜索\/观看' : '❌ 过盾失败,请切换线路后重试');\n  } else if (h.length > 20000) {\n    OM_toast(jv, '✅ 状态正常:免登录,线路 ' + line.replace(\/^https?:\\\/\\\/\/, '') + ' 可用,无验证拦截');\n  } else {\n    OM_toast(jv, '⚠ 线路响应异常(' + h.length + ' 字节),建议点「测试延时」或换线路');\n  }\n  try { jv.reLoginView(); } catch (e2) { }\n}\n\n<\/js>",
    "respondTime": 180000,
    "ruleBookInfo": {
        "author": "$.a",
        "coverUrl": "$.c",
        "init": "@js:\n(function(){\n  var h=String(result||'');\n  var J=Packages.org.jsoup.Jsoup, d=J.parse(h);\n  function tx(sel){ var e=d.select(sel).first(); return e?String(e.text()).trim():''; }\n  var name=tx('h1.title');\n  var pic=''; var im=d.select('a.pic img').first();\n  if(im) pic=String(im.attr('data-original')||im.attr('src')||'').trim();\n  if(pic && pic.indexOf('http')!==0 && pic.indexOf('\/\/')!==0) pic='https:\/\/as.cfhls.top\/'+pic.replace(\/^\\\/+\/,'');\n  if(pic.indexOf('\/\/')===0) pic='https:'+pic;\n  var meta=tx('p.data.hidden-xs');\n  var status=''; var ps=d.select('p.data');\n  for(var i=0;i<ps.size();i++){var t=String(ps.get(i).text());if(t.indexOf('状态')===0){status=t.replace(\/^状态[::]\\s*\/,'');break;}}\n  var intro=tx('.detail-content')||tx('.detail-sketch');\n  var o={\n    a:'OmoFun', c:pic, k:meta.replace(\/^类型[::]\\s*\/,'').replace(\/\\s*\\\/\\s*\/g,'·'),\n    w:status, t:status, i:intro, n:name, tocUrl:String(baseUrl||'')\n  };\n  return JSON.stringify(o);\n})()\n",
        "intro": "$.i",
        "kind": "$.k",
        "lastChapter": "$.t",
        "name": "$.n",
        "tocUrl": "$.tocUrl",
        "wordCount": "$.w"
    },
    "ruleContent": {
        "content": "@js:\n(function(){\n  \/\/ Legado 已把播放页 HTML 放进 result,直接用(避免重复请求触发风控)\n  var page=String(result||'');\n  if(page.length<3000 && (page.indexOf('系统安全验证')>=0||page.indexOf('verify_check')>=0)){\n    try{ OM_pass(java,OM_line(source),'search'); page=String(java.ajax(String(baseUrl||''))); }catch(e1){ }\n  }\n  \/\/ player_aaaa 是含嵌套 vod_data 的内联 JSON,贪婪正则会提前截断 → 括号配平提取\n  var i=page.indexOf('var player_aaaa=');\n  if(i<0) return '⚠ 未找到播放数据(页面结构可能已变更,或该集已下架)';\n  var st=page.indexOf('{',i), dep=0, end=-1, inStr=false, q='';\n  if(st<0) return '⚠ 播放数据起始符缺失';\n  for(var k=st;k<page.length;k++){\n    var c=page.charAt(k);\n    if(inStr){ if(c==='\\\\'){k++;continue;} if(c===q)inStr=false; continue; }\n    if(c==='\"'||c===\"'\"){ inStr=true; q=c; continue; }\n    if(c==='{')dep++; else if(c==='}'){ dep--; if(dep===0){ end=k+1; break; } }\n  }\n  if(end<0) return '⚠ 播放数据括号不配平';\n  var pd=null;\n  try{ pd=JSON.parse(page.slice(st,end)); }catch(e2){ return '⚠ 播放数据解析失败:'+e2; }\n  var rawUrl=String(pd.url||'');\n  if(String(pd.encrypt)==='1'){ try{ rawUrl=unescape(rawUrl); }catch(e3){ } }\n  else if(String(pd.encrypt)==='2'){ try{ rawUrl=String(java.base64Decode(rawUrl)); }catch(e4){ } }\n  if(!rawUrl) return '⚠ 该集暂无播放地址';\n  \/\/ 构造第三方播放器页参数(与 \/static\/player\/mp4.js 一致)\n  var dmid='';\n  try{ var dm=\/\"dmid\"\\s*:\\s*\"?(\\d+)\"?\/.exec(page); if(dm)dmid=dm[1]; }catch(e5){ }\n  if(!dmid)dmid=String(pd.id||'');\n  var pic=String(pd.vod_data&&pd.vod_data.vod_pic||'');\n  var picHost=pic.indexOf('http')===0?'':'https:\/\/as.cfhls.top\/';\n  var line=OM_line(source);\n  var qs=[\n    'url='+encodeURIComponent(rawUrl),\n    'dmid='+encodeURIComponent(dmid),\n    'next='+encodeURIComponent(String(pd.link_next||'')),\n    'name='+encodeURIComponent(String(pd.vod_data&&pd.vod_data.vod_name||'')),\n    'sid='+encodeURIComponent(String(pd.sid||'1')),\n    'nid='+encodeURIComponent(String(pd.nid||'1')),\n    'ph='+encodeURIComponent(picHost),\n    'h='+encodeURIComponent(line),\n    'pic='+encodeURIComponent(pic)\n  ];\n  var purl=OM_PLAYER+qs.join('&');\n  \/\/ 第三方播放器高峰期偶发返回极短空页(曾见 16 字节 <!DOCTYPE html>)\n  \/\/ → 3 次重试,每次换 ts 破缓存;仍失败给可读提示\n  var playerHtml='', mm=null;\n  for(var retry=0;retry<3;retry++){\n    var tryUrl=purl+(retry?'&_r='+Date.now()+'_'+retry:'');\n    try{ playerHtml=String(java.ajax(tryUrl)); }catch(e6){ playerHtml=''; }\n    mm=\/playData\\('([^']+)','([^']+)'\\)\/.exec(playerHtml);\n    if(mm) break;\n    try{ Packages.java.lang.Thread.sleep(400); }catch(eSleep){ }\n  }\n  if(!mm) return '⚠ 播放器解析繁忙(已重试 3 次),请稍后重试或在登录面板切换线路';\n  var cipher=mm[1], ivHex=mm[2];\n  try{\n    var keyBytes=java.strToBytes(OM_AESKEY);\n    var ivBytes=java.hexDecodeToByteArray(ivHex);\n    var c=java.createSymmetricCrypto('AES\/CBC\/PKCS7Padding',keyBytes,ivBytes);\n    var u=String(c.decryptStr(cipher)).trim();\n    \/\/ 站方偶发把协议头写成 org.h:\/\/ 之类的伪装,统一还原\n    if(u.indexOf('org.h:\/\/')===0) u='https:\/\/'+u.slice(8);\n    if(u.indexOf('http')!==0) return '⚠ 解析出的地址异常:'+u.slice(0,120);\n    return u;\n  }catch(e7){ return '⚠ 播放地址解密失败:'+e7; }\n})()\n"
    },
    "ruleExplore": {
        "bookList": "@js:\n(function(){\n  var h=String(result||'');\n  if(h.length<100)return [];\n  var J=Packages.org.jsoup.Jsoup,d=J.parse(h);\n  var as=d.select('a[href*=\/anime\/]');\n  var out=[],seen={};\n  for(var i=0;i<as.size();i++){\n    var a=as.get(i);\n    var u=String(a.attr('href')||'');\n    var pic=String(a.attr('data-original')||'');\n    var n=String(a.attr('title')||'').trim();\n    if(!u||!pic||!n||seen[u])continue;\n    seen[u]=1;\n    var kind='',last='';\n    var li=a.closest('li');\n    if(li){var bs=li.select('span b');if(bs.size()>=2){kind=String(bs.get(0).text());last=String(bs.get(bs.size()-1).text());}}\n    out.push(JSON.stringify({name:n,bookUrl:u,coverUrl:pic,kind:kind,lastChapter:last}));\n  }\n  \/\/ ★E 版 getElements 是 `result as List<Any>`(无 NativeArray 分支),\n  \/\/   返回 Rhino 原生数组会 ClassCastException → TocEmptyException\/列表空。\n  \/\/   必须返回 Java java.util.ArrayList,两版通吃。\n  var list=new Packages.java.util.ArrayList();\n  for(var j=0;j<out.length;j++) list.add(out[j]);\n  return list;\n})()\n",
        "bookUrl": "$.bookUrl",
        "coverUrl": "$.coverUrl",
        "kind": "$.kind",
        "lastChapter": "$.lastChapter",
        "name": "$.name"
    },
    "ruleSearch": {
        "bookList": "@js:\n(function(){\n  var h=String(result||'');\n  if(h.length<100)return [];\n  var J=Packages.org.jsoup.Jsoup,d=J.parse(h);\n  var as=d.select('a[href*=\/anime\/]');\n  var out=[],seen={};\n  for(var i=0;i<as.size();i++){\n    var a=as.get(i);\n    var u=String(a.attr('href')||'');\n    var pic=String(a.attr('data-original')||'');\n    var n=String(a.attr('title')||'').trim();\n    if(!u||!pic||!n||seen[u])continue;\n    seen[u]=1;\n    var kind='',last='';\n    var li=a.closest('li');\n    if(li){var bs=li.select('span b');if(bs.size()>=2){kind=String(bs.get(0).text());last=String(bs.get(bs.size()-1).text());}}\n    out.push(JSON.stringify({name:n,bookUrl:u,coverUrl:pic,kind:kind,lastChapter:last}));\n  }\n  \/\/ ★E 版 getElements 是 `result as List<Any>`(无 NativeArray 分支),\n  \/\/   返回 Rhino 原生数组会 ClassCastException → TocEmptyException\/列表空。\n  \/\/   必须返回 Java java.util.ArrayList,两版通吃。\n  var list=new Packages.java.util.ArrayList();\n  for(var j=0;j<out.length;j++) list.add(out[j]);\n  return list;\n})()\n",
        "bookUrl": "$.bookUrl",
        "coverUrl": "$.coverUrl",
        "kind": "$.kind",
        "lastChapter": "$.lastChapter",
        "name": "$.name"
    },
    "ruleToc": {
        "chapterList": "@js:\n(function(){\n  var h=String(result||'');\n  var J=Packages.org.jsoup.Jsoup, d=J.parse(h);\n  var panes=d.select('div[id^=playlist]');\n  var out=[], seen={};\n  \/\/ E 版 getElements 只认 Java List,末尾统一转 ArrayList\n  \/\/ 只取第一条播放线路(精品),避免 4 条线路重复 800+ 集\n  if(panes.size()>0){\n    var as=panes.get(0).select('ul.play-list-content li.play-list-content-item a');\n    for(var i=0;i<as.size();i++){\n      var a=as.get(i), u=String(a.attr('href')||''), n=String(a.attr('title')||a.text()||'').trim();\n      if(!u||seen[u])continue; seen[u]=1;\n      if(u.indexOf('\/play\/')<0)continue;\n      out.push(JSON.stringify({n:n||('第'+(out.length+1)+'集'),u:u}));\n    }\n  }\n  if(out.length===0){\n    \/\/ 兜底:详情页「立即播放」按钮\n    var pb=d.select('a.play-btn, .play-btn a').first();\n    var u2=pb?String(pb.attr('href')||''):'';\n    if(u2.indexOf('\/play\/')>=0) out.push(JSON.stringify({n:'▶ 播放',u:u2}));\n  }\n  var list=new Packages.java.util.ArrayList();\n  for(var j=0;j<out.length;j++) list.add(out[j]);\n  return list;\n})()\n",
        "chapterName": "$.n",
        "chapterUrl": "$.u"
    },
    "searchUrl": "@js:(function(){var line=OM_line(source);var now=Date.now(),last=0;try{last=parseInt(OM_g(source.get('om_search_last')),10)||0;}catch(e){}var wait=3000-(now-last);if(wait>0&&wait<=3000){try{java.log('[OmoFun] 搜索节流 '+wait+'ms');}catch(e2){}try{Packages.java.lang.Thread.sleep(wait);}catch(e4){}}try{source.put('om_search_last',String(Date.now()));}catch(e3){}return line+'\/search\/'+java.encodeURI(String(key))+'-------------.html';})()",
    "weight": 0
}
广告