├── .github └── workflows │ └── build.yml ├── .gitignore ├── .husky ├── .gitignore ├── pre-commit └── pre-push ├── .prettierignore ├── .prettierrc ├── AriaNg ├── LICENSE ├── css │ ├── aria-ng-371f8daad9.min.css │ ├── bootstrap-3.4.1.min.css │ └── plugins-ccac6fc3fc.min.css ├── favicon.ico ├── favicon.png ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── index.html ├── index.manifest ├── js │ ├── angular-packages-1.6.10.min.js │ ├── aria-ng-7bf49194bf.min.js │ ├── bootstrap-3.4.1.min.js │ ├── echarts-common-3.8.5.min.js │ ├── jquery-3.3.1.min.js │ ├── moment-with-locales-2.29.4.min.js │ └── plugins-b3cb190423.min.js ├── langs │ ├── fr_FR.txt │ ├── it_IT.txt │ ├── zh_Hans.txt │ └── zh_Hant.txt ├── robots.txt ├── tileicon.png └── touchicon.png ├── LICENSE ├── README.md ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── plugins ├── bundle-aria.ts ├── clean-build.ts ├── make-manifest.ts └── move-entry.ts ├── public ├── background-page.html ├── favicon.ico ├── logo128.png ├── logo48.png └── robots.txt ├── renovate.json ├── src ├── aria2 │ └── index.ts ├── background │ └── index.ts ├── browser │ └── index.ts ├── components │ ├── content │ │ └── index.tsx │ ├── create │ │ └── index.tsx │ ├── header │ │ └── index.tsx │ ├── panel │ │ └── index.tsx │ ├── progress │ │ └── index.tsx │ └── setting │ │ └── index.tsx ├── lib │ ├── magnet │ │ └── index.ts │ └── queries │ │ └── index.ts ├── main │ ├── App.tsx │ ├── index.html │ └── index.tsx ├── manifest.ts ├── theme │ └── index.tsx ├── types │ └── index.ts ├── utils │ └── index.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.mts └── webext.config.cjs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build/release 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: 'lts/*' 18 | cache: 'npm' 19 | - run: npm ci 20 | - run: npm run build --if-present 21 | 22 | deployment: 23 | runs-on: ubuntu-latest 24 | needs: [build] 25 | if: github.ref == 'refs/heads/main' 26 | strategy: 27 | matrix: 28 | target: ['FIREFOX', 'CHROME'] 29 | steps: 30 | - uses: actions/checkout@v4 31 | 32 | - name: Install Node.js, NPM 33 | uses: actions/setup-node@v4 34 | with: 35 | node-version: 'lts/*' 36 | - run: npm install 37 | 38 | - name: build extension 39 | run: TARGET=${{ matrix.target }} npm run complete 40 | 41 | - name: Get VERSION 42 | run: | 43 | echo "VERSION=$(awk -F'"' '/"version": ".+"/{ print $4; exit; }' package.json)" >> $GITHUB_ENV 44 | 45 | - name: Upload build artifact 46 | uses: actions/upload-artifact@v4 47 | with: 48 | name: aria2_integration_extension_${{ matrix.target }}_${{ env.VERSION }} 49 | path: artifacts/aria2_integration_extension-${{ env.VERSION }}.zip 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | 84 | # Nuxt.js build / generate output 85 | .nuxt 86 | dist 87 | 88 | # Gatsby files 89 | .cache/ 90 | # Comment in the public line in if your project uses Gatsby and not Next.js 91 | # https://nextjs.org/blog/next-9-1#public-directory-support 92 | # public 93 | 94 | # vuepress build output 95 | .vuepress/dist 96 | 97 | # Serverless directories 98 | .serverless/ 99 | 100 | # FuseBox cache 101 | .fusebox/ 102 | 103 | # DynamoDB Local files 104 | .dynamodb/ 105 | 106 | # TernJS port file 107 | .tern-port 108 | 109 | # Stores VSCode versions used for testing VSCode extensions 110 | .vscode-test 111 | 112 | # yarn v2 113 | 114 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .pnp.* 118 | 119 | ### macOS template 120 | # General 121 | .DS_Store 122 | .AppleDouble 123 | .LSOverride 124 | 125 | # Icon must end with two \r 126 | Icon 127 | 128 | # Thumbnails 129 | ._* 130 | 131 | # Files that might appear in the root of a volume 132 | .DocumentRevisions-V100 133 | .fseventsd 134 | .Spotlight-V100 135 | .TemporaryItems 136 | .Trashes 137 | .VolumeIcon.icns 138 | .com.apple.timemachine.donotpresent 139 | 140 | # Directories potentially created on remote AFP share 141 | .AppleDB 142 | .AppleDesktop 143 | Network Trash Folder 144 | Temporary Items 145 | .apdisk 146 | 147 | ### Windows template 148 | # Windows thumbnail cache filesK 149 | Thumbs.db 150 | Thumbs.db:encryptable 151 | ehthumbs.db 152 | ehthumbs_vista.db 153 | 154 | # Dump file 155 | *.stackdump 156 | 157 | # Folder config file 158 | [Dd]esktop.ini 159 | 160 | # Recycle Bin used on file shares 161 | $RECYCLE.BIN/ 162 | 163 | # Windows Installer files 164 | *.cab 165 | *.msi 166 | *.msix 167 | *.msm 168 | *.msp 169 | 170 | # Windows shortcuts 171 | *.lnk 172 | 173 | ### Example user template template 174 | ### Example user template 175 | 176 | # IntelliJ project files 177 | .idea 178 | *.iml 179 | out 180 | gen 181 | ### Linux template 182 | *~ 183 | 184 | # temporary files which can be created if a process still has a handle open of a deleted file 185 | .fuse_hidden* 186 | 187 | # KDE directory preferences 188 | .directory 189 | 190 | # Linux trash folder which might appear on any partition or disk 191 | .Trash-* 192 | 193 | # .nfs files are created when an open file is removed but is still being accessed 194 | .nfs* 195 | 196 | build 197 | artifacts 198 | public/manifest.json 199 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | npm run type-check 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | npm-debug.log* 6 | yarn-debug.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | *.lcov 23 | 24 | # nyc test coverage 25 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Snowpack dependency directory (https://snowpack.dev/) 42 | # TypeScript cache 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | # Microbundle cache 48 | .rpt2_cache/ 49 | .rts2_cache_cjs/ 50 | .rts2_cache_es/ 51 | .rts2_cache_umd/ 52 | 53 | # Optional REPL history 54 | .node_repl_history 55 | 56 | # Output of 'npm pack' 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | .env.test 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .parcel-cache 66 | 67 | # Next.js build output 68 | .next 69 | # Nuxt.js build / generate output 70 | .nuxt 71 | # Gatsby files 72 | # Comment in the public line in if your project uses Gatsby and not Next.js 73 | # https://nextjs.org/blog/next-9-1#public-directory-support 74 | # public 75 | 76 | # vuepress build output 77 | .vuepress/dist 78 | 79 | # Serverless directories 80 | .serverless/ 81 | 82 | # FuseBox cache 83 | .fusebox/ 84 | 85 | # DynamoDB Local files 86 | .dynamodb/ 87 | 88 | # TernJS port file 89 | .tern-port 90 | 91 | # Stores VSCode versions used for testing VSCode extensions 92 | .vscode-test 93 | 94 | # yarn v2 95 | .yarn/cache 96 | .yarn/unplugged 97 | .yarn/build-state.yml 98 | .yarn/install-state.gz 99 | .pnp.* 100 | 101 | ### Example user template template 102 | ### Example user template 103 | 104 | # IntelliJ project files 105 | .idea 106 | *.iml 107 | 108 | build 109 | artifacts 110 | AriaNg -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@trivago/prettier-plugin-sort-imports"], 3 | "arrowParens": "avoid", 4 | "semi": true, 5 | "singleQuote": true, 6 | "importOrder": ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"], 7 | "importOrderSeparation": true 8 | } 9 | -------------------------------------------------------------------------------- /AriaNg/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2024 MaysWind (i@mayswind.net) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /AriaNg/css/aria-ng-371f8daad9.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * AriaNg 3 | * https://github.com/mayswind/AriaNg 4 | */body,html{margin:0;padding:0}body{-webkit-user-select:none;-moz-user-select:none;user-select:none}select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23555555%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E")!important;background-repeat:no-repeat,repeat!important;background-position:right .7em top 50%,0 0!important;background-size:.65em auto,100%!important;border:none;border-radius:0;padding:0 0 0 .35em}select::-ms-expand{display:none}td{vertical-align:middle!important}.modal textarea{resize:none}.blur{-moz-filter:blur(5px);-ms-filter:blur(5px);filter:blur(5px);filter:progid:DXImageTransform.Microsoft.Blur(PixelRadius=5,MakeShadow=false)}.disable-overlay{position:fixed;left:0;top:0;right:0;bottom:0;z-index:9999;cursor:not-allowed}.main-header .logo{overflow:visible}.main-header .logo .dropdown-menu{z-index:2000}.main-header .navbar .nav>li{display:inline-block}.main-header .navbar .nav>li>a{padding-left:10px;padding-right:10px}.main-header .navbar .nav>li.disabled>a{pointer-events:none!important}.main-header .navbar .navbar-nav{margin-left:5px}.main-header .navbar .navbar-searchbar{padding-top:8px;padding-right:20px;float:right}.main-header .logo .logo-mini{font-size:14px!important}.main-header .logo .logo-lg{cursor:pointer}.main-header .rpcselect-dropdown{max-height:200px;overflow-x:hidden}@media (min-height:360px){.main-header .rpcselect-dropdown{max-height:260px}}@media (min-height:480px){.main-header .rpcselect-dropdown{max-height:360px}}@media (min-height:600px){.main-header .rpcselect-dropdown{max-height:480px}}@media (min-height:720px){.main-header .rpcselect-dropdown{max-height:600px}}.main-sidebar{z-index:1010}.main-sidebar .sidebar-menu>li.treeview>ul.treeview-menu>li>a{padding:6px 5px 6px 41px}@supports (padding-left:max(15px,0px)){@media screen and (orientation:landscape){.main-sidebar ul.sidebar-menu>li.header,.main-sidebar ul.sidebar-menu>li>a{padding-left:max(15px,env(safe-area-inset-left))}.main-sidebar ul.sidebar-menu>li.treeview>ul.treeview-menu>li>a{padding-left:max(41px,calc(26px + env(safe-area-inset-left)))}}}.content-wrapper{min-height:calc(100vh - 48px)}.content-wrapper,.right-side{background-color:#fff}.content-wrapper>.content-body{overflow-y:scroll}@media screen and (orientation:portrait){.main-footer{padding-bottom:calc(15px + env(safe-area-inset-bottom))}}.main-footer>.navbar{margin-bottom:0;min-height:inherit}.main-footer>.navbar>.navbar-toolbar>.nav{float:left;margin:0}.main-footer>.navbar>.navbar-toolbar>.nav>li{display:inline-block;float:left}.main-footer>.navbar>.navbar-toolbar>.nav>li>a{padding:0 10px}.main-footer>.navbar>.navbar-toolbar>.nav>li:first-child>a{padding-left:0}.dropdown-menu.right-align{left:inherit;right:0}.default-cursor{cursor:default!important}.pointer-cursor{cursor:pointer!important}.text-cursor{cursor:text!important}.allow-word-break{word-wrap:break-word;word-break:break-all}.auto-ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:absolute;border:1px solid #eee;background-color:#fff}.main-footer>.navbar>.navbar-toolbar>.nav>li>a{padding-left:8px;padding-right:8px}}.toolbar{cursor:pointer}.toolbar:active{box-shadow:inset 0 2px 6px rgba(0,0,0,.125)}.dropdown-menu small{color:#999}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#ccc;margin-top:5px;margin-right:-10px}.dropdown-menu>li.dropdown-submenu:hover{background-color:#e1e3e9}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;border-radius:6px 0 6px 6px}::-webkit-scrollbar{width:10px}::-webkit-scrollbar-thumb{background-clip:padding-box;background-color:#c4d2db;min-height:28px}::-webkit-scrollbar-track{background-color:#fff}::-webkit-scrollbar-thumb:active,::-webkit-scrollbar-thumb:hover{background-color:#d4dfe7}@media (max-width:767px){::-webkit-scrollbar{width:6px}}.fade-in.ng-enter{transition:all .3s linear;opacity:0}.fade-in.ng-enter.ng-enter-active{opacity:1}.keyboard-key{font-size:.85em;padding:1px 3px;border-radius:2px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:none!important}.btn-group+.btn{margin-left:4px}.btn-sm.promise-btn-style{padding-top:6px;padding-bottom:6px}.btn.btn-xs:not(.is-loading) .btn-spinner:not(:required){margin-left:-21px}.progress-bar{transition:initial!important}.input-group-addon-compact{padding:0 4px}.nav-tabs-custom .nav-tabs>li>a{display:inline-block}.nav-tabs-custom .nav-tabs>li>a.nav-tab-close{padding-left:0;margin-left:-12px}.nav-tabs-custom .nav-tabs>li.nav-tab-title-rpcname>a{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}@media (max-width:991px){.nav-tabs-custom .nav-tabs>li.nav-tab-title-rpcname>a{max-width:150px}}@media (max-width:767px){.nav-tabs-custom .nav-tabs>li.nav-tab-title-rpcname>a{max-width:120px}}.input-group.input-group-multiple>.input-group-addon{border-left:0;border-right:0}.input-group.input-group-multiple>.input-group-addon-container:first-child,.input-group.input-group-multiple>.input-group-addon:first-child{border-left:1px solid #d2d6de}.input-group .input-group-addon-container{width:1%;display:table-cell}.label{font-size:85%}.fa-half{font-size:.5em}.fa-1_1x{font-size:1.1em}.fa-rotate-45{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(45deg);filter:none}.fa-right-bottom{position:relative;right:0;bottom:-6px}.fa-display-order{margin-left:3px}.fa-order-asc,.fa-order-desc{position:relative}.fa-order-asc{bottom:-2px}.fa-order-desc{bottom:2px}.checkbox input[type=checkbox]:focus+label:before,.checkbox input[type=radio]:focus+label:before{outline:none!important}.checkbox input[type=checkbox],.checkbox input[type=radio]{cursor:pointer}.checkbox input.disable-clickable{pointer-events:none!important}.checkbox.checkbox-hide{padding-left:0}.checkbox.checkbox-hide>input,.checkbox.checkbox-hide>input+label:after,.checkbox.checkbox-hide>input+label:before{display:none!important}.checkbox.checkbox-hide>label{padding-left:0}.checkbox-compact{margin-top:2px;margin-bottom:2px}.checkbox-inline{display:inline-block}.icon-dir-expand+.checkbox{margin-left:6px}input-dropdown[input-class-name=form-control]>.input-dropdown{width:100%}.input-dropdown ul{border:1px solid #888}.input-dropdown ul>li.active{background-color:#e1e3e9}.input-dropdown ul>li{padding:2px 14px}.gu-mirror{cursor:grabbing;cursor:-webkit-grabbing}.global-status{cursor:pointer}.global-status>.realtime-speed{padding:0 15px}.global-status>.realtime-speed:first-child{padding-left:5px}.global-status>.realtime-speed:last-child{padding-right:5px}.global-status span.realtime-speed>i{padding-right:2px}.task-table{margin-left:15px;margin-right:15px}@media screen and (orientation:landscape){.content>.task-table,.tab-pane>.task-table{margin-right:calc(15px + env(safe-area-inset-right))}}.task-table .task-table-title{font-size:12px;padding-top:4px;padding-bottom:4px}.task-table .task-table-title a{color:#000;cursor:pointer}.task-table>.task-table-body.draggable{cursor:move;cursor:grab;cursor:-webkit-grab}@media screen and (orientation:landscape){.content>.task-table div.row,.tab-pane>.task-table div.row{margin-right:calc(-1 * (15px + env(safe-area-inset-right)));padding-right:env(safe-area-inset-right)}}.task-table>.task-table-body>div.row{padding-top:8px;padding-bottom:8px;border-top:1px solid #ddd}.task-table>div.row:first-child{border-top:inherit}@media (max-width:767px){.task-table>.task-table-title{display:none!important}.task-table>.task-table-body>div.row:first-child{border-top:inherit}}.task-table .task-name{font-size:14px;display:block}.task-table .peer-name-wrapper{display:inline-block;width:100%}.task-table .task-files,.task-table .task-size{font-size:12px;display:block}.task-table .progress{margin-bottom:0}.task-table .task-last-time,.task-table .task-last-time+.task-download-speed,.task-table .task-seeders{color:#888;font-size:12px}.task-table .task-last-time+.task-download-speed,.task-table .task-seeders{margin-top:1px}.task-table .task-last-time+.task-download-speed{padding-left:20px}.task-table .task-download-speed,.task-table .task-peer-download-speed{font-size:12px}.task-table .checkbox,.task-table .radio{margin-top:0;margin-bottom:0}.task-table .progress{position:relative}.task-table .progress span{position:absolute;display:block;width:100%}.task-table .progress span.progress-lower{color:#000}@media (max-width:767px){.task-table .task-peer-download-speed{float:right}}.task-table .task-right-arrow{visibility:hidden;position:absolute;right:14px;margin-top:-12px}.task-table .row:hover .task-right-arrow,.task-table .row[data-selected=true] .task-right-arrow{visibility:visible}.task-table .task-right-arrow i{color:#c8c8c8;font-size:60px}.task-table .task-right-arrow i:hover{color:#d8d8d8}.settings-table{margin-left:15px;margin-right:15px}@media screen and (orientation:landscape){.content>.settings-table,.tab-pane>.settings-table{margin-right:calc(15px + env(safe-area-inset-right))}}.settings-table .settings-table-title{font-size:12px;padding-top:4px;padding-bottom:4px}.settings-table .settings-table-title a{color:#000;cursor:pointer}.settings-table .settings-table-title .settings-table-title-toolbar{display:inline-block;margin-left:10px}.settings-table>div.row{padding-top:8px;padding-bottom:8px;border-top:1px solid #ddd}@media screen and (orientation:landscape){.content>.settings-table>div.row,.tab-pane>.settings-table>div.row{margin-right:calc(-1 * (15px + env(safe-area-inset-right)));padding-right:env(safe-area-inset-right)}}.settings-table>div.row:first-child{border-top:inherit}.settings-table+.settings-table>div.row:first-child{border-top:1px solid #ddd}.settings-table .input-group-addon{background-color:#eee}.settings-table .asterisk{color:red}.settings-table .description,.settings-table .description-inline{color:#888;font-size:12px;font-weight:400;font-style:normal}.settings-table .description{display:block}.settings-table .description-inline{display:inline-block}.settings-table em{color:#888;font-size:12px;font-weight:400}.settings-table .setting-value .form-group{margin-bottom:0}.settings-table .setting-value .form-group .form-control-icon{color:#3c8dbc}.settings-table .setting-value .form-group select.form-control+.form-control-icon>.form-control-feedback{right:10px}.settings-table .setting-value .input-group .form-group .form-control:focus{z-index:inherit}.settings-table .setting-value .input-group .form-control-rpcport{min-width:70px}.settings-table .setting-value .input-group .form-control-rpcinterface{min-width:100px}@media (max-width:991px){.settings-table .setting-value .input-group .form-control-rpcinterface,.settings-table .setting-value .input-group .form-control-rpcport{min-width:60px}}.settings-table .tip{font-size:12px;padding:4px 8px}.settings-table .multi-line{display:block}@media (max-width:767px){.settings-table .setting-key{font-weight:700}.settings-table .description{display:inline-block}}@media (min-width:768px){.settings-table .setting-key-without-desc{padding-top:6px}}.new-task-table{margin-left:15px;margin-right:15px}@media screen and (orientation:landscape){.content>.new-task-table,.tab-pane>.new-task-table{margin-right:calc(15px + env(safe-area-inset-right))}}.new-task-table>div.row{padding-top:8px;padding-bottom:8px}@media screen and (orientation:landscape){.content>.new-task-table>div.row,.tab-pane>.new-task-table>div.row{margin-right:calc(-1 * (15px + env(safe-area-inset-right)));padding-right:env(safe-area-inset-right)}}.new-task-table>div.row:first-child{border-top:inherit}.new-task-table .new-task-toollink>a{margin-right:20px}@media (max-width:767px){.new-task-table .new-task-toollink>a{display:block}}.settings-table .new-task-filter-title{padding-top:6px}.piece-bar-wrapper{height:20px}.piece-bar{width:100%}.piece-map{padding-left:6px;padding-right:2px;line-height:11px}@media screen and (orientation:landscape){.tab-pane>.piece-map{padding-right:calc(2px + env(safe-area-inset-right))}}.piece-legends{text-align:center;margin-top:4px;margin-bottom:4px}@media screen and (orientation:landscape){.tab-pane>.piece-legends{padding-right:env(safe-area-inset-right)}}.piece-legend{display:inline-block;margin-right:4px}.piece-legend>.piece,.piece-map .piece{width:10px;height:10px;background-color:#eef2f4;border:1px solid #dee2e5;display:inline-block;margin-right:1px}.piece-legend>.piece.piece-completed,.piece-map .piece.piece-completed{background-color:#b8dd69;border-color:#b8dd69}.piece-legend>.piece{margin-right:4px}.chart-popover{max-width:320px}.chart-popover .popover-content{padding:0}.chart-pop-wrapper{padding-left:4px;padding-right:4px;overflow-x:hidden}.chart-pop{display:table}.chart-pop .loading{width:100%;height:100%;display:table-cell;text-align:center;vertical-align:middle}.global-status-chart{width:312px;height:200px}.task-status-chart-wrapper{overflow-x:hidden}@keyframes three-quarters{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.btn-spinner{font-family:sans-serif;font-weight:100}.btn-spinner:not(:required){animation:three-quarters 1.25s infinite linear;border:3px solid #8c8c8c;border-right-color:transparent;border-radius:100%;box-sizing:border-box;display:inline-block;position:relative;vertical-align:middle;overflow:hidden;text-indent:-9999px;width:18px;height:18px}.btn-danger .btn-spinner:not(:required),.btn-primary .btn-spinner:not(:required){border:3px solid #efefef;border-right-color:transparent}.btn-spinner:not(:required){margin-left:-17px;opacity:0;transition:margin .4s ease-out,opacity .2s ease-out}.is-loading .btn-spinner{transition:margin .2s ease-in,opacity .4s ease-in;margin-left:5px;opacity:1}.skin-aria-ng{overflow-y:hidden}.skin-aria-ng,.skin-aria-ng .h1,.skin-aria-ng .h2,.skin-aria-ng .h3,.skin-aria-ng .h4,.skin-aria-ng .h5,.skin-aria-ng .h6,.skin-aria-ng .main-header .logo,.skin-aria-ng .popover,.skin-aria-ng .sweet-alert,.skin-aria-ng .tooltip,.skin-aria-ng h1,.skin-aria-ng h2,.skin-aria-ng h3,.skin-aria-ng h4,.skin-aria-ng h5,.skin-aria-ng h6{font-family:Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.skin-aria-ng .main-header .navbar{background-color:#f6f6f6;border-bottom:1px solid #ddd}.skin-aria-ng .main-header .navbar .nav>li>a{color:#707070;font-size:16px}.skin-aria-ng .main-header .navbar .nav .open>a,.skin-aria-ng .main-header .navbar .nav .open>a:focus,.skin-aria-ng .main-header .navbar .nav .open>a:hover,.skin-aria-ng .main-header .navbar .nav>.active>a,.skin-aria-ng .main-header .navbar .nav>li>a:active,.skin-aria-ng .main-header .navbar .nav>li>a:focus,.skin-aria-ng .main-header .navbar .nav>li>a:hover{color:#0080ff}.skin-aria-ng .main-header .navbar .nav>li.disabled>a{color:#ccc!important}.skin-aria-ng .main-header .navbar .nav>li.divider{padding:10px 0;margin-left:4px;margin-right:4px;border-right:1px solid #ccc;position:relative;top:15px}.skin-aria-ng .main-header .navbar .navbar-searchbar .form-control-icon{color:#999}@media (max-width:767px){.skin-aria-ng .main-header .navbar{padding-left:20px}.skin-aria-ng .main-header .navbar .navbar-nav{margin-left:-10px}.skin-aria-ng .main-header .navbar .nav>li.divider{margin-left:0;margin-right:0;top:5px}}.skin-aria-ng .main-header .logo{background-color:#3c4852;color:#fff;border-bottom:1px solid #59636b}.skin-aria-ng .main-header .logo .logo-lg:hover{color:#ccc}.skin-aria-ng .main-header .logo .logo-lg .logo-lg-title{margin-right:6px}.skin-aria-ng .main-header .logo .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.skin-aria-ng .main-header .logo .dropdown-menu>li.active>a{color:#777;background-color:#fff}.skin-aria-ng .main-header .logo .dropdown-menu>li.active:hover>a{color:#333;background-color:#e1e3e9}.skin-aria-ng .content-header{background:transparent}.skin-aria-ng .left-side,.skin-aria-ng .main-sidebar,.skin-aria-ng .wrapper{background-color:#3c4852}.skin-aria-ng .sidebar-menu>li.header{color:#707070;background-color:#2e343c;padding-top:3px;padding-bottom:3px}.skin-aria-ng .sidebar-menu>li>a{border-left:3px solid transparent;padding-top:9px;padding-bottom:9px}.skin-aria-ng .sidebar-menu>li:hover>a{color:#dfdfdf;background-color:#313a42}.skin-aria-ng .sidebar-menu>li.active>a{color:#5399e8;background-color:#252c30}.skin-aria-ng .sidebar-menu>li.treeview:hover>a{color:#a2b5b9;background-color:#3c4852}.skin-aria-ng .sidebar-menu>li.treeview.active:hover>a,.skin-aria-ng .sidebar-menu>li.treeview.active>a{color:#5399e8;background-color:#3c4852}.skin-aria-ng .sidebar-menu>li.treeview>a:hover{color:#dfdfdf;background-color:#313a42}.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu{margin:0;padding:0;background-color:#3c4852}.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu>li>a{color:#8aa4af}.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu>li>a:hover{color:#dfdfdf;background-color:#313a42}.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu>li.active>a{color:#5399e8;background-color:#252c30}.skin-aria-ng .sidebar-menu i.fa{font-size:18px;margin-right:2px}.skin-aria-ng .sidebar a{color:#a2b5b9}.skin-aria-ng .sidebar a:hover{text-decoration:none}@media (max-width:767px){.skin-aria-ng .main-sidebar{margin-top:5px}}.skin-aria-ng .sidebar .status-label{max-width:95px}.right-side,.skin-aria-ng .content-wrapper{background-color:#fff}.skin-aria-ng .content-wrapper .form-control[type=text]{padding-left:14px}@media screen and (max-width:767px){.skin-aria-ng .content-wrapper .content{margin-top:5px}}.skin-aria-ng .main-footer{font-size:12px}.skin-aria-ng .main-footer>.navbar{border:0}.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li.divider{padding-top:16px;margin-left:4px;margin-right:4px;border-right:1px solid #ccc;position:relative;top:0}.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a{color:#707070;line-height:17px}.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li.open>a,.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a:active,.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a:focus,.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a:hover{color:#0080ff;background:none}.skin-aria-ng .global-status{margin-right:10px;color:inherit}.skin-aria-ng .global-status:hover{border:1px solid #ccc;margin-right:9px;margin-top:-1px}.skin-aria-ng .progress-bar-primary{background-color:#208fe5}.skin-aria-ng .progress-bar-warning{background-color:#f39c12}.skin-aria-ng .icon-primary{color:#3c8dbc}.skin-aria-ng .icon-download{color:#3a89e9}.skin-aria-ng .icon-upload{color:#74a329}.skin-aria-ng .icon-seeder{margin-left:4px;color:#74a329}.skin-aria-ng .icon-error{margin-left:4px;color:#dd4b39}.skin-aria-ng .icon-expand{color:#5399e8}.skin-aria-ng .icon-dir-expand{color:#999}.skin-aria-ng .nav-tabs-custom{box-shadow:none;margin-bottom:0}.skin-aria-ng .nav-tabs-custom>.nav-tabs{border-bottom:1px dotted #ddd}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li{border-top:0;margin-top:1px;margin-right:2px}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.active{border-bottom:2px solid #208fe5}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.slim>a{padding-left:5px;padding-right:5px}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.slim.active{margin-bottom:-1px;border-bottom:inherit}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.divider{padding:10px 0;margin-left:4px;margin-right:4px;margin-top:11px;border-right:1px solid #ccc}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.nav-toolbar{margin-left:15px;margin-right:5px}@media (max-width:767px){.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.nav-toolbar{float:right}}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.nav-toolbar>.btn-group{margin-right:4px}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a{color:#aaa;border:0}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.active>a,.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a:active,.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a:hover{color:#208fe5}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a.nav-tab-close:hover{color:#dd4b39!important;margin-left:-14px}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a.nav-tab-close:hover>.fa-times:before{content:"\f057";font-size:1.1em}.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>div.btn-group{padding-top:5px;padding-bottom:5px}.skin-aria-ng .tooltip.tooltip-error.in,.skin-aria-ng .tooltip.tooltip-success.in,.skin-aria-ng .tooltip.tooltip-warning.in{filter:alpha(opacity=95);opacity:.95}.skin-aria-ng .tooltip.tooltip-success.top-left .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-success.top-right .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-success.top .tooltip-arrow{border-top-color:#00a65a}.skin-aria-ng .tooltip.tooltip-warning.top-left .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-warning.top-right .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-warning.top .tooltip-arrow{border-top-color:#f39c12}.skin-aria-ng .tooltip.tooltip-error.top-left .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-error.top-right .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-error.top .tooltip-arrow{border-top-color:#dd4b39}.skin-aria-ng .tooltip.tooltip-success.left .tooltip-arrow{border-left-color:#00a65a}.skin-aria-ng .tooltip.tooltip-warning.left .tooltip-arrow{border-left-color:#f39c12}.skin-aria-ng .tooltip.tooltip-error.left .tooltip-arrow{border-left-color:#dd4b39}.skin-aria-ng .tooltip.tooltip-success.right .tooltip-arrow{border-right-color:#00a65a}.skin-aria-ng .tooltip.tooltip-warning.right .tooltip-arrow{border-right-color:#f39c12}.skin-aria-ng .tooltip.tooltip-error.right .tooltip-arrow{border-right-color:#dd4b39}.skin-aria-ng .tooltip.tooltip-success.bottom-left .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-success.bottom-right .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-success.bottom .tooltip-arrow{border-bottom-color:#00a65a}.skin-aria-ng .tooltip.tooltip-warning.bottom-left .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-warning.bottom-right .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-warning.bottom .tooltip-arrow{border-bottom-color:#f39c12}.skin-aria-ng .tooltip.tooltip-error.bottom-left .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-error.bottom-right .tooltip-arrow,.skin-aria-ng .tooltip.tooltip-error.bottom .tooltip-arrow{border-bottom-color:#dd4b39}.skin-aria-ng .tooltip.tooltip-success .tooltip-inner{background-color:#00a65a}.skin-aria-ng .tooltip.tooltip-warning .tooltip-inner{background-color:#f39c12}.skin-aria-ng .tooltip.tooltip-error .tooltip-inner{background-color:#dd4b39}.skin-aria-ng .input-group .form-group.has-success+.input-group-addon{border-color:#00a65a;background-color:#00a65a;color:#fff}.skin-aria-ng .input-group .form-group.has-warning+.input-group-addon{border-color:#f39c12;background-color:#f39c12;color:#fff}.skin-aria-ng .input-group .form-group.has-error+.input-group-addon{border-color:#dd4b39;background-color:#dd4b39;color:#fff}.skin-aria-ng input:-moz-placeholder,.skin-aria-ng input:-ms-input-placeholder,.skin-aria-ng input::-moz-placeholder,.skin-aria-ng input::-webkit-input-placeholder{color:#999}.skin-aria-ng select.placeholder{color:#999}.skin-aria-ng .checkbox-primary input[type=checkbox]:checked+label:before,.skin-aria-ng .checkbox-primary input[type=checkbox]:indeterminate+label:before,.skin-aria-ng .checkbox-primary input[type=radio]:checked+label:before,.skin-aria-ng .checkbox-primary input[type=radio]:indeterminate+label:before{background-color:#208fe5;border-color:#208fe5}.skin-aria-ng .task-table{background-color:#fff}.skin-aria-ng .task-table>.task-table-body>div.row:nth-of-type(odd){background-color:#f9f9f9}.skin-aria-ng .task-table>.task-table-body>div.row:hover{background-color:#f5f5f5}.skin-aria-ng .task-table .peer-client{color:#888;font-size:12px}.skin-aria-ng .settings-table{background-color:#fff}.skin-aria-ng .settings-table.striped>div.row:nth-of-type(odd){background-color:#f9f9f9}.skin-aria-ng .settings-table.striped>div.row.no-background{background-color:inherit}.skin-aria-ng .settings-table.hoverable>div.row:hover{background-color:#f5f5f5}.skin-aria-ng .settings-table.hoverable>div.row.no-hover:hover{background-color:inherit}.skin-aria-ng .settings-table.hoverable>div.row:nth-of-type(odd).no-hover:hover{background-color:#f9f9f9}.skin-aria-ng .new-task-table{background-color:#fff}.skin-aria-ng .keyboard-key{border:1px solid #aaa;background-color:#f8f8f8;box-shadow:1px 2px 2px #ddd}.theme-dark.skin-aria-ng{color:#eee;background-color:#1a1a1a}.theme-dark.skin-aria-ng select{background-image:url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23cccccc%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E")!important}.theme-dark.skin-aria-ng .cg-busy-backdrop{background-color:#1a1a1a}.theme-dark.skin-aria-ng .btn-default{color:#eee;border-color:#3f3f3f;background-color:#333}.theme-dark.skin-aria-ng .btn-default.hover,.theme-dark.skin-aria-ng .btn-default:active,.theme-dark.skin-aria-ng .btn-default:hover{color:#fff;background-color:#444}.theme-dark.skin-aria-ng .close{color:#eee}.theme-dark.skin-aria-ng .form-control,.theme-dark.skin-aria-ng pre{background-color:#121212;border-color:#666;color:#eee}.theme-dark.skin-aria-ng .form-control:focus{border-color:#5399e8}.theme-dark.skin-aria-ng .form-control[disabled],.theme-dark.skin-aria-ng .form-control[readonly],.theme-dark.skin-aria-ng fieldset[disabled] .form-control{background-color:#333}.theme-dark.skin-aria-ng .input-group-addon{color:#eee;border-color:#666;background-color:#333}.theme-dark.skin-aria-ng .input-group.input-group-multiple>.input-group-addon-container:first-child,.theme-dark.skin-aria-ng .input-group.input-group-multiple>.input-group-addon:first-child{border-color:#666}.theme-dark.skin-aria-ng .progress{background-color:#444}.theme-dark.skin-aria-ng .nav-tabs-custom{background-color:#1a1a1a}.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs{border-color:#333}.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a{color:#ddd}.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.active:hover>a,.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.active>a{background-color:#1a1a1a}.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.active>a,.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a:active,.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs>li>a:hover{color:#208fe5}.theme-dark.skin-aria-ng .nav-tabs-custom>.nav-tabs>li.divider{border-color:#666}.theme-dark.skin-aria-ng .nav-tabs-custom>.tab-content{background-color:#1a1a1a}.theme-dark.skin-aria-ng .popover{background-color:#1a1a1a;border-color:rgba(0,0,0,.6)}.theme-dark.skin-aria-ng .popover.top .arrow:after{border-top-color:#1a1a1a}.theme-dark.skin-aria-ng .popover.right .arrow:after{border-right-color:#1a1a1a}.theme-dark.skin-aria-ng .popover.bottom .arrow:after{border-bottom-color:#1a1a1a}.theme-dark.skin-aria-ng .popover.left .arrow:after{border-left-color:#1a1a1a}.theme-dark.skin-aria-ng .modal-header{border-color:#333}.theme-dark.skin-aria-ng .modal-content{background-color:#1a1a1a;border-color:rgba(0,0,0,.6)}.theme-dark.skin-aria-ng .modal-footer{border-color:#333}.theme-dark.skin-aria-ng .dropdown-menu{background-color:#1a1a1a;border-color:#333}.theme-dark.skin-aria-ng .dropdown-menu>li>a{color:#eee}.theme-dark.skin-aria-ng .dropdown-menu>li>a:hover{color:#fff;background-color:#333}.theme-dark.skin-aria-ng .dropdown-menu>li.dropdown-submenu:hover{background-color:#333}.theme-dark.skin-aria-ng .dropdown-menu>.divider{background-color:#666}.theme-dark.skin-aria-ng .sweet-overlay{background-color:rgba(0,0,0,.6)}.theme-dark.skin-aria-ng .sweet-alert{background-color:#222}.theme-dark.skin-aria-ng .sweet-alert h2,.theme-dark.skin-aria-ng .sweet-alert p{color:#ddd}.theme-dark.skin-aria-ng .sweet-alert .sa-icon.sa-success .sa-fix,.theme-dark.skin-aria-ng .sweet-alert .sa-icon.sa-success:after,.theme-dark.skin-aria-ng .sweet-alert .sa-icon.sa-success:before{background-color:#222}.theme-dark.skin-aria-ng .main-header .navbar{background-color:#121212;border-color:#333}.theme-dark.skin-aria-ng .main-header .navbar .nav>li>a{color:#eee}.theme-dark.skin-aria-ng .main-header .navbar .nav .open>a,.theme-dark.skin-aria-ng .main-header .navbar .nav .open>a:focus,.theme-dark.skin-aria-ng .main-header .navbar .nav .open>a:hover,.theme-dark.skin-aria-ng .main-header .navbar .nav>.active>a,.theme-dark.skin-aria-ng .main-header .navbar .nav>li>a:active,.theme-dark.skin-aria-ng .main-header .navbar .nav>li>a:focus,.theme-dark.skin-aria-ng .main-header .navbar .nav>li>a:hover{color:#0080ff;background-color:transparent}.theme-dark.skin-aria-ng .main-header .navbar .nav .open>a,.theme-dark.skin-aria-ng .main-header .navbar .nav .open>a:focus,.theme-dark.skin-aria-ng .main-header .navbar .nav .open>a:hover,.theme-dark.skin-aria-ng .main-header .navbar .nav>li>a:active{background-color:#444}.theme-dark.skin-aria-ng .main-header .navbar .nav>li.disabled>a{color:#8f8f8f!important}.theme-dark.skin-aria-ng .main-header .navbar .nav>li.divider{border-color:#666}.theme-dark.skin-aria-ng .main-header .logo{background-color:#282828;border-color:#222}.theme-dark.skin-aria-ng .main-header .logo .dropdown-menu>li.active>a{color:#eee;background-color:#1a1a1a}.theme-dark.skin-aria-ng .main-header .logo .dropdown-menu>li.active:hover>a{color:#fff;background-color:#333}.theme-dark.skin-aria-ng .left-side,.theme-dark.skin-aria-ng .main-sidebar,.theme-dark.skin-aria-ng .wrapper{background-color:#282828}.theme-dark.skin-aria-ng .sidebar-menu>li.header{color:#ccc;background-color:#3c3c3c}.theme-dark.skin-aria-ng .sidebar-menu>li:hover>a{color:#fff;background-color:#444}.theme-dark.skin-aria-ng .sidebar-menu>li.active>a{color:#5399e8;background-color:#333}.theme-dark.skin-aria-ng .sidebar-menu>li.treeview:hover>a{color:#fff;background-color:#282828}.theme-dark.skin-aria-ng .sidebar-menu>li.treeview.active:hover>a,.theme-dark.skin-aria-ng .sidebar-menu>li.treeview.active>a{color:#5399e8;background-color:#282828}.theme-dark.skin-aria-ng .sidebar-menu>li.treeview>a:hover{color:#fff;background-color:#444}.theme-dark.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu{background-color:#282828}.theme-dark.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu>li>a{color:#eee}.theme-dark.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu>li>a:hover{color:#fff;background-color:#444}.theme-dark.skin-aria-ng .sidebar-menu>li.treeview>ul.treeview-menu>li.active>a{color:#5399e8;background-color:#333}.theme-dark.skin-aria-ng .sidebar a{color:#eee}.theme-dark.right-side,.theme-dark.skin-aria-ng .content-wrapper{background-color:#1a1a1a}.theme-dark.skin-aria-ng .main-footer{background-color:#1a1a1a;border-color:#383838}.theme-dark.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a{color:#eee}.theme-dark.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li.open>a,.theme-dark.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a:active,.theme-dark.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a:focus,.theme-dark.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li>a:hover{color:#0080ff;background:none}.theme-dark.skin-aria-ng .main-footer>.navbar>.navbar-toolbar>.nav>li.divider{border-color:#666}.theme-dark.skin-aria-ng .global-status{color:#eee}.theme-dark.skin-aria-ng input:-moz-placeholder,.theme-dark.skin-aria-ng input:-ms-input-placeholder,.theme-dark.skin-aria-ng input::-moz-placeholder,.theme-dark.skin-aria-ng input::-webkit-input-placeholder{color:#aaa}.theme-dark.skin-aria-ng select.placeholder{color:#aaa}.theme-dark.skin-aria-ng .input-dropdown ul>li{background-color:#121212}.theme-dark.skin-aria-ng .input-dropdown ul>li.active{background-color:#333}.theme-dark.skin-aria-ng .cg-busy-default-sign{color:#eee;border-color:#666;background-color:#444;text-shadow:0 1px 1px #000}.theme-dark.skin-aria-ng .cg-busy-default-text{color:#eee}.theme-dark.skin-aria-ng .cg-busy-default-spinner div{background-color:#eee}.theme-dark.skin-aria-ng ::-webkit-scrollbar-track{background-color:#1a1a1a}.theme-dark.skin-aria-ng ::-webkit-scrollbar-thumb{background-color:#3c4144}.theme-dark.skin-aria-ng .piece-legend>.piece,.theme-dark.skin-aria-ng .piece-map .piece{background-color:#242424;border-color:#3c3d3e}.theme-dark.skin-aria-ng .piece-legend>.piece.piece-completed,.theme-dark.skin-aria-ng .piece-map .piece.piece-completed{background-color:#b8dd69;border-color:#b8dd69}.theme-dark.skin-aria-ng .task-table{background-color:#1a1a1a}.theme-dark.skin-aria-ng .task-table .task-table-title a{color:#eee}.theme-dark.skin-aria-ng .task-table>.task-table-body>div.row{border-color:#333}.theme-dark.skin-aria-ng .task-table>.task-table-body>div.row:nth-of-type(odd){background-color:#262626}.theme-dark.skin-aria-ng .task-table>.task-table-body>div.row:hover{background-color:#2a2a2a}.theme-dark.skin-aria-ng .task-table .progress span.progress-lower{color:#eee}.theme-dark.skin-aria-ng .settings-table{background-color:#1a1a1a}.theme-dark.skin-aria-ng .settings-table .settings-table-title a{color:#eee}.theme-dark.skin-aria-ng .settings-table>div.row{border-color:#333}.theme-dark.skin-aria-ng .settings-table.striped>div.row:nth-of-type(odd){background-color:#202020}.theme-dark.skin-aria-ng .settings-table.hoverable>div.row:hover{background-color:#242424}.theme-dark.skin-aria-ng .settings-table.hoverable>div.row:nth-of-type(odd).no-hover:hover{background-color:#202020}.theme-dark.skin-aria-ng .new-task-table{background-color:#1a1a1a}.theme-dark.skin-aria-ng .keyboard-key{border:1px solid #666;background-color:#282828;box-shadow:1px 2px 2px #333} -------------------------------------------------------------------------------- /AriaNg/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/favicon.ico -------------------------------------------------------------------------------- /AriaNg/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/favicon.png -------------------------------------------------------------------------------- /AriaNg/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /AriaNg/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /AriaNg/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /AriaNg/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /AriaNg/index.html: -------------------------------------------------------------------------------- 1 | AriaNg
-------------------------------------------------------------------------------- /AriaNg/index.manifest: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | 3 | CACHE: 4 | css/aria-ng-1bfa53e627.min.css 5 | css/bootstrap-3.4.1.min.css 6 | css/plugins-ccac6fc3fc.min.css 7 | js/angular-packages-1.6.10.min.js 8 | js/aria-ng-fd6fee90bc.min.js 9 | js/bootstrap-3.4.1.min.js 10 | js/echarts-common-3.8.5.min.js 11 | js/jquery-3.3.1.min.js 12 | js/moment-with-locales-2.24.0.min.js 13 | js/plugins-a42b0d6763.min.js 14 | fonts/fontawesome-webfont.woff2 15 | index.html 16 | 17 | NETWORK: 18 | * 19 | 20 | SETTINGS: 21 | prefer-online 22 | 23 | # hash: 670dfc17794046bd426ab5314839e32c19562542e8ee3681950b7a5652fda412 -------------------------------------------------------------------------------- /AriaNg/langs/zh_Hans.txt: -------------------------------------------------------------------------------- 1 | [global] 2 | AriaNg Version=AriaNg 版本 3 | Operation Result=操作结果 4 | Operation Succeeded=操作成功 5 | is connected=已连接 6 | Error=错误 7 | OK=确定 8 | Confirm=确认 9 | Cancel=取消 10 | Close=关闭 11 | True=是 12 | False=否 13 | DEBUG=调试 (Debug) 14 | INFO=普通 (Info) 15 | WARN=警告 (Warn) 16 | ERROR=错误 (Error) 17 | Connecting=连接中 18 | Connected=已连接 19 | Disconnected=未连接 20 | Reconnecting=重连中 21 | Waiting to reconnect=等待重连 22 | Global=全局 23 | New=新建 24 | Start=开始任务 25 | Pause=暂停任务 26 | Retry=重试 27 | Retry Selected Tasks=重试选中的任务 28 | Delete=删除任务 29 | Select All=全部选中 30 | Select None=全部不选 31 | Select Invert=反向选择 32 | Select All Failed Tasks=全选失败的任务 33 | Select All Completed Tasks=全选已完成的任务 34 | Select All Tasks=全部选中任务 35 | Display Order=显示顺序 36 | Copy Download Url=复制下载链接 37 | Copy Magnet Link=复制磁力链接 38 | Help=帮助 39 | Search=搜索 40 | Default=默认 41 | Expand=展开 42 | Collapse=折叠 43 | Expand All=全部展开 44 | Collapse All=全部折叠 45 | Open=打开 46 | Save=保存 47 | Import=导入 48 | Remove Task=删除任务 49 | Remove Selected Task=删除选中的任务 50 | Clear Stopped Tasks=清空已结束任务 51 | Click to view task detail=点击查看任务详情 52 | By File Name=按文件名 53 | By File Size=按文件大小 54 | By Progress=按进度 55 | By Selected Status=按选中状态 56 | By Remaining=按剩余时间 57 | By Download Speed=按下载速度 58 | By Upload Speed=按上传速度 59 | By Peer Address=按节点地址 60 | By Client Name=按客户端名称 61 | Filters=过滤器 62 | Download=下载 63 | Upload=上传 64 | Downloading=正在下载 65 | Pending Verification=等待验证 66 | Verifying=正在验证 67 | Seeding=正在做种 68 | Waiting=正在等待 69 | Paused=已暂停 70 | Completed=已完成 71 | Error Occurred=发生错误 72 | Removed=已删除 73 | Finished / Stopped=已完成 / 已停止 74 | Uncompleted=未完成 75 | Click to pin=点击固定 76 | Settings=系统设置 77 | AriaNg Settings=AriaNg 设置 78 | Aria2 Settings=Aria2 设置 79 | Basic Settings=基本设置 80 | HTTP/FTP/SFTP Settings=HTTP/FTP/SFTP 设置 81 | HTTP Settings=HTTP 设置 82 | FTP/SFTP Settings=FTP/SFTP 设置 83 | BitTorrent Settings=BitTorrent 设置 84 | Metalink Settings=Metalink 设置 85 | RPC Settings=RPC 设置 86 | Advanced Settings=高级设置 87 | AriaNg Debug Console=AriaNg 调试控制台 88 | Aria2 Status=Aria2 状态 89 | File Name=文件名 90 | File Size=大小 91 | Progress=进度 92 | Share Ratio=分享率 93 | Remaining=剩余时间 94 | Download Speed=下载速度 95 | Upload Speed=上传速度 96 | Links=链接 97 | Torrent File=种子文件 98 | Metalink File=Metalink 文件 99 | File Name:=文件名: 100 | Options=选项 101 | Overview=总览 102 | Pieces=区块信息 103 | Files=文件列表 104 | Peers=连接状态 105 | Task Name=任务名称 106 | Task Size=任务大小 107 | Task Status=任务状态 108 | Error Description=错误描述 109 | Health Percentage=健康度 110 | Info Hash=特征值 111 | Seeders=种子数 112 | Connections=连接数 113 | Seed Creation Time=种子创建时间 114 | Download Url=下载地址 115 | Download Dir=下载路径 116 | BT Tracker Servers=BT 服务器 117 | Copy=复制 118 | (Choose Files)=(选择文件) 119 | Videos=视频 120 | Audios=音频 121 | Pictures=图片 122 | Documents=文档 123 | Applications=应用程序 124 | Archives=存档文件 125 | Other=其他 126 | Custom=自定义 127 | Custom Choose File=自定义选择文件 128 | Address=地址 129 | Client=客户端 130 | Status=状态 131 | Speed=速度 132 | (local)=(本机) 133 | No Data=无数据 134 | No connected peers=没有连接到其他节点 135 | Failed to change some tasks state.=修改一些任务状态时失败. 136 | Confirm Retry=确认重试 137 | Are you sure you want to retry the selected task? AriaNg will create same task after clicking OK.=您是否要重试选中的任务? 点击 "确定" 后, AriaNg 将会创建相同的任务. 138 | Failed to retry this task.=该任务重试失败. 139 | {successCount} tasks have been retried and {failedCount} tasks are failed.={{successCount}} 个任务重试成功以及 {{failedCount}} 个任务失败. 140 | Confirm Remove=确认删除 141 | Are you sure you want to remove the selected task?=您是否要删除选中的任务? 142 | Failed to remove some task(s).=删除一些任务时失败. 143 | Confirm Clear=确认清除 144 | Are you sure you want to clear stopped tasks?=您是否要清除已结束的任务? 145 | Download Links:=下载链接: 146 | Download Now=立即下载 147 | Download Later=手动下载 148 | Open Torrent File=打开种子文件 149 | Open Metalink File=打开 Metalink 文件 150 | Support multiple URLs, one URL per line.=支持多个 URL 地址, 每个地址占一行. 151 | Your browser does not support loading file!=您的浏览器不支持加载文件! 152 | The selected file type is invalid!=选择的文件类型无效! 153 | Failed to load file!=加载文件失败! 154 | Download Completed=下载完成 155 | BT Download Completed=BT 下载完成 156 | Download Error=下载出错 157 | AriaNg Url=AriaNg 地址 158 | Command API Url=命令行 API 地址 159 | Export Command API=导出命令行 API 160 | Export=导出 161 | Copied=已复制 162 | Pause After Task Created=任务创建后暂停 163 | Language=语言 164 | Theme=主题 165 | Light=浅色 166 | Dark=深色 167 | Follow system settings=跟随系统设置 168 | Debug Mode=调试模式 169 | Page Title=页面标题 170 | Preview=预览 171 | Tips: You can use the "noprefix" tag to ignore the prefix, "nosuffix" tag to ignore the suffix, and "scale\=n" tag to set the decimal precision.=小提示: 您可以使用 "noprefix" 标签忽略前缀, "nosuffix" 标签忽略后缀, 以及 "scale\=n" 标签设置小数的精度. 172 | Example: ${downspeed:noprefix:nosuffix:scale\=1}=示例: ${downspeed:noprefix:nosuffix:scale\=1} 173 | Updating Page Title Interval=页面标题更新间隔 174 | Enable Browser Notification=启用浏览器通知 175 | Browser Notification Sound=浏览器通知声音 176 | Browser Notification Frequency=浏览器通知频次 177 | Unlimited=无限制 178 | High (Up to 10 Notifications / 1 Minute)=高 (最多 10 条通知 / 每分钟) 179 | Middle (Up to 1 Notification / 1 Minute)=中 (最多 1 条通知 / 每分钟) 180 | Low (Up to 1 Notification / 5 Minutes)=低 (最多 1 条通知 / 每5分钟) 181 | WebSocket Auto Reconnect Interval=WebSocket 自动重连时间 182 | Aria2 RPC Alias=Aria2 RPC 别名 183 | Aria2 RPC Address=Aria2 RPC 地址 184 | Aria2 RPC Protocol=Aria2 RPC 协议 185 | Aria2 RPC Http Request Method=Aria2 RPC Http 请求方法 186 | POST method only supports aria2 v1.15.2 and above.=POST 方法仅支持 aria2 v1.15.2 及以上. 187 | Aria2 RPC Request Headers=Aria2 RPC 请求头 188 | Support multiple request headers, one header per line, each line containing "header name: header value".=支持多个请求头, 每个请求头占一行, 每行包含 "请求头名: 请求头值". 189 | Aria2 RPC Secret Token=Aria2 RPC 密钥 190 | Activate=激活 191 | Reset Settings=重置设置 192 | Confirm Reset=确认重置 193 | Are you sure you want to reset all settings?=您是否要重置所有设置? 194 | Clear Settings History=清除设置历史 195 | Are you sure you want to clear all settings history?=您是否要清除所有设置的历史记录? 196 | Delete RPC Setting=删除 RPC 设置 197 | Add New RPC Setting=添加新 RPC 设置 198 | Are you sure you want to remove rpc setting "{rpcName}"?=您是否要删除 RPC 设置 "{{rpcName}}"? 199 | Updating Global Stat Interval=全局状态更新间隔 200 | Updating Task Information Interval=任务信息更新间隔 201 | Keyboard Shortcuts=键盘快捷键 202 | Supported Keyboard Shortcuts=支持的键盘快捷键 203 | Set Focus On Search Box=将焦点放在搜索框上 204 | Swipe Gesture=滑动手势 205 | Change Tasks Order by Drag-and-drop=拖拽任务排序 206 | Action After Creating New Tasks=创建新任务后执行操作 207 | Navigate to Task List Page=转到任务列表页面 208 | Navigate to Task Detail Page=转到任务详情页面 209 | Action After Retrying Task=重试任务后执行操作 210 | Navigate to Downloading Tasks Page=转到正在下载列表页面 211 | Stay on Current Page=留在当前页面 212 | Remove Old Tasks After Retrying=重试任务后删除原任务 213 | Confirm Task Removal=任务删除前确认 214 | Include Prefix When Copying From Task Details=任务详情页复制时包括前缀 215 | Show Pieces Info In Task Detail Page=任务详情页显示区块信息 216 | Pieces Amount is Less than or Equal to {value}=区块数量小于等于 {{value}} 217 | RPC List Display Order=RPC 列表显示顺序 218 | Each Task List Page Uses Independent Display Order=各任务列表页面使用独立显示顺序 219 | Recently Used=最近使用 220 | RPC Alias=RPC 别名 221 | Import / Export AriaNg Settings=导入 / 导出 AriaNg 设置 222 | Import Settings=导入设置 223 | Export Settings=导出设置 224 | AriaNg settings data=AriaNg 设置数据 225 | Confirm Import=确认导入 226 | Are you sure you want to import all settings?=您是否要导入所有设置? 227 | Invalid settings data format!=无效的设置数据格式! 228 | Data has been copied to clipboard.=数据已经复制到剪贴板中. 229 | Supported Placeholder=支持的占位符 230 | AriaNg Title=AriaNg 标题 231 | Current RPC Alias=当前 RPC 别名 232 | Downloading Count=正在下载数量 233 | Waiting Count=正在等待数量 234 | Stopped Count=已停止数量 235 | You have disabled notification in your browser. You should change your browser's settings before you enable this function.=您已经在浏览器中禁用通知功能. 如需使用此功能, 请修改您浏览器的设置. 236 | Language resource has been updated, please reload the page for the changes to take effect.=语言资源已经更新, 请重新加载页面使其生效. 237 | Configuration has been modified, please reload the page for the changes to take effect.=配置已经修改, 请重新加载页面使其生效. 238 | Reload AriaNg=重新加载 AriaNg 239 | Show Secret=显示密钥 240 | Hide Secret=隐藏密钥 241 | Aria2 Version=Aria2 版本 242 | Enabled Features=已启用的功能 243 | Operations=操作 244 | Reconnect=重新连接 245 | Save Session=保存会话 246 | Shutdown Aria2=关闭 Aria2 247 | Confirm Shutdown=确认关闭 248 | Are you sure you want to shutdown aria2?=您是否要关闭 aria2? 249 | Session has been saved successfully.=会话已经成功保存. 250 | Aria2 has been shutdown successfully.=Aria2 已经成功关闭. 251 | Toggle Navigation=切换导航 252 | Shortcut=快捷方式 253 | Global Rate Limit=全局速度限制 254 | Loading=正在加载... 255 | More Than One Day=超过1天 256 | Unknown=未知 257 | Bytes=字节 258 | Hours=小时 259 | Minutes=分 260 | Seconds=秒 261 | Milliseconds=毫秒 262 | Http=Http 263 | Http (Disabled)=Http (已禁用) 264 | Https=Https 265 | WebSocket=WebSocket 266 | WebSocket (Disabled)=WebSocket (已禁用) 267 | WebSocket (Security)=WebSocket (安全) 268 | Http and WebSocket would be disabled when accessing AriaNg via Https.=使用 Https 访问 AriaNg 时,Http 和 WebSocket 将被禁用. 269 | POST=POST 270 | GET=GET 271 | Enabled=启用 272 | Disabled=禁用 273 | Always=始终 274 | Never=从不 275 | BitTorrent=BitTorrent 276 | Changes to the settings take effect after refreshing page.=设置将在页面刷新后生效. 277 | Logging Time=记录时间 278 | Log Level=日志级别 279 | Auto Refresh=自动刷新 280 | Refresh Now=立即刷新 281 | Clear Logs=清空日志 282 | Are you sure you want to clear debug logs?=您是否要清除调试日志? 283 | Show Detail=显示详情 284 | Log Detail=日志详情 285 | Aria2 RPC Debug=Aria2 RPC 调试 286 | Aria2 RPC Request Method=Aria2 RPC 请求方法 287 | Aria2 RPC Request Parameters=Aria2 RPC 请求参数 288 | Aria2 RPC Response=Aria2 RPC 响应 289 | Execute=执行 290 | RPC method is illegal!=RPC方法错误! 291 | AriaNg does not support this RPC method!=AriaNg 不支持该RPC方法! 292 | RPC request parameters are invalid!=RPC 请求参数无效! 293 | Type is illegal!=类型错误! 294 | Parameter is invalid!=请求参数无效! 295 | Option value cannot be empty!=参数内容不能为空! 296 | Input number is invalid!=输入的数字无效! 297 | Input number is below min value!=输入的数字小于最小值 {{value}} ! 298 | Input number is above max value!=输入的数字大于最大值 {{value}} ! 299 | Input value is invalid!=输入的内容无效! 300 | Protocol is invalid!=协议无效! 301 | RPC host cannot be empty!=RPC 主机不能为空! 302 | RPC secret is not base64 encoded!=RPC 密钥不是 Base64 编码后的字符串! 303 | URL is not base64 encoded!=指定 URL 不是 Base64 编码后的字符串! 304 | Tap to configure and get started with AriaNg.=您还没有进行过设置, 点击这里进行设置. 305 | Cannot initialize WebSocket!=无法初始化 WebSocket! 306 | Cannot connect to aria2!=无法连接到 aria2! 307 | Access Denied!=拒绝访问! 308 | You cannot use AriaNg because this browser does not meet the minimum requirements for data storage.=您无法使用 AriaNg, 因为这个浏览器不满足数据存储的最低要求. 309 | 310 | [error] 311 | unknown=未知错误. 312 | operation.timeout=操作超时. 313 | resource.notfound=无法找到指定资源. 314 | resource.notfound.max-file-not-found=无法找到指定资源. 参见 --max-file-not-found option 参数. 315 | download.aborted.lowest-speed-limit=由于下载速度过慢, 下载已经终止. 参见 --lowest-speed-limit option 参数. 316 | network.problem=网络问题. 317 | resume.notsupported=服务器不支持断点续传. 318 | space.notenough=可用磁盘空间不足. 319 | piece.length.different=分片大小与 .aria2 控制文件中的不同. 参见 --allow-piece-length-change 参数. 320 | download.sametime=aria2 已经下载了另一个相同文件. 321 | download.torrent.sametime=aria2 已经下载了另一个相同哈希的种子文件. 322 | file.exists=文件已经存在. 参见 --allow-overwrite 参数. 323 | file.rename.failed=文件重命名失败. 参见 --auto-file-renaming 参数. 324 | file.open.failed=文件打开失败. 325 | file.create.failed=文件创建或删除已有文件失败. 326 | io.error=文件系统出错. 327 | directory.create.failed=无法创建指定目录. 328 | name.resolution.failed=域名解析失败. 329 | metalink.file.parse.failed=解析 Metalink 文件失败. 330 | ftp.command.failed=FTP 命令执行失败. 331 | http.response.header.bad=HTTP 返回头无效或无法识别. 332 | redirects.toomany=指定地址重定向过多. 333 | http.authorization.failed=HTTP 认证失败. 334 | bencoded.file.parse.failed=解析种子文件失败. 335 | torrent.file.corrupted=指定 ".torrent" 种子文件已经损坏或缺少 aria2 需要的信息. 336 | magnet.uri.bad=指定磁链地址无效. 337 | option.bad=设置错误. 338 | server.overload=远程服务器繁忙, 无法处理当前请求. 339 | rpc.request.parse.failed=处理 RPC 请求失败. 340 | checksum.failed=文件校验失败. 341 | 342 | [languages] 343 | English=英语 344 | Simplified Chinese=简体中文 345 | Traditional Chinese=繁体中文 346 | Italian=意大利语 347 | French=法语 348 | 349 | [format] 350 | longdate=YYYY年MM月DD日 HH:mm:ss 351 | time.millisecond={{value}} 毫秒 352 | time.milliseconds={{value}} 毫秒 353 | time.second={{value}} 秒 354 | time.seconds={{value}} 秒 355 | time.minute={{value}} 分钟 356 | time.minutes={{value}} 分钟 357 | time.hour={{value}} 小时 358 | time.hours={{value}} 小时 359 | requires.aria2-version=需要 aria2 v{{version}} 或更高版本 360 | task.new.download-links=下载链接 ({{count}} 个链接): 361 | task.pieceinfo=已完成: {{completed}}, 共计: {{total}} 块 362 | task.error-occurred=发生错误 ({{errorcode}}) 363 | task.verifying-percent=正在验证 ({{verifiedPercent}}%) 364 | settings.file-count=({{count}} 个文件) 365 | settings.total-count=(共计: {{count}}个) 366 | debug.latest-logs=最近 {{count}} 条日志 367 | 368 | [rpc.error] 369 | unauthorized=认证失败! 370 | 371 | [option] 372 | true=是 373 | false=否 374 | default=默认 375 | none=无 376 | hide=隐藏 377 | full=完整 378 | http=Http 379 | https=Https 380 | ftp=Ftp 381 | mem=仅内存 382 | get=GET 383 | tunnel=TUNNEL 384 | plain=明文 385 | arc4=ARC4 386 | binary=二进制 387 | ascii=ASCII 388 | debug=调试 (Debug) 389 | info=普通 (Info) 390 | notice=一般 (Notice) 391 | warn=警告 (Warn) 392 | error=错误 (Error) 393 | adaptive=自适应 394 | epoll=epoll 395 | falloc=falloc 396 | feedback=反馈 397 | geom=几何 398 | inorder=顺序 399 | kqueue=kqueue 400 | poll=poll 401 | port=port 402 | prealloc=prealloc 403 | random=随机 404 | select=select 405 | trunc=trunc 406 | SSLv3=SSLv3 407 | TLSv1=TLSv1 408 | TLSv1.1=TLSv1.1 409 | TLSv1.2=TLSv1.2 410 | 411 | [options] 412 | dir.name=下载路径 413 | dir.description= 414 | log.name=日志文件 415 | log.description=日志文件的路径. 如果设置为 "-", 日志则写入到 stdout. 如果设置为空字符串(""), 日志将不会记录到磁盘上. 416 | max-concurrent-downloads.name=最大同时下载数 417 | max-concurrent-downloads.description= 418 | check-integrity.name=检查完整性 419 | check-integrity.description=通过对文件的每个分块或整个文件进行哈希验证来检查文件的完整性. 此选项仅对BT、Metalink及设置了 --checksum 选项的 HTTP(S)/FTP 链接生效. 420 | continue.name=断点续传 421 | continue.description=继续下载部分完成的文件. 启用此选项可以继续下载从浏览器或其他程序按顺序下载的文件. 此选项目前只支持 HTTP(S)/FTP 下载的文件. 422 | all-proxy.name=代理服务器 423 | all-proxy.description=设置所有协议的代理服务器地址. 您还可以针对特定的协议覆盖此选项, 即使用 --http-proxy, --https-proxy 和 --ftp-proxy 选项. 此设置将会影响所有下载. 代理服务器地址的格式为 [http://][USER:PASSWORD@]HOST[:PORT]. 424 | all-proxy-user.name=代理服务器用户名 425 | all-proxy-user.description= 426 | all-proxy-passwd.name=代理服务器密码 427 | all-proxy-passwd.description= 428 | checksum.name=校验和 429 | checksum.description=设置校验和. 选项值格式为 TYPE=DIGEST. TYPE 为哈希类型. 支持的哈希类型列在 aria2c -v 的 Hash Algorithms 中. DIGEST 是十六进制摘要. 例如, 设置 sha-1 摘要如同这样: sha-1=0192ba11326fe2298c8cb4de616f4d4140213838 此选项仅对 HTTP(S)/FTP 下载生效. 430 | connect-timeout.name=连接超时时间 431 | connect-timeout.description=设置建立 HTTP/FTP/代理服务器 连接的超时时间(秒). 当连接建立后, 此选项不再生效, 请使用 --timeout 选项. 432 | dry-run.name=模拟运行 433 | dry-run.description=如果设置为"是", aria2 将仅检查远程文件是否存在而不会下载文件内容. 此选项仅对 HTTP/FTP 下载生效. 如果设置为 true, BT 下载将会直接取消. 434 | lowest-speed-limit.name=最小速度限制 435 | lowest-speed-limit.description=当下载速度低于此选项设置的值(B/s) 时将会关闭连接. 0 表示不设置最小速度限制. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 此选项不会影响 BT 下载. 436 | max-connection-per-server.name=单服务器最大连接数 437 | max-connection-per-server.description= 438 | max-file-not-found.name=文件未找到重试次数 439 | max-file-not-found.description=如果 aria2 从远程 HTTP/FTP 服务器收到 "文件未找到" 的状态超过此选项设置的次数后下载将会失败. 设置为 0 将会禁用此选项. 此选项仅影响 HTTP/FTP 服务器. 重试时同时会记录重试次数, 所以也需要设置 --max-tries 这个选项. 440 | max-tries.name=最大尝试次数 441 | max-tries.description=设置最大尝试次数. 0 表示不限制. 442 | min-split-size.name=最小文件分片大小 443 | min-split-size.description=aria2 不会分割小于 2*SIZE 字节的文件. 例如, 文件大小为 20MB, 如果 SIZE 为 10M, aria2 会把文件分成 2 段 [0-10MB) 和 [10MB-20MB), 并且使用 2 个源进行下载 (如果 --split >= 2). 如果 SIZE 为 15M, 由于 2*15M > 20MB, 因此 aria2 不会分割文件并使用 1 个源进行下载. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 可以设置的值为: 1M-1024M. 444 | netrc-path.name=.netrc 文件路径 445 | netrc-path.description= 446 | no-netrc.name=禁用 netrc 447 | no-netrc.description= 448 | no-proxy.name=不使用代理服务器列表 449 | no-proxy.description=设置不使用代理服务器的主机名, 域名, 包含或不包含子网掩码的网络地址, 多个使用逗号分隔. 450 | out.name=文件名 451 | out.description=下载文件的文件名. 其总是相对于 --dir 选项中设置的路径. 当使用 --force-sequential 参数时此选项无效. 452 | proxy-method.name=代理服务器请求方法 453 | proxy-method.description=设置用来请求代理服务器的方法. 方法可设置为 GET 或 TUNNEL. HTTPS 下载将忽略此选项并总是使用 TUNNEL. 454 | remote-time.name=获取服务器文件时间 455 | remote-time.description=从 HTTP/FTP 服务获取远程文件的时间戳, 如果可用将设置到本地文件 456 | reuse-uri.name=URI 复用 457 | reuse-uri.description=当所有给定的 URI 地址都已使用, 继续使用已经使用过的 URI 地址. 458 | retry-wait.name=重试等待时间 459 | retry-wait.description=设置重试间隔时间(秒). 当此选项的值大于 0 时, aria2 在 HTTP 服务器返回 503 响应时将会重试. 460 | server-stat-of.name=服务器状态保存文件 461 | server-stat-of.description=指定用来保存服务器状态的文件名. 您可以使用 --server-stat-if 参数读取保存的数据. 462 | server-stat-timeout.name=服务器状态超时 463 | server-stat-timeout.description=指定服务器状态的过期时间 (单位为秒). 464 | split.name=单任务连接数 465 | split.description=下载时使用 N 个连接. 如果提供超过 N 个 URI 地址, 则使用前 N 个地址, 剩余的地址将作为备用. 如果提供的 URI 地址不足 N 个, 这些地址多次使用以保证同时建立 N 个连接. 同一服务器的连接数会被 --max-connection-per-server 选项限制. 466 | stream-piece-selector.name=分片选择算法 467 | stream-piece-selector.description=指定 HTTP/FTP 下载使用的分片选择算法. 分片表示的是并行下载时固定长度的分隔段. 如果设置为"默认", aria2 将会按减少建立连接数选择分片. 由于建立连接操作的成本较高, 因此这是合理的默认行为. 如果设置为"顺序", aria2 将选择索引最小的分片. 索引为 0 时表示为文件的第一个分片. 这将有助于视频的边下边播. --enable-http-pipelining 选项有助于减少重连接的开销. 请注意, aria2 依赖于 --min-split-size 选项, 所以有必要对 --min-split-size 选项设置一个合理的值. 如果设置为"随机", aria2 将随机选择一个分片. 就像"顺序"一样, 依赖于 --min-split-size 选项. 如果设置为"几何", aria2 会先选择索引最小的分片, 然后会为之前选择的分片保留指数增长的空间. 这将减少建立连接的次数, 同时文件开始部分将会先行下载. 这也有助于视频的边下边播. 468 | timeout.name=超时时间 469 | timeout.description= 470 | uri-selector.name=URI 选择算法 471 | uri-selector.description=指定 URI 选择的算法. 可选的值包括 "按顺序", "反馈" 和 "自适应". 如果设置为"按顺序", URI 将按列表中出现的顺序使用. 如果设置为"反馈", aria2 将根据之前的下载速度选择 URI 列表中下载速度最快的服务器. 同时也将有效跳过无效镜像. 之前统计的下载速度将作为服务器状态文件的一部分, 参见 --server-stat-of 和 --server-stat-if 选项. 如果设置为"自适应", 将从最好的镜像和保留的连接里选择一项. 补充说明, 其返回的镜像没有被测试过, 同时如果每个镜像都已经被测试过时, 返回的镜像还会被重新测试. 否则, 其将不会选择其他镜像. 例如"反馈", 其使用服务器状态文件. 472 | check-certificate.name=检查证书 473 | check-certificate.description= 474 | http-accept-gzip.name=支持 GZip 475 | http-accept-gzip.description=如果远程服务器的响应头中包含 Content-Encoding: gzip 或 Content-Encoding: deflate , 将发送包含 Accept: deflate, gzip 的请求头并解压缩响应. 476 | http-auth-challenge.name=认证质询 477 | http-auth-challenge.description=仅当服务器需要时才发送 HTTP 认证请求头. 如果设置为"否", 每次都会发送认证请求头. 例外: 如果用户名和密码包含在 URI 中, 将忽略此选项并且每次都会发送认证请求头. 478 | http-no-cache.name=禁用缓存 479 | http-no-cache.description=发送的请求头中将包含 Cache-Control: no-cache 和 Pragma: no-cache header 以避免内容被缓存. 如果设置为"否", 上述请求头将不会发送, 同时您也可以使用 --header 选项将 Cache-Control 请求头添加进去. 480 | http-user.name=HTTP 默认用户名 481 | http-user.description= 482 | http-passwd.name=HTTP 默认密码 483 | http-passwd.description= 484 | http-proxy.name=HTTP 代理服务器 485 | http-proxy.description= 486 | http-proxy-user.name=HTTP 代理服务器用户名 487 | http-proxy-user.description= 488 | http-proxy-passwd.name=HTTP 代理服务器密码 489 | http-proxy-passwd.description= 490 | https-proxy.name=HTTPS 代理服务器 491 | https-proxy.description= 492 | https-proxy-user.name=HTTPS 代理服务器用户名 493 | https-proxy-user.description= 494 | https-proxy-passwd.name=HTTPS 代理服务器密码 495 | https-proxy-passwd.description= 496 | referer.name=请求来源 497 | referer.description=设置 HTTP 请求来源 (Referer). 此选项将影响所有 HTTP/HTTPS 下载. 如果设置为 *, 请求来源将设置为下载链接. 此选项可以配合 --parameterized-uri 选项使用. 498 | enable-http-keep-alive.name=启用持久连接 499 | enable-http-keep-alive.description=启用 HTTP/1.1 持久连接. 500 | enable-http-pipelining.name=启用 HTTP 管线化 501 | enable-http-pipelining.description=启用 HTTP/1.1 管线化. 502 | header.name=自定义请求头 503 | header.description=增加 HTTP 请求头内容. 每行放置一项, 每项包含 "请求头名: 请求头值". 504 | save-cookies.name=Cookies 保存路径 505 | save-cookies.description=以 Mozilla/Firefox(1.x/2.x)/Netscape 格式将 Cookies 保存到文件中. 如果文件已经存在, 将被覆盖. 会话过期的 Cookies 也将会保存, 其过期时间将会设置为 0. 506 | use-head.name=启用 HEAD 方法 507 | use-head.description=第一次请求 HTTP 服务器时使用 HEAD 方法. 508 | user-agent.name=自定义 User Agent 509 | user-agent.description= 510 | ftp-user.name=FTP 默认用户名 511 | ftp-user.description= 512 | ftp-passwd.name=FTP 默认密码 513 | ftp-passwd.description=如果 URI 中包含用户名单不包含密码, aria2 首先会从 .netrc 文件中获取密码. 如果在 .netrc 文件中找到密码, 则使用该密码. 否则, 使用此选项设置的密码. 514 | ftp-pasv.name=被动模式 515 | ftp-pasv.description=在 FTP 中使用被动模式. 如果设置为"否", 则使用主动模式. 此选项不适用于 SFTP 传输. 516 | ftp-proxy.name=FTP 代理服务器 517 | ftp-proxy.description= 518 | ftp-proxy-user.name=FTP 代理服务器用户名 519 | ftp-proxy-user.description= 520 | ftp-proxy-passwd.name=FTP 代理服务器密码 521 | ftp-proxy-passwd.description= 522 | ftp-type.name=传输类型 523 | ftp-type.description= 524 | ftp-reuse-connection.name=连接复用 525 | ftp-reuse-connection.description= 526 | ssh-host-key-md.name=SSH 公钥校验和 527 | ssh-host-key-md.description=设置 SSH 主机公钥的校验和. 选项值格式为 TYPE=DIGEST. TYPE 为哈希类型. 支持的哈希类型为 sha-1 和 md5. DIGEST 是十六进制摘要. 例如: sha-1=b030503d4de4539dc7885e6f0f5e256704edf4c3. 此选项可以在使用 SFTP 时用来验证服务器的公钥. 如果此选项不设置, 即保留默认, 不会进行任何验证。 528 | bt-detach-seed-only.name=分离仅做种任务 529 | bt-detach-seed-only.description=统计当前活动下载任务(参见 -j 选项) 时排除仅做种的任务. 这意味着, 如果参数设置为 -j3, 此选项打开并且当前有 3 个正在活动的任务, 并且其中有 1 个进入做种模式, 那么其会从正在下载的数量中排除(即数量会变为 2), 在队列中等待的下一个任务将会开始执行. 但要知道, 在 RPC 方法中, 做种的任务仍然被认为是活动的下载任务. 530 | bt-enable-hook-after-hash-check.name=启用哈希检查完成事件 531 | bt-enable-hook-after-hash-check.description=允许 BT 下载哈希检查(参见 -V 选项) 完成后调用命令. 默认情况下, 当哈希检查成功后, 通过 --on-bt-download-complete 设置的命令将会被执行. 如果要禁用此行为, 请设置为"否". 532 | bt-enable-lpd.name=启用本地节点发现 (LPD) 533 | bt-enable-lpd.description= 534 | bt-exclude-tracker.name=BT 排除服务器地址 535 | bt-exclude-tracker.description=逗号分隔的 BT 排除服务器地址. 您可以使用 * 匹配所有地址, 因此将排除所有服务器地址. 当在 shell 命令行使用 * 时, 需要使用转义符或引号. 536 | bt-external-ip.name=外部 IP 地址 537 | bt-external-ip.description=指定用在 BitTorrent 下载和 DHT 中的外部 IP 地址. 它可能被发送到 BitTorrent 服务器. 对于 DHT, 此选项将会报告本地节点正在下载特定的种子. 这对于在私有网络中使用 DHT 非常关键. 虽然这个方法叫外部, 但其可以接受各种类型的 IP 地址. 538 | bt-force-encryption.name=强制加密 539 | bt-force-encryption.description=BT 消息中的内容需要使用 arc4 加密. 此选项是设置 --bt-require-crypto --bt-min-crypto-level=arc4 这两个选项的快捷方式. 此选项不会修改上述两个选项的内容. 如果设置为"是", 将拒绝以前的 BT 握手, 并仅使用模糊握手及加密消息. 540 | bt-hash-check-seed.name=做种前检查文件哈希 541 | bt-hash-check-seed.description=如果设置为"是", 当使用 --check-integrity 选项完成哈希检查及文件完成后才继续做种. 如果您希望仅当文件损坏或未完成时检查文件, 请设置为"否". 此选项仅对 BT 下载有效 542 | bt-load-saved-metadata.name=加载已保存的元数据文件 543 | bt-load-saved-metadata.description=当使用磁链下载时, 在从 DHT 获取种子元数据之前, 首先尝试加载使用 --bt-save-metadata 选项保存的文件. 如果文件加载成功, 则不会从 DHT 下载元数据. 544 | bt-max-open-files.name=最多打开文件数 545 | bt-max-open-files.description=设置 BT/Metalink 下载全局打开的最大文件数. 546 | bt-max-peers.name=最大连接节点数 547 | bt-max-peers.description=设置每个 BT 下载的最大连接节点数. 0 表示不限制. 548 | bt-metadata-only.name=仅下载种子文件 549 | bt-metadata-only.description=仅下载种子文件. 种子文件中描述的文件将不会下载. 此选项仅对磁链生效. 550 | bt-min-crypto-level.name=最低加密级别 551 | bt-min-crypto-level.description=设置加密方法的最小级别. 如果节点提供多种加密方法, aria2 将选择满足给定级别的最低级别. 552 | bt-prioritize-piece.name=优先下载 553 | bt-prioritize-piece.description=尝试先下载每个文件开头或结尾的分片. 此选项有助于预览文件. 参数可以包括两个关键词: head 和 tail. 如果包含两个关键词, 需要使用逗号分隔. 每个关键词可以包含一个参数, SIZE. 例如, 如果指定 head=SIZE, 每个文件的最前 SIZE 数据将会获得更高的优先级. tail=SIZE 表示每个文件的最后 SIZE 数据. SIZE 可以包含 K 或 M (1K = 1024, 1M = 1024K). 554 | bt-remove-unselected-file.name=删除未选择的文件 555 | bt-remove-unselected-file.description=当 BT 任务完成后删除未选择的文件. 要选择需要下载的文件, 请使用 --select-file 选项. 如果没有选择, 则所有文件都默认为需要下载. 此选项会从磁盘上直接删除文件, 请谨慎使用此选项. 556 | bt-require-crypto.name=需要加密 557 | bt-require-crypto.description=如果设置为"是", aria 将不会接受以前的 BitTorrent 握手协议(\19BitTorrent 协议)并建立连接. 因此 aria2 总是模糊握手. 558 | bt-request-peer-speed-limit.name=期望下载速度 559 | bt-request-peer-speed-limit.description=如果一个 BT 下载的整体下载速度低于此选项设置的值, aria2 会临时提高连接数以提高下载速度. 在某些情况下, 设置期望下载速度可以提高您的下载速度. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 560 | bt-save-metadata.name=保存种子文件 561 | bt-save-metadata.description=保存种子文件为 ".torrent" 文件. 此选项仅对磁链生效. 文件名为十六进制编码后的哈希值及 ".torrent"后缀. 保存的目录与下载文件的目录相同. 如果相同的文件已存在, 种子文件将不会保存. 562 | bt-seed-unverified.name=不检查已经下载的文件 563 | bt-seed-unverified.description=不检查之前下载文件中每个分片的哈希值. 564 | bt-stop-timeout.name=无速度时自动停止时间 565 | bt-stop-timeout.description=当 BT 任务下载速度持续为 0, 达到此选项设置的时间后停止下载. 如果设置为 0, 此功能将禁用. 566 | bt-tracker.name=BT 服务器地址 567 | bt-tracker.description=逗号分隔的 BT 服务器地址. 这些地址不受 --bt-exclude-tracker 选项的影响, 因为这些地址在 --bt-exclude-tracker 选项排除掉其他地址之后才会添加. 568 | bt-tracker-connect-timeout.name=BT 服务器连接超时时间 569 | bt-tracker-connect-timeout.description=设置 BT 服务器的连接超时时间 (秒). 当连接建立后, 此选项不再生效, 请使用 --bt-tracker-timeout 选项. 570 | bt-tracker-interval.name=BT 服务器连接间隔时间 571 | bt-tracker-interval.description=设置请求 BT 服务器的间隔时间 (秒). 此选项将完全覆盖服务器返回的最小间隔时间和间隔时间, aria2 仅使用此选项的值.如果设置为 0, aria2 将根据服务器的响应情况和下载进程决定时间间隔. 572 | bt-tracker-timeout.name=BT 服务器超时时间 573 | bt-tracker-timeout.description= 574 | dht-file-path.name=DHT (IPv4) 文件 575 | dht-file-path.description=修改 IPv4 DHT 路由表文件路径. 576 | dht-file-path6.name=DHT (IPv6) 文件 577 | dht-file-path6.description=修改 IPv6 DHT 路由表文件路径. 578 | dht-listen-port.name=DHT 监听端口 579 | dht-listen-port.description=设置 DHT (IPv4, IPv6) 和 UDP 服务器使用的 UDP 端口. 多个端口可以使用逗号 "," 分隔, 例如: 6881,6885. 您还可以使用短横线 "-" 表示范围: 6881-6999, 或可以一起使用: 6881-6889, 6999. 580 | dht-message-timeout.name=DHT 消息超时时间 581 | dht-message-timeout.description= 582 | enable-dht.name=启用 DHT (IPv4) 583 | enable-dht.description=启用 IPv4 DHT 功能. 此选项同时会启用 UDP 服务器支持. 如果种子设置为私有, 即使此选项设置为"是", aria2 也不会启用 DHT. 584 | enable-dht6.name=启用 DHT (IPv6) 585 | enable-dht6.description=启用 IPv6 DHT 功能. 如果种子设置为私有, 即使此选项设置为"是", aria2 也不会启用 DHT. 使用 --dht-listen-port 选项设置监听的端口. 586 | enable-peer-exchange.name=启用节点交换 587 | enable-peer-exchange.description=启用节点交换扩展. 如果种子设置为私有, 即使此选项设置为"是", aria2 也不会启用此功能. 588 | follow-torrent.name=下载种子中的文件 589 | follow-torrent.description=如果设置为"是"或"仅内存", 当后缀为 .torrent 或内容类型为 application/x-bittorrent 的文件下载完成时, aria2 将按种子文件读取并下载该文件中提到的文件. 如果设置为"仅内存", 该种子文件将不会写入到磁盘中, 而仅会存储在内存中. 如果设置为"否", 则 .torrent 文件会下载到磁盘中, 但不会按种子文件读取并且其中的文件不会进行下载. 590 | listen-port.name=监听端口 591 | listen-port.description=设置 BT 下载的 TCP 端口. 多个端口可以使用逗号 "," 分隔, 例如: 6881,6885. 您还可以使用短横线 "-" 表示范围: 6881-6999, 或可以一起使用: 6881-6889, 6999. 592 | max-overall-upload-limit.name=全局最大上传速度 593 | max-overall-upload-limit.description=设置全局最大上传速度 (字节/秒). 0 表示不限制. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 594 | max-upload-limit.name=最大上传速度 595 | max-upload-limit.description=设置每个任务的最大上传速度 (字节/秒). 0 表示不限制. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 596 | peer-id-prefix.name=节点 ID 前缀 597 | peer-id-prefix.description=指定节点 ID 的前缀. BT 中节点 ID 长度为 20 字节. 如果超过 20 字节, 将仅使用前 20 字节. 如果少于 20 字节, 将在其后不足随机的数据保证为 20 字节. 598 | peer-agent.name=Peer Agent 599 | peer-agent.description=指定 BT 扩展握手期间用于节点客户端版本的字符串. 600 | seed-ratio.name=最小分享率 601 | seed-ratio.description=指定分享率. 当分享率达到此选项设置的值时会完成做种. 强烈建议您将此选项设置为大于等于 1.0. 如果您想不限制分享比率, 可以设置为 0.0. 如果同时设置了 --seed-time 选项, 当任意一个条件满足时将停止做种. 602 | seed-time.name=最小做种时间 603 | seed-time.description=以 (小数形式的) 分钟指定做种时间. 此选项设置为 0 时, 将在 BT 任务下载完成后不进行做种. 604 | follow-metalink.name=下载 Metalink 中的文件 605 | follow-metalink.description=如果设置为"是"或"仅内存", 当后缀为 .meta4 或 .metalink 或内容类型为 application/metalink4+xml 或 application/metalink+xml 的文件下载完成时, aria2 将按 Metalink 文件读取并下载该文件中提到的文件. 如果设置为"仅内存", 该 Metalink 文件将不会写入到磁盘中, 而仅会存储在内存中. 如果设置为"否", 则 .metalink 文件会下载到磁盘中, 但不会按 Metalink 文件读取并且其中的文件不会进行下载. 606 | metalink-base-uri.name=基础 URI 607 | metalink-base-uri.description=指定基础 URI 以便解析本地磁盘中存储的 Metalink 文件里 metalink:url 和 metalink:metaurl 中的相对 URI 地址. 如果 URI 表示的为目录, 最后需要以 / 结尾. 608 | metalink-language.name=语言 609 | metalink-language.description= 610 | metalink-location.name=首选服务器位置 611 | metalink-location.description=首选服务器所在的位置. 可以使用逗号分隔的列表, 例如: jp,us. 612 | metalink-os.name=操作系统 613 | metalink-os.description=下载文件的操作系统. 614 | metalink-version.name=版本号 615 | metalink-version.description=下载文件的版本号. 616 | metalink-preferred-protocol.name=首选使用协议 617 | metalink-preferred-protocol.description=指定首选使用的协议. 可以设置为 http, https, ftp 或"无". 设置为"无"时禁用此选项. 618 | metalink-enable-unique-protocol.name=仅使用唯一协议 619 | metalink-enable-unique-protocol.description=如果一个 Metalink 文件可用多种协议, 并且此选项设置为"是", aria2 将只会使用其中一种. 使用 --metalink-preferred-protocol 参数指定首选的协议. 620 | enable-rpc.name=启用 JSON-RPC/XML-RPC 服务器 621 | enable-rpc.description= 622 | pause-metadata.name=种子文件下载完后暂停 623 | pause-metadata.description=当种子文件下载完成后暂停后续的下载. 在 aria2 中有 3 种种子文件的下载类型: (1) 下载 .torrent 文件. (2) 通过磁链下载的种子文件. (3) 下载 Metalink 文件. 这些种子文件下载完后会根据文件内容继续进行下载. 此选项会暂停这些后续的下载. 此选项仅当 --enable-rpc 选项启用时生效. 624 | rpc-allow-origin-all.name=接受所有远程请求 625 | rpc-allow-origin-all.description=在 RPC 响应头增加 Access-Control-Allow-Origin 字段, 值为 * . 626 | rpc-listen-all.name=在所有网卡上监听 627 | rpc-listen-all.description=在所有网络适配器上监听 JSON-RPC/XML-RPC 的请求, 如果设置为"否", 仅监听本地网络的请求. 628 | rpc-listen-port.name=监听端口 629 | rpc-listen-port.description= 630 | rpc-max-request-size.name=最大请求大小 631 | rpc-max-request-size.description=设置 JSON-RPC/XML-RPC 最大的请求大小. 如果 aria2 检测到请求超过设定的字节数, 会直接取消连接. 632 | rpc-save-upload-metadata.name=保存上传的种子文件 633 | rpc-save-upload-metadata.description=在 dir 选项设置的目录中保存上传的种子文件或 Metalink 文件. 文件名包括 SHA-1 哈希后的元数据和扩展名两部分. 对于种子文件, 扩展名为 '.torrent'. 对于 Metalink 为 '.meta4'. 如果此选项设置为"否", 通过 aria2.addTorrent() 或 aria2.addMetalink() 方法添加的下载将无法通过 --save-session 选项保存. 634 | rpc-secure.name=启用 SSL/TLS 635 | rpc-secure.description=RPC 将通过 SSL/TLS 加密传输. RPC 客户端需要使用 https 协议连接服务器. 对于 WebSocket 客户端, 使用 wss 协议. 使用 --rpc-certificate 和 --rpc-private-key 选项设置服务器的证书和私钥. 636 | allow-overwrite.name=允许覆盖 637 | allow-overwrite.description=如果相应的控制文件不存在时从头重新下载文件. 参见 --auto-file-renaming 选项. 638 | allow-piece-length-change.name=允许分片大小变化 639 | allow-piece-length-change.description=如果设置为"否", 当分片长度与控制文件中的不同时, aria2 将会中止下载. 如果设置为"是", 您可以继续, 但部分下载进度将会丢失. 640 | always-resume.name=始终断点续传 641 | always-resume.description=始终断点续传. 如果设置为"是", aria2 始终尝试断点续传, 如果无法恢复, 则中止下载. 如果设置为"否", 对于不支持断点续传的 URI 或 aria2 遇到 N 个不支持断点续传的 URI (N 为 --max-resume-failure-tries 选项设置的值), aria2 会从头下载文件. 参见 --max-resume-failure-tries 参数. 642 | async-dns.name=异步 DNS 643 | async-dns.description= 644 | auto-file-renaming.name=文件自动重命名 645 | auto-file-renaming.description=重新命名已经存在的文件. 此选项仅对 HTTP(S)/FTP 下载有效. 新的文件名后会在文件名后、扩展名 (如果有) 前追加句点和数字(1..9999). 646 | auto-save-interval.name=自动保存间隔 647 | auto-save-interval.description=每隔设置的秒数自动保存控制文件(*.aria2). 如果设置为 0, 下载期间控制文件不会自动保存. 不论设置的值为多少, aria2 会在任务结束时保存控制文件. 可以设置的值为 0 到 600. 648 | conditional-get.name=条件下载 649 | conditional-get.description=仅当本地文件比远程文件旧时才进行下载. 此功能仅适用于 HTTP(S) 下载. 如果在 Metalink 中文件大小已经被指定则功能无法生效. 同时此功能还将忽略 Content-Disposition 响应头. 如果存在控制文件, 此选项将被忽略. 此功能通过 If-Modified-Since 请求头获取较新的文件. 当获取到本地文件的修改时间时, 此功能将使用用户提供的文件名 (参见 --out 选项), 如果没有指定 --out 选项则使用 URI 中的文件名. 为了覆盖已经存在的文件, 需要使用 --allow-overwrite 参数. 650 | conf-path.name=配置文件路径 651 | conf-path.description= 652 | console-log-level.name=控制台日志级别 653 | console-log-level.description= 654 | content-disposition-default-utf8.name=使用 UTF-8 处理 Content-Disposition 655 | content-disposition-default-utf8.description=处理 "Content-Disposition" 头中的字符串时使用 UTF-8 字符集来代替 ISO-8859-1, 例如, 文件名参数, 但不是扩展版本的文件名. 656 | daemon.name=启用后台进程 657 | daemon.description= 658 | deferred-input.name=延迟加载 659 | deferred-input.description=如果设置为"是", aria2 在启动时不会读取 --input-file 选项设置的文件中的所有 URI 地址, 而是会在之后需要时按需读取. 如果输入文件中包含大量要下载的 URI, 此选项可以减少内存的使用. 如果设置为"否", aria2 会在启动时读取所有的 URI. 当 -save-session 使用时将会禁用 --deferred-input 选项. 660 | disable-ipv6.name=禁用 IPv6 661 | disable-ipv6.description= 662 | disk-cache.name=磁盘缓存 663 | disk-cache.description=启用磁盘缓存. 如果设置为 0, 将禁用磁盘缓存. 此功能将下载的数据缓存在内存中, 最多占用此选项设置的字节数. 缓存存储由 aria2 实例创建并对所有下载共享. 由于数据以较大的单位写入并按文件的偏移重新排序, 所以磁盘缓存的一个优点是减少磁盘的 I/O. 如果调用哈希检查时并且数据缓存在内存中时, 将不需要从磁盘中读取. 大小可以包含 K 或 M (1K = 1024, 1M = 1024K). 664 | download-result.name=下载结果 665 | download-result.description=此选项将修改下载结果的格式. 如果设置为"默认", 将打印 GID, 状态, 平均下载速度和路径/URI. 如果涉及多个文件, 仅打印第一个请求文件的路径/URI, 其余的将被忽略. 如果设置为"完整", 将打印 GID, 状态, 平均下载速度, 下载进度和路径/URI. 其中, 下载进度和路径/URI 将会每个文件打印一行. 如果设置为"隐藏", 下载结果将会隐藏. 666 | dscp.name=DSCP 667 | dscp.description=为 QoS 设置 BT 上行 IP 包的 DSCP 值. 此参数仅设置 IP 包中 TOS 字段的 DSCP 位, 而不是整个字段. 如果您从 /usr/include/netinet/ip.h 得到的值, 需要除以 4 (否则值将不正确, 例如您的 CS1 类将会转为 CS4). 如果您从 RFC, 网络供应商的文档, 维基百科或其他来源采取常用的值, 可以直接使用. 668 | rlimit-nofile.name=最多打开的文件描述符 669 | rlimit-nofile.description=设置打开的文件描述符的软限制 (soft limit). 此选项仅当满足如下条件时开放: a. 系统支持它 (posix). b. 限制没有超过硬限制 (hard limit). c. 指定的限制比当前的软限制高. 这相当于设置 ulimit, 除了其不能降低限制. 此选项仅当系统支持 rlimit API 时有效. 670 | enable-color.name=终端输出使用颜色 671 | enable-color.description= 672 | enable-mmap.name=启用 MMap 673 | enable-mmap.description=内存中存放映射文件. 当文件空间没有预先分配至, 此选项无效. 参见 --file-allocation. 674 | event-poll.name=事件轮询方法 675 | event-poll.description=设置事件轮询的方法. 可选的值包括 epoll, kqueue, port, poll 和 select. 对于 epoll, kqueue, port 和 poll, 只有系统支持时才可用. 最新的 Linux 支持 epoll. 各种 *BSD 系统包括 Mac OS X 支持 kqueue. Open Solaris 支持 port. 默认值根据您使用的操作系统不同而不同. 676 | file-allocation.name=文件分配方法 677 | file-allocation.description=指定文件分配方法. "无" 不会预先分配文件空间. "prealloc"会在下载开始前预先分配空间. 这将会根据文件的大小需要一定的时间. 如果您使用的是较新的文件系统, 例如 ext4 (带扩展支持), btrfs, xfs 或 NTFS (仅 MinGW 构建), "falloc" 是最好的选择. 其几乎可以瞬间分配大(数 GiB)文件. 不要在旧的文件系统, 例如 ext3 和 FAT32 上使用 falloc, 因为与 prealloc 花费的时间相同, 并且其会阻塞 aria2 直到分配完成. 当您的系统不支持 posix_fallocate(3) 函数时, falloc 可能无法使用. "trunc" 使用 ftruncate(2) 系统调用或平台特定的实现将文件截取到特定的长度. 在多文件的 BitTorrent 下载中, 若某文件与其相邻的文件共享相同的分片时, 则相邻的文件也会被分配. 678 | force-save.name=强制保存 679 | force-save.description=即使任务完成或删除时使用 --save-session 选项时也保存该任务. 此选项在这种情况下还会保存控制文件. 此选项可以保存被认为已经完成但正在做种的 BT 任务. 680 | save-not-found.name=保存未找到的文件 681 | save-not-found.description=当使用 --save-session 选项时, 即使当任务中的文件不存在时也保存该下载任务. 此选项同时会将这种情况保存到控制文件中. 682 | hash-check-only.name=仅哈希检查 683 | hash-check-only.description=如果设置为"是", 哈希检查完使用 --check-integrity 选项, 根据是否下载完成决定是否终止下载. 684 | human-readable.name=控制台可读输出 685 | human-readable.description=在控制台输出可读格式的大小和速度 (例如, 1.2Ki, 3.4Mi). 686 | keep-unfinished-download-result.name=保留未完成的任务 687 | keep-unfinished-download-result.description=保留所有未完成的下载结果, 即使超过了 --max-download-result 选项设置的数量. 这将有助于在会话文件中保存所有的未完成的下载 (参考 --save-session 选项). 需要注意的是, 未完成任务的数量没有上限. 如果不希望这样, 请关闭此选项. 688 | max-download-result.name=最多下载结果 689 | max-download-result.description=设置内存中存储最多的下载结果数量. 下载结果包括已完成/错误/已删除的下载. 下载结果存储在一个先进先出的队列中, 因此其可以存储最多指定的下载结果的数量. 当队列已满且有新的下载结果创建时, 最老的下载结果将从队列的最前部移除, 新的将放在最后. 此选项设置较大的值后如果经过几千次的下载将导致较高的内存消耗. 设置为 0 表示不存储下载结果. 注意, 未完成的下载将始终保存在内存中, 不考虑该选项的设置. 参考 --keep-unfinished-download-result 选项. 690 | max-mmap-limit.name=MMap 最大限制 691 | max-mmap-limit.description=设置启用 MMap (参见 --enable-mmap 选项) 最大的文件大小. 文件大小由一个下载任务中所有文件大小的和决定. 例如, 如果一个下载包含 5 个文件, 那么文件大小就是这些文件的总大小. 如果文件大小超过此选项设置的大小时, MMap 将会禁用. 692 | max-resume-failure-tries.name=最大断点续传尝试次数 693 | max-resume-failure-tries.description=当 --always-resume 选项设置为"否"时, 如果 aria2 检测到有 N 个 URI 不支持断点续传时, 将从头开始下载文件. 如果 N 设置为 0, 当所有 URI 都不支持断点续传时才会从头下载文件. 参见 --always-resume 选项. 694 | min-tls-version.name=最低 TLS 版本 695 | min-tls-version.description=指定启用的最低 SSL/TLS 版本. 696 | log-level.name=日志级别 697 | log-level.description= 698 | optimize-concurrent-downloads.name=优化并发下载 699 | optimize-concurrent-downloads.description=根据可用带宽优化并发下载的数量. aria2 使用之前统计的下载速度通过规则 N = A + B Log10 (速度单位为 Mbps) 得到并发下载的数量. 其中系数 A 和 B 可以在参数中以冒号分隔自定义. 默认值 (A=5, B=25) 可以在 1Mbps 网络上使用通常 5 个并发下载, 在 100Mbps 网络上为 50 个. 并发下载的数量保持在 --max-concurrent-downloads 参数定义的最大之下. 700 | piece-length.name=文件分片大小 701 | piece-length.description=设置 HTTP/FTP 下载的分配大小. aria2 根据这个边界分割文件. 所有的分割都是这个长度的倍数. 此选项不适用于 BitTorrent 下载. 如果 Metalink 文件中包含分片哈希的结果此选项也不适用. 702 | show-console-readout.name=显示控制台输出 703 | show-console-readout.description= 704 | summary-interval.name=下载摘要输出间隔 705 | summary-interval.description=设置下载进度摘要的输出间隔(秒). 设置为 0 禁止输出. 706 | max-overall-download-limit.name=全局最大下载速度 707 | max-overall-download-limit.description=设置全局最大下载速度 (字节/秒). 0 表示不限制. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 708 | max-download-limit.name=最大下载速度 709 | max-download-limit.description=设置每个任务的最大下载速度 (字节/秒). 0 表示不限制. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 710 | no-conf.name=禁用配置文件 711 | no-conf.description= 712 | no-file-allocation-limit.name=文件分配限制 713 | no-file-allocation-limit.description=不对比此参数设置大小小的分配文件. 您可以增加数值的单位 K 或 M (1K = 1024, 1M = 1024K). 714 | parameterized-uri.name=启用参数化 URI 支持 715 | parameterized-uri.description=启用参数化 URI 支持. 您可以指定部分的集合: http://{sv1,sv2,sv3}/foo.iso. 同时您也可以使用步进计数器指定数字化的序列: http://host/image[000-100:2].img. 步进计数器可以省略. 如果所有 URI 地址不指向同样的文件, 例如上述第二个示例, 需要使用 -Z 选项. 716 | quiet.name=禁用控制台输出 717 | quiet.description= 718 | realtime-chunk-checksum.name=实时数据块验证 719 | realtime-chunk-checksum.description=如果提供了数据块的校验和, 将在下载过程中通过校验和验证数据块. 720 | remove-control-file.name=删除控制文件 721 | remove-control-file.description=在下载前删除控制文件. 使用 --allow-overwrite=true 选项时, 总是从头开始下载文件. 此选项将有助于使用不支持断点续传代理服务器的用户. 722 | save-session.name=状态保存文件 723 | save-session.description=当退出时保存错误及未完成的任务到指定的文件中. 您可以在重启 aria2 时使用 --input-file 选项重新加载. 如果您希望输出的内容使用 GZip 压缩, 您可以在文件名后增加 .gz 扩展名. 请注意, 通过 aria2.addTorrent() 和 aria2.addMetalink() RPC 方法添加的下载, 其元数据没有保存到文件的将不会保存. 通过 aria2.remove() 和 aria2.forceRemove() 删除的下载将不会保存. 724 | save-session-interval.name=保存状态间隔 725 | save-session-interval.description=每隔此选项设置的时间(秒)后会保存错误或未完成的任务到 --save-session 选项指定的文件中. 如果设置为 0, 仅当 aria2 退出时才会保存. 726 | socket-recv-buffer-size.name=Socket 接收缓冲区大小 727 | socket-recv-buffer-size.description=设置 Socket 接收缓冲区最大的字节数. 指定为 0 时将禁用此选项. 当使用 SO_RCVBUF 选项调用 setsockopt() 时此选项的值将设置到 Socket 的文件描述符中. 728 | stop.name=自动关闭时间 729 | stop.description=在此选项设置的时间(秒)后关闭应用. 如果设置为 0, 此功能将禁用. 730 | truncate-console-readout.name=缩短控制台输出内容 731 | truncate-console-readout.description=缩短控制台输出的内容在一行中. 732 | -------------------------------------------------------------------------------- /AriaNg/langs/zh_Hant.txt: -------------------------------------------------------------------------------- 1 | [global] 2 | AriaNg Version=AriaNg 版本 3 | Operation Result=操作結果 4 | Operation Succeeded=操作成功 5 | is connected=已連線 6 | Error=錯誤 7 | OK=確定 8 | Confirm=確認 9 | Cancel=取消 10 | Close=關閉 11 | True=是 12 | False=否 13 | DEBUG=偵錯 (Debug) 14 | INFO=普通 (Info) 15 | WARN=警告 (Warn) 16 | ERROR=錯誤 (Error) 17 | Connecting=連線中 18 | Connected=已連線 19 | Disconnected=未連線 20 | Reconnecting=重連中 21 | Waiting to reconnect=等待重連 22 | Global=全域 23 | New=新增 24 | Start=開始工作 25 | Pause=暫停工作 26 | Retry=重試 27 | Retry Selected Tasks=重試選中的工作 28 | Delete=刪除工作 29 | Select All=全部選中 30 | Select None=全部不選 31 | Select Invert=反向選擇 32 | Select All Failed Tasks=全選失敗的工作 33 | Select All Completed Tasks=全選已完成的工作 34 | Select All Tasks=全部選中工作 35 | Display Order=顯示順序 36 | Copy Download Url=複製下載連結 37 | Copy Magnet Link=複製磁力連結 38 | Help=說明 39 | Search=搜尋 40 | Default=預設 41 | Expand=展開 42 | Collapse=摺疊 43 | Expand All=全部展開 44 | Collapse All=全部摺疊 45 | Open=打開 46 | Save=儲存 47 | Import=匯入 48 | Remove Task=刪除工作 49 | Remove Selected Task=刪除選中的工作 50 | Clear Stopped Tasks=清除已結束工作 51 | Click to view task detail=點選檢視工作詳情 52 | By File Name=依檔名 53 | By File Size=依檔案大小 54 | By Progress=依進度 55 | By Selected Status=依選中狀態 56 | By Remaining=依剩餘時間 57 | By Download Speed=依下載速度 58 | By Upload Speed=依上傳速度 59 | By Peer Address=依節點位址 60 | By Client Name=依客戶端名 61 | Filters=篩選器 62 | Download=下載 63 | Upload=上傳 64 | Downloading=正在下載 65 | Pending Verification=等待驗證 66 | Verifying=正在驗證 67 | Seeding=正在做種 68 | Waiting=正在等待 69 | Paused=已暫停 70 | Completed=已完成 71 | Error Occurred=發生錯誤 72 | Removed=已刪除 73 | Finished / Stopped=已完成 / 已停止 74 | Uncompleted=未完成 75 | Click to pin=點選固定 76 | Settings=系統設定 77 | AriaNg Settings=AriaNg 設定 78 | Aria2 Settings=Aria2 設定 79 | Basic Settings=基本設定 80 | HTTP/FTP/SFTP Settings=HTTP/FTP/SFTP 設定 81 | HTTP Settings=HTTP 設定 82 | FTP/SFTP Settings=FTP/SFTP 設定 83 | BitTorrent Settings=BitTorrent 設定 84 | Metalink Settings=Metalink 設定 85 | RPC Settings=RPC 設定 86 | Advanced Settings=進階設定 87 | AriaNg Debug Console=AriaNg 偵錯控制台 88 | Aria2 Status=Aria2 狀態 89 | File Name=檔名 90 | File Size=大小 91 | Progress=進度 92 | Share Ratio=分享率 93 | Remaining=剩餘時間 94 | Download Speed=下載速度 95 | Upload Speed=上傳速度 96 | Links=連結 97 | Torrent File=種子檔案 98 | Metalink File=Metalink 檔案 99 | File Name:=檔名: 100 | Options=選項 101 | Overview=總覽 102 | Pieces=區塊資訊 103 | Files=檔案清單 104 | Peers=連線狀態 105 | Task Name=工作名稱 106 | Task Size=工作大小 107 | Task Status=工作狀態 108 | Error Description=錯誤描述 109 | Health Percentage=健康度 110 | Info Hash=特徵值 111 | Seeders=種子數 112 | Connections=連線數 113 | Seed Creation Time=種子建立時間 114 | Download Url=下載位址 115 | Download Dir=下載路徑 116 | BT Tracker Servers=BT 伺服器 117 | Copy=複製 118 | (Choose Files)=(選擇檔案) 119 | Videos=影片 120 | Audios=音訊 121 | Pictures=圖片 122 | Documents=文件 123 | Applications=應用程式 124 | Archives=封存檔案 125 | Other=其他 126 | Custom=自訂 127 | Custom Choose File=自訂選擇檔案 128 | Address=位址 129 | Client=客戶端 130 | Status=狀態 131 | Speed=速度 132 | (local)=(本機) 133 | No Data=無資料 134 | No connected peers=沒有連線到其他節點 135 | Failed to change some tasks state.=修改一些工作狀態時失敗. 136 | Confirm Retry=確認重試 137 | Are you sure you want to retry the selected task? AriaNg will create same task after clicking OK.=您是否要重試選中的工作? 點選 "確定" 後, AriaNg 將會建立相同的工作. 138 | Failed to retry this task.=該工作重試失敗. 139 | {successCount} tasks have been retried and {failedCount} tasks are failed.={{successCount}} 個工作重試成功以及 {{failedCount}} 個工作失敗. 140 | Confirm Remove=確認刪除 141 | Are you sure you want to remove the selected task?=您是否要刪除選中的工作? 142 | Failed to remove some task(s).=刪除一些工作時失敗. 143 | Confirm Clear=確認清除 144 | Are you sure you want to clear stopped tasks?=您是否要清除已結束的工作? 145 | Download Links:=下載連結: 146 | Download Now=立即下載 147 | Download Later=手動下載 148 | Open Torrent File=打開種子檔案 149 | Open Metalink File=打開 Metalink 檔案 150 | Support multiple URLs, one URL per line.=支援多個 URL 位址, 每個位址佔一行. 151 | Your browser does not support loading file!=您的瀏覽器不支援載入檔案! 152 | The selected file type is invalid!=選擇的檔案類型無效! 153 | Failed to load file!=載入檔案失敗! 154 | Download Completed=下載完成 155 | BT Download Completed=BT 下載完成 156 | Download Error=下載出錯 157 | AriaNg Url=AriaNg 連結 158 | Command API Url=命令行 API 連結 159 | Export Command API=匯出命令行 API 160 | Export=匯出 161 | Copied=已複製 162 | Pause After Task Created=工作建立後暫停 163 | Language=語言 164 | Theme=主題 165 | Light=淺色 166 | Dark=深色 167 | Follow system settings=跟隨系统設定 168 | Debug Mode=偵錯模式 169 | Page Title=頁面標題 170 | Preview=預覽 171 | Tips: You can use the "noprefix" tag to ignore the prefix, "nosuffix" tag to ignore the suffix, and "scale\=n" tag to set the decimal precision.=小提示: 您可以使用 "noprefix" 標籤忽略前綴, "nosuffix" 標籤忽略副檔名, 以及 "scale\=n" 標籤設定小數的精度. 172 | Example: ${downspeed:noprefix:nosuffix:scale\=1}=示例: ${downspeed:noprefix:nosuffix:scale\=1} 173 | Updating Page Title Interval=頁面標題更新間隔 174 | Enable Browser Notification=啟用瀏覽器通知 175 | Browser Notification Sound=瀏覽器通知聲音 176 | Browser Notification Frequency=瀏覽器通知頻次 177 | Unlimited=無限制 178 | High (Up to 10 Notifications / 1 Minute)=高 (最多 10 條通知 / 每分鐘) 179 | Middle (Up to 1 Notification / 1 Minute)=中 (最多 1 條通知 / 每分鐘) 180 | Low (Up to 1 Notification / 5 Minutes)=低 (最多 1 條通知 / 每5分鐘) 181 | WebSocket Auto Reconnect Interval=WebSocket 自动重連線時間 182 | Aria2 RPC Alias=Aria2 RPC 別名 183 | Aria2 RPC Address=Aria2 RPC 位址 184 | Aria2 RPC Protocol=Aria2 RPC 協定 185 | Aria2 RPC Http Request Method=Aria2 RPC Http 要求方法 186 | POST method only supports aria2 v1.15.2 and above.=POST 方法僅支援 aria2 v1.15.2 及以上. 187 | Aria2 RPC Request Headers=Aria2 RPC 要求標頭 188 | Support multiple request headers, one header per line, each line containing "header name: header value".=支援多個要求標頭, 每個要求標頭佔一行, 每行包含 "標頭名: 標頭值". 189 | Aria2 RPC Secret Token=Aria2 RPC 金鑰 190 | Activate=啟用 191 | Reset Settings=重設設定 192 | Confirm Reset=確認重設 193 | Are you sure you want to reset all settings?=您是否要重設所有設定? 194 | Clear Settings History=清除設定歷史 195 | Are you sure you want to clear all settings history?=您是否要清除所有設定的歷史紀錄? 196 | Delete RPC Setting=刪除 RPC 設定 197 | Add New RPC Setting=加入新 RPC 設定 198 | Are you sure you want to remove rpc setting "{rpcName}"?=您是否要刪除 RPC 設定 "{{rpcName}}"? 199 | Updating Global Stat Interval=全域狀態更新間隔 200 | Updating Task Information Interval=工作資訊更新間隔 201 | Keyboard Shortcuts=鍵盤快速鍵 202 | Supported Keyboard Shortcuts=支援的鍵盤快速鍵 203 | Set Focus On Search Box=將焦點設在搜尋框上 204 | Swipe Gesture=滑動手勢 205 | Change Tasks Order by Drag-and-drop=拖拽工作排序 206 | Action After Creating New Tasks=建立新工作後執行操作 207 | Navigate to Task List Page=轉到工作清單頁面 208 | Navigate to Task Detail Page=轉到工作詳情頁面 209 | Action After Retrying Task=重試工作後執行操作 210 | Navigate to Downloading Tasks Page=轉到正在下載工作頁面 211 | Stay on Current Page=留在目前頁面 212 | Remove Old Tasks After Retrying=重試工作後刪除原工作 213 | Confirm Task Removal=工作刪除前確認 214 | Include Prefix When Copying From Task Details=工作詳情頁複製時包括前綴 215 | Show Pieces Info In Task Detail Page=工作詳情頁顯示區塊資訊 216 | Pieces Amount is Less than or Equal to {value}=區塊數量小於等於 {{value}} 217 | RPC List Display Order=RPC 清單顯示順序 218 | Each Task List Page Uses Independent Display Order=各工作清單頁面使用獨立顯示順序 219 | Recently Used=最近使用 220 | RPC Alias=RPC 別名 221 | Import / Export AriaNg Settings=匯入 / 匯出 AriaNg 設定 222 | Import Settings=匯入設定 223 | Export Settings=匯出設定 224 | AriaNg settings data=AriaNg 設定資料 225 | Confirm Import=確認匯入 226 | Are you sure you want to import all settings?=您是否要匯入所有設定? 227 | Invalid settings data format!=無效的設定資料格式! 228 | Data has been copied to clipboard.=資料已經複製到剪貼簿中. 229 | Supported Placeholder=支援的預留位置 230 | AriaNg Title=AriaNg 標題 231 | Current RPC Alias=目前 RPC 別名 232 | Downloading Count=正在下載數量 233 | Waiting Count=正在等待數量 234 | Stopped Count=已停止數量 235 | You have disabled notification in your browser. You should change your browser's settings before you enable this function.=您已經在瀏覽器中停用通知功能. 如需使用此功能, 請修改您瀏覽器的設定. 236 | Language resource has been updated, please reload the page for the changes to take effect.=語言資源已經更新, 請重新載入頁面使其生效. 237 | Configuration has been modified, please reload the page for the changes to take effect.=配置已經修改, 請重新載入頁面使其生效. 238 | Reload AriaNg=重新載入 AriaNg 239 | Show Secret=顯示金鑰 240 | Hide Secret=隱藏金鑰 241 | Aria2 Version=Aria2 版本 242 | Enabled Features=已啟用的功能 243 | Operations=操作 244 | Reconnect=重新連線 245 | Save Session=儲存會話 246 | Shutdown Aria2=關閉 Aria2 247 | Confirm Shutdown=確認關閉 248 | Are you sure you want to shutdown aria2?=您是否要關閉 aria2? 249 | Session has been saved successfully.=會話已經成功儲存. 250 | Aria2 has been shutdown successfully.=Aria2 已經成功關閉. 251 | Toggle Navigation=切換導航 252 | Shortcut=捷徑 253 | Global Rate Limit=全域速度限制 254 | Loading=正在載入... 255 | More Than One Day=超過1天 256 | Unknown=不詳 257 | Bytes=位元組 258 | Hours=小時 259 | Minutes=分 260 | Seconds=秒 261 | Milliseconds=毫秒 262 | Http=Http 263 | Http (Disabled)=Http (已停用) 264 | Https=Https 265 | WebSocket=WebSocket 266 | WebSocket (Disabled)=WebSocket (已停用) 267 | WebSocket (Security)=WebSocket (安全) 268 | Http and WebSocket would be disabled when accessing AriaNg via Https.=使用 Https 訪問 AriaNg 時,Http 和 WebSocket 將被停用. 269 | POST=POST 270 | GET=GET 271 | Enabled=啟用 272 | Disabled=停用 273 | Always=始終 274 | Never=從不 275 | BitTorrent=BitTorrent 276 | Changes to the settings take effect after refreshing page.=設定將在頁面重新整理後生效. 277 | Logging Time=記錄時間 278 | Log Level=記錄層級 279 | Auto Refresh=自動刷新 280 | Refresh Now=立即刷新 281 | Clear Logs=清除記錄 282 | Are you sure you want to clear debug logs?=您是否要清除偵錯記錄? 283 | Show Detail=顯示詳情 284 | Log Detail=記錄詳情 285 | Aria2 RPC Debug=Aria2 RPC 偵錯 286 | Aria2 RPC Request Method=Aria2 RPC 要求方法 287 | Aria2 RPC Request Parameters=Aria2 RPC 要求參數 288 | Aria2 RPC Response=Aria2 RPC 回應 289 | Execute=執行 290 | RPC method is illegal!=RPC方法錯誤! 291 | AriaNg does not support this RPC method!=AriaNg 不支援該RPC方法! 292 | RPC request parameters are invalid!=RPC 要求參數無效! 293 | Type is illegal!=類型錯誤! 294 | Parameter is invalid!=要求參數無效! 295 | Option value cannot be empty!=參數內容不能為空! 296 | Input number is invalid!=輸入的數字無效! 297 | Input number is below min value!=輸入的數字小於最小值 {{value}} ! 298 | Input number is above max value!=輸入的數字大於最大值 {{value}} ! 299 | Input value is invalid!=輸入的內容無效! 300 | Protocol is invalid!=協定無效! 301 | RPC host cannot be empty!=RPC 主機不能為空! 302 | RPC secret is not base64 encoded!=RPC 金鑰不是 Base64 編碼後的字串! 303 | URL is not base64 encoded!=指定 URL 不是 Base64 編碼後的字串! 304 | Tap to configure and get started with AriaNg.=您還沒有進行過設定, 點選這裡進行設定. 305 | Cannot initialize WebSocket!=無法初始化 WebSocket! 306 | Cannot connect to aria2!=無法連線到 aria2! 307 | Access Denied!=拒絕訪問! 308 | You cannot use AriaNg because this browser does not meet the minimum requirements for data storage.=您無法使用 AriaNg, 因為此瀏覽器不滿足資料儲存的最低要求. 309 | 310 | [error] 311 | unknown=不詳錯誤. 312 | operation.timeout=操作超時. 313 | resource.notfound=無法找到指定資源. 314 | resource.notfound.max-file-not-found=無法找到指定資源. 參見 --max-file-not-found option 參數. 315 | download.aborted.lowest-speed-limit=由於下載速度過慢, 下載已經終止. 參見 --lowest-speed-limit option 參數. 316 | network.problem=網路問題. 317 | resume.notsupported=伺服器不支援斷點續傳. 318 | space.notenough=可用磁碟空間不足. 319 | piece.length.different=分段大小與 .aria2 控制檔案中的不同. 參見 --allow-piece-length-change 參數. 320 | download.sametime=aria2 已經下載了另一個相同檔案. 321 | download.torrent.sametime=aria2 已經下載了另一個相同雜湊的種子檔案. 322 | file.exists=檔案已經存在. 參見 --allow-overwrite 參數. 323 | file.rename.failed=檔案重命名失敗. 參見 --auto-file-renaming 參數. 324 | file.open.failed=檔案打開失敗. 325 | file.create.failed=檔案建立或刪除已有檔案失敗. 326 | io.error=檔案系統出錯. 327 | directory.create.failed=無法建立指定目錄. 328 | name.resolution.failed=域名解析失敗. 329 | metalink.file.parse.failed=解析 Metalink 檔案失敗. 330 | ftp.command.failed=FTP 命令執行失敗. 331 | http.response.header.bad=HTTP 返回頭無效或無法識別. 332 | redirects.toomany=指定位址重新導向過多. 333 | http.authorization.failed=HTTP 認證失敗. 334 | bencoded.file.parse.failed=解析種子檔案失敗. 335 | torrent.file.corrupted=指定 ".torrent" 種子檔案已經損壞或缺少 aria2 需要的資訊. 336 | magnet.uri.bad=指定磁力連結位址無效. 337 | option.bad=設定錯誤. 338 | server.overload=遠端伺服器繁忙, 無法處理目前要求. 339 | rpc.request.parse.failed=處理 RPC 要求失敗. 340 | checksum.failed=檔案校驗失敗. 341 | 342 | [languages] 343 | English=英語 344 | Simplified Chinese=簡體中文 345 | Traditional Chinese=繁體中文 346 | Italian=義大利語 347 | French=法語 348 | 349 | [format] 350 | longdate=YYYY年MM月DD日 HH:mm:ss 351 | time.millisecond={{value}} 毫秒 352 | time.milliseconds={{value}} 毫秒 353 | time.second={{value}} 秒 354 | time.seconds={{value}} 秒 355 | time.minute={{value}} 分鐘 356 | time.minutes={{value}} 分鐘 357 | time.hour={{value}} 小時 358 | time.hours={{value}} 小時 359 | requires.aria2-version=需要 aria2 v{{version}} 或更高版本 360 | task.new.download-links=下載連結 ({{count}} 個連結): 361 | task.pieceinfo=已完成: {{completed}}, 共計: {{total}} 塊 362 | task.error-occurred=發生錯誤 ({{errorcode}}) 363 | task.verifying-percent=正在驗證 ({{verifiedPercent}}%) 364 | settings.file-count=({{count}} 個檔案) 365 | settings.total-count=(共計: {{count}}個) 366 | debug.latest-logs=最近 {{count}} 條記錄 367 | 368 | [rpc.error] 369 | unauthorized=認證失敗! 370 | 371 | [option] 372 | true=是 373 | false=否 374 | default=預設 375 | none=無 376 | hide=隱藏 377 | full=完整 378 | http=Http 379 | https=Https 380 | ftp=Ftp 381 | mem=僅記憶體 382 | get=GET 383 | tunnel=TUNNEL 384 | plain=明文 385 | arc4=ARC4 386 | binary=二進位 387 | ascii=ASCII 388 | debug=偵錯 (Debug) 389 | info=普通 (Info) 390 | notice=一般 (Notice) 391 | warn=警告 (Warn) 392 | error=錯誤 (Error) 393 | adaptive=自適應 394 | epoll=epoll 395 | falloc=falloc 396 | feedback=反饋 397 | geom=幾何 398 | inorder=順序 399 | kqueue=kqueue 400 | poll=poll 401 | port=port 402 | prealloc=prealloc 403 | random=隨機 404 | select=select 405 | trunc=trunc 406 | SSLv3=SSLv3 407 | TLSv1=TLSv1 408 | TLSv1.1=TLSv1.1 409 | TLSv1.2=TLSv1.2 410 | 411 | [options] 412 | dir.name=下載路徑 413 | dir.description= 414 | log.name=記錄檔案 415 | log.description=記錄檔案的路徑. 如果設定為 "-", 記錄則寫入到 stdout. 如果設定為空字串(""), 記錄將不會記錄到磁碟上. 416 | max-concurrent-downloads.name=最大同時下載數 417 | max-concurrent-downloads.description= 418 | check-integrity.name=檢查完整性 419 | check-integrity.description=通過對檔案的每個分塊或整個檔案進行雜湊驗證來檢查檔案的完整性. 此選項僅對BT、Metalink及設定了 --checksum 選項的 HTTP(S)/FTP 鏈接生效. 420 | continue.name=斷點續傳 421 | continue.description=繼續下載部分完成的檔案. 啟用此選項可以繼續下載從瀏覽器或其他程式依順序下載的檔案. 此選項目前只支援 HTTP(S)/FTP 下載的檔案. 422 | all-proxy.name=代理伺服器 423 | all-proxy.description=設定所有協定的代理伺服器位址. 您還可以針對特定的協定覆蓋此選項, 即使用 --http-proxy, --https-proxy 和 --ftp-proxy 選項. 此設定將會影響所有下載. 代理伺服器位址的格式為 [http://][USER:PASSWORD@]HOST[:PORT]. 424 | all-proxy-user.name=代理伺服器使用者名稱 425 | all-proxy-user.description= 426 | all-proxy-passwd.name=代理伺服器密碼 427 | all-proxy-passwd.description= 428 | checksum.name=總和檢查碼 429 | checksum.description=設定總和檢查碼. 選項值格式為 TYPE=DIGEST. TYPE 為雜湊類型. 支援的雜湊類型列在 aria2c -v 的 Hash Algorithms 中. DIGEST 是十六進位摘要. 例如, 設定 sha-1 摘要如同這樣: sha-1=0192ba11326fe2298c8cb4de616f4d4140213838 此選項僅對 HTTP(S)/FTP 下載生效. 430 | connect-timeout.name=連線超時時間 431 | connect-timeout.description=設定建立 HTTP/FTP/代理伺服器 連線的超時時間(秒). 當連線建立後, 此選項不再生效, 請使用 --timeout 選項. 432 | dry-run.name=模擬運行 433 | dry-run.description=如果設定為"是", aria2 將僅檢查遠端檔案是否存在而不會下載檔案內容. 此選項僅對 HTTP/FTP 下載生效. 如果設定為 true, BT 下載將會直接取消. 434 | lowest-speed-limit.name=最小速度限制 435 | lowest-speed-limit.description=當下載速度低於此選項設定的值(B/s) 時將會關閉連線. 0 表示不設定最小速度限制. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 此選項不會影響 BT 下載. 436 | max-connection-per-server.name=單伺服器最大連線數 437 | max-connection-per-server.description= 438 | max-file-not-found.name=檔案未找到重試次數 439 | max-file-not-found.description=如果 aria2 從遠端 HTTP/FTP 伺服器收到 "檔案未找到" 的狀態超過此選項設定的次數後下載將會失敗. 設定為 0 將會停用此選項. 此選項僅影響 HTTP/FTP 伺服器. 重試時同時會記錄重試次數, 所以也需要設定 --max-tries 這個選項. 440 | max-tries.name=最大嘗試次數 441 | max-tries.description=設定最大嘗試次數. 0 表示不限制. 442 | min-split-size.name=最小檔案分段大小 443 | min-split-size.description=aria2 不會分割小於 2*SIZE 位元組的檔案. 例如, 檔案大小為 20MB, 如果 SIZE 為 10M, aria2 會把檔案分成 2 段 [0-10MB) 和 [10MB-20MB), 並且使用 2 個源進行下載 (如果 --split >= 2). 如果 SIZE 為 15M, 由於 2*15M > 20MB, 因此 aria2 不會分割檔案並使用 1 個源進行下載. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 可以設定的值為: 1M-1024M. 444 | netrc-path.name=.netrc 檔案路徑 445 | netrc-path.description= 446 | no-netrc.name=停用 netrc 447 | no-netrc.description= 448 | no-proxy.name=不使用代理伺服器清單 449 | no-proxy.description=設定不使用代理伺服器的主機名, 域名, 包含或不包含子網掩碼的網路位址, 多個使用逗號分隔. 450 | out.name=檔名 451 | out.description=下載檔案的檔名. 其總是相對於 --dir 選項中設定的路徑. 當使用 --force-sequential 參數時此選項無效. 452 | proxy-method.name=代理伺服器要求方法 453 | proxy-method.description=設定用來要求代理伺服器的方法. 方法可設定為 GET 或 TUNNEL. HTTPS 下載將忽略此選項並總是使用 TUNNEL. 454 | remote-time.name=獲取伺服器檔案時間 455 | remote-time.description=從 HTTP/FTP 服務獲取遠端檔案的時間戳, 如果可用將設定到本機檔案 456 | reuse-uri.name=URI 復用 457 | reuse-uri.description=當所有給定的 URI 位址都已使用, 繼續使用已經使用過的 URI 位址. 458 | retry-wait.name=重試等待時間 459 | retry-wait.description=設定重試間隔時間(秒). 當此選項的值大於 0 時, aria2 在 HTTP 伺服器返回 503 響應時將會重試. 460 | server-stat-of.name=伺服器狀態儲存檔案 461 | server-stat-of.description=指定用來儲存伺服器狀態的檔名. 您可以使用 --server-stat-if 參數讀取儲存的資料. 462 | server-stat-timeout.name=伺服器狀態超時 463 | server-stat-timeout.description=指定伺服器狀態的過期時間 (單位為秒). 464 | split.name=單工作連線數 465 | split.description=下載時使用 N 個連線. 如果提供超過 N 個 URI 位址, 則使用前 N 個位址, 剩餘的位址將作為備用. 如果提供的 URI 位址不足 N 個, 這些位址多次使用以保證同時建立 N 個連線. 同一伺服器的連線數會被 --max-connection-per-server 選項限制. 466 | stream-piece-selector.name=分段選擇演算法 467 | stream-piece-selector.description=指定 HTTP/FTP 下載使用的分段選擇演算法. 分段表示的是並行下載時固定長度的分隔段. 如果設定為"預設", aria2 將會依減少建立連線數選擇分段. 由於建立連線操作的成本較高, 因此這是合理的預設行為. 如果設定為"順序", aria2 將選擇索引最小的分段. 索引為 0 時表示為檔案的第一個分段. 這將有助於視頻的邊下邊播. --enable-http-pipelining 選項有助於減少重連線的開銷. 請注意, aria2 依賴於 --min-split-size 選項, 所以有必要對 --min-split-size 選項設定一個合理的值. 如果設定為"隨機", aria2 將隨機選擇一個分段. 就像"順序"一樣, 依賴於 --min-split-size 選項. 如果設定為"幾何", aria2 會先選擇索引最小的分段, 然後會為之前選擇的分段保留指數增長的空間. 這將減少建立連線的次數, 同時檔案開始部分將會先行下載. 這也有助於視頻的邊下邊播. 468 | timeout.name=超時時間 469 | timeout.description= 470 | uri-selector.name=URI 選擇演算法 471 | uri-selector.description=指定 URI 選擇的演算法. 可選的值包括 "依順序", "反饋" 和 "自適應". 如果設定為"依順序", URI 將依清單中出現的順序使用. 如果設定為"反饋", aria2 將根據之前的下載速度選擇 URI 清單中下載速度最快的伺服器. 同時也將有效跳過無效鏡像. 之前統計的下載速度將作為伺服器狀態檔案的一部分, 參見 --server-stat-of 和 --server-stat-if 選項. 如果設定為"自適應", 將從最好的鏡像和保留的連線裡選擇一項. 補充說明, 其返回的鏡像沒有被測試過, 同時如果每個鏡像都已經被測試過時, 返回的鏡像還會被重新測試. 否則, 其將不會選擇其他鏡像. 例如"反饋", 其使用伺服器狀態檔案. 472 | check-certificate.name=檢查證書 473 | check-certificate.description= 474 | http-accept-gzip.name=支援 GZip 475 | http-accept-gzip.description=如果遠端伺服器的響應頭中包含 Content-Encoding: gzip 或 Content-Encoding: deflate , 將發送包含 Accept: deflate, gzip 的要求標頭並解壓縮響應. 476 | http-auth-challenge.name=認證質詢 477 | http-auth-challenge.description=僅當伺服器需要時才發送 HTTP 認證要求標頭. 如果設定為"否", 每次都會發送認證要求標頭. 例外: 如果使用者名稱和密碼包含在 URI 中, 將忽略此選項並且每次都會發送認證要求標頭. 478 | http-no-cache.name=停用快取 479 | http-no-cache.description=發送的要求標頭中將包含 Cache-Control: no-cache 和 Pragma: no-cache header 以避免內容被快取. 如果設定為"否", 上述要求標頭將不會發送, 同時您也可以使用 --header 選項將 Cache-Control 要求標頭加入進去. 480 | http-user.name=HTTP 預設使用者名稱 481 | http-user.description= 482 | http-passwd.name=HTTP 預設密碼 483 | http-passwd.description= 484 | http-proxy.name=HTTP 代理伺服器 485 | http-proxy.description= 486 | http-proxy-user.name=HTTP 代理伺服器使用者名稱 487 | http-proxy-user.description= 488 | http-proxy-passwd.name=HTTP 代理伺服器密碼 489 | http-proxy-passwd.description= 490 | https-proxy.name=HTTPS 代理伺服器 491 | https-proxy.description= 492 | https-proxy-user.name=HTTPS 代理伺服器使用者名稱 493 | https-proxy-user.description= 494 | https-proxy-passwd.name=HTTPS 代理伺服器密碼 495 | https-proxy-passwd.description= 496 | referer.name=要求來源 497 | referer.description=設定 HTTP 要求來源 (Referer). 此選項將影響所有 HTTP/HTTPS 下載. 如果設定為 *, 要求來源將設定為下載連結. 此選項可以配合 --parameterized-uri 選項使用. 498 | enable-http-keep-alive.name=啟用持久連線 499 | enable-http-keep-alive.description=啟用 HTTP/1.1 持久連線. 500 | enable-http-pipelining.name=啟用 HTTP 管線化 501 | enable-http-pipelining.description=啟用 HTTP/1.1 管線化. 502 | header.name=自訂要求標頭 503 | header.description=增加 HTTP 要求標頭內容. 每行放置一項, 每項包含 "標頭名: 標頭值". 504 | save-cookies.name=Cookies 儲存路徑 505 | save-cookies.description=以 Mozilla/Firefox(1.x/2.x)/Netscape 格式將 Cookies 儲存到檔案中. 如果檔案已經存在, 將被覆蓋. 會話過期的 Cookies 也將會儲存, 其過期時間將會設定為 0. 506 | use-head.name=啟用 HEAD 方法 507 | use-head.description=第一次要求 HTTP 伺服器時使用 HEAD 方法. 508 | user-agent.name=自訂 User Agent 509 | user-agent.description= 510 | ftp-user.name=FTP 預設使用者名稱 511 | ftp-user.description= 512 | ftp-passwd.name=FTP 預設密碼 513 | ftp-passwd.description=如果 URI 中包含使用者名稱單不包含密碼, aria2 首先會從 .netrc 檔案中獲取密碼. 如果在 .netrc 檔案中找到密碼, 則使用該密碼. 否則, 使用此選項設定的密碼. 514 | ftp-pasv.name=被動模式 515 | ftp-pasv.description=在 FTP 中使用被動模式. 如果設定為"否", 則使用主動模式. 此選項不適用於 SFTP 傳輸. 516 | ftp-proxy.name=FTP 代理伺服器 517 | ftp-proxy.description= 518 | ftp-proxy-user.name=FTP 代理伺服器使用者名稱 519 | ftp-proxy-user.description= 520 | ftp-proxy-passwd.name=FTP 代理伺服器密碼 521 | ftp-proxy-passwd.description= 522 | ftp-type.name=傳輸類型 523 | ftp-type.description= 524 | ftp-reuse-connection.name=連線復用 525 | ftp-reuse-connection.description= 526 | ssh-host-key-md.name=SSH 公鑰總和檢查碼 527 | ssh-host-key-md.description=設定 SSH 主機公鑰的總和檢查碼. 選項值格式為 TYPE=DIGEST. TYPE 為雜湊類型. 支援的雜湊類型為 sha-1 和 md5. DIGEST 是十六進位摘要. 例如: sha-1=b030503d4de4539dc7885e6f0f5e256704edf4c3. 此選項可以在使用 SFTP 時用來驗證伺服器的公鑰. 如果此選項不設定, 即保留預設, 不會進行任何驗證。 528 | bt-detach-seed-only.name=分離僅做種工作 529 | bt-detach-seed-only.description=統計目前活動下載工作(參見 -j 選項) 時排除僅做種的工作. 這意味著, 如果參數設定為 -j3, 此選項打開並且目前有 3 個正在活動的工作, 並且其中有 1 個進入做種模式, 那麼其會從正在下載的數量中排除(即數量會變為 2), 在隊列中等待的下一個工作將會開始執行. 但要知道, 在 RPC 方法中, 做種的工作仍然被認為是活動的下載工作. 530 | bt-enable-hook-after-hash-check.name=啟用雜湊檢查完成事件 531 | bt-enable-hook-after-hash-check.description=允許 BT 下載雜湊檢查(參見 -V 選項) 完成後調用命令. 預設情況下, 當雜湊檢查成功後, 通過 --on-bt-download-complete 設定的命令將會被執行. 如果要停用此行為, 請設定為"否". 532 | bt-enable-lpd.name=啟用本機節點發現 (LPD) 533 | bt-enable-lpd.description= 534 | bt-exclude-tracker.name=BT 排除伺服器位址 535 | bt-exclude-tracker.description=逗號分隔的 BT 排除伺服器位址. 您可以使用 * 匹配所有位址, 因此將排除所有伺服器位址. 當在 shell 命令行使用 * 時, 需要使用跳脫字元或引號. 536 | bt-external-ip.name=外部 IP 位址 537 | bt-external-ip.description=指定用在 BitTorrent 下載和 DHT 中的外部 IP 位址. 它可能被發送到 BitTorrent 伺服器. 對於 DHT, 此選項將會報告本機節點正在下載特定的種子. 這對於在私有網路中使用 DHT 非常關鍵. 雖然這個方法叫外部, 但其可以接受各種類型的 IP 位址. 538 | bt-force-encryption.name=強制加密 539 | bt-force-encryption.description=BT 消息中的內容需要使用 arc4 加密. 此選項是設定 --bt-require-crypto --bt-min-crypto-level=arc4 這兩個選項的快捷方式. 此選項不會修改上述兩個選項的內容. 如果設定為"是", 將拒絕以前的 BT 握手, 並僅使用模糊握手及加密消息. 540 | bt-hash-check-seed.name=做種前檢查檔案雜湊 541 | bt-hash-check-seed.description=如果設定為"是", 當使用 --check-integrity 選項完成雜湊檢查及檔案完成後才繼續做種. 如果您希望僅當檔案損壞或未完成時檢查檔案, 請設定為"否". 此選項僅對 BT 下載有效 542 | bt-load-saved-metadata.name=載入已儲存的中繼資料檔案 543 | bt-load-saved-metadata.description=當使用磁力連結下載時, 在從 DHT 獲取種子中繼資料之前, 首先嘗試載入使用 --bt-save-metadata 選項儲存的檔案. 如果檔案載入成功, 則不會從 DHT 下載中繼資料. 544 | bt-max-open-files.name=最多打開檔案數 545 | bt-max-open-files.description=設定 BT/Metalink 下載全域打開的最大檔案數. 546 | bt-max-peers.name=最大連線節點數 547 | bt-max-peers.description=設定每個 BT 下載的最大連線節點數. 0 表示不限制. 548 | bt-metadata-only.name=僅下載種子檔案 549 | bt-metadata-only.description=僅下載種子檔案. 種子檔案中描述的檔案將不會下載. 此選項僅對磁力連結生效. 550 | bt-min-crypto-level.name=最低加密層級 551 | bt-min-crypto-level.description=設定加密方法的最小層級. 如果節點提供多種加密方法, aria2 將選擇滿足給定層級的最低層級. 552 | bt-prioritize-piece.name=優先下載 553 | bt-prioritize-piece.description=嘗試先下載每個檔案開頭或結尾的分段. 此選項有助於預覽檔案. 參數可以包括兩個關鍵詞: head 和 tail. 如果包含兩個關鍵詞, 需要使用逗號分隔. 每個關鍵詞可以包含一個參數, SIZE. 例如, 如果指定 head=SIZE, 每個檔案的最前 SIZE 資料將會獲得更高的優先順序. tail=SIZE 表示每個檔案的最後 SIZE 資料. SIZE 可以包含 K 或 M (1K = 1024, 1M = 1024K). 554 | bt-remove-unselected-file.name=刪除未選擇的檔案 555 | bt-remove-unselected-file.description=當 BT 工作完成後刪除未選擇的檔案. 要選擇需要下載的檔案, 請使用 --select-file 選項. 如果沒有選擇, 則所有檔案都預設為需要下載. 此選項會從磁碟上直接刪除檔案, 請謹慎使用此選項. 556 | bt-require-crypto.name=需要加密 557 | bt-require-crypto.description=如果設定為"是", aria 將不會接受以前的 BitTorrent 握手協定(\19BitTorrent 協定)並建立連線. 因此 aria2 總是模糊握手. 558 | bt-request-peer-speed-limit.name=期望下載速度 559 | bt-request-peer-speed-limit.description=如果一個 BT 下載的整體下載速度低於此選項設定的值, aria2 會臨時提高連線數以提高下載速度. 在某些情況下, 設定期望下載速度可以提高您的下載速度. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 560 | bt-save-metadata.name=儲存種子檔案 561 | bt-save-metadata.description=儲存種子檔案為 ".torrent" 檔案. 此選項僅對磁力連結生效. 檔名為十六進位編碼後的雜湊值及 ".torrent"副檔名. 儲存的目錄與下載檔案的目錄相同. 如果相同的檔案已存在, 種子檔案將不會儲存. 562 | bt-seed-unverified.name=不檢查已經下載的檔案 563 | bt-seed-unverified.description=不檢查之前下載檔案中每個分段的雜湊值. 564 | bt-stop-timeout.name=無速度時自動停止時間 565 | bt-stop-timeout.description=當 BT 工作下載速度持續為 0, 達到此選項設定的時間後停止下載. 如果設定為 0, 此功能將停用. 566 | bt-tracker.name=BT 伺服器位址 567 | bt-tracker.description=逗號分隔的 BT 伺服器位址. 這些位址不受 --bt-exclude-tracker 選項的影響, 因為這些位址在 --bt-exclude-tracker 選項排除掉其他位址之後才會加入. 568 | bt-tracker-connect-timeout.name=BT 伺服器連線超時時間 569 | bt-tracker-connect-timeout.description=設定 BT 伺服器的連線超時時間 (秒). 當連線建立後, 此選項不再生效, 請使用 --bt-tracker-timeout 選項. 570 | bt-tracker-interval.name=BT 伺服器連線間隔時間 571 | bt-tracker-interval.description=設定要求 BT 伺服器的間隔時間 (秒). 此選項將完全覆蓋伺服器返回的最小間隔時間和間隔時間, aria2 僅使用此選項的值.如果設定為 0, aria2 將根據伺服器的響應情況和下載處理程序決定時間間隔. 572 | bt-tracker-timeout.name=BT 伺服器超時時間 573 | bt-tracker-timeout.description= 574 | dht-file-path.name=DHT (IPv4) 檔案 575 | dht-file-path.description=修改 IPv4 DHT 路由表檔案路徑. 576 | dht-file-path6.name=DHT (IPv6) 檔案 577 | dht-file-path6.description=修改 IPv6 DHT 路由表檔案路徑. 578 | dht-listen-port.name=DHT 監聽埠 579 | dht-listen-port.description=設定 DHT (IPv4, IPv6) 和 UDP 伺服器使用的 UDP 埠. 多個埠可以使用逗號 "," 分隔, 例如: 6881,6885. 您還可以使用短橫線 "-" 表示範圍: 6881-6999, 或可以一起使用: 6881-6889, 6999. 580 | dht-message-timeout.name=DHT 消息超時時間 581 | dht-message-timeout.description= 582 | enable-dht.name=啟用 DHT (IPv4) 583 | enable-dht.description=啟用 IPv4 DHT 功能. 此選項同時會啟用 UDP 伺服器支援. 如果種子設定為私有, 即使此選項設定為"是", aria2 也不會啟用 DHT. 584 | enable-dht6.name=啟用 DHT (IPv6) 585 | enable-dht6.description=啟用 IPv6 DHT 功能. 如果種子設定為私有, 即使此選項設定為"是", aria2 也不會啟用 DHT. 使用 --dht-listen-port 選項設定監聽的埠. 586 | enable-peer-exchange.name=啟用節點交換 587 | enable-peer-exchange.description=啟用節點交換擴充. 如果種子設定為私有, 即使此選項設定為"是", aria2 也不會啟用此功能. 588 | follow-torrent.name=下載種子中的檔案 589 | follow-torrent.description=如果設定為"是"或"僅記憶體", 當副檔名為 .torrent 或內容類型為 application/x-bittorrent 的檔案下載完成時, aria2 將依種子檔案讀取並下載該檔案中提到的檔案. 如果設定為"僅記憶體", 該種子檔案將不會寫入到磁碟中, 而僅會儲存在記憶體中. 如果設定為"否", 則 .torrent 檔案會下載到磁碟中, 但不會依種子檔案讀取並且其中的檔案不會進行下載. 590 | listen-port.name=監聽埠 591 | listen-port.description=設定 BT 下載的 TCP 埠. 多個埠可以使用逗號 "," 分隔, 例如: 6881,6885. 您還可以使用短橫線 "-" 表示範圍: 6881-6999, 或可以一起使用: 6881-6889, 6999. 592 | max-overall-upload-limit.name=全域最大上傳速度 593 | max-overall-upload-limit.description=設定全域最大上傳速度 (位元組/秒). 0 表示不限制. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 594 | max-upload-limit.name=最大上傳速度 595 | max-upload-limit.description=設定每個工作的最大上傳速度 (位元組/秒). 0 表示不限制. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 596 | peer-id-prefix.name=節點 ID 前綴 597 | peer-id-prefix.description=指定節點 ID 的前綴. BT 中節點 ID 長度為 20 位元組. 如果超過 20 位元組, 將僅使用前 20 位元組. 如果少於 20 位元組, 將在其後不足隨機的資料保證為 20 位元組. 598 | peer-agent.name=Peer Agent 599 | peer-agent.description=指定 BT 擴充握手期間用於節點客戶端版本的字串. 600 | seed-ratio.name=最小分享率 601 | seed-ratio.description=指定分享率. 當分享率達到此選項設定的值時會完成做種. 強烈建議您將此選項設定為大於等於 1.0. 如果您想不限制分享比率, 可以設定為 0.0. 如果同時設定了 --seed-time 選項, 當任意一個條件滿足時將停止做種. 602 | seed-time.name=最小做種時間 603 | seed-time.description=以 (小數形式的) 分鐘指定做種時間. 此選項設置爲 0 時, 將在 BT 任務下載完成後不進行做種. 604 | follow-metalink.name=下載 Metalink 中的檔案 605 | follow-metalink.description=如果設定為"是"或"僅記憶體", 當副檔名為 .meta4 或 .metalink 或內容類型為 application/metalink4+xml 或 application/metalink+xml 的檔案下載完成時, aria2 將依 Metalink 檔案讀取並下載該檔案中提到的檔案. 如果設定為"僅記憶體", 該 Metalink 檔案將不會寫入到磁碟中, 而僅會儲存在記憶體中. 如果設定為"否", 則 .metalink 檔案會下載到磁碟中, 但不會依 Metalink 檔案讀取並且其中的檔案不會進行下載. 606 | metalink-base-uri.name=基礎 URI 607 | metalink-base-uri.description=指定基礎 URI 以便解析本機磁碟中儲存的 Metalink 檔案裡 metalink:url 和 metalink:metaurl 中的相對 URI 位址. 如果 URI 表示的為目錄, 最後需要以 / 結尾. 608 | metalink-language.name=語言 609 | metalink-language.description= 610 | metalink-location.name=首選伺服器位置 611 | metalink-location.description=首選伺服器所在的位置. 可以使用逗號分隔的清單, 例如: jp,us. 612 | metalink-os.name=操作系統 613 | metalink-os.description=下載檔案的操作系統. 614 | metalink-version.name=版本號 615 | metalink-version.description=下載檔案的版本號. 616 | metalink-preferred-protocol.name=首選使用協定 617 | metalink-preferred-protocol.description=指定首選使用的協定. 可以設定為 http, https, ftp 或"無". 設定為"無"時停用此選項. 618 | metalink-enable-unique-protocol.name=僅使用唯一協定 619 | metalink-enable-unique-protocol.description=如果一個 Metalink 檔案可用多種協定, 並且此選項設定為"是", aria2 將只會使用其中一種. 使用 --metalink-preferred-protocol 參數指定首選的協定. 620 | enable-rpc.name=啟用 JSON-RPC/XML-RPC 伺服器 621 | enable-rpc.description= 622 | pause-metadata.name=種子檔案下載完後暫停 623 | pause-metadata.description=當種子檔案下載完成後暫停後續的下載. 在 aria2 中有 3 種種子檔案的下載類型: (1) 下載 .torrent 檔案. (2) 通過磁力連結下載的種子檔案. (3) 下載 Metalink 檔案. 這些種子檔案下載完後會根據檔案內容繼續進行下載. 此選項會暫停這些後續的下載. 此選項僅當 --enable-rpc 選項啟用時生效. 624 | rpc-allow-origin-all.name=接受所有遠端要求 625 | rpc-allow-origin-all.description=在 RPC 響應頭增加 Access-Control-Allow-Origin 欄位, 值為 * . 626 | rpc-listen-all.name=在所有網卡上監聽 627 | rpc-listen-all.description=在所有網路適配器上監聽 JSON-RPC/XML-RPC 的要求, 如果設定為"否", 僅監聽本機網路的要求. 628 | rpc-listen-port.name=監聽埠 629 | rpc-listen-port.description= 630 | rpc-max-request-size.name=最大要求大小 631 | rpc-max-request-size.description=設定 JSON-RPC/XML-RPC 最大的要求大小. 如果 aria2 檢測到要求超過設定的位元組數, 會直接取消連線. 632 | rpc-save-upload-metadata.name=儲存上傳的種子檔案 633 | rpc-save-upload-metadata.description=在 dir 選項設定的目錄中儲存上傳的種子檔案或 Metalink 檔案. 檔名包括 SHA-1 雜湊後的中繼資料和副檔名兩部分. 對於種子檔案, 副檔名為 '.torrent'. 對於 Metalink 為 '.meta4'. 如果此選項設定為"否", 通過 aria2.addTorrent() 或 aria2.addMetalink() 方法加入的下載將無法通過 --save-session 選項儲存. 634 | rpc-secure.name=啟用 SSL/TLS 635 | rpc-secure.description=RPC 將通過 SSL/TLS 加密傳輸. RPC 客戶端需要使用 https 協定連線伺服器. 對於 WebSocket 客戶端, 使用 wss 協定. 使用 --rpc-certificate 和 --rpc-private-key 選項設定伺服器的證書和私鑰. 636 | allow-overwrite.name=允許覆蓋 637 | allow-overwrite.description=如果相應的控制檔案不存在時從頭重新下載檔案. 參見 --auto-file-renaming 選項. 638 | allow-piece-length-change.name=允許分段大小變化 639 | allow-piece-length-change.description=如果設定為"否", 當分段長度與控制檔案中的不同時, aria2 將會中止下載. 如果設定為"是", 您可以繼續, 但部分下載進度將會丟失. 640 | always-resume.name=始終斷點續傳 641 | always-resume.description=始終斷點續傳. 如果設定為"是", aria2 始終嘗試斷點續傳, 如果無法恢復, 則中止下載. 如果設定為"否", 對於不支援斷點續傳的 URI 或 aria2 遇到 N 個不支援斷點續傳的 URI (N 為 --max-resume-failure-tries 選項設定的值), aria2 會從頭下載檔案. 參見 --max-resume-failure-tries 參數. 642 | async-dns.name=非同步 DNS 643 | async-dns.description= 644 | auto-file-renaming.name=檔案自動重命名 645 | auto-file-renaming.description=重新命名已經存在的檔案. 此選項僅對 HTTP(S)/FTP 下載有效. 新的檔名後會在檔名後、副檔名 (如果有) 前追加句點和數字(1..9999). 646 | auto-save-interval.name=自動儲存間隔 647 | auto-save-interval.description=每隔設定的秒數自動儲存控制檔案(*.aria2). 如果設定為 0, 下載期間控制檔案不會自動儲存. 不論設定的值為多少, aria2 會在工作結束時儲存控制檔案. 可以設定的值為 0 到 600. 648 | conditional-get.name=條件下載 649 | conditional-get.description=僅當本機檔案比遠端檔案舊時才進行下載. 此功能僅適用於 HTTP(S) 下載. 如果在 Metalink 中檔案大小已經被指定則功能無法生效. 同時此功能還將忽略 Content-Disposition 響應頭. 如果存在控制檔案, 此選項將被忽略. 此功能通過 If-Modified-Since 要求標頭獲取較新的檔案. 當獲取到本機檔案的修改時間時, 此功能將使用使用者提供的檔名 (參見 --out 選項), 如果沒有指定 --out 選項則使用 URI 中的檔名. 為了覆蓋已經存在的檔案, 需要使用 --allow-overwrite 參數. 650 | conf-path.name=設定檔案路徑 651 | conf-path.description= 652 | console-log-level.name=控制台記錄層級 653 | console-log-level.description= 654 | content-disposition-default-utf8.name=使用 UTF-8 處理 Content-Disposition 655 | content-disposition-default-utf8.description=處理 "Content-Disposition" 頭中的字串時使用 UTF-8 字集來代替 ISO-8859-1, 例如, 檔名參數, 但不是擴充版本的檔名. 656 | daemon.name=啟用背景處理程序 657 | daemon.description= 658 | deferred-input.name=延遲載入 659 | deferred-input.description=如果設定為"是", aria2 在啟動時不會讀取 --input-file 選項設定的檔案中的所有 URI 位址, 而是會在之後需要時依需讀取. 如果輸入檔案中包含大量要下載的 URI, 此選項可以減少記憶體的使用. 如果設定為"否", aria2 會在啟動時讀取所有的 URI. 當 -save-session 使用時將會停用 --deferred-input 選項. 660 | disable-ipv6.name=停用 IPv6 661 | disable-ipv6.description= 662 | disk-cache.name=磁碟快取 663 | disk-cache.description=啟用磁碟快取. 如果設定為 0, 將停用磁碟快取. 此功能將下載的資料快取在記憶體中, 最多佔用此選項設定的位元組數. 快取儲存由 aria2 實例建立並對所有下載共享. 由於資料以較大的單位寫入並依檔案的偏移重新排序, 所以磁碟快取的一個優點是減少磁碟的 I/O. 如果調用雜湊檢查時並且資料快取在記憶體中時, 將不需要從磁碟中讀取. 大小可以包含 K 或 M (1K = 1024, 1M = 1024K). 664 | download-result.name=下載結果 665 | download-result.description=此選項將修改下載結果的格式. 如果設定為"預設", 將列印 GID, 狀態, 平均下載速度和路徑/URI. 如果涉及多個檔案, 僅列印第一個要求檔案的路徑/URI, 其餘的將被忽略. 如果設定為"完整", 將列印 GID, 狀態, 平均下載速度, 下載進度和路徑/URI. 其中, 下載進度和路徑/URI 將會每個檔案列印一行. 如果設定為"隱藏", 下載結果將會隱藏. 666 | dscp.name=DSCP 667 | dscp.description=為 QoS 設定 BT 上行 IP 包的 DSCP 值. 此參數僅設定 IP 包中 TOS 欄位的 DSCP 位, 而不是整個欄位. 如果您從 /usr/include/netinet/ip.h 得到的值, 需要除以 4 (否則值將不正確, 例如您的 CS1 類將會轉為 CS4). 如果您從 RFC, 網路供應商的文件, 維基百科或其他來源採取常用的值, 可以直接使用. 668 | rlimit-nofile.name=最多打開的檔案描述項 669 | rlimit-nofile.description=設定打開的檔案描述項的軟限制 (soft limit). 此選項僅當滿足如下條件時開放: a. 系統支援它 (posix). b. 限制沒有超過硬限制 (hard limit). c. 指定的限制比目前的軟限制高. 這相當於設定 ulimit, 除了其不能降低限制. 此選項僅當系統支援 rlimit API 時有效. 670 | enable-color.name=終端輸出使用顏色 671 | enable-color.description= 672 | enable-mmap.name=啟用 MMap 673 | enable-mmap.description=記憶體中存放映射檔案. 當檔案空間沒有預先分配至, 此選項無效. 參見 --file-allocation. 674 | event-poll.name=事件輪詢方法 675 | event-poll.description=設定事件輪詢的方法. 可選的值包括 epoll, kqueue, port, poll 和 select. 對於 epoll, kqueue, port 和 poll, 只有系統支援時才可用. 最新的 Linux 支援 epoll. 各種 *BSD 系統包括 Mac OS X 支援 kqueue. Open Solaris 支援 port. 預設值根據您使用的操作系統不同而不同. 676 | file-allocation.name=檔案分配方法 677 | file-allocation.description=指定檔案分配方法. "無" 不會預先分配檔案空間. "prealloc"會在下載開始前預先分配空間. 這將會根據檔案的大小需要一定的時間. 如果您使用的是較新的檔案系統, 例如 ext4 (帶擴充支援), btrfs, xfs 或 NTFS (僅 MinGW 組建), "falloc" 是最好的選擇. 其幾乎可以瞬間分配大(數 GiB)檔案. 不要在舊的檔案系統, 例如 ext3 和 FAT32 上使用 falloc, 因為與 prealloc 花費的時間相同, 並且其會阻塞 aria2 直到分配完成. 當您的系統不支援 posix_fallocate(3) 函數時, falloc 可能無法使用. "trunc" 使用 ftruncate(2) 系統調用或平台特定的實現將檔案截取到特定的長度. 在多檔案的 BitTorrent 下載中, 若某檔案與其相鄰的檔案共享相同的分段時, 則相鄰的檔案也會被分配. 678 | force-save.name=強制儲存 679 | force-save.description=即使工作完成或刪除時使用 --save-session 選項時也儲存該工作. 此選項在這種情況下還會儲存控制檔案. 此選項可以儲存被認為已經完成但正在做種的 BT 工作. 680 | save-not-found.name=儲存未找到的檔案 681 | save-not-found.description=當使用 --save-session 選項時, 即使當工作中的檔案不存在時也儲存該下載工作. 此選項同時會將這種情況儲存到控制檔案中. 682 | hash-check-only.name=僅雜湊檢查 683 | hash-check-only.description=如果設定為"是", 雜湊檢查完使用 --check-integrity 選項, 根據是否下載完成決定是否終止下載. 684 | human-readable.name=控制台可讀輸出 685 | human-readable.description=在控制台輸出可讀格式的大小和速度 (例如, 1.2Ki, 3.4Mi). 686 | keep-unfinished-download-result.name=保留未完成的工作 687 | keep-unfinished-download-result.description=保留所有未完成的下載結果, 即使超過了 --max-download-result 選項設定的數量. 這將有助於在會話檔案中儲存所有的未完成的下載 (參考 --save-session 選項). 需要注意的是, 未完成工作的數量沒有上限. 如果不希望這樣, 請關閉此選項. 688 | max-download-result.name=最多下載結果 689 | max-download-result.description=設定記憶體中儲存最多的下載結果數量. 下載結果包括已完成/錯誤/已刪除的下載. 下載結果儲存在一個先進先出的隊列中, 因此其可以儲存最多指定的下載結果的數量. 當隊列已滿且有新的下載結果建立時, 最老的下載結果將從隊列的最前部移除, 新的將放在最後. 此選項設定較大的值後如果經過幾千次的下載將導致較高的記憶體消耗. 設定為 0 表示不儲存下載結果. 注意, 未完成的下載將始終儲存在記憶體中, 不考慮該選項的設定. 參考 --keep-unfinished-download-result 選項. 690 | max-mmap-limit.name=MMap 最大限制 691 | max-mmap-limit.description=設定啟用 MMap (參見 --enable-mmap 選項) 最大的檔案大小. 檔案大小由一個下載工作中所有檔案大小的和決定. 例如, 如果一個下載包含 5 個檔案, 那麼檔案大小就是這些檔案的總大小. 如果檔案大小超過此選項設定的大小時, MMap 將會停用. 692 | max-resume-failure-tries.name=最大斷點續傳嘗試次數 693 | max-resume-failure-tries.description=當 --always-resume 選項設定為"否"時, 如果 aria2 檢測到有 N 個 URI 不支援斷點續傳時, 將從頭開始下載檔案. 如果 N 設定為 0, 當所有 URI 都不支援斷點續傳時才會從頭下載檔案. 參見 --always-resume 選項. 694 | min-tls-version.name=最低 TLS 版本 695 | min-tls-version.description=指定啟用的最低 SSL/TLS 版本. 696 | log-level.name=記錄層級 697 | log-level.description= 698 | optimize-concurrent-downloads.name=最佳化同時下載 699 | optimize-concurrent-downloads.description=根據可用頻寬最佳化同時下載的數量. aria2 使用之前統計的下載速度通過規則 N = A + B Log10 (速度單位為 Mbps) 得到同時下載的數量. 其中係數 A 和 B 可以在參數中以冒號分隔自訂. 預設值 (A=5, B=25) 可以在 1Mbps 網路上使用通常 5 個同時下載, 在 100Mbps 網路上為 50 個. 同時下載的數量保持在 --max-concurrent-downloads 參數定義的最大之下. 700 | piece-length.name=檔案分段大小 701 | piece-length.description=設定 HTTP/FTP 下載的分配大小. aria2 根據這個邊界分割檔案. 所有的分割都是這個長度的倍數. 此選項不適用於 BitTorrent 下載. 如果 Metalink 檔案中包含分段雜湊的結果此選項也不適用. 702 | show-console-readout.name=顯示控制台輸出 703 | show-console-readout.description= 704 | summary-interval.name=下載摘要輸出間隔 705 | summary-interval.description=設定下載進度摘要的輸出間隔(秒). 設定為 0 禁止輸出. 706 | max-overall-download-limit.name=全域最大下載速度 707 | max-overall-download-limit.description=設定全域最大下載速度 (位元組/秒). 0 表示不限制. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 708 | max-download-limit.name=最大下載速度 709 | max-download-limit.description=設定每個工作的最大下載速度 (位元組/秒). 0 表示不限制. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 710 | no-conf.name=停用設定檔案 711 | no-conf.description= 712 | no-file-allocation-limit.name=檔案分配限制 713 | no-file-allocation-limit.description=不對比此參數設定大小小的分配檔案. 您可以增加數值的單位 K 或 M (1K = 1024, 1M = 1024K). 714 | parameterized-uri.name=啟用參數化 URI 支援 715 | parameterized-uri.description=啟用參數化 URI 支援. 您可以指定部分的集合: http://{sv1,sv2,sv3}/foo.iso. 同時您也可以使用步進計數器指定數字化的序列: http://host/image[000-100:2].img. 步進計數器可以省略. 如果所有 URI 位址不指向同樣的檔案, 例如上述第二個示例, 需要使用 -Z 選項. 716 | quiet.name=停用控制台輸出 717 | quiet.description= 718 | realtime-chunk-checksum.name=即時資料區塊驗證 719 | realtime-chunk-checksum.description=如果提供了資料區塊的總和檢查碼, 將在下載過程中通過總和檢查碼驗證資料區塊. 720 | remove-control-file.name=刪除控制檔案 721 | remove-control-file.description=在下載前刪除控制檔案. 使用 --allow-overwrite=true 選項時, 總是從頭開始下載檔案. 此選項將有助於使用不支援斷點續傳代理伺服器的使用者. 722 | save-session.name=狀態儲存檔案 723 | save-session.description=當離開時儲存錯誤及未完成的工作到指定的檔案中. 您可以在重啟 aria2 時使用 --input-file 選項重新載入. 如果您希望輸出的內容使用 GZip 壓縮, 您可以在檔名後增加 .gz 副檔名. 請注意, 通過 aria2.addTorrent() 和 aria2.addMetalink() RPC 方法加入的下載, 其中繼資料沒有儲存到檔案的將不會儲存. 通過 aria2.remove() 和 aria2.forceRemove() 刪除的下載將不會儲存. 724 | save-session-interval.name=儲存狀態間隔 725 | save-session-interval.description=每隔此選項設定的時間(秒)後會儲存錯誤或未完成的工作到 --save-session 選項指定的檔案中. 如果設定為 0, 僅當 aria2 離開時才會儲存. 726 | socket-recv-buffer-size.name=Socket 接收緩衝區大小 727 | socket-recv-buffer-size.description=設定 Socket 接收緩衝區最大的位元組數. 指定為 0 時將停用此選項. 當使用 SO_RCVBUF 選項調用 setsockopt() 時此選項的值將設定到 Socket 的檔案描述項中. 728 | stop.name=自動關閉時間 729 | stop.description=在此選項設定的時間(秒)後關閉應用程式. 如果設定為 0, 此功能將停用. 730 | truncate-console-readout.name=縮短控制台輸出內容 731 | truncate-console-readout.description=縮短控制台輸出的內容在一行中. 732 | -------------------------------------------------------------------------------- /AriaNg/robots.txt: -------------------------------------------------------------------------------- 1 | # AriaNg 2 | 3 | User-agent: * 4 | Disallow: / 5 | -------------------------------------------------------------------------------- /AriaNg/tileicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/tileicon.png -------------------------------------------------------------------------------- /AriaNg/touchicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/AriaNg/touchicon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aria2 Integration Web extension 2 | 3 | Web extension for Aria2 4 | 5 | ## Known Issues 6 | 7 | - [pop up menu and auto downloading not working in chrome](https://github.com/zluo01/aria2-extension/issues/910) 8 | 9 | ## Installation 10 | 11 | - [Firefox](https://addons.mozilla.org/en-US/firefox/addon/aria2-integration-extension/?utm_content=addons-manager-reviews-link&utm_medium=firefox-browser&utm_source=firefox-browser) 12 | - [Chrome](https://chrome.google.com/webstore/detail/aria2-integration-extensi/chehmbmmchaagpilhabnocngnmjllgfi?hl=en&authuser=0) 13 | 14 | ## Features 15 | 16 | - Replace browser default download manager with Aria2 17 | - Add, start, pause and stop jobs in extension tab 18 | - Trigger download with right click context menu 19 | - Allow more operations with integrated AriaNg 20 | - Custom configuration allows instance other than default aria2 domains & ports 21 | - Third party scripts support, allow user to add scripts using integrated editor. (Removed after **0.5.0**, see below) 22 | 23 | ## Scripts 24 | 25 | > This feature is **removed** after 0.5.0 due to manifest 3 does not allow arbitrary code execution per [guideline](https://developer.chrome.com/docs/extensions/develop/migrate/improve-security). 26 | 27 | The new script extension allows user to download certain resources on websites without going into page source manually. 28 | 29 | ### Example 30 | 31 | ``` 32 | (async function (url) { 33 | 'use strict'; 34 | 35 | const res = await fetch(url); 36 | if (res.ok) { 37 | const data = await res.text(); 38 | const parser = new DOMParser(); 39 | const doc = parser.parseFromString(data, "text/html"); 40 | const matches = doc.querySelector(""); 41 | return matches.src; 42 | } 43 | throw res.statusText; 44 | })(); 45 | ``` 46 | 47 |

