18AV
https://mjv006.com
分享者: laizhiyu (6509)发布时间: 1天前
也是根据洛亚橙大佬18AV订阅源修复而来。
开始使用先登录,点获取网址,右上角√返回会自动写入。
测试软件:legadoTeam,sigma其他款自测
AI修复,白嫖的TOKEN,用力蹬,用完就不蹬了
{
"articleStyle": 3,
"cacheFirst": false,
"contentBlacklist": ".*(?:pemsrv.com|mmfree3.com|magsrv.com).*,.*adcg.*gif",
"customOrder": 49,
"enableJs": true,
"enabled": true,
"enabledCookieJar": true,
"header": "@js:\n(()=>{\nlet headerss=gethd();\nheaderss['User-Agent']=java.getWebViewUA();\n return JSON.stringify(headerss);\n})();",
"injectJs": "if (window.local) {\n function copyText(button, weizi) {\nconst text = weizi == 'before' ?\nbutton.previousElementSibling.textContent :\nweizi == 'after' ? button.nextElementSibling.textContent :\n'异常';\n const btext = button.textContent;\n navigator.clipboard.writeText(text)\n .then(() => {\n button.textContent = \"✓已复制\";\n setTimeout(() => {\n button.textContent = btext;\n }, 1500);\n })\n .catch(err => {\n button.textContent = \"✗失败\";\n setTimeout(() => {\n button.textContent = btext;\n }, 1500);\n });\n}\n\nlet pressTimer = null;\ndocument.querySelector('.jianjie').addEventListener('touchstart', (e) => {\n\tlet ee=e.target;\n\tif (ee.tagName=='A')\n\tpressTimer = setTimeout(() => {\n\t\tlet txt=ee.textContent+\"::\"+ee.href;\nnavigator.clipboard.writeText(txt).then(()=>{\n\t\/\/showToast(\"已复制到剪贴板,可填入登录界面自定义分类。<br>\"+txt, 1600);\n java.longToast(\"已复制,可填入登录界面自定义分类。\\n\"+txt);\n\t});\n}, 1200);\n});\n \ndocument.querySelector('.jianjie').addEventListener('touchend', e => {\n\tif (pressTimer) {\n\t\tclearTimeout(pressTimer);\n\t\tpressTimer = null;}\n});\n\ndocument.querySelector('.jianjie').addEventListener('click', e => {\n\tlet ee=event.target.closest('a');\n\tif (ee) {\n\t\te.preventDefault();\n\t\tlet url=ee.href;\n\t\tlet tt=ee.textContent;\n\t\tif (!tt) {\n\t\t\tlet a=ee.nextElementSibling.querySelector('a');\n\t\t\tif (a) tt=a.textContent;\n\t\t\t}\n url = url.replace(\/[^\\\/].html\/,'');\n\t\tjava.open(\"sort\",JSON.stringify({[tt]:`data:;base64,{{java.base64Encode(\"${url}\"+page+\".html\")}},{\"type\":\"18av\"}`}));\n\t\t}\n});\n\ndocument.querySelector('.posts').addEventListener('click', e => {\n\tlet ee=event.target.closest('a');\n\tif (ee) {\n\t\te.preventDefault();\n\t\tlet tt=ee.innerText;\n\t\tif (!tt) {\n\t\t\tlet h3=ee.parentElement.nextElementSibling.querySelector('h3');\n\t\t\tif (h3) tt=h3.textContent;\n\t\t\t}\n let bs64=java.base64Encode(ee.href.replace(\/^http.{4}[^\\\/]+\/,''));\njava.open(\"rss\",`${ee.href}@js:\n'data:;base64,${bs64},{\"type\":\"18av\"}'\n`,tt);\n\t\t}\n});\n\n}",
"jsLib": "banben=\"1.1.7\";\nsleep=Packages.java.lang.Thread.sleep;\nu_a = 'Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36 Edg\/135.0.0.0';\nx_r_w = ''; \/\/com.microsoft.emmx\ns_c_u_p = `\"Windows\"`;\ns_c_u = `\"Microsoft Edge\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\"`;\na_l = 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7';\n\nfunction gethd(url='',ck='') {\n\tlet ck0 = this.source.getLoginHeader()||'';\n\tlet headerss = {\n\t\t \t'User-Agent': u_a,\n\t\t \t'Cookie': ck0,\n\t\t \t'x-requested-with': x_r_w,\n \t'Accept-Language': a_l\n \t }\nif (ck==1&&url!='') return url+','+JSON.stringify({'headers':headerss,'webView':true});\nif (ck!='') headerss['Cookie']=ck;\nif (url=='') return headerss;\nreturn url+','+JSON.stringify({'headers':headerss});\n}\n\nfunction uinfo() {\n\tlet m = null;\n\ttry {\n\t\tlet src = (typeof source !== 'undefined' && source) ? source : this.source;\n\t\tm = src.getLoginInfoMap();\n\t} catch (e) { m = null; }\n\treturn (m && typeof m === 'object') ? m : {};\n}\n\nfunction gets(x, r) {\nreturn r == undefined ? String(this.java.getString(x)) : String(this.java.getString(x,r));}\n\nfunction surl() {\n\tlet surl=uinfo()['网址']+'';\n\tif (!surl||surl=='undefined') surl='https:\/\/mjv006.com';\n\treturn surl.replace(\/\\\/$\/,'');}\n\nfunction tkey() {\n\tlet tkey = uinfo()['每日更新日期']+[];\n\tif (tkey&&tkey!='undefined') {\n\t\ttkey = tkey.replace(\/^\\d{0,2}(\\d{2})\\D(\\d{1,2})\\D(\\d{1,2})$\/, (mt, p1, p2, p3)=>{\n\t\t\tp1='20'+p1;\n\t\t\tp2=p2.padStart(2, '0');\n\t\t\tp3=p3.padStart(2, '0');\n\t\t\tp2>12&&(p2='012')\n\t\t\tp3>31&&(p3='031')\n\t\t\treturn p1+'-'+p2+'-'+p3;\n\t\t\t});\n\t\t\tif (!\/^\\d{4}-\\d{2}-\\d{2}$\/.test(tkey)) {\n\t\t\t\ttkey='';\n\t\t\t\tthis.java.toast('\\n❎请输入正确的时间格式');}\n}\nif (!tkey||tkey=='undefined') {tkey = this.java.timeFormatUTC(Date.now(),\"yyyy-MM-dd\",6*60*60*1000)+[];}\n\treturn tkey;}\n\t\nfunction rrcheck(url, webr) {\n\tconst {java, source, cookie} = this;\n\tlet webr2 = org.jsoup.Jsoup.parse(webr);\n\tlet url19 = (webr2.select('#fwin_dialog_submit').attr('onclick')+'').match(\/http[^\"']+\/);\nif (url19) {\n\tlet ck=java.connect(url19)\n\t.headers()\n\t.values(\"Set-Cookie\");\n\tlet cks = [];\n\tfor (let char of ck) {\n\tchar=(char+'').replace(\/;.+\/,'');\n\tcks.push(char);}\n\tck=cks.join('; '); \/\/带ajax得到的会话ck访问时不会有ck返回,为激活ck(主要用于不启用jar)\nck=ck||cookie.getCookie(url)+'';\n\/\/勾选ckjar就能getck读取本次软件会话ck(重启软件失效),setck函数才会一直保存源ck\nif (ck=='')\nck=source.getLoginHeader(); \/\/获取现有ck\nelse\nsource.putLoginHeader(ck); \/\/保存新ck\ncookie.setWebCookie(url,ck); \/\/同步ck到内置浏览器\n\tsleep(120); \/\/服务器记录可能需要延时\n\twebr=java.ajax(url); \/\/访问前会执行一次请求头规则\n\t}\n\treturn java.t2s(webr)+'';\n}\n\n\nfunction gengxin(){\nconst {java, source} = this;\n\tlet url0='https:\/\/gitee.com\/lyc486\/yuedu2\/commits\/main\/订阅\/18AV\/18av_特供版.json?browser_history=0&page=1&per=3';\n\tlet rr0=java.get(url0,{'User-Agent': 'Mozilla\/5.0 (Linux; Android 14)','X-Requested-With': 'XMLHttpRequest'}).body();\n\tlet gxlrr = com.jayway.jsonpath.JsonPath.read(rr0, '$.list[0:3].title');\n\tlet [gxlr, gxlr2, gxlr3] = gxlrr;\n\tgxlr=String(gxlr).split(\/^([^\\-]+)-\/);\n\tgxlr[0]=='' ? '' : gxlr=['','0',gxlr[0]];\n\tlet gxts;\n\tif (gxlr[1]>banben){gxts=['更新','0,140,186'];}\n\telse {gxts= ['导入','76,175,80'];};\n\tlet htm = `<html><head>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>18AV ${gxts[0]}<\/title>\n<style>\ntable {\n\ttext-align: center;\n\tmargin: 0 auto;\n\t}\n.ann {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\theight: 50vh;\n\t}\nbutton {\n\tbackground-color: rgb(${gxts[1]});\n\tcolor: white;\n\tborder: none;\n\tborder-radius: 4px;\n\theight: 6vh;\n\twidth: 30vw;\n\toverflow: hidden;\n\t}\nbutton span {\n cursor: pointer;\n display: inline-block;\n position: relative;\n transition: 0.4s;\n}\nbutton span:after {\n content: '>';\n position: absolute;\n opacity: 0;\n top: 0;\n right: 30px;\n transition: 0.2s;\n}\nbutton:active span {\n padding-right: 20px;\n}\nbutton:active span:after {\n opacity: 1;\n right: -40px;\n}\n<\/style><\/head>\n<body>\n<table border=\"1\" cellspacing=\"0\">\n<th colspan=\"2\">最新日志:${gxlr[2]}<\/th>\n<tr><td>仓库版本:${gxlr[1]}<\/td>\n<td>本地版本:${banben}<\/td><\/tr>\n<tr>\n<td colspan=\"2\" style=\"text-align: left;\">往期日志:${gxlr2}<br> ${gxlr3}<\/td>\n<\/tr>\n<\/table>\n<div class=\"ann\">\n<a href=\"legado:\/\/import\/importonline?src=https:\/\/gitee.com\/lyc486\/yuedu2\/raw\/main\/订阅\/18AV\/18av_特供版.json\"><button><span>${gxts[0]}<\/span><\/button><\/a>\n<\/div>\n<a href=\"legado:\/\/import\/importonline?src=https:\/\/cdn.jsdelivr.net\/gh\/Luoyacheng\/yuedu@main\/订阅\/18AV\/18av_特供版.json\" style=\"font-size: 10px;\">备用链接<\/a>\n<\/body>\n<\/html>`;\njava.startBrowser(`data:text\/html;base64,${java.base64Encode(htm)}`,'订阅源更新');\r\n}\n\nvar toastjs=`const toastStyle = document.createElement('style');\ntoastStyle.textContent = \".toast-container {position: fixed; z-index: 1000; pointer-events: none; display: flex; flex-direction: column; align-items: center;} .toast-container.top-center { top: 20px; left: 50%; transform: translateX(-50%); } .toast-container.top-left { top: 20px; left: 20px; } .toast-container.top-right { top: 20px; right: 20px; } .toast-container.bottom-center { bottom: 20px; left: 50%; transform: translateX(-50%); } .toast-container.bottom-left { bottom: 20px; left: 20px; } .toast-container.bottom-right { bottom: 20px; right: 20px; } .toast {background: rgba(0, 0, 0, 0.8); color: white; padding: 12px 14px; border-radius: 4px; margin-bottom: 10px; word-break: initial; max-width: 80vw; opacity: 0; transform: translateY(20px); transition: opacity 0.3s, transform 0.3s; pointer-events: none;} .toast.show {opacity: 1; transform: translateY(0);} .toast.success {background: rgba(46, 204, 113, 0.9);} .toast.error {background: rgba(231, 76, 60, 0.9);} .toast.warning {background: rgba(241, 196, 15, 0.9);} .toast.info {background: rgba(52, 152, 219, 0.9);}\";\ndocument.head.appendChild(toastStyle);\nwindow.showToast = function (message, duration = 3000, type = 'default', position = 'bottom-center') {\n let container = document.querySelector('.toast-container');\n if (!container) {\n container = document.createElement('div');\n container.className = 'toast-container';\n document.body.appendChild(container);\n }\n container.className = \"toast-container \" + position;\n const toast = document.createElement('div');\n toast.className = \"toast \" + type;\n toast.innerHTML = message;\n container.appendChild(toast);\n setTimeout(() => {\n toast.classList.add('show');\n }, 10);\n setTimeout(() => {\n toast.classList.remove('show');\n setTimeout(() => {\n if (toast.parentNode) {\n toast.parentNode.removeChild(toast);\n }\n }, 300);\n }, duration);\n}`;\n\n\/* ================= [自定义分类 · 修复块] ================= *\/\n\/* 说明:本块会同时注入 jsLib 与 loginUrl。\n - 放在末尾,同名函数后定义覆盖先定义,因此无需改动原有 uinfo()。\n - sortUrl 环境只加载 jsLib;登录按钮环境只加载 loginUrl,两边都放一份才都有。 *\/\nvar FKEY_UIKEYS = ['网址', '每日更新日期', '继续播放', '自动播放', '显示链接', '预览截图', '随机主题', '启动页', '滑动等级', '播放器', '双击⏭️时间', '长按⏩倍速', '自定义分类'];\nvar FKEY_NAME = '自定义分类';\n\nfunction _fkSrc() {\n try { if (typeof source !== 'undefined' && source) return source; } catch (e) {}\n try { if (this && this.source) return this.source; } catch (e2) {}\n return null;\n}\n\n\/* 判断一行是否为「key=value」的新字段。\n 难点:分类网址常带查询参数(?t=1),朴素的 indexOf('=') 会把续行误判成新字段,\n 导致已填的分类整段丢失。这里只在「= 左边确实像字段名」时才认定是新字段。 *\/\nfunction _fkIsKey(line) {\n var i = line.indexOf('=');\n if (i <= 0) return null;\n var k = line.substring(0, i);\n if (FKEY_UIKEYS.indexOf(k) > -1) return k;\n if (k.indexOf('\/') > -1 || k.indexOf('::') > -1 || k.indexOf(':') > -1) return null;\n if (k.indexOf('?') > -1 || k.indexOf('&') > -1 || k.indexOf('.') > -1) return null;\n if (\/[\\s\"'`<>\\[\\]{}|\\\\^]\/.test(k)) return null;\n if (k.length > 12) return null;\n return k;\n}\n\n\/* 直接解析 loginInfo 原始串,能还原被 getLoginInfoMap() 截断的多行值 *\/\nfunction _fkRawMap() {\n var m = {};\n var src = _fkSrc();\n if (!src) return m;\n var raw = '';\n try { raw = src.getLoginInfo() + ''; } catch (e) { raw = ''; }\n if (!raw || raw == 'null' || raw == 'undefined') return m;\n var lines = String(raw).split(\/[\\r\\n]+\/);\n var cur = null;\n for (var i = 0; i < lines.length; i++) {\n var line = lines[i];\n if (!line.length) continue;\n var k = _fkIsKey(line);\n if (k) {\n cur = k;\n m[cur] = line.substring(line.indexOf('=') + 1);\n } else if (cur) {\n m[cur] = m[cur] + '\\n' + line;\n }\n }\n return m;\n}\n\n\/* 覆盖原 uinfo():Map 结果 + 原始串补全,两者取更完整的那份 *\/\nfunction uinfo() {\n var m = {};\n var src = _fkSrc();\n if (src) {\n var mm = null;\n try { mm = src.getLoginInfoMap(); } catch (e) { mm = null; }\n if (mm) {\n var got = false;\n try {\n var it = mm.keySet().iterator();\n while (it.hasNext()) {\n var k = it.next() + '';\n if (_fkBadKey(k)) continue; \/\/ 丢掉分类碎片\n var v = mm.get(k);\n m[k] = (v === null || v === undefined) ? '' : (v + '');\n }\n got = true;\n } catch (e2) { got = false; }\n if (!got) {\n try {\n for (var kk in mm) {\n if (kk == 'class' || kk == 'empty') continue;\n if (_fkBadKey(kk)) continue;\n m[kk] = mm[kk] + '';\n }\n } catch (e3) {}\n }\n }\n }\n var rm = {};\n try { rm = _fkRawMap(); } catch (e4) { rm = {}; }\n for (var k2 in rm) {\n if (!Object.prototype.hasOwnProperty.call(rm, k2)) continue;\n var nv = rm[k2] + '';\n var ov = (m[k2] === undefined ? '' : m[k2] + '');\n if (nv && (!ov || nv.length > ov.length)) m[k2] = nv;\n }\n return m;\n}\n\nfunction _fkVal(v) {\n v = (v === undefined || v === null) ? '' : (v + '');\n return (v == 'undefined' || v == 'null') ? '' : v;\n}\n\n\/* ArtPlayer 版本号:同步拉取 unpkg。\n 原写法把它内联在 loginUi 里,一旦网络不可达就抛异常,导致整个登录界面渲染失败(0 控件)。\n 这里包了 try\/catch,失败返回 '?'。 *\/\nfunction apver() {\n try {\n var s = java.importScript('https:\/\/unpkg.com\/artplayer\/dist\/artplayer.js');\n var v = s && String(s).match(\/artplayer\\.js.*v([0-9][^\\s\"']*)\/i);\n return v ? v[1] : '?';\n } catch (e) { return '?'; }\n}\n\n\/* 判断是否为「分类碎片」key。\n 分类网址里带 ?t=1 时,legado 的 getLoginInfoMap() 会把整行当成新字段存进来\n (key = \"演员A::https:\/\/...?t\")。这些碎片必须剔除,否则回写 loginInfo 时会污染数据。 *\/\nfunction _fkBadKey(k) {\n k = String(k || '');\n if (!k) return true;\n if (FKEY_UIKEYS.indexOf(k) > -1) return false;\n if (k.indexOf('::') > -1) return true;\n if (k.indexOf('\/') > -1) return true;\n if (k.indexOf('?') > -1 || k.indexOf('&') > -1) return true;\n if (k.indexOf('.') > -1) return true;\n if (k.indexOf('=') > -1) return true;\n return false;\n}\n\n\/* 读「自定义分类」原文:loginInfo 与源变量 fkey 两路,取「能解析出更多条目」的那份。\n 注意不能简单取更长的——归一化后两条内容常常重复,取更长会得到重复来源。 *\/\nfunction fkeyRaw() {\n var a = '';\n try { a = _fkVal(uinfo()[FKEY_NAME]); } catch (e) { a = ''; }\n var b = '';\n var src = _fkSrc();\n if (src) { try { b = _fkVal(src.get('fkey')); } catch (e2) { b = ''; } }\n if (!String(a).trim()) return b;\n if (!String(b).trim()) return a;\n try { return (fkeyCount(b) > fkeyCount(a)) ? b : a; } catch (e3) { return a; }\n}\n\n\/* 归一化成「单条一行、用 ; 分隔」的单行串 *\/\nfunction fkeyNorm(raw) {\n raw = (raw || '') + '';\n if (raw == 'undefined' || raw == 'null') raw = '';\n if (!String(raw).trim()) return '';\n var items = String(raw).split(\/[\\r\\n,,;;|]+\/);\n var out = [];\n for (var i = 0; i < items.length; i++) {\n var t = String(items[i] || '').replace(\/^[\\s\"'“”`]+|[\\s\"'“”`]+$\/g, '');\n if (t) out.push(t);\n }\n if (out.length < 2 && \/\\s\/.test(raw)) {\n var t2 = String(raw).trim().split(\/\\s+\/);\n var o2 = [];\n for (var j = 0; j < t2.length; j++) if (t2[j]) o2.push(t2[j]);\n if (o2.length > out.length) out = o2;\n }\n return out.join(';');\n}\n\nfunction _fkNameOf(url, i) {\n var seg = String(url).replace(\/\\\/+$\/, '').split('\/').pop();\n if (!seg || \/^(?:page|\\d+)(?:\\.html)?$\/i.test(seg)) {\n var tmp = String(url).replace(\/\\\/+$\/, '').split('\/');\n seg = tmp.length > 1 ? tmp[tmp.length - 2] : '';\n }\n seg = String(seg || '').replace(\/\\.html$\/i, '');\n try { seg = decodeURIComponent(seg); } catch (e) {}\n return seg || ('分类' + (i + 1));\n}\n\n\/* 把原始文本切成条目列表(不生成最终 URL) *\/\nfunction fkeyItems(raw) {\n raw = (raw || '') + '';\n if (raw == 'undefined' || raw == 'null') raw = '';\n if (!String(raw).trim()) return [];\n var items = String(raw).split(\/[\\r\\n,,;;|]+\/);\n var list = [];\n for (var i = 0; i < items.length; i++) {\n var t = String(items[i] || '').replace(\/^[\\s\"'“”`]+|[\\s\"'“”`]+$\/g, '');\n if (t) list.push(t);\n }\n if (list.length < 2 && \/\\s\/.test(raw)) {\n var t2 = String(raw).trim().split(\/\\s+\/);\n var l2 = [];\n for (var j = 0; j < t2.length; j++) if (t2[j]) l2.push(t2[j]);\n if (l2.length > list.length) list = l2;\n }\n var out = [];\n for (var n = 0; n < list.length; n++) {\n var item = list[n];\n var name = '', url = '';\n var p = item.indexOf('::');\n if (p > -1) { name = item.substring(0, p).trim(); url = item.substring(p + 2).trim(); }\n else { url = item; }\n if (!\/[\\\/\\.]\/.test(url)) continue; \/\/ 纯文字碎片,丢弃\n url = url.replace(\/^\\s*(?:https?:)?\\\/\\\/[^\\\/]+\/i, '').replace(\/\\s+\/g, '');\n if (!url) continue;\n if (url.charAt(0) != '\/') url = '\/' + url;\n var q = '';\n var qi = url.indexOf('?');\n if (qi > -1) { q = url.substring(qi + 1); url = url.substring(0, qi); }\n if (!name) name = _fkNameOf(url, n);\n name = String(name).replace(\/[::\\r\\n]\/g, '').trim() || ('分类' + (n + 1));\n out.push({ name: name, url: url, q: q });\n }\n return out;\n}\n\n\/* 条目 → sortUrl 行 *\/\nfunction fkeyRow(it) {\n var url = it.url, q = it.q, body;\n if (\/\\\/page\\.html$\/i.test(url) || \/\\\/\\d+\\.html$\/i.test(url)) {\n body = '\"' + url.replace(\/\\\/(?:page|\\d+)\\.html$\/i, '\/') + '\"+page+\".html\"';\n } else if (\/\\\/index\\.html$\/i.test(url)) {\n body = '\"' + url + '?t=\"+page';\n } else if (\/\\.html$\/i.test(url)) {\n body = '\"' + url + '\"'; \/\/ 日期页\/固定页:单页\n } else if (\/(?:^|&)t=\/.test(q)) {\n body = '\"' + url + '?t=\"+page';\n } else if (\/\\\/$\/.test(url)) {\n body = '\"' + url + '\"+page+\".html\"';\n } else {\n body = '\"' + url + '\/\"+page+\".html\"';\n }\n return it.name + '::data:;base64,{{java.base64Encode(' + body + ')}},{\"type\":\"18av\"}';\n}\n\nfunction fkeyFrom(raw) {\n var items = fkeyItems(raw), out = [];\n for (var i = 0; i < items.length; i++) out.push(fkeyRow(items[i]));\n return out;\n}\n\nfunction fkeyCount(raw) {\n try { return fkeyItems(raw).length; } catch (e) { return 0; }\n}\n\n\/* 解析成 sortUrl 行数组(sortUrl 与按钮都调它) *\/\nfunction fkey() {\n try { return fkeyFrom(fkeyRaw()); } catch (e) { return []; }\n}\n\n\/* 归一化 + 双写(源变量 fkey)。登录时会自动跑一次 *\/\nfunction normFkey() {\n var raw = fkeyRaw();\n if (!String(raw).trim()) return 0;\n var single = fkeyNorm(raw);\n if (!single) return 0;\n var n = fkeyCount(single);\n var src = _fkSrc();\n if (!src) return n;\n try { src.put('fkey', single); } catch (e) {}\n if (raw.indexOf('\\n') < 0 && raw === single) return n; \/\/ 已是单行,不冒险改 loginInfo\n var raw0 = '';\n try { raw0 = src.getLoginInfo() + ''; } catch (e2) { raw0 = ''; }\n if (!raw0 || raw0 == 'null' || raw0 == 'undefined') return n;\n try {\n var m = uinfo();\n var clean = {};\n for (var k in m) {\n if (!Object.prototype.hasOwnProperty.call(m, k)) continue;\n if (FKEY_UIKEYS.indexOf(k) < 0) continue; \/\/ 只写真正的 UI 字段\n clean[k] = m[k];\n }\n clean[FKEY_NAME] = single;\n src.putLoginInfo(JSON.stringify(clean));\n \/\/ 校验只看 loginInfo 本身(不能用 uinfo(),它会被源变量兜底而掩盖失败)\n var back = '';\n try { back = _fkVal(_fkRawMap()[FKEY_NAME]); } catch (e3) {}\n if (back.indexOf(fkeyItems(single)[0].name) < 0 && back.indexOf(single.split(';')[0]) < 0) {\n try { src.putLoginInfo(raw0); } catch (e4) {} \/\/ 回写失败则还原\n }\n } catch (e5) {}\n return n;\n}\n\n\/* 诊断:把真实读到的一切弹出来,便于定位 *\/\nfunction diagFkey() {\n var L = [];\n var src = _fkSrc();\n L.push('① source 对象:' + (src ? '可用' : '不可用'));\n var rawInfo = '(读不到)';\n try { rawInfo = src ? (src.getLoginInfo() + '') : '(无 source)'; } catch (e) { rawInfo = 'getLoginInfo() 异常: ' + e; }\n L.push('② loginInfo 原始串(前400字):\\n' + String(rawInfo).substring(0, 400));\n var keys = [];\n try { var mm = uinfo(); for (var k in mm) if (Object.prototype.hasOwnProperty.call(mm, k)) keys.push(k); } catch (e2) {}\n L.push('③ 解析到字段(' + keys.length + '):' + keys.join(' \/ '));\n var v = fkeyRaw();\n L.push('④ 自定义分类 原文(前300字):\\n' + (String(v).trim() ? String(v).substring(0, 300) : '(空)'));\n var sv = '(空)';\n try { sv = src ? _fkVal(src.get('fkey')) : '(无 source)'; } catch (e3) {}\n L.push('⑤ 源变量 fkey(前300字):\\n' + (String(sv).trim() ? String(sv).substring(0, 300) : '(空)'));\n var arr = [];\n var err = '';\n try { arr = fkey(); } catch (e4) { err = e4 + ''; }\n if (err) L.push('⑥ 解析异常:' + err);\n else if (!arr.length) L.push('⑥ 解析结果:0 个(没识别出有效条目)');\n else {\n var names = [];\n for (var i = 0; i < arr.length; i++) names.push(' ' + (i + 1) + '. ' + arr[i].split('::')[0]);\n L.push('⑥ 解析结果:' + arr.length + ' 个\\n' + names.join('\\n'));\n }\n var txt = '🔧 自定义分类诊断\\n\\n' + L.join('\\n\\n');\n try { java.setClipboard(txt); } catch (e5) {}\n try { java.longToast('\\n' + txt + '\\n\\n(已复制到剪贴板)'); } catch (e6) {}\n return txt;\n}\n\n\/* 校验:告诉用户当前识别到什么 *\/\nfunction checkFkey() {\n var raw = fkeyRaw();\n var tip;\n if (!String(raw).trim()) {\n tip = '❎ 没读到分类内容。\\n\\n'\n + '请按下面顺序操作:\\n'\n + '1) 在上面的「自定义分类」框里填内容\\n'\n + '2) 点右上角 √ 保存(很关键,不保存读不到)\\n'\n + '3) 再回到本页点「🧪校验分类」\\n\\n'\n + '【格式】每行一个:\\n分类名::网址\\n\\n'\n + '多个分类用 换行 \/ 分号 \/ 逗号 隔开都行。\\n'\n + '在视频详情页长按简介里的分类或演员名,会自动复制成「名称::网址」。\\n\\n'\n + '还是不行就点右边的「🔧诊断」把结果发我。';\n } else {\n var arr = [];\n var err = '';\n try { arr = fkey(); } catch (e) { err = e + ''; }\n if (err) tip = '❎ 解析出错:' + err;\n else if (!arr.length) tip = '❎ 读到了内容,但没识别出有效条目:\\n' + String(raw).substring(0, 300)\n + '\\n\\n每条要像这样:分类名::https:\/\/mjv006.com\/zh\/xxx\/';\n else {\n var names = [];\n for (var i = 0; i < arr.length; i++) names.push(' ' + (i + 1) + '. ' + arr[i].split('::')[0]);\n tip = '✅ 已识别 ' + arr.length + ' 个分类:\\n\\n' + names.join('\\n')\n + '\\n\\n返回后发现页生效(需重新进发现页)。';\n }\n }\n java.longToast('\\n' + tip);\n}\n\n\/* 备份:写入源变量,避免 loginInfo 读取异常时丢失 *\/\nfunction saveFkey() {\n var n = 0;\n try { n = normFkey(); } catch (e) {}\n if (!n) { try { n = fkeyCount(fkeyRaw()); } catch (e0) {} }\n var raw = fkeyRaw();\n if (!String(raw).trim()) {\n java.longToast('\\n❎ 读到的内容为空。\\n请先填写 → 点右上角 √ 保存 → 再进本页点备份。\\n\\n若已保存仍读不到,点「🔧诊断」看原因。');\n return;\n }\n java.longToast('\\n✅ 已备份 ' + n + ' 条:\\n\\n' + String(raw).substring(0, 300)\n + '\\n\\n备份内容存在源变量 fkey 里,即使登录信息丢失也能恢复。');\n}\n\n\/* 清空备份 *\/\nfunction clearFkey() {\n var src = _fkSrc();\n try { if (src) src.put('fkey', ''); } catch (e) {}\n java.longToast('\\n✅ 已清空备份的自定义分类。\\n(发现页会只剩内置分类,登录框里的内容不受影响)');\n}\n\/* =============== [自定义分类 · 修复块 end] =============== *\/\n",
"lastUpdateTime": 1788075530188,
"loadWithBaseUrl": true,
"loginUi": "@js:\n`[\n {\n name: \"网址\",\n type: \"text\"\n },\n {\n name: \"每日更新日期\",\n type: \"text\"\n },\n \t{\n name: \"ArtPlayer\",\n viewName: \"'ArtPlayer V'+apver()\",\n type: \"button\",\n action: \"bfq()\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": true\n }\n },\n \t{\n name: \"订阅源版本 \",\n viewName:\"'订阅源版本 V'+banben\",\n type: \"button\",\n action: \"gengxin()\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false\n }\n },\n \t{\n name: \"继续播放\",\n type: \"toggle\",\n chars:[\"🔳\", \"✅\"],\n default:\"✅\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": true,\n \"layout_justifySelf\":\"right\"\n }\n },\n \t{\n name: \"自动播放\",\n type: \"toggle\",\n chars:[\"🔳\", \"✅\"],\n default:\"🔳\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false,\n \"layout_justifySelf\":\"right\"\n }\n },\n \t{\n name: \"显示链接\",\n type: \"toggle\",\n chars:[\"🔳\", \"✅\"],\n default:\"✅\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false,\n \"layout_justifySelf\":\"right\"\n }\n },\n \t{\n name: \"预览截图\",\n type: \"toggle\",\n chars:[\"🔳\", \"✅\"],\n default:\"✅\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false,\n \"layout_justifySelf\":\"right\"\n }\n },\n \t{\n name: \"随机主题\",\n type: \"toggle\",\n chars:[\"🔳\", \"✅\"],\n default:\"✅\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false,\n \"layout_justifySelf\":\"right\"\n }\n },\n \t{\n name: \"启动页\",\n type: \"toggle\",\n chars:[\"🔳\", \"✅\"],\n default:\"✅\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false,\n \"layout_justifySelf\":\"right\"\n }\n },\n \t{\n name: \"滑动等级\",\n type: \"select\",\n chars: [\"1️⃣\",\"2️⃣\",\"3️⃣\",\"4️⃣\",\"5️⃣\",\"6️⃣\",\"7️⃣\"],\n default:\"3️⃣\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": true\n }\n },\n \t{\n name: \"播放器\",\n type: \"select\",\n chars:['默认','阅读','外部'],\n default:\"默认\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false\n }\n },\n {\n name: \"双击⏭️时间\",\n type: \"select\",\n chars:[5,10,15,20,30,45],\n default: \"10\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false\n }\n },\n {\n name: \"长按⏩倍速\",\n type: \"select\",\n chars:[1.5,2.0,2.5,3.0,3.5,4.0,5.0,6.0],\n default: \"3.0\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false\n }\n },\n {\n name: \"自定义分类\",\n type: \"text\"\n },\n \t{\n name: \"🧪校验分类\",\n type: \"button\",\n action: \"checkFkey()\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": true\n }\n },\n \t{\n name: \"💾备份分类\",\n type: \"button\",\n action: \"saveFkey()\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false\n }\n },\n \t{\n name: \"🗑️清空备份\",\n type: \"button\",\n action: \"clearFkey()\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false\n }\n },\n \t{\n name: \"🔧诊断\",\n type: \"button\",\n action: \"diagFkey()\",\n style: {\n \"layout_flexGrow\": 1,\n \"layout_flexBasisPercent\": 0.4,\n \"layout_wrapBefore\": false\n }\n },\n \t{\n name: \"🌐获取网址\",\n type: \"button\",\n action: \"fabuye()\",\n style: {\n \"layout_flexGrow\": 0,\n \"layout_flexBasisPercent\": -1,\n \"layout_wrapBefore\": true\n }\n }\n]`",
"loginUrl": "function login() {\n try { normFkey(); } catch (e) {}\n}\n\nfunction fabuye(){\n\tlet fbyu = source.get('fbyu')+'';\n\tlet headers = source.getHeaderMap(false);\n\ttry {java.head(fbyu,headers,6000);}\n\tcatch(e) {fbyu='https:\/\/mm18vd.com';}\n\tlet webr = java.startBrowserAwait(fbyu,'发布页',false).body();\n\tlet rr = org.jsoup.Jsoup.parse(webr);\n\tfbyu = rr.select('.list:nth-of-type(3) a').attr('href')+'';\n\tsource.put('fbyu',fbyu);\n\tlet surl = (rr.select('.list:nth-of-type(6) a').attr('href')+'').replace('http:\/\/','https:\/\/');\n\ttry {java.head(surl,headers,6000);\n\tjava.longToast('已填写最新网址\\n'+surl);\n\tlet Info={};\n\tInfo['网址'] = surl;\n java.upLoginData(Info);\n} catch(e) {java.longToast('得到的网址\\n'+surl+'\\n无法访问,不进行记录');}\n}\n\nfunction bfq(){\njava.toast('\\n正在更新播放器...');\nvar bb='?';\ntry { java.deleteFile('dcc1a0ac99b90ead.js'); } catch(e0) {}\ntry {\n\tvar js = java.cacheFile('https:\/\/unpkg.com\/artplayer\/dist\/artplayer.js')+'';\n\tvar mv = js.match(\/artplayer\\.js.*v([0-9][^\\s\"']*)\/i);\n\tif (mv) bb = mv[1];\n} catch(e) { bb='?'; }\njava.longToast('\\n当前版本号:V'+bb+(bb=='?' ? '(获取失败,多为网络问题,不影响分类与播放)' : ''));\n\t}\n\n\/* ================= [自定义分类 · 修复块] ================= *\/\n\/* 说明:本块会同时注入 jsLib 与 loginUrl。\n - 放在末尾,同名函数后定义覆盖先定义,因此无需改动原有 uinfo()。\n - sortUrl 环境只加载 jsLib;登录按钮环境只加载 loginUrl,两边都放一份才都有。 *\/\nvar FKEY_UIKEYS = ['网址', '每日更新日期', '继续播放', '自动播放', '显示链接', '预览截图', '随机主题', '启动页', '滑动等级', '播放器', '双击⏭️时间', '长按⏩倍速', '自定义分类'];\nvar FKEY_NAME = '自定义分类';\n\nfunction _fkSrc() {\n try { if (typeof source !== 'undefined' && source) return source; } catch (e) {}\n try { if (this && this.source) return this.source; } catch (e2) {}\n return null;\n}\n\n\/* 判断一行是否为「key=value」的新字段。\n 难点:分类网址常带查询参数(?t=1),朴素的 indexOf('=') 会把续行误判成新字段,\n 导致已填的分类整段丢失。这里只在「= 左边确实像字段名」时才认定是新字段。 *\/\nfunction _fkIsKey(line) {\n var i = line.indexOf('=');\n if (i <= 0) return null;\n var k = line.substring(0, i);\n if (FKEY_UIKEYS.indexOf(k) > -1) return k;\n if (k.indexOf('\/') > -1 || k.indexOf('::') > -1 || k.indexOf(':') > -1) return null;\n if (k.indexOf('?') > -1 || k.indexOf('&') > -1 || k.indexOf('.') > -1) return null;\n if (\/[\\s\"'`<>\\[\\]{}|\\\\^]\/.test(k)) return null;\n if (k.length > 12) return null;\n return k;\n}\n\n\/* 直接解析 loginInfo 原始串,能还原被 getLoginInfoMap() 截断的多行值 *\/\nfunction _fkRawMap() {\n var m = {};\n var src = _fkSrc();\n if (!src) return m;\n var raw = '';\n try { raw = src.getLoginInfo() + ''; } catch (e) { raw = ''; }\n if (!raw || raw == 'null' || raw == 'undefined') return m;\n var lines = String(raw).split(\/[\\r\\n]+\/);\n var cur = null;\n for (var i = 0; i < lines.length; i++) {\n var line = lines[i];\n if (!line.length) continue;\n var k = _fkIsKey(line);\n if (k) {\n cur = k;\n m[cur] = line.substring(line.indexOf('=') + 1);\n } else if (cur) {\n m[cur] = m[cur] + '\\n' + line;\n }\n }\n return m;\n}\n\n\/* 覆盖原 uinfo():Map 结果 + 原始串补全,两者取更完整的那份 *\/\nfunction uinfo() {\n var m = {};\n var src = _fkSrc();\n if (src) {\n var mm = null;\n try { mm = src.getLoginInfoMap(); } catch (e) { mm = null; }\n if (mm) {\n var got = false;\n try {\n var it = mm.keySet().iterator();\n while (it.hasNext()) {\n var k = it.next() + '';\n if (_fkBadKey(k)) continue; \/\/ 丢掉分类碎片\n var v = mm.get(k);\n m[k] = (v === null || v === undefined) ? '' : (v + '');\n }\n got = true;\n } catch (e2) { got = false; }\n if (!got) {\n try {\n for (var kk in mm) {\n if (kk == 'class' || kk == 'empty') continue;\n if (_fkBadKey(kk)) continue;\n m[kk] = mm[kk] + '';\n }\n } catch (e3) {}\n }\n }\n }\n var rm = {};\n try { rm = _fkRawMap(); } catch (e4) { rm = {}; }\n for (var k2 in rm) {\n if (!Object.prototype.hasOwnProperty.call(rm, k2)) continue;\n var nv = rm[k2] + '';\n var ov = (m[k2] === undefined ? '' : m[k2] + '');\n if (nv && (!ov || nv.length > ov.length)) m[k2] = nv;\n }\n return m;\n}\n\nfunction _fkVal(v) {\n v = (v === undefined || v === null) ? '' : (v + '');\n return (v == 'undefined' || v == 'null') ? '' : v;\n}\n\n\/* ArtPlayer 版本号:同步拉取 unpkg。\n 原写法把它内联在 loginUi 里,一旦网络不可达就抛异常,导致整个登录界面渲染失败(0 控件)。\n 这里包了 try\/catch,失败返回 '?'。 *\/\nfunction apver() {\n try {\n var s = java.importScript('https:\/\/unpkg.com\/artplayer\/dist\/artplayer.js');\n var v = s && String(s).match(\/artplayer\\.js.*v([0-9][^\\s\"']*)\/i);\n return v ? v[1] : '?';\n } catch (e) { return '?'; }\n}\n\n\/* 判断是否为「分类碎片」key。\n 分类网址里带 ?t=1 时,legado 的 getLoginInfoMap() 会把整行当成新字段存进来\n (key = \"演员A::https:\/\/...?t\")。这些碎片必须剔除,否则回写 loginInfo 时会污染数据。 *\/\nfunction _fkBadKey(k) {\n k = String(k || '');\n if (!k) return true;\n if (FKEY_UIKEYS.indexOf(k) > -1) return false;\n if (k.indexOf('::') > -1) return true;\n if (k.indexOf('\/') > -1) return true;\n if (k.indexOf('?') > -1 || k.indexOf('&') > -1) return true;\n if (k.indexOf('.') > -1) return true;\n if (k.indexOf('=') > -1) return true;\n return false;\n}\n\n\/* 读「自定义分类」原文:loginInfo 与源变量 fkey 两路,取「能解析出更多条目」的那份。\n 注意不能简单取更长的——归一化后两条内容常常重复,取更长会得到重复来源。 *\/\nfunction fkeyRaw() {\n var a = '';\n try { a = _fkVal(uinfo()[FKEY_NAME]); } catch (e) { a = ''; }\n var b = '';\n var src = _fkSrc();\n if (src) { try { b = _fkVal(src.get('fkey')); } catch (e2) { b = ''; } }\n if (!String(a).trim()) return b;\n if (!String(b).trim()) return a;\n try { return (fkeyCount(b) > fkeyCount(a)) ? b : a; } catch (e3) { return a; }\n}\n\n\/* 归一化成「单条一行、用 ; 分隔」的单行串 *\/\nfunction fkeyNorm(raw) {\n raw = (raw || '') + '';\n if (raw == 'undefined' || raw == 'null') raw = '';\n if (!String(raw).trim()) return '';\n var items = String(raw).split(\/[\\r\\n,,;;|]+\/);\n var out = [];\n for (var i = 0; i < items.length; i++) {\n var t = String(items[i] || '').replace(\/^[\\s\"'“”`]+|[\\s\"'“”`]+$\/g, '');\n if (t) out.push(t);\n }\n if (out.length < 2 && \/\\s\/.test(raw)) {\n var t2 = String(raw).trim().split(\/\\s+\/);\n var o2 = [];\n for (var j = 0; j < t2.length; j++) if (t2[j]) o2.push(t2[j]);\n if (o2.length > out.length) out = o2;\n }\n return out.join(';');\n}\n\nfunction _fkNameOf(url, i) {\n var seg = String(url).replace(\/\\\/+$\/, '').split('\/').pop();\n if (!seg || \/^(?:page|\\d+)(?:\\.html)?$\/i.test(seg)) {\n var tmp = String(url).replace(\/\\\/+$\/, '').split('\/');\n seg = tmp.length > 1 ? tmp[tmp.length - 2] : '';\n }\n seg = String(seg || '').replace(\/\\.html$\/i, '');\n try { seg = decodeURIComponent(seg); } catch (e) {}\n return seg || ('分类' + (i + 1));\n}\n\n\/* 把原始文本切成条目列表(不生成最终 URL) *\/\nfunction fkeyItems(raw) {\n raw = (raw || '') + '';\n if (raw == 'undefined' || raw == 'null') raw = '';\n if (!String(raw).trim()) return [];\n var items = String(raw).split(\/[\\r\\n,,;;|]+\/);\n var list = [];\n for (var i = 0; i < items.length; i++) {\n var t = String(items[i] || '').replace(\/^[\\s\"'“”`]+|[\\s\"'“”`]+$\/g, '');\n if (t) list.push(t);\n }\n if (list.length < 2 && \/\\s\/.test(raw)) {\n var t2 = String(raw).trim().split(\/\\s+\/);\n var l2 = [];\n for (var j = 0; j < t2.length; j++) if (t2[j]) l2.push(t2[j]);\n if (l2.length > list.length) list = l2;\n }\n var out = [];\n for (var n = 0; n < list.length; n++) {\n var item = list[n];\n var name = '', url = '';\n var p = item.indexOf('::');\n if (p > -1) { name = item.substring(0, p).trim(); url = item.substring(p + 2).trim(); }\n else { url = item; }\n if (!\/[\\\/\\.]\/.test(url)) continue; \/\/ 纯文字碎片,丢弃\n url = url.replace(\/^\\s*(?:https?:)?\\\/\\\/[^\\\/]+\/i, '').replace(\/\\s+\/g, '');\n if (!url) continue;\n if (url.charAt(0) != '\/') url = '\/' + url;\n var q = '';\n var qi = url.indexOf('?');\n if (qi > -1) { q = url.substring(qi + 1); url = url.substring(0, qi); }\n if (!name) name = _fkNameOf(url, n);\n name = String(name).replace(\/[::\\r\\n]\/g, '').trim() || ('分类' + (n + 1));\n out.push({ name: name, url: url, q: q });\n }\n return out;\n}\n\n\/* 条目 → sortUrl 行 *\/\nfunction fkeyRow(it) {\n var url = it.url, q = it.q, body;\n if (\/\\\/page\\.html$\/i.test(url) || \/\\\/\\d+\\.html$\/i.test(url)) {\n body = '\"' + url.replace(\/\\\/(?:page|\\d+)\\.html$\/i, '\/') + '\"+page+\".html\"';\n } else if (\/\\\/index\\.html$\/i.test(url)) {\n body = '\"' + url + '?t=\"+page';\n } else if (\/\\.html$\/i.test(url)) {\n body = '\"' + url + '\"'; \/\/ 日期页\/固定页:单页\n } else if (\/(?:^|&)t=\/.test(q)) {\n body = '\"' + url + '?t=\"+page';\n } else if (\/\\\/$\/.test(url)) {\n body = '\"' + url + '\"+page+\".html\"';\n } else {\n body = '\"' + url + '\/\"+page+\".html\"';\n }\n return it.name + '::data:;base64,{{java.base64Encode(' + body + ')}},{\"type\":\"18av\"}';\n}\n\nfunction fkeyFrom(raw) {\n var items = fkeyItems(raw), out = [];\n for (var i = 0; i < items.length; i++) out.push(fkeyRow(items[i]));\n return out;\n}\n\nfunction fkeyCount(raw) {\n try { return fkeyItems(raw).length; } catch (e) { return 0; }\n}\n\n\/* 解析成 sortUrl 行数组(sortUrl 与按钮都调它) *\/\nfunction fkey() {\n try { return fkeyFrom(fkeyRaw()); } catch (e) { return []; }\n}\n\n\/* 归一化 + 双写(源变量 fkey)。登录时会自动跑一次 *\/\nfunction normFkey() {\n var raw = fkeyRaw();\n if (!String(raw).trim()) return 0;\n var single = fkeyNorm(raw);\n if (!single) return 0;\n var n = fkeyCount(single);\n var src = _fkSrc();\n if (!src) return n;\n try { src.put('fkey', single); } catch (e) {}\n if (raw.indexOf('\\n') < 0 && raw === single) return n; \/\/ 已是单行,不冒险改 loginInfo\n var raw0 = '';\n try { raw0 = src.getLoginInfo() + ''; } catch (e2) { raw0 = ''; }\n if (!raw0 || raw0 == 'null' || raw0 == 'undefined') return n;\n try {\n var m = uinfo();\n var clean = {};\n for (var k in m) {\n if (!Object.prototype.hasOwnProperty.call(m, k)) continue;\n if (FKEY_UIKEYS.indexOf(k) < 0) continue; \/\/ 只写真正的 UI 字段\n clean[k] = m[k];\n }\n clean[FKEY_NAME] = single;\n src.putLoginInfo(JSON.stringify(clean));\n \/\/ 校验只看 loginInfo 本身(不能用 uinfo(),它会被源变量兜底而掩盖失败)\n var back = '';\n try { back = _fkVal(_fkRawMap()[FKEY_NAME]); } catch (e3) {}\n if (back.indexOf(fkeyItems(single)[0].name) < 0 && back.indexOf(single.split(';')[0]) < 0) {\n try { src.putLoginInfo(raw0); } catch (e4) {} \/\/ 回写失败则还原\n }\n } catch (e5) {}\n return n;\n}\n\n\/* 诊断:把真实读到的一切弹出来,便于定位 *\/\nfunction diagFkey() {\n var L = [];\n var src = _fkSrc();\n L.push('① source 对象:' + (src ? '可用' : '不可用'));\n var rawInfo = '(读不到)';\n try { rawInfo = src ? (src.getLoginInfo() + '') : '(无 source)'; } catch (e) { rawInfo = 'getLoginInfo() 异常: ' + e; }\n L.push('② loginInfo 原始串(前400字):\\n' + String(rawInfo).substring(0, 400));\n var keys = [];\n try { var mm = uinfo(); for (var k in mm) if (Object.prototype.hasOwnProperty.call(mm, k)) keys.push(k); } catch (e2) {}\n L.push('③ 解析到字段(' + keys.length + '):' + keys.join(' \/ '));\n var v = fkeyRaw();\n L.push('④ 自定义分类 原文(前300字):\\n' + (String(v).trim() ? String(v).substring(0, 300) : '(空)'));\n var sv = '(空)';\n try { sv = src ? _fkVal(src.get('fkey')) : '(无 source)'; } catch (e3) {}\n L.push('⑤ 源变量 fkey(前300字):\\n' + (String(sv).trim() ? String(sv).substring(0, 300) : '(空)'));\n var arr = [];\n var err = '';\n try { arr = fkey(); } catch (e4) { err = e4 + ''; }\n if (err) L.push('⑥ 解析异常:' + err);\n else if (!arr.length) L.push('⑥ 解析结果:0 个(没识别出有效条目)');\n else {\n var names = [];\n for (var i = 0; i < arr.length; i++) names.push(' ' + (i + 1) + '. ' + arr[i].split('::')[0]);\n L.push('⑥ 解析结果:' + arr.length + ' 个\\n' + names.join('\\n'));\n }\n var txt = '🔧 自定义分类诊断\\n\\n' + L.join('\\n\\n');\n try { java.setClipboard(txt); } catch (e5) {}\n try { java.longToast('\\n' + txt + '\\n\\n(已复制到剪贴板)'); } catch (e6) {}\n return txt;\n}\n\n\/* 校验:告诉用户当前识别到什么 *\/\nfunction checkFkey() {\n var raw = fkeyRaw();\n var tip;\n if (!String(raw).trim()) {\n tip = '❎ 没读到分类内容。\\n\\n'\n + '请按下面顺序操作:\\n'\n + '1) 在上面的「自定义分类」框里填内容\\n'\n + '2) 点右上角 √ 保存(很关键,不保存读不到)\\n'\n + '3) 再回到本页点「🧪校验分类」\\n\\n'\n + '【格式】每行一个:\\n分类名::网址\\n\\n'\n + '多个分类用 换行 \/ 分号 \/ 逗号 隔开都行。\\n'\n + '在视频详情页长按简介里的分类或演员名,会自动复制成「名称::网址」。\\n\\n'\n + '还是不行就点右边的「🔧诊断」把结果发我。';\n } else {\n var arr = [];\n var err = '';\n try { arr = fkey(); } catch (e) { err = e + ''; }\n if (err) tip = '❎ 解析出错:' + err;\n else if (!arr.length) tip = '❎ 读到了内容,但没识别出有效条目:\\n' + String(raw).substring(0, 300)\n + '\\n\\n每条要像这样:分类名::https:\/\/mjv006.com\/zh\/xxx\/';\n else {\n var names = [];\n for (var i = 0; i < arr.length; i++) names.push(' ' + (i + 1) + '. ' + arr[i].split('::')[0]);\n tip = '✅ 已识别 ' + arr.length + ' 个分类:\\n\\n' + names.join('\\n')\n + '\\n\\n返回后发现页生效(需重新进发现页)。';\n }\n }\n java.longToast('\\n' + tip);\n}\n\n\/* 备份:写入源变量,避免 loginInfo 读取异常时丢失 *\/\nfunction saveFkey() {\n var n = 0;\n try { n = normFkey(); } catch (e) {}\n if (!n) { try { n = fkeyCount(fkeyRaw()); } catch (e0) {} }\n var raw = fkeyRaw();\n if (!String(raw).trim()) {\n java.longToast('\\n❎ 读到的内容为空。\\n请先填写 → 点右上角 √ 保存 → 再进本页点备份。\\n\\n若已保存仍读不到,点「🔧诊断」看原因。');\n return;\n }\n java.longToast('\\n✅ 已备份 ' + n + ' 条:\\n\\n' + String(raw).substring(0, 300)\n + '\\n\\n备份内容存在源变量 fkey 里,即使登录信息丢失也能恢复。');\n}\n\n\/* 清空备份 *\/\nfunction clearFkey() {\n var src = _fkSrc();\n try { if (src) src.put('fkey', ''); } catch (e) {}\n java.longToast('\\n✅ 已清空备份的自定义分类。\\n(发现页会只剩内置分类,登录框里的内容不受影响)');\n}\n\/* =============== [自定义分类 · 修复块 end] =============== *\/\n\ntry { normFkey(); } catch (e) {}\n",
"preload": true,
"preloadJs": "window.java = java;\nwindow.run = (typeof run !== 'undefined') ? run : function () { return ''; };\nwindow.source = source;\nwindow.importScriptAwait = importScriptAwait;\nwindow.playu = function(url) {\n \/\/[修复] 1) cache 在 WebView 中可能未注入,直接解引用会 ReferenceError 导致取不到播放链接\n \/\/ 2) 实测该站从加载到 m3u8 出现约需 10~13 秒(含大量广告请求),9 秒超时偏紧\n var h = '';\n try { h = (typeof cache != 'undefined' && cache && cache.get) ? (cache.get(url) + '') : ''; } catch (e) { h = ''; }\n return webViewGetSourceAwait(h, url, null, \".+cdn.+\\\\.m3u8\", true, 15000);\n}",
"ruleArticles": "<js>\nlet url = java.hexDecodeToString(result) + '';\nlet sf = url.replace(\/^http.{4}[^\\\/]+\/, '');\nurl = surl() + sf;\nlet webrh = cache.get(url) + '';\nif (webrh.length < 6999) {\n webrh = java.ajax(url);\n webrh = rrcheck(url, webrh);\n let webr = org.jsoup.Jsoup.parse(webrh);\n webr = webr.select('.posts>.post');\n if (webr == '') {\n result = [];\n } else {\n let fmurl = webr.first().select(\"img\").attr(\"src\");\n if (fmurl == '') {\n result = [];\n } else {\n \/\/正确网络加载\n result = webr;\n let cfm = true;\n if (\/animation_list\\\/all|dt_list\\\/all\/.test(url)) {\n cache.put(url, webrh, 3600 * 24 * 3);\n } else if (\/content_news\\\/all\/.test(url)) {\n let _e1 = (result.size() > 1) ? result.get(1) : null;\n let fmurlmr = _e1 ? _e1.select(\"img\").attr(\"src\") : '';\n source.put('fmurlmr', fmurlmr);\n cache.put(url, webrh, 3600 * 24 * 2);\n } else if (\/fc_search\\\/all\/.test(url)) {\n cache.put(url, webrh, 3600 * 24 * 2);\n } else if (\/uncensored_list\\\/all|chinese_list\\\/all\/.test(url)) {\n cache.put(url, webrh, 3600 * 24);\n } else {\n if (!url.includes(\"_random\")) {\n cfm = false;\n switch (String(Date.now()).substr(-1)) {\n case \"1\":\n source.put('fmsj1', fmurl);\n break;\n case \"2\":\n source.put('fmsj2', fmurl);\n break;\n case \"3\":\n source.put('fmsj3', fmurl);\n break;\n }\n }\n cache.put(url, webrh, 3600 * 4);\n \/\/其他分类\n }\n\n if (cfm) source.put('fm' + sf, fmurl);\n \/\/有封面\n }\n \/\/有dom\n }\n \/\/没缓存\n} else {\n let mk = cache.getFromMemory(url + '2') || 0;\n cache.putMemory(url + '2', mk + 1);\n let tmh = new Date().getHours();\n if (\/content_news\\\/all|animation_list\\\/all|fc_search\\\/all\/.test(url)) {\n mk > 2 && cache.delete(url);\n } else if (mk > 1 || tmh > 1 && tmh < 4) {\n cache.deleteMemory(url + '2');\n cache.delete(url);\n }\n webrh = org.jsoup.Jsoup.parse(webrh);\n result = webrh.select('.posts>.post');\n}\nresult\n<\/js>",
"ruleContent": "@js:\nlet uu = java.hexDecodeToString(result) + '';\nresult = uu.replace(\/^https|[\\\/\\.\\_\\-\\:\\s]|html$\/g, '');\nlet ssurl = surl();\nlet url = ssurl + uu;\nif (uu.startsWith(\"http\")) url = uu;\nlet webr = cache.get(url) + '';\nif (webr.length < 6999) {\n webr = java.ajax(url);\n webr = rrcheck(url, webr);\n cache.put(url, webr, 3600 * 24 * 20);\n}\nlet title = gets('h1.0@text', webr);\nlet pimg = gets('#player-wrap img@src', webr);\nlet yimgs = gets('.ut1_img_content_smallcg .img_smallcg_hc@html||.ut1_img_content_smallcg div@html', webr).replaceAll('https:\/\/', 'https:\/\/cdn.cdnjson.com\/pic.html?url=');\nlet jianjie = gets('.posts-inner-details-text,.actor-right-details-images,.posts-inner-details-text@html', webr).replaceAll('https:\/\/pics.', 'https:\/\/cdn.cdnjson.com\/pic.html?url=pics.');\n\n\nlet posts = org.jsoup.Jsoup.parse(webr).select('.video .posts');\nposts.select('video').remove();\n\n\/\/获取配置\nvar info = uinfo();\nvar pc = info['播放器'] || '默认';\n\/\/[修复] 未打开配置页时 info 各项为 undefined,会导致下面生成 0.u \/ undefined 而整块脚本崩溃\nvar _lv = String(info['滑动等级']==undefined?'':info['滑动等级']).charAt(0);\nif(!\/^[0-9]$\/.test(_lv)) _lv='3';\nvar _ff = Number(info['长按⏩倍速']); if(!(_ff>0)) _ff=3;\nvar _tz = Number(info['双击⏭️时间']); if(!(_tz>0)) _tz=10;\nlet imgc='';\nif (info['预览截图']=='✅') {\n imgc = `<div class=\"img_content\">${yimgs}<\/div>`;\n}\nlet sjzt='';\nif (info['随机主题']=='✅') {\n sjzt = `<h2 onclick=\"sjzt()\" style=\"text-align: center;\">▼随机主题▼<\/h2>${posts}`;\n}\n\nresult = `\n <!DOCTYPE html>\n <html lang=\"zh-CN\">\n \n <head>\n <meta charset=\"UTF-8\">\n <title>${rssArticle.sort}<\/title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n <style>.artplayer-app{position:relative;display:block;width:100%;height:0;padding-bottom:56.25%;min-height:200px;background:#000;overflow:hidden;box-sizing:border-box}.artplayer-app>img{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}.play-indicator{position:absolute;top:0;left:0;width:100%;height:100%;display:-webkit-box;-webkit-box-align:center;-webkit-box-pack:center;display:-webkit-flex;-webkit-align-items:center;-webkit-justify-content:center;display:flex;align-items:center;justify-content:center;font-size:44px;line-height:1;color:#fff;text-shadow:0 0 10px rgba(0,0,0,0.9);z-index:9;transform:none;-webkit-transform:none;margin:0}.av18-diag{margin:10px 0;padding:10px;border:1px solid #ff6600;background:#fff6e5;color:#333;font-size:13px;line-height:1.7;word-break:break-all;white-space:pre-wrap}<\/style><script>window.__err=[];window.addEventListener('error',function(e){try{window.__err.push(String(e.message)+' @'+(e.lineno||0));}catch(x){}});window.addEventListener('unhandledrejection',function(e){try{window.__err.push('promise:'+String(e.reason));}catch(x){}});<\/script>\n <\/head>\n \n <body>\n <div class=\"artplayer-app\">\n${pc == '默认'?'':'<img src=\"'+pimg+'\" width=\"100%\">'}\n<\/div>\n <div class=\"jianjie\">\n <h3 id=\"title\">${title}<\/h3>\n ${imgc}\n ${jianjie}\n <div id=\"pyurl\"><\/div>\n <br>\n <\/div>\n ${sjzt}\n <br>\n <br>\n <script>\n window.local = true;\n\/\/ window.run(\"toastjs\").then(r=>eval(r));\n <\/script>\n <script>\nconst posts=document.querySelector('.posts');\nfunction sjzt() {\n\tposts.classList.toggle('active');\n\t}\nlet mrp=${pc == '默认'};\nfunction setting() {\n\tlet art = new Artplayer({\n container: '.artplayer-app',\n poster: '${pimg}',\n id: '${result}',\n theme: '#ffad00',\n autoplay: ${info['自动播放']=='✅'},\n muted: false,\n fullscreen: true,\n autoSize: true,\n autoMini: false,\n playbackRate: true,\n setting: true,\n lock: true,\n gesture: true,\n fastForward: true,\n autoPlayback: ${info['继续播放']=='✅'},\n autoOrientation: true,\n });\n Artplayer.RECONNECT_TIME_MAX = 3;\n Artplayer.LOG_VERSION = false;\n Artplayer.PLAYBACK_RATE = [0.75, 1, 2, 4, 6];\n Artplayer.AUTO_PLAYBACK_MAX=50;\n Artplayer.MOBILE_DBCLICK_PLAY = false;\n Artplayer.TOUCH_MOVE_RATIO = 0.${_lv};\n Artplayer.FAST_FORWARD_TIME = 500;\n Artplayer.FAST_FORWARD_VALUE=${_ff};\n \n art.on('dblclick', (event) => {\n\t const tmz = ${_tz};\n const clix = event.clientX;\n const rwid = art.rect.width;\n\t if (clix < rwid \/ 3) art.backward = tmz;\n\t else if (clix > rwid * 2 \/ 3) art.forward = tmz;\n\t else art.toggle();\n});\n\nart.once('ready', () => {\nlet lo=localStorage.getItem(\"artplayer_settings\")+'';\nif (lo.length<9)\nlocalStorage.setItem('artplayer_settings',source.get('localS'));\nelse\nsource.put('localS',lo); \n});\n\nart.on('error', (ee, tt) => {\n if (tt>2) {\n source.put(\"av18${result}\", '');\n }\n });\nreturn art;\n}\n\nasync function init () {\nvar art = null;\n\/*[修复] 实测两件事,因此不再使用 Artplayer,改由 hls.js + 原生 video 播放:\n 1) 该站 CDN 有 Referer 防盗链:不带 Referer 必然 404。Artplayer \/ 外部播放器 \/ 系统播放器\n 都是拿裸 URL 去请求,一定失败;只有页面内由 WebView 发起的请求才会自动带上 Referer。\n 2) Artplayer 在这套参数下会抛 \"Cannot read properties of undefined (reading 'enabled')\",\n 且它依赖 unpkg.com,国内经常加载不到。 *\/\nmrp = false;\nlet srcurl = source.get(\"av18${result}\");\n if (!srcurl) {\n try {\n srcurl = await playu(\"${url}\");\n } catch(err) {\n console.log(err);\n srcurl = '';}\n if (srcurl.length > 9) {\n source.put(\"av18${result}\", srcurl);\n } else {\n java.longToast('💔未获取到播放链接');\n cache.delete(\"${url}\");\n }\n }\nconst u = srcurl;\nconst uok = (typeof u === 'string' && u.length > 9 && (u.indexOf('http:') === 0 || u.indexOf('https:') === 0));\nconst _tip = function (m) { try { java.longToast(m); } catch (e) {} };\n\nfunction _ensureDiagBox() {\n var box = document.getElementById('av18diag');\n if (!box) {\n box = document.createElement('div');\n box.id = 'av18diag';\n box.className = 'av18-diag';\n box.style.display = 'none';\n var py = document.querySelector('.artplayer-app');\n if (py && py.parentNode) py.parentNode.insertBefore(box, py.nextSibling);\n else document.body.appendChild(box);\n }\n return box;\n}\nfunction _diag(obj) {\n window.__diag = obj;\n try { obj['页面地址'] = location.href; } catch (e0) {}\n try { obj['页面错误'] = window.__err || []; } catch (e) {}\n try { console.log('AV18DIAG ' + JSON.stringify(obj)); } catch (e2) {}\n try {\n var box = _ensureDiagBox();\n box.textContent = '诊断信息(可截图发给开发者)' + String.fromCharCode(10) + JSON.stringify(obj, null, 1);\n box.style.display = 'block';\n } catch (e3) {}\n return obj;\n}\n\n\/* ================= 播放器核心:hls.js + 原生 video ================= *\/\nconst HLS_CDN = [\n 'https:\/\/cdn.jsdelivr.net\/npm\/hls.js@1.5.17\/dist\/hls.min.js',\n 'https:\/\/unpkg.com\/hls.js@1.5.17\/dist\/hls.min.js',\n 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/hls.js\/1.5.17\/hls.min.js',\n 'https:\/\/cdn.bootcdn.net\/ajax\/libs\/hls.js\/1.5.17\/hls.min.js'\n];\nconst _NL = String.fromCharCode(10);\n\nfunction _abs(base, p) {\n p = String(p == null ? '' : p);\n if (!p) return p;\n if (p.indexOf('http:') === 0 || p.indexOf('https:') === 0) return p;\n try { return new URL(p, base).href; } catch (e) {}\n while (p.charAt(0) === '\/') p = p.substring(1);\n var i = base.lastIndexOf('\/');\n var dir = i > 0 ? base.substring(0, i + 1) : base + '\/';\n return dir + p;\n}\nfunction _loadJs(src, ms) {\n return new Promise(function (res, rej) {\n var sc = document.createElement('script');\n var done = false;\n var t = setTimeout(function () { if (!done) { done = true; rej(new Error('超时')); } }, ms || 15000);\n sc.src = src;\n sc.onload = function () { if (!done) { done = true; clearTimeout(t); res(); } };\n sc.onerror = function () { if (!done) { done = true; clearTimeout(t); rej(new Error('加载失败')); } };\n document.head.appendChild(sc);\n });\n}\nasync function _loadHls() {\n var errs = [];\n for (var i = 0; i < HLS_CDN.length; i++) {\n try {\n await _loadJs(HLS_CDN[i]);\n if (typeof Hls !== 'undefined') return { ok: HLS_CDN[i], errs: errs };\n errs.push(HLS_CDN[i] + ':已加载但无 Hls');\n } catch (e) { errs.push(HLS_CDN[i].replace('https:\/\/', '') + ':' + e.message); }\n }\n return { ok: null, errs: errs };\n}\n\/* 把清单里的相对地址(含 AES key 的 URI)全部改写成绝对地址。\n 实测 EXT-X-KEY 的 URI 是相对的 aavv.vv,不改的话喂 blob: 后 hls.js 解析不到,会 fragLoadError。 *\/\nasync function _absPlaylist(mu) {\n var r = await fetch(mu);\n if (!r.ok) throw new Error('清单 HTTP ' + r.status + '(无 Referer 时 CDN 返回 404)');\n var txt = await r.text();\n return txt.split(_NL).map(function (line) {\n var t = line.trim();\n if (!t) return line;\n if (t.charAt(0) === '#') {\n var k = t.indexOf('URI=');\n if (k < 0) return line;\n var q1 = t.indexOf(String.fromCharCode(34), k);\n var q2 = q1 < 0 ? -1 : t.indexOf(String.fromCharCode(34), q1 + 1);\n if (q1 < 0 || q2 <= q1) return line;\n return t.substring(0, q1 + 1) + _abs(mu, t.substring(q1 + 1, q2)) + t.substring(q2);\n }\n return _abs(mu, t);\n }).join(_NL);\n}\n\/* 手势:双击左右快进 \/ 中间暂停,长按右侧倍速,横向拖动 Seek(替代 Artplayer 的那套) *\/\nfunction _bindControls(v) {\n var FFV = ${_ff}, TMZ = ${_tz}, RATIO = 0.${_lv};\n if (!(FFV > 0)) FFV = 3;\n if (!(TMZ > 0)) TMZ = 10;\n if (!(RATIO > 0)) RATIO = 0.3;\n var pt = null, sx = 0, stime = 0;\n function rect() { return v.getBoundingClientRect(); }\n v.addEventListener('dblclick', function (ev) {\n var r = rect(), x = ev.clientX - r.left;\n if (x < r.width \/ 3) v.currentTime = Math.max(0, v.currentTime - TMZ);\n else if (x > r.width * 2 \/ 3) v.currentTime = v.currentTime + TMZ;\n else { if (v.paused) v.play(); else v.pause(); }\n });\n v.addEventListener('touchstart', function (ev) {\n if (!ev.touches || !ev.touches.length) return;\n var r = rect(), x = ev.touches[0].clientX - r.left;\n sx = x; stime = v.currentTime;\n if (x > r.width * 0.72) pt = setTimeout(function () { v.playbackRate = FFV; _tip('⏩ ' + FFV + ' 倍速'); }, 500);\n }, { passive: true });\n v.addEventListener('touchmove', function (ev) {\n if (!ev.touches || !ev.touches.length) return;\n var r = rect(), x = ev.touches[0].clientX - r.left;\n if (Math.abs(x - sx) > 12) {\n if (pt) { clearTimeout(pt); pt = null; v.playbackRate = 1; }\n v.currentTime = Math.max(0, Math.min(v.duration || 0, stime + (x - sx) * RATIO));\n }\n }, { passive: true });\n function endTouch() { if (pt) { clearTimeout(pt); pt = null; } v.playbackRate = 1; }\n v.addEventListener('touchend', endTouch);\n v.addEventListener('touchcancel', endTouch);\n}\nasync function _startPlay(pyv, mu) {\n var h = await _loadHls();\n if (!h.ok) {\n _diag({ '失败环节': '加载 hls.js', '已尝试': h.errs, '说明': '四个 CDN 都不通,请检查网络' });\n return null;\n }\n var pl;\n try { pl = await _absPlaylist(mu); }\n catch (e2) {\n _diag({ '失败环节': '取 m3u8 清单', '原因': e2.message,\n '说明': '该站 CDN 有 Referer 防盗链。若页面地址是 data: 或 about:blank,请求不会带 Referer,会直接 404' });\n return null;\n }\n var v = document.createElement('video');\n v.controls = true; v.playsInline = true;\n v.setAttribute('webkit-playsinline', 'true');\n v.setAttribute('x5-playsinline', 'true');\n v.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;background:#000';\n pyv.appendChild(v);\n var burl = URL.createObjectURL(new Blob([pl], { type: 'application\/vnd.apple.mpegurl' }));\n var hl = new Hls({ enableWorker: true });\n hl.on(Hls.Events.ERROR, function (e3, dd) {\n if (dd && dd.fatal) _diag({ '失败环节': '播放', '错误': dd.type + '\/' + dd.details });\n });\n hl.loadSource(burl);\n hl.attachMedia(v);\n _bindControls(v);\n return v;\n}\n\nlet pyv = document.querySelector('.artplayer-app');\nif (!pyv) { pyv = document.createElement('div'); pyv.className = 'artplayer-app'; document.body.appendChild(pyv); }\n\/*[修复] 高度兜底:直接算像素高度写回,不依赖 aspect-ratio \/ min-height 是否被注入 *\/\nvar _sz = function () {\n var w = pyv.clientWidth || document.documentElement.clientWidth || window.innerWidth || 360;\n if (!w) { setTimeout(_sz, 80); return; }\n pyv.style.height = Math.round(w * 9 \/ 16) + 'px';\n pyv.style.paddingBottom = '0';\n};\n_sz();\nwindow.addEventListener('resize', _sz);\n\nvar _started = false, _ov = null;\nfunction _setOv(t) { if (_ov) _ov.textContent = t; }\nasync function _go() {\n if (_started) return;\n _started = true;\n if (!uok) {\n _tip('💔未获取到播放链接,请返回重试');\n _diag({ '失败环节': '抓取 m3u8', 'srcurl长度': (u || '').length,\n 'playu可用': (typeof playu === 'function'), 'java可用': (typeof java !== 'undefined') });\n return;\n }\n _setOv('⏳正在加载播放器…');\n var v = await _startPlay(pyv, u);\n if (!v) { _setOv('⚠️播放失败,见下方诊断'); return; }\n if (_ov && _ov.parentNode) { try { _ov.parentNode.removeChild(_ov); } catch (e) {} _ov = null; }\n try { v.play(); } catch (e4) {}\n}\n\nif (${info['自动播放']=='✅'}) _go();\nelse {\n _ov = document.createElement('div');\n _ov.className = 'play-indicator';\n _ov.textContent = '▶️';\n pyv.appendChild(_ov);\n pyv.addEventListener('click', function () { _go(); });\n}\nif (${info['显示链接']=='✅'})\ndocument.getElementById(\"pyurl\").innerHTML=\\`<p>播放链接:<span>\\${u}<\/span><button onclick=\"copyText(this,'before')\" style=\"margin-left: 2ex;\">⎘复制<\/button><\/p>\\`;\n\t}\nsetTimeout(init, 50);\n<\/script>\n <\/body>\n <\/html>\n `;",
"ruleImage": "img@src",
"ruleLink": "a.0@href\n<js>\nlet url=java.base64Encode(result.replace(\/^http.{4}[^\\\/]+\/,''));\nresult+`@js:'data:;base64,${url},{\"type\":\"18av\"}'`;\n<\/js>",
"ruleNextPage": "page",
"rulePubDate": ".con .meta@text",
"ruleTitle": ".con a@text",
"searchUrl": "data:;base64,{{java.base64Encode(\"\/zh\/fc_search\/all\/\"+key+\"\/\"+page+\".html\")}},{\"type\":\"18av\"}",
"shouldOverrideUrlLoading": "if (url.includes(\"\/chinese_category\/\")) {\n\tjava.open(\"sort\",`data:;base64,{{java.base64Encode(\"${url.replace(\/[^\\\\\/].html\/,'')}\"+page+\".html\")}},{\"type\":\"18av\"}`);\n\ttrue\n}\nelse false",
"showWebLog": true,
"singleUrl": false,
"sortUrl": "@js:\nvar _fk = \"\";\ntry { _fk = fkey().join(\"\\n\"); } catch (e) { _fk = \"\"; }\n`中文字幕::data:;base64,{{java.base64Encode(\"\/zh\/chinese_list\/all\/\"+page+\".html\")}},{\"type\":\"18av\"}\n每日更新::data:;base64,{{java.base64Encode(\"\/zh\/content_news\/all\/\"+tkey()+\".html\")}},{\"type\":\"18av\"}\n随机近作::data:;base64,{{java.base64Encode(\"\/zh\/chinese_random\/all\/index.html?t=\"+page)}},{\"type\":\"18av\"}\n随机所有::data:;base64,{{java.base64Encode(\"\/zh\/chinese_randomall\/all\/index.html?t=\"+page)}},{\"type\":\"18av\"}${_fk ? \"\\n\" + _fk : \"\"}`",
"sourceComment": "地址发佈页:mm18vd.com、mm18news.com\n邮件到 ebnrks87@gmail.com 或 ebnrks88@gmail.com 获取最新网站网址。",
"sourceGroup": "🙈",
"sourceIcon": "https:\/\/gitee.com\/lyc486\/yuedu\/raw\/master\/0GP0yD.gif",
"sourceName": "18AV",
"sourceUrl": "https:\/\/mjv006.com",
"startHtml": "@js:\n(function() {\n\n var info = uinfo();\n if (info['启动页'] != '✅') {\n return '';\n }\n\n let su = surl();\n let zm = source.get('fm' + \"\/zh\/chinese_list\/all\/1.html\") + '';\n let wm = source.get('fm' + \"\/zh\/uncensored_list\/all\/1.html\") + '';\n let sjd = source.get('fm' + \"\/zh\/chinese_random\/all\/index.html?t=1\") + '';\n let sja = source.get('fm' + \"\/zh\/chinese_randomall\/all\/index.html?t=1\") + '';\n let sjm = source.get('fm' + \"\/zh\/animation_random\/all\/index.html?t=1\") + '';\n let gc = source.get('fm' + \"\/zh\/dt_list\/all\/1.html\") + '';\n let dm = source.get('fm' + \"\/zh\/animation_list\/all\/1.html\") + '';\n let mr = source.get('fmurlmr') + '';\n let fmsj1 = source.get('fmsj1') + '';\n let fmsj2 = source.get('fmsj2') + '';\n\n return `<html>\n<head>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n <title>18AV<\/title>\n<\/head>\n<body>\n <div class=\"container\">\n <form id=\"ss\">\n <input type=\"text\" name=\"key\" id=\"searchkey\" placeholder=\"输入关键词\">\n <button type=\"submit\">搜 索<\/button>\n <\/form>\n<div class=\"history-container\">\n<div class=\"history-header\">\n<div class=\"history-title\">搜索历史<\/div>\n<button id=\"clearHistory\">清除记录<\/button>\n<\/div>\n<div id=\"historyList\"><\/div>\n<\/div>\n <div id=\"fl\">\n <div onclick=\"ff('中文字幕')\">${zm?`<img src=\"${zm}\" alt=\"暂无图片\">`:''}<span>中文字幕<\/span><\/div>\n <div onclick=\"ff('高清无码')\">${wm?`<img src=\"${wm}\" alt=\"暂无图片\">`:''}<span>高清无码<\/span><\/div>\n <div onclick=\"ff('动画动漫')\">${dm?`<img src=\"${dm}\" alt=\"动画动漫\">`:''}<span>动画动漫<\/span><\/div>\n <div onclick=\"ff('随机近作')\">${sjd?`<img src=\"${sjd}\" alt=\"暂无图片\">`:''}<span>随机近作<\/span><\/div>\n <div onclick=\"ff('随机所有')\">${sja?`<img src=\"${sja}\" alt=\"暂无图片\">`:''}<span>随机所有<\/span><\/div>\n <div onclick=\"ff('随机动漫')\">${sjm?`<img src=\"${sjm}\" alt=\"暂无图片\">`:''}<span>随机动漫<\/span><\/div>\n <div onclick=\"ff('每日更新')\">${mr?`<img src=\"${mr}\" alt=\"暂无图片\">`:''}<span>每日更新<\/span><\/div>\n <div onclick=\"ff('国产自拍')\">${gc?`<img src=\"${gc}\" alt=\"暂无图片\">`:''}<span>国产自拍<\/span><\/div>\n <div onclick=\"ff('自选分类')\">${fmsj2?`<img src=\"${fmsj2}\" alt=\"暂无图片\">`:''}<span>自选分类<\/span><\/div>\n <div onclick=\"window.location.href='${su}\/zh\/chinese_categorylist\/list\/index.html'\">${fmsj1?`<img src=\"${fmsj1}\" alt=\"暂无图片\">`:''}<span>分类清单<\/span><\/div>\n <\/div>\n <div class=\"bottom-line\"><\/div>\n <\/div>\n<\/body>\n<\/html>`;\n\n})();",
"startJs": "const HISTORY_KEY = 'searchHistory';\nconst MAX_HISTORY_ITEMS = 10;\n const ss = document.getElementById('ss');\n const historyList = document.getElementById('historyList');\n const clearHistoryBtn = document.getElementById('clearHistory');\n const searchInput = document.getElementById('searchkey');\n function performSearch(keyword) {\n if (!(keyword.length>1)) return;\n addHistoryItem(keyword);\n searchInput.value=\"\";\n java.open(\"sort\", JSON.stringify({ [keyword]: `data:;base64,{{java.base64Encode(\"\/zh\/fc_search\/all\"+\"\/${keyword}\/\"+page+\".html\")}},{\"type\":\"18av\"}` }));\n }\n \r\n ss.addEventListener('submit', function (event) {\r\n event.preventDefault();\r\n const formData = new FormData(ss);\r\n const key = formData.get('key');\r\n if (key) {\n \tperformSearch(key);\n } else {\n \tsearchInput.placeholder = \"输入关键词,亲\";\n searchInput.focus();}\r\n });\n\/\/const tt = new Intl.DateTimeFormat('zh-CN', {year: 'numeric', month: '2-digit', day: '2-digit'}).format(new Date()).replace(\/\\\/\/g, '-');\nconst fl = {\n\t\"中文字幕\":`data:;base64,{{java.base64Encode(\"\/zh\/chinese_list\/all\/\"+page+\".html\")}},{\"type\":\"18av\"}`,\n\t\t\"高清无码\":`data:;base64,{{java.base64Encode(\"\/zh\/uncensored_list\/all\/\"+page+\".html\")}},{\"type\":\"18av\"}`,\n\t\t\t\"随机近作\":`data:;base64,{{java.base64Encode(\"\/zh\/chinese_random\/all\/index.html?t=\"+page)}},{\"type\":\"18av\"}`,\n\t\t\t\t\"随机所有\":`data:;base64,{{java.base64Encode(\"\/zh\/chinese_randomall\/all\/index.html?t=\"+page)}},{\"type\":\"18av\"}`,\n\t\t\t\t\t\"国产自拍\":`data:;base64,{{java.base64Encode(\"\/zh\/dt_list\/all\/\"+page+\".html\")}},{\"type\":\"18av\"}`,\n\t\t\t\t\t\"每日更新\":`data:;base64,{{java.base64Encode(\"\/zh\/content_news\/all\/\"+tkey()+\".html\")}},{\"type\":\"18av\"}`,\n\t\t\t\t\t\"动画动漫\":`data:;base64,{{java.base64Encode(\"\/zh\/animation_list\/all\/\"+page+\".html\")}},{\"type\":\"18av\"}`,\n\t\t\t\t\t\"随机动漫\":`data:;base64,{{java.base64Encode(\"\/zh\/animation_random\/all\/index.html?t=\"+page)}},{\"type\":\"18av\"}`\n\t}\n function ff(n) {\n \tif (n=='自选分类') {\n \t\t(async () => {\n \t\t\tconst fkey = await window.run(\"uinfo()['自定义分类']\");\n \t\t\tlet obj = {};\n \t\t\tif (fkey.length>9) {\n \t\t\tfkey.trim().split(\/\\s+\/).forEach(x=> {\n \t\t\tlet [y1,y2] = x.split('::');\n \t\t\tobj[y1]=`data:;base64,{{java.base64Encode(\"${y2.replace(\/\\d+\\.html$\/,'\"+page+\".html\"')})}},{\"type\":\"18av\"}`});\n \t\tjava.open(\"sort\", JSON.stringify(obj));\n \t\t} else alert(\"请在登录界面填写自定义分类\")\n})();\n \t\treturn\n \t\t}\n \tlet url = fl[n];\n \tif (url) java.open(\"sort\", JSON.stringify({ [n]: url}));\n \t}\r\n\r\n ss.style.animation = `fadeIn 0.6s ease-out forwards`;\r\n document.querySelectorAll('#fl > div').forEach((child, index) => {\r\n child.style.animation = `fadeIn ${0.8 + index * 0.3}s ease-out forwards`;\r\n });\n \nfunction loadHistory() {\n return JSON.parse(localStorage.getItem(HISTORY_KEY)) || [];\n}\nfunction saveHistory(history) {\nlocalStorage.setItem(HISTORY_KEY, JSON.stringify(history));\n}\nfunction addHistoryItem(keyword) {\n let history = loadHistory();\n history = history.filter(item => item !== keyword);\n history.unshift(keyword);\n if (history.length > MAX_HISTORY_ITEMS) {\n history = history.slice(0, MAX_HISTORY_ITEMS);\n }\n saveHistory(history);\n renderHistory();\n }\nfunction removeHistoryItem(keyword) {\n let history = loadHistory();\n history = history.filter(item => item !== keyword);\n saveHistory(history);\n renderHistory();\n }\nfunction clearHistory() {\n if (confirm('确定要清除所有搜索历史吗?')) {\n localStorage.removeItem(HISTORY_KEY);\n renderHistory();\n }\n }\n\/\/ 显示历史\n function renderHistory() {\n const history = loadHistory();\n historyList.innerHTML = '';\n if (history.length === 0) {\n historyList.innerHTML = '<div class=\"no-history\">暂无搜索历史<\/div>';\n return;\n }\n \n history.forEach(keyword => {\n const item = document.createElement('div');\n item.className = 'history-item';\n item.innerHTML = `${keyword}<span class=\"fa-times\" data-keyword=\"${keyword}\">🗑️<\/span>`;\n historyList.appendChild(item);\n \n \/\/ 点击搜索\n item.addEventListener('click', (e) => {\n if (!e.target.classList.contains('fa-times')) {\n searchInput.value = keyword;\n performSearch(keyword);\n }\n });\n \n \/\/ 处理删除按钮\n const deleteBtn = item.querySelector('.fa-times');\n deleteBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n removeHistoryItem(keyword);\n });\n });\n }\n\nclearHistoryBtn.addEventListener('click',clearHistory); \nrenderHistory();\n\n\/\/console.log(document.documentElement.outerHTML);",
"startStyle": " * {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;\n user-select: none;\r\n }\r\n ::-webkit-scrollbar {\r\n display: none;\r\n }\r\n body {\r\n display: flex;\r\n justify-content: center;\r\n min-height: 100vh;\r\n background: linear-gradient(135deg, #617696 0%, #e4edf5 100%);\r\n padding: 10px 20px 20px 20px;\r\n color: #333;\r\n }\r\n .container {\r\n width: 100%;\r\n text-align: center;\r\n background: rgba(255, 255, 255, 0.86);\r\n border-radius: 24px;\r\n box-shadow: 0 15px 50px rgba(0, 0, 0, 0.08);\r\n position: relative;\r\n overflow-y: auto;\r\n }\r\n .container::before {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 28px;\r\n background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);\r\n }\r\n .bottom-line {\r\n width: 100%;\r\n height: 28px;\r\n background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);\r\n }\r\n #ss {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n margin: 30px auto;\r\n width: 100%;\r\n position: relative;\r\n }\r\n #ss input[type=\"text\"] {\r\n width: 60%;\r\n padding: 0px 20px;\r\n margin-right: 10px;\r\n height: 60px;\r\n font-size: 1.15rem;\r\n border: 2px solid #e3eef9;\r\n border-radius: 16px;\r\n outline: none;\r\n background: #ffffff;\r\n color: #2c3e50;\r\n box-shadow:\r\n 0 5px 20px rgba(0, 0, 0, 0.03),\r\n inset 0 1px 3px rgba(0, 0, 0, 0.05);\r\n transition: all 0.3s ease;\r\n }\r\n #ss input[type=\"text\"]::placeholder {\r\n color: #b0c1d3;\r\n font-weight: 400;\r\n }\r\n #ss input[type=\"text\"]:focus {\r\n border-color: #4facfe;\r\n box-shadow:\r\n 0 8px 25px rgba(79, 172, 254, 0.15),\r\n inset 0 1px 3px rgba(0, 0, 0, 0.03);\r\n }\r\n #ss button {\r\n position: relative;\r\n width: 30%;\r\n height: 40px;\r\n padding: 0px;\r\n font-size: 1.1rem;\r\n font-weight: 600;\r\n border: none;\r\n border-radius: 12px;\r\n background: linear-gradient(90deg, #3f8abc 0%, #177eae 100%);\r\n color: white;\r\n transition: all 0.3s ease;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n #ss button:hover {\r\n background: linear-gradient(90deg, #4290d4 0%, #1c98d2 100%);\r\n transform: translateY(-2px);\r\n }\r\n #ss button:active {\r\n box-shadow: 0 4px 15px rgba(79, 172, 254, 0.3);\r\n transform: translateY(1px);\r\n }\r\n #ss button i {\r\n margin-right: 10px;\r\n font-size: 1rem;\r\n }\r\n #fl {\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: center;\r\n align-items: center;\r\n }\r\n #fl>div {\r\n width: 300px;\r\n aspect-ratio: 16\/9;\n height: 169px;\r\n overflow: hidden;\r\n \/* 约束图片高度 *\/\r\n border: 2px dotted;\r\n margin-bottom: 10px;\r\n position: relative;\r\n }\r\n #fl>div>span {\r\n display: block;\n z-index: 2;\r\n position: absolute;\r\n top: 3%;\r\n right: 3%;\r\n font-weight: bold;\r\n font-family: 'Microsoft YaHei', 'PingFang SC', 'Noto Sans CJK SC', 'Source Han Sans SC', 'Heiti SC', 'WenQuanYi Micro Hei', sans-serif;\n font-size: 15px;\r\n text-shadow: 2px 2px 6px #333, 0 0 20px #1c98d2;\r\n color: #16263a;\n background-color: rgba(191, 220, 245, 0.6);\r\n border-radius: 20px;\r\n padding: 5px 10px;\r\n }\r\n #fl>div>img {\r\n width: 100%;\r\n height: 100%;\r\n display: block;\r\n object-fit: cover;\r\n object-position: center center;\r\n filter: brightness(0.9);\r\n }\r\n @keyframes fadeIn {\r\n from {\r\n opacity: 0;\r\n transform: translateY(20px);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n }\n\n.history-container {\n width: 90%;\n max-width: 700px;\n margin: 0 auto 25px;\n padding: 15px;\n background: rgba(255, 255, 255, 0.95);\n border-radius: 16px;\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);\n text-align: left;\n animation: fadeIn 0.5s ease-out forwards;\n }\n .history-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eaeaea;\n }\n .history-title {\n font-size: 1.2rem;\n font-weight: 600;\n color: #2c3e50;\n display: flex;\n align-items: center;\n }\n #clearHistory {\n background: none;\n border: none;\n color: #888;\n font-size: 0.9rem;\n cursor: pointer;\n display: flex;\n align-items: center;\n transition: color 0.2s;\n }\n #clearHistory:hover {\n color: #ff6b6b;\n }\n #historyList {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n }\n .history-item {\n background: rgba(79, 172, 254, 0.1);\n border: 1px solid rgba(79, 172, 254, 0.3);\n border-radius: 50px;\n padding: 8px 10px 8px 16px;\n font-size: 0.95rem;\n transition: all 0.2s;\n color: #3f8abc;\n display: flex;\n align-items: center;\n }\n .history-item:hover {\n background: rgba(79, 172, 254, 0.2);\n transform: translateY(-2px);\n box-shadow: 0 3px 8px rgba(79, 172, 254, 0.2);\n }\n.fa-times {\nmargin-left: 10px;\nfont-size: 10px;\nwidth: 10px;\n\t}",
"style": "* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n html,\n body {\n width: 100%;\n overflow-x: hidden;\n overflow-wrap: break-word;\n word-break: break-all;\n }\n .artplayer-app {\n position: relative;\n display: block;\n width: 100%;\n height: 0;\n padding-bottom: 56.25%;\n min-height: 200px;\n background: #000;\n overflow: hidden;\n box-sizing: border-box;\n }\n .play-indicator {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 44px;\n line-height: 1;\n color: #fff;\n text-shadow: 0 0 10px rgba(0, 0, 0, 0.9);\n z-index: 9;\n transform: none;\n }\n .img_content {\n display: flex;\n flex-wrap: wrap;\n line-height: 0;\n justify-content: space-between;\n }\n .img_smallcg_hc {\n width:100vw;\n height: auto;\n }\n .img_smallcg img {\n width: 50vw;\n height: auto;\n }\n .posts-inner-details-text-top {\n display: flex;\n flex-wrap: wrap;\n }\n .posts-inner-details-text-left ul {\n display: flex;\n width: 50vw;\n flex-wrap: wrap;\n }\n .posts-inner-details-text-left li {\n width: 25vw;\n list-style: none;\n flex-wrap: wrap;\n }\n .posts-inner-details-text-under li span {\n margin-right: 1ex;\n }\n h3 {\n margin: 1ex;\n }\n .posts {\n \tmax-height: 0;\n \toverflow: hidden;\n \t}\n \t.posts.active {\n \tmax-height: max-content;\n \t}",
"type": 0
}