├── .github ├── ISSUE_TEMPLATE │ └── config.yml └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── README.md ├── assets ├── 1.png ├── 2.png └── 3.png ├── docs ├── assets │ ├── book-toc.png │ └── html-style.png ├── principle.md ├── sites │ └── zhihu.md ├── workflows.md └── wrx.md ├── history.txt ├── latest.json ├── package.json ├── releases └── wrx-v0.0.8.6.zip ├── scripts ├── utils.js └── weread │ ├── build.js │ ├── check-update.js │ ├── patch.js │ └── transformer.js ├── src ├── assets │ ├── bootstrap.min.css │ ├── ebook-disabled.png │ ├── ebook-enabled.png │ └── popup.js ├── background.js ├── common │ ├── inject.js │ ├── utils.js │ └── xhr-fetch.js ├── manifest.json ├── overrides │ └── weread │ │ ├── 8.09d8684c.js │ │ ├── 8.126c7638.js │ │ ├── 8.1b1a58af.js │ │ ├── 8.2b33baa9.js │ │ ├── 8.6727fad4.js │ │ ├── 8.82196ea9.js │ │ ├── 8.a0b49d48.js │ │ ├── 8.c1484fdd.js │ │ ├── 8.e62d5026.js │ │ ├── 9.16e91701.js │ │ └── 9.58102893.js ├── popup.html ├── rules │ └── weread.json └── sites │ ├── weread │ ├── auto.js │ ├── content.js │ ├── crypto-js@4.2.0.min.js │ ├── store.js │ ├── toc.css │ └── utils.js │ └── zhihu │ ├── auto.js │ ├── content.js │ ├── store.js │ └── toc.css └── yarn.lock /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | schedule: 5 | - cron: "0 * * * *" 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | check-update: 12 | name: 检查微信读书核心js是否有更新 13 | runs-on: ubuntu-latest 14 | if: github.repository == 'jooooock/wrx' 15 | outputs: 16 | needUpgrade: ${{ steps.step3.outputs.exists }} 17 | 18 | steps: 19 | - name: 拉取代码 20 | uses: actions/checkout@v4 21 | 22 | - name: 安装依赖 23 | run: yarn 24 | 25 | - name: 检查微信读书是否更新 26 | id: step3 27 | run: | 28 | node ./scripts/weread/check-update.js 29 | test -e ./scripts/weread/tmp/url && echo "exists=true" >> "$GITHUB_OUTPUT" || echo "exists=false" >> "$GITHUB_OUTPUT" 30 | 31 | build: 32 | name: 打包发布 33 | runs-on: ubuntu-latest 34 | needs: check-update 35 | if: needs.check-update.outputs.needUpgrade == 'true' 36 | 37 | steps: 38 | - name: 拉取代码 39 | uses: actions/checkout@v4 40 | 41 | - name: 安装依赖 42 | run: yarn 43 | 44 | - name: 执行构建 45 | id: build 46 | run: | 47 | node ./scripts/weread/check-update.js 48 | node ./scripts/weread/patch.js 49 | node ./scripts/weread/build.js 50 | echo "version=v$(jq -r .version latest.json)" >> "$GITHUB_OUTPUT" 51 | 52 | - name: 输出构建结果 53 | run: | 54 | echo "build result: ${{ toJSON(steps.build.outputs) }}" 55 | 56 | - name: 提交代码 57 | run: | 58 | git config --local user.name bot 59 | git config --local user.email bot@github.com 60 | git add . 61 | git commit -m "release $(echo $version)" 62 | git tag $(echo $version) 63 | git pull 64 | git push origin master 65 | git push --tags 66 | 67 | - name: 发布 Release 68 | uses: ncipollo/release-action@v1 69 | with: 70 | artifacts: "releases/wrx-${{ steps.build.outputs.version }}.zip" 71 | tag: "${{ steps.build.outputs.version }}" 72 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: 测试 Actions 2 | 3 | on: 4 | push: 5 | branches: 6 | - dev 7 | 8 | 9 | jobs: 10 | check-update: 11 | name: 检查微信读书核心js是否有更新 12 | if: github.repository == 'jooooock/wrx' 13 | runs-on: ubuntu-latest 14 | outputs: 15 | needUpgrade: ${{ steps.step3.outputs.exists }} 16 | 17 | steps: 18 | - name: 拉取代码 19 | uses: actions/checkout@v4 20 | 21 | - name: 安装依赖 22 | run: yarn 23 | 24 | - name: 检查微信读书是否更新 25 | id: step3 26 | run: | 27 | node ./scripts/weread/check-update.js 28 | test -e ./scripts/weread/tmp/url && echo "exists=true" >> "$GITHUB_OUTPUT" || echo "exists=false" >> "$GITHUB_OUTPUT" 29 | 30 | build: 31 | name: 打包发布 32 | needs: check-update 33 | runs-on: ubuntu-latest 34 | if: needs.check-update.outputs.needUpgrade == 'true' 35 | 36 | steps: 37 | - name: 打印job结果 38 | run: | 39 | echo "job result: ${{ toJSON(needs.check-update.outputs) }}" 40 | 41 | - name: 拉取代码 42 | uses: actions/checkout@v4 43 | 44 | - name: 安装依赖 45 | run: yarn 46 | 47 | - name: 执行构建 48 | id: build 49 | run: | 50 | node ./scripts/weread/check-update.js 51 | node ./scripts/weread/patch.js 52 | node ./scripts/weread/build.js 53 | echo "version=v$(jq -r .version latest.json)" >> "$GITHUB_OUTPUT" 54 | 55 | - name: 输出构建结果 56 | run: | 57 | echo "build result: ${{ toJSON(steps.build.outputs) }}" 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | _metadata 4 | node_modules 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wrx 魏如雪 2 | 3 | 一套电子书导出工具,由【采集端([chrome插件](https://github.com/ckcock/wrx/releases))】和【解密端([在线网站](https://ebook-exporter.deno.dev))】两个组件构成,支持导出 HTML 和 EPUB 两种格式。本项目为**采集端**,**解密端**暂时没有开源。 4 | 5 | ## 所有人使用注意 6 | 该项目当前处于**暂停维护**阶段,原因是多方面的,至于后续会不会继续维护,尚不可知。 7 | 8 | 如果出现 json 文件导出失败的问题,请先自查原因,如果是因为网络请求的问题,请自行解决网络问题。如果不是网络问题,则大概率暂时无法解决。 9 | 10 | 以上,请悉知。 11 | 12 | 13 | ## 相关说明 14 | 本套工具为电子书导出工具,导出的内容为你在电子书阅读网站上真正阅读过的内容,通过 **缓存 + 解密** 技术生成相关书籍的 HTML / EPUB 版本的电子书。 15 | 16 | 因此,对于那些需要 vip 才能阅读的内容,如果你没有相关 vip 权益而无法阅读相关内容,则本工具也无能为力。 17 | 18 | ## 支持的电子书平台 19 | 20 | - [微信读书](https://weread.qq.com/) 21 | - [知乎书店](https://www.zhihu.com/pub) 进度可以查看[接入知乎](docs/sites/zhihu.md) 22 | 23 | > [!WARNING] 24 | > 根据部分用户的反馈,在微信读书平台使用该插件有封号风险,请酌情使用。如果有小号的,尽量用小号使用该插件。 25 | > 【解密端】的使用不存在风险,请放心使用。 26 | 27 | 28 | ## 安装 29 | 从 [Release 页面](https://github.com/ckcock/wrx/releases)下载最新版本的 wrx.zip 并解压,然后打开 Chrome 插件管理页面(chrome://extensions/),如下: 30 | 31 | ![image](assets/1.png) 32 | 33 | 开启右上角的开发者模式,然后通过左上角的【Load unpacked】按钮安装这个 **wrx** 插件。 34 | 35 | 36 | ## 使用 37 | 38 | 打开微信读书阅读页面,打开目录弹框,你需要把这些红点点都点成绿色的,如下所示: 39 | 40 | ![image](assets/2.png) 41 | 42 | 43 | ## 数据导出 44 | 45 | 在阅读页面的右上角,点击【导出本书数据】按钮,即可将已缓存的数据导出(此数据是加密数据,需要使用 https://ebook-exporter.deno.dev 进行解密)。 46 | 47 | 48 | ## 插件更新 49 | 50 | 该程序由 Github Actions 自动构建,通过 schedule 每个小时执行一次检测,如果发现微读web版有更新,会自动开始新的构建并发布新版本。 51 | 当插件有新版本时,需要重新按照上面的【安装】指南进行安装。 52 | 53 | 54 | ## 数据清理 55 | 56 | 如果担心该插件缓存的数据占用太多空间,或者仅仅是想知道怎么清除这些缓存的数据,那么可以打开浏览器的控制台,按照如下操作删除 **IndexedDB** 下面的 **wrx** 数据库即可: 57 | 58 | ![image](assets/3.png) 59 | 60 | ## 常见问题 61 | 62 | ### 1. 自动翻页功能失效了,我该怎么办? 63 | 插件中内置的自动翻页功能是通过修改源网站相关js实现的,如果源网站相关js更新了,则会导致该功能失效。 64 | 65 | 不过你不用担心,程序会通过 Github Actions 的定时器每隔1个小时去检查相关文件是否有更新,然后通过自动打补丁的方式发布新版本。 66 | 67 | 通常你只需要检查该仓库是否有发布新的版本,并用新版本替换即可。 68 | 69 | ### 2. 为什么不发布到插件市场,然后自动更新?而采用这种手动更新的“笨拙”方式? 70 | 由于上面说的原因,该插件中需要包含源网站的相关js文件,而该js文件通常是经过混淆的,但是 chrome 的插件市场不能发布带有混淆代码的插件,所以无法发布到 chrome 的插件市场。 71 | -------------------------------------------------------------------------------- /assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/assets/1.png -------------------------------------------------------------------------------- /assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/assets/2.png -------------------------------------------------------------------------------- /assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/assets/3.png -------------------------------------------------------------------------------- /docs/assets/book-toc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/docs/assets/book-toc.png -------------------------------------------------------------------------------- /docs/assets/html-style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/docs/assets/html-style.png -------------------------------------------------------------------------------- /docs/principle.md: -------------------------------------------------------------------------------- 1 | # 实现原理 2 | 3 | 微信读书里面的书大概分2类: epub 和 txt,另外还有一种 pdf 格式的比较特殊,后面单独说明。 4 | 5 | 每本书都是按照章节进行阅读和下载的,比如下面这本书的目录: 6 | 7 | ![目录结构](assets/book-toc.png) 8 | 9 | 每一行都是一个章节,总的章节数据可以通过下面这个接口获取: 10 | ```http request 11 | POST /web/book/publicchapterInfos 12 | Host: https://weread.qq.com 13 | Content-Type: application/json;charset=UTF-8 14 | 15 | {"bookIds":["3300076204"]} 16 | ``` 17 | 返回的 json 里面,`data.updated`字段即章节数组,比如第一章: 18 | ```json 19 | { 20 | "chapterUid": 1, 21 | "chapterIdx": 1, 22 | "updateTime": 1698825146, 23 | "tar": "https://res.weread.qq.com/wrco/tar_CB_3300076204_1", 24 | "title": "封面", 25 | "wordCount": 2, 26 | "price": 0, 27 | "isMPChapter": 0, 28 | "level": 1, 29 | "files": [ 30 | "Text/coverpage.xhtml" 31 | ] 32 | } 33 | ``` 34 | 35 | > 注意里面有一个`tar`字段,是指向一个文件,打包了这一章里面的所有图片资源。 36 | > 这个`tar`包文件的下载,是需要登录信息的。 37 | 38 | 第一章通常是封面,但是一般不显示在 web 版的目录里面,这一章的图片资源通常只有一张,也就是封面图。 39 | 40 | 有的章节比较特殊,比如下面这个: 41 | ```json 42 | { 43 | "chapterUid": 5, 44 | "chapterIdx": 5, 45 | "updateTime": 1698825146, 46 | "tar": "", 47 | "title": "第一部分 全能自恋及其变化", 48 | "wordCount": 761, 49 | "price": 0, 50 | "isMPChapter": 0, 51 | "level": 1, 52 | "files": [ 53 | "Text/chaper003.xhtml" 54 | ], 55 | "anchors": [ 56 | { 57 | "title": "引言 如何看全能感", 58 | "anchor": "sigil_toc_id_1", 59 | "level": 2 60 | } 61 | ] 62 | } 63 | ``` 64 | 内部有一个`anchors`数组,这个数组里面的部分也属于目录的一部分,但是却没有单独的`chapterUid`等信息,也就是说,同一章节里面包含了多条目录数据。 65 | 66 | 有了目录数据,我们就可以用下载接口下载章节数据了。 67 | 68 | 不同格式的书需要用不同接口进行下载,其中`epub`格式的书下载接口如下: 69 | ``` 70 | https://weread.qq.com/web/book/chapter/e_0 (html 片段) 71 | https://weread.qq.com/web/book/chapter/e_1 (html 片段) 72 | https://weread.qq.com/web/book/chapter/e_2 (style) 73 | https://weread.qq.com/web/book/chapter/e_3 (html 片段) 74 | ``` 75 | `txt`格式的书的下载接口如下: 76 | ``` 77 | https://weread.qq.com/web/book/chapter/t_0 (text 片段) 78 | https://weread.qq.com/web/book/chapter/t_1 (text 片段) 79 | ``` 80 | 81 | 每一本`epub`格式的书的章节都分成了 4 部分,其中 0、1、3 是`html`片段,下载完之后需要拼起来才是完整的`html`数据,2 是`style`。 82 | `txt`格式的书分2个部分,都是`html`数据,没有样式。 83 | 84 | 每一章的下载参数如下: 85 | ```json5 86 | { 87 | "b": "bb432f60813ab8444g014d61", // bookId 的编码值 88 | "c": "ecc32f3013eccbc87e4b62e", // chapterUid 的编码值 89 | "r": 97344, // 随机数 90 | "st": 0, // 0表示html,1表示style 91 | "ct": 1699415043, // 当前时间戳 92 | "ps": "57d32ab07a2119e6g017bd2", // 打开这本书时服务端时间戳的编码值 93 | "pc": "a8f324b07a2119e7g017cec", // 打开这本书时客户端时间戳的编码值 94 | "sc": 0, 95 | "prevChapter": false, // 是否是点击顶部的"上一章"按钮进行获取的 96 | "s": "d97170d6" // 以上这些数据的签名 97 | } 98 | ``` 99 | 100 | 下载的数据是加密的,经过解密拼接之后,就是这个章节的 html 文档和 style 样式了,如下图所示: 101 | ![解密后的html和style](assets/html-style.png) 102 | 103 | 通过遍历章节列表,把所有的章节数据都下载下来,我们把 style 部分内嵌到 html 中,这样每一个章节都是一个单独的 html 文档,然后我们再把 html 文档合并成一个 html 文档,这就是下载 html 格式的原理。 104 | 我们把 html 格式里面的图片单独下载下来,然后替换图片的 src 属性,并把所有内容打包到 epub 里面,这就是下载 epub 格式的原理。 105 | -------------------------------------------------------------------------------- /docs/sites/zhihu.md: -------------------------------------------------------------------------------- 1 | # 知乎书店 2 | 3 | ## 接入任务完成情况 4 | 5 | ### 图标切换与启用/禁用 6 | 7 | - [x] 添加启用匹配规则 8 | - [x] 添加图标切换判断条件 9 | 10 | ### 数据缓存 11 | 12 | - [x] 在`host_permissions`字段中增加相关域名 13 | - [x] 在`xhr-fetch.js`中的`INTERCEPT_APIS`中增加接口匹配规则 14 | - [ ] `content.js`脚本 15 | - [ ] `store.js`脚本 16 | - [ ] `toc.css`样式标记章节缓存状态 17 | 18 | ### 数据解密 19 | 20 | - [x] 逆向解密算法 21 | - [ ] 调整解密端的导出逻辑 22 | 23 | ### 自动翻页 24 | 25 | - [x] `auto.js`脚本 26 | 27 | 28 | ## 额外任务 29 | 30 | 逆向参数加密逻辑,直接调用接口一键缓存全书,但存在风险,过度使用容易被禁账号。 31 | 32 | ## 接口分析 33 | 34 | ### 元数据接口 35 | 36 | 接口地址: 37 | ```http request 38 | GET https://www.zhihu.com/api/v3/books/[bookId]?include= 39 | ``` 40 | 41 |
42 | 查看响应示例 43 | 44 | ```json 45 | { 46 | "read_count": 10618, 47 | "right": { 48 | "raw_value": 0, 49 | "left_top_day_icon": "vip_free_day_icon", 50 | "main_title": "加入知乎 · 读书会,享会员专属权益", 51 | "sub_title": "精选好书免费读、折扣购,更有大咖领读经典免费听", 52 | "value": "0", 53 | "entrance_url": "zhihu://knowledge_market/purchase/1", 54 | "left_top_night_icon": "vip_free_night_icon", 55 | "book_vip_url": "zhihu://knowledge_market/purchase/1", 56 | "privilege": "会员立享折扣优惠", 57 | "cashier_url": "zhihu://wallet/cashier/933667430576807936", 58 | "subscription_id": 2, 59 | "guidance": "知乎·读书会会员免费读此书,还有更多知乎精选电子书等你免费读~", 60 | "right_type": 0 61 | }, 62 | "svip_privileges": false, 63 | "generation": 1, 64 | "review_stats": { 65 | "bad": 3, 66 | "medium": 13, 67 | "review_count": 504, 68 | "score": "4.3", 69 | "good": 84 70 | }, 71 | "is_subscribed": false, 72 | "preface": "一个陌生女人的电话,一起失踪事件,动物医生手岛伯朗卷入一场正在进行的犯罪事件。没有任何线索,甚至连同伴也不能完全信任。那个隐藏在家人中的凶手到底是谁?\n\n「维纳斯」不是某个人,而是存在于我们每个人心中令人疯狂的东西。恶不是一开始就存在,东野圭吾在这本书里诠释了好人是如何变成恶魔的。复杂的情节,反转再反转,但反转的是故事,还是人心?", 73 | "can_subscribe": false, 74 | "author_preface": "", 75 | "sku_id": "1092469025979895808", 76 | "global_anonymous": 0, 77 | "words_count": "17.3 万字", 78 | "table": "- 封面\n- 版权\n- 1\n- 2\n- 3\n- 4\n- 5\n- 6\n- 7\n- 8\n- 9\n- 10\n- 11\n- 12\n- 13\n- 14\n- 15\n- 16\n- 17\n- 18\n- 19\n- 20\n- 21\n- 22\n- 23\n- 24\n- 25\n- 26\n- 27\n- 28\n- 29\n- 30\n- 31", 79 | "book_version": "190804001", 80 | "publisher_name": "磨铁图书", 81 | "id": 119601575, 82 | "book_hash": "d41d8cd98f00b204e9800998ecf8427e", 83 | "review_count": 145, 84 | "title": "危险的维纳斯", 85 | "is_voted": false, 86 | "book_size": 911633, 87 | "comment_count": 504, 88 | "vote_count": 61, 89 | "score": 8.4, 90 | "anonymous_status": 0, 91 | "type": "ebook", 92 | "member_role": "normal", 93 | "on_shelf": false, 94 | "description": "「维纳斯」不是某个人,而是存在于我们每个人心中令人疯狂的东西。 ", 95 | "is_purchased": false, 96 | "qrcode_url": "", 97 | "is_access_reader": true, 98 | "collection": { 99 | "token": "book", 100 | "surname": "精选", 101 | "name": "知乎精选好书", 102 | "id": 0 103 | }, 104 | "pintag": [], 105 | "authors": [ 106 | { 107 | "avatar_url": "https://pic1.zhimg.com/50/v2-beeba36f24b04afe3e3fd689f8bc5c78_qhd.jpg?source=f11ebe26", 108 | "name": "东野圭吾", 109 | "url": "", 110 | "gender": 1, 111 | "type": "outer_author", 112 | "id": "" 113 | } 114 | ], 115 | "sales_bubble": { 116 | "is_pop": false 117 | }, 118 | "privilege_status": 0, 119 | "coupons": { 120 | "status": 0 121 | }, 122 | "is_own": false, 123 | "url": "https://www.zhihu.com/pub/book/119601575", 124 | "message": "", 125 | "cover": "https://pic1.zhimg.com/v2-c70fa52c486461e7d62f8c1f30269932_200x0.jpg?source=f11ebe26", 126 | "prompts": [], 127 | "promotion": { 128 | "pay_type": "wallet", 129 | "is_promotion": false, 130 | "zhihu_bean": 4500, 131 | "price": 4500, 132 | "origin_price": 4500 133 | }, 134 | "on_shelves": true 135 | } 136 | ``` 137 |
138 | 139 | 可以通过`include`参数来包含额外信息,格式为: 140 | ```js 141 | encodeURIComponent('pub_time,dci_number,dci_cert,categories,authors[*].url_token,is_following,badge[?topics]') 142 | ``` 143 | 144 | ### 目录接口 145 | 146 | 接口地址: 147 | ```http request 148 | GET https://www.zhihu.com/api/v3/books/[bookId]/chapters 149 | ``` 150 | 151 |
152 | 查看响应示例 153 | 154 | ```json 155 | { 156 | "deleted": [], 157 | "updated": [ 158 | { 159 | "is_own": true, 160 | "hash": "", 161 | "title": "\u5c01\u9762", 162 | "new_hash": "", 163 | "level": 1, 164 | "is_own_book": false, 165 | "word_count": 1, 166 | "version": "190804001", 167 | "content_file": "", 168 | "chapter_title": "\u5c01\u9762", 169 | "chapter_uid": "1141324025534943232", 170 | "is_cover": true, 171 | "is_trial": true, 172 | "chapter_index": 0, 173 | "size": 115 174 | }, 175 | { 176 | "is_own": true, 177 | "hash": "", 178 | "title": "\u7248\u6743", 179 | "new_hash": "", 180 | "level": 1, 181 | "is_own_book": false, 182 | "word_count": 90, 183 | "version": "190804001", 184 | "content_file": "", 185 | "chapter_title": "\u7248\u6743", 186 | "chapter_uid": "1141324025581142016", 187 | "is_cover": false, 188 | "is_trial": true, 189 | "chapter_index": 1, 190 | "size": 338 191 | }, 192 | { 193 | "is_own": true, 194 | "hash": "", 195 | "title": "1", 196 | "new_hash": "", 197 | "level": 1, 198 | "is_own_book": false, 199 | "word_count": 1905, 200 | "version": "190804001", 201 | "content_file": "", 202 | "chapter_title": "1", 203 | "chapter_uid": "1092470252998324224", 204 | "is_cover": false, 205 | "is_trial": true, 206 | "chapter_index": 2, 207 | "size": 7085 208 | }, 209 | { 210 | "is_own": true, 211 | "hash": "", 212 | "title": "2", 213 | "new_hash": "", 214 | "level": 1, 215 | "is_own_book": false, 216 | "word_count": 9199, 217 | "version": "190804001", 218 | "content_file": "", 219 | "chapter_title": "2", 220 | "chapter_uid": "1092470253497470976", 221 | "is_cover": false, 222 | "is_trial": true, 223 | "chapter_index": 3, 224 | "size": 32457 225 | }, 226 | { 227 | "is_own": true, 228 | "hash": "", 229 | "title": "3", 230 | "new_hash": "", 231 | "level": 1, 232 | "is_own_book": false, 233 | "word_count": 5527, 234 | "version": "190804001", 235 | "content_file": "", 236 | "chapter_title": "3", 237 | "chapter_uid": "1092470255116500992", 238 | "is_cover": false, 239 | "is_trial": true, 240 | "chapter_index": 4, 241 | "size": 19943 242 | }, 243 | { 244 | "is_own": true, 245 | "hash": "", 246 | "title": "4", 247 | "new_hash": "", 248 | "level": 1, 249 | "is_own_book": false, 250 | "word_count": 4666, 251 | "version": "190804001", 252 | "content_file": "", 253 | "chapter_title": "4", 254 | "chapter_uid": "1092470256202776576", 255 | "is_cover": false, 256 | "is_trial": true, 257 | "chapter_index": 5, 258 | "size": 16702 259 | }, 260 | { 261 | "is_own": true, 262 | "hash": "", 263 | "title": "5", 264 | "new_hash": "", 265 | "level": 1, 266 | "is_own_book": false, 267 | "word_count": 5192, 268 | "version": "190804001", 269 | "content_file": "", 270 | "chapter_title": "5", 271 | "chapter_uid": "1092470257092042752", 272 | "is_cover": false, 273 | "is_trial": true, 274 | "chapter_index": 6, 275 | "size": 18162 276 | }, 277 | { 278 | "is_own": false, 279 | "hash": "", 280 | "title": "6", 281 | "new_hash": "", 282 | "level": 1, 283 | "is_own_book": false, 284 | "word_count": 5271, 285 | "version": "190804001", 286 | "content_file": "", 287 | "chapter_title": "6", 288 | "chapter_uid": "1092470258077626368", 289 | "is_cover": false, 290 | "is_trial": false, 291 | "chapter_index": 7, 292 | "size": 19415 293 | }, 294 | { 295 | "is_own": false, 296 | "hash": "", 297 | "title": "7", 298 | "new_hash": "", 299 | "level": 1, 300 | "is_own_book": false, 301 | "word_count": 4988, 302 | "version": "190804001", 303 | "content_file": "", 304 | "chapter_title": "7", 305 | "chapter_uid": "1092470259155607552", 306 | "is_cover": false, 307 | "is_trial": false, 308 | "chapter_index": 8, 309 | "size": 18159 310 | }, 311 | { 312 | "is_own": false, 313 | "hash": "", 314 | "title": "8", 315 | "new_hash": "", 316 | "level": 1, 317 | "is_own_book": false, 318 | "word_count": 8740, 319 | "version": "190804001", 320 | "content_file": "", 321 | "chapter_title": "8", 322 | "chapter_uid": "1092470260191559680", 323 | "is_cover": false, 324 | "is_trial": false, 325 | "chapter_index": 9, 326 | "size": 31912 327 | }, 328 | { 329 | "is_own": false, 330 | "hash": "", 331 | "title": "9", 332 | "new_hash": "", 333 | "level": 1, 334 | "is_own_book": false, 335 | "word_count": 5849, 336 | "version": "190804001", 337 | "content_file": "", 338 | "chapter_title": "9", 339 | "chapter_uid": "1092470261810544640", 340 | "is_cover": false, 341 | "is_trial": false, 342 | "chapter_index": 10, 343 | "size": 20711 344 | }, 345 | { 346 | "is_own": false, 347 | "hash": "", 348 | "title": "10", 349 | "new_hash": "", 350 | "level": 1, 351 | "is_own_book": false, 352 | "word_count": 9111, 353 | "version": "190804001", 354 | "content_file": "", 355 | "chapter_title": "10", 356 | "chapter_uid": "1092470262951383040", 357 | "is_cover": false, 358 | "is_trial": false, 359 | "chapter_index": 11, 360 | "size": 33040 361 | }, 362 | { 363 | "is_own": false, 364 | "hash": "", 365 | "title": "11", 366 | "new_hash": "", 367 | "level": 1, 368 | "is_own_book": false, 369 | "word_count": 4047, 370 | "version": "190804001", 371 | "content_file": "", 372 | "chapter_title": "11", 373 | "chapter_uid": "1092470264641785856", 374 | "is_cover": false, 375 | "is_trial": false, 376 | "chapter_index": 12, 377 | "size": 15062 378 | }, 379 | { 380 | "is_own": false, 381 | "hash": "", 382 | "title": "12", 383 | "new_hash": "", 384 | "level": 1, 385 | "is_own_book": false, 386 | "word_count": 4367, 387 | "version": "190804001", 388 | "content_file": "", 389 | "chapter_title": "12", 390 | "chapter_uid": "1092470265463812096", 391 | "is_cover": false, 392 | "is_trial": false, 393 | "chapter_index": 13, 394 | "size": 15373 395 | }, 396 | { 397 | "is_own": false, 398 | "hash": "", 399 | "title": "13", 400 | "new_hash": "", 401 | "level": 1, 402 | "is_own_book": false, 403 | "word_count": 5631, 404 | "version": "190804001", 405 | "content_file": "", 406 | "chapter_title": "13", 407 | "chapter_uid": "1092470266302758912", 408 | "is_cover": false, 409 | "is_trial": false, 410 | "chapter_index": 14, 411 | "size": 20679 412 | }, 413 | { 414 | "is_own": false, 415 | "hash": "", 416 | "title": "14", 417 | "new_hash": "", 418 | "level": 1, 419 | "is_own_book": false, 420 | "word_count": 6821, 421 | "version": "190804001", 422 | "content_file": "", 423 | "chapter_title": "14", 424 | "chapter_uid": "1092470267426791424", 425 | "is_cover": false, 426 | "is_trial": false, 427 | "chapter_index": 15, 428 | "size": 24744 429 | }, 430 | { 431 | "is_own": false, 432 | "hash": "", 433 | "title": "15", 434 | "new_hash": "", 435 | "level": 1, 436 | "is_own_book": false, 437 | "word_count": 6634, 438 | "version": "190804001", 439 | "content_file": "", 440 | "chapter_title": "15", 441 | "chapter_uid": "1092470268815089664", 442 | "is_cover": false, 443 | "is_trial": false, 444 | "chapter_index": 16, 445 | "size": 24678 446 | }, 447 | { 448 | "is_own": false, 449 | "hash": "", 450 | "title": "16", 451 | "new_hash": "", 452 | "level": 1, 453 | "is_own_book": false, 454 | "word_count": 4763, 455 | "version": "190804001", 456 | "content_file": "", 457 | "chapter_title": "16", 458 | "chapter_uid": "1092470270157205504", 459 | "is_cover": false, 460 | "is_trial": false, 461 | "chapter_index": 17, 462 | "size": 17556 463 | }, 464 | { 465 | "is_own": false, 466 | "hash": "", 467 | "title": "17", 468 | "new_hash": "", 469 | "level": 1, 470 | "is_own_book": false, 471 | "word_count": 6931, 472 | "version": "190804001", 473 | "content_file": "", 474 | "chapter_title": "17", 475 | "chapter_uid": "1092470271147151360", 476 | "is_cover": false, 477 | "is_trial": false, 478 | "chapter_index": 18, 479 | "size": 25016 480 | }, 481 | { 482 | "is_own": false, 483 | "hash": "", 484 | "title": "18", 485 | "new_hash": "", 486 | "level": 1, 487 | "is_own_book": false, 488 | "word_count": 5454, 489 | "version": "190804001", 490 | "content_file": "", 491 | "chapter_title": "18", 492 | "chapter_uid": "1092470272539623424", 493 | "is_cover": false, 494 | "is_trial": false, 495 | "chapter_index": 19, 496 | "size": 20475 497 | }, 498 | { 499 | "is_own": false, 500 | "hash": "", 501 | "title": "19", 502 | "new_hash": "", 503 | "level": 1, 504 | "is_own_book": false, 505 | "word_count": 3543, 506 | "version": "190804001", 507 | "content_file": "", 508 | "chapter_title": "19", 509 | "chapter_uid": "1092470273605009408", 510 | "is_cover": false, 511 | "is_trial": false, 512 | "chapter_index": 20, 513 | "size": 12967 514 | }, 515 | { 516 | "is_own": false, 517 | "hash": "", 518 | "title": "20", 519 | "new_hash": "", 520 | "level": 1, 521 | "is_own_book": false, 522 | "word_count": 5824, 523 | "version": "190804001", 524 | "content_file": "", 525 | "chapter_title": "20", 526 | "chapter_uid": "1092470274347360256", 527 | "is_cover": false, 528 | "is_trial": false, 529 | "chapter_index": 21, 530 | "size": 20885 531 | }, 532 | { 533 | "is_own": false, 534 | "hash": "", 535 | "title": "21", 536 | "new_hash": "", 537 | "level": 1, 538 | "is_own_book": false, 539 | "word_count": 5694, 540 | "version": "190804001", 541 | "content_file": "", 542 | "chapter_title": "21", 543 | "chapter_uid": "1092470275509133312", 544 | "is_cover": false, 545 | "is_trial": false, 546 | "chapter_index": 22, 547 | "size": 21252 548 | }, 549 | { 550 | "is_own": false, 551 | "hash": "", 552 | "title": "22", 553 | "new_hash": "", 554 | "level": 1, 555 | "is_own_book": false, 556 | "word_count": 6902, 557 | "version": "190804001", 558 | "content_file": "", 559 | "chapter_title": "22", 560 | "chapter_uid": "1092470277400817664", 561 | "is_cover": false, 562 | "is_trial": false, 563 | "chapter_index": 23, 564 | "size": 25499 565 | }, 566 | { 567 | "is_own": false, 568 | "hash": "", 569 | "title": "23", 570 | "new_hash": "", 571 | "level": 1, 572 | "is_own_book": false, 573 | "word_count": 6125, 574 | "version": "190804001", 575 | "content_file": "", 576 | "chapter_title": "23", 577 | "chapter_uid": "1092470278780776448", 578 | "is_cover": false, 579 | "is_trial": false, 580 | "chapter_index": 24, 581 | "size": 22558 582 | }, 583 | { 584 | "is_own": false, 585 | "hash": "", 586 | "title": "24", 587 | "new_hash": "", 588 | "level": 1, 589 | "is_own_book": false, 590 | "word_count": 8329, 591 | "version": "190804001", 592 | "content_file": "", 593 | "chapter_title": "24", 594 | "chapter_uid": "1092470280013897728", 595 | "is_cover": false, 596 | "is_trial": false, 597 | "chapter_index": 25, 598 | "size": 29258 599 | }, 600 | { 601 | "is_own": false, 602 | "hash": "", 603 | "title": "25", 604 | "new_hash": "", 605 | "level": 1, 606 | "is_own_book": false, 607 | "word_count": 5087, 608 | "version": "190804001", 609 | "content_file": "", 610 | "chapter_title": "25", 611 | "chapter_uid": "1092470281523826688", 612 | "is_cover": false, 613 | "is_trial": false, 614 | "chapter_index": 26, 615 | "size": 18790 616 | }, 617 | { 618 | "is_own": false, 619 | "hash": "", 620 | "title": "26", 621 | "new_hash": "", 622 | "level": 1, 623 | "is_own_book": false, 624 | "word_count": 3892, 625 | "version": "190804001", 626 | "content_file": "", 627 | "chapter_title": "26", 628 | "chapter_uid": "1092470282551427072", 629 | "is_cover": false, 630 | "is_trial": false, 631 | "chapter_index": 27, 632 | "size": 14197 633 | }, 634 | { 635 | "is_own": false, 636 | "hash": "", 637 | "title": "27", 638 | "new_hash": "", 639 | "level": 1, 640 | "is_own_book": false, 641 | "word_count": 2602, 642 | "version": "190804001", 643 | "content_file": "", 644 | "chapter_title": "27", 645 | "chapter_uid": "1092470283327426560", 646 | "is_cover": false, 647 | "is_trial": false, 648 | "chapter_index": 28, 649 | "size": 9602 650 | }, 651 | { 652 | "is_own": false, 653 | "hash": "", 654 | "title": "28", 655 | "new_hash": "", 656 | "level": 1, 657 | "is_own_book": false, 658 | "word_count": 9352, 659 | "version": "190804001", 660 | "content_file": "", 661 | "chapter_title": "28", 662 | "chapter_uid": "1092470283914514432", 663 | "is_cover": false, 664 | "is_trial": false, 665 | "chapter_index": 29, 666 | "size": 31836 667 | }, 668 | { 669 | "is_own": false, 670 | "hash": "", 671 | "title": "29", 672 | "new_hash": "", 673 | "level": 1, 674 | "is_own_book": false, 675 | "word_count": 5655, 676 | "version": "190804001", 677 | "content_file": "", 678 | "chapter_title": "29", 679 | "chapter_uid": "1092470285504180224", 680 | "is_cover": false, 681 | "is_trial": false, 682 | "chapter_index": 30, 683 | "size": 19990 684 | }, 685 | { 686 | "is_own": false, 687 | "hash": "", 688 | "title": "30", 689 | "new_hash": "", 690 | "level": 1, 691 | "is_own_book": false, 692 | "word_count": 3254, 693 | "version": "190804001", 694 | "content_file": "", 695 | "chapter_title": "30", 696 | "chapter_uid": "1092470286561177600", 697 | "is_cover": false, 698 | "is_trial": false, 699 | "chapter_index": 31, 700 | "size": 11921 701 | }, 702 | { 703 | "is_own": false, 704 | "hash": "", 705 | "title": "31", 706 | "new_hash": "", 707 | "level": 1, 708 | "is_own_book": false, 709 | "word_count": 1501, 710 | "version": "190804001", 711 | "content_file": "", 712 | "chapter_title": "31", 713 | "chapter_uid": "1092470287207133184", 714 | "is_cover": false, 715 | "is_trial": false, 716 | "chapter_index": 32, 717 | "size": 5872 718 | } 719 | ], 720 | "version": "190804001" 721 | } 722 | ``` 723 |
724 | 725 | ### 获取章节的公钥接口 726 | 727 | 接口地址: 728 | ```http request 729 | GET https://www.zhihu.com/api/v3/books/[bookId]/chapters/[chapterUid]/download_info 730 | ``` 731 | 732 |
733 | 查看响应示例 734 | 735 | ```json 736 | { 737 | "key_hash": "E48FBEA6AACC8177E10AF1190421E92B", 738 | "key": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgkB5vTONXv15SukpyFKKbkO3m\nMbZ8z4u8HwtV14qEoOJaOhh6pu75o6bojX3RFnWm3wHxFjmdJu1+JurChFiY2fxD\nQ+SZWXKzNvfK/fvi3JNMfgVfp0HcuCzKDWE+vPeactLeTNnjFRYlnaUygiwm0KNE\nhDDHw2/41xjcPLmPpQIDAQAB\n-----END PUBLIC KEY-----" 739 | } 740 | ``` 741 |
742 | 743 | 其中,`key_hash`参与签名的计算,`key`则用于加密客户端生成的一次性密码参数,加密后通过`trans_key`传给后端。 744 | 745 | ### 获取章节资源接口 746 | 747 | 接口地址: 748 | ```http request 749 | POST https://www.zhihu.com/api/v3/books/[bookId]/chapters/[chapterUid]/download 750 | 751 | client_id: 5774b305d2ae4469a2c9258956ea48 752 | key_hash: E48FBEA6AACC8177E10AF1190421E92B 753 | signature: 4b18cc7ffa392f6687901658c3f14b572e97c4d3 754 | timestamp: 1723791957338 755 | trans_key: 2eg1rIgURbckAtGLuWMEs4ZEWQzYugrFLWnZo+O+qu0GJ2xlACgZ73gIqc0b1M+Dh43RQzH2ElfeBq5mPYoCYWt1Wz56vlfRwcGSiZjImaweKfc/M1SHVCSRrbfKm8VZAhoKBvZKzUb2NV7fWQ9qetOAEIC5YcWEJwnaDlHDtW0= 756 | ``` 757 | 758 |
759 | 查看响应示例 760 | 761 | ```json 762 | { 763 | "format_html_path": "http://book.zhimg.com/chapters/119601575/format_x86bskwm_190804001.html", 764 | "zip_path": "", 765 | "key_hash": "4DE1C8950141B0C56FA41E078F4AFFE9", 766 | "is_access_reader": true, 767 | "html_path": "http://book.zhimg.com/chapters/119601575/craw5akb_190804001.html", 768 | "download_url": "", 769 | "key": "kaxlNCdIny/oMkj3Vgi3YmIwqOoe1BtnnJu+pc/7pfE=", 770 | "message": "", 771 | "default_css": "https://book.zhimg.com/books/61E37BD8AC9209F6E3B5C18A7BF231E0.css", 772 | "css_path": [ 773 | "https://book.zhimg.com/books/61E37BD8AC9209F6E3B5C18A7BF231E0.css", 774 | "https://book.zhimg.com/books/EC6E21149A53D434CE13B1F13F7C3545.css", 775 | "https://book.zhimg.com/books/1F451EC236F03D3A1166E4D8540AA024.css" 776 | ] 777 | } 778 | ``` 779 | 780 | 其中,`html_path`字段即为该章节的文件地址,直接下载该文件可得到密文内容。`key`为解密该内容的密码,只不过这个密码不是明文,是经过我们前面生成的那个一次性密码用`aes-128-cfb8`算法加密得到的。 781 |
782 | 783 | 784 | 785 | ## 加解密算法分析 786 | 787 | ### 流程梳理 788 | 789 | 1. 调用【获取章节的公钥接口】获取到对应章节的公钥`key`和`keyHash`参数 790 | 2. 调用【获取章节资源接口】获取章节的html路径,以及解密所需的`key`,参数的签名及加密算法见下面的代码 791 | 792 | ```ts 793 | // 计算 transKey,使用公钥 key 对 secret 进行加密,即得到 trans_key 794 | function getTransKey(key: string, secret: string) { 795 | const body = Buffer.alloc(128 - secret.length) 796 | const buf = Buffer.concat([body, Buffer.from(secret)]) 797 | return crypto.publicEncrypt({ 798 | key: key, 799 | padding: 3, 800 | }, buf).toString('base64') 801 | } 802 | 803 | // 计算签名 804 | function signPayload(payload: string[]) { 805 | const e = crypto.createHmac("sha1", "key") 806 | for (const value of payload) { 807 | e.update(value) 808 | } 809 | return e.digest("hex") 810 | } 811 | 812 | // 这两个参数由【获取章节的公钥接口】获取 813 | const key = '' 814 | const key_hash = '' 815 | 816 | const client_id = "5774b305d2ae4469a2c9258956ea48"; 817 | const timestamp = Number(new Date()) 818 | 819 | // 生成临时密码 820 | const secret = Array.from({length: 16}).map(() => Math.floor(16 * Math.random()).toString(16).toUpperCase()).join("") 821 | 822 | // 对临时密码进行公钥加密,以便在网络上传输 823 | const transKey = getTransKey(key, secret) 824 | const signature = signPayload([chapterUid, transKey, client_id, timestamp, key_hash]) 825 | ``` 826 | 827 | 3. 下载 html 资源,拿到密文 828 | 4. 还原密钥并进行解密 829 | 830 | ```js 831 | // 用 aes-128-cfb8 算法解密数据 832 | function decrypt(secret, data, encoding) { 833 | let iv, body; 834 | if ("string" == typeof data) { 835 | let buf = Buffer.from(data, "base64"); 836 | iv = Buffer.alloc(16) 837 | body = Buffer.alloc(buf.length - 16) 838 | buf.copy(iv, 0, 0, 16) 839 | buf.copy(body, 0, 16) 840 | } else { 841 | iv = data.slice(0, 16) 842 | body = data.slice(16) 843 | } 844 | return crypto.createDecipheriv("aes-128-cfb8", secret, iv).update(body, null, encoding) 845 | } 846 | 847 | // secret 即上面生成的临时密码,key是【获取章节资源接口】返回的 848 | const htmlSecret = decrypt(secret, key, 'utf8') 849 | 850 | // buffer 为密文内容 851 | const plainText = decrypt(htmlSecret, buffer, 'utf8') 852 | ``` 853 | 854 | ### 总结 855 | 856 | 整个加解密的过程如下: 857 | 858 | 知乎服务器对每本书的每个章节的`html`内容用`aes-128-cfb8`对称加密算法进行加密,并保存该章节对应的密码(每个章节都有自己的密码,我们称这个密码为`k1`),只要知道该密码,即可解密`html`内容。 859 | 860 | 然后浏览器在查看该章节的内容时,会在前端生成一个临时密码(我们称为`k2`),这个`k2`并不会在网络上传输,只会保留在本地内存中,并且每次生成的都不一样。 861 | 862 | 然后前端用一对公私钥中的公钥(调接口获取)对`k2`进行加密并传给知乎服务器,由于`k2`是用非对称加密算法加密的,所以只有浏览器前端和知乎知道`k2`的内容。 863 | 864 | 然后知乎服务器用`k2`作为密码,使用对称加密算法`aes-128-cfb8`对`k1`的明文进行加密,并返回给前端(【获取章节资源接口】所返回的`key`,即`k1`的密文)。 865 | 866 | 然后前端使用相同的对称加密算法`aes-128-cfb8`解密出`k1`的明文(因为加密的密码就是`k2`),然后下载html密文,并用`k1`解密其内容。 867 | 868 | 最后,至于是每个章节都有一对公私钥,还是说所有书籍共用一对公私钥,都无所谓。因为通过网络的数据都不可能解密出`k2`的内容。 869 | -------------------------------------------------------------------------------- /docs/workflows.md: -------------------------------------------------------------------------------- 1 | # 自动检测并发版 2 | 3 | ## check-update: 检查微信核心脚本是否有更新 4 | 5 | ### 运行环境 6 | [Ubuntu2204-Readme.md](https://github.com/actions/runner-images/blob/ubuntu22/20240804.1/images/ubuntu/Ubuntu2204-Readme.md) 7 | 8 | ### 输出 9 | ```yaml 10 | outputs: 11 | needUpgrade: ${{ steps.step3.outputs.exists }} 12 | ``` 13 | 通过`step3`中的`test`命令来确认是否有新版本文件存在,如果有,则会启动下一个任务进行打包发版。 14 | 15 | 16 | ## build: 打包发版 17 | 18 | 依赖于`check-update`任务,然后通过以下条件决定是否执行该任务: 19 | 20 | ```yaml 21 | needs: check-update 22 | if: needs.check-update.outputs.needUpgrade == 'true' 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/wrx.md: -------------------------------------------------------------------------------- 1 | # wrx 插件相关说明 2 | 3 | ## 缓存数据结构 4 | 5 | ### `chapters` store 6 | 7 | 用于存储章节内容。 8 | 9 | 对象数据结构如下: 10 | 11 | ```ts 12 | // key 采用【书籍id】 13 | const key = `${bid}` 14 | 15 | // value 16 | interface StoreObjectValue { 17 | /** 18 | * 书籍id (keyPath) 19 | */ 20 | bid: string 21 | 22 | /** 23 | * 书籍的所有章节数据 24 | */ 25 | chapters: any[] 26 | } 27 | ``` 28 | 29 | ### `tocs` store 30 | 31 | 用于存储目录内容。 32 | 33 | 对象数据结构如下: 34 | 35 | ```ts 36 | // key 采用【书籍id】 37 | const key = `${bid}` 38 | 39 | // value 40 | interface StoreObjectValue { 41 | /** 42 | * 书籍id (keyPath) 43 | */ 44 | bid: string 45 | 46 | bookId: string 47 | 48 | /** 49 | * 书籍的目录数据 50 | */ 51 | toc: any[] 52 | } 53 | ``` 54 | 55 | 56 | ### `details` store 57 | 58 | 用于存储书籍的元数据。 59 | 60 | 对象数据结构如下: 61 | 62 | ```ts 63 | // key 采用【书籍id】 64 | const key = `${bid}` 65 | 66 | // value 67 | interface StoreObjectValue { 68 | /** 69 | * 书籍id (keyPath) 70 | */ 71 | bid: string 72 | 73 | bookId: string 74 | 75 | /** 76 | * 书籍的目录数据 77 | */ 78 | detail: any 79 | } 80 | ``` 81 | 82 | ### 数据导出的 json 结构 83 | 84 | ```json5 85 | { 86 | // 加密的 bookId 87 | bid: "d733256071eeeed9d7322fd", 88 | 89 | // 原始的 bookId 90 | bookId: "32435929", 91 | 92 | // 目录 93 | toc: [], 94 | 95 | // 元数据 96 | meta: {}, 97 | 98 | // 章节数据 99 | chapters: [], 100 | 101 | // 网站 102 | site: "https://weread.qq.com", 103 | 104 | // 导出时间 105 | date: 1723566852598, 106 | } 107 | ``` 108 | 109 | ## 添加新的网站支持需要做的事情 110 | 111 | ### 图标切换与启用/禁用 112 | 113 | 修改`src/background.js`,增加相应的规则及判断条件。 114 | 115 | ### 缓存数据 116 | 117 | 修改`src/manifest.json`文件中的`host_permissions`字段,将目标网站及接口的域名包含进来。 118 | 119 | 修改`src/common/xhr-fetch.js`文件中的`INTERCEPT_APIS`,增加目标网站的数据接口拦截规则。 120 | 该规则可以是字符串,也可以是正则表达式,都将匹配接口的`origin + pathname`。 121 | 122 | 在`src/sites`目录下新建对应网站的目录,比如叫`weread`,来存放需要注入到该网站的脚本与样式文件。 123 | 124 | 该目录中需要包含以下这些文件: 125 | 126 | #### 1. content.js 127 | 该文件负责在页面上添加【导出本书数据】按钮,同时需要接收`xhr-fetch`发送过来的接口数据(通过`message`事件)。 128 | 129 | #### 2. store.js 130 | 创建`wrx`数据库,并提供数据存储与导出相关功能,供`content.js`文件使用 131 | 132 | #### 3. toc.css 133 | 在书籍的目录上添加标记,方便查看哪些章节已下载,哪些章节未下载。 134 | 135 | #### 4. 其他一些工具函数 136 | 137 | 最后,在`src/manifest.json`文件的`content_scripts`中注入这些资源。 138 | 139 | ### 自动翻页 140 | 在对应网站目录下新建`auto.js`,这个脚本会被注入到目标页面中,负责接收`popup`发送过来的事件,并执行按钮点击动作。 141 | -------------------------------------------------------------------------------- /history.txt: -------------------------------------------------------------------------------- 1 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.0cf1335d.js 2 | last-modified: Tue, 16 Jan 2024 09:17:03 GMT 3 | etag: "b00b61ff4704003ed28b3638e3ae3387" 4 | 5 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.dd196dae.js 6 | last-modified: Tue, 23 Jan 2024 04:12:11 GMT 7 | etag: "0064c56c63bec431c2f8ddc1a7badc55" 8 | 9 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.9a7afd68.js 10 | last-modified: Thu, 29 Feb 2024 07:27:01 GMT 11 | etag: "5c85e2b7d6bafadb856956ea96035a95" 12 | 13 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.014ecba2.js 14 | last-modified: Fri, 15 Mar 2024 07:08:57 GMT 15 | etag: "833eed5efcf897ec03dcc180a51bf96c" 16 | 17 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.86c67fdd.js 18 | last-modified: Mon, 18 Mar 2024 04:08:14 GMT 19 | etag: "69b18ce8d9d22358920127bc9c4c9759" 20 | 21 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.0f8531aa.js 22 | last-modified: Thu, 21 Mar 2024 14:02:59 GMT 23 | etag: "92dc69a34a4724665789585dddbe0840" 24 | 25 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.977228d7.js 26 | last-modified: Tue, 26 Mar 2024 08:53:35 GMT 27 | etag: "78bb44745126c5575313979b5401c97e" 28 | 29 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/6.8e17f59a.js 30 | last-modified: Thu, 28 Mar 2024 09:09:25 GMT 31 | etag: "8a991ef88e25880f23980f77688d6a5a" 32 | 33 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.2fb98ce9.js 34 | last-modified: Fri, 19 Apr 2024 04:00:19 GMT 35 | etag: "eac112d982a257e663c63f927ba428fd" 36 | 37 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.c9fda82f.js 38 | last-modified: Fri, 19 Apr 2024 10:16:56 GMT 39 | etag: "29a010a3fc7f62bf25140021e1da8a5c" 40 | 41 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.20b30d46.js 42 | last-modified: Thu, 25 Apr 2024 08:11:39 GMT 43 | etag: "bc1b716593f279f4895c83f2c7ad75dc" 44 | 45 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.9202be9e.js 46 | last-modified: Fri, 26 Apr 2024 04:00:52 GMT 47 | etag: "e8da5bdf84b67057b77979b23b3e2593" 48 | 49 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.9a9d9490.js 50 | last-modified: Fri, 26 Apr 2024 12:53:45 GMT 51 | etag: "4bd162fbe21b47a5bf8a3e04c2e127ac" 52 | 53 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.ab56cea8.js 54 | last-modified: Mon, 29 Apr 2024 13:28:38 GMT 55 | etag: "2b3ab62c9c1ae161008cd99c7231df93" 56 | 57 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.dc67f89b.js 58 | last-modified: Tue, 30 Apr 2024 10:35:40 GMT 59 | etag: "5367c159cd9fa8562b9f4371342ec7c0" 60 | 61 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.79c38430.js 62 | last-modified: Mon, 06 May 2024 02:47:55 GMT 63 | etag: "e4e21d660fe5aba53386aca5245944eb" 64 | 65 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.f6ac3b8d.js 66 | last-modified: Tue, 07 May 2024 09:02:57 GMT 67 | etag: "3bfa38b5f2fd44edc1c81af39608ea33" 68 | 69 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.bd5fbf27.js 70 | last-modified: Fri, 10 May 2024 12:57:11 GMT 71 | etag: "a050f7c6f641f5e7a1dd557ebd81d1cd" 72 | 73 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.f554b531.js 74 | last-modified: Mon, 20 May 2024 12:07:55 GMT 75 | etag: "c25c033bf573faa67019ef3a459a7de1" 76 | 77 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.8c1ddae9.js 78 | last-modified: Tue, 21 May 2024 02:58:27 GMT 79 | etag: "8a7e5e00c7334ea7e63fffe4901c57a0" 80 | 81 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.fab7d9dc.js 82 | last-modified: Tue, 21 May 2024 06:39:17 GMT 83 | etag: "97950ea4c189814b89caf5910526938e" 84 | 85 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.ce60691e.js 86 | last-modified: Tue, 21 May 2024 08:35:59 GMT 87 | etag: "637b6494148c4e6d9cbc12ec4de3b474" 88 | 89 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.3b8878b8.js 90 | last-modified: Thu, 23 May 2024 12:53:34 GMT 91 | etag: "f535b61aa1ac0e9b936200322d8bd6ac" 92 | 93 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.947c2729.js 94 | last-modified: Tue, 28 May 2024 10:11:32 GMT 95 | etag: "5cbc82146d8341426cb2fbd1b7cdd738" 96 | 97 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.f69c4bdc.js 98 | last-modified: Fri, 31 May 2024 09:26:35 GMT 99 | etag: "c3c0af8df512179d769fe2435d76ae67" 100 | 101 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.a06eabbb.js 102 | last-modified: Sun, 09 Jun 2024 12:45:28 GMT 103 | etag: "b869a4a09b67960ed6d29e7d63dba3ae" 104 | 105 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.24acd39e.js 106 | last-modified: Wed, 19 Jun 2024 08:38:59 GMT 107 | etag: "529f04ff0cd47700cc22c2e272be53e0" 108 | 109 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.0a38f2f5.js 110 | last-modified: Fri, 21 Jun 2024 13:42:29 GMT 111 | etag: "4f46dc1faf454f9726edb4bceb9020ba" 112 | 113 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.9b51c786.js 114 | last-modified: Sun, 23 Jun 2024 12:48:08 GMT 115 | etag: "4c05c702a8481736e526fba077c39317" 116 | 117 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.a320306a.js 118 | last-modified: Mon, 01 Jul 2024 13:15:42 GMT 119 | etag: "1c6f68deaeb9007e8609019046dd8afc" 120 | 121 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.76a189e5.js 122 | last-modified: Tue, 02 Jul 2024 11:49:44 GMT 123 | etag: "c1f0803414d70bd10e873715908c7ba9" 124 | 125 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.1a24df31.js 126 | last-modified: Fri, 12 Jul 2024 08:51:21 GMT 127 | etag: "e39ed99ba9b70a51b90a0b4dc5b96a5a" 128 | 129 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.d69be44e.js 130 | last-modified: Fri, 12 Jul 2024 16:02:21 GMT 131 | etag: "ce792b9c3f255dc0838dfa69a148fd4f" 132 | 133 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.c9283531.js 134 | last-modified: Sat, 13 Jul 2024 05:37:32 GMT 135 | etag: "4131dbee0eb664bdee3ebe22dcadcd8d" 136 | 137 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.9409adc3.js 138 | last-modified: Thu, 18 Jul 2024 08:16:05 GMT 139 | etag: "3125906aa136adeed12484d008093322" 140 | 141 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.c7402365.js 142 | last-modified: Fri, 19 Jul 2024 10:43:33 GMT 143 | etag: "52f7e50e0ff91a2cbce7e85704bf7b75" 144 | 145 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.3394fd13.js 146 | last-modified: Fri, 19 Jul 2024 13:10:07 GMT 147 | etag: "ff341f1e9d61868378928051b3ed2154" 148 | 149 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.3da2de76.js 150 | last-modified: Wed, 24 Jul 2024 13:38:25 GMT 151 | etag: "079e5247e220eefbd485562aea86bb4d" 152 | 153 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.da762a26.js 154 | last-modified: Fri, 26 Jul 2024 09:58:40 GMT 155 | etag: "e6525796dbf66466ccfe9cbb01723ea9" 156 | 157 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.dc814d1d.js 158 | last-modified: Fri, 26 Jul 2024 13:09:00 GMT 159 | etag: "554bc680a5892910deae918c31482967" 160 | 161 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.dc814d1d.js 162 | last-modified: Fri, 26 Jul 2024 13:09:00 GMT 163 | etag: "554bc680a5892910deae918c31482967" 164 | 165 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.04bb0a1c.js 166 | last-modified: Mon, 29 Jul 2024 06:05:00 GMT 167 | etag: "cfd7bf58ac39bdf88da1ab78e53eb560" 168 | 169 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.70713d4a.js 170 | last-modified: Thu, 01 Aug 2024 09:04:32 GMT 171 | etag: "81414bdc71c4fdeda53a707bf8249500" 172 | 173 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.2a0ae8be.js 174 | last-modified: Fri, 02 Aug 2024 09:58:40 GMT 175 | etag: "9d4bed8b9d77f91382c34575ba4251e9" 176 | 177 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.f8cd399c.js 178 | last-modified: Tue, 13 Aug 2024 06:57:46 GMT 179 | etag: "efa857e06f31b7b8c94a91e28b8b0696" 180 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.f8cd399c.js 181 | last-modified: Tue, 13 Aug 2024 06:57:46 GMT 182 | etag: "efa857e06f31b7b8c94a91e28b8b0696" 183 | 184 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.f8cd399c.js 185 | last-modified: Tue, 13 Aug 2024 06:57:46 GMT 186 | etag: "efa857e06f31b7b8c94a91e28b8b0696" 187 | 188 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.5853f5ce.js 189 | last-modified: Thu, 15 Aug 2024 03:46:31 GMT 190 | etag: "bea18d09d6ec3c95c0ed50e32579d114" 191 | 192 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.cfe384e5.js 193 | last-modified: Thu, 15 Aug 2024 04:58:12 GMT 194 | etag: "e3bfa60acdc5224250489eebd322b7aa" 195 | 196 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.8e1d03c5.js 197 | last-modified: Thu, 15 Aug 2024 07:11:02 GMT 198 | etag: "559ed8943609196fc9aef6a2f41ddef2" 199 | 200 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.905964f9.js 201 | last-modified: Fri, 23 Aug 2024 07:28:18 GMT 202 | etag: "dbf28500dc32b75b1f3bf4d4acd3687c" 203 | 204 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.68f5013a.js 205 | last-modified: Fri, 23 Aug 2024 13:43:54 GMT 206 | etag: "57a46ac6a4e31ba200993d8d282b6f2c" 207 | 208 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.c2344937.js 209 | last-modified: Mon, 26 Aug 2024 08:00:53 GMT 210 | etag: "e4c01a1cba2da03eb0123fef9529f2fe" 211 | 212 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.c0d178f2.js 213 | last-modified: Thu, 29 Aug 2024 13:53:28 GMT 214 | etag: "9867ced19f426ce766ce2145bc446300" 215 | 216 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.e5ed2744.js 217 | last-modified: Mon, 02 Sep 2024 07:28:08 GMT 218 | etag: "b3836a71d9924a3c5a125d25fcd89152" 219 | 220 | url: https://weread-1258476243.file.myqcloud.com/web/wrwebnjlogic/js/7.bcc01791.js 221 | last-modified: Wed, 04 Sep 2024 03:32:08 GMT 222 | etag: "2aa1e4060aa1baf06cf7a1e09e329ede" 223 | 224 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/7.1c8aad8c.js 225 | last-modified: Wed, 04 Sep 2024 11:22:54 GMT 226 | etag: "d220b92f3624826c5ab632a114ab3e8a" 227 | 228 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/7.6c432187.js 229 | last-modified: Fri, 06 Sep 2024 09:57:51 GMT 230 | etag: "f39037ea56f4706c03305db3c4137715" 231 | 232 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/7.22936e1b.js 233 | last-modified: Tue, 10 Sep 2024 06:42:24 GMT 234 | etag: "ce207c1b58f3c7ba414327da3547ce9e" 235 | 236 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/7.22936e1b.js 237 | last-modified: Tue, 10 Sep 2024 06:42:24 GMT 238 | etag: "ce207c1b58f3c7ba414327da3547ce9e" 239 | 240 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/7.8f773b30.js 241 | last-modified: Tue, 24 Sep 2024 06:17:08 GMT 242 | etag: "1bd02da7a61c1075c39862c125b0dbd2" 243 | 244 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/7.8f33e82b.js 245 | last-modified: Wed, 25 Sep 2024 11:49:30 GMT 246 | etag: "8a2ce0fdb621d1173a340e83e7bf8f49" 247 | 248 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.69d43536.js 249 | last-modified: Thu, 26 Sep 2024 09:39:14 GMT 250 | etag: "347e8fe4c9f6ab9902a8b16b91a5548d" 251 | 252 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.69d43536.js 253 | last-modified: Thu, 26 Sep 2024 09:39:14 GMT 254 | etag: "347e8fe4c9f6ab9902a8b16b91a5548d" 255 | 256 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.e03cf306.js 257 | last-modified: Sun, 29 Sep 2024 08:00:24 GMT 258 | etag: "5f2b374bccefefb037f0bf06982df46e" 259 | 260 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.eb149885.js 261 | last-modified: Wed, 09 Oct 2024 08:28:41 GMT 262 | etag: "cb18efc128817c936eaf2764c817df82" 263 | 264 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.30625fca.js 265 | last-modified: Fri, 11 Oct 2024 06:29:33 GMT 266 | etag: "d619411e7f5fbe31f20154e0f7f4b86e" 267 | 268 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.c4846ee2.js 269 | last-modified: Wed, 16 Oct 2024 06:20:47 GMT 270 | etag: "a4668fc4245a2cefdf095073a963528f" 271 | 272 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.044e9459.js 273 | last-modified: Thu, 24 Oct 2024 11:59:01 GMT 274 | etag: "47c9e6a9b1facd58ba341bf78cce3ee3" 275 | 276 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.e3fd8354.js 277 | last-modified: Mon, 28 Oct 2024 08:31:26 GMT 278 | etag: "bfee2fa0c1cc25d241fce74886e4fc4e" 279 | 280 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.60621a39.js 281 | last-modified: Mon, 28 Oct 2024 11:57:54 GMT 282 | etag: "7cff54327350558301b1288bcf78461e" 283 | 284 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.f0c2104c.js 285 | last-modified: Mon, 04 Nov 2024 05:42:50 GMT 286 | etag: "5437928e3b63083665c1587ee867ac6b" 287 | 288 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.c02802b1.js 289 | last-modified: Thu, 14 Nov 2024 11:36:25 GMT 290 | etag: "cc19b01e861f539eb4730868b7bf0c04" 291 | 292 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.1f0816c9.js 293 | last-modified: Thu, 09 Jan 2025 07:27:22 GMT 294 | etag: "76c736e6c57b853100085a730947d741" 295 | 296 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.a0b49d48.js 297 | last-modified: Mon, 13 Jan 2025 08:26:45 GMT 298 | etag: "0a9157f638c43db524fabf14923484a6" 299 | 300 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.e62d5026.js 301 | last-modified: Thu, 06 Feb 2025 09:54:40 GMT 302 | etag: "9ec95cb0202aa487d7cd358718bd0dd7" 303 | 304 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.1b1a58af.js 305 | last-modified: Thu, 20 Mar 2025 12:51:03 GMT 306 | etag: "0977b21c9e834a3e3e4ad41a2c4b4ca0" 307 | 308 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.6727fad4.js 309 | last-modified: Thu, 27 Mar 2025 13:12:41 GMT 310 | etag: "42603b4a8b4b764096d023f1f46e72e6" 311 | 312 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.82196ea9.js 313 | last-modified: Tue, 01 Apr 2025 14:35:39 GMT 314 | etag: "ae66d9d9f66d07745f9c0b56ebb5bf3d" 315 | 316 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.2b33baa9.js 317 | last-modified: Tue, 01 Apr 2025 16:06:51 GMT 318 | etag: "7179d83b8c9d742ac64400bdbf5c3172" 319 | 320 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.126c7638.js 321 | last-modified: Thu, 10 Apr 2025 04:55:34 GMT 322 | etag: "cf52253be436cd25d52e90cbcecfd4fc" 323 | 324 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.09d8684c.js 325 | last-modified: Thu, 10 Apr 2025 11:31:13 GMT 326 | etag: "af8a642227f3a0a0b6aed429eeb5487c" 327 | 328 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/8.c1484fdd.js 329 | last-modified: Fri, 11 Apr 2025 07:19:56 GMT 330 | etag: "7c25a3ee507b6cda654e261485c5979f" 331 | 332 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/9.16e91701.js 333 | last-modified: Fri, 23 May 2025 09:46:23 GMT 334 | etag: "c52c1b846b75bca6198eb478b450a329" 335 | 336 | url: https://cdn.weread.qq.com/web/wrwebnjlogic/js/9.58102893.js 337 | last-modified: Mon, 26 May 2025 10:09:33 GMT 338 | etag: "e63f71722761138d6ae27720163d2078" 339 | 340 | -------------------------------------------------------------------------------- /latest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.8.6", 3 | "weread": [ 4 | "9.58102893.js", 5 | "9.16e91701.js", 6 | "8.c1484fdd.js", 7 | "8.09d8684c.js", 8 | "8.126c7638.js", 9 | "8.2b33baa9.js", 10 | "8.82196ea9.js", 11 | "8.6727fad4.js", 12 | "8.1b1a58af.js", 13 | "8.e62d5026.js" 14 | ], 15 | "build_time": "2025-05-26 13:37:40" 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wrx", 3 | "version": "1.0.1", 4 | "main": "index.js", 5 | "repository": "git@github.com:jooooock/wrx.git", 6 | "author": "jooooock", 7 | "type": "module", 8 | "license": "MIT", 9 | "dependencies": { 10 | "dayjs": "^1.11.12", 11 | "fs-extra": "^11.1.1", 12 | "jscodeshift": "^0.15.1", 13 | "jsdom": "^23.2.0", 14 | "zip-lib": "^0.7.3" 15 | }, 16 | "devDependencies": { 17 | "@types/jscodeshift": "^0.11.11", 18 | "@types/node": "^22.4.0", 19 | "ast-types": "^0.14.2", 20 | "chrome-types": "^0.1.242" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /releases/wrx-v0.0.8.6.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/releases/wrx-v0.0.8.6.zip -------------------------------------------------------------------------------- /scripts/utils.js: -------------------------------------------------------------------------------- 1 | import https from 'node:https' 2 | import fs from 'node:fs' 3 | import path from 'node:path' 4 | import fse from 'fs-extra' 5 | import {fileURLToPath} from 'node:url' 6 | 7 | 8 | /** 9 | * GET 拉取文件内容 10 | * @param url 11 | * @return {Promise} 12 | */ 13 | export function get(url) { 14 | return new Promise((resolve, reject) => { 15 | https.get(url, { 16 | headers: { 17 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36", 18 | referer: "https://weread.qq.com/" 19 | } 20 | }, resp => { 21 | if (resp.statusCode !== 200) { 22 | reject(new Error(`请求微信(${url})失败: ${resp.statusMessage}(${resp.statusCode})`)) 23 | return 24 | } 25 | 26 | let body = '' 27 | resp.on('data', (chunk) => { 28 | body += chunk.toString() 29 | }); 30 | resp.on('end', () => { 31 | resolve({ 32 | body: body, 33 | headers: resp.headers, 34 | }) 35 | }) 36 | }) 37 | }) 38 | } 39 | 40 | export function readFile(relativeFilePath) { 41 | return fs.readFileSync(new URL(relativeFilePath, import.meta.url), {encoding: 'utf-8'}) 42 | } 43 | 44 | export function writeFile(relativeFilePath, content, flag = 'w') { 45 | const target = fileURLToPath(new URL(relativeFilePath, import.meta.url)) 46 | fse.ensureDirSync(path.dirname(target), {}) 47 | 48 | fs.writeFileSync(target, content, {encoding: 'utf-8', flag: flag}) 49 | return target 50 | } 51 | 52 | export function readJson(relativeFilePath) { 53 | return JSON.parse(fs.readFileSync(new URL(relativeFilePath, import.meta.url), {encoding: 'utf-8'})) 54 | } 55 | export function writeJson(relativeFilePath, json) { 56 | const target = fileURLToPath(new URL(relativeFilePath, import.meta.url)) 57 | fse.ensureDirSync(path.dirname(target), {}) 58 | 59 | fs.writeFileSync(target, JSON.stringify(json, null, 2), {encoding: 'utf-8'}) 60 | return target 61 | } 62 | 63 | 64 | export function sleep(duration) { 65 | return new Promise(resolve => { 66 | setTimeout(resolve, duration) 67 | }) 68 | } 69 | 70 | 71 | /** 72 | * 递增版本号 73 | * @param version 74 | * @return {string} 75 | */ 76 | export function incrementVersion(version) { 77 | const parts = version.split('.').reverse() 78 | let i = 0 79 | while (parts[i] >= 9) { 80 | i++ 81 | if (i >= parts.length) { 82 | throw new RangeError('version已超出可用范围') 83 | } 84 | } 85 | parts[i]++ 86 | while (i >= 1) { 87 | parts[i-1]=0 88 | i-- 89 | } 90 | return parts.reverse().join('.') 91 | } 92 | -------------------------------------------------------------------------------- /scripts/weread/build.js: -------------------------------------------------------------------------------- 1 | import fse from "fs-extra"; 2 | import {fileURLToPath} from "node:url"; 3 | import fs from "node:fs"; 4 | import {readFile, readJson, writeJson} from "../utils.js"; 5 | import zl from "zip-lib"; 6 | import dayjs from "dayjs"; 7 | import path from "node:path"; 8 | 9 | 10 | function readVersion() { 11 | return readJson('../src/manifest.json').version 12 | } 13 | 14 | export async function build() { 15 | // 拷贝src目录到crx目录 16 | const src = fileURLToPath(new URL('../../src', import.meta.url)) 17 | const crx = fileURLToPath(new URL('../../crx', import.meta.url)) 18 | fse.copySync(src, crx) 19 | 20 | // 读取 manifest.json 中的版本号 21 | const version = readVersion() 22 | 23 | const zipPath = fileURLToPath(new URL(`../../releases/wrx-v${version}.zip`, import.meta.url)) 24 | await zl.archiveFolder(crx, zipPath) 25 | console.log('构建完成') 26 | } 27 | 28 | function cleanup() { 29 | // 读取 manifest.json 中的版本号 30 | const version = readVersion() 31 | 32 | // 删除 crx 临时目录 33 | const crx = fileURLToPath(new URL('../../crx', import.meta.url)) 34 | fse.removeSync(crx) 35 | 36 | // 删除 releases 目录下面的老版本 37 | const files = fs.readdirSync(fileURLToPath(new URL('../../releases', import.meta.url))) 38 | for (const file of files) { 39 | if (!file.includes(version)) { 40 | fse.removeSync(fileURLToPath(new URL(`../../releases/${file}`, import.meta.url))) 41 | } 42 | } 43 | 44 | // 记录本次构建的结果 45 | const now = dayjs().format('YYYY-MM-DD HH:mm:ss') 46 | const latest = readJson('../latest.json') 47 | latest.version = version 48 | latest.build_time = now 49 | const url = readFile('./weread/tmp/url') 50 | latest.weread.unshift(path.basename(url)) 51 | // 只保留最近10个版本 52 | if (latest.weread.length > 10) { 53 | latest.weread.length = 10 54 | } 55 | writeJson('../latest.json', latest) 56 | 57 | // 清理weread临时目录 58 | fse.removeSync(fileURLToPath(new URL('tmp', import.meta.url))) 59 | } 60 | 61 | 62 | ;(async () => { 63 | await build() 64 | cleanup() 65 | })(); 66 | -------------------------------------------------------------------------------- /scripts/weread/check-update.js: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import {JSDOM} from "jsdom"; 3 | import {get, readJson, writeFile} from "../utils.js"; 4 | 5 | process.on('uncaughtException', (error) => { 6 | console.log(error) 7 | // if (error.code !== 'ECONNRESET') { 8 | // } 9 | }) 10 | 11 | /** 12 | * 检测微信读书是否有更新 13 | */ 14 | async function hasNewVersion() { 15 | const {body: html} = await get('https://weread.qq.com/web/reader/22a327a0813ab86fbg010c7d') 16 | const dom = new JSDOM(html) 17 | const scripts = dom.window.document.querySelectorAll('script') 18 | if (scripts.length <= 0) { 19 | throw new Error('拉取的html中没有发现script元素') 20 | } 21 | 22 | // 最后一个 script 元素 23 | const lastScript = scripts[scripts.length - 1] 24 | const coreJSURL = lastScript.src 25 | console.log('[微信读书]: 最新的js文件url: ', coreJSURL) 26 | 27 | const latest = readJson('../latest.json') 28 | return [!latest.weread.includes(path.basename(coreJSURL)), coreJSURL] 29 | } 30 | 31 | ;(async () => { 32 | const [needUpgrade, url] = await hasNewVersion() 33 | if (!needUpgrade) { 34 | console.log('[微信读书]: js文件无变化') 35 | process.exit(0) 36 | } 37 | 38 | // 目标url写入临时文件 39 | writeFile('./weread/tmp/url', url) 40 | })(); 41 | -------------------------------------------------------------------------------- /scripts/weread/patch.js: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import {get, incrementVersion, readFile, readJson, writeFile} from "../utils.js"; 3 | import {transform} from "./transformer.js"; 4 | import fse from "fs-extra"; 5 | import {fileURLToPath} from "node:url"; 6 | import fs from "node:fs"; 7 | 8 | 9 | process.on('uncaughtException', (error) => { 10 | console.log(error) 11 | // if (error.code !== 'ECONNRESET') { 12 | // } 13 | }) 14 | 15 | async function patchFile(url) { 16 | const {body: content, headers} = await get(url) 17 | const info = `url: ${url}\nlast-modified: ${headers["last-modified"]}\netag: ${headers['etag']}\n\n` 18 | writeFile('../history.txt', info, 'as') 19 | 20 | // 写入临时目录 21 | const patchedContent = transform(content) 22 | writeFile(`./weread/tmp/${path.basename(url)}`, patchedContent) 23 | } 24 | 25 | function replaceProjectFile(url) { 26 | // 更新 overrides 目录,保留最新的10个文件 27 | const latest = readJson('../latest.json') 28 | const overridesFiles = fs.readdirSync(fileURLToPath(new URL('../../src/overrides/weread', import.meta.url))) 29 | for (const file of overridesFiles) { 30 | if (!latest.weread.includes(file)) { 31 | fse.removeSync(fileURLToPath(new URL(`../../src/overrides/weread/${file}`, import.meta.url))) 32 | } 33 | } 34 | 35 | // 将最新的补丁文件拷贝到 overrides 目录 36 | const latestJs = new URL(`./tmp/${path.basename(url)}`, import.meta.url) 37 | const dest = new URL('../../src/overrides/weread', import.meta.url) 38 | fse.copySync(fileURLToPath(latestJs), fileURLToPath(new URL(`../../src/overrides/weread/${path.basename(url)}`, import.meta.url))) 39 | 40 | // 基于最新的 overrides 目录生成规则文件 41 | const files = fs.readdirSync(fileURLToPath(dest)) 42 | let counter = 1 43 | const ruleJson = files.map(filename => ({ 44 | "id": counter++, 45 | "priority": 1, 46 | "condition": { 47 | "urlFilter": filename 48 | }, 49 | "action": { 50 | "type": "redirect", 51 | "redirect": { 52 | "extensionPath": "/overrides/weread/" + filename 53 | } 54 | } 55 | })) 56 | writeFile('../src/rules/weread.json', JSON.stringify(ruleJson, null, 2)) 57 | 58 | // 更新 manifest.json 59 | const manifest = readJson('../src/manifest.json') 60 | manifest.version = incrementVersion(latest.version) 61 | manifest["web_accessible_resources"][1]["resources"] = files.map(filename => `/overrides/weread/${filename}`) 62 | writeFile('../src/manifest.json', JSON.stringify(manifest, null, 2)) 63 | } 64 | 65 | ;(async () => { 66 | const url = readFile('./weread/tmp/url') 67 | 68 | await patchFile(url) 69 | replaceProjectFile(url) 70 | })(); 71 | -------------------------------------------------------------------------------- /scripts/weread/transformer.js: -------------------------------------------------------------------------------- 1 | import jscodeshift from 'jscodeshift' 2 | 3 | 4 | // 需要使用 babylon 解析器 5 | const j = jscodeshift.withParser('babylon') 6 | 7 | 8 | /** 9 | * 查找指定的方法,返回对应的函数表达式节点 10 | * @param root 根节点 11 | * @param name 方法名 12 | * @return {FunctionExpression} 13 | */ 14 | function findMethod(root, name) { 15 | return root.find(j.ObjectProperty, node => { 16 | return j.StringLiteral.check(node.key) && node.key.value === name && j.FunctionExpression.check(node.value) 17 | }).map(path => path.get('value')) 18 | } 19 | 20 | // 删除 eventProtect 方法体 21 | function deleteEventProtectMethodBody(root) { 22 | findMethod(root, 'eventProtect').forEach(path => { 23 | path.get('body').replace(j.blockStatement([])) 24 | }) 25 | } 26 | 27 | function fixButtonCode(root, btn) { 28 | findMethod(root, btn).forEach(path => { 29 | const statements = path.get('body', 'body').value 30 | for (const statement of statements) { 31 | if (j.ExpressionStatement.check(statement)) { 32 | const expression = statement.expression 33 | if (j.LogicalExpression.check(expression) && j.CallExpression.check(expression.right)) { 34 | statement.expression = expression.right 35 | } 36 | } 37 | } 38 | }) 39 | } 40 | 41 | function handle(root) { 42 | deleteEventProtectMethodBody(root) 43 | fixButtonCode(root, 'handleClickNextChapterButton') 44 | fixButtonCode(root, 'handleClickPrevChapterButton') 45 | fixButtonCode(root, 'handleClickPrevSectionButton') 46 | fixButtonCode(root, 'handleClickNextSectionButton') 47 | } 48 | 49 | export function transform(input) { 50 | const root = j(input) 51 | handle(root) 52 | return root.toSource() 53 | } 54 | -------------------------------------------------------------------------------- /src/assets/ebook-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/src/assets/ebook-disabled.png -------------------------------------------------------------------------------- /src/assets/ebook-enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/src/assets/ebook-enabled.png -------------------------------------------------------------------------------- /src/assets/popup.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | document.querySelector('#start').addEventListener('click', async (evt) => { 3 | evt.preventDefault() 4 | 5 | const formData = new FormData(document.querySelector('form')) 6 | const direction = formData.get('direction') 7 | let interval = parseInt(formData.get('interval').toString()) 8 | if (Number.isNaN(interval)) { 9 | interval = 5 10 | } 11 | const isScroll = formData.get('isScroll') === 'on' 12 | 13 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 14 | chrome.tabs.sendMessage(tabs[0].id, { 15 | type: "start", 16 | args: { 17 | direction: direction, 18 | interval: interval, 19 | isScroll: isScroll, 20 | }, 21 | }) 22 | }); 23 | }) 24 | document.querySelector('#stop').addEventListener('click', (evt) => { 25 | evt.preventDefault() 26 | 27 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 28 | chrome.tabs.sendMessage(tabs[0].id, { 29 | type: "stop", 30 | }) 31 | }); 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(async () => { 2 | await chrome.action.disable(); 3 | 4 | chrome.declarativeContent.onPageChanged.removeRules(undefined, () => { 5 | // 文档: https://developer.chrome.com/docs/extensions/reference/api/declarativeContent 6 | const readerRule = { 7 | conditions: [ 8 | // 微信读书 9 | new chrome.declarativeContent.PageStateMatcher({ 10 | pageUrl: { 11 | urlPrefix: 'https://weread.qq.com/web/reader/' 12 | }, 13 | }), 14 | // 知乎书店 15 | new chrome.declarativeContent.PageStateMatcher({ 16 | pageUrl: { 17 | urlPrefix: 'https://www.zhihu.com/pub/reader/' 18 | }, 19 | }) 20 | ], 21 | actions: [ 22 | // 启用插件 23 | new chrome.declarativeContent.ShowAction(), 24 | ], 25 | }; 26 | 27 | const rules = [readerRule]; 28 | chrome.declarativeContent.onPageChanged.addRules(rules); 29 | }); 30 | }); 31 | 32 | chrome.tabs.onUpdated.addListener(async (tabId) => { 33 | const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); 34 | if (tabId === tab?.id) { 35 | await handleIconChange(tab); 36 | } 37 | }); 38 | 39 | chrome.tabs.onActivated.addListener(async () => { 40 | const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); 41 | await handleIconChange(tab); 42 | }); 43 | 44 | chrome.runtime.onMessage.addListener(async (data) => { 45 | if (data.msg === 'updateIcon') { 46 | const [tab] = await chrome.tabs.query({ currentWindow: true, active: true }); 47 | await handleIconChange(tab); 48 | return true; 49 | } 50 | }); 51 | 52 | // 更换插件图标 53 | async function handleIconChange(tab) { 54 | const {url} = tab 55 | 56 | let iconPath = '' 57 | if (url && ( 58 | url.startsWith('https://weread.qq.com/web/reader/') || 59 | url.startsWith('https://www.zhihu.com/pub/reader/') 60 | )) { 61 | iconPath = 'assets/ebook-enabled.png' 62 | } else { 63 | iconPath = 'assets/ebook-disabled.png' 64 | } 65 | await chrome.action.setIcon({ 66 | tabId: tab.id, 67 | path: { 68 | 96: iconPath, 69 | }, 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /src/common/inject.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | // 注入自定义的 xhr 和 fetch,实现接口数据的拦截 3 | const script = document.createElement('script'); 4 | script.src = chrome.runtime.getURL('common/xhr-fetch.js'); 5 | script.onload = function () { 6 | this.remove(); 7 | }; 8 | ;(document.head || document.documentElement).appendChild(script); 9 | })(); 10 | -------------------------------------------------------------------------------- /src/common/utils.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | /** 3 | * 下载文件 4 | * @param {File} file 文件对象 5 | */ 6 | function downloadFile(file) { 7 | const tmpLink = document.createElement("a"); 8 | const objectUrl = URL.createObjectURL(file); 9 | 10 | tmpLink.href = objectUrl; 11 | tmpLink.download = file.name; 12 | document.body.appendChild(tmpLink); 13 | tmpLink.click(); 14 | 15 | document.body.removeChild(tmpLink); 16 | URL.revokeObjectURL(objectUrl); 17 | } 18 | 19 | function log(msg, ...args) { 20 | if (typeof msg === 'string') { 21 | console.debug(`[wrx]: ${msg}`, ...args) 22 | } else { 23 | console.debug('[wrx]:', msg, ...args) 24 | } 25 | } 26 | 27 | window.wrx_common_utils = { 28 | downloadFile: downloadFile, 29 | log: log, 30 | } 31 | })() 32 | -------------------------------------------------------------------------------- /src/common/xhr-fetch.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | // 需要拦截的api 3 | const INTERCEPT_APIS = [ 4 | // 微信读书相关接口 5 | 'https://weread.qq.com/web/book/chapter/e_0', 6 | 'https://weread.qq.com/web/book/chapter/e_1', 7 | 'https://weread.qq.com/web/book/chapter/e_2', 8 | 'https://weread.qq.com/web/book/chapter/e_3', 9 | 'https://weread.qq.com/web/book/chapter/t_0', 10 | 'https://weread.qq.com/web/book/chapter/t_1', 11 | 'https://weread.qq.com/web/book/publicinfos', // 详情数据(未登录 POST) 12 | 'https://weread.qq.com/web/book/publicchapterInfos', // 章节数据(未登录 POST) 13 | 'https://weread.qq.com/web/book/info', // 详情数据(已登录 GET ?bookId=xx) 14 | 'https://weread.qq.com/web/book/chapterInfos', // 章节数据(已登录 POST) 15 | 16 | // 知乎书店相关接口 17 | /^https:\/\/www\.zhihu\.com\/api\/v3\/books\/[a-z0-9]+\/chapters$/i, // 目录数据 18 | /^https:\/\/www\.zhihu\.com\/api\/v3\/books\/[a-z0-9]+$/i, // 详情数据 19 | /^https:\/\/www\.zhihu\.com\/api\/v3\/books\/[a-z0-9]+\/chapters\/[a-z0-9]+\/download$/i, //章节数据 20 | ] 21 | 22 | // send to content script 23 | function emit(url, request, response) { 24 | // if a big media file, can createObjectURL before send to a content script 25 | //window.postMessage({ response: URL.createObjectURL(response) }, '*'); 26 | window.postMessage({ 27 | from: 'wrx', 28 | site: url.origin, 29 | api: url.pathname, 30 | pathname: url.pathname, 31 | search: url.search, 32 | url: url.href, 33 | request: request, 34 | response: response, 35 | }, '*'); 36 | } 37 | 38 | // 拦截 xhr 的请求与响应 39 | ;(function (XMLHttpRequest) { 40 | const XHR = XMLHttpRequest.prototype; 41 | 42 | const open = XHR.open; 43 | const send = XHR.send; 44 | 45 | XHR.open = function (method, url) { 46 | this._url = url; 47 | return open.apply(this, arguments); 48 | }; 49 | 50 | XHR.send = function (postData) { 51 | this.addEventListener('load', () => { 52 | const url = new URL(this._url, window.location.origin) 53 | const endpoint = url.origin + url.pathname 54 | 55 | // 判断是否命中拦截规则 56 | const hitRule = INTERCEPT_APIS.some(rule => { 57 | if (typeof rule === 'string') { 58 | return rule === endpoint 59 | } else if (rule instanceof RegExp) { 60 | return rule.test(endpoint) 61 | } else { 62 | return false 63 | } 64 | }) 65 | if (hitRule) { 66 | emit(url, postData, this.response) 67 | } 68 | }, {once: true}); 69 | return send.apply(this, arguments); 70 | }; 71 | })(XMLHttpRequest); 72 | 73 | // 重写 fetch,暂时不需要 74 | const {fetch: origFetch} = window; 75 | window.fetch = async (...args) => { 76 | const response = await origFetch(...args); 77 | response 78 | .clone() 79 | .text() // maybe json(), text(), blob(),因为通常电子书接口数据都是文本格式,所以我们用text() 80 | .then(data => { 81 | let _url 82 | let request 83 | const [resource, options] = args 84 | if (resource instanceof Request) { 85 | _url = resource.url 86 | request = options || resource 87 | } else { 88 | _url = resource.toString() 89 | request = options 90 | } 91 | const url = new URL(_url, window.location.origin) 92 | const endpoint = url.origin + url.pathname 93 | 94 | // 判断是否命中拦截规则 95 | const hitRule = INTERCEPT_APIS.some(rule => { 96 | if (typeof rule === 'string') { 97 | return rule === endpoint 98 | } else if (rule instanceof RegExp) { 99 | return rule.test(endpoint) 100 | } else { 101 | return false 102 | } 103 | }) 104 | if (hitRule) { 105 | emit(url, request, data) 106 | } 107 | }) 108 | .catch(err => console.error(err)); 109 | return response; 110 | }; 111 | })() 112 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "魏如雪", 4 | "description": "电子书采集工具,目前仅支持微信读书", 5 | "version": "0.0.8.6", 6 | "permissions": [ 7 | "declarativeNetRequest", 8 | "declarativeNetRequestFeedback", 9 | "declarativeContent" 10 | ], 11 | "host_permissions": [ 12 | "https://*.weread.qq.com/*", 13 | "https://*.file.myqcloud.com/*", 14 | "https://www.zhihu.com/*" 15 | ], 16 | "declarative_net_request": { 17 | "rule_resources": [ 18 | { 19 | "id": "ruleset_weread", 20 | "enabled": true, 21 | "path": "rules/weread.json" 22 | } 23 | ] 24 | }, 25 | "content_scripts": [ 26 | { 27 | "matches": [ 28 | "https://*.weread.qq.com/web/reader/*" 29 | ], 30 | "js": [ 31 | "common/inject.js", 32 | "common/utils.js", 33 | "sites/weread/crypto-js@4.2.0.min.js", 34 | "sites/weread/utils.js", 35 | "sites/weread/store.js", 36 | "sites/weread/content.js", 37 | "sites/weread/auto.js" 38 | ], 39 | "css": [ 40 | "sites/weread/toc.css" 41 | ], 42 | "run_at": "document_start", 43 | "world": "ISOLATED", 44 | "all_frames": false 45 | }, 46 | { 47 | "matches": [ 48 | "https://www.zhihu.com/pub/reader/*" 49 | ], 50 | "js": [ 51 | "common/inject.js", 52 | "common/utils.js", 53 | "sites/zhihu/store.js", 54 | "sites/zhihu/content.js", 55 | "sites/zhihu/auto.js" 56 | ], 57 | "css": [ 58 | "sites/zhihu/toc.css" 59 | ], 60 | "run_at": "document_start", 61 | "world": "ISOLATED", 62 | "all_frames": false 63 | } 64 | ], 65 | "web_accessible_resources": [ 66 | { 67 | "matches": [ 68 | "" 69 | ], 70 | "resources": [ 71 | "common/xhr-fetch.js" 72 | ] 73 | }, 74 | { 75 | "matches": [ 76 | "https://*.weread.qq.com/*" 77 | ], 78 | "resources": [ 79 | "/overrides/weread/8.09d8684c.js", 80 | "/overrides/weread/8.126c7638.js", 81 | "/overrides/weread/8.1b1a58af.js", 82 | "/overrides/weread/8.2b33baa9.js", 83 | "/overrides/weread/8.6727fad4.js", 84 | "/overrides/weread/8.82196ea9.js", 85 | "/overrides/weread/8.a0b49d48.js", 86 | "/overrides/weread/8.c1484fdd.js", 87 | "/overrides/weread/8.e62d5026.js", 88 | "/overrides/weread/9.16e91701.js", 89 | "/overrides/weread/9.58102893.js" 90 | ] 91 | } 92 | ], 93 | "action": { 94 | "default_popup": "popup.html", 95 | "default_icon": "assets/ebook-disabled.png" 96 | }, 97 | "background": { 98 | "service_worker": "background.js" 99 | } 100 | } -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 自动翻页 7 | 8 | 9 | 23 | 24 | 25 |

自动翻页

26 |
27 |
28 |
29 | 翻页方式: 30 |
31 | 32 | 35 |
36 |
37 | 38 | 41 |
42 |
43 | 44 | 47 |
48 |
49 |
50 | 间隔: 51 |
52 | 53 | 54 |
55 |
56 | 57 |
58 | 59 | 60 |
61 |
62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /src/rules/weread.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "priority": 1, 5 | "condition": { 6 | "urlFilter": "8.09d8684c.js" 7 | }, 8 | "action": { 9 | "type": "redirect", 10 | "redirect": { 11 | "extensionPath": "/overrides/weread/8.09d8684c.js" 12 | } 13 | } 14 | }, 15 | { 16 | "id": 2, 17 | "priority": 1, 18 | "condition": { 19 | "urlFilter": "8.126c7638.js" 20 | }, 21 | "action": { 22 | "type": "redirect", 23 | "redirect": { 24 | "extensionPath": "/overrides/weread/8.126c7638.js" 25 | } 26 | } 27 | }, 28 | { 29 | "id": 3, 30 | "priority": 1, 31 | "condition": { 32 | "urlFilter": "8.1b1a58af.js" 33 | }, 34 | "action": { 35 | "type": "redirect", 36 | "redirect": { 37 | "extensionPath": "/overrides/weread/8.1b1a58af.js" 38 | } 39 | } 40 | }, 41 | { 42 | "id": 4, 43 | "priority": 1, 44 | "condition": { 45 | "urlFilter": "8.2b33baa9.js" 46 | }, 47 | "action": { 48 | "type": "redirect", 49 | "redirect": { 50 | "extensionPath": "/overrides/weread/8.2b33baa9.js" 51 | } 52 | } 53 | }, 54 | { 55 | "id": 5, 56 | "priority": 1, 57 | "condition": { 58 | "urlFilter": "8.6727fad4.js" 59 | }, 60 | "action": { 61 | "type": "redirect", 62 | "redirect": { 63 | "extensionPath": "/overrides/weread/8.6727fad4.js" 64 | } 65 | } 66 | }, 67 | { 68 | "id": 6, 69 | "priority": 1, 70 | "condition": { 71 | "urlFilter": "8.82196ea9.js" 72 | }, 73 | "action": { 74 | "type": "redirect", 75 | "redirect": { 76 | "extensionPath": "/overrides/weread/8.82196ea9.js" 77 | } 78 | } 79 | }, 80 | { 81 | "id": 7, 82 | "priority": 1, 83 | "condition": { 84 | "urlFilter": "8.a0b49d48.js" 85 | }, 86 | "action": { 87 | "type": "redirect", 88 | "redirect": { 89 | "extensionPath": "/overrides/weread/8.a0b49d48.js" 90 | } 91 | } 92 | }, 93 | { 94 | "id": 8, 95 | "priority": 1, 96 | "condition": { 97 | "urlFilter": "8.c1484fdd.js" 98 | }, 99 | "action": { 100 | "type": "redirect", 101 | "redirect": { 102 | "extensionPath": "/overrides/weread/8.c1484fdd.js" 103 | } 104 | } 105 | }, 106 | { 107 | "id": 9, 108 | "priority": 1, 109 | "condition": { 110 | "urlFilter": "8.e62d5026.js" 111 | }, 112 | "action": { 113 | "type": "redirect", 114 | "redirect": { 115 | "extensionPath": "/overrides/weread/8.e62d5026.js" 116 | } 117 | } 118 | }, 119 | { 120 | "id": 10, 121 | "priority": 1, 122 | "condition": { 123 | "urlFilter": "9.16e91701.js" 124 | }, 125 | "action": { 126 | "type": "redirect", 127 | "redirect": { 128 | "extensionPath": "/overrides/weread/9.16e91701.js" 129 | } 130 | } 131 | }, 132 | { 133 | "id": 11, 134 | "priority": 1, 135 | "condition": { 136 | "urlFilter": "9.58102893.js" 137 | }, 138 | "action": { 139 | "type": "redirect", 140 | "redirect": { 141 | "extensionPath": "/overrides/weread/9.58102893.js" 142 | } 143 | } 144 | } 145 | ] -------------------------------------------------------------------------------- /src/sites/weread/auto.js: -------------------------------------------------------------------------------- 1 | function randomInteger(min, max) { 2 | const rand = min + Math.random() * (max + 1 - min); 3 | return Math.floor(rand); 4 | } 5 | function sleep(duration) { 6 | return new Promise(resolve => setTimeout(resolve, duration)) 7 | } 8 | 9 | let wantStop = false 10 | let isRunning = false 11 | let retry = 0 12 | 13 | const selectorMap = { 14 | prev: 'button.readerHeaderButton', 15 | next: 'button.readerFooter_button:not(.readerFooter_button_twoLines)', 16 | } 17 | 18 | 19 | // 在页面添加一个按钮 20 | document.addEventListener('DOMContentLoaded', async () => { 21 | const span = document.createElement('span') 22 | span.textContent = '自动阅读: 关' 23 | span.id = '__wrx_auto_read__' 24 | span.style.position = 'fixed' 25 | span.style.top = '20px' 26 | span.style.right = '20px' 27 | span.style.fontSize = '16px' 28 | span.style.fontWeight = 'bold' 29 | span.style.background = '#e9e9e99c' 30 | span.style.color = '#a32727' 31 | span.style.padding = '.5em .75em' 32 | span.style.borderRadius = '5px' 33 | span.style.zIndex = '9999' 34 | document.body.appendChild(span) 35 | 36 | const storageUsage = await navigator.storage.estimate() 37 | const indexedSize = (storageUsage.usage / 1024 / 1024).toFixed(2) 38 | window.wrx_common_utils.log(`IndexedDB 使用了 %c~${indexedSize}M%c 存储空间`, 'color: red;font-size:16px;', '') 39 | }) 40 | 41 | function setAutoRunningStatus(open) { 42 | const span = document.querySelector('#__wrx_auto_read__') 43 | span.textContent = `自动阅读: ${open ? '开' : '关'}` 44 | if (open) { 45 | span.style.background = 'green' 46 | span.style.color = 'white' 47 | } else { 48 | span.style.background = '#e9e9e99c' 49 | span.style.color = '#a32727' 50 | } 51 | } 52 | 53 | function checkStop() { 54 | if (wantStop) { 55 | isRunning = false 56 | setAutoRunningStatus(isRunning) 57 | return true 58 | } 59 | return false 60 | } 61 | 62 | async function run(direction, interval, isScroll) { 63 | if (checkStop()) return 64 | 65 | isRunning = true 66 | setAutoRunningStatus(isRunning) 67 | 68 | let btn = document.querySelector(selectorMap[direction]) 69 | if (!btn) { 70 | // 按钮不存在,可能还没有加载出来 71 | if (!document.querySelector('.renderTargetContainer')) { 72 | if (retry <= 6) { 73 | retry++ 74 | window.wrx_common_utils.log('按钮没有出现,5秒后重试') 75 | setTimeout(() => { 76 | run(direction, interval, isScroll) 77 | }, 5000) 78 | } else { 79 | window.wrx_common_utils.log('未找到按钮,超时结束') 80 | isRunning = false 81 | setAutoRunningStatus(isRunning) 82 | } 83 | } else { 84 | window.wrx_common_utils.log(`已经翻到${direction === 'prev' ? '第' : '最后'}一页了,正常结束`) 85 | isRunning = false 86 | setAutoRunningStatus(isRunning) 87 | } 88 | return 89 | } 90 | 91 | // 按钮出现,重置重试次数 92 | retry = 0 93 | if (isScroll) { 94 | await sleep(1000) 95 | if (checkStop()) return 96 | 97 | if (direction === 'prev') { 98 | window.scroll({top: 0, behavior: 'smooth'}) 99 | } else { 100 | window.scroll({top: 100000, behavior: 'smooth'}) 101 | } 102 | 103 | await sleep(1500) 104 | if (checkStop()) return 105 | } 106 | window.wrx_common_utils.log('开始翻页') 107 | btn.click() 108 | 109 | if (checkStop()) return 110 | const adjustInterval = randomInteger(interval * 1000, (interval + 1) * 1000 + 500) 111 | setTimeout(() => { 112 | run(direction, interval, isScroll) 113 | }, adjustInterval) 114 | window.wrx_common_utils.log(`${adjustInterval}ms 后执行`) 115 | } 116 | 117 | chrome.runtime.onMessage.addListener(async (msg) => { 118 | window.wrx_common_utils.log(msg) 119 | const {type, args = {}} = msg 120 | const {direction, interval, isScroll} = args 121 | 122 | switch (type) { 123 | case 'start': 124 | if (isRunning) { 125 | // 避免重复执行 126 | window.wrx_common_utils.log('正在运行中,不需要重复执行') 127 | return 128 | } 129 | wantStop = false 130 | await run(direction, interval, isScroll) 131 | break 132 | case 'stop': 133 | wantStop = true 134 | setAutoRunningStatus(false) 135 | break 136 | } 137 | }) 138 | -------------------------------------------------------------------------------- /src/sites/weread/content.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | // 在页面添加一个导出按钮 3 | document.addEventListener('DOMContentLoaded', async () => { 4 | const parts = window.location.pathname.split('/') 5 | // 加密的 bookId 6 | const bid = parts[parts.length - 1].split('k')[0] 7 | 8 | const btn = document.createElement('button') 9 | btn.textContent = '导出本书数据' 10 | btn.id = '__wrx_export__' 11 | btn.style.position = 'fixed' 12 | btn.style.top = '80px' 13 | btn.style.right = '20px' 14 | btn.style.fontSize = '16px' 15 | btn.style.fontWeight = 'bold' 16 | btn.style.background = '#e9e9e99c' 17 | btn.style.color = '#a32727' 18 | btn.style.padding = '.5em .75em' 19 | btn.style.borderRadius = '5px' 20 | btn.style.zIndex = '9999' 21 | btn.addEventListener('click', async () => { 22 | try { 23 | await window.wrx_weread_store.exportBookData(bid) 24 | } catch (e) { 25 | alert(e.message) 26 | console.warn(e) 27 | } 28 | }) 29 | document.body.appendChild(btn) 30 | 31 | await window.wrx_weread_store.initBookState(bid) 32 | }) 33 | 34 | // 接收 xhr 拦截的接口数据 35 | window.addEventListener('message', async ({data}) => { 36 | if (data && data.from === 'wrx') { 37 | switch (data.site) { 38 | case 'https://weread.qq.com': 39 | await handleWereadSite(data) 40 | break 41 | default: 42 | console.warn('未知网站数据: ', data) 43 | break 44 | } 45 | } 46 | }); 47 | 48 | // 处理微信读书数据 49 | async function handleWereadSite(data) { 50 | let bookId 51 | 52 | const {pathname, search, request, response} = data 53 | switch (pathname) { 54 | case '/web/book/chapter/e_0': 55 | case '/web/book/chapter/e_1': 56 | case '/web/book/chapter/e_2': 57 | case '/web/book/chapter/e_3': 58 | case '/web/book/chapter/t_0': 59 | case '/web/book/chapter/t_1': 60 | // 保存 chapter 数据 61 | const {b: bid, c: cid} = JSON.parse(request) 62 | await window.wrx_weread_store.storeBookChapter(bid, cid, pathname, response) 63 | window.wrx_weread_store.markChapterDownloaded(cid) 64 | break 65 | case '/web/book/publicinfos': 66 | bookId = JSON.parse(request).bookIds[0] 67 | await window.wrx_weread_store.storeBookDetail(bookId, JSON.parse(response)) 68 | break 69 | case '/web/book/info': 70 | bookId = new URLSearchParams(search).get('bookId') 71 | await window.wrx_weread_store.storeBookDetail(bookId, JSON.parse(response)) 72 | break 73 | case '/web/book/publicchapterInfos': 74 | case '/web/book/chapterInfos': 75 | bookId = JSON.parse(request).bookIds[0] 76 | await window.wrx_weread_store.storeBookToc(bookId, JSON.parse(response)) 77 | 78 | // 目录数据缓存之后,更新页面上的目录 dom 上方便后续处理 79 | await window.wrx_weread_store.updatePageCatalog(window.wrx_weread_utils.hash(bookId)) 80 | break 81 | } 82 | } 83 | })(); 84 | -------------------------------------------------------------------------------- /src/sites/weread/crypto-js@4.2.0.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports?module.exports=exports=e():"function"==typeof define&&define.amd?define([],e):t.CryptoJS=e()}(this,function(){var W,O,I,U,K,X,L,l,j,T,t,N,q,e,Z,V,G,J,Q,Y,$,t1,e1,r1,i1,o1,n1,s,s1,c1,a1,h1,l1,o,f1,r,d1,u1,n,c,a,h,f,d,i=function(h){var i;if("undefined"!=typeof window&&window.crypto&&(i=window.crypto),"undefined"!=typeof self&&self.crypto&&(i=self.crypto),!(i=!(i=!(i="undefined"!=typeof globalThis&&globalThis.crypto?globalThis.crypto:i)&&"undefined"!=typeof window&&window.msCrypto?window.msCrypto:i)&&"undefined"!=typeof global&&global.crypto?global.crypto:i)&&"function"==typeof require)try{i=require("crypto")}catch(t){}var r=Object.create||function(t){return e.prototype=t,t=new e,e.prototype=null,t};function e(){}var t={},o=t.lib={},n=o.Base={extend:function(t){var e=r(this);return t&&e.mixIn(t),e.hasOwnProperty("init")&&this.init!==e.init||(e.init=function(){e.$super.init.apply(this,arguments)}),(e.init.prototype=e).$super=this,e},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}},l=o.WordArray=n.extend({init:function(t,e){t=this.words=t||[],this.sigBytes=null!=e?e:4*t.length},toString:function(t){return(t||c).stringify(this)},concat:function(t){var e=this.words,r=t.words,i=this.sigBytes,o=t.sigBytes;if(this.clamp(),i%4)for(var n=0;n>>2]>>>24-n%4*8&255;e[i+n>>>2]|=s<<24-(i+n)%4*8}else for(var c=0;c>>2]=r[c>>>2];return this.sigBytes+=o,this},clamp:function(){var t=this.words,e=this.sigBytes;t[e>>>2]&=4294967295<<32-e%4*8,t.length=h.ceil(e/4)},clone:function(){var t=n.clone.call(this);return t.words=this.words.slice(0),t},random:function(t){for(var e=[],r=0;r>>2]>>>24-o%4*8&255;i.push((n>>>4).toString(16)),i.push((15&n).toString(16))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>3]|=parseInt(t.substr(i,2),16)<<24-i%8*4;return new l.init(r,e/2)}},a=s.Latin1={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],o=0;o>>2]>>>24-o%4*8&255;i.push(String.fromCharCode(n))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>2]|=(255&t.charCodeAt(i))<<24-i%4*8;return new l.init(r,e)}},f=s.Utf8={stringify:function(t){try{return decodeURIComponent(escape(a.stringify(t)))}catch(t){throw new Error("Malformed UTF-8 data")}},parse:function(t){return a.parse(unescape(encodeURIComponent(t)))}},d=o.BufferedBlockAlgorithm=n.extend({reset:function(){this._data=new l.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=f.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(t){var e,r=this._data,i=r.words,o=r.sigBytes,n=this.blockSize,s=o/(4*n),c=(s=t?h.ceil(s):h.max((0|s)-this._minBufferSize,0))*n,t=h.min(4*c,o);if(c){for(var a=0;a>>2]|=t[i]<<24-i%4*8;I.call(this,r,e)}else I.apply(this,arguments)}).prototype=p),i),p1=u.lib.WordArray;function _1(t){return t<<8&4278255360|t>>>8&16711935}(u=u.enc).Utf16=u.Utf16BE={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],o=0;o>>2]>>>16-o%4*8&65535;i.push(String.fromCharCode(n))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>1]|=t.charCodeAt(i)<<16-i%2*16;return p1.create(r,2*e)}},u.Utf16LE={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],o=0;o>>2]>>>16-o%4*8&65535);i.push(String.fromCharCode(n))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>1]|=_1(t.charCodeAt(i)<<16-i%2*16);return p1.create(r,2*e)}},U=(p=i).lib.WordArray,p.enc.Base64={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=this._map,o=(t.clamp(),[]),n=0;n>>2]>>>24-n%4*8&255)<<16|(e[n+1>>>2]>>>24-(n+1)%4*8&255)<<8|e[n+2>>>2]>>>24-(n+2)%4*8&255,c=0;c<4&&n+.75*c>>6*(3-c)&63));var a=i.charAt(64);if(a)for(;o.length%4;)o.push(a);return o.join("")},parse:function(t){var e=t.length,r=this._map;if(!(i=this._reverseMap))for(var i=this._reverseMap=[],o=0;o>>6-u%4*2,s=s|n,f[d>>>2]|=s<<24-d%4*8,d++);return U.create(f,d)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="},K=(u=i).lib.WordArray,u.enc.Base64url={stringify:function(t,e){for(var r=t.words,i=t.sigBytes,o=(e=void 0===e?!0:e)?this._safe_map:this._map,n=(t.clamp(),[]),s=0;s>>2]>>>24-s%4*8&255)<<16|(r[s+1>>>2]>>>24-(s+1)%4*8&255)<<8|r[s+2>>>2]>>>24-(s+2)%4*8&255,a=0;a<4&&s+.75*a>>6*(3-a)&63));var h=o.charAt(64);if(h)for(;n.length%4;)n.push(h);return n.join("")},parse:function(t,e){var r=t.length,i=(e=void 0===e?!0:e)?this._safe_map:this._map;if(!(o=this._reverseMap))for(var o=this._reverseMap=[],n=0;n>>6-u%4*2,c=c|s,f[d>>>2]|=c<<24-d%4*8,d++);return K.create(f,d)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",_safe_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"};for(var y1=Math,p=i,g1=(u=p.lib).WordArray,v1=u.Hasher,u=p.algo,A=[],B1=0;B1<64;B1++)A[B1]=4294967296*y1.abs(y1.sin(B1+1))|0;function z(t,e,r,i,o,n,s){t=t+(e&r|~e&i)+o+s;return(t<>>32-n)+e}function H(t,e,r,i,o,n,s){t=t+(e&i|r&~i)+o+s;return(t<>>32-n)+e}function C(t,e,r,i,o,n,s){t=t+(e^r^i)+o+s;return(t<>>32-n)+e}function R(t,e,r,i,o,n,s){t=t+(r^(e|~i))+o+s;return(t<>>32-n)+e}u=u.MD5=v1.extend({_doReset:function(){this._hash=new g1.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(t,e){for(var r=0;r<16;r++){var i=e+r,o=t[i];t[i]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8)}var n=this._hash.words,s=t[e+0],c=t[e+1],a=t[e+2],h=t[e+3],l=t[e+4],f=t[e+5],d=t[e+6],u=t[e+7],p=t[e+8],_=t[e+9],y=t[e+10],g=t[e+11],v=t[e+12],B=t[e+13],w=t[e+14],k=t[e+15],x=z(n[0],S=n[1],m=n[2],b=n[3],s,7,A[0]),b=z(b,x,S,m,c,12,A[1]),m=z(m,b,x,S,a,17,A[2]),S=z(S,m,b,x,h,22,A[3]);x=z(x,S,m,b,l,7,A[4]),b=z(b,x,S,m,f,12,A[5]),m=z(m,b,x,S,d,17,A[6]),S=z(S,m,b,x,u,22,A[7]),x=z(x,S,m,b,p,7,A[8]),b=z(b,x,S,m,_,12,A[9]),m=z(m,b,x,S,y,17,A[10]),S=z(S,m,b,x,g,22,A[11]),x=z(x,S,m,b,v,7,A[12]),b=z(b,x,S,m,B,12,A[13]),m=z(m,b,x,S,w,17,A[14]),x=H(x,S=z(S,m,b,x,k,22,A[15]),m,b,c,5,A[16]),b=H(b,x,S,m,d,9,A[17]),m=H(m,b,x,S,g,14,A[18]),S=H(S,m,b,x,s,20,A[19]),x=H(x,S,m,b,f,5,A[20]),b=H(b,x,S,m,y,9,A[21]),m=H(m,b,x,S,k,14,A[22]),S=H(S,m,b,x,l,20,A[23]),x=H(x,S,m,b,_,5,A[24]),b=H(b,x,S,m,w,9,A[25]),m=H(m,b,x,S,h,14,A[26]),S=H(S,m,b,x,p,20,A[27]),x=H(x,S,m,b,B,5,A[28]),b=H(b,x,S,m,a,9,A[29]),m=H(m,b,x,S,u,14,A[30]),x=C(x,S=H(S,m,b,x,v,20,A[31]),m,b,f,4,A[32]),b=C(b,x,S,m,p,11,A[33]),m=C(m,b,x,S,g,16,A[34]),S=C(S,m,b,x,w,23,A[35]),x=C(x,S,m,b,c,4,A[36]),b=C(b,x,S,m,l,11,A[37]),m=C(m,b,x,S,u,16,A[38]),S=C(S,m,b,x,y,23,A[39]),x=C(x,S,m,b,B,4,A[40]),b=C(b,x,S,m,s,11,A[41]),m=C(m,b,x,S,h,16,A[42]),S=C(S,m,b,x,d,23,A[43]),x=C(x,S,m,b,_,4,A[44]),b=C(b,x,S,m,v,11,A[45]),m=C(m,b,x,S,k,16,A[46]),x=R(x,S=C(S,m,b,x,a,23,A[47]),m,b,s,6,A[48]),b=R(b,x,S,m,u,10,A[49]),m=R(m,b,x,S,w,15,A[50]),S=R(S,m,b,x,f,21,A[51]),x=R(x,S,m,b,v,6,A[52]),b=R(b,x,S,m,h,10,A[53]),m=R(m,b,x,S,y,15,A[54]),S=R(S,m,b,x,c,21,A[55]),x=R(x,S,m,b,p,6,A[56]),b=R(b,x,S,m,k,10,A[57]),m=R(m,b,x,S,d,15,A[58]),S=R(S,m,b,x,B,21,A[59]),x=R(x,S,m,b,l,6,A[60]),b=R(b,x,S,m,g,10,A[61]),m=R(m,b,x,S,a,15,A[62]),S=R(S,m,b,x,_,21,A[63]),n[0]=n[0]+x|0,n[1]=n[1]+S|0,n[2]=n[2]+m|0,n[3]=n[3]+b|0},_doFinalize:function(){for(var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes,o=(e[i>>>5]|=128<<24-i%32,y1.floor(r/4294967296)),o=(e[15+(64+i>>>9<<4)]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),e[14+(64+i>>>9<<4)]=16711935&(r<<8|r>>>24)|4278255360&(r<<24|r>>>8),t.sigBytes=4*(e.length+1),this._process(),this._hash),n=o.words,s=0;s<4;s++){var c=n[s];n[s]=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8)}return o},clone:function(){var t=v1.clone.call(this);return t._hash=this._hash.clone(),t}}),p.MD5=v1._createHelper(u),p.HmacMD5=v1._createHmacHelper(u),u=(p=i).lib,X=u.WordArray,L=u.Hasher,u=p.algo,l=[],u=u.SHA1=L.extend({_doReset:function(){this._hash=new X.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],o=r[1],n=r[2],s=r[3],c=r[4],a=0;a<80;a++){a<16?l[a]=0|t[e+a]:(h=l[a-3]^l[a-8]^l[a-14]^l[a-16],l[a]=h<<1|h>>>31);var h=(i<<5|i>>>27)+c+l[a];h+=a<20?1518500249+(o&n|~o&s):a<40?1859775393+(o^n^s):a<60?(o&n|o&s|n&s)-1894007588:(o^n^s)-899497514,c=s,s=n,n=o<<30|o>>>2,o=i,i=h}r[0]=r[0]+i|0,r[1]=r[1]+o|0,r[2]=r[2]+n|0,r[3]=r[3]+s|0,r[4]=r[4]+c|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=Math.floor(r/4294967296),e[15+(64+i>>>9<<4)]=r,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=L.clone.call(this);return t._hash=this._hash.clone(),t}}),p.SHA1=L._createHelper(u),p.HmacSHA1=L._createHmacHelper(u);var w1=Math,p=i,k1=(u=p.lib).WordArray,x1=u.Hasher,u=p.algo,b1=[],m1=[];function S1(t){return 4294967296*(t-(0|t))|0}for(var A1=2,z1=0;z1<64;)!function(t){for(var e=w1.sqrt(t),r=2;r<=e;r++)if(!(t%r))return;return 1}(A1)||(z1<8&&(b1[z1]=S1(w1.pow(A1,.5))),m1[z1]=S1(w1.pow(A1,1/3)),z1++),A1++;var _=[],u=u.SHA256=x1.extend({_doReset:function(){this._hash=new k1.init(b1.slice(0))},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],o=r[1],n=r[2],s=r[3],c=r[4],a=r[5],h=r[6],l=r[7],f=0;f<64;f++){f<16?_[f]=0|t[e+f]:(d=_[f-15],u=_[f-2],_[f]=((d<<25|d>>>7)^(d<<14|d>>>18)^d>>>3)+_[f-7]+((u<<15|u>>>17)^(u<<13|u>>>19)^u>>>10)+_[f-16]);var d=i&o^i&n^o&n,u=l+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&a^~c&h)+m1[f]+_[f],l=h,h=a,a=c,c=s+u|0,s=n,n=o,o=i,i=u+(((i<<30|i>>>2)^(i<<19|i>>>13)^(i<<10|i>>>22))+d)|0}r[0]=r[0]+i|0,r[1]=r[1]+o|0,r[2]=r[2]+n|0,r[3]=r[3]+s|0,r[4]=r[4]+c|0,r[5]=r[5]+a|0,r[6]=r[6]+h|0,r[7]=r[7]+l|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=w1.floor(r/4294967296),e[15+(64+i>>>9<<4)]=r,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=x1.clone.call(this);return t._hash=this._hash.clone(),t}}),p=(p.SHA256=x1._createHelper(u),p.HmacSHA256=x1._createHmacHelper(u),j=(p=i).lib.WordArray,u=p.algo,T=u.SHA256,u=u.SHA224=T.extend({_doReset:function(){this._hash=new j.init([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428])},_doFinalize:function(){var t=T._doFinalize.call(this);return t.sigBytes-=4,t}}),p.SHA224=T._createHelper(u),p.HmacSHA224=T._createHmacHelper(u),i),H1=p.lib.Hasher,y=(u=p.x64).Word,C1=u.WordArray,u=p.algo;function g(){return y.create.apply(y,arguments)}for(var R1=[g(1116352408,3609767458),g(1899447441,602891725),g(3049323471,3964484399),g(3921009573,2173295548),g(961987163,4081628472),g(1508970993,3053834265),g(2453635748,2937671579),g(2870763221,3664609560),g(3624381080,2734883394),g(310598401,1164996542),g(607225278,1323610764),g(1426881987,3590304994),g(1925078388,4068182383),g(2162078206,991336113),g(2614888103,633803317),g(3248222580,3479774868),g(3835390401,2666613458),g(4022224774,944711139),g(264347078,2341262773),g(604807628,2007800933),g(770255983,1495990901),g(1249150122,1856431235),g(1555081692,3175218132),g(1996064986,2198950837),g(2554220882,3999719339),g(2821834349,766784016),g(2952996808,2566594879),g(3210313671,3203337956),g(3336571891,1034457026),g(3584528711,2466948901),g(113926993,3758326383),g(338241895,168717936),g(666307205,1188179964),g(773529912,1546045734),g(1294757372,1522805485),g(1396182291,2643833823),g(1695183700,2343527390),g(1986661051,1014477480),g(2177026350,1206759142),g(2456956037,344077627),g(2730485921,1290863460),g(2820302411,3158454273),g(3259730800,3505952657),g(3345764771,106217008),g(3516065817,3606008344),g(3600352804,1432725776),g(4094571909,1467031594),g(275423344,851169720),g(430227734,3100823752),g(506948616,1363258195),g(659060556,3750685593),g(883997877,3785050280),g(958139571,3318307427),g(1322822218,3812723403),g(1537002063,2003034995),g(1747873779,3602036899),g(1955562222,1575990012),g(2024104815,1125592928),g(2227730452,2716904306),g(2361852424,442776044),g(2428436474,593698344),g(2756734187,3733110249),g(3204031479,2999351573),g(3329325298,3815920427),g(3391569614,3928383900),g(3515267271,566280711),g(3940187606,3454069534),g(4118630271,4000239992),g(116418474,1914138554),g(174292421,2731055270),g(289380356,3203993006),g(460393269,320620315),g(685471733,587496836),g(852142971,1086792851),g(1017036298,365543100),g(1126000580,2618297676),g(1288033470,3409855158),g(1501505948,4234509866),g(1607167915,987167468),g(1816402316,1246189591)],D1=[],E1=0;E1<80;E1++)D1[E1]=g();u=u.SHA512=H1.extend({_doReset:function(){this._hash=new C1.init([new y.init(1779033703,4089235720),new y.init(3144134277,2227873595),new y.init(1013904242,4271175723),new y.init(2773480762,1595750129),new y.init(1359893119,2917565137),new y.init(2600822924,725511199),new y.init(528734635,4215389547),new y.init(1541459225,327033209)])},_doProcessBlock:function(W,O){for(var t=this._hash.words,e=t[0],r=t[1],i=t[2],o=t[3],n=t[4],s=t[5],c=t[6],t=t[7],I=e.high,a=e.low,U=r.high,h=r.low,K=i.high,l=i.low,X=o.high,f=o.low,L=n.high,d=n.low,j=s.high,u=s.low,T=c.high,p=c.low,N=t.high,_=t.low,y=I,g=a,v=U,B=h,w=K,k=l,q=X,x=f,b=L,m=d,Z=j,S=u,V=T,G=p,J=N,Q=_,A=0;A<80;A++)var z,H,C=D1[A],R=(A<16?(H=C.high=0|W[O+2*A],z=C.low=0|W[O+2*A+1]):(F=(P=D1[A-15]).high,P=P.low,M=(E=D1[A-2]).high,E=E.low,D=(R=D1[A-7]).high,R=R.low,$=(Y=D1[A-16]).high,H=(H=((F>>>1|P<<31)^(F>>>8|P<<24)^F>>>7)+D+((z=(D=(P>>>1|F<<31)^(P>>>8|F<<24)^(P>>>7|F<<25))+R)>>>0>>0?1:0))+((M>>>19|E<<13)^(M<<3|E>>>29)^M>>>6)+((z+=P=(E>>>19|M<<13)^(E<<3|M>>>29)^(E>>>6|M<<26))>>>0

