web写源添加订阅源

pull/1395/head
gedoor 3 years ago
parent fe1d2720a0
commit 6e79fb489c
  1. 7
      app/src/main/assets/web/bookSource/index.html
  2. 42
      app/src/main/assets/web/bookSource/index.js
  3. 150
      app/src/main/assets/web/rssSource/index.css
  4. 242
      app/src/main/assets/web/rssSource/index.html
  5. 488
      app/src/main/assets/web/rssSource/index.js
  6. 12
      app/src/main/java/io/legado/app/api/ReaderProvider.kt
  7. 2
      app/src/main/java/io/legado/app/api/controller/BookSourceController.kt
  8. 83
      app/src/main/java/io/legado/app/api/controller/RssSourceController.kt
  9. 2
      app/src/main/java/io/legado/app/data/entities/RssSource.kt
  10. 18
      app/src/main/java/io/legado/app/web/HttpServer.kt

@ -80,7 +80,7 @@
</div> </div>
<div> <div>
<div>校验文字:</div> <div>校验文字:</div>
<textarea rows="3" id="checkKeyWord" class="base" title="checkKeyWord" <textarea rows="3" id="checkKeyWord" class="ruleSearch" title="checkKeyWord"
placeholder="校验关键字"></textarea> placeholder="校验关键字"></textarea>
</div> </div>
<div> <div>
@ -253,6 +253,11 @@
<textarea rows="1" id="ruleToc_isVip" class="ruleToc" title="isVip" <textarea rows="1" id="ruleToc_isVip" class="ruleToc" title="isVip"
placeholder="章节是否为VIP章节 (规则结果为Bool)"></textarea> placeholder="章节是否为VIP章节 (规则结果为Bool)"></textarea>
</div> </div>
<div>
<div>购买标识:</div>
<textarea rows="1" id="ruleToc_isPay" class="ruleToc" title="ruleToc_isPay"
placeholder="章节是否为已购买 (规则结果为Bool)"></textarea>
</div>
<div> <div>
<div>章节信息:</div> <div>章节信息:</div>
<textarea rows="1" id="ruleToc_updateTime" class="ruleToc" title="updateTime" <textarea rows="1" id="ruleToc_updateTime" class="ruleToc" title="updateTime"

