Compare commits

...

378 Commits

Author SHA1 Message Date
Dr_rOot 4b8de781a4 refactor: improve download error message #293 2019-06-19 19:56:07 +08:00
Dr_rOot 8224423d8c refactor: update panel content padding 2019-06-18 21:07:14 +08:00
Dr_rOot 1e05e1e4f3 fix: element ui message link color 2019-06-17 20:27:51 +08:00
Dr_rOot bfd1e1b6fa refactor: removeTaskRecord error complete removed 2019-06-17 19:48:57 +08:00
Dr_rOot 8110409402 feat: empty addTaskOptions when task added 2019-06-15 20:23:11 +08:00
Dr_rOot 37b446b6f0 feat: add parseHeader util for restart task dialog 2019-06-14 23:22:36 +08:00
Dr_rOot 97801fff32 refactor: task item actions restart & stop seeding 2019-06-14 22:38:07 +08:00
Dr_rOot 4ca4f5e606 refactor: add task to support addTaskOptions 2019-06-14 22:20:34 +08:00
Dr_rOot e9caf1b0b4 refactor: transform add options keys to kebab case 2019-06-13 20:47:33 +08:00
Dr_rOot d67dca00b5 feat: add getTaskOption action 2019-06-12 20:36:03 +08:00
Dr_rOot 038536259e feat: add task restart icon 2019-06-12 20:34:22 +08:00
Dr_rOot 02d83534d1 refactor: check is exist before delete task file 2019-06-11 22:41:07 +08:00
Dr_rOot 2ae3642c62 feat: add check task is seeder util 2019-06-07 20:07:20 +08:00
Dr_rOot 5ecc08d66d refactor: clean up useless code 2019-06-04 22:05:51 +08:00
Dr_rOot e7b1a898fa feat: add task history icon 2019-06-03 19:57:04 +08:00
Dr_rOot 93acf1ba63 fix: el-progress status value for element ui 2.9.x 2019-06-02 02:22:13 +08:00
Dr_rOot a5f0236661 Merge pull request #296 from agalwood/feature/transfer_speed_setting_201905282346
fix: refactor transfer settings
2019-05-30 20:44:28 +08:00
Dr_rOot 6df52988b0 chore: i18n preferences transfer settings 2019-05-29 23:57:42 +08:00
Dr_rOot 6359c729ad fix: horizontal scrollbar height 2019-05-29 23:53:58 +08:00
Dr_rOot 04db94daa6 refactor: i18n calc form label width with locale 2019-05-29 23:50:33 +08:00
Dr_rOot d41d809f00 Merge pull request #295 from gee1k/master
fix: remove the button in the aside panel to drag and drop
2019-05-29 15:25:37 +08:00
Svend efdfae37b5 fix:remove the button in the aside panel to drag and drop 2019-05-29 14:01:30 +08:00
Dr_rOot 1a097c02e3 refactor: transfer speed setting to select options 2019-05-28 23:51:34 +08:00
Dr_rOot 0217b5573a fix: theme merge code error 2019-05-28 23:27:57 +08:00
Dr_rOot 86611604d5 Merge branch 'feature/theme_switch_lang_201905232132'
# Conflicts:
#	src/renderer/components/Preference/Advanced.vue
#	src/renderer/components/Preference/Basic.vue
2019-05-28 23:24:08 +08:00
Dr_rOot 0bd8ab3d11 Merge pull request #294 from agalwood/feature/change_global_options_201905241932
feat: change preferences without relaunch app #24 #172 #197
2019-05-28 23:14:05 +08:00
Dr_rOot e9eadb2eba feat: i18n save preferences message 2019-05-28 22:52:50 +08:00
Dr_rOot 72afd845ba fix: default enable check auto update on macOS 2019-05-28 22:35:19 +08:00
Gary Li 91670fac37 fix: updated en-US translation (#290)
* Updated translation: en-US

* Update: en-US translation
2019-05-27 22:38:58 +08:00
Dr_rOot bc14e5b287 Merge pull request #292 from jjandxa/master
feat: add transfer speed setting
2019-05-27 22:33:11 +08:00
jjandxa 2d01431e7d feat: add transfer speed setting 2019-05-26 22:13:38 +08:00
Dr_rOot 81531ef9fc feat: change preference show message 2019-05-26 19:40:13 +08:00
Dr_rOot 04126363ca refactor: change preference without relaunch app 2019-05-25 17:17:00 +08:00
Dr_rOot bcb07656b9 refactor: rename isSyncTracker to trackerSyncing 2019-05-24 19:49:34 +08:00
Dr_rOot b9cd991fd5 refactor: delayed engine client call 2019-05-24 19:45:56 +08:00
Dr_rOot 1b2b22f039 feat: api add changeGlobalOption 2019-05-24 19:45:01 +08:00
Dr_rOot 170a481d48 feat: add diffConfig util 2019-05-24 19:43:53 +08:00
Dr_rOot 48bf7c9e2e refactor: move theme switcher & lang to basic 2019-05-23 21:46:58 +08:00
Dr_rOot 03cfb013e5 docs: fix cn readme typo 2019-05-22 10:31:43 +08:00
Dr_rOot b4e23d8805 chore: add feature request cn issue template 2019-05-22 10:31:25 +08:00
Dr_rOot 43965c5740 chore: update bug report issue template 2019-05-21 10:56:50 +08:00
Dr_rOot 5c8d2e4ca4 fix: unified scrollbar style 2019-05-20 15:01:44 +08:00
Dr_rOot b51c144ace fix: i18n task code format 2019-05-20 14:58:55 +08:00
Dr_rOot feb839b7fa Merge pull request #273 from agalwood/feature/selective_torrent_201905142048
feat: torrent selective download #253 #229 #211
2019-05-19 16:09:09 +08:00
Dr_rOot fa36397afa fix: i18n add task style 2019-05-19 15:48:53 +08:00
Dr_rOot 583fe0ffe2 feat: i18n select torrent 2019-05-19 15:48:03 +08:00
Dr_rOot 8b3a5fbaa8 fix: select torrent reset 2019-05-19 15:45:39 +08:00
Dr_rOot 85493c263b fix: select torrent layout 2019-05-19 15:45:20 +08:00
Dr_rOot 78e106abe4 fix: add selective torrent list utils 2019-05-19 11:44:49 +08:00
Dr_rOot bbf90e9344 fix: torrent file list dark theme style 2019-05-19 11:44:21 +08:00
Dr_rOot dc30c25a83 refactor: select torrent to support selective 2019-05-19 11:42:47 +08:00
Dr_rOot e1cce4f50c refactor: add task to support selective torrent 2019-05-18 21:11:03 +08:00
Dr_rOot 61654ff0ff refactor: remove add task iLoveEggFeatures 2019-05-17 22:39:27 +08:00
Dr_rOot 0142aedfa5 feat: element dialog & table dark theme style 2019-05-17 23:11:24 +08:00
Dr_rOot 323f85ca40 feat: add video audio image files filter utils 2019-05-17 23:08:47 +08:00
Dr_rOot f35e696c6a feat: add file type icon 2019-05-16 20:05:13 +08:00
Dr_rOot d49cbcdc00 feat: add seed-time to system config 2019-05-16 19:54:35 +08:00
Dr_rOot c85ac4e895 feat: set bt-remove-unselected-file to true 2019-05-15 22:06:05 +08:00
Dr_rOot f831cc51ed fix: add task number input text overflow 2019-05-14 20:51:57 +08:00
Dr_rOot d47eb9ba0f refactor: subnav li style 2019-05-14 20:51:04 +08:00
Dr_rOot 119a374db6 fix: clean subnav code 2019-05-13 19:34:47 +08:00
Dr_rOot d26808e43a Merge pull request #262 from agalwood/feature/open_source_license_201905102203
feat: add open source license link to about panel
2019-05-12 11:36:34 +08:00
Dr_rOot 8cb3e54be2 fix: i18n add missing fa about license translate 2019-05-11 10:38:40 +08:00
Dr_rOot c55902cda7 fix: about pannel add license link 2019-05-10 22:33:16 +08:00
Dr_rOot 426b54201b Merge pull request #258 from agalwood/feature/keep_window_size_201905072013
feat: keep window size & position #232 #224 #48
2019-05-09 16:58:40 +08:00
Dr_rOot 6c032cde2d feat: i18n preference keep window state 2019-05-09 12:23:43 +08:00
Dr_rOot eb42ac6cfd feat: preference basic add keep window state chk 2019-05-08 11:40:49 +08:00
Dr_rOot 90f0a7c5f6 feat: restore window state at init 2019-05-08 00:04:53 +08:00
Dr_rOot 9b61dad2c3 feat: store window state 2019-05-08 00:03:25 +08:00
Dr_rOot 671fb82e6a feat: window manager emit resized, moved, closed 2019-05-07 23:56:38 +08:00
Dr_rOot b8f444ca4d fix: detect thunder resource uris add default val 2019-05-06 23:05:56 +08:00
Dr_rOot 7509442e4e refactor: thunder link tip improve 2019-05-05 12:01:11 +08:00
Dr_rOot ee2e3de782 Merge pull request #246 from agalwood/feature/auto_launch_201905031132
feat: auto open at login #164 #210 #237
2019-05-04 20:18:08 +08:00
Dr_rOot c6dd1e333e docs: add fixUserConfig comment 2019-05-04 20:02:16 +08:00
Dr_rOot fba761a29c feat: auto fix user config open-at-login value
Auto fix user config `open-at-login` value when user delete Motrix in OS startup manage
2019-05-04 11:50:51 +08:00
Dr_rOot 4fb94ae8bb fix: second instance could not restore window #233 2019-05-03 22:21:08 +08:00
Dr_rOot 79721822c2 feat: hide window when app launch at login 2019-05-03 15:40:00 +08:00
Dr_rOot 556f58f6b3 chore: i18n open at login 2019-05-03 15:39:18 +08:00
Dr_rOot e690f15335 feat: preference open at login 2019-05-03 15:20:34 +08:00
Dr_rOot 1876587973 feat: auto launch manager 2019-05-03 15:18:57 +08:00
Dr_rOot b89c651132 refactor: auto check update 2019-05-02 15:37:46 +08:00
zhtw ad9d3ed15a fix: some zh-TW typos (#242)
* Update preferences.js

* Update subnav.js

* Update task.js

* Update task.js
2019-05-01 17:54:17 +08:00
Dr_rOot 6aa362332a feat: add dir class watch 2019-05-01 17:09:49 +08:00
Dr_rOot 18d107c062 feat: detect text direction 2019-05-01 17:03:52 +08:00
Dr_rOot ebeba4663c docs: update readme 2019-04-30 21:01:05 +08:00
FloatingShuYin 9bff4257a8 docs: Add tips on how to install package management tools on Windows (#241)
* Add tips on how to install package management tools on Windows

* Remove extra spaces

* Layout revision
2019-04-30 20:16:41 +08:00
Dr_rOot e07ef7dd11 fix: bump version to 1.3.8 2019-04-29 09:49:33 +08:00
Dr_rOot ebb0926d1d fix: preference basic & lab save config fail #128
v1.3.7 btTracker.trim()
2019-04-29 09:46:52 +08:00
Dr_rOot 5333438825 fix: screenshots file name add @2x suffix 2019-04-28 17:23:28 +08:00
Dr_rOot 3664341822 docs: add app screenshots 2019-04-28 17:21:32 +08:00
Dr_rOot d3cbff6dd1 fix: preference link color in dark mode 2019-04-28 17:08:51 +08:00
Dr_rOot 5668b0773d fix: sync icon stroke color 2019-04-28 12:41:25 +08:00
Dr_rOot b757112c30 fix: update readme supported languages 2019-04-28 11:49:32 +08:00
Dr_rOot dc3a01fb3c fix: bump version to 1.3.7 2019-04-27 22:02:23 +08:00
KOZ39 591de11072 fix: Improve Korean translation (#238) 2019-04-27 21:54:46 +08:00
Dr_rOot 853c361e6d fix: bump version to 1.3.6 2019-04-27 19:37:35 +08:00
Dr_rOot b0ebc737e2 fix: i18n rename and add some keys 2019-04-27 19:36:02 +08:00
Dr_rOot bc9db25dfd fix: position of the proxy input is incorrect 2019-04-27 19:34:52 +08:00
KOZ39 08c9902759 feat: Add Korean translation (#236)
* Add Korean translation

* Fix typo
2019-04-27 18:31:10 +08:00
Nima Rasooli 8d2f52710d feat: Translated to Persian/Farsi (#235)
* Create about.js

* Rename src/shared/locales/fa-IR/about.js to src/shared/locales/fa/about.js

* Translated

* Translated

* Translated

* Translated

* Create index.js

* Translated

* Translated

* Translated

* translated

* Translated
2019-04-27 18:30:46 +08:00
Dr_rOot dd48fd79db fix: bump version to 1.3.5 2019-04-27 18:03:34 +08:00
Dr_rOot 9e7ad10309 Merge branch 'feature/bt_experience_opt_201904262310' 2019-04-27 18:02:19 +08:00
Dr_rOot 6e17102210 fix: mo protocol new-bt-task 2019-04-27 17:54:27 +08:00
Dr_rOot 5ed3b68a57 fix: handle launch argv 2019-04-27 17:31:56 +08:00
Dr_rOot a705427ce2 fix: open url in win & linux 2019-04-27 15:55:34 +08:00
Dr_rOot c37f7c73ce doc: change Manual link to github wiki 2019-04-27 12:14:05 +08:00
Dr_rOot efcf01d59b feat: handle magnet protocol 2019-04-27 12:13:02 +08:00
Dr_rOot 324b61567d fix: typo tip => tips 2019-04-26 23:57:55 +08:00
Dr_rOot d1e24fced0 fix: i18n preference bt tracker 2019-04-26 23:57:11 +08:00
Dr_rOot cb50d6709b feat: sync bt tracker from github
An updated list of public BitTorrent trackers
https://github.com/ngosang/trackerslist
2019-04-26 23:27:23 +08:00
Dr_rOot 900ac4c8d1 feat: preference advanced add bt tracker input 2019-04-26 23:19:10 +08:00
Dr_rOot 6696b0a538 refactor: move bt-tracker to config manager 2019-04-26 23:15:01 +08:00
Dr_rOot 110c83bcb8 fix: dht config optimization 2019-04-26 23:13:58 +08:00
Dr_rOot 136b4969bd chore: update readme
Add dark mode
2019-04-25 15:45:58 +08:00
weearc a901fdd2b6 docs: readme added linux installation guide (#228)
* add aur helper command in readme

* add aur helper command in readme
2019-04-25 15:07:56 +08:00
Dr_rOot 215bdbc416 fix: prepare for electron 5.0.x
Set webPreferences nodeIntegration to true
2019-04-25 10:53:25 +08:00
Dr_rOot 4e8d226460 fix: optimized for small screen users #48 #224 2019-04-25 10:16:16 +08:00
Dr_rOot 23f3357d27 fix: bump version to 1.3.3 2019-04-24 21:56:38 +08:00
Dr_rOot 7adafab7fe fix: i18n ja 2019-04-24 21:55:39 +08:00
HBKRKZK e3afb14e4a feat: Add Japanese language translation (#225)
* Create darwin.json

* Add files via upload

* Create about.js

* Add files via upload

* Update all.js

* Update app.js

* Update index.js

* fix

* fix preferences.js
2019-04-24 21:48:41 +08:00
Dr_rOot 228cd1fa7d fix: bump version to 1.3.2 2019-04-24 16:29:41 +08:00
Dr_rOot 47a7464bf9 doc: update translation guide 2019-04-24 16:28:04 +08:00
Dr_rOot 8efd86af3d feat: remove similar unuse config key 2019-04-24 11:35:31 +08:00
Dr_rOot ee78ae262e fix: bump version v1.3.1 2019-04-24 10:22:52 +08:00
Dr_rOot 7562f6a7d1 fix: downgrade electron-store 2019-04-24 10:22:32 +08:00
Dr_rOot defacd50e8 fix: build script —experimental-worker 2019-04-24 00:06:01 +08:00
Dr_rOot f5ed0d89cc fix: bump version to 1.3.x 2019-04-23 23:47:14 +08:00
Dr_rOot a3bb1c7ff1 Merge branch 'feature/dark_mode_201904201437' 2019-04-23 23:43:04 +08:00
Dr_rOot d0085b295d refactor: bt complete show seeding tips #179 #134 2019-04-23 23:40:01 +08:00
Dr_rOot 6cb278fd9e chore: manual update tracker 2019-04-23 18:14:50 +08:00
Dr_rOot aeeb9813ed refactor: lint fix 2019-04-23 17:56:45 +08:00
Dr_rOot 359da895e7 fix: migration electron-log from v2 to v3 2019-04-23 17:23:52 +08:00
Dr_rOot 8a3f510c38 refactor: check pid is running before kill it 2019-04-23 17:22:57 +08:00
Dr_rOot 1345994402 feat: add config schema valid 2019-04-23 17:21:56 +08:00
Dr_rOot c770108a6c refactor: electron store sort config key 2019-04-23 17:21:27 +08:00
Dr_rOot 178f2103f1 fix: destroy tray when app exit 2019-04-23 10:41:11 +08:00
Dr_rOot d5c71b50e8 fix: change application module init order 2019-04-23 10:40:30 +08:00
Dr_rOot ce09e76ffb refactor: theme style improve 2019-04-23 10:38:44 +08:00
Dr_rOot 4688b521fa fix: trim after split task link text rows #221
closed #221
2019-04-22 20:52:33 +08:00
Dr_rOot 33960dc264 fix: decrease theme switcher theme text font size 2019-04-22 20:49:15 +08:00
Dr_rOot d29d0d3321 fix: add task dialog background color 2019-04-22 15:42:09 +08:00
Dr_rOot f4c3e7be69 feat: preference advanced add theme switcher 2019-04-22 15:17:48 +08:00
Dr_rOot 338b51975c feat: preference theme switcher i18n 2019-04-22 15:17:23 +08:00
Dr_rOot e1a287350d feat: add preference theme thumb 2019-04-22 15:16:53 +08:00
Dr_rOot afeee1fc86 fix: change theme config 2019-04-21 23:42:52 +08:00
Dr_rOot a39820f8ab refactor: app style 2019-04-21 22:29:27 +08:00
Dr_rOot aea604278c fix: copyright link color 2019-04-21 22:28:40 +08:00
Dr_rOot 0b92e6b96e fix: app info style 2019-04-21 22:28:19 +08:00
Dr_rOot 2ba0040bdd feat: title bar close btn 2019-04-21 22:28:01 +08:00
Dr_rOot 04bf2b6b9d fix: logo svg fill 2019-04-21 22:26:36 +08:00
Dr_rOot cc367f4c5c fix: change show in folder icon size 2019-04-21 22:24:40 +08:00
Dr_rOot 73809a6501 refactor: app theme & add dark theme 2019-04-21 22:23:39 +08:00
Dr_rOot 5067ad7306 feat: app ui respond to system theme changed 2019-04-21 21:36:37 +08:00
Dr_rOot d5b3ad5933 fix: tray icon adapts to the dark mode #206 #207
close #206
close #207
2019-04-21 21:30:07 +08:00
Dr_rOot 7a903e112c feat: theme manager 2019-04-21 21:26:45 +08:00
Dr_rOot 829eb83a4c fix: get task full path error when file path empty 2019-04-17 15:45:20 +08:00
Dr_rOot 886acc2486 fix: thunder link decoding error #201 #209
close #209
2019-04-17 15:12:13 +08:00
Dr_rOot 87440701ab fix: block some ui text selections when drag app 2019-04-10 21:11:10 +08:00
Dr_rOot e880430e3a fix: html tpl 2019-04-07 13:31:00 +08:00
Dr_rOot 2febff883b fix: add missing auto check update TR translation 2019-04-01 21:11:07 +08:00
Bo Yuan 7d8bb443b8 feat: preferences add auto check updates setting
* add auto check update, time out: 1 min.

* fix: add auto check update, check interval: 30 min.

* no message

* no message

* fix: modify update check interval to 7 days, add display of [last check update time] to preference.

* no message

* fix: modified the group of auto-check-update, fix the display of last-check-update-time

* no message

* fix: hide last-check-update-time when never check.
2019-04-01 20:55:30 +08:00
Dr_rOot 1489d58a63 chore: rebuild snap for snapcraft 2019-03-31 11:45:54 +08:00
Dr_rOot 00fdafe824 fix: upgrade electron-builder 2019-03-30 11:46:55 +08:00
Dr_rOot 81c91e8cb1 fix: update readme badges 2019-03-28 22:58:28 +08:00
Dr_rOot 0fd34254de chore: add latest release badge 2019-03-26 20:58:22 +08:00
Dr_rOot d9298f0d46 fix: read me typo 2019-03-25 00:15:34 +08:00
Dr_rOot 83e12a699e docs: update readme add new feature 2019-03-24 21:54:28 +08:00
Dr_rOot 83b55b806d fix: bump version to 1.2.2 2019-03-24 20:44:30 +08:00
Dr_rOot d4dab6de9b fix: linux parse argv 2019-03-24 20:43:23 +08:00
Dr_rOot deae5e8e33 fix: bump version 2019-03-24 10:42:34 +08:00
Dr_rOot b098c8c9bc fix: open file argv path undefined 2019-03-24 10:42:05 +08:00
Dr_rOot b12553cc45 fix: try to fix linux read file eof 2019-03-23 18:15:13 +08:00
Dr_rOot 8891b9a79c refactor: windows tray icon changed to png 2019-03-23 16:12:27 +08:00
Dr_rOot a44f088139 fix: try to downgrade electron-builder 2019-03-22 23:45:47 +08:00
Dr_rOot 7ea0cd3e75 fix: fileAssociations for AppImage 2019-03-22 22:16:29 +08:00
Dr_rOot c93cc9d23b fix: remove node from devDependencies 2019-03-22 22:02:21 +08:00
Dr_rOot 8d2e14486d fix: remove npm audit fix 2019-03-22 21:56:14 +08:00
Dr_rOot 3775da5e36 Merge branch 'hotfix/main_refactor_201903151054' 2019-03-22 21:50:40 +08:00
Dr_rOot 2e21285f99 fix: update dependencies 2019-03-22 21:49:23 +08:00
Dr_rOot 6c2dde8e59 chore: try add npm audit fix to ci shell 2019-03-22 21:47:58 +08:00
Dr_rOot e014580512 feat: update tray icon based on changes in the download status 2019-03-22 21:45:11 +08:00
Dr_rOot e970a51b71 refactor: remove unuse menu config json 2019-03-21 18:23:07 +08:00
Antoine Leblanc 019f8be306 fix: i18n fr in preferences (#152) 2019-03-21 11:10:39 +08:00
Dr_rOot 20f79dacb7 refactor: menu i18n 2019-03-20 23:22:48 +08:00
Dr_rOot 1bdd17a80a feat: add tray 2019-03-20 23:18:30 +08:00
Dr_rOot b01e006073 refactor: window close event 2019-03-20 22:57:31 +08:00
Dr_rOot 82728a2b23 refactor: windows manager show hide 2019-03-20 22:55:37 +08:00
Dr_rOot 217d2df5be fix: open-file dialog cancel 2019-03-20 21:10:02 +08:00
Dr_rOot 960f0674ef fix: open url send command to all 2019-03-19 23:17:53 +08:00
Dr_rOot 54e9f326d9 refactor: return change language promise 2019-03-19 23:16:41 +08:00
Dr_rOot 4e7267028f fix: sendCommandToAll fn typo 2019-03-19 23:14:58 +08:00
Dr_rOot efd7f97a36 fix: add open-file to task i18n file 2019-03-19 23:13:04 +08:00
Dr_rOot 5bb790822d fix: mac dock download speed text add /s 2019-03-19 18:30:19 +08:00
Dr_rOot 71f3fb601b fix: send command no effect when no focused window 2019-03-19 18:29:35 +08:00
Dr_rOot af710aa265 refactor: rename handleFileAssociation 2019-03-19 18:05:20 +08:00
Dr_rOot 52b6fb0849 fix: add pt menu missing translations 2019-03-17 12:09:06 +08:00
Dr_rOot b8d63374b7 fix: add fr menu missing translations 2019-03-17 11:50:19 +08:00
Dr_rOot ff38d4ea5c refactor: main index to launcher 2019-03-17 11:36:18 +08:00
Dr_rOot a24a7acfaf feat: add support for windows x86 2019-03-17 11:34:44 +08:00
Dr_rOot 16e9c8d848 feat: add torrent to file associations 2019-03-17 11:33:44 +08:00
Dr_rOot f36cdccf9f feat: open torrent from file dialog 2019-03-17 11:31:54 +08:00
Dr_rOot c826a7dd77 fix: on download pause message 2019-03-17 11:20:22 +08:00
Dr_rOot b96518a42c fix: remove task confirm message typo 2019-03-17 10:54:41 +08:00
Dr_rOot 02f0b66500 fix: select torrent counld not replace 2019-03-17 10:54:12 +08:00
Dr_rOot 377bc47d87 refactor: engine fetchTaskItem 2019-03-17 00:29:18 +08:00
Dr_rOot f8a4ef267d fix: i18n startEngine error message 2019-03-17 00:27:52 +08:00
Dr_rOot baea3f0345 refactor: task removeTask action 2019-03-16 21:26:52 +08:00
Dr_rOot 1fcf0bb0d0 fix: main i18n #120
close #120
2019-03-16 16:11:10 +08:00
Dr_rOot 85e1acfe5b chore: config manager add doc link comment 2019-03-16 16:07:05 +08:00
Dr_rOot 41ac6ad335 fix: add missing translations 2019-03-16 16:05:15 +08:00
Dr_rOot b151c45e70 fix: remove useless page configuration 2019-03-16 16:04:06 +08:00
Dr_rOot b3686fff04 fix: torrent dragger on drop 2019-03-15 23:32:58 +08:00
Dr_rOot af81107b3d fix: temp set max-overall-upload-limit=128K #136
Preferences will be added to the speed limit setting.
close #136
2019-03-14 21:24:08 +08:00
Dr_rOot 88224998b7 fix: readme link 2019-03-14 21:03:18 +08:00
Dr_rOot 8508ca9b3c fix: i18n pt-BR 2019-03-14 21:01:38 +08:00
Dr_rOot 6fad6a876c chore: update readme i18n progress 2019-03-14 20:59:14 +08:00
Dr_rOot 6c8a844cd9 fix: copyright left & right width 2019-03-14 20:47:52 +08:00
Schlömi 4657a503f9 feat: added german (#140)
* added german

* Improved german

* more details for preferences.continue

* shorted some translations, added missing seperator
2019-03-14 20:46:14 +08:00
Dr_rOot 324347e591 fix: menu task.clear-recent-tasks no effect #135
close #135
2019-03-10 15:28:53 +08:00
Kay 16c376c108 refactor: drag and drop file (#124)
* add drag file

* refactor: drag and drop file

* add template
2019-03-10 10:47:57 +08:00
monomagentaeggroll 79cad28cc1 fix: README.md editing (#123)
* Update README.md

Revision sweep of the README.md file.
Some style changes, fixes a few typos, and corrects some grammar.

* Update README.md
2019-03-04 20:52:22 +08:00
Dr_rOot ac16bd91c2 fix: i18n pt explanation 2019-03-03 19:43:21 +08:00
Dr_rOot fafeaca47d chore: update bug_report_cn issue template 2019-03-03 19:36:07 +08:00
Dr_rOot 3152252fb8 refactor: i18n sort locale key 2019-03-03 19:25:53 +08:00
FramboisePi a5240bfeb0 fix: French spelling improvement (#117)
* Accents correction

* French: accent improvement in edit.js

* French: accent improvement in help.js

* French: accent improvement in menu.js

* French spelling improvement in preferences.js

* French spelling improvement in task.js
2019-03-03 19:24:25 +08:00
Andre Noberto 64cda63587 feat: Adding the Brazilian portuguese translation to the app [Done] (#111)
* Added locales for pt-BR

* Added menu support for pt-BR

* Configuring pt-BR in locales

* Fixing typo

* Make use of abbreviation

* Fixed typo & adjustments on variable naming case
2019-03-03 18:34:38 +08:00
Dr_rOot fe071a3783 fix: temp limit the task list num 2019-03-01 22:25:14 +08:00
Dr_rOot 5d812e9c4e fix: readme typo 2019-03-01 22:07:25 +08:00
Dr_rOot f36ba605e2 fix: update readme 2019-03-01 21:32:26 +08:00
Dr_rOot e7ed58e394 fix: i18n for some reason 2019-03-01 21:27:44 +08:00
Dr_rOot be789e7ea8 Merge pull request #108 from Yukaii/locale-zh-tw
feat: Add zh-TW language support
2019-03-01 20:44:08 +08:00
Yukai Huang 780a01e404 feat: Adding zh-TW language support 2019-03-01 14:51:04 +08:00
Dr_rOot b45518a5b6 fix: increase engine retry max to 10 2019-02-28 13:17:12 +08:00
Dr_rOot 2401d68a24 fix: add missing brew cask cmd 2019-02-28 13:16:36 +08:00
Dr_rOot 6cb2986f39 style: readme adjust the paragraph 2019-02-28 13:03:53 +08:00
Dr_rOot cb8e0dbd2e refactor: advanced preference change lng 2019-02-28 13:01:03 +08:00
Dr_rOot b4faed0621 docs: update readme 2019-02-28 12:59:55 +08:00
Dr_rOot 249aa616fe Merge pull request #101 from gpatarin/master
feat: Adding the french translation to the app [Done]
2019-02-28 12:57:52 +08:00
Gael Patarin 482ccb3293 Update utils.js to match latest changes 2019-02-27 23:33:21 +01:00
Gael Patarin f1be6bc1a4 Translated the app to french 2019-02-27 19:52:56 +01:00
Dr_rOot 326e8c700b fix: update i18n progress 2019-02-27 21:13:57 +08:00
Dr_rOot 2a538fd7c3 fix: getTaskFullPath bittorrent 2019-02-27 21:07:00 +08:00
Dr_rOot c424b981b3 Merge pull request #97 from agalwood/revert-93-master
fix: Revert "feat: Adding the french translation to the app [WIP]"
2019-02-27 21:05:26 +08:00
Dr_rOot a284aba2cc Revert "feat: Adding the french translation to the app [WIP]" 2019-02-27 20:54:07 +08:00
Dr_rOot c8bfdbf087 Merge pull request #93 from gpatarin/master
feat: Adding the french translation to the app [WIP]
2019-02-27 20:47:49 +08:00
Gael Patarin eb80f3698f More translation in french (WIP) 2019-02-26 19:18:55 +01:00
Gael Patarin 23f3d85ad4 Added french translation 2019-02-26 19:13:28 +01:00
Dr_rOot 24019123ba fix: add i18n guide link to readme 2019-02-25 23:48:56 +08:00
Dr_rOot 06a471982a fix: add CONTRIBUTING Guide 2019-02-25 23:46:24 +08:00
Dr_rOot 43627aba50 fix: linux AppImage dock icon missing #83
Close #83
2019-02-25 19:33:23 +08:00
Dr_rOot 8116234b3e fix: update npm dependencies 2019-02-24 15:21:45 +08:00
Dr_rOot e488de5c3f fix: magnet link task moveTaskFilesToTrash bug 2019-02-24 15:05:19 +08:00
Dr_rOot 084f834a41 fix: update readme 2019-02-24 13:33:18 +08:00
Dr_rOot 97d30ee85e fix: update bug report issue template 2019-02-24 13:05:32 +08:00
Dr_rOot 387ccc9cf7 fix: i18n download-session-path error 2019-02-23 18:32:50 +08:00
Dr_rOot fed5744607 docs: update github issue template 2019-02-21 12:56:30 +08:00
Dr_rOot 3e2b448e09 fix: openDownloadDock & updateDockBadge mac only 2019-02-20 18:05:43 +08:00
Dr_rOot c12c13066d fix: magnet task file path did not start with dir 2019-02-20 12:54:51 +08:00
Dr_rOot 5c36454ca0 fix: copy link fail when bttorrent info is empty 2019-02-20 12:54:17 +08:00
Dr_rOot 7b6ba13d94 fix: eslint 2019-02-19 20:52:03 +08:00
Dr_rOot 5d93d4d7bb fix: bt task copy link bug #39
Close #39
2019-02-19 20:50:36 +08:00
Dr_rOot 4d024eb6a6 feat: fetch engineOptions (getGlobalOption) 2019-02-19 20:48:39 +08:00
Dr_rOot 182acec265 fix: remove task with file name is empty bug #75 2019-02-18 22:54:05 +08:00
Dr_rOot e293b16343 fix: one msg notify twice error 2019-02-18 22:29:39 +08:00
Dr_rOot 70cf36d7b2 Merge pull request #73 from Raistlin916/master
refactor: message refactor
2019-02-18 21:33:57 +08:00
Kay f3d8cac9ba fix: update .editorconfig 2019-02-18 21:24:03 +08:00
Dr_rOot 620b243794 Merge pull request #72 from mesquka/master
fix: Consistant hms capitalisation
2019-02-18 11:46:08 +08:00
gaokai 37fe7d4dbd feat: msg add max length with queue 2019-02-18 01:05:25 +08:00
gaokai 11b6d651b8 refactor: message default options 2019-02-18 00:36:21 +08:00
Kieran Mesquita 8a5bc11d88 Merge remote-tracking branch 'upstream/master' 2019-02-17 23:07:24 +08:00
Kieran Mesquita bf69146035 Decapitalise 'h' for hms consistancy 2019-02-17 23:06:43 +08:00
Dr_rOot 14ff0c4bbe fix: add preferences missing translations 2019-02-17 11:02:29 +08:00
Dr_rOot d9706a2c3e Merge pull request #70 from abdullah/improve-tr-localization-key
refactor: rename Turkish i18n key from 'Tr-tr' to 'tr'
2019-02-17 10:57:58 +08:00
Abdullah mara c36f7b73f1 refactor: rename Turkish i18n key from 'Tr-tr' to 'tr'
This commit also includes menu localization keys.
2019-02-16 21:06:20 +03:00
Dr_rOot 7e6d0542cc fix: update readme 2019-02-16 23:11:12 +08:00
Dr_rOot 72bf4ec909 style: update i18n 2019-02-16 23:09:40 +08:00
Dr_rOot fa87b8ff9b fix: i18n hash link 2019-02-16 23:07:38 +08:00
Dr_rOot 31203e72fc fix: update README i18n 2019-02-16 23:06:07 +08:00
Dr_rOot 5bd9875f61 Merge pull request #67 from abdullah/add-turkish
feat: Add Turkish.
2019-02-16 21:56:54 +08:00
Abdullah mara 646ad28fbc Add Turkish. 2019-02-16 16:30:51 +03:00
Dr_rOot a66d482d3a fix: aside draggable area not response click event 2019-02-16 19:45:59 +08:00
Dr_rOot 5dfc8cce37 Merge pull request #63 from mesquka/master
feat: Make notifications dismissible and truncate long messages
2019-02-16 10:28:16 +08:00
Kieran Mesquita 4c88431147 Make all message notifications dismissable 2019-02-16 02:49:04 +08:00
Dr_rOot bb06b3970d refactor: util getTaskName 2019-02-15 23:48:26 +08:00
Dr_rOot c86767bc3f fix: el message text style 2019-02-15 23:44:17 +08:00
Dr_rOot 3c6d14ec7e Merge pull request #59 from mesquka/master
fix: Form Labels in add Task
2019-02-15 14:13:11 +08:00
Kieran Mesquita 88a9a69c27 Adjust width of add task labels to fit 'user-agent' 2019-02-15 13:52:02 +08:00
Kieran Mesquita da7241cd6a Fix Form Labels 2019-02-15 13:44:10 +08:00
Dr_rOot 397055826e Merge pull request #57 from Raistlin916/master
feat: drag & drop torrent file to add task
2019-02-15 11:54:31 +08:00
raistlin916 2e6b8bb5fa add drag file 2019-02-15 10:57:04 +08:00
Dr_rOot ed4f3afcbd fix: update app screenshot 2019-02-14 21:35:47 +08:00
Dr_rOot 53ab75fbf9 fix: update hide app menu default 2019-02-14 21:20:33 +08:00
Dr_rOot c19cf17b07 fix: update read me 2019-02-14 17:23:54 +08:00
Dr_rOot a994f45e8f fix: add manual link to readme 2019-02-14 13:28:24 +08:00
Dr_rOot 1bdaeeb3a6 fix: Windows title bar drag 2019-02-13 12:08:18 +08:00
Dr_rOot 32f0a3d084 fix: windows &linux hide app menu fail 2019-02-12 23:25:24 +08:00
Dr_rOot ba4ef326f7 feat: add lock bot config 2019-02-11 21:57:42 +08:00
Dr_rOot 10a8f0bd16 fix: remove pacman && update version to 1.1.3 2019-02-10 23:14:47 +08:00
Dr_rOot d3c40a8fc3 fix: remove rpm && update version to 1.1.2 2019-02-10 23:04:30 +08:00
Dr_rOot 34a9247270 fix: update version to 1.1.1 2019-02-10 22:52:48 +08:00
Dr_rOot 0b0f9b41e9 feat: add more linux targets 2019-02-10 22:51:47 +08:00
Dr_rOot 827eb7c32e feat: update version to 1.1.0 2019-02-10 22:23:32 +08:00
Dr_rOot 0b598f6c17 feat: add zip to win target 2019-02-09 20:54:54 +08:00
Dr_rOot 19e7394266 Merge branch 'master' of github.com:agalwood/Motrix 2019-02-09 18:18:44 +08:00
Dr_rOot f6b345ec55 fix: i18n update 2019-02-09 18:16:27 +08:00
Dr_rOot 091201d7ff fix: remove resume and pause message notify 2019-02-09 18:12:49 +08:00
Dr_rOot b109764b8e Update issue templates 2019-02-07 22:34:18 +08:00
Dr_rOot 79a488b863 doc: update readme 2019-02-06 17:17:26 +08:00
Dr_rOot 962c8a6516 fix: temp hide task item action 'more' 2019-02-06 17:17:13 +08:00
Dr_rOot f94cd01a3a Merge branch 'feature/i18next_201901042000'
# Conflicts:
#	README.md
#	src/main/menus/en-US/linux.json
#	src/main/menus/en-US/win32.json
#	src/main/menus/zh-CN/linux.json
#	src/main/menus/zh-CN/win32.json
#	src/renderer/pages/index/App.vue
2019-02-01 21:22:41 +08:00
Dr_rOot 31081bbbb2 fix: add under development message 2019-01-31 23:08:42 +08:00
Dr_rOot 0d1ba16525 refactor: remove console 2019-01-31 23:08:11 +08:00
Dr_rOot 5b4b00ae41 refactor: locale manager 2019-01-31 23:07:08 +08:00
Dr_rOot e0ca5d8724 doc: add touch bar 2019-01-27 21:54:03 +08:00
Dr_rOot fd5b21c271 fix: addTask with multi uris fail #33 2019-01-25 22:19:33 +08:00
Dr_rOot ac5e2f755c doc: add platform badge 2019-01-23 22:00:40 +08:00
Dr_rOot a4415f0688 doc: add app ui screenshot 2019-01-23 21:57:41 +08:00
Dr_rOot 8a2bb03b62 doc: delete related files when removing tasks 2019-01-22 23:56:10 +08:00
Dr_rOot 7011160587 feat: autofill resource uri when open add task 2019-01-22 22:41:11 +08:00
Dr_rOot 31fdff3d3b doc: add total download badge 2019-01-22 20:54:19 +08:00
Dr_rOot 580bb5d622 doc: fix license 2019-01-21 18:04:43 +08:00
Dr_rOot d5a51a8422 doc: readme i18n 2019-01-21 18:02:41 +08:00
Dr_rOot 5f5b22d8d3 doc: update readme 2019-01-20 22:47:00 +08:00
Dr_rOot 397f689be7 fix: i18n error 2019-01-20 22:38:38 +08:00
Dr_rOot 3ddffe6daf refactor: code clean 2019-01-20 22:35:39 +08:00
Dr_rOot ab1861b068 feat: i18n change locale 2019-01-20 22:34:28 +08:00
Dr_rOot 5bf870acdc feat: i18n refactor 2019-01-20 21:52:43 +08:00
Dr_rOot d85f4ea8d4 fix: ConfigManager add getLocale util 2019-01-20 21:47:04 +08:00
Dr_rOot 0a93c1954d fix: menu text 2019-01-20 21:44:42 +08:00
Dr_rOot c4df9ff34e feat: i18n add task 2019-01-19 23:03:00 +08:00
Dr_rOot 648587f52c feat: open task when double click completed task
#23
2019-01-19 21:53:47 +08:00
Dr_rOot bc17c2d685 fix: task i18n params error 2019-01-19 21:51:11 +08:00
Dr_rOot 6e7327a3fe feat: add support for hide app menu on linux 2019-01-17 11:08:53 +08:00
Dr_rOot db15cd959b fix: i18n key error 2019-01-16 23:46:13 +08:00
Dr_rOot ff43994902 feat: hide app menu 2019-01-16 23:44:59 +08:00
Dr_rOot 8226e14e0a fix: titlebar style background 2019-01-16 12:53:32 +08:00
Dr_rOot f5b30efc7d refactor: new title-bar component 2019-01-15 23:48:50 +08:00
Dr_rOot 4c27c36e90 fix: windows manager getWindows() forEach error 2019-01-11 17:51:53 +08:00
Dr_rOot 9dd950e510 fix: windows manager getWindows() forEach error 2019-01-11 17:49:06 +08:00
Dr_rOot fffa7b9839 feat: app about add release link 2019-01-09 23:34:20 +08:00
Dr_rOot 2d3ca45c87 fix: preferences label 2019-01-08 23:14:09 +08:00
Dr_rOot c0f032a6c6 fix: add package 2019-01-07 21:50:19 +08:00
Dr_rOot f8848ba0e5 feat: i18n main logic 2019-01-07 21:49:14 +08:00
Dr_rOot 33a3229c22 feat: engine client i18n 2019-01-07 21:27:21 +08:00
Dr_rOot 5a626cece9 feat: task i18n 2019-01-07 20:17:22 +08:00
Dr_rOot 16c80e75b5 feat: preferences i18n 2019-01-06 23:14:13 +08:00
Dr_rOot 4c604304d8 feat: add determineLocale util 2019-01-06 21:39:54 +08:00
Dr_rOot a921695efc feat: tasks i18n 2019-01-06 21:33:45 +08:00
Dr_rOot 09dfe1c8ea feat: select torrent i18n 2019-01-06 21:32:41 +08:00
Dr_rOot 1fbaeefca5 feat: app subnav i18n 2019-01-06 21:25:02 +08:00
Dr_rOot 9a969040ef feat: task list i18n 2019-01-06 21:19:07 +08:00
Dr_rOot b7615a178b feat: task item actions i18n 2019-01-06 21:18:27 +08:00
Dr_rOot 894b9c9a54 feat: task actions i18n 2019-01-06 21:14:20 +08:00
Dr_rOot 6dc4b9718e feat: app about i18n 2019-01-06 21:03:35 +08:00
Dr_rOot 6435ad6594 refactor: remove code 2019-01-06 18:31:34 +08:00
Dr_rOot 173e071a86 fix: linux application menu command chain 2019-01-06 18:31:34 +08:00
Dr_rOot 8812b6409e fix: win32 & linux first menu label text to File 2019-01-06 18:31:34 +08:00
Dr_rOot 56bd63a418 refactor: remove code 2019-01-06 18:31:34 +08:00
Dr_rOot 915b2fcaa6 fix: bt enable dht 2019-01-06 18:31:34 +08:00
Dr_rOot 7eb63f105a fix: only show title bar when is renderer 2019-01-06 18:31:34 +08:00
Dr_rOot d6bc8a0436 refactor: remove unuse style code 2019-01-06 18:31:34 +08:00
Dr_rOot 6ea8603074 fix: disable text selection within a draggable 2019-01-06 18:31:34 +08:00
Dr_rOot a652e7b893 refactor: remove code 2019-01-06 18:25:41 +08:00
Dr_rOot 0e4b7dbc43 fix: linux application menu command chain 2019-01-06 18:25:01 +08:00
Dr_rOot e73e9c5b27 fix: win32 & linux first menu label text to File 2019-01-06 18:22:21 +08:00
Dr_rOot 078ddab4ad refactor: remove code 2019-01-06 18:16:07 +08:00
Dr_rOot 749c77ab3e refactor: store app module 2019-01-06 18:10:21 +08:00
Dr_rOot 7c2eb7997a feat: add en locales 2019-01-06 18:08:22 +08:00
Dr_rOot 6beb1ca5a4 feat: add zh locales 2019-01-06 18:08:15 +08:00
Dr_rOot b9bd866d4b fix: bt enable dht 2019-01-06 17:53:08 +08:00
Dr_rOot 08c96b5728 fix: only show title bar when is renderer 2019-01-05 19:38:36 +08:00
Dr_rOot 4f77a8ebdc refactor: remove unuse style code 2019-01-04 20:18:20 +08:00
Dr_rOot 75d7fad29f fix: disable text selection within a draggable 2019-01-04 20:05:53 +08:00
Dr_rOot 0adc62f04c refactor: change will deprecated api 2019-01-04 16:31:18 +08:00
Dr_rOot dadc0801c2 fix: update readme 2019-01-04 13:04:55 +08:00
Dr_rOot 334d4e6386 feat: support thunder links 2019-01-04 13:03:40 +08:00
Dr_rOot 47b8ad6e56 fix: change default seed-ratio 2019-01-03 22:08:15 +08:00
Dr_rOot c621fc457d fix: remove unuse preference basic form keys 2019-01-03 22:07:16 +08:00
244 changed files with 13613 additions and 5119 deletions
+8
View File
@@ -0,0 +1,8 @@
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
-14
View File
@@ -143,20 +143,6 @@ let webConfig = {
? path.resolve(__dirname, '../node_modules')
: false
}),
new HtmlWebpackPlugin({
title: '关于',
filename: 'about.html',
chunks: ['about'],
template: path.resolve(__dirname, '../src/about.ejs'),
// minify: {
// collapseWhitespace: true,
// removeAttributeQuotes: true,
// removeComments: true
// },
nodeModules: devMode
? path.resolve(__dirname, '../node_modules')
: false
}),
new webpack.DefinePlugin({
'process.env.IS_WEB': 'true'
}),
+39
View File
@@ -0,0 +1,39 @@
---
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 close the "Hide Menu Bar" in Preferences - Advanced Settings - Appearance. After saving and applying, the menu bar will appear. -->
**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
@@ -0,0 +1,43 @@
---
name: 错误反馈
about: 创建一个错误报告帮助改进「请按照模板提交,提供详细的信息,方便我们复现之后处理」
title: ''
labels: ''
assignees: ''
---
<!--
反馈之前请搜索一下已有 issues 和 帮助文档,看是否有类似问题可以解决你的问题
https://github.com/agalwood/Motrix/issues
http://motrix.app/support
按以下格式填写反馈信息,谢谢
-->
**错误描述**
清楚简洁地描述错误,方便我们复现之后处理。
**如何重现**
重现步骤,如:
1. 点击新建任务按钮
2. 黏贴链接(如链接不涉及到隐私和版权问题,请顺便提供)
3. 点击提交
4. 发现报错
**预期的行为**
清楚简洁地描述你期望发生的事情。
**截图**
请添加屏幕截图以帮助解释你的问题:
打开应用菜单中的「帮助」——「开发者工具」—— 切换到 console,然后**完整**截图。
<!-- Windows 和 Linux 版本默认隐藏了应用菜单,请到偏好设置-进阶设置-外观里关闭“隐藏菜单栏”,保存并应用之后菜单栏就出现了 -->
**运行环境**
- 操作系统类型: [如 macOS, Windows, Linux]
- 具体版本: [如 macOS 10.14.2, Windows 10, Ubuntu 18.04]
- Motrix 版本: [如 v1.1.3, v1.1.0]
- 安装包类型:[如 dmg, AppImage]
**更多信息**
补充有关该问题的其他信息。
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement ✨
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,28 @@
---
name: 新功能请求
about: 你期望 Motrix 未来添加的新功能
title: ''
labels: enhancement ✨
assignees: ''
---
<!--
反馈之前请搜索一下已有 issues 和 帮助文档,看是否已经有人提交了类似的新功能请求
https://github.com/agalwood/Motrix/issues
http://motrix.app/support
按以下格式填写反馈信息,谢谢
-->
**请描述一下你的新功能请求是否与已知问题有关?**
简明扼要地描述了问题所在。
**描述你想要的解决方案**
简明扼要地描述你想要的解决方案。
**描述你考虑过的替代方案**
简明扼要地描述你考虑过的任何替代解决方案或功能。
**更多信息**
补充有关该新功能的其他信息。
+38
View File
@@ -0,0 +1,38 @@
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 30
# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels: []
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: >
This thread has been automatically locked since there has not been
any recent activity after it was closed. Please open a new issue for
related bugs.
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: true
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo
+1
View File
@@ -9,3 +9,4 @@ npm-debug.log.*
thumbs.db
!.gitkeep
release/*
.idea/
+39
View File
@@ -0,0 +1,39 @@
# Motrix 贡献指南
## 🌍 翻译指南
首先你要确定一个语言的英文简写作为 **locale**,如 en-US,这个 locale 值请严格参考 [Electron 的 Locales 文档](https://electronjs.org/docs/api/locales)
Motrix 的国际化分两部分:
- Element UI
- 菜单和主界面
### Element UI
Element UI 的国际化由 [Element 社区](http://element.eleme.io/#/en-US/component/i18n)提供,找到 **locale** 对应的语言包文件「两者 locale 命名可能不一致」,在 `src/shared/locales/all.js` 中引入,如
```javascript
import eleLocaleEn from 'element-ui/lib/locale/lang/en'
import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN'
```
### 菜单和主界面
Motrix 使用 i18next 作为翻译支持库,所以你可能需要简单了解一下它的[使用方法](https://www.i18next.com/overview/getting-started)。
配置文件按照语言 (**locale**) 划分目录:`src/shared/locales`,如:`src/shared/locales/en-US``src/shared/locales/zh-CN`
目录里面有按业务模块划分的语言文件
菜单模块经过重构之后,国际化已经打散到了以下文件里了,不再需要再复制 `src/main/menus` 里的配置。
- about.js
- app.js
- edit.js
- help.js
- index.js
- menu.js
- preferences.js
- subnav.js
- task.js
- window.js
+39
View File
@@ -0,0 +1,39 @@
# Motrix Contributing Guide
## 🌍 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).
The internationalization of Motrix is divided into two parts:
- Element UI
- Menu & Main Interface
### Element UI
The internationalization of Element UI is provided by the [Element community](http://element.eleme.io/#/en-US/component/i18n), then find the language pack file corresponding to **locale** (both locale naming may be inconsistent), which is import in `src/shared/locales/all.js`, such as
```javascript
import eleLocaleEn from 'element-ui/lib/locale/lang/en'
import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN'
```
### Menu & Main Interface
Motrix uses the [i18next](https://www.i18next.com/overview/getting-started) library for internationalization, so you need a quick look at how to use it.
The configuration files are divided by **locale**: `src/shared/locales`, such as `src/shared/locales/en-US` and `src/shared/locales/zh-CN`.
There are language files in the directory according to the business module.
After the menu module is refactored, the internationalization of the menu has been dispersed into the following files, and there is no need to copy the configuration in `src/main/menus`.
- about.js
- app.js
- edit.js
- help.js
- index.js
- menu.js
- preferences.js
- subnav.js
- task.js
- window.js
+147
View File
@@ -0,0 +1,147 @@
# 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" />
</a>
[English](./README.md) | 简体中文
## 一款全能的下载工具
[![GitHub release](https://img.shields.io/github/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) [![Build Status](https://travis-ci.org/agalwood/Motrix.svg?branch=master)](https://travis-ci.org/agalwood/Motrix) [![Build status](https://ci.appveyor.com/api/projects/status/l11d5h05xwwcvoux/branch/master?svg=true)](https://ci.appveyor.com/project/agalwood/motrix/branch/master) [![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg)](https://github.com/agalwood/Motrix/releases) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
我是个兴趣使然的桌面应用开发者🤓,利用搬砖之余开发了 Motrix。
Motrix 是一款全能的下载工具,支持下载 HTTP、FTP、BT、磁力链、百度网盘等资源。它的界面简洁易用,希望大家喜欢 👻。
✈️ 去 [官网](https://motrix.app/zh-CN) 逛逛 | 📖 查看 [帮助手册](http://motrix.app/support/issues)
## 💽 安装稳定版
[GitHub](https://github.com/agalwood/Motrix/releases) 和 [官网](https://motrix.app/zh-CN) 提供了已经编译好的稳定版安装包,当然你也可以自己克隆代码编译打包。
### Windows
建议使用安装包(Motrix-Setup-x.y.z.exe)安装 Motrix 以确保完整的体验,例如关联 torrent 文件,捕获磁力链等。
如果你更喜欢便携版,你可以使用 [scoop](https://github.com/lukesampson/scoop)(需要 Windows 7+)安装最新便携版本的 Motrix。
```bash
scoop install motrix
```
### macOS
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
```
### Linux
你可以下载 AppImage(适用于所有 Linux 发行版)软件包或 snap 或从源代码构建安装 Motrix。
构建请阅读 **编译打包** 部分。
对于 Arch Linux 用户,可以使用 [aur](https://aur.archlinux.org/packages/motrix/) 安装 Motrix,感谢维护者 [weearc](https://github.com/weearc)。
运行以下命令进行安装:
```bash
yay motrix
```
## ✨ 特性
- 🕹 简洁明了的图形操作界面
- 🦄 支持BT和磁力链任务
- 💾 支持下载百度云盘资源
- 🎛 最高支持 10 个任务同时下载
- 🚀 单任务最高支持 64 线程下载
- 🕶 模拟用户代理UA
- 🔔 下载完成后通知
- 💻 支持触控栏快捷键 (Mac 专享)
- 🤖 常驻系统托盘,操作更加便捷
- 🌑 深色模式
- 🗑 移除任务时可同时删除相关文件
- 🌍 国际化,[查看已可选的语言](#-国际化)
- 🎏 ...
## 🖥 应用界面
![motrix-screenshot-task-cn.png](https://cdn.nlark.com/yuque/0/2019/png/129147/1550151234585-e513bd4f-e127-402f-accb-1ebbba9b3c41.png)
## ⌨️ 本地开发
### 克隆代码
```bash
git clone git@github.com:agalwood/Motrix.git
```
### 安装依赖
```bash
cd Motrix
npm install
```
天朝大陆用户建议使用淘宝的 npm 源
```bash
npm config set registry 'https://registry.npm.taobao.org'
export ELECTRON_MIRROR='https://npm.taobao.org/mirrors/electron/'
export SASS_BINARY_SITE='https://npm.taobao.org/mirrors/node-sass'
```
如果喜欢 [Yarn](https://yarnpkg.com/),也可以使用 `yarn` 安装依赖
### 开发模式
```bash
npm run dev
```
### 编译打包
```bash
npm run build
```
完成之后可以在项目的 `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 位的)
## ☑️ TODO
开发计划请移步 [Trello](https://trello.com/b/qNUzA0bv/motrix) 查看
## 🤝 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
如果你有兴趣参与共同开发,欢迎 FORK 和 PR。
## 🌍 国际化
欢迎大家将 Motrix 翻译成更多的语言版本 🧐,开工之前请先阅读一下 [翻译指南](./CONTRIBUTING-CN.md#-翻译指南)。
| Key | Name | Status |
|-------|:--------------------|:-------------|
| de | German | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
| en-US | English | ✔️ |
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
| zh-CN | 简体中文 | ✔️ |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) |
## 📜 开源许可
基于 [MIT license](https://opensource.org/licenses/MIT) 许可进行开源。
+106 -32
View File
@@ -1,65 +1,139 @@
# Motrix
<a href="https://motrix.app">
<img src="https://motrix.app/images/app-icon@2x.png" width="256" alt="App Icon" />
<img src="https://cdn.nlark.com/yuque/0/2018/png/129147/1543735425232-a5d2c99f-d788-43e4-9781-558ff6d21027.png" width="256" alt="App Icon" />
</a>
## 一款全能的下载工具
[![Build Status](https://travis-ci.org/agalwood/Motrix.svg?branch=master)](https://travis-ci.org/agalwood/Motrix) [![Build status](https://ci.appveyor.com/api/projects/status/l11d5h05xwwcvoux/branch/master?svg=true)](https://ci.appveyor.com/project/agalwood/motrix/branch/master)
## A full-featured download manager
[![GitHub release](https://img.shields.io/github/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) [![Build Status](https://travis-ci.org/agalwood/Motrix.svg?branch=master)](https://travis-ci.org/agalwood/Motrix) [![Build status](https://ci.appveyor.com/api/projects/status/l11d5h05xwwcvoux/branch/master?svg=true)](https://ci.appveyor.com/project/agalwood/motrix/branch/master) [![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg)](https://github.com/agalwood/Motrix/releases) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
支持下载 HTTP、FTP、BT、磁力链、百度网盘等资源
English | [简体中文](./README-CN.md)
<span style="font-size: 30px">我</span>是个兴趣使然的桌面应用开发者🤓,出于兴趣爱好,利用搬砖之余开发了 [MO 1.0](https://moapp.me) 版本,做出来有大半年了,没做过什么推广,所以大家可能都没怎么听过这个应用吧~👻~
Motrix is a full-featured download manager that supports downloading HTTP, FTP, BitTorrent, Magnet, Baidu Net Disk, etc.
本着自己用得舒(折)服(腾)😌的想法,🤠撸出了个全新的版本,并更名为 Motrix。新版本不仅优化了性能,还重新设计了图形操作界面,操作更简便!
Motrix has a clean and easy to use interface. I hope you will like it 👻.
官网提供了已经编译好的应用安装包([去官网下载](https://motrix.app)),当然你也可以自己克隆代码进行编译打包。
✈️ [Official Website](https://motrix.app) | 📖 [Manual](https://github.com/agalwood/Motrix/wiki)
## 🛠 技术栈
- [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 位的)
## 💽 Installation
## 📦 自行编译
Download from [GitHub Releases](https://github.com/agalwood/Motrix/releases) and install it.
### Windows
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 prefer the portable version, you can use [scoop](https://github.com/lukesampson/scoop) (need Windows 7+) to install Motrix.
```bash
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).
```bash
brew update && brew cask install motrix
```
### Linux
You can download the AppImage (for all Linux distributions) package or snap or just build from source code to install Motrix.
Please read the **Build** section.
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
```
## ✨ Features
- 🕹 Simple and clear user interface
- 🦄 Supports BitTorrent & Magnet
- 💾 Supports downloading Baidu Net Disk
- 🎛 Up to 10 concurrent download tasks
- 🚀 Supports 64 threads in a single task
- 🕶 Mock User-Agent
- 🔔 Download completed Notification
- 💻 Ready for Touch Bar (Mac only)
- 🤖 Resident system tray for quick operation
- 🌑 Dark mode
- 🗑 Delete related files when removing tasks (optional)
- 🌍 I18n, [View supported languages](#-internationalization).
- 🎏 ...
## 🖥 User Interface
![motrix-screenshot-task-en.png](https://cdn.nlark.com/yuque/0/2019/png/129147/1550151166169-94b4bfb0-746e-42b8-aad7-0b6890f89abb.png)
## ⌨️ Development
### Clone Code
### 克隆代码
```bash
git clone git@github.com:agalwood/Motrix.git
```
### 安装依赖
### Install Dependencies
```bash
cd Motrix
npm install
```
天朝大陆用户建议使用淘宝的npm源
```bash
npm config set registry 'https://registry.npm.taobao.org'
export ELECTRON_MIRROR='https://npm.taobao.org/mirrors/electron/'
export SASS_BINARY_SITE='https://npm.taobao.org/mirrors/node-sass'
```
如果喜欢 [Yarn](https://yarnpkg.com/),也可以使用 `yarn` 安装依赖
### 开发模式
If you like [Yarn](https://yarnpkg.com/), you can also use `yarn` to install dependencies.
### Dev Mode
```bash
npm run dev
```
### 编译打包
### Build Release
```bash
npm run build
```
完成之后可以在项目的 release 目录看到编译打包好的应用文件
After building, the application will be found in the project's `release` directory.
## 🛠 Technology Stack
- [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)
## ☑️ TODO
- [ ] 国际化支持
- [ ] macOS Mojave 深色模式
- [ ] Windows 和 Linux 版本理论上会有,还未调试
- [ ] 测试用例
## 🤝 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
如果你有兴趣参与共同开发,欢迎 FORK 和 PR。
Development Roadmap see: [Trello](https://trello.com/b/qNUzA0bv/motrix)
## 📜 开源许可
基于 [MIT license](https://opensource.org/licenses/MIT) 许可进行开源。
## 🤝 Contribute [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
If you are interested in participating in joint development, PR and Forks are welcome!
## 🌍 Internationalization
Translations into versions for other languages are welcome 🧐! Please read the [translation guide](./CONTRIBUTING.md#-translation-guide) before starting translations.
| Key | Name | Status |
|-------|:--------------------|:-------------|
| de | German | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
| en-US | English | ✔️ |
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
| zh-CN | 简体中文 | ✔️ |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) |
## 📜 License
[MIT](https://opensource.org/licenses/MIT) Copyright (c) 2018-present Dr_rOot
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

+18 -13
View File
@@ -33,7 +33,7 @@
# 单个任务下载速度限制, 默认:0
#@max-download-limit=0
# 整体上传速度限制, 运行时可修改, 默认:0
#@max-overall-upload-limit=0
max-overall-upload-limit=128K
# 单个任务上传速度限制, 默认:0
#@max-upload-limit=0
# 禁用IPv6, 默认:false
@@ -73,26 +73,30 @@ rpc-listen-all=true
# 当下载的是一个种子(以.torrent结尾)时, 自动开始BT任务, 默认:true
#follow-torrent=true
# BT监听端口, 当端口被屏蔽时使用, 默认:6881-6999
listen-port=51413
listen-port=50101-50109
# 单个种子最大连接数, 默认:55
#bt-max-peers=55
# 打开DHT功能, PT需要禁用, 默认:true
enable-dht=false
enable-dht=true
# 打开IPv6 DHT功能, PT需要禁用
#enable-dht6=false
enable-dht6=true
# DHT网络监听端口, 默认:6881-6999
#dht-listen-port=6881-6999
dht-listen-port=50101-50109
# 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
# 本地节点查找, PT需要禁用, 默认:false
#bt-enable-lpd=false
bt-enable-lpd=true
# 种子交换, PT需要禁用, 默认:true
#enable-peer-exchange=false
enable-peer-exchange=true
# 每个种子限速, 对少种的PT很有用, 默认:50K
#bt-request-peer-speed-limit=50K
# 客户端伪装, PT需要
peer-id-prefix=-TR2770-
user-agent=Transmission/2.94
# 当种子的分享率达到这个数时, 自动停止做种, 0为一直做种, 默认:1.0
seed-ratio=0
seed-ratio=1.0
# 强制保存会话, 即使任务已经完成, 默认:false
# 较新的版本开启后会在任务完成后依然保留.aria2文件
#force-save=false
@@ -101,8 +105,9 @@ seed-ratio=0
# 继续之前的BT任务时, 无需再次校验, 默认:false
bt-seed-unverified=true
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
bt-save-metadata=true
# bt-tracker数据来自https://github.com/ngosang/trackerslist/blob/master/trackers_best.txt
bt-tracker=udp://tracker.coppersurfer.tk:6969/announce,udp://tracker.opentrackr.org:1337/announce,udp://tracker.internetwarriors.net:1337/announce,udp://9.rarbg.to:2710/announce,udp://exodus.desync.com:6969/announce,udp://tracker2.itzmx.com:6961/announce,udp://tracker1.itzmx.com:8080/announce,udp://explodie.org:6969/announce,http://tracker.tfile.me:80/announce.php,http://tracker.tfile.me:80/announce,http://tracker.tfile.co:80/announce,http://peersteers.org:80/announce,udp://tracker.tiny-vps.com:6969/announce,udp://ipv4.tracker.harry.lu:80/announce,udp://denis.stalker.upeer.me:6969/announce,udp://tracker.torrent.eu.org:451/announce,udp://tracker.port443.xyz:6969/announce,udp://tracker.cyberia.is:6969/announce,udp://thetracker.org:80/announce,udp://retracker.lanta-net.ru:2710/announce
bt-save-metadata=false
# Removes the unselected files when download is completed in BitTorrent.
# To select files, use --select-file option. If it is not used,
# all files are assumed to be selected. Please use this option with care
# because it will actually remove files from your disk. Default: false
bt-remove-unselected-file=true
+18 -13
View File
@@ -33,7 +33,7 @@ file-allocation=trunc
# 单个任务下载速度限制, 默认:0
#@max-download-limit=0
# 整体上传速度限制, 运行时可修改, 默认:0
#@max-overall-upload-limit=0
max-overall-upload-limit=128K
# 单个任务上传速度限制, 默认:0
#@max-upload-limit=0
# 禁用IPv6, 默认:false
@@ -73,26 +73,30 @@ rpc-listen-all=true
# 当下载的是一个种子(以.torrent结尾)时, 自动开始BT任务, 默认:true
#follow-torrent=true
# BT监听端口, 当端口被屏蔽时使用, 默认:6881-6999
listen-port=51413
listen-port=50101-50109
# 单个种子最大连接数, 默认:55
#bt-max-peers=55
# 打开DHT功能, PT需要禁用, 默认:true
enable-dht=false
enable-dht=true
# 打开IPv6 DHT功能, PT需要禁用
#enable-dht6=false
enable-dht6=true
# DHT网络监听端口, 默认:6881-6999
#dht-listen-port=6881-6999
dht-listen-port=50101-50109
# 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
# 本地节点查找, PT需要禁用, 默认:false
#bt-enable-lpd=false
bt-enable-lpd=true
# 种子交换, PT需要禁用, 默认:true
#enable-peer-exchange=false
enable-peer-exchange=true
# 每个种子限速, 对少种的PT很有用, 默认:50K
#bt-request-peer-speed-limit=50K
# 客户端伪装, PT需要
peer-id-prefix=-TR2770-
user-agent=Transmission/2.94
# 当种子的分享率达到这个数时, 自动停止做种, 0为一直做种, 默认:1.0
seed-ratio=0
seed-ratio=1.0
# 强制保存会话, 即使任务已经完成, 默认:false
# 较新的版本开启后会在任务完成后依然保留.aria2文件
#force-save=false
@@ -101,8 +105,9 @@ seed-ratio=0
# 继续之前的BT任务时, 无需再次校验, 默认:false
bt-seed-unverified=true
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
bt-save-metadata=true
# bt-tracker数据来自https://github.com/ngosang/trackerslist/blob/master/trackers_best.txt
bt-tracker=udp://tracker.coppersurfer.tk:6969/announce,udp://tracker.opentrackr.org:1337/announce,udp://tracker.internetwarriors.net:1337/announce,udp://9.rarbg.to:2710/announce,udp://exodus.desync.com:6969/announce,udp://tracker2.itzmx.com:6961/announce,udp://tracker1.itzmx.com:8080/announce,udp://explodie.org:6969/announce,http://tracker.tfile.me:80/announce.php,http://tracker.tfile.me:80/announce,http://tracker.tfile.co:80/announce,http://peersteers.org:80/announce,udp://tracker.tiny-vps.com:6969/announce,udp://ipv4.tracker.harry.lu:80/announce,udp://denis.stalker.upeer.me:6969/announce,udp://tracker.torrent.eu.org:451/announce,udp://tracker.port443.xyz:6969/announce,udp://tracker.cyberia.is:6969/announce,udp://thetracker.org:80/announce,udp://retracker.lanta-net.ru:2710/announce
bt-save-metadata=false
# Removes the unselected files when download is completed in BitTorrent.
# To select files, use --select-file option. If it is not used,
# all files are assumed to be selected. Please use this option with care
# because it will actually remove files from your disk. Default: false
bt-remove-unselected-file=true
+18 -13
View File
@@ -33,7 +33,7 @@ file-allocation=falloc
# 单个任务下载速度限制, 默认:0
#@max-download-limit=0
# 整体上传速度限制, 运行时可修改, 默认:0
#@max-overall-upload-limit=0
max-overall-upload-limit=128K
# 单个任务上传速度限制, 默认:0
#@max-upload-limit=0
# 禁用IPv6, 默认:false
@@ -73,26 +73,30 @@ rpc-listen-all=true
# 当下载的是一个种子(以.torrent结尾)时, 自动开始BT任务, 默认:true
#follow-torrent=true
# BT监听端口, 当端口被屏蔽时使用, 默认:6881-6999
listen-port=51413
listen-port=50101-50109
# 单个种子最大连接数, 默认:55
#bt-max-peers=55
# 打开DHT功能, PT需要禁用, 默认:true
enable-dht=false
enable-dht=true
# 打开IPv6 DHT功能, PT需要禁用
#enable-dht6=false
enable-dht6=true
# DHT网络监听端口, 默认:6881-6999
#dht-listen-port=6881-6999
dht-listen-port=50101-50109
# 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
# 本地节点查找, PT需要禁用, 默认:false
#bt-enable-lpd=false
bt-enable-lpd=true
# 种子交换, PT需要禁用, 默认:true
#enable-peer-exchange=false
enable-peer-exchange=true
# 每个种子限速, 对少种的PT很有用, 默认:50K
#bt-request-peer-speed-limit=50K
# 客户端伪装, PT需要
peer-id-prefix=-TR2770-
user-agent=Transmission/2.94
# 当种子的分享率达到这个数时, 自动停止做种, 0为一直做种, 默认:1.0
seed-ratio=0
seed-ratio=1.0
# 强制保存会话, 即使任务已经完成, 默认:false
# 较新的版本开启后会在任务完成后依然保留.aria2文件
#force-save=false
@@ -101,8 +105,9 @@ seed-ratio=0
# 继续之前的BT任务时, 无需再次校验, 默认:false
bt-seed-unverified=true
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
bt-save-metadata=true
# bt-tracker数据来自https://github.com/ngosang/trackerslist/blob/master/trackers_best.txt
bt-tracker=udp://tracker.coppersurfer.tk:6969/announce,udp://tracker.opentrackr.org:1337/announce,udp://tracker.internetwarriors.net:1337/announce,udp://9.rarbg.to:2710/announce,udp://exodus.desync.com:6969/announce,udp://tracker2.itzmx.com:6961/announce,udp://tracker1.itzmx.com:8080/announce,udp://explodie.org:6969/announce,http://tracker.tfile.me:80/announce.php,http://tracker.tfile.me:80/announce,http://tracker.tfile.co:80/announce,http://peersteers.org:80/announce,udp://tracker.tiny-vps.com:6969/announce,udp://ipv4.tracker.harry.lu:80/announce,udp://denis.stalker.upeer.me:6969/announce,udp://tracker.torrent.eu.org:451/announce,udp://tracker.port443.xyz:6969/announce,udp://tracker.cyberia.is:6969/announce,udp://thetracker.org:80/announce,udp://retracker.lanta-net.ru:2710/announce
bt-save-metadata=false
# Removes the unselected files when download is completed in BitTorrent.
# To select files, use --select-file option. If it is not used,
# all files are assumed to be selected. Please use this option with care
# because it will actually remove files from your disk. Default: false
bt-remove-unselected-file=true
+5924 -3320
View File
File diff suppressed because it is too large Load Diff
+85 -46
View File
@@ -1,6 +1,6 @@
{
"name": "Motrix",
"version": "1.0.10",
"version": "1.3.8",
"description": "A full-featured download manager",
"homepage": "https://motrix.app",
"author": {
@@ -32,6 +32,14 @@
"build": {
"productName": "Motrix",
"appId": "net.agalwood.Motrix",
"fileAssociations": [
{
"ext": "torrent",
"mimeType": "application/x-bittorrent",
"name": "Torrent",
"role": "Viewer"
}
],
"asar": true,
"directories": {
"output": "release"
@@ -39,6 +47,21 @@
"files": [
"dist/electron/**/*"
],
"protocols": [
{
"name": "Motrix Protocol",
"schemes": [
"mo",
"motrix"
]
},
{
"name": "Magnet Protocol",
"schemes": [
"magnet"
]
}
],
"dmg": {
"window": {
"width": 540,
@@ -75,21 +98,31 @@
"binaries": [
"./release/mac/Motrix.app/Contents/Resources/engine/aria2c"
],
"category": "public.app-category.utilities",
"protocols": [
{
"name": "Motrix Protocol",
"schemes": [
"mo",
"motrix"
]
}
]
"category": "public.app-category.utilities"
},
"win": {
"target": [
"nsis",
"portable"
{
"target": "nsis",
"arch": [
"x64",
"ia32"
]
},
{
"target": "zip",
"arch": [
"x64",
"ia32"
]
},
{
"target": "portable",
"arch": [
"x64",
"ia32"
]
}
],
"extraResources": {
"from": "./extra/win32/",
@@ -104,8 +137,10 @@
"allowToChangeInstallationDirectory": true
},
"linux": {
"category": "Network",
"target": [
"deb",
"snap",
"AppImage"
],
"extraResources": {
@@ -127,65 +162,69 @@
]
},
"dependencies": {
"@panter/vue-i18next": "^0.15.0",
"aria2": "^4.0.3",
"axios": "^0.18.0",
"clipboard-polyfill": "^2.7.0",
"electron-debug": "^2.0.0",
"blob-util": "^2.0.2",
"clipboard-polyfill": "^2.8.0",
"electron-debug": "^2.2.0",
"electron-is": "^3.0.0",
"electron-log": "^2.2.17",
"electron-updater": "^4.0.7",
"element-ui": "^2.4.11",
"electron-log": "^3.0.5",
"electron-updater": "^4.0.9",
"element-ui": "^2.7.2",
"forever-monitor": "^1.7.1",
"i18next": "^15.0.9",
"lodash": "^4.17.11",
"normalize.css": "^8.0.1",
"parse-torrent": "^6.1.2",
"svg-innerhtml": "^1.1.0",
"vue": "^2.5.17",
"vue": "^2.6.10",
"vue-electron": "^1.0.6",
"vue-router": "^3.0.2",
"vuex": "^3.0.1",
"vue-router": "^3.0.6",
"vuex": "^3.1.0",
"vuex-router-sync": "^5.0.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.2.2",
"@vue/cli-plugin-eslint": "^3.2.2",
"@vue/cli-service": "^3.2.2",
"@vue/cli-plugin-babel": "^3.6.0",
"@vue/cli-plugin-eslint": "^3.6.0",
"@vue/cli-service": "^3.6.0",
"@vue/eslint-config-standard": "^4.0.0",
"ajv": "^6.6.2",
"ajv": "^6.10.0",
"babel-core": "^6.26.3",
"babel-eslint": "^10.0.1",
"babel-loader": "^7.1.4",
"babel-loader": "^7.1.5",
"babel-plugin-component": "^1.1.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"babili-webpack-plugin": "^0.1.2",
"cfonts": "^2.2.3",
"chalk": "^2.4.1",
"copy-webpack-plugin": "^4.6.0",
"cfonts": "^2.4.2",
"chalk": "^2.4.2",
"copy-webpack-plugin": "^5.0.3",
"cross-env": "^5.1.6",
"css-loader": "^1.0.1",
"del": "^3.0.0",
"css-loader": "^2.1.1",
"del": "^4.1.0",
"devtron": "^1.4.0",
"electron": "^4.0.0",
"electron-builder": "^20.38.4",
"electron": "^4.1.5",
"electron-builder": "^20.39.0",
"electron-devtools-installer": "^2.2.4",
"electron-notarize": "^0.0.5",
"electron-osx-sign": "^0.4.11",
"electron-store": "^2.0.0",
"eslint": "^5.11.1",
"eslint": "^5.16.0",
"eslint-config-standard": "^12.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^2.1.1",
"eslint-loader": "^2.1.2",
"eslint-plugin-html": "^4.0.6",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-import": "^2.17.2",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-standard": "^4.0.0",
"file-loader": "^2.0.0",
"eslint-plugin-vue": "^5.2.2",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "0.5.0",
"mini-css-extract-plugin": "0.6.0",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"node-sass": "^4.10.0",
@@ -194,13 +233,13 @@
"style-loader": "^0.23.1",
"url-loader": "^1.1.2",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.4.2",
"vue-loader": "^15.7.0",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.28.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.30.0",
"webpack-cli": "^3.3.1",
"webpack-dev-server": "^3.3.1",
"webpack-hot-middleware": "^2.24.3",
"webpack-merge": "^4.1.4"
"webpack-merge": "^4.2.1"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

+5 -3
View File
@@ -23,9 +23,11 @@
</section>
</div>
<!-- Set `__static` path to static files in production -->
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<% if (!process.browser) { %>
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<% } %>
<!-- webpack builds are automatically injected -->
</body>
+210 -30
View File
@@ -1,29 +1,44 @@
import { EventEmitter } from 'events'
import { app, shell, dialog, ipcMain } from 'electron'
import is from 'electron-is'
import { readFile } from 'fs'
import { extname, basename } from 'path'
import logger from './core/Logger'
import ExceptionHandler from './core/ExceptionHandler'
import ConfigManager from './core/ConfigManager'
import { setupLocaleManager } from '@/ui/Locale'
import Engine from './core/Engine'
import AutoLaunchManager from './core/AutoLaunchManager'
import UpdateManager from './core/UpdateManager'
import EnergyManager from './core/EnergyManager'
import ProtocolManager from './core/ProtocolManager'
import WindowManager from './ui/WindowManager'
import MenuManager from './ui/MenuManager'
import TouchBarManager from './ui/TouchBarManager'
import TrayManager from './ui/TrayManager'
import ThemeManager from './ui/ThemeManager'
import { AUTO_CHECK_UPDATE_INTERVAL } from '@shared/constants'
export default class Application extends EventEmitter {
constructor () {
super()
this.isReady = false
this.init()
}
this.exceptionHandler = new ExceptionHandler()
this.locale = app.getLocale()
logger.log('[Motrix] Locale: ', this.locale)
init () {
this.configManager = new ConfigManager()
this.windowManager = new WindowManager()
this.locale = this.configManager.getLocale()
this.localeManager = setupLocaleManager(this.locale)
this.i18n = this.localeManager.getI18n()
this.menuManager = new MenuManager()
this.menuManager.setup(this.locale)
this.initTouchBarManager()
this.initWindowManager()
this.engine = new Engine({
systemConfig: this.configManager.getSystemConfig(),
@@ -31,10 +46,11 @@ export default class Application extends EventEmitter {
})
this.startEngine()
this.menuManager = new MenuManager()
this.menuManager.setup(this.locale)
this.trayManager = new TrayManager()
this.touchBarManager = new TouchBarManager()
this.autoLaunchManager = new AutoLaunchManager()
this.initThemeManager()
this.energyManager = new EnergyManager()
@@ -43,6 +59,7 @@ export default class Application extends EventEmitter {
this.initProtocolManager()
this.handleCommands()
this.handleIpcMessages()
}
@@ -50,12 +67,11 @@ export default class Application extends EventEmitter {
try {
this.engine.start()
} catch (err) {
const { message } = err
dialog.showMessageBox({
type: 'error',
title: '系统错误',
message: `应用启动失败:${err.message}`,
buttons: ['知道了'],
cancelId: 1
title: this.i18n.t('app.system-error-title'),
message: this.i18n.t('app.system-error-message', { message })
}, () => {
setTimeout(() => {
app.quit()
@@ -64,13 +80,69 @@ export default class Application extends EventEmitter {
}
}
start (page) {
this.showPage(page)
initWindowManager () {
this.windowManager = new WindowManager({
userConfig: this.configManager.getUserConfig()
})
this.windowManager.on('window-resized', (data) => {
this.storeWindowState(data)
})
this.windowManager.on('window-moved', (data) => {
this.storeWindowState(data)
})
this.windowManager.on('window-closed', (data) => {
this.storeWindowState(data)
})
}
showPage (page) {
const win = this.windowManager.openWindow(page)
this.touchBarManager.setup(page, win)
storeWindowState (data = {}) {
const enabled = this.configManager.getUserConfig('keep-window-state')
if (!enabled) {
return
}
const state = this.configManager.getUserConfig('window-state', {})
const { page, bounds } = data
const newState = {
...state,
[page]: bounds
}
this.configManager.setUserConfig('window-state', newState)
}
start (page, options = {}) {
this.showPage(page, options)
}
showPage (page, options = {}) {
const { openedAtLogin } = options
const win = this.windowManager.openWindow(page, {
hidden: openedAtLogin
})
win.once('ready-to-show', () => {
this.isReady = true
this.emit('ready')
})
if (is.macOS()) {
this.touchBarManager.setup(page, win)
}
}
show (page = 'index') {
this.windowManager.showWindow(page)
}
hide (page) {
if (page) {
this.windowManager.hideWindow(page)
} else {
this.windowManager.hideAllWindow()
}
}
toggle (page = 'index') {
this.windowManager.toggleWindow(page)
}
closePage (page) {
@@ -80,6 +152,7 @@ export default class Application extends EventEmitter {
stop () {
this.engine.stop()
this.energyManager.stopPowerSaveBlocker()
this.trayManager.destroy()
}
sendCommand (command, ...args) {
@@ -93,41 +166,94 @@ export default class Application extends EventEmitter {
sendCommandToAll (command, ...args) {
if (!this.emit(command, ...args)) {
this.windowManager.getWindows().forEach(window => {
this.windowManager.getWindowList().forEach(window => {
this.windowManager.sendCommandTo(window, command, ...args)
})
}
}
sendMessageToAll (channel, ...args) {
this.windowManager.getWindows().forEach(window => {
this.windowManager.getWindowList().forEach(window => {
this.windowManager.sendMessageTo(window, channel, ...args)
})
}
initThemeManager () {
this.themeManager = new ThemeManager()
this.themeManager.on('system-theme-changed', (theme) => {
this.trayManager.changeIconTheme(theme)
this.sendCommandToAll('application:system-theme', theme)
})
}
initTouchBarManager () {
if (!is.macOS()) {
return
}
this.touchBarManager = new TouchBarManager()
}
initProtocolManager () {
if (is.mas()) {
if (is.dev() || is.mas()) {
return
}
this.protocolManager = new ProtocolManager()
}
handleProtocol (url) {
if (is.mas()) {
if (is.dev() || is.mas()) {
return
}
this.show()
this.protocolManager.handle(url)
this.showPage('index')
}
handleFile (filePath) {
if (!filePath) {
return
}
if (extname(filePath).toLowerCase() !== '.torrent') {
return
}
this.show()
const fileName = 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)
})
}
initUpdaterManager () {
if (is.mas()) {
return
}
this.updateManager = new UpdateManager()
this.updateManager = new UpdateManager({
autoCheck: this.isNeedAutoCheck(),
setCheckTime: this.configManager
})
this.handleUpdaterEvents()
}
isNeedAutoCheck () {
const enable = this.configManager.getUserConfig('auto-check-update')
if (!enable) {
return false
}
const lastCheck = this.configManager.getUserConfig('last-check-update-time')
return (Date.now() - lastCheck > AUTO_CHECK_UPDATE_INTERVAL)
}
handleUpdaterEvents () {
this.updateManager.on('checking', (event) => {
this.menuManager.updateMenuItemEnabledState('app.check-for-updates', false)
@@ -176,12 +302,29 @@ export default class Application extends EventEmitter {
})
this.on('application:exit', () => {
this.engine.stop()
this.stop()
app.exit()
})
this.on('application:show', (page = 'index') => {
this.showPage(page)
this.on('application:open-at-login', (openAtLogin) => {
console.log('application:open-at-login===>', openAtLogin)
if (is.linux()) {
return
}
if (openAtLogin) {
this.autoLaunchManager.enable()
} else {
this.autoLaunchManager.disable()
}
})
this.on('application:show', (page) => {
this.show(page)
})
this.on('application:hide', (page) => {
this.hide(page)
})
this.on('application:reset', () => {
@@ -193,8 +336,41 @@ export default class Application extends EventEmitter {
this.updateManager.check()
})
this.on('application:set-locale', (locale) => {
this.menuManager.setup(locale)
this.on('application:change-theme', (theme) => {
this.themeManager.updateAppAppearance(theme)
this.sendCommandToAll('application:theme', theme)
})
this.on('application:change-locale', (locale) => {
logger.info('[Motrix] application:change-locale===>', locale)
this.localeManager.changeLanguageByLocale(locale)
.then(() => {
this.menuManager.setup(locale)
this.trayManager.setup(locale)
})
})
this.on('application:open-file', (event) => {
dialog.showOpenDialog({
properties: ['openFile'],
filters: [
{
name: 'Torrent',
extensions: ['torrent']
}
]
}, (filePaths) => {
if (!filePaths || filePaths.length === 0) {
return
}
const [filePath] = filePaths
this.handleFile(filePath)
})
})
this.on('application:clear-recent-tasks', () => {
app.clearRecentDocuments()
})
this.on('help:official-website', () => {
@@ -227,5 +403,9 @@ export default class Application extends EventEmitter {
ipcMain.on('update-menu-states', (event, visibleStates, enabledStates, checkedStates) => {
this.menuManager.updateStates(visibleStates, enabledStates, checkedStates)
})
ipcMain.on('download-status-change', (event, status) => {
this.trayManager.updateStatus(status)
})
}
}
+185
View File
@@ -0,0 +1,185 @@
import { EventEmitter } from 'events'
import { app } from 'electron'
import is from 'electron-is'
import ExceptionHandler from './core/ExceptionHandler'
import logger from './core/Logger'
import Application from './Application'
import {
splitArgv,
parseArgvAsUrl,
parseArgvAsFile
} from './utils'
const EMPTY_STRING = ''
export default class Launcher extends EventEmitter {
constructor () {
super()
this.url = EMPTY_STRING
this.file = EMPTY_STRING
this.makeSingleInstance(() => {
this.init()
})
}
makeSingleInstance (callback) {
// Mac App Store Sandboxed App not support requestSingleInstanceLock
if (is.mas()) {
callback()
return
}
const gotSingleLock = app.requestSingleInstanceLock()
if (!gotSingleLock) {
app.quit()
} else {
app.on('second-instance', (event, argv, workingDirectory) => {
logger.warn('second-instance====>', argv, workingDirectory)
global.application.showPage('index')
if (!is.macOS() && argv.length > 1) {
this.handleAppLaunchArgv(argv)
}
})
callback()
}
}
init () {
this.exceptionHandler = new ExceptionHandler()
this.openedAtLogin = is.macOS()
? app.getLoginItemSettings().wasOpenedAtLogin
: false
if (process.argv.length > 1) {
this.handleAppLaunchArgv(process.argv)
}
logger.warn('openedAtLogin===>', this.openedAtLogin)
this.handleAppEvents()
}
handleAppEvents () {
this.handleOpenUrl()
this.handleOpenFile()
this.handelAppReady()
this.handleAppWillQuit()
}
/**
* handleOpenUrl
* Event 'open-url' macOS only
* "name": "Motrix Protocol",
* "schemes": ["mo", "motrix"]
*/
handleOpenUrl () {
if (is.mas() || !is.macOS()) {
return
}
app.on('open-url', (event, url) => {
logger.info(`[Motrix] open-url: ${url}`)
event.preventDefault()
this.url = url
this.sendUrlToApplication()
})
}
/**
* handleOpenFile
* Event 'open-file' macOS only
* handle open torrent file
*/
handleOpenFile () {
if (!is.macOS()) {
return
}
app.on('open-file', (event, path) => {
logger.info(`[Motrix] open-file: ${path}`)
event.preventDefault()
this.file = path
this.sendFileToApplication()
})
}
/**
* handleAppLaunchArgv
* For Windows, Linux
* @param {array} argv
*/
handleAppLaunchArgv (argv) {
logger.info('handleAppLaunchArgv===>', argv)
// args: array, extra: map
const { args, extra } = splitArgv(argv)
logger.info('splitArgv.args===>', args)
logger.info('splitArgv.extra===>', extra)
if (extra['--opened-at-login'] === '1') {
this.openedAtLogin = true
}
const file = parseArgvAsFile(args)
if (file) {
this.file = file
this.sendFileToApplication()
}
const url = parseArgvAsUrl(args)
if (url) {
this.url = url
this.sendUrlToApplication()
}
}
sendUrlToApplication () {
if (this.url && global.application && global.application.isReady) {
global.application.handleProtocol(this.url)
this.url = EMPTY_STRING
}
}
sendFileToApplication () {
if (this.file && global.application && global.application.isReady) {
global.application.handleFile(this.file)
this.file = EMPTY_STRING
}
}
handelAppReady () {
app.on('ready', () => {
global.application = new Application()
const { openedAtLogin } = this
global.application.start('index', {
openedAtLogin
})
global.application.on('ready', () => {
this.sendUrlToApplication()
this.sendFileToApplication()
})
})
app.on('activate', () => {
if (global.application) {
logger.info('[Motrix] activate')
global.application.showPage('index')
}
})
}
handleAppWillQuit () {
app.on('will-quit', () => {
logger.info('[Motrix] will-quit')
if (global.application) {
global.application.stop()
}
})
}
}
-12
View File
@@ -13,17 +13,5 @@ export default {
},
bindCloseToHide: true,
url: is.dev() ? `http://localhost:9080` : `file://${__dirname}/index.html`
},
about: {
attrs: {
title: '关于',
width: 580,
height: 320,
backgroundColor: '#FFFFFF',
resizable: false,
minimizable: false,
maximizable: false
},
url: is.dev() ? `http://localhost:9080/about` : `file://${__dirname}/about.html`
}
}
+1
View File
@@ -1,6 +1,7 @@
export default {
'task-list': 'application:task-list',
'new-task': 'application:new-task',
'new-bt-task': 'application:new-bt-task',
'pause-all-task': 'application:pause-all-task',
'resume-all-task': 'application:resume-all-task',
'preferences': 'application:preferences',
+35
View File
@@ -0,0 +1,35 @@
import { app } from 'electron'
export default class AutoLaunchManager {
enable () {
return new Promise((resolve, reject) => {
const enabled = app.getLoginItemSettings().openAtLogin
if (enabled) {
resolve()
}
app.setLoginItemSettings({
openAtLogin: true,
// For Windows
args: [
'--opened-at-login=1'
]
})
resolve()
})
}
disable () {
return new Promise((resolve, reject) => {
app.setLoginItemSettings({ openAtLogin: false })
resolve()
})
}
isEnabled () {
return new Promise((resolve, reject) => {
const enabled = app.getLoginItemSettings().openAtLogin
resolve(enabled)
})
}
}
+101 -22
View File
@@ -2,6 +2,7 @@ import { app } from 'electron'
import is from 'electron-is'
import Store from 'electron-store'
import {
getDhtPath,
getLogPath,
getSessionPath,
getUserDownloadsPath
@@ -20,27 +21,77 @@ export default class ConfigManager {
this.initUserConfig()
}
/**
* Some aria2 conf
* https://aria2.github.io/manual/en/html/aria2c.html
*
* Best bt trackers
* https://github.com/ngosang/trackerslist
*/
initSystemConfig () {
this.systemConfig = new Store({
name: 'system',
defaults: {
dir: getUserDownloadsPath(),
// 断点续传
continue: true,
pause: true,
split: 16,
'all-proxy': '',
'allow-overwrite': true,
'auto-file-renaming': true,
'bt-tracker': [
'udp://62.138.0.158:6969/announce',
'udp://188.241.58.209:6969/announce',
'udp://188.241.58.209:6969/announce',
'udp://208.83.20.20:6969/announce',
'udp://151.80.120.115:2710/announce',
'udp://185.225.17.100:1337/announce',
'udp://151.80.120.113:2710/announce',
'udp://62.210.88.151:1337/announce',
'http://176.113.71.19:6961/announce',
'http://104.27.134.253:8080/announce',
'udp://5.2.79.219:1337/announce',
'udp://91.216.110.52:451/announce',
'udp://5.206.58.23:6969/announce',
'udp://159.100.245.181:6969/announce',
'udp://5.2.79.22:6969/announce',
'udp://176.31.241.153:80/announce',
'udp://95.211.168.204:2710/announce',
'udp://188.246.227.212:80/announce',
'udp://51.38.184.185:6969/announce',
'udp://51.15.40.114:80/announce',
'udp://tracker.coppersurfer.tk:6969/announce',
'udp://tracker.open-internet.nl:6969/announce',
'udp://tracker.leechers-paradise.org:6969/announce',
'udp://exodus.desync.com:6969/announce',
'udp://tracker.internetwarriors.net:1337/announce',
'udp://9.rarbg.to:2710/announce',
'udp://9.rarbg.me:2710/announce',
'udp://tracker.opentrackr.org:1337/announce',
'http://tracker3.itzmx.com:6961/announce',
'http://tracker1.itzmx.com:8080/announce',
'udp://open.demonii.si:1337/announce',
'udp://tracker.torrent.eu.org:451/announce',
'udp://tracker.tiny-vps.com:6969/announce',
'udp://tracker.cyberia.is:6969/announce',
'udp://denis.stalker.upeer.me:6969/announce',
'udp://thetracker.org:80/announce',
'udp://bt.xxx-tracker.com:2710/announce',
'udp://open.stealth.si:80/announce',
'udp://tracker.port443.xyz:6969/announce',
'udp://ipv4.tracker.harry.lu:80/announce'
].join(','),
'continue': true,
'dht-file-path': getDhtPath(4),
'dht-file-path6': getDhtPath(6),
'dir': getUserDownloadsPath(),
'max-concurrent-downloads': 5,
'max-connection-per-server': is.macOS() ? 64 : 16,
'max-download-limit': 0,
'max-overall-download-limit': 0,
'max-overall-upload-limit': '128K',
'min-split-size': '1M',
'pause': true,
'rpc-listen-port': 16800,
'rpc-secret': '',
'auto-file-renaming': true,
'allow-overwrite': true,
'max-concurrent-downloads': 5,
// macOS 版本修改过源码自己编译的,Linux 和 Windows 版本 暂未处理
'max-connection-per-server': is.macOS() ? 64 : 16,
'min-split-size': '1M',
'max-overall-download-limit': 0,
'max-overall-upload-limit': 0,
'max-download-limit': 0,
'all-proxy': '',
'seed-time': 60,
'split': 16,
'user-agent': 'Transmission/2.94'
}
})
@@ -49,19 +100,43 @@ export default class ConfigManager {
initUserConfig () {
this.userConfig = new Store({
name: 'user',
// Schema need electron-store upgrade to 3.x.x,
// but it will cause the application build to fail.
// schema: {
// theme: {
// type: 'string',
// enum: ['auto', 'light', 'dark']
// }
// },
defaults: {
'resume-all-when-app-launched': false,
'task-notification': true,
'all-proxy-backup': '',
'auto-check-update': is.macOS(),
'hide-app-menu': is.windows() || is.linux(),
'last-check-update-time': 0,
'locale': app.getLocale(),
'log-path': getLogPath(),
'new-task-show-downloading': true,
'auto-check-for-updates': false,
'open-at-login': false,
'resume-all-when-app-launched': false,
'keep-window-state': false,
'session-path': getSessionPath(),
'task-notification': true,
'theme': 'auto',
'update-channel': 'latest',
'use-proxy': false,
'all-proxy-backup': '',
'log-path': getLogPath(),
'session-path': getSessionPath(),
'locale': app.getLocale()
'window-state': {}
}
})
this.fixUserConfig()
}
fixUserConfig () {
// Fix the value of open-at-login when the user delete
// the Motrix self-starting item through startup management.
const openAtLogin = app.getLoginItemSettings().openAtLogin
if (this.getUserConfig('open-at-login') !== openAtLogin) {
this.setUserConfig('open-at-login', openAtLogin)
}
}
getSystemConfig (key, defaultValue) {
@@ -82,6 +157,10 @@ export default class ConfigManager {
return this.userConfig.get(key, defaultValue)
}
getLocale () {
return this.getUserConfig('locale') || app.getLocale()
}
setSystemConfig (...args) {
this.systemConfig.set(...args)
}
+36 -9
View File
@@ -6,6 +6,7 @@ import { existsSync } from 'fs'
import { resolve, join } from 'path'
import forever from 'forever-monitor'
import logger from './Logger'
import { getI18n } from '@/ui/Locale'
import {
getEngineBin,
getSessionPath,
@@ -16,6 +17,9 @@ export default class Engine {
static instance = null
constructor (options = {}) {
this.options = options
this.i18n = getI18n()
this.systemConfig = options.systemConfig
this.userConfig = options.userConfig
}
@@ -30,14 +34,14 @@ export default class Engine {
const binName = getEngineBin(platform)
if (!binName) {
throw new Error('引擎已损坏,请重新安装: (')
throw new Error(this.i18n.t('app.engine-damaged-message'))
}
let binPath = join(basePath, `/engine/${binName}`)
const binIsExist = existsSync(binPath)
if (!binIsExist) {
logger.error('[Motrix] engine bin is not exist===>', binPath)
throw new Error('引擎文件缺失,请重新安装: (')
throw new Error(this.i18n.t('app.engine-missing-message'))
}
let confPath = join(basePath, '/engine/aria2.conf')
@@ -60,7 +64,7 @@ export default class Engine {
const sh = this.getStartSh()
logger.info('[Motrix] Engine start sh===>', sh)
this.instance = forever.start(sh, {
max: 3,
max: 10,
parser: function (command, args) {
return {
command: command,
@@ -73,13 +77,37 @@ export default class Engine {
const { child } = this.instance
logger.info('[Motrix] Engine pid===>', child.pid)
this.instance.on('exit:code', function (code) {
logger.info(`[Motrix] Engine has exited after 3 restarts===> ${code}`)
this.instance.on('error', (err) => {
logger.info(`[Motrix] Engine error===> ${err}`)
})
this.instance.on('error', (err) => {
logger.info(`[Motrix] Engine has exited after 3 restarts===> ${err}`)
this.instance.on('start', function (process, data) {
logger.info(`[Motrix] Engine started===>`)
})
this.instance.on('stop', function (process) {
logger.info(`[Motrix] Engine stopped===>`)
})
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}`)
// })
}
isRunning (pid) {
try {
return process.kill(pid, 0)
} catch (e) {
return e.code === 'EPERM'
}
}
stop () {
@@ -87,7 +115,6 @@ export default class Engine {
try {
logger.info('[Motrix] Engine stopping===>')
this.instance.stop()
logger.info('[Motrix] Engine stopped===>', pid)
} catch (err) {
logger.error('[Motrix] Engine stop fail===>', err.message)
this.forceStop(pid)
@@ -97,7 +124,7 @@ export default class Engine {
forceStop (pid) {
try {
if (pid) {
if (pid && this.isRunning(pid)) {
process.kill(pid)
}
} catch (err) {
+1 -1
View File
@@ -26,7 +26,7 @@ export default class ExceptionHandler {
logger.error(stack)
if (showDialog && app.isReady()) {
dialog.showErrorBox('系统错误', message)
dialog.showErrorBox('Error: ', message)
}
})
}
+2 -1
View File
@@ -1,7 +1,8 @@
import is from 'electron-is'
import logger from 'electron-log'
logger.transports.file.level = is.production() ? 'warn' : 'info'
logger.transports.file.level = is.production() ? 'warn' : 'silly'
logger.info('Logger init')
logger.warn('[Motrix] Logger init')
export default logger
+28 -2
View File
@@ -13,17 +13,43 @@ export default class ProtocolManager extends EventEmitter {
}
init () {
// package.json:build.mac.protocols[].schemes[]
// package.json:build.protocols[].schemes[]
if (!app.isDefaultProtocolClient('mo')) {
app.setAsDefaultProtocolClient('mo')
}
if (!app.isDefaultProtocolClient('motrix')) {
app.setAsDefaultProtocolClient('motrix')
}
if (!app.isDefaultProtocolClient('magnet')) {
app.setAsDefaultProtocolClient('magnet')
}
}
handle (url) {
logger.info(`[Motrix] protocol url: ${url}`)
if (url.toLowerCase().startsWith('magnet:')) {
return this.handleMagnetProtocol(url)
}
if (
url.toLowerCase().startsWith('mo:') ||
url.toLowerCase().startsWith('motrix:')
) {
return this.handleMoProtocol(url)
}
}
handleMagnetProtocol (url) {
if (!url) {
return
}
logger.error(`[Motrix] handleMagnetProtocol url: ${url}`)
global.application.sendCommandToAll('application:new-task', 'uri', url)
}
handleMoProtocol (url) {
const parsed = new URL(url)
const { host } = parsed
logger.info('[Motrix] protocol parsed:', parsed, host)
@@ -37,6 +63,6 @@ export default class ProtocolManager extends EventEmitter {
// 如果按顺序传递,那 url 的 query string 就要求有序的了
// const query = queryString.parse(parsed.query)
const args = []
global.application.sendCommand(command, ...args)
global.application.sendCommandToAll(command, ...args)
}
}
+31 -12
View File
@@ -4,6 +4,7 @@ import is from 'electron-is'
import { autoUpdater } from 'electron-updater'
import { resolve } from 'path'
import logger from './Logger'
import { getI18n } from '@/ui/Locale'
if (is.dev()) {
autoUpdater.updateConfigPath = resolve(__dirname, '../../../app-update.yml')
@@ -13,10 +14,15 @@ export default class UpdateManager extends EventEmitter {
constructor (options = {}) {
super()
this.options = options
this.i18n = getI18n()
this.updater = autoUpdater
this.updater.autoDownload = false
this.updater.logger = logger
this.autoCheckData = {
checkEnable: this.options.autoCheck,
userCheck: false
}
this.init()
}
@@ -34,9 +40,17 @@ export default class UpdateManager extends EventEmitter {
this.updater.on('download-progress', this.updateDownloadProgress.bind(this))
this.updater.on('update-downloaded', this.updateDownloaded.bind(this))
this.updater.on('error', this.updateError.bind(this))
if (this.autoCheckData.checkEnable) {
this.autoCheckData.userCheck = false
this.options.setCheckTime.setUserConfig('last-check-update-time', Date.now())
this.updater.checkForUpdates()
}
}
check () {
this.options.setCheckTime.setUserConfig('last-check-update-time', Date.now())
this.autoCheckData.userCheck = true
this.updater.checkForUpdates()
}
@@ -48,9 +62,9 @@ export default class UpdateManager extends EventEmitter {
this.emit('update-available', info)
dialog.showMessageBox({
type: 'info',
title: '发现新版本',
message: '发现新版本,是否现在更新?',
buttons: ['是', '否'],
title: this.i18n.t('app.check-for-updates-title'),
message: this.i18n.t('app.update-available-message'),
buttons: [this.i18n.t('app.yes'), this.i18n.t('app.no')],
cancelId: 1
}, (buttonIndex) => {
if (buttonIndex === 0) {
@@ -61,10 +75,12 @@ export default class UpdateManager extends EventEmitter {
updateNotAvailable (event, info) {
this.emit('update-not-available', info)
dialog.showMessageBox({
title: '没有更新的版本',
message: '您目前使用的已是最新版本'
})
if (this.autoCheckData.userCheck) {
dialog.showMessageBox({
title: this.i18n.t('app.check-for-updates-title'),
message: this.i18n.t('app.update-not-available-message')
})
}
}
/**
@@ -84,8 +100,8 @@ export default class UpdateManager extends EventEmitter {
this.emit('update-downloaded', info)
this.updater.logger.log(`Update Downloaded: ${info}`)
dialog.showMessageBox({
title: '安装更新',
message: '更新下载完成,应用程序将退出并开始更新...'
title: this.i18n.t('app.check-for-updates-title'),
message: this.i18n.t('app.update-downloaded-message')
}, () => {
this.emit('will-updated')
setImmediate(() => {
@@ -96,8 +112,11 @@ export default class UpdateManager extends EventEmitter {
updateError (event, error) {
this.emit('update-error', error)
const msg = error == null ? '未知错误' : (error.stack || error).toString()
this.updater.logger.warn(`Update Error: ${msg}`)
dialog.showErrorBox('错误: ', msg)
const msg = (error == null)
? this.i18n.t('update-error-message')
: (error.stack || error).toString()
this.updater.logger.warn(`[Motrix] update-error: ${msg}`)
dialog.showErrorBox(msg)
}
}
+3 -63
View File
@@ -1,8 +1,7 @@
import { app } from 'electron'
import is from 'electron-is'
import logger from './core/Logger'
import Application from './Application'
import Launcher from './Launcher'
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
@@ -12,67 +11,8 @@ if (process.env.NODE_ENV !== 'development') {
* Fix Windows notification func
* appId defined in .electron-vue/webpack.main.config.js
*/
if (process.platform === 'win32') {
if (is.windows()) {
app.setAppUserModelId(appId)
}
function _init () {
let openURL = null
if (!is.mas()) {
app.on('open-url', (event, url) => {
logger.info(`You arrived from: ${url}`)
event.preventDefault()
openURL = url
if (global.application) {
global.application.handleProtocol(openURL)
}
})
}
app.on('ready', () => {
global.application = new Application()
global.application.start('index')
if (openURL) {
global.application.handleProtocol(openURL)
}
})
app.on('will-quit', () => {
logger.warn('will-quit')
global.application && global.application.stop()
})
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it's common NOT to close app even if all windows are closed
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
global.application.showPage('index')
})
}
function init () {
// Mac App Store Sandboxed App Not support requestSingleInstanceLock
if (is.mas()) {
_init()
} else {
const gotSingleLock = app.requestSingleInstanceLock()
if (!gotSingleLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
global.application.showPage('index')
})
_init()
}
}
}
init()
global.launcher = new Launcher()
View File
+76
View File
@@ -0,0 +1,76 @@
{
"menu": [
{
"id": "menu.app",
"submenu": [
{ "id": "app.about", "command": "application:about", "command-before": "application:show,index" },
{ "type": "separator" },
{ "id": "app.preferences", "command": "application:preferences" },
{ "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "id": "app.hide", "role": "hide" },
{ "id": "app.hide-others", "role": "hideothers" },
{ "id": "app.unhide", "role": "unhide" },
{ "type": "separator" },
{ "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" },
{ "type": "separator" },
{ "id": "task.pause-task", "command": "application:pause-task" },
{ "id": "task.resume-task", "command": "application:resume-task" },
{ "id": "task.delete-task", "command": "application:delete-task" },
{ "id": "task.move-task-up", "command": "application:move-task-up" },
{ "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"id": "menu.edit",
"submenu": [
{ "id": "edit.undo", "role": "undo" },
{ "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "id": "edit.cut", "role": "cut" },
{ "id": "edit.copy", "role": "copy" },
{ "id": "edit.paste", "role": "paste" },
{ "id": "edit.delete", "role": "delete" },
{ "id": "edit.select-all", "role": "selectall" }
]
},
{
"role": "window",
"id": "menu.window",
"submenu": [
{ "id": "window.reload", "role": "reload" },
{ "id": "window.close", "role": "close" },
{ "id": "window.minimize", "role": "minimize" },
{ "id": "window.zoom", "role": "zoom" },
{ "id": "window.toggle-fullscreen", "role": "togglefullscreen" },
{ "type": "separator" },
{ "id": "window.front", "role": "front" }
]
},
{
"role": "help",
"id": "menu.help",
"submenu": [
{ "id": "help.official-website", "command": "help:official-website" },
{ "id": "help.manual", "command": "help:manual" },
{ "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
-81
View File
@@ -1,81 +0,0 @@
{
"menu": [
{
"label": "Motrix",
"id": "app",
"submenu": [
{ "label": "About Motrix", "id": "app.about", "command": "application:about", "command-before": "application:show,index"},
{ "type": "separator" },
{ "label": "Preferences...", "id": "app.preferences", "command": "application:preferences" },
{ "label": "Check for Updates...", "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "label": "Services", "role": "services", "submenu": [] },
{ "label": "Hide Motrix", "role": "hide" },
{ "label": "Hide Others", "role": "hideothers" },
{ "label": "Show All", "role": "unhide" },
{ "type": "separator" },
{ "label": "Quit Motrix", "id": "app.quit", "role": "quit" }
]
},
{
"label": "Task",
"id": "task",
"submenu": [
{ "label": "New Task", "id": "task.new-task", "command": "application:new-task", "command-arg": "uri", "command-after": "application:show,index"},
{ "label": "New BT Task", "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index"},
{ "type": "separator" },
{ "label": "Pause Task", "id": "task.pause-task", "command": "application:pause-task" },
{ "label": "Resume Task", "id": "task.resume-task", "command": "application:resume-task" },
{ "label": "Delete Task", "id": "task.delete-task", "command": "application:delete-task" },
{ "label": "Move Task Up", "id": "task.move-task-up", "command": "application:move-task-up" },
{ "label": "Move Task Down", "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "label": "Pause All Task", "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "label": "Resume All Task", "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "label": "Clear Recent Tasks", "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"label": "Edit",
"id": "edit",
"submenu": [
{ "label": "Undo", "id": "edit.undo", "role": "undo" },
{ "label": "Redo", "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "label": "Cut", "id": "edit.cut", "role": "cut" },
{ "label": "Copy", "id": "edit.copy", "role": "copy" },
{ "label": "Paste", "id": "edit.paste", "role": "paste" },
{ "label": "Delete", "id": "edit.delete", "role": "delete" },
{ "label": "Select All", "id": "edit.select-all", "role": "selectall" }
]
},
{
"label": "Window",
"role": "window",
"id": "window",
"submenu": [
{ "label": "Reload", "id": "view.reload", "role": "reload" },
{ "label": "Close", "role": "close" },
{ "label": "Minimize", "role": "minimize" },
{ "label": "Zoom", "role": "zoom" },
{ "label": "Enter Full Screen", "role": "togglefullscreen" },
{ "type": "separator" },
{ "label": "Bring All to Front", "role": "front" }
]
},
{
"label": "Help",
"role": "help",
"id": "help",
"submenu": [
{ "label": "Motrix Website", "id": "help.official-website", "command": "help:official-website" },
{ "label": "Manual", "id": "help.manual", "command": "help:manual" },
{ "label": "Release Notes...", "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "label": "Report Problem", "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "label": "Toggle Developer Tools", "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
-81
View File
@@ -1,81 +0,0 @@
{
"menu": [
{
"label": "Motrix",
"id": "app",
"submenu": [
{ "label": "About Motrix", "id": "app.about", "command": "application:about" },
{ "type": "separator" },
{ "label": "Preferences...", "id": "app.preferences", "command": "application:preferences" },
{ "label": "Check for Updates...", "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "label": "Services", "role": "services", "submenu": [] },
{ "label": "Hide Motrix", "role": "hide" },
{ "label": "Hide Others", "role": "hideothers" },
{ "label": "Show All", "role": "unhide" },
{ "type": "separator" },
{ "label": "Quit Motrix", "id": "app.quit", "role": "quit" }
]
},
{
"label": "Task",
"id": "task",
"submenu": [
{ "label": "New Task", "id": "task.new-task", "command": "application:new-task", "command-arg": "uri" },
{ "label": "New BT Task", "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent" },
{ "type": "separator" },
{ "label": "Pause Task", "id": "task.pause-task", "command": "application:pause-task" },
{ "label": "Resume Task", "id": "task.resume-task", "command": "application:resume-task" },
{ "label": "Delete Task", "id": "task.delete-task", "command": "application:delete-task" },
{ "label": "Move Task Up", "id": "task.move-task-up", "command": "application:move-task-up" },
{ "label": "Move Task Down", "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "label": "Pause All Task", "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "label": "Resume All Task", "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "label": "Clear Recent Tasks", "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"label": "Edit",
"id": "edit",
"submenu": [
{ "label": "Undo", "id": "edit.undo", "role": "undo" },
{ "label": "Redo", "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "label": "Cut", "id": "edit.cut", "role": "cut" },
{ "label": "Copy", "id": "edit.copy", "role": "copy" },
{ "label": "Paste", "id": "edit.paste", "role": "paste" },
{ "label": "Delete", "id": "edit.delete", "role": "delete" },
{ "label": "Select All", "id": "edit.select-all", "role": "selectall" }
]
},
{
"label": "Window",
"role": "window",
"id": "window",
"submenu": [
{ "label": "Reload", "id": "view.reload", "role": "reload" },
{ "label": "Close", "role": "close" },
{ "label": "Minimize", "role": "minimize" },
{ "label": "Zoom", "role": "zoom" },
{ "label": "Enter Full Screen", "role": "togglefullscreen" },
{ "type": "separator" },
{ "label": "Bring All to Front", "role": "front" }
]
},
{
"label": "Help",
"role": "help",
"id": "help",
"submenu": [
{ "label": "Motrix Website", "id": "help.official-website", "command": "help:official-website" },
{ "label": "Manual", "id": "help.manual", "command": "help:manual" },
{ "label": "Release Notes...", "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "label": "Report Problem", "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "label": "Toggle Developer Tools", "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
-81
View File
@@ -1,81 +0,0 @@
{
"menu": [
{
"label": "Motrix",
"id": "app",
"submenu": [
{ "label": "About Motrix", "id": "app.about", "command": "application:about", "command-before": "application:show,index"},
{ "type": "separator" },
{ "label": "Preferences...", "id": "app.preferences", "command": "application:preferences" },
{ "label": "Check for Updates...", "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "label": "Services", "role": "services", "submenu": [] },
{ "label": "Hide Motrix", "role": "hide" },
{ "label": "Hide Others", "role": "hideothers" },
{ "label": "Show All", "role": "unhide" },
{ "type": "separator" },
{ "label": "Quit Motrix", "id": "app.quit", "role": "quit" }
]
},
{
"label": "Task",
"id": "task",
"submenu": [
{ "label": "New Task", "id": "task.new-task", "command": "application:new-task", "command-arg": "uri", "command-after": "application:show,index"},
{ "label": "New BT Task", "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index"},
{ "type": "separator" },
{ "label": "Pause Task", "id": "task.pause-task", "command": "application:pause-task" },
{ "label": "Resume Task", "id": "task.resume-task", "command": "application:resume-task" },
{ "label": "Delete Task", "id": "task.delete-task", "command": "application:delete-task" },
{ "label": "Move Task Up", "id": "task.move-task-up", "command": "application:move-task-up" },
{ "label": "Move Task Down", "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "label": "Pause All Task", "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "label": "Resume All Task", "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "label": "Clear Recent Tasks", "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"label": "Edit",
"id": "edit",
"submenu": [
{ "label": "Undo", "id": "edit.undo", "role": "undo" },
{ "label": "Redo", "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "label": "Cut", "id": "edit.cut", "role": "cut" },
{ "label": "Copy", "id": "edit.copy", "role": "copy" },
{ "label": "Paste", "id": "edit.paste", "role": "paste" },
{ "label": "Delete", "id": "edit.delete", "role": "delete" },
{ "label": "Select All", "id": "edit.select-all", "role": "selectall" }
]
},
{
"label": "Window",
"role": "window",
"id": "window",
"submenu": [
{ "label": "Reload", "id": "view.reload", "role": "reload" },
{ "label": "Close", "role": "close" },
{ "label": "Minimize", "role": "minimize" },
{ "label": "Zoom", "role": "zoom" },
{ "label": "Enter Full Screen", "role": "togglefullscreen" },
{ "type": "separator" },
{ "label": "Bring All to Front", "role": "front" }
]
},
{
"label": "Help",
"role": "help",
"id": "help",
"submenu": [
{ "label": "Motrix Website", "id": "help.official-website", "command": "help:official-website" },
{ "label": "Manual", "id": "help.manual", "command": "help:manual" },
{ "label": "Release Notes...", "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "label": "Report Problem", "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "label": "Toggle Developer Tools", "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
+72
View File
@@ -0,0 +1,72 @@
{
"menu": [
{
"id": "menu.file",
"submenu": [
{ "id": "app.about", "command": "application:about", "command-before": "application:show,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" },
{ "type": "separator" },
{ "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" },
{ "type": "separator" },
{ "id": "task.pause-task", "command": "application:pause-task" },
{ "id": "task.resume-task", "command": "application:resume-task" },
{ "id": "task.delete-task", "command": "application:delete-task" },
{ "id": "task.move-task-up", "command": "application:move-task-up" },
{ "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "id": "task.resume-all-task", "command": "application:resume-all-task" }
]
},
{
"id": "menu.edit",
"submenu": [
{ "id": "edit.undo", "role": "undo" },
{ "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "id": "edit.cut", "role": "cut" },
{ "id": "edit.copy", "role": "copy" },
{ "id": "edit.paste", "role": "paste" },
{ "id": "edit.delete", "role": "delete" },
{ "id": "edit.select-all", "role": "selectall" }
]
},
{
"role": "window",
"id": "menu.window",
"submenu": [
{ "id": "window.reload", "role": "reload" },
{ "id": "window.close", "role": "close" },
{ "id": "window.minimize", "role": "minimize" },
{ "id": "window.zoom", "role": "zoom" },
{ "id": "window.toggle-fullscreen", "role": "togglefullscreen" },
{ "type": "separator" },
{ "id": "window.front", "role": "front" }
]
},
{
"role": "help",
"id": "menu.help",
"submenu": [
{ "id": "help.official-website", "command": "help:official-website" },
{ "id": "help.manual", "command": "help:manual" },
{ "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
+11
View File
@@ -0,0 +1,11 @@
[
{ "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" },
{ "type": "separator" },
{ "id": "app.show", "command": "application:show", "command-arg": "index" },
{ "id": "help.manual", "command": "help:manual" },
{ "type": "separator" },
{ "id": "app.preferences", "command": "application:preferences", "command-before": "application:show,index" },
{ "id": "app.quit", "role": "quit" }
]
+74
View File
@@ -0,0 +1,74 @@
{
"menu": [
{
"id": "menu.file",
"submenu": [
{ "id": "app.about", "command": "application:about", "command-before": "application:show,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" },
{ "type": "separator" },
{ "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" },
{ "type": "separator" },
{ "id": "task.pause-task", "command": "application:pause-task" },
{ "id": "task.resume-task", "command": "application:resume-task" },
{ "id": "task.delete-task", "command": "application:delete-task" },
{ "id": "task.move-task-up", "command": "application:move-task-up" },
{ "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"id": "menu.edit",
"submenu": [
{ "id": "edit.undo", "role": "undo" },
{ "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "id": "edit.cut", "role": "cut" },
{ "id": "edit.copy", "role": "copy" },
{ "id": "edit.paste", "role": "paste" },
{ "id": "edit.delete", "role": "delete" },
{ "id": "edit.select-all", "role": "selectall" }
]
},
{
"role": "window",
"id": "menu.window",
"submenu": [
{ "id": "window.reload", "role": "reload" },
{ "id": "window.close", "role": "close" },
{ "id": "window.minimize", "role": "minimize" },
{ "id": "window.zoom", "role": "zoom" },
{ "id": "window.toggle-fullscreen", "role": "togglefullscreen" },
{ "type": "separator" },
{ "id": "window.front", "role": "front" }
]
},
{
"role": "help",
"id": "menu.help",
"submenu": [
{ "id": "help.official-website", "command": "help:official-website" },
{ "id": "help.manual", "command": "help:manual" },
{ "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
-81
View File
@@ -1,81 +0,0 @@
{
"menu": [
{
"label": "Motrix",
"id": "app",
"submenu": [
{ "label": "关于 Motrix", "id": "app.about", "command": "application:about", "command-before": "application:show,index"},
{ "type": "separator" },
{ "label": "偏好设置...", "id": "app.preferences", "command": "application:preferences" },
{ "label": "检查更新...", "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "label": "服务", "role": "services", "submenu": [] },
{ "label": "隐藏 Motrix", "role": "hide" },
{ "label": "隐藏其他", "role": "hideothers" },
{ "label": "显示全部", "role": "unhide" },
{ "type": "separator" },
{ "label": "退出 Motrix", "id": "app.quit", "role": "quit" }
]
},
{
"label": "任务",
"id": "task",
"submenu": [
{ "label": "新建任务", "id": "task.new-task", "command": "application:new-task", "command-after": "application:show,index"},
{ "label": "新建 BT 任务", "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index"},
{ "type": "separator" },
{ "label": "暂停任务", "id": "task.pause-task", "command": "application:pause-task" },
{ "label": "恢复任务", "id": "task.resume-task", "command": "application:resume-task" },
{ "label": "删除任务", "id": "task.delete-task", "command": "application:delete-task" },
{ "label": "上移任务", "id": "task.move-task-up", "command": "application:move-task-up" },
{ "label": "下移任务", "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "label": "暂停所有任务", "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "label": "恢复所有任务", "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "label": "清除最近的下载记录", "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"label": "编辑",
"id": "edit",
"submenu": [
{ "label": "撤销", "id": "edit.undo", "role": "undo" },
{ "label": "重做", "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "label": "剪切", "id": "edit.cut", "role": "cut" },
{ "label": "复制", "id": "edit.copy", "role": "copy" },
{ "label": "黏贴", "id": "edit.paste", "role": "paste" },
{ "label": "删除", "id": "edit.delete", "role": "delete" },
{ "label": "全选", "id": "edit.select-all", "role": "selectall" }
]
},
{
"label": "窗口",
"role": "window",
"id": "window",
"submenu": [
{ "label": "重新加载", "id": "view.reload", "role": "reload" },
{ "label": "关闭", "role": "close" },
{ "label": "最小化", "role": "minimize" },
{ "label": "放大", "role": "zoom" },
{ "label": "进入全屏幕", "role": "togglefullscreen" },
{ "type": "separator" },
{ "label": "前置全部窗口", "role": "front" }
]
},
{
"label": "帮助",
"role": "help",
"id": "help",
"submenu": [
{ "label": "Motrix 官网", "id": "help.official-website", "command": "help:official-website" },
{ "label": "使用手册", "id": "help.manual", "command": "help:manual" },
{ "label": "发行说明...", "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "label": "报告问题", "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "label": "开发者工具", "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
-81
View File
@@ -1,81 +0,0 @@
{
"menu": [
{
"label": "Motrix",
"id": "app",
"submenu": [
{ "label": "关于 Motrix", "id": "app.about", "command": "application:about" },
{ "type": "separator" },
{ "label": "偏好设置...", "id": "app.preferences", "command": "application:preferences" },
{ "label": "检查更新...", "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "label": "服务", "role": "services", "submenu": [] },
{ "label": "隐藏 Motrix", "role": "hide" },
{ "label": "隐藏其他", "role": "hideothers" },
{ "label": "显示全部", "role": "unhide" },
{ "type": "separator" },
{ "label": "退出 Motrix", "id": "app.quit", "role": "quit" }
]
},
{
"label": "任务",
"id": "task",
"submenu": [
{ "label": "新建任务", "id": "task.new-task", "command": "application:new-task" },
{ "label": "新建 BT 任务", "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent" },
{ "type": "separator" },
{ "label": "暂停任务", "id": "task.pause-task", "command": "application:pause-task" },
{ "label": "恢复任务", "id": "task.resume-task", "command": "application:resume-task" },
{ "label": "删除任务", "id": "task.delete-task", "command": "application:delete-task" },
{ "label": "上移任务", "id": "task.move-task-up", "command": "application:move-task-up" },
{ "label": "下移任务", "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "label": "暂停所有任务", "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "label": "恢复所有任务", "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "label": "清除最近的下载记录", "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"label": "编辑",
"id": "edit",
"submenu": [
{ "label": "撤销", "id": "edit.undo", "role": "undo" },
{ "label": "重做", "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "label": "剪切", "id": "edit.cut", "role": "cut" },
{ "label": "复制", "id": "edit.copy", "role": "copy" },
{ "label": "黏贴", "id": "edit.paste", "role": "paste" },
{ "label": "删除", "id": "edit.delete", "role": "delete" },
{ "label": "全选", "id": "edit.select-all", "role": "selectall" }
]
},
{
"label": "窗口",
"role": "window",
"id": "window",
"submenu": [
{ "label": "重新加载", "id": "view.reload", "role": "reload" },
{ "label": "关闭", "role": "close" },
{ "label": "最小化", "role": "minimize" },
{ "label": "放大", "role": "zoom" },
{ "label": "进入全屏幕", "role": "togglefullscreen" },
{ "type": "separator" },
{ "label": "前置全部窗口", "role": "front" }
]
},
{
"label": "帮助",
"role": "help",
"id": "help",
"submenu": [
{ "label": "Motrix 官网", "id": "help.official-website", "command": "help:official-website" },
{ "label": "使用手册", "id": "help.manual", "command": "help:manual" },
{ "label": "发行说明...", "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "label": "报告问题", "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "label": "开发者工具", "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
-81
View File
@@ -1,81 +0,0 @@
{
"menu": [
{
"label": "Motrix",
"id": "app",
"submenu": [
{ "label": "关于 Motrix", "id": "app.about", "command": "application:about", "command-before": "application:show,index"},
{ "type": "separator" },
{ "label": "偏好设置...", "id": "app.preferences", "command": "application:preferences" },
{ "label": "检查更新...", "id": "app.check-for-updates", "command": "application:check-for-updates" },
{ "label": "服务", "role": "services", "submenu": [] },
{ "label": "隐藏 Motrix", "role": "hide" },
{ "label": "隐藏其他", "role": "hideothers" },
{ "label": "显示全部", "role": "unhide" },
{ "type": "separator" },
{ "label": "退出 Motrix", "id": "app.quit", "role": "quit" }
]
},
{
"label": "任务",
"id": "task",
"submenu": [
{ "label": "新建任务", "id": "task.new-task", "command": "application:new-task", "command-after": "application:show,index"},
{ "label": "新建 BT 任务", "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index"},
{ "type": "separator" },
{ "label": "暂停任务", "id": "task.pause-task", "command": "application:pause-task" },
{ "label": "恢复任务", "id": "task.resume-task", "command": "application:resume-task" },
{ "label": "删除任务", "id": "task.delete-task", "command": "application:delete-task" },
{ "label": "上移任务", "id": "task.move-task-up", "command": "application:move-task-up" },
{ "label": "下移任务", "id": "task.move-task-down", "command": "application:move-task-down" },
{ "type": "separator" },
{ "label": "暂停所有任务", "id": "task.pause-all-task", "command": "application:pause-all-task" },
{ "label": "恢复所有任务", "id": "task.resume-all-task", "command": "application:resume-all-task" },
{ "type": "separator" },
{ "label": "清除最近的下载记录", "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" }
]
},
{
"label": "编辑",
"id": "edit",
"submenu": [
{ "label": "撤销", "id": "edit.undo", "role": "undo" },
{ "label": "重做", "id": "edit.redo", "role": "redo" },
{ "type": "separator" },
{ "label": "剪切", "id": "edit.cut", "role": "cut" },
{ "label": "复制", "id": "edit.copy", "role": "copy" },
{ "label": "黏贴", "id": "edit.paste", "role": "paste" },
{ "label": "删除", "id": "edit.delete", "role": "delete" },
{ "label": "全选", "id": "edit.select-all", "role": "selectall" }
]
},
{
"label": "窗口",
"role": "window",
"id": "window",
"submenu": [
{ "label": "重新加载", "id": "view.reload", "role": "reload" },
{ "label": "关闭", "role": "close" },
{ "label": "最小化", "role": "minimize" },
{ "label": "放大", "role": "zoom" },
{ "label": "进入全屏幕", "role": "togglefullscreen" },
{ "type": "separator" },
{ "label": "前置全部窗口", "role": "front" }
]
},
{
"label": "帮助",
"role": "help",
"id": "help",
"submenu": [
{ "label": "Motrix 官网", "id": "help.official-website", "command": "help:official-website" },
{ "label": "使用手册", "id": "help.manual", "command": "help:manual" },
{ "label": "发行说明...", "id": "help.release-notes", "command": "help:release-notes" },
{ "type": "separator" },
{ "label": "报告问题", "id": "help.report-problem", "command": "help:report-problem" },
{ "type": "separator" },
{ "label": "开发者工具", "id": "help.toggle-dev-tools", "role": "toggledevtools" }
]
}
]
}
+24
View File
@@ -0,0 +1,24 @@
import resources from '@shared/locales/app'
import LocaleManager from '@shared/locales/LocaleManager'
const localeManager = new LocaleManager({
resources
})
export function getLocaleManager () {
return localeManager
}
export function setupLocaleManager (locale) {
localeManager.changeLanguageByLocale(locale)
return localeManager
}
export function getI18n () {
return localeManager.getI18n()
}
export function getI18nTranslator () {
return localeManager.getI18n().t
}
+21 -20
View File
@@ -6,29 +6,24 @@ import {
updateStates
} from '../utils/menu'
import keymap from '@shared/keymap'
import { getI18n } from '@/ui/Locale'
export default class MenuManager extends EventEmitter {
constructor (options) {
super()
this.options = options
this.i18n = getI18n()
this.keymap = keymap
this.template = []
this.menu = null
this.items = {}
this.load()
this.setup()
}
load (locale = 'en-US') {
let template = null
try {
template = require(`../menus/${locale}/${process.platform}.json`)
if (!template) {
template = require(`../menus/en-US/${process.platform}.json`)
}
} catch (err) {
template = require(`../menus/en-US/${process.platform}.json`)
}
load () {
let template = require(`../menus/${process.platform}.json`)
this.template = template['menu']
}
@@ -38,15 +33,21 @@ export default class MenuManager extends EventEmitter {
keystrokesByCommand[this.keymap[item]] = item
}
const tpl = translateTemplate(this.template, keystrokesByCommand)
this.menu = Menu.buildFromTemplate(tpl)
// Deepclone the menu template to refresh menu
const template = JSON.parse(JSON.stringify(this.template))
const tpl = translateTemplate(template, keystrokesByCommand, this.i18n)
const menu = Menu.buildFromTemplate(tpl)
return menu
}
setup (locale) {
this.load(locale)
this.build()
Menu.setApplicationMenu(this.menu)
this.items = flattenMenuItems(this.menu)
setup () {
const menu = this.build()
Menu.setApplicationMenu(menu)
this.items = flattenMenuItems(menu)
}
rebuild () {
this.setup()
}
updateStates (visibleStates, enabledStates, checkedStates) {
+46
View File
@@ -0,0 +1,46 @@
import { EventEmitter } from 'events'
import { systemPreferences } from 'electron'
import is from 'electron-is'
import { LIGHT_THEME, DARK_THEME } from '@shared/constants'
export default class ThemeManager extends EventEmitter {
constructor (options = {}) {
super()
this.init()
}
init () {
this.handleEvents()
}
getSystemTheme () {
let result = LIGHT_THEME
if (!is.macOS()) {
return result
}
result = systemPreferences.isDarkMode() ? DARK_THEME : LIGHT_THEME
return result
}
handleEvents () {
if (!is.macOS()) {
return
}
systemPreferences.subscribeNotification(
'AppleInterfaceThemeChangedNotification',
() => {
const theme = this.getSystemTheme()
this.updateAppAppearance(theme)
this.emit('system-theme-changed', theme)
}
)
}
updateAppAppearance (theme) {
if (!is.macOS() || theme !== LIGHT_THEME || theme !== DARK_THEME) {
return
}
systemPreferences.setAppLevelAppearance(theme)
}
}
+117 -1
View File
@@ -1,3 +1,119 @@
export default class TrayManager {
import { EventEmitter } from 'events'
import { join } from 'path'
import { Tray, Menu, systemPreferences } from 'electron'
import is from 'electron-is'
import { translateTemplate } from '../utils/menu'
import { getI18n } from '@/ui/Locale'
import { LIGHT_THEME, DARK_THEME } from '@shared/constants'
let tray = null
export default class TrayManager extends EventEmitter {
constructor (options = {}) {
super()
this.i18n = getI18n()
this.menu = null
this.load()
this.init()
this.setup()
this.handleEvents()
}
load () {
this.template = require(`../menus/tray.json`)
const theme = systemPreferences.isDarkMode() ? DARK_THEME : LIGHT_THEME
if (is.macOS()) {
this.normalIcon = join(__static, `./mo-tray-${theme}-normal.png`)
this.activeIcon = join(__static, `./mo-tray-${theme}-active.png`)
} else {
this.normalIcon = join(__static, './mo-tray-colorful-normal.png')
this.activeIcon = join(__static, './mo-tray-colorful-active.png')
}
}
build () {
const keystrokesByCommand = {}
for (let item in this.keymap) {
keystrokesByCommand[this.keymap[item]] = item
}
// Deepclone the menu template to refresh menu
const template = JSON.parse(JSON.stringify(this.template))
const tpl = translateTemplate(template, keystrokesByCommand, this.i18n)
this.menu = Menu.buildFromTemplate(tpl)
}
setup () {
this.build()
/**
* Linux requires setContextMenu to be called
* in order for the context menu to populate correctly
*/
if (process.platform === 'linux') {
tray.setContextMenu(this.menu)
}
}
init () {
tray = new Tray(this.normalIcon)
tray.setToolTip('Motrix')
}
handleEvents () {
tray.on('click', this.handleTrayClick)
tray.on('double-click', this.handleTrayDbClick)
tray.on('right-click', this.handleTrayRightClick)
tray.on('drop-files', this.handleTrayDropFile)
}
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])
}
updateStatus (status) {
this.status = status
this.updateIcon()
}
updateIcon () {
const icon = this.status ? this.activeIcon : this.normalIcon
tray.setImage(icon)
}
changeIconTheme (theme = LIGHT_THEME) {
if (!is.macOS()) {
return
}
this.normalIcon = join(__static, `./mo-tray-${theme}-normal.png`)
this.activeIcon = join(__static, `./mo-tray-${theme}-active.png`)
this.updateIcon()
}
destroy () {
tray.destroy()
}
}
+138 -21
View File
@@ -1,63 +1,113 @@
import { join } from 'path'
import { EventEmitter } from 'events'
import { app, shell, BrowserWindow } from 'electron'
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 = {
titleBarStyle: 'hiddenInset',
useContentSize: true,
show: false,
width: 1024,
height: 768
height: 768,
webPreferences: {
nodeIntegration: true
}
}
export default class WindowManager extends EventEmitter {
constructor (options = {}) {
super()
this.options = options
this.userConfig = options.userConfig || {}
this.windows = {}
this.willQuit = false
app.on('before-quit', () => {
this.setWillQuit(true)
})
this.handleBeforeQuit()
this.handleAllWindowClosed()
}
setWillQuit (flag) {
this.willQuit = flag
}
openWindow (page) {
const options = pageConfig[page] || {}
getPageOptions (page) {
const result = pageConfig[page] || {}
const hideAppMenu = this.userConfig['hide-app-menu']
if (hideAppMenu) {
result.attrs.frame = false
}
// Optimized for small screen users
const { width, height } = screen.getPrimaryDisplay().workAreaSize
const widthScale = width >= 1280 ? 1 : 0.875
const heightScale = height >= 800 ? 1 : 0.875
result.attrs.width *= widthScale
result.attrs.height *= heightScale
// fix AppImage Dock Icon Missing
// https://github.com/AppImage/AppImageKit/wiki/Bundling-Electron-apps
if (is.linux()) {
result.attrs.icon = join(__static, './512x512.png')
}
return result
}
getPageBounds (page) {
const enabled = this.userConfig['keep-window-state']
const windowStateMap = this.userConfig['window-state'] || {}
let result = null
if (enabled) {
result = windowStateMap[page]
}
return result
}
openWindow (page, options = {}) {
const pageOptions = this.getPageOptions(page)
const { hidden } = options
let window = this.windows[page] || null
if (window) {
window.restore()
window.show()
window.focus()
return window
}
window = new BrowserWindow({
...defaultBrowserOptions,
...options.attrs
...pageOptions.attrs
})
const bounds = this.getPageBounds(page)
console.log('bounds ====>', bounds)
if (bounds) {
window.setBounds(bounds)
}
window.webContents.on('new-window', (e, url) => {
e.preventDefault()
shell.openExternal(url)
})
if (options.url) {
window.loadURL(options.url)
if (pageOptions.url) {
window.loadURL(pageOptions.url)
}
window.once('ready-to-show', () => {
window.show()
if (!hidden) {
window.show()
}
})
if (options.bindCloseToHide && process.platform === 'darwin') {
this.bindCloseToHide(page, window)
}
this.handleWindowState(page, window)
this.handleWindowClose(pageOptions, page, window)
this.bindAfterClosed(page, window)
@@ -70,7 +120,11 @@ export default class WindowManager extends EventEmitter {
}
getWindows () {
return this.windows
return this.windows || {}
}
getWindowList () {
return Object.values(this.getWindows())
}
addWindow (page, window) {
@@ -80,6 +134,9 @@ export default class WindowManager extends EventEmitter {
destroyWindow (page) {
const win = this.getWindow(page)
this.removeWindow(page)
win.removeListener('closed')
win.removeListener('move')
win.removeListener('resize')
win.destroy()
}
@@ -93,24 +150,84 @@ export default class WindowManager extends EventEmitter {
})
}
bindCloseToHide (page, window) {
handleWindowState (page, window) {
window.on('resize', debounce(() => {
const bounds = window.getBounds()
this.emit('window-resized', { page, bounds })
}, 500))
window.on('move', debounce(() => {
const bounds = window.getBounds()
this.emit('window-moved', { page, bounds })
}, 500))
}
handleWindowClose (pageOptions, page, window) {
window.on('close', (event) => {
if (!this.willQuit) {
if (pageOptions.bindCloseToHide && !this.willQuit) {
event.preventDefault()
window.hide()
}
const bounds = window.getBounds()
this.emit('window-closed', { page, bounds })
})
}
showWindow (page) {
const window = this.getWindow(page)
if (!window) {
return
}
window.show()
}
hideWindow (page) {
const window = this.getWindow(page)
if (!window) {
return
}
window.hide()
}
hideAllWindow () {
this.getWindowList().forEach((window) => {
window.hide()
})
}
toggleWindow (page) {
const window = this.getWindow(page)
if (!window) {
return
}
if (window.isVisible()) {
window.hide()
} else {
window.show()
}
}
getFocusedWindow () {
return BrowserWindow.getFocusedWindow()
}
handleBeforeQuit () {
app.on('before-quit', () => {
this.setWillQuit(true)
})
}
handleAllWindowClosed () {
app.on('window-all-closed', (event) => {
event.preventDefault()
})
}
sendCommandTo (window, command, ...args) {
if (!window) {
return
}
console.log('sendCommandTo====>', window, command, ...args)
logger.info('[Motrix] sendCommandTo===>', window, command, ...args)
window.webContents.send('command', command, ...args)
}
+68 -2
View File
@@ -1,6 +1,7 @@
import { app } from 'electron'
import is from 'electron-is'
import { resolve } from 'path'
import { existsSync, lstatSync } from 'fs'
import logger from '../core/Logger'
import engineBinMap from '../configs/engine'
@@ -8,6 +9,11 @@ export function getLogPath () {
return logger.transports.file.file
}
export function getDhtPath (protocol) {
const name = protocol === 6 ? 'dht6.dat' : 'dht.dat'
return resolve(app.getPath('userData'), `./${name}`)
}
export function getSessionPath () {
return resolve(app.getPath('userData'), './download.session')
}
@@ -44,17 +50,77 @@ export function isRunningInDmg () {
return result
}
export function moveAppToApplicationsFolder () {
export function moveAppToApplicationsFolder (errorMsg = '') {
return new Promise((resolve, reject) => {
try {
const result = app.moveToApplicationsFolder()
if (result) {
resolve(result)
} else {
reject(new Error('应用程序移动失败'))
reject(new Error(errorMsg))
}
} catch (err) {
reject(err)
}
})
}
export function splitArgv (argv) {
const args = []
const extra = {}
for (const arg of argv) {
if (arg.startsWith('--')) {
const kv = arg.split('=')
const key = kv[0]
const value = kv[1] || '1'
extra[key] = value
continue
}
args.push(arg)
}
return { args, extra }
}
export function parseArgvAsUrl (argv) {
let arg = argv[1]
if (!arg) {
return
}
if (checkIsSupportedSchema(arg)) {
return arg
}
}
export function checkIsSupportedSchema (url = '') {
const str = url.toLowerCase()
if (
str.startsWith('mo:') ||
str.startsWith('motrix:') ||
str.startsWith('http:') ||
str.startsWith('https:') ||
str.startsWith('ftp:') ||
str.startsWith('magnet:') ||
str.startsWith('thunder:')
) {
return true
} else {
return false
}
}
export function isDirectory (path) {
return existsSync(path) && lstatSync(path).isDirectory()
}
export function parseArgvAsFile (argv) {
let arg = argv[1]
if (!arg || isDirectory(arg)) {
return
}
if (is.linux()) {
arg = arg.replace('file://', '')
}
return arg
}
+16 -6
View File
@@ -71,19 +71,29 @@ function findById (template, id) {
return null
}
export function translateTemplate (template, keystrokesByCommand) {
export function translateTemplate (template, keystrokesByCommand, i18n) {
for (let i in template) {
let item = template[i]
if (item.command) {
item.accelerator = acceleratorForCommand(item.command, keystrokesByCommand)
}
// If label is specified, label is used as the key of i18n.t(key),
// which mainly solves the inaccurate translation of item.id.
if (i18n) {
if (item.label) {
item.label = i18n.t(item.label)
} else if (item.id) {
item.label = i18n.t(item.id)
}
}
item.click = () => {
console.log('click sendCommand', item)
handleCommand(item)
}
if (item.submenu) {
translateTemplate(item.submenu, keystrokesByCommand)
translateTemplate(item.submenu, keystrokesByCommand, i18n)
}
}
return template
@@ -96,7 +106,7 @@ export function handleCommand (item) {
? [item.command, item['command-arg']]
: [item.command]
global.application.sendCommand(...args)
global.application.sendCommandToAll(...args)
handleCommandAfter(item)
}
@@ -108,7 +118,7 @@ function handleCommandBefore (item) {
}
const [ command, ...args ] = item['command-before'].split(',')
console.log('handleCommandBefore==2=>', command, ...args)
global.application.sendCommand(command, ...args)
global.application.sendCommandToAll(command, ...args)
}
function handleCommandAfter (item) {
@@ -118,7 +128,7 @@ function handleCommandAfter (item) {
}
const [ command, ...args ] = item['command-after'].split(',')
console.log('handleCommandAfter==2=>', command, ...args)
global.application.sendCommand(command, ...args)
global.application.sendCommandToAll(command, ...args)
}
function acceleratorForCommand (command, keystrokesByCommand) {
+63 -9
View File
@@ -9,6 +9,10 @@ import {
changeKeysToCamelCase,
changeKeysToKebabCase
} from '@shared/utils'
import {
BEST_TRACKERS_URL,
BEST_TRACKERS_IP_URL
} from '@shared/constants'
const application = remote.getGlobal('application')
@@ -80,9 +84,9 @@ export default class Api {
savePreference (params = {}) {
const kebabParams = changeKeysToKebabCase(params)
if (is.renderer()) {
this.savePreferenceToNativeStore(kebabParams)
return this.savePreferenceToNativeStore(kebabParams)
} else {
this.savePreferenceToLocalStorage(kebabParams)
return this.savePreferenceToLocalStorage(kebabParams)
}
}
@@ -95,6 +99,7 @@ export default class Api {
if (!isEmpty(system)) {
console.info('[Motrix] save system config: ', system)
application.configManager.setSystemConfig(system)
this.changeGlobalOption(system)
}
if (!isEmpty(user)) {
@@ -111,6 +116,36 @@ export default class Api {
return this.client.call('getVersion')
}
changeGlobalOption (options) {
const args = {}
Object.keys(options).forEach((key) => {
args[key] = `${options[key]}`
})
return this.client.call('changeGlobalOption', args)
}
getGlobalOption () {
return new Promise((resolve) => {
this.client.call('getGlobalOption')
.then((data) => {
resolve(changeKeysToCamelCase(data))
})
})
}
getOption (params = {}) {
const { gid } = params
const args = compactUndefined([gid])
return new Promise((resolve) => {
this.client.call('getOption', ...args)
.then((data) => {
resolve(changeKeysToCamelCase(data))
})
})
}
getGlobalStat () {
return this.client.call('getGlobalStat')
}
@@ -120,8 +155,12 @@ export default class Api {
uris,
options
} = params
const args = compactUndefined([uris, options])
return this.client.call('addUri', ...args)
const kebabOptions = changeKeysToKebabCase(options)
const tasks = uris.map((uri) => {
const args = compactUndefined([[uri], kebabOptions])
return [ 'aria2.addUri', ...args ]
})
return this.client.multicall(tasks)
}
addTorrent (params) {
@@ -129,7 +168,8 @@ export default class Api {
torrent,
options
} = params
const args = compactUndefined([torrent, [], options])
const kebabOptions = changeKeysToKebabCase(options)
const args = compactUndefined([torrent, [], kebabOptions])
return this.client.call('addTorrent', ...args)
}
@@ -138,12 +178,13 @@ export default class Api {
metalink,
options
} = params
const args = compactUndefined([metalink, options])
const kebabOptions = changeKeysToKebabCase(options)
const args = compactUndefined([metalink, kebabOptions])
return this.client.call('addMetalink', ...args)
}
fetchDownloadingTaskList (params = {}) {
const { offset = 0, num = 200, keys } = params
const { offset = 0, num = 20, keys } = params
const activeArgs = compactUndefined([keys])
const waitingArgs = compactUndefined([offset, num, keys])
return new Promise((resolve, reject) => {
@@ -162,13 +203,13 @@ export default class Api {
}
fetchWaitingTaskList (params = {}) {
const { offset = 0, num = 200, keys } = params
const { offset = 0, num = 20, keys } = params
const args = compactUndefined([offset, num, keys])
return this.client.call('tellWaiting', ...args)
}
fetchStoppedTaskList (params = {}) {
const { offset = 0, num = 200, keys } = params
const { offset = 0, num = 20, keys } = params
const args = compactUndefined([offset, num, keys])
return this.client.call('tellStopped', ...args)
}
@@ -256,4 +297,17 @@ export default class Api {
stopPowerSaveBlocker () {
application.energyManager.stopPowerSaveBlocker()
}
fetchBtTrackerFromGitHub () {
const now = Date.now()
const promises = [
fetch(`${BEST_TRACKERS_IP_URL}?t=${now}`).then((res) => res.text()),
fetch(`${BEST_TRACKERS_URL}?t=${now}`).then((res) => res.text())
]
return Promise.all(promises).then((values) => {
let result = values.join('\r\n').replace(/^\s*[\r\n]/gm, '')
return result
})
}
}
+10
View File
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
<rect x="4" y="3" width="16" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
<line data-color="color-2" x1="1" y1="6" x2="1" y2="18" fill="none" stroke-miterlimit="10"/>
<line data-color="color-2" x1="23" y1="6" x2="23" y2="18" fill="none" stroke-miterlimit="10"/>
<polyline data-cap="butt" points="10 15 10 8 16 8 16 14" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
<circle data-stroke="none" cx="9" cy="15" r="2" stroke="none"/>
<circle data-stroke="none" cx="15" cy="14" r="2" stroke="none"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 747 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
<polyline data-cap="butt" data-color="color-2" points="1 20 6 14 10 18 17 10 23 17" fill="none" stroke-miterlimit="10"/>
<rect x="1" y="3" width="22" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
<circle data-color="color-2" cx="9" cy="8" r="2" fill="none" stroke-miterlimit="10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 509 B

+10
View File
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
<path fill="none" stroke="currentColor" stroke-miterlimit="10" d="M3,14V4 c0-0.552,0.448-1,1-1h16c0.552,0,1,0.448,1,1v6"/>
<path fill="none" stroke="currentColor" stroke-miterlimit="10" d="M10,18H1v0 c0,1.657,1.343,3,3,3h6"/>
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M14.126,17 c0.444-1.725,2.01-3,3.874-3c1.48,0,2.772,0.804,3.464,1.999"/>
<polygon data-color="color-2" data-stroke="none" points="23.22,13.649 22.792,18 18.522,17.061 " stroke-linejoin="miter" stroke-linecap="square" stroke="none"/>
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M21.874,20 c-0.444,1.725-2.01,3-3.874,3c-1.48,0-2.772-0.804-3.464-1.999"/>
<polygon data-color="color-2" data-stroke="none" points="12.78,23.351 13.208,19 17.478,19.939 " stroke-linejoin="miter" stroke-linecap="square" stroke="none"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<g>
<path d="M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12s12-5.383,12-12S18.617,0,12,0z M19,13h-8V5h2v6h6V13z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 214 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
<path data-cap="butt" fill="none" stroke="currentColor" stroke-miterlimit="10" d="M6.121,20.121 C7.727,21.22,9.907,22,12,22c5.523,0,10-4.477,10-10c0-5.523-4.477-10-10-10C8.101,2,4.728,4.233,3.078,7.488"/>
<polyline fill="none" stroke="currentColor" stroke-miterlimit="10" points="2.278,1.588 3.078,7.488 9.078,6.688 "/>
<circle data-color="color-2" fill="none" stroke-miterlimit="10" cx="4" cy="18" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 609 B

+12
View File
@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
<line data-cap="butt" data-color="color-2" x1="2" y1="6" x2="22" y2="6" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="12" y1="2" x2="12" y2="22" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="7" y1="2" x2="7" y2="6" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="17" y1="2" x2="17" y2="6" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="2" y1="18" x2="22" y2="18" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="7" y1="22" x2="7" y2="18" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="17" y1="22" x2="17" y2="18" fill="none" stroke-miterlimit="10"/>
<rect x="2" y="2" width="20" height="20" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="12" height="12">
<g class="nc-icon-wrapper" stroke-width="1" fill="#111111" stroke="#111111">
<line x1="1.5" y1="1.5" x2="10.5" y2="10.5" fill="none" stroke="#111111" stroke-linecap="round" stroke-linejoin="round"/>
<line x1="10.5" y1="1.5" x2="1.5" y2="10.5" fill="none" stroke="#111111" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 429 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="12" height="12">
<g class="nc-icon-wrapper" stroke-width="1" fill="#111111" stroke="#111111">
<polyline points="5.5 1.5 10.5 1.5 10.5 6.5" fill="none" stroke="#111111" stroke-linecap="round" stroke-linejoin="round"/>
<polyline points="1.5 5.5 1.5 10.5 6.5 10.5" fill="none" stroke-linecap="round" stroke-linejoin="round" data-color="color-2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 435 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="12" height="12">
<g class="nc-icon-wrapper" stroke-width="1" fill="#111111" stroke="#111111">
<line x1="1" y1="6" x2="11" y2="6" fill="none" stroke="#111111" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

@@ -31,7 +31,6 @@
},
data () {
const version = this.$electron.remote.app.getVersion()
console.log('version===>', version)
return {
version
}
+5 -6
View File
@@ -3,11 +3,10 @@
<div class="app-version">
<mo-logo :width="93" :height="21" style="vertical-align: bottom;" />
<span>Version {{version}}</span>
<!-- <p>一款 macOS 全能下载工具</p> -->
</div>
<div class="app-icon"></div>
<div class="engine-info" v-if="!!engine">
<h4>引擎版本 {{engine.version}}</h4>
<h4>{{ $t('about.engine-version') }} {{engine.version}}</h4>
<ul v-if="!isMas()">
<li
v-for="(feature, index) in engine.enabledFeatures"
@@ -50,13 +49,13 @@
<style lang="scss">
.app-info {
position: relative;
padding: 8px 0;
margin: 8px 0;
.app-version span {
display: inline-block;
vertical-align: bottom;
font-size: $--font-size-large;
margin-left: 20px;
color: $--color-text-regular;
color: $--app-version-color;
line-height: 18px;
}
.app-icon {
@@ -73,11 +72,11 @@
h4 {
font-size: $--font-size-base;
font-weight: $--font-weight-secondary;
color: $--color-text-regular;
color: $--app-engine-title-color;
}
ul {
font-size: 12px;
color: $--color-text-secondary;
color: $--app-engine-info-color;
list-style: none;
padding: 0;
line-height: 20px;
+11 -10
View File
@@ -1,16 +1,22 @@
<template>
<el-row class="copyright">
<el-col :span="8" class="copyright-left">
<el-col :span="6" class="copyright-left">
<a target="_blank" href="https://motrix.app/" rel="noopener noreferrer">
&copy;2018 Motrix
</a>
</el-col>
<el-col :span="16" class="copyright-right">
<el-col :span="18" class="copyright-right">
<a target="_blank" href="https://motrix.app/license" rel="noopener noreferrer">
{{ $t('about.license') }}
</a>
<a target="_blank" href="https://motrix.app/about" rel="noopener noreferrer">
关于我们
{{ $t('about.about') }}
</a>
<a target="_blank" href="https://motrix.app/support" rel="noopener noreferrer">
帮助支持
{{ $t('about.support') }}
</a>
<a target="_blank" href="https://motrix.app/release" rel="noopener noreferrer">
{{ $t('about.release') }}
</a>
</el-col>
</el-row>
@@ -22,21 +28,17 @@
}
</script>
<style lang="scss">
.copyright {
width: 100%;
font-size: $--font-size-small;
a {
color: $--color-text-regular;
color: $--app-copyright-color;
text-decoration: none;
}
}
.copyright-left {
text-align: left;
a {
color: $--color-text-regular;
}
}
.copyright-right {
@@ -45,5 +47,4 @@
margin-left: 30px;
}
}
</style>
+12 -18
View File
@@ -1,20 +1,20 @@
<template>
<el-aside width="78px" class="aside" :class="{ draggable: !isWindows() }">
<el-aside width="78px" :class="['aside', { 'draggable': asideDraggable }]">
<div class="aside-inner">
<mo-logo-mini />
<ul class="menu top-menu">
<li @click="nav('/task')">
<li @click="nav('/task')" class="non-draggable">
<mo-icon name="menu-task" width="20" height="20" />
</li>
<li @click="showAddTask()">
<li @click="showAddTask()" class="non-draggable">
<mo-icon name="menu-add" width="20" height="20" />
</li>
</ul>
<ul class="menu bottom-menu">
<li @click="nav('/preference')">
<li @click="nav('/preference')" class="non-draggable">
<mo-icon name="menu-preference" width="20" height="20" />
</li>
<li @click="showAboutPanel">
<li @click="showAboutPanel" class="non-draggable">
<mo-icon name="menu-about" width="20" height="20" />
</li>
</ul>
@@ -24,7 +24,7 @@
<script>
import is from 'electron-is'
import { mapState, mapActions } from 'vuex'
import { mapState } from 'vuex'
import LogoMini from '@/components/Logo/LogoMini'
import '@/components/Icons/menu-task'
import '@/components/Icons/menu-add'
@@ -39,33 +39,27 @@
computed: {
...mapState('app', {
currentPage: state => state.currentPage
})
}),
asideDraggable: function () {
return is.macOS()
}
},
methods: {
isRenderer: is.renderer,
isWindows: is.windows,
open (link) {
this.$electron.shell.openExternal(link)
},
showAddTask (taskType = 'uri') {
this.$store.dispatch('app/showAddTaskDialog', taskType)
},
showAboutPanel () {
// if (this.isRenderer()) {
// if (is.renderer()) {
// this.$electron.ipcRenderer.send('command', 'application:about')
// } else {
this.$store.dispatch('app/showAboutPanel')
// }
},
nav (page) {
console.log('nav page===>', page)
this.$router.push({
path: page
})
},
...mapActions('app', [
'changeCurrentPage'
])
}
}
}
</script>
+44 -7
View File
@@ -1,17 +1,47 @@
import router from '@/router'
import store from '@/store'
import CommandManager from './CommandManager'
import { Message } from 'element-ui'
import { getLocaleManager } from '@/components/Locale'
import { base64StringToBlob } from 'blob-util'
import { buildFileList } from '@shared/utils'
const commands = new CommandManager()
const i18n = getLocaleManager().getI18n()
function updateSystemTheme (theme) {
store.dispatch('app/updateSystemTheme', theme)
}
function updateTheme (theme) {
store.dispatch('preference/changeThemeConfig', theme)
}
function showAboutPanel () {
store.dispatch('app/showAboutPanel')
}
function showAddTask (taskType = 'uri') {
function showAddTask (taskType = 'uri', task = '') {
if (taskType === 'uri' && task) {
store.dispatch('app/updateAddTaskUrl', task)
}
store.dispatch('app/showAddTaskDialog', taskType)
}
function showAddBtTask () {
store.dispatch('app/showAddTaskDialog', 'torrent')
}
function showAddBtTaskWithFile (fileName, base64Data = '') {
const blob = base64StringToBlob(base64Data, 'application/x-bittorrent')
const file = new File([blob], fileName, { type: 'application/x-bittorrent' })
const fileList = buildFileList(file)
store.dispatch('app/showAddTaskDialog', 'torrent')
setTimeout(() => {
store.dispatch('app/addTaskAddTorrents', { fileList })
}, 200)
}
function navigateTaskList (status = 'active') {
router.push({ path: `/task/${status}` })
}
@@ -20,24 +50,28 @@ function navigatePreferences () {
router.push({ path: '/preference' })
}
function pauseTask () {
function showUnderDevelopmentMessage () {
Message.info(i18n.t('app.under-development-message'))
}
function pauseTask () {
showUnderDevelopmentMessage()
}
function resumeTask () {
showUnderDevelopmentMessage()
}
function deleteTask () {
showUnderDevelopmentMessage()
}
function moveTaskUp () {
showUnderDevelopmentMessage()
}
function moveTaskDown () {
showUnderDevelopmentMessage()
}
function pauseAllTask () {
@@ -48,9 +82,12 @@ function resumeAllTask () {
store.dispatch('task/resumeAllTask')
}
commands.register('application:system-theme', updateSystemTheme)
commands.register('application:theme', updateTheme)
commands.register('application:about', showAboutPanel)
commands.register('application:new-task', showAddTask)
commands.register('application:new-bt-task', showAddTask)
commands.register('application:new-bt-task', showAddBtTask)
commands.register('application:new-bt-task-with-file', showAddBtTaskWithFile)
commands.register('application:task-list', navigateTaskList)
commands.register('application:preferences', navigatePreferences)
+48
View File
@@ -0,0 +1,48 @@
<template>
<div v-if="false"></div>
</template>
<script>
export default {
name: 'mo-dragger',
mounted () {
this.preventDefault = ev => ev.preventDefault()
let count = 0
this.onDragEnter = (ev) => {
if (count === 0) {
this.$store.dispatch('app/showAddTaskDialog', 'torrent')
}
count++
}
this.onDragLeave = (ev) => {
count--
if (count === 0) {
this.$store.dispatch('app/hideAddTaskDialog')
}
}
this.onDrop = (ev) => {
count = 0
const fileList = [...ev.dataTransfer.files]
.map(item => ({ raw: item, name: item.name }))
.filter(item => /\.torrent$/.test(item.name))
if (!fileList.length) {
this.$msg.error(this.$t('task.select-torrent'))
}
}
document.addEventListener('dragover', this.preventDefault)
document.body.addEventListener('dragenter', this.onDragEnter)
document.body.addEventListener('dragleave', this.onDragLeave)
document.body.addEventListener('drop', this.onDrop)
},
destroyed () {
document.removeEventListener('dragover', this.preventDefault)
document.body.removeEventListener('dragenter', this.onDragEnter)
document.body.removeEventListener('dragleave', this.onDragLeave)
document.body.removeEventListener('drop', this.onDrop)
}
}
</script>
+20
View File
@@ -0,0 +1,20 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'audio': {
'width': 24,
'height': 24,
'raw': `<rect x="4" y="3" width="16" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
<line data-color="color-2" x1="1" y1="6" x2="1" y2="18" fill="none" stroke-miterlimit="10"/>
<line data-color="color-2" x1="23" y1="6" x2="23" y2="18" fill="none" stroke-miterlimit="10"/>
<polyline data-cap="butt" points="10 15 10 8 16 8 16 14" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
<circle data-stroke="none" cx="9" cy="15" r="2" stroke="none"/>
<circle data-stroke="none" cx="15" cy="14" r="2" stroke="none"/>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2'
}
}
})
+17
View File
@@ -0,0 +1,17 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'image': {
'width': 24,
'height': 24,
'raw': `<polyline data-cap="butt" data-color="color-2" points="1 20 6 14 10 18 17 10 23 17" fill="none" stroke-miterlimit="10"/>
<rect x="1" y="3" width="22" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
<circle data-color="color-2" cx="9" cy="8" r="2" fill="none" stroke-miterlimit="10"/>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2'
}
}
})
+22
View File
@@ -0,0 +1,22 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'sync': {
'width': 24,
'height': 24,
'raw': `<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path fill="none" stroke-miterlimit="10" d="M3,14V4 c0-0.552,0.448-1,1-1h16c0.552,0,1,0.448,1,1v6"></path>
<path fill="none" stroke-miterlimit="10" d="M10,18H1v0 c0,1.657,1.343,3,3,3h6"></path>
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M14.126,17 c0.444-1.725,2.01-3,3.874-3c1.48,0,2.772,0.804,3.464,1.999"></path>
<polygon data-color="color-2" data-stroke="none" points="23.22,13.649 22.792,18 18.522,17.061 " stroke-linejoin="miter" stroke-linecap="square" stroke="none"></polygon>
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M21.874,20 c-0.444,1.725-2.01,3-3.874,3c-1.48,0-2.772-0.804-3.464-1.999"></path>
<polygon data-color="color-2" data-stroke="none" points="12.78,23.351 13.208,19 17.478,19.939 " stroke-linejoin="miter" stroke-linecap="square" stroke="none"></polygon>
</g>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2'
}
}
})
@@ -0,0 +1,11 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'task-history': {
'width': 24,
'height': 24,
'paths': [{
'd': 'M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12s12-5.383,12-12S18.617,0,12,0z M19,13h-8V5h2v6h6V13z'
}]
}
})
@@ -0,0 +1,17 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'task-restart': {
'width': 24,
'height': 24,
'raw': `<path data-cap="butt" fill="none" stroke="currentColor" stroke-miterlimit="10" d="M6.121,20.121 C7.727,21.22,9.907,22,12,22c5.523,0,10-4.477,10-10c0-5.523-4.477-10-10-10C8.101,2,4.728,4.233,3.078,7.488"/>
<polyline fill="none" stroke="currentColor" stroke-miterlimit="10" points="2.278,1.588 3.078,7.488 9.078,6.688 "/>
<circle data-color="color-2" fill="none" stroke-miterlimit="10" cx="4" cy="18" r="3"/>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2'
}
}
})
+22
View File
@@ -0,0 +1,22 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'video': {
'width': 24,
'height': 24,
'raw': `<line data-cap="butt" data-color="color-2" x1="2" y1="6" x2="22" y2="6" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="12" y1="2" x2="12" y2="22" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="7" y1="2" x2="7" y2="6" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="17" y1="2" x2="17" y2="6" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="2" y1="18" x2="22" y2="18" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="7" y1="22" x2="7" y2="18" fill="none" stroke-miterlimit="10"/>
<line data-cap="butt" data-color="color-2" x1="17" y1="22" x2="17" y2="18" fill="none" stroke-miterlimit="10"/>
<rect x="2" y="2" width="20" height="20" fill="none" stroke="currentColor" stroke-miterlimit="10"/>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2'
}
}
})
@@ -0,0 +1,16 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'win-close': {
'width': 12,
'height': 12,
'raw': `<line x1="1.5" y1="1.5" x2="10.5" y2="10.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<line x1="10.5" y1="1.5" x2="1.5" y2="10.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '1'
}
}
})
@@ -0,0 +1,16 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'win-maximize': {
'width': 12,
'height': 12,
'raw': `<polyline points="5.5 1.5 10.5 1.5 10.5 6.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<polyline points="1.5 5.5 1.5 10.5 6.5 10.5" fill="none" stroke-linecap="round" stroke-linejoin="round" />`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '1'
}
}
})
@@ -0,0 +1,15 @@
import Icon from '@/components/Icons/Icon'
Icon.register({
'win-minimize': {
'width': 12,
'height': 12,
'raw': `<line x1="1" y1="6" x2="11" y2="6" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
'g': {
'stroke': 'currentColor',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '1'
}
}
})
+10
View File
@@ -0,0 +1,10 @@
import resources from '@shared/locales/all'
import LocaleManager from '@shared/locales/LocaleManager'
const localeManager = new LocaleManager({
resources
})
export function getLocaleManager () {
return localeManager
}
+5 -5
View File
@@ -2,7 +2,7 @@
<div class="logo">
<a target="_blank" href="https://motrix.app/">
<svg xmlns="http://www.w3.org/2000/svg" :width="width" :height="height" viewBox="0 0 62 14">
<g :fill="fill" fill-rule="evenodd">
<g fill-rule="evenodd">
<path d="M40,2 C40,1 41,1.53477231e-14 42,1.53477231e-14 C42,1.53477231e-14 60,1.27897692e-14 60,1.53477231e-14 C61,1.27897692e-14 62,1 62,2 C62,2 62,12 62,12 C62,13 61,14 60,14 C60,14 42,14 42,14 C41,14 40,13 40,12 C40,12 40,2 40,2 Z M44,3.5 C44,3.5 44,10.5 44,10.5 C44,11 44.5,11.5 45,11.5 C45,11.5 57,11.5 57,11.5 C57.5,11.5 58,11 58,10.5 C58,10.5 58,3.5 58,3.5 C58,3 57.5,2.5 57,2.5 C57,2.5 45,2.5 45,2.5 C44.5,2.5 44,3 44,3.5 Z"/>
<rect width="4" height="2" x="32" y="6" rx=".5"/>
<path d="M2,0 L26,0 C27,-2.04003481e-15 28,1 28,2 L28,14 L24,14 L24,3.5 C24,3 23.5,2.5 23,2.5 L16,2.5 L16,14 L12,14 L12,2.5 L5,2.5 C4.5,2.5 4,3 4,3.5 L4,14 L0,14 L0,2 C0,1 1,-2.04003481e-15 2,0 Z"/>
@@ -23,10 +23,6 @@
height: {
type: Number,
default: 14
},
fill: {
type: String,
default: '#4D515A'
}
}
}
@@ -43,6 +39,10 @@
height: 100%;
text-align: center;
font-size: 0;
color: $--app-logo-color;
}
svg {
fill: currentColor;
}
}
</style>
+5 -25
View File
@@ -1,11 +1,12 @@
<template>
<el-container id="container">
<mo-aside />
<router-view></router-view>
<router-view />
<mo-speedometer />
<mo-add-task :visible="addTaskVisible" :type="addTaskType" />
<mo-about-panel :visible="aboutPanelVisible" />
<mo-task-item-info :visible="taskItemInfoVisible" :task="currentTaskItem" />
<mo-dragger />
</el-container>
</template>
@@ -13,20 +14,20 @@
import { mapState } from 'vuex'
import AboutPanel from '@/components/About/AboutPanel'
import Aside from '@/components/Aside/Index'
import Subnav from '@/components/Subnav/Index'
import Speedometer from '@/components/Speedometer/Speedometer'
import AddTask from '@/components/Task/AddTask'
import TaskItemInfo from '@/components/Task/TaskItemInfo'
import Dragger from '@/components/Dragger/Index'
export default {
name: 'mo-main',
components: {
[AboutPanel.name]: AboutPanel,
[Aside.name]: Aside,
[Subnav.name]: Subnav,
[Speedometer.name]: Speedometer,
[AddTask.name]: AddTask,
[TaskItemInfo.name]: TaskItemInfo
[TaskItemInfo.name]: TaskItemInfo,
[Dragger.name]: Dragger
},
computed: {
...mapState('app', {
@@ -50,25 +51,4 @@
right: 36px;
bottom: 24px;
}
.panel {
background: $--panel-background;
.panel-header {
position: relative;
padding: 44px 0 12px;
margin: 0 36px;
border-bottom: 2px solid $--panel-border-color;
h4 {
margin: 0;
color: $--panel-title-color;
font-size: 16px;
font-weight: normal;
line-height: 24px;
}
}
.panel-content {
position: relative;
padding: 16px 36px 24px;
height: 100%;
}
}
</style>
+42
View File
@@ -0,0 +1,42 @@
const queue = []
const maxLength = 5
export default {
install: function (Vue, Message, defaultOption = {}) {
Vue.prototype.$msg = new Proxy(Message, {
get (obj, prop) {
return (arg) => {
if (!(arg instanceof Object)) {
arg = { message: arg }
}
const task = {
run () {
obj[prop]({
...defaultOption,
...arg,
onClose (...data) {
const currentTask = queue.pop()
if (currentTask) {
currentTask.run()
}
if (arg.onClose) {
arg.onClose(...data)
}
}
})
}
}
if (queue.length >= maxLength) {
queue.pop()
}
queue.unshift(task)
if (queue.length === 1) {
queue.pop().run()
}
}
}
})
}
}
+98 -44
View File
@@ -3,6 +3,7 @@
</template>
<script>
import is from 'electron-is'
import { mapState } from 'vuex'
import api from '@/api'
import {
@@ -18,10 +19,17 @@
export default {
name: 'mo-engine-client',
data: function () {
return {
downloading: false
}
},
computed: {
isRenderer: () => is.renderer(),
...mapState('app', {
downloadSpeed: state => state.stat.downloadSpeed,
interval: state => state.interval
interval: state => state.interval,
numActive: state => state.stat.numActive
}),
...mapState('task', {
taskItemInfoVisible: state => state.taskItemInfoVisible,
@@ -34,105 +42,146 @@
watch: {
downloadSpeed: function (val, oldVal) {
showDownloadSpeedInDock(val)
},
numActive: function (val, oldVal) {
this.downloading = val > 0
},
downloading: function (val, oldVal) {
if (val !== oldVal && this.isRenderer) {
this.$electron.ipcRenderer.send('download-status-change', val)
}
}
},
methods: {
fetchTaskItem ({ gid }) {
return api.fetchTaskItem({ gid })
.catch((e) => {
console.warn(`fetchTaskItem fail: ${e.message}`)
})
},
onDownloadStart: function (event) {
this.$store.dispatch('task/fetchList')
this.$store.dispatch('app/resetInterval')
console.log('aria2 onDownloadStart', event)
const [{ gid }] = event
api.fetchTaskItem({ gid })
this.fetchTaskItem({ gid })
.then((task) => {
const message = `开始下载 ${getTaskName(task)}`
this.$message.info(message)
const taskName = getTaskName(task)
const message = this.$t('task.download-start-message', { taskName })
this.$msg.info(message)
})
},
onDownloadPause: function (event) {
console.log('aria2 onDownloadPause')
const [{ gid }] = event
api.fetchTaskItem({ gid })
this.fetchTaskItem({ gid })
.then((task) => {
const message = `暂停下载 ${getTaskName(task)}`
this.$message.info(message)
const taskName = getTaskName(task)
const message = this.$t('task.download-pause-message', { taskName })
this.$msg.info(message)
})
},
onDownloadStop: function (event) {
console.log('aria2 onDownloadStop')
const [{ gid }] = event
api.fetchTaskItem({ gid })
this.fetchTaskItem({ gid })
.then((task) => {
const message = `${getTaskName(task)} 下载中止`
this.$message.info(message)
const taskName = getTaskName(task)
const message = this.$t('task.download-stop-message', { taskName })
this.$msg.info(message)
})
},
onDownloadError: function (event) {
console.log('aria2 onDownloadError', event)
const [{ gid }] = event
api.fetchTaskItem({ gid })
this.fetchTaskItem({ gid })
.then((task) => {
const message = `${getTaskName(task)} 下载发生错误`
this.$message.error(message)
const taskName = getTaskName(task)
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>`
this.$msg({
type: 'error',
showClose: true,
duration: 5000,
dangerouslyUseHTMLString: true,
message: `${message} ${link}`
})
})
},
onDownloadComplete: function (event) {
console.log('aria2 onDownloadComplete')
this.$store.dispatch('task/fetchList')
const [{ gid }] = event
api.fetchTaskItem({ gid })
this.fetchTaskItem({ gid })
.then((task) => {
this.showTaskCompleteNotify(task)
this.handleDownloadComplete(task, false)
})
},
onBtDownloadComplete: function (event) {
console.log('aria2 onBtDownloadComplete')
this.$store.dispatch('task/fetchList')
const [{ gid }] = event
api.fetchTaskItem({ gid })
this.fetchTaskItem({ gid })
.then((task) => {
this.showTaskCompleteNotify(task)
this.handleDownloadComplete(task, true)
})
},
showTaskCompleteNotify: function (task) {
if (!this.taskNotification) {
return
}
const taskName = getTaskName(task)
handleDownloadComplete: function (task, isBT) {
const path = getTaskFullPath(task)
addToRecentTask(task)
openDownloadDock(path)
const message = `${taskName} 下载完成`
this.$message.success(message)
/* eslint-disable no-new */
const notify = new Notification('下载完成', {
body: taskName
})
notify.onclick = () => {
showItemInFolder(path)
}
this.showTaskCompleteNotify(task, isBT, path)
},
showTaskErrorNotify: function (task) {
showTaskCompleteNotify: function (task, isBT, path) {
const taskName = getTaskName(task)
const message = isBT
? this.$t('task.bt-download-complete-message', { taskName })
: this.$t('task.download-complete-message', { taskName })
const tips = isBT
? '\n' + this.$t('task.bt-download-complete-tips')
: ''
this.$msg.success(`${message}${tips}`)
if (!this.taskNotification) {
return
}
/* eslint-disable no-new */
const notifyMessage = isBT
? this.$t('task.bt-download-complete-notify')
: this.$t('task.download-complete-notify')
const notify = new Notification(notifyMessage, {
body: `${taskName}${tips}`
})
notify.onclick = () => {
showItemInFolder(path, {
errorMsg: this.$t('task.file-not-exist')
})
}
},
showTaskErrorNotify: function (task) {
const taskName = getTaskName(task)
const message = `${taskName} 下载失败`
this.$message.success(message)
const message = this.$t('task.download-fail-message', { taskName })
this.$msg.success(message)
if (!this.taskNotification) {
return
}
/* eslint-disable no-new */
new Notification('下载失败', {
new Notification(this.$t('task.download-fail-notify'), {
body: taskName
})
},
bindEngineEvents: function () {
api.client.on('onDownloadStart', this.onDownloadStart)
api.client.on('onDownloadPause', this.onDownloadPause)
// api.client.on('onDownloadPause', this.onDownloadPause)
api.client.on('onDownloadStop', this.onDownloadStop)
api.client.on('onDownloadComplete', this.onDownloadComplete)
api.client.on('onDownloadError', this.onDownloadError)
@@ -140,7 +189,7 @@
},
unbindEngineEvents: function () {
api.client.removeListener('onDownloadStart', this.onDownloadStart)
api.client.removeListener('onDownloadPause', this.onDownloadPause)
// api.client.removeListener('onDownloadPause', this.onDownloadPause)
api.client.removeListener('onDownloadStop', this.onDownloadStop)
api.client.removeListener('onDownloadComplete', this.onDownloadComplete)
api.client.removeListener('onDownloadError', this.onDownloadError)
@@ -155,6 +204,7 @@
polling: function () {
this.$store.dispatch('app/fetchGlobalStat')
this.$store.dispatch('task/fetchList')
if (this.taskItemInfoVisible && this.currentTaskItem) {
this.$store.dispatch('task/fetchItem', this.currentTaskItem.gid)
}
@@ -165,12 +215,16 @@
}
},
created: function () {
this.$store.dispatch('app/fetchEngineInfo')
this.startPolling()
this.bindEngineEvents()
},
mounted: function () {
setTimeout(() => {
this.$store.dispatch('app/fetchEngineInfo')
this.$store.dispatch('app/fetchEngineOptions')
this.startPolling()
}, 100)
},
destroyed: function () {
this.$store.dispatch('task/saveSession')
@@ -1,6 +1,6 @@
<template>
<i @click.stop="onFolderClick">
<mo-icon name="folder" width="14" height="14" />
<mo-icon name="folder" width="10" height="10" />
</i>
</template>
@@ -21,7 +21,9 @@
if (!this.path) {
return
}
this.$electron.shell.showItemInFolder(this.path)
this.$electron.shell.showItemInFolder(this.path, {
errorMsg: this.$t('task.file-not-exist')
})
}
}
}
@@ -0,0 +1,96 @@
<template>
<div class="title-bar">
<div class="title-bar-dragger"></div>
<ul v-if="showActions" class="window-actions">
<li @click="handleMinimize">
<mo-icon name="win-minimize" width="12" height="12" />
</li>
<li @click="handleMaximize">
<mo-icon name="win-maximize" width="12" height="12" />
</li>
<li @click="handleClose" class="win-close-btn">
<mo-icon name="win-close" width="12" height="12" />
</li>
</ul>
</div>
</template>
<script>
import '@/components/Icons/win-minimize'
import '@/components/Icons/win-maximize'
import '@/components/Icons/win-close'
export default {
name: 'mo-title-bar',
props: {
showActions: {
type: Boolean
}
},
computed: {
win: function () {
return this.$electron.remote.getCurrentWindow()
}
},
methods: {
handleMinimize: function () {
this.win.minimize()
},
handleMaximize: function () {
if (this.win.isMaximized()) {
this.win.unmaximize()
} else {
this.win.maximize()
}
},
handleClose: function () {
this.win.close()
}
}
}
</script>
<style lang="scss">
.title-bar {
position: fixed;
top: 0;
left: 0;
display: flex;
flex-direction: row;
width: 100%;
height: 36px;
z-index: 5000;
.title-bar-dragger {
flex: 1;
user-select: none;
-webkit-app-region: drag;
-webkit-user-select: none;
}
.window-actions {
opacity: 0.4;
transition: $--fade-transition;
list-style: none;
padding: 0;
margin: 0;
z-index: 5100;
> li {
display: inline-block;
padding: 5px 10px;
margin: 0 5px;
color: $--titlebar-actions-color;
&:hover {
background-color: $--titlebar-actions-active-background;
}
&.win-close-btn:hover {
color: $--titlebar-close-active-color;
background-color: $--titlebar-close-active-background;
}
}
}
&:hover {
.window-actions {
opacity: 1;
}
}
}
</style>
+68 -14
View File
@@ -2,9 +2,11 @@ import is from 'electron-is'
import { existsSync } from 'fs'
import { Message } from 'element-ui'
import {
isMagnetTask,
getTaskFullPath,
bytesToSize
} from '@shared/utils'
import { LIGHT_THEME, DARK_THEME } from '@shared/constants'
const remote = is.renderer() ? require('electron').remote : {}
@@ -14,31 +16,59 @@ export function getUserDownloadsPath () {
export function prettifyDir (dir) {
const downloads = getUserDownloadsPath()
const result = dir === downloads ? '下载' : dir
const result = dir === downloads ? 'Downloads' : dir
return result
}
export function showItemInFolder (fullPath) {
export function showItemInFolder (fullPath, { errorMsg }) {
if (!fullPath) {
return
}
const result = remote.shell.showItemInFolder(fullPath)
if (!result) {
Message.error('目标文件不存在或已删除')
if (!result && errorMsg) {
Message.error(errorMsg)
}
return result
}
export function moveTaskFilesToTrash (task) {
export function openItem (fullPath, { errorMsg }) {
if (!fullPath) {
return
}
const result = remote.shell.openItem(fullPath)
if (!result && errorMsg) {
Message.error(errorMsg)
}
return result
}
export function moveTaskFilesToTrash (task, messages = {}) {
/**
* 磁力链接任务,有 bittorrent,但没有 bittorrent.info
* 在没下完变成BT任务之前 path 不是一个完整路径,
* 未避免误删所在目录,所以删除时直接返回 true
*/
if (isMagnetTask(task)) {
return true
}
const { pathErrorMsg, delFailMsg, delConfigFailMsg } = messages
const { dir } = task
const path = getTaskFullPath(task)
if (!path) {
Message.error('文件路径异常,请手动删除')
if (!path || dir === path) {
if (pathErrorMsg) {
Message.error(pathErrorMsg)
}
return false
}
const deleteResult1 = remote.shell.moveItemToTrash(path)
if (!deleteResult1) {
Message.error('删除任务文件失败,请手动删除')
let deleteResult1 = true
const isFileExist = existsSync(path)
if (isFileExist) {
deleteResult1 = remote.shell.moveItemToTrash(path)
if (!deleteResult1 && delFailMsg) {
Message.error(delFailMsg)
}
}
let deleteResult2 = true
@@ -46,8 +76,8 @@ export function moveTaskFilesToTrash (task) {
const isExtraExist = existsSync(extraFilePath)
if (isExtraExist) {
deleteResult2 = remote.shell.moveItemToTrash(extraFilePath)
if (!deleteResult2) {
Message.error('删除任务配置文件失败,请手动删除')
if (!deleteResult2 && delConfigFailMsg) {
Message.error(delConfigFailMsg)
}
}
@@ -55,30 +85,54 @@ export function moveTaskFilesToTrash (task) {
}
export function openDownloadDock (path) {
if (!is.macOS()) {
return
}
remote.app.dock.downloadFinished(path)
}
export function updateDockBadge (text) {
if (!is.macOS()) {
return
}
remote.app.dock.setBadge(text)
}
export function showDownloadSpeedInDock (downloadSpeed) {
if (is.windows()) {
if (!is.macOS()) {
return
}
const text = downloadSpeed > 0 ? bytesToSize(downloadSpeed) : ''
const text = downloadSpeed > 0 ? `${bytesToSize(downloadSpeed)}/s` : ''
updateDockBadge(text)
}
export function addToRecentTask (task) {
if (is.linux()) {
return
}
const path = getTaskFullPath(task)
remote.app.addRecentDocument(path)
}
export function addToRecentTaskByPath (path) {
if (is.linux()) {
return
}
remote.app.addRecentDocument(path)
}
export function clearRecentTasks () {
if (is.linux()) {
return
}
remote.app.clearRecentDocuments()
}
export function getSystemTheme () {
let result = LIGHT_THEME
if (!is.macOS()) {
return result
}
result = remote.systemPreferences.isDarkMode() ? DARK_THEME : LIGHT_THEME
return result
}
+117 -29
View File
@@ -6,15 +6,15 @@
<el-main class="panel-content">
<el-form
class="form-preference"
ref="basicForm"
ref="advancedForm"
label-position="right"
size="mini"
:model="form"
:rules="rules">
<el-form-item label="代理: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('preferences.proxy')}: `" :label-width="formLabelWidth">
<el-switch
v-model="form.useProxy"
active-text="使用代理服务器"
:active-text="$t('preferences.use-proxy')"
@change="onUseProxyChange"
>
</el-switch>
@@ -28,9 +28,48 @@
</el-input>
</el-col>
</el-form-item>
<el-form-item label="开发者: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('preferences.bt-tracker')}: `" :label-width="formLabelWidth">
<div class="bt-tracker">
<el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
auto-complete="off"
:placeholder="`${$t('preferences.bt-tracker-input-tips')}`"
v-model="form.btTracker">
</el-input>
<div class="sync-tracker">
<el-tooltip
class="item"
effect="dark"
:content="$t('preferences.sync-tracker-tips')"
placement="bottom"
>
<el-button
@click="syncTrackerFromGitHub"
>
<mo-icon
name="refresh"
width="12"
height="12"
:spin="true"
v-if="trackerSyncing"
/>
<mo-icon name="sync" width="12" height="12" v-else />
</el-button>
</el-tooltip>
</div>
</div>
<div class="el-form-item__info" style="margin-top: 8px;">
{{ $t('preferences.bt-tracker-tips') }}
<a target="_blank" href="https://github.com/ngosang/trackerslist" rel="noopener noreferrer">
https://github.com/ngosang/trackerslist
<mo-icon name="link" width="12" height="12" />
</a>
</div>
</el-form-item>
<el-form-item :label="`${$t('preferences.developer')}: `" :label-width="formLabelWidth">
<el-col class="form-item-sub" :span="24">
模拟用户代理
{{ $t('preferences.mock-user-agent') }}
<el-input
type="textarea"
:autosize="{ minRows: 2, maxRows: 3 }"
@@ -45,7 +84,7 @@
</el-button-group>
</el-col>
<el-col class="form-item-sub" :span="24">
应用日志路径
{{ $t('preferences.app-log-path') }}
<el-input placeholder="" disabled v-model="logPath">
<mo-show-in-folder
slot="append"
@@ -55,7 +94,7 @@
</el-input>
</el-col>
<el-col class="form-item-sub" :span="24">
下载会话记录
{{ $t('preferences.download-session-path') }}
<el-input placeholder="" disabled v-model="sessionPath">
<mo-show-in-folder
slot="append"
@@ -65,13 +104,15 @@
</el-input>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-button plain type="danger" @click="() => onResetClick()">恢复初始设置</el-button>
<el-button plain type="danger" @click="() => onFactoryResetClick()">
{{ $t('preferences.factory-reset') }}
</el-button>
</el-col>
</el-form-item>
</el-form>
<div class="form-actions">
<el-button type="primary" @click="submitForm('basicForm')">保存并应用</el-button>
<el-button @click="resetForm('basicForm')">放弃</el-button>
<el-button type="primary" @click="submitForm('advancedForm')">{{ $t('preferences.save') }}</el-button>
<el-button @click="resetForm('advancedForm')">{{ $t('preferences.discard') }}</el-button>
</div>
</el-main>
</el-container>
@@ -80,20 +121,33 @@
<script>
import is from 'electron-is'
import { mapState } from 'vuex'
import { cloneDeep } from 'lodash'
import ShowInFolder from '@/components/Native/ShowInFolder'
import userAgentMap from '@shared/ua'
import {
calcFormLabelWidth,
convertCommaToLine,
convertLineToComma,
diffConfig
} from '@shared/utils'
import '@/components/Icons/sync'
import '@/components/Icons/refresh'
const initialForm = (config) => {
const {
useProxy,
allProxy,
allProxyBackup,
btTracker,
hideAppMenu,
useProxy,
userAgent
} = config
const result = {
useProxy,
allProxy,
allProxyBackup,
btTracker: convertCommaToLine(btTracker),
hideAppMenu,
useProxy,
userAgent
}
return result
@@ -105,15 +159,22 @@
[ShowInFolder.name]: ShowInFolder
},
data: function () {
const { locale } = this.$store.state.preference.config
const form = initialForm(this.$store.state.preference.config)
return {
formLabelWidth: '23%',
form: initialForm(this.$store.state.preference.config),
rules: {}
form,
formLabelWidth: calcFormLabelWidth(locale),
formOriginal: cloneDeep(form),
rules: {},
trackerSyncing: false
}
},
computed: {
title: function () {
return '进阶设置'
return this.$t('preferences.advanced')
},
showHideAppMenuOption: function () {
return is.windows() || is.linux()
},
...mapState('preference', {
config: state => state.config,
@@ -122,34 +183,41 @@
})
},
watch: {
},
methods: {
isRenderer: is.renderer,
syncTrackerFromGitHub () {
this.trackerSyncing = true
this.$store.dispatch('preference/fetchBtTracker')
.then((data) => {
console.log('syncTrackerFromGitHub data====>', data)
this.form.btTracker = data
})
.finally(() => {
this.trackerSyncing = false
})
},
onUseProxyChange (flag) {
this.form.allProxy = flag ? this.form.allProxyBackup : ''
console.log('this.form.allProxy===>', flag, this.form.allProxy)
},
onAllProxyBackupChange (value) {
this.form.allProxy = value
},
changeUA (type) {
const ua = userAgentMap[type]
console.log('changeUA===>', ua)
if (!ua) {
return
}
this.form.userAgent = ua
},
onResetClick () {
onFactoryResetClick () {
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: '恢复初始设置',
message: '你确定要恢复为初始设置吗?',
buttons: ['是', '否'],
title: this.$t('preferences.factory-reset'),
message: this.$t('preferences.factory-reset-confirm'),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1
}, (buttonIndex) => {
// 点击的按钮是哪个按钮 0: 是, 1: 否
if (buttonIndex === 0) {
this.$electron.ipcRenderer.send('command', 'application:reset')
}
@@ -161,11 +229,23 @@
console.log('error submit!!')
return false
}
const changed = diffConfig(this.formOriginal, this.form)
const data = {
...changed,
btTracker: convertLineToComma(this.form.btTracker)
}
console.log('changed====》', data)
this.$store.dispatch('preference/save', data)
.then(() => {
this.$store.dispatch('app/fetchEngineOptions')
this.$msg.success(this.$t('preferences.save-success-message'))
})
.catch(() => {
this.$msg.success(this.$t('preferences.save-fail-message'))
})
console.log('this.form===>', this.form)
this.$store.dispatch('preference/save', this.form)
if (this.isRenderer()) {
this.$electron.ipcRenderer.send('command', 'application:relaunch')
}
})
},
@@ -177,7 +257,15 @@
</script>
<style lang="scss">
.ua-group {
margin-top: 8px;
.bt-tracker {
position: relative;
.sync-tracker {
position: absolute;
top: 8px;
right: 8px;
}
}
.ua-group {
margin-top: 8px;
}
</style>
+193 -34
View File
@@ -11,12 +11,65 @@
size="mini"
:model="form"
:rules="rules">
<el-form-item label="启动: " :label-width="formLabelWidth">
<el-checkbox v-model="form.resumeAllWhenAppLaunched">
启动后自动开始未完成任务
</el-checkbox>
<el-form-item :label="`${$t('preferences.appearance')}: `" :label-width="formLabelWidth">
<el-col class="form-item-sub" :span="24">
<mo-theme-switcher
v-model="form.theme"
@change="handleThemeChange"
/>
</el-col>
<el-col v-if="showHideAppMenuOption" class="form-item-sub" :span="16">
<el-checkbox v-model="form.hideAppMenu">
{{ $t('preferences.hide-app-menu') }}
</el-checkbox>
</el-col>
</el-form-item>
<el-form-item label="默认下载路径: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('preferences.language')}: `" :label-width="formLabelWidth">
<el-col class="form-item-sub" :span="16">
<el-select
v-model="form.locale"
@change="handleLocaleChange"
:placeholder="$t('preferences.change-language')">
<el-option
v-for="item in locales"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-form-item>
<el-form-item :label="`${$t('preferences.startup')}: `" :label-width="formLabelWidth">
<el-col
class="form-item-sub"
:span="24"
v-if="!isLinux()"
>
<el-checkbox v-model="form.openAtLogin">
{{ $t('preferences.open-at-login') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.keepWindowState">
{{ $t('preferences.keep-window-state') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.resumeAllWhenAppLaunched">
{{ $t('preferences.auto-resume-all') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.autoCheckUpdate">
{{ $t('preferences.auto-check-update') }}
</el-checkbox>
<div class="el-form-item__info" style="margin-top: 8px;" v-if="form.lastCheckUpdateTime !== 0">
{{ $t('preferences.last-check-update-time') + ': ' + (form.lastCheckUpdateTime !== 0 ? new
Date(form.lastCheckUpdateTime).toLocaleString() : new Date().toLocaleString()) }}
</div>
</el-col>
</el-form-item>
<el-form-item :label="`${$t('preferences.default-dir')}: `" :label-width="formLabelWidth">
<el-input placeholder="" v-model="downloadDir" :readonly="isMas()">
<mo-select-directory
v-if="isRenderer()"
@@ -25,50 +78,74 @@
/>
</el-input>
<div class="el-form-item__info" v-if="isMas()" style="margin-top: 8px;">
App Store 的沙箱权限限制默认下载路径建议设置为您的下载目录
{{ $t('preferences.mas-default-dir-tips') }}
</div>
</el-form-item>
<el-form-item label="任务管理: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('preferences.transfer-settings')}: `" :label-width="formLabelWidth">
<el-col class="form-item-sub" :span="24">
同时下载的最大任务数
{{ $t('preferences.transfer-speed-upload') }}
<el-select v-model="form.maxOverallUploadLimit">
<el-option
v-for="item in speedOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
<el-col class="form-item-sub" :span="24">
{{ $t('preferences.transfer-speed-download') }}
<el-select v-model="form.maxOverallDownloadLimit">
<el-option
v-for="item in speedOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-form-item>
<el-form-item :label="`${$t('preferences.task-manage')}: `" :label-width="formLabelWidth">
<el-col class="form-item-sub" :span="24">
{{ $t('preferences.max-concurrent-downloads') }}
<el-input-number
v-model="form.maxConcurrentDownloads"
controls-position="right"
:min="1"
:max="10"
label="同时下载最大任务数">
:label="$t('preferences.max-concurrent-downloads')">
</el-input-number>
</el-col>
<el-col class="form-item-sub" :span="24">
单任务下载的线程数
{{ $t('preferences.max-connection-per-server') }}
<el-input-number
v-model="form.split"
controls-position="right"
:min="1"
:max="form.maxConnectionPerServer"
label="单任务下载线程数">
:label="$t('preferences.max-connection-per-server')">
</el-input-number>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.continue">
断点续传
{{ $t('preferences.continue') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.newTaskShowDownloading">
新建任务后自动跳转到下载页面
{{ $t('preferences.new-task-show-downloading') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.taskNotification">
下载完成后通知
{{ $t('preferences.task-completed-notify') }}
</el-checkbox>
</el-col>
</el-form-item>
</el-form>
<div class="form-actions">
<el-button type="primary" @click="submitForm('basicForm')">保存并应用</el-button>
<el-button @click="resetForm('basicForm')">放弃</el-button>
<el-button type="primary" @click="submitForm('basicForm')">{{ $t('preferences.save') }}</el-button>
<el-button @click="resetForm('basicForm')">{{ $t('preferences.discard') }}</el-button>
</div>
</el-main>
</el-container>
@@ -77,32 +154,51 @@
<script>
import is from 'electron-is'
import { mapState } from 'vuex'
import { cloneDeep } from 'lodash'
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 '@/components/Native/utils'
import { calcFormLabelWidth, diffConfig } from '@shared/utils'
const initialForm = (config) => {
const {
autoCheckUpdate,
dir,
split,
resumeAllWhenAppLaunched,
hideAppMenu,
keepWindowState,
lastCheckUpdateTime,
locale,
maxConcurrentDownloads,
maxConnectionPerServer,
maxOverallUploadLimit,
maxOverallDownloadLimit,
newTaskShowDownloading,
openAtLogin,
resumeAllWhenAppLaunched,
split,
taskNotification,
newTaskShowDownloading
theme
} = config
console.log('initialForm===>', dir, split)
const result = {
dir,
split,
userAgent: '',
referer: '',
cookie: '',
autoCheckUpdate,
continue: config.continue,
resumeAllWhenAppLaunched,
dir,
hideAppMenu,
keepWindowState,
lastCheckUpdateTime,
locale,
maxConcurrentDownloads,
maxConnectionPerServer,
maxOverallUploadLimit,
maxOverallDownloadLimit,
newTaskShowDownloading,
openAtLogin,
resumeAllWhenAppLaunched,
split,
taskNotification,
newTaskShowDownloading
theme
}
return result
}
@@ -110,18 +206,27 @@
export default {
name: 'mo-preference-basic',
components: {
[SelectDirectory.name]: SelectDirectory
[SelectDirectory.name]: SelectDirectory,
[ThemeSwitcher.name]: ThemeSwitcher
},
data: function () {
const { locale } = this.$store.state.preference.config
const form = initialForm(this.$store.state.preference.config)
return {
formLabelWidth: '23%',
form: initialForm(this.$store.state.preference.config),
rules: {}
form,
formLabelWidth: calcFormLabelWidth(locale),
formOriginal: cloneDeep(form),
locales: availableLanguages,
rules: {},
speedOptions: this.buildSpeedOptions()
}
},
computed: {
title: function () {
return '基础设置'
return this.$t('preferences.basic')
},
showHideAppMenuOption: function () {
return is.windows() || is.linux()
},
downloadDir: function () {
return prettifyDir(this.form.dir)
@@ -133,6 +238,45 @@
methods: {
isRenderer: is.renderer,
isMas: is.mas,
isLinux: is.linux,
handleLocaleChange (locale) {
const lng = getLanguage(locale)
getLocaleManager().changeLanguage(lng)
this.speedOptions = this.buildSpeedOptions()
this.$electron.ipcRenderer.send('command', 'application:change-locale', lng)
},
handleThemeChange (theme) {
this.form.theme = theme
this.$electron.ipcRenderer.send('command', 'application:change-theme', theme)
},
buildSpeedOptions () {
return [
{
label: this.$t('preferences.transfer-speed-unlimited'),
value: 0
},
{
label: '128 KB/s',
value: '128K'
},
{
label: '512 KB/s',
value: '512K'
},
{
label: '1 MB/s',
value: '1M'
},
{
label: '5 MB/s',
value: '5M'
},
{
label: '10 MB/s',
value: '10M'
}
]
},
onDirectorySelected (dir) {
this.form.dir = dir
},
@@ -143,9 +287,24 @@
return false
}
this.$store.dispatch('preference/save', this.form)
const { openAtLogin } = this.form
const changed = diffConfig(this.formOriginal, this.form)
const data = {
...changed
}
console.log('changed====》', data)
this.$store.dispatch('preference/save', data)
.then(() => {
this.$store.dispatch('app/fetchEngineOptions')
this.$msg.success(this.$t('preferences.save-success-message'))
})
.catch(() => {
this.$msg.success(this.$t('preferences.save-fail-message'))
})
if (this.isRenderer()) {
this.$electron.ipcRenderer.send('command', 'application:relaunch')
this.$electron.ipcRenderer.send('command', 'application:open-at-login', openAtLogin)
}
})
},
+21 -24
View File
@@ -1,9 +1,9 @@
<template>
<el-container class="content panel" direction="horizontal">
<el-aside width="200px" class="subnav">
<router-view name="subnav"></router-view>
<router-view name="subnav" />
</el-aside>
<router-view name="form"></router-view>
<router-view name="form" />
</el-container>
</template>
@@ -11,19 +11,8 @@
export default {
name: 'mo-content-preference',
props: {
category: {
type: String
}
},
computed: {
title: function () {
const titles = {
'basic': '基础设置',
'advanced': '进阶设置',
'lab': '实验室'
}
return titles[this.category]
}
},
components: {
},
@@ -39,6 +28,19 @@
font-weight: $--font-weight-secondary;
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: 24px;
}
@@ -47,22 +49,17 @@
}
.form-item-sub {
margin-bottom: 12px;
a {
color: $--color-text-regular;
text-decoration: none;
&:hover {
color: $--color-text-primary;
text-decoration: underline;
}
&:last-of-type {
margin-bottom: 0;
}
}
}
.form-actions {
position: absolute;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
left: auto;
z-index: 10;
box-sizing: border-box;
padding: 24px 36px;
padding: 24px 36px 24px 0;
}
</style>
+21 -15
View File
@@ -6,41 +6,43 @@
<el-main class="panel-content">
<el-form
class="form-preference"
ref="basicForm"
ref="labForm"
label-position="right"
size="mini"
:model="form"
:rules="rules">
<el-form-item :label-width="formLabelWidth">
<div class="el-form-item__error">启用实验特性可能造成应用崩溃或数据丢失请自行决定</div>
<div class="el-form-item__error">
{{ $t('preferences.lab-warning') }}
</div>
</el-form-item>
<el-form-item label="下载协议: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('preferences.download-protocol')}: `" :label-width="formLabelWidth">
<el-col class="form-item-sub" :span="24">
<el-switch
v-model="form.enableEggFeatures"
active-text="支持更多下载协议"
:active-text="$t('preferences.support-more-download-protocols')"
>
</el-switch>
</el-col>
</el-form-item>
<el-form-item label="浏览器扩展: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('preferences.browser-extensions')}: `" :label-width="formLabelWidth">
<el-col class="form-item-sub" :span="24">
<a target="_blank" href="https://motrix.app/release/BaiduExporter.zip" rel="noopener noreferrer">
百度网盘助手
<mo-icon name="link" width="14" height="14" />
{{ $t('preferences.baidu-exporter') }}
<mo-icon name="link" width="12" height="12" />
</a>
<div class="el-form-item__info" style="margin-top: 8px;">
社区提供的浏览器扩展不保证可用性
{{ $t('preferences.browser-extensions-tips') }}
<a target="_blank" href="https://motrix.app/extensions/baidu" rel="noopener noreferrer">
点此查看使用说明
{{ $t('preferences.baidu-exporter-help') }}
</a>
</div>
</el-col>
</el-form-item>
</el-form>
<div class="form-actions">
<el-button type="primary" @click="submitForm('basicForm')">保存并应用</el-button>
<el-button @click="resetForm('basicForm')">放弃</el-button>
<el-button type="primary" @click="submitForm('labForm')">{{ $t('preferences.save') }}</el-button>
<el-button @click="resetForm('labForm')">{{ $t('preferences.discard') }}</el-button>
</div>
</el-main>
</el-container>
@@ -50,6 +52,9 @@
import is from 'electron-is'
import { mapState } from 'vuex'
import '@/components/Icons/info-square'
import {
calcFormLabelWidth
} from '@shared/utils'
const initialForm = (config) => {
const {
@@ -66,15 +71,17 @@
components: {
},
data: function () {
const { locale } = this.$store.state.preference.config
const form = initialForm(this.$store.state.preference.config)
return {
formLabelWidth: '23%',
form: initialForm(this.$store.state.preference.config),
form,
formLabelWidth: calcFormLabelWidth(locale),
rules: {}
}
},
computed: {
title: function () {
return '实验室'
return this.$t('preferences.lab')
},
...mapState('preference', {
config: state => state.config
@@ -95,7 +102,6 @@
console.log('this.form===>', this.form)
this.$store.dispatch('preference/save', this.form)
if (this.isRenderer()) {
this.$electron.ipcRenderer.send('command', 'application:relaunch')
}
})
},
@@ -0,0 +1,107 @@
<template>
<div>
<ul class="theme-switcher">
<li
class="theme-item theme-item-auto"
:class="{ active: currentValue === 'auto' }"
@click.prevent="() => handleChange('auto')"
>
<div class="theme-thumb"></div>
<span>{{ $t('preferences.theme-auto') }}</span>
</li>
<li
class="theme-item theme-item-light"
:class="{ active: currentValue === 'light' }"
@click.prevent="() => handleChange('light')"
>
<div class="theme-thumb"></div>
<span>{{ $t('preferences.theme-light') }}</span>
</li>
<li
class="theme-item theme-item-dark"
:class="{ active: currentValue === 'dark' }"
@click.prevent="() => handleChange('dark')"
>
<div class="theme-thumb"></div>
<span>{{ $t('preferences.theme-dark') }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'mo-theme-switcher',
components: {
},
props: {
value: {
type: String,
default: 'auto'
}
},
data: function () {
return {
currentValue: this.value
}
},
watch: {
currentValue: function (val) {
this.$emit('change', val)
}
},
methods: {
handleChange (theme) {
this.currentValue = theme
}
}
}
</script>
<style lang="scss">
.theme-switcher {
padding: 0;
margin: 0;
font-size: 0;
line-height: 0;
.theme-item {
text-align: center;
display: inline-block;
margin: 0 16px 0 0;
cursor: pointer;
span {
font-size: 13px;
line-height: 20px;
}
&.active {
.theme-thumb {
border-color: $--color-primary;
box-shadow: 0 0 1px $--color-primary;
}
span {
color: $--color-primary;
}
}
&.theme-item-auto .theme-thumb {
background: url('~@/assets/theme-auto@2x.png') center center no-repeat;
background-size: 68px 44px;
}
&.theme-item-light .theme-thumb {
background: url('~@/assets/theme-light@2x.png') center center no-repeat;
background-size: 68px 44px;
}
&.theme-item-dark .theme-thumb {
background: url('~@/assets/theme-dark@2x.png') center center no-repeat;
background-size: 68px 44px;
}
}
.theme-thumb {
box-sizing: border-box;
border: 1px solid #aaa;
border-radius: 5px;
width: 68px;
height: 44px;
margin-bottom: 8px;
}
}
</style>
@@ -47,9 +47,6 @@
</script>
<style lang="scss">
@import '../Theme/Variables';
@import '../Theme/Darkness/Variables';
.mo-speedometer {
font-size: 12px;
position: relative;
@@ -144,12 +141,4 @@
color: $--speedometer-primary-color;
}
}
.darkness .mo-speedometer {
border: 1px solid $--dk-speedometer-border-color;
background: $--dk-speedometer-background;
&:hover {
border-color: $--dk-speedometer-hover-border-color;
}
}
</style>
-73
View File
@@ -1,73 +0,0 @@
<template>
<el-aside width="200px" class="subnav">
<router-view name="subnav"></router-view>
</el-aside>
</template>
<script>
// import { mapState, mapGetters } from 'vuex'
export default {
name: 'mo-subnav',
components: {
},
computed: {
// ...mapState('app', {
// currentPage: state => state.currentPage
// }),
// ...mapGetters('app', {
// title: 'currentPageTitle'
// })
},
methods: {
}
}
</script>
<style lang="scss">
.subnav-inner {
margin-top: 44px;
padding: 0 16px;
h3 {
font-size: 16px;
color: #071D43;
font-weight: normal;
line-height: 24px;
margin: 0 0 28px;
}
ul {
list-style: none;
padding: 0;
margin: 0;
user-select: none;
cursor: default;
li {
margin-bottom: 8px;
padding: 8px 10px;
font-size: 14px;
line-height: 20px;
border-radius: 3px;
cursor: pointer;
i, span {
vertical-align: middle;
display: inline-block;
}
&:hover, &.active {
background-color: #EAECF0;
i, span, svg {
color: $--subnav-active-text-color;
}
}
}
}
}
.subnav-icon {
padding: 2px;
height: 16px;
margin-right: 12px;
svg {
width: 16px;
height: 16px;
}
}
</style>
@@ -4,37 +4,36 @@
<ul>
<li
@click="() => nav('basic')"
v-bind:class="[ current === 'basic' ? 'active' : '' ]"
:class="[ current === 'basic' ? 'active' : '' ]"
>
<i class="subnav-icon">
<mo-icon name='preference-basic' width="20" height="20" />
</i>
<span>基础设置</span>
<span>{{ $t('preferences.basic') }}</span>
</li>
<li
@click="() => nav('advanced')"
v-bind:class="[ current === 'advanced' ? 'active' : '' ]"
:class="[ current === 'advanced' ? 'active' : '' ]"
>
<i class="subnav-icon">
<mo-icon name='preference-advanced' width="20" height="20" />
</i>
<span>进阶设置</span>
<span>{{ $t('preferences.advanced') }}</span>
</li>
<li
@click="() => nav('lab')"
v-bind:class="[ current === 'lab' ? 'active' : '' ]"
:class="[ current === 'lab' ? 'active' : '' ]"
>
<i class="subnav-icon">
<mo-icon name='preference-lab' width="20" height="20" />
</i>
<span>实验室</span>
<span>{{ $t('preferences.lab') }}</span>
</li>
</ul>
</nav>
</template>
<script>
import is from 'electron-is'
import '@/components/Icons/preference-basic'
import '@/components/Icons/preference-advanced'
import '@/components/Icons/preference-lab'
@@ -49,13 +48,11 @@
},
computed: {
title: function () {
return '偏好设置'
return this.$t('subnav.preferences')
}
},
methods: {
isMas: is.mas,
nav: function (category = 'basic') {
console.log('nav category===>', category)
this.$router.push({
path: `/preference/${category}`
})
@@ -4,30 +4,30 @@
<ul>
<li
@click="() => nav('active')"
v-bind:class="[ current === 'active' ? 'active' : '' ]"
:class="[ current === 'active' ? 'active' : '' ]"
>
<i class="subnav-icon">
<mo-icon name="task-start" width="20" height="20" />
</i>
<span>下载中</span>
<span>{{ $t('task.active') }}</span>
</li>
<li
@click="() => nav('waiting')"
v-bind:class="[ current === 'waiting' ? 'active' : '' ]"
:class="[ current === 'waiting' ? 'active' : '' ]"
>
<i class="subnav-icon">
<mo-icon name="task-pause" width="20" height="20" />
</i>
<span>已暂停</span>
<span>{{ $t('task.waiting') }}</span>
</li>
<li
@click="() => nav('stopped')"
v-bind:class="[ current === 'stopped' ? 'active' : '' ]"
:class="[ current === 'stopped' ? 'active' : '' ]"
>
<i class="subnav-icon">
<mo-icon name="task-stop" width="20" height="20" />
</i>
<span>已完成</span>
<span>{{ $t('task.stopped') }}</span>
</li>
</ul>
</nav>
@@ -48,12 +48,11 @@
},
computed: {
title: function () {
return '任务列表'
return this.$t('subnav.task-list')
}
},
methods: {
nav: function (status = 'active') {
console.log('nav status===>', status)
this.$router.push({
path: `/task/${status}`
})
+116 -95
View File
@@ -5,6 +5,7 @@
:visible.sync="visible"
:before-close="handleClose"
@open="handleOpen"
@opened="handleOpened"
@closed="handleClosed">
<el-form
ref="taskForm"
@@ -12,22 +13,23 @@
:model="form"
:rules="rules">
<el-tabs :value="type" @tab-click="handleTabClick">
<el-tab-pane label="链接任务" name="uri">
<el-tab-pane :label="$t('task.uri-task')" name="uri">
<el-form-item>
<el-input
ref="uri"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
auto-complete="off"
placeholder="添加多个下载链接时,请确保每行只有一个链接"
:placeholder="$t('task.uri-task-tips')"
@change="handleUriChange"
@paste.native="handleUriPaste"
v-model="form.uris">
</el-input>
</el-form-item>
</el-tab-pane>
<el-tab-pane
label="种子任务"
:label="$t('task.torrent-task')"
name="torrent"
v-if="iLoveEggFeatures"
>
<el-form-item>
<mo-select-torrent
@@ -38,13 +40,13 @@
</el-tabs>
<el-row :gutter="12">
<el-col :span="15">
<el-form-item label="文件名: " :label-width="formLabelWidth">
<el-input placeholder="请输入文件名" v-model="form.out">
<el-form-item :label="`${$t('task.task-out')}: `" :label-width="formLabelWidth">
<el-input :placeholder="$t('task.task-out-tips')" v-model="form.out">
</el-input>
</el-form-item>
</el-col>
<el-col :span="9">
<el-form-item label="下载线程: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('task.task-split')}: `" :label-width="formLabelWidth">
<el-input-number
v-model="form.split"
@change="handleSplitChange"
@@ -52,12 +54,12 @@
:min="1"
:max="config.maxConnectionPerServer"
:value="config.split"
label="下载线程">
:label="$t('task.task-split')">
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="存储路径: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('task.task-dir')}: `" :label-width="formLabelWidth">
<el-input placeholder="" v-model="downloadDir" :readonly="isMas()">
<mo-select-directory
v-if="isRenderer()"
@@ -67,42 +69,42 @@
</el-input>
</el-form-item>
<div v-if="showAdvanced">
<el-form-item label="UA: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('task.task-user-agent')}: `" :label-width="formLabelWidth">
<el-input
type="textarea"
:autosize="{ minRows: 2, maxRows: 3 }"
auto-complete="off"
placeholder="User-Agent"
:placeholder="$t('task.task-user-agent')"
v-model="form.userAgent">
</el-input>
</el-form-item>
<el-form-item label="Referer: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('task.task-referer')}: `" :label-width="formLabelWidth">
<el-input
type="textarea"
:autosize="{ minRows: 2, maxRows: 3 }"
auto-complete="off"
placeholder="Referer"
:placeholder="$t('task.task-referer')"
v-model="form.referer">
</el-input>
</el-form-item>
<el-form-item label="Cookie: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('task.task-cookie')}: `" :label-width="formLabelWidth">
<el-input
type="textarea"
:autosize="{ minRows: 2, maxRows: 3 }"
auto-complete="off"
placeholder="Cookie"
:placeholder="$t('task.task-cookie')"
v-model="form.cookie">
</el-input>
</el-form-item>
<el-form-item label="" :label-width="formLabelWidth">
<el-checkbox class="chk" v-model="form.newTaskShowDownloading">跳转到下载页面</el-checkbox>
<el-checkbox class="chk" v-model="form.newTaskShowDownloading">{{$t('task.navigate-to-downloading')}}</el-checkbox>
</el-form-item>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-checkbox class="chk" v-model="showAdvanced">显示高级选项</el-checkbox>
<el-button @click="handleCancel('taskForm')"> </el-button>
<el-button type="primary" @click="submitForm('taskForm')"> </el-button>
<el-checkbox class="chk" v-model="showAdvanced">{{$t('task.show-advanced-options')}}</el-checkbox>
<el-button @click="handleCancel('taskForm')">{{$t('app.cancel')}}</el-button>
<el-button type="primary" @click="submitForm('taskForm')">{{$t('app.submit')}}</el-button>
</div>
</el-dialog>
</template>
@@ -110,25 +112,35 @@
<script>
import is from 'electron-is'
import { mapState } from 'vuex'
import { isEmpty, compact } from 'lodash'
import { isEmpty } from 'lodash'
import SelectDirectory from '@/components/Native/SelectDirectory'
import SelectTorrent from '@/components/Task/SelectTorrent'
import { prettifyDir } from '@/components/Native/utils'
import {
NONE_SELECTED_FILES,
SELECTED_ALL_FILES
} from '@shared/constants'
import {
detectResource,
splitTaskLinks
} from '@shared/utils'
import '@/components/Icons/inbox'
import { splitTextRows, needCheckCopyright } from '@shared/utils'
const initialForm = (state) => {
const { addTaskUrl, addTaskOptions } = state.app
const { dir, split, newTaskShowDownloading } = state.preference.config
const result = {
uris: '',
uris: addTaskUrl,
torrent: '',
selectFile: NONE_SELECTED_FILES,
out: '',
userAgent: '',
referer: '',
cookie: '',
dir,
split,
newTaskShowDownloading
newTaskShowDownloading,
...addTaskOptions
}
return result
}
@@ -151,12 +163,10 @@
},
data () {
return {
formLabelWidth: '75px',
formLabelWidth: '100px',
showAdvanced: false,
torrentName: '',
form: {},
rules: {
}
rules: {}
}
},
computed: {
@@ -171,10 +181,7 @@
}),
...mapState('preference', {
config: state => state.config
}),
iLoveEggFeatures: function () {
return !this.isMas() || (this.isMas() && this.config.enableEggFeatures)
}
})
},
watch: {
taskType: function (current, previous) {
@@ -195,25 +202,56 @@
handleOpen () {
this.form = initialForm(this.$store.state)
if (this.taskType === 'uri') {
this.autofillResourceLink()
setTimeout(() => {
this.$refs.uri && this.$refs.uri.focus()
}, 50)
}
},
autofillResourceLink () {
const content = this.$electron.clipboard.readText()
const hasResource = detectResource(content)
if (!hasResource) {
return
}
if (isEmpty(this.form.uris)) {
this.form.uris = content
}
},
handleOpened () {
this.detectThunderResource(this.form.uris)
},
handleClose (done) {
this.$store.dispatch('app/hideAddTaskDialog')
this.$store.dispatch('app/updateAddTaskOptions', {})
},
handleClosed () {
this.reset()
},
handleTabClick (tab, event) {
console.log(tab, tab.name, event)
this.$store.dispatch('app/changeAddTaskType', tab.name)
},
handleTorrentChange (torrent, file, fileList) {
// TODO 种子选择部分文件下载
// console.log('handleTorrentChange===>', torrent, file, fileList)
handleUriPaste () {
setImmediate(() => {
const uris = this.$refs.uri.value
this.detectThunderResource(uris)
})
},
detectThunderResource (uris = '') {
if (uris.includes('thunder://')) {
this.$msg({
type: 'warning',
message: this.$t('task.thunder-link-tips'),
duration: 6000
})
}
},
handleUriChange () {
console.log('handleUriChange===>', this.form.uris)
},
handleTorrentChange (torrent, selectedFileIndex) {
this.form.torrent = torrent
this.form.selectFile = selectedFileIndex
},
handleSplitChange (value) {
console.log('handleSplitChange===>', value)
@@ -222,6 +260,7 @@
this.form.dir = dir
},
reset () {
this.showAdvanced = false
this.form = initialForm(this.$store.state)
},
handleCancel (formName) {
@@ -244,9 +283,12 @@
}
return result
},
buildOption (form) {
buildOption (type, form) {
const {
dir, out, split
dir,
out,
selectFile,
split
} = form
const result = {}
@@ -258,6 +300,15 @@
result.out = out
}
if (type === 'torrent') {
if (
selectFile !== SELECTED_ALL_FILES &&
selectFile !== NONE_SELECTED_FILES
) {
result.selectFile = selectFile
}
}
if (split > 0) {
result.split = split
}
@@ -270,8 +321,11 @@
},
buildUriPayload (form) {
let { uris } = form
uris = compact(splitTextRows(uris))
const options = this.buildOption(form)
if (isEmpty(uris)) {
throw new Error(this.$t('task.new-task-uris-required'))
}
uris = splitTaskLinks(uris)
const options = this.buildOption('uri', form)
const result = {
uris,
options
@@ -280,12 +334,14 @@
},
buildTorrentPayload (form) {
const { torrent } = form
const options = this.buildOption(form)
if (isEmpty(torrent)) {
throw new Error(this.$t('task.new-task-torrent-required'))
}
const options = this.buildOption('torrent', form)
const result = {
torrent,
options
}
console.log('buildTorrentPayload===>', result)
return result
},
addTask (type, form) {
@@ -294,13 +350,13 @@
payload = this.buildUriPayload(form)
this.$store.dispatch('task/addUri', payload)
.catch((err) => {
this.$message.error(err.message)
this.$msg.error(err.message)
})
} else if (type === 'torrent') {
payload = this.buildTorrentPayload(form)
this.$store.dispatch('task/addTorrent', payload)
.catch((err) => {
this.$message.error(err.message)
this.$msg.error(err.message)
})
} else if (type === 'metalink') {
// @TODO addMetalink
@@ -308,61 +364,24 @@
console.error('addTask fail', form)
}
},
checkCopyright (type, form) {
let { uris } = form
uris = compact(splitTextRows(uris))
return new Promise((resolve, reject) => {
if (type !== 'uri') {
resolve()
}
if (!needCheckCopyright(uris)) {
resolve()
return
}
if (this.iLoveEggFeatures) {
resolve()
return
}
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: '版权检查',
message: '您要下载的文件可能是有版权的音视频,请确保您有相应的版权方授权!',
buttons: ['是,我有版权方授权', '否'],
cancelId: 1
}, (buttonIndex, checkboxChecked) => {
// 点击的按钮是哪个按钮 0: 是, 1: 否
if (buttonIndex === 0) {
resolve()
} else {
reject(new Error('因版权问题,添加任务失败'))
}
})
})
},
submitForm (formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
return false
}
this.checkCopyright(this.type, this.form)
.then(() => {
this.addTask(this.type, this.form)
this.$store.dispatch('app/hideAddTaskDialog')
if (this.form.newTaskShowDownloading) {
this.$router.push({
path: '/task/active'
})
}
})
.catch((err) => {
this.$message.error(err.message)
// this.reset()
})
try {
this.addTask(this.type, this.form)
this.$store.dispatch('app/hideAddTaskDialog')
if (this.form.newTaskShowDownloading) {
this.$router.push({
path: '/task/active'
})
}
} catch (err) {
this.$msg.error(err.message)
}
})
}
}
@@ -370,14 +389,17 @@
</script>
<style lang="scss">
.add-task-dialog {
.el-dialog.add-task-dialog {
max-width: 632px;
.el-tabs__header {
user-select: none;
}
.el-input-number.el-input-number--mini {
width: 100%;
}
.el-dialog__footer {
padding-top: 20px;
background: #f5f5f5;
background-color: $--add-task-dialog-footer-background;
border-radius: 0 0 5px 5px;
}
.dialog-footer {
@@ -386,6 +408,5 @@
line-height: 28px;
}
}
}
</style>

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