>>0?1:0),z+=F=Y.low,C.high=H=H+$+(z>>>0>>0?1:0),C.low=z),b&Z^~b&V),D=m&S^~m&G,E=y&v^y&w^v&w,M=(g>>>28|y<<4)^(g<<30|y>>>2)^(g<<25|y>>>7),P=R1[A],Y=P.high,$=P.low,F=Q+((m>>>14|b<<18)^(m>>>18|b<<14)^(m<<23|b>>>9)),C=J+((b>>>14|m<<18)^(b>>>18|m<<14)^(b<<23|m>>>9))+(F>>>0>>0?1:0),t1=M+(g&B^g&k^B&k),J=V,Q=G,V=Z,G=S,Z=b,S=m,b=q+(C=C+R+((F=F+D)>>>0>>0?1:0)+Y+((F=F+$)>>>0<$>>>0?1:0)+H+((F=F+z)>>>0>>0?1:0))+((m=x+F|0)>>>0>>0?1:0)|0,q=w,x=k,w=v,k=B,v=y,B=g,y=C+(((y>>>28|g<<4)^(y<<30|g>>>2)^(y<<25|g>>>7))+E+(t1>>>0>>0?1:0))+((g=F+t1|0)>>>0>>0?1:0)|0;a=e.low=a+g,e.high=I+y+(a>>>0>>0?1:0),h=r.low=h+B,r.high=U+v+(h>>>0>>0?1:0),l=i.low=l+k,i.high=K+w+(l>>>0>>0?1:0),f=o.low=f+x,o.high=X+q+(f>>>0>>0?1:0),d=n.low=d+m,n.high=L+b+(d>>>0>>0?1:0),u=s.low=u+S,s.high=j+Z+(u>>>0>>0?1:0),p=c.low=p+G,c.high=T+V+(p>>>0>>0?1:0),_=t.low=_+Q,t.high=N+J+(_>>>0>>0?1:0)},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[30+(128+i>>>10<<5)]=Math.floor(r/4294967296),e[31+(128+i>>>10<<5)]=r,t.sigBytes=4*e.length,this._process(),this._hash.toX32()},clone:function(){var t=H1.clone.call(this);return t._hash=this._hash.clone(),t},blockSize:32}),p.SHA512=H1._createHelper(u),p.HmacSHA512=H1._createHmacHelper(u),u=(p=i).x64,t=u.Word,N=u.WordArray,u=p.algo,q=u.SHA512,u=u.SHA384=q.extend({_doReset:function(){this._hash=new N.init([new t.init(3418070365,3238371032),new t.init(1654270250,914150663),new t.init(2438529370,812702999),new t.init(355462360,4144912697),new t.init(1731405415,4290775857),new t.init(2394180231,1750603025),new t.init(3675008525,1694076839),new t.init(1203062813,3204075428)])},_doFinalize:function(){var t=q._doFinalize.call(this);return t.sigBytes-=16,t}}),p.SHA384=q._createHelper(u),p.HmacSHA384=q._createHmacHelper(u);for(var M1=Math,p=i,P1=(u=p.lib).WordArray,F1=u.Hasher,W1=p.x64.Word,u=p.algo,O1=[],I1=[],U1=[],v=1,B=0,K1=0;K1<24;K1++){O1[v+5*B]=(K1+1)*(K1+2)/2%64;var X1=(2*v+3*B)%5;v=B%5,B=X1}for(v=0;v<5;v++)for(B=0;B<5;B++)I1[v+5*B]=B+(2*v+3*B)%5*5;for(var L1=1,j1=0;j1<24;j1++){for(var T1,N1=0,q1=0,Z1=0;Z1<7;Z1++)1&L1&&((T1=(1<>>32-e}function Y1(t){return"string"==typeof t?f1:o}function $1(t,e,r){var i,o=this._iv;o?(i=o,this._iv=void 0):i=this._prevBlock;for(var n=0;n>24&255)?(r=t>>8&255,i=255&t,255===(e=t>>16&255)?(e=0,255===r?(r=0,255===i?i=0:++i):++r):++e,t=0,t=(t+=e<<16)+(r<<8)+i):t+=1<<24,t}u=u.SHA3=F1.extend({cfg:F1.cfg.extend({outputLength:512}),_doReset:function(){for(var t=this._state=[],e=0;e<25;e++)t[e]=new W1.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(t,e){for(var r=this._state,i=this.blockSize/2,o=0;o>>24)|4278255360&(n<<24|n>>>8);(x=r[o]).high^=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),x.low^=n}for(var c=0;c<24;c++){for(var a=0;a<5;a++){for(var h=0,l=0,f=0;f<5;f++)h^=(x=r[a+5*f]).high,l^=x.low;var d=D[a];d.high=h,d.low=l}for(a=0;a<5;a++)for(var u=D[(a+4)%5],p=D[(a+1)%5],_=p.high,p=p.low,h=u.high^(_<<1|p>>>31),l=u.low^(p<<1|_>>>31),f=0;f<5;f++)(x=r[a+5*f]).high^=h,x.low^=l;for(var y=1;y<25;y++){var g=(x=r[y]).high,v=x.low,B=O1[y],g=(l=B<32?(h=g<>>32-B,v<>>32-B):(h=v<>>64-B,g<>>64-B),D[I1[y]]);g.high=h,g.low=l}var w=D[0],k=r[0];w.high=k.high,w.low=k.low;for(a=0;a<5;a++)for(f=0;f<5;f++){var x=r[y=a+5*f],b=D[y],m=D[(a+1)%5+5*f],S=D[(a+2)%5+5*f];x.high=b.high^~m.high&S.high,x.low=b.low^~m.low&S.low}x=r[0],w=U1[c];x.high^=w.high,x.low^=w.low}},_doFinalize:function(){for(var t=this._data,e=t.words,r=(this._nDataBytes,8*t.sigBytes),i=32*this.blockSize,o=(e[r>>>5]|=1<<24-r%32,e[(M1.ceil((1+r)/i)*i>>>5)-1]|=128,t.sigBytes=4*e.length,this._process(),this._state),r=this.cfg.outputLength/8,n=r/8,s=[],c=0;c>>24)|4278255360&(h<<24|h>>>8);s.push(16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8)),s.push(h)}return new P1.init(s,r)},clone:function(){for(var t=F1.clone.call(this),e=t._state=this._state.slice(0),r=0;r<25;r++)e[r]=e[r].clone();return t}}),p.SHA3=F1._createHelper(u),p.HmacSHA3=F1._createHmacHelper(u),Math,u=(p=i).lib,e=u.WordArray,Z=u.Hasher,u=p.algo,V=e.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),G=e.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),J=e.create([11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),Q=e.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),Y=e.create([0,1518500249,1859775393,2400959708,2840853838]),$=e.create([1352829926,1548603684,1836072691,2053994217,0]),u=u.RIPEMD160=Z.extend({_doReset:function(){this._hash=e.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var r=0;r<16;r++){var i=e+r,o=t[i];t[i]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8)}for(var n,s,c,a,h,l,f=this._hash.words,d=Y.words,u=$.words,p=V.words,_=G.words,y=J.words,g=Q.words,v=n=f[0],B=s=f[1],w=c=f[2],k=a=f[3],x=h=f[4],r=0;r<80;r+=1)l=(l=Q1(l=(l=n+t[e+p[r]]|0)+(r<16?(s^c^a)+d[0]:r<32?G1(s,c,a)+d[1]:r<48?((s|~c)^a)+d[2]:r<64?J1(s,c,a)+d[3]:(s^(c|~a))+d[4])|0,y[r]))+h|0,n=h,h=a,a=Q1(c,10),c=s,s=l,l=(l=Q1(l=(l=v+t[e+_[r]]|0)+(r<16?(B^(w|~k))+u[0]:r<32?J1(B,w,k)+u[1]:r<48?((B|~w)^k)+u[2]:r<64?G1(B,w,k)+u[3]:(B^w^k)+u[4])|0,g[r]))+x|0,v=x,x=k,k=Q1(w,10),w=B,B=l;l=f[1]+c+k|0,f[1]=f[2]+a+x|0,f[2]=f[3]+h+v|0,f[3]=f[4]+n+B|0,f[4]=f[0]+s+w|0,f[0]=l},_doFinalize:function(){for(var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes,i=(e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=16711935&(r<<8|r>>>24)|4278255360&(r<<24|r>>>8),t.sigBytes=4*(e.length+1),this._process(),this._hash),o=i.words,n=0;n<5;n++){var s=o[n];o[n]=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8)}return i},clone:function(){var t=Z.clone.call(this);return t._hash=this._hash.clone(),t}}),p.RIPEMD160=Z._createHelper(u),p.HmacRIPEMD160=Z._createHmacHelper(u),u=(p=i).lib.Base,t1=p.enc.Utf8,p.algo.HMAC=u.extend({init:function(t,e){t=this._hasher=new t.init,"string"==typeof e&&(e=t1.parse(e));for(var r=t.blockSize,i=4*r,t=((e=e.sigBytes>i?t.finalize(e):e).clamp(),this._oKey=e.clone()),e=this._iKey=e.clone(),o=t.words,n=e.words,s=0;s>>2];t.sigBytes-=e}},P.BlockCipher=h1.extend({cfg:h1.cfg.extend({mode:r,padding:u}),reset:function(){h1.reset.call(this);var t,e=this.cfg,r=e.iv,e=e.mode;this._xformMode==this._ENC_XFORM_MODE?t=e.createEncryptor:(t=e.createDecryptor,this._minBufferSize=1),this._mode&&this._mode.__creator==t?this._mode.init(this,r&&r.words):(this._mode=t.call(e,this,r&&r.words),this._mode.__creator=t)},_doProcessBlock:function(t,e){this._mode.processBlock(t,e)},_doFinalize:function(){var t,e=this.cfg.padding;return this._xformMode==this._ENC_XFORM_MODE?(e.pad(this._data,this.blockSize),t=this._process(!0)):(t=this._process(!0),e.unpad(t)),t},blockSize:4}),l1=P.CipherParams=p.extend({init:function(t){this.mixIn(t)},toString:function(t){return(t||this.formatter).stringify(this)}}),r=(w.format={}).OpenSSL={stringify:function(t){var e=t.ciphertext,t=t.salt,t=t?s.create([1398893684,1701076831]).concat(t).concat(e):e;return t.toString(c1)},parse:function(t){var e,t=c1.parse(t),r=t.words;return 1398893684==r[0]&&1701076831==r[1]&&(e=s.create(r.slice(2,4)),r.splice(0,4),t.sigBytes-=16),l1.create({ciphertext:t,salt:e})}},o=P.SerializableCipher=p.extend({cfg:p.extend({format:r}),encrypt:function(t,e,r,i){i=this.cfg.extend(i);var o=t.createEncryptor(r,i),e=o.finalize(e),o=o.cfg;return l1.create({ciphertext:e,key:r,iv:o.iv,algorithm:t,mode:o.mode,padding:o.padding,blockSize:t.blockSize,formatter:i.format})},decrypt:function(t,e,r,i){return i=this.cfg.extend(i),e=this._parse(e,i.format),t.createDecryptor(r,i).finalize(e.ciphertext)},_parse:function(t,e){return"string"==typeof t?e.parse(t,this):t}}),u=(w.kdf={}).OpenSSL={execute:function(t,e,r,i,o){i=i||s.random(8),o=(o?a1.create({keySize:e+r,hasher:o}):a1.create({keySize:e+r})).compute(t,i);t=s.create(o.words.slice(e),4*r);return o.sigBytes=4*e,l1.create({key:o,iv:t,salt:i})}},f1=P.PasswordBasedCipher=o.extend({cfg:o.cfg.extend({kdf:u}),encrypt:function(t,e,r,i){r=(i=this.cfg.extend(i)).kdf.execute(r,t.keySize,t.ivSize,i.salt,i.hasher),i.iv=r.iv,t=o.encrypt.call(this,t,e,r.key,i);return t.mixIn(r),t},decrypt:function(t,e,r,i){i=this.cfg.extend(i),e=this._parse(e,i.format);r=i.kdf.execute(r,t.keySize,t.ivSize,e.salt,i.hasher);return i.iv=r.iv,o.decrypt.call(this,t,e,r.key,i)}})),i.mode.CFB=((p=i.lib.BlockCipherMode.extend()).Encryptor=p.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize;t2.call(this,t,e,i,r),this._prevBlock=t.slice(e,e+i)}}),p.Decryptor=p.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,o=t.slice(e,e+i);t2.call(this,t,e,i,r),this._prevBlock=o}}),p),i.mode.CTR=(r=i.lib.BlockCipherMode.extend(),w=r.Encryptor=r.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,o=this._iv,n=this._counter,s=(o&&(n=this._counter=o.slice(0),this._iv=void 0),n.slice(0));r.encryptBlock(s,0),n[i-1]=n[i-1]+1|0;for(var c=0;c>>2]|=e<<24-r%4*8,t.sigBytes+=e},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},i.pad.Iso10126={pad:function(t,e){e*=4,e-=t.sigBytes%e;t.concat(i.lib.WordArray.random(e-1)).concat(i.lib.WordArray.create([e<<24],1))},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},i.pad.Iso97971={pad:function(t,e){t.concat(i.lib.WordArray.create([2147483648],1)),i.pad.ZeroPadding.pad(t,e)},unpad:function(t){i.pad.ZeroPadding.unpad(t),t.sigBytes--}},i.pad.ZeroPadding={pad:function(t,e){e*=4;t.clamp(),t.sigBytes+=e-(t.sigBytes%e||e)},unpad:function(t){for(var e=t.words,r=t.sigBytes-1,r=t.sigBytes-1;0<=r;r--)if(e[r>>>2]>>>24-r%4*8&255){t.sigBytes=r+1;break}}},i.pad.NoPadding={pad:function(){},unpad:function(){}},d1=(P=i).lib.CipherParams,u1=P.enc.Hex,P.format.Hex={stringify:function(t){return t.ciphertext.toString(u1)},parse:function(t){t=u1.parse(t);return d1.create({ciphertext:t})}};for(var w=i,p=w.lib.BlockCipher,u=w.algo,k=[],r2=[],i2=[],o2=[],n2=[],s2=[],c2=[],a2=[],h2=[],l2=[],x=[],b=0;b<256;b++)x[b]=b<128?b<<1:b<<1^283;for(var m=0,S=0,b=0;b<256;b++){var E=S^S<<1^S<<2^S<<3^S<<4,f2=(k[m]=E=E>>>8^255&E^99,x[r2[E]=m]),d2=x[f2],u2=x[d2],M=257*x[E]^16843008*E;i2[m]=M<<24|M>>>8,o2[m]=M<<16|M>>>16,n2[m]=M<<8|M>>>24,s2[m]=M,c2[E]=(M=16843009*u2^65537*d2^257*f2^16843008*m)<<24|M>>>8,a2[E]=M<<16|M>>>16,h2[E]=M<<8|M>>>24,l2[E]=M,m?(m=f2^x[x[x[u2^f2]]],S^=x[x[S]]):m=S=1}var p2=[0,1,2,4,8,16,32,64,128,27,54],u=u.AES=p.extend({_doReset:function(){if(!this._nRounds||this._keyPriorReset!==this._key){for(var t=this._keyPriorReset=this._key,e=t.words,r=t.sigBytes/4,i=4*(1+(this._nRounds=6+r)),o=this._keySchedule=[],n=0;n>>24]<<24|k[a>>>16&255]<<16|k[a>>>8&255]<<8|k[255&a]):(a=k[(a=a<<8|a>>>24)>>>24]<<24|k[a>>>16&255]<<16|k[a>>>8&255]<<8|k[255&a],a^=p2[n/r|0]<<24),o[n]=o[n-r]^a);for(var s=this._invKeySchedule=[],c=0;c>>24]]^a2[k[a>>>16&255]]^h2[k[a>>>8&255]]^l2[k[255&a]]}}},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._keySchedule,i2,o2,n2,s2,k)},decryptBlock:function(t,e){var r=t[e+1],r=(t[e+1]=t[e+3],t[e+3]=r,this._doCryptBlock(t,e,this._invKeySchedule,c2,a2,h2,l2,r2),t[e+1]);t[e+1]=t[e+3],t[e+3]=r},_doCryptBlock:function(t,e,r,i,o,n,s,c){for(var a=this._nRounds,h=t[e]^r[0],l=t[e+1]^r[1],f=t[e+2]^r[2],d=t[e+3]^r[3],u=4,p=1;p>>24]^o[l>>>16&255]^n[f>>>8&255]^s[255&d]^r[u++],y=i[l>>>24]^o[f>>>16&255]^n[d>>>8&255]^s[255&h]^r[u++],g=i[f>>>24]^o[d>>>16&255]^n[h>>>8&255]^s[255&l]^r[u++],v=i[d>>>24]^o[h>>>16&255]^n[l>>>8&255]^s[255&f]^r[u++],h=_,l=y,f=g,d=v;_=(c[h>>>24]<<24|c[l>>>16&255]<<16|c[f>>>8&255]<<8|c[255&d])^r[u++],y=(c[l>>>24]<<24|c[f>>>16&255]<<16|c[d>>>8&255]<<8|c[255&h])^r[u++],g=(c[f>>>24]<<24|c[d>>>16&255]<<16|c[h>>>8&255]<<8|c[255&l])^r[u++],v=(c[d>>>24]<<24|c[h>>>16&255]<<16|c[l>>>8&255]<<8|c[255&f])^r[u++];t[e]=_,t[e+1]=y,t[e+2]=g,t[e+3]=v},keySize:8}),P=(w.AES=p._createHelper(u),i),_2=(w=P.lib).WordArray,w=w.BlockCipher,p=P.algo,y2=[57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4],g2=[14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32],v2=[1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28],B2=[{0:8421888,268435456:32768,536870912:8421378,805306368:2,1073741824:512,1342177280:8421890,1610612736:8389122,1879048192:8388608,2147483648:514,2415919104:8389120,2684354560:33280,2952790016:8421376,3221225472:32770,3489660928:8388610,3758096384:0,4026531840:33282,134217728:0,402653184:8421890,671088640:33282,939524096:32768,1207959552:8421888,1476395008:512,1744830464:8421378,2013265920:2,2281701376:8389120,2550136832:33280,2818572288:8421376,3087007744:8389122,3355443200:8388610,3623878656:32770,3892314112:514,4160749568:8388608,1:32768,268435457:2,536870913:8421888,805306369:8388608,1073741825:8421378,1342177281:33280,1610612737:512,1879048193:8389122,2147483649:8421890,2415919105:8421376,2684354561:8388610,2952790017:33282,3221225473:514,3489660929:8389120,3758096385:32770,4026531841:0,134217729:8421890,402653185:8421376,671088641:8388608,939524097:512,1207959553:32768,1476395009:8388610,1744830465:2,2013265921:33282,2281701377:32770,2550136833:8389122,2818572289:514,3087007745:8421888,3355443201:8389120,3623878657:0,3892314113:33280,4160749569:8421378},{0:1074282512,16777216:16384,33554432:524288,50331648:1074266128,67108864:1073741840,83886080:1074282496,100663296:1073758208,117440512:16,134217728:540672,150994944:1073758224,167772160:1073741824,184549376:540688,201326592:524304,218103808:0,234881024:16400,251658240:1074266112,8388608:1073758208,25165824:540688,41943040:16,58720256:1073758224,75497472:1074282512,92274688:1073741824,109051904:524288,125829120:1074266128,142606336:524304,159383552:0,176160768:16384,192937984:1074266112,209715200:1073741840,226492416:540672,243269632:1074282496,260046848:16400,268435456:0,285212672:1074266128,301989888:1073758224,318767104:1074282496,335544320:1074266112,352321536:16,369098752:540688,385875968:16384,402653184:16400,419430400:524288,436207616:524304,452984832:1073741840,469762048:540672,486539264:1073758208,503316480:1073741824,520093696:1074282512,276824064:540688,293601280:524288,310378496:1074266112,327155712:16384,343932928:1073758208,360710144:1074282512,377487360:16,394264576:1073741824,411041792:1074282496,427819008:1073741840,444596224:1073758224,461373440:524304,478150656:0,494927872:16400,511705088:1074266128,528482304:540672},{0:260,1048576:0,2097152:67109120,3145728:65796,4194304:65540,5242880:67108868,6291456:67174660,7340032:67174400,8388608:67108864,9437184:67174656,10485760:65792,11534336:67174404,12582912:67109124,13631488:65536,14680064:4,15728640:256,524288:67174656,1572864:67174404,2621440:0,3670016:67109120,4718592:67108868,5767168:65536,6815744:65540,7864320:260,8912896:4,9961472:256,11010048:67174400,12058624:65796,13107200:65792,14155776:67109124,15204352:67174660,16252928:67108864,16777216:67174656,17825792:65540,18874368:65536,19922944:67109120,20971520:256,22020096:67174660,23068672:67108868,24117248:0,25165824:67109124,26214400:67108864,27262976:4,28311552:65792,29360128:67174400,30408704:260,31457280:65796,32505856:67174404,17301504:67108864,18350080:260,19398656:67174656,20447232:0,21495808:65540,22544384:67109120,23592960:256,24641536:67174404,25690112:65536,26738688:67174660,27787264:65796,28835840:67108868,29884416:67109124,30932992:67174400,31981568:4,33030144:65792},{0:2151682048,65536:2147487808,131072:4198464,196608:2151677952,262144:0,327680:4198400,393216:2147483712,458752:4194368,524288:2147483648,589824:4194304,655360:64,720896:2147487744,786432:2151678016,851968:4160,917504:4096,983040:2151682112,32768:2147487808,98304:64,163840:2151678016,229376:2147487744,294912:4198400,360448:2151682112,425984:0,491520:2151677952,557056:4096,622592:2151682048,688128:4194304,753664:4160,819200:2147483648,884736:4194368,950272:4198464,1015808:2147483712,1048576:4194368,1114112:4198400,1179648:2147483712,1245184:0,1310720:4160,1376256:2151678016,1441792:2151682048,1507328:2147487808,1572864:2151682112,1638400:2147483648,1703936:2151677952,1769472:4198464,1835008:2147487744,1900544:4194304,1966080:64,2031616:4096,1081344:2151677952,1146880:2151682112,1212416:0,1277952:4198400,1343488:4194368,1409024:2147483648,1474560:2147487808,1540096:64,1605632:2147483712,1671168:4096,1736704:2147487744,1802240:2151678016,1867776:4160,1933312:2151682048,1998848:4194304,2064384:4198464},{0:128,4096:17039360,8192:262144,12288:536870912,16384:537133184,20480:16777344,24576:553648256,28672:262272,32768:16777216,36864:537133056,40960:536871040,45056:553910400,49152:553910272,53248:0,57344:17039488,61440:553648128,2048:17039488,6144:553648256,10240:128,14336:17039360,18432:262144,22528:537133184,26624:553910272,30720:536870912,34816:537133056,38912:0,43008:553910400,47104:16777344,51200:536871040,55296:553648128,59392:16777216,63488:262272,65536:262144,69632:128,73728:536870912,77824:553648256,81920:16777344,86016:553910272,90112:537133184,94208:16777216,98304:553910400,102400:553648128,106496:17039360,110592:537133056,114688:262272,118784:536871040,122880:0,126976:17039488,67584:553648256,71680:16777216,75776:17039360,79872:537133184,83968:536870912,88064:17039488,92160:128,96256:553910272,100352:262272,104448:553910400,108544:0,112640:553648128,116736:16777344,120832:262144,124928:537133056,129024:536871040},{0:268435464,256:8192,512:270532608,768:270540808,1024:268443648,1280:2097152,1536:2097160,1792:268435456,2048:0,2304:268443656,2560:2105344,2816:8,3072:270532616,3328:2105352,3584:8200,3840:270540800,128:270532608,384:270540808,640:8,896:2097152,1152:2105352,1408:268435464,1664:268443648,1920:8200,2176:2097160,2432:8192,2688:268443656,2944:270532616,3200:0,3456:270540800,3712:2105344,3968:268435456,4096:268443648,4352:270532616,4608:270540808,4864:8200,5120:2097152,5376:268435456,5632:268435464,5888:2105344,6144:2105352,6400:0,6656:8,6912:270532608,7168:8192,7424:268443656,7680:270540800,7936:2097160,4224:8,4480:2105344,4736:2097152,4992:268435464,5248:268443648,5504:8200,5760:270540808,6016:270532608,6272:270540800,6528:270532616,6784:8192,7040:2105352,7296:2097160,7552:0,7808:268435456,8064:268443656},{0:1048576,16:33555457,32:1024,48:1049601,64:34604033,80:0,96:1,112:34603009,128:33555456,144:1048577,160:33554433,176:34604032,192:34603008,208:1025,224:1049600,240:33554432,8:34603009,24:0,40:33555457,56:34604032,72:1048576,88:33554433,104:33554432,120:1025,136:1049601,152:33555456,168:34603008,184:1048577,200:1024,216:34604033,232:1,248:1049600,256:33554432,272:1048576,288:33555457,304:34603009,320:1048577,336:33555456,352:34604032,368:1049601,384:1025,400:34604033,416:1049600,432:1,448:0,464:34603008,480:33554433,496:1024,264:1049600,280:33555457,296:34603009,312:1,328:33554432,344:1048576,360:1025,376:34604032,392:33554433,408:34603008,424:0,440:34604033,456:1049601,472:1024,488:33555456,504:1048577},{0:134219808,1:131072,2:134217728,3:32,4:131104,5:134350880,6:134350848,7:2048,8:134348800,9:134219776,10:133120,11:134348832,12:2080,13:0,14:134217760,15:133152,2147483648:2048,2147483649:134350880,2147483650:134219808,2147483651:134217728,2147483652:134348800,2147483653:133120,2147483654:133152,2147483655:32,2147483656:134217760,2147483657:2080,2147483658:131104,2147483659:134350848,2147483660:0,2147483661:134348832,2147483662:134219776,2147483663:131072,16:133152,17:134350848,18:32,19:2048,20:134219776,21:134217760,22:134348832,23:131072,24:0,25:131104,26:134348800,27:134219808,28:134350880,29:133120,30:2080,31:134217728,2147483664:131072,2147483665:2048,2147483666:134348832,2147483667:133152,2147483668:32,2147483669:134348800,2147483670:134217728,2147483671:134219808,2147483672:134350880,2147483673:134217760,2147483674:134219776,2147483675:0,2147483676:133120,2147483677:2080,2147483678:131104,2147483679:134350848}],w2=[4160749569,528482304,33030144,2064384,129024,8064,504,2147483679],k2=p.DES=w.extend({_doReset:function(){for(var t=this._key.words,e=[],r=0;r<56;r++){var i=y2[r]-1;e[r]=t[i>>>5]>>>31-i%32&1}for(var o=this._subKeys=[],n=0;n<16;n++){for(var s=o[n]=[],c=v2[n],r=0;r<24;r++)s[r/6|0]|=e[(g2[r]-1+c)%28]<<31-r%6,s[4+(r/6|0)]|=e[28+(g2[r+24]-1+c)%28]<<31-r%6;s[0]=s[0]<<1|s[0]>>>31;for(r=1;r<7;r++)s[r]=s[r]>>>4*(r-1)+3;s[7]=s[7]<<5|s[7]>>>27}for(var a=this._invSubKeys=[],r=0;r<16;r++)a[r]=o[15-r]},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._subKeys)},decryptBlock:function(t,e){this._doCryptBlock(t,e,this._invSubKeys)},_doCryptBlock:function(t,e,r){this._lBlock=t[e],this._rBlock=t[e+1],x2.call(this,4,252645135),x2.call(this,16,65535),b2.call(this,2,858993459),b2.call(this,8,16711935),x2.call(this,1,1431655765);for(var i=0;i<16;i++){for(var o=r[i],n=this._lBlock,s=this._rBlock,c=0,a=0;a<8;a++)c|=B2[a][((s^o[a])&w2[a])>>>0];this._lBlock=s,this._rBlock=n^c}var h=this._lBlock;this._lBlock=this._rBlock,this._rBlock=h,x2.call(this,1,1431655765),b2.call(this,8,16711935),b2.call(this,2,858993459),x2.call(this,16,65535),x2.call(this,4,252645135),t[e]=this._lBlock,t[e+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});function x2(t,e){e=(this._lBlock>>>t^this._rBlock)&e;this._rBlock^=e,this._lBlock^=e<>>t^this._lBlock)&e;this._lBlock^=e,this._rBlock^=e<192.");var e=t.slice(0,2),r=t.length<4?t.slice(0,2):t.slice(2,4),t=t.length<6?t.slice(0,2):t.slice(4,6);this._des1=k2.createEncryptor(_2.create(e)),this._des2=k2.createEncryptor(_2.create(r)),this._des3=k2.createEncryptor(_2.create(t))},encryptBlock:function(t,e){this._des1.encryptBlock(t,e),this._des2.decryptBlock(t,e),this._des3.encryptBlock(t,e)},decryptBlock:function(t,e){this._des3.decryptBlock(t,e),this._des2.encryptBlock(t,e),this._des1.decryptBlock(t,e)},keySize:6,ivSize:2,blockSize:2}),P.TripleDES=w._createHelper(p);var u=i,P=u.lib.StreamCipher,w=u.algo,m2=w.RC4=P.extend({_doReset:function(){for(var t=this._key,e=t.words,r=t.sigBytes,i=this._S=[],o=0;o<256;o++)i[o]=o;for(var o=0,n=0;o<256;o++){var s=o%r,s=e[s>>>2]>>>24-s%4*8&255,n=(n+i[o]+s)%256,s=i[o];i[o]=i[n],i[n]=s}this._i=this._j=0},_doProcessBlock:function(t,e){t[e]^=S2.call(this)},keySize:8,ivSize:0});function S2(){for(var t=this._S,e=this._i,r=this._j,i=0,o=0;o<4;o++){var r=(r+t[e=(e+1)%256])%256,n=t[e];t[e]=t[r],t[r]=n,i|=t[(t[e]+t[r])%256]<<24-8*o}return this._i=e,this._j=r,i}function A2(){for(var t=this._X,e=this._C,r=0;r<8;r++)c[r]=e[r];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],o=65535&i,n=i>>>16;a[r]=((o*o>>>17)+o*n>>>15)+n*n^((4294901760&i)*i|0)+((65535&i)*i|0)}t[0]=a[0]+(a[7]<<16|a[7]>>>16)+(a[6]<<16|a[6]>>>16)|0,t[1]=a[1]+(a[0]<<8|a[0]>>>24)+a[7]|0,t[2]=a[2]+(a[1]<<16|a[1]>>>16)+(a[0]<<16|a[0]>>>16)|0,t[3]=a[3]+(a[2]<<8|a[2]>>>24)+a[1]|0,t[4]=a[4]+(a[3]<<16|a[3]>>>16)+(a[2]<<16|a[2]>>>16)|0,t[5]=a[5]+(a[4]<<8|a[4]>>>24)+a[3]|0,t[6]=a[6]+(a[5]<<16|a[5]>>>16)+(a[4]<<16|a[4]>>>16)|0,t[7]=a[7]+(a[6]<<8|a[6]>>>24)+a[5]|0}function z2(){for(var t=this._X,e=this._C,r=0;r<8;r++)f[r]=e[r];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],o=65535&i,n=i>>>16;d[r]=((o*o>>>17)+o*n>>>15)+n*n^((4294901760&i)*i|0)+((65535&i)*i|0)}t[0]=d[0]+(d[7]<<16|d[7]>>>16)+(d[6]<<16|d[6]>>>16)|0,t[1]=d[1]+(d[0]<<8|d[0]>>>24)+d[7]|0,t[2]=d[2]+(d[1]<<16|d[1]>>>16)+(d[0]<<16|d[0]>>>16)|0,t[3]=d[3]+(d[2]<<8|d[2]>>>24)+d[1]|0,t[4]=d[4]+(d[3]<<16|d[3]>>>16)+(d[2]<<16|d[2]>>>16)|0,t[5]=d[5]+(d[4]<<8|d[4]>>>24)+d[3]|0,t[6]=d[6]+(d[5]<<16|d[5]>>>16)+(d[4]<<16|d[4]>>>16)|0,t[7]=d[7]+(d[6]<<8|d[6]>>>24)+d[5]|0}u.RC4=P._createHelper(m2),w=w.RC4Drop=m2.extend({cfg:m2.cfg.extend({drop:192}),_doReset:function(){m2._doReset.call(this);for(var t=this.cfg.drop;0>>24)|4278255360&(t[r]<<24|t[r]>>>8);for(var i=this._X=[t[0],t[3]<<16|t[2]>>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],o=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]],r=this._b=0;r<4;r++)A2.call(this);for(r=0;r<8;r++)o[r]^=i[r+4&7];if(e){var e=e.words,n=e[0],e=e[1],n=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8),e=16711935&(e<<8|e>>>24)|4278255360&(e<<24|e>>>8),s=n>>>16|4294901760&e,c=e<<16|65535&n;o[0]^=n,o[1]^=s,o[2]^=e,o[3]^=c,o[4]^=n,o[5]^=s,o[6]^=e,o[7]^=c;for(r=0;r<4;r++)A2.call(this)}},_doProcessBlock:function(t,e){var r=this._X;A2.call(this),n[0]=r[0]^r[5]>>>16^r[3]<<16,n[1]=r[2]^r[7]>>>16^r[5]<<16,n[2]=r[4]^r[1]>>>16^r[7]<<16,n[3]=r[6]^r[3]>>>16^r[1]<<16;for(var i=0;i<4;i++)n[i]=16711935&(n[i]<<8|n[i]>>>24)|4278255360&(n[i]<<24|n[i]>>>8),t[e+i]^=n[i]},blockSize:4,ivSize:2}),p.Rabbit=u._createHelper(P),p=(w=i).lib.StreamCipher,u=w.algo,h=[],f=[],d=[],u=u.RabbitLegacy=p.extend({_doReset:function(){for(var t=this._key.words,e=this.cfg.iv,r=this._X=[t[0],t[3]<<16|t[2]>>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],i=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]],o=this._b=0;o<4;o++)z2.call(this);for(o=0;o<8;o++)i[o]^=r[o+4&7];if(e){var t=e.words,e=t[0],t=t[1],e=16711935&(e<<8|e>>>24)|4278255360&(e<<24|e>>>8),t=16711935&(t<<8|t>>>24)|4278255360&(t<<24|t>>>8),n=e>>>16|4294901760&t,s=t<<16|65535&e;i[0]^=e,i[1]^=n,i[2]^=t,i[3]^=s,i[4]^=e,i[5]^=n,i[6]^=t,i[7]^=s;for(o=0;o<4;o++)z2.call(this)}},_doProcessBlock:function(t,e){var r=this._X;z2.call(this),h[0]=r[0]^r[5]>>>16^r[3]<<16,h[1]=r[2]^r[7]>>>16^r[5]<<16,h[2]=r[4]^r[1]>>>16^r[7]<<16,h[3]=r[6]^r[3]>>>16^r[1]<<16;for(var i=0;i<4;i++)h[i]=16711935&(h[i]<<8|h[i]>>>24)|4278255360&(h[i]<<24|h[i]>>>8),t[e+i]^=h[i]},blockSize:4,ivSize:2}),w.RabbitLegacy=p._createHelper(u);{w=(P=i).lib.BlockCipher,p=P.algo;const F=16,D2=[608135816,2242054355,320440878,57701188,2752067618,698298832,137296536,3964562569,1160258022,953160567,3193202383,887688300,3232508343,3380367581,1065670069,3041331479,2450970073,2306472731],E2=[[3509652390,2564797868,805139163,3491422135,3101798381,1780907670,3128725573,4046225305,614570311,3012652279,134345442,2240740374,1667834072,1901547113,2757295779,4103290238,227898511,1921955416,1904987480,2182433518,2069144605,3260701109,2620446009,720527379,3318853667,677414384,3393288472,3101374703,2390351024,1614419982,1822297739,2954791486,3608508353,3174124327,2024746970,1432378464,3864339955,2857741204,1464375394,1676153920,1439316330,715854006,3033291828,289532110,2706671279,2087905683,3018724369,1668267050,732546397,1947742710,3462151702,2609353502,2950085171,1814351708,2050118529,680887927,999245976,1800124847,3300911131,1713906067,1641548236,4213287313,1216130144,1575780402,4018429277,3917837745,3693486850,3949271944,596196993,3549867205,258830323,2213823033,772490370,2760122372,1774776394,2652871518,566650946,4142492826,1728879713,2882767088,1783734482,3629395816,2517608232,2874225571,1861159788,326777828,3124490320,2130389656,2716951837,967770486,1724537150,2185432712,2364442137,1164943284,2105845187,998989502,3765401048,2244026483,1075463327,1455516326,1322494562,910128902,469688178,1117454909,936433444,3490320968,3675253459,1240580251,122909385,2157517691,634681816,4142456567,3825094682,3061402683,2540495037,79693498,3249098678,1084186820,1583128258,426386531,1761308591,1047286709,322548459,995290223,1845252383,2603652396,3431023940,2942221577,3202600964,3727903485,1712269319,422464435,3234572375,1170764815,3523960633,3117677531,1434042557,442511882,3600875718,1076654713,1738483198,4213154764,2393238008,3677496056,1014306527,4251020053,793779912,2902807211,842905082,4246964064,1395751752,1040244610,2656851899,3396308128,445077038,3742853595,3577915638,679411651,2892444358,2354009459,1767581616,3150600392,3791627101,3102740896,284835224,4246832056,1258075500,768725851,2589189241,3069724005,3532540348,1274779536,3789419226,2764799539,1660621633,3471099624,4011903706,913787905,3497959166,737222580,2514213453,2928710040,3937242737,1804850592,3499020752,2949064160,2386320175,2390070455,2415321851,4061277028,2290661394,2416832540,1336762016,1754252060,3520065937,3014181293,791618072,3188594551,3933548030,2332172193,3852520463,3043980520,413987798,3465142937,3030929376,4245938359,2093235073,3534596313,375366246,2157278981,2479649556,555357303,3870105701,2008414854,3344188149,4221384143,3956125452,2067696032,3594591187,2921233993,2428461,544322398,577241275,1471733935,610547355,4027169054,1432588573,1507829418,2025931657,3646575487,545086370,48609733,2200306550,1653985193,298326376,1316178497,3007786442,2064951626,458293330,2589141269,3591329599,3164325604,727753846,2179363840,146436021,1461446943,4069977195,705550613,3059967265,3887724982,4281599278,3313849956,1404054877,2845806497,146425753,1854211946],[1266315497,3048417604,3681880366,3289982499,290971e4,1235738493,2632868024,2414719590,3970600049,1771706367,1449415276,3266420449,422970021,1963543593,2690192192,3826793022,1062508698,1531092325,1804592342,2583117782,2714934279,4024971509,1294809318,4028980673,1289560198,2221992742,1669523910,35572830,157838143,1052438473,1016535060,1802137761,1753167236,1386275462,3080475397,2857371447,1040679964,2145300060,2390574316,1461121720,2956646967,4031777805,4028374788,33600511,2920084762,1018524850,629373528,3691585981,3515945977,2091462646,2486323059,586499841,988145025,935516892,3367335476,2599673255,2839830854,265290510,3972581182,2759138881,3795373465,1005194799,847297441,406762289,1314163512,1332590856,1866599683,4127851711,750260880,613907577,1450815602,3165620655,3734664991,3650291728,3012275730,3704569646,1427272223,778793252,1343938022,2676280711,2052605720,1946737175,3164576444,3914038668,3967478842,3682934266,1661551462,3294938066,4011595847,840292616,3712170807,616741398,312560963,711312465,1351876610,322626781,1910503582,271666773,2175563734,1594956187,70604529,3617834859,1007753275,1495573769,4069517037,2549218298,2663038764,504708206,2263041392,3941167025,2249088522,1514023603,1998579484,1312622330,694541497,2582060303,2151582166,1382467621,776784248,2618340202,3323268794,2497899128,2784771155,503983604,4076293799,907881277,423175695,432175456,1378068232,4145222326,3954048622,3938656102,3820766613,2793130115,2977904593,26017576,3274890735,3194772133,1700274565,1756076034,4006520079,3677328699,720338349,1533947780,354530856,688349552,3973924725,1637815568,332179504,3949051286,53804574,2852348879,3044236432,1282449977,3583942155,3416972820,4006381244,1617046695,2628476075,3002303598,1686838959,431878346,2686675385,1700445008,1080580658,1009431731,832498133,3223435511,2605976345,2271191193,2516031870,1648197032,4164389018,2548247927,300782431,375919233,238389289,3353747414,2531188641,2019080857,1475708069,455242339,2609103871,448939670,3451063019,1395535956,2413381860,1841049896,1491858159,885456874,4264095073,4001119347,1565136089,3898914787,1108368660,540939232,1173283510,2745871338,3681308437,4207628240,3343053890,4016749493,1699691293,1103962373,3625875870,2256883143,3830138730,1031889488,3479347698,1535977030,4236805024,3251091107,2132092099,1774941330,1199868427,1452454533,157007616,2904115357,342012276,595725824,1480756522,206960106,497939518,591360097,863170706,2375253569,3596610801,1814182875,2094937945,3421402208,1082520231,3463918190,2785509508,435703966,3908032597,1641649973,2842273706,3305899714,1510255612,2148256476,2655287854,3276092548,4258621189,236887753,3681803219,274041037,1734335097,3815195456,3317970021,1899903192,1026095262,4050517792,356393447,2410691914,3873677099,3682840055],[3913112168,2491498743,4132185628,2489919796,1091903735,1979897079,3170134830,3567386728,3557303409,857797738,1136121015,1342202287,507115054,2535736646,337727348,3213592640,1301675037,2528481711,1895095763,1721773893,3216771564,62756741,2142006736,835421444,2531993523,1442658625,3659876326,2882144922,676362277,1392781812,170690266,3921047035,1759253602,3611846912,1745797284,664899054,1329594018,3901205900,3045908486,2062866102,2865634940,3543621612,3464012697,1080764994,553557557,3656615353,3996768171,991055499,499776247,1265440854,648242737,3940784050,980351604,3713745714,1749149687,3396870395,4211799374,3640570775,1161844396,3125318951,1431517754,545492359,4268468663,3499529547,1437099964,2702547544,3433638243,2581715763,2787789398,1060185593,1593081372,2418618748,4260947970,69676912,2159744348,86519011,2512459080,3838209314,1220612927,3339683548,133810670,1090789135,1078426020,1569222167,845107691,3583754449,4072456591,1091646820,628848692,1613405280,3757631651,526609435,236106946,48312990,2942717905,3402727701,1797494240,859738849,992217954,4005476642,2243076622,3870952857,3732016268,765654824,3490871365,2511836413,1685915746,3888969200,1414112111,2273134842,3281911079,4080962846,172450625,2569994100,980381355,4109958455,2819808352,2716589560,2568741196,3681446669,3329971472,1835478071,660984891,3704678404,4045999559,3422617507,3040415634,1762651403,1719377915,3470491036,2693910283,3642056355,3138596744,1364962596,2073328063,1983633131,926494387,3423689081,2150032023,4096667949,1749200295,3328846651,309677260,2016342300,1779581495,3079819751,111262694,1274766160,443224088,298511866,1025883608,3806446537,1145181785,168956806,3641502830,3584813610,1689216846,3666258015,3200248200,1692713982,2646376535,4042768518,1618508792,1610833997,3523052358,4130873264,2001055236,3610705100,2202168115,4028541809,2961195399,1006657119,2006996926,3186142756,1430667929,3210227297,1314452623,4074634658,4101304120,2273951170,1399257539,3367210612,3027628629,1190975929,2062231137,2333990788,2221543033,2438960610,1181637006,548689776,2362791313,3372408396,3104550113,3145860560,296247880,1970579870,3078560182,3769228297,1714227617,3291629107,3898220290,166772364,1251581989,493813264,448347421,195405023,2709975567,677966185,3703036547,1463355134,2715995803,1338867538,1343315457,2802222074,2684532164,233230375,2599980071,2000651841,3277868038,1638401717,4028070440,3237316320,6314154,819756386,300326615,590932579,1405279636,3267499572,3150704214,2428286686,3959192993,3461946742,1862657033,1266418056,963775037,2089974820,2263052895,1917689273,448879540,3550394620,3981727096,150775221,3627908307,1303187396,508620638,2975983352,2726630617,1817252668,1876281319,1457606340,908771278,3720792119,3617206836,2455994898,1729034894,1080033504],[976866871,3556439503,2881648439,1522871579,1555064734,1336096578,3548522304,2579274686,3574697629,3205460757,3593280638,3338716283,3079412587,564236357,2993598910,1781952180,1464380207,3163844217,3332601554,1699332808,1393555694,1183702653,3581086237,1288719814,691649499,2847557200,2895455976,3193889540,2717570544,1781354906,1676643554,2592534050,3230253752,1126444790,2770207658,2633158820,2210423226,2615765581,2414155088,3127139286,673620729,2805611233,1269405062,4015350505,3341807571,4149409754,1057255273,2012875353,2162469141,2276492801,2601117357,993977747,3918593370,2654263191,753973209,36408145,2530585658,25011837,3520020182,2088578344,530523599,2918365339,1524020338,1518925132,3760827505,3759777254,1202760957,3985898139,3906192525,674977740,4174734889,2031300136,2019492241,3983892565,4153806404,3822280332,352677332,2297720250,60907813,90501309,3286998549,1016092578,2535922412,2839152426,457141659,509813237,4120667899,652014361,1966332200,2975202805,55981186,2327461051,676427537,3255491064,2882294119,3433927263,1307055953,942726286,933058658,2468411793,3933900994,4215176142,1361170020,2001714738,2830558078,3274259782,1222529897,1679025792,2729314320,3714953764,1770335741,151462246,3013232138,1682292957,1483529935,471910574,1539241949,458788160,3436315007,1807016891,3718408830,978976581,1043663428,3165965781,1927990952,4200891579,2372276910,3208408903,3533431907,1412390302,2931980059,4132332400,1947078029,3881505623,4168226417,2941484381,1077988104,1320477388,886195818,18198404,3786409e3,2509781533,112762804,3463356488,1866414978,891333506,18488651,661792760,1628790961,3885187036,3141171499,876946877,2693282273,1372485963,791857591,2686433993,3759982718,3167212022,3472953795,2716379847,445679433,3561995674,3504004811,3574258232,54117162,3331405415,2381918588,3769707343,4154350007,1140177722,4074052095,668550556,3214352940,367459370,261225585,2610173221,4209349473,3468074219,3265815641,314222801,3066103646,3808782860,282218597,3406013506,3773591054,379116347,1285071038,846784868,2669647154,3771962079,3550491691,2305946142,453669953,1268987020,3317592352,3279303384,3744833421,2610507566,3859509063,266596637,3847019092,517658769,3462560207,3443424879,370717030,4247526661,2224018117,4143653529,4112773975,2788324899,2477274417,1456262402,2901442914,1517677493,1846949527,2295493580,3734397586,2176403920,1280348187,1908823572,3871786941,846861322,1172426758,3287448474,3383383037,1655181056,3139813346,901632758,1897031941,2986607138,3066810236,3447102507,1393639104,373351379,950779232,625454576,3124240540,4148612726,2007998917,544563296,2244738638,2330496472,2058025392,1291430526,424198748,50039436,29584100,3605783033,2429876329,2791104160,1057563949,3255363231,3075367218,3463963227,1469046755,985887462]];var H2={pbox:[],sbox:[]};function C2(t,e){var r=t.sbox[0][e>>24&255]+t.sbox[1][e>>16&255];return r=(r^=t.sbox[2][e>>8&255])+t.sbox[3][255&e]}function R2(e,t,r){let i=t,o=r,n;for(let t=0;t=a&&(e=0);let r=0,i=0,o=0;for(let t=0;t { 2 | // 打开数据库 3 | function openDatabase() { 4 | return new Promise((resolve, reject) => { 5 | const request = window.indexedDB.open('wrx', 1); 6 | request.onerror = (evt) => { 7 | reject(evt) 8 | } 9 | request.onsuccess = () => { 10 | resolve(request.result) 11 | } 12 | request.onupgradeneeded = () => { 13 | const db = request.result 14 | 15 | // 创建 v1 版本相关的数据库 16 | db.createObjectStore("chapters", {keyPath: 'bid'}) 17 | db.createObjectStore("tocs", {keyPath: 'bid'}) 18 | db.createObjectStore("details", {keyPath: 'bid'}) 19 | } 20 | }) 21 | } 22 | 23 | 24 | // 存储 chapter 数据 25 | async function storeBookChapter(bid, cid, pathname, content) { 26 | const db = await openDatabase() 27 | 28 | return new Promise((resolve, reject) => { 29 | const chapterStore = db.transaction('chapters', 'readwrite').objectStore('chapters') 30 | const request = chapterStore.get(bid) 31 | request.onsuccess = () => { 32 | let chapterObj = request.result 33 | if (!chapterObj) { 34 | chapterObj = { 35 | bid: bid, 36 | chapters: [], 37 | } 38 | } 39 | let chapter = chapterObj.chapters.find(chapter => chapter.cid === cid) 40 | if (!chapter) { 41 | chapter = { 42 | cid: cid, 43 | } 44 | chapterObj.chapters.push(chapter) 45 | } 46 | chapter[pathname] = content 47 | 48 | // 写入store 49 | chapterStore.put(chapterObj) 50 | resolve() 51 | } 52 | request.onerror = (evt) => { 53 | reject(evt) 54 | } 55 | }) 56 | } 57 | 58 | // 存储 toc 数据 59 | async function storeBookToc(bookId, response) { 60 | const db = await openDatabase() 61 | 62 | return new Promise((resolve, reject) => { 63 | const tocStore = db.transaction('tocs', 'readwrite').objectStore('tocs') 64 | const bid = window.wrx_weread_utils.hash(bookId) 65 | const request = tocStore.get(bid) 66 | request.onsuccess = () => { 67 | let tocObj = request.result 68 | if (!tocObj) { 69 | tocObj = { 70 | bid: bid, 71 | bookId: bookId, 72 | toc: response.data[0].updated, 73 | } 74 | } 75 | tocStore.put(tocObj) 76 | resolve() 77 | } 78 | request.onerror = (evt) => { 79 | reject(evt) 80 | } 81 | }) 82 | } 83 | 84 | // 存储 detail 数据 85 | async function storeBookDetail(bookId, response) { 86 | const db = await openDatabase() 87 | 88 | return new Promise((resolve, reject) => { 89 | const detailStore = db.transaction('details', 'readwrite').objectStore('details') 90 | const bid = window.wrx_weread_utils.hash(bookId) 91 | const request = detailStore.get(bid) 92 | request.onsuccess = () => { 93 | let detailObj = request.result 94 | if (!detailObj) { 95 | detailObj = { 96 | bid: bid, 97 | bookId: bookId, 98 | detail: response, 99 | } 100 | } 101 | detailStore.put(detailObj) 102 | resolve() 103 | } 104 | request.onerror = (evt) => { 105 | reject(evt) 106 | } 107 | }) 108 | } 109 | 110 | // 更新页面目录 111 | async function updatePageCatalog(bid) { 112 | const db = await openDatabase() 113 | 114 | return new Promise((resolve, reject) => { 115 | const tocStore = db.transaction('tocs', 'readonly').objectStore('tocs') 116 | const request = tocStore.get(bid) 117 | request.onsuccess = () => { 118 | const tocObj = request.result 119 | 120 | if (tocObj && Array.isArray(tocObj.toc) && tocObj.toc.length > 0) { 121 | // 避免重复更新 122 | const $ul = document.querySelector('.readerCatalog ul') 123 | if ($ul.dataset.updated === '1') { 124 | resolve() 125 | return 126 | } 127 | 128 | // 需要把 toc 转成扁平结构 129 | for (let i = 0; i < tocObj.toc.length; i++) { 130 | const chapter = tocObj.toc[i] 131 | 132 | chapter.level = chapter.level || 1 133 | 134 | if (Array.isArray(chapter.anchors) && chapter.anchors.length > 0) { 135 | const anchors = chapter.anchors.filter(_ => _.level > 1) 136 | delete chapter['anchors'] 137 | // 过滤掉与大标题一样的,参考这本书: https://weread.qq.com/web/reader/739322a07269560473951d3 138 | const anchorChapters = anchors.filter(_ => _.title !== chapter.title).map((anchor) => ({ 139 | ...chapter, 140 | title: anchor.title, 141 | level: anchor.level, 142 | anchor: anchor.anchor, 143 | isAnchor: true, 144 | })) 145 | 146 | // 把 anchor 插入到顶层位置 147 | tocObj.toc.splice(i+1, 0, ...anchorChapters) 148 | } 149 | } 150 | 151 | const tocHasCover = tocObj.toc[0].title === '封面' 152 | const $lis = $ul.querySelectorAll('li') 153 | const catalogHasCover = $lis[0].textContent === '封面' 154 | for (let i = 0; i < $lis.length; i++) { 155 | const $li = $lis[i] 156 | // 找到对应的目录信息 157 | // txt书籍需要去掉标题开头的 第*章 158 | const re = /^第\d+?章\s/ 159 | const liTitle = $li.textContent.replace(re, '') 160 | 161 | // 页面上的 li 元素对应 toc 中的数据 162 | let tocData 163 | if (!catalogHasCover && tocHasCover) { 164 | tocData = tocObj.toc[i+1] 165 | } else { 166 | tocData = tocObj.toc[i] 167 | } 168 | 169 | // 这里需要把   替换成普通空格 170 | if (tocData.title.replace(re, '').replace(/\[\d+]/g, '').replaceAll(' ', ' ').replaceAll('\'', ' ') === liTitle) { 171 | $li.dataset.cid = window.wrx_weread_utils.hash(tocData.chapterUid) 172 | } else { 173 | // console.log(tocData.title) 174 | // console.log(liTitle) 175 | console.warn(`这本书(${bid})的目录数据需要特殊适配,请联系开发者`) 176 | } 177 | } 178 | 179 | $ul.dataset.updated = '1' 180 | resolve() 181 | } 182 | } 183 | request.onerror = (evt) => { 184 | reject(evt) 185 | } 186 | }) 187 | } 188 | 189 | // 标记对应章节的下载状态 190 | function markChapterDownloaded(cid) { 191 | document.querySelectorAll(`li[data-cid="${cid}"]`).forEach($li => { 192 | $li.dataset.downloaded = '1' 193 | }) 194 | 195 | if (checkWholeBookDownloaded()) { 196 | // 标记整本书下载完成 197 | markBookDownloaded() 198 | } 199 | } 200 | 201 | // 标记整本书已下载完整 202 | function markBookDownloaded() { 203 | const targetDom = document.querySelector('.readerCatalog > .readerCatalog_bookInfo') 204 | if (targetDom.dataset.downloaded === '1') { 205 | return 206 | } 207 | targetDom.dataset.downloaded = '1' 208 | const btn = document.querySelector('#__wrx_export__') 209 | if (btn) { 210 | btn.textContent += ' (全)' 211 | btn.style.background = 'green' 212 | btn.style.color = 'white' 213 | } 214 | } 215 | 216 | // 检查整本书是否下载完整 217 | function checkWholeBookDownloaded() { 218 | const $lis = document.querySelectorAll('.readerCatalog ul > li') 219 | return Array.from($lis).every($li => $li.dataset.downloaded === '1') 220 | } 221 | 222 | // 从缓存中读取数据更新到页面 223 | async function initBookState(bid) { 224 | // 初始化 catalog 225 | await updatePageCatalog(bid) 226 | 227 | setTimeout(async () => { 228 | const db = await openDatabase() 229 | const chapterStore = db.transaction('chapters', 'readonly').objectStore('chapters') 230 | const request = chapterStore.get(bid) 231 | request.onsuccess = () => { 232 | let chapterObj = request.result 233 | if (!chapterObj) { 234 | return 235 | } 236 | chapterObj.chapters.forEach(chapter => { 237 | markChapterDownloaded(chapter.cid) 238 | }) 239 | } 240 | }, 500) 241 | } 242 | 243 | // 导出缓存中的书籍数据 244 | async function exportBookData(bid) { 245 | const db = await openDatabase() 246 | 247 | return new Promise((resolve, reject) => { 248 | const tx = db.transaction(['chapters', 'tocs', 'details'], 'readonly') 249 | const chapterStore = tx.objectStore('chapters') 250 | const tocStore = tx.objectStore('tocs') 251 | const detailStore = tx.objectStore('details') 252 | 253 | const chapterRequest = chapterStore.get(bid) 254 | chapterRequest.onsuccess = () => { 255 | let chapterObj = chapterRequest.result 256 | if (!chapterObj || !Array.isArray(chapterObj.chapters)) { 257 | reject(new Error('数据不完整,缺少chapters')) 258 | return 259 | } 260 | 261 | const tocRequest = tocStore.get(bid) 262 | tocRequest.onsuccess = () => { 263 | let tocObj = tocRequest.result 264 | if (!tocObj || !Array.isArray(tocObj.toc)) { 265 | reject(new Error('数据不完整,缺少目录')) 266 | return 267 | } 268 | 269 | const detailRequest = detailStore.get(bid) 270 | detailRequest.onsuccess = () => { 271 | let detailObj = detailRequest.result 272 | if (!detailObj || !detailObj.detail) { 273 | reject(new Error('数据不完整,缺少元数据')) 274 | return 275 | } 276 | 277 | const book = { 278 | bid: tocObj.bid, 279 | bookId: tocObj.bookId, 280 | toc: tocObj.toc, 281 | meta: detailObj.detail, 282 | chapters: chapterObj.chapters, 283 | site: window.location.origin, 284 | date: new Date().getTime(), 285 | } 286 | const file = new File([JSON.stringify(book)], detailObj.detail.title + ".json", { 287 | type: "application/json", 288 | }) 289 | window.wrx_common_utils.downloadFile(file) 290 | resolve() 291 | } 292 | detailRequest.onerror = (evt) => { 293 | reject(evt) 294 | } 295 | } 296 | tocRequest.onerror = (evt) => { 297 | reject(evt) 298 | } 299 | } 300 | chapterRequest.onerror = (evt) => { 301 | reject(evt) 302 | } 303 | }) 304 | } 305 | 306 | window.wrx_weread_store = { 307 | storeBookChapter: storeBookChapter, 308 | storeBookToc: storeBookToc, 309 | storeBookDetail: storeBookDetail, 310 | updatePageCatalog: updatePageCatalog, 311 | markChapterDownloaded: markChapterDownloaded, 312 | initBookState: initBookState, 313 | exportBookData: exportBookData, 314 | } 315 | })() 316 | -------------------------------------------------------------------------------- /src/sites/weread/toc.css: -------------------------------------------------------------------------------- 1 | ul.readerCatalog_list > li > .readerCatalog_list_item_inner { 2 | position: relative; 3 | } 4 | ul.readerCatalog_list > li > .readerCatalog_list_item_inner::before { 5 | width: 8px; 6 | height: 8px; 7 | border-radius: 100%; 8 | content: ""; 9 | display: block; 10 | background: red; 11 | position: absolute; 12 | left: -15px; 13 | top: 50%; 14 | transform: translateY(-50%); 15 | } 16 | ul.readerCatalog_list > li[data-downloaded="1"] > .readerCatalog_list_item_inner::before { 17 | background: #15a96c; 18 | } 19 | #__wrx_export__:hover { 20 | box-shadow: rgba(0, 0, 0, 0.17) 0 -23px 25px 0 inset, 21 | rgba(0, 0, 0, 0.15) 0 -36px 30px 0 inset, 22 | rgba(0, 0, 0, 0.1) 0 -79px 40px 0 inset, 23 | rgba(0, 0, 0, 0.06) 0 2px 1px, 24 | rgba(0, 0, 0, 0.09) 0 4px 2px, 25 | rgba(0, 0, 0, 0.09) 0 8px 4px, 26 | rgba(0, 0, 0, 0.09) 0 16px 8px, 27 | rgba(0, 0, 0, 0.09) 0 32px 16px; 28 | } 29 | -------------------------------------------------------------------------------- /src/sites/weread/utils.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | // 提前引入的库 3 | const cryptoJS = window.CryptoJS 4 | 5 | function md5(data) { 6 | return cryptoJS.MD5(data).toString() 7 | } 8 | 9 | /** 10 | * 计算参数的编码 11 | * @param {string} data 12 | * @return {string} 13 | */ 14 | function hash(data) { 15 | if (typeof data === "number") { 16 | data = data.toString(); 17 | } 18 | if (typeof data !== "string") { 19 | return data; 20 | } 21 | 22 | const dataMd5 = md5(data); 23 | let _0x38b4d1 = dataMd5.substr(0, 3); 24 | const _0x4718f7 = function (data) { 25 | if (/^\d*$/.test(data)) { 26 | const dataLen = data.length; 27 | const _0xd2c2b1 = []; 28 | for (let i = 0; i < dataLen; i += 9) { 29 | const _0x56eaa4 = data.slice(i, Math.min(i + 9, dataLen)); 30 | _0xd2c2b1.push(parseInt(_0x56eaa4).toString(16)); 31 | } 32 | return ["3", _0xd2c2b1]; 33 | } 34 | 35 | let _0x397242 = ""; 36 | for (let i = 0; i < data.length; i++) { 37 | _0x397242 += data.charCodeAt(i).toString(16); 38 | } 39 | return ["4", [_0x397242]]; 40 | }(data); 41 | 42 | _0x38b4d1 += _0x4718f7[0]; 43 | _0x38b4d1 += 2 + dataMd5.substr(dataMd5.length - 2, 2); 44 | 45 | const _0x1e41f3 = _0x4718f7[1]; 46 | for (let i = 0; i < _0x1e41f3.length; i++) { 47 | let _0x5c593c = _0x1e41f3[i].length.toString(16); 48 | 1 === _0x5c593c.length && (_0x5c593c = "0" + _0x5c593c); 49 | _0x38b4d1 += _0x5c593c; 50 | _0x38b4d1 += _0x1e41f3[i]; 51 | i < _0x1e41f3.length - 1 && (_0x38b4d1 += "g"); 52 | } 53 | 54 | if (_0x38b4d1.length < 20) { 55 | _0x38b4d1 += dataMd5.substr(0, 20 - _0x38b4d1.length); 56 | } 57 | 58 | return _0x38b4d1 + md5(_0x38b4d1).substr(0, 3); 59 | } 60 | 61 | window.wrx_weread_utils = { 62 | hash: hash, 63 | } 64 | })() 65 | -------------------------------------------------------------------------------- /src/sites/zhihu/auto.js: -------------------------------------------------------------------------------- 1 | function randomInteger(min, max) { 2 | const rand = min + Math.random() * (max + 1 - min); 3 | return Math.floor(rand); 4 | } 5 | function sleep(duration) { 6 | return new Promise(resolve => setTimeout(resolve, duration)) 7 | } 8 | 9 | let wantStop = false 10 | let isRunning = false 11 | let retry = 0 12 | 13 | const selectorMap = { 14 | prev: '#button-prev-chapter > a', 15 | next: '#button-next-chapter > a', 16 | } 17 | 18 | 19 | // 在页面添加一个按钮 20 | document.addEventListener('DOMContentLoaded', async () => { 21 | const span = document.createElement('span') 22 | span.textContent = '自动阅读: 关' 23 | span.id = '__wrx_auto_read__' 24 | span.style.position = 'fixed' 25 | span.style.top = '80px' 26 | span.style.right = '20px' 27 | span.style.fontSize = '16px' 28 | span.style.fontWeight = 'bold' 29 | span.style.background = '#e9e9e99c' 30 | span.style.color = '#a32727' 31 | span.style.padding = '.5em .75em' 32 | span.style.borderRadius = '5px' 33 | span.style.zIndex = '9999' 34 | document.body.appendChild(span) 35 | 36 | const storageUsage = await navigator.storage.estimate() 37 | const indexedSize = (storageUsage.usage / 1024 / 1024).toFixed(2) 38 | window.wrx_common_utils.log(`IndexedDB 使用了 %c~${indexedSize}M%c 存储空间`, 'color: red;font-size:16px;', '') 39 | }) 40 | 41 | function setAutoRunningStatus(open) { 42 | const span = document.querySelector('#__wrx_auto_read__') 43 | span.textContent = `自动阅读: ${open ? '开' : '关'}` 44 | if (open) { 45 | span.style.background = 'green' 46 | span.style.color = 'white' 47 | } else { 48 | span.style.background = '#e9e9e99c' 49 | span.style.color = '#a32727' 50 | } 51 | } 52 | 53 | function checkStop() { 54 | if (wantStop) { 55 | isRunning = false 56 | setAutoRunningStatus(isRunning) 57 | return true 58 | } 59 | return false 60 | } 61 | 62 | async function run(direction, interval, isScroll) { 63 | if (checkStop()) return 64 | 65 | isRunning = true 66 | setAutoRunningStatus(isRunning) 67 | 68 | const sectionEl = document.querySelector('.reader-container > .reader-chapter-content > div > section') 69 | let btn = document.querySelector(selectorMap[direction]) 70 | if (btn.classList.contains('disabled')) { 71 | // 翻到尽头了 72 | window.wrx_common_utils.log(`已经翻到${direction === 'prev' ? '第' : '最后'}一页了,正常结束`) 73 | isRunning = false 74 | setAutoRunningStatus(isRunning) 75 | return 76 | } else if (!sectionEl) { 77 | // 页面还没加载完成 78 | if (retry <= 10) { 79 | retry++ 80 | window.wrx_common_utils.log('页面没有加载完毕,2秒后重试') 81 | setTimeout(() => { 82 | run(direction, interval, isScroll) 83 | }, 2000) 84 | } else { 85 | window.wrx_common_utils.log('页面加载超时') 86 | isRunning = false 87 | setAutoRunningStatus(isRunning) 88 | } 89 | return 90 | } 91 | 92 | // 按钮出现,重置重试次数 93 | retry = 0 94 | if (isScroll) { 95 | await sleep(1000) 96 | if (checkStop()) return 97 | 98 | if (direction === 'prev') { 99 | window.scroll({top: 0, behavior: 'smooth'}) 100 | } else { 101 | window.scroll({top: 100000, behavior: 'smooth'}) 102 | } 103 | 104 | await sleep(1500) 105 | if (checkStop()) return 106 | } 107 | window.wrx_common_utils.log('开始翻页') 108 | btn.click() 109 | 110 | if (checkStop()) return 111 | const adjustInterval = randomInteger(interval * 1000, (interval + 1) * 1000 + 500) 112 | setTimeout(() => { 113 | run(direction, interval, isScroll) 114 | }, adjustInterval) 115 | window.wrx_common_utils.log(`${adjustInterval}ms 后执行`) 116 | } 117 | 118 | chrome.runtime.onMessage.addListener(async (msg) => { 119 | window.wrx_common_utils.log(msg) 120 | const {type, args = {}} = msg 121 | const {direction, interval, isScroll} = args 122 | 123 | switch (type) { 124 | case 'start': 125 | if (isRunning) { 126 | // 避免重复执行 127 | window.wrx_common_utils.log('正在运行中,不需要重复执行') 128 | return 129 | } 130 | wantStop = false 131 | await run(direction, interval, isScroll) 132 | break 133 | case 'stop': 134 | wantStop = true 135 | setAutoRunningStatus(false) 136 | break 137 | } 138 | }) 139 | -------------------------------------------------------------------------------- /src/sites/zhihu/content.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | await chrome.runtime.sendMessage({msg: 'updateIcon'}) 3 | 4 | // 接收 xhr 拦截的接口数据 5 | window.addEventListener('message', async ({data}) => { 6 | if (data && data.from === 'wrx') { 7 | console.log(data) 8 | 9 | switch (data.site) { 10 | case 'https://www.zhihu.com': 11 | await handleZhihuSite(data) 12 | break 13 | default: 14 | console.warn('未知网站数据: ', data) 15 | break 16 | } 17 | } 18 | }); 19 | 20 | // 处理知乎书店数据 21 | async function handleZhihuSite(data) { 22 | let bookId 23 | 24 | // console.log(data) 25 | const {pathname, search, request, response} = data 26 | } 27 | })(); 28 | -------------------------------------------------------------------------------- /src/sites/zhihu/store.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/src/sites/zhihu/store.js -------------------------------------------------------------------------------- /src/sites/zhihu/toc.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jooooock/wrx/a790942743ab7b7a733b26b01d4909244184c7fa/src/sites/zhihu/toc.css --------------------------------------------------------------------------------