Compare commits

...

377 Commits

Author SHA1 Message Date
Dr_rOot 513f566a59 chore: bump version to 1.8.17 2023-05-02 18:08:49 +08:00
Dr_rOot f49f083044 fix: update-downloaded set progress bar value to 1 2023-05-02 18:06:44 +08:00
Dr_rOot 830bc9336e chore: bump version to 1.8.16 2023-05-02 17:35:14 +08:00
Dr_rOot 3b049219e4 chore: update deps 2023-05-02 17:33:51 +08:00
Dr_rOot bbc68b6a8a chore: github action remove notarization api key 2023-05-02 17:33:22 +08:00
Dr_rOot 0afc01080d fix: preference save relaunch application twice 2023-04-29 11:26:26 +08:00
Dr_rOot af1cc4db8e chore: bump version to 1.8.15 2023-04-28 22:54:29 +08:00
Dr_rOot 8defa9ee71 Merge pull request #1466 from agalwood/hotfix/aria2_x64_20230428
fix: rebuild darwin x64 aria2c bin
2023-04-28 22:51:56 +08:00
Dr_rOot cb6d9f2b20 chore: change node mini version to 16 2023-04-28 22:50:01 +08:00
Dr_rOot 9f6c31ffa3 fix: rebuild darwin x64 aria2c bin 2023-04-28 22:49:10 +08:00
greenhandzdl 0b3161ca6f docs: fix yay install script (#1465)
* Fix Doc
2023-04-28 22:39:06 +08:00
Dr_rOot c79bdff4b8 Merge pull request #1464 from agalwood/hotfix/aria2_arm64_20230428
fix: rebuild arm64 aria2c bin
2023-04-28 18:45:00 +08:00
Dr_rOot b92957668b chore: update deps 2023-04-28 18:19:57 +08:00
Dr_rOot b1b52e2640 fix: rebuild arm64 aria2c bin 2023-04-28 18:16:01 +08:00
Dr_rOot a39fa2979c chore: bump version to 1.8.14 2023-04-27 17:49:04 +08:00
Dr_rOot e0a74f7979 fix: selected task item border color in dark mode 2023-04-27 17:47:59 +08:00
Dr_rOot a7b8333710 chore: bump version to 1.8.13 2023-04-27 17:08:14 +08:00
Dr_rOot 77850da0fe Merge pull request #1455 from agalwood/hotfix/auto_update_20230427
fix: app auto update failed
2023-04-27 17:06:53 +08:00
Dr_rOot 71f4eeeb25 chore: change app update generic url 2023-04-27 16:51:28 +08:00
Dr_rOot e5eace479d fix: handle auto updater update canceled event 2023-04-27 16:51:06 +08:00
Dr_rOot 7e927fea10 chore: bump version to 1.8.12 2023-04-27 11:11:34 +08:00
Dr_rOot 38cd952b44 Merge pull request #1453 from agalwood/hotfix/stop_engine_20230427
fix: stop engine failed when app quit
2023-04-27 11:10:23 +08:00
Dr_rOot b07fedeba0 chore: electron build mac target add universal 2023-04-27 10:54:38 +08:00
Dr_rOot 2bbaae5d7d feat: preference advanced add log level setting 2023-04-27 10:53:27 +08:00
Dr_rOot 003e9ffccb fix: stop engine failed when app quit 2023-04-27 10:23:08 +08:00
Dr_rOot 7a51dbaf5e chore: update bug report issue template 2023-04-27 08:00:07 +08:00
Dr_rOot 4fa4878e61 fix: appx for ms store & bump version to 1.8.11 2023-04-26 21:14:33 +08:00
Dr_rOot daa04d995e fix: publisher for ms store & bump version to 1.8.10 2023-04-26 20:59:50 +08:00
Dr_rOot 74e4bce6bf fix: identity for ms store & bump version to 1.8.9 2023-04-26 20:55:29 +08:00
Dr_rOot a0aa100a99 fix: author for ms store & bump version to 1.8.8 2023-04-26 20:08:53 +08:00
Dr_rOot 4c3b248cc0 chore: bump version to 1.8.7 2023-04-26 18:59:56 +08:00
Dr_rOot 96d525832f fix: github action SNAPCRAFT_STORE_CREDENTIALS 2023-04-26 18:59:34 +08:00
Dr_rOot 9255c95ecc chore: bump version to 1.8.6 2023-04-26 18:31:00 +08:00
Dr_rOot e117d5925a fix: notarize apple id password 2023-04-26 18:29:35 +08:00
Dr_rOot 6a5d7637c2 chore: bump version to 1.8.5 2023-04-26 18:22:19 +08:00
Dr_rOot 93e8bdff00 chore: notarize tool 2023-04-26 18:21:55 +08:00
Dr_rOot a82afbf97b chore: bump version to 1.8.4 2023-04-26 17:48:52 +08:00
Dr_rOot e1363d3fe2 fix: electron build notarization tool 2023-04-26 17:48:43 +08:00
Dr_rOot 1338861dc3 chore: bump version to 1.8.3 2023-04-26 17:34:32 +08:00
Dr_rOot b9068559e2 refactor: electron build notarization 2023-04-26 17:34:07 +08:00
Dr_rOot b7ed072966 chore: bump version to 1.8.2 2023-04-26 16:56:36 +08:00
Dr_rOot e281b71d4e refactor: github electron build action 2023-04-26 16:56:21 +08:00
Dr_rOot 67d26e4b1f chore: bump version to 1.8.1 2023-04-26 16:01:41 +08:00
Dr_rOot 1b27980992 Merge pull request #1445 from agalwood/feature/show_progess_bar_20230426
feat: preference basic show progress bar
2023-04-26 15:04:08 +08:00
Dr_rOot 292ad15152 chore: update deps 2023-04-26 13:27:39 +08:00
Dr_rOot df1b30d6f4 chore: sign darwin aria2c bin 2023-04-26 13:27:31 +08:00
Dr_rOot 0d3aa86d31 chore: i18n show progress bar 2023-04-26 13:27:04 +08:00
Dr_rOot a07f81e20c feat: preference basic show progress bar 2023-04-26 13:26:47 +08:00
Dr_rOot 61365d738c chore: lint fix after sign hook js 2023-04-26 10:53:17 +08:00
Dr_rOot 33f13b51b0 fix: electron builder win targets artifact name 2023-04-25 11:35:07 +08:00
Dr_rOot 2bda3b40eb fix: github action snapcraft token env (#1441) 2023-04-24 21:14:20 +08:00
Dr_rOot e456ba660b Merge pull request #1440 from agalwood/feature/aria2_conf_path_20230423 2023-04-23 19:41:57 +08:00
Dr_rOot 342b2b2069 chore: aria2 max-overall-upload-limit default 0 2023-04-23 18:17:55 +08:00
Dr_rOot 91697aee42 chore: i18n aria2 conf path 2023-04-23 18:17:03 +08:00
Dr_rOot f4e56ca4b7 feat: preference advanced show aria2 conf path 2023-04-23 18:16:44 +08:00
Dr_rOot 363cef2959 Merge pull request #1439 from agalwood/feature/appx_20230421
feat: build output add windows appx
2023-04-21 21:19:57 +08:00
Dr_rOot e938f30377 chore: rename appId avoid linux .desktop 2023-04-21 21:02:24 +08:00
Dr_rOot ed622ade1f chore: build output add windows appx 2023-04-21 21:01:13 +08:00
Dr_rOot 06f0ccf2e5 Merge pull request #1436 from agalwood/feature/rpc_listen_port_20230419
feat: preference advanced config rpc listen port
2023-04-19 21:55:15 +08:00
Dr_rOot 6885da1b86 feat: linux build add mime types 2023-04-19 18:14:42 +08:00
Dr_rOot 3ffce0566b feat: task detail files show completed percent 2023-04-19 18:14:16 +08:00
Dr_rOot a215df0360 feat: preference advanced config rpc listen port 2023-04-19 18:13:22 +08:00
Dr_rOot 6bea4322c1 chore: upgrade electron builder action (#1435) 2023-04-18 10:45:52 +08:00
Dr_rOot 493df2b333 feat: release support arm arch linux (#1434)
* feat: release support arm arch linux

Aria2 build:
https://github.com/motrixapp/Aria2-Pro-Core/tree/build_for_motrix

* chore: update github action yml

* chore: revert github action ubuntu-arm64
2023-04-17 21:31:08 +08:00
Chiao-Wei Wang 73c5a6790a feat: Add cURL support for convenient download with cookie/referer information (#1430)
* Add CURL support

* Fix GET request parameters problem

* Add AppleSilicon build command

* Use patched curl-to-json

* Add AppleSilicon building instruction on README

* Update curl-to-json to get rid of warnings

* fix: cannot correctly parse curl -b option

* fix: curl cannot use -A -e options

* fix: update curl-to-json to patch

* chore: update curl-to-json to v1.2.7

* Use --arm64 instead of --universal for build::applesilicon
2023-04-17 11:29:46 +08:00
Dr_rOot 644966e438 feat: app menu add task list and bind hotkey 2023-04-15 21:50:57 +08:00
Chiao-Wei Wang af6e0f33c0 fix: bug regarding cookie/referer (#1428)
When cookie/referer are set at the same time, aria2 failed to parse the cookie/referer arguments.
2023-04-15 11:11:45 +08:00
Dr_rOot 81656a2cd0 Merge pull request #1427 from agalwood/feature/dir_history_20230412
feat: choose dir from history directory
2023-04-13 15:20:22 +08:00
Dr_rOot 74b9f465a1 fix: update nat-api to fix xml2js vulnerability 2023-04-13 14:44:17 +08:00
Dr_rOot 92177a2c4b feat: choose dir from history directory 2023-04-13 12:49:25 +08:00
Dr_rOot bc0114a164 refactor: tracker utils 2023-04-13 12:42:24 +08:00
Dr_rOot ff918c916c refactor: text direction 2023-04-13 12:24:49 +08:00
Dr_rOot d81c0bbb9a chore: update git ignore 2023-04-13 12:23:04 +08:00
Dr_rOot b240029a47 refactor: parseHeader replace substr to substring 2023-04-12 19:52:50 +08:00
Dr_rOot ba887bcc2b fix: directory history props 2023-04-12 14:40:01 +08:00
Dr_rOot 1d90231ebd feat: add task dir history 2023-04-12 11:21:07 +08:00
Dr_rOot 39d92907e9 Merge pull request #1426 from agalwood/hotfix/linux_rendering_bug_20230410
fix: BrowserWindow rendering bug under linux
2023-04-12 09:51:33 +08:00
Dr_rOot 4272a53f69 fix: preference basic need confirm but not changed
The modified preferences will be lost, are you sure you want to leave?
2023-04-11 12:02:06 +08:00
Dr_rOot 0bbe980d48 chore: declare node version >= 18 2023-04-10 21:24:23 +08:00
Dr_rOot d2017a6c25 fix: BrowserWindow rendering bug under linux 2023-04-10 18:23:37 +08:00
Dr_rOot e3da125cc7 refactor: update some aria2 default conf (#1425)
* chore: update deps

* chore: update axios

* chore: update default aria2 config

Some configurations are learned from
https://github.com/P3TERX/aria2.conf

* feat: preferences basic add bt-force-encryption ui
2023-04-09 21:43:04 +08:00
Shatyuka 41a81e7793 feat: add taskbar progress (#1424) 2023-04-09 21:12:19 +08:00
Dr_rOot 11c08b0dc2 chore: update new cn bug report issue template 2023-04-06 10:44:55 +08:00
Imamuzzaki Abu Salam dbc95965aa chore: Create New Bug Report Issue Template to prevent bad contributor experience 2023-04-06 10:16:18 +08:00
Dr_rOot 26b5662c82 refactor: adjust the style of the speed limit 2023-04-05 15:24:28 +08:00
Dr_rOot bb37d4f328 Merge branch 'l10n-tw-master'
chore: improve zh-TW localization quality
2023-04-05 14:23:01 +08:00
Dr_rOot b355bcf623 Merge branch 'master' of github.com:l10n-tw/Motrix into l10n-tw-master
# Conflicts:
#	src/shared/locales/zh-TW/preferences.js
2023-04-05 14:22:13 +08:00
Dr_rOot 9076ec08f8 chore: update pt-BR task stopped translate
closed #1045
2023-04-04 23:55:11 +08:00
Dr_rOot 65794cabab Merge pull request #1420 from agalwood/hotfix/mac_dmg_20230401
fix: electron-builder build mac dmg
2023-04-04 23:42:43 +08:00
Dr_rOot e3f506ec0c fix: electron-builder dmg build fail 2023-04-04 23:40:14 +08:00
Dr_rOot 70e7c99ff9 refactor: move dependencies to devDependencies
Reduce the final size of the app release
2023-04-04 23:39:57 +08:00
Dr_rOot bc66511647 refactor: remove electron-debug 2023-04-04 23:34:57 +08:00
Dr_rOot 002ef830dd fix: addTask autofillResourceLink this error 2023-04-04 23:29:15 +08:00
Shatyuka 06ffe6bd87 feat: lock AddTask dialog (#1416) 2023-04-04 23:23:32 +08:00
Sammy ecf59583d6 fix: add nullcheck for legacy speed settings (#1417) 2023-04-04 19:37:56 +08:00
Sammy 7201f17aed feat: Allow more customization for transfer speeds (#1413) 2023-04-03 10:57:51 +08:00
Dr_rOot b4df97b471 fix: hide tray not show when run mode changed 2023-04-01 22:34:08 +08:00
Dr_rOot 5ea19eb441 Merge pull request #1412 from agalwood/feature/hide_tray_20230401
feat: hide tray application
2023-04-01 17:51:02 +08:00
Dr_rOot 8857290a0a chore: update deps & bump version to 1.8.0 2023-04-01 17:26:53 +08:00
Dr_rOot 1dec09b005 feature: refactor run mode add hide tray 2023-04-01 17:13:16 +08:00
Dr_rOot d67ba74b44 refactor: application init 2023-04-01 17:11:58 +08:00
Dr_rOot 696d5c7f02 docs: update translation guide locales 2023-04-01 16:17:21 +08:00
Dr_rOot 106410cb23 docs: update lost nl locale 2023-04-01 15:36:29 +08:00
Dr_rOot 237df6011e chore: update task authorization i18n 2023-04-01 15:36:10 +08:00
Dr_rOot 20dbeb33ac refactor: mock ua 2023-03-31 20:57:07 +08:00
Dr_rOot 55e2a42f3d chore: upgrade aria2 version to 1.36 (#1411)
* chore: update build scripts

* fix: setup protocols client empty error

* refactor: auto change theme

* refactor: engine max connection per server

* refactor: adjust add task dialog style

* refactor: aria2 client lib import

* chore: update deps

* chore: striped darwin arm64 aria2c bin

* chore: strip darwin engine x64 aria2c bin

* chore: upgrade linux engine arai2c bin to 1.36

* chore: upgrade windows engine arai2c bin to 1.36
2023-03-31 20:08:19 +08:00
Dr_rOot 637d5e9a80 Merge pull request #1408 from agalwood/hotfix/some_url_fix_20230328
fix: some fix
2023-03-29 22:03:01 +08:00
Dr_rOot 9867c578dc chore: i18n update stop seeding tip 2023-03-29 21:22:45 +08:00
Dr_rOot a8bac652d1 chore: bump @motrix/nat-api version 2023-03-29 21:21:15 +08:00
Dr_rOot 7d0a94ee37 chore: change notarize deps 2023-03-29 11:54:11 +08:00
Dr_rOot 0276c8e5b4 refactor: simplified upnp mapping fail log 2023-03-29 11:53:25 +08:00
RaymondSalim e93d6a2c92 feat: add http authorization header (#1263)
Co-authored-by: Dr_rOot <agalwood.net@gmail.com>
2023-03-29 10:48:33 +08:00
Dr_rOot 8743e8a025 chore: change jsdelivr cdn url 2023-03-28 13:21:58 +08:00
Dr_rOot 680fdac38b refactor: dock manager 2023-03-28 13:21:32 +08:00
Dr_rOot e06850b5a2 chore: update Transmission mock version 2023-03-28 13:20:55 +08:00
Dr_rOot 038511fed4 fix: github actions mac skip notarize 2023-03-27 22:10:44 +08:00
Dr_rOot 6b2deb8303 fix: engine aria2 bin path 20230327 (#1405)
* fix: engine aria2 bin

* chore: upgrade github actions release node version

* chore: github actions skip notarization if not tag
2023-03-27 21:42:07 +08:00
Dr_rOot a8846eaf92 fix: update deps 20230324 (#1404)
* refactor: i18n thai language

* chore: rename renderer app name

* refactor: remove svg-innerhtml import

* refactor: update deps

* fix: protocol config save error

* fix: arch engine path copy

* chore: build arm64 arch aria2 v1.36.0
2023-03-27 21:19:56 +08:00
Dr_rOot 6039a89441 Merge pull request #1056 from 149segolte/update-windows-aria2-executables
Update Windows aria2 executables per architecture
2023-03-24 21:48:25 +08:00
Nesaku 2bfab8b9c8 fix: Improve English Translations (#1367) 2023-03-24 21:40:44 +08:00
Esteban Zapata e6c533e139 fix: spanish translation (#1361) 2023-03-24 21:40:14 +08:00
MagicLike 1c2fd0de05 fix: German translation (#1302)
- Fixed German translations for "preferences", "theme-auto", "thunder" & "auto-check-update"
2023-03-24 21:38:49 +08:00
Minseo Lee a91af1032c fix: Update Korean translation (#1290)
* Update about.js

* Update app.js

* Update edit.js

* Update help.js

* Update menu.js

* Update preferences.js

* Update app.js

* Update preferences.js

* Update task.js

* Update window.js

* Update task.js

* Update preferences.js

* Update app.js

* Update preferences.js

* Update preferences.js

* Update task.js
2023-03-24 21:38:20 +08:00
CDzungx c80102c5f5 refactor: Small Vietnamese translation update (#1162)
* refactor: Small Vietnamese translation fix

* Update edit.js

* Update task.js
2023-03-24 21:36:26 +08:00
Non-Official NPM Mirror Bot ff312df849 docs: update taobao npm registry address (#1155)
Co-authored-by: NPM Mirror Bot <npmmirror@localhost>
2023-03-24 21:35:56 +08:00
Nawapon Boonjua 65bf25dc1a feat: Translated to Thai Language (#1046) 2023-03-24 21:32:19 +08:00
toto6038 6f35e64c2a chore(l10n): Improve zh-tw localization quality 2022-04-08 23:11:21 +08:00
SiderealArt 4c5bba89ef chore: update zh-TW translation (#1107) 2022-02-13 22:37:37 +08:00
Nick Bouwhuis 51b1be7bbc feat: add Dutch language translations (#1104)
* add Dutch language translations

* fix typo

* add 'nl' to `src/shared/locales/app.js` and `src/shared/locales/all.js`

* fix indenting

Co-authored-by: Nick Bouwhuis <nick@bouwhuis.io>
Co-authored-by: Nick Bouwhuis <n.bouwhuis@speakup.nl>
2021-12-02 15:50:49 +08:00
Dr_rOot 1bf594b369 Merge pull request #1097 from tso1158687/chinese-traditional
refactor: adjust chinese traditional translation
2021-12-01 10:38:25 +08:00
Dr_rOot de1db5687b fix: add session-reset & session-reset-confirm to de language (#1103)
fix: add session-reset & session-reset-confirm to de language
2021-12-01 10:38:09 +08:00
Dr_rOot 98580fc094 Merge pull request #1093 from shatyuka/webpack_cli
fix: upgrade webpack cli parameters
2021-12-01 10:36:08 +08:00
Dr_rOot 8994312ee0 Merge pull request #1092 from shatyuka/save_preference
refactor: improve preference
2021-12-01 10:34:32 +08:00
Nick Bouwhuis bd9b5eca12 add session-reset & session-reset-confirm to de language 2021-11-27 19:44:34 +01:00
jason zheng 4ed0bba8ba adjust chinese traditional translation 2021-11-23 12:59:52 +08:00
Shatyuka 7868a4870b fix: update webpack cli parameters 2021-11-22 03:55:36 +08:00
Shatyuka 8052aa047e refactor: improve preference 2021-11-22 03:30:17 +08:00
Dr_rOot 559df6bb8f Merge pull request #1085 from shatyuka/macos_tray_tooltip
fix: do not show tray tooltip for macOS
2021-11-18 10:45:59 +08:00
Shatyuka c70a35e083 fix: do not show tray tooltip for macOS 2021-11-12 13:29:25 +08:00
149segolte 30817eac86 docs: Update Readme 2021-09-24 11:05:46 +05:30
149segolte 751efd6b00 feat: Update windows aria2 binaries to 1.36.0 2021-09-23 14:20:34 +05:30
149segolte 7688115bd4 build: Update Windows aria2 executables 2021-09-17 13:29:43 +05:30
Eshagh 732327f5a3 fix: update persian translation (#1024)
* update persian translation

* update persian translate
2021-09-13 10:48:07 +08:00
Dr_rOot 38fb29382b Merge pull request #1022 from shatyuka/fix_macos_dark_menu
fix: macos dark menu
2021-08-15 14:36:15 +08:00
Dr_rOot ee05252163 Merge pull request #1019 from shatyuka/fix_minimize
fix: showWindow won't restore minimized window
2021-08-15 14:36:04 +08:00
Shatyuka 74ba6ec5ab fix: macos dark menu icon 2021-08-09 12:10:09 +08:00
Shatyuka 1f5f5dc5f0 fix: showWindow won't restore minimized window 2021-08-06 00:17:54 +08:00
Dr_rOot 6be0afd5ff Merge pull request #998 from agalwood/feature/app_improve_20210530 2021-07-04 23:04:01 +08:00
Dr_rOot 2c269e3a52 Merge branch 'master' into feature/app_improve_20210530 2021-07-04 22:21:21 +08:00
Dr_rOot 79dea2f4b7 fix: update node version for electron-builder 2021-07-04 22:03:42 +08:00
Dr_rOot 9db5c52f13 chore: update deps 2021-07-04 22:02:48 +08:00
Dr_rOot 81a73557e8 refactor: sort i18n locale key 2021-07-04 22:02:21 +08:00
Dr_rOot 6fc02f752d chore: i18n bt-auto-download-content 2021-07-04 21:59:48 +08:00
Dr_rOot 6d893bd1ce refactor: drag select 2021-07-04 15:17:45 +08:00
albanobattistella 68ba9207f9 refactor: i18n update it (#990)
* Update app.js

* Update preferences.js

* Update task.js
2021-06-30 12:11:31 +08:00
Dr_rOot b01d43cf73 refactor: bt auto download content 2021-06-19 22:39:01 +08:00
Dr_rOot ba97bbfa66 refactor: set follow torrent metalink default true 2021-06-19 15:53:25 +08:00
dicarne b8dbc902a3 fix: reorganize the code 2021-06-19 15:51:13 +08:00
dicarne b79ef074dd feat: add switch for auto download magnet and torrent 2021-06-19 15:51:13 +08:00
dicarne 3ceccea9da fix: tasks list has old wrong items 2021-06-19 15:51:13 +08:00
CDzungx c591ea9655 refactor: Update Vietnamese translation (#978) 2021-06-19 15:51:13 +08:00
Dr_rOot 3245bbdaa1 Merge pull request #980 from dicarne/master
fix: tasks list has old wrong items and add switch for auto download torrent
2021-06-19 15:49:41 +08:00
dicarne 60161518c6 fix: reorganize the code 2021-06-15 19:50:27 +08:00
Dr_rOot 1de2acc505 fix: spawn ENAMETOOLONG by reduceTrackerString 2021-06-15 12:06:35 +08:00
dicarne a77e62066a feat: add switch for auto download magnet and torrent 2021-06-09 13:09:28 +08:00
dicarne c6722d141b fix: tasks list has old wrong items 2021-06-09 00:37:54 +08:00
CDzungx 509c79989b refactor: Update Vietnamese translation (#978) 2021-06-08 22:33:30 +08:00
Dr_rOot 333f01afd3 refactor: improve rtl support 2021-05-30 14:33:50 +08:00
Dr_rOot edd4053730 refactor: improve responsive style 2021-05-30 14:30:13 +08:00
Dr_rOot 7db1e4ff5b refactor: page init skeleton 2021-05-30 14:28:03 +08:00
Dr_rOot a9f5312a90 refactor: util bytesToSize precision 2021-05-30 14:22:17 +08:00
Dr_rOot 6d9a640030 fix: task item status 2021-05-30 13:51:03 +08:00
Dr_rOot 74c241f819 chore: remove task item action more button 2021-05-30 13:50:16 +08:00
Dr_rOot 3e0df405ee chore: update deps 2021-05-29 18:39:21 +08:00
Dr_rOot 4dd6e948f4 docs: update readme chocolatey 2021-05-29 18:35:42 +08:00
Dr_rOot c0b7612e3a Merge pull request #967 from agalwood/dependabot/npm_and_yarn/dns-packet-1.3.4
chore(deps): bump dns-packet from 1.3.1 to 1.3.4
2021-05-29 18:34:03 +08:00
dependabot[bot] 0486e4b417 chore(deps): bump dns-packet from 1.3.1 to 1.3.4
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-29 04:12:41 +00:00
Dr_rOot 160fe9725d docs: readme add chocolatey pkg 2021-05-21 11:26:44 +08:00
Dr_rOot 577a9b32a1 Merge pull request #957 from shatyuka/fix_windows_titlebar
fix: window resize
2021-05-20 10:37:42 +08:00
Shatyuka f05994d9f8 fix: window resize 2021-05-19 22:22:58 +08:00
Dr_rOot dec938f2f0 chore: setup github action codeql 2021-05-19 12:01:37 +08:00
Dr_rOot 084b5350fd docs: update readme add flatpak 2021-05-19 10:56:10 +08:00
Dr_rOot e971f86932 docs: update readme release status of nb 2021-05-18 21:18:52 +08:00
Dr_rOot 0be90dda31 docs: update i18n contributors add nb 2021-05-18 21:14:41 +08:00
Dr_rOot 2bc4a8c61e fix: i18n get language by electron locale 2021-05-18 21:14:34 +08:00
Dr_rOot 6baef42274 Merge pull request #953 from shatyuka/macos_vibrancy
feat: add vibrancy sidebar for macos
2021-05-18 21:06:48 +08:00
Shatyuka f4cc731e63 feat: add vibrancy sidebar for macos 2021-05-18 20:54:40 +08:00
rubjo 480039c4cc feat: Added Norwegian Bokmål (#952)
* feat: Added Norwegian Bokmål (nb-NO)

* fix: Escape apstrophe

* refactor: Rename from nb-NO to nb

* fix: Learn alphabet
2021-05-18 17:07:26 +08:00
Dr_rOot b0242d3d5b chore: bump version v1.6.11 2021-05-15 22:30:00 +08:00
Dr_rOot 58cfd2b873 Merge pull request #944 from agalwood/feature/improve_detail_20210513
feat: improve task detail
2021-05-15 22:27:00 +08:00
Dr_rOot 804f864709 chore: update deps 2021-05-15 22:12:19 +08:00
Dr_rOot 57fb4d95ae feat: get file selection util 2021-05-15 21:59:01 +08:00
Dr_rOot 8030699e15 chore: i18n task select at least one 2021-05-15 21:58:22 +08:00
Dr_rOot 1185a81c0c chore: i18n save & reset 2021-05-15 21:45:41 +08:00
Dr_rOot aa4941f842 feat: save task file selection 2021-05-15 21:44:21 +08:00
Dr_rOot 841bd8b923 fix: task detail activity graphic align center 2021-05-13 21:52:57 +08:00
Dr_rOot 85ae4cdbca Merge pull request #942 from agalwood/hotfix/auto_update_20210511
fix: some bug
2021-05-12 23:34:25 +08:00
Dr_rOot c257816608 fix: element ui message z-index 2021-05-12 22:32:53 +08:00
Dr_rOot 1b44ef725b fix: task detail not auto hide when remove task 2021-05-12 11:47:18 +08:00
Dr_rOot bffe919b93 fix: add one task triggered rename rule 2021-05-12 11:24:51 +08:00
Dr_rOot 87635ade34 fix: auto theme 2021-05-12 11:24:15 +08:00
Dr_rOot c6a9eb226d fix: auto update exception 2021-05-11 15:18:21 +08:00
Dr_rOot da2b6638d9 Merge pull request #939 from agalwood/feature/lab_dark_20210509
feat: preference lab panel dark theme
2021-05-11 15:06:22 +08:00
Dr_rOot 91072509d3 refactor: mo icon 2021-05-09 22:12:47 +08:00
Dr_rOot 0d75370f95 fix: drawer header close icon could not click 2021-05-09 22:12:21 +08:00
Dr_rOot 994d351998 feat: lab page dark theme 2021-05-09 12:07:15 +08:00
Dr_rOot 166aba7747 chore: bump version v1.6.10 2021-05-07 23:22:26 +08:00
Dr_rOot bc3ea97780 Merge pull request #928 from agalwood/hotfix/app_style_20210507
fix: app style
2021-05-07 22:37:43 +08:00
Dr_rOot 2beb6f14a8 chore: update app info icon 2021-05-07 21:49:32 +08:00
Dr_rOot 5d8566a934 feat: mock ua add more ua options 2021-05-07 21:36:52 +08:00
Dr_rOot 84b002d513 feat: add more speed options 2021-05-07 21:25:29 +08:00
Dr_rOot a4fb082088 refactor: style code format 2021-05-07 21:25:04 +08:00
Dr_rOot 3aa18e7f72 fix: revert mac target 2021-05-07 19:19:10 +08:00
Dr_rOot e939f9e5dc Merge pull request #925 from agalwood/hotfix/app_hang_20210507 2021-05-07 16:40:04 +08:00
Dr_rOot 53ec0b1dee chore: update deps 2021-05-07 14:11:34 +08:00
Dr_rOot fcfd32a71e refactor: task detail style 2021-05-07 14:06:35 +08:00
Dr_rOot 2ff56a3770 refactor: task item style 2021-05-07 14:06:28 +08:00
Dr_rOot 48768f0658 fix: copyright year 2021-05-07 14:03:32 +08:00
Dr_rOot 3e84230b33 fix: app reset engine not start 2021-05-07 14:03:13 +08:00
Dr_rOot 5490946267 fix: app hang caused by child_process spawn
https://github.com/electron/electron/issues/24329#issuecomment-760699187
2021-05-07 14:02:48 +08:00
Dr_rOot 9461381042 chore: bump version v1.6.8 2021-05-06 15:30:01 +08:00
Dr_rOot 364d7a8f66 chore: lock electron-builder version to 22.10.5 2021-05-06 15:27:39 +08:00
Dr_rOot 7a5a1554ca chore: bump version & electron-builder -> 22.10.5 2021-05-06 14:58:50 +08:00
Dr_rOot 8410be59b2 chore: bump version 1.6.6 2021-05-06 14:19:22 +08:00
Dr_rOot 795db0b926 chore: github action build force release 2021-05-06 14:18:30 +08:00
Dr_rOot 079cab8544 chore: bump version v1.6.5 2021-05-06 13:57:32 +08:00
Dr_rOot 7109747e00 chore: update github actions 2021-05-06 13:56:57 +08:00
Dr_rOot 9b698f5a0e chore: bump version v1.6.3 2021-05-06 12:05:25 +08:00
Dr_rOot 2b35fb9bc2 chore: bump version v1.6.1 2021-05-06 12:02:41 +08:00
Dr_rOot 2b80127ad0 docs: update readme i18n 2021-05-06 12:01:57 +08:00
Dr_rOot d47fa3d705 chore: bump version to 1.6.0 2021-05-06 11:24:16 +08:00
Dr_rOot 555db61ec0 Merge pull request #922 from agalwood/hotfix/theme_style_20210506
refactor: task detail dark theme style
2021-05-06 11:09:57 +08:00
Dr_rOot 7c2fc774ca chore: update deps 2021-05-06 10:48:31 +08:00
Dr_rOot 8eeab3d3fa feat: task graphic dark theme style 2021-05-06 10:36:33 +08:00
Dr_rOot 7a5b16aecc refactor: task status theme 2021-05-06 10:36:02 +08:00
Dr_rOot c5f72414e2 refactor: preference store actions 2021-05-06 10:35:26 +08:00
Dr_rOot 4016f5c02e Merge pull request #921 from agalwood/feature/task_detail_20210503 2021-05-05 23:20:09 +08:00
Dr_rOot af7eb1f359 Merge branch 'master' into feature/task_detail_20210503 2021-05-05 23:08:19 +08:00
Dr_rOot f30ed3c8f7 fix: update task peers table column width 2021-05-05 22:56:44 +08:00
Dr_rOot e9e86fbc83 refactor: task detail tab content scrollable 2021-05-05 22:36:14 +08:00
Dr_rOot d7b985bc3f docs: update app features 2021-05-05 16:54:40 +08:00
Dr_rOot e307240a60 fix: remove useless code 2021-05-05 16:52:28 +08:00
Dr_rOot 8dd1d84485 chore: i18n task detail 2021-05-05 16:51:12 +08:00
Dr_rOot 3c7b0b26e3 chore: i18n bt settings 2021-05-05 16:26:01 +08:00
Dr_rOot 778e092c17 docs: update contributing locale guide 2021-05-05 15:54:32 +08:00
Dr_rOot 6e462166d1 chore: i18n task files 2021-05-05 11:35:30 +08:00
Dr_rOot 812b419379 chore: update task list i18n 2021-05-05 10:39:21 +08:00
Dr_rOot 04dcddd3e1 docs: update translation contributors 2021-05-05 09:45:23 +08:00
أحمد الطبراني 1cfec289b7 fix: Complete translation into Arabic (#919)
* refactor: Complete translation into Arabic

* refactor: Complete translation into Arabic

* Update preferences.js
2021-05-05 09:41:03 +08:00
Dr_rOot 2dcfb8c25e refactor: change some log 2021-05-04 20:38:47 +08:00
Dr_rOot 482b9312b1 chore: i18n task detail 2021-05-04 15:30:16 +08:00
Dr_rOot dbb1889fc9 feat: task detail 2021-05-04 15:06:44 +08:00
Dr_rOot a6de12c875 feat: task activity 2021-05-04 15:04:02 +08:00
Dr_rOot d14ddef8e8 feat: task peers 2021-05-04 15:03:33 +08:00
Dr_rOot c8698e5b80 refactor: task files component 2021-05-04 15:03:10 +08:00
Dr_rOot e8c99caf87 feat: task trackers 2021-05-04 14:59:31 +08:00
Dr_rOot f70595915d feat: task general info 2021-05-04 14:58:39 +08:00
Dr_rOot c5962041cb feat: task bitfield graphic 2021-05-04 14:55:39 +08:00
Dr_rOot 28bbf26334 refactor: select torrent file list 2021-05-03 20:00:07 +08:00
Dr_rOot 3d6931f8d8 refactor: main menu quit 2021-05-03 19:43:14 +08:00
Dr_rOot 46d686bee5 fix: remove file to trash for electron v11.x 2021-05-03 19:41:41 +08:00
Dr_rOot 9ffaf1116f Merge pull request #915 from agalwood/feature/bt_config_202104231109 2021-05-03 19:19:57 +08:00
Dr_rOot 2d4a05f3b0 chore: update deps lock 2021-05-03 19:01:54 +08:00
Dr_rOot e04d3a582e fix: store task checkTaskIsBT 2021-05-03 18:51:12 +08:00
Hadi Alqattan 943830f2ed feat: Add Arabic Localization
* Partial Arabic Language support!

* Partial Arabic Language support!

* translate more words...
2021-05-03 18:35:58 +08:00
Dr_rOot 137927d44a chore: revert electron from 12.x to 11.x 2021-05-03 18:33:29 +08:00
Dr_rOot 819f86632e feat: task progress info show seeder number 2021-05-03 18:18:55 +08:00
Dr_rOot 06f881932c refactor: dev env add engine log 2021-05-03 18:17:41 +08:00
Dr_rOot cb845ea65a refactor: update pause task speed use force pause 2021-05-03 18:15:24 +08:00
Dr_rOot 60e4907be6 fix: stop seeding params gid 2021-04-26 15:00:59 +08:00
Dr_rOot bc38978d6a refactor: open url external 2021-04-23 17:37:16 +08:00
Dr_rOot 90c3bbff13 fix: deps electron remote 2021-04-23 17:36:45 +08:00
Dr_rOot 3116c53734 refactor: electron remote 2021-04-23 15:12:40 +08:00
Dr_rOot 78fb6ba455 refactor: css-minimizer-webpack-plugin
replace css-minimizer-webpack-plugin with css-minimizer-webpack-plugin
2021-04-23 14:51:41 +08:00
Dr_rOot 86304c126d chore: update deps 2021-04-23 11:48:56 +08:00
Dr_rOot 3424424e67 feat: preference bt setting bt-save-metadata 2021-04-23 11:39:34 +08:00
Dr_rOot 7206f2fc3c Merge pull request #892 from agalwood/feature/bt_202103131952
feat: new app icon & bt setting
2021-04-01 23:39:57 +08:00
Dr_rOot bd25e566c8 chore: i18n zh-TW bt keep seeding 2021-03-24 23:26:02 +08:00
Dr_rOot f54bc32e90 feat: bt keep seeding 2021-03-24 23:23:04 +08:00
Dr_rOot f6dde55234 feat: new app icon 2021-03-13 21:14:43 +08:00
Dr_rOot 1303713f5e feat: reset session 2021-03-13 20:08:14 +08:00
Dr_rOot 57a5c0c932 Merge pull request #877 from agalwood/feature/engine_refactor_202102182037
refactor: replace forever with child process
2021-03-13 19:50:43 +08:00
Dr_rOot 22a41c332f Merge branch 'master' into feature/engine_refactor_202102182037
# Conflicts:
#	yarn.lock
2021-03-13 18:37:25 +08:00
Dr_rOot 76e7223e85 Merge pull request #875 from agalwood/dependabot/npm_and_yarn/elliptic-6.5.4
chore(deps): bump elliptic from 6.5.3 to 6.5.4
2021-03-13 18:35:15 +08:00
Dr_rOot 014619ed46 chore: update deps 2021-03-13 18:29:02 +08:00
Dr_rOot aec3a25e3a chore: engine error code text remove # 2021-03-13 11:23:57 +08:00
dependabot[bot] 02f6ffa3cb chore(deps): bump elliptic from 6.5.3 to 6.5.4
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 07:16:59 +00:00
Dr_rOot 5d32fc633c feat: add more speed options 2021-03-01 11:12:49 +08:00
Dr_rOot cde2832ae3 chore: aria2 conf http accept gzip 2021-03-01 11:10:02 +08:00
Dr_rOot 8656fa0862 refactor: replace forever with child process 2021-03-01 11:09:14 +08:00
Dr_rOot 0cd1ed1e34 Merge pull request #865 from agalwood/feature/tracker_cdn_202102181833
feat: add tracker cdn source
2021-02-18 20:31:17 +08:00
Dr_rOot d58e485846 refactor: auto sync tracker 2021-02-18 19:03:19 +08:00
Dr_rOot a6aca44672 refactor: preference tracker cdn source select 2021-02-18 18:41:57 +08:00
Dr_rOot cc28e4b19b chore: replace default tracker source config 2021-02-18 18:41:14 +08:00
Dr_rOot 599dec1d6e feat: add tracker cdn urls 2021-02-18 18:35:10 +08:00
Dr_rOot d020359058 Merge pull request #851 from upuppz/master
fix: Function overload error, unable to trigger events bound in EngineClient.vue
2021-01-14 20:23:02 +08:00
upuppz 04d68e293b fix: Function overload error, unable to trigger events bound in EngineClient.vue(函数重载错误, 无法触发在 EngineClient.vue 中绑定的事件) 2021-01-12 18:25:48 +08:00
upuppz 6b93a80888 Merge pull request #4 from agalwood/master
Sync
2021-01-12 18:20:49 +08:00
Dr_rOot c3400d936e Merge pull request #844 from alyn3d/master
feat: Added Romanian translation
2021-01-05 11:12:11 +08:00
alyn3d 871be123d4 feat: Added Romanian translation 2021-01-03 00:29:01 +02:00
Dr_rOot bed2148ba0 Merge pull request #843 from agalwood/hotfix/tray_retina_202012292116
refactor: dynamic tray scale
2020-12-29 21:24:16 +08:00
Dr_rOot 8b97c90e88 refactor: dynamic tray scale 2020-12-29 21:16:36 +08:00
Dr_rOot 832befbafd Merge pull request #842 from agalwood/hotfix/style_202012291454
fix: some style improve
2020-12-29 21:05:31 +08:00
Dr_rOot ddbf27e2f9 refactor: form actions position sticky 2020-12-29 14:55:57 +08:00
Dr_rOot 64b30b6c26 fix: task progress info text font style normal 2020-12-29 14:55:38 +08:00
Dr_rOot 32c8767e65 Merge pull request #840 from agalwood/hotfix/multispinner_202012282315
chore: replace multispinner deps
2020-12-28 23:27:48 +08:00
Dr_rOot 69391c6de0 chore: update deps 2020-12-28 23:21:47 +08:00
Dr_rOot fbec4e7c4d Merge pull request #839 from shatyuka/big_sur_tray
fix: big sur tray color
2020-12-28 23:14:13 +08:00
Shatyuka 7640ba583d fix: big sur tray color 2020-12-28 19:45:53 +08:00
Dr_rOot 924b397727 Merge pull request #838 from agalwood/hotfix/tray_workder_202012271447
fix: tray worker cannot find module 'lodash'
2020-12-27 21:45:06 +08:00
Dr_rOot 565afdde74 chore: update deps & change npm to yarn 2020-12-27 20:07:53 +08:00
Dr_rOot 3dfd4ec4da fix: tray worker cannot find module 'lodash' 2020-12-27 14:47:55 +08:00
Dr_rOot 0a21f590ff Merge pull request #836 from shatyuka/apple_silicon
feat: Support Apple Silicon
2020-12-27 14:45:37 +08:00
カワリミ人形 79bf90bb3c docs: Fix installation instructions in README.md (#834) 2020-12-26 21:09:55 +08:00
Shatyuka f4602b4b68 fix: import JSONRPCError 2020-12-26 13:00:29 +08:00
Shatyuka 93a6f1cfbd fix: Shadow not complete 2020-12-26 12:00:08 +08:00
Shatyuka f0d14afb7e chore: update dependency sass-loader 2020-12-26 11:10:02 +08:00
Shatyuka d9a69ae009 fix: use iframe instead of webview 2020-12-26 09:29:26 +08:00
Shatyuka 0f5398b106 fix: engine failed to quit 2020-12-26 09:03:38 +08:00
Shatyuka c6eb96547a fix: tray menu crash 2020-12-26 07:39:00 +08:00
Shatyuka 31ab487d82 feat: Add native arm support for macos aria2
universal binary
2020-12-26 06:03:36 +08:00
Shatyuka 0b2e271663 feat: Update electron to 11 to support Apple Silicon
Update almost all of the dependencies.
This commit only promises the app would be successfully built and run.
2020-12-26 00:09:44 +08:00
jimman2003 a4a4b2321f fix: updated axios and forever-monitor to get rid of error in dev enviroment (#818) 2020-12-09 20:51:18 +08:00
XIU2 ef372da87b fix: replace TrackersListCollection other.txt with http.txt (#809) 2020-11-25 13:59:00 +08:00
Dr_rOot 10cab1a9a0 docs: readme i18n add Italiano 2020-10-09 21:08:55 +08:00
blackcat-917 a4a651b397 feat: Added Italian language (#794)
* Feat: added italian

* Small fix

* Update index.js to match to the original indent style
2020-10-09 20:56:06 +08:00
Dr_rOot b2770795ad docs: i18n add el (Greek) 2020-08-30 13:22:17 +08:00
Akis S f4c8ff66d5 feat: Added Greek language (#774)
* feat: Added Greek language

* Update index.js
2020-08-30 13:17:49 +08:00
Dr_rOot 1b450c8022 docs: i18n add missing locales es & hu 2020-08-04 21:52:14 +08:00
zalnars e5b8846286 feat: Added Hungarian language (#754)
* Added Hungarian language

* Update all.js

* Update app.js

* Update all.js
2020-07-26 21:06:19 +08:00
Samuel Martineau 8de8591725 fix: correction of the french locale (#742)
* Correction of the French locale

- `transfer-speed-upload`and `transfer-speed-download` were the same
- `transfer-speed-unlimited` was still in English

* fix: correction of the french locale

* fix: correction of the french locale
2020-07-15 20:55:17 +08:00
Dr_rOot 20c30279b0 docs: update readme cn 2020-07-04 21:43:00 +08:00
Stanisław Nieradko 9000be502b feat: Added Polish (#731)
* Added Polish

* Sorted imports in src/shared/locales/app.js
2020-07-04 21:36:34 +08:00
Dr_rOot cfe66cf337 Merge pull request #720 from agalwood/feature/tray_speedometer_202006211216
feat: menu bar tray speedometer #643
2020-06-21 21:30:51 +08:00
Dr_rOot 3557d17bb6 chore: update deps, upgrade electron to 9.x
> That was a bug in Chromium, which got fixed by this [commit](https://chromium.googlesource.com/chromium/src/+/09514b7fbd4fb14ce12a43bc7f4807179612fa94) (available in Chrome v83).

https://stackoverflow.com/questions/61268950/offscreen-converttoblob-very-slow
2020-06-21 21:10:22 +08:00
Dr_rOot 2af891aab8 chore: i18n tray speedometer 2020-06-21 16:23:07 +08:00
Dr_rOot 0d276de39b feat: preference tray speedometer setting 2020-06-21 16:12:35 +08:00
Dr_rOot 8a6beda335 feat: tray speedometer 2020-06-21 16:12:04 +08:00
Dr_rOot 68f1cdc4de chore: update copyright year 2020-06-21 15:30:28 +08:00
Dr_rOot 5290fcfa14 feat: dockmanager add handleSpeedChange 2020-06-21 15:21:06 +08:00
Dr_rOot d865b630a3 refactor: rename enable args 2020-06-21 15:19:50 +08:00
Dr_rOot 9d95d294cd refactor: rename store actions mutations 2020-06-21 13:47:02 +08:00
Dr_rOot ea46d9b3c6 refactor: load config fn use ipc invoke 2020-06-21 12:33:28 +08:00
Dr_rOot ef2c992af9 refactor: remove deprecated fns 2020-06-21 12:30:43 +08:00
Dr_rOot 64ee097c85 Merge pull request #718 from agalwood/feature/stop_seeding_202006201236
fix: stop bt task seeding notification #604
2020-06-20 18:29:48 +08:00
Dr_rOot 31517f93cb chore: i18n stopping seeding tip 2020-06-20 15:51:24 +08:00
Dr_rOot 6d54e557b9 fix: oddly stop seeding notify #604 2020-06-20 15:50:18 +08:00
Dr_rOot 3e61adcea3 Merge pull request #716 from agalwood/feature/url_protocol_202005290826
refactor: redesign motrix scheme
2020-06-20 12:34:52 +08:00
Dr_rOot 84d9ced137 Merge pull request #714 from agalwood/hotfix/hide_run_mode_202006192140
fix: preference show run mode only in macOS
2020-06-19 23:01:36 +08:00
Dr_rOot bc45ed9d5b refactor: add jsconfig 2020-06-19 21:44:53 +08:00
Dr_rOot 3d98104dbf fix: preference show run mode only in macOS 2020-06-19 21:42:00 +08:00
Dr_rOot fb6986ff6e docs: update readme app icon 2020-06-19 20:55:07 +08:00
Dr_rOot b8507570c6 refactor: rename initialForm to initForm 2020-06-12 17:22:38 +08:00
Dr_rOot cf3ef7606c fix: new task uri 2020-06-06 23:31:24 +08:00
Dr_rOot 12a9fa92c1 chore: bump version 2020-06-05 23:54:14 +08:00
Dr_rOot 0fd6617eba docs: update readme i18n 2020-06-05 23:52:34 +08:00
Dr_rOot 7ebbf929d5 fix: i18n no-confirm-before-delete-task missed 2020-06-05 23:49:27 +08:00
Dr_rOot 14223c2204 chore: bump version 2020-06-05 23:26:11 +08:00
Dr_rOot 2cfb6b1914 Merge pull request #689 from agalwood/hotfix/handle_url_202006051331
fix: open app with url handle fail
2020-06-05 14:04:27 +08:00
Dr_rOot c38cf80589 chore: update deps 2020-06-05 13:40:30 +08:00
Dr_rOot 3ee98eae1d fix: open app with resource url 2020-06-05 13:33:22 +08:00
Dr_rOot d2cff6356a Merge pull request #686 from NickoAilus/master
fix: edited Russian locale
2020-06-01 22:12:46 +08:00
NickoAilus 3ee432d683 Fixed Russian locale 2020-06-01 16:10:36 +03:00
Dmitry Kalinin 7f1822bb7e feat: Added bulgarian (bg) translations (#685)
* Added bulgarian (bg) translations

* Improve structure

* Fixed bugs

* Deleted comma
2020-06-01 17:33:46 +08:00
Dr_rOot 0223e691ff docs: readme i18n add vi 2020-05-31 22:57:56 +08:00
Duy–Thanh Doan 117dba9f37 feat: Add vietnamese translation (#680)
* Add vietnamese translation

* Update app.js and index.js

* Translated about.js

* Translated app.js

* first draft

* final draft

* final update - ready to review

* Update format
2020-05-31 22:52:26 +08:00
Dr_rOot e5241d09d7 refactor: protocol manager init 2020-05-29 17:28:03 +08:00
Dr_rOot c9ea1dece2 refactor: motrix url protocol 2020-05-29 17:02:43 +08:00
Dr_rOot 60d4108ddc refactor: promise all to allSettled 2020-05-29 17:00:47 +08:00
Dr_rOot ffc8de4766 refactor: add task utils 2020-05-29 16:58:35 +08:00
Dr_rOot 66f114bf72 Merge pull request #674 from agalwood/hotfix/upnp_cb_202005271904
fix: nat-api autoUpdate cb is not a function
2020-05-28 15:28:28 +08:00
Dr_rOot 44f00483f9 chore: update deps 2020-05-28 14:42:43 +08:00
Dr_rOot 1866e3fd4f fix: nat-api autoUpdate cb is not a function
replace nat-api to @motrix/nat-api

@motrix/nat-api forked from https://github.com/alxhotel/nat-api
2020-05-28 14:42:05 +08:00
Dr_rOot cfac883cbf chore: remove deprecated vue-html-loader 2020-05-28 12:11:30 +08:00
Dr_rOot 1431bab366 Merge pull request #670 from agalwood/hotfix/full_screen_preference_202005271103
fix: full screen save preference issue #663
2020-05-27 11:20:23 +08:00
Dr_rOot bb373947ff fix: full screen save preference issue #663 2020-05-27 11:06:51 +08:00
Dr_rOot 8f0dc65341 refactor: rename auto hide window 2020-05-27 11:06:31 +08:00
335 changed files with 19676 additions and 20187 deletions
+14 -12
View File
@@ -5,10 +5,8 @@ process.env.NODE_ENV = 'production'
const { say } = require('cfonts')
const chalk = require('chalk')
const del = require('del')
const { spawn } = require('child_process')
const webpack = require('webpack')
const Multispinner = require('multispinner')
const Webpack = require('webpack')
const Multispinner = require('@motrix/multispinner')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
@@ -77,7 +75,7 @@ function build () {
function pack (config) {
return new Promise((resolve, reject) => {
config.mode = 'production'
webpack(config, (err, stats) => {
Webpack(config, (err, stats) => {
if (err) {
reject(err.stack || err)
} else if (stats.hasErrors()) {
@@ -104,9 +102,9 @@ function pack (config) {
}
function web () {
del.sync(['dist/web/*', '!.gitkeep'])
deleteSync(['dist/web/*', '!.gitkeep'])
webConfig.mode = 'production'
webpack(webConfig, (err, stats) => {
Webpack(webConfig, (err, stats) => {
if (err || stats.hasErrors()) console.log(err)
console.log(stats.toString({
@@ -122,16 +120,20 @@ function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 85) text = 'lets-build'
else if (cols > 60) text = 'lets-|build'
else text = false
if (cols > 85) {
text = 'lets-build'
} else if (cols > 60) {
text = 'lets-|build'
} else {
text = false
}
if (text && !isCI) {
say(text, {
colors: ['yellow'],
colors: ['magentaBright'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n lets-build'))
} else console.log(chalk.magentaBright.bold('\n lets-build'))
console.log()
}
+25 -43
View File
@@ -5,17 +5,14 @@ const electron = require('electron')
const path = require('path')
const { say } = require('cfonts')
const { spawn } = require('child_process')
const webpack = require('webpack')
const Webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const webpackHotMiddleware = require('webpack-hot-middleware')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
let electronProcess = null
let manualRestart = false
let hotMiddleware
function logStats (proc, data) {
let log = ''
@@ -40,41 +37,22 @@ function logStats (proc, data) {
}
function startRenderer () {
return new Promise((resolve, reject) => {
rendererConfig.entry.index = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.index)
return new Promise(async (resolve, reject) => {
rendererConfig.entry.index = rendererConfig.entry.index
rendererConfig.mode = 'development'
const compiler = webpack(rendererConfig)
hotMiddleware = webpackHotMiddleware(compiler, {
log: false,
heartbeat: 2500
})
compiler.hooks.compilation.tap('compilation', compilation => {
HtmlWebpackPlugin.getHooks(compilation).afterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
const compiler = Webpack(rendererConfig)
const devServerOptions = {
...rendererConfig.devServer,
port: 9080,
static: {
directory: path.resolve(__dirname, "../"),
},
};
compiler.hooks.done.tap('done', stats => {
logStats('Renderer', stats)
})
const server = new WebpackDevServer(
compiler,
{
contentBase: path.join(__dirname, '../'),
quiet: true,
before (app, ctx) {
app.use(hotMiddleware)
ctx.middleware.waitUntilValid(() => {
resolve()
})
}
}
)
server.listen(9080)
const server = new WebpackDevServer(devServerOptions, compiler)
await server.start()
resolve()
})
}
@@ -82,11 +60,11 @@ function startMain () {
return new Promise((resolve, reject) => {
mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
mainConfig.mode = 'development'
const compiler = webpack(mainConfig)
const compiler = Webpack(mainConfig)
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
logStats('Main', chalk.white.bold('compiling...'))
hotMiddleware.publish({ action: 'compiling' })
// hotMiddleware.publish({ action: 'compiling' })
done()
})
@@ -150,17 +128,21 @@ function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 104) text = 'electron-vue'
else if (cols > 76) text = 'electron-|vue'
else text = false
if (cols > 104) {
text = 'motrix-dev'
} else if (cols > 76) {
text = 'motrix-|dev'
} else {
text = false
}
if (text) {
say(text, {
colors: ['yellow'],
colors: ['magentaBright'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n electron-vue'))
} else console.log(chalk.magentaBright.bold('\n motrix-dev'))
console.log(chalk.blue(' getting ready...') + '\n')
}
+12 -18
View File
@@ -4,9 +4,11 @@ process.env.BABEL_ENV = 'main'
const devMode = process.env.NODE_ENV !== 'production'
const path = require('path')
const { dependencies, build } = require('../package.json')
const webpack = require('webpack')
const { dependencies } = require('../package.json')
const { appId } = require('../electron-builder.json')
const Webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');
let mainConfig = {
entry: {
@@ -17,17 +19,6 @@ let mainConfig = {
],
module: {
rules: [
{
test: /\.(js)$/,
enforce: 'pre',
exclude: /node_modules/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}
},
{
test: /\.js$/,
use: 'babel-loader',
@@ -49,7 +40,10 @@ let mainConfig = {
path: path.join(__dirname, '../dist/electron')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
new Webpack.NoEmitOnErrorsPlugin(),
new ESLintPlugin({
formatter: require('eslint-friendly-formatter')
})
],
resolve: {
alias: {
@@ -74,9 +68,9 @@ let mainConfig = {
*/
if (devMode) {
mainConfig.plugins.push(
new webpack.DefinePlugin({
new Webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`,
'appId': `"${build.appId}"`
'appId': `"${appId}"`
})
)
}
@@ -86,9 +80,9 @@ if (devMode) {
*/
if (!devMode) {
mainConfig.plugins.push(
new webpack.DefinePlugin({
new Webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"',
'appId': `"${build.appId}"`
'appId': `"${appId}"`
})
)
}
+29 -62
View File
@@ -5,13 +5,14 @@ process.env.BABEL_ENV = 'renderer'
const devMode = process.env.NODE_ENV !== 'production'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const Webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const ESLintPlugin = require('eslint-webpack-plugin');
/**
* List of node_modules to include in webpack bundle
@@ -23,7 +24,6 @@ const { VueLoaderPlugin } = require('vue-loader')
let whiteListedModules = ['vue']
let rendererConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
index: path.join(__dirname, '../src/renderer/pages/index/main.js')
},
@@ -33,14 +33,10 @@ let rendererConfig = {
module: {
rules: [
{
test: /\.(js|vue)$/,
enforce: 'pre',
exclude: /node_modules/,
test: /\.worker\.js$/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
loader: 'worker-loader',
options: { filename: '[name].js' }
}
},
{
@@ -52,7 +48,7 @@ let rendererConfig = {
loader: 'sass-loader',
options: {
implementation: require('sass'),
prependData: '@import "@/components/Theme/Variables.scss";',
additionalData: '@import "@/components/Theme/Variables.scss";',
sassOptions: {
includePaths:[__dirname, 'src']
}
@@ -70,7 +66,7 @@ let rendererConfig = {
options: {
implementation: require('sass'),
indentedSyntax: true,
prependData: '@import "@/components/Theme/Variables.scss";',
additionalData: '@import "@/components/Theme/Variables.scss";',
sassOptions: {
includePaths:[__dirname, 'src']
}
@@ -93,10 +89,6 @@ let rendererConfig = {
'css-loader'
]
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
@@ -122,31 +114,15 @@ let rendererConfig = {
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]'
}
}
type: 'asset/inline'
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name]--[folder].[ext]'
}
type: 'asset/resource'
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]'
}
}
type: 'asset/inline'
}
]
},
@@ -160,45 +136,35 @@ let rendererConfig = {
filename: '[name].css',
chunkFilename: '[id].css'
}),
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true,
discardComments: { removeAll: true }
}
}),
new HtmlWebpackPlugin({
title: 'Motrix',
filename: 'index.html',
chunks: ['index'],
template: path.resolve(__dirname, '../src/index.ejs'),
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options
},
process
}
},
// minify: {
// collapseWhitespace: true,
// removeAttributeQuotes: true,
// removeComments: true
// },
isBrowser: false,
isDev: process.env.NODE_ENV !== 'production',
nodeModules: devMode
? path.resolve(__dirname, '../node_modules')
: false
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
new Webpack.HotModuleReplacementPlugin(),
new Webpack.NoEmitOnErrorsPlugin(),
new ESLintPlugin({
extensions: ['js', 'vue'],
formatter: require('eslint-friendly-formatter')
})
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
path: path.join(__dirname, '../dist/electron'),
globalObject: 'this',
publicPath: ''
},
resolve: {
alias: {
@@ -214,7 +180,8 @@ let rendererConfig = {
minimizer: [
new TerserPlugin({
extractComments: false,
})
}),
new CssMinimizerPlugin(),
],
},
}
@@ -223,8 +190,10 @@ let rendererConfig = {
* Adjust rendererConfig for development settings
*/
if (devMode) {
rendererConfig.devtool = 'eval-cheap-module-source-map'
rendererConfig.plugins.push(
new webpack.DefinePlugin({
new Webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
@@ -234,8 +203,6 @@ if (devMode) {
* Adjust rendererConfig for production settings
*/
if (!devMode) {
rendererConfig.devtool = ''
rendererConfig.plugins.push(
new CopyWebpackPlugin({
patterns: [{
@@ -244,10 +211,10 @@ if (!devMode) {
globOptions: { ignore: [ '.*' ] }
}]
}),
new webpack.DefinePlugin({
new Webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
new Webpack.LoaderOptionsPlugin({
minimize: false
})
)
+33 -53
View File
@@ -5,13 +5,14 @@ process.env.BABEL_ENV = 'web'
const devMode = process.env.NODE_ENV !== 'production'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const Webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const ESLintPlugin = require('eslint-webpack-plugin');
/**
* List of node_modules to include in webpack bundle
@@ -23,7 +24,6 @@ const { VueLoaderPlugin } = require('vue-loader')
let whiteListedModules = ['vue']
let webConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
index: path.join(__dirname, '../src/renderer/pages/index/main.js')
},
@@ -33,14 +33,10 @@ let webConfig = {
module: {
rules: [
{
test: /\.(js|vue)$/,
enforce: 'pre',
exclude: /node_modules/,
test: /\.worker\.js$/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
loader: 'worker-loader',
options: { filename: '[name].js' }
}
},
{
@@ -52,7 +48,7 @@ let webConfig = {
loader: 'sass-loader',
options: {
implementation: require('sass'),
prependData: '@import "@/components/Theme/Variables.scss";',
additionalData: '@import "@/components/Theme/Variables.scss";',
sassOptions: {
includePaths:[__dirname, 'src']
}
@@ -70,7 +66,7 @@ let webConfig = {
options: {
implementation: require('sass'),
indentedSyntax: true,
prependData: '@import "@/components/Theme/Variables.scss";',
additionalData: '@import "@/components/Theme/Variables.scss";',
sassOptions: {
includePaths:[__dirname, 'src']
}
@@ -115,23 +111,11 @@ let webConfig = {
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name].[ext]'
}
}
type: 'asset/inline'
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
}
type: 'asset/inline'
}
]
},
@@ -141,47 +125,37 @@ let webConfig = {
filename: '[name].css',
chunkFilename: '[id].css'
}),
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true,
discardComments: { removeAll: true }
}
}),
new HtmlWebpackPlugin({
title: 'Motrix',
filename: 'index.html',
chunks: ['index'],
template: path.resolve(__dirname, '../src/index.ejs'),
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options
},
process
}
},
// minify: {
// collapseWhitespace: true,
// removeAttributeQuotes: true,
// removeComments: true
// },
isBrowser: true,
isDev: process.env.NODE_ENV !== 'production',
nodeModules: devMode
? path.resolve(__dirname, '../node_modules')
: false
}),
new webpack.DefinePlugin({
new Webpack.DefinePlugin({
'process.env.IS_WEB': 'true'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
new Webpack.HotModuleReplacementPlugin(),
new Webpack.NoEmitOnErrorsPlugin(),
new ESLintPlugin({
extensions: ['js', 'vue'],
formatter: require('eslint-friendly-formatter')
})
],
output: {
filename: '[name].js',
path: path.join(__dirname, '../dist/web')
path: path.join(__dirname, '../dist/web'),
globalObject: 'this',
publicPath: ''
},
resolve: {
alias: {
@@ -197,17 +171,23 @@ let webConfig = {
minimizer: [
new TerserPlugin({
extractComments: false,
})
}),
new CssMinimizerPlugin(),
],
},
}
/**
* Adjust webConfig for development settings
*/
if (devMode) {
webConfig.devtool = 'eval-cheap-module-source-map'
}
/**
* Adjust webConfig for production settings
*/
if (!devMode) {
webConfig.devtool = ''
webConfig.plugins.push(
new CopyWebpackPlugin({
patterns: [{
@@ -216,10 +196,10 @@ if (!devMode) {
globOptions: { ignore: [ '.*' ] }
}]
}),
new webpack.DefinePlugin({
new Webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
new Webpack.LoaderOptionsPlugin({
minimize: true
})
)
+76
View File
@@ -0,0 +1,76 @@
name: 🐛 [NEW] Bug Report
description: File a bug report here
title: "[BUG]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report 🤗
Make sure there aren't any open/closed issues for this topic 😃
- type: textarea
id: bug-description
attributes:
label: Description of the bug
description: Give us a brief description of what happened and what should have happened
validations:
required: true
- type: textarea
id: app-version
attributes:
label: Motrix Version
description: Please provide detailed version information and installation method, such as macOS Apple silicon dmg, Windows Universal installation file, etc.
validations:
required: true
- type: textarea
attributes:
label: Environment
description: |
Run this command in your project's root folder and paste the result:
```sh
npx envinfo --system --binaries --browsers
```
add `| pbcopy` if you're in macOS for easy copy paste.
Alternatively, you can manually gather the version information from your environment.
validations:
required: true
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
More info: A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is **required**, otherwise the issue might be closed without further notice. [**Why & How?**](https://antfu.me/posts/why-reproductions-are-required)
validations:
required: true
- type: textarea
id: additional-information
attributes:
label: Additional Information
description: |
Provide any additional information such as logs, screenshots, likes, scenarios in which the bug occurs so that it facilitates resolving the issue.
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Follow our [Code of Conduct](https://github.com/agalwood/Motrix/blob/master/CODE_OF_CONDUCT.md)
required: true
- label: Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
required: true
- label: Check that this is a concrete bug. For Q&A, please open a [GitHub Discussion](https://github.com/agalwood/Motrix/discussions) instead.
required: true
- label: The provided reproduction is a [minimal reproducible](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
required: true
@@ -0,0 +1,76 @@
name: 🐛 [新] Bug 报告
description: 在这里提交 Bug 报告
title: "[BUG]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
感谢您抽出时间来填写这份 Bug 报告 🤗
请确保此问题没有已存在的开放/关闭问题 😃
- type: textarea
id: bug-description
attributes:
label: Bug 描述
description: 给我们一个简短的描述,说明发生了什么以及应该发生什么。
validations:
required: true
- type: textarea
id: app-version
attributes:
label: Motrix 版本
description: 请提供详细的版本信息以及安装的方式,如 macOS Apple silicon dmg、Windows Universal 安装文件等
validations:
required: true
- type: textarea
attributes:
label: 环境
description: |
在项目的根目录下运行以下命令,并将结果粘贴到下方:
```sh
npx envinfo --system --binaries --browsers
```
在 macOS 中,如果您需要轻松复制粘贴,可以添加 `| pbcopy`。
或者,您也可以手动收集您的环境版本信息。
validations:
required: true
- type: textarea
id: steps-to-reproduce
attributes:
label: 复现步骤
description: 复现该问题的步骤。
placeholder: |
1. 前往 '...'
2. 点击 '...'
3. 滚动到 '...'
4. 查看错误
更多信息:[最小复现例子](https://stackoverflow.com/help/minimal-reproducible-example) 是必需的,否则该问题可能会被关闭而没有进一步的通知。[**为什么 & 如何?**](https://antfu.me/posts/why-reproductions-are-required)
validations:
required: true
- type: textarea
id: additional-information
attributes:
label: 额外信息
description: |
提供任何额外的信息,例如日志、截图、喜欢、发生该 Bug 的场景,以便有助于解决问题。
- type: checkboxes
id: checkboxes
attributes:
label: 验证
description: 在提交问题之前,请确保您完成了以下操作
options:
- label: 遵循我们的[行为准则](https//github.com/agalwood/Motrix/blob/master/CODE_OF_CONDUCT.md)
required: true
- label: 确认是否已经有一个报告了相同的 Bug,以避免创建重复的问题。
required: true
- label: 确认此问题是一个具体的 Bug。若要进行问答,请开启 [GitHub 讨论](https://github.com/agalwood/Motrix/discussions)。
required: true
- label: 提供的复现是该 Bug 的 [最小复现例子](https://stackoverflow.com/help/minimal-reproducible-example)。
required: true
-39
View File
@@ -1,39 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Before feedback**
Before you feedback, please search for the issues to see if there are similar problems that can solve your problem.
**Please delete the above and the contents of this line, then fill in the feedback form in the following format, Thanks.**
<!-- Windows and Linux versions hide the application menu by default. Please use the keyboard shortcut "Ctrl+Shift+I" to open "Developer Tools" -->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following information):**
- OS & Version: [e.g. macOS, Windows, Linux]
- Version: [e.g. macOS 10.14.2, Windows 10, Ubuntu 18.04]
- Motrix Version: [e.g. v1.1.3, v1.1.0]
- Installation package type: [e.g. dmg, AppImage]
**Additional context**
Add any other context about the problem here.
-43
View File
@@ -1,43 +0,0 @@
---
name: 错误反馈
about: 创建一个错误报告帮助改进「请按照模板提交,提供详细的信息,方便我们复现之后处理」
title: ''
labels: ''
assignees: ''
---
<!--
反馈之前请搜索一下已有 issues 和 帮助文档,看是否有类似问题可以解决你的问题
https://github.com/agalwood/Motrix/issues
http://motrix.app/support
按以下格式填写反馈信息,谢谢
-->
**错误描述**
清楚简洁地描述错误,方便我们复现之后处理。
**如何重现**
重现步骤,如:
1. 点击新建任务按钮
2. 黏贴链接(如链接不涉及到隐私和版权问题,请顺便提供)
3. 点击提交
4. 发现报错
**预期的行为**
清楚简洁地描述你期望发生的事情。
**截图**
请添加屏幕截图以帮助解释你的问题:
打开应用菜单中的「帮助」——「开发者工具」—— 切换到 console,然后**完整**截图。
<!-- Windows 和 Linux 版本默认隐藏了应用菜单,请使用键盘快捷键 Ctrl+Shift+I 打开「开发者工具」 -->
**运行环境**
- 操作系统类型: [如 macOS, Windows, Linux]
- 具体版本: [如 macOS 10.14.2, Windows 10, Ubuntu 18.04]
- Motrix 版本: [如 v1.1.3, v1.1.0]
- 安装包类型:[如 dmg, AppImage]
**更多信息**
补充有关该问题的其他信息。
+71
View File
@@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '23 8 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+18 -18
View File
@@ -17,30 +17,27 @@ jobs:
steps:
- name: Check out Git repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install Node.js, NPM and Yarn
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: 12
node-version: 18
- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v1
uses: samuelmeuli/action-snapcraft@v2
# Only install Snapcraft on Ubuntu
if: startsWith(matrix.os, 'ubuntu')
with:
# Log in to Snap Store
snapcraft_token: ${{ secrets.snapcraft_token }}
env:
# Snapcraft
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.snapcraft_token }}
- name: Prepare for app notarization (macOS)
if: startsWith(matrix.os, 'macos')
# Import Apple API key for app notarization on macOS
run: |
mkdir -p ~/private_keys/
echo '${{ secrets.api_key }}' > ~/private_keys/AuthKey_${{ secrets.api_key_id }}.p8
- name: Test Snapcraft
if: startsWith(matrix.os, 'ubuntu')
run: snapcraft --help
- name: Build/release Electron app
uses: samuelmeuli/action-electron-builder@v1
uses: motrixapp/action-electron-builder@v2
with:
build_script_name: 'build:github'
# GitHub token, automatically provided to the action
@@ -53,8 +50,11 @@ jobs:
# If the commit is tagged with a version (e.g. "v1.0.0"),
# release the app after building
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
release: ${{ vars.skip_publish != 'true' }}
env:
# macOS notarization API key
API_KEY_ID: ${{ secrets.api_key_id }}
API_KEY_ISSUER_ID: ${{ secrets.api_key_issuer_id }}
# Snapcraft
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.snapcraft_token }}
# macOS notarization
TEAM_ID: ${{ secrets.team_id }}
APPLE_ID: ${{ secrets.apple_id }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.apple_app_specific_password }}
+21 -8
View File
@@ -1,12 +1,25 @@
!.gitkeep
.DS_Store
dist/electron/*
dist/web/*
.env
.idea/
.vs/
.vscode/
*.log
node_modules/
thumbs.db
# npm package
.npmrc
npm-debug.log.*
# Eslint Cache
.eslintcache*
# electron builder
*.provisionprofile
build/*.plist
node_modules/
npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep
dist/electron/*
dist/web/*
# release
release/*
.idea/
+1 -1
View File
@@ -4,7 +4,7 @@
## 🌍 翻译指南
首先你要确定一个语言的英文简写作为 **locale**,如 en-US,这个 locale 值请严格参考 [Electron 的 Locales 文档](https://electronjs.org/docs/api/locales)
首先你要确定一个语言的英文简写作为 **locale**,如 en-US,这个 locale 值请严格参考 [Electron 的 Locales 文档](https://www.electronjs.org/docs/api/app#appgetlocale) 和 [Chromium 源代码](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/l10n/l10n_util.cc)
Motrix 的国际化分两部分:
+1 -1
View File
@@ -4,7 +4,7 @@ Before you start contributing, make sure you already understand [GitHub flow](ht
## 🌍 Translation Guide
First you need to determine the English abbreviation of a language as **locale**, such as en-US, this locale value should strictly refer to the [electron's documentation](https://electronjs.org/docs/api/locales).
First you need to determine the English abbreviation of a language as **locale**, such as en-US, this locale value should strictly refer to the [Electron's Documentation](https://www.electronjs.org/docs/api/app#appgetlocale) and [Chromium Source Code](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/l10n/l10n_util.cc).
The internationalization of Motrix is divided into two parts:
+55 -12
View File
@@ -1,7 +1,7 @@
# Motrix
<a href="https://motrix.app">
<img src="https://cdn.nlark.com/yuque/0/2018/png/129147/1543735425232-a5d2c99f-d788-43e4-9781-558ff6d21027.png" width="256" alt="App Icon" />
<img src="./static/512x512.png" width="256" alt="App Icon" />
</a>
## 一款全能的下载工具
@@ -24,6 +24,20 @@ Motrix 是一款全能的下载工具,支持下载 HTTP、FTP、BT、磁力链
建议使用安装包(Motrix-Setup-x.y.z.exe)安装 Motrix 以确保完整的体验,例如关联 torrent 文件,捕获磁力链等。
如果你在 Windows 是用包管理工具来管理应用,如 [Chocolatey](https://chocolatey.org)、[scoop](https://github.com/lukesampson/scoop),你可以使用它们安装 Motrix。
#### Chocolatey
感谢 [@Yato](https://github.com/iYato) 持续维护着 [Motrix Chocolatey](https://community.chocolatey.org/packages/motrix) 包。要安装 Motrix,请从 `命令行``PowerShell` 中运行以下命令:
```bash
# 安装
choco install motrix
# 升级
choco upgrade motrix
```
#### scoop
如果你更喜欢便携版,你可以使用 [scoop](https://github.com/lukesampson/scoop)(需要 Windows 7+,天朝用户可能需要设置 Git 代理)安装最新便携版本的 Motrix。
```bash
@@ -33,16 +47,18 @@ scoop install motrix
### macOS
macOS 用户可以使用 `brew cask` 安装 Motrix,感谢 [Mitscherlich](https://github.com/Mitscherlich) 的 [PR](https://github.com/Homebrew/homebrew-cask/pull/59494)。
macOS 用户可以使用 `brew cask` 安装 Motrix,感谢 [@Mitscherlich](https://github.com/Mitscherlich) 的 [PR](https://github.com/Homebrew/homebrew-cask/pull/59494)。
```bash
brew update && brew cask install motrix
brew update && brew install --cask motrix
```
### Linux
你可以下载 `AppImage` (适用于所有 Linux 发行版)或 `snap` 来安装 Motrix,更多 Linux 安装包格式请查看 [GitHub/release](https://github.com/agalwood/Motrix/releases) 。
Motrix 在 Linux 中首次启动可能需要使用 `sudo` 运行,因为可能没有创建下载会话文件的权限 (`/var/cache/aria2.session`)。
如果你想自己通过编译源码来安装,请阅读 **编译打包** 部分。
#### AppImage
@@ -70,15 +86,24 @@ v1.5.10 提示
请更新到 v1.5.12 及以上版本,可以使用键盘组合快捷键 <kbd>Ctrl</kbd> + <kbd>q</kbd> 快速退出应用。
#### AUR
对于 Arch Linux 用户,可以使用 [aur](https://aur.archlinux.org/packages/motrix/) 安装 Motrix,感谢维护者 [weearc](https://github.com/weearc)。
对于 Arch Linux 用户,可以使用 [aur](https://aur.archlinux.org/packages/motrix/) 安装 Motrix,感谢维护者 [@weearc](https://github.com/weearc)。
运行以下命令进行安装:
```bash
yay motrix
yay -S motrix
```
Motrix 在 Linux 中首次启动可能需要使用 `sudo` 运行,因为可能没有创建下载会话文件的权限 (`/var/cache/aria2.session`)。
#### Flatpak
感谢 [@proletarius101](https://github.com/proletarius101) 的 [PR](https://github.com/flathub/flathub/pull/2334)Motrix 已经上架 [Flathub](https://flathub.org/apps/details/net.agalwood.Motrix),喜欢 Flatpak 的 Linux 用户可以尝试。
```bash
# 安装
flatpak install flathub net.agalwood.Motrix
# 运行
flatpak run net.agalwood.Motrix
```
## ✨ 特性
@@ -94,6 +119,7 @@ Motrix 在 Linux 中首次启动可能需要使用 `sudo` 运行,因为可能
- 🔔 下载完成后通知
- 💻 支持触控栏快捷键 (Mac 专享)
- 🤖 常驻系统托盘,操作更加便捷
- 📟 系统托盘速度仪表显示实时速度 (Mac 专享)
- 🌑 深色模式
- 🗑 移除任务时可同时删除相关文件
- 🌍 国际化,[查看已可选的语言](#-国际化)
@@ -115,13 +141,14 @@ git clone git@github.com:agalwood/Motrix.git
```bash
cd Motrix
npm install
yarn
```
天朝大陆用户建议使用淘宝的 npm 源
```bash
npm config set registry 'https://registry.npm.taobao.org'
yarn config set registry 'https://registry.npmmirror.com'
npm config set registry 'https://registry.npmmirror.com'
export ELECTRON_MIRROR='https://npm.taobao.org/mirrors/electron/'
export SASS_BINARY_SITE='https://npm.taobao.org/mirrors/node-sass'
```
@@ -133,22 +160,26 @@ export SASS_BINARY_SITE='https://npm.taobao.org/mirrors/node-sass'
### 开发模式
```bash
npm run dev
yarn run dev
```
### 编译打包
```bash
npm run build
yarn run build
```
#### 编译 Apple Silicon 版本
```bash
yarn run build:applesilicon
```
完成之后可以在项目的 `release` 目录看到编译打包好的应用文件
## 🛠 技术栈
- [Electron](https://electronjs.org/)
- [Vue](https://vuejs.org/) + [VueX](https://vuex.vuejs.org/) + [Element](https://element.eleme.io)
- [Aria2](https://aria2.github.io/) (注:macOS 和 Linux 版本使用的是 64 位的 aria2cWindows 版使用的 32 位的)
- [Aria2](https://aria2.github.io/)
## ☑️ TODO
@@ -164,20 +195,32 @@ npm run build
| Key | Name | Status |
|-------|:--------------------|:-------------|
| ar | Arabic | ✔️ [@hadialqattan](https://github.com/hadialqattan), [@AhmedElTabarani](https://github.com/AhmedElTabarani) |
| bg | Българският език | ✔️ [@null-none](https://github.com/null-none) |
| ca | Català | ✔️ [@marcizhu](https://github.com/marcizhu) |
| de | Deutsch | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
| el | Ελληνικά | ✔️ [@Likecinema](https://github.com/Likecinema) |
| en-US | English | ✔️ |
| es | Español | ✔️ [@Chofito](https://github.com/Chofito)|
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
| hu | Hungarian | ✔️ [@zalnaRs](https://github.com/zalnaRs) |
| id | Indonesia | ✔️ [@aarestu](https://github.com/aarestu) |
| it | Italiano | ✔️ [@blackcat-917](https://github.com/blackcat-917) |
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
| nb | Norsk Bokmål | ✔️ [@rubjo](https://github.com/rubjo) |
| nl | Nederlands | ✔️ [@nickbouwhuis](https://github.com/nickbouwhuis) |
| pl | Polski | ✔️ [@KanarekLife](https://github.com/KanarekLife) |
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
| ro | Română | ✔️ [@alyn3d](https://github.com/alyn3d) |
| ru | Русский | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| th | แบบไทย | ✔️ [@nxanywhere](https://github.com/nxanywhere) |
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
| uk | Українська | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| vi | Tiếng Việt | ✔️ [@duythanhvn](https://github.com/duythanhvn) |
| zh-CN | 简体中文 | ✔️ |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) [@5idereal](https://github.com/5idereal) |
## 📜 开源许可
+54 -11
View File
@@ -1,7 +1,7 @@
# Motrix
<a href="https://motrix.app">
<img src="https://cdn.nlark.com/yuque/0/2018/png/129147/1543735425232-a5d2c99f-d788-43e4-9781-558ff6d21027.png" width="256" alt="App Icon" />
<img src="./static/512x512.png" width="256" alt="App Icon" />
</a>
## A full-featured download manager
@@ -24,6 +24,20 @@ Download from [GitHub Releases](https://github.com/agalwood/Motrix/releases) and
It is recommended to install Motrix using the installation package (Motrix-Setup-x.y.z.exe) to ensure a complete experience, such as associating torrent files, capturing magnet links, etc.
If you use package management tools to manage applications on Windows, such as [Chocolatey](https://chocolatey.org), [scoop](https://github.com/lukesampson/scoop). You can use them to install Motrix.
#### Chocolatey
Thanks to [@Yato](https://github.com/iYato) for continuing to maintain the [Motrix Chocolatey](https://community.chocolatey.org/packages/motrix) package. To install motrix, run the following command from the `command line` or from `PowerShell`:
```bash
# Install
choco install motrix
# Upgrade
choco upgrade motrix
```
#### scoop
If you prefer the portable version, you can use [scoop](https://github.com/lukesampson/scoop) (need Windows 7+) to install Motrix.
```bash
@@ -33,16 +47,18 @@ scoop install motrix
### macOS
The macOS users can install Motrix using `brew cask`, thanks to [PR](https://github.com/Homebrew/homebrew-cask/pull/59494) of [Mitscherlich](https://github.com/Mitscherlich).
The macOS users can install Motrix using `brew cask`, thanks to [PR](https://github.com/Homebrew/homebrew-cask/pull/59494) of [@Mitscherlich](https://github.com/Mitscherlich).
```bash
brew update && brew cask install motrix
brew update && brew install --cask motrix
```
### Linux
You can download the `AppImage` (for all Linux distributions) or `snap` to install Motrix, see [GitHub/release](https://github.com/agalwood/Motrix/releases) for more Linux installation package formats.
Motrix may need to run with `sudo` for the first time in Linux because there is no permission to create the download session file (`/var/cache/aria2.session`).
If you want to build from source code, please read the **Build** section.
#### AppImage
@@ -72,15 +88,24 @@ Please unchecked Preferences--Basic Settings--Hide App Menu (Windows & Linux Onl
Please update to v1.5.12 and above, you can use the keyboard shortcut <kbd>Ctrl</kbd> + <kbd>q</kbd> to quickly exit the application.
#### AUR
For Arch Linux users, Motrix is available in [aur](https://aur.archlinux.org/packages/motrix/), thanks to the maintainer [weearc](https://github.com/weearc).
For Arch Linux users, Motrix is available in [aur](https://aur.archlinux.org/packages/motrix/), thanks to the maintainer [@weearc](https://github.com/weearc).
Run the following command to install:
```bash
yay motrix
yay -S motrix
```
Motrix may need to run with `sudo` for the first time in Linux because there is no permission to create the download session file (`/var/cache/aria2.session`).
#### Flatpak
Thanks to the [PR](https://github.com/flathub/flathub/pull/2334) of [@proletarius101](https://github.com/proletarius101), Motrix has been listed [Flathub](https://flathub.org/apps/details/net.agalwood.Motrix), Linux users who like the Flatpak can try it.
```bash
# Install
flatpak install flathub net.agalwood.Motrix
# Run
flatpak run net.agalwood.Motrix
```
## ✨ Features
@@ -96,6 +121,7 @@ Motrix may need to run with `sudo` for the first time in Linux because there is
- 🔔 Download completed Notification
- 💻 Ready for Touch Bar (Mac only)
- 🤖 Resident system tray for quick operation
- 📟 Tray speed meter displays real-time speed (Mac only)
- 🌑 Dark mode
- 🗑 Delete related files when removing tasks (optional)
- 🌍 I18n, [View supported languages](#-internationalization).
@@ -117,7 +143,7 @@ git clone git@github.com:agalwood/Motrix.git
```bash
cd Motrix
npm install
yarn
```
> Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
@@ -127,13 +153,18 @@ npm install
### Dev Mode
```bash
npm run dev
yarn run dev
```
### Build Release
```bash
npm run build
yarn run build
```
#### Build for Apple Silicon
```bash
yarn run build:applesilicon
```
After building, the application will be found in the project's `release` directory.
@@ -142,7 +173,7 @@ After building, the application will be found in the project's `release` directo
- [Electron](https://electronjs.org/)
- [Vue](https://vuejs.org/) + [VueX](https://vuex.vuejs.org/) + [Element](https://element.eleme.io)
- [Aria2](https://aria2.github.io/) (Note: macOS and Linux versions use 64-bit aria2c, Windows version uses 32-bit)
- [Aria2](https://aria2.github.io/)
## ☑️ TODO
@@ -158,20 +189,32 @@ Translations into versions for other languages are welcome 🧐! Please read the
| Key | Name | Status |
|-------|:--------------------|:-------------|
| ar | Arabic | ✔️ [@hadialqattan](https://github.com/hadialqattan), [@AhmedElTabarani](https://github.com/AhmedElTabarani) |
| bg | Българският език | ✔️ [@null-none](https://github.com/null-none) |
| ca | Català | ✔️ [@marcizhu](https://github.com/marcizhu) |
| de | Deutsch | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
| el | Ελληνικά | ✔️ [@Likecinema](https://github.com/Likecinema) |
| en-US | English | ✔️ |
| es | Español | ✔️ [@Chofito](https://github.com/Chofito)|
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
| hu | Hungarian | ✔️ [@zalnaRs](https://github.com/zalnaRs) |
| id | Indonesia | ✔️ [@aarestu](https://github.com/aarestu) |
| it | Italiano | ✔️ [@blackcat-917](https://github.com/blackcat-917) |
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
| nb | Norsk Bokmål | ✔️ [@rubjo](https://github.com/rubjo) |
| nl | Nederlands | ✔️ [@nickbouwhuis](https://github.com/nickbouwhuis) |
| pl | Polski | ✔️ [@KanarekLife](https://github.com/KanarekLife) |
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
| ro | Română | ✔️ [@alyn3d](https://github.com/alyn3d) |
| ru | Русский | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| th | แบบไทย | ✔️ [@nxanywhere](https://github.com/nxanywhere) |
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
| uk | Українська | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| vi | Tiếng Việt | ✔️ [@duythanhvn](https://github.com/duythanhvn) |
| zh-CN | 简体中文 | ✔️ |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) [@5idereal](https://github.com/5idereal) |
## 📜 License
+1 -1
View File
@@ -1,2 +1,2 @@
provider: generic
url: 'https://dl.motrix.app/release/'
url: 'https://dl.motrix.app/releases/'
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 24 KiB

+21 -12
View File
@@ -1,6 +1,7 @@
require('dotenv').config()
const { notarize } = require('electron-notarize')
const pkg = require('../package.json')
const { join } = require('path')
const { notarize } = require('@electron/notarize')
const { appId } = require('../electron-builder.json')
exports.default = async function (context) {
const { electronPlatformName, appOutDir } = context
@@ -9,19 +10,27 @@ exports.default = async function (context) {
}
const skipNotarize = process.env.SKIP_NOTARIZE
if (skipNotarize === 'yes') {
console.log('skipping notarize')
if (skipNotarize === 'true') {
console.log('Skipping notarize')
return
}
const appBundleId = pkg.build.appId
const appBundleId = appId
const appName = context.packager.appInfo.productFilename
const appPath = join(appOutDir, `${appName}.app`)
return await notarize({
appBundleId,
appPath: `${appOutDir}/${appName}.app`,
ascProvider: process.env.TEAM_ID,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PWD
})
try {
await notarize({
tool: 'notarytool',
appBundleId,
appPath,
teamId: process.env.TEAM_ID,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD
})
} catch (error) {
console.error(error)
}
console.log(`Done notarizing ${appId}`)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 62 KiB

+200
View File
@@ -0,0 +1,200 @@
{
"productName": "Motrix",
"appId": "app.motrix.native",
"afterPack": "./build/afterPackHook.js",
"afterSign": "./build/afterSignHook.js",
"fileAssociations": [
{
"ext": "torrent",
"mimeType": "application/x-bittorrent",
"name": "Torrent",
"role": "Viewer"
}
],
"asar": true,
"directories": {
"output": "release"
},
"files": [
"dist/electron/**/*"
],
"protocols": [
{
"name": "Motrix Protocol",
"schemes": [
"mo",
"motrix"
]
},
{
"name": "Magnet Protocol",
"schemes": [
"magnet"
]
},
{
"name": "Thunder Protocol",
"schemes": [
"thunder"
]
}
],
"dmg": {
"window": {
"width": 540,
"height": 380
},
"contents": [
{
"x": 410,
"y": 230,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 230,
"type": "file"
}
]
},
"mac": {
"target": [
{
"target": "dmg",
"arch": [
"x64",
"arm64",
"universal"
]
},
{
"target": "zip",
"arch": [
"x64",
"arm64",
"universal"
]
}
],
"type": "development",
"darkModeSupport": true,
"hardenedRuntime": false,
"notarize": false,
"extraResources": {
"from": "./extra/darwin/${arch}/",
"to": "./",
"filter": [
"**/*"
]
},
"category": "public.app-category.utilities"
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64",
"ia32"
]
},
{
"target": "appx",
"arch": [
"x64",
"ia32"
]
},
{
"target": "zip",
"arch": [
"x64",
"ia32"
]
},
{
"target": "portable",
"arch": [
"x64",
"ia32"
]
}
],
"extraResources": {
"from": "./extra/win32/${arch}/",
"to": "./",
"filter": [
"**/*"
]
}
},
"nsis": {
"artifactName": "${productName}-Setup-${version}.${ext}",
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"appx": {
"artifactName": "${productName}-${version}-${arch}.${ext}",
"applicationId": "app.motrix.native",
"identityName": "59744DrrOot.Motrix",
"publisher": "CN=5BB4961D-30D8-4993-9ADF-05E1E1F5A395",
"publisherDisplayName": "Dr_rOot"
},
"portable": {
"artifactName": "${productName}-${version}-${arch}.${ext}"
},
"linux": {
"category": "Network",
"mimeTypes": [
"application/x-bittorrent",
"x-scheme-handler/magnet"
],
"target": [
{
"target": "AppImage",
"arch": [
"x64",
"arm64",
"armv7l"
]
},
{
"target": "deb",
"arch": [
"x64",
"arm64",
"armv7l"
]
},
{
"target": "rpm",
"arch": [
"x64"
]
},
{
"target": "snap",
"arch": [
"x64"
]
}
],
"extraResources": {
"from": "./extra/linux/${arch}/",
"to": "./",
"filter": [
"**/*"
]
}
},
"publish": [
{
"provider": "generic",
"url": "https://dl.motrix.app/releases/"
},
{
"provider": "github"
}
]
}
@@ -19,9 +19,11 @@ rpc-listen-all=true
# Save a control file(*.aria2) every SEC seconds.
auto-save-interval=10
# Enable disk cache.
disk-cache=32M
disk-cache=64M
# Specify file allocation method.
file-allocation=falloc
file-allocation=none
# No file allocation is made for files whose size is smaller than SIZE
no-file-allocation-limit=64M
# Save error/unfinished downloads to a file specified by --save-session option every SEC seconds.
save-session-interval=10
@@ -33,13 +35,25 @@ bt-detach-seed-only=true
check-certificate=false
# If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times
# without getting a single byte, then force the download to fail.
max-file-not-found=5
max-file-not-found=10
# Set number of tries.
max-tries=5
max-tries=0
# Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response.
retry-wait=10
# Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead.
connect-timeout=10
# Set timeout in seconds.
timeout=10
# aria2 does not split less than 2*SIZE byte range.
min-split-size=1M
# Set user agent for HTTP(S) downloads.
user-agent=Transmission/2.94
# Send Accept: deflate, gzip request header.
http-accept-gzip=true
# Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file.
remote-time=true
# Set interval in seconds to output download progress summary. Setting 0 suppresses the output.
summary-interval=0
# Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*.
content-disposition-default-utf8=true
################ BT Task ################
@@ -50,13 +64,17 @@ bt-enable-lpd=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=255
bt-max-peers=128
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
bt-seed-unverified=true
bt-seed-unverified=false
# Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead.
bt-tracker-connect-timeout=10
# Set timeout in seconds.
bt-tracker-timeout=10
# Set host and port as an entry point to IPv4 DHT network.
dht-entry-point=dht.transmissionbt.com:6881
# Set host and port as an entry point to IPv6 DHT network.
@@ -68,6 +86,6 @@ enable-dht6=true
# Enable Peer Exchange extension.
enable-peer-exchange=true
# Specify the string used during the bitorrent extended handshake for the peer's client version.
peer-agent=Transmission/2.94
peer-agent=Transmission/3.00
# Specify the prefix of peer ID.
peer-id-prefix=-TR2940-
peer-id-prefix=-TR3000-
BIN
View File
Binary file not shown.
Binary file not shown.
+91
View File
@@ -0,0 +1,91 @@
###############################
# Motrix macOS Aria2 config file
#
# @see https://aria2.github.io/manual/en/html/aria2c.html
#
###############################
################ RPC ################
# Enable JSON-RPC/XML-RPC server.
enable-rpc=true
# Add Access-Control-Allow-Origin header field with value * to the RPC response.
rpc-allow-origin-all=true
# Listen incoming JSON-RPC/XML-RPC requests on all network interfaces.
rpc-listen-all=true
################ File system ################
# Save a control file(*.aria2) every SEC seconds.
auto-save-interval=10
# Enable disk cache.
disk-cache=64M
# Specify file allocation method.
file-allocation=none
# No file allocation is made for files whose size is smaller than SIZE
no-file-allocation-limit=64M
# Save error/unfinished downloads to a file specified by --save-session option every SEC seconds.
save-session-interval=10
################ Task ################
# Exclude seed only downloads when counting concurrent active downloads
bt-detach-seed-only=true
# Verify the peer using certificates specified in --ca-certificate option.
check-certificate=false
# If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times
# without getting a single byte, then force the download to fail.
max-file-not-found=10
# Set number of tries.
max-tries=0
# Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response.
retry-wait=10
# Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead.
connect-timeout=10
# Set timeout in seconds.
timeout=10
# aria2 does not split less than 2*SIZE byte range.
min-split-size=1M
# Send Accept: deflate, gzip request header.
http-accept-gzip=true
# Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file.
remote-time=true
# Set interval in seconds to output download progress summary. Setting 0 suppresses the output.
summary-interval=0
# Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*.
content-disposition-default-utf8=true
################ BT Task ################
# Enable Local Peer Discovery.
bt-enable-lpd=true
# Requires BitTorrent message payload encryption with arc4.
# bt-force-encryption=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=128
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
bt-seed-unverified=false
# Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead.
bt-tracker-connect-timeout=10
# Set timeout in seconds.
bt-tracker-timeout=10
# Set host and port as an entry point to IPv4 DHT network.
dht-entry-point=dht.transmissionbt.com:6881
# Set host and port as an entry point to IPv6 DHT network.
dht-entry-point6=dht.transmissionbt.com:6881
# Enable IPv4 DHT functionality. It also enables UDP tracker support.
enable-dht=true
# Enable IPv6 DHT functionality.
enable-dht6=true
# Enable Peer Exchange extension.
enable-peer-exchange=true
# Specify the string used during the bitorrent extended handshake for the peer's client version.
peer-agent=Transmission/3.00
# Specify the prefix of peer ID.
peer-id-prefix=-TR3000-
BIN
View File
Binary file not shown.
@@ -19,9 +19,11 @@ rpc-listen-all=true
# Save a control file(*.aria2) every SEC seconds.
auto-save-interval=10
# Enable disk cache.
disk-cache=32M
disk-cache=64M
# Specify file allocation method.
file-allocation=trunc
# No file allocation is made for files whose size is smaller than SIZE
no-file-allocation-limit=64M
# Save error/unfinished downloads to a file specified by --save-session option every SEC seconds.
save-session-interval=10
@@ -33,13 +35,25 @@ bt-detach-seed-only=true
check-certificate=false
# If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times
# without getting a single byte, then force the download to fail.
max-file-not-found=5
max-file-not-found=10
# Set number of tries.
max-tries=5
max-tries=0
# Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response.
retry-wait=10
# Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead.
connect-timeout=10
# Set timeout in seconds.
timeout=10
# aria2 does not split less than 2*SIZE byte range.
min-split-size=1M
# Set user agent for HTTP(S) downloads.
user-agent=Transmission/2.94
# Send Accept: deflate, gzip request header.
http-accept-gzip=true
# Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file.
remote-time=true
# Set interval in seconds to output download progress summary. Setting 0 suppresses the output.
summary-interval=0
# Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*.
content-disposition-default-utf8=true
################ BT Task ################
@@ -50,13 +64,17 @@ bt-enable-lpd=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=255
bt-max-peers=128
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
bt-seed-unverified=true
bt-seed-unverified=false
# Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead.
bt-tracker-connect-timeout=10
# Set timeout in seconds.
bt-tracker-timeout=10
# Set host and port as an entry point to IPv4 DHT network.
dht-entry-point=dht.transmissionbt.com:6881
# Set host and port as an entry point to IPv6 DHT network.
@@ -68,6 +86,6 @@ enable-dht6=true
# Enable Peer Exchange extension.
enable-peer-exchange=true
# Specify the string used during the bitorrent extended handshake for the peer's client version.
peer-agent=Transmission/2.94
peer-agent=Transmission/3.00
# Specify the prefix of peer ID.
peer-id-prefix=-TR2940-
peer-id-prefix=-TR3000-
BIN
View File
Binary file not shown.
+91
View File
@@ -0,0 +1,91 @@
###############################
# Motrix Linux Aria2 config file
#
# @see https://aria2.github.io/manual/en/html/aria2c.html
#
###############################
################ RPC ################
# Enable JSON-RPC/XML-RPC server.
enable-rpc=true
# Add Access-Control-Allow-Origin header field with value * to the RPC response.
rpc-allow-origin-all=true
# Listen incoming JSON-RPC/XML-RPC requests on all network interfaces.
rpc-listen-all=true
################ File system ################
# Save a control file(*.aria2) every SEC seconds.
auto-save-interval=10
# Enable disk cache.
disk-cache=64M
# Specify file allocation method.
file-allocation=trunc
# No file allocation is made for files whose size is smaller than SIZE
no-file-allocation-limit=64M
# Save error/unfinished downloads to a file specified by --save-session option every SEC seconds.
save-session-interval=10
################ Task ################
# Exclude seed only downloads when counting concurrent active downloads
bt-detach-seed-only=true
# Verify the peer using certificates specified in --ca-certificate option.
check-certificate=false
# If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times
# without getting a single byte, then force the download to fail.
max-file-not-found=10
# Set number of tries.
max-tries=0
# Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response.
retry-wait=10
# Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead.
connect-timeout=10
# Set timeout in seconds.
timeout=10
# aria2 does not split less than 2*SIZE byte range.
min-split-size=1M
# Send Accept: deflate, gzip request header.
http-accept-gzip=true
# Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file.
remote-time=true
# Set interval in seconds to output download progress summary. Setting 0 suppresses the output.
summary-interval=0
# Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*.
content-disposition-default-utf8=true
################ BT Task ################
# Enable Local Peer Discovery.
bt-enable-lpd=true
# Requires BitTorrent message payload encryption with arc4.
# bt-force-encryption=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=128
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
bt-seed-unverified=false
# Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead.
bt-tracker-connect-timeout=10
# Set timeout in seconds.
bt-tracker-timeout=10
# Set host and port as an entry point to IPv4 DHT network.
dht-entry-point=dht.transmissionbt.com:6881
# Set host and port as an entry point to IPv6 DHT network.
dht-entry-point6=dht.transmissionbt.com:6881
# Enable IPv4 DHT functionality. It also enables UDP tracker support.
enable-dht=true
# Enable IPv6 DHT functionality.
enable-dht6=true
# Enable Peer Exchange extension.
enable-peer-exchange=true
# Specify the string used during the bitorrent extended handshake for the peer's client version.
peer-agent=Transmission/3.00
# Specify the prefix of peer ID.
peer-id-prefix=-TR3000-
BIN
View File
Binary file not shown.
+91
View File
@@ -0,0 +1,91 @@
###############################
# Motrix Linux Aria2 config file
#
# @see https://aria2.github.io/manual/en/html/aria2c.html
#
###############################
################ RPC ################
# Enable JSON-RPC/XML-RPC server.
enable-rpc=true
# Add Access-Control-Allow-Origin header field with value * to the RPC response.
rpc-allow-origin-all=true
# Listen incoming JSON-RPC/XML-RPC requests on all network interfaces.
rpc-listen-all=true
################ File system ################
# Save a control file(*.aria2) every SEC seconds.
auto-save-interval=10
# Enable disk cache.
disk-cache=64M
# Specify file allocation method.
file-allocation=trunc
# No file allocation is made for files whose size is smaller than SIZE
no-file-allocation-limit=64M
# Save error/unfinished downloads to a file specified by --save-session option every SEC seconds.
save-session-interval=10
################ Task ################
# Exclude seed only downloads when counting concurrent active downloads
bt-detach-seed-only=true
# Verify the peer using certificates specified in --ca-certificate option.
check-certificate=false
# If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times
# without getting a single byte, then force the download to fail.
max-file-not-found=10
# Set number of tries.
max-tries=0
# Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response.
retry-wait=10
# Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead.
connect-timeout=10
# Set timeout in seconds.
timeout=10
# aria2 does not split less than 2*SIZE byte range.
min-split-size=1M
# Send Accept: deflate, gzip request header.
http-accept-gzip=true
# Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file.
remote-time=true
# Set interval in seconds to output download progress summary. Setting 0 suppresses the output.
summary-interval=0
# Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*.
content-disposition-default-utf8=true
################ BT Task ################
# Enable Local Peer Discovery.
bt-enable-lpd=true
# Requires BitTorrent message payload encryption with arc4.
# bt-force-encryption=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=128
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
bt-seed-unverified=false
# Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead.
bt-tracker-connect-timeout=10
# Set timeout in seconds.
bt-tracker-timeout=10
# Set host and port as an entry point to IPv4 DHT network.
dht-entry-point=dht.transmissionbt.com:6881
# Set host and port as an entry point to IPv6 DHT network.
dht-entry-point6=dht.transmissionbt.com:6881
# Enable IPv4 DHT functionality. It also enables UDP tracker support.
enable-dht=true
# Enable IPv6 DHT functionality.
enable-dht6=true
# Enable Peer Exchange extension.
enable-peer-exchange=true
# Specify the string used during the bitorrent extended handshake for the peer's client version.
peer-agent=Transmission/3.00
# Specify the prefix of peer ID.
peer-id-prefix=-TR3000-
Binary file not shown.
+91
View File
@@ -0,0 +1,91 @@
###############################
# Motrix Windows Aria2 config file
#
# @see https://aria2.github.io/manual/en/html/aria2c.html
#
###############################
################ RPC ################
# Enable JSON-RPC/XML-RPC server.
enable-rpc=true
# Add Access-Control-Allow-Origin header field with value * to the RPC response.
rpc-allow-origin-all=true
# Listen incoming JSON-RPC/XML-RPC requests on all network interfaces.
rpc-listen-all=true
################ File system ################
# Save a control file(*.aria2) every SEC seconds.
auto-save-interval=10
# Enable disk cache.
disk-cache=64M
# Specify file allocation method.
file-allocation=none
# No file allocation is made for files whose size is smaller than SIZE
no-file-allocation-limit=64M
# Save error/unfinished downloads to a file specified by --save-session option every SEC seconds.
save-session-interval=10
################ Task ################
# Exclude seed only downloads when counting concurrent active downloads
bt-detach-seed-only=true
# Verify the peer using certificates specified in --ca-certificate option.
check-certificate=false
# If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times
# without getting a single byte, then force the download to fail.
max-file-not-found=10
# Set number of tries.
max-tries=0
# Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response.
retry-wait=10
# Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead.
connect-timeout=10
# Set timeout in seconds.
timeout=10
# aria2 does not split less than 2*SIZE byte range.
min-split-size=1M
# Send Accept: deflate, gzip request header.
http-accept-gzip=true
# Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file.
remote-time=true
# Set interval in seconds to output download progress summary. Setting 0 suppresses the output.
summary-interval=0
# Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*.
content-disposition-default-utf8=true
################ BT Task ################
# Enable Local Peer Discovery.
bt-enable-lpd=true
# Requires BitTorrent message payload encryption with arc4.
# bt-force-encryption=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=128
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
bt-seed-unverified=false
# Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead.
bt-tracker-connect-timeout=10
# Set timeout in seconds.
bt-tracker-timeout=10
# Set host and port as an entry point to IPv4 DHT network.
dht-entry-point=dht.transmissionbt.com:6881
# Set host and port as an entry point to IPv6 DHT network.
dht-entry-point6=dht.transmissionbt.com:6881
# Enable IPv4 DHT functionality. It also enables UDP tracker support.
enable-dht=true
# Enable IPv6 DHT functionality.
enable-dht6=true
# Enable Peer Exchange extension.
enable-peer-exchange=true
# Specify the string used during the bitorrent extended handshake for the peer's client version.
peer-agent=Transmission/3.00
# Specify the prefix of peer ID.
peer-id-prefix=-TR3000-
BIN
View File
Binary file not shown.
@@ -19,9 +19,11 @@ rpc-listen-all=true
# Save a control file(*.aria2) every SEC seconds.
auto-save-interval=10
# Enable disk cache.
disk-cache=32M
disk-cache=64M
# Specify file allocation method.
file-allocation=falloc
# No file allocation is made for files whose size is smaller than SIZE
no-file-allocation-limit=64M
# Save error/unfinished downloads to a file specified by --save-session option every SEC seconds.
save-session-interval=10
@@ -33,13 +35,25 @@ bt-detach-seed-only=true
check-certificate=false
# If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times
# without getting a single byte, then force the download to fail.
max-file-not-found=5
max-file-not-found=10
# Set number of tries.
max-tries=5
max-tries=0
# Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response.
retry-wait=10
# Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead.
connect-timeout=10
# Set timeout in seconds.
timeout=10
# aria2 does not split less than 2*SIZE byte range.
min-split-size=1M
# Set user agent for HTTP(S) downloads.
user-agent=Transmission/2.94
# Send Accept: deflate, gzip request header.
http-accept-gzip=true
# Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file.
remote-time=true
# Set interval in seconds to output download progress summary. Setting 0 suppresses the output.
summary-interval=0
# Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*.
content-disposition-default-utf8=true
################ BT Task ################
@@ -50,13 +64,17 @@ bt-enable-lpd=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=255
bt-max-peers=128
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
bt-seed-unverified=true
bt-seed-unverified=false
# Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead.
bt-tracker-connect-timeout=10
# Set timeout in seconds.
bt-tracker-timeout=10
# Set host and port as an entry point to IPv4 DHT network.
dht-entry-point=dht.transmissionbt.com:6881
# Set host and port as an entry point to IPv6 DHT network.
@@ -68,6 +86,6 @@ enable-dht6=true
# Enable Peer Exchange extension.
enable-peer-exchange=true
# Specify the string used during the bitorrent extended handshake for the peer's client version.
peer-agent=Transmission/2.94
peer-agent=Transmission/3.00
# Specify the prefix of peer ID.
peer-id-prefix=-TR2940-
peer-id-prefix=-TR3000-
BIN
View File
Binary file not shown.
+14
View File
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/renderer/*"
],
"@shared/*": [
"./src/shared/*"
]
}
},
"exclude": ["node_modules", "dist"]
}
-17286
View File
File diff suppressed because it is too large Load Diff
+78 -228
View File
@@ -1,13 +1,13 @@
{
"name": "Motrix",
"version": "1.5.13",
"version": "1.8.17",
"description": "A full-featured download manager",
"homepage": "https://motrix.app",
"author": {
"name": "AGALWOOD",
"name": "Dr_rOot",
"email": "agalwood.net@gmail.com"
},
"copyright": "Copyright© AGALWOOD",
"copyright": "Copyright© Dr_rOot",
"license": "MIT",
"main": "./dist/electron/main.js",
"repository": {
@@ -17,248 +17,98 @@
"scripts": {
"release": "npm run build --publish onTagOrDraft",
"build": "node .electron-vue/build.js && electron-builder",
"build:applesilicon": "node .electron-vue/build.js && electron-builder --arm64 --mac",
"build:github": "node .electron-vue/build.js",
"build:dir": "node .electron-vue/build.js && electron-builder --dir",
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
"dev": "node .electron-vue/dev-runner.js",
"dev:renderer": "webpack-dev-server --hot --colors --config .electron-vue/webpack.renderer.config.js --port 9080 --content-base app/dist",
"dev:renderer": "webpack serve --node-env development --hot --color --config .electron-vue/webpack.renderer.config.js --port 9080 --content-base app/dist",
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src",
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src",
"pack": "npm run pack:main && npm run pack:renderer",
"pack:main": "cross-env NODE_ENV=production webpack --mode production --progress --colors --config .electron-vue/webpack.main.config.js",
"pack:renderer": "cross-env NODE_ENV=production webpack --mode production --progress --colors --config .electron-vue/webpack.renderer.config.js",
"pack:main": "webpack --node-env production --progress --color --config .electron-vue/webpack.main.config.js",
"pack:renderer": "webpack --node-env production --progress --color --config .electron-vue/webpack.renderer.config.js",
"postinstall": "electron-builder install-app-deps && npm run lint:fix"
},
"build": {
"productName": "Motrix",
"appId": "net.agalwood.Motrix",
"afterPack": "./build/afterPackHook.js",
"afterSign": "electron-builder-notarize",
"fileAssociations": [
{
"ext": "torrent",
"mimeType": "application/x-bittorrent",
"name": "Torrent",
"role": "Viewer"
}
],
"asar": true,
"directories": {
"output": "release"
},
"files": [
"dist/electron/**/*"
],
"protocols": [
{
"name": "Motrix Protocol",
"schemes": [
"mo",
"motrix"
]
},
{
"name": "Magnet Protocol",
"schemes": [
"magnet"
]
},
{
"name": "Thunder Protocol",
"schemes": [
"thunder"
]
}
],
"dmg": {
"window": {
"width": 540,
"height": 380
},
"contents": [
{
"x": 410,
"y": 230,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 230,
"type": "file"
}
]
},
"mac": {
"target": [
"dmg",
"zip"
],
"type": "distribution",
"darkModeSupport": true,
"hardenedRuntime": true,
"extraResources": {
"from": "./extra/darwin/",
"to": "./",
"filter": [
"**/*"
]
},
"category": "public.app-category.utilities"
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64",
"ia32"
]
},
{
"target": "zip",
"arch": [
"x64",
"ia32"
]
},
{
"target": "portable",
"arch": [
"x64",
"ia32"
]
}
],
"extraResources": {
"from": "./extra/win32/",
"to": "./",
"filter": [
"**/*"
]
}
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"linux": {
"category": "Network",
"target": [
"AppImage",
"deb",
"rpm",
"snap"
],
"extraResources": {
"from": "./extra/linux/",
"to": "./",
"filter": [
"**/*"
]
}
},
"snap": {
"publish": [
{
"provider": "github"
},
{
"provider": "snapStore",
"channel": "edge"
}
]
},
"publish": [
{
"provider": "generic",
"url": "https://dl.motrix.app/release/"
},
{
"provider": "github"
}
]
"engines": {
"node" : ">=16.0.0"
},
"dependencies": {
"@babel/runtime": "^7.9.6",
"@panter/vue-i18next": "^0.15.2",
"aria2": "^4.1.0",
"axios": "^0.19.2",
"blob-util": "^2.0.2",
"clipboard-polyfill": "^2.8.6",
"electron-debug": "^3.0.1",
"electron-is": "^3.0.0",
"electron-log": "^4.1.2",
"electron-store": "^5.1.1",
"electron-updater": "^4.3.1",
"element-ui": "^2.13.2",
"forever-monitor": "1.7.2",
"i18next": "^19.4.4",
"lodash": "^4.17.15",
"nat-api": "^0.3.0",
"node-fetch": "^2.6.0",
"normalize.css": "^8.0.1",
"parse-torrent": "^7.1.3",
"randomatic": "^3.1.1",
"svg-innerhtml": "^1.1.0",
"vue": "^2.6.11",
"vue-electron": "^1.0.6",
"vue-router": "^3.2.0",
"vuex": "^3.4.0",
"vuex-router-sync": "^5.0.0"
"node-fetch": "^2.6.1",
"ws": "^8.13.0"
},
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@babel/register": "^7.9.0",
"@vue/eslint-config-standard": "^5.1.2",
"ajv": "^6.12.2",
"@babel/core": "^7.21.5",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-transform-runtime": "^7.21.4",
"@babel/preset-env": "^7.21.5",
"@babel/register": "^7.21.0",
"@babel/runtime": "^7.21.5",
"@bany/curl-to-json": "^1.2.7",
"@electron/notarize": "^1.2.3",
"@electron/osx-sign": "^1.0.4",
"@electron/remote": "^2.0.9",
"@motrix/multispinner": "^0.2.4",
"@motrix/nat-api": "^0.3.4",
"@panter/vue-i18next": "^0.15.2",
"@vue/eslint-config-standard": "^6.1.0",
"ajv": "^8.12.0",
"axios": "^1.4.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-loader": "^9.1.2",
"babel-plugin-component": "^1.1.1",
"cfonts": "^2.8.2",
"chalk": "^4.0.0",
"copy-webpack-plugin": "^6.0.1",
"cross-env": "^7.0.2",
"css-loader": "^3.5.3",
"del": "^5.1.0",
"devtron": "^1.4.0",
"electron": "^8.3.0",
"electron-builder": "^22.6.1",
"electron-builder-notarize": "^1.1.2",
"electron-devtools-installer": "^3.0.0",
"electron-notarize": "^0.3.0",
"electron-osx-sign": "^0.4.16",
"eslint": "^7.0.0",
"bittorrent-peerid": "^1.3.6",
"blob-util": "^2.0.2",
"cfonts": "^3.1.1",
"chalk": "^4.1.2",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^5.0.0",
"del": "^6.1.1",
"electron": "^22.3.7",
"electron-builder": "^24.3.0",
"electron-devtools-installer": "^3.2.0",
"electron-is": "^3.0.0",
"electron-log": "^4.4.8",
"electron-store": "^8.1.0",
"electron-updater": "^6.1.0",
"element-ui": "^2.15.13",
"eslint": "^7.32.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.3.0",
"mini-css-extract-plugin": "0.9.0",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"terser-webpack-plugin": "^3.0.1",
"url-loader": "^4.1.0",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.9.2",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^4.2.2"
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.11.0",
"eslint-webpack-plugin": "^4.0.1",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.1",
"i18next": "^22.4.15",
"lodash": "^4.17.21",
"mini-css-extract-plugin": "2.7.5",
"node-loader": "^2.0.0",
"normalize.css": "^8.0.1",
"parse-torrent": "^9.1.5",
"randomatic": "^3.1.1",
"sass": "1.62.1",
"sass-loader": "^12.6.0",
"style-loader": "^3.3.2",
"terser-webpack-plugin": "^5.3.7",
"vue": "^2.7.14",
"vue-electron": "^1.0.6",
"vue-loader": "^15.10.1",
"vue-router": "^3.6.5",
"vue-selectable": "^0.5.0",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.7.14",
"vuex": "^3.6.2",
"vuex-router-sync": "^5.0.0",
"webpack": "^5.81.0",
"webpack-cli": "^5.0.2",
"webpack-dev-server": "^4.13.3",
"webpack-hot-middleware": "^2.25.3",
"webpack-merge": "^5.8.0",
"worker-loader": "^3.0.8"
}
}
+32 -5
View File
@@ -10,22 +10,49 @@
</script>
<% } %>
</head>
<style>
.skeleton-aside {
background-color: rgba(0, 0, 0, 0.8);
width: 78px;
}
.skeleton-subnav {
background-color: #f4f5f7;
width: 200px;
}
.skeleton-main {
background-color: #fff;
}
@media (prefers-color-scheme: dark) {
.skeleton-aside {
background-color: rgba(0, 0, 0, 0.9);
}
.skeleton-subnav {
background-color: #2D2D2D;
}
.skeleton-main {
background-color: #343434;
}
}
</style>
<body>
<div id="app">
<div class="title-bar"></div>
<section class="el-container" id="container">
<aside class="el-aside aside" style="width: 78px;">
<aside class="el-aside skeleton-aside hidden-sm-and-down">
</aside>
<aside class="el-aside subnav" style="width: 200px;">
<aside class="el-aside skeleton-subnav hidden-xs-only">
</aside>
<section class="el-container main">
<section class="el-container skeleton-main">
</section>
</section>
</div>
<!-- Set `__static` path to static files in production -->
<% if (!process.browser) { %>
<% if (!htmlWebpackPlugin.options.isBrowser && !htmlWebpackPlugin.options.isDev) { %>
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<% } %>
+338 -91
View File
@@ -1,13 +1,25 @@
import { EventEmitter } from 'events'
import { app, shell, dialog, ipcMain } from 'electron'
import is from 'electron-is'
import { readFile } from 'fs'
import { readFile, unlink } from 'fs'
import { extname, basename } from 'path'
import { isEmpty } from 'lodash'
import { isEmpty, isEqual } from 'lodash'
import {
APP_RUN_MODE,
AUTO_SYNC_TRACKER_INTERVAL,
AUTO_CHECK_UPDATE_INTERVAL
} from '@shared/constants'
import { checkIsNeedRun } from '@shared/utils'
import {
convertTrackerDataToComma,
fetchBtTrackerFromSource,
reduceTrackerString
} from '@shared/utils/tracker'
import logger from './core/Logger'
import Context from './core/Context'
import ConfigManager from './core/ConfigManager'
import { setupLocaleManager } from '@/ui/Locale'
import { setupLocaleManager } from './ui/Locale'
import Engine from './core/Engine'
import EngineClient from './core/EngineClient'
import UPnPManager from './core/UPnPManager'
@@ -21,16 +33,6 @@ import TouchBarManager from './ui/TouchBarManager'
import TrayManager from './ui/TrayManager'
import DockManager from './ui/DockManager'
import ThemeManager from './ui/ThemeManager'
import {
APP_RUN_MODE,
AUTO_SYNC_TRACKER_INTERVAL,
AUTO_CHECK_UPDATE_INTERVAL
} from '@shared/constants'
import { checkIsNeedRun } from '@shared/utils'
import {
convertTrackerDataToComma,
fetchBtTrackerFromSource
} from '@shared/utils/tracker'
export default class Application extends EventEmitter {
constructor () {
@@ -40,11 +42,13 @@ export default class Application extends EventEmitter {
}
init () {
this.configManager = new ConfigManager()
this.initContext()
this.locale = this.configManager.getLocale()
this.localeManager = setupLocaleManager(this.locale)
this.i18n = this.localeManager.getI18n()
this.initConfigManager()
this.setupLogger()
this.initLocaleManager()
this.setupApplicationMenu()
@@ -64,9 +68,9 @@ export default class Application extends EventEmitter {
this.initDockManager()
this.autoLaunchManager = new AutoLaunchManager()
this.initAutoLaunchManager()
this.energyManager = new EnergyManager()
this.initEnergyManager()
this.initUpdaterManager()
@@ -78,9 +82,49 @@ export default class Application extends EventEmitter {
this.handleIpcMessages()
this.handleIpcInvokes()
this.emit('application:initialized')
}
initContext () {
this.context = new Context()
}
initConfigManager () {
this.configListeners = {}
this.configManager = new ConfigManager()
}
offConfigListeners () {
try {
Object.keys(this.configListeners).forEach((key) => {
this.configListeners[key]()
})
} catch (e) {
logger.warn('[Motrix] offConfigListeners===>', e)
}
this.configListeners = {}
}
setupLogger () {
const { userConfig } = this.configManager
const key = 'log-level'
const logLevel = userConfig.get(key)
logger.transports.file.level = logLevel
this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue)
logger.transports.file.level = newValue
})
}
initLocaleManager () {
this.locale = this.configManager.getLocale()
this.localeManager = setupLocaleManager(this.locale)
this.i18n = this.localeManager.getI18n()
}
setupApplicationMenu () {
this.menuManager = new MenuManager()
this.menuManager.setup(this.locale)
@@ -121,14 +165,17 @@ export default class Application extends EventEmitter {
}
async stopEngine () {
logger.info('[Motrix] stopEngine===>')
try {
await this.engineClient.shutdown({ force: true })
logger.info('[Motrix] stopEngine.setImmediate===>')
setImmediate(() => {
this.engine.stop()
})
} catch (err) {
logger.warn('[Motrix] shutdown engine fail: ', err.message)
} finally {
// no finally
}
}
@@ -141,9 +188,47 @@ export default class Application extends EventEmitter {
})
}
initAutoLaunchManager () {
this.autoLaunchManager = new AutoLaunchManager()
}
initEnergyManager () {
this.energyManager = new EnergyManager()
}
initTrayManager () {
this.trayManager = new TrayManager({
theme: this.configManager.getUserConfig('tray-theme')
theme: this.configManager.getUserConfig('tray-theme'),
systemTheme: this.themeManager.getSystemTheme(),
speedometer: this.configManager.getUserConfig('tray-speedometer'),
runMode: this.configManager.getUserConfig('run-mode')
})
this.watchTraySpeedometerEnabledChange()
this.trayManager.on('mouse-down', ({ focused }) => {
this.sendCommandToAll('application:update-tray-focused', { focused })
})
this.trayManager.on('mouse-up', ({ focused }) => {
this.sendCommandToAll('application:update-tray-focused', { focused })
})
this.trayManager.on('drop-files', (files = []) => {
this.handleFile(files[0])
})
this.trayManager.on('drop-text', (text) => {
this.handleProtocol(text)
})
}
watchTraySpeedometerEnabledChange () {
const { userConfig } = this.configManager
const key = 'tray-speedometer'
this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue)
this.trayManager.handleSpeedometerEnableChange(newValue)
})
}
@@ -153,15 +238,78 @@ export default class Application extends EventEmitter {
})
}
watchOpenAtLoginChange () {
const { userConfig } = this.configManager
const key = 'open-at-login'
this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue)
if (is.linux()) {
return
}
if (newValue) {
this.autoLaunchManager.enable()
} else {
this.autoLaunchManager.disable()
}
})
}
watchProtocolsChange () {
const { userConfig } = this.configManager
const key = 'protocols'
this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue)
if (!newValue || isEqual(newValue, oldValue)) {
return
}
logger.info('[Motrix] setup protocols client:', newValue)
this.protocolManager.setup(newValue)
})
}
watchRunModeChange () {
const { userConfig } = this.configManager
const key = 'run-mode'
this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue)
this.trayManager.handleRunModeChange(newValue)
if (newValue !== APP_RUN_MODE.TRAY) {
this.dockManager.show()
} else {
this.dockManager.hide()
// Hiding the dock icon will trigger the entire app to hide.
this.show()
}
})
}
watchShowProgressBarChange () {
const { userConfig } = this.configManager
const key = 'show-progress-bar'
this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue)
if (newValue) {
this.bindProgressChange()
} else {
this.unbindProgressChange()
}
})
}
initUPnPManager () {
this.upnp = new UPnPManager()
this.watchEnableUPnPChange()
this.watchUPnPEnabledChange()
this.watchPortsChange()
this.watchUPnPPortsChange()
const enable = this.configManager.getUserConfig('enable-upnp')
if (!enable) {
const enabled = this.configManager.getUserConfig('enable-upnp')
if (!enabled) {
return
}
@@ -177,9 +325,9 @@ export default class Application extends EventEmitter {
this.upnp.map(dhtPort)
]
try {
await Promise.all(promises)
await Promise.allSettled(promises)
} catch (e) {
logger.warn('[Motrix] start UPnP mapping fail', e)
logger.warn('[Motrix] start UPnP mapping fail', e.message)
}
}
@@ -192,17 +340,18 @@ export default class Application extends EventEmitter {
this.upnp.unmap(dhtPort)
]
try {
await Promise.all(promises)
await Promise.allSettled(promises)
} catch (e) {
logger.warn('[Motrix] stop UPnP mapping fail', e)
}
}
watchPortsChange () {
watchUPnPPortsChange () {
const { systemConfig } = this.configManager
const watchKeys = ['listen-port', 'dht-listen-port']
watchKeys.map((key) => {
this.configManager.systemConfig.onDidChange(key, async (newValue, oldValue) => {
watchKeys.forEach((key) => {
this.configListeners[key] = systemConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info('[Motrix] detected port change event:', key, newValue, oldValue)
const enable = this.configManager.getUserConfig('enable-upnp')
if (!enable) {
@@ -214,7 +363,7 @@ export default class Application extends EventEmitter {
this.upnp.map(newValue)
]
try {
await Promise.all(promises)
await Promise.allSettled(promises)
} catch (e) {
logger.info('[Motrix] change UPnP port mapping failed:', e)
}
@@ -222,8 +371,10 @@ export default class Application extends EventEmitter {
})
}
watchEnableUPnPChange () {
this.configManager.userConfig.onDidChange('enable-upnp', async (newValue, oldValue) => {
watchUPnPEnabledChange () {
const { userConfig } = this.configManager
const key = 'enable-upnp'
this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => {
logger.info('[Motrix] detected enable-upnp value change event:', newValue, oldValue)
if (newValue) {
this.startUPnPMapping()
@@ -252,11 +403,20 @@ export default class Application extends EventEmitter {
return
}
const source = this.configManager.getUserConfig('tracker-source')
if (isEmpty(source)) {
return
}
setTimeout(() => {
const source = this.configManager.getUserConfig('tracker-source')
fetchBtTrackerFromSource(source).then((data) => {
logger.warn('[Motrix] auto sync tracker data:', data)
const tracker = convertTrackerDataToComma(data)
if (!data || data.length === 0) {
return
}
let tracker = convertTrackerDataToComma(data)
tracker = reduceTrackerString(tracker)
this.savePreference({
system: {
'bt-tracker': tracker
@@ -303,7 +463,7 @@ export default class Application extends EventEmitter {
this.windowManager.on('leave-full-screen', (window) => {
const mode = this.configManager.getUserConfig('run-mode')
if (mode !== APP_RUN_MODE.STANDARD) {
if (mode === APP_RUN_MODE.TRAY) {
this.dockManager.hide()
}
})
@@ -331,6 +491,7 @@ export default class Application extends EventEmitter {
this.isReady = true
this.emit('ready')
})
if (is.macOS()) {
this.touchBarManager.setup(page, win)
}
@@ -350,7 +511,7 @@ export default class Application extends EventEmitter {
hide (page) {
if (page) {
this.windowManager.autoHideWindow(page)
this.windowManager.hideWindow(page)
} else {
this.windowManager.hideAllWindow()
}
@@ -364,22 +525,27 @@ export default class Application extends EventEmitter {
this.windowManager.destroyWindow(page)
}
async stop () {
stop () {
try {
await this.shutdownUPnPManager()
const promises = [
this.stopEngine(),
this.shutdownUPnPManager(),
this.energyManager.stopPowerSaveBlocker(),
this.trayManager.destroy()
]
this.energyManager.stopPowerSaveBlocker()
await this.stopEngine()
this.trayManager.destroy()
return promises
} catch (err) {
logger.warn('[Motrix] stop error: ', err.message)
}
}
async stopAllSettled () {
await Promise.allSettled(this.stop())
}
async quit () {
await this.stop()
await this.stopAllSettled()
app.exit()
}
@@ -409,8 +575,8 @@ export default class Application extends EventEmitter {
initThemeManager () {
this.themeManager = new ThemeManager()
this.themeManager.on('system-theme-change', (theme) => {
this.trayManager.changeIconTheme(theme)
this.sendCommandToAll('application:update-system-theme', theme)
this.trayManager.handleSystemThemeChange(theme)
this.sendCommandToAll('application:update-system-theme', { theme })
})
}
@@ -423,10 +589,6 @@ export default class Application extends EventEmitter {
}
initProtocolManager () {
if (is.dev() || is.mas()) {
return
}
const protocols = this.configManager.getUserConfig('protocols', {})
this.protocolManager = new ProtocolManager({
protocols
@@ -434,10 +596,6 @@ export default class Application extends EventEmitter {
}
handleProtocol (url) {
if (is.dev() || is.mas()) {
return
}
this.show()
this.protocolManager.handle(url)
@@ -454,15 +612,17 @@ export default class Application extends EventEmitter {
this.show()
const fileName = basename(filePath)
const name = basename(filePath)
readFile(filePath, (err, data) => {
if (err) {
logger.warn(`[Motrix] read file error: ${filePath}`, err.message)
return
}
const file = Buffer.from(data).toString('base64')
const args = [fileName, file]
this.sendCommandToAll('application:new-bt-task-with-file', ...args)
const dataURL = Buffer.from(data).toString('base64')
this.sendCommandToAll('application:new-bt-task-with-file', {
name,
dataURL
})
})
}
@@ -471,10 +631,10 @@ export default class Application extends EventEmitter {
return
}
const enable = this.configManager.getUserConfig('auto-check-update')
const enabled = this.configManager.getUserConfig('auto-check-update')
const lastTime = this.configManager.getUserConfig('last-check-update-time')
this.updateManager = new UpdateManager({
autoCheck: checkIsNeedRun(enable, lastTime, AUTO_CHECK_UPDATE_INTERVAL)
autoCheck: checkIsNeedRun(enabled, lastTime, AUTO_CHECK_UPDATE_INTERVAL)
})
this.handleUpdaterEvents()
}
@@ -500,11 +660,19 @@ export default class Application extends EventEmitter {
this.menuManager.updateMenuItemEnabledState('app.check-for-updates', true)
this.trayManager.updateMenuItemEnabledState('app.check-for-updates', true)
const win = this.windowManager.getWindow('index')
win.setProgressBar(0)
win.setProgressBar(1)
})
this.updateManager.on('will-updated', (event) => {
this.updateManager.on('update-cancelled', (event) => {
this.menuManager.updateMenuItemEnabledState('app.check-for-updates', true)
this.trayManager.updateMenuItemEnabledState('app.check-for-updates', true)
const win = this.windowManager.getWindow('index')
win.setProgressBar(-1)
})
this.updateManager.on('will-updated', async (event) => {
this.windowManager.setWillQuit(true)
await this.stopAllSettled()
})
this.updateManager.on('update-error', (event) => {
@@ -513,12 +681,27 @@ export default class Application extends EventEmitter {
})
}
relaunch () {
this.stop()
async relaunch () {
await this.stopAllSettled()
app.relaunch()
app.exit()
}
async resetSession () {
await this.stopEngine()
app.clearRecentDocuments()
const sessionPath = this.context.get('session-path')
setTimeout(() => {
unlink(sessionPath, function (err) {
logger.info('[Motrix] Removed the download seesion file:', err)
})
this.engine.start()
}, 3000)
}
savePreference (config = {}) {
logger.info('[Motrix] save preference:', config)
const { system, user } = config
@@ -537,6 +720,10 @@ export default class Application extends EventEmitter {
handleCommands () {
this.on('application:save-preference', this.savePreference)
this.on('application:update-tray', (tray) => {
this.trayManager.updateTrayByImage(tray)
})
this.on('application:relaunch', () => {
this.relaunch()
})
@@ -545,27 +732,18 @@ export default class Application extends EventEmitter {
this.quit()
})
this.on('application:open-at-login', (openAtLogin) => {
if (is.linux()) {
return
}
if (openAtLogin) {
this.autoLaunchManager.enable()
} else {
this.autoLaunchManager.disable()
}
})
this.on('application:show', (page) => {
this.on('application:show', ({ page }) => {
this.show(page)
})
this.on('application:hide', (page) => {
this.on('application:hide', ({ page }) => {
this.hide(page)
})
this.on('application:reset-session', () => this.resetSession())
this.on('application:reset', () => {
this.offConfigListeners()
this.configManager.reset()
this.relaunch()
})
@@ -575,15 +753,15 @@ export default class Application extends EventEmitter {
})
this.on('application:change-theme', (theme) => {
this.themeManager.updateAppAppearance(theme)
this.sendCommandToAll('application:update-theme', theme)
this.themeManager.updateSystemTheme(theme)
this.sendCommandToAll('application:update-theme', { theme })
})
this.on('application:change-locale', (locale) => {
this.localeManager.changeLanguageByLocale(locale)
.then(() => {
this.trayManager.setup(locale)
this.menuManager.handleLocaleChange(locale)
this.trayManager.handleLocaleChange(locale)
})
})
@@ -634,36 +812,48 @@ export default class Application extends EventEmitter {
})
this.on('application:setup-protocols-client', (protocols) => {
if (is.dev() || is.mas()) {
if (is.dev() || is.mas() || !protocols) {
return
}
logger.info('[Motrix] setup protocols client:', protocols)
this.protocolManager.setup(protocols)
})
this.on('application:open-external', (url) => {
this.openExternal(url)
})
this.on('help:official-website', () => {
const url = 'https://motrix.app/'
shell.openExternal(url)
this.openExternal(url)
})
this.on('help:manual', () => {
const url = 'https://motrix.app/manual'
shell.openExternal(url)
this.openExternal(url)
})
this.on('help:release-notes', () => {
const url = 'https://motrix.app/release'
shell.openExternal(url)
this.openExternal(url)
})
this.on('help:report-problem', () => {
const url = 'https://motrix.app/report'
shell.openExternal(url)
this.openExternal(url)
})
}
openExternal (url) {
if (!url) {
return
}
shell.openExternal(url)
}
handleConfigChange (configName) {
this.sendCommandToAll('application:update-preference-config', configName)
this.sendCommandToAll('application:update-preference-config', { configName })
}
handleEvents () {
@@ -678,8 +868,13 @@ export default class Application extends EventEmitter {
this.configManager.userConfig.onDidAnyChange(() => this.handleConfigChange('user'))
this.configManager.systemConfig.onDidAnyChange(() => this.handleConfigChange('system'))
this.watchOpenAtLoginChange()
this.watchProtocolsChange()
this.watchRunModeChange()
this.watchShowProgressBarChange()
this.on('download-status-change', (downloading) => {
this.trayManager.updateTrayByStatus(downloading)
this.trayManager.handleDownloadStatusChange(downloading)
if (downloading) {
this.energyManager.startPowerSaveBlocker()
} else {
@@ -687,13 +882,50 @@ export default class Application extends EventEmitter {
}
})
this.on('download-speed-change', (speed) => {
this.dockManager.setBadge(speed)
this.on('speed-change', (speed) => {
this.dockManager.handleSpeedChange(speed)
this.trayManager.handleSpeedChange(speed)
})
this.on('task-download-complete', (task, path) => {
this.dockManager.openDock(path)
if (is.linux()) {
return
}
app.addRecentDocument(path)
})
if (this.configManager.userConfig.get('show-progress-bar')) {
this.bindProgressChange()
}
}
handleProgressChange (progress) {
if (this.updateManager.isChecking) {
return
}
if (!is.windows() && progress === 2) {
progress = 0
}
this.windowManager.getWindow('index').setProgressBar(progress)
}
bindProgressChange () {
if (this.listeners('progress-change').length > 0) {
return
}
this.on('progress-change', this.handleProgressChange)
}
unbindProgressChange () {
if (this.listeners('progress-change').length === 0) {
return
}
this.off('progress-change', this.handleProgressChange)
this.windowManager.getWindow('index').setProgressBar(-1)
}
handleIpcMessages () {
@@ -707,4 +939,19 @@ export default class Application extends EventEmitter {
this.emit(eventName, ...args)
})
}
handleIpcInvokes () {
ipcMain.handle('get-app-config', async () => {
const systemConfig = this.configManager.getSystemConfig()
const userConfig = this.configManager.getUserConfig()
const context = this.context.get()
const result = {
...systemConfig,
...userConfig,
...context
}
return result
})
}
}
+8
View File
@@ -63,6 +63,7 @@ export default class Launcher extends EventEmitter {
}
handleAppEvents () {
this.handleRendererRemote()
this.handleOpenUrl()
this.handleOpenFile()
@@ -70,6 +71,12 @@ export default class Launcher extends EventEmitter {
this.handleAppWillQuit()
}
handleRendererRemote () {
app.on('browser-window-created', (_, window) => {
require('@electron/remote/main').enable(window.webContents)
})
}
/**
* handleOpenUrl
* Event 'open-url' macOS only
@@ -176,6 +183,7 @@ export default class Launcher extends EventEmitter {
app.on('will-quit', () => {
logger.info('[Motrix] will-quit')
if (global.application) {
logger.info('[Motrix] will-quit.application.stop')
global.application.stop()
}
})
+18 -1
View File
@@ -1,5 +1,22 @@
export default {
export const engineBinMap = {
darwin: 'aria2c',
win32: 'aria2c.exe',
linux: 'aria2c'
}
export const engineArchMap = {
darwin: {
x64: 'x64',
arm64: 'arm64'
},
win32: {
ia32: 'ia32',
x64: 'x64',
arm64: 'x64'
},
linux: {
x64: 'x64',
arm: 'armv7l',
arm64: 'arm64'
}
}
+4 -4
View File
@@ -6,12 +6,12 @@ export default {
title: 'Motrix',
width: 1024,
height: 768,
minWidth: 400,
minWidth: 478,
minHeight: 420,
// backgroundColor: '#FFFFFF',
transparent: !is.windows()
transparent: is.macOS()
},
bindCloseToHide: true,
url: is.dev() ? 'http://localhost:9080' : `file://${__dirname}/index.html`
openDevTools: is.dev(),
url: is.dev() ? 'http://localhost:9080' : require('path').join('file://', __dirname, '/index.html')
}
}
+27 -13
View File
@@ -4,8 +4,6 @@ import Store from 'electron-store'
import {
getDhtPath,
getLogPath,
getSessionPath,
getUserDownloadsPath,
getMaxConnectionPerServer
} from '../utils/index'
@@ -15,10 +13,12 @@ import {
EMPTY_STRING,
IP_VERSION,
LOGIN_SETTING_OPTIONS,
NGOSANG_TRACKERS_BEST_IP_URL,
NGOSANG_TRACKERS_BEST_URL
NGOSANG_TRACKERS_BEST_IP_URL_CDN,
NGOSANG_TRACKERS_BEST_URL_CDN
} from '@shared/constants'
import { CHROME_UA } from '@shared/ua'
import { separateConfig } from '@shared/utils'
import { reduceTrackerString } from '@shared/utils/tracker'
export default class ConfigManager {
constructor () {
@@ -51,27 +51,33 @@ export default class ConfigManager {
'allow-overwrite': false,
'auto-file-renaming': true,
'bt-exclude-tracker': EMPTY_STRING,
'bt-force-encryption': false,
'bt-load-saved-metadata': true,
'bt-save-metadata': true,
'bt-tracker': EMPTY_STRING,
'continue': true,
'dht-file-path': getDhtPath(IP_VERSION.V4),
'dht-file-path6': getDhtPath(IP_VERSION.V6),
'dht-listen-port': 26701,
'dir': getUserDownloadsPath(),
'enable-dht6': true,
'follow-metalink': true,
'follow-torrent': true,
'listen-port': 21301,
'max-concurrent-downloads': 5,
'max-connection-per-server': getMaxConnectionPerServer(),
'max-download-limit': 0,
'max-overall-download-limit': 0,
'max-overall-upload-limit': '256K',
'min-split-size': '1M',
'max-overall-upload-limit': 0,
'no-proxy': EMPTY_STRING,
'pause-metadata': false,
'pause': true,
'rpc-listen-port': 16800,
'rpc-secret': EMPTY_STRING,
'seed-ratio': 1,
'seed-time': 60,
'split': getMaxConnectionPerServer(),
'user-agent': 'Transmission/2.94'
'user-agent': CHROME_UA
}
/* eslint-enable quote-props */
})
@@ -97,26 +103,30 @@ export default class ConfigManager {
'auto-sync-tracker': true,
'enable-upnp': true,
'engine-max-connection-per-server': getMaxConnectionPerServer(),
'favorite-directories': [],
'hide-app-menu': is.windows() || is.linux(),
'history-directories': [],
'keep-seeding': false,
'keep-window-state': false,
'last-check-update-time': 0,
'last-sync-tracker-time': 0,
'locale': app.getLocale(),
'log-path': getLogPath(),
'log-level': 'warn',
'new-task-show-downloading': true,
'no-confirm-before-delete-task': false,
'open-at-login': false,
'protocols': { 'magnet': true, 'thunder': false },
'resume-all-when-app-launched': false,
'run-mode': APP_RUN_MODE.STANDARD,
'session-path': getSessionPath(),
'show-progress-bar': true,
'task-notification': true,
'theme': APP_THEME.AUTO,
'tracker-source': [
NGOSANG_TRACKERS_BEST_IP_URL,
NGOSANG_TRACKERS_BEST_URL
NGOSANG_TRACKERS_BEST_IP_URL_CDN,
NGOSANG_TRACKERS_BEST_URL_CDN
],
'tray-theme': APP_THEME.AUTO,
'tray-speedometer': is.macOS(),
'update-channel': 'latest',
'use-proxy': false,
'window-state': {}
@@ -136,6 +146,10 @@ export default class ConfigManager {
Object.keys(others).forEach(key => {
this.systemConfig.delete(key)
})
// Fix spawn ENAMETOOLONG on Windows
const tracker = reduceTrackerString(this.systemConfig.get('bt-tracker'))
this.setSystemConfig('bt-tracker', tracker)
}
fixUserConfig () {
@@ -148,8 +162,8 @@ export default class ConfigManager {
if (this.getUserConfig('tracker-source').length === 0) {
this.setUserConfig('tracker-source', [
NGOSANG_TRACKERS_BEST_IP_URL,
NGOSANG_TRACKERS_BEST_URL
NGOSANG_TRACKERS_BEST_IP_URL_CDN,
NGOSANG_TRACKERS_BEST_URL_CDN
])
}
}
+37 -1
View File
@@ -1,3 +1,39 @@
export default class Context {
import logger from './Logger'
import {
getEnginePath,
getAria2BinPath,
getAria2ConfPath,
getLogPath,
getSessionPath
} from '../utils'
const { platform, arch } = process
export default class Context {
constructor () {
this.init()
}
init () {
// The key of Context cannot be the same as that of userConfig and systemConfig.
this.context = {
platform: platform,
arch: arch,
'log-path': getLogPath(),
'session-path': getSessionPath(),
'engine-path': getEnginePath(platform, arch),
'aria2-bin-path': getAria2BinPath(platform, arch),
'aria2-conf-path': getAria2ConfPath(platform, arch)
}
logger.info('[Motrix] Context.init===>', this.context)
}
get (key) {
if (typeof key === 'undefined') {
return this.context
}
return this.context[key]
}
}
+2
View File
@@ -5,6 +5,7 @@ import logger from './Logger'
let psbId
export default class EnergyManager {
startPowerSaveBlocker () {
logger.info('[Motrix] EnergyManager.startPowerSaveBlocker', psbId)
if (psbId && powerSaveBlocker.isStarted(psbId)) {
return
}
@@ -14,6 +15,7 @@ export default class EnergyManager {
}
stopPowerSaveBlocker () {
logger.info('[Motrix] EnergyManager.stopPowerSaveBlocker', psbId)
if (typeof psbId === 'undefined' || !powerSaveBlocker.isStarted(psbId)) {
return
}
+85 -90
View File
@@ -1,18 +1,21 @@
import { app } from 'electron'
import is from 'electron-is'
import { existsSync } from 'fs'
import { resolve, join } from 'path'
import forever from 'forever-monitor'
import { existsSync, writeFile, unlink } from 'fs'
import { spawn } from 'child_process'
import logger from './Logger'
import { getI18n } from '@/ui/Locale'
import { getI18n } from '../ui/Locale'
import {
getEngineBin,
getEnginePidPath,
getAria2BinPath,
getAria2ConfPath,
getSessionPath,
transformConfig
} from '../utils/index'
const { platform, arch } = process
export default class Engine {
// ChildProcess | null
static instance = null
constructor (options = {}) {
@@ -23,82 +26,99 @@ export default class Engine {
this.userConfig = options.userConfig
}
getStartSh () {
const { platform } = process
let basePath = resolve(app.getAppPath(), '..')
start () {
const pidPath = getEnginePidPath()
logger.info('[Motrix] Engie pid path:', pidPath)
if (this.instance) {
return
}
const binPath = this.getEngineBinPath()
const args = this.getStartArgs()
this.instance = spawn(binPath, args, {
windowsHide: false,
stdio: is.dev() ? 'pipe' : 'ignore'
})
const pid = this.instance.pid.toString()
this.writePidFile(pidPath, pid)
this.instance.once('close', function () {
try {
unlink(pidPath, function (err) {
if (err) {
logger.warn(`[Motrix] Unlink engine process pid file failed: ${err}`)
}
})
} catch (err) {
logger.warn(`[Motrix] Unlink engine process pid file failed: ${err}`)
}
})
if (is.dev()) {
basePath = resolve(__dirname, `../../../extra/${platform}`)
}
this.instance.stdout.on('data', function (data) {
logger.log('[Motrix] engine stdout===>', data.toString())
})
const binName = getEngineBin(platform)
if (!binName) {
throw new Error(this.i18n.t('app.engine-damaged-message'))
this.instance.stderr.on('data', function (data) {
logger.log('[Motrix] engine stderr===>', data.toString())
})
}
}
const binPath = join(basePath, `/engine/${binName}`)
const binIsExist = existsSync(binPath)
stop () {
logger.info('[Motrix] engine.stop.instance')
if (this.instance) {
this.instance.kill()
this.instance = null
}
}
writePidFile (pidPath, pid) {
writeFile(pidPath, pid, (err) => {
if (err) {
logger.error(`[Motrix] Write engine process pid failed: ${err}`)
}
})
}
getEngineBinPath () {
const result = getAria2BinPath(platform, arch)
const binIsExist = existsSync(result)
if (!binIsExist) {
logger.error('[Motrix] engine bin is not exist:', binPath)
logger.error('[Motrix] engine bin is not exist:', result)
throw new Error(this.i18n.t('app.engine-missing-message'))
}
const confPath = join(basePath, '/engine/aria2.conf')
const sessionPath = this.userConfig['session-path'] || getSessionPath()
const sessionIsExist = existsSync(sessionPath)
let result = [`${binPath}`, `--conf-path=${confPath}`, `--save-session=${sessionPath}`]
if (sessionIsExist) {
result = [...result, `--input-file=${sessionPath}`]
}
const extraConfig = transformConfig(this.systemConfig)
result = [...result, ...extraConfig]
return result
}
start () {
const sh = this.getStartSh()
logger.info('[Motrix] Engine start sh:', sh)
this.instance = forever.start(sh, {
max: is.dev() ? 0 : 100,
parser: function (command, args) {
return {
command: command,
args: args
}
},
silent: !is.dev()
})
getStartArgs () {
const confPath = getAria2ConfPath(platform, arch)
const { child } = this.instance
logger.info('[Motrix] Engine pid:', child.pid)
const sessionPath = getSessionPath()
const sessionIsExist = existsSync(sessionPath)
this.instance.on('error', (err) => {
logger.info(`[Motrix] Engine error: ${err}`)
})
let result = [`--conf-path=${confPath}`, `--save-session=${sessionPath}`]
if (sessionIsExist) {
result = [...result, `--input-file=${sessionPath}`]
}
this.instance.on('start', function (process, data) {
logger.info('[Motrix] Engine started')
})
const extraConfig = {
...this.systemConfig
}
const keepSeeding = this.userConfig['keep-seeding']
const seedRatio = this.systemConfig['seed-ratio']
if (keepSeeding || seedRatio === 0) {
extraConfig['seed-ratio'] = 0
delete extraConfig['seed-time']
}
console.log('extraConfig===>', extraConfig)
this.instance.on('stop', function (process) {
logger.info('[Motrix] Engine stopped')
})
const extra = transformConfig(extraConfig)
result = [...result, ...extra]
// this.instance.on('restart', function (forever) {
// logger.info(`[Motrix] Engine exit:`)
// })
// this.instance.on('exit:code', function (code) {
// logger.info(`[Motrix] Engine exit: ${code}`)
// })
// this.instance.on('stderr', (data) => {
// logger.info(`[Motrix] Engine stderr: ${data}`)
// })
return result
}
isRunning (pid) {
@@ -109,31 +129,6 @@ export default class Engine {
}
}
stop () {
const { pid } = this.instance.child
try {
logger.info('[Motrix] Engine stopping')
this.instance.stop()
} catch (err) {
logger.error('[Motrix] Engine stop fail:', err.message)
this.forceStop(pid)
} finally {
this.instance.removeAllListeners('start')
this.instance.removeAllListeners('error')
this.instance.removeAllListeners('stop')
}
}
forceStop (pid) {
try {
if (pid && this.isRunning(pid)) {
process.kill(pid)
}
} catch (err) {
logger.warn('[Motrix] Engine force stop fail:', err)
}
}
restart () {
this.stop()
this.start()
+1 -1
View File
@@ -1,6 +1,6 @@
'use strict'
import Aria2 from 'aria2'
import { Aria2 } from '@shared/aria2'
import logger from './Logger'
import {
+1 -1
View File
@@ -1,7 +1,7 @@
import is from 'electron-is'
import logger from 'electron-log'
logger.transports.file.level = is.production() ? 'warn' : 'silly'
logger.transports.file.level = is.production() ? 'info' : 'silly'
logger.info('[Motrix] Logger init')
logger.warn('[Motrix] Logger init')
+20 -11
View File
@@ -1,6 +1,7 @@
import { EventEmitter } from 'events'
import { app } from 'electron'
import is from 'electron-is'
import { parse } from 'querystring'
import logger from './Logger'
import protocolMap from '../configs/protocol'
@@ -27,7 +28,11 @@ export default class ProtocolManager extends EventEmitter {
this.setup(protocols)
}
setup (protocols) {
setup (protocols = {}) {
if (is.dev() || is.mas()) {
return
}
Object.keys(protocols).forEach((protocol) => {
const enabled = protocols[protocol]
if (enabled) {
@@ -44,10 +49,13 @@ export default class ProtocolManager extends EventEmitter {
logger.info(`[Motrix] protocol url: ${url}`)
if (
url.toLowerCase().startsWith('ftp:') ||
url.toLowerCase().startsWith('http:') ||
url.toLowerCase().startsWith('https:') ||
url.toLowerCase().startsWith('magnet:') ||
url.toLowerCase().startsWith('thunder:')
) {
return this.handleMagnetAndThunderProtocol(url)
return this.handleResourceProtocol(url)
}
if (
@@ -58,17 +66,20 @@ export default class ProtocolManager extends EventEmitter {
}
}
handleMagnetAndThunderProtocol (url) {
handleResourceProtocol (url) {
if (!url) {
return
}
global.application.sendCommandToAll('application:new-task', ADD_TASK_TYPE.URI, url)
global.application.sendCommandToAll('application:new-task', {
type: ADD_TASK_TYPE.URI,
uri: url
})
}
handleMoProtocol (url) {
const parsed = new URL(url)
const { host } = parsed
const { host, search } = parsed
logger.info('[Motrix] protocol parsed:', parsed, host)
const command = protocolMap[host]
@@ -76,10 +87,8 @@ export default class ProtocolManager extends EventEmitter {
return
}
// @TODO 没想明白怎么传参数好
// 如果按顺序传递,那 url 的 query string 就要求有序的了
// const query = queryString.parse(parsed.query)
const args = []
global.application.sendCommandToAll(command, ...args)
const query = search.startsWith('?') ? search.replace('?', '') : search
const args = parse(query)
global.application.sendCommandToAll(command, args)
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
import NatAPI from 'nat-api'
import NatAPI from '@motrix/nat-api'
import logger from './Logger'
@@ -35,7 +35,7 @@ export default class UPnPManager {
try {
client.map(port, (err) => {
if (err) {
logger.warn(`[Motrix] UPnPManager map ${port} failed, error: `, err)
logger.warn(`[Motrix] UPnPManager map ${port} failed, error: `, err.message)
reject(err.message)
return
}
+19 -6
View File
@@ -5,7 +5,7 @@ import is from 'electron-is'
import { autoUpdater } from 'electron-updater'
import logger from './Logger'
import { getI18n } from '@/ui/Locale'
import { getI18n } from '../ui/Locale'
if (is.dev()) {
autoUpdater.updateConfigPath = resolve(__dirname, '../../../app-update.yml')
@@ -17,8 +17,10 @@ export default class UpdateManager extends EventEmitter {
this.options = options
this.i18n = getI18n()
this.isChecking = false
this.updater = autoUpdater
this.updater.autoDownload = false
this.updater.autoInstallOnAppQuit = false
this.updater.logger = logger
this.autoCheckData = {
checkEnable: this.options.autoCheck,
@@ -40,9 +42,10 @@ export default class UpdateManager extends EventEmitter {
this.updater.on('update-not-available', this.updateNotAvailable.bind(this))
this.updater.on('download-progress', this.updateDownloadProgress.bind(this))
this.updater.on('update-downloaded', this.updateDownloaded.bind(this))
this.updater.on('update-cancelled', this.updateCancelled.bind(this))
this.updater.on('error', this.updateError.bind(this))
if (this.autoCheckData.checkEnable) {
if (this.autoCheckData.checkEnable && !this.isChecking) {
this.autoCheckData.userCheck = false
this.updater.checkForUpdates()
}
@@ -54,6 +57,7 @@ export default class UpdateManager extends EventEmitter {
}
checkingForUpdate () {
this.isChecking = true
this.emit('checking')
}
@@ -68,11 +72,14 @@ export default class UpdateManager extends EventEmitter {
}).then(({ response }) => {
if (response === 0) {
this.updater.downloadUpdate()
} else {
this.emit('update-cancelled', info)
}
})
}
updateNotAvailable (event, info) {
this.isChecking = false
this.emit('update-not-available', info)
if (this.autoCheckData.userCheck) {
dialog.showMessageBox({
@@ -102,20 +109,26 @@ export default class UpdateManager extends EventEmitter {
title: this.i18n.t('app.check-for-updates-title'),
message: this.i18n.t('app.update-downloaded-message')
}).then(_ => {
this.isChecking = false
this.emit('will-updated')
setImmediate(() => {
setTimeout(() => {
this.updater.quitAndInstall()
})
}, 200)
})
}
updateCancelled () {
this.isChecking = false
}
updateError (event, error) {
this.isChecking = false
this.emit('update-error', error)
const msg = (error == null)
? this.i18n.t('update-error-message')
? this.i18n.t('app.update-error-message')
: (error.stack || error).toString()
this.updater.logger.warn(`[Motrix] update-error: ${msg}`)
dialog.showErrorBox(msg)
dialog.showErrorBox('Error', msg)
}
}
-6
View File
@@ -7,12 +7,6 @@
/* eslint-disable */
// Install `electron-debug` with `devtron`
require('electron-debug')({
// devToolsMode: 'right',
showDevTools: true
})
// Install `vue-devtools`
require('electron').app.whenReady().then(() => {
let installExtension = require('electron-devtools-installer')
+6
View File
@@ -1,8 +1,14 @@
import { app } from 'electron'
import is from 'electron-is'
import { initialize } from '@electron/remote/main'
import Launcher from './Launcher'
/**
* initialize the main-process side of the remote module
*/
initialize()
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'
if (process.env.NODE_ENV !== 'development') {
+6 -5
View File
@@ -3,7 +3,7 @@
{
"id": "menu.app",
"submenu": [
{ "id": "app.about", "command": "application:about", "command-before": "application:show,index" },
{ "id": "app.about", "command": "application:about", "command-before": "application:show?page=index" },
{ "type": "separator" },
{ "id": "app.preferences", "command": "application:preferences" },
{ "id": "app.check-for-updates", "command": "application:check-for-updates" },
@@ -11,16 +11,17 @@
{ "id": "app.hide-others", "role": "hideothers" },
{ "id": "app.unhide", "role": "unhide" },
{ "type": "separator" },
{ "id": "app.quit", "command": "application:quit" }
{ "id": "app.quit", "role": "quit" }
]
},
{
"id": "menu.task",
"submenu": [
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show,index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show,index" },
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-after": "application:show?page=index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" },
{ "type": "separator" },
{ "id": "app.task-list", "command": "application:task-list" },
{ "id": "task.pause-task", "command": "application:pause-task" },
{ "id": "task.resume-task", "command": "application:resume-task" },
{ "id": "task.delete-task", "command": "application:delete-task" },
+7 -6
View File
@@ -3,22 +3,23 @@
{
"id": "menu.file",
"submenu": [
{ "id": "app.about", "command": "application:about", "command-before": "application:show,index" },
{ "id": "app.about", "command": "application:about", "command-before": "application:show?page=index" },
{ "type": "separator" },
{ "id": "app.preferences", "command": "application:preferences" },
{ "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "id": "app.show", "command": "application:show", "command-arg": "index" },
{ "id": "app.show", "command": "application:show", "command-arg": { "page": "index" } },
{ "type": "separator" },
{ "id": "app.quit", "command": "application:quit" }
{ "id": "app.quit", "role": "quit" }
]
},
{
"id": "menu.task",
"submenu": [
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show,index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show,index" },
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-after": "application:show?page=index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" },
{ "type": "separator" },
{ "id": "app.task-list", "command": "application:task-list" },
{ "id": "task.pause-task", "command": "application:pause-task" },
{ "id": "task.resume-task", "command": "application:resume-task" },
{ "id": "task.delete-task", "command": "application:delete-task" },
+5 -5
View File
@@ -1,6 +1,6 @@
[
{
"type": "button", "icon": "new-task", "id": "task.new-task", "command": "application:new-task", "command-arg": "uri", "command-after": "application:show,index"
"type": "button", "icon": "new-task", "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index"
},
{
"type": "spacer", "size": "small"
@@ -10,13 +10,13 @@
"id": "task.task-list",
"items": [
{
"type": "button", "icon": "task-active", "command": "application:task-list", "command-arg": "active"
"type": "button", "icon": "task-active", "command": "application:task-list", "command-arg": { "status": "active" }
},
{
"type": "button", "icon": "task-waiting", "command": "application:task-list", "command-arg": "waiting"
"type": "button", "icon": "task-waiting", "command": "application:task-list", "command-arg": { "status": "waiting" }
},
{
"type": "button", "icon": "task-stopped", "command": "application:task-list", "command-arg": "stopped"
"type": "button", "icon": "task-stopped", "command": "application:task-list", "command-arg": { "status": "stopped" }
}
]
},
@@ -30,6 +30,6 @@
"type": "spacer", "size": "small"
},
{
"type": "button", "icon": "about", "id": "app.about", "command": "application:about", "command-before": "application:show,index"
"type": "button", "icon": "about", "id": "app.about", "command": "application:about", "command-before": "application:show?page=index"
}
]
+6 -5
View File
@@ -1,12 +1,13 @@
[
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show,index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show,index" },
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": { "type": "torrent" }, "command-after": "application:show?page=index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" },
{ "type": "separator" },
{ "id": "app.show", "command": "application:show", "command-arg": "index" },
{ "id": "app.show", "command": "application:show", "command-arg": { "page": "index" } },
{ "id": "help.manual", "command": "help:manual" },
{ "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "type": "separator" },
{ "id": "app.preferences", "command": "application:preferences", "command-before": "application:show,index" },
{ "id": "app.task-list", "command": "application:task-list", "command-before": "application:show?page=index" },
{ "id": "app.preferences", "command": "application:preferences", "command-before": "application:show?page=index" },
{ "id": "app.quit", "command": "application:quit" }
]
+7 -6
View File
@@ -3,22 +3,23 @@
{
"id": "menu.file",
"submenu": [
{ "id": "app.about", "command": "application:about", "command-before": "application:show,index" },
{ "id": "app.about", "command": "application:about", "command-before": "application:show?page=index" },
{ "type": "separator" },
{ "id": "app.preferences", "command": "application:preferences" },
{ "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "id": "app.show", "command": "application:show", "command-arg": "index" },
{ "id": "app.show", "command": "application:show", "command-arg": { "page": "index" } },
{ "type": "separator" },
{ "id": "app.quit", "command": "application:quit" }
{ "id": "app.quit", "role": "quit" }
]
},
{
"id": "menu.task",
"submenu": [
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show,index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show,index" },
{ "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" },
{ "id": "task.new-bt-task", "command": "application:new-bt-task", "command-after": "application:show?page=index" },
{ "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" },
{ "type": "separator" },
{ "id": "app.task-list", "command": "application:task-list" },
{ "id": "task.pause-task", "command": "application:pause-task" },
{ "id": "task.resume-task", "command": "application:resume-task" },
{ "id": "task.delete-task", "command": "application:delete-task" },
+42 -14
View File
@@ -2,35 +2,63 @@ import is from 'electron-is'
import { EventEmitter } from 'events'
import { app } from 'electron'
import { bytesToSize } from '@shared/utils'
import {
APP_RUN_MODE
} from '@shared/constants'
const isMac = is.macOS()
const enabled = is.macOS()
export default class DockManager extends EventEmitter {
constructor (options) {
super()
this.options = options
const { runMode } = this.options
if (runMode !== APP_RUN_MODE.STANDARD) {
if (runMode === APP_RUN_MODE.TRAY) {
this.hide()
}
}
show = isMac ? () => {
return app.dock.show()
} : () => {}
show = enabled
? () => {
if (app.dock.isVisible()) {
return
}
hide = isMac ? () => {
app.dock.hide()
} : () => {}
return app.dock.show()
}
: () => {}
setBadge = isMac ? (text) => {
app.dock.setBadge(text)
} : (text) => {}
hide = enabled
? () => {
if (!app.dock.isVisible()) {
return
}
openDock = isMac ? (path) => {
app.dock.downloadFinished(path)
} : (path) => {}
app.dock.hide()
}
: () => {}
// macOS setBadge not working
// @see https://github.com/electron/electron/issues/25745#issuecomment-702826143
setBadge = enabled
? (text) => {
app.dock.setBadge(text)
}
: (text) => {}
handleSpeedChange = enabled
? (speed) => {
const { downloadSpeed } = speed
const text = downloadSpeed > 0 ? `${bytesToSize(downloadSpeed)}/s` : ''
this.setBadge(text)
}
: (text) => {}
openDock = enabled
? (path) => {
app.dock.downloadFinished(path)
}
: (path) => {}
}
+2 -2
View File
@@ -1,13 +1,13 @@
import { EventEmitter } from 'events'
import { Menu } from 'electron'
import keymap from '@shared/keymap'
import {
translateTemplate,
flattenMenuItems,
updateStates
} from '../utils/menu'
import keymap from '@shared/keymap'
import { getI18n } from '@/ui/Locale'
import { getI18n } from '../ui/Locale'
export default class MenuManager extends EventEmitter {
constructor (options) {
+6 -16
View File
@@ -1,8 +1,8 @@
import { EventEmitter } from 'events'
import { nativeTheme, systemPreferences } from 'electron'
import is from 'electron-is'
import { nativeTheme } from 'electron'
import { APP_THEME } from '@shared/constants'
import logger from '../core/Logger'
import { getSystemTheme } from '../utils'
export default class ThemeManager extends EventEmitter {
@@ -24,26 +24,16 @@ export default class ThemeManager extends EventEmitter {
}
handleEvents () {
if (!is.macOS()) {
return
}
nativeTheme.on('updated', () => {
const theme = getSystemTheme()
this.systemTheme = theme
console.log('nativeTheme updated===>', theme)
logger.info('[Motrix] nativeTheme updated===>', theme)
this.emit('system-theme-change', theme)
})
}
/**
* deprecated
* @see https://www.electronjs.org/docs/all#systempreferencessetapplevelappearanceappearance-macos-deprecated
*/
updateAppAppearance (theme) {
if (!is.macOS() || theme !== APP_THEME.LIGHT || theme !== APP_THEME.DARK) {
return
}
systemPreferences.setAppLevelAppearance(theme)
updateSystemTheme (theme) {
theme = theme === APP_THEME.AUTO ? 'system' : theme
nativeTheme.themeSource = theme
}
}
+255 -57
View File
@@ -1,54 +1,131 @@
import { EventEmitter } from 'events'
import { join } from 'path'
import { Tray, Menu, nativeTheme } from 'electron'
import { Tray, Menu, nativeImage } from 'electron'
import is from 'electron-is'
import { APP_RUN_MODE, APP_THEME } from '@shared/constants'
import { getInverseTheme } from '@shared/utils'
import logger from '../core/Logger'
import { getI18n } from './Locale'
import {
translateTemplate,
flattenMenuItems,
updateStates
} from '../utils/menu'
import { getI18n } from '@/ui/Locale'
import { APP_THEME } from '@shared/constants'
import { convertArrayBufferToBuffer } from '../utils/index'
let tray = null
const { platform } = process
export default class TrayManager extends EventEmitter {
constructor (options = {}) {
super()
this.options = options
this.theme = options.theme || APP_THEME.AUTO
this.systemTheme = options.systemTheme
this.inverseSystemTheme = getInverseTheme(this.systemTheme)
this.macOS = platform === 'darwin'
this.speedometer = options.speedometer
this.runMode = options.runMode
this.i18n = getI18n()
this.menu = null
this.cache = {}
this.uploadSpeed = 0
this.downloadSpeed = 0
this.status = false
this.focused = false
this.initialized = false
this.load()
this.init()
this.setup()
this.handleEvents()
}
load () {
this.template = require('../menus/tray.json')
let theme = APP_THEME.LIGHT
if (is.windows()) {
theme = 'colorful'
} else if (is.macOS()) {
theme = nativeTheme.shouldUseDarkColors ? APP_THEME.DARK : APP_THEME.LIGHT
} else if (is.linux()) {
theme = (this.theme === APP_THEME.AUTO) ? APP_THEME.DARK : this.theme
init () {
if (tray || this.initialized || this.runMode === APP_RUN_MODE.HIDE_TRAY) {
return
}
this.setIcons(theme)
this.loadTemplate()
this.loadImages()
this.initTray()
this.setupMenu()
this.bindEvents()
this.initialized = true
}
setIcons (theme) {
this.normalIcon = join(__static, `./mo-tray-${theme}-normal.png`)
this.activeIcon = join(__static, `./mo-tray-${theme}-active.png`)
loadTemplate () {
this.template = require('../menus/tray.json')
}
build () {
loadImages () {
switch (platform) {
case 'darwin':
this.loadImagesForMacOS()
break
case 'win32':
this.loadImagesForWindows()
break
case 'linux':
this.loadImagesForLinux()
break
default:
this.loadImagesForDefault()
break
}
}
loadImagesForMacOS () {
this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-light-normal.png')
}
loadImagesForWindows () {
this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-colorful-normal.png')
this.activeIcon = this.getFromCacheOrCreateImage('mo-tray-colorful-active.png')
}
loadImagesForLinux () {
const { theme } = this
if (theme === APP_THEME.AUTO) {
this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-dark-normal.png')
this.activeIcon = this.getFromCacheOrCreateImage('mo-tray-dark-active.png')
} else {
this.normalIcon = this.getFromCacheOrCreateImage(`mo-tray-${theme}-normal.png`)
this.activeIcon = this.getFromCacheOrCreateImage(`mo-tray-${theme}-active.png`)
}
}
loadImagesForDefault () {
this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-light-normal.png')
this.activeIcon = this.getFromCacheOrCreateImage('mo-tray-light-active.png')
}
getFromCacheOrCreateImage (key) {
let file = this.getCache(key)
if (file) {
return file
}
file = nativeImage.createFromPath(join(__static, `./${key}`))
file.setTemplateImage(this.macOS)
this.setCache(key, file)
return file
}
getCache (key) {
return this.cache[key]
}
setCache (key, value) {
this.cache[key] = value
}
buildMenu () {
const keystrokesByCommand = {}
for (const item in this.keymap) {
keystrokesByCommand[this.keymap[item]] = item
@@ -61,69 +138,139 @@ export default class TrayManager extends EventEmitter {
this.items = flattenMenuItems(this.menu)
}
setup () {
this.build()
setupMenu () {
this.buildMenu()
this.updateContextMenu()
}
init () {
tray = new Tray(this.normalIcon)
tray.setToolTip('Motrix')
initTray () {
const { icon } = this.getIcons()
tray = new Tray(icon)
// tray.setPressedImage(inverseIcon)
if (!this.macOS) {
tray.setToolTip('Motrix')
}
}
handleEvents () {
bindEvents () {
// All OS
tray.on('click', this.handleTrayClick)
// macOS, Windows
tray.on('double-click', this.handleTrayDbClick)
// tray.on('double-click', this.handleTrayDbClick)
tray.on('right-click', this.handleTrayRightClick)
tray.on('mouse-down', this.handleTrayMouseDown)
tray.on('mouse-up', this.handleTrayMouseUp)
// macOS only
tray.on('drop-files', this.handleTrayDropFile)
tray.setIgnoreDoubleClickEvents(true)
tray.on('drop-files', this.handleTrayDropFiles)
tray.on('drop-text', this.handleTrayDropText)
}
unbindEvents () {
// All OS
tray.removeListener('click', this.handleTrayClick)
// macOS, Windows
tray.removeListener('right-click', this.handleTrayRightClick)
tray.removeListener('mouse-down', this.handleTrayMouseDown)
tray.removeListener('mouse-up', this.handleTrayMouseUp)
// macOS only
tray.removeListener('drop-files', this.handleTrayDropFiles)
tray.removeListener('drop-text', this.handleTrayDropText)
}
handleTrayClick = (event) => {
event.preventDefault()
global.application.toggle()
}
handleTrayDbClick = (event) => {
event.preventDefault()
global.application.show()
}
handleTrayRightClick = (event) => {
event.preventDefault()
tray.popUpContextMenu(this.menu)
}
handleTrayDropFile = (event, files) => {
global.application.show()
global.application.handleFile(files[0])
handleTrayMouseDown = (event) => {
this.focused = true
this.emit('mouse-down', {
focused: true,
theme: this.inverseSystemTheme
})
this.renderTray()
}
updateTrayByStatus (status) {
this.status = status
this.updateTray()
handleTrayMouseUp = (event) => {
this.focused = false
this.emit('mouse-up', {
focused: false,
theme: this.theme
})
this.renderTray()
}
updateTray () {
const icon = this.status ? this.activeIcon : this.normalIcon
handleTrayDropFiles = (event, files) => {
this.emit('drop-files', files)
}
handleTrayDropText = (event, text) => {
this.emit('drop-text', text)
}
toggleSpeedometer (enabled) {
this.speedometer = enabled
}
async renderTray () {
if (!tray || this.speedometer) {
return
}
const { icon } = this.getIcons()
tray.setImage(icon)
// tray.setPressedImage(inverseIcon)
this.updateContextMenu()
}
changeIconTheme (theme = APP_THEME.LIGHT) {
if (!is.macOS()) {
getIcons () {
if (this.macOS) {
return { icon: this.normalIcon }
}
const { focused, status, systemTheme } = this
const icon = status ? this.activeIcon : this.normalIcon
if (systemTheme === APP_THEME.DARK) {
return {
icon
}
}
const inverseIcon = status ? this.inverseActiveIcon : this.inverseNormalIcon
return {
icon: focused ? inverseIcon : icon
// inverseIcon: focused ? icon : inverseIcon
}
}
updateContextMenu () {
/**
* Linux requires setContextMenu to be called
* in order for the context menu to populate correctly
*/
if (!tray || process.platform !== 'linux') {
return
}
this.setIcons(theme)
this.updateTray()
tray.setContextMenu(this.menu)
}
updateMenuStates (visibleStates, enabledStates, checkedStates) {
@@ -146,26 +293,77 @@ export default class TrayManager extends EventEmitter {
this.updateMenuStates(null, enabledStates, null)
}
updateContextMenu () {
/**
* Linux requires setContextMenu to be called
* in order for the context menu to populate correctly
*/
if (process.platform !== 'linux') {
handleLocaleChange (locale) {
this.setupMenu()
}
handleRunModeChange (mode) {
this.runMode = mode
if (mode === APP_RUN_MODE.HIDE_TRAY) {
this.destroy()
} else {
this.init()
}
}
handleSpeedometerEnableChange (enabled) {
this.toggleSpeedometer(enabled)
this.renderTray()
}
handleSystemThemeChange (systemTheme = APP_THEME.LIGHT) {
if (!is.macOS()) {
return
}
tray.setContextMenu(this.menu)
this.systemTheme = systemTheme
this.inverseSystemTheme = getInverseTheme(systemTheme)
this.loadImages()
this.renderTray()
}
handleDownloadStatusChange (status) {
this.status = status
this.renderTray()
}
async handleSpeedChange ({ uploadSpeed, downloadSpeed }) {
if (!this.speedometer) {
return
}
this.uploadSpeed = uploadSpeed
this.downloadSpeed = downloadSpeed
await this.renderTray()
}
async updateTrayByImage (ab) {
if (!tray) {
return
}
const buffer = convertArrayBufferToBuffer(ab)
const image = nativeImage.createFromBuffer(buffer, {
scaleFactor: 2
})
image.setTemplateImage(this.macOS)
tray.setImage(image)
}
destroy () {
logger.info('[Motrix] TrayManager.destroy')
if (tray) {
tray.removeListener('click', this.handleTrayClick)
tray.removeListener('double-click', this.handleTrayDbClick)
tray.removeListener('right-click', this.handleTrayRightClick)
tray.removeListener('drop-files', this.handleTrayDropFile)
this.unbindEvents()
}
tray.destroy()
tray = null
this.initialized = false
}
}
+35 -10
View File
@@ -1,22 +1,35 @@
import { join } from 'path'
import { EventEmitter } from 'events'
import { debounce } from 'lodash'
import { app, shell, screen, BrowserWindow } from 'electron'
import is from 'electron-is'
import pageConfig from '../configs/page'
import logger from '../core/Logger'
import { debounce } from 'lodash'
const defaultBrowserOptions = {
const baseBrowserOptions = {
titleBarStyle: 'hiddenInset',
show: false,
width: 1024,
height: 768,
backgroundColor: '#fff',
webPreferences: {
nodeIntegration: true,
webviewTag: true
nodeIntegration: true
}
}
// fix: BrowserWindow rendering bug under linux
const defaultBrowserOptions = is.macOS()
? {
...baseBrowserOptions,
vibrancy: 'ultra-dark',
visualEffectState: 'active',
backgroundColor: '#00000000'
}
: {
...baseBrowserOptions
}
export default class WindowManager extends EventEmitter {
constructor (options = {}) {
super()
@@ -82,7 +95,13 @@ export default class WindowManager extends EventEmitter {
window = new BrowserWindow({
...defaultBrowserOptions,
...pageOptions.attrs
...pageOptions.attrs,
webPreferences: {
enableRemoteModule: true,
contextIsolation: false,
nodeIntegration: true,
nodeIntegrationInWorker: true
}
})
const bounds = this.getPageBounds(page)
@@ -90,9 +109,13 @@ export default class WindowManager extends EventEmitter {
window.setBounds(bounds)
}
window.webContents.on('new-window', (e, url) => {
e.preventDefault()
if (is.dev() && pageOptions.openDevTools) {
window.webContents.openDevTools()
}
window.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url)
return { action: 'deny' }
})
if (pageOptions.url) {
@@ -123,6 +146,7 @@ export default class WindowManager extends EventEmitter {
if (autoHideWindow) {
this.handleWindowBlur()
}
return window
}
@@ -194,15 +218,16 @@ export default class WindowManager extends EventEmitter {
showWindow (page) {
const window = this.getWindow(page)
if (!window) {
if (!window || (window.isVisible() && !window.isMinimized())) {
return
}
window.show()
}
autoHideWindow (page) {
hideWindow (page) {
const window = this.getWindow(page)
if (!window) {
if (!window || !window.isVisible()) {
return
}
window.hide()
+55 -8
View File
@@ -8,12 +8,10 @@ import {
ENGINE_MAX_CONNECTION_PER_SERVER,
IP_VERSION
} from '@shared/constants'
import logger from '../core/Logger'
import engineBinMap from '../configs/engine'
import { engineBinMap, engineArchMap } from '../configs/engine'
export function getLogPath () {
return logger.transports.file.file
return app.getPath('logs')
}
export function getDhtPath (protocol) {
@@ -25,6 +23,10 @@ export function getSessionPath () {
return resolve(app.getPath('userData'), './download.session')
}
export function getEnginePidPath () {
return resolve(app.getPath('userData'), './engine.pid')
}
export function getUserDataPath () {
return app.getPath('userData')
}
@@ -38,6 +40,42 @@ export function getEngineBin (platform) {
return result
}
export function getEngineArch (platform, arch) {
if (!['darwin', 'win32', 'linux'].includes(platform)) {
return ''
}
const result = engineArchMap[platform][arch]
return result
}
export const getDevEnginePath = (platform, arch) => {
const ah = getEngineArch(platform, arch)
const base = `../../../extra/${platform}/${ah}/engine`
const result = resolve(__dirname, base)
return result
}
export const getProdEnginePath = () => {
return resolve(app.getAppPath(), '../engine')
}
export const getEnginePath = (platform, arch) => {
return is.dev() ? getDevEnginePath(platform, arch) : getProdEnginePath()
}
export const getAria2BinPath = (platform, arch) => {
const base = getEnginePath(platform, arch)
const binName = getEngineBin(platform)
const result = resolve(base, `./${binName}`)
return result
}
export const getAria2ConfPath = (platform, arch) => {
const base = getEnginePath(platform, arch)
return resolve(base, './aria2.conf')
}
export function transformConfig (config) {
const result = []
for (const [k, v] of Object.entries(config)) {
@@ -102,13 +140,13 @@ export function parseArgvAsUrl (argv) {
export function checkIsSupportedSchema (url = '') {
const str = url.toLowerCase()
if (
str.startsWith('mo:') ||
str.startsWith('motrix:') ||
str.startsWith('ftp:') ||
str.startsWith('http:') ||
str.startsWith('https:') ||
str.startsWith('ftp:') ||
str.startsWith('magnet:') ||
str.startsWith('thunder:')
str.startsWith('thunder:') ||
str.startsWith('mo:') ||
str.startsWith('motrix:')
) {
return true
} else {
@@ -141,3 +179,12 @@ export const getSystemTheme = () => {
result = nativeTheme.shouldUseDarkColors ? APP_THEME.DARK : APP_THEME.LIGHT
return result
}
export const convertArrayBufferToBuffer = (arrayBuffer) => {
const buffer = Buffer.alloc(arrayBuffer.byteLength)
const view = new Uint8Array(arrayBuffer)
for (let i = 0; i < buffer.length; ++i) {
buffer[i] = view[i]
}
return buffer
}
+8 -4
View File
@@ -1,3 +1,5 @@
import { parse } from 'querystring'
export function concat (template, submenu, submenuToAdd) {
submenuToAdd.forEach(sub => {
let relativeItem = null
@@ -115,16 +117,18 @@ function handleCommandBefore (item) {
if (!item['command-before']) {
return
}
const [command, ...args] = item['command-before'].split(',')
global.application.sendCommandToAll(command, ...args)
const [command, params] = item['command-before'].split('?')
const args = parse(params)
global.application.sendCommandToAll(command, args)
}
function handleCommandAfter (item) {
if (!item['command-after']) {
return
}
const [command, ...args] = item['command-after'].split(',')
global.application.sendCommandToAll(command, ...args)
const [command, params] = item['command-after'].split('?')
const args = parse(params)
global.application.sendCommandToAll(command, args)
}
function acceleratorForCommand (command, keystrokesByCommand) {
+38 -13
View File
@@ -1,7 +1,7 @@
import { ipcRenderer, remote } from 'electron'
import { ipcRenderer } from 'electron'
import is from 'electron-is'
import { isEmpty, clone } from 'lodash'
import Aria2 from 'aria2'
import { Aria2 } from '@shared/aria2'
import {
separateConfig,
compactUndefined,
@@ -12,8 +12,6 @@ import {
} from '@shared/utils'
import { ENGINE_RPC_HOST } from '@shared/constants'
const application = remote.getGlobal('application')
export default class Api {
constructor (options = {}) {
this.options = options
@@ -21,8 +19,8 @@ export default class Api {
this.init()
}
init () {
this.config = this.loadConfig()
async init () {
this.config = await this.loadConfig()
this.client = this.initClient()
this.client.open()
@@ -34,17 +32,14 @@ export default class Api {
return result
}
loadConfigFromNativeStore () {
const systemConfig = application.configManager.getSystemConfig()
const userConfig = application.configManager.getUserConfig()
const result = { ...systemConfig, ...userConfig }
async loadConfigFromNativeStore () {
const result = await ipcRenderer.invoke('get-app-config')
return result
}
loadConfig () {
async loadConfig () {
let result = is.renderer()
? this.loadConfigFromNativeStore()
? await this.loadConfigFromNativeStore()
: this.loadConfigFromLocalStorage()
result = changeKeysToCamelCase(result)
@@ -240,6 +235,12 @@ export default class Api {
return this.client.call('tellStopped', ...args)
}
fetchActiveTaskList (params = {}) {
const { keys } = params
const args = compactUndefined([keys])
return this.client.call('tellActive', ...args)
}
fetchTaskList (params = {}) {
const { type } = params
switch (type) {
@@ -260,6 +261,30 @@ export default class Api {
return this.client.call('tellStatus', ...args)
}
fetchTaskItemWithPeers (params = {}) {
const { gid, keys } = params
const statusArgs = compactUndefined([gid, keys])
const peersArgs = compactUndefined([gid])
return new Promise((resolve, reject) => {
this.client.multicall([
['aria2.tellStatus', ...statusArgs],
['aria2.getPeers', ...peersArgs]
]).then((data) => {
console.log('[Motrix] fetchTaskItemWithPeers:', data)
const result = data[0] && data[0][0]
const peers = data[1] && data[1][0]
result.peers = peers || []
console.log('[Motrix] fetchTaskItemWithPeers.result:', result)
console.log('[Motrix] fetchTaskItemWithPeers.peers:', peers)
resolve(result)
}).catch((err) => {
console.log('[Motrix] fetch downloading task list fail:', err)
reject(err)
})
})
}
fetchTaskItemPeers (params = {}) {
const { gid, keys } = params
const args = compactUndefined([gid, keys])
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g stroke-linecap="square" stroke-linejoin="miter" stroke-width="2" fill="#000000" stroke="#000000">
<line fill="none" stroke-miterlimit="10" x1="8.2" y1="4.5" x2="11.1" y2="7.3"></line>
<line fill="none" stroke-miterlimit="10" x1="16.7" y1="12.9" x2="19.5" y2="15.8"></line>
<path fill="none" stroke="#000000" stroke-miterlimit="10"
d="M12.5,17.2 c-1.6,1.6-4.1,1.6-5.7,0c-1.6-1.6-1.6-4.1,0-5.7l7.1-7.1l-2.8-2.8L4,8.7C0.9,11.8,0.9,16.9,4,20s8.2,3.1,11.3,0l7.1-7.1l-2.8-2.8 L12.5,17.2z">
</path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 617 B

View File
+10 -9
View File
@@ -2,7 +2,7 @@
<el-dialog
custom-class="app-about-dialog"
width="61.8vw"
:visible.sync="visible"
:visible="visible"
@open="handleOpen"
:before-close="handleClose"
@closed="handleClosed">
@@ -15,6 +15,7 @@
import { mapState } from 'vuex'
import AppInfo from '@/components/About/AppInfo'
import Copyright from '@/components/About/Copyright'
import { app } from '@electron/remote'
export default {
name: 'mo-about-panel',
@@ -29,7 +30,7 @@
}
},
data () {
const version = this.$electron.remote.app.getVersion()
const version = app.getVersion()
return {
version
}
@@ -53,12 +54,12 @@
</script>
<style lang="scss">
.app-about-dialog {
max-width: 632px;
min-width: 380px;
.el-dialog__header {
padding-top: 0;
padding-bottom: 0;
}
.app-about-dialog {
max-width: 632px;
min-width: 380px;
.el-dialog__header {
padding-top: 0;
padding-bottom: 0;
}
}
</style>
+37 -37
View File
@@ -49,45 +49,45 @@
</script>
<style lang="scss">
.app-info {
position: relative;
margin: 8px 0;
.app-version span {
display: inline-block;
vertical-align: bottom;
font-size: $--font-size-large;
margin-left: 20px;
color: $--app-version-color;
line-height: 18px;
.app-info {
position: relative;
margin: 8px 0;
.app-version span {
display: inline-block;
vertical-align: bottom;
font-size: $--font-size-large;
margin-left: 20px;
color: $--app-version-color;
line-height: 18px;
}
.app-icon {
position: absolute;
top: 0;
right: 0;
background: transparent url('~@/assets/app-icon.png') center center no-repeat;
background-size: 128px 128px;
width: 128px;
height: 128px;
}
.engine-info {
margin: 50px 35% 0 8px;
h4 {
font-size: $--font-size-base;
font-weight: normal;
color: $--app-engine-title-color;
}
.app-icon {
position: absolute;
top: 0;
right: 0;
background: transparent url('~@/assets/app-icon.png') center center no-repeat;
background-size: 128px 128px;
width: 128px;
height: 128px;
}
.engine-info {
margin: 50px 35% 0 8px;
h4 {
font-size: $--font-size-base;
font-weight: normal;
color: $--app-engine-title-color;
}
ul {
font-size: 12px;
color: $--app-engine-info-color;
list-style: none;
padding: 0;
line-height: 20px;
@include clearfix();
li {
float: left;
width: 50%;
}
ul {
font-size: 12px;
color: $--app-engine-info-color;
list-style: none;
padding: 0;
line-height: 20px;
@include clearfix();
li {
float: left;
width: 50%;
}
}
}
}
</style>
+23 -17
View File
@@ -2,7 +2,7 @@
<el-row class="copyright">
<el-col :span="6" class="copyright-left">
<a target="_blank" rel="noopener noreferrer" href="https://motrix.app/">
&copy;2019 Motrix
&copy;{{ year }} Motrix
</a>
</el-col>
<el-col :span="18" class="copyright-right">
@@ -24,27 +24,33 @@
<script>
export default {
name: 'mo-copyright'
name: 'mo-copyright',
data () {
const year = new Date().getFullYear()
return {
year
}
}
}
</script>
<style lang="scss">
.copyright {
width: 100%;
font-size: $--font-size-small;
a {
color: $--app-copyright-color;
text-decoration: none;
}
}
.copyright-left {
text-align: left;
.copyright {
width: 100%;
font-size: $--font-size-small;
a {
color: $--app-copyright-color;
text-decoration: none;
}
}
.copyright-left {
text-align: left;
}
.copyright-right {
text-align: right;
a {
margin-left: 30px;
}
.copyright-right {
text-align: right;
a {
margin-left: 30px;
}
}
</style>
+41 -34
View File
@@ -1,5 +1,5 @@
<template>
<el-aside width="78px" :class="['aside', 'hidden-sm-and-down', { 'draggable': asideDraggable }]">
<el-aside width="78px" :class="['aside', 'hidden-sm-and-down', { 'draggable': asideDraggable }]" :style="vibrancy">
<div class="aside-inner">
<mo-logo-mini />
<ul class="menu top-menu">
@@ -43,6 +43,13 @@
}),
asideDraggable () {
return is.macOS()
},
vibrancy () {
return is.macOS()
? {
backgroundColor: 'transparent'
}
: {}
}
},
methods: {
@@ -64,40 +71,40 @@
</script>
<style lang="scss">
.aside-inner {
display: flex;
height: 100%;
flex-flow: column;
}
.logo-mini {
margin-top: 40px;
}
.menu {
list-style: none;
padding: 0;
margin: 0 auto;
user-select: none;
cursor: default;
> li {
width: 32px;
height: 32px;
margin-top: 24px;
cursor: pointer;
border-radius: 16px;
transition: background-color 0.25s;
&:hover {
background-color: rgba(255, 255, 255, 0.15);
}
}
svg {
padding: 6px;
color: #fff;
.aside-inner {
display: flex;
height: 100%;
flex-flow: column;
}
.logo-mini {
margin-top: 40px;
}
.menu {
list-style: none;
padding: 0;
margin: 0 auto;
user-select: none;
cursor: default;
> li {
width: 32px;
height: 32px;
margin-top: 24px;
cursor: pointer;
border-radius: 16px;
transition: background-color 0.25s;
&:hover {
background-color: rgba(255, 255, 255, 0.15);
}
}
.top-menu {
flex: 1;
}
.bottom-menu {
margin-bottom: 24px;
svg {
padding: 6px;
color: #fff;
}
}
.top-menu {
flex: 1;
}
.bottom-menu {
margin-bottom: 24px;
}
</style>
+12 -15
View File
@@ -1,21 +1,18 @@
<template>
<div ref="webviewViewport" class="webview-viewport">
<webview
<iframe
class="mo-webview"
ref="webview"
ref="iframe"
:src="src"
></webview>
></iframe>
</div>
</template>
<script>
import is from 'electron-is'
import { webContents } from '@electron/remote'
import { Loading } from 'element-ui'
import {
openExternal
} from '@/utils/native'
export default {
name: 'mo-browser',
components: {
@@ -35,11 +32,11 @@
isRenderer: () => is.renderer()
},
mounted () {
const { webview } = this.$refs
const { iframe } = this.$refs
webview.addEventListener('did-start-loading', this.loadStart.bind(this))
webview.addEventListener('did-stop-loading', this.loadStop.bind(this))
webview.addEventListener('dom-ready', this.ready.bind(this))
iframe.addEventListener('did-start-loading', this.loadStart.bind(this))
iframe.addEventListener('did-stop-loading', this.loadStop.bind(this))
iframe.addEventListener('dom-ready', this.ready.bind(this))
},
methods: {
loadStart () {
@@ -54,12 +51,12 @@
})
},
ready () {
const { webview } = this.$refs
const { iframe } = this.$refs
const webContents = this.$electron.remote.webContents.fromId(webview.getWebContentsId())
webContents.on('new-window', (event, url) => {
const wc = webContents.fromId(iframe.getWebContentsId())
wc.setWindowOpenHandler((event, url) => {
event.preventDefault()
openExternal(url)
this.$electron.ipcRenderer.send('command', 'application:open-external', url)
})
}
}
@@ -30,7 +30,7 @@ export default class CommandManager extends EventEmitter {
}
execute (id, ...args) {
var fn = this.commands[id]
const fn = this.commands[id]
if (fn) {
try {
this.emit('beforeExecuteCommand', id)
+7 -8
View File
@@ -8,6 +8,11 @@
</template>
<script>
const getCoords = (e, containerRect) => ({
x: e.clientX - containerRect.left,
y: e.clientY - containerRect.top
})
const getDimensions = (p1, p2) => ({
width: Math.abs(p1.x - p2.x),
height: Math.abs(p1.y - p2.y)
@@ -51,12 +56,6 @@
const self = this
let containerRect = container.getBoundingClientRect()
const getCoords = e => ({
x: e.clientX - containerRect.left,
y: e.clientY - containerRect.top
})
const box = this.createBox()
let start = { x: 0, y: 0 }
let end = { x: 0, y: 0 }
@@ -74,7 +73,7 @@
function startDrag (e) {
containerRect = container.getBoundingClientRect()
self.children = container.childNodes
start = getCoords(e)
start = getCoords(e, containerRect)
end = start
document.addEventListener('mousemove', drag)
document.addEventListener('touchmove', touchMove)
@@ -87,7 +86,7 @@
}
function drag (e) {
end = getCoords(e)
end = getCoords(e, containerRect)
const dimensions = getDimensions(start, end)
if (end.x < start.x) {
+54 -52
View File
@@ -1,5 +1,6 @@
<template>
<svg version="1.1"
<svg
version="1.1"
:class="klass"
:role="label ? 'img' : 'presentation'"
:aria-label="label"
@@ -8,60 +9,25 @@
:width="width"
:height="height"
:viewBox="box"
:style="style">
:style="style"
>
<slot>
<template v-if="icon && icon.paths">
<path v-for="(path, i) in icon.paths" :key="`path-${i}`" v-bind="path"/>
<path v-for="(path, i) in icon.paths" :key="`path-${i}`" v-bind="path" />
</template>
<template v-if="icon && icon.polygons">
<polygon v-for="(polygon, i) in icon.polygons" :key="`polygon-${i}`" v-bind="polygon"/>
<polygon v-for="(polygon, i) in icon.polygons" :key="`polygon-${i}`" v-bind="polygon" />
</template>
<template v-if="icon && icon.raw"><g v-html="raw" v-bind="icon.g"></g></template>
<template v-if="icon && icon.raw"><g v-bind="icon.g" v-html="raw" /></template>
</slot>
</svg>
</template>
<style>
.fa-icon {
display: inline-block;
fill: currentColor;
}
.fa-flip-horizontal {
transform: scale(-1, 1);
}
.fa-flip-vertical {
transform: scale(1, -1);
}
.fa-spin {
animation: fa-spin 0.5s 0s infinite linear;
}
.fa-inverse {
color: #fff;
}
.fa-pulse {
animation: fa-spin 1s infinite steps(8);
}
@keyframes fa-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<script>
const icons = {}
export default {
name: 'fa-icon',
name: 'mo-icon',
props: {
name: {
type: String,
@@ -106,12 +72,12 @@
},
klass () {
return {
'fa-icon': true,
'fa-spin': this.spin,
'fa-flip-horizontal': this.flip === 'horizontal',
'fa-flip-vertical': this.flip === 'vertical',
'fa-inverse': this.inverse,
'fa-pulse': this.pulse,
'mo-icon': true,
'mo-spin': this.spin,
'mo-flip-horizontal': this.flip === 'horizontal',
'mo-flip-vertical': this.flip === 'vertical',
'mo-inverse': this.inverse,
'mo-pulse': this.pulse,
[this.$options.name]: true
}
},
@@ -184,7 +150,7 @@
let width = 0
let height = 0
this.$children.forEach(child => {
this.$children.forEach((child) => {
child.outerScale = this.normalizedScale
width = Math.max(width, child.width)
@@ -192,7 +158,7 @@
})
this.childrenWidth = width
this.childrenHeight = height
this.$children.forEach(child => {
this.$children.forEach((child) => {
child.x = (width - child.width) / 2
child.y = (height - child.height) / 2
})
@@ -221,8 +187,44 @@
icons
}
let cursor = 0xd4937
let cursor = 0xD4937
function getId () {
return `fa-${(cursor++).toString(16)}`
return `mo-${(cursor++).toString(16)}`
}
</script>
<style>
.mo-icon {
display: inline-block;
fill: currentColor;
}
.mo-flip-horizontal {
transform: scale(-1, 1);
}
.mo-flip-vertical {
transform: scale(1, -1);
}
.mo-spin {
animation: mo-spin 0.5s 0s infinite linear;
}
.mo-inverse {
color: #fff;
}
.mo-pulse {
animation: mo-spin 1s infinite steps(8);
}
@keyframes mo-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
+20
View File
@@ -0,0 +1,20 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'magnet': {
'width': 24,
'height': 24,
'raw': `
<line fill="none" stroke-miterlimit="10" x1="8.2" y1="4.5" x2="11.1" y2="7.3"></line>
<line fill="none" stroke-miterlimit="10" x1="16.7" y1="12.9" x2="19.5" y2="15.8"></line>
<path fill="none" stroke-miterlimit="10"
d="M12.5,17.2 c-1.6,1.6-4.1,1.6-5.7,0c-1.6-1.6-1.6-4.1,0-5.7l7.1-7.1l-2.8-2.8L4,8.7C0.9,11.8,0.9,16.9,4,20s8.2,3.1,11.3,0l7.1-7.1l-2.8-2.8 L12.5,17.2z">
</path>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2'
}
}
})
+1
View File
@@ -38,6 +38,7 @@
width: 100%;
height: 100%;
outline: none;
border: none;
text-align: center;
font-size: 0;
color: $--app-logo-color;
+2 -5
View File
@@ -10,11 +10,7 @@
<script>
export default {
name: 'mo-logo-mini',
components: {
},
methods: {
}
name: 'mo-logo-mini'
}
</script>
@@ -29,6 +25,7 @@
height: 28px;
text-align: center;
font-size: 0;
outline: none;
padding: 2px;
margin: 0 auto;
}
+15 -6
View File
@@ -5,7 +5,13 @@
<mo-speedometer />
<mo-add-task :visible="addTaskVisible" :type="addTaskType" />
<mo-about-panel :visible="aboutPanelVisible" />
<mo-task-item-info :visible="taskItemInfoVisible" :task="currentTaskItem" />
<mo-task-detail
:visible="taskDetailVisible"
:gid="currentTaskGid"
:task="currentTaskItem"
:files="currentTaskFiles"
:peers="currentTaskPeers"
/>
<mo-dragger />
</el-container>
</template>
@@ -16,7 +22,7 @@
import Aside from '@/components/Aside/Index'
import Speedometer from '@/components/Speedometer/Speedometer'
import AddTask from '@/components/Task/AddTask'
import TaskItemInfo from '@/components/Task/TaskItemInfo'
import TaskDetail from '@/components/TaskDetail/Index'
import Dragger from '@/components/Dragger/Index'
export default {
@@ -26,7 +32,7 @@
[Aside.name]: Aside,
[Speedometer.name]: Speedometer,
[AddTask.name]: AddTask,
[TaskItemInfo.name]: TaskItemInfo,
[TaskDetail.name]: TaskDetail,
[Dragger.name]: Dragger
},
computed: {
@@ -36,8 +42,11 @@
addTaskType: state => state.addTaskType
}),
...mapState('task', {
taskItemInfoVisible: state => state.taskItemInfoVisible,
currentTaskItem: state => state.currentTaskItem
taskDetailVisible: state => state.taskDetailVisible,
currentTaskGid: state => state.currentTaskGid,
currentTaskItem: state => state.currentTaskItem,
currentTaskFiles: state => state.currentTaskFiles,
currentTaskPeers: state => state.currentTaskPeers
})
},
methods: {
@@ -48,7 +57,7 @@
<style lang="scss">
.mo-speedometer {
position: fixed;
right: 36px;
right: 16px;
bottom: 24px;
z-index: 20;
}
@@ -0,0 +1,106 @@
<template>
<div style="display: none;">
<img
id="tray-icon-light-normal"
src="static/mo-tray-light-normal@2x.png"
>
<img
id="tray-icon-light-active"
src="static/mo-tray-light-active@2x.png"
>
<img
id="tray-icon-dark-normal"
src="static/mo-tray-dark-normal@2x.png"
>
<img
id="tray-icon-dark-active"
src="static/mo-tray-dark-active@2x.png"
>
</div>
</template>
<script>
import { mapState } from 'vuex'
import { getInverseTheme } from '@shared/utils'
import { APP_THEME } from '@shared/constants'
const cache = {}
export default {
name: 'mo-dynamic-tray',
computed: {
...mapState('app', {
iconStatus: state => state.stat.numActive > 0 ? 'active' : 'normal',
theme: state => state.systemTheme,
focused: state => state.trayFocused,
uploadSpeed: state => state.stat.uploadSpeed,
downloadSpeed: state => state.stat.downloadSpeed,
speed: state => state.stat.uploadSpeed + state.stat.downloadSpeed
}),
scale () {
return 2
},
currentTheme () {
const { theme, focused } = this
if (theme === APP_THEME.DARK) {
return theme
}
return focused ? getInverseTheme(theme) : theme
},
iconKey () {
const { bigSur, iconStatus, currentTheme } = this
return bigSur ? 'tray-icon-light-normal' : `tray-icon-${currentTheme}-${iconStatus}`
}
},
watch: {
async speed (val) {
await this.drawTray()
},
async iconKey (val) {
await this.drawTray()
}
},
mounted () {
setTimeout(async () => {
await this.drawTray()
}, 200)
},
methods: {
async getIcon (key) {
if (cache[key]) {
return cache[key]
}
const iconImage = document.getElementById(key)
const result = await createImageBitmap(iconImage)
cache[key] = result
return result
},
async drawTray () {
const {
currentTheme: theme,
uploadSpeed,
downloadSpeed,
scale,
iconKey
} = this
const icon = await this.getIcon(iconKey)
global.app.trayWorker.postMessage({
type: 'tray:draw',
payload: {
theme,
icon,
uploadSpeed,
downloadSpeed,
scale
}
})
}
}
}
</script>
+55 -32
View File
@@ -7,53 +7,57 @@
import { mapState } from 'vuex'
import api from '@/api'
import {
showItemInFolder,
addToRecentTask
getTaskFullPath,
showItemInFolder
} from '@/utils/native'
import {
bytesToSize,
getTaskName,
getTaskFullPath
} from '@shared/utils'
import { checkTaskIsBT, getTaskName } from '@shared/utils'
export default {
name: 'mo-engine-client',
data () {
return {
downloading: false
}
},
computed: {
isRenderer: () => is.renderer(),
...mapState('app', {
uploadSpeed: state => state.stat.uploadSpeed,
downloadSpeed: state => state.stat.downloadSpeed,
speed: state => state.stat.uploadSpeed + state.stat.downloadSpeed,
interval: state => state.interval,
numActive: state => state.stat.numActive
downloading: state => state.stat.numActive > 0,
progress: state => state.progress
}),
...mapState('task', {
taskItemInfoVisible: state => state.taskItemInfoVisible,
messages: state => state.messages,
seedingList: state => state.seedingList,
taskDetailVisible: state => state.taskDetailVisible,
enabledFetchPeers: state => state.enabledFetchPeers,
currentTaskGid: state => state.currentTaskGid,
currentTaskItem: state => state.currentTaskItem
}),
...mapState('preference', {
taskNotification: state => state.config.taskNotification
})
}),
currentTaskIsBT () {
return checkTaskIsBT(this.currentTaskItem)
}
},
watch: {
downloadSpeed (val) {
const speed = val > 0 ? `${bytesToSize(val)}/s` : ''
this.$electron.ipcRenderer.send('event', 'download-speed-change', speed)
},
numActive (val) {
this.downloading = val > 0
speed (val) {
const { uploadSpeed, downloadSpeed } = this
this.$electron.ipcRenderer.send('event', 'speed-change', {
uploadSpeed,
downloadSpeed
})
},
downloading (val, oldVal) {
if (val !== oldVal && this.isRenderer) {
this.$electron.ipcRenderer.send('event', 'download-status-change', val)
}
},
progress (val) {
this.$electron.ipcRenderer.send('event', 'progress-change', val)
}
},
methods: {
fetchTaskItem ({ gid }) {
async fetchTaskItem ({ gid }) {
return api.fetchTaskItem({ gid })
.catch((e) => {
console.warn(`fetchTaskItem fail: ${e.message}`)
@@ -63,18 +67,28 @@
this.$store.dispatch('task/fetchList')
this.$store.dispatch('app/resetInterval')
this.$store.dispatch('task/saveSession')
console.log('aria2 onDownloadStart', event)
const [{ gid }] = event
const { seedingList } = this
if (seedingList.includes(gid)) {
return
}
this.fetchTaskItem({ gid })
.then((task) => {
const { dir } = task
this.$store.dispatch('preference/recordHistoryDirectory', dir)
const taskName = getTaskName(task)
const message = this.$t('task.download-start-message', { taskName })
this.$msg.info(message)
})
},
onDownloadPause (event) {
console.log('aria2 onDownloadPause')
const [{ gid }] = event
const { seedingList } = this
if (seedingList.includes(gid)) {
return
}
this.fetchTaskItem({ gid })
.then((task) => {
const taskName = getTaskName(task)
@@ -83,7 +97,6 @@
})
},
onDownloadStop (event) {
console.log('aria2 onDownloadStop')
const [{ gid }] = event
this.fetchTaskItem({ gid })
.then((task) => {
@@ -100,7 +113,7 @@
const { errorCode, errorMessage } = task
console.error(`[Motrix] download error gid: ${gid}, #${errorCode}, ${errorMessage}`)
const message = this.$t('task.download-error-message', { taskName })
const link = `<a target="_blank" href="https://github.com/agalwood/Motrix/wiki/Error#${errorCode}" rel="noopener noreferrer">#${errorCode}</a>`
const link = `<a target="_blank" href="https://github.com/agalwood/Motrix/wiki/Error#${errorCode}" rel="noopener noreferrer">${errorCode}</a>`
this.$msg({
type: 'error',
showClose: true,
@@ -111,18 +124,25 @@
})
},
onDownloadComplete (event) {
console.log('aria2 onDownloadComplete')
this.$store.dispatch('task/fetchList')
const [{ gid }] = event
this.$store.dispatch('task/removeFromSeedingList', gid)
this.fetchTaskItem({ gid })
.then((task) => {
this.handleDownloadComplete(task, false)
})
},
onBtDownloadComplete (event) {
console.log('aria2 onBtDownloadComplete')
this.$store.dispatch('task/fetchList')
const [{ gid }] = event
const { seedingList } = this
if (seedingList.includes(gid)) {
return
}
this.$store.dispatch('task/addToSeedingList', gid)
this.fetchTaskItem({ gid })
.then((task) => {
this.handleDownloadComplete(task, true)
@@ -131,8 +151,6 @@
handleDownloadComplete (task, isBT) {
this.$store.dispatch('task/saveSession')
addToRecentTask(task)
const path = getTaskFullPath(task)
this.showTaskCompleteNotify(task, isBT, path)
this.$electron.ipcRenderer.send('event', 'task-download-complete', task, path)
@@ -205,10 +223,15 @@
},
polling () {
this.$store.dispatch('app/fetchGlobalStat')
this.$store.dispatch('app/fetchProgress')
this.$store.dispatch('task/fetchList')
if (this.taskItemInfoVisible && this.currentTaskItem) {
this.$store.dispatch('task/fetchItem', this.currentTaskItem.gid)
if (this.taskDetailVisible && this.currentTaskGid) {
if (this.currentTaskIsBT && this.enabledFetchPeers) {
this.$store.dispatch('task/fetchItemWithPeers', this.currentTaskGid)
} else {
this.$store.dispatch('task/fetchItem', this.currentTaskGid)
}
}
},
stopPolling () {
@@ -8,6 +8,7 @@
</template>
<script>
import { dialog } from '@electron/remote'
import '@/components/Icons/folder'
export default {
@@ -17,7 +18,7 @@
methods: {
onFolderClick () {
const self = this
this.$electron.remote.dialog.showOpenDialog({
dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory']
}).then(({ canceled, filePaths }) => {
if (canceled || filePaths.length === 0) {
@@ -31,6 +32,3 @@
}
}
</script>
<style lang="scss">
</style>
@@ -31,6 +31,3 @@
}
}
</script>
<style lang="scss">
</style>
+3 -1
View File
@@ -16,6 +16,7 @@
</template>
<script>
import { getCurrentWindow } from '@electron/remote'
import '@/components/Icons/win-minimize'
import '@/components/Icons/win-maximize'
import '@/components/Icons/win-close'
@@ -29,7 +30,7 @@
},
computed: {
win () {
return this.$electron.remote.getCurrentWindow()
return getCurrentWindow()
}
},
methods: {
@@ -61,6 +62,7 @@
height: 36px;
z-index: 5000;
.title-bar-dragger {
margin: 5px 0 0 5px;
flex: 1;
user-select: none;
-webkit-app-region: drag;
+263 -55
View File
@@ -60,14 +60,26 @@
v-if="form.useProxy"
style="margin-top: -16px;"
>
<el-col class="form-item-sub" :span="16">
<el-col
class="form-item-sub"
:xs="24"
:sm="20"
:md="16"
:lg="16"
>
<el-input
placeholder="[http://][USER:PASSWORD@]HOST[:PORT]"
@change="onAllProxyBackupChange"
v-model="form.allProxyBackup">
</el-input>
</el-col>
<el-col class="form-item-sub" :span="20">
<el-col
class="form-item-sub"
:xs="24"
:sm="24"
:md="20"
:lg="20"
>
<el-input
type="textarea"
rows="2"
@@ -107,7 +119,18 @@
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value">
:value="item.value"
>
<span style="float: left">{{ item.label }}</span>
<span style="float: right; margin-right: 24px">
<el-tag
type="success"
size="mini"
v-if="item.cdn"
>
CDN
</el-tag>
</span>
</el-option>
</el-option-group>
</el-select>
@@ -166,12 +189,71 @@
</div>
</div>
</el-form-item>
<el-form-item
:label="`${$t('preferences.rpc')}: `"
:label-width="formLabelWidth"
>
<el-row style="margin-bottom: 8px;">
<el-col
class="form-item-sub"
:xs="24"
:sm="18"
:md="10"
:lg="10"
>
{{ $t('preferences.rpc-listen-port') }}
<el-input
:placeholder="rpcDefaultPort"
:maxlength="8"
v-model="form.rpcListenPort"
@change="onRpcListenPortChange"
>
<i slot="append" @click.prevent="onRpcPortDiceClick">
<mo-icon name="dice" width="12" height="12" />
</i>
</el-input>
</el-col>
</el-row>
<el-row style="margin-bottom: 8px;">
<el-col
class="form-item-sub"
:xs="24"
:sm="18"
:md="18"
:lg="18"
>
{{ $t('preferences.rpc-secret') }}
<el-input
:show-password="hideRpcSecret"
placeholder="RPC Secret"
:maxlength="24"
v-model="form.rpcSecret"
>
<i slot="append" @click.prevent="onRpcSecretDiceClick">
<mo-icon name="dice" width="12" height="12" />
</i>
</el-input>
<div class="el-form-item__info" style="margin-top: 8px;">
<a target="_blank" href="https://github.com/agalwood/Motrix/wiki/RPC" rel="noopener noreferrer">
{{ $t('preferences.rpc-secret-tips') }}
<mo-icon name="link" width="12" height="12" />
</a>
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item
:label="`${$t('preferences.port')}: `"
:label-width="formLabelWidth"
>
<el-row style="margin-bottom: 8px;">
<el-col class="form-item-sub" :span="12">
<el-col
class="form-item-sub"
:xs="24"
:sm="18"
:md="12"
:lg="12"
>
<el-switch
v-model="form.enableUpnp"
active-text="UPnP/NAT-PMP"
@@ -180,21 +262,32 @@
</el-col>
</el-row>
<el-row style="margin-bottom: 8px;">
<el-col class="form-item-sub" :span="10">
<el-col class="form-item-sub"
:xs="24"
:sm="18"
:md="10"
:lg="10"
>
{{ $t('preferences.bt-port') }}
<el-input
placeholder="BT Port"
:maxlength="8"
v-model="form.listenPort"
>
<i slot="append" @click.prevent="onPortDiceClick">
<i slot="append" @click.prevent="onBtPortDiceClick">
<mo-icon name="dice" width="12" height="12" />
</i>
</el-input>
</el-col>
</el-row>
<el-row>
<el-col class="form-item-sub" :span="10">
<el-col
class="form-item-sub"
:xs="24"
:sm="18"
:md="10"
:lg="10"
>
{{ $t('preferences.dht-port') }}
<el-input
placeholder="DHT Port"
@@ -231,7 +324,7 @@
</el-col>
</el-form-item>
<el-form-item
:label="`${$t('preferences.security')}: `"
:label="`${$t('preferences.user-agent')}: `"
:label-width="formLabelWidth"
>
<el-col class="form-item-sub" :span="24">
@@ -244,42 +337,24 @@
v-model="form.userAgent">
</el-input>
<el-button-group class="ua-group">
<el-button @click="() => changeUA('aria2')">Aria2</el-button>
<el-button @click="() => changeUA('transmission')">Transmission</el-button>
<el-button @click="() => changeUA('chrome')">Chrome</el-button>
<el-button @click="() => changeUA('du')">du</el-button>
</el-button-group>
</el-col>
<el-col class="form-item-sub" :span="18">
{{ $t('preferences.rpc-secret') }}
<el-input
:show-password="hideRpcSecret"
placeholder="RPC Secret"
:maxlength="24"
v-model="form.rpcSecret"
>
<i slot="append" @click.prevent="onDiceClick">
<mo-icon name="dice" width="12" height="12" />
</i>
</el-input>
<div class="el-form-item__info" style="margin-top: 8px;">
<a target="_blank" href="https://github.com/agalwood/Motrix/wiki/RPC" rel="noopener noreferrer">
{{ $t('preferences.rpc-secret-tips') }}
<mo-icon name="link" width="12" height="12" />
</a>
</div>
</el-col>
</el-form-item>
<el-form-item
:label="`${$t('preferences.developer')}: `"
:label-width="formLabelWidth"
>
<el-col class="form-item-sub" :span="24">
{{ $t('preferences.app-log-path') }}
<el-input placeholder="" disabled v-model="logPath">
{{ $t('preferences.aria2-conf-path') }}
<el-input placeholder="" disabled v-model="aria2ConfPath">
<mo-show-in-folder
slot="append"
v-if="isRenderer"
:path="logPath"
:path="aria2ConfPath"
/>
</el-input>
</el-col>
@@ -294,6 +369,33 @@
</el-input>
</el-col>
<el-col class="form-item-sub" :span="24">
{{ $t('preferences.app-log-path') }}
<el-row :gutter="16">
<el-col :span="18">
<el-input placeholder="" disabled v-model="logPath">
<mo-show-in-folder
slot="append"
v-if="isRenderer"
:path="logPath"
/>
</el-input>
</el-col>
<el-col :span="6">
<el-select v-model="form.logLevel">
<el-option
v-for="item in logLevels"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
</el-col>
</el-row>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-button plain type="warning" @click="() => onSessionResetClick()">
{{ $t('preferences.session-reset') }}
</el-button>
<el-button plain type="danger" @click="() => onFactoryResetClick()">
{{ $t('preferences.factory-reset') }}
</el-button>
@@ -319,29 +421,33 @@
<script>
import is from 'electron-is'
import { dialog } from '@electron/remote'
import { mapState } from 'vuex'
import { cloneDeep } from 'lodash'
import { cloneDeep, extend, isEmpty } from 'lodash'
import randomize from 'randomatic'
import * as clipboard from 'clipboard-polyfill'
import ShowInFolder from '@/components/Native/ShowInFolder'
import SubnavSwitcher from '@/components/Subnav/SubnavSwitcher'
import userAgentMap from '@shared/ua'
import { trackerSourceOptions } from '@shared/constants'
import { trackerSourceOptions, ENGINE_RPC_PORT, EMPTY_STRING, LOG_LEVELS } from '@shared/constants'
import {
backupConfig,
buildRpcUrl,
calcFormLabelWidth,
changedConfig,
checkIsNeedRestart,
convertCommaToLine,
convertLineToComma,
diffConfig,
getRandomInt
generateRandomInt
} from '@shared/utils'
import { convertTrackerDataToLine } from '@shared/utils/tracker'
import { convertTrackerDataToLine, reduceTrackerString } from '@shared/utils/tracker'
import '@/components/Icons/dice'
import '@/components/Icons/sync'
import '@/components/Icons/refresh'
import { getLanguage } from '@shared/locales'
import { getLocaleManager } from '@/components/Locale'
const initialForm = (config) => {
const initForm = (config) => {
const {
allProxy,
allProxyBackup,
@@ -354,6 +460,7 @@
lastCheckUpdateTime,
lastSyncTrackerTime,
listenPort,
logLevel,
noProxy,
protocols,
rpcListenPort,
@@ -374,6 +481,7 @@
lastCheckUpdateTime,
lastSyncTrackerTime,
listenPort,
logLevel,
noProxy: convertCommaToLine(noProxy),
protocols: {
...protocols
@@ -395,8 +503,9 @@
},
data () {
const { locale } = this.$store.state.preference.config
const form = initialForm(this.$store.state.preference.config)
const formOriginal = cloneDeep(form)
const formOriginal = initForm(this.$store.state.preference.config)
let form = {}
form = initForm(extend(form, formOriginal, changedConfig.advanced))
return {
form,
@@ -432,19 +541,33 @@
}
]
},
rpcDefaultPort () {
return ENGINE_RPC_PORT
},
logLevels () {
return LOG_LEVELS
},
...mapState('preference', {
config: state => state.config,
aria2ConfPath: state => state.config.aria2ConfPath,
logPath: state => state.config.logPath,
sessionPath: state => state.config.sessionPath
})
},
watch: {
'form.rpcListenPort' (val) {
const url = buildRpcUrl({
port: this.form.rpcListenPort,
secret: val
})
navigator.clipboard.writeText(url)
},
'form.rpcSecret' (val) {
const url = buildRpcUrl({
port: this.form.rpcListenPort,
secret: val
})
clipboard.writeText(url)
navigator.clipboard.writeText(url)
}
},
methods: {
@@ -491,15 +614,25 @@
}
this.form.userAgent = ua
},
onPortDiceClick () {
const port = getRandomInt(20000, 24999)
onBtPortDiceClick () {
const port = generateRandomInt(20000, 24999)
this.form.listenPort = port
},
onDhtPortDiceClick () {
const port = getRandomInt(25000, 29999)
const port = generateRandomInt(25000, 29999)
this.form.dhtListenPort = port
},
onDiceClick () {
onRpcListenPortChange (value) {
console.log('onRpcListenPortChange===>', value)
if (EMPTY_STRING === value) {
this.form.rpcListenPort = this.rpcDefaultPort
}
},
onRpcPortDiceClick () {
const port = generateRandomInt(ENGINE_RPC_PORT, 20000)
this.form.rpcListenPort = port
},
onRpcSecretDiceClick () {
this.hideRpcSecret = false
const rpcSecret = randomize('Aa0', 12)
this.form.rpcSecret = rpcSecret
@@ -508,8 +641,25 @@
this.hideRpcSecret = true
}, 2000)
},
onSessionResetClick () {
dialog.showMessageBox({
type: 'warning',
title: this.$t('preferences.session-reset'),
message: this.$t('preferences.session-reset-confirm'),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1
}).then(({ response }) => {
if (response === 0) {
this.$store.dispatch('task/purgeTaskRecord')
this.$store.dispatch('task/pauseAllTask')
.then(() => {
this.$electron.ipcRenderer.send('command', 'application:reset-session')
})
}
})
},
onFactoryResetClick () {
this.$electron.remote.dialog.showMessageBox({
dialog.showMessageBox({
type: 'warning',
title: this.$t('preferences.factory-reset'),
message: this.$t('preferences.factory-reset-confirm'),
@@ -524,34 +674,52 @@
syncFormConfig () {
this.$store.dispatch('preference/fetchPreference')
.then((config) => {
this.form = initialForm(config)
this.form = initForm(config)
this.formOriginal = cloneDeep(this.form)
if (changedConfig.basic.theme !== undefined) {
this.$electron.ipcRenderer.send('command',
'application:change-theme', changedConfig.basic.theme)
}
})
},
submitForm (formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
console.log('[Motrix] preference form valid:', valid)
console.error('[Motrix] preference form valid:', valid)
return false
}
const changed = diffConfig(this.formOriginal, this.form)
const data = {
...changed,
protocols: {
...this.form.protocols
}
...diffConfig(this.formOriginal, this.form),
...changedConfig.basic
}
const {
btAutoDownloadContent,
autoHideWindow,
btTracker,
noProxy,
rpcListenPort
} = data
if ('btAutoDownloadContent' in data) {
data.pauseMetadata = !btAutoDownloadContent
data.followMetalink = btAutoDownloadContent
data.followTorrent = btAutoDownloadContent
}
const { btTracker, noProxy } = changed
if (btTracker) {
data.btTracker = convertLineToComma(btTracker)
data.btTracker = reduceTrackerString(convertLineToComma(btTracker))
}
if (noProxy) {
data.noProxy = convertLineToComma(noProxy)
}
if (rpcListenPort === EMPTY_STRING) {
data.rpcListenPort = this.rpcDefaultPort
}
console.log('[Motrix] preference changed data:', data)
this.$store.dispatch('preference/save', data)
@@ -564,9 +732,14 @@
this.$msg.success(this.$t('preferences.save-fail-message'))
})
changedConfig.basic = {}
changedConfig.advanced = {}
if (this.isRenderer) {
this.$electron.ipcRenderer.send('command',
'application:setup-protocols-client', data.protocols)
if ('autoHideWindow' in data) {
this.$electron.ipcRenderer.send('command',
'application:auto-hide-window', autoHideWindow)
}
if (checkIsNeedRestart(data)) {
this.$electron.ipcRenderer.send('command', 'application:relaunch')
@@ -577,6 +750,41 @@
resetForm (formName) {
this.syncFormConfig()
}
},
beforeRouteLeave (to, from, next) {
changedConfig.advanced = diffConfig(this.formOriginal, this.form)
if (to.path === '/preference/basic') {
next()
} else {
if (isEmpty(changedConfig.basic) && isEmpty(changedConfig.advanced)) {
next()
} else {
dialog.showMessageBox({
type: 'warning',
title: this.$t('preferences.not-saved'),
message: this.$t('preferences.not-saved-confirm'),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1
}).then(({ response }) => {
if (response === 0) {
if (changedConfig.basic.theme !== undefined) {
this.$electron.ipcRenderer.send('command',
'application:change-theme', backupConfig.theme)
}
if (changedConfig.basic.locale !== undefined) {
const lng = getLanguage(backupConfig.locale)
getLocaleManager().changeLanguage(lng)
this.$electron.ipcRenderer.send('command',
'application:change-locale', lng)
}
changedConfig.basic = {}
changedConfig.advanced = {}
backupConfig.theme = undefined
next()
}
})
}
}
}
}
</script>
+293 -66
View File
@@ -25,6 +25,7 @@
<mo-theme-switcher
v-model="form.theme"
@change="handleThemeChange"
ref="themeSwitcher"
/>
</el-col>
<el-col v-if="showHideAppMenuOption" class="form-item-sub" :span="16">
@@ -37,8 +38,19 @@
{{ $t('preferences.auto-hide-window') }}
</el-checkbox>
</el-col>
<el-col v-if="isMac" class="form-item-sub" :span="16">
<el-checkbox v-model="form.traySpeedometer">
{{ $t('preferences.tray-speedometer') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="16">
<el-checkbox v-model="form.showProgressBar">
{{ $t('preferences.show-progress-bar') }}
</el-checkbox>
</el-col>
</el-form-item>
<el-form-item
v-if="isMac"
:label="`${$t('preferences.run-mode')}: `"
:label-width="formLabelWidth"
>
@@ -100,10 +112,14 @@
:label-width="formLabelWidth"
>
<el-input placeholder="" v-model="form.dir" :readonly="isMas">
<mo-history-directory
slot="prepend"
@selected="handleHistoryDirectorySelected"
/>
<mo-select-directory
v-if="isRenderer"
slot="append"
@selected="onDirectorySelected"
@selected="handleNativeDirectorySelected"
/>
</el-input>
<div class="el-form-item__info" v-if="isMas" style="margin-top: 8px;">
@@ -116,9 +132,22 @@
>
<el-col class="form-item-sub" :span="24">
{{ $t('preferences.transfer-speed-upload') }}
<el-select v-model="form.maxOverallUploadLimit">
<el-input-number
v-model="maxOverallUploadLimitParsed"
controls-position="right"
:min="0"
:max="65535"
:step="1"
:label="$t('preferences.transfer-speed-download')"
>
</el-input-number>
<el-select
style="width: 100px;"
v-model="uploadUnit"
@change="handleUploadChange"
:placeholder="$t('preferences.speed-units')">
<el-option
v-for="item in speedOptions"
v-for="item in speedUnits"
:key="item.value"
:label="item.label"
:value="item.value">
@@ -127,9 +156,21 @@
</el-col>
<el-col class="form-item-sub" :span="24">
{{ $t('preferences.transfer-speed-download') }}
<el-select v-model="form.maxOverallDownloadLimit">
<el-input-number
v-model="maxOverallDownloadLimitParsed"
controls-position="right"
:min="0"
:max="65535"
:step="1"
:label="$t('preferences.transfer-speed-download')">
</el-input-number>
<el-select
style="width: 100px;"
v-model="downloadUnit"
@change="handleDownloadChange"
:placeholder="$t('preferences.speed-units')">
<el-option
v-for="item in speedOptions"
v-for="item in speedUnits"
:key="item.value"
:label="item.label"
:value="item.value">
@@ -137,6 +178,61 @@
</el-select>
</el-col>
</el-form-item>
<el-form-item
:label="`${$t('preferences.bt-settings')}: `"
:label-width="formLabelWidth"
>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.btSaveMetadata">
{{ $t('preferences.bt-save-metadata') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox
v-model="form.btAutoDownloadContent"
>
{{ $t('preferences.bt-auto-download-content') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox
v-model="form.btForceEncryption"
>
{{ $t('preferences.bt-force-encryption') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-switch
v-model="form.keepSeeding"
:active-text="$t('preferences.keep-seeding')"
@change="onKeepSeedingChange"
>
</el-switch>
</el-col>
<el-col class="form-item-sub" :span="24" v-if="!form.keepSeeding">
{{ $t('preferences.seed-ratio') }}
<el-input-number
v-model="form.seedRatio"
controls-position="right"
:min="1"
:max="100"
:step="0.1"
:label="$t('preferences.seed-ratio')">
</el-input-number>
</el-col>
<el-col class="form-item-sub" :span="24" v-if="!form.keepSeeding">
{{ $t('preferences.seed-time') }}
({{ $t('preferences.seed-time-unit') }})
<el-input-number
v-model="form.seedTime"
controls-position="right"
:min="60"
:max="525600"
:step="1"
:label="$t('preferences.seed-time')">
</el-input-number>
</el-col>
</el-form-item>
<el-form-item
:label="`${$t('preferences.task-manage')}: `"
:label-width="formLabelWidth"
@@ -147,7 +243,7 @@
v-model="form.maxConcurrentDownloads"
controls-position="right"
:min="1"
:max="10"
:max="maxConcurrentDownloads"
:label="$t('preferences.max-concurrent-downloads')">
</el-input-number>
</el-col>
@@ -202,27 +298,38 @@
<script>
import is from 'electron-is'
import { dialog } from '@electron/remote'
import { mapState } from 'vuex'
import { cloneDeep } from 'lodash'
import { cloneDeep, extend, isEmpty } from 'lodash'
import SubnavSwitcher from '@/components/Subnav/SubnavSwitcher'
import HistoryDirectory from '@/components/Preference/HistoryDirectory'
import SelectDirectory from '@/components/Native/SelectDirectory'
import ThemeSwitcher from '@/components/Preference/ThemeSwitcher'
import { availableLanguages, getLanguage } from '@shared/locales'
import { getLocaleManager } from '@/components/Locale'
import { prettifyDir } from '@/utils/native'
import {
backupConfig,
calcFormLabelWidth,
changedConfig,
checkIsNeedRestart,
diffConfig
convertLineToComma,
diffConfig,
extractSpeedUnit
} from '@shared/utils'
import { APP_RUN_MODE } from '@shared/constants'
import { APP_RUN_MODE, ENGINE_MAX_CONCURRENT_DOWNLOADS } from '@shared/constants'
import { reduceTrackerString } from '@shared/utils/tracker'
const initialForm = (config) => {
const initForm = (config) => {
const {
autoHideWindow,
btSaveMetadata,
btForceEncryption,
dir,
engineMaxConnectionPerServer,
followMetalink,
followTorrent,
hideAppMenu,
keepSeeding,
keepWindowState,
locale,
maxConcurrentDownloads,
@@ -232,17 +339,28 @@
newTaskShowDownloading,
noConfirmBeforeDeleteTask,
openAtLogin,
pauseMetadata,
resumeAllWhenAppLaunched,
runMode,
seedRatio,
seedTime,
showProgressBar,
taskNotification,
theme
theme,
traySpeedometer
} = config
const result = {
autoHideWindow,
btAutoDownloadContent: !pauseMetadata,
btSaveMetadata,
btForceEncryption,
continue: config.continue,
dir,
engineMaxConnectionPerServer,
followMetalink,
followTorrent,
hideAppMenu,
keepSeeding,
keepWindowState,
locale,
maxConcurrentDownloads,
@@ -254,8 +372,12 @@
openAtLogin,
resumeAllWhenAppLaunched,
runMode,
seedRatio,
seedTime,
showProgressBar,
taskNotification,
theme
theme,
traySpeedometer
}
return result
}
@@ -264,13 +386,22 @@
name: 'mo-preference-basic',
components: {
[SubnavSwitcher.name]: SubnavSwitcher,
[HistoryDirectory.name]: HistoryDirectory,
[SelectDirectory.name]: SelectDirectory,
[ThemeSwitcher.name]: ThemeSwitcher
},
data () {
const { locale } = this.$store.state.preference.config
const form = initialForm(this.$store.state.preference.config)
const formOriginal = cloneDeep(form)
const formOriginal = initForm(this.$store.state.preference.config)
let form = {}
form = initForm(extend(form, formOriginal, changedConfig.basic))
if (backupConfig.theme === undefined) {
backupConfig.theme = formOriginal.theme
} else {
formOriginal.theme = backupConfig.theme
}
backupConfig.locale = formOriginal.locale
return {
form,
@@ -282,52 +413,84 @@
},
computed: {
isRenderer: () => is.renderer(),
isMac: () => is.macOS(),
isMas: () => is.mas(),
isLinux () { return is.linux() },
title () {
return this.$t('preferences.basic')
},
maxConcurrentDownloads () {
return ENGINE_MAX_CONCURRENT_DOWNLOADS
},
maxOverallDownloadLimitParsed: {
get () {
return parseInt(this.form.maxOverallDownloadLimit)
},
set (value) {
const limit = value > 0 ? `${value}${this.downloadUnit}` : 0
this.form.maxOverallDownloadLimit = limit
}
},
maxOverallUploadLimitParsed: {
get () {
return parseInt(this.form.maxOverallUploadLimit)
},
set (value) {
const limit = value > 0 ? `${value}${this.uploadUnit}` : 0
this.form.maxOverallUploadLimit = limit
}
},
downloadUnit: {
get () {
const { maxOverallDownloadLimit } = this.form
return extractSpeedUnit(maxOverallDownloadLimit)
},
set (value) {
return value
}
},
uploadUnit: {
get () {
const { maxOverallUploadLimit } = this.form
return extractSpeedUnit(maxOverallUploadLimit)
},
set (value) {
return value
}
},
runModes () {
return [
let result = [
{
label: this.$t('preferences.run-mode-standard'),
value: 1
value: APP_RUN_MODE.STANDARD
},
{
label: this.$t('preferences.run-mode-menu-bar'),
value: 2
label: this.$t('preferences.run-mode-tray'),
value: APP_RUN_MODE.TRAY
}
]
if (this.isMac) {
result = [
...result,
{
label: this.$t('preferences.run-mode-hide-tray'),
value: APP_RUN_MODE.HIDE_TRAY
}
]
}
return result
},
speedOptions () {
speedUnits () {
return [
{
label: this.$t('preferences.transfer-speed-unlimited'),
value: 0
label: 'KB/s',
value: 'K'
},
{
label: '128 KB/s',
value: '128K'
},
{
label: '256 KB/s',
value: '256K'
},
{
label: '512 KB/s',
value: '512K'
},
{
label: '1 MB/s',
value: '1M'
},
{
label: '5 MB/s',
value: '5M'
},
{
label: '10 MB/s',
value: '10M'
label: 'MB/s',
value: 'M'
}
]
},
@@ -353,9 +516,6 @@
showHideAppMenuOption () {
return is.windows() || is.linux()
},
downloadDir () {
return prettifyDir(this.form.dir)
},
...mapState('preference', {
config: state => state.config
})
@@ -369,32 +529,70 @@
},
handleThemeChange (theme) {
this.form.theme = theme
// this.$store.dispatch('preference/changeThemeConfig', theme)
this.$electron.ipcRenderer.send('command',
'application:change-theme', theme)
},
handleDownloadChange (value) {
const speedLimit = parseInt(this.form.maxOverallDownloadLimit)
this.downloadUnit = value
const limit = speedLimit > 0 ? `${speedLimit}${value}` : 0
this.form.maxOverallDownloadLimit = limit
},
handleUploadChange (value) {
const speedLimit = parseInt(this.form.maxOverallUploadLimit)
this.uploadUnit = value
const limit = speedLimit > 0 ? `${speedLimit}${value}` : 0
this.form.maxOverallUploadLimit = limit
},
onKeepSeedingChange (enable) {
this.form.seedRatio = enable ? 0 : 1
this.form.seedTime = enable ? 525600 : 60
},
handleHistoryDirectorySelected (dir) {
this.form.dir = dir
},
handleNativeDirectorySelected (dir) {
this.form.dir = dir
this.$store.dispatch('preference/recordHistoryDirectory', dir)
},
onDirectorySelected (dir) {
this.form.dir = dir
},
syncFormConfig () {
this.$store.dispatch('preference/fetchPreference')
.then((config) => {
this.form = initialForm(config)
this.form = initForm(config)
this.formOriginal = cloneDeep(this.form)
})
},
submitForm (formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
console.log('[Motrix] preference form valid:', valid)
console.error('[Motrix] preference form valid:', valid)
return false
}
const { runMode, openAtLogin, autoHideWindow } = this.form
const changed = diffConfig(this.formOriginal, this.form)
const data = {
...changed
...diffConfig(this.formOriginal, this.form),
...changedConfig.advanced
}
const { btAutoDownloadContent, autoHideWindow, btTracker, noProxy } = data
if ('btAutoDownloadContent' in data) {
data.pauseMetadata = !btAutoDownloadContent
data.followMetalink = btAutoDownloadContent
data.followTorrent = btAutoDownloadContent
}
if (btTracker) {
data.btTracker = reduceTrackerString(convertLineToComma(btTracker))
}
if (noProxy) {
data.noProxy = convertLineToComma(noProxy)
}
console.log('[Motrix] preference changed data:', data)
this.$store.dispatch('preference/save', data)
@@ -407,29 +605,58 @@
this.$msg.success(this.$t('preferences.save-fail-message'))
})
changedConfig.basic = {}
changedConfig.advanced = {}
if (this.isRenderer) {
this.$electron.ipcRenderer.send('command',
'application:open-at-login', openAtLogin)
this.$electron.ipcRenderer.send('command',
'application:toggle-dock', runMode === APP_RUN_MODE.STANDARD)
this.$electron.ipcRenderer.send('command',
'application:auto-hide-window', autoHideWindow)
if ('autoHideWindow' in data) {
this.$electron.ipcRenderer.send('command',
'application:auto-hide-window', autoHideWindow)
}
if (checkIsNeedRestart(data)) {
this.$electron.ipcRenderer.send('command',
'application:relaunch')
this.$electron.ipcRenderer.send('command', 'application:relaunch')
}
}
})
},
resetForm (formName) {
this.$refs.themeSwitcher.currentValue = backupConfig.theme
this.handleLocaleChange(this.formOriginal.locale)
this.syncFormConfig()
}
},
beforeRouteLeave (to, from, next) {
changedConfig.basic = diffConfig(this.formOriginal, this.form)
if (to.path === '/preference/advanced') {
next()
} else {
if (isEmpty(changedConfig.basic) && isEmpty(changedConfig.advanced)) {
next()
} else {
dialog.showMessageBox({
type: 'warning',
title: this.$t('preferences.not-saved'),
message: this.$t('preferences.not-saved-confirm'),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1
}).then(({ response }) => {
if (response === 0) {
if (changedConfig.basic.theme !== undefined) {
this.$electron.ipcRenderer.send('command',
'application:change-theme', backupConfig.theme)
}
if (changedConfig.basic.locale !== undefined) {
this.handleLocaleChange(this.formOriginal.locale)
}
changedConfig.basic = {}
changedConfig.advanced = {}
backupConfig.theme = undefined
next()
}
})
}
}
}
}
</script>
<style lang="scss">
</style>
@@ -0,0 +1,229 @@
<template>
<div class="mo-history-directory">
<el-popover
popper-class="mo-directory-popper"
trigger="hover"
:placement="placement"
:width="width"
>
<el-empty class="mo-directory-empty" :image-size="48" v-if="empty" />
<ul class="mo-directory-list" v-if="favoriteDirectories.length > 0">
<li
v-for="directory in favoriteDirectories"
:key="directory"
@click.stop="() => handleSelectItem(directory)"
>
<span class="mo-directory-path" :title="directory">{{directory}}</span>
<span class="mo-directory-actions">
<i
class="el-icon-star-off icon-history-favorited"
@click.stop="() => handleCancelFavoriteItem(directory)"
/>
<i
class="el-icon-delete icon-history-remove"
@click.stop="() => handleRemoveItem(directory)"
/>
</span>
</li>
</ul>
<div class="mo-directory-divider" v-if="showDivider" />
<ul class="mo-directory-list" v-if="historyDirectories.length > 0">
<li
v-for="directory in historyDirectories"
:key="directory"
@click.stop="() => handleSelectItem(directory)"
>
<span class="mo-directory-path" :title="directory">{{directory}}</span>
<span class="mo-directory-actions">
<i
v-if="showFavoriteAction"
class="el-icon-star-off icon-history-favorite"
@click.stop="() => handleFavoriteItem(directory)"
/>
<i
class="el-icon-delete icon-history-remove"
@click.stop="() => handleRemoveItem(directory)"
/>
</span>
</li>
</ul>
<el-button
slot="reference"
:disabled="popoverDisabled"
>
<i class="el-icon-time" />
</el-button>
</el-popover>
</div>
</template>
<script>
import { mapState } from 'vuex'
import { MAX_NUM_OF_DIRECTORIES } from '@shared/constants'
import { cloneArray } from '@shared/utils'
export default {
name: 'mo-history-directory',
components: {
},
props: {
width: {
type: Number,
default: 360
},
placement: {
type: String,
default: 'bottom-start'
}
},
data () {
return {
visible: false
}
},
computed: {
...mapState('preference', {
historyDirectories: state => {
return cloneArray(state.config.historyDirectories, true)
},
favoriteDirectories: state => {
return cloneArray(state.config.favoriteDirectories, true)
}
}),
empty () {
const { favoriteDirectories, historyDirectories } = this
return favoriteDirectories.length + historyDirectories.length === 0
},
popoverDisabled () {
const { favoriteDirectories, historyDirectories } = this
return favoriteDirectories.length === 0 &&
historyDirectories.length === 0
},
showDivider () {
const { favoriteDirectories, historyDirectories } = this
return favoriteDirectories.length > 0 &&
historyDirectories.length > 0
},
showFavoriteAction () {
const { favoriteDirectories } = this
return favoriteDirectories.length < MAX_NUM_OF_DIRECTORIES
}
},
methods: {
handleIconClick () {
if (this.popoverDisabled) {
return
}
const { visible } = this
this.visible = !visible
},
handleSelectItem (directory) {
this.$emit('selected', directory.trim())
this.visible = false
},
handleFavoriteItem (directory) {
console.log('handleFavoriteItem==>', directory)
this.$store.dispatch('preference/favoriteDirectory', directory)
},
handleCancelFavoriteItem (directory) {
console.log('handleCancelFavoriteItem==>', directory)
this.$store.dispatch('preference/cancelFavoriteDirectory', directory)
},
handleRemoveItem (directory) {
console.log('handleRemoveItem==>', directory)
this.$store.dispatch('preference/removeDirectory', directory)
}
}
}
</script>
<style lang="scss">
.el-popover.mo-directory-popper {
padding: $--popover-padding 0;
}
.el-empty.mo-directory-empty {
padding: 20px 0;
}
.mo-directory-divider {
padding: 0 $--popover-padding;
margin: 6px 0;
&::after {
content: ' ';
display: block;
height: 1px;
width: 100%;
background: $--border-color-base;
}
}
.mo-directory-list {
padding: 0;
margin: 0;
list-style: none;
&> li {
display: flex;
align-items: center;
list-style: none;
line-height: $--font-line-height-primary;
margin: 0;
font-size: $--font-size-small;
color: $--color-text-regular;
cursor: pointer;
outline: none;
padding: 6px 6px 6px $--popover-padding;
&:focus, &:hover {
background-color: $--background-color-base;
color: $--color-primary-light-2;
}
}
.mo-directory-path {
display: inline-block;
flex: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.mo-directory-actions {
min-width: 40px;
text-align: right;
&> i {
padding: 3px;
margin-right: 3px;
display: inline-block;
}
}
.icon-history-favorite {
&:focus, &:hover {
color: $--color-warning;
}
}
.icon-history-favorited {
color: $--color-warning;
}
.icon-history-remove {
&:focus, &:hover {
color: $--color-danger;
}
}
}
.theme-dark {
.mo-directory-divider {
&::after {
background: $--dk-border-color-base;
}
}
.mo-directory-list {
&> li {
color: $--dk-font-color-base;
&:focus, &:hover {
background-color: $--color-primary;
color: $--color-white;
}
}
}
}
</style>
+48 -51
View File
@@ -1,5 +1,5 @@
<template>
<el-container class="content panel" direction="horizontal">
<el-container class="main panel" direction="horizontal">
<el-aside width="200px" class="subnav hidden-xs-only">
<router-view name="subnav" />
</el-aside>
@@ -17,62 +17,59 @@
</script>
<style lang="scss">
.form-preference {
padding: 16px 7% 64px 0;
.el-switch__label {
font-weight: normal;
.form-preference {
padding: 16px 7% 64px 16px;
.el-switch__label {
font-weight: normal;
color: $--color-text-regular;
&.is-active {
color: $--color-text-regular;
&.is-active {
color: $--color-text-regular;
}
}
.el-checkbox__input.is-checked + .el-checkbox__label {
color: $--color-text-regular;
}
.el-form-item {
a {
color: $--color-text-regular;
text-decoration: none;
&:hover {
color: $--color-text-primary;
text-decoration: underline;
}
}
.el-checkbox__input.is-checked + .el-checkbox__label {
color: $--color-text-regular;
}
.el-form-item {
a {
color: $--color-text-regular;
text-decoration: none;
&:hover {
color: $--color-text-primary;
text-decoration: underline;
}
&:active {
color: $--color-text-primary;
}
}
}
.el-form-item.el-form-item--mini {
margin-bottom: 32px;
}
.el-form-item__content {
color: $--color-text-regular;
}
.form-item-sub {
margin-bottom: 8px;
&:last-of-type {
margin-bottom: 0;
&:active {
color: $--color-text-primary;
}
}
}
.form-actions {
position: fixed;
bottom: 0;
left: auto;
z-index: 10;
width: -webkit-fill-available;
box-sizing: border-box;
padding: 24px 36px;
margin-left: -36px;
// aside.width + subnav.width + padding-left + scrollbar.width
margin-right: 322px;
.el-form-item.el-form-item--mini {
margin-bottom: 32px;
}
.action-link {
cursor: pointer;
color: $--link-color;
&:hover {
color: $--link-hover-color;
text-decoration: underline;
.el-form-item__content {
color: $--color-text-regular;
}
.form-item-sub {
margin-bottom: 8px;
&:last-of-type {
margin-bottom: 0;
}
}
}
.form-actions {
position: sticky;
bottom: 0;
left: auto;
z-index: 10;
width: -webkit-fill-available;
box-sizing: border-box;
padding: 24px 16px;
}
.action-link {
cursor: pointer;
color: $--link-color;
&:hover {
color: $--link-hover-color;
text-decoration: underline;
}
}
</style>
+24 -7
View File
@@ -11,7 +11,7 @@
<mo-browser
v-if="isRenderer"
class="lab-webview"
:src="src"
:src="url"
/>
</el-container>
</template>
@@ -20,6 +20,7 @@
import is from 'electron-is'
import { mapState } from 'vuex'
import { APP_THEME } from '@shared/constants'
import SubnavSwitcher from '@/components/Subnav/SubnavSwitcher'
import Browser from '@/components/Browser'
import '@/components/Icons/info-square'
@@ -33,11 +34,30 @@
data () {
const { locale } = this.$store.state.preference.config
return {
src: `https://motrix.app/lab?lite=true&lang=${locale}`
locale
}
},
computed: {
isRenderer: () => is.renderer(),
...mapState('app', {
systemTheme: state => state.systemTheme
}),
...mapState('preference', {
config: state => state.config,
theme: state => state.config.theme
}),
currentTheme () {
if (this.theme === APP_THEME.AUTO) {
return this.systemTheme
} else {
return this.theme
}
},
url () {
const { currentTheme, locale } = this
const result = `https://motrix.app/lab?lite=true&theme=${currentTheme}&lang=${locale}`
return result
},
title () {
return this.$t('preferences.lab')
},
@@ -59,10 +79,7 @@
route: '/preference/lab'
}
]
},
...mapState('preference', {
config: state => state.config
})
}
}
}
</script>
@@ -74,6 +91,6 @@
flex-basis: auto;
overflow: auto;
box-sizing: border-box;
padding: 0 0 0 36px;
padding: 0;
}
</style>

Some files were not shown because too many files have changed in this diff Show More