🔞(国内免翻版)绅士漫画

https://www.wn001.cfd/?qysg-source=2026.10.03.3

lonbeaubrunpjl81 (13720)3天前

免科学上网版绅士漫画
二维码导入(APP尚未完成该功能)
{
    "bookSourceUrl": "https:\/\/www.wn001.cfd\/?qysg-source=2026.10.03.3",
    "bookSourceName": " 🔞(国内免翻版)绅士漫画",
    "enabledExplore": true,
    "enabled": true,
    "bookSourceGroup": "WN001原生漫画",
    "author": "",
    "help": true,
    "html": "<!DOCTYPE html>\n<html lang=\"zh-CN\"><head>\n<meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>WN001 · 轻悦时光原生漫画源<\/title>\n<\/head><body>\n<p>WN001 轻悦时光原生漫画源,版本 2026.10.03.3。<\/p>\n<p>请完整导入原生书源 JSON。每个图集对应一本书,目录每 20 页生成一个阅读分段。<\/p>\n<p>遇到验证请在同一应用内网页完成,看到正常页面后返回。<\/p>\n<script>\nvar isCookieJar = true;\n(function () {\n  var loader = {version: \"2026.10.03.3\", implementation: null, error: \"\", completed: false};\n  window.__wn001Loader = loader;\n  window.wn001SourceVersion = loader.version;\n  window.isCookieJar = true;\n  window.addEventListener(\"error\", function (event) {\n    if (!loader.completed) loader.error = String(event.message || \"脚本加载错误\") + \"(行 \" + (event.lineno || \"未知\") + \")\";\n  });\n  function reportFailedLoad() {\n    if (loader.completed || loader.implementation) return;\n    var bridge = window.flutter_inappwebview;\n    if (!bridge || typeof bridge.callHandler !== \"function\") return;\n    loader.completed = true;\n    wn001DiagnosticLog(\"WN001 \" + loader.version + \":主脚本加载失败,\" + (loader.error || \"未执行完整 HTML\"));\n    try {\n      var result = bridge.callHandler(\"CookieJar\", true);\n      if (result && typeof result.catch === \"function\") result.catch(function () {});\n    } catch (_) {}\n  }\n  window.addEventListener(\"flutterInAppWebViewPlatformReady\", function () { setTimeout(reportFailedLoad, 1000); });\n  setTimeout(reportFailedLoad, 1500);\n})();\nfunction wn001DiagnosticLog(message) {\n  var bridge = window.flutter_inappwebview;\n  if (!bridge || typeof bridge.callHandler !== \"function\") return;\n  try {\n    var result = bridge.callHandler(\"log\", message);\n    if (result && typeof result.catch === \"function\") result.catch(function () {});\n  } catch (_) {}\n}\nasync function wn001Invoke(name, args) {\n  var loader = window.__wn001Loader;\n  wn001DiagnosticLog(\"WN001 \" + loader.version + \":入口 \" + name);\n  try {\n    if (!loader.implementation || typeof loader.implementation[name] !== \"function\") {\n      throw new Error(\"主脚本未完整执行:\" + (loader.error || \"请重新导入完整文件\"));\n    }\n    var result = await loader.implementation[name].apply(null, args);\n    wn001DiagnosticLog(\"WN001:\" + name + \" 完成,返回 \" + String(result == null ? \"\" : result).length + \" 字符\");\n    return result;\n  } catch (error) {\n    var message = \"WN001 \" + loader.version + \":\" + name + \" 失败:\" + String(error && error.message || error);\n    wn001DiagnosticLog(message);\n    try {\n      var bridge = window.flutter_inappwebview;\n      if (bridge && typeof bridge.callHandler === \"function\") {\n        var notice = bridge.callHandler(\"showToast\", message);\n        if (notice && typeof notice.catch === \"function\") notice.catch(function () {});\n      }\n    } catch (_) {}\n    throw error;\n  }\n}\nasync function search(key, page) { return await wn001Invoke(\"search\", [key, page]); }\nasync function info(bookurl) { return await wn001Invoke(\"info\", [bookurl]); }\nasync function chapter(tocUrl) { return await wn001Invoke(\"chapter\", [tocUrl]); }\nasync function content(url) { return await wn001Invoke(\"content\", [url]); }\nasync function getfinds() { return await wn001Invoke(\"getfinds\", []); }\nasync function find(url, page) { return await wn001Invoke(\"find\", [url, page]); }\nasync function getloginurl() { return await wn001Invoke(\"getloginurl\", []); }\nasync function login() { return await wn001Invoke(\"login\", []); }\nasync function pay(bookurl, url) { return await wn001Invoke(\"pay\", [bookurl, url]); }\nasync function gethelp() { return await wn001Invoke(\"gethelp\", []); }\nasync function verifyWN001() { return await wn001Invoke(\"verifyWN001\", []); }\nasync function cycleWN001Speed() { return await wn001Invoke(\"cycleWN001Speed\", []); }\nasync function clearWN001ImageCache() { return await wn001Invoke(\"clearWN001ImageCache\", []); }\nwindow.search = search; window.info = info; window.chapter = chapter; window.content = content;\nwindow.getfinds = getfinds; window.find = find; window.getloginurl = getloginurl; window.login = login;\nwindow.pay = pay; window.gethelp = gethelp; window.verifyWN001 = verifyWN001;\nwindow.cycleWN001Speed = cycleWN001Speed; window.clearWN001ImageCache = clearWN001ImageCache;\n\/\/ 所有原生入口和实现保留在同一个脚本块,兼容只提取首个 script 的导入方式。\n\/\/ WN001_MAIN_IMPLEMENTATION_BEGIN\n(function () {\n\"use strict\";\nvar baseurl = \"https:\/\/www.wn001.cfd\";\nvar sourceVersion = \"2026.10.03.3\";\nvar pagesPerChapter = 20;\nvar wn001UA = \"\", nativeReady = null, backgroundSettingsStarted = false, speedRevision = 0;\nwindow.wn001ReadyNotificationSent = false;\nvar requestQueue = Promise.resolve();\nvar detailCache = new Map(), pendingPages = new Map(), manifestCache = new Map(), pendingManifests = new Map();\nvar pendingContents = new Map(), searchPages = new Map();\nvar verificationPausedUntil = 0;\nvar imageConcurrency = 4, imageSpeedKey = \"WN001.imageConcurrency.v1\", activeImageRequests = 0, imageWaiters = [];\nvar imageCache = new Map(), pendingImages = new Map(), imageCacheCharacters = 0;\nvar imageCacheLimit = 12 * 1024 * 1024, imageCacheEntries = 128, imageCacheTTL = 10 * 60 * 1000;\nvar manifestTTL = 2 * 60 * 1000, imageCacheGeneration = 0, pageGeneration = 0, preferredImageScheme = \"https\";\nwindow.isCookieJar = true;\n\nfunction nativeCall(name) {\n  var bridge = window.flutter_inappwebview;\n  if (!bridge || typeof bridge.callHandler !== \"function\") return Promise.reject(new Error(\"请在轻悦时光的原生书源中运行本文件。\"));\n  var args = Array.prototype.slice.call(arguments, 1);\n  try { return Promise.resolve(bridge.callHandler.apply(bridge, [name].concat(args))); }\n  catch (error) { return Promise.reject(error); }\n}\nasync function optionalCall() {\n  var args = arguments, timer;\n  try {\n    return await Promise.race([nativeCall.apply(null, args), new Promise(function (resolve) {\n      timer = setTimeout(function () { resolve(null); }, 1500);\n    })]);\n  } catch (_) { return null; }\n  finally { clearTimeout(timer); }\n}\nfunction initializeWN001() {\n  if (nativeReady) return nativeReady;\n  nativeReady = (async function () {\n    wn001UA = wn001UA || navigator.userAgent;\n    var names = [\"search\", \"info\", \"chapter\", \"content\", \"getfinds\", \"find\", \"getloginurl\", \"login\", \"gethelp\"];\n    var missing = names.filter(function (name) { return typeof window[name] !== \"function\"; });\n    if (missing.length) throw new Error(\"WN001:书源入口未注册:\" + missing.join(\", \"));\n    \/\/ CookieJar 是准备通知。部分客户端不返回结果,不能阻塞后续入口。\n    var bridge = window.flutter_inappwebview;\n    if (!bridge || typeof bridge.callHandler !== \"function\") throw new Error(\"WN001:原生桥接尚未就绪。\");\n    var notification = bridge.callHandler(\"CookieJar\", true);\n    window.wn001ReadyNotificationSent = true;\n    if (notification && typeof notification.catch === \"function\") notification.catch(function (error) {\n      window.wn001ReadyNotificationSent = false;\n      nativeReady = null;\n      wn001DiagnosticLog(\"WN001:准备通知失败,将重试:\" + cleanText(error && error.message || error));\n    });\n    wn001DiagnosticLog(\"WN001 \" + sourceVersion + \":准备完成,入口已注册\");\n    if (!backgroundSettingsStarted) {\n      backgroundSettingsStarted = true;\n      optionalCall(\"getWebViewUA\").then(function (ua) { if (typeof ua === \"string\" && ua.trim()) wn001UA = ua.trim(); });\n      var revision = speedRevision;\n      optionalCall(\"cache.get\", imageSpeedKey).then(function (saved) {\n        var speed = Number(saved);\n        if (revision === speedRevision && [2, 4, 6].indexOf(speed) >= 0) { imageConcurrency = speed; startWaitingImages(); }\n      });\n    }\n  })();\n  var ready = nativeReady;\n  ready.catch(function () { if (nativeReady === ready) nativeReady = null; });\n  return ready;\n}\nfunction cleanText(value) { return String(value == null ? \"\" : value).replace(\/\\s+\/g, \" \").trim(); }\nfunction parseDoc(html) { return new DOMParser().parseFromString(String(html || \"\"), \"text\/html\"); }\nfunction absoluteUrl(value, from) {\n  if (!value) return \"\";\n  try {\n    var text = String(value).trim().replace(\/^\\\/{3,}\/, \"\/\/\");\n    var u = new URL(text, from || baseurl + \"\/\");\n    return \/^https?:$\/.test(u.protocol) && !u.username && !u.password ? u.href : \"\";\n  } catch (_) { return \"\"; }\n}\nfunction ownHost(host) { return \/^(?:www\\.)?wn001\\.cfd$\/i.test(host); }\nfunction sourceUrl(value, keepHash) {\n  var absolute = absoluteUrl(value);\n  if (!absolute) throw new Error(\"WN001:链接无效。\");\n  var u = new URL(absolute);\n  if (!ownHost(u.hostname) || u.port || u.username || u.password) throw new Error(\"WN001:书籍或章节链接必须属于 wn001.cfd。\");\n  if (!\/^(?:\\\/|\\\/albums(?:-[a-z0-9%_.~\\-]+)?\\.html|\\\/photos-(?:index|slide|item)(?:-[a-z0-9\\-]+)\\.html|\\\/search\\\/(?:index\\.php)?|\\\/users-login\\.html)$\/i.test(u.pathname)) {\n    throw new Error(\"WN001:不支持此页面链接。\");\n  }\n  u.protocol = \"https:\"; u.hostname = \"www.wn001.cfd\";\n  if (!keepHash) u.hash = \"\";\n  return u.href;\n}\nfunction albumId(value) {\n  var u = new URL(sourceUrl(value));\n  var m = \/^\\\/photos-(?:index|slide|item)-(?:page-\\d+-)?aid-(\\d+)\\.html$\/.exec(u.pathname);\n  if (!m || !Number(m[1])) throw new Error(\"WN001:缺少有效的图集编号,请重新打开书籍。\");\n  return m[1];\n}\nfunction bookUrl(value) { return baseurl + \"\/photos-index-aid-\" + albumId(value) + \".html\"; }\nfunction pageHeaders() {\n  return {\"User-Agent\": wn001UA || navigator.userAgent, \"Referer\": baseurl + \"\/\",\n    \"Accept-Language\": \"zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.5\"};\n}\nfunction responseObject(value) {\n  for (var i = 0; i < 3 && typeof value === \"string\"; i++) { try { value = JSON.parse(value); } catch (_) { return null; } }\n  return value && typeof value === \"object\" ? value : null;\n}\nfunction pageResult(value) {\n  var parsed = value;\n  for (var i = 0; i < 3 && typeof parsed === \"string\" && \/^\\s*[{\\\"]\/.test(parsed); i++) { try { parsed = JSON.parse(parsed); } catch (_) { break; } }\n  return {html: typeof parsed === \"string\" ? parsed.trim() : cleanHtml(parsed && (parsed.html || parsed.data || parsed.body)),\n    url: parsed && typeof parsed === \"object\" ? absoluteUrl(parsed.url || parsed.location) : \"\"};\n}\nfunction cleanHtml(value) { return typeof value === \"string\" ? value.trim() : \"\"; }\nfunction pageProblem(html) {\n  if (!html) return \"网页没有返回内容\";\n  var doc = parseDoc(html), title = cleanText(doc.title), body = cleanText(doc.body && doc.body.textContent);\n  if (\/too many requests|rate limit|请求过于频繁|訪問過於頻繁\/i.test(title) || \/^(?:429\\b|Too Many Requests\\b)\/i.test(body)) return \"网站限流,请稍后重试\";\n  if (\/attention required.*cloudflare|access denied|forbidden|^403\\b\/i.test(title) || \/sorry,? you have been blocked|you are unable to access|error\\s*1020\/i.test(body)) return \"Cloudflare 拒绝当前网络或浏览器的访问\";\n  if (\/just a moment|checking your browser|security verification|请稍候|請稍候\/i.test(title) || doc.querySelector(\"#challenge-form, #cf-challenge-running, #cf-error-details\") || \/(?:window\\.)?_cf_chl_opt\\s*=\/.test(html)) return \"需要完成 Cloudflare 人机验证\";\n  if (\/^401\\b|unauthorized|authentication required\/i.test(title)) return \"网站要求登录\";\n  if (\/^404\\b|not found|页面不存在|頁面不存在\/i.test(title)) return \"网页不存在或已经下架\";\n  if (\/^50[0234]\\b|bad gateway|service unavailable|gateway time.?out\/i.test(title)) return \"网站暂时不可用,请稍后重试\";\n  return \"\";\n}\nfunction albumLink(node, from) {\n  if (!node) return \"\";\n  var href = node.getAttribute(\"href\") || \"\";\n  if (\/^photos-index-(?:page-\\d+-)?aid-\\d+\\.html(?:[?#]|$)\/.test(href)) href = \"\/\" + href;\n  var absolute = absoluteUrl(href, from || baseurl + \"\/\");\n  if (!absolute) return \"\";\n  var u = new URL(absolute);\n  if (!ownHost(u.hostname) || u.port || !\/^\\\/photos-index-(?:page-\\d+-)?aid-\\d+\\.html$\/.test(u.pathname)) return \"\";\n  try { return bookUrl(u.href); } catch (_) { return \"\"; }\n}\nfunction listCards(doc, from) {\n  return Array.from(doc.querySelectorAll(\"li.gallary_item, .gallary_wrap .gallary_item, ul.col_2 > li, #classify_container > li\")).filter(function (card) {\n    if (card.closest(\"header, nav, footer, aside, .related, .recommend, .advertisement, .ad-banner\")) return false;\n    return Array.from(card.querySelectorAll(\"a[href]\")).some(function (a) { return !!albumLink(a, from); });\n  });\n}\nfunction noSearchResults(doc) {\n  if (Array.from(doc.querySelectorAll(\"a[href]\")).some(function (a) { return !!albumLink(a); })) return false;\n  var nodes = Array.from(doc.querySelectorAll(\".empty, .no-results, .search-empty, #no-results, .demo-error, .text-error, #classify_container, .gallary_wrap\"));\n  return nodes.some(function (node) {\n    var clone = node.cloneNode(true);\n    Array.from(clone.querySelectorAll(\"script, style, noscript, template\")).forEach(function (el) { el.remove(); });\n    var text = cleanText(clone.textContent);\n    return \/(?:沒有|没有|未)找到(?:符合|相關|相关|結果|结果|任何|漫畫|漫画|作品)|(?:沒有|没有|無|无)(?:搜尋|搜索|匹配)|(?:查無|查无)(?:結果|结果|資料|资料)|找不到[\\s\\S]*相符的(?:內容|内容|信息)|\\bno (?:search )?results\\b\/i.test(text);\n  });\n}\nfunction listIdentity(value) {\n  var u = new URL(sourceUrl(value)); u.hash = \"\";\n  [\"qysg-source\", \"p\", \"page\", \"c\"].forEach(function (key) { u.searchParams.delete(key); });\n  if (u.searchParams.get(\"m\") === \"\") u.searchParams.delete(\"m\");\n  u.pathname = u.pathname.replace(\/-page-\\d+(?=-|\\.html)\/, \"\");\n  if (u.pathname === \"\/albums.html\") u.pathname = \"\/albums-index.html\";\n  if (\/^\\\/search\\\/\/.test(u.pathname)) u.pathname = \"\/search\/\";\n  u.searchParams.sort(); return u.href;\n}\nfunction numberedPage(value) {\n  var n = Number(value == null || value === \"\" ? 1 : value);\n  if (n === 0) { wn001DiagnosticLog(\"WN001:客户端页码 0 按第一页读取\"); n = 1; }\n  if (!Number.isInteger(n) || n < 1) throw new Error(\"WN001:页码必须为正整数。\");\n  return n;\n}\nfunction currentPage(doc) {\n  var node = doc.querySelector(\".paginator .thispage, .block-pagination .thispage, span.thispage, [aria-current='page']\");\n  var text = cleanText(node && node.textContent);\n  return \/^\\d+$\/.test(text) ? Number(text) : null;\n}\nfunction pageNumber(value) {\n  var u = new URL(sourceUrl(value)), m = \/-page-(\\d+)(?:-|\\.html)\/.exec(u.pathname);\n  return numberedPage(m ? m[1] : u.searchParams.get(\"p\") || u.searchParams.get(\"page\") || 1);\n}\nfunction validPage(html, url, kind, observedUrl) {\n  if (pageProblem(html)) return false;\n  var doc = parseDoc(html), expected = new URL(url);\n  if (observedUrl) {\n    try {\n      var actual = new URL(sourceUrl(observedUrl));\n      if (kind === \"list\") { if (listIdentity(actual.href) !== listIdentity(expected.href)) return false; }\n      else if (actual.pathname !== expected.pathname) return false;\n    } catch (_) { return false; }\n  }\n  if (kind === \"detail\") {\n    var reader = doc.querySelector(\"#reader-btn[href], a[href*='photos-slide-aid-'], a[href*='photos-list-aid-']\");\n    var name = doc.querySelector(\"#comicName, .userwrap > h2, .bodywrap h2, h2\");\n    if (!reader || !cleanText(name && name.textContent)) return false;\n    try { return albumId(absoluteUrl(reader.getAttribute(\"href\"), url).replace(\/\\\/photos-list-aid-\/, \"\/photos-slide-aid-\")) === albumId(url); }\n    catch (_) { return false; }\n  }\n  if (kind === \"list\") {\n    var query = doc.querySelector('input[name=\"q\"]');\n    if (\/^\\\/search\\\/\/.test(expected.pathname) && (!query || (query.getAttribute(\"value\") || \"\") !== (expected.searchParams.get(\"q\") || \"\"))) return false;\n    return !!parseBooks(html, url).length || !!query && noSearchResults(doc);\n  }\n  return false;\n}\nfunction failWN001(message) { wn001DiagnosticLog(message); throw new Error(message); }\nfunction terminalStatus(error) { var n = Number(error && error.statusCode); return n === 404 || n === 410 || n === 429 || n >= 500; }\nfunction terminalProblem(problem) { return \/限流|不存在|暂时不可用\/.test(problem); }\nasync function webPage(url) {\n  return pageResult(await nativeCall(\"webview\", url, \"\", \"\", \"\", JSON.stringify(pageHeaders()), \"\", \"\"));\n}\nfunction assertHttp(response) {\n  if (!response) throw new Error(\"原生 HTTP 未返回响应\");\n  var status = Number(response.statusCode == null ? response.status : response.statusCode);\n  if (!(status >= 200 && status < 300)) { var error = new Error(\"请求返回 HTTP \" + (status || \"未知状态\")); error.statusCode = status; throw error; }\n}\nfunction decodeBase64(body) {\n  if (typeof body !== \"string\" || !body.trim()) throw new Error(\"原生 HTTP 缺少二进制 body(base64)\");\n  var value = body.replace(\/\\s+\/g, \"\").replace(\/-\/g, \"+\").replace(\/_\/g, \"\/\");\n  if (!\/^[A-Za-z0-9+\/]+={0,2}$\/.test(value) || value.length % 4 === 1) throw new Error(\"原生 HTTP body 不是有效 base64\");\n  while (value.length % 4) value += \"=\";\n  return value;\n}\nfunction utf8Response(response) {\n  assertHttp(response);\n  var binary = atob(decodeBase64(response.body));\n  if (typeof TextDecoder === \"function\") {\n    var bytes = new Uint8Array(binary.length);\n    for (var i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n    return new TextDecoder(\"utf-8\", {fatal: true}).decode(bytes);\n  }\n  var encoded = \"\";\n  for (var j = 0; j < binary.length; j++) encoded += \"%\" + (\"0\" + binary.charCodeAt(j).toString(16)).slice(-2);\n  return decodeURIComponent(encoded);\n}\nfunction responseText(response) {\n  assertHttp(response);\n  if (typeof response.data === \"string\" && response.data.trim()) return response.data;\n  if (typeof response.html === \"string\" && response.html.trim()) return response.html;\n  return utf8Response(response);\n}\nasync function httpPage(url) {\n  var response = responseObject(await nativeCall(\"http\", \"get\", url, \"\", JSON.stringify(pageHeaders()), true, \"\"));\n  return {html: responseText(response), url: absoluteUrl(response.url || response.location)};\n}\nfunction logPage(result, via, url, kind) {\n  var doc = parseDoc(result.html), problem = pageProblem(result.html);\n  wn001DiagnosticLog(\"WN001:\" + via + \" \" + kind + \",HTML \" + result.html.length + \" 字符,候选 \" + listCards(doc, url).length +\n    \",模板 \" + (doc.querySelector(\"ul.col_2, #classify_container\") ? \"手机\" : doc.querySelector(\".gallary_wrap\") ? \"桌面\" : \"未知\") +\n    \",页码 \" + (currentPage(doc) || \"无\") + (problem ? \",\" + problem : \"\"));\n}\nasync function readTarget(url, kind) {\n  var result = {html: \"\", url: \"\"}, firstError = \"\", problem = \"\";\n  try { result = await webPage(url); logPage(result, \"WebView\", url, kind); }\n  catch (e) { firstError = cleanText(e.message); if (terminalStatus(e)) throw e; }\n  if (validPage(result.html, url, kind, result.url)) return {html: result.html};\n  problem = pageProblem(result.html);\n  if (terminalProblem(problem)) return failWN001(\"WN001:\" + problem);\n  try {\n    var direct = await httpPage(url); logPage(direct, \"HTTP\", url, kind);\n    if (validPage(direct.html, url, kind, direct.url)) return {html: direct.html};\n    var directProblem = pageProblem(direct.html);\n    if (terminalProblem(directProblem)) return failWN001(\"WN001:\" + directProblem);\n    if (!problem || problem === \"网页没有返回内容\") problem = directProblem || problem;\n  } catch (e) {\n    if (terminalStatus(e) || terminalProblem(e.message)) throw e;\n    firstError = firstError || cleanText(e.message);\n  }\n  return {html: \"\", problem: problem, error: firstError};\n}\nasync function fetchPage(url, kind) {\n  await initializeWN001();\n  var result = await readTarget(url, kind);\n  if (result.html) return result.html;\n  if (Date.now() < verificationPausedUntil) return failWN001(\"WN001:上次验证未完成,请先点击发现页的“网页验证 \/ 登录”再重试。\");\n  await optionalCall(\"showToast\", \"请在应用内网页完成验证或登录,看到正常页面后返回。\");\n  var manual = {html: \"\", url: \"\"};\n  try { manual = pageResult(await nativeCall(\"startBrowser\", url, \"WN001:完成验证后返回\", JSON.stringify(pageHeaders()))); }\n  catch (e) { result.error = result.error || cleanText(e.message); }\n  if (validPage(manual.html, url, kind, manual.url)) { verificationPausedUntil = 0; return manual.html; }\n  \/\/ 有些客户端关闭浏览器只返回空值;此时重新读取目标页面以使用更新后的 Cookie。\n  var retry = await readTarget(url, kind);\n  if (retry.html) { verificationPausedUntil = 0; return retry.html; }\n  verificationPausedUntil = Date.now() + 30000;\n  return failWN001(\"WN001:\" + (pageProblem(manual.html) !== \"网页没有返回内容\" && pageProblem(manual.html) || retry.problem || result.problem || result.error || \"页面与目标不符或网站结构已变化\") + \"。可从发现页重新验证。\");\n}\nfunction loadPage(value, kind) {\n  var url = sourceUrl(value), generation = pageGeneration, key = generation + \":\" + kind + \":\" + url;\n  var cached = kind === \"detail\" && detailCache.get(url);\n  if (cached && Date.now() - cached.time < 30000) return Promise.resolve(cached.html);\n  if (pendingPages.has(key)) return pendingPages.get(key);\n  var task = requestQueue.catch(function () {}).then(function () { return fetchPage(url, kind); });\n  requestQueue = task.catch(function () {}); pendingPages.set(key, task);\n  return task.then(function (html) {\n    pendingPages.delete(key);\n    if (kind === \"detail\" && generation === pageGeneration) {\n      if (detailCache.size >= 8) detailCache.delete(detailCache.keys().next().value);\n      detailCache.set(url, {html: html, time: Date.now()});\n    }\n    return html;\n  }, function (err) { pendingPages.delete(key); throw err; });\n}\nfunction imageSrc(img) { return img ? img.getAttribute(\"data-original\") || img.getAttribute(\"data-src\") || img.getAttribute(\"src\") || \"\" : \"\"; }\nfunction mediaSource(value, referer) {\n  var url = absoluteUrl(value, referer);\n  if (!url) return \"\";\n  return url.replace(\/^http:\/, \"https:\").replace(\/\"\/g, \"%22\") + \",\" + JSON.stringify({headers: {\n    \"User-Agent\": wn001UA || navigator.userAgent, \"Referer\": referer || baseurl + \"\/\"\n  }});\n}\nfunction parseBooks(html, from) {\n  var doc = parseDoc(html), books = [], seen = new Set();\n  listCards(doc, from).forEach(function (card) {\n    function isBookLink(node) { return !!albumLink(node, from); }\n    var a = Array.from(card.querySelectorAll(\".title a[href]\")).find(isBookLink) || Array.from(card.querySelectorAll(\"a[href]\")).find(isBookLink);\n    var url = albumLink(a, from);\n    if (!url || seen.has(url)) return;\n    var img = card.querySelector(\".pic_box img, img\"), mobileName = card.querySelector(\".txtA\");\n    var name = cleanText(mobileName && mobileName.textContent) || cleanText(a.getAttribute(\"title\")) ||\n      cleanText(a.querySelector(\"span\") && a.querySelector(\"span\").textContent) || cleanText(a.textContent) || cleanText(img && img.getAttribute(\"alt\"));\n    if (!name) return;\n    seen.add(url);\n    var infoNode = card.querySelector(\".info_col, span.info\"), summary = cleanText(infoNode && infoNode.textContent);\n    var count = \/(\\d+)\\s*(?:P\\b|頁|页|張|张)\/i.exec(summary);\n    books.push({bookUrl: url, name: name, author: \"\", kind: \"漫画 \/ 图集\", coverUrl: mediaSource(imageSrc(img), baseurl + \"\/\"),\n      intro: summary, tocUrl: url, wordCount: count ? count[1] + \" 页\" : \"\", type: 2, latestChapterTitle: count ? \"共 \" + count[1] + \" 页\" : \"图集\"});\n  });\n  return books;\n}\nfunction rememberSearchLinks(doc, from, key, generation) {\n  if (generation !== pageGeneration) return;\n  var state = searchPages.get(key) || {time: Date.now(), links: new Map()};\n  Array.from(doc.querySelectorAll(\".paginator a[href], .block-pagination a[href]\")).forEach(function (a) {\n    try { var url = sourceUrl(absoluteUrl(a.getAttribute(\"href\"), from)); if (listIdentity(url) === key) state.links.set(pageNumber(url), url); } catch (_) {}\n  });\n  state.time = Date.now(); searchPages.delete(key); searchPages.set(key, state);\n  while (searchPages.size > 8) searchPages.delete(searchPages.keys().next().value);\n}\nasync function search(key, page) {\n  var p = numberedPage(page), u = new URL(baseurl + \"\/search\/\"), generation = pageGeneration;\n  u.searchParams.set(\"q\", String(key || \"\")); u.searchParams.set(\"f\", \"_all\");\n  u.searchParams.set(\"s\", \"create_time_DESC\"); u.searchParams.set(\"syn\", \"yes\");\n  var identity = listIdentity(u.href), state = searchPages.get(identity);\n  if (state && Date.now() - state.time > 5 * 60 * 1000) { searchPages.delete(identity); state = null; }\n  var target = state && state.links.get(p);\n  if (!target) { u.searchParams.set(\"p\", p); target = u.href; }\n  var html = await loadPage(target, \"list\"), doc = parseDoc(html), actual = currentPage(doc);\n  if (actual !== null && actual !== p || p > 1 && actual === null) return \"[]\";\n  rememberSearchLinks(doc, target, identity, generation);\n  var books = parseBooks(html, target);\n  if (!books.length && listCards(doc, target).length) return failWN001(\"WN001:有候选卡片但解析出 0 本,页面结构不兼容,请发送 WN001 日志。\");\n  return JSON.stringify(books);\n}\nfunction detailMetadata(html, url) {\n  var doc = parseDoc(html), header = doc.querySelector(\"#comicName, .userwrap > h2, .bodywrap h2, h2\");\n  var connection = doc.querySelector(\".uwconn\"), labels = connection ? Array.from(connection.querySelectorAll(\"label\")) : Array.from(doc.querySelectorAll(\"label\"));\n  var count = 0, category = \"\", description = \"\";\n  labels.forEach(function (label) {\n    var text = cleanText(label.textContent), m = \/(?:頁數|页数)\\s*[::]\\s*(\\d+)\\s*(?:P|頁|页)?\/i.exec(text);\n    if (m) count = Number(m[1]);\n    if (\/^分[類类]\\s*[::]\/.test(text)) category = text.replace(\/^分[類类]\\s*[::]\\s*\/, \"\");\n    if (\/^(?:簡介|简介|說明|说明)\\s*[::]\/.test(text)) description = text.replace(\/^[^::]+[::]\\s*\/, \"\");\n  });\n  if (!count) {\n    var mobileDate = doc.querySelector(\".Introduct_Sub .date\"), m = \/(\\d+)\\s*(?:P\\b|頁|页)\/i.exec(cleanText(mobileDate && mobileDate.textContent));\n    if (m) count = Number(m[1]);\n  }\n  if (!category) { var categoryNode = doc.querySelector(\".Introduct_Sub a[href*='\/albums-index-cate-']\"); category = cleanText(categoryNode && categoryNode.textContent); }\n  var descriptionNode = doc.querySelector(\".dv-desc-text, .uwconn > p\");\n  if (!description && descriptionNode) description = cleanText(descriptionNode.textContent);\n  var tags = Array.from(doc.querySelectorAll(\".uwconn a.tagshow, .dv-tags .dv-tag\")).map(function (a) { return cleanText(a.textContent); }).filter(Boolean);\n  var name = cleanText(header && header.textContent);\n  if (!name) throw new Error(\"WN001:作品标题未取得,网页结构可能已变化。\");\n  if (!Number.isInteger(count) || count < 0 || count > 10000) throw new Error(\"WN001:图集页数无效。\");\n  return {bookUrl: url, name: name, author: \"\", kind: category || \"漫画 \/ 图集\",\n    coverUrl: mediaSource(imageSrc(doc.querySelector(\"#Cover img, .uwthumb img\")), baseurl + \"\/\"),\n    intro: [description, count ? \"共 \" + count + \" 页;目录每 \" + pagesPerChapter + \" 页分一段。\" : \"目录根据图片清单生成。\",\n      tags.length ? \"标签:\" + tags.join(\" \/ \") : \"\"].filter(Boolean).join(\"\\n\"),\n    tocUrl: url, wordCount: count ? count + \" 页\" : \"\", type: 2, latestChapterTitle: count ? \"共 \" + count + \" 页\" : \"图集\", pageCount: count};\n}\nasync function info(value) { var url = bookUrl(value), result = detailMetadata(await loadPage(url, \"detail\"), url); delete result.pageCount; return JSON.stringify(result); }\n\/\/ 只提取 JSON 字符串数组;不执行图片接口中的网站脚本。\nfunction manifestSources(text, from) {\n  text = String(text);\n  var match = \/[\"']page_url[\"']\\s*:\\s*\\[\/.exec(text);\n  if (!match) throw new Error(\"图片数据中缺少 page_url,网站结构可能已变化\");\n  var pos = match.index + match[0].length, values = [], ended = false;\n  function whitespace() { while (pos < text.length && \/\\s\/.test(text.charAt(pos))) pos++; }\n  while (pos < text.length) {\n    whitespace();\n    if (text.charAt(pos) === \"]\") { ended = true; pos++; break; }\n    if (text.charAt(pos) !== '\"') throw new Error(\"图片地址数组含有无法解析的数据\");\n    var start = pos++, escaped = false, closed = false;\n    for (; pos < text.length; pos++) {\n      var c = text.charAt(pos);\n      if (escaped) escaped = false;\n      else if (c === \"\\\\\") escaped = true;\n      else if (c === '\"') { pos++; closed = true; break; }\n    }\n    if (!closed) throw new Error(\"图片地址数组被截断\");\n    var src = absoluteUrl(JSON.parse(text.slice(start, pos)), from);\n    if (!src) throw new Error(\"图片清单包含无效地址\");\n    values.push(src.replace(\/^http:\/, \"https:\"));\n    if (values.length > 10000) throw new Error(\"图集图片数量超出支持范围\");\n    whitespace();\n    if (text.charAt(pos) === \",\") pos++;\n    else if (text.charAt(pos) !== \"]\") throw new Error(\"图片地址数组格式已变化\");\n  }\n  if (!ended || !values.length) throw new Error(\"图片清单为空或不完整\");\n  return values;\n}\nasync function requestManifest(id) {\n  var url = baseurl + \"\/photos-item-aid-\" + id + \".html\", headers = pageHeaders();\n  headers.Referer = baseurl + \"\/photos-slide-aid-\" + id + \".html\";\n  headers.Accept = \"application\/javascript,text\/javascript,text\/plain,*\/*;q=0.8\";\n  var text = responseText(responseObject(await nativeCall(\"http\", \"get\", url, \"\", JSON.stringify(headers), true, \"\")));\n  if (\/^\\s*<\/.test(text)) throw new Error(pageProblem(text) || \"图片接口返回网页,未取得图片清单\");\n  return manifestSources(text, url);\n}\nfunction loadManifest(id, generation, force) {\n  if (generation == null) generation = imageCacheGeneration;\n  var saved = manifestCache.get(id);\n  if (!force && generation === imageCacheGeneration && saved && Date.now() - saved.time < manifestTTL) return Promise.resolve(saved.sources);\n  var pendingKey = generation + \":\" + id + (force ? \":refresh\" : \"\");\n  if (pendingManifests.has(pendingKey)) return pendingManifests.get(pendingKey);\n  var task = requestQueue.catch(function () {}).then(async function () {\n    await initializeWN001();\n    var firstError;\n    try { return await requestManifest(id); } catch (e) { firstError = e; }\n    if (terminalStatus(firstError)) return failWN001(\"WN001:图片清单读取失败:\" + cleanText(firstError.message));\n    if (Date.now() < verificationPausedUntil) return failWN001(\"WN001:图片清单读取失败,请先完成网页验证。\" + cleanText(firstError.message));\n    var url = baseurl + \"\/photos-index-aid-\" + id + \".html\";\n    await optionalCall(\"showToast\", \"请在应用内网页完成验证,看到正常图集页面后返回。\");\n    var manual = pageResult(await nativeCall(\"startBrowser\", url, \"WN001:完成验证后返回\", JSON.stringify(pageHeaders())));\n    \/\/ 无论浏览器是否返回 HTML,先重试图片清单;不把空回调认定为验证失败。\n    try { var sources = await requestManifest(id); verificationPausedUntil = 0; return sources; }\n    catch (e) {\n      if (terminalStatus(e)) return failWN001(\"WN001:图片清单读取失败:\" + cleanText(e.message));\n      verificationPausedUntil = Date.now() + 30000;\n      var problem = pageProblem(manual.html);\n      return failWN001(\"WN001:图片清单读取失败:\" + (problem && problem !== \"网页没有返回内容\" ? problem : cleanText(e.message)) + \"。请完成验证后刷新目录。\");\n    }\n  });\n  requestQueue = task.catch(function () {}); pendingManifests.set(pendingKey, task);\n  return task.then(function (sources) {\n    pendingManifests.delete(pendingKey);\n    if (generation === imageCacheGeneration) {\n      manifestCache.delete(id); manifestCache.set(id, {sources: sources, time: Date.now()});\n      while (manifestCache.size > 12) manifestCache.delete(manifestCache.keys().next().value);\n    }\n    return sources;\n  }, function (err) { pendingManifests.delete(pendingKey); throw err; });\n}\nasync function chapter(value) {\n  var url = bookUrl(value), id = albumId(url), metadata = detailMetadata(await loadPage(url, \"detail\"), url), total = metadata.pageCount;\n  if (!total) total = (await loadManifest(id)).length;\n  var result = [];\n  for (var start = 0; start < total; start += pagesPerChapter) {\n    var count = Math.min(pagesPerChapter, total - start);\n    result.push({name: \"第 \" + (start + 1) + \"—\" + (start + count) + \" 页 \/ 共 \" + total + \" 页\",\n      chapterId: baseurl + \"\/photos-slide-aid-\" + id + \".html#qysg-start=\" + start + \"&qysg-count=\" + count + \"&qysg-total=\" + total,\n      index: result.length, isPay: false, isVip: false, isVolume: false, tag: \"图集分页\"});\n  }\n  if (!result.length) return failWN001(\"WN001:没有取得图集页数,请刷新目录。\");\n  return JSON.stringify(result);\n}\nfunction imageDataUri(response) {\n  assertHttp(response);\n  var body = decodeBase64(response.body), signature;\n  try { signature = atob(body.slice(0, Math.min(64, body.length))); } catch (_) { throw new Error(\"图片 base64 解码失败\"); }\n  var mime = \"\";\n  if (signature.charCodeAt(0) === 255 && signature.charCodeAt(1) === 216 && signature.charCodeAt(2) === 255) mime = \"image\/jpeg\";\n  else if (signature.slice(0, 8) === \"\\x89PNG\\r\\n\\x1a\\n\") mime = \"image\/png\";\n  else if (\/^GIF8[79]a\/.test(signature)) mime = \"image\/gif\";\n  else if (signature.slice(0, 4) === \"RIFF\" && signature.slice(8, 12) === \"WEBP\") mime = \"image\/webp\";\n  else if (signature.slice(4, 8) === \"ftyp\" && \/avif|avis\/.test(signature.slice(8, 32))) mime = \"image\/avif\";\n  if (!mime) throw new Error(\"返回内容不是受支持的图片,可能为验证或错误页面\");\n  return \"data:\" + mime + \";base64,\" + body;\n}\nfunction startWaitingImages() {\n  while (imageWaiters.length && activeImageRequests < imageConcurrency) { activeImageRequests++; imageWaiters.shift()(); }\n}\nasync function withImageSlot(run) {\n  await new Promise(function (resolve) { imageWaiters.push(resolve); startWaitingImages(); });\n  try { return await run(); } finally { activeImageRequests--; startWaitingImages(); }\n}\nfunction removeCachedImage(key) { var entry = imageCache.get(key); if (entry) imageCacheCharacters -= entry.data.length; imageCache.delete(key); }\nfunction cachedImage(key) {\n  var entry = imageCache.get(key);\n  if (!entry) return \"\";\n  if (Date.now() - entry.time >= imageCacheTTL) { removeCachedImage(key); return \"\"; }\n  imageCache.delete(key); imageCache.set(key, entry); return entry.data;\n}\nfunction rememberImage(key, data, generation) {\n  if (generation !== imageCacheGeneration || data.length > imageCacheLimit) return;\n  removeCachedImage(key); imageCache.set(key, {data: data, time: Date.now()}); imageCacheCharacters += data.length;\n  while (imageCacheCharacters > imageCacheLimit || imageCache.size > imageCacheEntries) removeCachedImage(imageCache.keys().next().value);\n}\nasync function requestImage(src, referer, pageIndex) {\n  var headers = {\"User-Agent\": wn001UA || navigator.userAgent, \"Referer\": referer,\n    \"Accept\": \"image\/avif,image\/webp,image\/jpeg,image\/png,image\/gif;q=0.9,*\/*;q=0.5\"};\n  \/\/ 与所提供的原生源一致,https2 是应用 HTTP 桥接的兼容请求方式。\n  var schemes = preferredImageScheme === \"https2\" ? [\"https2\", \"https\"] : [\"https\", \"https2\"], lastError;\n  for (var attempt = 0; attempt < schemes.length; attempt++) {\n    try {\n      var response = responseObject(await nativeCall(\"http\", \"get\", src.replace(\/^https:\/, schemes[attempt] + \":\"), \"\", JSON.stringify(headers), true, \"\"));\n      var result = imageDataUri(response); preferredImageScheme = schemes[attempt]; return result;\n    } catch (e) {\n      lastError = e;\n      if (e.statusCode === 429 && imageConcurrency > 2) {\n        imageConcurrency = 2; speedRevision++;\n        wn001DiagnosticLog(\"WN001:图片请求限流,本次运行自动降为 2 并发。\");\n      }\n      if (terminalStatus(e)) break;\n    }\n  }\n  throw new Error(\"WN001:第 \" + (pageIndex + 1) + \" 页图片加载失败:\" + cleanText(lastError && lastError.message) + \"。请刷新本段后重试。\");\n}\nfunction downloadImage(src, referer, pageIndex, generation) {\n  var key = referer + \"\\n\" + src, saved = generation === imageCacheGeneration ? cachedImage(key) : \"\";\n  if (saved) return Promise.resolve(saved);\n  var pendingKey = generation + \":\" + key;\n  if (pendingImages.has(pendingKey)) return pendingImages.get(pendingKey);\n  var task = withImageSlot(function () { return requestImage(src, referer, pageIndex); }).then(function (data) {\n    rememberImage(key, data, generation); pendingImages.delete(pendingKey); return data;\n  }, function (err) { pendingImages.delete(pendingKey); throw err; });\n  pendingImages.set(pendingKey, task); return task;\n}\nasync function inlineImages(sources, referer, start, generation) {\n  var lines = new Array(sources.length);\n  for (var cachedIndex = 0; cachedIndex < sources.length; cachedIndex++) {\n    var saved = generation === imageCacheGeneration ? cachedImage(referer + \"\\n\" + sources[cachedIndex]) : \"\";\n    if (saved) lines[cachedIndex] = '<img src=\"' + saved + '\" \/>';\n  }\n  var next = 0, stopped = false, failure = null;\n  async function worker() {\n    while (!stopped && next < sources.length) {\n      var index = next++;\n      if (lines[index]) continue;\n      try { lines[index] = '<img src=\"' + await downloadImage(sources[index], referer, start + index, generation) + '\" \/>'; }\n      catch (e) { stopped = true; failure = failure || e; }\n    }\n  }\n  var workers = [];\n  for (var i = 0; i < Math.min(imageConcurrency, sources.length); i++) workers.push(worker());\n  await Promise.all(workers);\n  if (failure) return failWN001(failure.message);\n  return lines.join(\"\\r\\n\");\n}\nasync function content(value) {\n  var url = sourceUrl(value, true), u = new URL(url), id = albumId(url), generation = imageCacheGeneration, pendingKey = generation + \":\" + url;\n  if (pendingContents.has(pendingKey)) return pendingContents.get(pendingKey);\n  var range = new URLSearchParams(u.hash.replace(\/^#\/, \"\")), hasRange = range.has(\"qysg-start\");\n  function rangeInteger(key) { var text = range.get(key); return text != null && \/^\\d+$\/.test(text) ? Number(text) : NaN; }\n  var start = hasRange ? rangeInteger(\"qysg-start\") : 0;\n  var count = hasRange ? rangeInteger(\"qysg-count\") : pagesPerChapter;\n  var expected = hasRange ? rangeInteger(\"qysg-total\") : 0;\n  if (!hasRange && (range.has(\"qysg-count\") || range.has(\"qysg-total\")) || !Number.isInteger(start) || start < 0 ||\n      !Number.isInteger(count) || count < 1 || count > pagesPerChapter || hasRange &&\n      (!Number.isInteger(expected) || expected < 1 || expected > 10000 || start + count > expected)) {\n    return failWN001(\"WN001:阅读分段参数无效,请重新刷新目录。\");\n  }\n  var referer = baseurl + \"\/photos-slide-aid-\" + id + \".html\";\n  var task = (async function () {\n    await initializeWN001();\n    var started = Date.now(), sources = await loadManifest(id, generation);\n    if (expected && expected !== sources.length) {\n      if (generation === imageCacheGeneration) { manifestCache.delete(id); detailCache.delete(baseurl + \"\/photos-index-aid-\" + id + \".html\"); }\n      sources = await loadManifest(id, generation, true);\n      if (expected !== sources.length) return failWN001(\"WN001:图集页数已变化或图片清单不完整(目录 \" + expected + \" 页,清单 \" + sources.length + \" 页),请刷新目录。\");\n    }\n    if (!hasRange && sources.length > pagesPerChapter) return failWN001(\"WN001:此链接未包含分页信息,请刷新目录后从分段章节进入。\");\n    var selected = sources.slice(start, Math.min(start + count, sources.length));\n    if (!selected.length || hasRange && selected.length !== count) return failWN001(\"WN001:图片清单缺页,请刷新目录。\");\n    var hits = selected.filter(function (src) { return generation === imageCacheGeneration && !!cachedImage(referer + \"\\n\" + src); }).length;\n    wn001DiagnosticLog(\"WN001 \" + sourceVersion + \":读取第 \" + (start + 1) + \"—\" + (start + selected.length) + \" 页,缓存 \" + hits + \" 页,\" + imageConcurrency + \" 并发\");\n    try {\n      var result = await inlineImages(selected, referer, start, generation);\n      wn001DiagnosticLog(\"WN001:本段图片已完成,耗时 \" + ((Date.now() - started) \/ 1000).toFixed(1) + \" 秒\");\n      return result;\n    } catch (error) { if (generation === imageCacheGeneration) manifestCache.delete(id); throw error; }\n  })();\n  pendingContents.set(pendingKey, task);\n  try { return await task; } finally { pendingContents.delete(pendingKey); }\n}\nasync function getfinds() {\n  var finds = [\n    {title: \"WN001\", url: \"\", js: \"\", type: 0, width: 3},\n    {title: \"最新图集\", url: baseurl + \"\/albums-index-page-{{page}}.html\", js: \"\", type: 0, width: 0},\n    {title: \"收藏排行\", url: baseurl + \"\/albums-favorite_ranking.html\", js: \"\", type: 0, width: 0},\n    {title: \"网页验证 \/ 登录\", url: baseurl + \"\/?qysg-action=verify\", js: \"\", type: 0, width: 3},\n    {title: \"图片速度:\" + imageConcurrency + \" 并发(点击切换)\", url: baseurl + \"\/?qysg-action=speed\", js: \"\", type: 0, width: 3},\n    {title: \"清除图片缓存\", url: baseurl + \"\/?qysg-action=clear-images\", js: \"\", type: 0, width: 3},\n    {title: \"分类\", url: \"\", js: \"\", type: 0, width: 3}\n  ];\n  [[\"同人志 \/ CG\", \"5\"], [\"单行本\", \"6\"], [\"杂志 \/ 短篇\", \"7\"], [\"韩漫\", \"19\"], [\"3D \/ 漫画\", \"3\"]].forEach(function (item) {\n    finds.push({title: item[0], url: baseurl + \"\/albums-index-page-{{page}}-cate-\" + item[1] + \".html\", js: \"\", type: 0, width: 0});\n  });\n  return JSON.stringify(finds);\n}\nasync function find(value, page) {\n  var p = numberedPage(page);\n  if (!value) return \"[]\";\n  var u = new URL(sourceUrl(String(value).replace(\/\\{\\{page\\}\\}\/g, p)));\n  if (u.pathname === \"\/\" && u.searchParams.has(\"qysg-action\")) {\n    if (p > 1) return \"[]\";\n    var action = u.searchParams.get(\"qysg-action\");\n    await initializeWN001();\n    if (action === \"verify\") await verifyWN001();\n    else if (action === \"speed\") await cycleWN001Speed();\n    else if (action === \"clear-images\") await clearWN001ImageCache();\n    else throw new Error(\"WN001:未知发现操作,请刷新发现页。\");\n    return \"[]\";\n  }\n  if (u.pathname === \"\/albums-favorite_ranking.html\" && p > 1) return \"[]\";\n  if (\/^\\\/albums-index(?:-|\\.html)\/.test(u.pathname)) {\n    var path = u.pathname.replace(\/-page-\\d+(?=-|\\.html)\/, \"\"); u.pathname = path.replace(\/^\\\/albums-index\/, \"\/albums-index-page-\" + p);\n  } else if (u.pathname === \"\/albums.html\" || u.pathname === \"\/\") u.pathname = \"\/albums-index-page-\" + p + \".html\";\n  var html = await loadPage(u.href, \"list\"), doc = parseDoc(html), actual = currentPage(doc);\n  if (actual !== null && actual !== p || p > 1 && actual === null) return \"[]\";\n  var books = parseBooks(html, u.href);\n  if (!books.length && listCards(doc, u.href).length) return failWN001(\"WN001:发现列表有候选卡片但解析出 0 本,请发送 WN001 日志。\");\n  return JSON.stringify(books);\n}\nasync function cycleWN001Speed() {\n  await initializeWN001(); speedRevision++;\n  imageConcurrency = imageConcurrency === 4 ? 6 : imageConcurrency === 6 ? 2 : 4;\n  startWaitingImages(); await optionalCall(\"cache.set\", imageSpeedKey, String(imageConcurrency));\n  await optionalCall(\"showToast\", \"WN001:已切换为 \" + imageConcurrency + \" 并发,刷新发现页显示新档位。\");\n  return \"\";\n}\nfunction resetImageCaches() { imageCacheGeneration++; imageCache.clear(); imageCacheCharacters = 0; manifestCache.clear(); }\nasync function clearWN001ImageCache() {\n  resetImageCaches(); wn001DiagnosticLog(\"WN001:图片与图片地址缓存已清除\");\n  await optionalCall(\"showToast\", \"WN001:已清除本次运行的图片缓存,请刷新阅读分段。\"); return \"\";\n}\nfunction resetSessionCaches() { verificationPausedUntil = 0; pageGeneration++; detailCache.clear(); searchPages.clear(); resetImageCaches(); }\nasync function verifyWN001() {\n  await initializeWN001(); resetSessionCaches();\n  var task = requestQueue.catch(function () {}).then(async function () {\n    var url = baseurl + \"\/albums-index-page-1.html\";\n    var manual = pageResult(await nativeCall(\"startBrowser\", url, \"WN001:完成验证后返回\", JSON.stringify(pageHeaders())));\n    if (!validPage(manual.html, url, \"list\", manual.url)) {\n      var retry = await readTarget(url, \"list\");\n      if (!retry.html) { verificationPausedUntil = Date.now() + 30000; return failWN001(\"WN001:验证尚未完成,请看到正常图集列表后再返回。\"); }\n    }\n    await optionalCall(\"showToast\", \"WN001 验证完成,请重新搜索或刷新目录。\"); return \"\";\n  });\n  requestQueue = task.catch(function () {}); return task;\n}\nasync function getloginurl() { resetSessionCaches(); return baseurl + \"\/users-login.html\"; }\nasync function login() { resetSessionCaches(); return \"\"; }\nasync function pay() { return failWN001(\"WN001:本源读取公开图集,需要权限时请在应用内网页登录。\"); }\nasync function gethelp() {\n  return \"WN001 轻悦时光原生漫画源 \" + sourceVersion + \"\\n\" +\n    \"目标站点:https:\/\/www.wn001.cfd\/\\n\" +\n    \"请完整导入 JSON 文件到轻悦时光的原生书源;此文件采用你提供的原生接口格式。\\n\" +\n    \"搜索、发现和详情优先通过应用 WebView 读取,失败时补试原生 HTTP;搜索分页优先沿用网站游标。\\n\" +\n    \"每个图集对应一本书,目录每 20 页分一段;名称中的页码是图片顺序。\\n\" +\n    \"图片清单从公开 photos-item 接口解析,不执行网站数据脚本。正文带阅读页 Referer 下载,返回内嵌 base64。\\n\" +\n    \"默认 4 并发,可在发现页切换 6/2/4,网站限流时本次运行自动降为 2。\\n\" +\n    \"首次打开需等当前分段全部图片下载完成;长图或大图可能耗时较长。\\n\" +\n    \"图片缓存仅在本次运行内有效,10 分钟过期,最多 128 项且 base64 总字符数不超过 12 Mi;地址清单缓存 2 分钟。\\n\" +\n    \"遇到验证请在应用内网页完成,看到正常页面后返回;外部浏览器不保证共享 Cookie。\\n\" +\n    \"发现页操作执行后返回空书籍列表;返回并刷新发现页可查看新并发档位。\\n\" +\n    \"目录页数与图片清单不一致会提示刷新目录;更新源后清除应用中旧正文缓存。\\n\" +\n    \"本版将入口和实现放在同一个 script 中;准备通知不等待回调,并在原生桥接晚于脚本注入时补发。\\n\" +\n    \"已核对网站公开 HTML 与图片清单并使用模拟原生桥接测试;尚未在你的轻悦时光设备上实机验证。\";\n}\nwindow.__wn001Loader.implementation = {\n  search: search, info: info, chapter: chapter, content: content, getfinds: getfinds, find: find,\n  getloginurl: getloginurl, login: login, pay: pay, gethelp: gethelp,\n  verifyWN001: verifyWN001, cycleWN001Speed: cycleWN001Speed, clearWN001ImageCache: clearWN001ImageCache\n};\nwindow.__wn001Loader.completed = true; window.wn001SourceVersion = sourceVersion;\nfunction startWN001() {\n  var bridge = window.flutter_inappwebview;\n  if (!bridge || typeof bridge.callHandler !== \"function\") return;\n  initializeWN001().catch(function (error) {\n    wn001DiagnosticLog(\"WN001:初始化失败:\" + cleanText(error && error.message || error));\n  });\n}\nwindow.addEventListener(\"flutterInAppWebViewPlatformReady\", startWN001);\nstartWN001();\n\/\/ 兼容先执行脚本、后注入桥接且漏发 ready 事件的客户端;最多检查 10 秒。\nvar startupAttempts = 0;\nvar startupTimer = setInterval(function () {\n  if (window.wn001ReadyNotificationSent || ++startupAttempts >= 50) { clearInterval(startupTimer); return; }\n  startWN001();\n}, 200);\n})();\n\/\/ WN001_MAIN_IMPLEMENTATION_END\n<\/script>\n<\/body><\/html>\n",
    "login": true,
    "customButton": false,
    "eventListener": false,
    "lastUpdateTime": "1790993517553"
}
广告