@ -70,8 +70,8 @@ function newRule(rule) {
} }
// 缓存规则列表 // 缓存规则列表
var RuleSources = []; var RuleSources = [];
if (localStorage.getItem('RuleSources')) { if (localStorage.getItem('BookSources')) {
RuleSources = JSON.parse(localStorage.getItem('RuleSources')); RuleSources = JSON.parse(localStorage.getItem('BookSources'));
RuleSources.forEach(item => $('#RuleList').innerHTML += newRule(item)); RuleSources.forEach(item => $('#RuleList').innerHTML += newRule(item));
} }
// 页面加载完成事件 // 页面加载完成事件
@ -218,36 +218,36 @@ function json2rule(RuleEditor) {
} }
// 记录操作过程 // 记录操作过程
var course = { "old": [], "now": {}, "new": [] }; var course = { "old": [], "now": {}, "new": [] };
if (localStorage.getItem('course')) { if (localStorage.getItem('bookSourceCourse')) {
course = JSON.parse(localStorage.getItem('course')); course = JSON.parse(localStorage.getItem('bookSourceCourse'));
json2rule(course.now); json2rule(course.now);
} }
else { else {
course.now = rule2json(); course.now = rule2json();
window.localStorage.setItem('course', JSON.stringify(course)); window.localStorage.setItem('bookSourceCourse', JSON.stringify(course));
} }
function todo() { function todo() {
course.old.push(Object.assign({}, course.now)); course.old.push(Object.assign({}, course.now));
course.now = rule2json(); course.now = rule2json();
course.new = []; course.new = [];
if (course.old.length > 50) course.old.shift(); // 限制历史记录堆栈大小 if (course.old.length > 50) course.old.shift(); // 限制历史记录堆栈大小
localStorage.setItem('course', JSON.stringify(course)); localStorage.setItem('bookSourceCourse', JSON.stringify(course));
} }
function undo() { function undo() {
course = JSON.parse(localStorage.getItem('course')); course = JSON.parse(localStorage.getItem('bookSourceCourse'));
if (course.old.length > 0) { if (course.old.length > 0) {
course.new.push(course.now); course.new.push(course.now);
course.now = course.old.pop(); course.now = course.old.pop();
localStorage.setItem('course', JSON.stringify(course)); localStorage.setItem('bookSourceCourse', JSON.stringify(course));
json2rule(course.now); json2rule(course.now);
} }
} }
function redo() { function redo() {
course = JSON.parse(localStorage.getItem('course')); course = JSON.parse(localStorage.getItem('bookSourceCourse'));
if (course.new.length > 0) { if (course.new.length > 0) {
course.old.push(course.now); course.old.push(course.now);
course.now = course.new.pop(); course.now = course.new.pop();
localStorage.setItem('course', JSON.stringify(course)); localStorage.setItem('bookSourceCourse', JSON.stringify(course));
json2rule(course.now); json2rule(course.now);
} }
} }
@ -275,7 +275,7 @@ $('.menu').addEventListener('click', e => {
case 'push': case 'push':
$$('#RuleList>label>div').forEach(item => { item.className = ''; }); $$('#RuleList>label>div').forEach(item => { item.className = ''; });
(async () => { (async () => {
await HttpPost(`/saveSources`, RuleSources).then(json => { await HttpPost(`/saveBookSources`, RuleSources).then(json => {
if (json.isSuccess) { if (json.isSuccess) {
let okData = json.data; let okData = json.data;
if (Array.isArray(okData)) { if (Array.isArray(okData)) {
@ -303,10 +303,10 @@ $('.menu').addEventListener('click', e => {
case 'pull': case 'pull':
showTab('书源列表'); showTab('书源列表');
(async () => { (async () => {
await HttpGet(`/getSources`).then(json => { await HttpGet(`/getBookSources`).then(json => {
if (json.isSuccess) { if (json.isSuccess) {
$('#RuleList').innerHTML = '' $('#RuleList').innerHTML = ''
localStorage.setItem('RuleSources', JSON.stringify(RuleSources = json.data)); localStorage.setItem('BookSources', JSON.stringify(RuleSources = json.data));
RuleSources.forEach(item => { RuleSources.forEach(item => {
$('#RuleList').innerHTML += newRule(item); $('#RuleList').innerHTML += newRule(item);
}); });
@ -349,7 +349,7 @@ $('.menu').addEventListener('click', e => {
let DebugInfos = $('#DebugConsole'); let DebugInfos = $('#DebugConsole');
function DebugPrint(msg) { DebugInfos.value += `\n${msg}`; DebugInfos.scrollTop = DebugInfos.scrollHeight; } function DebugPrint(msg) { DebugInfos.value += `\n${msg}`; DebugInfos.scrollTop = DebugInfos.scrollHeight; }
let saveRule = [rule2json()]; let saveRule = [rule2json()];
HttpPost(`/saveSources`, saveRule).then(sResult => { HttpPost(`/saveBookSources`, saveRule).then(sResult => {
if (sResult.isSuccess) { if (sResult.isSuccess) {
let sKey = DebugKey.value ? DebugKey.value : '我的'; let sKey = DebugKey.value ? DebugKey.value : '我的';
$('#DebugConsole').value = `书源《${saveRule[0].bookSourceName}》保存成功!使用搜索关键字“${sKey}”开始调试...`; $('#DebugConsole').value = `书源《${saveRule[0].bookSourceName}》保存成功!使用搜索关键字“${sKey}”开始调试...`;
@ -377,7 +377,7 @@ $('.menu').addEventListener('click', e => {
case 'accept': case 'accept':
(async () => { (async () => {
let saveRule = [rule2json()]; let saveRule = [rule2json()];
await HttpPost(`/saveSources`, saveRule).then(json => { await HttpPost(`/saveBookSources`, saveRule).then(json => {
alert(json.isSuccess ? `书源《${saveRule[0].bookSourceName}》已成功保存到「阅读3.0APP」` : `书源《${saveRule[0].bookSourceName}》保存失败!\nErrorMsg: ${json.errorMsg}`); alert(json.isSuccess ? `书源《${saveRule[0].bookSourceName}》已成功保存到「阅读3.0APP」` : `书源《${saveRule[0].bookSourceName}》保存失败!\nErrorMsg: ${json.errorMsg}`);
setRule(saveRule[0]); setRule(saveRule[0]);
}).catch(err => { alert(`保存书源失败,无法连接到「阅读3.0APP」!\n${err}`); }); }).catch(err => { alert(`保存书源失败,无法连接到「阅读3.0APP」!\n${err}`); });
@ -426,7 +426,7 @@ $('#RuleList').addEventListener('click', e => {
if (editRule.bookSourceUrl == '') return; if (editRule.bookSourceUrl == '') return;
if (editRule.bookSourceName == '') editRule.bookSourceName = editRule.bookSourceUrl.replace(/.*?\/\/|\/.*/g, ''); if (editRule.bookSourceName == '') editRule.bookSourceName = editRule.bookSourceUrl.replace(/.*?\/\/|\/.*/g, '');
setRule(editRule); setRule(editRule);
localStorage.setItem('RuleSources', JSON.stringify(RuleSources)); localStorage.setItem('BookSources', JSON.stringify(RuleSources));
}); });
// 处理列表按钮事件 // 处理列表按钮事件
$('.tab3>.titlebar').addEventListener('click', e => { $('.tab3>.titlebar').addEventListener('click', e => {
@ -448,7 +448,7 @@ $('.tab3>.titlebar').addEventListener('click', e => {
let newSources = []; let newSources = [];
newSources.push(...fileJson); newSources.push(...fileJson);
if (window.confirm(`如何处理导入的书源?\n"确定": 覆盖当前列表(不会删除APP源)\n"取消": 插入列表尾部(自动忽略重复源)`)) { if (window.confirm(`如何处理导入的书源?\n"确定": 覆盖当前列表(不会删除APP源)\n"取消": 插入列表尾部(自动忽略重复源)`)) {
localStorage.setItem('RuleSources', JSON.stringify(RuleSources = newSources)); localStorage.setItem('BookSources', JSON.stringify(RuleSources = newSources));
$('#RuleList').innerHTML = '' $('#RuleList').innerHTML = ''
RuleSources.forEach(item => { RuleSources.forEach(item => {
$('#RuleList').innerHTML += newRule(item); $('#RuleList').innerHTML += newRule(item);
@ -457,7 +457,7 @@ $('.tab3>.titlebar').addEventListener('click', e => {
else { else {
newSources = newSources.filter(item => !JSON.stringify(RuleSources).includes(item.bookSourceUrl)); newSources = newSources.filter(item => !JSON.stringify(RuleSources).includes(item.bookSourceUrl));
RuleSources.push(...newSources); RuleSources.push(...newSources);
localStorage.setItem('RuleSources', JSON.stringify(RuleSources)); localStorage.setItem('BookSources', JSON.stringify(RuleSources));
newSources.forEach(item => { newSources.forEach(item => {
$('#RuleList').innerHTML += newRule(item); $('#RuleList').innerHTML += newRule(item);
}); });
@ -490,11 +490,11 @@ $('.tab3>.titlebar').addEventListener('click', e => {
let selectRuleUrl = selectRule.id; let selectRuleUrl = selectRule.id;
let deleteSources = RuleSources.filter(item => item.bookSourceUrl == selectRuleUrl); // 提取待删除的书源 let deleteSources = RuleSources.filter(item => item.bookSourceUrl == selectRuleUrl); // 提取待删除的书源
let laveSources = RuleSources.filter(item => !(item.bookSourceUrl == selectRuleUrl)); // 提取待留下的书源 let laveSources = RuleSources.filter(item => !(item.bookSourceUrl == selectRuleUrl)); // 提取待留下的书源
HttpPost(`/deleteSources`, deleteSources).then(json => { HttpPost(`/deleteBookSources`, deleteSources).then(json => {
if (json.isSuccess) { if (json.isSuccess) {
let selectNode = document.getElementById(selectRuleUrl).parentNode; let selectNode = document.getElementById(selectRuleUrl).parentNode;
selectNode.parentNode.removeChild(selectNode); selectNode.parentNode.removeChild(selectNode);
localStorage.setItem('RuleSources', JSON.stringify(RuleSources = laveSources)); localStorage.setItem('BookSources', JSON.stringify(RuleSources = laveSources));
if ($('#bookSourceUrl').value == selectRuleUrl) { if ($('#bookSourceUrl').value == selectRuleUrl) {
$$('.rules textarea').forEach(item => { item.value = '' }); $$('.rules textarea').forEach(item => { item.value = '' });
todo(); todo();
@ -507,7 +507,7 @@ $('.tab3>.titlebar').addEventListener('click', e => {
break; break;
case 'ClrAll': case 'ClrAll':
if (confirm(`确定要清空当前书源列表吗?\n(不会删除APP内书源)`)) { if (confirm(`确定要清空当前书源列表吗?\n(不会删除APP内书源)`)) {
localStorage.setItem('RuleSources', JSON.stringify(RuleSources = [])); localStorage.setItem('BookSources', JSON.stringify(RuleSources = []));
$('#RuleList').innerHTML = '' $('#RuleList').innerHTML = ''
} }
break; break;

@ -0,0 +1,150 @@
body {
margin: 0;
}
.editor {
display: flex;
align-items: stretch;
}
.setbox,
.menu,
.outbox {
flex: 1;
display: flex;
flex-flow: column;
max-height: 100vh;
overflow-y: auto;
}
.menu {
justify-content: center;
max-width: 90px;
margin: 0 5px;
}
.menu .button {
width: 90px;
height: 30px;
min-height: 30px;
margin: 5px 0px;
cursor: pointer;
}
@keyframes stroker {
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -240;
}
}
.button rect {
width: 100%;
height: 100%;
fill: transparent;
stroke: #666;
stroke-width: 2px;
}
.button rect.busy {
stroke: #fd1850;
stroke-dasharray: 30 90;
animation: stroker 1s linear infinite;
}
.button text {
text-anchor: middle;
dominant-baseline: middle;
}
.setbox {
min-width: 40em;
}
.rules {
overflow: auto;
}
.tabbox {
flex: 1;
display: flex;
flex-flow: column;
}
.rules > * {
display: flex;
margin: 2px 0;
}
.rules textarea {
flex: 1;
margin-left: 5px;
}
.rules > *,
.rules > * > div,
.rules textarea {
min-height: 1em;
}
textarea {
word-break: break-all;
}
.tabtitle {
display: flex;
z-index: 1;
justify-content: flex-end;
}
.tabtitle > div {
cursor: pointer;
padding: 1px 10px 0 10px;
border-bottom: 3px solid transparent;
font-weight: bold;
}
.tabtitle > .this {
color: #4f9da6;
border-bottom-color: #4ebbe4;
}
.tabbody {
flex: 1;
display: flex;
margin-top: -1px;
border: 1px solid #a9a9a9;
height: 0;
}
.tabbody > * {
flex: 1;
flex-flow: column;
display: none;
}
.tabbody > .this {
display: flex;
}
.tabbody > * > .titlebar {
display: flex;
}
.tabbody > * > .titlebar > * {
flex: 1;
margin: 1px 1px 1px 1px;
}
.tabbody > * > .context {
flex: 1;
flex-flow: column;
border: 0;
padding: 5px;
overflow-y: auto;
}
.tabbody > * > .inputbox {
border: 0;
border-bottom: #a9a9a9 solid 1px;
height: 15px;
text-align: center;
}
.link > * {
display: flex;
margin: 5px;
border-bottom: 1px solid;
text-decoration: none;
}
#RuleList > label > * {
background: #eee;
padding-left: 3px;
margin: 2px 0;
cursor: pointer;
}
#RuleList input[type="radio"] {
display: none;
}
#RuleList input[type="radio"]:checked + * {
background: #15cda8;
}
.isError {
color: #ff0000;
}

@ -0,0 +1,242 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>阅读3.0书源编辑器_V4.0</title>
<link rel="icon" href="favicon.ico">
<link rel="stylesheet" type="text/css" href="index.css"/>
</head>
<body>
<div class="editor">
<div class="setbox">
<div>
<a href="../index.html">←主页</a>
</div>
<div class="rules">
<div><b>基本</b></div>
<div>
<div>源域名 :</div>
<textarea rows="1" id="sourceUrl" class="base" title="sourceUrl"
placeholder="<必填>通常填写网站主页,例: https://www.qidian.com"></textarea>
</div>
<div>
<div>源名称 :</div>
<textarea rows="1" id="sourceName" class="base" title="sourceName"
placeholder="&lt;必填&gt;源名称"></textarea>
</div>
<div>
<div>图标  :</div>
<textarea rows="1" id="sourceIcon" class="base" title="sourceIcon"
placeholder="&lt;选填&gt;图标"></textarea>
</div>
<div>
<div>源分组 :</div>
<textarea rows="1" id="sourceGroup" class="base" title="sourceGroup"
placeholder="&lt;选填&gt;描述书源的特征信息"></textarea>
</div>
<div>
<div>源注释 :</div>
<textarea rows="1" id="sourceComment" class="base" title="sourceComment"
placeholder="&lt;选填&gt;描述书源作者和状态"></textarea>
</div>
<div>
<div>登录地址:</div>
<textarea rows="1" id="loginUrl" class="base" title="loginUrl"
placeholder="&lt;选填&gt;填写网站登录网址,仅在需要登录的书源有用"></textarea>
</div>
<div>
<div>登录界面:</div>
<textarea rows="3" id="loginUi" class="base" title="loginUi"
placeholder="&lt;选填&gt;自定义登录界面"></textarea>
</div>
<div>
<div>登录检测:</div>
<textarea rows="3" id="loginCheckJs" class="base" title="loginCheckJs"
placeholder="&lt;选填&gt;登录检测js"></textarea>
</div>
<div>
<div>并发率 :</div>
<textarea rows="1" id="concurrentRate" class="base" title="concurrentRate"
placeholder="&lt;选填&gt;并发率"></textarea>
</div>
<div>
<div>请求头 :</div>
<textarea rows="3" id="header" class="base" title="header"
placeholder="&lt;选填&gt;客户端标识"></textarea>
</div>
<div>
<div>分类地址:</div>
<textarea rows="1" id="sortUrl" class="base" title="sortUrl"
placeholder="&lt;选填&gt;例:&#10;名称1::网址(Url)1&#10;名称2::网址(Url)2&#10;..."></textarea>
</div>
<p></p>
<div><b>列表规则</b></div>
<div>
<div>列表样式:</div>
<textarea rows="3" id="articleStyle" class="base" title="articleStyle"
placeholder="列表样式:0,1,2"></textarea>
</div>
<div>
<div>列表规则:</div>
<textarea rows="3" id="ruleArticles" class="base" title="ruleArticles"
placeholder="列表规则 (规则结果为List&lt;Element&gt;)"></textarea>
</div>
<div>
<div>标题规则:</div>
<textarea rows="1" id="ruleTitle" class="base" title="ruleTitle"
placeholder="选择节点书名 (规则结果为String)"></textarea>
</div>
<div>
<div>时间规则:</div>
<textarea rows="1" id="rulePubDate" class="base" title="rulePubDate"
placeholder="发表时间 (规则结果为String)"></textarea>
</div>
<div>
<div>翻页规则:</div>
<textarea rows="1" id="ruleNextPage" class="base" title="ruleNextPage"
placeholder="下一页链接 (规则结果为List&lt;String&gt;或String)"></textarea>
</div>
<p></p>
<div><b>WebView规则</b></div>
<div>
<div>加载url :</div>
<textarea rows="1" id="loadWithBaseUrl" class="base" title="loadWithBaseUrl"
placeholder="是否加载url (启用: true 关闭: false (可选,默认true))"></textarea>
</div>
<div>
<div>启用Js :</div>
<textarea rows="1" id="enableJs" class="base" title="enableJs"
placeholder="是否启用Js (启用: true 关闭: false (可选,默认true))"></textarea>
</div>
<div>
<div>描述规则:</div>
<textarea rows="1" id="ruleDescription" class="base" title="ruleDescription"
placeholder="rss内容 (规则结果为String)"></textarea>
</div>
<div>
<div>图片Url :</div>
<textarea rows="1" id="ruleImage" class="base" title="ruleImage"
placeholder="图片rul规则 (规则结果为url)"></textarea>
</div>
<div>
<div>原文链接:</div>
<textarea rows="1" id="ruleLink" class="base" title="ruleLink"
placeholder="原文链接规则 (规则结果为url)"></textarea>
</div>
<div>
<div>内容规则:</div>
<textarea rows="1" id="ruleContent" class="base" title="ruleContent"
placeholder="内容规则 (规则结果为String)"></textarea>
</div>
<div>
<div>内容样式:</div>
<textarea rows="1" id="style" class="base" title="style"
placeholder="内容样式 (css样式)"></textarea>
</div>
<p></p>
<div><b>其它规则</b></div>
<div>
<div>启用  :</div>
<textarea rows="1" id="enabled" class="base" title="enabled"
placeholder="启用: true 关闭: false (可选,默认true)"></textarea>
</div>
<div>
<div>排序编号:</div>
<textarea rows="1" id="customOrder" class="base" title="customOrder"
placeholder="整数: 0~N (可选,默认0) | 数字越小越靠前"></textarea>
</div>
</div>
</div>
<div class="menu">
<svg class="button">
<text x="50%" y="55%">⇈推送书源</text>
<rect id="push"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">⇊拉取书源</text>
<rect id="pull"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">⋘编辑书源</text>
<rect id="editor"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">⋙生成书源</text>
<rect id="conver"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">✗清空表单</text>
<rect id="initial"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">↶撤销操作</text>
<rect id="undo"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">↷重做操作</text>
<rect id="redo"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">⇏调试书源</text>
<rect id="debug"></rect>
</svg>
<svg class="button">
<text x="50%" y="55%">✓保存书源</text>
<rect id="accept"></rect>
</svg>
</div>
<div class="outbox">
<div class="tabbox">
<div class="tabtitle">
<div name="编辑书源" class="tab1 this">编辑书源</div>
<div name="调试书源" class="tab2">调试书源</div>
<div name="书源列表" class="tab3">书源列表</div>
<div name="帮助信息" class="tab4">帮助信息</div>
</div>
<div class="tabbody">
<div class="tab1 this">
<textarea class="context" id="RuleJsonString"
placeholder="这里输出序列化的JSON数据,可直接导入'阅读'APP"></textarea>
</div>
<div class="tab2">
<input type="text" class="inputbox" id="DebugKey" placeholder="输入搜索关键字,默认搜「我的」">
<textarea class="context" id="DebugConsole" placeholder="这里用于输出调试信息"></textarea>
</div>
<div class="tab3">
<input type="text" class="inputbox" id="Filter"
placeholder="输入筛选关键词(源名称、源URL或源分组)后按回车筛选源">
<div class="titlebar">
<button id="Import">导入书源文件</button>
<button id="Export">导出书源文件</button>
<button id="Delete">删除选中书源</button>
<button id="ClrAll">清空当前列表</button>
</div>
<div class="context" id="RuleList"></div>
</div>
<div class="tab4">
<div class="context link">
<a target="_blank" href="https://alanskycn.gitee.io/teachme">源制作教程</a>
<a target="_blank"
href="https://zhuanlan.zhihu.com/p/29436838">Xpath基础教程</a>
<a target="_blank"
href="https://zhuanlan.zhihu.com/p/32187820">Xpath高级教程</a>
<a target="_blank" href="https://www.w3cschool.cn/regex_rmjc">正则表达式教程</a>
<a target="_blank" href="https://regexr.com">正则表达式在线验证工具</a>
<div>^$()[]{}.?+*| 这些是Java正则特殊符号,匹配需转义
<br>(?s) 前缀表示跨行解析
<br>(?m) 前缀表示逐行匹配
<br>(?i) 前缀表示忽略大小写
</div>
<a target="_blank" href="https://www.beta.browxy.com">代码在线运行工具</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>

@ -0,0 +1,488 @@
// 简化js原生选择器
function $(selector) { return document.querySelector(selector); }
function $$(selector) { return document.querySelectorAll(selector); }
// 读写Hash值(val未赋值时为读取)
function hashParam(key, val) {
let hashstr = decodeURIComponent(window.location.hash);
let regKey = new RegExp(`${key}=([^&]*)`);
let getVal = regKey.test(hashstr) ? hashstr.match(regKey)[1] : null;
if (val == undefined) return getVal;
if (hashstr == '' || hashstr == '#') {
window.location.hash = `#${key}=${val}`;
}
else {
if (getVal) window.location.hash = hashstr.replace(getVal, val);
else {
window.location.hash = hashstr.indexOf(key) > -1 ? hashstr.replace(regKey, `${key}=${val}`) : `${hashstr}&${key}=${val}`;
}
}
}
// 创建书源规则容器对象
function Container() {
let ruleJson = {};
// 基本以及其他
$$('.rules .base').forEach(item => ruleJson[item.title] = '');
ruleJson.customOrder = 0;
ruleJson.enabled = true;
return ruleJson;
}
// 选项卡Tab切换事件处理
function showTab(tabName) {
$$('.tabtitle>*').forEach(node => { node.className = node.className.replace(' this', ''); });
$$('.tabbody>*').forEach(node => { node.className = node.className.replace(' this', ''); });
$(`.tabbody>.${$(`.tabtitle>*[name=${tabName}]`).className}`).className += ' this';
$(`.tabtitle>*[name=${tabName}]`).className += ' this';
hashParam('tab', tabName);
}
// 书源列表列表标签构造函数
function newRule(rule) {
return `<label for="${rule.bookSourceUrl}"><input type="radio" name="rule" id="${rule.bookSourceUrl}"><div>${rule.bookSourceName}<br>${rule.bookSourceUrl}</div></label>`;
}
// 缓存规则列表
var RuleSources = [];
if (localStorage.getItem('RssSources')) {
RuleSources = JSON.parse(localStorage.getItem('RssSources'));
RuleSources.forEach(item => $('#RuleList').innerHTML += newRule(item));
}
// 页面加载完成事件
window.onload = () => {
$$('.tabtitle>*').forEach(item => {
item.addEventListener('click', () => {
showTab(item.innerHTML);
});
});
if (hashParam('tab')) showTab(hashParam('tab'));
}
// 获取数据
function HttpGet(url) {
return fetch(hashParam('domain') ? hashParam('domain') + url : url)
.then(res => res.json()).catch(err => console.error('Error:', err));
}
// 提交数据
function HttpPost(url, data) {
return fetch(hashParam('domain') ? hashParam('domain') + url : url, {
body: JSON.stringify(data),
method: 'POST',
mode: "cors",
headers: new Headers({
'Content-Type': 'application/json;charset=utf-8'
})
}).then(res => res.json()).catch(err => console.error('Error:', err));
}
// 将书源表单转化为书源对象
function rule2json() {
let RuleJSON = Container();
// 转换base
Object.keys(RuleJSON).forEach(key => {
if (!key.startsWith("rule")) {
RuleJSON[key] = $('#' + key).value;
}
});
// 转换搜索规则
let searchJson = {};
Object.keys(RuleJSON.ruleSearch).forEach(key => {
if ($('#' + 'ruleSearch_' + key).value)
searchJson[key] = $('#' + 'ruleSearch_' + key).value;
});
RuleJSON.ruleSearch = searchJson;
// 转换发现规则
let exploreJson = {};
Object.keys(RuleJSON.ruleExplore).forEach(key => {
if ($('#' + 'ruleExplore_' + key).value)
exploreJson[key] = $('#' + 'ruleExplore_' + key).value;
});
RuleJSON.ruleExplore = exploreJson;
// 转换详情页规则
let bookInfoJson = {};
Object.keys(RuleJSON.ruleBookInfo).forEach(key => {
if ($('#' + 'ruleBookInfo_' + key).value)
bookInfoJson[key] = $('#' + 'ruleBookInfo_' + key).value;
});
RuleJSON.ruleBookInfo = bookInfoJson;
// 转换目录规则
let tocJson = {};
Object.keys(RuleJSON.ruleToc).forEach(key => {
if ($('#' + 'ruleToc_' + key).value)
tocJson[key] = $('#' + 'ruleToc_' + key).value;
});
RuleJSON.ruleToc = tocJson;
// 转换正文规则
let contentJson = {};
Object.keys(RuleJSON.ruleContent).forEach(key => {
if ($('#' + 'ruleContent_' + key).value)
contentJson[key] = $('#' + 'ruleContent_' + key).value;
});
RuleJSON.ruleContent = contentJson;
RuleJSON.lastUpdateTime = new Date().getTime();
RuleJSON.customOrder = RuleJSON.customOrder == '' ? 0 : parseInt(RuleJSON.customOrder);
RuleJSON.weight = RuleJSON.weight == '' ? 0 : parseInt(RuleJSON.weight);
RuleJSON.bookSourceType == RuleJSON.bookSourceType == '' ? 0 : parseInt(RuleJSON.bookSourceType);
RuleJSON.enabled = RuleJSON.enabled == '' || String(RuleJSON.enabled).toLocaleLowerCase().replace(/^\s*|\s*$/g, '') == 'true';
RuleJSON.enabledExplore = RuleJSON.enabledExplore == '' || String(RuleJSON.enabledExplore).toLocaleLowerCase().replace(/^\s*|\s*$/g, '') == 'true';
return RuleJSON;
}
// 将书源对象填充到书源表单
function json2rule(RuleEditor) {
let RuleJSON = Container();
// 转换base
Object.keys(RuleJSON).forEach(key => {
if (!key.startsWith("rule")) {
let val = RuleEditor[key];
if (typeof val == "number") {
$("#" + key).value = val ? String(val) : '0';
}
else if (typeof val == "boolean") {
$("#" + key).value = val ? String(val) : 'false';
}
else {
$("#" + key).value = val ? String(val) : '';
}
}
});
// 转换搜索规则
if (RuleEditor.ruleSearch) {
let searchJson = RuleEditor.ruleSearch;
Object.keys(RuleJSON.ruleSearch).forEach(key => {
$('#' + 'ruleSearch_' + key).value = searchJson[key] ? searchJson[key] : '';
});
}
// 转换发现规则
if (RuleEditor.ruleExplore) {
let exploreJson = RuleEditor.ruleExplore;
Object.keys(RuleJSON.ruleExplore).forEach(key => {
$('#' + 'ruleExplore_' + key).value = exploreJson[key] ? exploreJson[key] : '';
});
}
// 转换详情页规则
if (RuleEditor.ruleBookInfo) {
let bookInfoJson = RuleEditor.ruleBookInfo;
Object.keys(RuleJSON.ruleBookInfo).forEach(key => {
$('#' + 'ruleBookInfo_' + key).value = bookInfoJson[key] ? bookInfoJson[key] : '';
});
}
// 转换目录规则
if (RuleEditor.ruleToc) {
let tocJson = RuleEditor.ruleToc;
Object.keys(RuleJSON.ruleToc).forEach(key => {
$('#' + 'ruleToc_' + key).value = tocJson[key] ? tocJson[key] : '';
});
}
// 转换正文规则
if (RuleEditor.ruleContent) {
let contentJson = RuleEditor.ruleContent;
Object.keys(RuleJSON.ruleContent).forEach(key => {
$('#' + 'ruleContent_' + key).value = contentJson[key] ? contentJson[key] : '';
});
}
}
// 记录操作过程
var course = { "old": [], "now": {}, "new": [] };
if (localStorage.getItem('rssSourceCourse')) {
course = JSON.parse(localStorage.getItem('rssSourceCourse'));
json2rule(course.now);
}
else {
course.now = rule2json();
window.localStorage.setItem('rssSourceCourse', JSON.stringify(course));
}
function todo() {
course.old.push(Object.assign({}, course.now));
course.now = rule2json();
course.new = [];
if (course.old.length > 50) course.old.shift(); // 限制历史记录堆栈大小
localStorage.setItem('rssSourceCourse', JSON.stringify(course));
}
function undo() {
course = JSON.parse(localStorage.getItem('rssSourceCourse'));
if (course.old.length > 0) {
course.new.push(course.now);
course.now = course.old.pop();
localStorage.setItem('rssSourceCourse', JSON.stringify(course));
json2rule(course.now);
}
}
function redo() {
course = JSON.parse(localStorage.getItem('rssSourceCourse'));
if (course.new.length > 0) {
course.old.push(course.now);
course.now = course.new.pop();
localStorage.setItem('rssSourceCourse', JSON.stringify(course));
json2rule(course.now);
}
}
function setRule(editRule) {
let checkRule = RuleSources.find(x => x.bookSourceUrl == editRule.bookSourceUrl);
if ($(`input[id="${editRule.bookSourceUrl}"]`)) {
Object.keys(checkRule).forEach(key => { checkRule[key] = editRule[key]; });
$(`input[id="${editRule.bookSourceUrl}"]+*`).innerHTML = `${editRule.bookSourceName}<br>${editRule.bookSourceUrl}`;
} else {
RuleSources.push(editRule);
$('#RuleList').innerHTML += newRule(editRule);
}
}
$$('input').forEach((item) => { item.addEventListener('change', () => { todo() }) });
$$('textarea').forEach((item) => { item.addEventListener('change', () => { todo() }) });
// 处理按钮点击事件
$('.menu').addEventListener('click', e => {
let thisNode = e.target;
thisNode = thisNode.parentNode.nodeName == 'svg' ? thisNode.parentNode.querySelector('rect') :
thisNode.nodeName == 'svg' ? thisNode.querySelector('rect') : null;
if (!thisNode) return;
if (thisNode.getAttribute('class') == 'busy') return;
thisNode.setAttribute('class', 'busy');
switch (thisNode.id) {
case 'push':
$$('#RuleList>label>div').forEach(item => { item.className = ''; });
(async () => {
await HttpPost(`/saveSources`, RuleSources).then(json => {
if (json.isSuccess) {
let okData = json.data;
if (Array.isArray(okData)) {
let failMsg = ``;
if (RuleSources.length > okData.length) {
RuleSources.forEach(item => {
if (okData.find(x => x.bookSourceUrl == item.bookSourceUrl)) { }
else { $(`#RuleList #${item.bookSourceUrl}+*`).className += 'isError'; }
});
failMsg = '\n推送失败的书源将用红色字体标注!';
}
alert(`批量推送书源到「阅读3.0APP」\n共计: ${RuleSources.length}\n成功: ${okData.length}\n失败: ${RuleSources.length - okData.length}${failMsg}`);
}
else {
alert(`批量推送书源到「阅读3.0APP」成功!\n共计: ${RuleSources.length}`);
}
}
else {
alert(`批量推送书源失败!\nErrorMsg: ${json.errorMsg}`);
}
}).catch(err => { alert(`批量推送书源失败,无法连接到「阅读3.0APP」!\n${err}`); });
thisNode.setAttribute('class', '');
})();
return;
case 'pull':
showTab('书源列表');
(async () => {
await HttpGet(`/getSources`).then(json => {
if (json.isSuccess) {
$('#RuleList').innerHTML = ''
localStorage.setItem('RssSources', JSON.stringify(RuleSources = json.data));
RuleSources.forEach(item => {
$('#RuleList').innerHTML += newRule(item);
});
alert(`成功拉取 ${RuleSources.length} 条书源`);
}
else {
alert(`批量拉取书源失败!\nErrorMsg: ${json.errorMsg}`);
}
}).catch(err => { alert(`批量拉取书源失败,无法连接到「阅读3.0APP」!\n${err}`); });
thisNode.setAttribute('class', '');
})();
return;
case 'editor':
if ($('#RuleJsonString').value == '') break;
try {
json2rule(JSON.parse($('#RuleJsonString').value));
todo();
} catch (error) {
console.log(error);
alert(error);
}
break;
case 'conver':
showTab('编辑书源');
$('#RuleJsonString').value = JSON.stringify(rule2json(), null, 4);
break;
case 'initial':
$$('.rules textarea').forEach(item => { item.value = '' });
todo();
break;
case 'undo':
undo()
break;
case 'redo':
redo()
break;
case 'debug':
showTab('调试书源');
let wsOrigin = (hashParam('domain') || location.origin).replace(/^.*?:/, 'ws:').replace(/\d+$/, (port) => (parseInt(port) + 1));
let DebugInfos = $('#DebugConsole');
function DebugPrint(msg) { DebugInfos.value += `\n${msg}`; DebugInfos.scrollTop = DebugInfos.scrollHeight; }
let saveRule = [rule2json()];
HttpPost(`/saveSources`, saveRule).then(sResult => {
if (sResult.isSuccess) {
let sKey = DebugKey.value ? DebugKey.value : '我的';
$('#DebugConsole').value = `书源《${saveRule[0].bookSourceName}》保存成功!使用搜索关键字“${sKey}”开始调试...`;
let ws = new WebSocket(`${wsOrigin}/sourceDebug`);
ws.onopen = () => {
ws.send(`{"tag":"${saveRule[0].bookSourceUrl}", "key":"${sKey}"}`);
};
ws.onmessage = (msg) => {
console.log('[调试]', msg);
DebugPrint(msg.data);
};
ws.onerror = (err) => {
throw `${err.data}`;
}
ws.onclose = () => {
thisNode.setAttribute('class', '');
DebugPrint(`\n调试服务已关闭!`);
}
} else throw `${sResult.errorMsg}`;
}).catch(err => {
DebugPrint(`调试过程意外中止,以下是详细错误信息:\n${err}`);
thisNode.setAttribute('class', '');
});
return;
case 'accept':
(async () => {
let saveRule = [rule2json()];
await HttpPost(`/saveSources`, saveRule).then(json => {
alert(json.isSuccess ? `书源《${saveRule[0].bookSourceName}》已成功保存到「阅读3.0APP」` : `书源《${saveRule[0].bookSourceName}》保存失败!\nErrorMsg: ${json.errorMsg}`);
setRule(saveRule[0]);
}).catch(err => { alert(`保存书源失败,无法连接到「阅读3.0APP」!\n${err}`); });
thisNode.setAttribute('class', '');
})();
return;
default:
}
setTimeout(() => { thisNode.setAttribute('class', ''); }, 500);
});
$('#DebugKey').addEventListener('keydown', e => {
if (e.keyCode == 13) {
let clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent("click", true, false);
$('#debug').dispatchEvent(clickEvent);
}
});
$('#Filter').addEventListener('keydown', e => {
if (e.keyCode == 13) {
let cashList = [];
$('#RuleList').innerHTML = "";
let sKey = Filter.value ? Filter.value : '';
if (sKey == '') {
cashList = RuleSources;
} else {
let patt = new RegExp(sKey);
RuleSources.forEach(source => {
if (patt.test(source.bookSourceUrl) || patt.test(source.bookSourceName) || patt.test(source.bookSourceGroup)) {
cashList.push(source);
}
})
}
cashList.forEach(source => {
$('#RuleList').innerHTML += newRule(source);
})
}
});
// 列表规则更改事件
$('#RuleList').addEventListener('click', e => {
let editRule = null;
if (e.target && e.target.getAttribute('name') == 'rule') {
editRule = rule2json();
json2rule(RuleSources.find(x => x.bookSourceUrl == e.target.id));
} else return;
if (editRule.bookSourceUrl == '') return;
if (editRule.bookSourceName == '') editRule.bookSourceName = editRule.bookSourceUrl.replace(/.*?\/\/|\/.*/g, '');
setRule(editRule);
localStorage.setItem('RssSources', JSON.stringify(RuleSources));
});
// 处理列表按钮事件
$('.tab3>.titlebar').addEventListener('click', e => {
let thisNode = e.target;
if (thisNode.nodeName != 'BUTTON') return;
switch (thisNode.id) {
case 'Import':
let fileImport = document.createElement('input');
fileImport.type = 'file';
fileImport.accept = '.json';
fileImport.addEventListener('change', () => {
let file = fileImport.files[0];
let reader = new FileReader();
reader.onloadend = function (evt) {
if (evt.target.readyState == FileReader.DONE) {
let fileText = evt.target.result;
try {
let fileJson = JSON.parse(fileText);
let newSources = [];
newSources.push(...fileJson);
if (window.confirm(`如何处理导入的书源?\n"确定": 覆盖当前列表(不会删除APP源)\n"取消": 插入列表尾部(自动忽略重复源)`)) {
localStorage.setItem('RssSources', JSON.stringify(RuleSources = newSources));
$('#RuleList').innerHTML = ''
RuleSources.forEach(item => {
$('#RuleList').innerHTML += newRule(item);
});
}
else {
newSources = newSources.filter(item => !JSON.stringify(RuleSources).includes(item.bookSourceUrl));
RuleSources.push(...newSources);
localStorage.setItem('RssSources', JSON.stringify(RuleSources));
newSources.forEach(item => {
$('#RuleList').innerHTML += newRule(item);
});
}
alert(`成功导入 ${newSources.length} 条书源`);
}
catch (err) {
alert(`导入书源文件失败!\n${err}`);
}
}
};
reader.readAsText(file);
}, false);
fileImport.click();
break;
case 'Export':
let fileExport = document.createElement('a');
fileExport.download = `Rules${Date().replace(/.*?\s(\d+)\s(\d+)\s(\d+:\d+:\d+).*/, '$2$1$3').replace(/:/g, '')}.json`;
let myBlob = new Blob([JSON.stringify(RuleSources, null, 4)], { type: "application/json" });
fileExport.href = window.URL.createObjectURL(myBlob);
fileExport.click();
break;
case 'Delete':
let selectRule = $('#RuleList input:checked');
if (!selectRule) {
alert(`没有书源被选中!`);
return;
}
if (confirm(`确定要删除选定书源吗?\n(同时删除APP内书源)`)) {
let selectRuleUrl = selectRule.id;
let deleteSources = RuleSources.filter(item => item.bookSourceUrl == selectRuleUrl); // 提取待删除的书源
let laveSources = RuleSources.filter(item => !(item.bookSourceUrl == selectRuleUrl)); // 提取待留下的书源
HttpPost(`/deleteSources`, deleteSources).then(json => {
if (json.isSuccess) {
let selectNode = document.getElementById(selectRuleUrl).parentNode;
selectNode.parentNode.removeChild(selectNode);
localStorage.setItem('RssSources', JSON.stringify(RuleSources = laveSources));
if ($('#bookSourceUrl').value == selectRuleUrl) {
$$('.rules textarea').forEach(item => { item.value = '' });
todo();
}
console.log(deleteSources);
console.log(`以上书源已删除!`)
}
}).catch(err => { alert(`删除书源失败,无法连接到「阅读3.0APP」!\n${err}`); });
}
break;
case 'ClrAll':
if (confirm(`确定要清空当前书源列表吗?\n(不会删除APP内书源)`)) {
localStorage.setItem('RssSources', JSON.stringify(RuleSources = []));
$('#RuleList').innerHTML = ''
}
break;
default:
}
});

@ -11,7 +11,7 @@ import android.database.MatrixCursor
import android.net.Uri import android.net.Uri
import com.google.gson.Gson import com.google.gson.Gson
import io.legado.app.api.controller.BookController import io.legado.app.api.controller.BookController
import io.legado.app.api.controller.SourceController import io.legado.app.api.controller.BookSourceController
import java.util.* import java.util.*
/** /**
@ -51,7 +51,7 @@ class ReaderProvider : ContentProvider() {
): Int { ): Int {
if (sMatcher.match(uri) < 0) return -1 if (sMatcher.match(uri) < 0) return -1
when (RequestCode.values()[sMatcher.match(uri)]) { when (RequestCode.values()[sMatcher.match(uri)]) {
RequestCode.DeleteSources -> SourceController.deleteSources(selection) RequestCode.DeleteSources -> BookSourceController.deleteSources(selection)
else -> throw IllegalStateException( else -> throw IllegalStateException(
"Unexpected value: " + RequestCode.values()[sMatcher.match(uri)].name "Unexpected value: " + RequestCode.values()[sMatcher.match(uri)].name
) )
@ -65,13 +65,13 @@ class ReaderProvider : ContentProvider() {
if (sMatcher.match(uri) < 0) return null if (sMatcher.match(uri) < 0) return null
when (RequestCode.values()[sMatcher.match(uri)]) { when (RequestCode.values()[sMatcher.match(uri)]) {
RequestCode.SaveSource -> values?.let { RequestCode.SaveSource -> values?.let {
SourceController.saveSource(values.getAsString(postBodyKey)) BookSourceController.saveSource(values.getAsString(postBodyKey))
} }
RequestCode.SaveBook -> values?.let { RequestCode.SaveBook -> values?.let {
BookController.saveBook(values.getAsString(postBodyKey)) BookController.saveBook(values.getAsString(postBodyKey))
} }
RequestCode.SaveSources -> values?.let { RequestCode.SaveSources -> values?.let {
SourceController.saveSources(values.getAsString(postBodyKey)) BookSourceController.saveSources(values.getAsString(postBodyKey))
} }
else -> throw IllegalStateException( else -> throw IllegalStateException(
"Unexpected value: " + RequestCode.values()[sMatcher.match(uri)].name "Unexpected value: " + RequestCode.values()[sMatcher.match(uri)].name
@ -95,8 +95,8 @@ class ReaderProvider : ContentProvider() {
map["path"] = arrayListOf(it) map["path"] = arrayListOf(it)
} }
return if (sMatcher.match(uri) < 0) null else when (RequestCode.values()[sMatcher.match(uri)]) { return if (sMatcher.match(uri) < 0) null else when (RequestCode.values()[sMatcher.match(uri)]) {
RequestCode.GetSource -> SimpleCursor(SourceController.getSource(map)) RequestCode.GetSource -> SimpleCursor(BookSourceController.getSource(map))
RequestCode.GetSources -> SimpleCursor(SourceController.sources) RequestCode.GetSources -> SimpleCursor(BookSourceController.sources)
RequestCode.GetBookshelf -> SimpleCursor(BookController.bookshelf) RequestCode.GetBookshelf -> SimpleCursor(BookController.bookshelf)
RequestCode.GetBookContent -> SimpleCursor(BookController.getBookContent(map)) RequestCode.GetBookContent -> SimpleCursor(BookController.getBookContent(map))
RequestCode.RefreshToc -> SimpleCursor(BookController.refreshToc(map)) RequestCode.RefreshToc -> SimpleCursor(BookController.refreshToc(map))

@ -10,7 +10,7 @@ import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.msg import io.legado.app.utils.msg
object SourceController { object BookSourceController {
val sources: ReturnData val sources: ReturnData
get() { get() {

@ -0,0 +1,83 @@
package io.legado.app.api.controller
import android.text.TextUtils
import io.legado.app.api.ReturnData
import io.legado.app.data.appDb
import io.legado.app.data.entities.RssSource
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.msg
object RssSourceController {
val sources: ReturnData
get() {
val source = appDb.rssSourceDao.all
val returnData = ReturnData()
return if (source.isEmpty()) {
returnData.setErrorMsg("订阅源列表为空")
} else returnData.setData(source)
}
fun saveSource(postData: String?): ReturnData {
val returnData = ReturnData()
postData ?: return returnData.setErrorMsg("数据不能为空")
kotlin.runCatching {
val source = GSON.fromJsonObject<RssSource>(postData)
if (source != null) {
if (TextUtils.isEmpty(source.sourceName) || TextUtils.isEmpty(source.sourceUrl)) {
returnData.setErrorMsg("源名称和URL不能为空")
} else {
appDb.rssSourceDao.insert(source)
returnData.setData("")
}
} else {
returnData.setErrorMsg("转换源失败")
}
}.onFailure {
returnData.setErrorMsg(it.msg)
}
return returnData
}
fun saveSources(postData: String?): ReturnData {
val okSources = arrayListOf<RssSource>()
kotlin.runCatching {
val source = GSON.fromJsonArray<RssSource>(postData)
if (source != null) {
for (rssSource in source) {
if (rssSource.sourceName.isBlank() || rssSource.sourceUrl.isBlank()) {
continue
}
appDb.rssSourceDao.insert(rssSource)
okSources.add(rssSource)
}
}
}
return ReturnData().setData(okSources)
}
fun getSource(parameters: Map<String, List<String>>): ReturnData {
val url = parameters["url"]?.firstOrNull()
val returnData = ReturnData()
if (url.isNullOrEmpty()) {
return returnData.setErrorMsg("参数url不能为空,请指定书源地址")
}
val source = appDb.rssSourceDao.getByKey(url)
?: return returnData.setErrorMsg("未找到源,请检查源地址")
return returnData.setData(source)
}
fun deleteSources(postData: String?): ReturnData {
kotlin.runCatching {
GSON.fromJsonArray<RssSource>(postData)?.let {
it.forEach { source ->
appDb.rssSourceDao.delete(source)
}
}
}
return ReturnData().setData("已执行"/*okSources*/)
}
}

@ -27,8 +27,8 @@ data class RssSource(
var loginCheckJs: String? = null, //登录检测js var loginCheckJs: String? = null, //登录检测js
var sortUrl: String? = null, var sortUrl: String? = null,
var singleUrl: Boolean = false, var singleUrl: Boolean = false,
var articleStyle: Int = 0,
//列表规则 //列表规则
var articleStyle: Int = 0, //列表样式,0,1,2
var ruleArticles: String? = null, var ruleArticles: String? = null,
var ruleNextPage: String? = null, var ruleNextPage: String? = null,
var ruleTitle: String? = null, var ruleTitle: String? = null,

@ -5,7 +5,8 @@ import com.google.gson.Gson
import fi.iki.elonen.NanoHTTPD import fi.iki.elonen.NanoHTTPD
import io.legado.app.api.ReturnData import io.legado.app.api.ReturnData
import io.legado.app.api.controller.BookController import io.legado.app.api.controller.BookController
import io.legado.app.api.controller.SourceController import io.legado.app.api.controller.BookSourceController
import io.legado.app.api.controller.RssSourceController
import io.legado.app.web.utils.AssetsWeb import io.legado.app.web.utils.AssetsWeb
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@ -38,11 +39,14 @@ class HttpServer(port: Int) : NanoHTTPD(port) {
val postData = files["postData"] val postData = files["postData"]
returnData = when (uri) { returnData = when (uri) {
"/saveSource" -> SourceController.saveSource(postData) "/saveBookSource" -> BookSourceController.saveSource(postData)
"/saveSources" -> SourceController.saveSources(postData) "/saveBookSources" -> BookSourceController.saveSources(postData)
"/deleteBookSources" -> BookSourceController.deleteSources(postData)
"/saveBook" -> BookController.saveBook(postData) "/saveBook" -> BookController.saveBook(postData)
"/deleteSources" -> SourceController.deleteSources(postData)
"/addLocalBook" -> BookController.addLocalBook(session.parameters) "/addLocalBook" -> BookController.addLocalBook(session.parameters)
"/saveRssSource" -> RssSourceController.saveSource(postData)
"/saveRssSources" -> RssSourceController.saveSources(postData)
"/deleteRssSources" -> RssSourceController.deleteSources(postData)
else -> null else -> null
} }
} }
@ -50,13 +54,15 @@ class HttpServer(port: Int) : NanoHTTPD(port) {
val parameters = session.parameters val parameters = session.parameters
returnData = when (uri) { returnData = when (uri) {
"/getSource" -> SourceController.getSource(parameters) "/getBookSource" -> BookSourceController.getSource(parameters)
"/getSources" -> SourceController.sources "/getBookSources" -> BookSourceController.sources
"/getBookshelf" -> BookController.bookshelf "/getBookshelf" -> BookController.bookshelf
"/getChapterList" -> BookController.getChapterList(parameters) "/getChapterList" -> BookController.getChapterList(parameters)
"/refreshToc" -> BookController.refreshToc(parameters) "/refreshToc" -> BookController.refreshToc(parameters)
"/getBookContent" -> BookController.getBookContent(parameters) "/getBookContent" -> BookController.getBookContent(parameters)
"/cover" -> BookController.getCover(parameters) "/cover" -> BookController.getCover(parameters)
"/getRssSource" -> RssSourceController.getSource(parameters)
"/getRssSources" -> RssSourceController.sources
else -> null else -> null
} }
} }

Loading…
Cancel
Save