Compare commits

...

213 Commits

Author SHA1 Message Date
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
Dr_rOot 6ea3112691 fix: update version to 1.0.10 2019-01-03 10:19:13 +08:00
Dr_rOot 8c63f97d8e fix: change win32 aria2c to 32bit 2019-01-03 10:12:21 +08:00
173 changed files with 10025 additions and 2203 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'
}),
+38
View File
@@ -0,0 +1,38 @@
---
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.**
**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.
+40
View File
@@ -0,0 +1,40 @@
---
name: 错误反馈
about: 创建一个错误报告帮助改进「请按照模板提交,提供详细的信息,方便我们复现之后处理」
title: ''
labels: ''
assignees: ''
---
**反馈之前**
反馈之前请搜索一下已有 issues 和 帮助文档,看是否有类似问题可以解决你的问题
https://github.com/agalwood/Motrix/issues
http://motrix.app/support
**请删除上面和本行的内容,然后按以下格式填写反馈信息,谢谢**
**错误描述**
清楚简洁地描述错误,方便我们复现之后处理。
**如何重现**
重现步骤,如:
1. 点击新建任务按钮
2. 黏贴链接(如链接不涉及到隐私和版权问题,请顺便提供)
3. 点击提交
4. 发现报错
**预期的行为**
清楚简洁地描述您期望发生的事情。
**截图**
请添加屏幕截图以帮助解释您的问题:
打开应用菜单中的「帮助」——「开发者工具」—— 切换到 console,然后**完整**截图。
**运行环境**
- 操作系统类型: [如 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.
+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
+37
View File
@@ -0,0 +1,37 @@
# 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` 中引入,如
```
import eleLocaleEn from 'element-ui/lib/locale/lang/en'
import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN'
```
### 应用菜单
应用菜单的国际化文件按照语言进行目录划分,每个目录里有三大操作系统对应的 JSON 文件:
- darwin.json
- linux.json
- win32.json
### 主界面
主界面和 Element UI 都是用 i18next 作为翻译支持库,所以你可能需要简单了解一下它的[使用方法](https://www.i18next.com/overview/getting-started)。
主界面的配置同样按照语言划分目录:`src/shared/locales`,如:`src/shared/locales/en-US``src/shared/locales/zh-CN`
目录里面有按业务模块划分的语言文件:
- about.js
- app.js
- edit.js
- help.js
- index.js
- menu.js
- preferences.js
- subnav.js
- task.js
- window.js
+37
View File
@@ -0,0 +1,37 @@
# 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 three parts:
- Element UI
- Application 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
```
import eleLocaleEn from 'element-ui/lib/locale/lang/en'
import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN'
```
### Application Menu
The internationalization files of the application menu are divided into directories according to the **locale**. Each directory has three JSON files corresponding to the OS:
- darwin.json
- linux.json
- win32.json
### Main Interface
Both the main interface and the Element UI use [i18next](https://www.i18next.com/overview/getting-started) as the translation support library, so you may need to take a brief look at how to use it.
The configuration of the main interface is also divided into directories according to the **locale**: `src/shared/locales`, such as: `src/shared/locales/en-US` and `src/shared/locales/zh-CN`.
There are language files in the directory divided by business modules:
- about.js
- app.js
- edit.js
- help.js
- index.js
- menu.js
- preferences.js
- subnav.js
- task.js
- window.js
+99
View File
@@ -0,0 +1,99 @@
# 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) | 简体中文
## 一款全能的下载工具
[![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) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
我是个兴趣使然的桌面应用开发者🤓,利用搬砖之余开发了 Motrix。
Motirx 是一款全能的下载工具,支持下载 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) 提供了已经编译好的稳定版安装包,当然你也可以自己克隆代码编译打包。
更新:macOS 用户支持 `brew cask` 安装,感谢 [Mitscherlich](https://github.com/Mitscherlich) 的 [PR](https://github.com/Homebrew/homebrew-cask/pull/59494)。
```bash
brew update && brew cask install 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-square)](http://makeapullrequest.com)
如果你有兴趣参与共同开发,欢迎 FORK 和 PR。
## 🌍 国际化
欢迎大家将 Motrix 翻译成更多的语言版本 🧐,开工之前请先阅读一下 [翻译指南](./CONTRIBUTING-CN.md#-翻译指南)。
| Key | Name | Status |
|-------|:--------------------|:-------------|
| de | German | 下版本发布 [@Schloemicher](https://github.com/Schloemicher) |
| en-US | English | ✔️ |
| fr | Français | 下版本发布 [@gpatarin](https://github.com/gpatarin) |
| 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) 许可进行开源。
+60 -32
View File
@@ -1,65 +1,93 @@
# 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.
[![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) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
English | [简体中文](./README-CN.md)
支持下载 HTTPFTP、BT、磁力链、百度网盘等资源
Motrix is a full-featured download manager that supports downloading HTTP, FTP, BitTorrent, Magnet, Baidu Net Disk, etc.
<span style="font-size: 30px">我</span>是个兴趣使然的桌面应用开发者🤓,出于兴趣爱好,利用搬砖之余开发了 [MO 1.0](https://moapp.me) 版本,做出来有大半年了,没做过什么推广,所以大家可能都没怎么听过这个应用吧~👻~
Motrix has a clean and easy to use interface. I hope you will like it 👻.
本着自己用得舒(折)服(腾)😌的想法,🤠撸出了个全新的版本,并更名为 Motrix。新版本不仅优化了性能,还重新设计了图形操作界面,操作更简便!
✈️ [Official Website](https://motrix.app) | 📖 [Manual](http://motrix.app/support/issues) (zh-CN)
官网提供了已经编译好的应用安装包([去官网下载](https://motrix.app)),当然你也可以自己克隆代码进行编译打包。
## 💽 Installation
Download from [GitHub Releases](https://github.com/agalwood/Motrix/releases) and install it.
## 🛠 技术栈
- [Electron](https://electronjs.org/)
- [Vue](https://vuejs.org/) + [VueX](https://vuex.vuejs.org/) + [Element](https://element.eleme.io)
- [Aria2](https://aria2.github.io/)
Update: macOS user support `brew cask` installation, thanks to [PR](https://github.com/Homebrew/homebrew-cask/pull/59494) of [Mitscherlich](https://github.com/Mitscherlich).
## 📦 自行编译
```bash
brew update && brew cask install 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)
- 🗑 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 版本理论上会有,还未调试
- [ ] 测试用例
Development Roadmap see: [Trello](https://trello.com/b/qNUzA0bv/motrix)
## 🤝 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
如果你有兴趣参与共同开发,欢迎 FORK 和 PR。
## 🤝 Contribute [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
If you are interested in participating in joint development, PR and Forks are welcome!
## 📜 开源许可
基于 [MIT license](https://opensource.org/licenses/MIT) 许可进行开源。
## 🌍 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 | Next Release [@Schloemicher](https://github.com/Schloemicher) |
| en-US | English | ✔️ |
| fr | Français | Next Release [@gpatarin](https://github.com/gpatarin) |
| pt-BR | Portuguese (Brazil) | Next Release [@andrenoberto](https://github.com/andrenoberto) |
| tr | Türkçe | Next Release [@abdullah](https://github.com/abdullah) |
| zh-CN | 简体中文 | ✔️ |
| zh-TW | 繁體中文 | Next Release [@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

+5 -5
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
@@ -77,22 +77,22 @@ listen-port=51413
# 单个种子最大连接数, 默认:55
#bt-max-peers=55
# 打开DHT功能, PT需要禁用, 默认:true
enable-dht=false
# enable-dht=false
# 打开IPv6 DHT功能, PT需要禁用
#enable-dht6=false
# DHT网络监听端口, 默认:6881-6999
#dht-listen-port=6881-6999
# 本地节点查找, 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
+5 -5
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
@@ -77,22 +77,22 @@ listen-port=51413
# 单个种子最大连接数, 默认:55
#bt-max-peers=55
# 打开DHT功能, PT需要禁用, 默认:true
enable-dht=false
# enable-dht=false
# 打开IPv6 DHT功能, PT需要禁用
#enable-dht6=false
# DHT网络监听端口, 默认:6881-6999
#dht-listen-port=6881-6999
# 本地节点查找, 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
+5 -5
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
@@ -77,22 +77,22 @@ listen-port=51413
# 单个种子最大连接数, 默认:55
#bt-max-peers=55
# 打开DHT功能, PT需要禁用, 默认:true
enable-dht=false
# enable-dht=false
# 打开IPv6 DHT功能, PT需要禁用
#enable-dht6=false
# DHT网络监听端口, 默认:6881-6999
#dht-listen-port=6881-6999
# 本地节点查找, 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
Binary file not shown.
+6103 -1015
View File
File diff suppressed because it is too large Load Diff
+63 -30
View File
@@ -1,6 +1,6 @@
{
"name": "Motrix",
"version": "1.0.9",
"version": "1.2.2",
"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"
@@ -88,8 +96,27 @@
},
"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 +131,10 @@
"allowToChangeInstallationDirectory": true
},
"linux": {
"category": "Network",
"target": [
"deb",
"snap",
"AppImage"
],
"extraResources": {
@@ -127,31 +156,34 @@
]
},
"dependencies": {
"@panter/vue-i18next": "^0.15.0",
"aria2": "^4.0.3",
"axios": "^0.18.0",
"blob-util": "^2.0.2",
"clipboard-polyfill": "^2.7.0",
"electron-debug": "^2.0.0",
"electron-debug": "^2.1.0",
"electron-is": "^3.0.0",
"electron-log": "^2.2.17",
"electron-updater": "^4.0.7",
"element-ui": "^2.4.11",
"electron-updater": "^4.0.8",
"element-ui": "^2.6.2",
"forever-monitor": "^1.7.1",
"i18next": "^15.0.6",
"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",
"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.5.1",
"@vue/cli-plugin-eslint": "^3.5.1",
"@vue/cli-service": "^3.5.1",
"@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",
@@ -161,29 +193,30 @@
"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.1",
"cross-env": "^5.1.6",
"css-loader": "^1.0.1",
"del": "^3.0.0",
"css-loader": "^2.1.1",
"del": "^4.0.0",
"devtron": "^1.4.0",
"electron": "^4.0.0",
"electron-builder": "^20.38.4",
"electron": "^4.1.1",
"electron-builder": "^20.38.5",
"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.15.3",
"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-import": "^2.16.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.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",
"multispinner": "^0.2.1",
@@ -194,13 +227,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.29.6",
"webpack-cli": "^3.2.3",
"webpack-dev-server": "^3.2.1",
"webpack-hot-middleware": "^2.24.3",
"webpack-merge": "^4.1.4"
"webpack-merge": "^4.2.1"
}
}
+111 -20
View File
@@ -1,9 +1,12 @@
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 UpdateManager from './core/UpdateManager'
import EnergyManager from './core/EnergyManager'
@@ -11,19 +14,25 @@ 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'
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.windowManager = new WindowManager({
userConfig: this.configManager.getUserConfig()
})
this.engine = new Engine({
systemConfig: this.configManager.getSystemConfig(),
@@ -36,6 +45,8 @@ export default class Application extends EventEmitter {
this.touchBarManager = new TouchBarManager()
this.trayManager = new TrayManager()
this.energyManager = new EnergyManager()
this.initUpdaterManager()
@@ -50,12 +61,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()
@@ -70,9 +80,29 @@ export default class Application extends EventEmitter {
showPage (page) {
const win = this.windowManager.openWindow(page)
win.once('ready-to-show', () => {
this.isReady = true
this.emit('ready')
})
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) {
this.windowManager.destroyWindow(page)
}
@@ -93,31 +123,56 @@ 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)
})
}
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 () {
@@ -180,8 +235,12 @@ export default class Application extends EventEmitter {
app.exit()
})
this.on('application:show', (page = 'index') => {
this.showPage(page)
this.on('application:show', (page) => {
this.show(page)
})
this.on('application:hide', (page) => {
this.hide(page)
})
this.on('application:reset', () => {
@@ -193,8 +252,36 @@ export default class Application extends EventEmitter {
this.updateManager.check()
})
this.on('application:set-locale', (locale) => {
this.menuManager.setup(locale)
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 +314,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)
})
}
}
+140
View File
@@ -0,0 +1,140 @@
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 { parseArgv } 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) => {
global.application.showPage('index')
if (!is.macOS() && argv.length > 1) { // Windows, Linux
this.file = parseArgv(argv)
this.sendFileToApplication()
}
})
callback()
}
}
init () {
this.exceptionHandler = new ExceptionHandler()
this.handleAppEvents()
}
handleAppEvents () {
this.handleOpenUrl()
this.handleOpenFile()
this.handelAppReady()
this.handleAppWillQuit()
}
/**
* handleOpenUrl
* "name": "Motrix Protocol",
* "schemes": ["mo", "motrix"]
*/
handleOpenUrl () {
if (is.mas()) {
return
}
app.on('open-url', (event, url) => {
logger.info(`[Motrix] open-url path: ${url}`)
event.preventDefault()
this.url = url
if (this.url && global.application && global.application.isReady) {
global.application.handleProtocol(this.url)
this.url = EMPTY_STRING
}
})
}
/**
* handleOpenFile [WIP]
* handle open torrent file
*/
handleOpenFile () {
// macOS
if (is.macOS()) {
app.on('open-file', (event, path) => {
logger.info(`[Motrix] open-file path: ${path}`)
event.preventDefault()
this.file = path
this.sendFileToApplication()
})
} else if (process.argv.length > 1) { // Windows, Linux
this.file = parseArgv(process.argv)
this.sendFileToApplication()
}
}
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()
global.application.start('index')
global.application.on('ready', () => {
if (this.url) {
global.application.handleProtocol(this.url)
}
if (this.file) {
global.application.handleFile(this.file)
}
})
})
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`
}
}
+9
View File
@@ -20,6 +20,10 @@ export default class ConfigManager {
this.initUserConfig()
}
/**
* some aria2 conf
* https://aria2.github.io/manual/en/html/aria2c.html
*/
initSystemConfig () {
this.systemConfig = new Store({
name: 'system',
@@ -52,6 +56,7 @@ export default class ConfigManager {
defaults: {
'resume-all-when-app-launched': false,
'task-notification': true,
'hide-app-menu': is.windows() || is.linux(),
'new-task-show-downloading': true,
'auto-check-for-updates': false,
'update-channel': 'latest',
@@ -82,6 +87,10 @@ export default class ConfigManager {
return this.userConfig.get(key, defaultValue)
}
getLocale () {
return this.getUserConfig('locale') || app.getLocale()
}
setSystemConfig (...args) {
this.systemConfig.set(...args)
}
+27 -8
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,29 @@ 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}`)
// })
}
stop () {
@@ -87,7 +107,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)
+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)
}
})
}
+1
View File
@@ -3,5 +3,6 @@ import logger from 'electron-log'
logger.transports.file.level = is.production() ? 'warn' : 'info'
logger.info('Logger init')
logger.warn('[Motrix] Logger init')
export default logger
+1 -1
View File
@@ -37,6 +37,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)
}
}
+12 -10
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,6 +14,7 @@ export default class UpdateManager extends EventEmitter {
constructor (options = {}) {
super()
this.options = options
this.i18n = getI18n()
this.updater = autoUpdater
this.updater.autoDownload = false
@@ -48,9 +50,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) {
@@ -62,8 +64,8 @@ export default class UpdateManager extends EventEmitter {
updateNotAvailable (event, info) {
this.emit('update-not-available', info)
dialog.showMessageBox({
title: '没有更新的版本',
message: '您目前使用的已是最新版本'
title: this.i18n.t('app.check-for-updates-title'),
message: this.i18n.t('app.update-not-available-message')
})
}
@@ -84,8 +86,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 +98,8 @@ 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) {
+95 -1
View File
@@ -1,3 +1,97 @@
export default class TrayManager {
import { EventEmitter } from 'events'
import { join } from 'path'
import { Tray, Menu } from 'electron'
import is from 'electron-is'
import { translateTemplate } from '../utils/menu'
import { getI18n } from '@/ui/Locale'
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`)
if (is.macOS()) {
this.normalIcon = join(__static, './mo-tray-normal.png')
this.activeIcon = join(__static, './mo-tray-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) {
const icon = status ? this.activeIcon : this.normalIcon
tray.setImage(icon)
}
}
+78 -8
View File
@@ -1,6 +1,9 @@
import { join } from 'path'
import { EventEmitter } from 'events'
import { app, shell, BrowserWindow } from 'electron'
import is from 'electron-is'
import pageConfig from '../configs/page'
import logger from '../core/Logger'
const defaultBrowserOptions = {
titleBarStyle: 'hiddenInset',
@@ -13,23 +16,40 @@ const defaultBrowserOptions = {
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
}
getPageOptions (page) {
const result = pageConfig[page] || {}
const hideAppMenu = this.userConfig['hide-app-menu']
if (hideAppMenu) {
result.attrs.frame = false
}
// 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
}
openWindow (page) {
const options = pageConfig[page] || {}
const options = this.getPageOptions(page)
let window = this.windows[page] || null
if (window) {
window.restore()
@@ -55,7 +75,7 @@ export default class WindowManager extends EventEmitter {
window.show()
})
if (options.bindCloseToHide && process.platform === 'darwin') {
if (options.bindCloseToHide) {
this.bindCloseToHide(page, window)
}
@@ -70,7 +90,11 @@ export default class WindowManager extends EventEmitter {
}
getWindows () {
return this.windows
return this.windows || {}
}
getWindowList () {
return Object.values(this.getWindows())
}
addWindow (page, window) {
@@ -102,15 +126,61 @@ export default class WindowManager extends EventEmitter {
})
}
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)
}
+19 -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'
@@ -44,17 +45,33 @@ 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 isDirectory (path) {
return existsSync(path) && lstatSync(path).isDirectory()
}
export function parseArgv (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) {
+17 -5
View File
@@ -111,6 +111,15 @@ export default class Api {
return this.client.call('getVersion')
}
getGlobalOption () {
return new Promise((resolve) => {
this.client.call('getGlobalOption')
.then((data) => {
resolve(changeKeysToCamelCase(data))
})
})
}
getGlobalStat () {
return this.client.call('getGlobalStat')
}
@@ -120,8 +129,11 @@ export default class Api {
uris,
options
} = params
const args = compactUndefined([uris, options])
return this.client.call('addUri', ...args)
const tasks = uris.map((uri) => {
const args = compactUndefined([[uri], options])
return [ 'aria2.addUri', ...args ]
})
return this.client.multicall(tasks)
}
addTorrent (params) {
@@ -143,7 +155,7 @@ export default class Api {
}
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 +174,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)
}
+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

@@ -31,7 +31,6 @@
},
data () {
const version = this.$electron.remote.app.getVersion()
console.log('version===>', version)
return {
version
}
+1 -2
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"
+7 -6
View File
@@ -1,16 +1,19 @@
<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/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,7 +25,6 @@
}
</script>
<style lang="scss">
.copyright {
width: 100%;
@@ -45,5 +47,4 @@
margin-left: 30px;
}
}
</style>
+8 -11
View File
@@ -1,5 +1,5 @@
<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">
@@ -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,11 +39,12 @@
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)
},
@@ -51,21 +52,17 @@
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>
+25 -5
View File
@@ -1,8 +1,13 @@
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 showAboutPanel () {
store.dispatch('app/showAboutPanel')
@@ -12,6 +17,16 @@ function showAddTask (taskType = 'uri') {
store.dispatch('app/showAddTaskDialog', taskType)
}
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 +35,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 () {
@@ -51,6 +70,7 @@ function resumeAllTask () {
commands.register('application:about', showAboutPanel)
commands.register('application:new-task', showAddTask)
commands.register('application:new-bt-task', showAddTask)
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>
@@ -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
}
+4 -1
View File
@@ -6,6 +6,7 @@
<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>
@@ -17,6 +18,7 @@
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',
@@ -26,7 +28,8 @@
[Subnav.name]: Subnav,
[Speedometer.name]: Speedometer,
[AddTask.name]: AddTask,
[TaskItemInfo.name]: TaskItemInfo
[TaskItemInfo.name]: TaskItemInfo,
[Dragger.name]: Dragger
},
computed: {
...mapState('app', {
+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()
}
}
}
})
}
}
+53 -24
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,52 +42,70 @@
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 message = this.$t('task.download-error-message', { taskName })
this.$msg.error(message)
})
},
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)
})
@@ -88,7 +114,7 @@
console.log('aria2 onBtDownloadComplete')
this.$store.dispatch('task/fetchList')
const [{ gid }] = event
api.fetchTaskItem({ gid })
this.fetchTaskItem({ gid })
.then((task) => {
this.showTaskCompleteNotify(task)
})
@@ -104,15 +130,17 @@
addToRecentTask(task)
openDownloadDock(path)
const message = `${taskName} 下载完成`
this.$message.success(message)
const message = this.$t('task.download-complete-message', { taskName })
this.$msg.success(message)
/* eslint-disable no-new */
const notify = new Notification('下载完成', {
const notify = new Notification(this.$t('task.download-complete-notify'), {
body: taskName
})
notify.onclick = () => {
showItemInFolder(path)
showItemInFolder(path, {
errorMsg: this.$t('task.file-not-exist')
})
}
},
showTaskErrorNotify: function (task) {
@@ -122,17 +150,17 @@
const taskName = getTaskName(task)
const message = `${taskName} 下载失败`
this.$message.success(message)
const message = this.$t('task.download-fail-message', { taskName })
this.$msg.success(message)
/* 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 +168,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)
@@ -166,6 +194,7 @@
},
created: function () {
this.$store.dispatch('app/fetchEngineInfo')
this.$store.dispatch('app/fetchEngineOptions')
this.startPolling()
@@ -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,91 @@
<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">
<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;
&:hover {
background-color: $--titlebar-actions-active-background;
}
}
}
&:hover {
.window-actions {
opacity: 1;
}
}
}
</style>
+53 -13
View File
@@ -2,6 +2,7 @@ import is from 'electron-is'
import { existsSync } from 'fs'
import { Message } from 'element-ui'
import {
isMagnetTask,
getTaskFullPath,
bytesToSize
} from '@shared/utils'
@@ -14,31 +15,55 @@ 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('删除任务文件失败,请手动删除')
if (!deleteResult1 && delFailMsg) {
Message.error(delFailMsg)
}
let deleteResult2 = true
@@ -46,8 +71,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 +80,45 @@ 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()
}
+57 -23
View File
@@ -6,15 +6,35 @@
<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.ui')}: `" :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-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="`${$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 +48,9 @@
</el-input>
</el-col>
</el-form-item>
<el-form-item label="开发者: " :label-width="formLabelWidth">
<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 +65,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 +75,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 +85,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>
@@ -82,15 +104,21 @@
import { mapState } from 'vuex'
import ShowInFolder from '@/components/Native/ShowInFolder'
import userAgentMap from '@shared/ua'
import { availableLanguages, getLanguage } from '@shared/locales'
import { getLocaleManager } from '@/components/Locale'
const initialForm = (config) => {
const {
locale,
hideAppMenu,
useProxy,
allProxy,
allProxyBackup,
userAgent
} = config
const result = {
locale,
hideAppMenu,
useProxy,
allProxy,
allProxyBackup,
@@ -108,12 +136,17 @@
return {
formLabelWidth: '23%',
form: initialForm(this.$store.state.preference.config),
rules: {}
rules: {},
color: '#c00',
locales: availableLanguages
}
},
computed: {
title: function () {
return '进阶设置'
return this.$t('preferences.advanced')
},
showHideAppMenuOption: function () {
return is.windows() || is.linux()
},
...mapState('preference', {
config: state => state.config,
@@ -122,34 +155,35 @@
})
},
watch: {
},
methods: {
isRenderer: is.renderer,
handleLocaleChange (locale) {
const lng = getLanguage(locale)
getLocaleManager().changeLanguage(lng)
this.$electron.ipcRenderer.send('command', 'application:change-locale', lng)
},
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')
}
@@ -177,7 +211,7 @@
</script>
<style lang="scss">
.ua-group {
margin-top: 8px;
}
.ua-group {
margin-top: 8px;
}
</style>
+15 -19
View File
@@ -11,12 +11,12 @@
size="mini"
:model="form"
:rules="rules">
<el-form-item label="启动: " :label-width="formLabelWidth">
<el-form-item :label="`${$t('preferences.startup')}: `" :label-width="formLabelWidth">
<el-checkbox v-model="form.resumeAllWhenAppLaunched">
启动后自动开始未完成任务
{{ $t('preferences.auto-resume-all') }}
</el-checkbox>
</el-form-item>
<el-form-item label="默认下载路径: " :label-width="formLabelWidth">
<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 +25,50 @@
/>
</el-input>
<div class="el-form-item__info" v-if="isMas()" style="margin-top: 8px;">
App Store 的沙箱权限限制默认下载路径建议设置为您的下载目录
{{ $t('preferences.mas-default-dir-tip') }}
</div>
</el-form-item>
<el-form-item label="任务管理: " :label-width="formLabelWidth">
<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>
@@ -90,13 +90,9 @@
taskNotification,
newTaskShowDownloading
} = config
console.log('initialForm===>', dir, split)
const result = {
dir,
split,
userAgent: '',
referer: '',
cookie: '',
continue: config.continue,
resumeAllWhenAppLaunched,
maxConcurrentDownloads,
@@ -121,7 +117,7 @@
},
computed: {
title: function () {
return '基础设置'
return this.$t('preferences.basic')
},
downloadDir: function () {
return prettifyDir(this.form.dir)
+3 -11
View File
@@ -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: {
},
@@ -47,6 +36,9 @@
}
.form-item-sub {
margin-bottom: 12px;
&:last-of-type {
margin-bottom: 0;
}
a {
color: $--color-text-regular;
text-decoration: none;
+13 -11
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">
百度网盘助手
{{ $t('preferences.baidu-exporter') }}
<mo-icon name="link" width="14" height="14" />
</a>
<div class="el-form-item__info" style="margin-top: 8px;">
社区提供的浏览器扩展不保证可用性
{{ $t('preferences.browser-extensions-tip') }}
<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>
@@ -74,7 +76,7 @@
},
computed: {
title: function () {
return '实验室'
return this.$t('preferences.lab')
},
...mapState('preference', {
config: state => state.config
-53
View File
@@ -5,19 +5,11 @@
</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: {
}
@@ -25,49 +17,4 @@
</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}`
})
+57 -37
View File
@@ -12,20 +12,21 @@
: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-tip')"
@change="handleUriChange"
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"
>
@@ -38,13 +39,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-tip')" 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 +53,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 +68,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,12 +111,16 @@
<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 '@/components/Icons/inbox'
import { splitTextRows, needCheckCopyright } from '@shared/utils'
import {
detectResource,
splitTaskLinks,
needCheckCopyright
} from '@shared/utils'
const initialForm = (state) => {
const { dir, split, newTaskShowDownloading } = state.preference.config
@@ -151,12 +156,11 @@
},
data () {
return {
formLabelWidth: '75px',
formLabelWidth: '100px',
showAdvanced: false,
torrentName: '',
form: {},
rules: {
}
rules: {}
}
},
computed: {
@@ -195,11 +199,20 @@
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
}
this.form.uris = content
},
handleClose (done) {
this.$store.dispatch('app/hideAddTaskDialog')
},
@@ -207,9 +220,20 @@
this.reset()
},
handleTabClick (tab, event) {
console.log(tab, tab.name, event)
this.$store.dispatch('app/changeAddTaskType', tab.name)
},
handleUriChange () {
// el-input does not support @paste event ?
// https://github.com/ElemeFE/element/blob/master/packages/input/src/input.vue
const { uris } = this.form
if (uris.includes('thunder://')) {
this.$msg({
type: 'warning',
message: this.$t('task.thunder-link-tip'),
duration: 6000
})
}
},
handleTorrentChange (torrent, file, fileList) {
// TODO 种子选择部分文件下载
// console.log('handleTorrentChange===>', torrent, file, fileList)
@@ -270,7 +294,7 @@
},
buildUriPayload (form) {
let { uris } = form
uris = compact(splitTextRows(uris))
uris = splitTaskLinks(uris)
const options = this.buildOption(form)
const result = {
uris,
@@ -294,13 +318,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
@@ -309,8 +333,7 @@
}
},
checkCopyright (type, form) {
let { uris } = form
uris = compact(splitTextRows(uris))
const { uris } = form
return new Promise((resolve, reject) => {
if (type !== 'uri') {
@@ -329,16 +352,15 @@
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: '版权检查',
message: '您要下载的文件可能是有版权的音视频,请确保您有相应的版权方授权!',
buttons: ['是,我有版权方授权', '否'],
title: this.$t('task.copyright-warning'),
message: this.$t('task.copyright-warning-message'),
buttons: [this.$t('task.copyright-yes'), this.$t('task.copyright-no')],
cancelId: 1
}, (buttonIndex, checkboxChecked) => {
// 点击的按钮是哪个按钮 0: 是, 1: 否
if (buttonIndex === 0) {
resolve()
} else {
reject(new Error('因版权问题,添加任务失败'))
reject(new Error(this.$t('task.copyright-error-message')))
}
})
})
@@ -360,8 +382,7 @@
}
})
.catch((err) => {
this.$message.error(err.message)
// this.reset()
this.$msg.error(err.message)
})
})
}
@@ -386,6 +407,5 @@
line-height: 28px;
}
}
}
</style>
+3 -3
View File
@@ -36,9 +36,9 @@
computed: {
title: function () {
const titles = {
'active': '下载中',
'waiting': '已暂停',
'stopped': '已完成'
'active': this.$t('task.active'),
'waiting': this.$t('task.waiting'),
'stopped': this.$t('task.stopped')
}
return titles[this.status]
}
+24 -8
View File
@@ -3,13 +3,16 @@
class="upload-torrent"
drag
action="/"
:limit="1"
:multiple="false"
accept=".torrent"
:on-change="handleChange"
:on-exceed="handleExceed"
:auto-upload="false"
:show-file-list="false">
<i class="upload-inbox-icon"><mo-icon name="inbox" width="24" height="24" /></i>
<div class="el-upload__text">
将种子拖到此处<em>点击选择</em>
{{ $t('task.select-torrent') }}
<div class="torrent-name" v-if="name">{{ name }}</div>
</div>
</el-upload>
@@ -19,7 +22,7 @@
import { mapState } from 'vuex'
import parseTorrent from 'parse-torrent'
import '@/components/Icons/inbox'
import { getAsBase64 } from '@shared/utils'
import { getAsBase64, buildFileList } from '@shared/utils'
export default {
name: 'mo-select-torrent',
@@ -35,11 +38,18 @@
computed: {
...mapState('preference', {
config: state => state.config
}),
...mapState('app', {
torrents: state => state.addTaskTorrents
})
},
methods: {
handleChange (file, fileList) {
console.log('file===>', file)
watch: {
torrents (fileList) {
const file = fileList[0]
if (fileList.length === 0) {
this.name = ''
return
}
if (!file.raw) {
return
}
@@ -54,14 +64,20 @@
this.$emit('change', torrent, file, fileList)
})
}
},
methods: {
handleChange (file, fileList) {
this.$store.dispatch('app/addTaskAddTorrents', { fileList })
},
handleExceed (files) {
const fileList = buildFileList(files[0])
this.$store.dispatch('app/addTaskAddTorrents', { fileList })
}
}
}
</script>
<style lang="scss">
@import '../Theme/Variables';
@import '../Theme/Darkness/Variables';
.upload-torrent {
width: 100%;
.el-upload, .el-upload-dragger {
+11 -13
View File
@@ -1,21 +1,21 @@
<template>
<div class="task-actions">
<el-tooltip class="item" effect="dark" content="刷新列表" placement="bottom">
<el-tooltip class="item" effect="dark" :content="$t('task.refresh-list')" placement="bottom">
<i @click="onRefreshClick">
<mo-icon name="refresh" width="14" height="14" :spin="refreshing" />
</i>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="恢复所有任务" placement="bottom">
<el-tooltip class="item" effect="dark" :content="$t('task.resume-all-task')" placement="bottom">
<i @click="onResumeAllClick">
<mo-icon name="task-start-line" width="14" height="14" />
</i>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="暂停所有任务" placement="bottom">
<el-tooltip class="item" effect="dark" :content="$t('task.pause-all-task')" placement="bottom">
<i @click="onPauseAllClick">
<mo-icon name="task-pause-line" width="14" height="14" />
</i>
</el-tooltip>
<!-- <el-tooltip class="item" effect="dark" content="移除选中的任务" placement="bottom">
<!-- <el-tooltip class="item" effect="dark" :content="$t('task.delete-selected-tasks')" placement="bottom">
<i>
<mo-icon name="delete" width="14" height="14" />
</i>
@@ -23,7 +23,7 @@
<el-tooltip
class="item"
effect="dark"
content="清除已完成的任务"
:content="$t('task.purge-record')"
placement="bottom"
v-if="currentList === 'stopped'"
>
@@ -44,7 +44,6 @@
import '@/components/Icons/purge'
import '@/components/Icons/more'
import {
getTaskName,
bytesToSize,
timeFormat
} from '@shared/utils'
@@ -70,7 +69,6 @@
timeFormat
},
methods: {
getTaskName,
refreshSpin: function () {
this.t && clearTimeout(this.t)
@@ -86,33 +84,33 @@
onResumeAllClick: function () {
this.$store.dispatch('task/resumeAllTask')
.then(() => {
this.$message.success(`恢复全部任务成功`)
this.$msg.success(this.$t('task.resume-all-task-success'))
})
.catch(({ code }) => {
if (code === 1) {
this.$message.error(`恢复全部任务失败`)
this.$msg.error(this.$t('task.resume-all-task-fail'))
}
})
},
onPauseAllClick: function () {
this.$store.dispatch('task/pauseAllTask')
.then(() => {
this.$message.success(`暂停全部任务成功`)
this.$msg.success(this.$t('task.pause-all-task-success'))
})
.catch(({ code }) => {
if (code === 1) {
this.$message.error(`暂停全部任务失败`)
this.$msg.error(this.$t('task.pause-all-task-fail'))
}
})
},
onPurgeRecordClick: function () {
this.$store.dispatch('task/purgeTaskRecord')
.then(() => {
this.$message.success(`移除全部下载记录成功`)
this.$msg.success(this.$t('task.purge-record-success'))
})
.catch(({ code }) => {
if (code === 1) {
this.$message.error(`移除全部下载记录失败`)
this.$msg.error(this.$t('task.purge-record-fail'))
}
})
}
+43 -9
View File
@@ -1,7 +1,7 @@
<template>
<li :key="task.gid" class="task-item" v-on:dblclick="toggleTask">
<div class="task-name" :title="taskName">
<span>{{ taskName }}</span>
<li :key="task.gid" class="task-item" v-on:dblclick="onDbClick">
<div class="task-name" :title="taskFullName">
<span>{{ taskFullName }}</span>
</div>
<mo-task-item-actions mode="LIST" :task="task" />
<div class="task-progress">
@@ -17,7 +17,17 @@
<div v-if="task.status ==='active'">
<span>{{ task.downloadSpeed | bytesToSize }}/s</span>
<span>
{{ remaining | timeFormat('剩余') }}
{{
remaining | timeFormat({
prefix: $t('task.remaining-prefix'),
i18n: {
'gt1d': $t('app.gt1d'),
'hour': $t('app.hour'),
'minute': $t('app.minute'),
'second': $t('app.second')
}
})
}}
</span>
</div>
</el-col>
@@ -37,10 +47,14 @@
import '@/components/Icons/more'
import {
getTaskName,
getTaskFullPath,
timeRemaining,
bytesToSize,
timeFormat
} from '@shared/utils'
import {
openItem
} from '@/components/Native/utils'
export default {
name: 'mo-task-item',
@@ -54,8 +68,16 @@
}
},
computed: {
taskFullName: function () {
return getTaskName(this.task, {
defaultName: this.$t('task.get-task-name'),
maxLen: -1
})
},
taskName: function () {
return getTaskName(this.task, '获取任务名中...')
return getTaskName(this.task, {
defaultName: this.$t('task.get-task-name')
})
},
remaining: function () {
const { totalLength, completedLength, downloadSpeed } = this.task
@@ -67,12 +89,23 @@
timeFormat
},
methods: {
getTaskName,
toggleTask () {
onDbClick () {
const { status } = this.task
if (['waiting', 'paused'].indexOf(status) === -1) {
return
if (status === 'complete') {
this.openTask()
} else if (['waiting', 'paused'].includes(status) !== -1) {
this.toggleTask()
}
},
openTask () {
const { taskName } = this
this.$msg.info(this.$t('task.opening-task-message', { taskName }))
const fullPath = getTaskFullPath(this.task)
openItem(fullPath, {
errorMsg: this.$t('task.file-not-exist')
})
},
toggleTask () {
this.$store.dispatch('task/toggleTask', this.task)
}
}
@@ -100,6 +133,7 @@
color: #505753;
margin-bottom: 32px;
margin-right: 240px;
word-break: break-all;
&> span {
font-size: 14px;
line-height: 26px;
@@ -1,8 +1,8 @@
<template>
<div class="task-item-actions" v-on:dblclick.stop="() => null">
<i @click.stop="onMoreClick">
<!-- <i @click.stop="onMoreClick">
<mo-icon name="more" width="14" height="14" />
</i>
</i> -->
<i @click.stop="onInfoClick" v-if="mode === 'LIST'">
<mo-icon name="info-circle" width="14" height="14" />
</i>
@@ -35,7 +35,7 @@
<script>
import is from 'electron-is'
import clipboard from 'clipboard-polyfill'
import * as clipboard from 'clipboard-polyfill'
import '@/components/Icons/task-start-line'
import '@/components/Icons/task-pause-line'
import '@/components/Icons/delete'
@@ -74,24 +74,30 @@
return getTaskFullPath(this.task)
}
},
filters: {
},
methods: {
isRenderer: is.renderer,
showConfirmBox: function () {
deleteTaskFiles: function (task) {
moveTaskFilesToTrash(task, {
pathErrorMsg: this.$t('task.file-path-error'),
delFailMsg: this.$t('task.remove-task-file-fail'),
delConfigFailMsg: this.$t('task.remove-task-config-file-fail')
})
},
removeTaskItem: function (task, isRemoveWithFiles) {
this.$store.dispatch('task/removeTask', this.task)
.then(() => {
if (isRemoveWithFiles) {
moveTaskFilesToTrash(task)
this.deleteTaskFiles(task)
}
this.$message.success(`移除任务「${this.taskName}」成功`)
this.$msg.success(this.$t('task.delete-task-success', {
taskName: this.taskName
}))
})
.catch(({ code }) => {
if (code === 1) {
this.$message.error(`移除任务「${this.taskName}」失败`)
this.$msg.error(this.$t('task.delete-task-fail', {
taskName: this.taskName
}))
}
})
},
@@ -99,35 +105,37 @@
this.$store.dispatch('task/removeTaskRecord', this.task)
.then(() => {
if (isRemoveWithFiles) {
moveTaskFilesToTrash(task)
this.deleteTaskFiles(task)
}
this.$message.success(`移除「${this.taskName}」下载记录成功`)
this.$msg.success(this.$t('task.remove-record-success', {
taskName: this.taskName
}))
})
.catch(({ code }) => {
if (code === 1) {
this.$message.error(`移除「${this.taskName}」下载记录失败`)
this.$msg.error(this.$t('task.remove-record-fail', {
taskName: this.taskName
}))
}
})
},
onResumeClick: function () {
this.$store.dispatch('task/resumeTask', this.task)
.then(() => {
this.$message.success(`恢复任务「${this.taskName}」成功`)
})
.catch(({ code }) => {
if (code === 1) {
this.$message.error(`恢复任务「${this.taskName}」失败`)
this.$msg.error(this.$t('task.resume-task-fail', {
taskName: this.taskName
}))
}
})
},
onPauseClick: function () {
const { taskName } = this
this.$msg.info(this.$t('task.download-pause-message', { taskName }))
this.$store.dispatch('task/pauseTask', this.task)
.then(() => {
this.$message.success(`暂停任务「${this.taskName}」成功`)
})
.catch(({ code }) => {
if (code === 1) {
this.$message.error(`暂停任务「${this.taskName}」失败`)
this.$msg.error(this.$t('task.pause-task-fail', { taskName }))
}
})
},
@@ -136,13 +144,12 @@
const { task } = this
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: '移除任务',
message: `你确定要移除任务「${this.taskName}」吗?`,
buttons: ['是', '否'],
title: this.$t('task.delete-task'),
message: this.$t('task.delete-task-confirm', { taskName: this.taskName }),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1,
checkboxLabel: '同时删除文件'
checkboxLabel: this.$t('task.delete-task-label')
}, (buttonIndex, checkboxChecked) => {
// 点击的按钮是哪个按钮 0: 是, 1: 否
if (buttonIndex === 0) {
self.removeTaskItem(task, checkboxChecked)
}
@@ -153,26 +160,31 @@
const { task } = this
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: '移除下载记录',
message: `你确定要移除「${this.taskName}」下载记录吗?`,
buttons: ['是', '否'],
title: this.$t('task.remove-record'),
message: this.$t('task.remove-record-confirm', { taskName: this.taskName }),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1,
checkboxLabel: '同时删除文件'
checkboxLabel: this.$t('task.remove-record-label')
}, (buttonIndex, checkboxChecked) => {
// 点击的按钮是哪个按钮 0: 是, 1: 否
if (buttonIndex === 0) {
self.removeTaskRecord(task, checkboxChecked)
}
})
},
onFolderClick: function () {
showItemInFolder(this.path)
showItemInFolder(this.path, {
errorMsg: this.$t('task.file-not-exist')
})
},
onLinkClick: function () {
const uri = getTaskUri(this.task)
clipboard.writeText(uri)
.then(() => {
this.$message.success('复制下载地址成功')
this.$store.dispatch('app/fetchEngineOptions')
.then((data) => {
const { btTracker } = data
const uri = getTaskUri(this.task, btTracker)
clipboard.writeText(uri)
.then(() => {
this.$msg.success(this.$t('task.copy-link-success'))
})
})
},
onInfoClick: function () {
+24 -8
View File
@@ -9,8 +9,8 @@
:before-close="handleClose"
@closed="handleClosed"
>
<div class="task-name" :title="taskName">
<span>{{ taskName }}</span>
<div class="task-name" :title="taskFullName">
<span>{{ taskFullName }}</span>
</div>
<mo-task-item-actions mode="ITEM" :task="task" />
<div class="task-progress">
@@ -26,7 +26,17 @@
<div v-if="task.status ==='active'">
<span>{{ task.downloadSpeed | bytesToSize }}/s</span>
<span>
{{ remaining | timeFormat('剩余') }}
{{
remaining | timeFormat({
prefix: $t('task.remaining-prefix'),
i18n: {
'gt1d': $t('app.gt1d'),
'hour': $t('app.hour'),
'minute': $t('app.minute'),
'second': $t('app.second')
}
})
}}
</span>
</div>
</el-col>
@@ -68,13 +78,20 @@
}
},
computed: {
taskFullName: function () {
return getTaskName(this.task, {
defaultName: this.$t('task.get-task-name'),
maxLen: -1
})
},
taskName: function () {
return getTaskName(this.task, '获取任务名中...')
return getTaskName(this.task, {
defaultName: this.$t('task.get-task-name'),
maxLen: 32
})
},
dialogTitle: function () {
const len = this.taskName.length
let title = len > 40 ? this.taskName.substr(0, 40) + '...' : this.taskName
return `${title} 详情`
return this.$t('task.task-info-dialog-title', { title: this.taskName })
},
remaining: function () {
const { totalLength, completedLength, downloadSpeed } = this.task
@@ -86,7 +103,6 @@
timeFormat
},
methods: {
getTaskName,
handleClose (done) {
this.$store.dispatch('task/hideTaskItemInfoDialog')
},
+1 -11
View File
@@ -4,7 +4,7 @@
</ul>
<div class="no-task" v-else>
<div class="no-task-inner">
当前没有下载任务
{{ $t('task.no-task') }}
</div>
</div>
</template>
@@ -12,12 +12,6 @@
<script>
import { mapState } from 'vuex'
import TaskItem from './TaskItem'
import {
getTaskName,
bytesToSize,
timeRemaining,
timeFormat
} from '@shared/utils'
export default {
name: 'mo-task-list',
@@ -30,10 +24,6 @@
})
},
methods: {
getTaskName,
bytesToSize,
timeRemaining,
timeFormat
}
}
</script>
+7 -10
View File
@@ -21,16 +21,6 @@ img {
@include clearfix();
}
.title-bar {
position: fixed;
top: 0;
width: 100%;
height: 40px;
user-select: none;
-webkit-app-region: drag;
z-index: 5000;
}
#app,
#container {
height: 100%;
@@ -38,6 +28,7 @@ img {
.draggable {
-webkit-app-region: drag;
-webkit-user-select: none;
}
.el-progress-bar__inner {
@@ -78,6 +69,11 @@ img {
outline: none;
}
.el-message__content {
line-height: 18px;
word-break: break-all;
}
.tab-title-dialog {
.el-dialog__header {
padding: 0 20px;
@@ -93,6 +89,7 @@ img {
.el-form-item--mini .el-form-item__info {
padding-top: 1px;
}
.el-form-item__info {
font-size: 12px;
line-height: 1;
+1 -1
View File
@@ -41,7 +41,7 @@
h3 {
font-size: 16px;
color: #071D43;
color: $--subnav-title-color;
font-weight: normal;
line-height: 24px;
margin: 0 0 28px;
@@ -731,6 +731,7 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
-------------------------- */
$--app-background: transparent !default;
// $--app-background: #fff !default;
$--titlebar-actions-active-background: #eee !default;
/* Aside
-------------------------- */
+10 -2
View File
@@ -1,6 +1,9 @@
<template>
<div id="app">
<div class="title-bar"></div>
<mo-title-bar
v-if="isRenderer()"
:showActions="showWindowActions"
/>
<router-view></router-view>
<mo-engine-client
:secret="rpcSecret"
@@ -11,6 +14,7 @@
<script>
import is from 'electron-is'
import TitleBar from '@/components/Native/TitleBar'
import EngineClient from '@/components/Native/EngineClient'
import Ipc from '@/components/Native/Ipc'
import { mapState } from 'vuex'
@@ -18,12 +22,16 @@
export default {
name: 'Motrix',
components: {
[TitleBar.name]: TitleBar,
[EngineClient.name]: EngineClient,
[Ipc.name]: Ipc
},
computed: {
...mapState('preference', {
rpcSecret: state => state.rpcSecret
showWindowActions: state => {
return (is.windows() || is.linux()) && state.config.hideAppMenu
},
rpcSecret: state => state.config.rpcSecret
})
},
methods: {
+58 -37
View File
@@ -1,47 +1,68 @@
import is from 'electron-is'
import Vue from 'vue'
import axios from 'axios'
import App from './App'
import VueI18Next from '@panter/vue-i18next'
import { sync } from 'vuex-router-sync'
import router from '@/router'
import store from '@/store'
import Element, { Loading } from 'element-ui'
import '../../components/Theme/Index.scss'
import Icon from '../../components/Icons/Icon'
import Element, { Loading, Message } from 'element-ui'
import axios from 'axios'
import 'svg-innerhtml'
import is from 'electron-is'
if (is.renderer()) {
Vue.use(require('vue-electron'))
import App from './App'
import router from '@/router'
import store from '@/store'
import { getLocaleManager } from '@/components/Locale'
import Icon from '@/components/Icons/Icon'
import Msg from '@/components/Msg'
import '@/components/Theme/Index.scss'
function init (config) {
if (is.renderer()) {
Vue.use(require('vue-electron'))
}
Vue.http = Vue.prototype.$http = axios
Vue.config.productionTip = false
const { locale } = config
const localeManager = getLocaleManager()
localeManager.changeLanguageByLocale(locale)
Vue.use(VueI18Next)
const i18n = new VueI18Next(localeManager.getI18n())
Vue.use(Element, {
size: 'mini',
i18n: (key, value) => i18n.t(key, value)
})
Vue.use(Msg, Message, {
showClose: true
})
const loading = Loading.service({
fullscreen: true,
background: 'rgba(0, 0, 0, 0.1)'
})
Vue.component('mo-icon', Icon)
sync(store, router)
/* eslint-disable no-new */
window.app = new Vue({
components: { App },
router,
store,
i18n,
template: '<App/>'
}).$mount('#app')
setTimeout(() => {
loading.close()
}, 400)
}
Vue.http = Vue.prototype.$http = axios
Vue.config.productionTip = false
Vue.use(Element, { size: 'mini' })
Vue.component('mo-icon', Icon)
sync(store, router)
const loading = Loading.service({
fullscreen: true,
background: 'rgba(0, 0, 0, 0.1)'
})
store.dispatch('preference/fetchPreference')
.then(() => {
/* eslint-disable no-new */
window.app = new Vue({
components: { App },
router,
store,
template: '<App/>'
}).$mount('#app')
setTimeout(() => {
loading.close()
}, 400)
.then((config) => {
console.info('[Motrix] fetchPreference===>', config)
init(config)
})
.catch((err) => {
alert(err)
+33 -31
View File
@@ -12,7 +12,7 @@ const state = {
version: '',
enabledFeatures: []
},
engineStatus: '',
engineOptions: {},
interval: BASE_INTERVAL,
stat: {
downloadSpeed: 0,
@@ -21,22 +21,12 @@ const state = {
numStopped: 0,
numWaiting: 0
},
currentPage: 'task',
addTaskVisible: false,
addTaskType: 'uri',
i18n: 'zh_CN'
}
const pageTitles = {
'task': '任务列表',
'preference': '偏好设置',
'about': '关于'
addTaskTorrents: []
}
const getters = {
currentPageTitle: (state, getters) => {
return pageTitles[state.currentPage] ? pageTitles[state.currentPage] : ''
}
}
const mutations = {
@@ -46,18 +36,21 @@ const mutations = {
UPDATE_ENGINE_INFO (state, engineInfo) {
state.engineInfo = { ...state.engineInfo, ...engineInfo }
},
UPDATE_ENGINE_OPTIONS (state, engineOptions) {
state.engineOptions = { ...state.engineOptions, ...engineOptions }
},
UPDATE_GLOBAL_STAT (state, stat) {
state.stat = stat
},
CHANGE_CURRENT_PAGE (state, currentPage) {
state.currentPage = currentPage
},
CHANGE_ADD_TASK_VISIBLE (state, visible) {
state.addTaskVisible = visible
},
CHANGE_ADD_TASK_TYPE (state, taskType) {
state.addTaskType = taskType
},
CHANGE_ADD_TASK_TORRENTS (state, fileList) {
state.addTaskTorrents = [...fileList]
},
UPDATE_INTERVAL (state, millisecond) {
let interval = millisecond
if (millisecond > MAX_INTERVAL) {
@@ -70,19 +63,16 @@ const mutations = {
return
}
state.interval = interval
console.log('current interval===>', state.interval)
},
INCREASE_INTERVAL (state, millisecond) {
if (state.interval < MAX_INTERVAL) {
state.interval += millisecond
}
console.log('current interval===>', state.interval)
},
DECREASE_INTERVAL (state, millisecond) {
if (state.interval > MIN_INTERVAL) {
state.interval -= millisecond
}
console.log('current interval===>', state.interval)
}
}
@@ -99,6 +89,15 @@ const actions = {
commit('UPDATE_ENGINE_INFO', data)
})
},
fetchEngineOptions ({ commit }) {
return new Promise((resolve) => {
api.getGlobalOption()
.then((data) => {
commit('UPDATE_ENGINE_OPTIONS', data)
resolve(data)
})
})
},
fetchGlobalStat ({ commit, dispatch }) {
api.getGlobalStat()
.then((data) => {
@@ -107,8 +106,9 @@ const actions = {
stat[key] = Number(data[key])
})
if (stat.numActive > 0) {
const interval = BASE_INTERVAL - PER_INTERVAL * stat.numActive
const { numActive } = stat
if (numActive > 0) {
const interval = BASE_INTERVAL - PER_INTERVAL * numActive
dispatch('updateInterval', interval)
} else {
// fix downloadSpeed when numActive = 0
@@ -117,33 +117,35 @@ const actions = {
}
commit('UPDATE_GLOBAL_STAT', stat)
// @4ET
if (!is.renderer()) {
return
}
if (stat.numActive > 0) {
api.startPowerSaveBlocker()
} else {
api.stopPowerSaveBlocker()
if (is.renderer()) {
dispatch('togglePowerSaveBlocker', numActive)
}
})
},
togglePowerSaveBlocker (context, numActive) {
if (numActive > 0) {
api.startPowerSaveBlocker()
} else {
api.stopPowerSaveBlocker()
}
},
increaseInterval ({ commit }, millisecond = 100) {
commit('INCREASE_INTERVAL', millisecond)
},
changeCurrentPage ({ commit }, currentPage) {
commit('CHANGE_CURRENT_PAGE', currentPage)
},
showAddTaskDialog ({ commit }, taskType) {
commit('CHANGE_ADD_TASK_TYPE', taskType)
commit('CHANGE_ADD_TASK_VISIBLE', true)
},
hideAddTaskDialog ({ commit }) {
commit('CHANGE_ADD_TASK_VISIBLE', false)
commit('CHANGE_ADD_TASK_TORRENTS', [])
},
changeAddTaskType ({ commit }, taskType) {
commit('CHANGE_ADD_TASK_TYPE', taskType)
},
addTaskAddTorrents ({ commit }, { fileList }) {
commit('CHANGE_ADD_TASK_TORRENTS', fileList)
},
updateInterval ({ commit }, millisecond) {
commit('UPDATE_INTERVAL', millisecond)
},
+10 -16
View File
@@ -7,16 +7,7 @@ const state = {
taskList: []
}
const listTitles = {
'active': '下载中',
'waiting': '已暂停',
'stopped': '已完成'
}
const getters = {
currentListTitle: (state, getters) => {
return listTitles[state.currentList] ? listTitles[state.currentList] : ''
}
}
const mutations = {
@@ -40,13 +31,13 @@ const actions = {
dispatch('fetchList')
},
fetchList ({ state, commit }) {
api.fetchTaskList({ type: state.currentList })
return api.fetchTaskList({ type: state.currentList })
.then((data) => {
commit('UPDATE_TASK_LIST', data)
})
},
fetchItem ({ dispatch }, gid) {
api.fetchTaskItem({ gid })
return api.fetchTaskItem({ gid })
.then((data) => {
dispatch('updateCurrentTaskItem', data)
})
@@ -84,13 +75,16 @@ const actions = {
},
removeTask ({ dispatch }, task) {
const { gid } = task
return api.removeTask({ gid })
.catch(() => {
return api.forceRemoveTask({ gid })
return api.forcePauseTask({ gid })
.catch((e) => {
console.log(`[Motrix] removeTask.forcePauseTask#[${gid}] fail`, e.message)
})
.finally(() => {
dispatch('fetchList')
dispatch('saveSession')
return api.removeTask({ gid })
.finally(() => {
dispatch('fetchList')
dispatch('saveSession')
})
})
},
pauseTask ({ dispatch }, task) {
+2
View File
@@ -1,7 +1,9 @@
const userKeys = [
'locale',
'cookie',
'resume-all-when-app-launched',
'task-notification',
'hide-app-menu',
'new-task-show-downloading',
'use-proxy',
'all-proxy-backup',
+1
View File
@@ -1,6 +1,7 @@
{
"cmd-n": "application:new-task",
"cmd-shift-n": "application:new-bt-task",
"cmd-o": "application:open-file",
"cmd-,": "application:preferences",
"cmd-shift-p": "application:pause-all-task",
"cmd-shift-r": "application:resume-all-task"
+26
View File
@@ -0,0 +1,26 @@
import i18next from 'i18next'
import { getLanguage } from '@shared/locales'
export default class LocaleManager {
constructor (options = {}) {
this.options = options
i18next.init({
fallbackLng: 'en-US',
resources: options.resources
})
}
changeLanguage (lng) {
return i18next.changeLanguage(lng)
}
changeLanguageByLocale (locale) {
const lng = getLanguage(locale)
return this.changeLanguage(lng)
}
getI18n () {
return i18next
}
}
+62
View File
@@ -0,0 +1,62 @@
import eleLocaleDe from 'element-ui/lib/locale/lang/de'
import eleLocaleEn from 'element-ui/lib/locale/lang/en'
import eleLocaleFr from 'element-ui/lib/locale/lang/fr'
import eleLocalePtBR from 'element-ui/lib/locale/lang/pt-br'
import eleLocaleTr from 'element-ui/lib/locale/lang/tr-TR'
import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN'
import eleLocaleZhTW from 'element-ui/lib/locale/lang/zh-TW'
import appLocaleDe from '@shared/locales/de'
import appLocaleEnUS from '@shared/locales/en-US'
import appLocaleFr from '@shared/locales/fr'
import appLocalePtBR from '@shared/locales/pt-BR'
import appLocaleTr from '@shared/locales/tr'
import appLocaleZhCN from '@shared/locales/zh-CN'
import appLocaleZhTW from '@shared/locales/zh-TW'
// Please keep the locale key in alphabetical order.
const resources = {
'de': {
translation: {
...eleLocaleDe,
...appLocaleDe
}
},
'en-US': {
translation: {
...eleLocaleEn,
...appLocaleEnUS
}
},
'fr': {
translation: {
...eleLocaleFr,
...appLocaleFr
}
},
'pt-BR': {
translation: {
...eleLocalePtBR,
...appLocalePtBR
}
},
'tr': {
translation: {
...eleLocaleTr,
...appLocaleTr
}
},
'zh-CN': {
translation: {
...eleLocaleZhCN,
...appLocaleZhCN
}
},
'zh-TW': {
translation: {
...eleLocaleZhTW,
...appLocaleZhTW
}
}
}
export default resources
+48
View File
@@ -0,0 +1,48 @@
import appLocaleDeDE from '@shared/locales/de'
import appLocaleEnUS from '@shared/locales/en-US'
import appLocaleFrFR from '@shared/locales/fr'
import appLocalePtBr from '@shared/locales/pt-BR'
import appLocaleTrTR from '@shared/locales/tr'
import appLocaleZhCN from '@shared/locales/zh-CN'
import appLocaleZhTW from '@shared/locales/zh-TW'
// Please keep the locale key in alphabetical order.
const resources = {
'de': {
translation: {
...appLocaleDeDE
}
},
'en-US': {
translation: {
...appLocaleEnUS
}
},
'fr': {
translation: {
...appLocaleFrFR
}
},
'pt-BR': {
translation: {
...appLocalePtBr
}
},
'tr': {
translation: {
...appLocaleTrTR
}
},
'zh-CN': {
translation: {
...appLocaleZhCN
}
},
'zh-TW': {
translation: {
...appLocaleZhTW
}
}
}
export default resources
+6
View File
@@ -0,0 +1,6 @@
export default {
'engine-version': 'Engine Version',
'about': 'Über',
'release': 'Versionen',
'support': 'Unterstützung anfordern'
}
+30
View File
@@ -0,0 +1,30 @@
export default {
'task-list': 'Aufgabenliste',
'add-task': 'Aufgabe hinzufügen',
'about': 'Über Motrix',
'preferences': 'Präferenzen...',
'check-for-updates': 'Nach Updates suchen...',
'check-for-updates-title': 'Nach Updates suchen',
'update-available-message': 'Eine neue Version von Motrix ist verfügbar, jetzt aktualisieren?',
'update-not-available-message': 'Sie sind auf dem neuesten Stand!',
'update-downloaded-message': 'Bereit zur Installation...',
'update-error-message': 'Aktualisierungsfehler',
'engine-damaged-message': 'Der Motor ist beschädigt, bitte neu installieren : (',
'engine-missing-message': 'Der Motor fehlt, bitte neu installieren : (',
'system-error-title': 'Systemfehler',
'system-error-message': 'Die Anwendung konnte nicht gestartet werden: {{message}}',
'hide': 'Motrix verbergen',
'hide-others': 'Andere verbergen',
'unhide': 'Alles anzeigen',
'show': 'Motrix anzeigen',
'quit': 'Motrix beenden',
'under-development-message': 'Entschuldigung, diese Funktion befindet sich in der Entwicklung...',
'yes': 'Ja',
'no': 'Nein',
'cancel': 'Abbrechen',
'submit': 'Übernehmen',
'gt1d': '> 1 Tag',
'hour': 'h',
'minute': 'm',
'second': 's'
}
+9
View File
@@ -0,0 +1,9 @@
export default {
'undo': 'Rückgängig',
'redo': 'Wiederholen',
'cut': 'Ausschneiden',
'copy': 'Kopieren',
'paste': 'Einfügen',
'delete': 'Löschen',
'select-all': 'Alles auswählen'
}
+7
View File
@@ -0,0 +1,7 @@
export default {
'official-website': 'Motrix Website',
'manual': 'Handbuch',
'release-notes': 'Versionshinweise...',
'report-problem': 'Problem melden',
'toggle-dev-tools': 'Entwicklerwerkzeuge umschalten'
}
+21
View File
@@ -0,0 +1,21 @@
import about from './about'
import app from './app'
import edit from './edit'
import help from './help'
import menu from './menu'
import preferences from './preferences'
import subnav from './subnav'
import task from './task'
import window from './window'
export default {
about,
app,
edit,
help,
menu,
preferences,
subnav,
task,
window
}
+8
View File
@@ -0,0 +1,8 @@
export default {
'app': 'Motrix',
'file': 'Datei',
'task': 'Aufgabe',
'edit': 'Bearbeiten',
'window': 'Fenster',
'help': 'Hilfe'
}
+37
View File
@@ -0,0 +1,37 @@
export default {
'basic': 'Standard',
'advanced': 'Erweitert',
'lab': 'Experimentell',
'save': 'Speichern & übernehmen',
'discard': 'Verwerfen',
'startup': 'Startup',
'auto-resume-all': 'Alle nicht abgeschlossenen Aufgaben automatisch fortsetzen',
'default-dir': 'Standard Speicherort',
'mas-default-dir-tip': 'Aufgrund der Einschränkungen durch Sandbox-Berechtigungen im App Store wird der Download Ordner als Standard empfohlen',
'task-manage': 'Aufgaben verwalten',
'max-concurrent-downloads': 'Maximal aktive Aufgaben',
'max-connection-per-server': 'Maximale Verbindungen pro Server',
'new-task-show-downloading': 'Nach hinzufügen einer Aufgabe zu aktiven Downloads wechseln',
'continue': 'HTTPS/FTP Downloads fortsetzen wenn bereits angefangen',
'task-completed-notify': 'Benachrichtigung nach abgeschlossenen Download anzeigen',
'auto-purge-record': 'Download Protokoll beim Schließen der App löschen',
'ui': 'UI',
'language': 'Sprache',
'change-language': 'Sprache ändern',
'hide-app-menu': 'App Menü ausblenden (nur auf Windows & Linux)',
'proxy': 'Proxy',
'use-proxy': 'Proxy aktivieren',
'developer': 'Entwickler',
'mock-user-agent': 'User-Agent simulieren',
'app-log-path': 'Appprotokollpfad',
'download-session-path': 'Downloadsitzungspfad',
'factory-reset': 'Werkseinstellungen',
'factory-reset-confirm': 'Sollen die Einstellungen auf die Werkseinstellungen unwiderruflich zurückgesetzt werden?',
'lab-warning': '⚠️ Die Aktivierung von experimentellen Funktionen kann zu App-Abstürzen oder Datenverlust führen!',
'download-protocol': 'Protokoll',
'support-more-download-protocols': 'Erweiterte Protokollunterstützung aktivieren',
'browser-extensions': 'Erweiterungen',
'baidu-exporter': 'Baidu Exporter',
'browser-extensions-tip': 'Von der Community bereitgestellt, ',
'baidu-exporter-help': 'mehr über die Verwendung zu erfahren'
}
+4
View File
@@ -0,0 +1,4 @@
export default {
'task-list': 'Aufgaben',
'preferences': 'Präferenzen'
}

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