魔丸小说
https://www.mowan.lol
mowan789 (14268)09/28 23:36
番茄小说,七猫小说,塔读小说,书旗小说,起点小说,所有来源均支持段评,本章说 tg群:https://t.me/mowanxiaoshuo 魔丸小说转为公益书源,免费为大家提供服务,会员有所特权但不多,会员费用将用于运营维护费,费用透明,可前往用户中心打赏排行榜查看,并可查看阅读榜,如果阅读异常,如每天看几万章这种,会进行封禁处理 书源直链:https://www.mowan.lol/api/book-sources/yuedu/download 注册地址:https://www.mowan.lol/user/register?invite_code=ZGWVOFHD
{
"enabled": true,
"enabledCookieJar": false,
"enabledExplore": true,
"customButton": false,
"eventListener": false,
"bookSourceType": 0,
"bookSourceUrl": "https:\/\/www.mowan.lol",
"bookSourceName": "魔丸小说",
"bookSourceGroup": "",
"bookSourceComment": "番茄小说,七猫小说,塔读小说,书旗小说,起点小说,所有来源均支持段评,本章说\n\ntg群:https:\/\/t.me\/mowanxiaoshuo\n\n魔丸小说转为公益书源,免费为大家提供服务,会员有所特权但不多,会员费用将用于运营维护费,费用透明,可前往用户中心打赏排行榜查看,并可查看阅读榜,如果阅读异常,如每天看几万章这种,会进行封禁处理\n\n书源直链:https:\/\/www.mowan.lol\/api\/book-sources\/yuedu\/download\n注册地址:https:\/\/www.mowan.lol\/user\/register?invite_code=ZGWVOFHD",
"loginUrl": "https:\/\/www.mowan.lol\/user\/me?app=yuedu&version=26.9.22",
"loginUi": "",
"loginCheckJs": "",
"coverDecodeJs": "",
"bookUrlPattern": "",
"header": "",
"variableComment": "",
"concurrentRate": "",
"jsLib": "let mw=\"https:\/\/www.mowan.lol\";function getCookie(k){let{java,cookie}=this;let cookieValue=String(cookie.getCookie(mw))||String(java.getCookie(mw));let parts=cookieValue.split(\";\");for(let i=0;i<parts.length;i++){let part=parts[i].trim();let idx=part.indexOf(k+\"=\");if(idx===0){return part.substring(k.length+1)}}return\"\"}function Api(url,method){method=method||'GET';let{java}=this;let XDeviceID=this.getCookie('X-Device-ID');let Authorization=this.getCookie('Authorization');if(typeof Authorization==='string'){Authorization=Authorization.replace(\"%20\",\" \")}else{Authorization=String(Authorization).replace(\"%20\",\" \")};let options={method:method,headers:JSON.stringify({'Authorization':Authorization,'X-Device-ID':XDeviceID,'Content-Type':'application\/json','User-Agent':'yuedu_app','Referer':mw})};return mw+url+','+JSON.stringify(options)}function showPara(url){let{java,cache}=this;let date=new Date().toLocaleDateString();let cacheKey=date+\"dp\";let htmlContent=cache.get(cacheKey);if(!htmlContent||String(htmlContent).indexOf(\"id=\")===-1){htmlContent=java.ajax(url);cache.put(cacheKey,htmlContent)}let browserConfig={\"expandedCornersRadius\":20,\"dismissOnTouchOutside\":true,\"isDraggable\":true,\"shouldDimBackground\":true,\"backgroundDimAmount\":0.5,\"hardwareAccelerated\":true,\"isNestedScrollingEnabled\":true,\"isGestureInsetBottomIgnored\":true,\"setFitToContents\":true,\"isHideable\":true,\"heightPercentage\":0.8};java.showBrowser(url,htmlContent,\"window.java=java;\",JSON.stringify(browserConfig))}function createBubble(number,url,bubble){let num=number>99?\"99+\":number.toString();bubble=bubble.replace('$num',num);var encodedBubble=this.java.base64Encode(bubble);encodedBubble=`data:image\/svg+xml;base64,${encodedBubble},{\"click\":\"showPara('${mw+url}' )\",\"style\":\"text\"}`;return`<img src=\"${encodedBubble}\">`}function showChapter(url){let{java,cache}=this;let date=new Date().toLocaleDateString();let cacheKey=date+\"end\";let htmlContent=cache.get(cacheKey);if(!htmlContent||String(htmlContent).indexOf(\"id=\")===-1){htmlContent=java.ajax(url);cache.put(cacheKey,htmlContent)}let browserConfig={\"expandedCornersRadius\":20,\"dismissOnTouchOutside\":true,\"isDraggable\":true,\"shouldDimBackground\":true,\"backgroundDimAmount\":0.5,\"hardwareAccelerated\":true,\"isNestedScrollingEnabled\":true,\"isGestureInsetBottomIgnored\":true,\"setFitToContents\":true,\"isHideable\":true,\"heightPercentage\":0.8};java.showBrowser(url,htmlContent,\"window.java=java;\",JSON.stringify(browserConfig))}function createChapter(url,svg){var encodedSvg=this.java.base64Encode(svg);encodedSvg=`data:image\/svg+xml;base64,${encodedSvg},{\"click\":\"showChapter('${mw+url}' )\",\"style\":\"FULL\"}`;return`<img src=\"${encodedSvg}\">`}\n\n\nfunction show(k, v) {\n const { java, cache } = this;\n cache.put(v,k)\n java.refreshExplore();\n}\n\n\nfunction create(k, chars, defaultV, v, size) {\n return {\n title: k,\n type: \"select\",\n chars: chars,\n default: defaultV,\n action: `show(infoMap['${k}'],'${v}')`,\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: size\n }\n };\n}\n\nfunction formatDate(input, fmt = 'YYYY-MM-DD HH:mm:ss') {\n const date = input instanceof Date ? input : new Date(input);\n if (isNaN(date.getTime())) return '';\n\n const pad = n => String(n).padStart(2, '0');\n\n const map = {\n YYYY: date.getFullYear(),\n MM: pad(date.getMonth() + 1),\n DD: pad(date.getDate()),\n HH: pad(date.getHours()),\n mm: pad(date.getMinutes()),\n ss: pad(date.getSeconds())\n };\n\n return fmt.replace(\/YYYY|MM|DD|HH|mm|ss\/g, m => map[m]);\n}\n\n\nfunction Env() {\n const { java, source } = this;\n try {\n java.qread();\n return 4;\n } catch (e) {}\n try {\n if (typeof java.reLoginView === 'function') return 1;\n new Packages.io.legato.kazusa.utils.TimeoutCancellationException('');\n return 1;\n } catch (e) {}\n try {\n java.deviceID();\n return 3;\n } catch (e) {}\n if (typeof source.loginUi === 'function') return 2;\n return 1;\n}\n\nfunction Content(res) {\n\tlet evn = this.Env();\n\tlet content = \"\";\n\tlet text = res.data.paragraphs;\n if (!text) {\n return res.data.msg || \"暂无数据,请稍后再试~\";\n }\n let enabledPara = res.data.comment.enabled;\n let bubble = enabledPara ? res.data.comment.bubble.svg : \"\";\n let enabledChapter = res.data.comment.chapter_enabled;\n let chapterSvgs = enabledChapter ? res.data.comment.chapter_card.svg : \"\";\n \n var contents = [];\n for (var i = 0; i < text.length; i++) {\n var item = text[i];\n var html = \"\";\n if (item.comment_count > 0 && enabledPara && bubble != '' && evn == 1) {\n html = '<p>' + item.text + this.createBubble(item.comment_count, item.comment_url, bubble) + '<\/p>';\n } else if (item.comment_count > 0 && enabledPara && bubble != '' && evn == 3) {\n \thtml = `<div rs-native>${item.text}<comment count=\"${item.comment_count}\" onPress=\"java.showReadingBrowser('${mw+item.comment_url}','段评')\"><\/div>`;\n \t} else {\n html = item.text;\n }\n if (item.image) {\n if (typeof item.image === 'string') {\n html += '<img src=\"' + item.image + '\">';\n } else if (Array.isArray(item.image)) {\n for (var j = 0; j < item.image.length; j++) {\n html += '<img src=\"' + item.image[j] + '\">';\n }\n }\n }\n\n contents.push(html);\n }\n\n content = contents.join(\"\\n\");\n \n if (enabledChapter&&chapterSvgs!=\"\" && evn == 1) {\n \t let chapterUrl = res.data.comment.chapter_card.url\n \t content += '\\n'+ this.createChapter(chapterUrl,chapterSvgs)\n \t} else if (enabledChapter&&chapterSvgs!=\"\" && evn == 3) {\n \t let chapterUrl = res.data.comment.chapter_card.url\n \t let enChapterSvgs = java.base64Encode(chapterSvgs);\n \t content += `<div rs-native><img onPress=\"java.showReadingBrowser('${mw+chapterUrl}','本章说')\" src=\"data:image\/svg+xml;base64,${enChapterSvgs}\"><\/div>`;\n \t}\n \n\treturn content;\n\t}",
"searchUrl": "<js>Api('\/api\/search?keyword={{key}}&page={{page}}&page_size=20')<\/js>",
"ruleSearch": {
"checkKeyWord": "我的",
"bookList": "<js>\n\/\/java.log(result)\nresult = JSON.parse(result);\nif (result.code!=0) {\n result = {\"data\":{\"items\":[\n {\n \"name\": \"❗❗❗当前未登录状态\",\n \"author\": \"请登录后使用\",\n \"latestChapterTitle\":\"请注册账号并登陆\",\n \"cover\": \"http:\/\/fc3tn.baidu.com\/it\/u=1573211303,3438572022&fm=203&src=787&fc_m=cvican\",\n \"description\":result.message,\n \"id\":\"\",\n \"source\":\"\"\n }\n ]}}\n}\nJSON.stringify(result)\n<\/js>\n$.data.items",
"name": "name",
"author": "author",
"kind": "{{$.category}},{{$.status}}",
"wordCount": "",
"lastChapter": "源站:{{$.source}}",
"intro": "description",
"coverUrl": "cover",
"bookUrl": "\/api\/book\/detail?source={{$.source}}&book_id={{$.id}}\n<js>\n`data:;base64,${java.base64Encode(result)},{\"type\":\"mw\"}`\n<\/js>",
"updateTime": ""
},
"exploreUrl": "<js>\nfunction styles(so) {\n var today = new Date();\n var dateT = today.getFullYear()\n + '-'\n + (today.getMonth() + 1)\n + '-'\n + today.getDate()\n + '_discover_tabs';\n\n var url = Api('\/api\/discover\/tabs');\n var res = null;\n var cacheData = '';\n var sourceItem = null;\n var result = [];\n var tab;\n var count = 0;\n var i;\n var u;\n\n try {\n cacheData = cache.get(dateT)+\"1\";\n\n if (cacheData) {\n try {\n res = JSON.parse(cacheData);\n } catch (e) {\n res = null;\n }\n }\n\n if (!res) {\n res = JSON.parse(java.ajax(url));\n\n if (res && res.code == 0) {\n cache.put(dateT, JSON.stringify(res));\n }\n }\n } catch (e) {\n return [{\n title: '数据加载失败,请刷新后重试',\n url: '',\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n }];\n }\n\n if (!res || res.code != 0 || !res.data || !res.data.sources) {\n return [{\n title: '请先登陆,登陆后刷新发现页即可',\n url: '',\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n }];\n }\n\n for (i = 0; i < res.data.sources.length; i++) {\n if (res.data.sources[i] && res.data.sources[i].name == so) {\n sourceItem = res.data.sources[i];\n break;\n }\n }\n\n if (!sourceItem || !sourceItem.tabs || sourceItem.tabs.length == 0) {\n return [{\n title: '暂无' + (so || '') + '发现内容',\n url: '',\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n }];\n }\n\n for (i = 0; i < sourceItem.tabs.length; i++) {\n tab = sourceItem.tabs[i];\n\n if (!tab) {\n continue;\n }\n\n if (tab.section) {\n if (count > 0) {\n while (count < 3) {\n result.push({\n title: '--',\n url: '',\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.25\n }\n });\n count++;\n }\n }\n\n result.push({\n title: tab.title || '',\n url: '',\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 1\n }\n });\n\n count = 0;\n continue;\n }\n\n u = '\/api\/discover\/content?source='\n + encodeURIComponent(sourceItem.source || '')\n + '&tab='\n + encodeURIComponent(tab.key || '')\n + '&page={{page}}&page_size=12';\n\n result.push({\n title: tab.title || '',\n url: Api(u),\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.25\n }\n });\n\n count++;\n\n if (count >= 3) {\n count = 0;\n }\n }\n\n if (count > 0) {\n while (count < 3) {\n result.push({\n title: '--',\n url: '',\n style: {\n layout_flexGrow: 1,\n layout_flexBasisPercent: 0.25\n }\n });\n\n count++;\n }\n }\n\n return result;\n}\n\nvar so = cache.get('sources') || '起点小说';\n\nvar s = [\n create(\n \"请选择平台\",\n [\"番茄小说\", \"七猫小说\", \"书旗小说\", \"塔读小说\",\"红烛故事\",\"西瓜小说\",\"轻小说\",\"星星短剧\",\"依依短剧\",\"起点小说\"],\n so,\n \"sources\",\n 1\n )\n];\n\n\n\nJSON.stringify(s.concat(styles(so)), null, 2);\n<\/js>",
"ruleExplore": {
"bookList": "",
"name": "",
"author": "",
"kind": "",
"wordCount": "",
"lastChapter": "",
"intro": "",
"coverUrl": "",
"bookUrl": "",
"updateTime": ""
},
"ruleBookInfo": {
"init": "<js>\nlet url = java.hexDecodeToString(result);\njava.ajax(Api(url));\n<\/js>",
"name": "$.data.name",
"author": "$.data.author",
"kind": "{{$.data.category}},{{$.data.status}}",
"wordCount": "",
"lastChapter": "",
"intro": "<js>\nresult = JSON.parse(result);\n\nlet typeM = {\n\t\"novel\" : \"小说\",\n\t\"audio\" : \"听书\",\n\t\"image\" : \"漫画\",\n\t\"video\" : \"短剧\"\n\t}\n\nconst {\n source: bookSource,\n latest_chapter,\n latest_update_time,\n description,\n type: sourceType\n} = result.data;\n\n[\n ` 源站:${bookSource}`,\n latest_chapter && `最新章节:${latest_chapter}`,\n latest_update_time && `更新时间:${formatDate(latest_update_time)}`,\n `类型:${typeM[sourceType] || '小说'}`,\n \"\\n\",\n description\n].filter(Boolean).join('\\n')\n<\/js>",
"coverUrl": "",
"tocUrl": "\/api\/book\/chapters?source={{$.data.source}}&book_id={{$.data.id}}&type={{$.data.type}}\n<js>\n`data:;base64,${java.base64Encode(result)},{\"type\":\"mw\"}`\n<\/js>",
"canReName": "",
"downloadUrls": "",
"updateTime": ""
},
"ruleToc": {
"preUpdateJs": "",
"chapterList": "<js>\nlet url = java.hexDecodeToString(result);\nlet resStr = java.ajax(Api(url));\nlet res = JSON.parse(resStr);\nlet sourceType = url.match(\/type=([^&]+)\/)[1];\njava.put('sourceType',sourceType);\n\n\nif (res.code != 0) {\n const parts = book.bookUrl.split(',')[1];\n const decodedUrl = java.base64Decode(parts);\n resStr = java.ajax(Api(decodedUrl.replace('book\/detail', 'book\/chapters')));\n}\n\nconst typeMap = {\n \"novel\": {\n ios: 0,\n default: 8\n },\n \"audio\": {\n ios: 1,\n default: 32\n },\n \"image\": {\n ios: 2,\n default: 64\n },\n \"video\": {\n ios: 3,\n default: 4\n }\n};\nconst currentType = typeMap[sourceType] || typeMap[\"novel\"];\nif (Env==3) {\nbook.type = currentType.ios;\n} else {\nbook.type = currentType.default;\n}\n\nresStr;\n<\/js>\n$.data.chapters",
"chapterName": "$.name",
"chapterUrl": "\/api\/chapter\/content?source={{$.source}}&chapter_id={{$.id}}&type={{java.get('sourceType')}}\n<js>\n`data:;base64,${java.base64Encode(result)},{\"type\":\"mw\"}`\n<\/js>",
"formatJs": "",
"isVolume": "",
"isVip": "",
"isPay": "",
"nextTocUrl": "",
"updateTime": "$.update_time\n<js>\nformatDate(result)\n<\/js>",
"wordCount": ""
},
"ruleContent": {
"content": "<js>\nlet url = java.hexDecodeToString(result);\njava.log(url)\nresult = java.ajax(Api(url));\nresult = JSON.parse(result);\n\nlet content;\nlet code = result.code;\nlet msg = result.message;\n\nlet sourceType = url.match(\/type=([^&]+)\/)[1];\n\nif (code != 0) {\n content = msg;\n} else if (sourceType && sourceType==\"novel\") {\n content = Content(result)\n} else {\n\tcontent = result.data.content;\n\t}\n\t\n<\/js>",
"title": "",
"nextContentUrl": "",
"webJs": "",
"sourceRegex": "",
"replaceRegex": "",
"subContent": "",
"imageStyle": "",
"imageDecode": "",
"payAction": "",
"callBackJs": ""
},
"lastUpdateTime": "1790609782867"
}