├── .gitignore ├── LICENSE ├── README.md ├── README.zh-CN.md ├── babel.config.js ├── docs ├── css │ ├── app.9ddb1b8c.css │ └── chunk-vendors.b693c99f.css ├── favicon.ico ├── fonts │ ├── fa-regular-400.222941bf.woff │ ├── fa-regular-400.6bf2d6c8.ttf │ ├── fa-regular-400.914437d6.woff2 │ ├── fa-regular-400.d30b8018.eot │ ├── fa-solid-900.140f4148.woff │ ├── fa-solid-900.4910ec73.eot │ ├── fa-solid-900.813b8aee.woff2 │ └── fa-solid-900.e57e108a.ttf ├── img │ ├── fa-regular-400.47d19eca.svg │ ├── fa-solid-900.e98a92ac.svg │ └── logo.bef6bb2c.png ├── index.html ├── js │ ├── app.8044abf8.js │ ├── app.8044abf8.js.map │ ├── chunk-vendors.3956b645.js │ └── chunk-vendors.3956b645.js.map └── media │ ├── complete.908421c0.mp3 │ └── error.7090c0b8.mp3 ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── screenshot ├── downloading.png ├── downloading.zh-CN.png ├── settings.png ├── settings.zh-CN.png ├── torrent.png └── torrent.zh-CN.png ├── src ├── App.vue ├── assets │ ├── complete.mp3 │ ├── error.mp3 │ └── logo.png ├── components │ ├── Main.vue │ └── Main │ │ ├── Downloading.vue │ │ ├── Finished.vue │ │ ├── NewTask.vue │ │ ├── Settings.vue │ │ └── Task │ │ └── Task.vue ├── lang │ ├── en-US.json │ └── zh-CN.json ├── main.js ├── router │ └── index.js ├── styles │ ├── option.css │ └── toolbar.css └── utils │ ├── aria2manager.js │ ├── aria2rpc.js │ ├── aria2server.js │ ├── converter.js │ ├── filetypes.js │ └── jsonrpc.js └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Alan Zhang 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Languages: [English](https://github.com/alanzhangzm/Photon-WebUI) [中文](https://github.com/alanzhangzm/Photon-WebUI/blob/master/README.zh-CN.md) 2 | 3 | # Photon WebUI 4 | 5 | *Photon WebUI* is a modern frontend for [aria2](https://github.com/aria2/aria2). 6 | 7 | For desktop downloader, please have a look at [*Photon*](https://github.com/alanzhangzm/Photon). 8 | 9 | 10 | ## Use 11 | 12 | Visit https://github.com/zmzhang8/Photon-WebUI 13 | 14 | ## Feature 15 | 16 | - Manage multiple aria2 servers 17 | - BT: selective downloading 18 | 19 | 20 | ## Screenshots 21 | 22 | ![downloading](screenshot/downloading.png) 23 | ![settings](screenshot/settings.png) 24 | ![torrent](screenshot/torrent.png) 25 | 26 | ## Development 27 | 28 | ``` bash 29 | # install dependencies 30 | npm install 31 | 32 | # serve with hot reload at localhost:8080 33 | npm run serve 34 | 35 | # build for production with minification 36 | npm run build 37 | ``` 38 | 39 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 40 | 41 | 42 | ## License 43 | [Apache-2.0](https://github.com/alanzhangzm/Photon-WebUI/blob/master/LICENSE) 44 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | Languages: [English](https://github.com/alanzhangzm/Photon-WebUI) [中文](https://github.com/alanzhangzm/Photon-WebUI/blob/master/README.zh-CN.md) 2 | 3 | # Photon WebUI 4 | 5 | *Photon WebUI* 是一款管理 [aria2](https://github.com/aria2/aria2) 的网页前端。 6 | 7 | 如果你想要一个桌面下载器,请尝试 [*Photon*](https://github.com/alanzhangzm/Photon/blob/master/README.zh-CN.md)。 8 | 9 | # 特色 10 | - 管理多个 aria2 服务器 11 | - BT:支持选择文件下载 12 | 13 | 14 | ## 使用 15 | 16 | 访问 https://github.com/zmzhang8/Photon-WebUI 17 | 18 | 19 | ## 截图 20 | 21 | ![downloading](screenshot/downloading.zh-CN.png) 22 | ![settings](screenshot/settings.zh-CN.png) 23 | ![torrent](screenshot/torrent.zh-CN.png) 24 | 25 | ## 许可证 26 | [Apache-2.0](https://github.com/alanzhangzm/Photon-WebUI/blob/master/LICENSE) 27 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/css/app.9ddb1b8c.css: -------------------------------------------------------------------------------- 1 | .sidebar[data-v-318adde5]{min-width:200px;max-width:200px;min-height:100%;max-height:100%;position:fixed;top:0;left:0;background-color:#444;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.profile_active[data-v-318adde5]{background-color:#666}.router-link-active[data-v-318adde5]{background-color:#00a0f1}.sidebar .row[data-v-318adde5]{padding:12px 12px;color:#ddd;text-decoration:none;font-size:16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.row>.icon[data-v-318adde5]{padding:0 8px;-webkit-box-flex:0;-ms-flex:0 0 20px;flex:0 0 20px;font-size:20px}.row>.title[data-v-318adde5]{padding:0 4px;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.row>.status[data-v-318adde5]{padding-right:4px;font-size:12px;-webkit-box-flex:0;-ms-flex:0 0 28px;flex:0 0 28px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:right;-ms-flex-pack:right;justify-content:right}.bubble[data-v-318adde5]{min-width:16px;padding:4px;background-color:#ddd;color:#444;border-radius:16px;text-align:center;font-weight:600}.logo[data-v-318adde5]{height:40px;padding:0 8px}.seperator-v[data-v-318adde5]{margin:16px 16px;border:1px solid #666}.main[data-v-318adde5]{margin-left:200px;font-size:20px}.row[data-v-8497525a]{height:60px;padding:0 8px;border-bottom:1px solid #ccc;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:16px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.row[data-v-8497525a]:hover{text-decoration:none;cursor:default}.row>.col-status[data-v-8497525a]{-webkit-box-flex:0;-ms-flex:0 0 32px;flex:0 0 32px;font-size:20px;text-align:center;color:#666}.row>.col-info[data-v-8497525a]{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;-ms-flex-direction:column;flex-direction:column;overflow:hidden}.row>.col-info[data-v-8497525a],.row>.col-progress[data-v-8497525a]{padding:0 8px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal}.row>.col-progress[data-v-8497525a]{-webkit-box-flex:0;-ms-flex:0 0 140px;flex:0 0 140px;-ms-flex-direction:column;flex-direction:column}.row>.col-speed[data-v-8497525a]{-webkit-box-flex:0;-ms-flex:0 0 100px;flex:0 0 100px;padding:0 8px;text-align:right}.title[data-v-8497525a]{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.detail[data-v-8497525a]{margin-top:4px;color:#666;font-size:12px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.detail-left[data-v-8497525a]{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.progress-bar[data-v-8497525a]{height:8px;border:1px solid #ccc;border-radius:8px;margin:4px 0}.progress-bar .progress[data-v-8497525a]{background-color:#0098ff;height:100%;width:0;border-radius:8px;-webkit-transition:width .6s ease;transition:width .6s ease}.progress-bar .progress-success[data-v-8497525a]{background-color:#0d0}.selected[data-v-8497525a]{background-color:#d6ecff}.toolbar[data-v-2b5d3450]{height:48px;position:-webkit-sticky;position:sticky;top:0;padding:0 8px;border-bottom:2px solid #d3d3d3;color:#333;background-color:#fff;font-size:24px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.toolbar>a[data-v-2b5d3450]{-webkit-box-flex:0;-ms-flex:0 0 48px;flex:0 0 48px;line-height:48px;text-align:center;color:#333}.toolbar>.disabled[data-v-2b5d3450]{opacity:.4;cursor:default;pointer-events:none}.seperator-h[data-v-2b5d3450]{margin:12px 12px;border:1px dashed #aaa}.toolbar[data-v-72c5201f]{height:48px;position:-webkit-sticky;position:sticky;top:0;padding:0 8px;border-bottom:2px solid #d3d3d3;color:#333;background-color:#fff;font-size:24px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.toolbar>a[data-v-72c5201f]{-webkit-box-flex:0;-ms-flex:0 0 48px;flex:0 0 48px;line-height:48px;text-align:center;color:#333}.toolbar>.disabled[data-v-72c5201f]{opacity:.4;cursor:default;pointer-events:none}.seperator-h[data-v-72c5201f]{margin:12px 12px;border:1px dashed #aaa}.toolbar[data-v-b9e39992]{height:48px;position:-webkit-sticky;position:sticky;top:0;padding:0 8px;border-bottom:2px solid #d3d3d3;color:#333;background-color:#fff;font-size:24px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.toolbar>a[data-v-b9e39992]{-webkit-box-flex:0;-ms-flex:0 0 48px;flex:0 0 48px;line-height:48px;text-align:center;color:#333}.toolbar>.disabled[data-v-b9e39992]{opacity:.4;cursor:default;pointer-events:none}.seperator-h[data-v-b9e39992]{margin:12px 12px;border:1px dashed #aaa}.group[data-v-b9e39992]{padding:8px 0;border-bottom:1px solid #d3d3d3}.header[data-v-b9e39992]{padding:8px 16px;font-size:16px;font-weight:700}.row[data-v-b9e39992]{padding:8px 16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.row>.left[data-v-b9e39992]{-webkit-box-flex:0;-ms-flex:0 0 140px;flex:0 0 140px;padding:0 8px}.row>.right[data-v-b9e39992]{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:0 8px}.pair[data-v-b9e39992]{display:-webkit-box;display:-ms-flexbox;display:flex;margin:0 -4px}.pair>.fixed[data-v-b9e39992]{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin:0 4px}.pair>.expanded[data-v-b9e39992]{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;margin:0 4px}label[data-v-b9e39992]{display:block;font-size:16px;text-align:right}input[data-v-b9e39992],select[data-v-b9e39992],textarea[data-v-b9e39992]{padding:8px 12px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #ccc;border-radius:4px;outline:none;font-size:16px}textarea[data-v-b9e39992]{height:160px;resize:vertical}input[type=password][data-v-b9e39992],input[type=text][data-v-b9e39992],textarea[data-v-b9e39992]{width:100%}input[type=number][data-v-b9e39992],select[data-v-b9e39992]{min-width:80px}input[data-v-b9e39992]:focus,textarea[data-v-b9e39992]:focus{border:1px solid #666}input[data-v-b9e39992]:invalid{border:1px solid red}input[data-v-b9e39992]:disabled{border:1px solid #ddd;color:#444;opacity:.5;pointer-events:none}.button[data-v-b9e39992]{padding:8px;border:1px solid #ccc;border-radius:4px;background-color:#fafafa;text-align:center;font-size:16px}.button[data-v-b9e39992]:hover{text-decoration:none;cursor:pointer;border:1px solid #666}.button-large[data-v-b9e39992]{min-width:64px;padding:8px 8px;font-size:20px}.vspace[data-v-b9e39992]{margin:12px 0 0 0}.hspace[data-v-b9e39992]{margin:0 0 0 16px}.hidden[data-v-b9e39992]{display:none}.disabled[data-v-b9e39992]{opacity:.5;cursor:default;pointer-events:none}.badge[data-v-b9e39992]{padding:4px;border-radius:4px;font-size:16px}.badge-success[data-v-b9e39992]{background-color:#00b246;color:#fff}.badge-danger[data-v-b9e39992]{background-color:#e30034;color:#fff}.group[data-v-02f53484]{padding:8px 0;border-bottom:1px solid #d3d3d3}.header[data-v-02f53484]{padding:8px 16px;font-size:16px;font-weight:700}.row[data-v-02f53484]{padding:8px 16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.row>.left[data-v-02f53484]{-webkit-box-flex:0;-ms-flex:0 0 140px;flex:0 0 140px;padding:0 8px}.row>.right[data-v-02f53484]{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:0 8px}.pair[data-v-02f53484]{display:-webkit-box;display:-ms-flexbox;display:flex;margin:0 -4px}.pair>.fixed[data-v-02f53484]{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin:0 4px}.pair>.expanded[data-v-02f53484]{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;margin:0 4px}label[data-v-02f53484]{display:block;font-size:16px;text-align:right}input[data-v-02f53484],select[data-v-02f53484],textarea[data-v-02f53484]{padding:8px 12px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #ccc;border-radius:4px;outline:none;font-size:16px}textarea[data-v-02f53484]{height:160px;resize:vertical}input[type=password][data-v-02f53484],input[type=text][data-v-02f53484],textarea[data-v-02f53484]{width:100%}input[type=number][data-v-02f53484],select[data-v-02f53484]{min-width:80px}input[data-v-02f53484]:focus,textarea[data-v-02f53484]:focus{border:1px solid #666}input[data-v-02f53484]:invalid{border:1px solid red}input[data-v-02f53484]:disabled{border:1px solid #ddd;color:#444;opacity:.5;pointer-events:none}.button[data-v-02f53484]{padding:8px;border:1px solid #ccc;border-radius:4px;background-color:#fafafa;text-align:center;font-size:16px}.button[data-v-02f53484]:hover{text-decoration:none;cursor:pointer;border:1px solid #666}.button-large[data-v-02f53484]{min-width:64px;padding:8px 8px;font-size:20px}.vspace[data-v-02f53484]{margin:12px 0 0 0}.hspace[data-v-02f53484]{margin:0 0 0 16px}.hidden[data-v-02f53484]{display:none}.disabled[data-v-02f53484]{opacity:.5;cursor:default;pointer-events:none}table[data-v-02f53484]{table-layout:fixed;width:100%;text-align:left;margin:auto;font-size:14px;color:#444}td[data-v-02f53484],th[data-v-02f53484]{padding:4px 8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}th[data-v-02f53484]{position:-webkit-sticky;position:sticky;top:0;border-top:1 solid #ddd;border-bottom:1 solid #ddd;-webkit-box-shadow:0 1px #ddd;box-shadow:0 1px #ddd;background:#fff}.scroll-div[data-v-02f53484]{max-height:55vh;overflow-y:auto;border:1px solid #ddd}.footnote[data-v-02f53484]{padding:8px 0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#aaa;font-size:16px}.footnote>.left[data-v-02f53484]{-webkit-box-flex:0;-ms-flex:0 0 140px;flex:0 0 140px}.footnote>.right[data-v-02f53484]{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:right}.footnote i[data-v-02f53484]{padding:0 4px;color:#888;font-size:20px}body{margin:0;background-color:#fff}body,button,input,output,select,textarea{font-family:Helvetica Neue,Helvetica,Arial,Dejavu Sans,PingFang SC,Hiragino Sans GB,Heiti SC,Microsoft YaHei,WenQuanYi Micro Hei,Noto Sans,sans-serif} -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/favicon.ico -------------------------------------------------------------------------------- /docs/fonts/fa-regular-400.222941bf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-regular-400.222941bf.woff -------------------------------------------------------------------------------- /docs/fonts/fa-regular-400.6bf2d6c8.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-regular-400.6bf2d6c8.ttf -------------------------------------------------------------------------------- /docs/fonts/fa-regular-400.914437d6.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-regular-400.914437d6.woff2 -------------------------------------------------------------------------------- /docs/fonts/fa-regular-400.d30b8018.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-regular-400.d30b8018.eot -------------------------------------------------------------------------------- /docs/fonts/fa-solid-900.140f4148.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-solid-900.140f4148.woff -------------------------------------------------------------------------------- /docs/fonts/fa-solid-900.4910ec73.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-solid-900.4910ec73.eot -------------------------------------------------------------------------------- /docs/fonts/fa-solid-900.813b8aee.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-solid-900.813b8aee.woff2 -------------------------------------------------------------------------------- /docs/fonts/fa-solid-900.e57e108a.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/fonts/fa-solid-900.e57e108a.ttf -------------------------------------------------------------------------------- /docs/img/logo.bef6bb2c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/img/logo.bef6bb2c.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | photon-webui
-------------------------------------------------------------------------------- /docs/js/app.8044abf8.js: -------------------------------------------------------------------------------- 1 | (function(t){function e(e){for(var n,r,o=e[0],l=e[1],c=e[2],u=0,f=[];u1&&n===t.manager.serverIndex},on:{click:function(e){return t.setServerIndex(n)}}},[t._m(0,!0),s("div",{staticClass:"title",staticStyle:{"font-size":"20px","font-weight":"bold",cursor:"default"}},[t._v(t._s(t.isDesktop&&0===n?"Photon":e.name))])])])})),s("div",{staticClass:"seperator-v"}),s("router-link",{staticClass:"row",attrs:{to:"/downloading",id:"sidebar-downloading"}},[s("div",{staticClass:"icon"},[s("i",{staticClass:"fas fa-arrow-down"})]),s("div",{staticClass:"title"},[t._v(t._s(t.$t("message.main.downloading")))]),t.downloadingNumber?s("div",{staticClass:"status"},[s("span",{staticClass:"bubble"},[t._v(t._s(t.downloadingNumber))])]):t._e()]),s("router-link",{staticClass:"row",attrs:{to:"/finished",id:"sidebar-finished"}},[s("div",{staticClass:"icon"},[s("i",{staticClass:"fas fa-check"})]),s("div",{staticClass:"title"},[t._v(t._s(t.$t("message.main.finished")))])]),s("router-link",{staticClass:"row",attrs:{to:"/settings",id:"sidebar-settings"},nativeOn:{click:function(e){return t.syncOptions()}}},[s("div",{staticClass:"icon"},[s("i",{staticClass:"fas fa-cog"})]),s("div",{staticClass:"title"},[t._v(t._s(t.$t("message.main.settings")))])])],2),s("div",{staticClass:"main"},[s("router-view",{attrs:{downloading:t.downloading,finished:t.finished,settings:t.settings,connection:t.server.connection,alias:t.server.name,rpc:t.server.rpc,options:t.server.options},on:{addTask:function(e){return t.addTask(e)},changeTaskStatus:function(e){return t.changeTaskStatus(e)},purgeTasks:function(e){return t.purgeTasks(e)},addServer:function(e){return t.addServer()},removeServer:function(e){return t.removeServer()},updateSettings:function(e){return t.updateSettings()}}})],1)])},i=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"icon",staticStyle:{padding:"0"}},[n("img",{staticClass:"logo",attrs:{src:s("cf05")}})])}],a=(s("7f7f"),{props:["manager","isDesktop"],computed:{server:function(){return this.manager.servers[this.manager.serverIndex]},isDefault:function(){return 0===this.manager.serverIndex},downloadingNumber:function(){var t=this.server.tasks;return t.active.length+t.waiting.length},downloading:function(){var t=this.server.tasks;return t.active.concat(t.waiting).concat(t.paused)},finished:function(){return this.server.tasks.stopped},settings:function(){var t=this.server;return{name:t.name,connection:t.connection,rpc:JSON.parse(JSON.stringify(t.rpc)),options:JSON.parse(JSON.stringify(t.options)),isDefault:this.isDefault,isDesktop:this.isDesktop}}},methods:{syncOptions:function(){this.server.checkConnection(),this.server.syncOptions()},addTask:function(t){this.server.addTask(t)},changeTaskStatus:function(t){this.server.changeTaskStatus(t.method,t.gids)},purgeTasks:function(t){this.server.purgeTasks(t)},addServer:function(){this.manager.addServer(),this.manager.serverIndex=this.manager.servers.length-1,this.updateSettings()},removeServer:function(){this.manager.removeServer(),this.updateSettings()},setServerIndex:function(t){this.manager.setServerIndex(t)},updateSettings:function(){var t=this.server,e=this.settings;t.setServer(e.name,e.rpc,e.options,!this.isDefault),this.manager.writeStorage()}}}),r=a,o=(s("f706"),s("154e"),s("37a3"),s("2877")),l=Object(o["a"])(r,n,i,!1,null,"318adde5",null);e["default"]=l.exports},"2af1":function(t,e,s){},3:function(t,e){},"37a3":function(t,e,s){"use strict";var n=s("793c"),i=s.n(n);i.a},"3a1d":function(t,e,s){"use strict";s.r(e);var n=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{attrs:{id:"new-task"}},[s("div",{staticClass:"group"},[s("div",{staticClass:"header"},[t._v(t._s(t.$t("message.newTask.task")))]),t.file?t._e():s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"new-task-urls"}},[t._v(t._s(t.$t("message.newTask.urls")))])]),s("div",{staticClass:"right"},[s("textarea",{directives:[{name:"model",rawName:"v-model",value:t.uris,expression:"uris"}],attrs:{id:"new-task-urls"},domProps:{value:t.uris},on:{input:function(e){e.target.composing||(t.uris=e.target.value)}}})])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"new-task-bt"}},[t._v(t._s(t.$t("message.newTask.btMetalink")))])]),s("div",{staticClass:"right pair"},[s("label",{staticClass:"button fixed",attrs:{for:"new-task-bt-choose"}},[t._v(t._s(t.$t("message.newTask.choose")))]),s("input",{staticClass:"hidden",attrs:{id:"new-task-bt-choose",type:"file",accept:".torrent, .metalink, .meta4"},on:{change:function(e){return t.readFile(e)}}}),s("input",{directives:[{name:"model",rawName:"v-model",value:t.filename,expression:"filename"}],staticClass:"expanded",attrs:{type:"text",disabled:""},domProps:{value:t.filename},on:{input:function(e){e.target.composing||(t.filename=e.target.value)}}})])]),t.filesInTorrent.length?s("div",{staticClass:"row"},[s("div",{staticClass:"left"}),s("div",{staticClass:"right"},[s("div",{staticClass:"scroll-div"},[s("table",[s("thead",[s("th",{staticStyle:{width:"20px"}},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.selectAll,expression:"selectAll"}],attrs:{id:"file-selection-all",type:"checkbox"},domProps:{checked:Array.isArray(t.selectAll)?t._i(t.selectAll,null)>-1:t.selectAll},on:{change:function(e){var s=t.selectAll,n=e.target,i=!!n.checked;if(Array.isArray(s)){var a=null,r=t._i(s,a);n.checked?r<0&&(t.selectAll=s.concat([a])):r>-1&&(t.selectAll=s.slice(0,r).concat(s.slice(r+1)))}else t.selectAll=i}}})]),s("th",[t._v(t._s(t.$t("message.newTask.filename")))]),s("th",{staticStyle:{width:"60px"}},[t._v(t._s(t.$t("message.newTask.filetype")))]),s("th",{staticStyle:{width:"60px"}},[t._v(t._s(t.$t("message.newTask.size")))])]),t._l(t.filesInTorrent,(function(e){return s("tbody",{key:e.index},[s("td",{staticStyle:{width:"20px"}},[s("input",{directives:[{name:"model",rawName:"v-model",value:e.selected,expression:"file.selected"}],attrs:{type:"checkbox"},domProps:{checked:Array.isArray(e.selected)?t._i(e.selected,null)>-1:e.selected},on:{change:function(s){var n=e.selected,i=s.target,a=!!i.checked;if(Array.isArray(n)){var r=null,o=t._i(n,r);i.checked?o<0&&t.$set(e,"selected",n.concat([r])):o>-1&&t.$set(e,"selected",n.slice(0,o).concat(n.slice(o+1)))}else t.$set(e,"selected",a)}}})]),s("td",[t._v(t._s(e.name))]),s("td",{staticStyle:{width:"60px"}},[t._v(t._s(e.extension))]),s("td",{staticStyle:{width:"60px"}},[t._v(t._s(t.bytesToString(e.size,1)))])])}))],2)]),s("div",{staticClass:"footnote"},[s("div",{staticClass:"left"},[s("a",{attrs:{href:"#"},on:{click:function(e){return t.selectVideos()}}},[s("i",{staticClass:"fas fa-file-video"})]),s("a",{attrs:{href:"#"},on:{click:function(e){return t.selectAudios()}}},[s("i",{staticClass:"fas fa-file-audio"})]),s("a",{attrs:{href:"#"},on:{click:function(e){return t.selectImages()}}},[s("i",{staticClass:"fas fa-file-image"})])]),s("div",{staticClass:"right"},[t._v(t._s(t.selectedFilesCount)+"/"+t._s(t.allFilesCount)+", "+t._s(t.bytesToString(t.selectedFilesSize,1))+"/"+t._s(t.bytesToString(t.allFilesSize,1)))])])])]):t._e(),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"new-task-seeding"}},[t._v(t._s(t.$t("message.newTask.seeding")))])]),s("div",{staticClass:"right"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.seeding,expression:"seeding"}],attrs:{id:"new-task-seeding",type:"checkbox"},domProps:{checked:Array.isArray(t.seeding)?t._i(t.seeding,null)>-1:t.seeding},on:{change:function(e){var s=t.seeding,n=e.target,i=!!n.checked;if(Array.isArray(s)){var a=null,r=t._i(s,a);n.checked?r<0&&(t.seeding=s.concat([a])):r>-1&&(t.seeding=s.slice(0,r).concat(s.slice(r+1)))}else t.seeding=i}}})])])]),s("div",{staticClass:"row vspace"},[s("div",{staticClass:"button button-large",class:{disabled:!t.uris&&!t.file},on:{click:function(e){return t.start()}}},[t._v(t._s(t.$t("message.newTask.start")))]),s("div",{staticClass:"button button-large hspace",on:{click:function(e){return t.cancel()}}},[t._v(t._s(t.$t("message.newTask.cancel")))])])])},i=[],a=(s("a481"),s("7f7f"),s("aef6"),s("6762"),s("2fdb"),s("ac6a"),s("28a5"),s("b944")),r=s.n(a),o=s("fbd9"),l=[".ai",".bmp",".eps",".gif",".icn",".ico",".jpeg",".jpg",".png",".psd",".raw",".sketch",".svg",".tif",".webp",".xd"],c=[".aac",".ape",".flac",".flav",".m4a",".mp3",".ogg",".wav",".wma"],d=[".avi",".m4a",".mkv",".mov",".mp4",".mpg",".rmvb",".vob",".wmv"],u={data:function(){return{type:"http",uris:"",file:void 0,filename:"",seeding:!0,filesInTorrent:[]}},computed:{urlList:function(){return this.uris?this.uris.split(/\n+/):[]},selectAll:{get:function(){return this.filesInTorrent&&this.filesInTorrent.length>0&&this.filesInTorrent.reduce((function(t,e){return t&&e.selected}),!0)},set:function(){var t=!this.selectAll;this.filesInTorrent.forEach((function(e){e.selected=t}))}},selectedFiles:function(){return this.filesInTorrent.filter((function(t){return t.selected}))},selectedFilesCount:function(){return this.selectedFiles.length},selectedFilesSize:function(){return this.selectedFiles.reduce((function(t,e){return t+e.size}),0)},allFilesCount:function(){return this.filesInTorrent.length},allFilesSize:function(){return this.filesInTorrent.reduce((function(t,e){return t+e.size}),0)}},methods:{bytesToString:o["a"].bytesToString,selectImages:function(){this.filesInTorrent.forEach((function(t){t.selected=l.includes("."+t.extension)}))},selectAudios:function(){this.filesInTorrent.forEach((function(t){t.selected=c.includes("."+t.extension)}))},selectVideos:function(){this.filesInTorrent.forEach((function(t){t.selected=d.includes("."+t.extension)}))},readFile:function(t){var e=this,s=t.target.files;if(s.length){var n=s[0];if(e.filename=n,n.name.endsWith(".torrent")?e.type="torrent":(n.name.endsWith(".metalink")||n.name.endsWith(".meta4"))&&(e.type="metalink"),"torrent"===e.type||"metalink"===e.type){var i=new FileReader;i.onload=function(t){e.file=t.target.result.replace(/^.*base64,/,""),e.filename=n.name},i.onerror=function(t){console.error(t.message),e.file=void 0,e.filename="",e.type="http"},i.readAsDataURL(n),"torrent"===e.type&&r.a.remote(n,(function(t,s){t?(e.filesInTorrent=[],console.error(t)):e.filesInTorrent=s.files.map((function(t,e){return{index:e+1,name:t.name,extension:t.name.includes(".")?t.name.split(".").pop():"",size:t.length,selected:!0}}))}))}}},start:function(){if("torrent"!==this.type||0!==this.selectedFiles.length){var t={type:this.type,uris:this.urlList,file:this.file,seeding:this.seeding};this.selectedFiles.length!==this.filesInTorrent.length&&(t["selectfile"]=this.selectedFiles.map((function(t){return t.index})).join(",")),this.$emit("addTask",t),this.$router.push("/downloads")}},cancel:function(){this.$router.push("/downloads")}}},f=u,p=(s("9e24"),s("1b74"),s("07e5"),s("88a7"),s("2877")),h=Object(p["a"])(f,n,i,!1,null,"02f53484",null);e["default"]=h.exports},"4a34":function(t,e,s){"use strict";s.r(e);var n=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{attrs:{id:"downloading"}},[s("div",{staticClass:"toolbar"},[s("router-link",{attrs:{to:"/newTask"}},[s("i",{staticClass:"fas fa-plus"})]),s("div",{staticClass:"seperator-h"}),s("a",{class:{disabled:0===t.selectedList.length},attrs:{href:"#"},on:{click:function(e){return t.changeTaskStatus({method:"unpause"})}}},[s("i",{staticClass:"fas fa-play"})]),s("a",{class:{disabled:0===t.selectedList.length},attrs:{href:"#"},on:{click:function(e){return t.changeTaskStatus({method:"pause"})}}},[s("i",{staticClass:"fas fa-pause"})]),s("a",{class:{disabled:0===t.selectedList.length},attrs:{href:"#"},on:{click:function(e){return t.changeTaskStatus({method:"remove"})}}},[s("i",{staticClass:"fas fa-times"})]),s("a",{attrs:{href:"#"},on:{click:function(e){return t.selectAll()}}},[s("i",{staticClass:"fas fa-list"})])],1),s("div",{staticClass:"content"},t._l(t.downloading,(function(e){return s("task",{key:e.gid+e.status,attrs:{selected:t.selected[e.gid],gid:e.gid,status:e.status,alias:e.name,totalLength:e.totalLength,completedLength:e.completedLength,uploadLength:e.uploadLength,downloadSpeed:e.downloadSpeed,uploadSpeed:e.uploadSpeed,connections:e.connections},on:{selectTask:function(e){return t.selectTask(e)},multiSelectTask:function(e){return t.multiSelectTask(e)},changeTaskStatus:function(e){return t.changeTaskStatus(e)}}})})),1)])},i=[],a=(s("ac6a"),s("456d"),s("97b6")),r={components:{Task:a["a"]},props:["downloading"],data:function(){return{selected:{}}},computed:{selectedList:function(){var t=this;return Object.keys(this.selected).filter((function(e){return t.selected[e]}))}},methods:{selectTask:function(t){this.selected={},this.$set(this.selected,t,!0)},multiSelectTask:function(t){var e=this.selected;e[t]?this.$set(e,t,!1):this.$set(e,t,!0)},selectAll:function(){var t=this;this.selectedList.length===this.downloading.length?this.selected={}:this.downloading.forEach((function(e){t.$set(t.selected,e.gid,!0)}))},changeTaskStatus:function(t){this.$emit("changeTaskStatus",{method:t.method,gids:t.gids||this.selectedList}),this.selected={}}}},o=r,l=(s("f117"),s("0131"),s("ec1a"),s("2877")),c=Object(l["a"])(o,n,i,!1,null,"2b5d3450",null);e["default"]=c.exports},"4bef":function(t){t.exports={main:{downloading:"Downloading",finished:"Finished",settings:"Settings"},newTask:{task:"Task",urls:"URLs",btMetalink:"BT / Metalink",seeding:"Seeding",choose:"Choose",start:"Start",cancel:"Cancel",filename:"File Name",filetype:"Type",size:"Size"},settings:{general:"General",profile:"Profile",rpc:"RPC",host:"Host",port:"Port",token:"Token",encryption:"SSL / TLS",status:"Status",connected:"Connected",disconnected:"Not Connected",download:"Download",directory:"Directory",choose:"Choose",maxDownloading:"Max Downloading",downloadLimit:"Download Limit",uploadLimit:"Upload Limit"}}},"56d7":function(t,e,s){"use strict";s.r(e);s("cadf"),s("551c"),s("f751"),s("097d");var n=s("a026"),i=s("a925"),a=s("8c4f");n["a"].use(a["a"]);var r=new a["a"]({routes:[{path:"/",name:"main",component:s("2614").default,children:[{path:"downloading",name:"downloading",component:s("4a34").default},{path:"finished",name:"finished",component:s("8ae0").default},{path:"settings",name:"settings",component:s("a541").default},{path:"newTask",name:"newTask",component:s("3a1d").default},{path:"*",redirect:"downloading"}]},{path:"*",redirect:"/"}]}),o=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{attrs:{id:"app"}},[s("router-view",{attrs:{manager:t.manager,isDesktop:!1}})],1)},l=[],c={name:"photon",props:["manager"]},d=c,u=(s("034f"),s("2877")),f=Object(u["a"])(d,o,l,!1,null,null,null),p=f.exports,h=(s("ac6a"),s("7f7f"),s("d225")),v=s("b0b4"),g=(s("a481"),s("6b54"),s("456d"),function(){function t(e){var s=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2?arguments[2]:void 0;Object(h["a"])(this,t),this.namespace=n,this.setAddress(e,s)}return Object(v["a"])(t,[{key:"setAddress",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this._url=(e?"https://":"http://")+t}},{key:"request",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],s=arguments.length>2?arguments[2]:void 0,n=arguments.length>3?arguments[3]:void 0,i=arguments.length>4?arguments[4]:void 0,a=this._formatData(t,e,s);this._fetch(this._url,a,n,i)}},{key:"batchRequest",value:function(t,e,s){var n=this;t.constructor!==Array&&(t=[t]);var i=t.map((function(t){return n._formatData(t.method,t.params,t.id)}));this._fetch(this._url,i,e,s)}},{key:"_formatData",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return{jsonrpc:"2.0",id:s,method:this.namespace+"."+t,params:e.constructor===Array?e:[e]}}},{key:"_fetch",value:function(t,e,s,n){fetch(t,{method:"POST",body:JSON.stringify(e)}).then((function(t){if(!t.ok)throw Error(t.status+" "+t.statusText);return t.json()})).then((function(t){"function"===typeof s&&s(t)})).catch((function(t){console.error("[fetch error]: "+t.message),"function"===typeof n&&n(t)}))}}]),t}()),m=function(){function t(e){var s=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2?arguments[2]:void 0;Object(h["a"])(this,t),this.namespace=n,this._listeners={},this.setAddress(e,s)}return Object(v["a"])(t,[{key:"setAddress",value:function(t,e){if(this._handles={},"function"!==typeof WebSocket)throw Error("This client does not support WebSocket.");var s=(e?"wss://":"ws://")+t;try{this._socket=new WebSocket(s);var n=this;this._socket.onclose=function(s){setTimeout((function(){n._socket.readyState>1&&n.setAddress(t,e)}),1e4)},this._socket.onerror=function(t){return n._onerror(t,n._handles)},this._socket.onmessage=function(t){return n._onmessage(t,n._handles,n._listeners)}}catch(i){console.error(i.message)}}},{key:"addListener",value:function(t,e){"function"===typeof e&&(this._listeners[this.namespace+"."+t]=e)}},{key:"removeListener",value:function(t){delete this._listeners[this.namespace+"."+t]}},{key:"request",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],s=arguments.length>2?arguments[2]:void 0,n=arguments.length>3?arguments[3]:void 0,i=arguments.length>4?arguments[4]:void 0;this._handles[s]={success:n,error:i};var a=this._formatData(t,e,s);this._send(a)}},{key:"batchRequest",value:function(t,e,s){var n=this;t.constructor!==Array&&(t=[t]),t.forEach((function(t){n._handles[t.id]={success:e,error:s}}));var i=t.map((function(t){return n._formatData(t.method,t.params,t.id)}));this._send(i)}},{key:"_formatData",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return{jsonrpc:"2.0",id:s,method:this.namespace+"."+t,params:e.constructor===Array?e:[e]}}},{key:"_send",value:function(t){var e=this,s=this._socket;s.readyState>1?s.onerror(Error("WebSocket is in state "+s.readyState+".")):0===s.readyState?setTimeout((function(){return e._send(t)}),1e3):s.send(JSON.stringify(t))}},{key:"_onerror",value:function(t,e){t.hasOwnProperty("message")&&console.error(t.message),Object.keys(e).forEach((function(s){"function"===typeof e[s].error&&e[s].error(t),delete e[s]}))}},{key:"_onmessage",value:function(t,e,s){var n=JSON.parse(t.data);if(n.constructor===Array)for(var i in n=n.reduce((function(t,e){return t.hasOwnProperty(e.id)?t[e.id].push(e):t[e.id]=[e],t}),{}),n)e.hasOwnProperty(i)&&("function"===typeof e[i].success&&e[i].success(n[i]),delete e[i]);else n.hasOwnProperty("id")?e.hasOwnProperty(n.id)&&("function"===typeof e[n.id].success&&e[n.id].success(n),delete e[n.id]):n.hasOwnProperty("method")&&s.hasOwnProperty(n.method)&&"function"===typeof s[n.method]&&s[n.method](n)}}]),t}(),y=1e3,k=["gid","status","totalLength","completedLength","uploadLength","downloadSpeed","uploadSpeed","connections","dir","files","bittorrent","errorCallbackCode","errorCallbackMessage"],w=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"127.0.0.1",s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:6800,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",i=arguments.length>3&&void 0!==arguments[3]&&arguments[3];Object(h["a"])(this,t),this._date=new Date,this.setRPC(e,s,n,i)}return Object(v["a"])(t,[{key:"setRPC",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"127.0.0.1",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:6800,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];this._token="token:"+s,this._address=t+":"+e+"/jsonrpc",this._rpc?this._rpc.setAddress(this._address,n):this._rpc=new g(this._address,n,"aria2")}},{key:"addUri",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=arguments.length>2?arguments[2]:void 0,n=arguments.length>3?arguments[3]:void 0,i="addUri";t.constructor!==Array&&(t=[t]);var a=t.map((function(t){return[t.constructor===Array?t:[t],e]}));this._batchRequest(i,a,s,n)}},{key:"addTorrent",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=arguments.length>2?arguments[2]:void 0,n=arguments.length>3?arguments[3]:void 0,i="addTorrent";this._request(i,[t,[],e],s,n)}},{key:"addMetalink",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=arguments.length>2?arguments[2]:void 0,n=arguments.length>3?arguments[3]:void 0,i="addMetalink";this._request(i,[t,e],s,n)}},{key:"tellStatus",value:function(t,e,s){var n="tellStatus";t.constructor!==Array&&(t=[t]);var i=t.map((function(t){return[t,k]}));this._batchRequest(n,i,e,s)}},{key:"tellActive",value:function(t,e){var s="tellActive";this._request(s,[k],t,e)}},{key:"tellWaiting",value:function(t,e){var s="tellWaiting";this._request(s,[0,y,k],t,e)}},{key:"tellStopped",value:function(t,e){var s="tellStopped";this._request(s,[0,y,k],t,e)}},{key:"changeGlobalOption",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1?arguments[1]:void 0,s=arguments.length>2?arguments[2]:void 0,n="changeGlobalOption";this._request(n,[t],e,s)}},{key:"_addListener",value:function(t,e){var s=this._responseHandler;this._rpc.constructor===m&&this._rpc.addListener(t,(function(n){s(t,n,e)}))}},{key:"_request",value:function(t,e,s,n){var i=this._responseHandler,a=t+"."+this._date.getTime();this._rpc.request(t,[this._token].concat(e),a,(function(e){i(t,e,s,n)}),n)}},{key:"_batchRequest",value:function(t,e,s,n){var i=this,a=t+"."+this._date.getTime(),r=e.map((function(e){return{method:t,params:[i._token].concat(e),id:a}})),o=this._responseHandler;this._rpc.batchRequest(r,(function(e){o(t,e,s,n)}),n)}},{key:"_responseHandler",value:function(t,e,s,n){if(e.constructor===Array){var i=e.filter((function(t){return t.hasOwnProperty("error")}));i.forEach((function(s){console.warn("[aria2."+t+" error]: "+e.error.code+" "+e.error.message)})),0!==i.length&&"function"===typeof n&&n(i);var a=e.filter((function(t){return!t.hasOwnProperty("error")})).map((function(t){return t.result||t.params}));0!==a.length&&"function"===typeof s&&s(a)}else e.hasOwnProperty("error")?(console.warn("[aria2."+t+" error]: "+e.error.code+" "+e.error.message),"function"===typeof n&&n(e)):"function"===typeof s&&s(e.result||e.params)}}]),t}();["onDownloadStart","onDownloadPause","onDownloadStop","onDownloadComplete","onDownloadError","onBtDownloadComplete"].forEach((function(t){Object.defineProperty(w.prototype,t,{get:function(){},set:function(e){this._addListener(t,e)}})})),["remove","pause","unpause","getUris","removeDownloadResult"].forEach((function(t){Object.defineProperty(w.prototype,t,{value:function(e,s,n){e.constructor!==Array&&(e=[e]);var i=e.map((function(t){return[t]}));this._batchRequest(t,i,s,n)}})})),["pauseAll","unpauseAll","getGlobalOption","getGlobalStat","purgeDownloadResult","getVersion","shutdown","saveSession"].forEach((function(t){Object.defineProperty(w.prototype,t,{value:function(e,s){this._request(t,[],e,s)}})}));var _={host:"127.0.0.1",port:"6800",token:"",encryption:!1},b={"max-concurrent-downloads":5,"max-overall-download-limit":0,"max-overall-upload-limit":262144},S={"seed-time":"43200","seed-ratio":"10"},C={"seed-time":"0","seed-ratio":"0.1"},T=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Default",s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:_,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:b;Object(h["a"])(this,t),this._handle=new w(s.host,s.port,s.token,s.encryption),this.name=e,this.rpc=Object.assign({},s),this.options=Object.assign({},n),this.connection=!1,this.tasks={active:[],waiting:[],paused:[],stopped:[]}}return Object(v["a"])(t,[{key:"setServer",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Default",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:_,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:b,n=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];this.name=t.slice(),this.rpc=Object.assign({},e);var i=this.options["dir"];this.options=Object.assign({},s),n&&(this.options["dir"]=i),this._handle.setRPC(e.host,e.port,e.token,e.encryption);var a={};for(var r in s)a[r]=s[r].toString();this._handle.changeGlobalOption(a)}},{key:"checkConnection",value:function(t,e){var s=this;this._handle.getVersion((function(e){s.connection=!0,"function"===typeof t&&t(e)}),(function(t){s.connection=!1,"function"===typeof e&&e(t)}))}},{key:"addTask",value:function(t,e,s){var n=this._handle,i=t.seeding?S:C;switch(t.type){case"torrent":t.selectfile&&(i["select-file"]=t.selectfile),n.addTorrent(t.file,i,e,s);break;case"metalink":n.addMetalink(t.file,i,e,s);break;case"http":n.addUri(t.uris,i,e,s);break;default:}}},{key:"changeTaskStatus",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],s=arguments.length>2?arguments[2]:void 0,n=arguments.length>3?arguments[3]:void 0;"unpause"===t?this._handle.unpause(e,s,n):"pause"===t?this._handle.pause(e,s,n):"remove"===t&&this._handle.remove(e,s,n)}},{key:"purgeTasks",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=arguments.length>1?arguments[1]:void 0,s=arguments.length>2?arguments[2]:void 0;this._handle.removeDownloadResult(t,e,s)}},{key:"syncDownloading",value:function(){var t=this,e=this.tasks;this._handle.tellActive((function(s){e.active=s.map((function(e){return t._formatTask(e)}))}),(function(t){e.active=[]})),this._handle.tellWaiting((function(s){e.waiting=s.filter((function(t){return"waiting"===t.status})).map((function(e){return t._formatTask(e)})),e.paused=s.filter((function(t){return"paused"===t.status})).map((function(e){return t._formatTask(e)}))}),(function(t){e.waiting=[],e.paused=[]}))}},{key:"syncFinished",value:function(){var t=this,e=this.tasks;this._handle.tellStopped((function(s){e.stopped=s.map((function(e){return t._formatTask(e)}))}),(function(t){e.stopped=[]}))}},{key:"syncOptions",value:function(){var t=this.options;this._handle.getGlobalOption((function(e){t["dir"]=e["dir"],t["max-concurrent-downloads"]=parseInt(e["max-concurrent-downloads"]),t["max-overall-download-limit"]=parseInt(e["max-overall-download-limit"]),t["max-overall-upload-limit"]=parseInt(e["max-overall-upload-limit"])}))}},{key:"_formatTask",value:function(t){var e=function(t){return t.substr(0,t.lastIndexOf("/"))};return{gid:t.gid,status:t.status,isBT:t.hasOwnProperty("bittorrent")&&t["bittorrent"].hasOwnProperty("info"),name:t.hasOwnProperty("bittorrent")&&t["bittorrent"].hasOwnProperty("info")?t["bittorrent"]["info"]["name"]:t["files"][0]["path"].replace(/^.*[\\/]/,""),totalLength:parseInt(t.totalLength),completedLength:parseInt(t.completedLength),uploadLength:parseInt(t.uploadLength),downloadSpeed:parseInt(t.downloadSpeed),uploadSpeed:parseInt(t.uploadSpeed),connections:parseInt(t.connections),dir:t.dir,path:e(t.files[0].path)===t.dir?t.files[0].path:t.files.map((function(t){return e(t.path)})).reduce((function(t,e){return t.length<=e.length?t:e}))}}},{key:"isDownloading",get:function(){return this.tasks.active.some((function(t){return t.completedLength!==t.totalLength}))}}]),t}();["onDownloadStart","onDownloadPause","onDownloadStop","onDownloadComplete","onDownloadError","onBtDownloadComplete"].forEach((function(t){Object.defineProperty(T.prototype,t,{get:function(){},set:function(e){var s=this._handle,n=this._formatTask;s[t]=function(t){var i=t.map((function(t){return t.gid}));s.tellStatus(i,(function(t){"function"===typeof e&&e(t.map((function(t){return n(t)})))}))}}})}));var x=function(){function t(){Object(h["a"])(this,t),this.servers=this._initServers(),this.serverIndex=0,this.sync=void 0}return Object(v["a"])(t,[{key:"addServer",value:function(){this.servers.push(new T)}},{key:"removeServer",value:function(){0!==this.servers.length&&this.servers.splice(this.serverIndex,1),this.serverIndex>=this.servers.length&&(this.serverIndex=this.servers.length-1)}},{key:"setServerIndex",value:function(t){this.serverIndex=Math.min(this.servers.length-1,Math.max(0,t))}},{key:"setSyncInterval",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:3e3;this.sync=setInterval((function(){return t.syncTasks()}),e)}},{key:"clearSyncInterval",value:function(){clearInterval(this.sync)}},{key:"syncTasks",value:function(){var t=this.servers[this.serverIndex];t.checkConnection(),t.syncDownloading(),t.syncFinished()}},{key:"writeStorage",value:function(){var t={servers:this.servers.map((function(t){return{name:t.name,rpc:t.rpc,options:t.options}}))};window.localStorage.setItem(this.constructor.name,JSON.stringify(t))}},{key:"_readStorage",value:function(){return JSON.parse(window.localStorage.getItem(this.constructor.name))||{}}},{key:"_initServers",value:function(){var t=this._readStorage().servers||[{}];return t.map((function(t){return new T(t.name,t.rpc,t.options)}))}}]),t}();["onDownloadStart","onDownloadPause","onDownloadStop","onDownloadComplete","onDownloadError","onBtDownloadComplete"].forEach((function(t){Object.defineProperty(x.prototype,t,{get:function(){},set:function(e){this.servers.forEach((function(s,n){s[t]=function(t){"function"===typeof e&&e(t,s.name,n)}}))}})})),n["a"].config.productionTip=!1,n["a"].config.devtools=!1,n["a"].use(i["a"]);var L={"en-US":{message:s("4bef")},"zh-CN":{message:s("6366")}},O=new i["a"]({locale:navigator.language,fallbackLocale:"en-US",messages:L}),$=new x;$.setSyncInterval(1e3),new n["a"]({components:{App:p},router:r,i18n:O,template:'',data:{manager:$}}).$mount("#app");var A=new Audio(s("fa5f")),D=new Audio(s("5769"));$.onBtDownloadComplete=function(){return A.play()},$.onDownloadComplete=function(t){t.some((function(t){return!t.isBT}))&&A.play()},$.onDownloadError=function(){return D.play()}},5769:function(t,e,s){t.exports=s.p+"media/error.7090c0b8.mp3"},6301:function(t,e,s){"use strict";var n=s("d200"),i=s.n(n);i.a},6366:function(t){t.exports={main:{downloading:"正在下载",finished:"已完成",settings:"设置"},newTask:{task:"任务",urls:"链接",btMetalink:"BT / Metalink",seeding:"做种",choose:"选择文件",start:"开始",cancel:"取消",filename:"文件名",filetype:"格式",size:"大小"},settings:{general:"通用",profile:"配置文件",rpc:"RPC",host:"主机",port:"端口",token:"密码",encryption:"SSL / TLS",status:"状态",connected:"已连接",disconnected:"未连接",download:"下载",directory:"文件夹",choose:"选择",maxDownloading:"最大同时下载",downloadLimit:"下载限速",uploadLimit:"上传限速"}}},"725b":function(t,e,s){},"793c":function(t,e,s){},"85ec":function(t,e,s){},"88a7":function(t,e,s){"use strict";var n=s("725b"),i=s.n(n);i.a},"8ae0":function(t,e,s){"use strict";s.r(e);var n=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{attrs:{id:"finished"}},[s("div",{staticClass:"toolbar"},[s("a",{class:{disabled:0===t.selectedList.length},attrs:{href:"#"},on:{click:function(e){return t.purgeTasks()}}},[s("i",{staticClass:"fas fa-trash"})]),s("a",{attrs:{href:"#"},on:{click:function(e){return t.selectAll()}}},[s("i",{staticClass:"fas fa-list"})])]),s("div",{staticClass:"content"},t._l(t.finished,(function(e){return s("task",{key:e.gid,attrs:{selected:t.selected[e.gid],gid:e.gid,status:e.status,alias:e.name,totalLength:e.totalLength,completedLength:e.completedLength,uploadLength:e.uploadLength,downloadSpeed:e.downloadSpeed,uploadSpeed:e.uploadSpeed,connections:e.connections},on:{selectTask:function(e){return t.selectTask(e)},multiSelectTask:function(e){return t.multiSelectTask(e)}}})})),1)])},i=[],a=(s("ac6a"),s("456d"),s("97b6")),r={components:{Task:a["a"]},props:["finished"],data:function(){return{selected:{}}},computed:{selectedList:function(){var t=this;return Object.keys(this.selected).filter((function(e){return t.selected[e]}))}},methods:{selectTask:function(t){this.selected={},this.$set(this.selected,t,!0)},multiSelectTask:function(t){var e=this.selected;e[t]?this.$set(e,t,!1):this.$set(e,t,!0)},selectAll:function(){var t=this;this.selectedList.length===this.finished.length?this.selected={}:this.finished.forEach((function(e){t.$set(t.selected,e.gid,!0)}))},purgeTasks:function(){this.$emit("purgeTasks",this.selectedList),this.selected={}}}},o=r,l=(s("a476"),s("0574"),s("db9a"),s("2877")),c=Object(l["a"])(o,n,i,!1,null,"72c5201f",null);e["default"]=c.exports},"97b6":function(t,e,s){"use strict";var n=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"row",class:{selected:t.selected},on:{click:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"left",37,e.key,["Left","ArrowLeft"])||"button"in e&&0!==e.button?null:(e.stopPropagation(),t.onClick())},contextmenu:function(e){return e.stopPropagation(),t.$emit("multiSelectTask",t.gid)}}},[s("div",{staticClass:"col-status"},[s("i",{class:t.icon[t.status]})]),s("div",{staticClass:"col-info"},[s("div",{staticClass:"title",attrs:{title:t.alias}},[t._v(t._s(t.alias))]),s("div",{staticClass:"detail detail-left"},[0!==t.totalLength?s("div",[t._v(t._s(t.bytesToString(t.totalLength,2)))]):t._e(),0!==t.uploadLength?s("div",{staticStyle:{"margin-left":"16px"}},[s("i",{staticClass:"far fa-arrow-alt-circle-up"}),t._v(" "+t._s(t.bytesToString(t.uploadLength,2))+" "),0!==t.uploadSpeed?s("span",[t._v(", "+t._s(t.bytesToString(t.uploadSpeed,1)+"B/s"))]):t._e()]):t._e()])]),s("div",{staticClass:"col-progress"},[s("div",{staticClass:"progress-bar"},[s("div",{staticClass:"progress",class:{"progress-success":t.completedLength===t.totalLength},style:{width:t.completedPercentage}})]),s("div",{staticClass:"detail"},["active"===t.status?s("div",[t._v(t._s(t.secondsToString(t.remainingTime)))]):t._e(),"active"===t.status?s("div",[t._v(t._s(t.completedPercentage))]):t._e()])]),t.finished?t._e():s("div",{staticClass:"col-speed"},["active"===t.status&&t.completedLength!==t.totalLength&&0!==t.downloadSpeed?s("div",[t._v(t._s(t.bytesToString(t.downloadSpeed,1)+"B/s"))]):t._e()])])},i=[],a=s("fbd9"),r={props:["selected","gid","status","alias","totalLength","completedLength","uploadLength","downloadSpeed","uploadSpeed","connections"],data:function(){return{icon:{active:["fas","fa-arrow-down"],waiting:["fas","fa-clock"],paused:["fas","fa-pause"],complete:["fas","fa-check"],removed:["fas","fa-times"],error:["fas","fa-exclamation"]},clicks:0,timer:void 0}},computed:{finished:function(){return"complete"===this.status||"removed"===this.status||"error"===this.status},completedPercentage:function(){return(this.totalLength?Math.round(this.completedLength/this.totalLength*100):0)+"%"},remainingTime:function(){return(this.totalLength-this.completedLength)/this.downloadSpeed}},methods:{secondsToString:a["a"].secondsToString,bytesToString:a["a"].bytesToString,onClick:function(){var t=this;1===++this.clicks?this.timer=setTimeout((function(){t.clicks=0,t.$emit("selectTask",t.gid)}),250):(clearTimeout(this.timer),this.clicks=0,this.finished||("paused"===this.status?this.$emit("changeTaskStatus",{method:"unpause",gids:this.gid}):this.$emit("changeTaskStatus",{method:"pause",gids:this.gid})))}}},o=r,l=(s("0957"),s("44f2"),s("64f3"),s("e082"),s("2877")),c=Object(l["a"])(o,n,i,!1,null,"8497525a",null);e["a"]=c.exports},a541:function(t,e,s){"use strict";s.r(e);var n=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{attrs:{id:"settings"}},[s("div",{staticClass:"toolbar"},[s("a",{attrs:{href:"#"},on:{click:function(e){return t.$emit("addServer")}}},[s("i",{staticClass:"fas fa-file-medical"})]),s("a",{class:{disabled:t.settings.isDefault},attrs:{href:"#"},on:{click:function(e){return t.$emit("removeServer")}}},[s("i",{staticClass:"fas fa-trash-alt"})])]),s("div",{staticClass:"content"},[s("form",{on:{change:function(e){return t.$emit("updateSettings")}}},[t.settings.isDesktop&&t.settings.isDefault?t._e():s("div",{staticClass:"group",attrs:{id:"settings-general"}},[s("div",{staticClass:"header"},[t._v(t._s(t.$t("message.settings.general")))]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-general-config"}},[t._v(t._s(t.$t("message.settings.profile")))])]),s("div",{staticClass:"right"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.settings.name,expression:"settings.name"}],attrs:{id:"settings-general-config",type:"text",required:""},domProps:{value:t.settings.name},on:{input:function(e){e.target.composing||t.$set(t.settings,"name",e.target.value)}}})])])]),t.settings.isDesktop&&t.settings.isDefault?t._e():s("div",{staticClass:"group",attrs:{id:"settings-rpc"}},[s("div",{staticClass:"header"},[t._v(t._s(t.$t("message.settings.rpc")))]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-rpc-address"}},[t._v(t._s(t.$t("message.settings.host")))])]),s("div",{staticClass:"right"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.settings.rpc.host,expression:"settings.rpc.host"}],attrs:{id:"settings-rpc-address",type:"text",required:""},domProps:{value:t.settings.rpc.host},on:{input:function(e){e.target.composing||t.$set(t.settings.rpc,"host",e.target.value)}}})])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-rpc-port"}},[t._v(t._s(t.$t("message.settings.port")))])]),s("div",{staticClass:"right"},[s("input",{directives:[{name:"model",rawName:"v-model.number",value:t.settings.rpc.port,expression:"settings.rpc.port",modifiers:{number:!0}}],attrs:{id:"settings-rpc-port",type:"number",min:"0",max:"65535",step:"1",required:""},domProps:{value:t.settings.rpc.port},on:{input:function(e){e.target.composing||t.$set(t.settings.rpc,"port",t._n(e.target.value))},blur:function(e){return t.$forceUpdate()}}})])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-rpc-token"}},[t._v(t._s(t.$t("message.settings.token")))])]),s("div",{staticClass:"right"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.settings.rpc.token,expression:"settings.rpc.token"}],attrs:{id:"settings-rpc-token",type:"password"},domProps:{value:t.settings.rpc.token},on:{input:function(e){e.target.composing||t.$set(t.settings.rpc,"token",e.target.value)}}})])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-rpc-protocol"}},[t._v(t._s(t.$t("message.settings.encryption")))])]),s("div",{staticClass:"right"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.settings.rpc.encryption,expression:"settings.rpc.encryption"}],attrs:{type:"checkbox"},domProps:{checked:Array.isArray(t.settings.rpc.encryption)?t._i(t.settings.rpc.encryption,null)>-1:t.settings.rpc.encryption},on:{change:function(e){var s=t.settings.rpc.encryption,n=e.target,i=!!n.checked;if(Array.isArray(s)){var a=null,r=t._i(s,a);n.checked?r<0&&t.$set(t.settings.rpc,"encryption",s.concat([a])):r>-1&&t.$set(t.settings.rpc,"encryption",s.slice(0,r).concat(s.slice(r+1)))}else t.$set(t.settings.rpc,"encryption",i)}}})])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-rpc-status"}},[t._v(t._s(t.$t("message.settings.status")))])]),s("div",{staticClass:"right"},[t.settings.connection?s("span",{staticClass:"badge badge-success"},[t._v(t._s(t.$t("message.settings.connected")))]):t._e(),t.settings.connection?t._e():s("span",{staticClass:"badge badge-danger"},[t._v(t._s(t.$t("message.settings.disconnected")))])])])]),s("div",{staticClass:"group",attrs:{id:"settings-download"}},[s("div",{staticClass:"header"},[t._v(t._s(t.$t("message.settings.download")))]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-download-path"}},[t._v(t._s(t.$t("message.settings.directory")))])]),s("div",{staticClass:"right pair"},[s("label",{staticClass:"button fixed",class:{disabled:!(t.settings.isDesktop&&t.settings.isDefault)},attrs:{for:"settings-download-path-choose"}},[t._v(t._s(t.$t("message.settings.choose")))]),s("input",{staticClass:"hidden",attrs:{id:"settings-download-path-choose",type:"file",webkitdirectory:"",mozdirectory:"",msdirectory:"",odirectory:"",directory:"",multiple:""},on:{change:function(e){return t.setDir(e)}}}),s("input",{directives:[{name:"model",rawName:"v-model",value:t.settings.options["dir"],expression:"settings.options['dir']"}],staticClass:"expanded",attrs:{type:"text",disabled:""},domProps:{value:t.settings.options["dir"]},on:{input:function(e){e.target.composing||t.$set(t.settings.options,"dir",e.target.value)}}})])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-download-max-downloading"}},[t._v(t._s(t.$t("message.settings.maxDownloading")))])]),s("div",{staticClass:"right"},[s("input",{directives:[{name:"model",rawName:"v-model.number",value:t.settings.options["max-concurrent-downloads"],expression:"settings.options['max-concurrent-downloads']",modifiers:{number:!0}}],attrs:{id:"settings-download-max-downloading",type:"number",min:"1",max:"100",step:"1",required:""},domProps:{value:t.settings.options["max-concurrent-downloads"]},on:{input:function(e){e.target.composing||t.$set(t.settings.options,"max-concurrent-downloads",t._n(e.target.value))},blur:function(e){return t.$forceUpdate()}}})])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-download-download-limit"}},[t._v(t._s(t.$t("message.settings.downloadLimit")))])]),s("div",{staticClass:"right pair"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.limits.download.number,expression:"limits.download.number"}],staticClass:"fixed",attrs:{id:"settings-download-download-limit-number",type:"number",min:"0",max:"1000",step:"1",required:""},domProps:{value:t.limits.download.number},on:{change:function(e){return t.setLimitNumber(e,"download")},input:function(e){e.target.composing||t.$set(t.limits.download,"number",e.target.value)}}}),s("select",{directives:[{name:"model",rawName:"v-model",value:t.limits.download.unit,expression:"limits.download.unit"}],staticClass:"fixed",attrs:{id:"settings-download-download-limit-unit"},on:{change:[function(e){var s=Array.prototype.filter.call(e.target.options,(function(t){return t.selected})).map((function(t){var e="_value"in t?t._value:t.value;return e}));t.$set(t.limits.download,"unit",e.target.multiple?s:s[0])},function(e){return t.setLimitUnit(e,"download")}]}},[s("option",{attrs:{value:"G"}},[t._v("GB/s")]),s("option",{attrs:{value:"M"}},[t._v("MB/s")]),s("option",{attrs:{value:"K"}},[t._v("KB/s")]),s("option",{attrs:{value:""}},[t._v("B/s")])])])]),s("div",{staticClass:"row"},[s("div",{staticClass:"left"},[s("label",{attrs:{for:"settings-download-upload-limit"}},[t._v(t._s(t.$t("message.settings.uploadLimit")))])]),s("div",{staticClass:"right pair"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.limits.upload.number,expression:"limits.upload.number"}],staticClass:"fixed",attrs:{id:"settings-download-upload-limit-number",type:"number",min:"0",max:"1000",step:"1",required:""},domProps:{value:t.limits.upload.number},on:{change:function(e){return t.setLimitNumber(e,"upload")},input:function(e){e.target.composing||t.$set(t.limits.upload,"number",e.target.value)}}}),s("select",{directives:[{name:"model",rawName:"v-model",value:t.limits.upload.unit,expression:"limits.upload.unit"}],staticClass:"fixed",attrs:{id:"settings-download-upload-limit-unit"},on:{change:[function(e){var s=Array.prototype.filter.call(e.target.options,(function(t){return t.selected})).map((function(t){var e="_value"in t?t._value:t.value;return e}));t.$set(t.limits.upload,"unit",e.target.multiple?s:s[0])},function(e){return t.setLimitUnit(e,"upload")}]}},[s("option",{attrs:{value:"G"}},[t._v("GB/s")]),s("option",{attrs:{value:"M"}},[t._v("MB/s")]),s("option",{attrs:{value:"K"}},[t._v("KB/s")]),s("option",{attrs:{value:""}},[t._v("B/s")])])])])])])])])},i=[],a=s("fbd9"),r={props:["settings"],computed:{limits:function(){var t=this,e=["download","upload"],s={};return e.forEach((function(e){var n=t.settings.options["max-overall-"+e+"-limit"];s[e]={number:parseInt(a["a"].bytesToString(n)),unit:a["a"].bytesToUnit(n)}})),s}},methods:{setDir:function(t){var e=t.target.files;e.length&&(this.settings.options["dir"]=e[0].path)},setLimitNumber:function(t,e){var s=parseInt(t.target.value)||0,n=a["a"].stringToBytes(s+this.limits[e].unit);this.settings.options["max-overall-"+e+"-limit"]=n},setLimitUnit:function(t,e){var s=t.target.value,n=a["a"].stringToBytes(this.limits[e].number+s);this.settings.options["max-overall-"+e+"-limit"]=n}}},o=r,l=(s("ce5f"),s("35d4"),s("f0d4"),s("ed7a"),s("6301"),s("2877")),c=Object(l["a"])(o,n,i,!1,null,"b9e39992",null);e["default"]=c.exports},c6ec:function(t,e,s){},cc82:function(t,e,s){},cf05:function(t,e,s){t.exports=s.p+"img/logo.bef6bb2c.png"},d200:function(t,e,s){},db9a:function(t,e,s){"use strict";var n=s("fece"),i=s.n(n);i.a},e082:function(t,e,s){"use strict";var n=s("c6ec"),i=s.n(n);i.a},ec1a:function(t,e,s){"use strict";var n=s("2af1"),i=s.n(n);i.a},ecfb:function(t,e,s){},ed7a:function(t,e,s){"use strict";var n=s("03e1"),i=s.n(n);i.a},f0d4:function(t,e,s){"use strict";var n=s("ecfb"),i=s.n(n);i.a},fa5f:function(t,e,s){t.exports=s.p+"media/complete.908421c0.mp3"},fbd9:function(t,e,s){"use strict";s.d(e,"a",(function(){return a}));s("aef6");var n=s("d225"),i=s("b0b4"),a=function(){function t(){Object(n["a"])(this,t)}return Object(i["a"])(t,null,[{key:"secondsToString",value:function(t){if(!t||t===1/0)return"";if("string"===typeof t&&(t=parseInt(t)),t>=86400)return"> 1 day";var e=Math.floor(t/3600);t%=3600;var s=Math.floor(t/60);return t%=60,t=Math.floor(t),e+":"+(s<10?"0":"")+s+":"+(t<10?"0":"")+t}},{key:"bytesToString",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;t||(t=0),"string"===typeof t&&(t=parseInt(t)),t=Math.round(t);var s=Math.pow(10,parseInt(e));return t>=1073741824?Math.round(t/1073741824*s)/s+"G":t>=1048576?Math.round(t/1048576*s)/s+"M":t>=1024?Math.round(t/1024*s)/s+"K":Math.round(t)+""}},{key:"bytesToUnit",value:function(t){return t||(t=0),"string"===typeof t&&(t=parseInt(t)),t=Math.round(t),t>=1073741824?"G":t>=1048576?"M":t>=1024?"K":""}},{key:"stringToBytes",value:function(t){t||(t="0");var e=parseFloat(t);return t.endsWith("G")?Math.round(1073741824*e):t.endsWith("M")?Math.round(1048576*e):t.endsWith("K")?Math.round(1024*e):Math.round(e)}}]),t}()},fece:function(t,e,s){}}); 2 | //# sourceMappingURL=app.8044abf8.js.map -------------------------------------------------------------------------------- /docs/media/complete.908421c0.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/media/complete.908421c0.mp3 -------------------------------------------------------------------------------- /docs/media/error.7090c0b8.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/docs/media/error.7090c0b8.mp3 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "photon-webui", 3 | "version": "0.2.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@fortawesome/fontawesome-free-webfonts": "^1.0.9", 12 | "core-js": "^2.6.5", 13 | "parse-torrent": "^7.0.0", 14 | "vue": "^2.6.10", 15 | "vue-i18n": "^8.12.0", 16 | "vue-router": "^3.0.7" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "^3.9.0", 20 | "@vue/cli-plugin-eslint": "^3.9.0", 21 | "@vue/cli-service": "^4.5.6", 22 | "babel-eslint": "^10.0.1", 23 | "eslint": "^5.16.0", 24 | "eslint-plugin-vue": "^5.0.0", 25 | "vue-template-compiler": "^2.6.10" 26 | }, 27 | "eslintConfig": { 28 | "root": true, 29 | "env": { 30 | "node": true 31 | }, 32 | "extends": [ 33 | "plugin:vue/essential", 34 | "eslint:recommended" 35 | ], 36 | "rules": { 37 | "no-console": "off", 38 | "no-unused-vars": [ 39 | "warn", 40 | { 41 | "vars": "local", 42 | "args": "none", 43 | "ignoreRestSiblings": false 44 | } 45 | ] 46 | }, 47 | "parserOptions": { 48 | "parser": "babel-eslint" 49 | } 50 | }, 51 | "postcss": { 52 | "plugins": { 53 | "autoprefixer": {} 54 | } 55 | }, 56 | "browserslist": [ 57 | "> 1%", 58 | "last 2 versions" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | photon-webui 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /screenshot/downloading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/screenshot/downloading.png -------------------------------------------------------------------------------- /screenshot/downloading.zh-CN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/screenshot/downloading.zh-CN.png -------------------------------------------------------------------------------- /screenshot/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/screenshot/settings.png -------------------------------------------------------------------------------- /screenshot/settings.zh-CN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/screenshot/settings.zh-CN.png -------------------------------------------------------------------------------- /screenshot/torrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/screenshot/torrent.png -------------------------------------------------------------------------------- /screenshot/torrent.zh-CN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/screenshot/torrent.zh-CN.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 28 | -------------------------------------------------------------------------------- /src/assets/complete.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/src/assets/complete.mp3 -------------------------------------------------------------------------------- /src/assets/error.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/src/assets/error.mp3 -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmzhang8/Photon-WebUI/92b7f9acc82aab1bc40cc9e8d3f7b51b391c5077/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/Main.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 127 | 128 | 129 | 130 | 211 | -------------------------------------------------------------------------------- /src/components/Main/Downloading.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/components/Main/Finished.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/components/Main/NewTask.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 221 | 222 | 223 | 224 | 225 | 281 | -------------------------------------------------------------------------------- /src/components/Main/Settings.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | 160 | 161 | 162 | 163 | 164 | 165 | 182 | -------------------------------------------------------------------------------- /src/components/Main/Task/Task.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 93 | 94 | 95 | 96 | 97 | 182 | -------------------------------------------------------------------------------- /src/lang/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "downloading": "Downloading", 4 | "finished": "Finished", 5 | "settings": "Settings" 6 | }, 7 | "newTask": { 8 | "task": "Task", 9 | "urls": "URLs", 10 | "btMetalink": "BT / Metalink", 11 | "seeding": "Seeding", 12 | "choose": "Choose", 13 | "start": "Start", 14 | "cancel": "Cancel", 15 | "filename": "File Name", 16 | "filetype": "Type", 17 | "size": "Size" 18 | }, 19 | "settings": { 20 | "general": "General", 21 | "profile": "Profile", 22 | "rpc": "RPC", 23 | "host": "Host", 24 | "port": "Port", 25 | "token": "Token", 26 | "encryption": "SSL / TLS", 27 | "status": "Status", 28 | "connected": "Connected", 29 | "disconnected": "Not Connected", 30 | "download": "Download", 31 | "directory": "Directory", 32 | "choose": "Choose", 33 | "maxDownloading": "Max Downloading", 34 | "downloadLimit": "Download Limit", 35 | "uploadLimit": "Upload Limit" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/lang/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "downloading": "正在下载", 4 | "finished": "已完成", 5 | "settings": "设置" 6 | }, 7 | "newTask": { 8 | "task": "任务", 9 | "urls": "链接", 10 | "btMetalink": "BT / Metalink", 11 | "seeding": "做种", 12 | "choose": "选择文件", 13 | "start": "开始", 14 | "cancel": "取消", 15 | "filename": "文件名", 16 | "filetype": "格式", 17 | "size": "大小" 18 | }, 19 | "settings": { 20 | "general": "通用", 21 | "profile": "配置文件", 22 | "rpc": "RPC", 23 | "host": "主机", 24 | "port": "端口", 25 | "token": "密码", 26 | "encryption": "SSL / TLS", 27 | "status": "状态", 28 | "connected": "已连接", 29 | "disconnected": "未连接", 30 | "download": "下载", 31 | "directory": "文件夹", 32 | "choose": "选择", 33 | "maxDownloading": "最大同时下载", 34 | "downloadLimit": "下载限速", 35 | "uploadLimit": "上传限速" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import router from './router' 4 | import App from './App.vue' 5 | 6 | import Aria2Manager from '@/utils/aria2manager' 7 | 8 | /* 9 | Vue 10 | */ 11 | Vue.config.productionTip = false 12 | Vue.config.devtools = process.env.NODE_ENV === 'development' 13 | 14 | Vue.use(VueI18n) 15 | const messages = { 16 | 'en-US': { message: require('@/lang/en-US.json') }, 17 | 'zh-CN': { message: require('@/lang/zh-CN.json') } 18 | } 19 | const i18n = new VueI18n({ 20 | locale: navigator.language, 21 | fallbackLocale: 'en-US', 22 | messages 23 | }) 24 | 25 | /* 26 | aria2 27 | */ 28 | let aria2manager = new Aria2Manager() 29 | aria2manager.setSyncInterval(1000) 30 | 31 | new Vue({ 32 | components: { App }, 33 | router, 34 | i18n, 35 | template: '', 36 | data: { 37 | manager: aria2manager 38 | } 39 | }).$mount('#app') 40 | 41 | /* 42 | WebUI 43 | */ 44 | let completeSound = new Audio(require('@/assets/complete.mp3')) 45 | let errorSound = new Audio(require('@/assets/error.mp3')) 46 | aria2manager.onBtDownloadComplete = () => completeSound.play() 47 | aria2manager.onDownloadComplete = (tasks) => { 48 | if (tasks.some(task => !task.isBT)) completeSound.play() 49 | } 50 | aria2manager.onDownloadError = () => errorSound.play() 51 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | export default new Router({ 7 | routes: [{ 8 | path: '/', 9 | name: 'main', 10 | component: require('@/components/Main').default, 11 | children: [ 12 | { 13 | path: 'downloading', 14 | name: 'downloading', 15 | component: require('@/components/Main/Downloading').default 16 | }, 17 | { 18 | path: 'finished', 19 | name: 'finished', 20 | component: require('@/components/Main/Finished').default 21 | }, 22 | { 23 | path: 'settings', 24 | name: 'settings', 25 | component: require('@/components/Main/Settings').default 26 | }, 27 | { 28 | path: 'newTask', 29 | name: 'newTask', 30 | component: require('@/components/Main/NewTask').default 31 | }, 32 | { 33 | path: '*', 34 | redirect: 'downloading' 35 | } 36 | ] 37 | }, 38 | { 39 | path: '*', 40 | redirect: '/' 41 | } 42 | ] 43 | }) 44 | -------------------------------------------------------------------------------- /src/styles/option.css: -------------------------------------------------------------------------------- 1 | .group { 2 | padding: 8px 0; 3 | border-bottom: 1px solid lightgray; 4 | } 5 | 6 | .header { 7 | padding: 8px 16px; 8 | font-size: 16px; 9 | font-weight: bold; 10 | } 11 | 12 | .row { 13 | padding: 8px 16px; 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | } 18 | 19 | .row > .left { 20 | flex: 0 0 140px; 21 | padding: 0 8px; 22 | } 23 | 24 | .row > .right { 25 | flex: 1 1 auto; 26 | padding: 0 8px; 27 | } 28 | 29 | .pair { 30 | display: flex; 31 | margin: 0 -4px; 32 | } 33 | 34 | .pair > .fixed { 35 | flex: 0 0 auto; 36 | margin: 0 4px; 37 | } 38 | 39 | .pair > .expanded { 40 | flex: 1 1 auto; 41 | margin: 0 4px; 42 | } 43 | 44 | label { 45 | display: block; 46 | font-size: 16px; 47 | text-align: right; 48 | } 49 | 50 | input, textarea, select { 51 | padding: 8px 12px; 52 | box-sizing: border-box; 53 | border: 1px solid #ccc; 54 | border-radius: 4px; 55 | outline: none; 56 | font-size: 16px; 57 | } 58 | 59 | textarea { 60 | height: 160px; 61 | resize: vertical; 62 | } 63 | 64 | input[type=text], input[type=password], textarea { 65 | width: 100%; 66 | } 67 | 68 | input[type=number], select{ 69 | min-width: 80px; 70 | } 71 | 72 | input:focus, textarea:focus { 73 | border: 1px solid #666; 74 | } 75 | 76 | input:invalid { 77 | border: 1px solid red; 78 | } 79 | 80 | input:disabled { 81 | border: 1px solid #ddd; 82 | color: #444; 83 | opacity: 0.5; 84 | pointer-events: none; 85 | } 86 | 87 | .button { 88 | padding: 8px; 89 | border: 1px solid #ccc; 90 | border-radius: 4px; 91 | background-color: #fafafa; 92 | text-align: center; 93 | font-size: 16px; 94 | } 95 | 96 | .button:hover { 97 | text-decoration: none; 98 | cursor: pointer; 99 | border: 1px solid #666; 100 | } 101 | 102 | .button-large { 103 | min-width: 64px; 104 | padding: 8px 8px; 105 | font-size: 20px; 106 | } 107 | 108 | .vspace { 109 | margin: 12px 0 0 0; 110 | } 111 | 112 | .hspace { 113 | margin: 0 0 0 16px; 114 | } 115 | 116 | .hidden { 117 | display: none; 118 | } 119 | 120 | .disabled { 121 | opacity: 0.5; 122 | cursor: default; 123 | pointer-events: none; 124 | } 125 | -------------------------------------------------------------------------------- /src/styles/toolbar.css: -------------------------------------------------------------------------------- 1 | .toolbar { 2 | height: 48px; 3 | position: sticky; 4 | top: 0px; 5 | padding: 0px 8px; 6 | border-bottom: 2px solid lightgray; 7 | color: #333; 8 | background-color: white; 9 | font-size: 24px; 10 | display: flex; 11 | align-items: stretch; 12 | } 13 | 14 | .toolbar > a { 15 | flex: 0 0 48px; 16 | line-height: 48px; 17 | text-align: center; 18 | color: #333; 19 | } 20 | 21 | .toolbar > .disabled { 22 | opacity: 0.4; 23 | cursor: default; 24 | pointer-events: none; 25 | } 26 | 27 | .seperator-h { 28 | margin: 12px 12px; 29 | border: 1px dashed #aaa; 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/aria2manager.js: -------------------------------------------------------------------------------- 1 | import Aria2Server from './aria2server' 2 | 3 | export default class Aria2Manager { 4 | constructor () { 5 | this.servers = this._initServers() 6 | this.serverIndex = 0 7 | this.sync = undefined 8 | } 9 | 10 | addServer () { 11 | this.servers.push(new Aria2Server()) 12 | } 13 | 14 | removeServer () { 15 | if (this.servers.length !== 0) this.servers.splice(this.serverIndex, 1) 16 | if (this.serverIndex >= this.servers.length) this.serverIndex = this.servers.length - 1 17 | } 18 | 19 | setServerIndex (index) { 20 | this.serverIndex = Math.min(this.servers.length - 1, Math.max(0, index)) 21 | } 22 | 23 | setSyncInterval (interval = 3000) { 24 | this.sync = setInterval(() => this.syncTasks(), interval) 25 | } 26 | 27 | clearSyncInterval () { 28 | clearInterval(this.sync) 29 | } 30 | 31 | syncTasks () { 32 | let server = this.servers[this.serverIndex] 33 | server.checkConnection() 34 | server.syncDownloading() 35 | server.syncFinished() 36 | } 37 | 38 | writeStorage () { 39 | let data = { 40 | servers: this.servers.map(server => { 41 | return { 42 | name: server.name, 43 | rpc: server.rpc, 44 | options: server.options 45 | } 46 | }) 47 | } 48 | window.localStorage.setItem(this.constructor.name, JSON.stringify(data)) 49 | } 50 | 51 | _readStorage () { 52 | return JSON.parse(window.localStorage.getItem(this.constructor.name)) || {} 53 | } 54 | 55 | _initServers () { 56 | let servers = this._readStorage().servers || [{}] 57 | return servers.map(server => new Aria2Server(server.name, server.rpc, server.options)) 58 | } 59 | } 60 | 61 | ['onDownloadStart', 'onDownloadPause', 'onDownloadStop', 'onDownloadComplete', 'onDownloadError', 'onBtDownloadComplete'].forEach(method => { 62 | Object.defineProperty(Aria2Manager.prototype, method, { 63 | get: function () { 64 | return undefined 65 | }, 66 | set: function (callback) { 67 | this.servers.forEach((server, serverIndex) => { 68 | server[method] = tasks => { 69 | if (typeof callback === 'function') callback(tasks, server.name, serverIndex) 70 | } 71 | }) 72 | } 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /src/utils/aria2rpc.js: -------------------------------------------------------------------------------- 1 | import { RPCHTTP, RPCWebSocket } from './jsonrpc' 2 | 3 | const maxTaskNumber = 1000 4 | const taskStatusKeys = ['gid', 'status', 'totalLength', 'completedLength', 'uploadLength', 'downloadSpeed', 'uploadSpeed', 'connections', 'dir', 'files', 'bittorrent', 'errorCallbackCode', 'errorCallbackMessage'] 5 | 6 | export default class Aria2RPC { 7 | constructor (host = '127.0.0.1', port = 6800, token = '', encryption = false) { 8 | this._date = new Date() 9 | this.setRPC(host, port, token, encryption) 10 | } 11 | 12 | setRPC (host = '127.0.0.1', port = 6800, token = '', encryption = false) { 13 | this._token = 'token:' + token 14 | this._address = host + ':' + port + '/jsonrpc' 15 | if (this._rpc) this._rpc.setAddress(this._address, encryption) 16 | else { 17 | this._rpc = new RPCHTTP(this._address, encryption, 'aria2') 18 | // Chrome doesn't allow unsecure websocket connections to localhost 19 | // https://stackoverflow.com/questions/50704614/websocket-connection-fails-on-chrome-without-ssl 20 | // try { 21 | // this._rpc = new RPCWebSocket(this._address, encryption, 'aria2') 22 | // } catch (error) { 23 | // console.error(error.message) 24 | // console.warn('Fall back to HTTP request.') 25 | // this._rpc = new RPCHTTP(this._address, encryption, 'aria2') 26 | // } 27 | } 28 | } 29 | 30 | addUri (uris, options = {}, successCallback, errorCallback) { 31 | const method = 'addUri' 32 | if (uris.constructor !== Array) uris = [uris] 33 | let paramsPool = uris.map(uriGroup => [uriGroup.constructor === Array ? uriGroup : [uriGroup], options]) 34 | this._batchRequest(method, paramsPool, successCallback, errorCallback) 35 | } 36 | 37 | addTorrent (torrent, options = {}, successCallback, errorCallback) { 38 | const method = 'addTorrent' 39 | this._request(method, [torrent, [], options], successCallback, errorCallback) 40 | } 41 | 42 | addMetalink (metalink, options = {}, successCallback, errorCallback) { 43 | const method = 'addMetalink' 44 | this._request(method, [metalink, options], successCallback, errorCallback) 45 | } 46 | 47 | tellStatus (gids, successCallback, errorCallback) { 48 | const method = 'tellStatus' 49 | if (gids.constructor !== Array) gids = [gids] 50 | let paramsPool = gids.map(gid => [gid, taskStatusKeys]) 51 | this._batchRequest(method, paramsPool, successCallback, errorCallback) 52 | } 53 | 54 | tellActive (successCallback, errorCallback) { 55 | const method = 'tellActive' 56 | this._request(method, [taskStatusKeys], successCallback, errorCallback) 57 | } 58 | 59 | tellWaiting (successCallback, errorCallback) { 60 | const method = 'tellWaiting' 61 | this._request(method, [0, maxTaskNumber, taskStatusKeys], successCallback, errorCallback) 62 | } 63 | 64 | tellStopped (successCallback, errorCallback) { 65 | const method = 'tellStopped' 66 | this._request(method, [0, maxTaskNumber, taskStatusKeys], successCallback, errorCallback) 67 | } 68 | 69 | changeGlobalOption (options = {}, successCallback, errorCallback) { 70 | const method = 'changeGlobalOption' 71 | this._request(method, [options], successCallback, errorCallback) 72 | } 73 | 74 | _addListener (method, callback) { 75 | let responseHandler = this._responseHandler 76 | if (this._rpc.constructor === RPCWebSocket) { 77 | this._rpc.addListener(method, response => { 78 | responseHandler(method, response, callback) 79 | }) 80 | } 81 | } 82 | 83 | _request (method, params, successCallback, errorCallback) { 84 | let responseHandler = this._responseHandler 85 | let id = method + '.' + this._date.getTime() 86 | this._rpc.request(method, [this._token].concat(params), id, response => { 87 | responseHandler(method, response, successCallback, errorCallback) 88 | }, errorCallback) 89 | } 90 | 91 | _batchRequest (method, paramsPool, successCallback, errorCallback) { 92 | let id = method + '.' + this._date.getTime() 93 | let requests = paramsPool.map(params => { 94 | return { 95 | method: method, 96 | params: [this._token].concat(params), 97 | id: id 98 | } 99 | }) 100 | let responseHandler = this._responseHandler 101 | this._rpc.batchRequest(requests, response => { 102 | responseHandler(method, response, successCallback, errorCallback) 103 | }, errorCallback) 104 | } 105 | 106 | _responseHandler (method, response, successCallback, errorCallback) { 107 | if (response.constructor === Array) { 108 | let errorResults = response.filter(result => result.hasOwnProperty('error')) 109 | errorResults.forEach(result => { 110 | console.warn('[aria2.' + method + ' error]: ' + response.error.code + ' ' + response.error.message) 111 | }) 112 | if (errorResults.length !== 0 && typeof errorCallback === 'function') errorCallback(errorResults) 113 | let successResults = response.filter(result => !result.hasOwnProperty('error')) 114 | .map(result => result.result || result.params) 115 | if (successResults.length !== 0 && typeof successCallback === 'function') successCallback(successResults) 116 | } else { 117 | if (response.hasOwnProperty('error')) { 118 | console.warn('[aria2.' + method + ' error]: ' + response.error.code + ' ' + response.error.message) 119 | if (typeof errorCallback === 'function') errorCallback(response) 120 | } else { 121 | if (typeof successCallback === 'function') successCallback(response.result || response.params) 122 | } 123 | } 124 | } 125 | } 126 | 127 | ['onDownloadStart', 'onDownloadPause', 'onDownloadStop', 'onDownloadComplete', 'onDownloadError', 'onBtDownloadComplete'].forEach(method => { 128 | Object.defineProperty(Aria2RPC.prototype, method, { 129 | get: function () { 130 | return undefined 131 | }, 132 | set: function (callback) { 133 | this._addListener(method, callback) 134 | } 135 | }) 136 | }); 137 | 138 | ['remove', 'pause', 'unpause', 'getUris', 'removeDownloadResult'].forEach(method => { 139 | Object.defineProperty(Aria2RPC.prototype, method, { 140 | value: function (gids, successCallback, errorCallback) { 141 | if (gids.constructor !== Array) gids = [gids] 142 | let paramsPool = gids.map(gid => [gid]) 143 | this._batchRequest(method, paramsPool, successCallback, errorCallback) 144 | } 145 | }) 146 | }); 147 | 148 | ['pauseAll', 'unpauseAll', 'getGlobalOption', 'getGlobalStat', 'purgeDownloadResult', 'getVersion', 'shutdown', 'saveSession'].forEach(method => { 149 | Object.defineProperty(Aria2RPC.prototype, method, { 150 | value: function (successCallback, errorCallback) { 151 | this._request(method, [], successCallback, errorCallback) 152 | } 153 | }) 154 | }) 155 | -------------------------------------------------------------------------------- /src/utils/aria2server.js: -------------------------------------------------------------------------------- 1 | import Aria2RPC from './aria2rpc' 2 | 3 | const defaultRPC = { 4 | host: '127.0.0.1', 5 | port: '6800', 6 | token: '', 7 | encryption: false 8 | } 9 | 10 | const defaultOptions = { 11 | 'max-concurrent-downloads': 5, 12 | 'max-overall-download-limit': 0, 13 | 'max-overall-upload-limit': 262144 14 | } 15 | 16 | const defaultSeedingOptions = { 17 | 'seed-time': '43200', 18 | 'seed-ratio': '10' 19 | } 20 | 21 | const defaultNoSeedingOptions = { 22 | 'seed-time': '0', 23 | 'seed-ratio': '0.1' 24 | } 25 | 26 | export default class Aria2Server { 27 | constructor (name = 'Default', rpc = defaultRPC, options = defaultOptions) { 28 | this._handle = new Aria2RPC(rpc.host, rpc.port, rpc.token, rpc.encryption) 29 | 30 | this.name = name 31 | this.rpc = Object.assign({}, rpc) 32 | this.options = Object.assign({}, options) 33 | this.connection = false 34 | this.tasks = { 35 | active: [], 36 | waiting: [], 37 | paused: [], 38 | stopped: [] 39 | } 40 | } 41 | 42 | get isDownloading () { 43 | return this.tasks.active.some(task => task.completedLength !== task.totalLength) 44 | } 45 | 46 | setServer (name = 'Default', rpc = defaultRPC, options = defaultOptions, ignoreDir = true) { 47 | this.name = name.slice() 48 | this.rpc = Object.assign({}, rpc) 49 | let dir = this.options['dir'] 50 | this.options = Object.assign({}, options) 51 | if (ignoreDir) this.options['dir'] = dir 52 | this._handle.setRPC(rpc.host, rpc.port, rpc.token, rpc.encryption) 53 | let strOptions = {} 54 | for (let key in options) strOptions[key] = options[key].toString() 55 | this._handle.changeGlobalOption(strOptions) 56 | } 57 | 58 | checkConnection (successCallback, errorCallback) { 59 | let that = this 60 | this._handle.getVersion(result => { 61 | that.connection = true 62 | if (typeof successCallback === 'function') successCallback(result) 63 | }, error => { 64 | that.connection = false 65 | if (typeof errorCallback === 'function') errorCallback(error) 66 | }) 67 | } 68 | 69 | addTask (task, successCallback, errorCallback) { 70 | let handle = this._handle 71 | let options = task.seeding ? defaultSeedingOptions : defaultNoSeedingOptions 72 | switch (task.type) { 73 | case 'torrent': 74 | if (task.selectfile) { 75 | options['select-file'] = task.selectfile 76 | } 77 | handle.addTorrent(task.file, options, successCallback, errorCallback) 78 | break 79 | case 'metalink': 80 | handle.addMetalink(task.file, options, successCallback, errorCallback) 81 | break 82 | case 'http': 83 | handle.addUri(task.uris, options, successCallback, errorCallback) 84 | break 85 | default: 86 | } 87 | } 88 | 89 | changeTaskStatus (method, gids = [], successCallback, errorCallback) { 90 | if (method === 'unpause') this._handle.unpause(gids, successCallback, errorCallback) 91 | else if (method === 'pause') this._handle.pause(gids, successCallback, errorCallback) 92 | else if (method === 'remove') this._handle.remove(gids, successCallback, errorCallback) 93 | } 94 | 95 | purgeTasks (gids = [], successCallback, errorCallback) { 96 | this._handle.removeDownloadResult(gids, successCallback, errorCallback) 97 | } 98 | 99 | syncDownloading () { 100 | let tasks = this.tasks 101 | this._handle.tellActive(results => { 102 | tasks.active = results.map(result => this._formatTask(result)) 103 | }, e => { 104 | tasks.active = [] 105 | }) 106 | this._handle.tellWaiting(results => { 107 | tasks.waiting = results.filter(result => result.status === 'waiting') 108 | .map(result => this._formatTask(result)) 109 | tasks.paused = results.filter(result => result.status === 'paused') 110 | .map(result => this._formatTask(result)) 111 | }, e => { 112 | tasks.waiting = [] 113 | tasks.paused = [] 114 | }) 115 | } 116 | 117 | syncFinished () { 118 | let tasks = this.tasks 119 | this._handle.tellStopped(results => { 120 | tasks.stopped = results.map(result => this._formatTask(result)) 121 | }, e => { 122 | tasks.stopped = [] 123 | }) 124 | } 125 | 126 | syncOptions () { 127 | let options = this.options 128 | this._handle.getGlobalOption(result => { 129 | options['dir'] = result['dir'] 130 | options['max-concurrent-downloads'] = parseInt(result['max-concurrent-downloads']) 131 | options['max-overall-download-limit'] = parseInt(result['max-overall-download-limit']) 132 | options['max-overall-upload-limit'] = parseInt(result['max-overall-upload-limit']) 133 | }) 134 | } 135 | 136 | _formatTask (task) { 137 | let pathDir = (path) => path.substr(0, path.lastIndexOf('/')) 138 | return { 139 | gid: task.gid, 140 | status: task.status, 141 | isBT: task.hasOwnProperty('bittorrent') && task['bittorrent'].hasOwnProperty('info'), 142 | name: task.hasOwnProperty('bittorrent') && task['bittorrent'].hasOwnProperty('info') ? task['bittorrent']['info']['name'] : task['files'][0]['path'].replace(/^.*[\\/]/, ''), 143 | totalLength: parseInt(task.totalLength), 144 | completedLength: parseInt(task.completedLength), 145 | uploadLength: parseInt(task.uploadLength), 146 | downloadSpeed: parseInt(task.downloadSpeed), 147 | uploadSpeed: parseInt(task.uploadSpeed), 148 | connections: parseInt(task.connections), 149 | dir: task.dir, 150 | path: pathDir(task.files[0].path) === task.dir ? task.files[0].path 151 | : task.files.map(task => pathDir(task.path)) 152 | .reduce((last, cur) => last.length <= cur.length ? last : cur) 153 | } 154 | } 155 | } 156 | 157 | ['onDownloadStart', 'onDownloadPause', 'onDownloadStop', 'onDownloadComplete', 'onDownloadError', 'onBtDownloadComplete'].forEach(method => { 158 | Object.defineProperty(Aria2Server.prototype, method, { 159 | get: function () { 160 | return undefined 161 | }, 162 | set: function (callback) { 163 | let handle = this._handle 164 | let formatTask = this._formatTask 165 | handle[method] = results => { 166 | let gids = results.map(result => result.gid) 167 | handle.tellStatus(gids, tasks => { 168 | if (typeof callback === 'function') callback(tasks.map(task => formatTask(task))) 169 | }) 170 | } 171 | } 172 | }) 173 | }) 174 | -------------------------------------------------------------------------------- /src/utils/converter.js: -------------------------------------------------------------------------------- 1 | export default class Converter { 2 | static secondsToString (seconds) { 3 | if (!seconds || seconds === Infinity) return '' 4 | if (typeof (seconds) === 'string') seconds = parseInt(seconds) 5 | if (seconds >= 86400) { 6 | return '> 1 day' 7 | } else { 8 | let hours = Math.floor(seconds / 3600) 9 | seconds %= 3600 10 | let minutes = Math.floor(seconds / 60) 11 | seconds %= 60 12 | seconds = Math.floor(seconds) 13 | return hours + ':' + (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds 14 | } 15 | } 16 | 17 | static bytesToString (bytes, precision = 0) { 18 | if (!bytes) bytes = 0 19 | if (typeof (bytes) === 'string') bytes = parseInt(bytes) 20 | bytes = Math.round(bytes) 21 | let base = Math.pow(10, parseInt(precision)) 22 | if (bytes >= 1073741824) { 23 | return Math.round((bytes / 1073741824) * base) / base + 'G' 24 | } else if (bytes >= 1048576) { 25 | return Math.round((bytes / 1048576) * base) / base + 'M' 26 | } else if (bytes >= 1024) { 27 | return Math.round((bytes / 1024) * base) / base + 'K' 28 | } else { 29 | return Math.round(bytes) + '' 30 | } 31 | } 32 | 33 | static bytesToUnit (bytes) { 34 | if (!bytes) bytes = 0 35 | if (typeof (bytes) === 'string') bytes = parseInt(bytes) 36 | bytes = Math.round(bytes) 37 | if (bytes >= 1073741824) { 38 | return 'G' 39 | } else if (bytes >= 1048576) { 40 | return 'M' 41 | } else if (bytes >= 1024) { 42 | return 'K' 43 | } else { 44 | return '' 45 | } 46 | } 47 | 48 | static stringToBytes (str) { 49 | if (!str) str = '0' 50 | let bytes = parseFloat(str) 51 | if (str.endsWith('G')) { 52 | return Math.round(bytes * 1073741824) 53 | } else if (str.endsWith('M')) { 54 | return Math.round(bytes * 1048576) 55 | } else if (str.endsWith('K')) { 56 | return Math.round(bytes * 1024) 57 | } else { 58 | return Math.round(bytes) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/utils/filetypes.js: -------------------------------------------------------------------------------- 1 | export const imageExtensions = [ 2 | '.ai', 3 | '.bmp', 4 | '.eps', 5 | '.gif', 6 | '.icn', 7 | '.ico', 8 | '.jpeg', 9 | '.jpg', 10 | '.png', 11 | '.psd', 12 | '.raw', 13 | '.sketch', 14 | '.svg', 15 | '.tif', 16 | '.webp', 17 | '.xd' 18 | ] 19 | 20 | export const audioExtensions = [ 21 | '.aac', 22 | '.ape', 23 | '.flac', 24 | '.flav', 25 | '.m4a', 26 | '.mp3', 27 | '.ogg', 28 | '.wav', 29 | '.wma' 30 | ] 31 | 32 | export const videoExtensions = [ 33 | '.avi', 34 | '.m4a', 35 | '.mkv', 36 | '.mov', 37 | '.mp4', 38 | '.mpg', 39 | '.rmvb', 40 | '.vob', 41 | '.wmv' 42 | ] 43 | -------------------------------------------------------------------------------- /src/utils/jsonrpc.js: -------------------------------------------------------------------------------- 1 | export class RPCHTTP { 2 | constructor (address, encryption = false, namespace) { 3 | this.namespace = namespace 4 | this.setAddress(address, encryption) 5 | } 6 | 7 | setAddress (address, encryption = false) { 8 | this._url = (encryption ? 'https://' : 'http://') + address 9 | } 10 | 11 | request (method, params = [], id, successCallback, errorCallback) { 12 | let data = this._formatData(method, params, id) 13 | this._fetch(this._url, data, successCallback, errorCallback) 14 | } 15 | 16 | batchRequest (requests, successCallback, errorCallback) { 17 | if (requests.constructor !== Array) requests = [requests] 18 | let data = requests.map(request => this._formatData(request.method, request.params, request.id)) 19 | this._fetch(this._url, data, successCallback, errorCallback) 20 | } 21 | 22 | _formatData (method, params = [], id = '') { 23 | return { 24 | jsonrpc: '2.0', 25 | id: id, 26 | method: this.namespace + '.' + method, 27 | params: params.constructor === Array ? params : [params] 28 | } 29 | } 30 | 31 | _fetch (url, data, successCallback, errorCallback) { 32 | fetch(url, { 33 | method: 'POST', 34 | body: JSON.stringify(data) 35 | }).then(response => { 36 | if (!response.ok) throw Error(response.status + ' ' + response.statusText) 37 | return response.json() 38 | }).then(result => { 39 | if (typeof (successCallback) === 'function') successCallback(result) 40 | }).catch(error => { 41 | console.error('[fetch error]: ' + error.message) 42 | if (typeof (errorCallback) === 'function') errorCallback(error) 43 | }) 44 | } 45 | } 46 | 47 | export class RPCWebSocket { 48 | constructor (address, encryption = false, namespace) { 49 | this.namespace = namespace 50 | this._listeners = {} 51 | this.setAddress(address, encryption) 52 | } 53 | 54 | setAddress (address, encryption) { 55 | this._handles = {} 56 | if (typeof WebSocket !== 'function') throw Error('This client does not support WebSocket.') 57 | else { 58 | let url = (encryption ? 'wss://' : 'ws://') + address 59 | try { 60 | this._socket = new WebSocket(url) 61 | let that = this 62 | this._socket.onclose = event => { 63 | setTimeout(() => { 64 | if (that._socket.readyState > 1) that.setAddress(address, encryption) 65 | }, 10000) 66 | } 67 | this._socket.onerror = error => that._onerror(error, that._handles) 68 | this._socket.onmessage = message => that._onmessage(message, that._handles, that._listeners) 69 | } catch (error) { 70 | console.error(error.message) 71 | } 72 | } 73 | } 74 | 75 | addListener (method, callback) { 76 | if (typeof callback === 'function') this._listeners[this.namespace + '.' + method] = callback 77 | } 78 | 79 | removeListener (method) { 80 | delete this._listeners[this.namespace + '.' + method] 81 | } 82 | 83 | request (method, params = [], id, successCallback, errorCallback) { 84 | this._handles[id] = { 85 | success: successCallback, 86 | error: errorCallback 87 | } 88 | let data = this._formatData(method, params, id) 89 | this._send(data) 90 | } 91 | 92 | batchRequest (requests, successCallback, errorCallback) { 93 | if (requests.constructor !== Array) requests = [requests] 94 | requests.forEach(request => { 95 | this._handles[request.id] = { 96 | success: successCallback, 97 | error: errorCallback 98 | } 99 | }) 100 | let data = requests.map(request => this._formatData(request.method, request.params, request.id)) 101 | this._send(data) 102 | } 103 | 104 | _formatData (method, params = [], id = '') { 105 | return { 106 | jsonrpc: '2.0', 107 | id: id, 108 | method: this.namespace + '.' + method, 109 | params: params.constructor === Array ? params : [params] 110 | } 111 | } 112 | 113 | _send (data) { 114 | let that = this 115 | let socket = this._socket 116 | if (socket.readyState > 1) socket.onerror(Error('WebSocket is in state ' + socket.readyState + '.')) 117 | else if (socket.readyState === 0) setTimeout(() => that._send(data), 1000) 118 | else socket.send(JSON.stringify(data)) 119 | } 120 | 121 | _onerror (error, handles) { 122 | if (error.hasOwnProperty('message')) console.error(error.message) 123 | Object.keys(handles).forEach(id => { 124 | if (typeof handles[id].error === 'function') handles[id].error(error) 125 | delete handles[id] 126 | }) 127 | } 128 | 129 | _onmessage (message, handles, listeners) { 130 | let data = JSON.parse(message.data) 131 | if (data.constructor === Array) { 132 | data = data.reduce((last, cur) => { 133 | if (last.hasOwnProperty(cur.id)) last[cur.id].push(cur) 134 | else last[cur.id] = [cur] 135 | return last 136 | }, {}) 137 | for (let id in data) { 138 | if (handles.hasOwnProperty(id)) { 139 | if (typeof handles[id].success === 'function') handles[id].success(data[id]) 140 | delete handles[id] 141 | } 142 | } 143 | } else if (data.hasOwnProperty('id')) { 144 | if (handles.hasOwnProperty(data.id)) { 145 | if (typeof handles[data.id].success === 'function') handles[data.id].success(data) 146 | delete handles[data.id] 147 | } 148 | } else if (data.hasOwnProperty('method')) { 149 | if (listeners.hasOwnProperty(data.method)) { 150 | if (typeof listeners[data.method] === 'function') listeners[data.method](data) 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | runtimeCompiler: true, 3 | publicPath: process.env.NODE_ENV === 'production' ? '/Photon-WebUI/' : '/', 4 | outputDir: 'docs' 5 | } 6 | --------------------------------------------------------------------------------