48 | With the input url as a parameter, user should be able to implement scripts utilize xPath or query selector to find out media sources or batch images on define domains 49 | and download all automatically in batch. 50 |

51 | 52 | ## Todo 53 | 54 | - Allow multiple Aria2 instances, currently only support one instance. 55 | - Background job monitor. 56 | 57 | ## Reference 58 | 59 | - https://github.com/aria2/aria2 60 | - https://github.com/mayswind/AriaNg 61 | - https://github.com/sonnyp/aria2.js/ 62 | - https://github.com/RossWang/Aria2-Integration 63 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import eslintConfigPrettier from 'eslint-config-prettier'; 3 | import pluginPromise from 'eslint-plugin-promise'; 4 | import pluginReact from 'eslint-plugin-react'; 5 | import pluginReactHooks from 'eslint-plugin-react-hooks'; 6 | import globals from 'globals'; 7 | import ts from 'typescript-eslint'; 8 | 9 | export default [ 10 | { files: ['**/*.{js,ts,tsx}'] }, 11 | { languageOptions: { globals: globals.browser } }, 12 | js.configs.recommended, 13 | ...ts.configs.recommended, 14 | pluginReact.configs.flat.recommended, 15 | pluginPromise.configs['flat/recommended'], 16 | eslintConfigPrettier, 17 | { 18 | plugins: { 19 | 'react-hooks': pluginReactHooks, 20 | }, 21 | rules: { 22 | 'react/react-in-jsx-scope': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | '@typescript-eslint/no-array-constructor': 'error', 25 | '@typescript-eslint/no-empty-function': ['error'], 26 | '@typescript-eslint/no-unused-vars': [ 27 | 2, 28 | { 29 | argsIgnorePattern: '^_', 30 | }, 31 | ], 32 | 'no-console': [ 33 | 2, 34 | { 35 | allow: ['warn', 'error'], 36 | }, 37 | ], 38 | ...pluginReactHooks.configs.recommended.rules, 39 | }, 40 | }, 41 | { 42 | ignores: [ 43 | 'build/*', 44 | 'artifacts/*', 45 | 'AriaNg/*', 46 | '*.config.*[t|j]s', 47 | 'plugins/*', 48 | ], 49 | }, 50 | ]; 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aria2-extension", 3 | "displayName": "Aria2 Integration Extension", 4 | "version": "0.5.2", 5 | "private": true, 6 | "dependencies": { 7 | "@emotion/react": "11.14.0", 8 | "@emotion/styled": "11.14.0", 9 | "@mui/icons-material": "6.4.11", 10 | "@mui/material": "6.4.11", 11 | "aria2": "4.1.2", 12 | "react": "19.1.0", 13 | "react-dom": "19.1.0", 14 | "react-router-dom": "7.6.1", 15 | "swr": "2.3.3" 16 | }, 17 | "devDependencies": { 18 | "@eslint/js": "9.28.0", 19 | "@trivago/prettier-plugin-sort-imports": "5.2.2", 20 | "@types/node": "22.15.29", 21 | "@types/react": "19.1.6", 22 | "@types/react-dom": "19.1.5", 23 | "@types/webextension-polyfill": "0.12.3", 24 | "@vitejs/plugin-react-swc": "3.10.0", 25 | "eslint": "9.28.0", 26 | "eslint-config-prettier": "10.1.5", 27 | "eslint-plugin-import": "2.31.0", 28 | "eslint-plugin-promise": "7.2.1", 29 | "eslint-plugin-react": "7.37.5", 30 | "eslint-plugin-react-hooks": "5.2.0", 31 | "globals": "16.2.0", 32 | "husky": "9.1.7", 33 | "lint-staged": "16.1.0", 34 | "prettier": "3.5.3", 35 | "typescript": "5.8.3", 36 | "typescript-eslint": "8.33.0", 37 | "vite": "6.3.5", 38 | "vite-plugin-eslint": "1.8.1", 39 | "vite-tsconfig-paths": "5.1.4", 40 | "web-ext": "8.7.1", 41 | "webextension-polyfill": "0.12.0" 42 | }, 43 | "scripts": { 44 | "dev": "npm run build && web-ext run --source-dir ./build/", 45 | "build": "vite build", 46 | "package": "web-ext build --config=webext.config.cjs", 47 | "complete": "npm run build && npm run package", 48 | "type-check": "tsc --pretty --noEmit", 49 | "format": "prettier --write .", 50 | "lint": "eslint .", 51 | "prepare": "husky" 52 | }, 53 | "browserslist": { 54 | "production": [ 55 | ">0.2%", 56 | "not dead", 57 | "not op_mini all" 58 | ], 59 | "development": [ 60 | "last 1 chrome version", 61 | "last 1 firefox version", 62 | "last 1 safari version" 63 | ] 64 | }, 65 | "lint-staged": { 66 | "*.{ts,tsx,js}": [ 67 | "eslint --cache --fix", 68 | "npm run lint", 69 | "npm run format" 70 | ], 71 | "*.{js,css,md}": "prettier --write" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /plugins/bundle-aria.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { resolve } from 'path'; 3 | import { PluginOption } from 'vite'; 4 | 5 | const ariaDir = resolve(__dirname, '..', 'AriaNg'); 6 | const outDir = resolve(__dirname, '..', 'build'); 7 | 8 | export default function bundleAria(): PluginOption { 9 | return { 10 | name: 'bundle-aria', 11 | writeBundle: { 12 | order: 'post', 13 | async handler() { 14 | if (!fs.existsSync(outDir)) { 15 | throw Error('Build directory not exists.'); 16 | } 17 | 18 | const outputDir = resolve(__dirname, '..', 'build', 'manager'); 19 | await fs.promises.cp(ariaDir, outputDir, { 20 | recursive: true, 21 | }); 22 | }, 23 | }, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /plugins/clean-build.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { resolve } from 'path'; 3 | import { PluginOption } from 'vite'; 4 | 5 | const outDir = resolve(__dirname, '..', 'build'); 6 | 7 | export default function cleanBuildScript(): PluginOption { 8 | return { 9 | name: 'clean-build', 10 | buildStart: { 11 | async handler() { 12 | if (fs.existsSync(outDir)) { 13 | await fs.promises.rm(outDir, { 14 | recursive: true, 15 | force: true, 16 | }); 17 | console.info('Cleanup build folder.'); 18 | return; 19 | } 20 | console.info('No build folder.'); 21 | }, 22 | }, 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /plugins/make-manifest.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { PluginOption } from 'vite'; 4 | 5 | import manifest from '../src/manifest'; 6 | 7 | const { resolve } = path; 8 | 9 | const outDir = resolve(__dirname, '..', 'build'); 10 | 11 | export default function makeManifest(): PluginOption { 12 | return { 13 | name: 'make-manifest', 14 | writeBundle: { 15 | order: 'post', 16 | async handler() { 17 | if (!fs.existsSync(outDir)) { 18 | throw Error('Build directory not exists.'); 19 | } 20 | 21 | let manifestContent = manifest; 22 | 23 | if (process.env.TARGET === 'CHROME') { 24 | manifestContent = { 25 | manifest_version: 2, 26 | name: manifestContent.name, 27 | version: manifestContent.version, 28 | icons: manifestContent.icons, 29 | permissions: [ 30 | ...(manifestContent.permissions as string[]), 31 | '', 32 | ], 33 | content_security_policy: "script-src 'self'; object-src 'self';", 34 | background: { 35 | page: 'background-page.html', 36 | }, 37 | commands: manifestContent.commands, 38 | options_ui: manifestContent.options_ui, 39 | browser_action: manifestContent.action, 40 | }; 41 | } 42 | 43 | const manifestPath = resolve(outDir, 'manifest.json'); 44 | await fs.promises.writeFile( 45 | manifestPath, 46 | JSON.stringify(manifestContent, null, 2), 47 | ); 48 | 49 | console.info(`Manifest file copy complete: ${manifestPath}`); 50 | }, 51 | }, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /plugins/move-entry.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { resolve } from 'path'; 3 | import { PluginOption } from 'vite'; 4 | 5 | const outDir = resolve(__dirname, '..', 'build'); 6 | 7 | export default function moveEntryScript(): PluginOption { 8 | return { 9 | name: 'move-entry', 10 | writeBundle: { 11 | order: 'post', 12 | async handler() { 13 | if (!fs.existsSync(outDir)) { 14 | throw Error('Build directory not exists.'); 15 | } 16 | const root = resolve(outDir, 'src'); 17 | const paths = await fs.promises.readdir(root); 18 | 19 | for (const path of paths) { 20 | const srcPath = resolve(root, path, 'index.html'); 21 | const fileName = path === 'main' ? 'index.html' : `${path}.html`; 22 | const dstPath = resolve(outDir, fileName); 23 | await fs.promises.rename(srcPath, dstPath); 24 | } 25 | 26 | await fs.promises.rm(root, { 27 | recursive: true, 28 | force: true, 29 | }); 30 | }, 31 | }, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /public/background-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/public/favicon.ico -------------------------------------------------------------------------------- /public/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/public/logo128.png -------------------------------------------------------------------------------- /public/logo48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zluo01/aria2-extension/dccb65e7ae19337c8b7739eb19aae4ca7f046029/public/logo48.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base", ":disableDependencyDashboard"], 4 | "schedule": ["every weekend"], 5 | "packageRules": [ 6 | { 7 | "updateTypes": ["minor", "patch", "pin", "digest"], 8 | "automerge": true 9 | }, 10 | { 11 | "matchDepTypes": ["devDependencies"], 12 | "matchPackagePatterns": ["lint", "prettier", "husky", "@trivago/"], 13 | "automerge": true 14 | }, 15 | { 16 | "groupName": "definitelyTyped", 17 | "matchPackagePrefixes": ["@types/"] 18 | }, 19 | { 20 | "groupName": "eslint", 21 | "matchPackagePrefixes": ["eslint", "@typescript-eslint/"] 22 | }, 23 | { 24 | "groupName": "styling", 25 | "matchPackagePrefixes": ["@emotion/", "@mui/"] 26 | }, 27 | { 28 | "groupName": "build-tools", 29 | "matchPackagePrefixes": ["vite", "@vitejs/"] 30 | } 31 | ], 32 | "postUpdateOptions": ["npmDedupe"] 33 | } 34 | -------------------------------------------------------------------------------- /src/aria2/index.ts: -------------------------------------------------------------------------------- 1 | import { getConfiguration, notify } from '@/browser'; 2 | import { DEFAULT_CONFIG, IConfig, IDownload, IJob } from '@/types'; 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | import Aria2 from 'aria2'; 6 | 7 | type Aria2ClientType = string; 8 | 9 | const Aria2WsClient: Aria2ClientType = 'ws'; 10 | const Aria2HttpClient: Aria2ClientType = 'http'; 11 | 12 | let aria2: any; 13 | let prevConfig: IConfig = DEFAULT_CONFIG; 14 | 15 | export async function ConstructAria2Instance(): Promise { 16 | const config: IConfig = await getConfiguration(); 17 | if (!aria2 || JSON.stringify(prevConfig) === JSON.stringify(config)) { 18 | const options = { 19 | path: '/jsonrpc', 20 | host: config.host, 21 | port: config.port, 22 | secure: config.protocol === 'https' || config.protocol === 'wss', 23 | secret: config.token, 24 | }; 25 | prevConfig = config; 26 | aria2 = new Aria2(options); 27 | } 28 | 29 | if (config.protocol === 'ws' || config.protocol === 'wss') { 30 | return Aria2WsClient; 31 | } 32 | return Aria2HttpClient; 33 | } 34 | 35 | export async function GetJobs(): Promise { 36 | const multiCallItems = [['tellActive'], ['tellWaiting', 0, 25]]; 37 | const data = await multiCall(multiCallItems); 38 | return flatten(data); 39 | } 40 | 41 | export async function StartJobs(...gid: string[]): Promise { 42 | if (!gid.length) { 43 | return; 44 | } 45 | try { 46 | const multiCallItems = gid.map(o => ['unpause', o]); 47 | await multiCall(multiCallItems); 48 | } catch (e) { 49 | console.error('Start Jobs', e); 50 | } 51 | } 52 | 53 | export async function PauseJobs(...gid: string[]): Promise { 54 | if (!gid.length) { 55 | return; 56 | } 57 | try { 58 | const multiCallItems = gid.map(o => ['pause', o]); 59 | await multiCall(multiCallItems); 60 | } catch (e) { 61 | console.error('Pause Jobs', e); 62 | } 63 | } 64 | 65 | export async function RemoveJobs(...gid: string[]): Promise { 66 | if (!gid.length) { 67 | return; 68 | } 69 | try { 70 | const multiCallItems = gid.map(o => ['remove', o]); 71 | await multiCall(multiCallItems); 72 | } catch (e) { 73 | console.error('Remove Jobs', e); 74 | } 75 | } 76 | 77 | export async function GetNumJobs(): Promise { 78 | const data = await singleCall(() => aria2.call('tellActive')); 79 | return data.length; 80 | } 81 | 82 | export async function AddUris(...uris: string[]): Promise { 83 | if (!uris.length) { 84 | return; 85 | } 86 | try { 87 | const multiCallItems = uris.map(o => ['addUri', [o]]); 88 | await multiCall(multiCallItems); 89 | await notify(`Start downloading ${uris.length} files using Aria2`); 90 | } catch (e) { 91 | if (e instanceof Error) { 92 | await notify(e.message); 93 | } 94 | } 95 | } 96 | 97 | export async function AddUri( 98 | link: string, 99 | fileName?: string, 100 | options?: IDownload, 101 | ): Promise { 102 | try { 103 | await singleCall(() => aria2.call('addUri', [link], options || {})); 104 | await notify(`Start downloading ${fileName || ''} using Aria2`); 105 | } catch (e) { 106 | if (e instanceof Error) { 107 | await notify(e.message); 108 | } 109 | } 110 | } 111 | 112 | function flatten(input: any[]): any[] { 113 | const stack = [...input]; 114 | const res = []; 115 | while (stack.length) { 116 | // pop value from stack 117 | const next = stack.pop(); 118 | if (Array.isArray(next)) { 119 | // push back array items, won't modify the original input 120 | stack.push(...next); 121 | } else { 122 | res.push(next); 123 | } 124 | } 125 | // reverse to restore input order 126 | return res.reverse(); 127 | } 128 | 129 | async function singleCall(func: () => Promise): Promise { 130 | const instanceType: Aria2ClientType = await ConstructAria2Instance(); 131 | const useWebSocket = instanceType === Aria2WsClient; 132 | if (useWebSocket) { 133 | await aria2.open(); 134 | } 135 | const data = await func(); 136 | if (useWebSocket) { 137 | await aria2.close(); 138 | } 139 | return data; 140 | } 141 | 142 | async function multiCall( 143 | callItems: (string | number | string[])[][], 144 | ): Promise { 145 | const instanceType: Aria2ClientType = await ConstructAria2Instance(); 146 | const useWebSocket = instanceType === Aria2WsClient; 147 | if (useWebSocket) { 148 | await aria2.open(); 149 | } 150 | const data = await aria2.multicall(callItems); 151 | if (useWebSocket) { 152 | await aria2.close(); 153 | } 154 | return data; 155 | } 156 | -------------------------------------------------------------------------------- /src/background/index.ts: -------------------------------------------------------------------------------- 1 | import { AddUri, GetNumJobs } from '@/aria2'; 2 | import { 3 | createDownloadPanel, 4 | notify, 5 | openDetail, 6 | removeBlankTab, 7 | updateBadge, 8 | } from '@/browser'; 9 | import { IFileDetail } from '@/types'; 10 | import { 11 | correctFileName, 12 | getFileName, 13 | getRequestHeaders, 14 | parseBytes, 15 | } from '@/utils'; 16 | import browser, { WebRequest } from 'webextension-polyfill'; 17 | 18 | import ResourceType = WebRequest.ResourceType; 19 | import OnSendHeadersDetailsType = WebRequest.OnSendHeadersDetailsType; 20 | import OnHeadersReceivedDetailsType = WebRequest.OnHeadersReceivedDetailsType; 21 | import OnErrorOccurredDetailsType = WebRequest.OnErrorOccurredDetailsType; 22 | import BlockingResponseOrPromise = WebRequest.BlockingResponseOrPromise; 23 | 24 | const request: OnSendHeadersDetailsType[] = []; 25 | const processQueue: IFileDetail[] = []; 26 | const CONTEXT_ID = 'download-with-aria'; 27 | 28 | browser.contextMenus.create({ 29 | id: CONTEXT_ID, 30 | title: 'Download with Aria2', 31 | contexts: ['link', 'video', 'audio'], 32 | }); 33 | 34 | browser.contextMenus.onClicked.addListener(async (info, _tab) => { 35 | if (info.menuItemId === CONTEXT_ID) { 36 | try { 37 | const uri = escapeHTML(info.linkUrl as string); 38 | await AddUri(uri); 39 | } catch (e) { 40 | if (e instanceof Error) { 41 | await notify(`fail to download url, msg: ${e.message}`); 42 | } 43 | } 44 | } 45 | }); 46 | 47 | // https://gist.github.com/Rob--W/ec23b9d6db9e56b7e4563f1544e0d546 48 | function escapeHTML(str: string) { 49 | // Note: string cast using String; may throw if `str` is non-serializable, e.g. a Symbol. 50 | // Most often this is not the case though. 51 | return str 52 | .replace(/&/g, '&') 53 | .replace(/"/g, '"') 54 | .replace(/'/g, ''') 55 | .replace(//g, '>'); 57 | } 58 | 59 | browser.commands.onCommand.addListener((command: string) => { 60 | if (command === 'open_detail') { 61 | openDetail(false); 62 | } 63 | }); 64 | 65 | async function prepareDownload(d: OnHeadersReceivedDetailsType) { 66 | const detail: IFileDetail = { url: d.url }; 67 | // get request item 68 | const id = request.findIndex(x => x.requestId === d.requestId); 69 | const reqFound = { ...request[id] }; 70 | if (id >= 0) { 71 | // create header 72 | detail.header = getRequestHeaders(reqFound); 73 | // delete request item 74 | request.splice(id, 1); 75 | } 76 | 77 | // process file name 78 | let fileName = decodeURIComponent(getFileName(d)); 79 | 80 | // issue #8 81 | fileName = decodeURI(encodeURIComponent(fileName)); 82 | 83 | // file name cannot have "" 84 | fileName = fileName.replace('UTF-8', ''); 85 | fileName = fileName.replaceAll(';', ''); 86 | fileName = fileName.replaceAll('"', ''); 87 | fileName = fileName.replaceAll("'", ''); 88 | 89 | // correct File Name 90 | detail.fileName = await correctFileName(fileName); 91 | // get file size 92 | if (d.responseHeaders) { 93 | const fid = d.responseHeaders.findIndex( 94 | x => x.name.toLowerCase() === 'content-length', 95 | ); 96 | detail.fileSize = 97 | fid >= 0 ? parseBytes(d.responseHeaders[fid].value as string) : ''; 98 | } 99 | 100 | // create download panel 101 | processQueue.push(detail); 102 | await removeBlankTab(); 103 | await createDownloadPanel(); 104 | } 105 | 106 | function observeResponse( 107 | d: OnHeadersReceivedDetailsType, 108 | ): BlockingResponseOrPromise | undefined { 109 | // bug0001: goo.gl 110 | if (d.statusCode === 200) { 111 | if ( 112 | d.responseHeaders?.find( 113 | x => x.name.toLowerCase() === 'content-disposition', 114 | ) 115 | ) { 116 | const contentDisposition = d.responseHeaders 117 | ?.find(x => x.name.toLowerCase() === 'content-disposition') 118 | ?.value?.toLowerCase(); 119 | if (contentDisposition?.slice(0, 10) === 'attachment') { 120 | prepareDownload(d).catch(err => console.error(err)); 121 | return { cancel: true }; 122 | } 123 | } 124 | if (d.responseHeaders?.find(x => x.name.toLowerCase() === 'content-type')) { 125 | const contentType = d.responseHeaders 126 | ?.find(x => x.name.toLowerCase() === 'content-type') 127 | ?.value?.toLowerCase(); 128 | if ( 129 | contentType?.slice(0, 11) === 'application' && 130 | contentType?.slice(12, 15) !== 'pdf' && 131 | contentType?.slice(12, 17) !== 'xhtml' && 132 | contentType?.slice(12, 23) !== 'x-xpinstall' && 133 | contentType?.slice(12, 29) !== 'x-shockwave-flash' && 134 | contentType?.slice(12, 15) !== 'rss' && 135 | contentType?.slice(12, 16) !== 'json' 136 | ) { 137 | prepareDownload(d).catch(err => console.error(err)); 138 | return { cancel: true }; 139 | } 140 | } 141 | } 142 | 143 | // get request item and delete 144 | const id = request.findIndex(x => x.requestId === d.requestId); 145 | if (id >= 0) { 146 | request.splice(id, 1); 147 | } 148 | } 149 | 150 | const types: ResourceType[] = ['main_frame', 'sub_frame']; 151 | 152 | function observeRequest(d: OnSendHeadersDetailsType) { 153 | request.push(d); 154 | } 155 | 156 | browser.webRequest.onSendHeaders.addListener( 157 | observeRequest, 158 | { 159 | urls: [''], 160 | types, 161 | }, 162 | ['requestHeaders'], 163 | ); 164 | 165 | browser.webRequest.onHeadersReceived.addListener( 166 | observeResponse, 167 | { 168 | urls: [''], 169 | types, 170 | }, 171 | ['blocking', 'responseHeaders'], 172 | ); 173 | 174 | function requestError(d: OnErrorOccurredDetailsType): void { 175 | const id = request.findIndex(x => x.requestId === d.requestId); 176 | if (id >= 0) { 177 | request.splice(id, 1); 178 | } 179 | } 180 | 181 | browser.webRequest.onErrorOccurred.addListener(requestError, { 182 | urls: [''], 183 | types, 184 | }); 185 | 186 | browser.runtime.onMessage.addListener((data: any, _sender) => { 187 | if (data.type === 'all') { 188 | return Promise.resolve(processQueue.pop()); 189 | } 190 | }); 191 | 192 | function updateActiveJobNumber() { 193 | GetNumJobs() 194 | .then(num => updateBadge(num)) 195 | .catch(err => console.error(err)); 196 | } 197 | 198 | setInterval(updateActiveJobNumber, 1000); 199 | -------------------------------------------------------------------------------- /src/browser/index.ts: -------------------------------------------------------------------------------- 1 | import { AddUri } from '@/aria2'; 2 | import { DEFAULT_CONFIG, IConfig, IDownload, IFileDetail } from '@/types'; 3 | import browser, { Action, Windows } from 'webextension-polyfill'; 4 | 5 | export async function getConfiguration(): Promise { 6 | const config = await browser.storage.local.get('config'); 7 | return (config.config as IConfig) || DEFAULT_CONFIG; 8 | } 9 | 10 | export async function setConfiguration(config: IConfig): Promise { 11 | return browser.storage.local.set({ config }); 12 | } 13 | 14 | export function openDetail(fromExtension: boolean): void { 15 | getConfiguration() 16 | .then( 17 | config => 18 | `manager/index.html#!/settings/rpc/set/${config.protocol}/${ 19 | config.host 20 | }/${config.port}/jsonrpc/${btoa(config.token)}`, 21 | ) 22 | .then(url => browser.tabs.create({ url })) 23 | .then(() => fromExtension && window.close()) 24 | .catch(err => console.error('Open Detail Page', err)); 25 | } 26 | 27 | export async function openSetting(): Promise { 28 | await browser.runtime.openOptionsPage(); 29 | window.close(); 30 | } 31 | 32 | export async function removeBlankTab(): Promise { 33 | try { 34 | const tabsInfo = await browser.tabs.query({ 35 | active: true, 36 | lastFocusedWindow: true, 37 | windowType: 'normal', 38 | }); 39 | if ( 40 | tabsInfo?.length > 0 && 41 | (tabsInfo[0].url === 'about:blank' || tabsInfo[0].title === 'New Tab') 42 | ) { 43 | await browser.tabs.remove(tabsInfo[0].id as number); 44 | } 45 | } catch (err) { 46 | console.error('Remove Blank Tab', err); 47 | } 48 | } 49 | 50 | export async function notify(msg: string): Promise { 51 | return browser.notifications.create({ 52 | type: 'basic', 53 | iconUrl: browser.runtime.getURL('logo48.png'), 54 | title: 'Aria2 Extension', 55 | message: msg, 56 | }); 57 | } 58 | 59 | // https://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen 60 | export async function createDownloadPanel(): Promise { 61 | const w = 560; 62 | const h = 365; 63 | // Fixes dual-screen position Most browsers Firefox 64 | const dualScreenLeft = 65 | window.screenLeft !== undefined ? window.screenLeft : window.screenX; 66 | const dualScreenTop = 67 | window.screenTop !== undefined ? window.screenTop : window.screenY; 68 | 69 | const width = window.innerWidth 70 | ? window.innerWidth 71 | : document.documentElement.clientWidth 72 | ? document.documentElement.clientWidth 73 | : screen.width; 74 | const height = window.innerHeight 75 | ? window.innerHeight 76 | : document.documentElement.clientHeight 77 | ? document.documentElement.clientHeight 78 | : screen.height; 79 | 80 | const systemZoom = width / window.screen.availWidth; 81 | const left = (width - w) / 2 / systemZoom + dualScreenLeft; 82 | const top = (height - h) / 2 / systemZoom + dualScreenTop; 83 | 84 | const url = browser.runtime.getURL('index.html'); 85 | 86 | const windowInfo = await getCurrentWindow(); 87 | return browser.windows.create({ 88 | top: Math.round(top), 89 | left: Math.round(left), 90 | url: url.concat('#/download'), 91 | type: 'popup', 92 | width: w, 93 | height: h, 94 | incognito: windowInfo.incognito, 95 | focused: true, 96 | }); 97 | } 98 | 99 | export async function getJobDetail(): Promise { 100 | return browser.runtime.sendMessage({ 101 | type: 'all', 102 | }); 103 | } 104 | 105 | export async function download( 106 | url: string, 107 | fileName: string, 108 | filePath: string, 109 | header: string[], 110 | ): Promise { 111 | try { 112 | const options: IDownload = { 113 | out: fileName, 114 | }; 115 | if (filePath) { 116 | options.dir = filePath.replace(/\\/g, '\\\\'); 117 | } 118 | if (header) { 119 | options.header = header as string[]; 120 | } 121 | await AddUri(url, fileName, options); 122 | const windowInfo = await getCurrentWindow(); 123 | if (windowInfo.id) { 124 | await browser.windows.remove(windowInfo.id); 125 | } 126 | } catch (err) { 127 | console.error('Download', err); 128 | } 129 | } 130 | 131 | export async function saveFile( 132 | url: string, 133 | fileName: string, 134 | as: boolean, 135 | ): Promise { 136 | try { 137 | const window = await getCurrentWindow(); 138 | const downloadOptions = 139 | fileName !== '' 140 | ? { 141 | // conflictAction: "prompt", //not work 142 | filename: fileName, 143 | incognito: window.incognito, // not work under 57 144 | saveAs: as, 145 | url, 146 | } 147 | : { 148 | // conflictAction: "prompt", //not work 149 | incognito: window.incognito, // not work under 57 150 | saveAs: as, 151 | url, 152 | }; 153 | await browser.downloads.download(downloadOptions); 154 | if (window.id && window.id !== 0) { 155 | await browser.windows.remove(window.id); 156 | } 157 | } catch (err) { 158 | if (err instanceof Error) { 159 | await notify(err.message); 160 | } 161 | } 162 | } 163 | 164 | async function getCurrentWindow(): Promise { 165 | return browser.windows.getCurrent(); 166 | } 167 | 168 | export async function updateBadge(num: number): Promise { 169 | const value = num > 0 ? num.toString() : null; 170 | const color = num > 0 ? '#303030' : ([217, 0, 0, 255] as Action.ColorArray); 171 | await browser.action.setBadgeText({ text: value }); 172 | await browser.action.setBadgeBackgroundColor({ color }); 173 | } 174 | -------------------------------------------------------------------------------- /src/components/content/index.tsx: -------------------------------------------------------------------------------- 1 | import { PauseJobs, StartJobs } from '@/aria2'; 2 | import { ACTIVE_JOB, IJob, PAUSED_JOB } from '@/types'; 3 | import { parseBytes } from '@/utils'; 4 | import PauseIcon from '@mui/icons-material/Pause'; 5 | import PlayArrowIcon from '@mui/icons-material/PlayArrow'; 6 | import Checkbox from '@mui/material/Checkbox'; 7 | import IconButton from '@mui/material/IconButton'; 8 | import List from '@mui/material/List'; 9 | import ListItem from '@mui/material/ListItem'; 10 | import ListItemIcon from '@mui/material/ListItemIcon'; 11 | import ListItemText from '@mui/material/ListItemText'; 12 | import Typography from '@mui/material/Typography'; 13 | import { styled } from '@mui/material/styles'; 14 | import React from 'react'; 15 | 16 | import Progress from '../progress'; 17 | 18 | const JobList = styled(List)(({ theme }) => ({ 19 | backgroundColor: theme.palette.background.paper, 20 | })); 21 | 22 | const Job = styled(ListItem)(() => ({ 23 | height: 60, 24 | })); 25 | 26 | const JobTitleSection = styled(ListItem)(() => ({ 27 | width: '100%', 28 | whiteSpace: 'nowrap', 29 | overflow: 'hidden', 30 | textOverflow: 'ellipsis', 31 | padding: 0, 32 | })); 33 | 34 | const JobSubInfoSection = styled(ListItem)(() => ({ 35 | width: '100%', 36 | display: 'flex', 37 | flexFlow: 'row wrap', 38 | justifyContent: 'space-between', 39 | padding: 0, 40 | })); 41 | 42 | interface IDownloadList { 43 | jobs: IJob[]; 44 | checked: string[]; 45 | toggle: (value: string) => () => void; 46 | } 47 | 48 | function DownloadList({ jobs, checked, toggle }: IDownloadList) { 49 | function getFileName(job: IJob): string { 50 | if (job.bittorrent && job.bittorrent.info) { 51 | return job.bittorrent.info.name; 52 | } 53 | const path = job.files[0].path; 54 | return path.split('/').slice(-1)[0]; 55 | } 56 | 57 | function getProgress(job: IJob): number { 58 | const progress = 59 | parseFloat(job.completedLength) / parseFloat(job.totalLength); 60 | if (isNaN(progress)) { 61 | return 0; 62 | } 63 | return progress * 100; 64 | } 65 | 66 | async function jobAction(job: IJob) { 67 | switch (job.status) { 68 | case ACTIVE_JOB: 69 | await PauseJobs(job.gid); 70 | break; 71 | case PAUSED_JOB: 72 | await StartJobs(job.gid); 73 | break; 74 | default: 75 | console.error(`Invalid job status ${job.status}`); 76 | break; 77 | } 78 | } 79 | 80 | return ( 81 | 82 | {jobs.map(o => { 83 | const labelId = `checkbox-list-label-${o.gid}`; 84 | return ( 85 | jobAction(o)} 95 | > 96 | {o.status === ACTIVE_JOB ? : } 97 | 98 | } 99 | > 100 | 101 | 108 | 109 | 113 | 119 | {getFileName(o)} 120 | 121 | 122 | } 123 | secondary={ 124 | 125 | 126 | 132 | {parseBytes(o.totalLength)} 133 | 134 | 140 | {parseBytes(o.downloadSpeed) + '\\s'} 141 | 142 | 143 | 144 | 145 | } 146 | /> 147 | 148 | ); 149 | })} 150 | 151 | ); 152 | } 153 | 154 | export default DownloadList; 155 | -------------------------------------------------------------------------------- /src/components/create/index.tsx: -------------------------------------------------------------------------------- 1 | import { notify } from '@/browser'; 2 | import { augmentDownloadLink } from '@/lib/magnet'; 3 | import { useSubmitTasksTrigger } from '@/lib/queries'; 4 | import Button from '@mui/material/Button'; 5 | import TextareaAutosize from '@mui/material/TextareaAutosize'; 6 | import { styled } from '@mui/material/styles'; 7 | import { useState } from 'react'; 8 | 9 | const CreationSection = styled('div')(({ theme }) => ({ 10 | width: 420, 11 | backgroundColor: theme.palette.background.paper, 12 | display: 'flex', 13 | flexFlow: 'column nowrap', 14 | })); 15 | 16 | interface ICreationArea { 17 | close: () => void; 18 | } 19 | 20 | function CreationArea({ close }: ICreationArea) { 21 | const { trigger } = useSubmitTasksTrigger(); 22 | 23 | const [text, setText] = useState(''); 24 | 25 | async function handleSubmit() { 26 | try { 27 | await trigger(text.split('\n').map(o => augmentDownloadLink(o))); 28 | } catch (e) { 29 | if (e instanceof Error) { 30 | await notify(`fail to download files, msg: ${e.message}.`); 31 | } 32 | } 33 | setText(''); 34 | close(); 35 | } 36 | 37 | return ( 38 | 39 | setText(e.target.value)} 45 | autoFocus 46 | /> 47 | 55 | 56 | ); 57 | } 58 | 59 | export default CreationArea; 60 | -------------------------------------------------------------------------------- /src/components/header/index.tsx: -------------------------------------------------------------------------------- 1 | import { PauseJobs, RemoveJobs, StartJobs } from '@/aria2'; 2 | import { openDetail, openSetting } from '@/browser'; 3 | import { ACTIVE_JOB, IJob, PAUSED_JOB } from '@/types'; 4 | import AddIcon from '@mui/icons-material/Add'; 5 | import DeleteIcon from '@mui/icons-material/Delete'; 6 | import MoreVertIcon from '@mui/icons-material/MoreVert'; 7 | import PauseIcon from '@mui/icons-material/Pause'; 8 | import PlayArrowIcon from '@mui/icons-material/PlayArrow'; 9 | import SettingsIcon from '@mui/icons-material/Settings'; 10 | import Checkbox from '@mui/material/Checkbox'; 11 | import IconButton from '@mui/material/IconButton'; 12 | import Stack from '@mui/material/Stack'; 13 | import { styled } from '@mui/material/styles'; 14 | import React from 'react'; 15 | 16 | interface IHeader { 17 | jobs: IJob[]; 18 | checked: string[]; 19 | show: boolean; 20 | setShow: () => void; 21 | setCheck: (value: string[]) => void; 22 | } 23 | 24 | const ActionGroup = styled('div')(({ theme }) => ({ 25 | paddingLeft: 3, 26 | display: 'flex', 27 | flexFlow: 'row nowrap', 28 | justifyContent: 'space-between', 29 | position: 'sticky', 30 | top: 0, 31 | zIndex: 10, 32 | backgroundColor: theme.palette.background.default, 33 | })); 34 | 35 | function Header({ jobs, checked, show, setShow, setCheck }: IHeader) { 36 | const disabled = checked.length === 1; 37 | 38 | const [isChecked, setIsChecked] = React.useState(false); 39 | 40 | function reset() { 41 | setCheck(['']); 42 | setIsChecked(false); 43 | } 44 | 45 | async function start(): Promise { 46 | const gid = checked.filter(o => o); 47 | const context = jobs 48 | .filter(o => gid.includes(o.gid) && o.status === PAUSED_JOB) 49 | .map(o => o.gid); 50 | await StartJobs(...context); 51 | reset(); 52 | } 53 | 54 | async function pause(): Promise { 55 | const gid = checked.filter(o => o); 56 | const context = jobs 57 | .filter(o => gid.includes(o.gid) && o.status === ACTIVE_JOB) 58 | .map(o => o.gid); 59 | await PauseJobs(...context); 60 | reset(); 61 | } 62 | 63 | async function remove(): Promise { 64 | const gid = checked.filter(o => o); 65 | await RemoveJobs(...gid); 66 | reset(); 67 | } 68 | 69 | function handleChange(event: React.ChangeEvent) { 70 | setIsChecked(event.target.checked); 71 | if (!event.target.checked) { 72 | setCheck(['']); 73 | } else { 74 | setCheck([''].concat(jobs.map(o => o.gid))); 75 | } 76 | } 77 | 78 | return ( 79 | 80 | 86 | 92 | setShow()}> 93 | 94 | 95 | start()}> 96 | 97 | 98 | pause()}> 99 | 100 | 101 | remove()}> 102 | 103 | 104 | openSetting()}> 105 | 106 | 107 | openDetail(true)}> 108 | 109 | 110 | 111 | 112 | ); 113 | } 114 | 115 | export default Header; 116 | -------------------------------------------------------------------------------- /src/components/panel/index.tsx: -------------------------------------------------------------------------------- 1 | import { getJobDetail, saveFile } from '@/browser'; 2 | import { useDownloadTrigger } from '@/lib/queries'; 3 | import { IFileDetail } from '@/types'; 4 | import { verifyFileName } from '@/utils'; 5 | import Button from '@mui/material/Button'; 6 | import Stack from '@mui/material/Stack'; 7 | import TextField from '@mui/material/TextField'; 8 | import TextareaAutosize from '@mui/material/TextareaAutosize'; 9 | import Typography from '@mui/material/Typography'; 10 | import { styled } from '@mui/material/styles'; 11 | import { ChangeEvent, useEffect, useState } from 'react'; 12 | 13 | const Panel = styled(Stack)(({ theme }) => ({ 14 | width: '98%', 15 | height: '100%', 16 | minWidth: 480, 17 | minHeight: 320, 18 | marginLeft: 'auto', 19 | marginRight: 'auto', 20 | backgroundColor: theme.palette.background.paper, 21 | })); 22 | 23 | const PanelButton = styled(Button)({ 24 | width: 130, 25 | }); 26 | 27 | const initialDetail: IFileDetail = { 28 | fileName: '', 29 | fileSize: '', 30 | header: [], 31 | url: '', 32 | }; 33 | 34 | function DownloadPanel() { 35 | const { trigger } = useDownloadTrigger(); 36 | 37 | const [detail, setDetail] = useState(initialDetail); 38 | const [inValid, isInValid] = useState(false); 39 | const [filePath, setFilePath] = useState(''); 40 | 41 | useEffect(() => { 42 | getJobDetail() 43 | .then(detail => { 44 | setDetail(detail); 45 | return verifyFileName(detail.fileName as string); 46 | }) 47 | .then(b => isInValid(b)) 48 | .catch(err => console.error(err)); 49 | }, []); 50 | 51 | function updateFileName( 52 | e: ChangeEvent, 53 | ): void { 54 | const name = e.target.value; 55 | setDetail({ ...detail, fileName: name }); 56 | verifyFileName(name) 57 | .then(b => isInValid(b)) 58 | .catch(err => console.error(err)); 59 | } 60 | 61 | async function downloadFile( 62 | url: string, 63 | fileName: string, 64 | filePath: string, 65 | headers: string[], 66 | ) { 67 | await trigger({ 68 | url, 69 | fileName, 70 | filePath, 71 | headers, 72 | }); 73 | } 74 | 75 | return ( 76 | 82 | 91 | setFilePath(e.target.value)} 96 | variant="standard" 97 | margin="dense" 98 | fullWidth 99 | /> 100 | 108 | 116 | {detail.fileSize} 117 | 118 | 124 | 131 | 135 | downloadFile( 136 | detail.url, 137 | detail.fileName as string, 138 | filePath, 139 | detail.header as string[], 140 | ) 141 | } 142 | > 143 | Download 144 | 145 | saveFile(detail.url, detail.fileName as string, false)} 149 | > 150 | Save 151 | 152 | saveFile(detail.url, detail.fileName as string, true)} 156 | > 157 | Save As 158 | 159 | 160 | 161 | ); 162 | } 163 | 164 | export default DownloadPanel; 165 | -------------------------------------------------------------------------------- /src/components/progress/index.tsx: -------------------------------------------------------------------------------- 1 | import Box from '@mui/material/Box'; 2 | import LinearProgress from '@mui/material/LinearProgress'; 3 | import Typography from '@mui/material/Typography'; 4 | import { styled } from '@mui/material/styles'; 5 | 6 | interface IProgressWithLabel { 7 | value: number; 8 | } 9 | 10 | function LinearProgressWithLabel(props: IProgressWithLabel) { 11 | const { value } = props; 12 | return ( 13 | 14 | 15 | 20 | 21 | 22 | {`${props.value.toFixed(1)}%`} 26 | 27 | 28 | ); 29 | } 30 | 31 | const ProgressHolder = styled('div')({ 32 | width: '100%', 33 | }); 34 | 35 | export default function LinearWithValueLabel({ value }: IProgressWithLabel) { 36 | return ( 37 | 38 | 39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/components/setting/index.tsx: -------------------------------------------------------------------------------- 1 | import { setConfiguration } from '@/browser'; 2 | import { useGetConfigurationQuery } from '@/lib/queries'; 3 | import manifest from '@/manifest'; 4 | import { IConfig, Theme } from '@/types'; 5 | import Container from '@mui/material/Container'; 6 | import Divider from '@mui/material/Divider'; 7 | import FormControl from '@mui/material/FormControl'; 8 | import FormHelperText from '@mui/material/FormHelperText'; 9 | import InputLabel from '@mui/material/InputLabel'; 10 | import MenuItem from '@mui/material/MenuItem'; 11 | import NativeSelect from '@mui/material/NativeSelect'; 12 | import TextField from '@mui/material/TextField'; 13 | import Typography from '@mui/material/Typography'; 14 | import React from 'react'; 15 | 16 | function Setting() { 17 | const protocol = { 18 | ws: 'WebSocket', 19 | wss: 'WebSocket (Security)', 20 | http: 'Http', 21 | https: 'Https', 22 | }; 23 | 24 | const { data: config, mutate: mutateConfig } = useGetConfigurationQuery(); 25 | 26 | async function updateTheme( 27 | e: React.ChangeEvent, 28 | ): Promise { 29 | if (config) { 30 | await updateConfig({ ...config, theme: e.target.value as Theme }); 31 | } 32 | } 33 | 34 | async function updateDownloadPath( 35 | e: React.ChangeEvent, 36 | ): Promise { 37 | if (config) { 38 | await updateConfig({ ...config, path: e.target.value }); 39 | } 40 | } 41 | 42 | async function updateHost( 43 | e: React.ChangeEvent, 44 | ): Promise { 45 | if (config) { 46 | await updateConfig({ ...config, host: e.target.value }); 47 | } 48 | } 49 | 50 | async function updatePort( 51 | e: React.ChangeEvent, 52 | ): Promise { 53 | if (config) { 54 | await updateConfig({ ...config, port: parseInt(e.target.value) }); 55 | } 56 | } 57 | 58 | async function updateToken( 59 | e: React.ChangeEvent, 60 | ): Promise { 61 | if (config) { 62 | await updateConfig({ ...config, token: e.target.value }); 63 | } 64 | } 65 | 66 | async function updateProtocol( 67 | e: React.ChangeEvent, 68 | ): Promise { 69 | if (config) { 70 | await updateConfig({ ...config, protocol: e.target.value }); 71 | } 72 | } 73 | 74 | async function updateConfig(config: IConfig) { 75 | try { 76 | await setConfiguration(config); 77 | await mutateConfig(); 78 | } catch (e) { 79 | console.error(e); 80 | } 81 | } 82 | 83 | return ( 84 | 85 | 95 | {Object.values(Theme).map(theme => ( 96 | 97 | {theme} 98 | 99 | ))} 100 | 101 | 110 | 111 | Protocol 112 | 117 | {Object.entries(protocol).map(([key, value]) => ( 118 | 121 | ))} 122 | 123 | 124 | RPC connection protocol of Aria2. Support ws, wss, http and https. 125 | 126 | 127 | 137 | 147 | 156 | 157 | 158 | v{manifest.version} 159 | 160 | 161 | ); 162 | } 163 | 164 | export default Setting; 165 | -------------------------------------------------------------------------------- /src/lib/magnet/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * https://en.wikipedia.org/wiki/Magnet_URI_scheme#Exact_Topic_(xt) 3 | * @param input url or hash string 4 | */ 5 | export function augmentDownloadLink(input: string): string { 6 | if (input.startsWith('http') || input.endsWith('https')) { 7 | return input; 8 | } 9 | 10 | // Remove any leading or trailing whitespace 11 | input = input.trim(); 12 | 13 | // Determine hash type and validate 14 | let hashType: string | null = null; 15 | 16 | if (/^[0-9a-f]{40}$/.test(input)) { 17 | // SHA-1 18 | hashType = 'btih'; 19 | } else if (/^[0-9a-f]{64}$/.test(input)) { 20 | // SHA-256 21 | hashType = 'btmh'; 22 | } else if (/^[a-f0-9]{32}$/i.test(input)) { 23 | // md5 24 | hashType = 'md5'; 25 | } else { 26 | return input; 27 | } 28 | return `magnet:?xt=urn:${hashType}:${input}`; 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/queries/index.ts: -------------------------------------------------------------------------------- 1 | import { AddUris, GetJobs } from '@/aria2'; 2 | import { download, getConfiguration } from '@/browser'; 3 | import { IConfig, IJob } from '@/types'; 4 | import useSWR from 'swr'; 5 | import useSWRMutation from 'swr/mutation'; 6 | 7 | enum FetchKey { 8 | TASKS = 'tasks', 9 | SETTING = 'setting', 10 | } 11 | 12 | export function useGetTasksQuery() { 13 | return useSWR(FetchKey.TASKS, GetJobs, { 14 | refreshInterval: 1000, 15 | }); 16 | } 17 | 18 | export function useGetConfigurationQuery() { 19 | return useSWR(FetchKey.SETTING, getConfiguration); 20 | } 21 | 22 | export function useSubmitTasksTrigger() { 23 | return useSWRMutation(FetchKey.TASKS, (_url, opts: { arg: string[] }) => 24 | AddUris(...opts.arg), 25 | ); 26 | } 27 | 28 | export function useDownloadTrigger() { 29 | return useSWRMutation( 30 | FetchKey.TASKS, 31 | async ( 32 | _url, 33 | opts: { 34 | arg: { 35 | url: string; 36 | fileName: string; 37 | filePath: string; 38 | headers: string[]; 39 | }; 40 | }, 41 | ) => { 42 | const { url, fileName, filePath, headers } = opts.arg; 43 | await download(url, fileName, filePath, headers); 44 | }, 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/App.tsx: -------------------------------------------------------------------------------- 1 | import { useGetTasksQuery } from '@/lib/queries'; 2 | import Container from '@mui/material/Container'; 3 | import CssBaseline from '@mui/material/CssBaseline'; 4 | import { styled, ThemeProvider } from '@mui/material/styles'; 5 | import { useState } from 'react'; 6 | import { HashRouter as Router, Route, Routes } from 'react-router-dom'; 7 | 8 | import DownloadList from '../components/content'; 9 | import CreationArea from '../components/create'; 10 | import Header from '../components/header'; 11 | import DownloadPanel from '../components/panel'; 12 | import Setting from '../components/setting'; 13 | import usePreferTheme from '../theme'; 14 | 15 | const DisplayHolder = styled(Container)(({ theme }) => ({ 16 | width: '100%', 17 | minWidth: 360, 18 | maxWidth: 420, 19 | backgroundColor: theme.palette.background.paper, 20 | })); 21 | 22 | function Display() { 23 | const [checked, setChecked] = useState(['']); 24 | const [show, setShow] = useState(false); 25 | 26 | const { data: jobs } = useGetTasksQuery(); 27 | 28 | const handleToggle = (value: string) => () => { 29 | const currentIndex = checked.indexOf(value); 30 | const newChecked = [...checked]; 31 | 32 | if (currentIndex === -1) { 33 | newChecked.push(value); 34 | } else { 35 | newChecked.splice(currentIndex, 1); 36 | } 37 | 38 | setChecked(newChecked); 39 | }; 40 | 41 | return ( 42 | 43 |
setShow(prevState => !prevState)} 48 | setCheck={setChecked} 49 | /> 50 | {show ? ( 51 | setShow(false)} /> 52 | ) : ( 53 | 58 | )} 59 | 60 | ); 61 | } 62 | 63 | function App() { 64 | const preferTheme = usePreferTheme(); 65 | 66 | return ( 67 | 68 | 69 | 70 | 71 | } /> 72 | } /> 73 | } /> 74 | 75 | 76 | 77 | ); 78 | } 79 | 80 | export default App; 81 | -------------------------------------------------------------------------------- /src/main/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | AriaEx 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | 4 | import App from './App'; 5 | 6 | const container = document.getElementById('root'); 7 | const root = createRoot(container as HTMLIFrameElement); 8 | 9 | root.render( 10 | 11 | 12 | , 13 | ); 14 | -------------------------------------------------------------------------------- /src/manifest.ts: -------------------------------------------------------------------------------- 1 | import type { Manifest } from 'webextension-polyfill'; 2 | 3 | import pkg from '../package.json'; 4 | 5 | const manifest: Manifest.WebExtensionManifest = { 6 | manifest_version: 3, 7 | name: pkg.displayName, 8 | version: pkg.version, 9 | icons: { 10 | '48': 'logo48.png', 11 | '128': 'logo128.png', 12 | }, 13 | permissions: [ 14 | 'tabs', 15 | 'contextMenus', 16 | 'notifications', 17 | 'storage', 18 | 'downloads', 19 | 'webRequest', 20 | 'webRequestBlocking', 21 | ], 22 | host_permissions: [''], 23 | content_security_policy: { 24 | extension_pages: "script-src 'self'; object-src 'self';", 25 | }, 26 | background: { 27 | scripts: ['background/index.js'], 28 | type: 'module', 29 | }, 30 | commands: { 31 | open_detail: { 32 | description: 'Open AriaNg', 33 | }, 34 | }, 35 | action: { 36 | default_popup: 'index.html', 37 | default_title: 'Aria2Ex', 38 | }, 39 | options_ui: { 40 | page: 'index.html#/setting', 41 | open_in_tab: true, 42 | }, 43 | browser_specific_settings: { 44 | gecko: { 45 | id: '{9e3f5f09-a4c6-43c2-8715-cac81530a5ce}', 46 | }, 47 | }, 48 | }; 49 | 50 | export default manifest; 51 | -------------------------------------------------------------------------------- /src/theme/index.tsx: -------------------------------------------------------------------------------- 1 | import { useGetConfigurationQuery } from '@/lib/queries'; 2 | import { Theme } from '@mui/material'; 3 | import { createTheme } from '@mui/material/styles'; 4 | import useMediaQuery from '@mui/material/useMediaQuery'; 5 | import { useEffect, useState } from 'react'; 6 | 7 | import { Theme as ThemeOptions } from '../types'; 8 | 9 | const lightTheme = createTheme({ 10 | palette: { 11 | mode: 'light', 12 | }, 13 | }); 14 | 15 | const darkTheme = createTheme({ 16 | palette: { 17 | mode: 'dark', 18 | }, 19 | }); 20 | 21 | function usePreferTheme() { 22 | const { data: config } = useGetConfigurationQuery(); 23 | 24 | const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); 25 | const [theme, setTheme] = useState(lightTheme); 26 | 27 | useEffect(() => { 28 | if (config) { 29 | switch (config.theme) { 30 | case ThemeOptions.DARK: 31 | setTheme(darkTheme); 32 | break; 33 | case ThemeOptions.LIGHT: 34 | setTheme(lightTheme); 35 | break; 36 | case ThemeOptions.FOLLOWING_SYSTEM: 37 | setTheme(prefersDarkMode ? darkTheme : lightTheme); 38 | break; 39 | } 40 | } 41 | }, [prefersDarkMode, config]); 42 | 43 | return theme; 44 | } 45 | 46 | export default usePreferTheme; 47 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface IJob { 2 | gid: string; 3 | downloadSpeed: string; 4 | completedLength: string; 5 | totalLength: string; 6 | status: string; 7 | bittorrent?: { 8 | info: { 9 | name: string; 10 | }; 11 | }; 12 | files: [ 13 | { 14 | path: string; 15 | }, 16 | ]; 17 | } 18 | 19 | export interface IDownload { 20 | out: string; 21 | dir?: string; 22 | header?: string[]; 23 | } 24 | 25 | export interface IFileDetail { 26 | url: string; 27 | fileName?: string; 28 | fileSize?: string; 29 | header?: string[]; 30 | } 31 | 32 | export const ACTIVE_JOB = 'active'; 33 | export const PAUSED_JOB = 'paused'; 34 | 35 | export enum Theme { 36 | LIGHT = 'Light', 37 | DARK = 'Dark', 38 | FOLLOWING_SYSTEM = 'Following System', 39 | } 40 | 41 | export interface IConfig { 42 | path: string; 43 | protocol: string; 44 | host: string; 45 | port: number; 46 | token: string; 47 | theme: Theme; 48 | } 49 | 50 | export const DEFAULT_CONFIG: IConfig = { 51 | path: '', 52 | protocol: 'ws', 53 | host: '127.0.0.1', 54 | port: 6800, 55 | token: '', 56 | theme: Theme.FOLLOWING_SYSTEM, 57 | }; 58 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import browser, { WebRequest } from 'webextension-polyfill'; 2 | 3 | // eslint-disable-next-line no-control-regex 4 | const regex = /[<>:"/\\|?*\x00-\x1F]/g; 5 | 6 | export async function verifyFileName(name: string): Promise { 7 | const platformInfo = await browser.runtime.getPlatformInfo(); 8 | let tmp: any = name.match(regex) || []; 9 | if (platformInfo.os === 'win') { 10 | if (name.search(/^(con|prn|aux|nul|com\d|lpt\d)$/i) !== -1) { 11 | tmp = tmp.concat(name); 12 | } 13 | if (name[name.length - 1] === ' ' || name[name.length - 1] === '.') { 14 | tmp = tmp.concat('Filenames cannot end in a space or dot.'); 15 | } 16 | } 17 | return tmp.length !== 0; 18 | } 19 | 20 | export async function correctFileName(name: string): Promise { 21 | let tmp = name; 22 | const platformInfo = await browser.runtime.getPlatformInfo(); 23 | tmp = tmp.replace(regex, '_'); 24 | if (platformInfo.os === 'win') { 25 | if (tmp.search(/^(con|prn|aux|nul|com\d|lpt\d)$/i) !== -1) tmp = '_' + tmp; 26 | if (tmp[tmp.length - 1] === ' ' || tmp[tmp.length - 1] === '.') 27 | tmp = tmp.slice(0, tmp.length - 1); 28 | } 29 | return tmp; 30 | } 31 | 32 | export function getFileName( 33 | d: WebRequest.OnHeadersReceivedDetailsType, 34 | ): string { 35 | if (!d.responseHeaders) { 36 | return ''; 37 | } 38 | // get file name 39 | const id = d.responseHeaders.findIndex( 40 | x => x.name.toLowerCase() === 'content-disposition', 41 | ); 42 | if (id >= 0) { 43 | const PARAM_REGEXP = 44 | // eslint-disable-next-line no-control-regex 45 | /;[\x09\x20]*([!#$%&'*+.\dA-Z^_`a-z|~-]+)[\x09\x20]*=[\x09\x20]*("(?:[\x20!\x23-\x5b\x5d-\x7e\x80-\xff]|\\[\x20-\x7e])*"|[!#$%&'*+.\dA-Z^_`a-z|~-]+)[\x09\x20]*/g; 46 | const EXT_VALUE_REGEXP = 47 | /^([A-Za-z\d!#$%&+\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[\dA-Fa-f]{2}|[A-Za-z\d!#$&+.^_`|~-])+)$/; 48 | // eslint-disable-next-line no-control-regex 49 | const QESC_REGEXP = /\\([\u0000-\u007f])/g; 50 | 51 | const string = d.responseHeaders[id].value; 52 | 53 | const match = PARAM_REGEXP.exec(string as string); 54 | if (!match) { 55 | return getFileNameURL(d.url); 56 | } 57 | 58 | const key = match[1].toLowerCase(); 59 | let value = match[2]; 60 | 61 | if (key.indexOf('*') + 1 === key.length) { 62 | if (!EXT_VALUE_REGEXP.exec(value)) { 63 | return getFileNameURL(d.url); 64 | } else { 65 | value = match[2]; 66 | } 67 | } 68 | 69 | if (value[0] === '"') { 70 | // remove quotes and escapes 71 | value = value.substr(1, value.length - 2).replace(QESC_REGEXP, '$1'); 72 | } 73 | return value; 74 | } 75 | return getFileNameURL(d.url); 76 | } 77 | 78 | function getFileNameURL(url: string) { 79 | const id = url.lastIndexOf('/'); 80 | if (id >= 0) { 81 | const id1 = url.lastIndexOf('?'); 82 | if (id1 === -1) { 83 | return url.slice(id + 1); 84 | } 85 | return url.slice(id + 1, id1); 86 | } 87 | return ''; 88 | } 89 | 90 | export function getRequestHeaders( 91 | d: WebRequest.OnSendHeadersDetailsType, 92 | ): string[] { 93 | // create header 94 | const requestHeaders: string[] = []; 95 | if (!d.requestHeaders) { 96 | return requestHeaders; 97 | } 98 | const headers = ['Referer', 'Cookie', 'Cookie2', 'Authorization']; 99 | for (let i = 0; i < headers.length; i++) { 100 | const j = d.requestHeaders.findIndex(x => x.name === headers[i]); 101 | if (j >= 0) { 102 | requestHeaders[i] = 103 | d.requestHeaders[j].name + ': ' + d.requestHeaders[j].value; 104 | } 105 | } 106 | return requestHeaders; 107 | } 108 | 109 | export function parseBytes(value: string): string { 110 | const symbol = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 111 | let speed = parseFloat(value); 112 | let order = 0; 113 | while (speed >= 1000 && order < symbol.length - 1) { 114 | order++; 115 | speed = speed / 1000; 116 | } 117 | 118 | return `${speed.toFixed(2)} ${symbol[order]}`; 119 | } 120 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "module": "esnext", 6 | "alwaysStrict": true, 7 | "allowJs": true, 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "jsx": "react-jsx", 20 | "noEmit": true, 21 | "baseUrl": ".", 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["src/*"], 27 | "exclude": ["node_modules", "build", "artifacts"], 28 | "references": [ 29 | { 30 | "path": "./tsconfig.node.json" 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "esnext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.mts", "plugins/*"] 10 | } 11 | -------------------------------------------------------------------------------- /vite.config.mts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react-swc'; 2 | import { resolve } from 'path'; 3 | import { defineConfig } from 'vite'; 4 | import eslint from 'vite-plugin-eslint'; 5 | import tsconfigPaths from 'vite-tsconfig-paths'; 6 | 7 | import bundleAria from './plugins/bundle-aria'; 8 | import cleanBuildScript from './plugins/clean-build'; 9 | import makeManifest from './plugins/make-manifest'; 10 | import moveEntryScript from './plugins/move-entry'; 11 | 12 | const root = resolve(__dirname, 'src'); 13 | const outDir = resolve(__dirname, 'build'); 14 | const publicDir = resolve(__dirname, 'public'); 15 | 16 | export default defineConfig({ 17 | resolve: { 18 | alias: { 19 | '@': root, 20 | }, 21 | }, 22 | plugins: [ 23 | react(), 24 | cleanBuildScript(), 25 | moveEntryScript(), 26 | makeManifest(), 27 | bundleAria(), 28 | tsconfigPaths(), 29 | eslint(), 30 | ], 31 | publicDir, 32 | build: { 33 | outDir, 34 | sourcemap: process.env.__DEV__ === 'true', 35 | emptyOutDir: false, 36 | rollupOptions: { 37 | input: { 38 | main: resolve(root, 'main', 'index.html'), 39 | background: resolve(root, 'background', 'index.ts'), 40 | }, 41 | output: { 42 | entryFileNames: chunk => `${chunk.name}/index.js`, 43 | }, 44 | }, 45 | }, 46 | }); 47 | -------------------------------------------------------------------------------- /webext.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Global. 3 | sourceDir: './build', 4 | artifactsDir: './artifacts', 5 | // Build. 6 | build: { 7 | overwriteDest: true, 8 | }, 9 | }; 10 | --------------------------------------------------------------------------------