Compare commits

...

81 Commits

Author SHA1 Message Date
Dr_rOot 12a9fa92c1 chore: bump version 2020-06-05 23:54:14 +08:00
Dr_rOot 0fd6617eba docs: update readme i18n 2020-06-05 23:52:34 +08:00
Dr_rOot 7ebbf929d5 fix: i18n no-confirm-before-delete-task missed 2020-06-05 23:49:27 +08:00
Dr_rOot 14223c2204 chore: bump version 2020-06-05 23:26:11 +08:00
Dr_rOot 2cfb6b1914 Merge pull request #689 from agalwood/hotfix/handle_url_202006051331
fix: open app with url handle fail
2020-06-05 14:04:27 +08:00
Dr_rOot c38cf80589 chore: update deps 2020-06-05 13:40:30 +08:00
Dr_rOot 3ee98eae1d fix: open app with resource url 2020-06-05 13:33:22 +08:00
Dr_rOot d2cff6356a Merge pull request #686 from NickoAilus/master
fix: edited Russian locale
2020-06-01 22:12:46 +08:00
NickoAilus 3ee432d683 Fixed Russian locale 2020-06-01 16:10:36 +03:00
Dmitry Kalinin 7f1822bb7e feat: Added bulgarian (bg) translations (#685)
* Added bulgarian (bg) translations

* Improve structure

* Fixed bugs

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

* Update app.js and index.js

* Translated about.js

* Translated app.js

* first draft

* final draft

* final update - ready to review

* Update format
2020-05-31 22:52:26 +08:00
Dr_rOot 66f114bf72 Merge pull request #674 from agalwood/hotfix/upnp_cb_202005271904
fix: nat-api autoUpdate cb is not a function
2020-05-28 15:28:28 +08:00
Dr_rOot 44f00483f9 chore: update deps 2020-05-28 14:42:43 +08:00
Dr_rOot 1866e3fd4f fix: nat-api autoUpdate cb is not a function
replace nat-api to @motrix/nat-api

@motrix/nat-api forked from https://github.com/alxhotel/nat-api
2020-05-28 14:42:05 +08:00
Dr_rOot cfac883cbf chore: remove deprecated vue-html-loader 2020-05-28 12:11:30 +08:00
Dr_rOot 1431bab366 Merge pull request #670 from agalwood/hotfix/full_screen_preference_202005271103
fix: full screen save preference issue #663
2020-05-27 11:20:23 +08:00
Dr_rOot bb373947ff fix: full screen save preference issue #663 2020-05-27 11:06:51 +08:00
Dr_rOot 8f0dc65341 refactor: rename auto hide window 2020-05-27 11:06:31 +08:00
Dr_rOot 402185e1a2 chore: bump version 2020-05-25 22:35:09 +08:00
Dr_rOot d59b5c9841 Merge pull request #667 from agalwood/hotfix/refactor_202005231656
fix: upnp client & full screen issues
2020-05-25 22:15:36 +08:00
Dr_rOot eb442e4a7a fix: windows auto launch config lose 2020-05-25 21:55:23 +08:00
Dr_rOot e82e567069 refactor: time constants 2020-05-24 20:15:20 +08:00
Dr_rOot dc2876098d fix: upnp client is destroyed #662 2020-05-24 20:13:43 +08:00
Dr_rOot 6287942fbc fix: full screen mode no traffic light #663 2020-05-24 17:04:08 +08:00
Dr_rOot 389dc080b6 refactor: improve macOS fullscreen mode usability 2020-05-24 15:30:50 +08:00
Dr_rOot 45a23d73e6 fix: code error 2020-05-23 17:16:16 +08:00
Dr_rOot 8303dd305b refactor: fn rename & format 2020-05-23 17:03:45 +08:00
Dr_rOot a98292ce1e refactor: remove title bar buttons spacer 2020-05-23 16:57:01 +08:00
Dr_rOot 8df97b8433 fix: switching system theme will not auto switch app theme 2020-05-22 11:21:45 +08:00
Dr_rOot f29e95c9bc chore: add electron apps banner 2020-05-21 16:32:10 +08:00
Dr_rOot 52e045c886 docs: update readme for github markdown 2020-05-21 08:56:28 +08:00
Dr_rOot 4632c3619a docs: update readme 2020-05-21 08:54:01 +08:00
Dr_rOot 54c48be29b chore: bump version 2020-05-21 07:51:47 +08:00
Dr_rOot 301f1403df Merge pull request #658 from aarestu/translation/indonesia
feat: add Indonesian translations
2020-05-21 07:17:12 +08:00
Dr_rOot 88de047778 Merge pull request #659 from agalwood/hotfix/webpack_copy_202005210655
fix: webpack copy plugin path
2020-05-21 07:07:29 +08:00
Dr_rOot c119de78ce fix: webpack copy plugin path 2020-05-21 06:56:04 +08:00
Restu Suhendar 9d381e16da add translation Indonesia 2020-05-21 04:24:07 +07:00
Dr_rOot e4c6d6a9c0 Merge pull request #655 from agalwood/hotfix/linux_tray_202005202115
fix: linux tray context menu
2020-05-20 21:33:13 +08:00
Dr_rOot 5bb727cb6f chore: update deps 2020-05-20 21:16:35 +08:00
Dr_rOot 00f3209c68 refactor: improve logs 2020-05-20 21:16:11 +08:00
Dr_rOot 3f8b0e6f5f fix: tray popUpContextMenu linux not support 2020-05-20 21:16:01 +08:00
Dr_rOot 0543bc4e2c chore: bump version 2020-05-20 16:32:55 +08:00
Dr_rOot 9ded42f127 chore: update docs 2020-05-20 13:53:20 +08:00
Dr_rOot 834a1ad839 docs: update readme linux section 2020-05-20 11:30:50 +08:00
Dr_rOot 8f830f6a0d fix: too fast to toggle tracker syncing spinner 2020-05-20 11:10:59 +08:00
Dr_rOot ee111c92ee fix: too fast to shut down the engine 2020-05-20 11:09:50 +08:00
Dr_rOot 74c3a3c696 Merge pull request #650 from agalwood/hotfix/linux_tray_20200519
fix: linux tray not support right-click event
2020-05-19 22:06:45 +08:00
Dr_rOot 4d964ae16e fix: tray destroy remove listener 2020-05-19 21:50:36 +08:00
Dr_rOot be2c2e8383 docs: update readme snapcraft markdown 2020-05-19 12:23:31 +08:00
Dr_rOot 4e6164816f fix: linux tray not support right click 2020-05-19 12:23:00 +08:00
Dr_rOot fa7daf377f docs: update app screenshots 2020-05-18 14:24:06 +08:00
Dr_rOot afbe364525 docs: fix release badge 2020-05-18 13:43:36 +08:00
Dr_rOot ece5cea512 docs: update readme 2020-05-18 13:37:36 +08:00
Dr_rOot dc8fa5c647 chore: bump version v1.5.10 2020-05-15 21:54:16 +08:00
Dr_rOot 4fd96e7cae chore: bump version v1.5.9 2020-05-15 21:45:22 +08:00
Dr_rOot 1f22b5efba Merge pull request #640 from agalwood/hotfix/task_manager_202005151229
fix: save session delay too long
fix: hide delete selected tasks on stopped task list
2020-05-15 16:44:31 +08:00
Dr_rOot 801db0ed38 chore: upgrade electron 2020-05-15 12:34:39 +08:00
Dr_rOot f72577de65 fix: hide delete selected tasks on stopped task list 2020-05-15 12:33:58 +08:00
Dr_rOot dd21c76dea fix: save session delay too long 2020-05-15 12:29:51 +08:00
Dr_rOot 5a5a932735 Merge pull request #639 from agalwood/feature/no_confirm_202005062312
feat: no confirm before delete task
feat: add task select video include sub files
feat: preference support add custom tracker source #627
feat: Ctrl or ⌘ + Enter quick submit task #638
feat: improve task total length is zero ui #614
fix: split & max-connection-per-server failed #270
fix: npm build:clean script
2020-05-15 00:03:54 +08:00
Dr_rOot d63c1d431d feat: improve task total length is zero ui #614 2020-05-14 23:43:42 +08:00
Dr_rOot 1a12256c4c feat: Ctrl or ⌘ + Enter quick submit task 2020-05-14 23:40:37 +08:00
Dr_rOot 9394a0a79b chore: update deps & bump version 2020-05-14 22:27:44 +08:00
Dr_rOot 5e66205298 chore: disable electron security warnings 2020-05-14 22:26:34 +08:00
Dr_rOot eb796c3c5f feat: add task select video include sub files 2020-05-14 22:26:11 +08:00
Dr_rOot 9d17b3e9b2 fix: split & max-connection-per-server failed #270 2020-05-14 22:25:35 +08:00
Dr_rOot 7ee8d4fa0f refactor: engine instance listeners 2020-05-14 22:21:57 +08:00
Dr_rOot dba4bfb0e7 fix: perference sync tracker empty 2020-05-14 22:20:23 +08:00
Dr_rOot f9755f69cd chore: improve aria2 bt config 2020-05-14 22:20:02 +08:00
Dr_rOot 7a68e1bb82 fix: npm build:clean script 2020-05-14 22:14:18 +08:00
Dr_rOot 3b12f3960f refactor: code format 2020-05-14 22:13:56 +08:00
Dr_rOot dbe26dfa98 feat: preference support add custom tracker source 2020-05-14 22:12:34 +08:00
Dr_rOot 3be8952cff refactor: task actions commands 2020-05-14 22:08:59 +08:00
Dr_rOot dc5a368e00 Merge pull request #630 from agalwood/hotfix/aria2c_darwin_202005081709
fix: rebuild darwin aria2c v1.35

closed #628
2020-05-08 17:30:53 +08:00
Dr_rOot 11aca7ea0b chore: remove bt-force-encryption 2020-05-08 17:16:48 +08:00
Dr_rOot c35af2b109 fix: rebuild aria2c #628
static build script:
https://github.com/aria2/aria2/blob/master/
makerelease-osx.mk
2020-05-08 17:12:04 +08:00
Dr_rOot b33b505ccb refactor: upnp close client 2020-05-06 23:21:49 +08:00
Dr_rOot dd22cc0306 fix: remove enable egg features config 2020-05-06 23:19:58 +08:00
Dr_rOot 206eda08aa refactor: renderer native utils 2020-05-06 23:19:08 +08:00
Dr_rOot f6e29700d0 feat: no confirm before delete config 2020-05-06 23:16:50 +08:00
113 changed files with 7359 additions and 3603 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ if (process.env.BUILD_TARGET === 'clean') {
}
function clean () {
del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
del.sync(['release/*', '!.gitkeep'])
console.log(`\n${doneLog}\n`)
process.exit()
}
+5 -9
View File
@@ -93,10 +93,6 @@ let rendererConfig = {
'css-loader'
]
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
@@ -237,13 +233,13 @@ if (!devMode) {
rendererConfig.devtool = ''
rendererConfig.plugins.push(
new CopyWebpackPlugin([
{
new CopyWebpackPlugin({
patterns: [{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/electron/static'),
ignore: ['.*']
}
]),
globOptions: { ignore: [ '.*' ] }
}]
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
+6 -6
View File
@@ -209,13 +209,13 @@ if (!devMode) {
webConfig.devtool = ''
webConfig.plugins.push(
new CopyWebpackPlugin([
{
new CopyWebpackPlugin({
patterns: [{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*']
}
]),
to: path.join(__dirname, '../dist/electron/static'),
globOptions: { ignore: [ '.*' ] }
}]
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
+38 -11
View File
@@ -4,15 +4,15 @@
<img src="https://cdn.nlark.com/yuque/0/2018/png/129147/1543735425232-a5d2c99f-d788-43e4-9781-558ff6d21027.png" width="256" alt="App Icon" />
</a>
[English](./README.md) | 简体中文
## 一款全能的下载工具
[![GitHub release](https://img.shields.io/github/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) ![Build/release](https://github.com/agalwood/Motrix/workflows/Build/release/badge.svg) [![Build Status](https://travis-ci.com/agalwood/Motrix.svg?branch=master)](https://travis-ci.com/agalwood/Motrix) [![Build status](https://ci.appveyor.com/api/projects/status/l11d5h05xwwcvoux/branch/master?svg=true)](https://ci.appveyor.com/project/agalwood/motrix/branch/master) [![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg)](https://github.com/agalwood/Motrix/releases) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
[![GitHub release](https://img.shields.io/github/v/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) ![Build/release](https://github.com/agalwood/Motrix/workflows/Build/release/badge.svg) ![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
[English](./README.md) | 简体中文
我是个兴趣使然的桌面应用开发者🤓,利用搬砖之余开发了 Motrix。
Motrix 是一款全能的下载工具,支持下载 HTTP、FTP、BT、磁力链、某盘等资源。它的界面简洁易用,希望大家喜欢 👻。
Motrix 是一款全能的下载工具,支持下载 HTTP、FTP、BT、磁力链等资源。它的界面简洁易用,希望大家喜欢 👻。
✈️ 去 [官网](https://motrix.app/zh-CN) 逛逛 | 📖 查看 [帮助手册](http://motrix.app/support/issues)
@@ -41,10 +41,35 @@ brew update && brew cask install motrix
### Linux
你可以下载 AppImage(适用于所有 Linux 发行版)软件包或 snap 或从源代码构建安装 Motrix
你可以下载 `AppImage` (适用于所有 Linux 发行版)`snap` 来安装 Motrix,更多 Linux 安装包格式请查看 [GitHub/release](https://github.com/agalwood/Motrix/releases)
构建请阅读 **编译打包** 部分。
如果你想自己通过编译源码来安装,请阅读 **编译打包** 部分。
#### AppImage
最新版的 Motrix AppImage 需要自己手动进执行桌面集成。请查看 [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) 的文档进行操作。
> 桌面集成
> electron-builder v21 之后,桌面集成不再是 AppImage 文件的一部分。
> 推荐使用 [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) 集成 AppImage。
Deepin 20 Beta 用户安装 Motrix 失败的问题,请按照以下方法处理:
打开`终端`,黏贴运行如下命令之后再次安装 Motrix。
```bash
sudo apt --fix-broken install
```
#### Snap
Motrix 已经上架 [Snapcraft](https://snapcraft.io/motrix) Ubuntu 用户推荐从 Snap 商店下载。
v1.5.10 提示
系统托盘可能无法正常显示指示器,导致退出应用程序不方便。
请取消勾选 偏好设置——基本设置——隐藏应用程序菜单(仅限Windows和Linux),点击保存并应用。然后点击 "文件 "菜单中的 "退出",退出应用程序。
请更新到 v1.5.12 及以上版本,可以使用键盘组合快捷键 <kbd>Ctrl</kbd> + <kbd>q</kbd> 快速退出应用。
#### AUR
对于 Arch Linux 用户,可以使用 [aur](https://aur.archlinux.org/packages/motrix/) 安装 Motrix,感谢维护者 [weearc](https://github.com/weearc)。
运行以下命令进行安装:
@@ -60,7 +85,8 @@ Motrix 在 Linux 中首次启动可能需要使用 `sudo` 运行,因为可能
- 🕹 简洁明了的图形操作界面
- 🦄 支持BT和磁力链任务
- ☑️ 支持选择性下载BT部分文件
- 💾 支持下载某盘资源
- 📡 每天自动更新 Tracker 服务器列表
- 🔌 UPnP & NAT-PMP 端口映射
- 🎛 最高支持 10 个任务同时下载
- 🚀 单任务最高支持 64 线程下载
- 🚥 设置上传/下载限速
@@ -71,11 +97,11 @@ Motrix 在 Linux 中首次启动可能需要使用 `sudo` 运行,因为可能
- 🌑 深色模式
- 🗑 移除任务时可同时删除相关文件
- 🌍 国际化,[查看已可选的语言](#-国际化)
- 🎏 ...
- 🛠 更多特性开发中
## 🖥 应用界面
![motrix-screenshot-task-cn.png](https://cdn.nlark.com/yuque/0/2019/png/129147/1550151234585-e513bd4f-e127-402f-accb-1ebbba9b3c41.png)
![motrix-screenshot-task-cn.png](https://cdn.nlark.com/yuque/0/2020/png/129147/1589782239990-fecb9065-19ac-4c35-938b-0be45621ca3a.png)
## ⌨️ 本地开发
@@ -104,8 +130,6 @@ export SASS_BINARY_SITE='https://npm.taobao.org/mirrors/node-sass'
`Electron` 下载安装失败的问题,解决方式请参考 https://github.com/electron/electron/issues/8466#issuecomment-571425574
如果喜欢 [Yarn](https://yarnpkg.com/),也可以使用 `yarn` 安装依赖
### 开发模式
```bash
@@ -140,17 +164,20 @@ npm run build
| Key | Name | Status |
|-------|:--------------------|:-------------|
| bg | Българският език | ✔️ [@null-none](https://github.com/null-none) |
| ca | Català | ✔️ [@marcizhu](https://github.com/marcizhu) |
| de | Deutsch | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
| en-US | English | ✔️ |
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
| id | Indonesia | ✔️ [@aarestu](https://github.com/aarestu) |
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
| ru | Русский | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
| uk | Українська | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| vi | Tiếng Việt | ✔️ [@duythanhvn](https://github.com/duythanhvn) |
| zh-CN | 简体中文 | ✔️ |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) |
+37 -8
View File
@@ -6,7 +6,7 @@
## A full-featured download manager
[![GitHub release](https://img.shields.io/github/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) ![Build/release](https://github.com/agalwood/Motrix/workflows/Build/release/badge.svg) [![Build Status](https://travis-ci.com/agalwood/Motrix.svg?branch=master)](https://travis-ci.com/agalwood/Motrix) [![Build status](https://ci.appveyor.com/api/projects/status/l11d5h05xwwcvoux/branch/master?svg=true)](https://ci.appveyor.com/project/agalwood/motrix/branch/master) [![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg)](https://github.com/agalwood/Motrix/releases) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
[![GitHub release](https://img.shields.io/github/v/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) ![Build/release](https://github.com/agalwood/Motrix/workflows/Build/release/badge.svg) ![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667)
English | [简体中文](./README-CN.md)
@@ -41,10 +41,37 @@ brew update && brew cask install motrix
### Linux
You can download the AppImage (for all Linux distributions) package or snap or just build from source code to install Motrix.
You can download the `AppImage` (for all Linux distributions) or `snap` to install Motrix, see [GitHub/release](https://github.com/agalwood/Motrix/releases) for more Linux installation package formats.
Please read the **Build** section.
If you want to build from source code, please read the **Build** section.
#### AppImage
The latest version of Motrix AppImage requires you to manually perform desktop integration. Please check the documentation of [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) .
> Desktop Integration
> Since electron-builder 21 desktop integration is not a part of produced AppImage file.
> [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) is the recommended way to integrate AppImages.
Deepin 20 Beta users failed to install Motrix, please follow the steps below:
Open the `Terminal`, paste and run the following command to install Motrix again.
```bash
sudo apt --fix-broken install
```
#### Snap
Motrix has been listed on [Snapcraft](https://snapcraft.io/motrix) , Ubuntu users recommend downloading from the Snap Store.
Tips for v1.5.10
The tray may not display the indicator normally, which makes it inconvenient to exit the application.
Please unchecked Preferences--Basic Settings--Hide App Menu (Windows & Linux Only), click Save & Apply. Then click "Exit" in the File menu to exit the application.
Please update to v1.5.12 and above, you can use the keyboard shortcut <kbd>Ctrl</kbd> + <kbd>q</kbd> to quickly exit the application.
#### AUR
For Arch Linux users, Motrix is available in [aur](https://aur.archlinux.org/packages/motrix/), thanks to the maintainer [weearc](https://github.com/weearc).
Run the following command to install:
@@ -60,7 +87,8 @@ Motrix may need to run with `sudo` for the first time in Linux because there is
- 🕹 Simple and clear user interface
- 🦄 Supports BitTorrent & Magnet
- ☑️ BitTorrent selective download
- 💾 Supports downloading BD Net Disk
- 📡 Update tracker list every day automatically
- 🔌 UPnP & NAT-PMP Port Mapping
- 🎛 Up to 10 concurrent download tasks
- 🚀 Supports 64 threads in a single task
- 🚥 Supports speed limit
@@ -71,11 +99,11 @@ Motrix may need to run with `sudo` for the first time in Linux because there is
- 🌑 Dark mode
- 🗑 Delete related files when removing tasks (optional)
- 🌍 I18n, [View supported languages](#-internationalization).
- 🎏 ...
- 🛠 More features in development
## 🖥 User Interface
![motrix-screenshot-task-en.png](https://cdn.nlark.com/yuque/0/2019/png/129147/1550151166169-94b4bfb0-746e-42b8-aad7-0b6890f89abb.png)
![motrix-screenshot-task-en.png](https://cdn.nlark.com/yuque/0/2020/png/129147/1589782238501-e7b39166-da58-4152-ae34-65a061cafa48.png)
## ⌨️ Development
@@ -96,8 +124,6 @@ npm install
`Electron` failed to install correctly, please refer to https://github.com/electron/electron/issues/8466#issuecomment-571425574
If you like [Yarn](https://yarnpkg.com/), you can also use `yarn` to install dependencies.
### Dev Mode
```bash
@@ -132,17 +158,20 @@ Translations into versions for other languages are welcome 🧐! Please read the
| Key | Name | Status |
|-------|:--------------------|:-------------|
| bg | Българският език | ✔️ [@null-none](https://github.com/null-none) |
| ca | Català | ✔️ [@marcizhu](https://github.com/marcizhu) |
| de | Deutsch | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
| en-US | English | ✔️ |
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
| id | Indonesia | ✔️ [@aarestu](https://github.com/aarestu) |
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
| ru | Русский | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
| uk | Українська | ✔️ [@bladeaweb](https://github.com/bladeaweb) |
| vi | Tiếng Việt | ✔️ [@duythanhvn](https://github.com/duythanhvn) |
| zh-CN | 简体中文 | ✔️ |
| zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) |
+3 -1
View File
@@ -46,11 +46,13 @@ user-agent=Transmission/2.94
# Enable Local Peer Discovery.
bt-enable-lpd=true
# Requires BitTorrent message payload encryption with arc4.
bt-force-encryption=true
# bt-force-encryption=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=255
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
Binary file not shown.
+3 -1
View File
@@ -46,11 +46,13 @@ user-agent=Transmission/2.94
# Enable Local Peer Discovery.
bt-enable-lpd=true
# Requires BitTorrent message payload encryption with arc4.
bt-force-encryption=true
# bt-force-encryption=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=255
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
+3 -1
View File
@@ -46,11 +46,13 @@ user-agent=Transmission/2.94
# Enable Local Peer Discovery.
bt-enable-lpd=true
# Requires BitTorrent message payload encryption with arc4.
bt-force-encryption=true
# bt-force-encryption=true
# If true is given, after hash check using --check-integrity option and file is complete, continue to seed file.
bt-hash-check-seed=true
# Specify the maximum number of peers per torrent.
bt-max-peers=255
# Try to download first and last pieces of each file first. This is useful for previewing files.
bt-prioritize-piece=head
# Removes the unselected files when download is completed in BitTorrent.
bt-remove-unselected-file=true
# Seed previously downloaded files without verifying piece hashes.
+5422 -2936
View File
File diff suppressed because it is too large Load Diff
+28 -29
View File
@@ -1,6 +1,6 @@
{
"name": "Motrix",
"version": "1.5.7",
"version": "1.5.15",
"description": "A full-featured download manager",
"homepage": "https://motrix.app",
"author": {
@@ -181,58 +181,58 @@
]
},
"dependencies": {
"@babel/runtime": "^7.9.2",
"@babel/runtime": "^7.10.2",
"@panter/vue-i18next": "^0.15.2",
"aria2": "^4.1.0",
"axios": "^0.19.2",
"blob-util": "^2.0.2",
"clipboard-polyfill": "^2.8.6",
"electron-debug": "^3.0.1",
"electron-debug": "^3.1.0",
"electron-is": "^3.0.0",
"electron-log": "^4.1.1",
"electron-log": "^4.2.1",
"electron-store": "^5.1.1",
"electron-updater": "^4.3.1",
"element-ui": "^2.13.1",
"forever-monitor": "1.7.2",
"i18next": "^19.4.4",
"element-ui": "^2.13.2",
"forever-monitor": "3.0.0",
"i18next": "^19.4.5",
"lodash": "^4.17.15",
"nat-api": "^0.1.3",
"@motrix/nat-api": "^0.3.1",
"node-fetch": "^2.6.0",
"normalize.css": "^8.0.1",
"parse-torrent": "^7.1.2",
"parse-torrent": "^7.1.3",
"randomatic": "^3.1.1",
"svg-innerhtml": "^1.1.0",
"vue": "^2.6.11",
"vue-electron": "^1.0.6",
"vue-router": "^3.1.6",
"vuex": "^3.3.0",
"vue-router": "^3.3.2",
"vuex": "^3.4.0",
"vuex-router-sync": "^5.0.0"
},
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@babel/register": "^7.9.0",
"@babel/core": "^7.10.2",
"@babel/plugin-proposal-class-properties": "^7.10.1",
"@babel/plugin-transform-runtime": "^7.10.1",
"@babel/preset-env": "^7.10.2",
"@babel/register": "^7.10.1",
"@vue/eslint-config-standard": "^5.1.2",
"ajv": "^6.12.2",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-component": "^1.1.1",
"cfonts": "^2.8.1",
"cfonts": "^2.8.2",
"chalk": "^4.0.0",
"copy-webpack-plugin": "^5.1.1",
"copy-webpack-plugin": "^6.0.2",
"cross-env": "^7.0.2",
"css-loader": "^3.5.3",
"del": "^5.1.0",
"devtron": "^1.4.0",
"electron": "^8.2.3",
"electron-builder": "^22.6.0",
"electron": "^8.3.1",
"electron-builder": "^22.7.0",
"electron-builder-notarize": "^1.1.2",
"electron-devtools-installer": "^3.0.0",
"electron-notarize": "^0.3.0",
"electron-osx-sign": "^0.4.15",
"eslint": "^6.8.0",
"electron-osx-sign": "^0.4.16",
"eslint": "^7.1.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.20.2",
@@ -241,23 +241,22 @@
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.2.0",
"html-webpack-plugin": "^4.3.0",
"mini-css-extract-plugin": "0.9.0",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"sass": "^1.26.5",
"sass": "^1.26.8",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.0",
"terser-webpack-plugin": "^2.3.6",
"style-loader": "^1.2.1",
"terser-webpack-plugin": "^3.0.3",
"url-loader": "^4.1.0",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.9.1",
"vue-loader": "^15.9.2",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3",
"webpack-dev-server": "^3.11.0",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^4.2.2"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+106 -34
View File
@@ -21,9 +21,16 @@ import TouchBarManager from './ui/TouchBarManager'
import TrayManager from './ui/TrayManager'
import DockManager from './ui/DockManager'
import ThemeManager from './ui/ThemeManager'
import { AUTO_SYNC_TRACKER_INTERVAL, AUTO_CHECK_UPDATE_INTERVAL } from '@shared/constants'
import {
APP_RUN_MODE,
AUTO_SYNC_TRACKER_INTERVAL,
AUTO_CHECK_UPDATE_INTERVAL
} from '@shared/constants'
import { checkIsNeedRun } from '@shared/utils'
import { convertTrackerDataToComma, fetchBtTrackerFromSource } from '@shared/utils/tracker'
import {
convertTrackerDataToComma,
fetchBtTrackerFromSource
} from '@shared/utils/tracker'
export default class Application extends EventEmitter {
constructor () {
@@ -39,39 +46,28 @@ export default class Application extends EventEmitter {
this.localeManager = setupLocaleManager(this.locale)
this.i18n = this.localeManager.getI18n()
this.menuManager = new MenuManager()
this.menuManager.setup(this.locale)
this.initTouchBarManager()
this.setupApplicationMenu()
this.initWindowManager()
this.engine = new Engine({
systemConfig: this.configManager.getSystemConfig(),
userConfig: this.configManager.getUserConfig()
})
this.initUPnPManager()
this.startEngine()
this.initEngineClient()
this.initUPnPManager()
this.initTouchBarManager()
this.autoSyncTracker()
this.initThemeManager()
this.trayManager = new TrayManager({
theme: this.configManager.getUserConfig('tray-theme')
})
this.initTrayManager()
this.dockManager = new DockManager({
runMode: this.configManager.getUserConfig('run-mode')
})
this.initDockManager()
this.autoLaunchManager = new AutoLaunchManager()
this.energyManager = new EnergyManager()
this.initThemeManager()
this.initUpdaterManager()
this.initProtocolManager()
@@ -81,12 +77,34 @@ export default class Application extends EventEmitter {
this.handleEvents()
this.handleIpcMessages()
this.emit('application:initialized')
}
setupApplicationMenu () {
this.menuManager = new MenuManager()
this.menuManager.setup(this.locale)
}
adjustMenu () {
if (is.mas()) {
const visibleStates = {
'app.check-for-updates': false,
'task.new-bt-task': false
}
this.menuManager.updateMenuStates(visibleStates, null, null)
this.trayManager.updateMenuStates(visibleStates, null, null)
}
}
startEngine () {
const self = this
try {
this.engine = new Engine({
systemConfig: this.configManager.getSystemConfig(),
userConfig: this.configManager.getUserConfig()
})
this.engine.start()
} catch (err) {
const { message } = err
@@ -105,10 +123,12 @@ export default class Application extends EventEmitter {
async stopEngine () {
try {
await this.engineClient.shutdown({ force: true })
setImmediate(() => {
this.engine.stop()
})
} catch (err) {
logger.warn('[Motrix] shutdown engine fail: ', err.message)
} finally {
this.engine.stop()
}
}
@@ -121,6 +141,18 @@ export default class Application extends EventEmitter {
})
}
initTrayManager () {
this.trayManager = new TrayManager({
theme: this.configManager.getUserConfig('tray-theme')
})
}
initDockManager () {
this.dockManager = new DockManager({
runMode: this.configManager.getUserConfig('run-mode')
})
}
initUPnPManager () {
this.upnp = new UPnPManager()
@@ -196,7 +228,8 @@ export default class Application extends EventEmitter {
if (newValue) {
this.startUPnPMapping()
} else {
this.stopUPnPMapping()
await this.stopUPnPMapping()
this.upnp.closeClient()
}
})
}
@@ -207,13 +240,14 @@ export default class Application extends EventEmitter {
await this.stopUPnPMapping()
}
this.upnp.destroy()
this.upnp.closeClient()
}
autoSyncTracker () {
const enable = this.configManager.getUserConfig('auto-sync-tracker')
const lastTime = this.configManager.getUserConfig('last-sync-tracker-time')
const result = checkIsNeedRun(enable, lastTime, AUTO_SYNC_TRACKER_INTERVAL)
logger.info('[Motrix] auto sync tracker checkIsNeedRun:', result)
if (!result) {
return
}
@@ -221,6 +255,7 @@ export default class Application extends EventEmitter {
setTimeout(() => {
const source = this.configManager.getUserConfig('tracker-source')
fetchBtTrackerFromSource(source).then((data) => {
logger.warn('[Motrix] auto sync tracker data:', data)
const tracker = convertTrackerDataToComma(data)
this.savePreference({
system: {
@@ -230,8 +265,19 @@ export default class Application extends EventEmitter {
'last-sync-tracker-time': Date.now()
}
})
}).catch((err) => {
logger.warn('[Motrix] auto sync tracker failed:', err.message)
})
}, 3000)
}, 500)
}
autoResumeTask () {
const enabled = this.configManager.getUserConfig('resume-all-when-app-launched')
if (!enabled) {
return
}
this.engineClient.call('unpauseAll')
}
initWindowManager () {
@@ -242,12 +288,25 @@ export default class Application extends EventEmitter {
this.windowManager.on('window-resized', (data) => {
this.storeWindowState(data)
})
this.windowManager.on('window-moved', (data) => {
this.storeWindowState(data)
})
this.windowManager.on('window-closed', (data) => {
this.storeWindowState(data)
})
this.windowManager.on('enter-full-screen', (window) => {
this.dockManager.show()
})
this.windowManager.on('leave-full-screen', (window) => {
const mode = this.configManager.getUserConfig('run-mode')
if (mode !== APP_RUN_MODE.STANDARD) {
this.dockManager.hide()
}
})
}
storeWindowState (data = {}) {
@@ -291,7 +350,7 @@ export default class Application extends EventEmitter {
hide (page) {
if (page) {
this.windowManager.autoHideWindow(page)
this.windowManager.hideWindow(page)
} else {
this.windowManager.hideAllWindow()
}
@@ -311,9 +370,9 @@ export default class Application extends EventEmitter {
this.energyManager.stopPowerSaveBlocker()
this.trayManager.destroy()
await this.stopEngine()
this.trayManager.destroy()
} catch (err) {
logger.warn('[Motrix] stop error: ', err.message)
}
@@ -349,9 +408,9 @@ export default class Application extends EventEmitter {
initThemeManager () {
this.themeManager = new ThemeManager()
this.themeManager.on('system-theme-changed', (theme) => {
this.themeManager.on('system-theme-change', (theme) => {
this.trayManager.changeIconTheme(theme)
this.sendCommandToAll('application:system-theme', theme)
this.sendCommandToAll('application:update-system-theme', theme)
})
}
@@ -359,6 +418,7 @@ export default class Application extends EventEmitter {
if (!is.macOS()) {
return
}
this.touchBarManager = new TouchBarManager()
}
@@ -366,6 +426,7 @@ export default class Application extends EventEmitter {
if (is.dev() || is.mas()) {
return
}
const protocols = this.configManager.getUserConfig('protocols', {})
this.protocolManager = new ProtocolManager({
protocols
@@ -515,14 +576,14 @@ export default class Application extends EventEmitter {
this.on('application:change-theme', (theme) => {
this.themeManager.updateAppAppearance(theme)
this.sendCommandToAll('application:theme', theme)
this.sendCommandToAll('application:update-theme', theme)
})
this.on('application:change-locale', (locale) => {
this.localeManager.changeLanguageByLocale(locale)
.then(() => {
this.menuManager.setup(locale)
this.trayManager.setup(locale)
this.menuManager.handleLocaleChange(locale)
})
})
@@ -601,10 +662,21 @@ export default class Application extends EventEmitter {
})
}
handleConfigChange (configName) {
this.sendCommandToAll('application:update-preference-config', configName)
}
handleEvents () {
// this.configManager.systemConfig.onDidAnyChange(() => {
// this.engineClient.changeGlobalOption(this.configManager.getSystemConfig())
// })
this.once('application:initialized', () => {
this.autoSyncTracker()
this.autoResumeTask()
this.adjustMenu()
})
this.configManager.userConfig.onDidAnyChange(() => this.handleConfigChange('user'))
this.configManager.systemConfig.onDidAnyChange(() => this.handleConfigChange('system'))
this.on('download-status-change', (downloading) => {
this.trayManager.updateTrayByStatus(downloading)
+6 -7
View File
@@ -1,19 +1,18 @@
import { app } from 'electron'
import { LOGIN_SETTING_OPTIONS } from '@shared/constants'
export default class AutoLaunchManager {
enable () {
return new Promise((resolve, reject) => {
const enabled = app.getLoginItemSettings().openAtLogin
const enabled = app.getLoginItemSettings(LOGIN_SETTING_OPTIONS).openAtLogin
if (enabled) {
resolve()
}
app.setLoginItemSettings({
openAtLogin: true,
// For Windows
args: [
'--opened-at-login=1'
]
...LOGIN_SETTING_OPTIONS,
openAtLogin: true
})
resolve()
})
@@ -28,7 +27,7 @@ export default class AutoLaunchManager {
isEnabled () {
return new Promise((resolve, reject) => {
const enabled = app.getLoginItemSettings().openAtLogin
const enabled = app.getLoginItemSettings(LOGIN_SETTING_OPTIONS).openAtLogin
resolve(enabled)
})
}
+7 -4
View File
@@ -13,9 +13,10 @@ import {
APP_RUN_MODE,
APP_THEME,
EMPTY_STRING,
IP_VERSION,
LOGIN_SETTING_OPTIONS,
NGOSANG_TRACKERS_BEST_IP_URL,
NGOSANG_TRACKERS_BEST_URL,
IP_VERSION
NGOSANG_TRACKERS_BEST_URL
} from '@shared/constants'
import { separateConfig } from '@shared/utils'
@@ -49,6 +50,7 @@ export default class ConfigManager {
'all-proxy': EMPTY_STRING,
'allow-overwrite': false,
'auto-file-renaming': true,
'bt-exclude-tracker': EMPTY_STRING,
'bt-tracker': EMPTY_STRING,
'continue': true,
'dht-file-path': getDhtPath(IP_VERSION.V4),
@@ -68,7 +70,7 @@ export default class ConfigManager {
'rpc-secret': EMPTY_STRING,
'seed-ratio': 1,
'seed-time': 60,
'split': 128,
'split': getMaxConnectionPerServer(),
'user-agent': 'Transmission/2.94'
}
/* eslint-enable quote-props */
@@ -102,6 +104,7 @@ export default class ConfigManager {
'locale': app.getLocale(),
'log-path': getLogPath(),
'new-task-show-downloading': true,
'no-confirm-before-delete-task': false,
'open-at-login': false,
'protocols': { 'magnet': true, 'thunder': false },
'resume-all-when-app-launched': false,
@@ -138,7 +141,7 @@ export default class ConfigManager {
fixUserConfig () {
// Fix the value of open-at-login when the user delete
// the Motrix self-starting item through startup management.
const openAtLogin = app.getLoginItemSettings().openAtLogin
const openAtLogin = app.getLoginItemSettings(LOGIN_SETTING_OPTIONS).openAtLogin
if (this.getUserConfig('open-at-login') !== openAtLogin) {
this.setUserConfig('open-at-login', openAtLogin)
}
+4 -1
View File
@@ -63,7 +63,7 @@ export default class Engine {
const sh = this.getStartSh()
logger.info('[Motrix] Engine start sh:', sh)
this.instance = forever.start(sh, {
max: is.dev() ? 1 : 100,
max: is.dev() ? 0 : 100,
parser: function (command, args) {
return {
command: command,
@@ -118,6 +118,9 @@ export default class Engine {
logger.error('[Motrix] Engine stop fail:', err.message)
this.forceStop(pid)
} finally {
this.instance.removeAllListeners('start')
this.instance.removeAllListeners('error')
this.instance.removeAllListeners('stop')
}
}
+5 -2
View File
@@ -44,10 +44,13 @@ export default class ProtocolManager extends EventEmitter {
logger.info(`[Motrix] protocol url: ${url}`)
if (
url.toLowerCase().startsWith('ftp:') ||
url.toLowerCase().startsWith('http:') ||
url.toLowerCase().startsWith('https:') ||
url.toLowerCase().startsWith('magnet:') ||
url.toLowerCase().startsWith('thunder:')
) {
return this.handleMagnetAndThunderProtocol(url)
return this.handleResourceProtocol(url)
}
if (
@@ -58,7 +61,7 @@ export default class ProtocolManager extends EventEmitter {
}
}
handleMagnetAndThunderProtocol (url) {
handleResourceProtocol (url) {
if (!url) {
return
}
+40 -25
View File
@@ -1,4 +1,4 @@
import NatAPI from 'nat-api'
import NatAPI from '@motrix/nat-api'
import logger from './Logger'
@@ -17,7 +17,9 @@ export default class UPnPManager {
return
}
client = new NatAPI()
client = new NatAPI({
autoUpdate: true
})
}
map (port) {
@@ -30,17 +32,21 @@ export default class UPnPManager {
return
}
client.map(port, (err) => {
if (err) {
logger.warn(`[Motrix] UPnPManager map ${port} failed, error: `, err)
reject(err.message)
return
}
try {
client.map(port, (err) => {
if (err) {
logger.warn(`[Motrix] UPnPManager map ${port} failed, error: `, err)
reject(err.message)
return
}
mappingStatus[port] = true
logger.info(`[Motrix] UPnPManager port ${port} mapping succeeded`)
resolve()
})
mappingStatus[port] = true
logger.info(`[Motrix] UPnPManager port ${port} mapping succeeded`)
resolve()
})
} catch (err) {
reject(err.message)
}
})
}
@@ -59,26 +65,35 @@ export default class UPnPManager {
return
}
client.unmap(port, (err) => {
if (err) {
logger.warn(`[Motrix] UPnPManager unmap ${port} failed, error: `, err)
reject(err.message)
return
}
try {
client.unmap(port, (err) => {
if (err) {
logger.warn(`[Motrix] UPnPManager unmap ${port} failed, error: `, err)
reject(err.message)
return
}
logger.info(`[Motrix] UPnPManager port ${port} unmapping succeeded`)
mappingStatus[port] = false
resolve()
})
logger.info(`[Motrix] UPnPManager port ${port} unmapping succeeded`)
mappingStatus[port] = false
resolve()
})
} catch (err) {
reject(err.message)
}
})
}
destroy () {
closeClient () {
if (!client) {
return
}
client.destroy()
client = null
try {
client.destroy(() => {
client = null
})
} catch (err) {
logger.warn('[Motrix] close UPnP client fail', err)
}
}
}
+2
View File
@@ -3,6 +3,8 @@ import is from 'electron-is'
import Launcher from './Launcher'
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
+8
View File
@@ -19,10 +19,18 @@ export default class DockManager extends EventEmitter {
}
show = isMac ? () => {
if (app.dock.isVisible()) {
return
}
return app.dock.show()
} : () => {}
hide = isMac ? () => {
if (!app.dock.isVisible()) {
return
}
app.dock.hide()
} : () => {}
+1 -1
View File
@@ -47,7 +47,7 @@ export default class MenuManager extends EventEmitter {
this.items = flattenMenuItems(menu)
}
rebuild () {
handleLocaleChange (locale) {
this.setup()
}
+15 -10
View File
@@ -1,40 +1,45 @@
import { EventEmitter } from 'events'
import { nativeTheme, systemPreferences } from 'electron'
import is from 'electron-is'
import { APP_THEME } from '@shared/constants'
import { getSystemTheme } from '../utils'
export default class ThemeManager extends EventEmitter {
constructor (options = {}) {
super()
this.options = options
this.init()
}
init () {
this.systemTheme = getSystemTheme()
this.handleEvents()
}
getSystemTheme () {
let result = APP_THEME.LIGHT
if (!is.macOS()) {
return result
}
result = nativeTheme.shouldUseDarkColors ? APP_THEME.DARK : APP_THEME.LIGHT
return result
return this.systemTheme
}
handleEvents () {
if (!is.macOS()) {
return
}
nativeTheme.on('updated', () => {
const theme = this.getSystemTheme()
this.updateAppAppearance(theme)
this.emit('system-theme-changed', theme)
const theme = getSystemTheme()
this.systemTheme = theme
console.log('nativeTheme updated===>', theme)
this.emit('system-theme-change', theme)
})
}
/**
* deprecated
* @see https://www.electronjs.org/docs/all#systempreferencessetapplevelappearanceappearance-macos-deprecated
*/
updateAppAppearance (theme) {
if (!is.macOS() || theme !== APP_THEME.LIGHT || theme !== APP_THEME.DARK) {
return
+28 -7
View File
@@ -64,13 +64,7 @@ export default class TrayManager extends EventEmitter {
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)
}
this.updateContextMenu()
}
init () {
@@ -79,10 +73,14 @@ export default class TrayManager extends EventEmitter {
}
handleEvents () {
// All OS
tray.on('click', this.handleTrayClick)
// macOS, Windows
tray.on('double-click', this.handleTrayDbClick)
tray.on('right-click', this.handleTrayRightClick)
// macOS only
tray.on('drop-files', this.handleTrayDropFile)
}
@@ -114,6 +112,8 @@ export default class TrayManager extends EventEmitter {
updateTray () {
const icon = this.status ? this.activeIcon : this.normalIcon
tray.setImage(icon)
this.updateContextMenu()
}
changeIconTheme (theme = APP_THEME.LIGHT) {
@@ -128,6 +128,8 @@ export default class TrayManager extends EventEmitter {
updateMenuStates (visibleStates, enabledStates, checkedStates) {
updateStates(this.items, visibleStates, enabledStates, checkedStates)
this.updateContextMenu()
}
updateMenuItemVisibleState (id, flag) {
@@ -144,7 +146,26 @@ export default class TrayManager extends EventEmitter {
this.updateMenuStates(null, enabledStates, null)
}
updateContextMenu () {
/**
* Linux requires setContextMenu to be called
* in order for the context menu to populate correctly
*/
if (process.platform !== 'linux') {
return
}
tray.setContextMenu(this.menu)
}
destroy () {
if (tray) {
tray.removeListener('click', this.handleTrayClick)
tray.removeListener('double-click', this.handleTrayDbClick)
tray.removeListener('right-click', this.handleTrayRightClick)
tray.removeListener('drop-files', this.handleTrayDropFile)
}
tray.destroy()
}
}
+25 -7
View File
@@ -105,6 +105,14 @@ export default class WindowManager extends EventEmitter {
}
})
window.on('enter-full-screen', () => {
this.emit('enter-full-screen', window)
})
window.on('leave-full-screen', () => {
this.emit('leave-full-screen', window)
})
this.handleWindowState(page, window)
this.handleWindowClose(pageOptions, page, window)
@@ -169,7 +177,15 @@ export default class WindowManager extends EventEmitter {
window.on('close', (event) => {
if (pageOptions.bindCloseToHide && !this.willQuit) {
event.preventDefault()
window.hide()
// @see https://github.com/electron/electron/issues/20263
if (window.isFullScreen()) {
window.once('leave-full-screen', () => window.hide())
window.setFullScreen(false)
} else {
window.hide()
}
}
const bounds = window.getBounds()
this.emit('window-closed', { page, bounds })
@@ -178,15 +194,16 @@ export default class WindowManager extends EventEmitter {
showWindow (page) {
const window = this.getWindow(page)
if (!window) {
if (!window || window.isVisible()) {
return
}
window.show()
}
autoHideWindow (page) {
hideWindow (page) {
const window = this.getWindow(page)
if (!window) {
if (!window || !window.isVisible()) {
return
}
window.hide()
@@ -203,10 +220,11 @@ export default class WindowManager extends EventEmitter {
if (!window) {
return
}
if (window.isVisible()) {
window.hide()
} else {
if (!window.isVisible() || window.isFullScreen()) {
window.show()
} else {
window.hide()
}
}
+13 -5
View File
@@ -1,12 +1,14 @@
import { app } from 'electron'
import { app, nativeTheme } from 'electron'
import is from 'electron-is'
import { resolve } from 'path'
import { existsSync, lstatSync } from 'fs'
import {
APP_THEME,
ENGINE_MAX_CONNECTION_PER_SERVER,
IP_VERSION
} from '@shared/constants'
import logger from '../core/Logger'
import engineBinMap from '../configs/engine'
@@ -100,13 +102,13 @@ export function parseArgvAsUrl (argv) {
export function checkIsSupportedSchema (url = '') {
const str = url.toLowerCase()
if (
str.startsWith('mo:') ||
str.startsWith('motrix:') ||
str.startsWith('ftp:') ||
str.startsWith('http:') ||
str.startsWith('https:') ||
str.startsWith('ftp:') ||
str.startsWith('magnet:') ||
str.startsWith('thunder:')
str.startsWith('thunder:') ||
str.startsWith('mo:') ||
str.startsWith('motrix:')
) {
return true
} else {
@@ -133,3 +135,9 @@ export function parseArgvAsFile (argv) {
export const getMaxConnectionPerServer = () => {
return ENGINE_MAX_CONNECTION_PER_SERVER
}
export const getSystemTheme = () => {
let result = APP_THEME.LIGHT
result = nativeTheme.shouldUseDarkColors ? APP_THEME.DARK : APP_THEME.LIGHT
return result
}
+20 -21
View File
@@ -1,6 +1,6 @@
import { ipcRenderer, remote } from 'electron'
import is from 'electron-is'
import { isEmpty } from 'lodash'
import { isEmpty, clone } from 'lodash'
import Aria2 from 'aria2'
import {
separateConfig,
@@ -18,13 +18,14 @@ export default class Api {
constructor (options = {}) {
this.options = options
this.client = null
this.init()
}
init () {
this.loadConfig()
this.initClient()
this.config = this.loadConfig()
this.client = this.initClient()
this.client.open()
}
loadConfigFromLocalStorage () {
@@ -47,7 +48,7 @@ export default class Api {
: this.loadConfigFromLocalStorage()
result = changeKeysToCamelCase(result)
this.config = result
return result
}
initClient () {
@@ -56,12 +57,11 @@ export default class Api {
rpcSecret: secret
} = this.config
const host = ENGINE_RPC_HOST
this.client = new Aria2({
return new Aria2({
host,
port,
secret
})
this.client.open()
}
closeClient () {
@@ -76,7 +76,7 @@ export default class Api {
fetchPreference () {
return new Promise((resolve) => {
this.loadConfig()
this.config = this.loadConfig()
resolve(this.config)
})
}
@@ -160,11 +160,10 @@ export default class Api {
}
changeOption (params = {}) {
let { gid, options = {} } = params
options = formatOptionsForEngine(options)
const { gid, options = {} } = params
const kebabOptions = changeKeysToKebabCase(options)
const args = compactUndefined([gid, kebabOptions])
const engineOptions = formatOptionsForEngine(options)
const args = compactUndefined([gid, engineOptions])
return this.client.call('changeOption', ...args)
}
@@ -180,11 +179,11 @@ export default class Api {
options
} = params
const tasks = uris.map((uri, index) => {
const kebabOptions = changeKeysToKebabCase(options)
const engineOptions = formatOptionsForEngine(options)
if (outs && outs[index]) {
kebabOptions.out = outs[index]
engineOptions.out = outs[index]
}
const args = compactUndefined([[uri], kebabOptions])
const args = compactUndefined([[uri], engineOptions])
return ['aria2.addUri', ...args]
})
return this.client.multicall(tasks)
@@ -195,8 +194,8 @@ export default class Api {
torrent,
options
} = params
const kebabOptions = changeKeysToKebabCase(options)
const args = compactUndefined([torrent, [], kebabOptions])
const engineOptions = formatOptionsForEngine(options)
const args = compactUndefined([torrent, [], engineOptions])
return this.client.call('addTorrent', ...args)
}
@@ -205,8 +204,8 @@ export default class Api {
metalink,
options
} = params
const kebabOptions = changeKeysToKebabCase(options)
const args = compactUndefined([metalink, kebabOptions])
const engineOptions = formatOptionsForEngine(options)
const args = compactUndefined([metalink, engineOptions])
return this.client.call('addMetalink', ...args)
}
@@ -328,8 +327,8 @@ export default class Api {
options = formatOptionsForEngine(options)
const data = gids.map((gid, index) => {
const kebabOptions = changeKeysToKebabCase(options)
const args = compactUndefined([gid, kebabOptions])
const _options = clone(options)
const args = compactUndefined([gid, _options])
return [method, ...args]
})
return this.client.multicall(data)
+1 -1
View File
@@ -41,7 +41,7 @@
...mapState('app', {
currentPage: state => state.currentPage
}),
asideDraggable: function () {
asideDraggable () {
return is.macOS()
}
},
+1 -1
View File
@@ -14,7 +14,7 @@
import {
openExternal
} from '@/components/Native/utils'
} from '@/utils/native'
export default {
name: 'mo-browser',
@@ -9,11 +9,11 @@ export default class CommandManager extends EventEmitter {
register (id, fn) {
if (this.commands[id]) {
console.log('Attempting to register an already-registered command: ' + id)
console.log('[Motrix] Attempting to register an already-registered command: ' + id)
return null
}
if (!id || !fn) {
console.error('Attempting to register a command with a missing id, or command function.')
console.error('[Motrix] Attempting to register a command with a missing id, or command function.')
return null
}
this.commands[id] = fn
@@ -21,6 +21,14 @@ export default class CommandManager extends EventEmitter {
this.emit('commandRegistered', id)
}
unregister (id) {
if (this.commands[id]) {
delete this.commands[id]
this.emit('commandUnregistered', id)
}
}
execute (id, ...args) {
var fn = this.commands[id]
if (fn) {
@@ -0,0 +1,3 @@
import CommandManager from '.'
export const commands = new CommandManager()
+1
View File
@@ -37,6 +37,7 @@
display: block;
width: 100%;
height: 100%;
outline: none;
text-align: center;
font-size: 0;
color: $--app-logo-color;
@@ -9,7 +9,7 @@
import {
showItemInFolder,
addToRecentTask
} from '@/components/Native/utils'
} from '@/utils/native'
import {
bytesToSize,
getTaskName,
@@ -18,7 +18,7 @@
export default {
name: 'mo-engine-client',
data: function () {
data () {
return {
downloading: false
}
@@ -39,11 +39,11 @@
})
},
watch: {
downloadSpeed (val, oldVal) {
downloadSpeed (val) {
const speed = val > 0 ? `${bytesToSize(val)}/s` : ''
this.$electron.ipcRenderer.send('event', 'download-speed-change', speed)
},
numActive (val, oldVal) {
numActive (val) {
this.downloading = val > 0
},
downloading (val, oldVal) {
@@ -62,6 +62,7 @@
onDownloadStart (event) {
this.$store.dispatch('task/fetchList')
this.$store.dispatch('app/resetInterval')
this.$store.dispatch('task/saveSession')
console.log('aria2 onDownloadStart', event)
const [{ gid }] = event
this.fetchTaskItem({ gid })
@@ -128,12 +129,12 @@
})
},
handleDownloadComplete (task, isBT) {
const path = getTaskFullPath(task)
this.showTaskCompleteNotify(task, isBT, path)
this.$store.dispatch('task/saveSession')
addToRecentTask(task)
const path = getTaskFullPath(task)
this.showTaskCompleteNotify(task, isBT, path)
this.$electron.ipcRenderer.send('event', 'task-download-complete', task, path)
},
showTaskCompleteNotify (task, isBT, path) {
+5 -25
View File
@@ -3,44 +3,24 @@
</template>
<script>
import is from 'electron-is'
import { mapState } from 'vuex'
import {
commands
} from '@/components/Command/index'
import { commands } from '@/components/CommandManager/instance'
export default {
name: 'mo-ipc',
computed: {
...mapState('preference', {
enableEggFeatures: state => state.config.enableEggFeatures
})
},
watch: {
},
methods: {
bindIpcEvents: function () {
bindIpcEvents () {
this.$electron.ipcRenderer.on('command', (event, command, ...args) => {
commands.execute(command, ...args)
})
},
unbindIpcEvents: function () {
unbindIpcEvents () {
this.$electron.ipcRenderer.removeAllListeners('command')
}
},
created: function () {
created () {
this.bindIpcEvents()
// id of the menu item
const visibleStates = {}
if (is.mas()) {
visibleStates['app.check-for-updates'] = false
if (!this.enableEggFeatures) {
visibleStates['task.new-bt-task'] = false
}
}
this.$electron.ipcRenderer.send('command', 'application:change-menu-states', visibleStates, null, null)
},
destroyed: function () {
destroyed () {
this.unbindIpcEvents()
}
}
@@ -15,7 +15,7 @@
props: {
},
methods: {
onFolderClick: function () {
onFolderClick () {
const self = this
this.$electron.remote.dialog.showOpenDialog({
properties: ['openDirectory', 'createDirectory']
@@ -8,7 +8,7 @@
import '@/components/Icons/folder'
import {
showItemInFolder
} from '@/components/Native/utils'
} from '@/utils/native'
export default {
name: 'mo-show-in-folder',
@@ -20,7 +20,7 @@
computed: {
},
methods: {
onFolderClick: function () {
onFolderClick () {
if (!this.path) {
return
}
+7 -5
View File
@@ -28,22 +28,22 @@
}
},
computed: {
win: function () {
win () {
return this.$electron.remote.getCurrentWindow()
}
},
methods: {
handleMinimize: function () {
handleMinimize () {
this.win.minimize()
},
handleMaximize: function () {
handleMaximize () {
if (this.win.isMaximized()) {
this.win.unmaximize()
} else {
this.win.maximize()
}
},
handleClose: function () {
handleClose () {
this.win.close()
}
}
@@ -73,9 +73,11 @@
padding: 0;
margin: 0;
z-index: 5100;
font-size: 0;
> li {
display: inline-block;
padding: 5px 15px;
padding: 5px 18px;
font-size: 16px;
margin: 0;
color: $--titlebar-actions-color;
&:hover {
@@ -94,6 +94,8 @@
<el-select
class="select-track-source"
v-model="form.trackerSource"
allow-create
filterable
multiple
>
<el-option-group
@@ -169,10 +171,10 @@
:label-width="formLabelWidth"
>
<el-row style="margin-bottom: 8px;">
<el-col class="form-item-sub" :span="10">
<el-col class="form-item-sub" :span="12">
<el-switch
v-model="form.enableUpnp"
active-text="UPnP"
active-text="UPnP/NAT-PMP"
>
</el-switch>
</el-col>
@@ -407,11 +409,11 @@
}
},
computed: {
isRenderer () { return is.renderer() },
isRenderer: () => is.renderer(),
title () {
return this.$t('preferences.advanced')
},
subnavs: function () {
subnavs () {
return [
{
key: 'basic',
@@ -463,8 +465,9 @@
const tracker = convertTrackerDataToLine(data)
this.form.lastSyncTrackerTime = Date.now()
this.form.btTracker = tracker
this.trackerSyncing = false
})
.finally(() => {
.catch((_) => {
this.trackerSyncing = false
})
},
@@ -589,6 +592,9 @@
.select-track-source {
width: 100%;
}
.el-select__tags {
overflow-x: auto;
}
}
}
.ua-group {
+20 -8
View File
@@ -176,6 +176,11 @@
{{ $t('preferences.task-completed-notify') }}
</el-checkbox>
</el-col>
<el-col class="form-item-sub" :span="24">
<el-checkbox v-model="form.noConfirmBeforeDeleteTask">
{{ $t('preferences.no-confirm-before-delete-task') }}
</el-checkbox>
</el-col>
</el-form-item>
</el-form>
<div class="form-actions">
@@ -204,7 +209,7 @@
import ThemeSwitcher from '@/components/Preference/ThemeSwitcher'
import { availableLanguages, getLanguage } from '@shared/locales'
import { getLocaleManager } from '@/components/Locale'
import { prettifyDir } from '@/components/Native/utils'
import { prettifyDir } from '@/utils/native'
import {
calcFormLabelWidth,
checkIsNeedRestart,
@@ -225,6 +230,7 @@
maxOverallDownloadLimit,
maxOverallUploadLimit,
newTaskShowDownloading,
noConfirmBeforeDeleteTask,
openAtLogin,
resumeAllWhenAppLaunched,
runMode,
@@ -244,6 +250,7 @@
maxOverallDownloadLimit,
maxOverallUploadLimit,
newTaskShowDownloading,
noConfirmBeforeDeleteTask,
openAtLogin,
resumeAllWhenAppLaunched,
runMode,
@@ -274,8 +281,8 @@
}
},
computed: {
isRenderer () { return is.renderer() },
isMas () { return is.mas() },
isRenderer: () => is.renderer(),
isMas: () => is.mas(),
isLinux () { return is.linux() },
title () {
return this.$t('preferences.basic')
@@ -324,7 +331,7 @@
}
]
},
subnavs: function () {
subnavs () {
return [
{
key: 'basic',
@@ -362,6 +369,7 @@
},
handleThemeChange (theme) {
this.form.theme = theme
// this.$store.dispatch('preference/changeThemeConfig', theme)
this.$electron.ipcRenderer.send('command',
'application:change-theme', theme)
},
@@ -403,11 +411,15 @@
this.$electron.ipcRenderer.send('command',
'application:open-at-login', openAtLogin)
this.$electron.ipcRenderer.send('command',
'application:toggle-dock', runMode === APP_RUN_MODE.STANDARD)
if ('runMode' in changed) {
this.$electron.ipcRenderer.send('command',
'application:toggle-dock', runMode === APP_RUN_MODE.STANDARD)
}
this.$electron.ipcRenderer.send('command',
'application:auto-hide-window', autoHideWindow)
if ('autoHideWindow' in changed) {
this.$electron.ipcRenderer.send('command',
'application:auto-hide-window', autoHideWindow)
}
if (checkIsNeedRestart(data)) {
this.$electron.ipcRenderer.send('command',
+2 -5
View File
@@ -10,11 +10,8 @@
<script>
export default {
name: 'mo-content-preference',
computed: {
},
components: {
},
methods: {
created () {
this.$store.dispatch('preference/fetchPreference')
}
}
</script>
+1 -1
View File
@@ -41,7 +41,7 @@
title () {
return this.$t('preferences.lab')
},
subnavs: function () {
subnavs () {
return [
{
key: 'basic',
@@ -27,7 +27,7 @@
default: APP_THEME.AUTO
}
},
data: function () {
data () {
return {
currentValue: this.value
}
@@ -54,7 +54,7 @@
}
},
watch: {
currentValue: function (val) {
currentValue (val) {
this.$emit('change', val)
}
},
@@ -47,12 +47,12 @@
}
},
computed: {
title: function () {
title () {
return this.$t('subnav.preferences')
}
},
methods: {
nav: function (category = 'basic') {
nav (category = 'basic') {
this.$router.push({
path: `/preference/${category}`
}).catch(err => {
@@ -47,12 +47,12 @@
}
},
computed: {
title: function () {
title () {
return this.$t('subnav.task-list')
}
},
methods: {
nav: function (status = 'active') {
nav (status = 'active') {
this.$router.push({
path: `/task/${status}`
}).catch(err => {
+52 -26
View File
@@ -49,7 +49,7 @@
:label-width="formLabelWidth"
>
<el-input-number
v-model="form.maxConnectionPerServer"
v-model="form.split"
controls-position="right"
:min="1"
:max="config.engineMaxConnectionPerServer"
@@ -172,7 +172,7 @@
import { isEmpty } from 'lodash'
import SelectDirectory from '@/components/Native/SelectDirectory'
import SelectTorrent from '@/components/Task/SelectTorrent'
import { prettifyDir } from '@/components/Native/utils'
import { prettifyDir } from '@/utils/native'
import { ADD_TASK_TYPE, NONE_SELECTED_FILES, SELECTED_ALL_FILES } from '@shared/constants'
import { detectResource, splitTaskLinks } from '@shared/utils'
import { buildOuts } from '@shared/utils/rename'
@@ -185,7 +185,8 @@
dir,
engineMaxConnectionPerServer,
maxConnectionPerServer,
newTaskShowDownloading
newTaskShowDownloading,
split
} = state.preference.config
const result = {
allProxy,
@@ -197,6 +198,7 @@
out: '',
referer: '',
selectFile: NONE_SELECTED_FILES,
split,
torrent: '',
uris: addTaskUrl,
userAgent: '',
@@ -230,23 +232,23 @@
}
},
computed: {
isRenderer () { return is.renderer() },
isMas () { return is.mas() },
taskType: function () {
return this.type
},
downloadDir: function () {
return prettifyDir(this.form.dir)
},
isRenderer: () => is.renderer(),
isMas: () => is.mas(),
...mapState('app', {
taskList: state => state.taskList
}),
...mapState('preference', {
config: state => state.config
})
}),
taskType () {
return this.type
},
downloadDir () {
return prettifyDir(this.form.dir)
}
},
watch: {
taskType: function (current, previous) {
taskType (current, previous) {
if (this.visible && previous === ADD_TASK_TYPE.URI) {
return
}
@@ -256,18 +258,16 @@
this.$refs.uri && this.$refs.uri.focus()
}, 50)
}
},
visible (current) {
if (current === true) {
document.addEventListener('keydown', this.handleHotkey)
} else {
document.removeEventListener('keydown', this.handleHotkey)
}
}
},
methods: {
handleOpen () {
this.form = initialForm(this.$store.state)
if (this.taskType === ADD_TASK_TYPE.URI) {
this.autofillResourceLink()
setTimeout(() => {
this.$refs.uri && this.$refs.uri.focus()
}, 50)
}
},
autofillResourceLink () {
const content = this.$electron.clipboard.readText()
const hasResource = detectResource(content)
@@ -278,9 +278,21 @@
this.form.uris = content
}
},
handleOpen () {
this.form = initialForm(this.$store.state)
if (this.taskType === ADD_TASK_TYPE.URI) {
this.autofillResourceLink()
setTimeout(() => {
this.$refs.uri && this.$refs.uri.focus()
}, 50)
}
},
handleOpened () {
this.detectThunderResource(this.form.uris)
},
handleCancel (formName) {
this.$store.dispatch('app/hideAddTaskDialog')
},
handleClose (done) {
this.$store.dispatch('app/hideAddTaskDialog')
this.$store.dispatch('app/updateAddTaskOptions', {})
@@ -288,6 +300,13 @@
handleClosed () {
this.reset()
},
handleHotkey (event) {
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
event.preventDefault()
this.submitForm('taskForm')
}
},
handleTabClick (tab, event) {
this.$store.dispatch('app/changeAddTaskType', tab.name)
},
@@ -317,9 +336,6 @@
this.showAdvanced = false
this.form = initialForm(this.$store.state)
},
handleCancel (formName) {
this.$store.dispatch('app/hideAddTaskDialog')
},
buildHeader (form) {
const { userAgent, referer, cookie } = form
const result = []
@@ -336,7 +352,13 @@
return result
},
buildOption (type, form) {
const { allProxy, dir, out, selectFile } = form
const {
allProxy,
dir,
out,
selectFile,
split
} = form
const result = {}
if (!isEmpty(allProxy)) {
@@ -351,6 +373,10 @@
result.out = out
}
if (split > 0) {
result.split = split
}
if (type === ADD_TASK_TYPE.TORRENT) {
if (
selectFile !== SELECTED_ALL_FILES &&
+317 -3
View File
@@ -33,10 +33,24 @@
</template>
<script>
import { mapState } from 'vuex'
import * as clipboard from 'clipboard-polyfill'
import { commands } from '@/components/CommandManager/instance'
import { ADD_TASK_TYPE } from '@shared/constants'
import TaskSubnav from '@/components/Subnav/TaskSubnav'
import TaskActions from '@/components/Task/TaskActions'
import TaskList from '@/components/Task/TaskList'
import SubnavSwitcher from '@/components/Subnav/SubnavSwitcher'
import {
getTaskUri,
parseHeader
} from '@shared/utils'
import {
delayDeleteTaskFiles,
showItemInFolder,
moveTaskFilesToTrash
} from '@/utils/native'
export default {
name: 'mo-content-task',
@@ -53,7 +67,15 @@
}
},
computed: {
subnavs: function () {
...mapState('task', {
taskList: state => state.taskList,
selectedGidList: state => state.selectedGidList,
selectedGidListCount: state => state.selectedGidList.length
}),
...mapState('preference', {
noConfirmBeforeDelete: state => state.config.noConfirmBeforeDeleteTask
}),
subnavs () {
return [
{
key: 'active',
@@ -72,7 +94,7 @@
}
]
},
title: function () {
title () {
const subnav = this.subnavs.find((item) => item.key === this.status)
return subnav.title
}
@@ -86,10 +108,302 @@
},
changeCurrentList () {
this.$store.dispatch('task/changeCurrentList', this.status)
},
directAddTask (uri, options = {}) {
const uris = [uri]
const payload = {
uris,
options: {
...options
}
}
this.$store.dispatch('task/addUri', payload)
.catch((err) => {
this.$msg.error(err.message)
})
},
showAddTaskDialog (uri, options = {}) {
const {
header,
...rest
} = options
console.log('[Motrix] show add task dialog options: ', options)
const headers = parseHeader(header)
const newOptions = {
...rest,
...headers
}
this.$store.dispatch('app/updateAddTaskUrl', uri)
this.$store.dispatch('app/updateAddTaskOptions', newOptions)
this.$store.dispatch('app/showAddTaskDialog', ADD_TASK_TYPE.URI)
},
deleteTaskFiles (task) {
try {
const result = moveTaskFilesToTrash(task)
if (!result) {
throw new Error('task.remove-task-file-fail')
}
} catch (err) {
this.$msg.error(this.$t(err.message))
}
},
removeTask (task, taskName, isRemoveWithFiles = false) {
this.$store.dispatch('task/forcePauseTask', task)
.finally(() => {
if (isRemoveWithFiles) {
this.deleteTaskFiles(task)
}
return this.removeTaskItem(task, taskName)
})
},
removeTaskRecord (task, taskName, isRemoveWithFiles = false) {
this.$store.dispatch('task/forcePauseTask', task)
.finally(() => {
if (isRemoveWithFiles) {
this.deleteTaskFiles(task)
}
return this.removeTaskRecordItem(task, taskName)
})
},
removeTaskItem (task, taskName) {
return this.$store.dispatch('task/removeTask', task)
.then(() => {
this.$msg.success(this.$t('task.delete-task-success', {
taskName
}))
})
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.delete-task-fail', {
taskName
}))
}
})
},
removeTaskRecordItem (task, taskName) {
return this.$store.dispatch('task/removeTaskRecord', task)
.then(() => {
this.$msg.success(this.$t('task.remove-record-success', {
taskName
}))
})
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.remove-record-fail', {
taskName
}))
}
})
},
removeTasks (taskList, isRemoveWithFiles = false) {
const gids = taskList.map((task) => task.gid)
this.$store.dispatch('task/batchForcePauseTask', gids)
.finally(() => {
if (isRemoveWithFiles) {
this.batchDeleteTaskFiles(taskList)
}
this.removeTaskItems(gids)
})
},
batchDeleteTaskFiles (taskList) {
const promises = taskList.map((task, index) => delayDeleteTaskFiles(task, index * 200))
Promise.all(promises).then(values => {
console.log('[Motrix] batch delete task files: ', values)
})
},
removeTaskItems (gids) {
this.$store.dispatch('task/batchRemoveTask', gids)
.then(() => {
this.$msg.success(this.$t('task.batch-delete-task-success'))
})
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.batch-delete-task-fail'))
}
})
},
handlePauseTask (payload) {
const { task, taskName } = payload
this.$msg.info(this.$t('task.download-pause-message', { taskName }))
this.$store.dispatch('task/pauseTask', task)
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.pause-task-fail', { taskName }))
}
})
},
handleResumeTask (payload) {
const { task, taskName } = payload
this.$store.dispatch('task/resumeTask', task)
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.resume-task-fail', {
taskName
}))
}
})
},
handleStopTaskSeeding (payload) {
const { task } = payload
this.$store.dispatch('task/stopSeeding', task)
},
handleRestartTask (payload) {
const { task, taskName, showDialog } = payload
const { gid } = task
const uri = getTaskUri(task)
this.$store.dispatch('task/getTaskOption', gid)
.then((data) => {
console.log('[Motrix] get task option:', data)
const { dir, header, split } = data
const options = {
dir,
header,
split,
out: taskName
}
if (showDialog) {
this.showAddTaskDialog(uri, options)
} else {
this.directAddTask(uri, options)
this.$store.dispatch('task/removeTaskRecord', task)
}
})
},
handleRevealInFolder (payload) {
const { path } = payload
showItemInFolder(path, {
errorMsg: this.$t('task.file-not-exist')
})
},
handleDeleteTask (payload) {
const { task, taskName, deleteWithFiles } = payload
const { noConfirmBeforeDelete } = this
if (noConfirmBeforeDelete) {
this.removeTask(task, taskName, deleteWithFiles)
return
}
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: this.$t('task.delete-task'),
message: this.$t('task.delete-task-confirm', { taskName }),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1,
checkboxLabel: this.$t('task.delete-task-label'),
checkboxChecked: deleteWithFiles
}).then(({ response, checkboxChecked }) => {
if (response === 0) {
this.removeTask(task, taskName, checkboxChecked)
}
})
},
handleDeleteTaskRecord (payload) {
const { task, taskName, deleteWithFiles } = payload
const { noConfirmBeforeDelete } = this
if (noConfirmBeforeDelete) {
this.removeTaskRecord(task, taskName, deleteWithFiles)
return
}
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: this.$t('task.remove-record'),
message: this.$t('task.remove-record-confirm', { taskName }),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1,
checkboxLabel: this.$t('task.remove-record-label'),
checkboxChecked: !!deleteWithFiles
}).then(({ response, checkboxChecked }) => {
if (response === 0) {
this.removeTaskRecord(task, taskName, checkboxChecked)
}
})
},
handleBatchDeleteTask (payload) {
const { deleteWithFiles } = payload
const {
noConfirmBeforeDelete,
selectedGidList,
selectedGidListCount,
taskList
} = this
if (selectedGidListCount === 0) {
return
}
const selectedTaskList = taskList.filter((task) => {
return selectedGidList.includes(task.gid)
})
if (noConfirmBeforeDelete) {
this.removeTasks(selectedTaskList, deleteWithFiles)
return
}
const count = `${selectedGidListCount}`
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: this.$t('task.delete-selected-task'),
message: this.$t('task.batch-delete-task-confirm', { count }),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1,
checkboxLabel: this.$t('task.delete-task-label'),
checkboxChecked: deleteWithFiles
}).then(({ response, checkboxChecked }) => {
if (response === 0) {
this.removeTasks(selectedTaskList, checkboxChecked)
}
})
},
handleCopyTaskLink (payload) {
const { task } = payload
const uri = getTaskUri(task)
clipboard.writeText(uri)
.then(() => {
this.$msg.success(this.$t('task.copy-link-success'))
})
},
handleShowTaskInfo (payload) {
const { task } = payload
this.$store.dispatch('task/showTaskItemInfoDialog', task)
}
},
created: function () {
created () {
this.changeCurrentList()
},
mounted () {
commands.on('pause-task', this.handlePauseTask)
commands.on('resume-task', this.handleResumeTask)
commands.on('stop-task-seeding', this.handleStopTaskSeeding)
commands.on('restart-task', this.handleRestartTask)
commands.on('reveal-in-folder', this.handleRevealInFolder)
commands.on('delete-task', this.handleDeleteTask)
commands.on('delete-task-record', this.handleDeleteTaskRecord)
commands.on('batch-delete-task', this.handleBatchDeleteTask)
commands.on('copy-task-link', this.handleCopyTaskLink)
commands.on('show-task-info', this.handleShowTaskInfo)
},
destroyed () {
commands.off('pause-task', this.handlePauseTask)
commands.off('resume-task', this.handleResumeTask)
commands.off('stop-task-seeding', this.handleStopTaskSeeding)
commands.off('restart-task', this.handleRestartTask)
commands.off('reveal-in-folder', this.handleRevealInFolder)
commands.off('delete-task', this.handleDeleteTask)
commands.off('delete-task-record', this.handleDeleteTaskRecord)
commands.off('batch-delete-task', this.handleBatchDeleteTask)
commands.off('copy-task-link', this.handleCopyTaskLink)
commands.off('show-task-info', this.handleShowTaskInfo)
}
}
</script>
@@ -129,25 +129,25 @@
}
},
computed: {
...mapState('preference', {
config: state => state.config
}),
...mapState('app', {
torrents: state => state.addTaskTorrents
}),
isTorrentsEmpty: function () {
...mapState('preference', {
config: state => state.config
}),
isTorrentsEmpty () {
return this.torrents.length === 0
},
selectedFilesCount: function () {
selectedFilesCount () {
return this.selectedFiles.length
},
selectedFilesTotalSize: function () {
selectedFilesTotalSize () {
const result = this.selectedFiles.reduce((acc, cur) => {
return acc + cur.length
}, 0)
return bytesToSize(result)
},
selectedFileIndex: function () {
selectedFileIndex () {
const { files, selectedFiles } = this
if (files.length === 0 || selectedFiles.length === 0) {
return NONE_SELECTED_FILES
@@ -174,7 +174,7 @@
parseTorrent.remote(file.raw, (err, parsedTorrent) => {
if (err) throw err
console.log(parsedTorrent)
console.log('[Motrix] parsed torrent: ', parsedTorrent)
this.files = listTorrentFiles(parsedTorrent.files)
this.$refs.torrentTable.toggleAllSelection()
+33 -84
View File
@@ -3,8 +3,8 @@
<el-tooltip
class="item hidden-md-and-up"
effect="dark"
:content="$t('task.new-task')"
placement="bottom"
:content="$t('task.new-task')"
>
<i class="task-action" @click.stop="onAddClick">
<mo-icon name="menu-add" width="14" height="14" />
@@ -15,6 +15,7 @@
effect="dark"
placement="bottom"
:content="$t('task.delete-selected-tasks')"
v-if="currentList !== 'stopped'"
>
<i
class="task-action"
@@ -23,17 +24,32 @@
<mo-icon name="delete" width="14" height="14" />
</i>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="$t('task.refresh-list')" placement="bottom">
<el-tooltip
class="item"
effect="dark"
placement="bottom"
:content="$t('task.refresh-list')"
>
<i class="task-action" @click="onRefreshClick">
<mo-icon name="refresh" width="14" height="14" :spin="refreshing" />
</i>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="$t('task.resume-all-task')" placement="bottom">
<el-tooltip
class="item"
effect="dark"
placement="bottom"
:content="$t('task.resume-all-task')"
>
<i class="task-action" @click="onResumeAllClick">
<mo-icon name="task-start-line" width="14" height="14" />
</i>
</el-tooltip>
<el-tooltip class="item" effect="dark" :content="$t('task.pause-all-task')" placement="bottom">
<el-tooltip
class="item"
effect="dark"
placement="bottom"
:content="$t('task.pause-all-task')"
>
<i class="task-action" @click="onPauseAllClick">
<mo-icon name="task-pause-line" width="14" height="14" />
</i>
@@ -41,8 +57,8 @@
<el-tooltip
class="item"
effect="dark"
:content="$t('task.purge-record')"
placement="bottom"
:content="$t('task.purge-record')"
v-if="currentList === 'stopped'"
>
<i class="task-action" @click="onPurgeRecordClick">
@@ -54,11 +70,10 @@
<script>
import { mapState } from 'vuex'
import { commands } from '@/components/CommandManager/instance'
import { ADD_TASK_TYPE } from '@shared/constants'
import { bytesToSize, timeFormat } from '@shared/utils'
import {
moveTaskFilesToTrash
} from '@/components/Native/utils'
import '@/components/Icons/menu-add'
import '@/components/Icons/refresh'
import '@/components/Icons/task-start-line'
@@ -72,7 +87,7 @@
components: {
},
props: ['task'],
data: function () {
data () {
return {
refreshing: false
}
@@ -80,8 +95,6 @@
computed: {
...mapState('task', {
currentList: state => state.currentList,
taskList: state => state.taskList,
selectedGidList: state => state.selectedGidList,
selectedGidListCount: state => state.selectedGidList.length
})
},
@@ -90,7 +103,7 @@
timeFormat
},
methods: {
refreshSpin: function () {
refreshSpin () {
this.t && clearTimeout(this.t)
this.refreshing = true
@@ -98,79 +111,15 @@
this.refreshing = false
}, 500)
},
delayDeleteTaskFiles (task, delay) {
return new Promise((resolve) => {
setTimeout(() => {
try {
const result = moveTaskFilesToTrash(task)
resolve(result)
} catch (err) {
console.log('[Motrix] batch delay delete task files fail', err)
resolve(false)
}
}, delay)
})
onBatchDeleteClick (event) {
const deleteWithFiles = !!event.shiftKey
commands.emit('batch-delete-task', { deleteWithFiles })
},
batchDeleteTaskFiles (taskList) {
const promises = taskList.map((task, index) => this.delayDeleteTaskFiles(task, index * 200))
Promise.all(promises).then(values => {
console.log(values)
})
},
removeTasks (taskList, isRemoveWithFiles) {
const gids = taskList.map((task) => task.gid)
this.$store.dispatch('task/batchForcePauseTask', gids)
.finally(() => {
if (isRemoveWithFiles) {
this.batchDeleteTaskFiles(taskList)
}
this.removeTaskItems(gids)
})
},
removeTaskItems (gids) {
this.$store.dispatch('task/batchRemoveTask', gids)
.then(() => {
this.$msg.success(this.$t('task.batch-delete-task-success'))
})
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.batch-delete-task-fail'))
}
})
},
onBatchDeleteClick: function (event) {
const self = this
const { taskList, selectedGidList, selectedGidListCount } = this
if (selectedGidListCount === 0) {
event.preventDefault()
return
}
const selectedTaskList = taskList.filter((task) => {
return selectedGidList.includes(task.gid)
})
const count = `${selectedGidListCount}`
const isChecked = !!event.shiftKey
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
title: this.$t('task.delete-selected-task'),
message: this.$t('task.batch-delete-task-confirm', { count }),
buttons: [this.$t('app.yes'), this.$t('app.no')],
cancelId: 1,
checkboxLabel: this.$t('task.delete-task-label'),
checkboxChecked: isChecked
}).then(({ response, checkboxChecked }) => {
if (response === 0) {
self.removeTasks(selectedTaskList, checkboxChecked)
}
})
},
onRefreshClick: function () {
onRefreshClick () {
this.refreshSpin()
this.$store.dispatch('task/fetchList')
},
onResumeAllClick: function () {
onResumeAllClick () {
this.$store.dispatch('task/resumeAllTask')
.then(() => {
this.$msg.success(this.$t('task.resume-all-task-success'))
@@ -181,7 +130,7 @@
}
})
},
onPauseAllClick: function () {
onPauseAllClick () {
this.$store.dispatch('task/pauseAllTask')
.then(() => {
this.$msg.success(this.$t('task.pause-all-task-success'))
@@ -192,7 +141,7 @@
}
})
},
onPurgeRecordClick: function () {
onPurgeRecordClick () {
this.$store.dispatch('task/purgeTaskRecord')
.then(() => {
this.$msg.success(this.$t('task.purge-record-success'))
@@ -203,7 +152,7 @@
}
})
},
onAddClick: function () {
onAddClick () {
this.$store.dispatch('app/showAddTaskDialog', ADD_TASK_TYPE.URI)
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
getTaskName
} from '@shared/utils'
import { TASK_STATUS } from '@shared/constants'
import { openItem } from '@/components/Native/utils'
import { openItem } from '@/utils/native'
import TaskItemActions from './TaskItemActions'
import TaskProgress from './TaskProgress'
import TaskProgressInfo from './TaskProgressInfo'
+46 -186
View File
@@ -36,19 +36,15 @@
</template>
<script>
import { mapState } from 'vuex'
import is from 'electron-is'
import * as clipboard from 'clipboard-polyfill'
import { ADD_TASK_TYPE, TASK_STATUS } from '@shared/constants'
import {
showItemInFolder,
moveTaskFilesToTrash
} from '@/components/Native/utils'
import { commands } from '@/components/CommandManager/instance'
import { TASK_STATUS } from '@shared/constants'
import {
checkTaskIsSeeder,
getTaskFullPath,
getTaskName,
getTaskUri,
parseHeader
getTaskName
} from '@shared/utils'
import '@/components/Icons/task-start-line'
import '@/components/Icons/task-pause-line'
@@ -84,6 +80,9 @@
}
},
computed: {
...mapState('preference', {
noConfirmBeforeDelete: state => state.config.noConfirmBeforeDeleteTask
}),
taskName () {
return getTaskName(this.task)
},
@@ -117,206 +116,67 @@
}
},
methods: {
deleteTaskFiles (task) {
try {
const result = moveTaskFilesToTrash(task)
if (!result) {
throw new Error('task.remove-task-file-fail')
}
} catch (err) {
this.$msg.error(this.$t(err.message))
}
},
removeTask (task, isRemoveWithFiles) {
this.$store.dispatch('task/forcePauseTask', task)
.finally(() => {
if (isRemoveWithFiles) {
this.deleteTaskFiles(task)
}
return this.removeTaskItem(task)
})
},
removeTaskItem (task) {
return this.$store.dispatch('task/removeTask', this.task)
.then(() => {
this.$msg.success(this.$t('task.delete-task-success', {
taskName: this.taskName
}))
})
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.delete-task-fail', {
taskName: this.taskName
}))
}
})
},
removeTaskRecord (task, isRemoveWithFiles) {
this.$store.dispatch('task/forcePauseTask', task)
.finally(() => {
if (isRemoveWithFiles) {
this.deleteTaskFiles(task)
}
return this.removeTaskRecordItem(task)
})
},
removeTaskRecordItem (task) {
return this.$store.dispatch('task/removeTaskRecord', this.task)
.then(() => {
this.$msg.success(this.$t('task.remove-record-success', {
taskName: this.taskName
}))
})
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.remove-record-fail', {
taskName: this.taskName
}))
}
})
},
onResumeClick () {
this.$store.dispatch('task/resumeTask', this.task)
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.resume-task-fail', {
taskName: this.taskName
}))
}
})
const { task, taskName } = this
commands.emit('resume-task', {
task,
taskName
})
},
onRestartClick (event) {
const { task, taskName } = this
const { gid, status } = task
const uri = getTaskUri(task)
const isNeedShowDialog = status === TASK_STATUS.COMPLETE || !!event.altKey
this.$store.dispatch('task/getTaskOption', gid)
.then((data) => {
console.log('[Motrix] get task option:', data)
const { dir, header, maxConnectionPerServer } = data
const options = {
dir,
header,
maxConnectionPerServer,
out: taskName
}
if (isNeedShowDialog) {
this.showAddTaskDialog(uri, options)
} else {
this.directAddTask(uri, options)
this.$store.dispatch('task/removeTaskRecord', task)
}
})
},
directAddTask (uri, options = {}) {
const uris = [uri]
const payload = {
uris,
options: {
...options
}
}
this.$store.dispatch('task/addUri', payload)
.catch((err) => {
this.$msg.error(err.message)
})
},
showAddTaskDialog (uri, options = {}) {
const {
header,
...rest
} = options
const headers = parseHeader(header)
const newOptions = {
...rest,
...headers
}
this.$store.dispatch('app/updateAddTaskUrl', uri)
this.$store.dispatch('app/updateAddTaskOptions', newOptions)
this.$store.dispatch('app/showAddTaskDialog', ADD_TASK_TYPE.URI)
const { status } = task
const showDialog = status === TASK_STATUS.COMPLETE || !!event.altKey
commands.emit('restart-task', {
task,
taskName,
showDialog
})
},
onPauseClick () {
this.pauseTask()
const { task, taskName } = this
commands.emit('pause-task', {
task,
taskName
})
},
onStopClick () {
this.stopSeeding()
},
stopSeeding () {
if (!this.isSeeder) {
return
}
this.$store.dispatch('task/stopSeeding', this.task)
},
pauseTask () {
const { taskName } = this
this.$msg.info(this.$t('task.download-pause-message', { taskName }))
this.$store.dispatch('task/pauseTask', this.task)
.catch(({ code }) => {
if (code === 1) {
this.$msg.error(this.$t('task.pause-task-fail', { taskName }))
}
})
const { task } = this
commands.emit('stop-task-seeding', { task })
},
onDeleteClick (event) {
const self = this
const { task } = this
const isChecked = !!event.shiftKey
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
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: this.$t('task.delete-task-label'),
checkboxChecked: isChecked
}).then(({ response, checkboxChecked }) => {
if (response === 0) {
self.removeTask(task, checkboxChecked)
}
const { task, taskName } = this
const deleteWithFiles = !!event.shiftKey
commands.emit('delete-task', {
task,
taskName,
deleteWithFiles
})
},
onTrashClick (event) {
const self = this
const { task } = this
const isChecked = !!event.shiftKey
this.$electron.remote.dialog.showMessageBox({
type: 'warning',
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: this.$t('task.remove-record-label'),
checkboxChecked: isChecked
}).then(({ response, checkboxChecked }) => {
if (response === 0) {
self.removeTaskRecord(task, checkboxChecked)
}
const { task, taskName } = this
const deleteWithFiles = !!event.shiftKey
commands.emit('delete-task-record', {
task,
taskName,
deleteWithFiles
})
},
onFolderClick () {
showItemInFolder(this.path, {
errorMsg: this.$t('task.file-not-exist')
})
const { path } = this
commands.emit('reveal-in-folder', { path })
},
onLinkClick () {
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'))
})
})
const { task } = this
commands.emit('copy-task-link', { task })
},
onInfoClick () {
this.$store.dispatch('task/showTaskItemInfoDialog', this.task)
const { task } = this
commands.emit('show-task-info', { task })
},
onMoreClick () {
}
@@ -54,19 +54,19 @@
}
},
computed: {
taskFullName: function () {
taskFullName () {
return getTaskName(this.task, {
defaultName: this.$t('task.get-task-name'),
maxLen: -1
})
},
taskName: function () {
taskName () {
return getTaskName(this.task, {
defaultName: this.$t('task.get-task-name'),
maxLen: 32
})
},
dialogTitle: function () {
dialogTitle () {
return this.$t('task.task-info-dialog-title', { title: this.taskName })
}
},
@@ -1,8 +1,9 @@
<template>
<el-row class="task-progress-info">
<el-col :span="8" class="task-progress-info-left">
<div v-if="task.totalLength > 0">
{{ task.completedLength | bytesToSize }} / {{ task.totalLength | bytesToSize }}
<div v-if="task.completedLength > 0 || task.totalLength > 0">
<span>{{ task.completedLength | bytesToSize }}</span>
<span v-if="task.totalLength > 0"> / {{ task.totalLength | bytesToSize }}</span>
</div>
</el-col>
<el-col :span="16" class="task-progress-info-right">
+9 -9
View File
@@ -29,7 +29,7 @@
[Ipc.name]: Ipc
},
computed: {
isRenderer () { return is.renderer() },
isRenderer: () => is.renderer(),
...mapState('app', {
systemTheme: state => state.systemTheme
}),
@@ -42,38 +42,38 @@
locale: state => state.config.locale,
dir: state => getLangDirection(state.config.locale)
}),
themeClass: function () {
themeClass () {
if (this.theme === APP_THEME.AUTO) {
return `theme-${this.systemTheme}`
} else {
return `theme-${this.theme}`
}
},
i18nClass: function () {
i18nClass () {
return `i18n-${this.locale}`
},
dirClass: function () {
dirClass () {
return `dir-${this.dir}`
}
},
methods: {
updateRootClassName: function () {
updateRootClassName () {
const { themeClass = '', i18nClass = '', dirClass = '' } = this
const className = `${themeClass} ${i18nClass} ${dirClass}`
document.documentElement.className = className
}
},
beforeMount: function () {
beforeMount () {
this.updateRootClassName()
},
watch: {
themeClass: function (val, oldVal) {
themeClass (val, oldVal) {
this.updateRootClassName()
},
i18nClass: function (val, oldVal) {
i18nClass (val, oldVal) {
this.updateRootClassName()
},
dirClass: function (val, oldVal) {
dirClass (val, oldVal) {
this.updateRootClassName()
}
}
@@ -6,35 +6,34 @@ import store from '@/store'
import { buildFileList } from '@shared/utils'
import { ADD_TASK_TYPE } from '@shared/constants'
import { getLocaleManager } from '@/components/Locale'
import CommandManager from './CommandManager'
import { commands } from '@/components/CommandManager/instance'
const commands = new CommandManager()
const i18n = getLocaleManager().getI18n()
function updateSystemTheme (theme) {
const updateSystemTheme = (theme) => {
store.dispatch('app/updateSystemTheme', theme)
}
function updateTheme (theme) {
const updateTheme = (theme) => {
store.dispatch('preference/changeThemeConfig', theme)
}
function showAboutPanel () {
const showAboutPanel = () => {
store.dispatch('app/showAboutPanel')
}
function showAddTask (taskType = ADD_TASK_TYPE.URI, task = '') {
if (taskType === ADD_TASK_TYPE.URI && task) {
store.dispatch('app/updateAddTaskUrl', task)
const showAddTask = (taskType = ADD_TASK_TYPE.URI, uri = '') => {
if (taskType === ADD_TASK_TYPE.URI && uri) {
store.dispatch('app/updateAddTaskUrl', uri)
}
store.dispatch('app/showAddTaskDialog', taskType)
}
function showAddBtTask () {
const showAddBtTask = () => {
store.dispatch('app/showAddTaskDialog', ADD_TASK_TYPE.TORRENT)
}
function showAddBtTaskWithFile (fileName, base64Data = '') {
const showAddBtTaskWithFile = (fileName, base64Data = '') => {
const blob = base64StringToBlob(base64Data, 'application/x-bittorrent')
const file = new File([blob], fileName, { type: 'application/x-bittorrent' })
const fileList = buildFileList(file)
@@ -44,63 +43,67 @@ function showAddBtTaskWithFile (fileName, base64Data = '') {
}, 200)
}
function navigateTaskList (status = 'active') {
const navigateTaskList = (status = 'active') => {
router.push({ path: `/task/${status}` }).catch(err => {
console.log(err)
})
}
function navigatePreferences () {
const navigatePreferences = () => {
router.push({ path: '/preference' }).catch(err => {
console.log(err)
})
}
function showUnderDevelopmentMessage () {
const showUnderDevelopmentMessage = () => {
Message.info(i18n.t('app.under-development-message'))
}
function pauseTask () {
const pauseTask = () => {
store.dispatch('task/batchPauseSelectedTasks')
}
function resumeTask () {
const resumeTask = () => {
store.dispatch('task/batchResumeSelectedTasks')
}
function deleteTask () {
const deleteTask = () => {
commands.emit('batch-delete-task', {
deleteWithFiles: false
})
}
const moveTaskUp = () => {
showUnderDevelopmentMessage()
}
function moveTaskUp () {
const moveTaskDown = () => {
showUnderDevelopmentMessage()
}
function moveTaskDown () {
showUnderDevelopmentMessage()
}
function pauseAllTask () {
const pauseAllTask = () => {
store.dispatch('task/pauseAllTask')
}
function resumeAllTask () {
const resumeAllTask = () => {
store.dispatch('task/resumeAllTask')
}
function selectAllTask () {
const selectAllTask = () => {
store.dispatch('task/selectAllTask')
}
commands.register('application:system-theme', updateSystemTheme)
commands.register('application:theme', updateTheme)
const fetchPreference = () => {
store.dispatch('preference/fetchPreference')
}
commands.register('application:task-list', navigateTaskList)
commands.register('application:preferences', navigatePreferences)
commands.register('application:about', showAboutPanel)
commands.register('application:new-task', showAddTask)
commands.register('application:new-bt-task', showAddBtTask)
commands.register('application:new-bt-task-with-file', showAddBtTaskWithFile)
commands.register('application:task-list', navigateTaskList)
commands.register('application:preferences', navigatePreferences)
commands.register('application:pause-task', pauseTask)
commands.register('application:resume-task', resumeTask)
commands.register('application:delete-task', deleteTask)
@@ -110,6 +113,6 @@ commands.register('application:pause-all-task', pauseAllTask)
commands.register('application:resume-all-task', resumeAllTask)
commands.register('application:select-all-task', selectAllTask)
export {
commands
}
commands.register('application:update-preference-config', fetchPreference)
commands.register('application:update-system-theme', updateSystemTheme)
commands.register('application:update-theme', updateTheme)
+6 -2
View File
@@ -12,6 +12,7 @@ import store from '@/store'
import { getLocaleManager } from '@/components/Locale'
import Icon from '@/components/Icons/Icon'
import Msg from '@/components/Msg'
import { commands } from '@/components/CommandManager/instance'
import '@/components/Theme/Index.scss'
@@ -36,17 +37,17 @@ function init (config) {
Vue.use(Msg, Message, {
showClose: true
})
Vue.component('mo-icon', Icon)
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({
global.app = new Vue({
components: { App },
router,
store,
@@ -54,6 +55,9 @@ function init (config) {
template: '<App/>'
}).$mount('#app')
global.app.commands = commands
require('./commands')
setTimeout(() => {
loading.close()
}, 400)
+3 -3
View File
@@ -1,6 +1,6 @@
import { ADD_TASK_TYPE } from '@shared/constants'
import api from '@/api'
import { getSystemTheme } from '@/components/Native/utils'
import { getSystemTheme } from '@/utils/native'
const BASE_INTERVAL = 1000
const PER_INTERVAL = 100
@@ -151,8 +151,8 @@ const actions = {
changeAddTaskType ({ commit }, taskType) {
commit('CHANGE_ADD_TASK_TYPE', taskType)
},
updateAddTaskUrl ({ commit }, text = '') {
commit('CHANGE_ADD_TASK_URL', text)
updateAddTaskUrl ({ commit }, uri = '') {
commit('CHANGE_ADD_TASK_URL', uri)
},
addTaskAddTorrents ({ commit }, { fileList }) {
commit('CHANGE_ADD_TASK_TORRENTS', fileList)
@@ -3,9 +3,9 @@ import { access, constants } from 'fs'
import { Message } from 'element-ui'
import {
isMagnetTask,
bytesToSize,
getTaskFullPath,
bytesToSize
isMagnetTask
} from '@shared/utils'
import { APP_THEME, TASK_STATUS } from '@shared/constants'
@@ -27,7 +27,7 @@ export function showItemInFolder (fullPath, { errorMsg }) {
}
access(fullPath, constants.F_OK, (err) => {
console.log(`${fullPath} ${err ? 'does not exist' : 'exists'}`)
console.log(`[Motrix] ${fullPath} ${err ? 'does not exist' : 'exists'}`)
if (err) {
Message.error(errorMsg)
return
@@ -67,7 +67,7 @@ export function moveTaskFilesToTrash (task) {
let deleteResult1 = true
access(path, constants.F_OK, (err) => {
console.log(`${path} ${err ? 'does not exist' : 'exists'}`)
console.log(`[Motrix] ${path} ${err ? 'does not exist' : 'exists'}`)
if (!err) {
deleteResult1 = remote.shell.moveItemToTrash(path)
}
@@ -81,7 +81,7 @@ export function moveTaskFilesToTrash (task) {
let deleteResult2 = true
const extraFilePath = `${path}.aria2`
access(extraFilePath, constants.F_OK, (err) => {
console.log(`${extraFilePath} ${err ? 'does not exist' : 'exists'}`)
console.log(`[Motrix] ${extraFilePath} ${err ? 'does not exist' : 'exists'}`)
if (!err) {
deleteResult2 = remote.shell.moveItemToTrash(extraFilePath)
}
@@ -150,3 +150,17 @@ export const openExternal = (url, options) => {
remote.shell.openExternal(url, options)
}
export const delayDeleteTaskFiles = (task, delay) => {
return new Promise((resolve) => {
setTimeout(() => {
try {
const result = moveTaskFilesToTrash(task)
resolve(result)
} catch (err) {
console.log('[Motrix] batch delay delete task files fail', err)
resolve(false)
}
}, delay)
})
}
+1
View File
@@ -14,6 +14,7 @@ const userKeys = [
'locale',
'log-path',
'new-task-show-downloading',
'no-confirm-before-delete-task',
'open-at-login',
'protocols',
'resume-all-when-app-launched',
+14 -2
View File
@@ -88,11 +88,16 @@ export const trackerSourceOptions = [
}
]
export const ONE_SECOND = 1000
export const ONE_MINUTE = ONE_SECOND * 60
export const ONE_HOUR = ONE_MINUTE * 60
export const ONE_DAY = ONE_HOUR * 24
// 12 Hours
export const AUTO_SYNC_TRACKER_INTERVAL = 12 * 60 * 60 * 1000
export const AUTO_SYNC_TRACKER_INTERVAL = ONE_HOUR * 12
// One Week
export const AUTO_CHECK_UPDATE_INTERVAL = 7 * 24 * 60 * 60 * 1000
export const AUTO_CHECK_UPDATE_INTERVAL = ONE_DAY * 7
export const NONE_SELECTED_FILES = 'none'
export const SELECTED_ALL_FILES = 'all'
@@ -103,3 +108,10 @@ export const IP_VERSION = {
V4: 4,
V6: 6
}
export const LOGIN_SETTING_OPTIONS = {
// For Windows
args: [
'--opened-at-login=1'
]
}
+7 -7
View File
@@ -1,10 +1,10 @@
{
"cmd-q": "application:quit",
"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",
"cmdctrl-q": "application:quit",
"cmdctrl-n": "application:new-task",
"cmdctrl-shift-n": "application:new-bt-task",
"cmdctrl-o": "application:open-file",
"cmdctrl-,": "application:preferences",
"cmdctrl-shift-p": "application:pause-all-task",
"cmdctrl-shift-r": "application:resume-all-task",
"ctrl-shift-a": "application:select-all-task"
}
+24
View File
@@ -1,28 +1,34 @@
import eleLocaleBg from 'element-ui/lib/locale/lang/bg'
import eleLocaleCa from 'element-ui/lib/locale/lang/ca'
import eleLocaleDe from 'element-ui/lib/locale/lang/de'
import eleLocaleEn from 'element-ui/lib/locale/lang/en'
import eleLocaleEs from 'element-ui/lib/locale/lang/es'
import eleLocaleFa from 'element-ui/lib/locale/lang/fa'
import eleLocaleFr from 'element-ui/lib/locale/lang/fr'
import eleLocaleId from 'element-ui/lib/locale/lang/id'
import eleLocaleJa from 'element-ui/lib/locale/lang/ja'
import eleLocaleKo from 'element-ui/lib/locale/lang/ko'
import eleLocalePtBR from 'element-ui/lib/locale/lang/pt-br'
import eleLocaleRu from 'element-ui/lib/locale/lang/ru-RU'
import eleLocaleTr from 'element-ui/lib/locale/lang/tr-TR'
import eleLocaleVi from 'element-ui/lib/locale/lang/vi'
import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN'
import eleLocaleZhTW from 'element-ui/lib/locale/lang/zh-TW'
import eleLocaleUk from 'element-ui/lib/locale/lang/ua'
import appLocaleBg from '@shared/locales/bg'
import appLocaleCa from '@shared/locales/ca'
import appLocaleDe from '@shared/locales/de'
import appLocaleEnUS from '@shared/locales/en-US'
import appLocaleEs from '@shared/locales/es'
import appLocaleFa from '@shared/locales/fa'
import appLocaleFr from '@shared/locales/fr'
import appLocaleId from '@shared/locales/id'
import appLocaleJa from '@shared/locales/ja'
import appLocaleKo from '@shared/locales/ko'
import appLocalePtBR from '@shared/locales/pt-BR'
import appLocaleRu from '@shared/locales/ru'
import appLocaleTr from '@shared/locales/tr'
import appLocaleVi from '@shared/locales/vi'
import appLocaleZhCN from '@shared/locales/zh-CN'
import appLocaleZhTW from '@shared/locales/zh-TW'
import appLocaleUk from '@shared/locales/uk'
@@ -66,6 +72,12 @@ const resources = {
...appLocaleFr
}
},
'id': {
translation: {
...eleLocaleId,
...appLocaleId
}
},
'ja': {
translation: {
...eleLocaleJa,
@@ -96,6 +108,12 @@ const resources = {
...appLocaleTr
}
},
'vi': {
translation: {
...eleLocaleVi,
...appLocaleVi
}
},
'zh-CN': {
translation: {
...eleLocaleZhCN,
@@ -113,6 +131,12 @@ const resources = {
...eleLocaleUk,
...appLocaleUk
}
},
'bg': {
translation: {
...eleLocaleBg,
...appLocaleBg
}
}
}
/* eslint-enable quote-props */
+18
View File
@@ -1,13 +1,16 @@
import appLocaleBg from '@shared/locales/bg'
import appLocaleCa from '@shared/locales/ca'
import appLocaleDe from '@shared/locales/de'
import appLocaleEnUS from '@shared/locales/en-US'
import appLocaleFa from '@shared/locales/fa'
import appLocaleFr from '@shared/locales/fr'
import appLocaleId from '@shared/locales/id'
import appLocaleJa from '@shared/locales/ja'
import appLocaleKo from '@shared/locales/ko'
import appLocalePtBR from '@shared/locales/pt-BR'
import appLocaleRu from '@shared/locales/ru'
import appLocaleTr from '@shared/locales/tr'
import appLocaleVi from '@shared/locales/vi'
import appLocaleZhCN from '@shared/locales/zh-CN'
import appLocaleZhTW from '@shared/locales/zh-TW'
import appLocaleUk from '@shared/locales/uk'
@@ -40,6 +43,11 @@ const resources = {
...appLocaleFr
}
},
'id': {
translation: {
...appLocaleId
}
},
'ja': {
translation: {
...appLocaleJa
@@ -65,6 +73,11 @@ const resources = {
...appLocaleTr
}
},
'vi': {
translation: {
...appLocaleVi
}
},
'zh-CN': {
translation: {
...appLocaleZhCN
@@ -79,6 +92,11 @@ const resources = {
translation: {
...appLocaleUk
}
},
'bg': {
translation: {
...appLocaleBg
}
}
}
/* eslint-enable quote-props */
+7
View File
@@ -0,0 +1,7 @@
export default {
'engine-version': 'Версия',
'license': 'Лиценз',
'about': 'Информация',
'release': 'Съобщение',
'support': 'Подкрепа'
}
+32
View File
@@ -0,0 +1,32 @@
export default {
'task-list': 'Списък със задачи',
'add-task': 'Добавяне на задача',
'about': 'О Motrix',
'preferences': 'Меню...',
'check-for-updates': 'Проверка на актуализацията...',
'check-updates-now': 'Проверете сега',
'checking-for-updates': 'Проверка за актуализации...',
'check-for-updates-title': 'Проверка за актуализации',
'update-available-message': 'Новата версия на Motrix е достъпна за изтегляне, Изтеглете сега?',
'update-not-available-message': 'Вече използвате най-новата версия!',
'update-downloaded-message': 'Готово за инсталиране...',
'update-error-message': 'Грешка при обновяване',
'engine-damaged-message': 'Програмата е повредена, моля преинсталирайте :(',
'engine-missing-message': 'Програмата е загубена, моля преинсталирайте :(',
'system-error-title': 'Грешка',
'system-error-message': 'Грешка при стартиране на приложението: {{message}}',
'hide': 'Скрия Motrix',
'hide-others': 'Скрийте всичко останало',
'unhide': 'Показване на всички',
'show': 'Показване Motrix',
'quit': 'Затваряне Motrix',
'under-development-message': 'За съжаление тази функция все още е в процес на разработка...',
'yes': 'Да',
'no': 'Не',
'cancel': 'Отказ',
'submit': 'Потвърдя',
'gt1d': '> 1 ден',
'hour': 'ч',
'minute': 'м',
'second': 'с'
}
+9
View File
@@ -0,0 +1,9 @@
export default {
'undo': 'Отказ',
'redo': 'Повторя',
'cut': 'Нарежа',
'copy': 'Копирам',
'paste': 'Поставя',
'delete': 'Премахна',
'select-all': 'Изберете всички'
}
+7
View File
@@ -0,0 +1,7 @@
export default {
'official-website': 'Сайт Motrix',
'manual': 'Инструкция',
'release-notes': 'Маркировки...',
'report-problem': 'Докладвайте за проблем',
'toggle-dev-tools': 'Превключване на инструменти за разработчици'
}
+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': 'Файл',
'task': 'Задача',
'edit': 'Редктиране',
'window': 'Прозорец',
'help': 'Грижа'
}
+72
View File
@@ -0,0 +1,72 @@
export default {
'basic':'Основа',
'advanced':'Разширени',
'lab':'Лаборатория',
'save': 'Съхронване и прилагане',
'save-success-message':'настройките са запазени успешно',
'save-fail-message': 'грешка при запазване на настройките',
'discard': 'Отказ',
'startup': 'стартиране',
'open-at-login': 'стартиране на програмата заедно със стартирането на операционната система',
'keep-window-state':'по време на затваряне на приложението, Запишете розмера и позицията на прозореца',
'auto-resume-all':'автоматично възобновяване на всички недовършени задачи',
'default-dir':'пея по подразбиране',
'mas-default-dir-tips': 'поради ограничения в App Store, препоръчително е да зададете пътя по подразбиране като ~/Downloads',
'transfer-settings':'скоростна кутия',
'transfer-speed-upload': 'лимит на откат',
'transfer-speed-download': 'лимит за изтегляне',
'transfer-speed-unlimited':'Unlimited',
'task-manage':'мениджър на задачи',
'max-concurrent-downloads': 'Максимум активни задачи',
'max-connection-per-server': 'максимални връзки към сървъра',
'new-task-show-downloading': 'автоматично показване на задача след добавяне',
'no-confirm-before-delete-task': 'Не се изисква потвърждение преди изтриване на задача',
'continue':'Продължи',
'task-completed-notify': 'съобщение след изтеглянето',
'auto-purge-record': 'автоматично почистване на записите за изтегляне след затваряне на приложението',
'ui': 'UI',
'appearance': 'външен вид',
'theme-auto':'автоматично',
'theme-light':'Светло',
'theme-dark':'dark',
'run-mode': 'Бягай като',
'auto-hide-window': 'Автоматично отваряне на прозорци',
'run-mode-standard': 'стандартно приложение',
'run-mode-menu-bar': 'лента с менюта на приложението',
'language':'Език',
'change-language': 'промяна на езика',
'hide-app-menu': 'Скриване на менюто на приложението (само за Windows и Linux)',
'proxy': 'Proxy',
'use-proxy': 'използване на Proxy',
'no-proxy-input-tips': 'заобикаляне на настройките на прокси за тези хостове и домейни, един по един на ред',
'proxy-tips': 'преглед на ръководството за прокси',
'bt-tracker':'Tracker сървър',
'bt-tracker-input-tips': 'Tracker сървър, един на ред',
'bt-tracker-tips': 'препоръчително:',
'sync-tracker-tips':'Синхронизация',
'auto-sync-tracker':'актуализиране на списъка с тракери всеки ден автоматично',
'port':'портове за слушане',
'bt-port':'пристанище на слушане BT',
'dht-port':'DHT слушане Порт',
'security':'сигурност',
'rpc-secret': 'RPC Secret',
'rpc-secret-tips': 'Гледайте инструкцията RPC Secret',
'developer':'developer',
'Mock-user-agent':'оформление User-Agent',
'app-log-path': 'път към дневника на приложението',
'download-session-path': 'качване на пътя на сесията',
'factory-reset': 'Настройки по подразбиране',
'factory-reset-confirm': 'Сигурни ли сте, че искате да се върнете към настройките по подразбиране?',
'lab-warning': '️ ️ включването на функциите на лабораторията може да доведе до срив на приложението и загуба на данни, решете на свой риск!',
'download-protocol':'протоколи',
'protocols-default-client':'Задаване като клиент по подразбиране за следните протоколи',
'protocols-magnet': 'Magnet [ magnet:// ]',
'protocols-thunder': 'Thunder [ thunder:// ]',
'browser-extensions': 'Розширения',
'baidu-exporter': 'BaiduExporter',
'browser-extensions-tips': 'предоставени от общността,',
'baidu-exporter-help': 'Кликнете тук, за да използвате',
'auto-update':'автоматично обновяване',
'auto-check-update':'автоматична проверка на актуализациите',
'last-check-update-time': 'последната актуализация е проверена'
}
+4
View File
@@ -0,0 +1,4 @@
export default {
'task-list':'Задачи',
'preferences':'Настройки'
}
+93
View File
@@ -0,0 +1,93 @@
export default {
'active':'Активен',
'waiting':'чакане',
'stopped':'спряно',
'new-task': 'нова задача',
'new-bt-task': 'Нова BT задача',
'open-file': 'отваряне на торент файл...',
'uri-task': 'URL',
'torrent-task': 'Torrent',
'uri-task-tips': 'една URL задача в низ (поддръжка на magnet)',
'thunder-link-tips': 'съвет: връзките от типа Thunder може да не се зареждат след декодиране',
'new-task-uris-required': 'въведете валиден URL адрес на ресурс',
'new-task-torrent-required': 'моля, изберете торент файл',
'file-name': 'Име на файл',
'file-extension':'тип файл',
'file-size': 'файлов Розмер',
'selected-files-sum': 'избрано: {{selectedFilesCount}} файлове, общ размер {{selectedFilesTotalSize}}',
'task-name':'Име на изтегляне',
'task-out': 'Преименуване',
'task-out-tips':'незадължителен',
'task-split': 'разделяне',
'task-dir': 'Запазване в',
'pause-task': 'пауза на задачата',
'task-ua': 'UA',
'task-user-agent': 'User-Agent',
'task-referer': 'препращане',
'task-cookie': 'Cookie',
'task-proxy': 'Proxy',
'navigate-to-downloading': 'напред към изтегляне',
'show-advanced-options': 'Разширени опции',
'copyright-warning':'предупреждение за авторски права',
'copyright-warning-message': 'файлът, който се опитвате да изтеглите, има авторски права върху видео или аудио съдържание, моля, проверете дали имате права да изтеглите този файл.',
'copyright-yes': 'Да, имам права',
'copyright-no': 'Не, нямам права',
'copyright-error-message': 'грешка при добавяне на задача поради проблеми с авторските права',
'pause-task-success': 'успешно спряна задача" {{TaskName}}"',
'pause-task-fail': 'грешка при спиране на задачата" {{taskName}}"',
'resume-task': 'възобновяване на задачата',
'resume-task-success': 'успешно възобновена задача" {{TaskName}}"',
'resume-task-fail': 'грешка при възобновяване на задачата" {{taskName}}"',
'delete-task': 'изтриване на задача',
'delete-selected-tasks': 'изтриване на избраните задачи',
'delete-task-confirm': 'сигурни ли Сте, че искате да изтриете задачата "{{taskName}}"?',
'batch-delete-task-confirm': 'Сигурни ли сте, че искате да изтриете {{count}} задачи за зареждане в партиден режим?',
'delete-task-label': 'изтриване заедно с файловете',
'delete-task-success': 'успешно изтрита задача" {{TaskName}}"',
'delete-task-fail': 'грешка при изтриване на задача" {{taskName}}"',
'remove-task-file-fail': 'грешка при изтриване на файл (файлове) на задача, моля, изтрийте го (тях) сами',
'remove-task-config-file-fail': 'грешка при изтриване на конфигурационния файл на заданието, моля, изтрийте го сами',
'move-task-up': 'Преместване на задача нагоре',
'move-task-down': 'Преместване на задача надолу',
'pause-all-task': 'пауза на всички задачи',
'pause-all-task-success': 'всички задачи са успешно прекратени',
'pause-all-task-fail': 'грешка при спиране на всички задачи',
'resume-all-task': 'възобновяване на всички задачи',
'resume-all-task-success': 'успешно възобновени всички задачи',
'resume-all-task-fail': 'грешка при възобновяване на всички задачи',
'select-all-task': 'Изберете всички задачи',
'clear-recent-tasks': 'Изчистване на последните задачи',
'purge-record': 'Изчистване на записките за задачи',
'purge-record-success': 'успешно изчистени записи на задачи',
'purge-record-fail': 'грешка при изчистване на записите за задачи',
'batch-delete-task-success': 'успешно изтриване на задачи в партиден режим',
'batch-delete-task-fail': 'Неуспешно изтриване на задачи в партиден режим',
'refresh-list': 'обновяване на списъка със задачи',
'no-task': 'няма текущи задачи',
'copy-link': 'копиране на връзка',
'copy-link-success': 'успешно копирана връзка',
'remove-record': 'изтриване на запис за задача',
'remove-record-confirm': 'Сигурни ли сте, че искате да изтриете записа за задачата "{{taskName}}"?',
'remove-record-label':'изтриване с файлове',
'remove-record-success': 'успешно изтрит запис за задача" {{taskName}}"',
'remove-record-fail': 'грешка при изтриване на запис за задача "{{taskName}}"',
'show-in-folder': 'показване на файловете със задачи в папка',
'file-not-exist': 'търсеният файл не съществува или е изтрит',
'file-path-error': 'Грешка в пътя към файла',
'opening-task-message': 'отваряне "{{TaskName}}" ...',
'get-task-name': 'получаване на име на задача...',
'remaining-prefix':'ляво',
'select-torrent':'плъзнете торент файла тук, или натиснете Избери',
'task-info-dialog-title':'{{Title}} детайли',
'download-start-message': 'изтеглянето започна {{taskName}}',
'download-pause-message': 'спиране на изтеглянето {{taskName}}',
'download-stop-message': 'спиране на изтеглянето {{taskName}}',
'download-error-message': 'грешка при изтегляне {{taskName}}',
'download-complete-message': 'Завършено изтегляне {{taskName}}',
'download-complete-notify':'изтеглянето Завършено',
'BT-download-complete-message': 'завършено изтегляне {{TaskName}}, раздаване',
'BT-download-complete-notify': 'BT изтеглянето приключи, раздаване...',
'BT-download-complete-tips': 'съвет: можете да спрете задачата, за да спрете раздаването',
'download-fail-message': 'не може да бъде изтеглено {{taskName}}',
'download-fail-notify': 'грешка при зареждане'
}
+8
View File
@@ -0,0 +1,8 @@
export default {
'reload': 'Перезагрузить',
'close': 'Закрыть',
'minimize': 'Свернуть',
'zoom': 'Увеличение',
'toggle-fullscreen': 'Перейти в полноэкранный режим',
'front': 'Поверх всех окон'
}
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'Tasques màximes actives',
'max-connection-per-server': 'Connexions màximes per servidor',
'new-task-show-downloading': 'Mostrar automàticament la descàrrega després d\'afegir una tasca',
'no-confirm-before-delete-task': 'No cal confirmar abans de suprimir la tasca',
'continue': 'Continuar',
'task-completed-notify': 'Notificar després que la descàrrega finalitzi',
'auto-purge-record': 'Purgar automàticament el registre de descàrregues en sortir',
+1
View File
@@ -20,6 +20,7 @@ export default {
'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',
'no-confirm-before-delete-task': 'Vor dem Löschen der Aufgabe ist keine Bestätigung erforderlich',
'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',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'Maximum active tasks',
'max-connection-per-server': 'Maximum connection per server',
'new-task-show-downloading': 'Automatically show downloading after adding task',
'no-confirm-before-delete-task': 'No confirmation is required before deleting task',
'continue': 'Continue',
'task-completed-notify': 'Notification after download is complete',
'auto-purge-record': 'Automatically purge download records when exiting app',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'Tareas máximas activas',
'max-connection-per-server': 'Conexiones máximas por servidor',
'new-task-show-downloading': 'Mostrar automáticamente la descarga después de añadir una tarea',
'no-confirm-before-delete-task': 'No se requiere confirmación antes de eliminar la tarea',
'continue': 'Continuar',
'task-completed-notify': 'Notificar después de que la descarga se complete',
'auto-purge-record': 'Eliminar automáticamente el registro de descargas al salir',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'حداکثر تسک‌های فعال',
'max-connection-per-server': 'حداکثر اتصال‌ برای هر سرور',
'new-task-show-downloading': 'بعد افزودن تسک به صورت اتوماتیک دانلود را نشان بده',
'no-confirm-before-delete-task': 'قبل از حذف کار نیازی به تأیید نیست',
'continue': 'ادامه',
'task-completed-notify': 'اعلان پس از اتمام دانلود',
'auto-purge-record': 'سابقه‌ی دانلود را اتوماتیک هنگام خروج پاک کن',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'Nombre de tâches active au maximum',
'max-connection-per-server': 'Nombre maximum de connexions par serveurs',
'new-task-show-downloading': 'Montrer automatiquement les téléchargements après l\'ajout d\'une tâche',
'no-confirm-before-delete-task': 'Aucune confirmation n\'est requise avant de supprimer la tâche',
'continue': 'Continuer',
'task-completed-notify': 'Notifier à la fin d\'un téléchargement',
'auto-purge-record': 'Purger l\'historique de téléchargement lorsque vous quittez l\'application',
+7
View File
@@ -0,0 +1,7 @@
export default {
'engine-version': 'Versi Mesin',
'license': 'Lisensi',
'about': 'Tentang',
'release': 'Rilis',
'support': 'Bantuan'
}
+32
View File
@@ -0,0 +1,32 @@
export default {
'task-list': 'Daftar Tugas',
'add-task': 'Tambah Tugas',
'about': 'Tentang Motrix',
'preferences': 'Preferensi...',
'check-for-updates': 'Periksa Pembaruan...',
'check-updates-now': 'Periksa Sekarang',
'checking-for-updates': 'Memeriksa pembaruan...',
'check-for-updates-title': 'Periksa Pembaruan',
'update-available-message': 'Versi Motrix terbaru telah tersedia, perbarui sekarang?',
'update-not-available-message': 'Aplikasi dalam kondisi ter-update!',
'update-downloaded-message': 'siap meng-install...',
'update-error-message': 'Update Gagal',
'engine-damaged-message': 'Mesin rusak, silahkan install ulang : (',
'engine-missing-message': 'Mesin hilang, silahkan install ulang : (',
'system-error-title': 'System Error',
'system-error-message': 'Gagal Menjalankan Aplikasi: {{message}}',
'hide': 'Sembungikan Motrix',
'hide-others': 'Sembunyikan yang lain',
'unhide': 'Tunjukan Semua',
'show': 'Tunjukan Motrix',
'quit': 'Keluarkan Motrix',
'under-development-message': 'Maaf, fitur ini dalam tahap development...',
'yes': 'Ya',
'no': 'Tidak',
'cancel': 'Batal',
'submit': 'Kirim',
'gt1d': '> 1 hari',
'hour': 'h',
'minute': 'm',
'second': 's'
}
+9
View File
@@ -0,0 +1,9 @@
export default {
'undo': 'Urungkan',
'redo': 'Ulangi',
'cut': 'Potong',
'copy': 'Salin',
'paste': 'Tempel',
'delete': 'Hapus',
'select-all': 'Pilih Semua'
}
+7
View File
@@ -0,0 +1,7 @@
export default {
'official-website': 'Motrix Website',
'manual': 'Panduan',
'release-notes': 'Catatan Rilis...',
'report-problem': 'Laporkan Masalah',
'toggle-dev-tools': 'Alihkan Alat Pengembang'
}
+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': 'Berlas',
'task': 'Tugas',
'edit': 'Edit',
'window': 'Window',
'help': 'Bantuan'
}
+72
View File
@@ -0,0 +1,72 @@
export default {
'basic': 'Pengaturan Dasar',
'advanced': 'Pengaturan Lanjut',
'lab': 'Lab',
'save': 'Simpan & Terapkan',
'save-success-message': 'Berhasil menyimpan pengaturan',
'save-fail-message': 'Gagal menyimpan pengaturan',
'discard': 'Batal',
'startup': 'Memulai',
'open-at-login': 'Buka saat login',
'keep-window-state': 'Pertahankan ukuran dan posisi jendela aplikasi saat keluar',
'auto-resume-all': 'Otomatis lanjutkan semua tugas yang belum selesai',
'default-dir': 'Lokasi Bawaan',
'mas-default-dir-tips': 'Karena pembatasan dari App Store, direktori unduhan default direkomendasikan untuk disetel ke ~/Downloads',
'transfer-settings': 'Transfer Setting',
'transfer-speed-upload': 'Limit Unggah',
'transfer-speed-download': 'Limit Unduh',
'transfer-speed-unlimited': 'Tak Terbatas',
'task-manage': 'Pengelola Tugas',
'max-concurrent-downloads': 'Maksimal tugas aktif',
'max-connection-per-server': 'Maksimal koneksi per server',
'new-task-show-downloading': 'Tampilkan pengunduhan secara otomatis setelah menambahkan tugas',
'no-confirm-before-delete-task': 'Konfirmasi tidak diperlukan sebelum menghapus tugas',
'continue': 'Lanjutkan',
'task-completed-notify': 'Pemberitahuan setelah pengunduhan selesai',
'auto-purge-record': 'Otomatis bersihkan catatan unduhan saat keluar dari aplikasi',
'ui': 'UI',
'appearance': 'Penampilan',
'theme-auto': 'Auto',
'theme-light': 'Terang',
'theme-dark': 'Gelap',
'run-mode': 'Jalankan Sebagai',
'auto-hide-window': 'Sembunyikan Otomatis Jendela',
'run-mode-standard': 'Aplikasi Standar',
'run-mode-menu-bar': 'Aplikasi Menu Bar',
'language': 'Bahasa',
'change-language': 'Ubah Bahasa',
'hide-app-menu': 'Sembunyikan Menu Aplikasi (hanya: Windows & Linux)',
'proxy': 'Proxy',
'use-proxy': 'Aktifkan Proxy',
'no-proxy-input-tips': 'Abaikan pengaturan proxy untuk Host dan Domain ini, satu per baris',
'proxy-tips': 'Lihat Manual Proxy',
'bt-tracker': 'Server Pelacak',
'bt-tracker-input-tips': 'Server pelacak, satu per baris',
'bt-tracker-tips': 'Direkomendasikan: ',
'sync-tracker-tips': 'Sinkronkan',
'auto-sync-tracker': 'Perbarui daftar pelacak setiap hari secara otomatis',
'port': 'Dengarkan Ports',
'bt-port': 'Dengarkan Port BT',
'dht-port': 'Dengarkan Port DHT',
'security': 'Keamanan',
'rpc-secret': 'RPC Secret',
'rpc-secret-tips': 'Lihat Petunjuk RPC Secret',
'developer': 'Developer',
'mock-user-agent': 'Mock User-Agent',
'app-log-path': 'Lokasi Log Aplikasi',
'download-session-path': 'Lokasi Session Unduhan',
'factory-reset': 'Reset Pabrik',
'factory-reset-confirm': 'Anda yakin ingin kembali ke pengaturan pabrik?',
'lab-warning': 'Mengaktifkan fitur lab dapat menyebabkan aplikasi tidak berjalan semestinya atau kehilangan data, risiko ditanggung Anda sendiri!',
'download-protocol': 'Protocols',
'protocols-default-client': 'Tetapkan sebagai klien untuk Protocol berikut',
'protocols-magnet': 'Magnet [ magnet:// ]',
'protocols-thunder': 'Thunder [ thunder:// ]',
'browser-extensions': 'Ekstensi',
'baidu-exporter': 'BaiduExporter',
'browser-extensions-tips': 'Disediakan oleh komunitas, ',
'baidu-exporter-help': 'Klik di sini untuk penggunaan',
'auto-update': 'Pembaruan Otomatis',
'auto-check-update': 'Secara otomatis memeriksa pembaruan',
'last-check-update-time': 'Terakhir Kali Memeriksa Pembaruan'
}
+4
View File
@@ -0,0 +1,4 @@
export default {
'task-list': 'Daftar Tugas',
'preferences': 'Pengaturan'
}
+93
View File
@@ -0,0 +1,93 @@
export default {
'active': 'Mengunduh',
'waiting': 'Mengunggu',
'stopped': 'Terhenti',
'new-task': 'Tugas Baru',
'new-bt-task': 'Tugas BT baru',
'open-file': 'Buka Berkas Torrent...',
'uri-task': 'URL',
'torrent-task': 'Torrent',
'uri-task-tips': 'Satu tugas per baris (mendukung magnet)',
'thunder-link-tips': 'Tip: Thunder tautan mungkin tidak dapat diunduh setelah decoding',
'new-task-uris-required': 'Silakan masukkan setidaknya satu url yang valid',
'new-task-torrent-required': 'Silahkan pilih berkas torrent',
'file-name': 'Nama Berkas',
'file-extension': 'Tipe Berkas',
'file-size': 'Ukuran Berkas',
'selected-files-sum': 'Terpilih: {{selectedFilesCount}} berkas, total ukuran {{selectedFilesTotalSize}}',
'task-name': 'Nama Tugas',
'task-out': 'Ubah Nama',
'task-out-tips': 'Opsional',
'task-split': 'Pecahan',
'task-dir': 'Simpan Ke',
'pause-task': 'Tunda Tugas',
'task-ua': 'UA',
'task-user-agent': 'User-Agent',
'task-referer': 'Referer',
'task-cookie': 'Cookie',
'task-proxy': 'Proxy',
'navigate-to-downloading': 'Beralih ke Unduhan',
'show-advanced-options': 'Setting Lanjutan',
'copyright-warning': 'Peringatan Hak Cipta',
'copyright-warning-message': 'File yang ingin Anda unduh mungkin berupa audio atau video yang dilindungi hak cipta, pastikan Anda memiliki izin untuk mengaksesnya.',
'copyright-yes': 'Ya, Saya punya izin',
'copyright-no': 'Tidak, Saya tidak punya izin',
'copyright-error-message': 'Gagal menambahkan tugas karena masalah hak cipta',
'pause-task-success': 'Tugas berhasil ditunda "{{taskName}}"',
'pause-task-fail': 'Gagal menunda tugas "{{taskName}}"',
'resume-task': 'Lanjutkan tugas',
'resume-task-success': 'Berhasil melanjutkan tugas "{{taskName}}"',
'resume-task-fail': 'Gagal melanjutkan tugas "{{taskName}}"',
'delete-task': 'Hapus tugas',
'delete-selected-tasks': 'Hapus tugas terpilih',
'delete-task-confirm': 'Anda yakin ingin menghapus tugas unduhan "{{taskName}}"?',
'batch-delete-task-confirm': 'Anda yakin ingin menghapus {{count}} tugas unduhan (batch)?',
'delete-task-label': 'Hapus dengan File',
'delete-task-success': 'Tugas "{{taskName}}" berhasil dihapus',
'delete-task-fail': 'Tugas "{{taskName}}" gagal dihapus',
'remove-task-file-fail': 'Gagal menghapus berkas tugas, silahkan hapus secara manual',
'remove-task-config-file-fail': 'Gagal menghapus pengaturan berkas tugas, silahkan hapus secara manual',
'move-task-up': 'Pidahkan Tugas ke Atas',
'move-task-down': 'Pidahkan Tugas ke Bawah',
'pause-all-task': 'Tunda Semua Tugas',
'pause-all-task-success': 'Berhasil menunda semua tugas',
'pause-all-task-fail': 'Gagal menunda semua tugas',
'resume-all-task': 'Lanjutkan Semua Tugas',
'resume-all-task-success': 'Berhasil melanjutkan semua tugas',
'resume-all-task-fail': 'Gagal melanjutkan semua tugas',
'select-all-task': 'Pilih Semua Tugas',
'clear-recent-tasks': 'Bersihkan tugas terakhir',
'purge-record': 'Bersihkan Catatan Tugas',
'purge-record-success': 'Berhasil membersihkan catatan tugas',
'purge-record-fail': 'Gagal membersihkan catatan tugas',
'batch-delete-task-success': 'Berhasil menghapus tugas (batch)',
'batch-delete-task-fail': 'Gagal menghapus tugas (batch)',
'refresh-list': 'Muat Ulang Daftar',
'no-task': 'Tidak ada tugas',
'copy-link': 'Salin Link',
'copy-link-success': 'Berhasil menyalin link',
'remove-record': 'Hapus Data Tugas',
'remove-record-confirm': 'Anda yakin ingin menghapus data unduhan "{{taskName}}"?',
'remove-record-label': 'Hapus dengan Berkas',
'remove-record-success': 'Catatan tugas berhasil dihapus untuk "{{taskName}}"',
'remove-record-fail': 'Gagal menghapus catatan tugas untuk "{{taskName}}"',
'show-in-folder': 'Tampilkan Tugas Di Folder',
'file-not-exist': 'Berkas target tidak ada atau telah dihapus',
'file-path-error': 'Lokasi berkas error',
'opening-task-message': 'Membuka "{{taskName}}" ...',
'get-task-name': 'Mendapatkan nama tugas...',
'remaining-prefix': 'Tersisa',
'select-torrent': 'Seret berkas torrent ke sini, atau klik untuk memilih',
'task-info-dialog-title': '{{title}} Detail',
'download-start-message': 'Memulai mengunduh {{taskName}}',
'download-pause-message': 'Menunda mengunduh {{taskName}}',
'download-stop-message': 'Berhenti mengunduh {{taskName}}',
'download-error-message': 'Terjadi kesalahan saat mengunduh {{taskName}}',
'download-complete-message': 'Selesai mengunduh {{taskName}}',
'download-complete-notify': 'Unduh Selesai',
'bt-download-complete-message': 'Selesai mengunduh {{taskName}}, penyemaian',
'bt-download-complete-notify': 'BT Unduh Selesai, penyemaian...',
'bt-download-complete-tips': 'Tips: Anda dapat menghentikan tugas untuk mengakhiri penyemaian',
'download-fail-message': 'Gagal mengunduh {{taskName}}',
'download-fail-notify': 'Unduhan Gagal'
}
+8
View File
@@ -0,0 +1,8 @@
export default {
'reload': 'Muat Ulang',
'close': 'Keluar',
'minimize': 'Perkecil',
'zoom': 'Zoom',
'toggle-fullscreen': 'Layar penuh',
'front': 'Bawa Semua ke Depan'
}
+12
View File
@@ -6,6 +6,10 @@
* Please keep the locale key in alphabetical order.
*/
export const availableLanguages = [
{
value: 'bg',
label: 'Българският език'
},
{
value: 'ca',
label: 'Català'
@@ -30,6 +34,10 @@ export const availableLanguages = [
value: 'fr',
label: 'Français'
},
{
value: 'id',
label: 'Indonesia'
},
{
value: 'ja',
label: '日本語'
@@ -50,6 +58,10 @@ export const availableLanguages = [
value: 'tr',
label: 'Türkçe'
},
{
value: 'vi',
label: 'Tiếng Việt'
},
{
value: 'zh-CN',
label: '简体中文'
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': '最大同時タスク数',
'max-connection-per-server': '最大サーバ接続数',
'new-task-show-downloading': '新規タスクを作成後自動的にタスク画面に移る',
'no-confirm-before-delete-task': 'タスクを削除する前に確認は必要ありません',
'continue': '続ける',
'task-completed-notify': 'タスク完了後に通知する',
'auto-purge-record': 'アプリケーション終了後自動的にタスク履歴を削除',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': '최대 활성 작업',
'max-connection-per-server': '서버당 최대 연결 수',
'new-task-show-downloading': '작업 추가 후 다운로드 자동 표시',
'no-confirm-before-delete-task': '작업을 삭제하기 전에 확인이 필요하지 않습니다',
'continue': '계속',
'task-completed-notify': '다운로드 완료 후 알림',
'auto-purge-record': '앱 종료 시 다운로드 기록 자동 삭제',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'Máximo de tarefas ativas',
'max-connection-per-server': 'Máximo de coneções por servidor',
'new-task-show-downloading': 'Auto exibir progresso depois de adicionar uma tarefa',
'no-confirm-before-delete-task': 'Nenhuma confirmação é necessária antes de excluir a tarefa',
'continue': 'Continuar',
'task-completed-notify': 'Notificação após o download ser completado',
'auto-purge-record': 'Auto remover registro de download quando o app for finalizado',
+2 -2
View File
@@ -11,8 +11,8 @@ export default {
'update-not-available-message': 'Вы уже используете самую последнюю версию!',
'update-downloaded-message': 'Готово к установке...',
'update-error-message': 'Ошибка обновления',
'engine-damaged-message': 'Движек поврежден, пожалуйста переустановите : (',
'engine-missing-message': 'Движек потерян, пожалуйста переустановите : (',
'engine-damaged-message': 'Движок поврежден. Пожалуйста, переустановите его : (',
'engine-missing-message': 'Движок потерян. Пожалуйста, переустановите его : (',
'system-error-title': 'Системная ошибка',
'system-error-message': 'Ошибка запуска приложения: {{message}}',
'hide': 'Спрятать Motrix',
+1 -1
View File
@@ -2,7 +2,7 @@ export default {
'app': 'Motrix',
'file': 'Файл',
'task': 'Задания',
'edit': 'Редктировать',
'edit': 'Редактировать',
'window': 'Окно',
'help': 'Помощь'
}
+11 -10
View File
@@ -2,15 +2,15 @@ export default {
'basic': 'Основные',
'advanced': 'Расширенные',
'lab': 'Лаборатория',
'save': 'Сохронить и Применить',
'save': 'Сохранить и применить',
'save-success-message': 'Настройки сохранены успешно',
'save-fail-message': 'Ошибка при сохранении настроек',
'discard': 'Отмена',
'startup': 'Запускать',
'open-at-login': 'Запускать програму вместе со стартом операционной системы',
'keep-window-state': 'Во время закрытия приложения, сохранять розмер и положения окна',
'keep-window-state': 'Во время закрытия приложения, сохранять размер и положение окна',
'auto-resume-all': 'Автоматически возобновлять все незавершенные задачи',
'default-dir': еть по умолчанию',
'default-dir': апка по умолчанию',
'mas-default-dir-tips': 'Из-за ограничения в App Store, рекомендуется устанавливать путь по умолчанию как ~/Downloads',
'transfer-settings': 'коробка передач',
'transfer-speed-upload': 'Лимит отдачи',
@@ -20,28 +20,29 @@ export default {
'max-concurrent-downloads': 'Максимум активных задач',
'max-connection-per-server': 'Максимум соединений на сервер',
'new-task-show-downloading': 'Автоматически отображать задачу после ее добавления',
'no-confirm-before-delete-task': 'Перед удалением задачи подтверждение не требуется',
'continue': 'Продолжить',
'task-completed-notify': 'Сообщение после окончани загрузки',
'task-completed-notify': 'Сообщение после окончания загрузки',
'auto-purge-record': 'Автоматически чистить записи о загрузках после закрытия приложения',
'ui': 'UI',
'appearance': 'Внешний вид',
'theme-auto': 'Автоматически',
'theme-light': 'Светлый',
'theme-dark': 'Темный',
'run-mode': 'Беги как',
'run-mode': 'Запускать как...',
'auto-hide-window': 'Автоскрытие окон',
'run-mode-standard': 'Стандартное приложение',
'run-mode-menu-bar': 'Панель меню приложения',
'language': 'Язык',
'change-language': 'Сменить язык',
'hide-app-menu': 'Скрыть меню приложения (Только для Windows и Linux)',
'hide-app-menu': 'Скрыть меню приложения (только для Windows и Linux)',
'proxy': 'Proxy',
'use-proxy': 'Использовать Proxy',
'no-proxy-input-tips': 'Обойти настройки прокси для этих хостов и доменов, по одному в строке',
'proxy-tips': 'Посмотреть руководство по прокси',
'bt-tracker': 'Tracker Сервер',
'bt-tracker-input-tips': 'Tracker сервера, один в строку',
'bt-tracker-tips': 'Рекомендованно: ',
'bt-tracker-tips': 'Рекомендовано: ',
'sync-tracker-tips': 'Синхронизация',
'auto-sync-tracker': 'Обновлять список трекеров каждый день автоматически',
'port': 'Порты прослушивания',
@@ -56,16 +57,16 @@ export default {
'download-session-path': 'Загрузить путь сессии',
'factory-reset': 'Настройки по умолчанию',
'factory-reset-confirm': 'Вы уверены, что хотите вернуться к настройкам по умолчанию?',
'lab-warning': '⚠️ Включения функций лаборатории может привести к сбою приложения и потери данных, решайте на свой риск!',
'lab-warning': '⚠️ Включение функций лаборатории может привести к сбоям приложения и потери данных. Вы действуете на свой страх и риск!',
'download-protocol': 'Протоколы',
'protocols-default-client': 'Установить как клиента по умолчанию для следующих протоколов',
'protocols-magnet': 'Magnet [ magnet:// ]',
'protocols-thunder': 'Thunder [ thunder:// ]',
'browser-extensions': 'Розширения',
'browser-extensions': 'Расширения',
'baidu-exporter': 'BaiduExporter',
'browser-extensions-tips': 'Предоставляются сообществом, ',
'baidu-exporter-help': 'Нажмите здесь для использования',
'auto-update': 'Автоматическое обновление',
'auto-check-update': 'Автоматически проверять обновления',
'last-check-update-time': 'В последний раз обновление проверялось'
'last-check-update-time': 'Последняя проверка на обновления прошла в'
}
+16 -16
View File
@@ -1,16 +1,16 @@
export default {
'active': 'Загрузки',
'waiting': 'Ожидание',
'stopped': 'Остановленно',
'new-task': 'Нове задание',
'new-bt-task': 'Нове BT задание',
'stopped': 'Остановлено',
'new-task': 'Новое задание',
'new-bt-task': 'Новое BT задание',
'open-file': 'Открыть Torrent файл...',
'uri-task': 'URL',
'torrent-task': 'Torrent',
'uri-task-tips': 'Один URL-задания в строку (поддержка magnet)',
'thunder-link-tips': 'Совет: Ссылки типа Thunder могут не загружаться после декодированния',
'new-task-uris-required': 'Введите хотябы один действительный URL-адрес ресурса',
'new-task-torrent-required': 'Пожалуйста выберите torrent файл',
'new-task-uris-required': 'Введите хотя бы один действительный URL-адрес ресурса',
'new-task-torrent-required': 'Пожалуйста, выберите torrent файл',
'file-name': 'Имя файла',
'file-extension': 'Тип файла',
'file-size': 'Розмер файла',
@@ -19,7 +19,7 @@ export default {
'task-out': 'Переименовать',
'task-out-tips': 'Необязательный',
'task-split': 'Разбить',
'task-dir': 'Сохранить в',
'task-dir': 'Сохранить как',
'pause-task': 'Приостановить задание',
'task-ua': 'UA',
'task-user-agent': 'User-Agent',
@@ -33,10 +33,10 @@ export default {
'copyright-yes': 'Да, у меня есть права',
'copyright-no': 'Нет, у меня нет прав',
'copyright-error-message': 'Ошибка при добавлении задачи из-за проблем с авторскими правами',
'pause-task-success': 'Успешно приостановленно задание "{{taskName}}"',
'pause-task-fail': 'Ошибка во время приостановленния задания "{{taskName}}"',
'resume-task': 'Возобновить Задание',
'resume-task-success': 'Успешно возобновленно задание "{{taskName}}"',
'pause-task-success': 'Успешно остановлено задание "{{taskName}}"',
'pause-task-fail': 'Ошибка во время остановки задания "{{taskName}}"',
'resume-task': 'Возобновить задание',
'resume-task-success': 'Успешно возобновлено задание "{{taskName}}"',
'resume-task-fail': 'Ошибка во время возобновления задания "{{taskName}}"',
'delete-task': 'Удалить задание',
'delete-selected-tasks': 'Удалить выбранные задания',
@@ -45,15 +45,15 @@ export default {
'delete-task-label': 'Удалить вместе с файлами',
'delete-task-success': 'Успешно удалено задание "{{taskName}}"',
'delete-task-fail': 'Ошибка во время удаления задания "{{taskName}}"',
'remove-task-file-fail': 'Ошибка во время удаления файла(файлов) задания, пожалуйста удалите его(их) самостоятельно',
'remove-task-config-file-fail': 'Ошибка при удалении файла конфигурации задания, пожалуйста удалите его самостоятельно',
'remove-task-file-fail': 'Ошибка во время удаления файла(ов) задания. Пожалуйста, удалите его (их) самостоятельно',
'remove-task-config-file-fail': 'Ошибка при удалении файла конфигурации задания. Пожалуйста, удалите его самостоятельно',
'move-task-up': 'Переместить задание вверх',
'move-task-down': 'Переместить задание вниз',
'pause-all-task': 'Приостановить все задания',
'pause-all-task-success': 'Успешно приостановленны все задания',
'pause-all-task-fail': 'Ошибка во время приостановления всех заданий',
'pause-all-task-success': 'Успешно приостановлены все задания',
'pause-all-task-fail': 'Ошибка во время остановки всех заданий',
'resume-all-task': 'Возобновить все задания',
'resume-all-task-success': 'Успешно возобновленны все задания',
'resume-all-task-success': 'Успешно возобновлены все задания',
'resume-all-task-fail': 'Ошибка во время возобновления всех заданий',
'select-all-task': 'Выберите все задачи',
'clear-recent-tasks': 'Очистить последние задания',
@@ -72,7 +72,7 @@ export default {
'remove-record-success': 'Успешно удалена запись про задание "{{taskName}}"',
'remove-record-fail': 'Ошибка при удалении записи про задание "{{taskName}}"',
'show-in-folder': 'Отобразить файлы заданий в папке',
'file-not-exist': 'Розыскиваемый файл не существует или был удален',
'file-not-exist': 'Запрошенный файл не существует или был удален',
'file-path-error': 'Ошибка в пути к файлу',
'opening-task-message': 'Открытие "{{taskName}}" ...',
'get-task-name': 'Получить имя задания...',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'Maksimum aktif görev',
'max-connection-per-server': 'Sunucu başına maksimum bağlantı',
'new-task-show-downloading': 'Görev ekledikten sonra indirmeyi otomatik göster',
'no-confirm-before-delete-task': 'Görevi silmeden önce onay gerekmez',
'continue': 'Devamlı',
'task-completed-notify': 'İndirme bittikten sonra bildirim göster',
'auto-purge-record': 'Auto purge download record when app exit',
+1
View File
@@ -20,6 +20,7 @@ export default {
'max-concurrent-downloads': 'Максимум активних завдань',
'max-connection-per-server': 'Максимум з\'єднання на сервер',
'new-task-show-downloading': 'Автоматично відображати завантаження після додавання завдання',
'no-confirm-before-delete-task': 'Перед видаленням завдання не потрібно підтверджувати',
'continue': 'Продовжити',
'task-completed-notify': 'Повідомлення після завершення завантаження',
'auto-purge-record': 'Автоматично чистити записи про завантаження перед закриттям додатка',
+7
View File
@@ -0,0 +1,7 @@
export default {
'engine-version': 'Phiên bản Ứng dụng',
'license': 'Giấy phép',
'about': 'Về Motrix',
'release': 'Phát hành',
'support': 'Hỗ trợ'
}
+32
View File
@@ -0,0 +1,32 @@
export default {
'task-list': 'Danh sách Tác vụ',
'add-task': 'Thêm tác vụ',
'about': 'Về Motrix',
'preferences': 'Cài đặt...',
'check-for-updates': 'Kiểm tra Cập nhật...',
'check-updates-now': 'Cập nhật ngay',
'checking-for-updates': 'Đang kiểm tra Cập nhật...',
'check-for-updates-title': 'Kiểm tra Cập nhật',
'update-available-message': 'Bạn ơi, đang có một bản cập nhật mới từ Motrix, bạn có muốn cập nhật không?',
'update-not-available-message': 'Không có bản cập nhật mới.',
'update-downloaded-message': 'Đã sẵn sàng để cài đặt...',
'update-error-message': 'Cập nhật lỗi',
'engine-damaged-message': 'Ứng dụng bị lỗi, vui lòng cài đặt lại : (',
'engine-missing-message': 'Ứng dụng bị thiếu tập tin, vui lòng cài đặt lại : (',
'system-error-title': 'Lỗi ứng dụng',
'system-error-message': 'Khởi động ứng dụng thất bại: {{message}}',
'hide': 'Ẩn Motrix',
'hide-others': 'Ẩn tất cả',
'unhide': 'Hiển thị tất cả',
'show': 'Hiển thị Motrix',
'quit': 'Thoát Motrix',
'under-development-message': 'Xin lỗi, tính năng này đang được phát triển...',
'yes': 'Có',
'no': 'Không',
'cancel': 'Huỷ',
'submit': 'Tải về',
'gt1d': '> 1 ngày',
'hour': ' giờ',
'minute': ' phút',
'second': ' giây'
}

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