▶ OmoFun 动漫(发布页多线路)
https://fb.omoo.tv/
laizhiyu (6509)11/05 00:38
我错了,混元4厉害(๑•̀ㅂ•́)و✧,做书源很快,
{
"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": "1793810310737",
"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
}