├── .github
└── workflows
│ ├── delete.yml
│ └── update.yaml
├── README.md
├── books
├── Bot.json
├── Japan.json
├── bilinovel-like.json
├── bilinovel.json
├── esjzone.json
├── fishhawk.json
├── fqweb.json
├── lk-lightnovel-us.json
├── masiro.json
├── rezero.json
├── wenku.json
└── zaimanhua.json
├── light-novel.js
├── light-novel.sh
└── source
├── light-novel-web.json
└── light-novel.json
/.github/workflows/delete.yml:
--------------------------------------------------------------------------------
1 | name: Cleanup Old Workflows
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0 0 * * 0'
7 |
8 | jobs:
9 | cleanup:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Delete releases and workflows runs
13 | uses: ophub/delete-releases-workflows@main
14 | with:
15 | delete_releases: false
16 | releases_keep_latest: 5
17 | delete_workflows: true
18 | workflows_keep_day: 10
19 | gh_token: ${{ secrets.GITHUB_TOKEN }}
20 |
--------------------------------------------------------------------------------
/.github/workflows/update.yaml:
--------------------------------------------------------------------------------
1 | name: Update
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0 0 * * 0'
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - name: Node.JS
16 | uses: actions/setup-node@v4
17 |
18 | - name: Update
19 | run: |
20 | chmod 777 -R *
21 | bash light-novel.sh
22 |
23 | - name: Push
24 | run: |
25 | git config --local user.name "github-actions[bot]"
26 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
27 | git add .
28 | git diff-index --quiet HEAD || git commit -m "Update"
29 | git remote rm origin
30 | git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}"
31 | git push -f -u origin main
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## [阅读自用轻小说书源(整合)](https://github.com/gedoor/legado)
2 |
3 | |类型|订阅链接|
4 | |---|---|
5 | |书源|https://raw.githubusercontent.com/MajoSissi/legado-source/main/source/light-novel.json|
6 | |订阅源|https://raw.githubusercontent.com/MajoSissi/legado-source/main/source/light-novel-web.json|
7 |
--------------------------------------------------------------------------------
/books/Bot.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "bookSourceComment": "by·holzora",
4 | "bookSourceGroup": "轻小说",
5 | "bookSourceName": "机翻机器人",
6 | "bookSourceType": 0,
7 | "bookSourceUrl": "https://books.fishhawk.top",
8 | "bookUrlPattern": "(?:https?://)?books\\d?.fishhawk.top/(?:api/)?novel/\\w+/[\\w-]+",
9 | "customOrder": -41336,
10 | "enabled": true,
11 | "enabledCookieJar": false,
12 | "enabledExplore": true,
13 | "exploreUrl": "\ndiscover = [];\npush = (title, url, style) => discover.push({ title: title, url: url, style: { layout_flexGrow: 1, layout_flexBasisPercent: style } });\naddSlots = (count, style) => {\n remainder = count % 3;\n slotsAdd = remainder === 0 ? 0 : 3 - remainder;\n for (i = 0; i < slotsAdd; i++) {\n push(\"\", null, style);\n }\n};\npush(\"收藏夹\", null, 1);\ntry {\n favored = java.ajax(`${baseUrl}/api/user/favored`);\n if (favored.includes(\"Token不合法或者过期\")) {\n push(\"账号未登录\", null, 0.25);\n addSlots(1, 0.25);\n } else {\n favoredData = JSON.parse(favored).favoredWeb;\n favoredData.forEach(item => {\n push(item.title, `/api/user/favored-web/${item.id}?page={{(page-1)}}&pageSize=30&sort=update`, 0.25);\n });\n addSlots(favoredData.length, 0.25);\n }\n} catch (e) {\n push(\"未知错误\", null, 0.25);\n addSlots(1, 0.25);\n};\npush(\"来源\", null, 1);\nproviders = [\"全部\", \"kakuyomu\", \"syosetu\", \"novelup\", \"hameln\", \"pixiv\", \"alphapolis\"];\nproviders.forEach(provider => {\n url = provider === \"全部\" ? \n `/api/novel?page={{(page-1)}}&pageSize=20&query=&provider=${providers.slice(1).join('%2C')}&type=0&level=0&translate=0&sort=0` : \n `/api/novel?page={{(page-1)}}&pageSize=20&query=&provider=${provider}&type=0&level=0&translate=0&sort=0`;\n push(provider, url, 0.25);\n});\naddSlots(providers.length, 0.25);\npush(\"类型\", null, 1);\ntypes = [\"全部\", \"连载中\", \"已完结\", \"短篇\"];\ntypes.forEach((type, index) => {\n url = `/api/novel?page={{(page-1)}}&pageSize=20&query=&provider=kakuyomu%2Csyosetu%2Cnovelup%2Chameln%2Cpixiv%2Calphapolis&type=${index}&level=0&translate=0&sort=0`;\n push(type, url, 0.25);\n});\naddSlots(types.length, 0.25);\npush(\"排序\", null, 1);\nsorts = [\"更新\", \"点击\"];\nsorts.forEach((sort, index) => {\n url = `/api/novel?page={{(page-1)}}&pageSize=20&query=&provider=kakuyomu%2Csyosetu%2Cnovelup%2Chameln%2Cpixiv%2Calphapolis&type=0&level=0&translate=0&sort=${index}`;\n push(sort, url, 0.25);\n});\naddSlots(sorts.length, 0.25);\nJSON.stringify(discover);\n",
14 | "header": "\nheader = {\n \"Referer\": baseUrl\n};\nJSON.stringify(header);\n",
15 | "lastUpdateTime": 1743052461039,
16 | "loginUi": "[\n {\n \"name\": \"账号\",\n \"type\": \"text\"\n },\n {\n \"name\": \"密码\",\n \"type\": \"password\"\n }\n]",
17 | "loginUrl": "\nfunction login() {\n username = source.getLoginInfoMap().get(\"账号\");\n password = source.getLoginInfoMap().get(\"密码\");\n url = `${baseUrl}/api/auth/sign-in`;\n body = JSON.stringify({\n emailOrUsername: username,\n password: password\n });\n headers = {\n \"Content-Type\": \"application/json\"\n };\n response = java.post(url, body, headers);\n responseBody = response.body();\n authorizationObject = {\n Authorization: `Bearer ${responseBody}`\n };\n header = JSON.stringify(authorizationObject);\n source.putLoginHeader(header);\n}\nlogin();\n",
18 | "respondTime": 180000,
19 | "ruleBookInfo": {
20 | "author": "authors..name",
21 | "coverUrl": "",
22 | "init": "\nurl = book.bookUrl.includes('api/novel') ? book.bookUrl : book.bookUrl.replace('novel', 'api/novel');\nbody = book.bookUrl.includes('api/novel') ? result : java.ajax(url);\nif (body.includes('ID不合适,应当使用')) {\n match = body.match(/\\/\\w+\\/[\\w-]+/);\n url = `${book.origin}/api/novel${match}`;\n body = java.ajax(url);\n}\njava.put('url', url);\nbody\n",
23 | "intro": "\nvalue = String(book.getVariable(\"custom\")) || String(source.getVariable());\nresult = JSON.parse(result);\nintro = ['5', '8', '9'].includes(value) ? result.introductionJp : result.introductionZh || result.introductionJp;\ntimeStamp = '{{$.toc[-1].createAt}}' || '{{$.syncAt}}';\nintro = ' 📬最近' + ('{{$.toc[-1].createAt}}' ? '更新' : '同步') + ':' + java.timeFormat(timeStamp * 1000) + '\\n\\n' + intro;\nintro\n",
24 | "kind": "{{$.type##中|已}},{{$.attentions}},{{$.keywords[:3]}}",
25 | "lastChapter": "",
26 | "name": "\nvalue = String(book.getVariable(\"custom\")) || String(source.getVariable());\nresult = JSON.parse(result);\nname = ['5', '8', '9'].includes(value) ? result.titleJp : result.titleZh || result.titleJp;\nname\n",
27 | "tocUrl": "{{java.get('url')}}",
28 | "wordCount": "totalCharacters"
29 | },
30 | "ruleContent": {
31 | "content": "\nvalue = String(book.getVariable(\"custom\")) || String(source.getVariable());\nget = (value >= '1' && value <= '9') ? value : '1';\nconfig = {\n 1: 'sakuraParagraphs',\n 2: 'gptParagraphs',\n 3: 'youdaoParagraphs',\n 4: 'baiduParagraphs',\n 5: 'paragraphs'\n};\nresult = JSON.parse(result);\ncontent = result[config[get]] || result[config[2]] || result[config[3]] || result[config[4]] || result[config[5]];\nif (['6', '7', '8', '9'].includes(get)) {\n type = ['6', '8'].includes(get);\n jpContent = result[config[5]];\n cnContent = result[config[1]] || result[config[2]] || result[config[3]] || result[config[4]];\n if (cnContent) {\n content = jpContent.flatMap((jp, i) => (type ? [cnContent[i], jp] : [jp, cnContent[i]])).filter(Boolean);\n } else {\n content = jpContent;\n }\n};\nregex = content => {\n displayedImages = new Set();\n return JSON.stringify(content).replace(/<图片>(https?:\\/\\/[^\"\\s]+)/g, (_, p1) => {\n if (displayedImages.has(p1)) return '';\n displayedImages.add(p1);\n return '
';\n });\n};\nformat = content => content.replace(/^\\[|\\]$/g, '').split(',').map(item => item.trim().replace(/^\"|\"$/g, '')).filter(Boolean).map((item, i) => item.startsWith('{\"headers\"') && i > 0 ? ',' + item : ' ' + item).join(\"\\n\");\nformat(regex(content));\n",
32 | "payAction": "",
33 | "replaceRegex": "",
34 | "title": "",
35 | "webJs": ""
36 | },
37 | "ruleExplore": {
38 | "bookList": "",
39 | "intro": ""
40 | },
41 | "ruleSearch": {
42 | "author": "",
43 | "bookList": "items",
44 | "bookUrl": "/api/novel/{{$.providerId}}/{{$..novelId}}",
45 | "checkKeyWord": "",
46 | "intro": " 源站:{{$.providerId}}{{'\\n'}}📬最近同步:{{java.timeFormat(java.getString('$.updateAt')*1000)}}{{'\\n'}}总计{{$.total}}|百度{{$.baidu}}|有道{{$.youdao}}|gpt{{$.gpt}}|sakura{{$.sakura}}",
47 | "kind": "{{$.type##中|已}},{{$.attentions}},{{$.keywords}}",
48 | "lastChapter": "",
49 | "name": "\nvalue = String(book.getVariable(\"custom\")) || String(source.getVariable());\nname = ['5', '8', '9'].includes(value) ? result.titleJp : result.titleZh || result.titleJp;\nname\n"
50 | },
51 | "ruleToc": {
52 | "chapterList": "toc\n\nresult.forEach(item => {\n if ('chapterId' in item) {\n item.isVolume = false;\n item.url = `${baseUrl}/chapter/${item.chapterId}`;\n timeStamp = item.createAt ? item.createAt * 1000 : null;\n if (timeStamp) {\n item.time = java.timeFormat(timeStamp);\n } else {\n item.time = '';\n }\n } else {\n item.isVolume = true;\n }\n});\nresult\n",
53 | "chapterName": "\nvalue = String(book.getVariable(\"custom\")) || String(source.getVariable());\nname = ['5', '8', '9'].includes(value) ? result.titleJp : result.titleZh || result.titleJp;\nname\n",
54 | "chapterUrl": "url",
55 | "formatJs": "",
56 | "isPay": "",
57 | "isVolume": "isVolume",
58 | "updateTime": "time"
59 | },
60 | "searchUrl": "\nproviders = {\n kakuyomu: /(?:https?:\\/\\/)?kakuyomu\\.jp\\/works\\/(\\d+)/,\n syosetu: /(?:https?:\\/\\/)?(ncode|novel18)\\.syosetu\\.com\\/(\\w+)/,\n novelup: /(?:https?:\\/\\/)?novelup\\.plus\\/story\\/(\\d+)/,\n hameln: /(?:https?:\\/\\/)?syosetu\\.org\\/novel\\/(\\d+)/,\n pixiv: /(?:https?:\\/\\/)?(www\\.pixiv\\.net\\/novel\\/series\\/(\\d+)|www\\.pixiv\\.net\\/novel\\/show\\.php\\?id=(\\d+))/,\n alphapolis: /(?:https?:\\/\\/)?www\\.alphapolis\\.co\\.jp\\/novel\\/(\\d+)\\/(\\d+)/,\n sakura: /(?:https?:\\/\\/)?books\\d?\\.fishhawk\\.top\\/(?:api\\/)?novel\\/(\\w+)\\/([\\w-]+)/\n};\nfor (provider in providers) {\n match = key.match(providers[provider]);\n if (match) {\n providerId = provider === 'sakura' ? match[1] : provider;\n novelId = provider === 'alphapolis' ? `${match[1]}-${match[2]}` : provider === 'pixiv' ? (match[3] ? `s${match[3]}` : match[2]) : (match[2] || match[1]);\n url = `${baseUrl}/api/novel/${providerId}/${novelId}`;\n break;\n } else {\n url = `${baseUrl}/api/novel?page=${page-1}&pageSize=20&query=${key}&provider=kakuyomu,syosetu,novelup,hameln,pixiv,alphapolis&type=0&level=0&translate=0&sort=1`;\n }\n url\n}\n",
61 | "variableComment": "输入对应数字刷新即可获取对应文本,{1: \"sakura译文\", 2: \"gpt译文\", 3: \"有道译文\", 4: \"百度译文\", 5: \"原文\", 6: \"中日\", 7: \"日中\", 8: \"5+6\", 9: \"5+7\"},默认sakura译文。书籍变量作用于一本书,源变量作用于整个书源,书籍变量优先级高于源变量。",
62 | "weight": 0
63 | }
64 | ]
65 |
--------------------------------------------------------------------------------
/books/bilinovel-like.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "bookSourceComment": "建议登录\n\n酷安 @吉王义昊\nGitHub:https://github.com/jiwangyihao/source-j-legado\n\n# 关于许可的额外声明(在线版本参见 GitHub,以在线版本为准)\n\n- 当许可证与本声明冲突时,以本声明为准;\n- 对于本仓库中的任意代码片段:按照 `MPL 2.0` 中有关约定执行;\n- 对于本仓库中的某一完整书源的转载或二次开发,需满足以下全部条件:\n - 在[本仓库](https://github.com/jiwangyihao/source-j-legado)的 `issue` 中提出请求并具体说明转载地址、二次开发后的书源开源地址以及其他必要信息;\n - 等待原作者(即本仓库的初始所有者和初始代码贡献者 [@jiwangyihao](https://github.com/jiwangyihao))查看并通过 `issue` 或依据原作者要求更改转载方式或补充更详细的信息。\n - 考虑到本项目弃坑的可能,新 `issue` 开启后超过 20 个工作日原作者没有回复或者原作者回复要求更改的 `issue` 在更改后超过 20 个工作日原作者没有回复即视为原作者通过该 `issue`:\n - 此处的 `issue` 仅包括在[本仓库](https://github.com/jiwangyihao/source-j-legado)开启的,处于「开启状态」的 issue。(也就是说,请不要在已经关闭的 issue 中回复)。\n - 对于此种方式通过的 issue,转载/二次开发者仍应当遵守本声明中已经写明的相关约定。\n - 不得上传至源仓库或整理至`非轻小说专用`的书源合集中并应当避免其他人将转载/二次开发版本上传至源仓库或整理至`非轻小说专用`的书源合集中:\n - 关于轻小说的定义的额外说明:不包括国内的原创网络文学作品(如 `SF 轻小说` 中的原创轻小说以及`起点中文网`中标签包含轻小说的作品)。\n - 轻小说专用的定义:有且仅有想看轻小说的人可能会添加。\n - 必须在转载/二次开发地址的明显位置完整包含本声明的全部内容。\n - 必须保留源注释中原有的更改记录。",
4 | "bookSourceGroup": "轻小说,哔轻类似物",
5 | "bookSourceName": "轻之文库轻小说",
6 | "bookSourceType": 0,
7 | "bookSourceUrl": "https://m.wenkuchina.com",
8 | "bookUrlPattern": "",
9 | "concurrentRate": "",
10 | "coverDecodeJs": "",
11 | "customOrder": -41323,
12 | "enabled": true,
13 | "enabledCookieJar": true,
14 | "enabledExplore": true,
15 | "exploreUrl": "@js:\nres = []\n\n//筛选链接生成\nfunction generateFilterUrl(new_values) {\n values = {\n order: 'lastupdate',\n anime: '0',\n sortid: '0',\n typeid: '0',\n words: '0',\n rgroupid: '0',\n update: '0',\n isfull: '0',\n page: '{{page}}',\n }\n for (key in new_values) {\n \t values[key]=new_values[key]\n \t}\n return source.bookSourceUrl + '/wenku/' + values.order + '_' + values.anime + '_' + values.sortid + '_' + values.typeid + '_' + values.words + '_' + values.rgroupid + '_' + values.update + '_' + values.isfull + '_' + values.page + '.html';\n}\n\n//书架\nuser = ajax(source.bookSourceUrl + \"/user.php\")\n//java.toast(user)\ntry {\nif (user.match(/
(登录|错误).*轻之文库轻小说.+<\\/h2>/gi)) {\n //未登录\n res.push(\n {\n title: `>> 我的书架 | 未登录 <<`,\n url: '',\n style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\n })\n} else {\n //已登录\n res.push(\n {\n title: `>> 我的书架 | ${user.match(/.+<\\/span>/gi)[0].replace(/<\\/?span.*?>/gi,'')} <<`,\n url: '',\n style: { layout_flexGrow: 1, layout_flexBasisPercent: 1 }\n })\n bookcase = ajax(source.bookSourceUrl + \"/bookcase.php\")\n bookcase.match(/