Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b8de781a4 | |||
| 8224423d8c | |||
| 1e05e1e4f3 | |||
| bfd1e1b6fa | |||
| 8110409402 | |||
| 37b446b6f0 | |||
| 97801fff32 | |||
| 4ca4f5e606 | |||
| e9caf1b0b4 | |||
| d67dca00b5 | |||
| 038536259e | |||
| 02d83534d1 | |||
| 2ae3642c62 | |||
| 5ecc08d66d | |||
| e7b1a898fa | |||
| 93acf1ba63 | |||
| a5f0236661 | |||
| 6df52988b0 | |||
| 6359c729ad | |||
| 04db94daa6 | |||
| d41d809f00 | |||
| efdfae37b5 | |||
| 1a097c02e3 | |||
| 0217b5573a | |||
| 86611604d5 | |||
| 0bd8ab3d11 | |||
| e9eadb2eba | |||
| 72afd845ba | |||
| 91670fac37 | |||
| bc14e5b287 | |||
| 2d01431e7d | |||
| 81531ef9fc | |||
| 04126363ca | |||
| bcb07656b9 | |||
| b9cd991fd5 | |||
| 1b2b22f039 | |||
| 170a481d48 | |||
| 48bf7c9e2e | |||
| 03cfb013e5 | |||
| b4e23d8805 | |||
| 43965c5740 | |||
| 5c8d2e4ca4 | |||
| b51c144ace | |||
| feb839b7fa | |||
| fa36397afa | |||
| 583fe0ffe2 | |||
| 8b3a5fbaa8 | |||
| 85493c263b | |||
| 78e106abe4 | |||
| bbf90e9344 | |||
| dc30c25a83 | |||
| e1cce4f50c | |||
| 61654ff0ff | |||
| 0142aedfa5 | |||
| 323f85ca40 | |||
| f35e696c6a | |||
| d49cbcdc00 | |||
| c85ac4e895 | |||
| f831cc51ed | |||
| d47eb9ba0f | |||
| 119a374db6 | |||
| d26808e43a | |||
| 8cb3e54be2 | |||
| c55902cda7 | |||
| 426b54201b | |||
| 6c032cde2d | |||
| eb42ac6cfd | |||
| 90f0a7c5f6 | |||
| 9b61dad2c3 | |||
| 671fb82e6a | |||
| b8f444ca4d | |||
| 7509442e4e | |||
| ee2e3de782 | |||
| c6dd1e333e | |||
| fba761a29c | |||
| 4fb94ae8bb | |||
| 79721822c2 | |||
| 556f58f6b3 | |||
| e690f15335 | |||
| 1876587973 | |||
| b89c651132 | |||
| ad9d3ed15a | |||
| 6aa362332a | |||
| 18d107c062 | |||
| ebeba4663c | |||
| 9bff4257a8 | |||
| e07ef7dd11 | |||
| ebb0926d1d | |||
| 5333438825 | |||
| 3664341822 | |||
| d3cbff6dd1 | |||
| 5668b0773d | |||
| b757112c30 | |||
| dc3a01fb3c | |||
| 591de11072 |
@@ -11,6 +11,7 @@ assignees: ''
|
||||
Before you feedback, please search for the issues to see if there are similar problems that can solve your problem.
|
||||
|
||||
**Please delete the above and the contents of this line, then fill in the feedback form in the following format, Thanks.**
|
||||
<!-- Windows and Linux versions hide the application menu by default. Please close the "Hide Menu Bar" in Preferences - Advanced Settings - Appearance. After saving and applying, the menu bar will appear. -->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
@@ -6,12 +6,14 @@ labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**反馈之前**
|
||||
|
||||
<!--
|
||||
反馈之前请搜索一下已有 issues 和 帮助文档,看是否有类似问题可以解决你的问题
|
||||
https://github.com/agalwood/Motrix/issues
|
||||
http://motrix.app/support
|
||||
|
||||
**请删除上面和本行的内容,然后按以下格式填写反馈信息,谢谢**
|
||||
按以下格式填写反馈信息,谢谢
|
||||
-->
|
||||
|
||||
**错误描述**
|
||||
清楚简洁地描述错误,方便我们复现之后处理。
|
||||
@@ -24,11 +26,12 @@ http://motrix.app/support
|
||||
4. 发现报错
|
||||
|
||||
**预期的行为**
|
||||
清楚简洁地描述您期望发生的事情。
|
||||
清楚简洁地描述你期望发生的事情。
|
||||
|
||||
**截图**
|
||||
请添加屏幕截图以帮助解释您的问题:
|
||||
请添加屏幕截图以帮助解释你的问题:
|
||||
打开应用菜单中的「帮助」——「开发者工具」—— 切换到 console,然后**完整**截图。
|
||||
<!-- Windows 和 Linux 版本默认隐藏了应用菜单,请到偏好设置-进阶设置-外观里关闭“隐藏菜单栏”,保存并应用之后菜单栏就出现了 -->
|
||||
|
||||
**运行环境**
|
||||
- 操作系统类型: [如 macOS, Windows, Linux]
|
||||
@@ -37,4 +40,4 @@ http://motrix.app/support
|
||||
- 安装包类型:[如 dmg, AppImage]
|
||||
|
||||
**更多信息**
|
||||
添加有关此问题的任何其他上下文信息。
|
||||
补充有关该问题的其他信息。
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: 新功能请求
|
||||
about: 你期望 Motrix 未来添加的新功能
|
||||
title: ''
|
||||
labels: enhancement ✨
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
反馈之前请搜索一下已有 issues 和 帮助文档,看是否已经有人提交了类似的新功能请求
|
||||
https://github.com/agalwood/Motrix/issues
|
||||
http://motrix.app/support
|
||||
|
||||
按以下格式填写反馈信息,谢谢
|
||||
-->
|
||||
|
||||
**请描述一下你的新功能请求是否与已知问题有关?**
|
||||
简明扼要地描述了问题所在。
|
||||
|
||||
**描述你想要的解决方案**
|
||||
简明扼要地描述你想要的解决方案。
|
||||
|
||||
**描述你考虑过的替代方案**
|
||||
简明扼要地描述你考虑过的任何替代解决方案或功能。
|
||||
|
||||
**更多信息**
|
||||
补充有关该新功能的其他信息。
|
||||
@@ -20,9 +20,19 @@ Motrix 是一款全能的下载工具,支持下载 HTTP、FTP、BT、磁力链
|
||||
|
||||
[GitHub](https://github.com/agalwood/Motrix/releases) 和 [官网](https://motrix.app/zh-CN) 提供了已经编译好的稳定版安装包,当然你也可以自己克隆代码编译打包。
|
||||
|
||||
### Windows
|
||||
|
||||
建议使用安装包(Motrix-Setup-x.y.z.exe)安装 Motrix 以确保完整的体验,例如关联 torrent 文件,捕获磁力链等。
|
||||
|
||||
如果你更喜欢便携版,你可以使用 [scoop](https://github.com/lukesampson/scoop)(需要 Windows 7+)安装最新便携版本的 Motrix。
|
||||
|
||||
```bash
|
||||
scoop install motrix
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
更新:macOS 用户支持 `brew cask` 安装,感谢 [Mitscherlich](https://github.com/Mitscherlich) 的 [PR](https://github.com/Homebrew/homebrew-cask/pull/59494)。
|
||||
macOS 用户可以使用 `brew cask` 安装 Motrix,感谢 [Mitscherlich](https://github.com/Mitscherlich) 的 [PR](https://github.com/Homebrew/homebrew-cask/pull/59494)。
|
||||
|
||||
```bash
|
||||
brew update && brew cask install motrix
|
||||
@@ -30,7 +40,7 @@ brew update && brew cask install motrix
|
||||
|
||||
### Linux
|
||||
|
||||
您可以下载 AppImage(适用于所有Linux发行版)软件包或 snap 或从源代码构建。
|
||||
你可以下载 AppImage(适用于所有 Linux 发行版)软件包或 snap 或从源代码构建安装 Motrix。
|
||||
|
||||
构建请阅读 **编译打包** 部分。
|
||||
|
||||
@@ -51,7 +61,7 @@ yay motrix
|
||||
- 🚀 单任务最高支持 64 线程下载
|
||||
- 🕶 模拟用户代理UA
|
||||
- 🔔 下载完成后通知
|
||||
- 💻 支持触控栏快捷健 (Mac 专享)
|
||||
- 💻 支持触控栏快捷键 (Mac 专享)
|
||||
- 🤖 常驻系统托盘,操作更加便捷
|
||||
- 🌑 深色模式
|
||||
- 🗑 移除任务时可同时删除相关文件
|
||||
@@ -123,8 +133,10 @@ npm run build
|
||||
|-------|:--------------------|:-------------|
|
||||
| de | German | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
|
||||
| en-US | English | ✔️ |
|
||||
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
|
||||
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
|
||||
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
|
||||
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
|
||||
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
|
||||
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
|
||||
| zh-CN | 简体中文 | ✔️ |
|
||||
|
||||
@@ -20,9 +20,19 @@ Motrix has a clean and easy to use interface. I hope you will like it 👻.
|
||||
|
||||
Download from [GitHub Releases](https://github.com/agalwood/Motrix/releases) and install it.
|
||||
|
||||
### Windows
|
||||
|
||||
It is recommended to install Motrix using the installation package (Motrix-Setup-x.y.z.exe) to ensure a complete experience, such as associating torrent files, capturing magnet links, etc.
|
||||
|
||||
If you prefer the portable version, you can use [scoop](https://github.com/lukesampson/scoop) (need Windows 7+) to install Motrix.
|
||||
|
||||
```bash
|
||||
scoop install motrix
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
Update: macOS user support `brew cask` installation, thanks to [PR](https://github.com/Homebrew/homebrew-cask/pull/59494) of [Mitscherlich](https://github.com/Mitscherlich).
|
||||
The macOS users can install Motrix using `brew cask`, thanks to [PR](https://github.com/Homebrew/homebrew-cask/pull/59494) of [Mitscherlich](https://github.com/Mitscherlich).
|
||||
|
||||
```bash
|
||||
brew update && brew cask install motrix
|
||||
@@ -30,7 +40,7 @@ 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.
|
||||
You can download the AppImage (for all Linux distributions) package or snap or just build from source code to install Motrix.
|
||||
|
||||
Please read the **Build** section.
|
||||
|
||||
@@ -115,8 +125,10 @@ Translations into versions for other languages are welcome 🧐! Please read the
|
||||
|-------|:--------------------|:-------------|
|
||||
| de | German | ✔️ [@Schloemicher](https://github.com/Schloemicher) |
|
||||
| en-US | English | ✔️ |
|
||||
| fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) |
|
||||
| fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) |
|
||||
| ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) |
|
||||
| ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) |
|
||||
| pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) |
|
||||
| tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) |
|
||||
| zh-CN | 简体中文 | ✔️ |
|
||||
|
||||
@@ -106,3 +106,8 @@ seed-ratio=1.0
|
||||
bt-seed-unverified=true
|
||||
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
|
||||
bt-save-metadata=false
|
||||
# Removes the unselected files when download is completed in BitTorrent.
|
||||
# To select files, use --select-file option. If it is not used,
|
||||
# all files are assumed to be selected. Please use this option with care
|
||||
# because it will actually remove files from your disk. Default: false
|
||||
bt-remove-unselected-file=true
|
||||
|
||||
@@ -106,3 +106,8 @@ seed-ratio=1.0
|
||||
bt-seed-unverified=true
|
||||
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
|
||||
bt-save-metadata=false
|
||||
# Removes the unselected files when download is completed in BitTorrent.
|
||||
# To select files, use --select-file option. If it is not used,
|
||||
# all files are assumed to be selected. Please use this option with care
|
||||
# because it will actually remove files from your disk. Default: false
|
||||
bt-remove-unselected-file=true
|
||||
|
||||
@@ -106,3 +106,8 @@ seed-ratio=1.0
|
||||
bt-seed-unverified=true
|
||||
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
|
||||
bt-save-metadata=false
|
||||
# Removes the unselected files when download is completed in BitTorrent.
|
||||
# To select files, use --select-file option. If it is not used,
|
||||
# all files are assumed to be selected. Please use this option with care
|
||||
# because it will actually remove files from your disk. Default: false
|
||||
bt-remove-unselected-file=true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Motrix",
|
||||
"version": "1.3.4",
|
||||
"version": "1.3.8",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -5829,8 +5829,7 @@
|
||||
"version": "4.6.0",
|
||||
"resolved": "http://registry.npm.taobao.org/co/download/co-4.6.0.tgz",
|
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"coa": {
|
||||
"version": "2.0.2",
|
||||
@@ -9699,8 +9698,7 @@
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@@ -9718,13 +9716,11 @@
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -9737,18 +9733,15 @@
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -9851,8 +9844,7 @@
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -9862,7 +9854,6 @@
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -9875,20 +9866,17 @@
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@@ -9905,7 +9893,6 @@
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -9978,8 +9965,7 @@
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -9989,7 +9975,6 @@
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -10065,8 +10050,7 @@
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@@ -10096,7 +10080,6 @@
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -10114,7 +10097,6 @@
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -10153,13 +10135,11 @@
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
"bundled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16024,8 +16004,7 @@
|
||||
"version": "4.0.8",
|
||||
"resolved": "http://registry.npm.taobao.org/rx-lite/download/rx-lite-4.0.8.tgz",
|
||||
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"rx-lite-aggregates": {
|
||||
"version": "4.0.8",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Motrix",
|
||||
"version": "1.3.6",
|
||||
"version": "1.3.8",
|
||||
"description": "A full-featured download manager",
|
||||
"homepage": "https://motrix.app",
|
||||
"author": {
|
||||
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 38 KiB |
@@ -8,6 +8,7 @@ import logger from './core/Logger'
|
||||
import ConfigManager from './core/ConfigManager'
|
||||
import { setupLocaleManager } from '@/ui/Locale'
|
||||
import Engine from './core/Engine'
|
||||
import AutoLaunchManager from './core/AutoLaunchManager'
|
||||
import UpdateManager from './core/UpdateManager'
|
||||
import EnergyManager from './core/EnergyManager'
|
||||
import ProtocolManager from './core/ProtocolManager'
|
||||
@@ -16,6 +17,7 @@ import MenuManager from './ui/MenuManager'
|
||||
import TouchBarManager from './ui/TouchBarManager'
|
||||
import TrayManager from './ui/TrayManager'
|
||||
import ThemeManager from './ui/ThemeManager'
|
||||
import { AUTO_CHECK_UPDATE_INTERVAL } from '@shared/constants'
|
||||
|
||||
export default class Application extends EventEmitter {
|
||||
constructor () {
|
||||
@@ -36,9 +38,7 @@ export default class Application extends EventEmitter {
|
||||
|
||||
this.initTouchBarManager()
|
||||
|
||||
this.windowManager = new WindowManager({
|
||||
userConfig: this.configManager.getUserConfig()
|
||||
})
|
||||
this.initWindowManager()
|
||||
|
||||
this.engine = new Engine({
|
||||
systemConfig: this.configManager.getSystemConfig(),
|
||||
@@ -48,6 +48,8 @@ export default class Application extends EventEmitter {
|
||||
|
||||
this.trayManager = new TrayManager()
|
||||
|
||||
this.autoLaunchManager = new AutoLaunchManager()
|
||||
|
||||
this.initThemeManager()
|
||||
|
||||
this.energyManager = new EnergyManager()
|
||||
@@ -57,6 +59,7 @@ export default class Application extends EventEmitter {
|
||||
this.initProtocolManager()
|
||||
|
||||
this.handleCommands()
|
||||
|
||||
this.handleIpcMessages()
|
||||
}
|
||||
|
||||
@@ -77,12 +80,46 @@ export default class Application extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
start (page) {
|
||||
this.showPage(page)
|
||||
initWindowManager () {
|
||||
this.windowManager = new WindowManager({
|
||||
userConfig: this.configManager.getUserConfig()
|
||||
})
|
||||
|
||||
this.windowManager.on('window-resized', (data) => {
|
||||
this.storeWindowState(data)
|
||||
})
|
||||
this.windowManager.on('window-moved', (data) => {
|
||||
this.storeWindowState(data)
|
||||
})
|
||||
this.windowManager.on('window-closed', (data) => {
|
||||
this.storeWindowState(data)
|
||||
})
|
||||
}
|
||||
|
||||
showPage (page) {
|
||||
const win = this.windowManager.openWindow(page)
|
||||
storeWindowState (data = {}) {
|
||||
const enabled = this.configManager.getUserConfig('keep-window-state')
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
const state = this.configManager.getUserConfig('window-state', {})
|
||||
const { page, bounds } = data
|
||||
const newState = {
|
||||
...state,
|
||||
[page]: bounds
|
||||
}
|
||||
this.configManager.setUserConfig('window-state', newState)
|
||||
}
|
||||
|
||||
start (page, options = {}) {
|
||||
this.showPage(page, options)
|
||||
}
|
||||
|
||||
showPage (page, options = {}) {
|
||||
const { openedAtLogin } = options
|
||||
const win = this.windowManager.openWindow(page, {
|
||||
hidden: openedAtLogin
|
||||
})
|
||||
win.once('ready-to-show', () => {
|
||||
this.isReady = true
|
||||
this.emit('ready')
|
||||
@@ -201,14 +238,22 @@ export default class Application extends EventEmitter {
|
||||
return
|
||||
}
|
||||
this.updateManager = new UpdateManager({
|
||||
autoCheck: this.configManager.getUserConfig('auto-check-update')
|
||||
? (new Date().getTime() - this.configManager.getUserConfig('last-check-update-time') > 7 * 24 * 60 * 60 * 1000)
|
||||
: false,
|
||||
autoCheck: this.isNeedAutoCheck(),
|
||||
setCheckTime: this.configManager
|
||||
})
|
||||
this.handleUpdaterEvents()
|
||||
}
|
||||
|
||||
isNeedAutoCheck () {
|
||||
const enable = this.configManager.getUserConfig('auto-check-update')
|
||||
if (!enable) {
|
||||
return false
|
||||
}
|
||||
|
||||
const lastCheck = this.configManager.getUserConfig('last-check-update-time')
|
||||
return (Date.now() - lastCheck > AUTO_CHECK_UPDATE_INTERVAL)
|
||||
}
|
||||
|
||||
handleUpdaterEvents () {
|
||||
this.updateManager.on('checking', (event) => {
|
||||
this.menuManager.updateMenuItemEnabledState('app.check-for-updates', false)
|
||||
@@ -257,10 +302,23 @@ export default class Application extends EventEmitter {
|
||||
})
|
||||
|
||||
this.on('application:exit', () => {
|
||||
this.engine.stop()
|
||||
this.stop()
|
||||
app.exit()
|
||||
})
|
||||
|
||||
this.on('application:open-at-login', (openAtLogin) => {
|
||||
console.log('application:open-at-login===>', openAtLogin)
|
||||
if (is.linux()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (openAtLogin) {
|
||||
this.autoLaunchManager.enable()
|
||||
} else {
|
||||
this.autoLaunchManager.disable()
|
||||
}
|
||||
})
|
||||
|
||||
this.on('application:show', (page) => {
|
||||
this.show(page)
|
||||
})
|
||||
|
||||
@@ -5,7 +5,11 @@ import is from 'electron-is'
|
||||
import ExceptionHandler from './core/ExceptionHandler'
|
||||
import logger from './core/Logger'
|
||||
import Application from './Application'
|
||||
import { parseArgvAsUrl, parseArgvAsFile } from './utils'
|
||||
import {
|
||||
splitArgv,
|
||||
parseArgvAsUrl,
|
||||
parseArgvAsFile
|
||||
} from './utils'
|
||||
|
||||
const EMPTY_STRING = ''
|
||||
|
||||
@@ -33,9 +37,9 @@ export default class Launcher extends EventEmitter {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, argv, workingDirectory) => {
|
||||
logger.warn('second-instance====>', event, argv, workingDirectory)
|
||||
logger.warn('second-instance====>', argv, workingDirectory)
|
||||
global.application.showPage('index')
|
||||
if (!is.macOS() && argv.length > 1) { // Windows, Linux
|
||||
if (!is.macOS() && argv.length > 1) {
|
||||
this.handleAppLaunchArgv(argv)
|
||||
}
|
||||
})
|
||||
@@ -47,6 +51,16 @@ export default class Launcher extends EventEmitter {
|
||||
init () {
|
||||
this.exceptionHandler = new ExceptionHandler()
|
||||
|
||||
this.openedAtLogin = is.macOS()
|
||||
? app.getLoginItemSettings().wasOpenedAtLogin
|
||||
: false
|
||||
|
||||
if (process.argv.length > 1) {
|
||||
this.handleAppLaunchArgv(process.argv)
|
||||
}
|
||||
|
||||
logger.warn('openedAtLogin===>', this.openedAtLogin)
|
||||
|
||||
this.handleAppEvents()
|
||||
}
|
||||
|
||||
@@ -60,11 +74,12 @@ export default class Launcher extends EventEmitter {
|
||||
|
||||
/**
|
||||
* handleOpenUrl
|
||||
* Event 'open-url' macOS only
|
||||
* "name": "Motrix Protocol",
|
||||
* "schemes": ["mo", "motrix"]
|
||||
*/
|
||||
handleOpenUrl () {
|
||||
if (is.mas()) {
|
||||
if (is.mas() || !is.macOS()) {
|
||||
return
|
||||
}
|
||||
app.on('open-url', (event, url) => {
|
||||
@@ -77,30 +92,44 @@ export default class Launcher extends EventEmitter {
|
||||
|
||||
/**
|
||||
* handleOpenFile
|
||||
* Event 'open-file' macOS only
|
||||
* handle open torrent file
|
||||
*/
|
||||
handleOpenFile () {
|
||||
// macOS
|
||||
if (is.macOS()) {
|
||||
app.on('open-file', (event, path) => {
|
||||
logger.info(`[Motrix] open-file: ${path}`)
|
||||
event.preventDefault()
|
||||
this.file = path
|
||||
this.sendFileToApplication()
|
||||
})
|
||||
} else if (process.argv.length > 1) { // Windows, Linux
|
||||
this.handleAppLaunchArgv(process.argv)
|
||||
if (!is.macOS()) {
|
||||
return
|
||||
}
|
||||
app.on('open-file', (event, path) => {
|
||||
logger.info(`[Motrix] open-file: ${path}`)
|
||||
event.preventDefault()
|
||||
this.file = path
|
||||
this.sendFileToApplication()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* handleAppLaunchArgv
|
||||
* For Windows, Linux
|
||||
* @param {array} argv
|
||||
*/
|
||||
handleAppLaunchArgv (argv) {
|
||||
const file = parseArgvAsFile(argv)
|
||||
logger.info('handleAppLaunchArgv===>', argv)
|
||||
|
||||
// args: array, extra: map
|
||||
const { args, extra } = splitArgv(argv)
|
||||
logger.info('splitArgv.args===>', args)
|
||||
logger.info('splitArgv.extra===>', extra)
|
||||
if (extra['--opened-at-login'] === '1') {
|
||||
this.openedAtLogin = true
|
||||
}
|
||||
|
||||
const file = parseArgvAsFile(args)
|
||||
if (file) {
|
||||
this.file = file
|
||||
this.sendFileToApplication()
|
||||
}
|
||||
|
||||
const url = parseArgvAsUrl(argv)
|
||||
const url = parseArgvAsUrl(args)
|
||||
if (url) {
|
||||
this.url = url
|
||||
this.sendUrlToApplication()
|
||||
@@ -125,7 +154,10 @@ export default class Launcher extends EventEmitter {
|
||||
app.on('ready', () => {
|
||||
global.application = new Application()
|
||||
|
||||
global.application.start('index')
|
||||
const { openedAtLogin } = this
|
||||
global.application.start('index', {
|
||||
openedAtLogin
|
||||
})
|
||||
|
||||
global.application.on('ready', () => {
|
||||
this.sendUrlToApplication()
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { app } from 'electron'
|
||||
|
||||
export default class AutoLaunchManager {
|
||||
enable () {
|
||||
return new Promise((resolve, reject) => {
|
||||
const enabled = app.getLoginItemSettings().openAtLogin
|
||||
if (enabled) {
|
||||
resolve()
|
||||
}
|
||||
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: true,
|
||||
// For Windows
|
||||
args: [
|
||||
'--opened-at-login=1'
|
||||
]
|
||||
})
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
disable () {
|
||||
return new Promise((resolve, reject) => {
|
||||
app.setLoginItemSettings({ openAtLogin: false })
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
isEnabled () {
|
||||
return new Promise((resolve, reject) => {
|
||||
const enabled = app.getLoginItemSettings().openAtLogin
|
||||
resolve(enabled)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,7 @@ export default class ConfigManager {
|
||||
'pause': true,
|
||||
'rpc-listen-port': 16800,
|
||||
'rpc-secret': '',
|
||||
'seed-time': 60,
|
||||
'split': 16,
|
||||
'user-agent': 'Transmission/2.94'
|
||||
}
|
||||
@@ -109,20 +110,33 @@ export default class ConfigManager {
|
||||
// },
|
||||
defaults: {
|
||||
'all-proxy-backup': '',
|
||||
'auto-check-update': false,
|
||||
'auto-check-update': is.macOS(),
|
||||
'hide-app-menu': is.windows() || is.linux(),
|
||||
'last-check-update-time': 0,
|
||||
'locale': app.getLocale(),
|
||||
'log-path': getLogPath(),
|
||||
'new-task-show-downloading': true,
|
||||
'open-at-login': false,
|
||||
'resume-all-when-app-launched': false,
|
||||
'keep-window-state': false,
|
||||
'session-path': getSessionPath(),
|
||||
'task-notification': true,
|
||||
'theme': 'auto',
|
||||
'update-channel': 'latest',
|
||||
'use-proxy': false
|
||||
'use-proxy': false,
|
||||
'window-state': {}
|
||||
}
|
||||
})
|
||||
this.fixUserConfig()
|
||||
}
|
||||
|
||||
fixUserConfig () {
|
||||
// Fix the value of open-at-login when the user delete
|
||||
// the Motrix self-starting item through startup management.
|
||||
const openAtLogin = app.getLoginItemSettings().openAtLogin
|
||||
if (this.getUserConfig('open-at-login') !== openAtLogin) {
|
||||
this.setUserConfig('open-at-login', openAtLogin)
|
||||
}
|
||||
}
|
||||
|
||||
getSystemConfig (key, defaultValue) {
|
||||
|
||||
@@ -43,13 +43,13 @@ export default class UpdateManager extends EventEmitter {
|
||||
|
||||
if (this.autoCheckData.checkEnable) {
|
||||
this.autoCheckData.userCheck = false
|
||||
this.options.setCheckTime.setUserConfig('last-check-update-time', new Date().getTime())
|
||||
this.options.setCheckTime.setUserConfig('last-check-update-time', Date.now())
|
||||
this.updater.checkForUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
check () {
|
||||
this.options.setCheckTime.setUserConfig('last-check-update-time', new Date().getTime())
|
||||
this.options.setCheckTime.setUserConfig('last-check-update-time', Date.now())
|
||||
this.autoCheckData.userCheck = true
|
||||
this.updater.checkForUpdates()
|
||||
}
|
||||
@@ -112,7 +112,10 @@ export default class UpdateManager extends EventEmitter {
|
||||
|
||||
updateError (event, error) {
|
||||
this.emit('update-error', error)
|
||||
const msg = error == null ? this.i18n.t('update-error-message') : (error.stack || error).toString()
|
||||
const msg = (error == null)
|
||||
? this.i18n.t('update-error-message')
|
||||
: (error.stack || error).toString()
|
||||
|
||||
this.updater.logger.warn(`[Motrix] update-error: ${msg}`)
|
||||
dialog.showErrorBox(msg)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import { app, shell, screen, BrowserWindow } from 'electron'
|
||||
import is from 'electron-is'
|
||||
import pageConfig from '../configs/page'
|
||||
import logger from '../core/Logger'
|
||||
import { debounce } from 'lodash'
|
||||
|
||||
const defaultBrowserOptions = {
|
||||
titleBarStyle: 'hiddenInset',
|
||||
useContentSize: true,
|
||||
show: false,
|
||||
width: 1024,
|
||||
height: 768,
|
||||
@@ -57,37 +57,57 @@ export default class WindowManager extends EventEmitter {
|
||||
return result
|
||||
}
|
||||
|
||||
openWindow (page) {
|
||||
const options = this.getPageOptions(page)
|
||||
getPageBounds (page) {
|
||||
const enabled = this.userConfig['keep-window-state']
|
||||
const windowStateMap = this.userConfig['window-state'] || {}
|
||||
let result = null
|
||||
if (enabled) {
|
||||
result = windowStateMap[page]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
openWindow (page, options = {}) {
|
||||
const pageOptions = this.getPageOptions(page)
|
||||
const { hidden } = options
|
||||
|
||||
let window = this.windows[page] || null
|
||||
if (window) {
|
||||
window.restore()
|
||||
window.show()
|
||||
window.focus()
|
||||
return window
|
||||
}
|
||||
|
||||
window = new BrowserWindow({
|
||||
...defaultBrowserOptions,
|
||||
...options.attrs
|
||||
...pageOptions.attrs
|
||||
})
|
||||
|
||||
const bounds = this.getPageBounds(page)
|
||||
console.log('bounds ====>', bounds)
|
||||
if (bounds) {
|
||||
window.setBounds(bounds)
|
||||
}
|
||||
|
||||
window.webContents.on('new-window', (e, url) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
|
||||
if (options.url) {
|
||||
window.loadURL(options.url)
|
||||
if (pageOptions.url) {
|
||||
window.loadURL(pageOptions.url)
|
||||
}
|
||||
|
||||
window.once('ready-to-show', () => {
|
||||
window.show()
|
||||
if (!hidden) {
|
||||
window.show()
|
||||
}
|
||||
})
|
||||
|
||||
if (options.bindCloseToHide) {
|
||||
this.bindCloseToHide(page, window)
|
||||
}
|
||||
this.handleWindowState(page, window)
|
||||
|
||||
this.handleWindowClose(pageOptions, page, window)
|
||||
|
||||
this.bindAfterClosed(page, window)
|
||||
|
||||
@@ -114,6 +134,9 @@ export default class WindowManager extends EventEmitter {
|
||||
destroyWindow (page) {
|
||||
const win = this.getWindow(page)
|
||||
this.removeWindow(page)
|
||||
win.removeListener('closed')
|
||||
win.removeListener('move')
|
||||
win.removeListener('resize')
|
||||
win.destroy()
|
||||
}
|
||||
|
||||
@@ -127,12 +150,26 @@ export default class WindowManager extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
bindCloseToHide (page, window) {
|
||||
handleWindowState (page, window) {
|
||||
window.on('resize', debounce(() => {
|
||||
const bounds = window.getBounds()
|
||||
this.emit('window-resized', { page, bounds })
|
||||
}, 500))
|
||||
|
||||
window.on('move', debounce(() => {
|
||||
const bounds = window.getBounds()
|
||||
this.emit('window-moved', { page, bounds })
|
||||
}, 500))
|
||||
}
|
||||
|
||||
handleWindowClose (pageOptions, page, window) {
|
||||
window.on('close', (event) => {
|
||||
if (!this.willQuit) {
|
||||
if (pageOptions.bindCloseToHide && !this.willQuit) {
|
||||
event.preventDefault()
|
||||
window.hide()
|
||||
}
|
||||
const bounds = window.getBounds()
|
||||
this.emit('window-closed', { page, bounds })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -65,25 +65,50 @@ export function moveAppToApplicationsFolder (errorMsg = '') {
|
||||
})
|
||||
}
|
||||
|
||||
export function splitArgv (argv) {
|
||||
const args = []
|
||||
const extra = {}
|
||||
for (const arg of argv) {
|
||||
if (arg.startsWith('--')) {
|
||||
const kv = arg.split('=')
|
||||
const key = kv[0]
|
||||
const value = kv[1] || '1'
|
||||
extra[key] = value
|
||||
continue
|
||||
}
|
||||
args.push(arg)
|
||||
}
|
||||
return { args, extra }
|
||||
}
|
||||
|
||||
export function parseArgvAsUrl (argv) {
|
||||
let arg = argv[1]
|
||||
if (!arg) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
arg.toLowerCase().startsWith('mo:') ||
|
||||
arg.toLowerCase().startsWith('motrix:') ||
|
||||
arg.toLowerCase().startsWith('http:') ||
|
||||
arg.toLowerCase().startsWith('https:') ||
|
||||
arg.toLowerCase().startsWith('ftp:') ||
|
||||
arg.toLowerCase().startsWith('magnet:') ||
|
||||
arg.toLowerCase().startsWith('thunder:')
|
||||
) {
|
||||
if (checkIsSupportedSchema(arg)) {
|
||||
return arg
|
||||
}
|
||||
}
|
||||
|
||||
export function checkIsSupportedSchema (url = '') {
|
||||
const str = url.toLowerCase()
|
||||
if (
|
||||
str.startsWith('mo:') ||
|
||||
str.startsWith('motrix:') ||
|
||||
str.startsWith('http:') ||
|
||||
str.startsWith('https:') ||
|
||||
str.startsWith('ftp:') ||
|
||||
str.startsWith('magnet:') ||
|
||||
str.startsWith('thunder:')
|
||||
) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function isDirectory (path) {
|
||||
return existsSync(path) && lstatSync(path).isDirectory()
|
||||
}
|
||||
|
||||
@@ -84,9 +84,9 @@ export default class Api {
|
||||
savePreference (params = {}) {
|
||||
const kebabParams = changeKeysToKebabCase(params)
|
||||
if (is.renderer()) {
|
||||
this.savePreferenceToNativeStore(kebabParams)
|
||||
return this.savePreferenceToNativeStore(kebabParams)
|
||||
} else {
|
||||
this.savePreferenceToLocalStorage(kebabParams)
|
||||
return this.savePreferenceToLocalStorage(kebabParams)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ export default class Api {
|
||||
if (!isEmpty(system)) {
|
||||
console.info('[Motrix] save system config: ', system)
|
||||
application.configManager.setSystemConfig(system)
|
||||
this.changeGlobalOption(system)
|
||||
}
|
||||
|
||||
if (!isEmpty(user)) {
|
||||
@@ -115,6 +116,15 @@ export default class Api {
|
||||
return this.client.call('getVersion')
|
||||
}
|
||||
|
||||
changeGlobalOption (options) {
|
||||
const args = {}
|
||||
Object.keys(options).forEach((key) => {
|
||||
args[key] = `${options[key]}`
|
||||
})
|
||||
|
||||
return this.client.call('changeGlobalOption', args)
|
||||
}
|
||||
|
||||
getGlobalOption () {
|
||||
return new Promise((resolve) => {
|
||||
this.client.call('getGlobalOption')
|
||||
@@ -124,6 +134,18 @@ export default class Api {
|
||||
})
|
||||
}
|
||||
|
||||
getOption (params = {}) {
|
||||
const { gid } = params
|
||||
const args = compactUndefined([gid])
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this.client.call('getOption', ...args)
|
||||
.then((data) => {
|
||||
resolve(changeKeysToCamelCase(data))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getGlobalStat () {
|
||||
return this.client.call('getGlobalStat')
|
||||
}
|
||||
@@ -133,8 +155,9 @@ export default class Api {
|
||||
uris,
|
||||
options
|
||||
} = params
|
||||
const kebabOptions = changeKeysToKebabCase(options)
|
||||
const tasks = uris.map((uri) => {
|
||||
const args = compactUndefined([[uri], options])
|
||||
const args = compactUndefined([[uri], kebabOptions])
|
||||
return [ 'aria2.addUri', ...args ]
|
||||
})
|
||||
return this.client.multicall(tasks)
|
||||
@@ -145,7 +168,8 @@ export default class Api {
|
||||
torrent,
|
||||
options
|
||||
} = params
|
||||
const args = compactUndefined([torrent, [], options])
|
||||
const kebabOptions = changeKeysToKebabCase(options)
|
||||
const args = compactUndefined([torrent, [], kebabOptions])
|
||||
return this.client.call('addTorrent', ...args)
|
||||
}
|
||||
|
||||
@@ -154,7 +178,8 @@ export default class Api {
|
||||
metalink,
|
||||
options
|
||||
} = params
|
||||
const args = compactUndefined([metalink, options])
|
||||
const kebabOptions = changeKeysToKebabCase(options)
|
||||
const args = compactUndefined([metalink, kebabOptions])
|
||||
return this.client.call('addMetalink', ...args)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
|
||||
<rect x="4" y="3" width="16" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
|
||||
<line data-color="color-2" x1="1" y1="6" x2="1" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-color="color-2" x1="23" y1="6" x2="23" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<polyline data-cap="butt" points="10 15 10 8 16 8 16 14" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
|
||||
<circle data-stroke="none" cx="9" cy="15" r="2" stroke="none"/>
|
||||
<circle data-stroke="none" cx="15" cy="14" r="2" stroke="none"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 747 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
|
||||
<polyline data-cap="butt" data-color="color-2" points="1 20 6 14 10 18 17 10 23 17" fill="none" stroke-miterlimit="10"/>
|
||||
<rect x="1" y="3" width="22" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
|
||||
<circle data-color="color-2" cx="9" cy="8" r="2" fill="none" stroke-miterlimit="10"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 509 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
|
||||
<path fill="none" stroke="currentColor" stroke-miterlimit="10" d="M3,14V4 c0-0.552,0.448-1,1-1h16c0.552,0,1,0.448,1,1v6"/>
|
||||
<path fill="none" stroke="currentColor" stroke-miterlimit="10" d="M10,18H1v0 c0,1.657,1.343,3,3,3h6"/>
|
||||
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M14.126,17 c0.444-1.725,2.01-3,3.874-3c1.48,0,2.772,0.804,3.464,1.999"/>
|
||||
<polygon data-color="color-2" data-stroke="none" points="23.22,13.649 22.792,18 18.522,17.061 " stroke-linejoin="miter" stroke-linecap="square" stroke="none"/>
|
||||
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M21.874,20 c-0.444,1.725-2.01,3-3.874,3c-1.48,0-2.772-0.804-3.464-1.999"/>
|
||||
<polygon data-color="color-2" data-stroke="none" points="12.78,23.351 13.208,19 17.478,19.939 " stroke-linejoin="miter" stroke-linecap="square" stroke="none"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<g>
|
||||
<path d="M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12s12-5.383,12-12S18.617,0,12,0z M19,13h-8V5h2v6h6V13z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 214 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
|
||||
<path data-cap="butt" fill="none" stroke="currentColor" stroke-miterlimit="10" d="M6.121,20.121 C7.727,21.22,9.907,22,12,22c5.523,0,10-4.477,10-10c0-5.523-4.477-10-10-10C8.101,2,4.728,4.233,3.078,7.488"/>
|
||||
<polyline fill="none" stroke="currentColor" stroke-miterlimit="10" points="2.278,1.588 3.078,7.488 9.078,6.688 "/>
|
||||
<circle data-color="color-2" fill="none" stroke-miterlimit="10" cx="4" cy="18" r="3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 609 B |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke="currentColor">
|
||||
<line data-cap="butt" data-color="color-2" x1="2" y1="6" x2="22" y2="6" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="12" y1="2" x2="12" y2="22" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="7" y1="2" x2="7" y2="6" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="17" y1="2" x2="17" y2="6" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="2" y1="18" x2="22" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="7" y1="22" x2="7" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="17" y1="22" x2="17" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<rect x="2" y="2" width="20" height="20" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -6,6 +6,9 @@
|
||||
</a>
|
||||
</el-col>
|
||||
<el-col :span="18" class="copyright-right">
|
||||
<a target="_blank" href="https://motrix.app/license" rel="noopener noreferrer">
|
||||
{{ $t('about.license') }}
|
||||
</a>
|
||||
<a target="_blank" href="https://motrix.app/about" rel="noopener noreferrer">
|
||||
{{ $t('about.about') }}
|
||||
</a>
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
<div class="aside-inner">
|
||||
<mo-logo-mini />
|
||||
<ul class="menu top-menu">
|
||||
<li @click="nav('/task')">
|
||||
<li @click="nav('/task')" class="non-draggable">
|
||||
<mo-icon name="menu-task" width="20" height="20" />
|
||||
</li>
|
||||
<li @click="showAddTask()">
|
||||
<li @click="showAddTask()" class="non-draggable">
|
||||
<mo-icon name="menu-add" width="20" height="20" />
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu bottom-menu">
|
||||
<li @click="nav('/preference')">
|
||||
<li @click="nav('/preference')" class="non-draggable">
|
||||
<mo-icon name="menu-preference" width="20" height="20" />
|
||||
</li>
|
||||
<li @click="showAboutPanel">
|
||||
<li @click="showAboutPanel" class="non-draggable">
|
||||
<mo-icon name="menu-about" width="20" height="20" />
|
||||
</li>
|
||||
</ul>
|
||||
@@ -45,9 +45,6 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open (link) {
|
||||
this.$electron.shell.openExternal(link)
|
||||
},
|
||||
showAddTask (taskType = 'uri') {
|
||||
this.$store.dispatch('app/showAddTaskDialog', taskType)
|
||||
},
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import Icon from '@/components/Icons/Icon'
|
||||
|
||||
Icon.register({
|
||||
'audio': {
|
||||
'width': 24,
|
||||
'height': 24,
|
||||
'raw': `<rect x="4" y="3" width="16" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
|
||||
<line data-color="color-2" x1="1" y1="6" x2="1" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-color="color-2" x1="23" y1="6" x2="23" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<polyline data-cap="butt" points="10 15 10 8 16 8 16 14" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
|
||||
<circle data-stroke="none" cx="9" cy="15" r="2" stroke="none"/>
|
||||
<circle data-stroke="none" cx="15" cy="14" r="2" stroke="none"/>`,
|
||||
'g': {
|
||||
'stroke': 'currentColor',
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
'stroke-width': '2'
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,17 @@
|
||||
import Icon from '@/components/Icons/Icon'
|
||||
|
||||
Icon.register({
|
||||
'image': {
|
||||
'width': 24,
|
||||
'height': 24,
|
||||
'raw': `<polyline data-cap="butt" data-color="color-2" points="1 20 6 14 10 18 17 10 23 17" fill="none" stroke-miterlimit="10"/>
|
||||
<rect x="1" y="3" width="22" height="18" fill="none" stroke="currentColor" stroke-miterlimit="10"/>
|
||||
<circle data-color="color-2" cx="9" cy="8" r="2" fill="none" stroke-miterlimit="10"/>`,
|
||||
'g': {
|
||||
'stroke': 'currentColor',
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
'stroke-width': '2'
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -6,7 +6,7 @@ Icon.register({
|
||||
'height': 24,
|
||||
'raw': `<g stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<path fill="none" stroke-miterlimit="10" d="M3,14V4 c0-0.552,0.448-1,1-1h16c0.552,0,1,0.448,1,1v6"></path>
|
||||
<path fill="none" stroke="#111111" stroke-miterlimit="10" d="M10,18H1v0 c0,1.657,1.343,3,3,3h6"></path>
|
||||
<path fill="none" stroke-miterlimit="10" d="M10,18H1v0 c0,1.657,1.343,3,3,3h6"></path>
|
||||
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M14.126,17 c0.444-1.725,2.01-3,3.874-3c1.48,0,2.772,0.804,3.464,1.999"></path>
|
||||
<polygon data-color="color-2" data-stroke="none" points="23.22,13.649 22.792,18 18.522,17.061 " stroke-linejoin="miter" stroke-linecap="square" stroke="none"></polygon>
|
||||
<path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M21.874,20 c-0.444,1.725-2.01,3-3.874,3c-1.48,0-2.772-0.804-3.464-1.999"></path>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import Icon from '@/components/Icons/Icon'
|
||||
|
||||
Icon.register({
|
||||
'task-history': {
|
||||
'width': 24,
|
||||
'height': 24,
|
||||
'paths': [{
|
||||
'd': 'M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12s12-5.383,12-12S18.617,0,12,0z M19,13h-8V5h2v6h6V13z'
|
||||
}]
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,17 @@
|
||||
import Icon from '@/components/Icons/Icon'
|
||||
|
||||
Icon.register({
|
||||
'task-restart': {
|
||||
'width': 24,
|
||||
'height': 24,
|
||||
'raw': `<path data-cap="butt" fill="none" stroke="currentColor" stroke-miterlimit="10" d="M6.121,20.121 C7.727,21.22,9.907,22,12,22c5.523,0,10-4.477,10-10c0-5.523-4.477-10-10-10C8.101,2,4.728,4.233,3.078,7.488"/>
|
||||
<polyline fill="none" stroke="currentColor" stroke-miterlimit="10" points="2.278,1.588 3.078,7.488 9.078,6.688 "/>
|
||||
<circle data-color="color-2" fill="none" stroke-miterlimit="10" cx="4" cy="18" r="3"/>`,
|
||||
'g': {
|
||||
'stroke': 'currentColor',
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
'stroke-width': '2'
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
import Icon from '@/components/Icons/Icon'
|
||||
|
||||
Icon.register({
|
||||
'video': {
|
||||
'width': 24,
|
||||
'height': 24,
|
||||
'raw': `<line data-cap="butt" data-color="color-2" x1="2" y1="6" x2="22" y2="6" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="12" y1="2" x2="12" y2="22" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="7" y1="2" x2="7" y2="6" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="17" y1="2" x2="17" y2="6" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="2" y1="18" x2="22" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="7" y1="22" x2="7" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<line data-cap="butt" data-color="color-2" x1="17" y1="22" x2="17" y2="18" fill="none" stroke-miterlimit="10"/>
|
||||
<rect x="2" y="2" width="20" height="20" fill="none" stroke="currentColor" stroke-miterlimit="10"/>`,
|
||||
'g': {
|
||||
'stroke': 'currentColor',
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
'stroke-width': '2'
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -14,7 +14,6 @@
|
||||
import { mapState } from 'vuex'
|
||||
import AboutPanel from '@/components/About/AboutPanel'
|
||||
import Aside from '@/components/Aside/Index'
|
||||
import Subnav from '@/components/Subnav/Index'
|
||||
import Speedometer from '@/components/Speedometer/Speedometer'
|
||||
import AddTask from '@/components/Task/AddTask'
|
||||
import TaskItemInfo from '@/components/Task/TaskItemInfo'
|
||||
@@ -25,7 +24,6 @@
|
||||
components: {
|
||||
[AboutPanel.name]: AboutPanel,
|
||||
[Aside.name]: Aside,
|
||||
[Subnav.name]: Subnav,
|
||||
[Speedometer.name]: Speedometer,
|
||||
[AddTask.name]: AddTask,
|
||||
[TaskItemInfo.name]: TaskItemInfo,
|
||||
|
||||
@@ -92,13 +92,21 @@
|
||||
})
|
||||
},
|
||||
onDownloadError: function (event) {
|
||||
console.log('aria2 onDownloadError', event)
|
||||
const [{ gid }] = event
|
||||
this.fetchTaskItem({ gid })
|
||||
.then((task) => {
|
||||
const taskName = getTaskName(task)
|
||||
const { errorCode, errorMessage } = task
|
||||
console.error(`[Motrix] download error===> Gid: ${gid}, #${errorCode}, ${errorMessage}`)
|
||||
const message = this.$t('task.download-error-message', { taskName })
|
||||
this.$msg.error(message)
|
||||
const link = `<a target="_blank" href="https://github.com/agalwood/Motrix/wiki/Error#${errorCode}" rel="noopener noreferrer">#${errorCode}</a>`
|
||||
this.$msg({
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
duration: 5000,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `${message} ${link}`
|
||||
})
|
||||
})
|
||||
},
|
||||
onDownloadComplete: function (event) {
|
||||
@@ -196,6 +204,7 @@
|
||||
polling: function () {
|
||||
this.$store.dispatch('app/fetchGlobalStat')
|
||||
this.$store.dispatch('task/fetchList')
|
||||
|
||||
if (this.taskItemInfoVisible && this.currentTaskItem) {
|
||||
this.$store.dispatch('task/fetchItem', this.currentTaskItem.gid)
|
||||
}
|
||||
@@ -206,13 +215,16 @@
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.$store.dispatch('app/fetchEngineInfo')
|
||||
this.$store.dispatch('app/fetchEngineOptions')
|
||||
|
||||
this.startPolling()
|
||||
|
||||
this.bindEngineEvents()
|
||||
},
|
||||
mounted: function () {
|
||||
setTimeout(() => {
|
||||
this.$store.dispatch('app/fetchEngineInfo')
|
||||
this.$store.dispatch('app/fetchEngineOptions')
|
||||
|
||||
this.startPolling()
|
||||
}, 100)
|
||||
},
|
||||
destroyed: function () {
|
||||
this.$store.dispatch('task/saveSession')
|
||||
|
||||
|
||||
@@ -62,9 +62,13 @@ export function moveTaskFilesToTrash (task, messages = {}) {
|
||||
return false
|
||||
}
|
||||
|
||||
const deleteResult1 = remote.shell.moveItemToTrash(path)
|
||||
if (!deleteResult1 && delFailMsg) {
|
||||
Message.error(delFailMsg)
|
||||
let deleteResult1 = true
|
||||
const isFileExist = existsSync(path)
|
||||
if (isFileExist) {
|
||||
deleteResult1 = remote.shell.moveItemToTrash(path)
|
||||
if (!deleteResult1 && delFailMsg) {
|
||||
Message.error(delFailMsg)
|
||||
}
|
||||
}
|
||||
|
||||
let deleteResult2 = true
|
||||
|
||||
@@ -11,34 +11,6 @@
|
||||
size="mini"
|
||||
:model="form"
|
||||
:rules="rules">
|
||||
<el-form-item :label="`${$t('preferences.appearance')}: `" :label-width="formLabelWidth">
|
||||
<el-col class="form-item-sub" :span="24">
|
||||
<mo-theme-switcher
|
||||
v-model="form.theme"
|
||||
@change="handleThemeChange"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col v-if="showHideAppMenuOption" class="form-item-sub" :span="16">
|
||||
<el-checkbox v-model="form.hideAppMenu">
|
||||
{{ $t('preferences.hide-app-menu') }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`${$t('preferences.language')}: `" :label-width="formLabelWidth">
|
||||
<el-col class="form-item-sub" :span="16">
|
||||
<el-select
|
||||
v-model="form.locale"
|
||||
@change="handleLocaleChange"
|
||||
:placeholder="$t('preferences.change-language')">
|
||||
<el-option
|
||||
v-for="item in locales"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`${$t('preferences.proxy')}: `" :label-width="formLabelWidth">
|
||||
<el-switch
|
||||
v-model="form.useProxy"
|
||||
@@ -80,7 +52,7 @@
|
||||
width="12"
|
||||
height="12"
|
||||
:spin="true"
|
||||
v-if="isSyncTracker"
|
||||
v-if="trackerSyncing"
|
||||
/>
|
||||
<mo-icon name="sync" width="12" height="12" v-else />
|
||||
</el-button>
|
||||
@@ -149,13 +121,14 @@
|
||||
<script>
|
||||
import is from 'electron-is'
|
||||
import { mapState } from 'vuex'
|
||||
import ThemeSwitcher from '@/components/Preference/ThemeSwitcher'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import ShowInFolder from '@/components/Native/ShowInFolder'
|
||||
import userAgentMap from '@shared/ua'
|
||||
import { availableLanguages, getLanguage } from '@shared/locales'
|
||||
import { getLocaleManager } from '@/components/Locale'
|
||||
import {
|
||||
convertToTextRows
|
||||
calcFormLabelWidth,
|
||||
convertCommaToLine,
|
||||
convertLineToComma,
|
||||
diffConfig
|
||||
} from '@shared/utils'
|
||||
import '@/components/Icons/sync'
|
||||
import '@/components/Icons/refresh'
|
||||
@@ -166,18 +139,14 @@
|
||||
allProxyBackup,
|
||||
btTracker,
|
||||
hideAppMenu,
|
||||
locale,
|
||||
theme,
|
||||
useProxy,
|
||||
userAgent
|
||||
} = config
|
||||
const result = {
|
||||
allProxy,
|
||||
allProxyBackup,
|
||||
btTracker: convertToTextRows(btTracker),
|
||||
btTracker: convertCommaToLine(btTracker),
|
||||
hideAppMenu,
|
||||
locale,
|
||||
theme,
|
||||
useProxy,
|
||||
userAgent
|
||||
}
|
||||
@@ -187,17 +156,17 @@
|
||||
export default {
|
||||
name: 'mo-preference-advanced',
|
||||
components: {
|
||||
[ThemeSwitcher.name]: ThemeSwitcher,
|
||||
[ShowInFolder.name]: ShowInFolder
|
||||
},
|
||||
data: function () {
|
||||
const { locale } = this.$store.state.preference.config
|
||||
const form = initialForm(this.$store.state.preference.config)
|
||||
return {
|
||||
formLabelWidth: '23%',
|
||||
form: initialForm(this.$store.state.preference.config),
|
||||
isSyncTracker: false,
|
||||
form,
|
||||
formLabelWidth: calcFormLabelWidth(locale),
|
||||
formOriginal: cloneDeep(form),
|
||||
rules: {},
|
||||
color: '#c00',
|
||||
locales: availableLanguages
|
||||
trackerSyncing: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -217,24 +186,15 @@
|
||||
},
|
||||
methods: {
|
||||
isRenderer: is.renderer,
|
||||
handleLocaleChange (locale) {
|
||||
const lng = getLanguage(locale)
|
||||
getLocaleManager().changeLanguage(lng)
|
||||
this.$electron.ipcRenderer.send('command', 'application:change-locale', lng)
|
||||
},
|
||||
handleThemeChange (theme) {
|
||||
this.form.theme = theme
|
||||
this.$electron.ipcRenderer.send('command', 'application:change-theme', theme)
|
||||
},
|
||||
syncTrackerFromGitHub () {
|
||||
this.isSyncTracker = true
|
||||
this.trackerSyncing = true
|
||||
this.$store.dispatch('preference/fetchBtTracker')
|
||||
.then((data) => {
|
||||
console.log('syncTrackerFromGitHub data====>', data)
|
||||
this.form.btTracker = data
|
||||
})
|
||||
.finally(() => {
|
||||
this.isSyncTracker = false
|
||||
this.trackerSyncing = false
|
||||
})
|
||||
},
|
||||
onUseProxyChange (flag) {
|
||||
@@ -269,11 +229,23 @@
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
const changed = diffConfig(this.formOriginal, this.form)
|
||||
const data = {
|
||||
...changed,
|
||||
btTracker: convertLineToComma(this.form.btTracker)
|
||||
}
|
||||
console.log('changed====》', data)
|
||||
|
||||
this.$store.dispatch('preference/save', data)
|
||||
.then(() => {
|
||||
this.$store.dispatch('app/fetchEngineOptions')
|
||||
this.$msg.success(this.$t('preferences.save-success-message'))
|
||||
})
|
||||
.catch(() => {
|
||||
this.$msg.success(this.$t('preferences.save-fail-message'))
|
||||
})
|
||||
|
||||
console.log('this.form===>', this.form)
|
||||
this.$store.dispatch('preference/save', this.form)
|
||||
if (this.isRenderer()) {
|
||||
this.$electron.ipcRenderer.send('command', 'application:relaunch')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -11,7 +11,49 @@
|
||||
size="mini"
|
||||
:model="form"
|
||||
:rules="rules">
|
||||
<el-form-item :label="`${$t('preferences.appearance')}: `" :label-width="formLabelWidth">
|
||||
<el-col class="form-item-sub" :span="24">
|
||||
<mo-theme-switcher
|
||||
v-model="form.theme"
|
||||
@change="handleThemeChange"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col v-if="showHideAppMenuOption" class="form-item-sub" :span="16">
|
||||
<el-checkbox v-model="form.hideAppMenu">
|
||||
{{ $t('preferences.hide-app-menu') }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`${$t('preferences.language')}: `" :label-width="formLabelWidth">
|
||||
<el-col class="form-item-sub" :span="16">
|
||||
<el-select
|
||||
v-model="form.locale"
|
||||
@change="handleLocaleChange"
|
||||
:placeholder="$t('preferences.change-language')">
|
||||
<el-option
|
||||
v-for="item in locales"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`${$t('preferences.startup')}: `" :label-width="formLabelWidth">
|
||||
<el-col
|
||||
class="form-item-sub"
|
||||
:span="24"
|
||||
v-if="!isLinux()"
|
||||
>
|
||||
<el-checkbox v-model="form.openAtLogin">
|
||||
{{ $t('preferences.open-at-login') }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
<el-col class="form-item-sub" :span="24">
|
||||
<el-checkbox v-model="form.keepWindowState">
|
||||
{{ $t('preferences.keep-window-state') }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
<el-col class="form-item-sub" :span="24">
|
||||
<el-checkbox v-model="form.resumeAllWhenAppLaunched">
|
||||
{{ $t('preferences.auto-resume-all') }}
|
||||
@@ -39,6 +81,30 @@
|
||||
{{ $t('preferences.mas-default-dir-tips') }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`${$t('preferences.transfer-settings')}: `" :label-width="formLabelWidth">
|
||||
<el-col class="form-item-sub" :span="24">
|
||||
{{ $t('preferences.transfer-speed-upload') }}
|
||||
<el-select v-model="form.maxOverallUploadLimit">
|
||||
<el-option
|
||||
v-for="item in speedOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col class="form-item-sub" :span="24">
|
||||
{{ $t('preferences.transfer-speed-download') }}
|
||||
<el-select v-model="form.maxOverallDownloadLimit">
|
||||
<el-option
|
||||
v-for="item in speedOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`${$t('preferences.task-manage')}: `" :label-width="formLabelWidth">
|
||||
<el-col class="form-item-sub" :span="24">
|
||||
{{ $t('preferences.max-concurrent-downloads') }}
|
||||
@@ -88,32 +154,51 @@
|
||||
<script>
|
||||
import is from 'electron-is'
|
||||
import { mapState } from 'vuex'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import SelectDirectory from '@/components/Native/SelectDirectory'
|
||||
import ThemeSwitcher from '@/components/Preference/ThemeSwitcher'
|
||||
import { availableLanguages, getLanguage } from '@shared/locales'
|
||||
import { getLocaleManager } from '@/components/Locale'
|
||||
import { prettifyDir } from '@/components/Native/utils'
|
||||
import { calcFormLabelWidth, diffConfig } from '@shared/utils'
|
||||
|
||||
const initialForm = (config) => {
|
||||
const {
|
||||
autoCheckUpdate,
|
||||
dir,
|
||||
split,
|
||||
resumeAllWhenAppLaunched,
|
||||
hideAppMenu,
|
||||
keepWindowState,
|
||||
lastCheckUpdateTime,
|
||||
locale,
|
||||
maxConcurrentDownloads,
|
||||
maxConnectionPerServer,
|
||||
taskNotification,
|
||||
autoCheckUpdate,
|
||||
maxOverallUploadLimit,
|
||||
maxOverallDownloadLimit,
|
||||
newTaskShowDownloading,
|
||||
lastCheckUpdateTime
|
||||
openAtLogin,
|
||||
resumeAllWhenAppLaunched,
|
||||
split,
|
||||
taskNotification,
|
||||
theme
|
||||
} = config
|
||||
const result = {
|
||||
dir,
|
||||
split,
|
||||
autoCheckUpdate,
|
||||
continue: config.continue,
|
||||
resumeAllWhenAppLaunched,
|
||||
dir,
|
||||
hideAppMenu,
|
||||
keepWindowState,
|
||||
lastCheckUpdateTime,
|
||||
locale,
|
||||
maxConcurrentDownloads,
|
||||
maxConnectionPerServer,
|
||||
taskNotification,
|
||||
autoCheckUpdate,
|
||||
maxOverallUploadLimit,
|
||||
maxOverallDownloadLimit,
|
||||
newTaskShowDownloading,
|
||||
lastCheckUpdateTime
|
||||
openAtLogin,
|
||||
resumeAllWhenAppLaunched,
|
||||
split,
|
||||
taskNotification,
|
||||
theme
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -121,19 +206,28 @@
|
||||
export default {
|
||||
name: 'mo-preference-basic',
|
||||
components: {
|
||||
[SelectDirectory.name]: SelectDirectory
|
||||
[SelectDirectory.name]: SelectDirectory,
|
||||
[ThemeSwitcher.name]: ThemeSwitcher
|
||||
},
|
||||
data: function () {
|
||||
const { locale } = this.$store.state.preference.config
|
||||
const form = initialForm(this.$store.state.preference.config)
|
||||
return {
|
||||
formLabelWidth: '23%',
|
||||
form: initialForm(this.$store.state.preference.config),
|
||||
rules: {}
|
||||
form,
|
||||
formLabelWidth: calcFormLabelWidth(locale),
|
||||
formOriginal: cloneDeep(form),
|
||||
locales: availableLanguages,
|
||||
rules: {},
|
||||
speedOptions: this.buildSpeedOptions()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title: function () {
|
||||
return this.$t('preferences.basic')
|
||||
},
|
||||
showHideAppMenuOption: function () {
|
||||
return is.windows() || is.linux()
|
||||
},
|
||||
downloadDir: function () {
|
||||
return prettifyDir(this.form.dir)
|
||||
},
|
||||
@@ -144,6 +238,45 @@
|
||||
methods: {
|
||||
isRenderer: is.renderer,
|
||||
isMas: is.mas,
|
||||
isLinux: is.linux,
|
||||
handleLocaleChange (locale) {
|
||||
const lng = getLanguage(locale)
|
||||
getLocaleManager().changeLanguage(lng)
|
||||
this.speedOptions = this.buildSpeedOptions()
|
||||
this.$electron.ipcRenderer.send('command', 'application:change-locale', lng)
|
||||
},
|
||||
handleThemeChange (theme) {
|
||||
this.form.theme = theme
|
||||
this.$electron.ipcRenderer.send('command', 'application:change-theme', theme)
|
||||
},
|
||||
buildSpeedOptions () {
|
||||
return [
|
||||
{
|
||||
label: this.$t('preferences.transfer-speed-unlimited'),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '128 KB/s',
|
||||
value: '128K'
|
||||
},
|
||||
{
|
||||
label: '512 KB/s',
|
||||
value: '512K'
|
||||
},
|
||||
{
|
||||
label: '1 MB/s',
|
||||
value: '1M'
|
||||
},
|
||||
{
|
||||
label: '5 MB/s',
|
||||
value: '5M'
|
||||
},
|
||||
{
|
||||
label: '10 MB/s',
|
||||
value: '10M'
|
||||
}
|
||||
]
|
||||
},
|
||||
onDirectorySelected (dir) {
|
||||
this.form.dir = dir
|
||||
},
|
||||
@@ -154,9 +287,24 @@
|
||||
return false
|
||||
}
|
||||
|
||||
this.$store.dispatch('preference/save', this.form)
|
||||
const { openAtLogin } = this.form
|
||||
const changed = diffConfig(this.formOriginal, this.form)
|
||||
const data = {
|
||||
...changed
|
||||
}
|
||||
console.log('changed====》', data)
|
||||
|
||||
this.$store.dispatch('preference/save', data)
|
||||
.then(() => {
|
||||
this.$store.dispatch('app/fetchEngineOptions')
|
||||
this.$msg.success(this.$t('preferences.save-success-message'))
|
||||
})
|
||||
.catch(() => {
|
||||
this.$msg.success(this.$t('preferences.save-fail-message'))
|
||||
})
|
||||
|
||||
if (this.isRenderer()) {
|
||||
this.$electron.ipcRenderer.send('command', 'application:relaunch')
|
||||
this.$electron.ipcRenderer.send('command', 'application:open-at-login', openAtLogin)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -52,6 +52,9 @@
|
||||
import is from 'electron-is'
|
||||
import { mapState } from 'vuex'
|
||||
import '@/components/Icons/info-square'
|
||||
import {
|
||||
calcFormLabelWidth
|
||||
} from '@shared/utils'
|
||||
|
||||
const initialForm = (config) => {
|
||||
const {
|
||||
@@ -68,9 +71,11 @@
|
||||
components: {
|
||||
},
|
||||
data: function () {
|
||||
const { locale } = this.$store.state.preference.config
|
||||
const form = initialForm(this.$store.state.preference.config)
|
||||
return {
|
||||
formLabelWidth: '23%',
|
||||
form: initialForm(this.$store.state.preference.config),
|
||||
form,
|
||||
formLabelWidth: calcFormLabelWidth(locale),
|
||||
rules: {}
|
||||
}
|
||||
},
|
||||
@@ -97,7 +102,6 @@
|
||||
console.log('this.form===>', this.form)
|
||||
this.$store.dispatch('preference/save', this.form)
|
||||
if (this.isRenderer()) {
|
||||
this.$electron.ipcRenderer.send('command', 'application:relaunch')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<template>
|
||||
<el-aside width="200px" class="subnav">
|
||||
<router-view name="subnav" />
|
||||
</el-aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'mo-subnav',
|
||||
components: {
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -5,6 +5,7 @@
|
||||
:visible.sync="visible"
|
||||
:before-close="handleClose"
|
||||
@open="handleOpen"
|
||||
@opened="handleOpened"
|
||||
@closed="handleClosed">
|
||||
<el-form
|
||||
ref="taskForm"
|
||||
@@ -21,6 +22,7 @@
|
||||
auto-complete="off"
|
||||
:placeholder="$t('task.uri-task-tips')"
|
||||
@change="handleUriChange"
|
||||
@paste.native="handleUriPaste"
|
||||
v-model="form.uris">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -28,7 +30,6 @@
|
||||
<el-tab-pane
|
||||
:label="$t('task.torrent-task')"
|
||||
name="torrent"
|
||||
v-if="iLoveEggFeatures"
|
||||
>
|
||||
<el-form-item>
|
||||
<mo-select-torrent
|
||||
@@ -115,26 +116,31 @@
|
||||
import SelectDirectory from '@/components/Native/SelectDirectory'
|
||||
import SelectTorrent from '@/components/Task/SelectTorrent'
|
||||
import { prettifyDir } from '@/components/Native/utils'
|
||||
import '@/components/Icons/inbox'
|
||||
import {
|
||||
NONE_SELECTED_FILES,
|
||||
SELECTED_ALL_FILES
|
||||
} from '@shared/constants'
|
||||
import {
|
||||
detectResource,
|
||||
splitTaskLinks,
|
||||
needCheckCopyright
|
||||
splitTaskLinks
|
||||
} from '@shared/utils'
|
||||
import '@/components/Icons/inbox'
|
||||
|
||||
const initialForm = (state) => {
|
||||
const { addTaskUrl } = state.app
|
||||
const { addTaskUrl, addTaskOptions } = state.app
|
||||
const { dir, split, newTaskShowDownloading } = state.preference.config
|
||||
const result = {
|
||||
uris: addTaskUrl,
|
||||
torrent: '',
|
||||
selectFile: NONE_SELECTED_FILES,
|
||||
out: '',
|
||||
userAgent: '',
|
||||
referer: '',
|
||||
cookie: '',
|
||||
dir,
|
||||
split,
|
||||
newTaskShowDownloading
|
||||
newTaskShowDownloading,
|
||||
...addTaskOptions
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -159,7 +165,6 @@
|
||||
return {
|
||||
formLabelWidth: '100px',
|
||||
showAdvanced: false,
|
||||
torrentName: '',
|
||||
form: {},
|
||||
rules: {}
|
||||
}
|
||||
@@ -176,10 +181,7 @@
|
||||
}),
|
||||
...mapState('preference', {
|
||||
config: state => state.config
|
||||
}),
|
||||
iLoveEggFeatures: function () {
|
||||
return !this.isMas() || (this.isMas() && this.config.enableEggFeatures)
|
||||
}
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
taskType: function (current, previous) {
|
||||
@@ -216,8 +218,12 @@
|
||||
this.form.uris = content
|
||||
}
|
||||
},
|
||||
handleOpened () {
|
||||
this.detectThunderResource(this.form.uris)
|
||||
},
|
||||
handleClose (done) {
|
||||
this.$store.dispatch('app/hideAddTaskDialog')
|
||||
this.$store.dispatch('app/updateAddTaskOptions', {})
|
||||
},
|
||||
handleClosed () {
|
||||
this.reset()
|
||||
@@ -225,10 +231,13 @@
|
||||
handleTabClick (tab, event) {
|
||||
this.$store.dispatch('app/changeAddTaskType', tab.name)
|
||||
},
|
||||
handleUriChange () {
|
||||
// el-input does not support @paste event ?
|
||||
// https://github.com/ElemeFE/element/blob/master/packages/input/src/input.vue
|
||||
const { uris } = this.form
|
||||
handleUriPaste () {
|
||||
setImmediate(() => {
|
||||
const uris = this.$refs.uri.value
|
||||
this.detectThunderResource(uris)
|
||||
})
|
||||
},
|
||||
detectThunderResource (uris = '') {
|
||||
if (uris.includes('thunder://')) {
|
||||
this.$msg({
|
||||
type: 'warning',
|
||||
@@ -237,10 +246,12 @@
|
||||
})
|
||||
}
|
||||
},
|
||||
handleTorrentChange (torrent, file, fileList) {
|
||||
// TODO 种子选择部分文件下载
|
||||
// console.log('handleTorrentChange===>', torrent, file, fileList)
|
||||
handleUriChange () {
|
||||
console.log('handleUriChange===>', this.form.uris)
|
||||
},
|
||||
handleTorrentChange (torrent, selectedFileIndex) {
|
||||
this.form.torrent = torrent
|
||||
this.form.selectFile = selectedFileIndex
|
||||
},
|
||||
handleSplitChange (value) {
|
||||
console.log('handleSplitChange===>', value)
|
||||
@@ -249,6 +260,7 @@
|
||||
this.form.dir = dir
|
||||
},
|
||||
reset () {
|
||||
this.showAdvanced = false
|
||||
this.form = initialForm(this.$store.state)
|
||||
},
|
||||
handleCancel (formName) {
|
||||
@@ -271,9 +283,12 @@
|
||||
}
|
||||
return result
|
||||
},
|
||||
buildOption (form) {
|
||||
buildOption (type, form) {
|
||||
const {
|
||||
dir, out, split
|
||||
dir,
|
||||
out,
|
||||
selectFile,
|
||||
split
|
||||
} = form
|
||||
const result = {}
|
||||
|
||||
@@ -285,6 +300,15 @@
|
||||
result.out = out
|
||||
}
|
||||
|
||||
if (type === 'torrent') {
|
||||
if (
|
||||
selectFile !== SELECTED_ALL_FILES &&
|
||||
selectFile !== NONE_SELECTED_FILES
|
||||
) {
|
||||
result.selectFile = selectFile
|
||||
}
|
||||
}
|
||||
|
||||
if (split > 0) {
|
||||
result.split = split
|
||||
}
|
||||
@@ -297,8 +321,11 @@
|
||||
},
|
||||
buildUriPayload (form) {
|
||||
let { uris } = form
|
||||
if (isEmpty(uris)) {
|
||||
throw new Error(this.$t('task.new-task-uris-required'))
|
||||
}
|
||||
uris = splitTaskLinks(uris)
|
||||
const options = this.buildOption(form)
|
||||
const options = this.buildOption('uri', form)
|
||||
const result = {
|
||||
uris,
|
||||
options
|
||||
@@ -307,12 +334,14 @@
|
||||
},
|
||||
buildTorrentPayload (form) {
|
||||
const { torrent } = form
|
||||
const options = this.buildOption(form)
|
||||
if (isEmpty(torrent)) {
|
||||
throw new Error(this.$t('task.new-task-torrent-required'))
|
||||
}
|
||||
const options = this.buildOption('torrent', form)
|
||||
const result = {
|
||||
torrent,
|
||||
options
|
||||
}
|
||||
console.log('buildTorrentPayload===>', result)
|
||||
return result
|
||||
},
|
||||
addTask (type, form) {
|
||||
@@ -335,58 +364,24 @@
|
||||
console.error('addTask fail', form)
|
||||
}
|
||||
},
|
||||
checkCopyright (type, form) {
|
||||
const { uris } = form
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (type !== 'uri') {
|
||||
resolve()
|
||||
}
|
||||
|
||||
if (!needCheckCopyright(uris)) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
if (this.iLoveEggFeatures) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
this.$electron.remote.dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
title: this.$t('task.copyright-warning'),
|
||||
message: this.$t('task.copyright-warning-message'),
|
||||
buttons: [this.$t('task.copyright-yes'), this.$t('task.copyright-no')],
|
||||
cancelId: 1
|
||||
}, (buttonIndex, checkboxChecked) => {
|
||||
if (buttonIndex === 0) {
|
||||
resolve()
|
||||
} else {
|
||||
reject(new Error(this.$t('task.copyright-error-message')))
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
submitForm (formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.checkCopyright(this.type, this.form)
|
||||
.then(() => {
|
||||
this.addTask(this.type, this.form)
|
||||
this.$store.dispatch('app/hideAddTaskDialog')
|
||||
if (this.form.newTaskShowDownloading) {
|
||||
this.$router.push({
|
||||
path: '/task/active'
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$msg.error(err.message)
|
||||
})
|
||||
try {
|
||||
this.addTask(this.type, this.form)
|
||||
|
||||
this.$store.dispatch('app/hideAddTaskDialog')
|
||||
if (this.form.newTaskShowDownloading) {
|
||||
this.$router.push({
|
||||
path: '/task/active'
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
this.$msg.error(err.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +47,6 @@
|
||||
'status': 'onStatusChange'
|
||||
},
|
||||
methods: {
|
||||
open (link) {
|
||||
this.$electron.shell.openExternal(link)
|
||||
},
|
||||
onStatusChange () {
|
||||
this.changeCurrentList()
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class="upload-torrent"
|
||||
drag
|
||||
action="/"
|
||||
v-if="isTorrentsEmpty"
|
||||
:limit="1"
|
||||
:multiple="false"
|
||||
accept=".torrent"
|
||||
@@ -16,23 +17,115 @@
|
||||
<div class="torrent-name" v-if="name">{{ name }}</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div
|
||||
class="selective-torrent"
|
||||
v-else
|
||||
>
|
||||
<el-row class="torrent-info" :gutter="12">
|
||||
<el-col class="torrent-name" :span="20">
|
||||
<el-tooltip class="item" effect="dark" :content="name" placement="top">
|
||||
<span>{{ name }}</span>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col class="torrent-actions" :span="4">
|
||||
<span
|
||||
@click="handleTrashClick"
|
||||
>
|
||||
<mo-icon name="trash" width="14" height="14" />
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="torrent-file-list">
|
||||
<el-table
|
||||
stripe
|
||||
ref="torrentTable"
|
||||
height="200"
|
||||
:data="files"
|
||||
tooltip-effect="dark"
|
||||
style="width: 100%"
|
||||
@row-dblclick="handleRowDbClick"
|
||||
@selection-change="handleSelectionChange">
|
||||
<el-table-column
|
||||
type="selection"
|
||||
width="35">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('task.file-name')"
|
||||
show-overflow-tooltip>
|
||||
<template slot-scope="scope">{{ scope.row.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('task.file-extension')"
|
||||
width="80">
|
||||
<template slot-scope="scope">{{ scope.row.extension | removeExtensionDot }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('task.file-size')"
|
||||
width="90">
|
||||
<template slot-scope="scope">{{ scope.row.length | bytesToSize }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-row :gutter="12">
|
||||
<el-col class="file-filters" :span="8">
|
||||
<el-button-group>
|
||||
<el-button @click="toggleVideoSelection()">
|
||||
<mo-icon name="video" width="12" height="12" />
|
||||
</el-button>
|
||||
<el-button @click="toggleAudioSelection()">
|
||||
<mo-icon name="audio" width="12" height="12" />
|
||||
</el-button>
|
||||
<el-button @click="toggleImageSelection()">
|
||||
<mo-icon name="image" width="12" height="12" />
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</el-col>
|
||||
<el-col :span="16" style="text-align: right">
|
||||
{{ $t('task.selected-files-sum', { selectedFilesCount, selectedFilesTotalSize }) }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { isEmpty } from 'lodash'
|
||||
import parseTorrent from 'parse-torrent'
|
||||
import '@/components/Icons/inbox'
|
||||
import { getAsBase64, buildFileList } from '@shared/utils'
|
||||
import '@/components/Icons/video'
|
||||
import '@/components/Icons/audio'
|
||||
import '@/components/Icons/image'
|
||||
import {
|
||||
NONE_SELECTED_FILES,
|
||||
SELECTED_ALL_FILES
|
||||
} from '@shared/constants'
|
||||
import {
|
||||
buildFileList,
|
||||
listTorrentFiles,
|
||||
bytesToSize,
|
||||
filterVideoFiles,
|
||||
filterAudioFiles,
|
||||
filterImageFiles,
|
||||
getAsBase64,
|
||||
removeExtensionDot
|
||||
} from '@shared/utils'
|
||||
|
||||
export default {
|
||||
name: 'mo-select-torrent',
|
||||
components: {
|
||||
},
|
||||
filters: {
|
||||
bytesToSize,
|
||||
removeExtensionDot
|
||||
},
|
||||
props: {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
name: ''
|
||||
name: '',
|
||||
currentTorrent: '',
|
||||
files: [],
|
||||
selectedFiles: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -41,15 +134,40 @@
|
||||
}),
|
||||
...mapState('app', {
|
||||
torrents: state => state.addTaskTorrents
|
||||
})
|
||||
}),
|
||||
isTorrentsEmpty: function () {
|
||||
return this.torrents.length === 0
|
||||
},
|
||||
selectedFilesCount: function () {
|
||||
return this.selectedFiles.length
|
||||
},
|
||||
selectedFilesTotalSize: function () {
|
||||
const result = this.selectedFiles.reduce((acc, cur) => {
|
||||
return acc + cur.length
|
||||
}, 0)
|
||||
return bytesToSize(result)
|
||||
},
|
||||
selectedFileIndex: function () {
|
||||
const { files, selectedFiles } = this
|
||||
if (files.length === 0 || selectedFiles.length === 0) {
|
||||
return NONE_SELECTED_FILES
|
||||
}
|
||||
if (files.length === selectedFiles.length) {
|
||||
return SELECTED_ALL_FILES
|
||||
}
|
||||
const indexArr = this.selectedFiles.map((item) => item.idx)
|
||||
const result = indexArr.join(',')
|
||||
return result
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
torrents (fileList) {
|
||||
const file = fileList[0]
|
||||
if (fileList.length === 0) {
|
||||
this.name = ''
|
||||
this.reset()
|
||||
return
|
||||
}
|
||||
|
||||
const file = fileList[0]
|
||||
if (!file.raw) {
|
||||
return
|
||||
}
|
||||
@@ -57,21 +175,68 @@
|
||||
parseTorrent.remote(file.raw, (err, parsedTorrent) => {
|
||||
if (err) throw err
|
||||
console.log(parsedTorrent)
|
||||
})
|
||||
this.files = listTorrentFiles(parsedTorrent.files)
|
||||
this.$refs.torrentTable.toggleAllSelection()
|
||||
|
||||
getAsBase64(file.raw, (torrent) => {
|
||||
this.name = file.name
|
||||
this.$emit('change', torrent, file, fileList)
|
||||
getAsBase64(file.raw, (torrent) => {
|
||||
this.name = file.name
|
||||
this.currentTorrent = torrent
|
||||
this.$emit('change', torrent, SELECTED_ALL_FILES)
|
||||
})
|
||||
})
|
||||
},
|
||||
selectedFileIndex () {
|
||||
const { currentTorrent, selectedFileIndex } = this
|
||||
this.$emit('change', currentTorrent, selectedFileIndex)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reset () {
|
||||
this.name = ''
|
||||
this.currentTorrent = ''
|
||||
this.files = []
|
||||
if (this.$refs.torrentTable) {
|
||||
this.$refs.torrentTable.clearSelection()
|
||||
}
|
||||
this.$emit('change', '', NONE_SELECTED_FILES)
|
||||
},
|
||||
handleChange (file, fileList) {
|
||||
this.$store.dispatch('app/addTaskAddTorrents', { fileList })
|
||||
},
|
||||
handleExceed (files) {
|
||||
const fileList = buildFileList(files[0])
|
||||
this.$store.dispatch('app/addTaskAddTorrents', { fileList })
|
||||
},
|
||||
handleTrashClick () {
|
||||
this.$store.dispatch('app/addTaskAddTorrents', { fileList: [] })
|
||||
},
|
||||
toggleSelection (rows) {
|
||||
if (isEmpty(rows)) {
|
||||
this.$refs.torrentTable.clearSelection()
|
||||
} else {
|
||||
this.$refs.torrentTable.clearSelection()
|
||||
rows.forEach(row => {
|
||||
this.$refs.torrentTable.toggleRowSelection(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
toggleVideoSelection () {
|
||||
const filtered = filterVideoFiles(this.files)
|
||||
this.toggleSelection(filtered)
|
||||
},
|
||||
toggleAudioSelection () {
|
||||
const filtered = filterAudioFiles(this.files)
|
||||
this.toggleSelection(filtered)
|
||||
},
|
||||
toggleImageSelection () {
|
||||
const filtered = filterImageFiles(this.files)
|
||||
this.toggleSelection(filtered)
|
||||
},
|
||||
handleRowDbClick (row, column, event) {
|
||||
this.$refs.torrentTable.toggleRowSelection(row)
|
||||
},
|
||||
handleSelectionChange (val) {
|
||||
this.selectedFiles = val
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,4 +264,42 @@
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
.selective-torrent {
|
||||
.torrent-name {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.torrent-info {
|
||||
margin-bottom: 15px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.torrent-actions {
|
||||
text-align: right;
|
||||
line-height: 16px;
|
||||
&> span {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 14px;
|
||||
padding: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.file-filters {
|
||||
button {
|
||||
font-size: 0;
|
||||
}
|
||||
}
|
||||
.torrent-file-list {
|
||||
border: 1px solid #ebeef5;
|
||||
border-bottom: none;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
margin-bottom: 8px;
|
||||
.el-table th {
|
||||
padding: 2px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
<template>
|
||||
<div class="task-item-actions" v-on:dblclick.stop="() => null">
|
||||
<!-- <i @click.stop="onMoreClick">
|
||||
<mo-icon name="more" width="14" height="14" />
|
||||
</i> -->
|
||||
<i @click.stop="onInfoClick" v-if="mode === 'LIST'">
|
||||
<mo-icon name="info-circle" width="14" height="14" />
|
||||
</i>
|
||||
<i @click.stop="onLinkClick">
|
||||
<mo-icon name="link" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="isRenderer()" @click.stop="onFolderClick">
|
||||
<mo-icon name="folder" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="task.status ==='complete' ||
|
||||
task.status ==='removed' ||
|
||||
task.status ==='error'"
|
||||
@click.stop="onTrashClick">
|
||||
<mo-icon name="trash" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="task.status ==='active' ||
|
||||
task.status ==='waiting' ||
|
||||
task.status ==='paused'"
|
||||
@click.stop="onDeleteClick">
|
||||
<mo-icon name="delete" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="task.status ==='active'" @click.stop="onPauseClick">
|
||||
<mo-icon name="task-pause-line" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="task.status ==='waiting' || task.status ==='paused'" @click.stop="onResumeClick">
|
||||
<mo-icon name="task-start-line" width="14" height="14" />
|
||||
</i>
|
||||
</div>
|
||||
<ul :key="task.gid" class="task-item-actions" v-on:dblclick.stop="() => null">
|
||||
<li v-for="action in taskActions" :key="action" class="task-item-action">
|
||||
<i v-if="action ==='PAUSE'" @click.stop="onPauseClick">
|
||||
<mo-icon name="task-pause-line" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action ==='STOP'" @click.stop="onStopClick">
|
||||
<mo-icon name="task-stop-line" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action === 'RESUME'" @click.stop="onResumeClick">
|
||||
<mo-icon name="task-start-line" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action === 'RESTART'" @click="onRestartClick">
|
||||
<mo-icon name="task-restart" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action === 'DELETE'" @click.stop="onDeleteClick">
|
||||
<mo-icon name="delete" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action === 'TRASH'" @click.stop="onTrashClick">
|
||||
<mo-icon name="trash" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action ==='FOLDER'" @click.stop="onFolderClick">
|
||||
<mo-icon name="folder" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action ==='LINK'" @click.stop="onLinkClick">
|
||||
<mo-icon name="link" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action ==='INFO'" @click.stop="onInfoClick">
|
||||
<mo-icon name="info-circle" width="14" height="14" />
|
||||
</i>
|
||||
<i v-if="action ==='MORE'" @click.stop="onMoreClick">
|
||||
<mo-icon name="more" width="14" height="14" />
|
||||
</i>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -38,6 +40,8 @@
|
||||
import * as clipboard from 'clipboard-polyfill'
|
||||
import '@/components/Icons/task-start-line'
|
||||
import '@/components/Icons/task-pause-line'
|
||||
import '@/components/Icons/task-stop-line'
|
||||
import '@/components/Icons/task-restart'
|
||||
import '@/components/Icons/delete'
|
||||
import '@/components/Icons/folder'
|
||||
import '@/components/Icons/link'
|
||||
@@ -49,11 +53,23 @@
|
||||
moveTaskFilesToTrash
|
||||
} from '@/components/Native/utils'
|
||||
import {
|
||||
checkTaskIsSeeder,
|
||||
getTaskFullPath,
|
||||
getTaskName,
|
||||
getTaskUri,
|
||||
getTaskFullPath
|
||||
parseHeader
|
||||
} from '@shared/utils'
|
||||
|
||||
const taskActionsMap = {
|
||||
active: ['PAUSE', 'DELETE'],
|
||||
paused: ['RESUME', 'DELETE'],
|
||||
waiting: ['RESUME', 'DELETE'],
|
||||
error: ['RESTART', 'TRASH'],
|
||||
complete: ['RESTART', 'TRASH'],
|
||||
removed: ['RESTART', 'TRASH'],
|
||||
seeding: ['STOP', 'DELETE']
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'mo-task-item-actions',
|
||||
props: {
|
||||
@@ -67,23 +83,48 @@
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
taskName: function () {
|
||||
taskName () {
|
||||
return getTaskName(this.task)
|
||||
},
|
||||
path: function () {
|
||||
path () {
|
||||
return getTaskFullPath(this.task)
|
||||
},
|
||||
isSeeder () {
|
||||
return checkTaskIsSeeder(this.task)
|
||||
},
|
||||
taskStatus () {
|
||||
const { task, isSeeder } = this
|
||||
if (isSeeder) {
|
||||
return 'seeding'
|
||||
} else {
|
||||
return task.status
|
||||
}
|
||||
},
|
||||
taskCommonActions () {
|
||||
let result = is.renderer() ? ['FOLDER'] : []
|
||||
result = (this.mode === 'LIST')
|
||||
? [...result, 'LINK', 'INFO']
|
||||
: [...result, 'LINK']
|
||||
|
||||
return result
|
||||
},
|
||||
taskActions () {
|
||||
const { taskStatus, taskCommonActions } = this
|
||||
const actions = taskActionsMap[taskStatus] || []
|
||||
const result = [...actions, ...taskCommonActions].reverse()
|
||||
return result
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isRenderer: is.renderer,
|
||||
deleteTaskFiles: function (task) {
|
||||
deleteTaskFiles (task) {
|
||||
moveTaskFilesToTrash(task, {
|
||||
pathErrorMsg: this.$t('task.file-path-error'),
|
||||
delFailMsg: this.$t('task.remove-task-file-fail'),
|
||||
delConfigFailMsg: this.$t('task.remove-task-config-file-fail')
|
||||
})
|
||||
},
|
||||
removeTaskItem: function (task, isRemoveWithFiles) {
|
||||
removeTaskItem (task, isRemoveWithFiles) {
|
||||
this.$store.dispatch('task/removeTask', this.task)
|
||||
.then(() => {
|
||||
if (isRemoveWithFiles) {
|
||||
@@ -101,7 +142,7 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
removeTaskRecord: function (task, isRemoveWithFiles) {
|
||||
removeTaskRecord (task, isRemoveWithFiles) {
|
||||
this.$store.dispatch('task/removeTaskRecord', this.task)
|
||||
.then(() => {
|
||||
if (isRemoveWithFiles) {
|
||||
@@ -119,7 +160,7 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
onResumeClick: function () {
|
||||
onResumeClick () {
|
||||
this.$store.dispatch('task/resumeTask', this.task)
|
||||
.catch(({ code }) => {
|
||||
if (code === 1) {
|
||||
@@ -129,7 +170,75 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
onPauseClick: function () {
|
||||
onRestartClick (event) {
|
||||
const { task, taskName } = this
|
||||
const { gid, status } = task
|
||||
const uri = getTaskUri(task)
|
||||
const isNeedShowDialog = status === 'complete' || !!event.altKey
|
||||
this.$store.dispatch('task/getTaskOption', gid)
|
||||
.then((data) => {
|
||||
console.log('getTaskOption===>', data)
|
||||
const { dir, header, split } = data
|
||||
const options = {
|
||||
dir,
|
||||
header,
|
||||
split,
|
||||
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', 'uri')
|
||||
},
|
||||
onPauseClick () {
|
||||
this.pauseTask()
|
||||
},
|
||||
onStopClick () {
|
||||
this.stopSeeding()
|
||||
},
|
||||
stopSeeding () {
|
||||
if (!this.isSeeder) {
|
||||
return
|
||||
}
|
||||
this.$store.dispatch('task/pauseTask', this.task)
|
||||
.then(() => {
|
||||
this.$store.dispatch('task/resumeTask', this.task)
|
||||
})
|
||||
},
|
||||
pauseTask () {
|
||||
const { taskName } = this
|
||||
this.$msg.info(this.$t('task.download-pause-message', { taskName }))
|
||||
this.$store.dispatch('task/pauseTask', this.task)
|
||||
@@ -139,7 +248,7 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
onDeleteClick: function () {
|
||||
onDeleteClick () {
|
||||
const self = this
|
||||
const { task } = this
|
||||
this.$electron.remote.dialog.showMessageBox({
|
||||
@@ -155,7 +264,7 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
onTrashClick: function () {
|
||||
onTrashClick () {
|
||||
const self = this
|
||||
const { task } = this
|
||||
this.$electron.remote.dialog.showMessageBox({
|
||||
@@ -171,12 +280,12 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
onFolderClick: function () {
|
||||
onFolderClick () {
|
||||
showItemInFolder(this.path, {
|
||||
errorMsg: this.$t('task.file-not-exist')
|
||||
})
|
||||
},
|
||||
onLinkClick: function () {
|
||||
onLinkClick () {
|
||||
this.$store.dispatch('app/fetchEngineOptions')
|
||||
.then((data) => {
|
||||
const { btTracker } = data
|
||||
@@ -187,10 +296,10 @@
|
||||
})
|
||||
})
|
||||
},
|
||||
onInfoClick: function () {
|
||||
onInfoClick () {
|
||||
this.$store.dispatch('task/showTaskItemInfoDialog', this.task)
|
||||
},
|
||||
onMoreClick: function () {
|
||||
onMoreClick () {
|
||||
console.log('onMoreClick===>')
|
||||
}
|
||||
}
|
||||
@@ -218,7 +327,7 @@
|
||||
background-color: $--task-item-action-hover-background;
|
||||
width: auto;
|
||||
}
|
||||
&> i {
|
||||
&> .task-item-action {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
margin: 0 4px;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
v-if="status === 'active'"
|
||||
:percentage="percent"
|
||||
:show-text="false"
|
||||
status="text"
|
||||
status="success"
|
||||
:color="color">
|
||||
</el-progress>
|
||||
<el-progress
|
||||
|
||||
@@ -307,12 +307,52 @@
|
||||
/* Dialog */
|
||||
.el-dialog {
|
||||
background-color: $--dk-dialog-background;
|
||||
.el-dialog__body {
|
||||
color: $--dk-dialog-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.add-task-dialog .el-dialog__footer {
|
||||
background-color: $--dk-add-task-dialog-footer-background;
|
||||
}
|
||||
|
||||
.torrent-file-list {
|
||||
border-color: $--dk-table-border-color;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
background-color: $--dk-table-background;
|
||||
color: $--dk-table-text-color;
|
||||
tr {
|
||||
background-color: $--dk-table-background;
|
||||
}
|
||||
th {
|
||||
background-color: $--dk-table-th-background;
|
||||
color: $--dk-table-text-color;
|
||||
}
|
||||
th.is-leaf, td {
|
||||
background-color: $--dk-table-background;
|
||||
color: $--dk-table-text-color;
|
||||
border-bottom-color: $--dk-table-border-color;
|
||||
}
|
||||
}
|
||||
.el-table thead th.is-leaf {
|
||||
background-color: $--dk-table-th-background;
|
||||
}
|
||||
|
||||
.el-table--enable-row-hover .el-table__body tr:hover > td,
|
||||
.el-table--enable-row-hover .el-table__body tr.el-table__row--striped:hover > td {
|
||||
background-color: $--dk-table-hover-background;
|
||||
}
|
||||
|
||||
.el-table--group::after, .el-table--border::after, .el-table::before {
|
||||
background-color: $--dk-table-border-color;
|
||||
}
|
||||
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped td {
|
||||
background-color: $--dk-table-striped-background;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.el-tabs__item {
|
||||
color: #a2a3a4;
|
||||
@@ -332,4 +372,16 @@
|
||||
.form-preference .el-switch__label {
|
||||
color: $--dk-preference-form-text-color;
|
||||
}
|
||||
|
||||
.form-preference .el-form-item {
|
||||
a {
|
||||
color: #dfdfdf;
|
||||
&:hover {
|
||||
color: #eee;
|
||||
}
|
||||
&:active {
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ $--dk-panel-border-color: #EBECF0 !default;
|
||||
-------------------------- */
|
||||
$--dk-task-action-color: #eee !default;
|
||||
$--dk-task-action-hover-color: $--color-primary !default;
|
||||
$--dk-task-item-backgroud: #2D2D2D !default;
|
||||
$--dk-task-item-backgroud: #2d2d2d !default;
|
||||
$--dk-task-item-border-color: #555 !default;
|
||||
$--dk-task-item-hover-border-color: $--color-primary !default;
|
||||
$--dk-task-item-hover-background: $--color-primary !default;
|
||||
@@ -50,7 +50,7 @@ $--dk-add-task-dialog-footer-background: #4a4a4a !default;
|
||||
|
||||
/* Preference
|
||||
-------------------------- */
|
||||
$--dk-preference-form-text-color: #dfdfdf;
|
||||
$--dk-preference-form-text-color: #dfdfdf !default;
|
||||
|
||||
/* Speedometer
|
||||
-------------------------- */
|
||||
@@ -64,3 +64,10 @@ $--dk-speedometer-text-color: #9b9b9b !default;
|
||||
/* Element UI
|
||||
-------------------------- */
|
||||
$--dk-dialog-background: #343434 !default;
|
||||
$--dk-dialog-text-color: #9b9b9b !default;
|
||||
$--dk-table-background: #2a2b2c !default;
|
||||
$--dk-table-striped-background: #1f2122 !default;
|
||||
$--dk-table-hover-background: #565656 !default;
|
||||
$--dk-table-th-background: #1f2122 !default;
|
||||
$--dk-table-text-color: #9b9b9b !default;
|
||||
$--dk-table-border-color: #565656 !default;
|
||||
|
||||
@@ -5,10 +5,34 @@ body {
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Monospaced Number', 'Chinese Quote', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-family: "Monospaced Number", "Chinese Quote", -apple-system,
|
||||
BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB",
|
||||
"Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-variant: tabular-nums;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 8px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:window-inactive {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-resizer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
@@ -28,7 +52,7 @@ img {
|
||||
|
||||
.el-progress--line.is-text {
|
||||
.el-progress-bar__inner::before {
|
||||
content: '';
|
||||
content: "";
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -63,6 +87,9 @@ img {
|
||||
.el-message__content {
|
||||
line-height: 18px;
|
||||
word-break: break-all;
|
||||
a {
|
||||
color: $--link-color;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-title-dialog {
|
||||
@@ -88,6 +115,17 @@ img {
|
||||
color: $--color-info;
|
||||
}
|
||||
|
||||
.el-input-number.el-input-number--mini {
|
||||
&.is-controls-left .el-input__inner {
|
||||
padding-left: 34px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
&.is-controls-right .el-input__inner {
|
||||
padding-left: 5px;
|
||||
padding-right: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
/* App Main
|
||||
-------------------------- */
|
||||
#app,
|
||||
@@ -101,6 +139,10 @@ img {
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.non-draggable {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.aside {
|
||||
background-color: $--aside-background;
|
||||
color: $--aside-text-color;
|
||||
@@ -133,15 +175,16 @@ img {
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 10px;
|
||||
font-size: 14px;
|
||||
font-size: 0;
|
||||
line-height: 20px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
i,
|
||||
span {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
@@ -191,7 +234,7 @@ img {
|
||||
}
|
||||
.panel-content {
|
||||
position: relative;
|
||||
padding: 16px 36px 52px;
|
||||
padding: 16px 36px 64px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import EngineClient from '@/components/Native/EngineClient'
|
||||
import Ipc from '@/components/Native/Ipc'
|
||||
import { mapState } from 'vuex'
|
||||
import { getLangDirection } from '@shared/utils'
|
||||
|
||||
export default {
|
||||
name: 'Motrix',
|
||||
@@ -36,7 +37,8 @@
|
||||
},
|
||||
rpcSecret: state => state.config.rpcSecret,
|
||||
theme: state => state.config.theme,
|
||||
locale: state => state.config.locale
|
||||
locale: state => state.config.locale,
|
||||
dir: state => getLangDirection(state.config.locale)
|
||||
}),
|
||||
themeClass: function () {
|
||||
if (this.theme === 'auto') {
|
||||
@@ -47,13 +49,17 @@
|
||||
},
|
||||
i18nClass: function () {
|
||||
return `i18n-${this.locale}`
|
||||
},
|
||||
dirClass: function () {
|
||||
return `dir-${this.dir}`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isRenderer: is.renderer,
|
||||
updateRootClassName: function () {
|
||||
const { themeClass = '', i18nClass = '' } = this
|
||||
document.documentElement.className = `${themeClass} ${i18nClass}`
|
||||
const { themeClass = '', i18nClass = '', dirClass = '' } = this
|
||||
const className = `${themeClass} ${i18nClass} ${dirClass}`
|
||||
document.documentElement.className = className
|
||||
}
|
||||
},
|
||||
beforeMount: function () {
|
||||
@@ -65,6 +71,9 @@
|
||||
},
|
||||
i18nClass: function (val, oldVal) {
|
||||
this.updateRootClassName()
|
||||
},
|
||||
dirClass: function (val, oldVal) {
|
||||
this.updateRootClassName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,14 @@ const state = {
|
||||
downloadSpeed: 0,
|
||||
uploadSpeed: 0,
|
||||
numActive: 0,
|
||||
numStopped: 0,
|
||||
numWaiting: 0
|
||||
numWaiting: 0,
|
||||
numStopped: 0
|
||||
},
|
||||
addTaskVisible: false,
|
||||
addTaskType: 'uri',
|
||||
addTaskUrl: '',
|
||||
addTaskTorrents: []
|
||||
addTaskTorrents: [],
|
||||
addTaskOptions: {}
|
||||
}
|
||||
|
||||
const getters = {
|
||||
@@ -60,6 +61,11 @@ const mutations = {
|
||||
CHANGE_ADD_TASK_TORRENTS (state, fileList) {
|
||||
state.addTaskTorrents = [...fileList]
|
||||
},
|
||||
UPDATE_ADD_TASK_OPTIONS (state, options) {
|
||||
state.addTaskOptions = {
|
||||
...options
|
||||
}
|
||||
},
|
||||
UPDATE_INTERVAL (state, millisecond) {
|
||||
let interval = millisecond
|
||||
if (millisecond > MAX_INTERVAL) {
|
||||
@@ -162,6 +168,9 @@ const actions = {
|
||||
addTaskAddTorrents ({ commit }, { fileList }) {
|
||||
commit('CHANGE_ADD_TASK_TORRENTS', fileList)
|
||||
},
|
||||
updateAddTaskOptions ({ commit }, options = {}) {
|
||||
commit('UPDATE_ADD_TASK_OPTIONS', options)
|
||||
},
|
||||
updateInterval ({ commit }, millisecond) {
|
||||
commit('UPDATE_INTERVAL', millisecond)
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import api from '@/api'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
const state = {
|
||||
engineMode: 'MAX',
|
||||
@@ -21,13 +22,11 @@ const actions = {
|
||||
})
|
||||
})
|
||||
},
|
||||
save ({ commit }, data) {
|
||||
let { btTracker } = data
|
||||
btTracker = btTracker.trim().replace(/(?:\r\n|\r|\n)/g, ',')
|
||||
const config = {
|
||||
...data,
|
||||
btTracker
|
||||
save ({ commit }, config) {
|
||||
if (isEmpty(config)) {
|
||||
return
|
||||
}
|
||||
|
||||
commit('UPDATE_PREFERENCE_DATA', config)
|
||||
return api.savePreference(config)
|
||||
},
|
||||
|
||||
@@ -57,6 +57,7 @@ const actions = {
|
||||
return api.addUri({ uris, options })
|
||||
.then(() => {
|
||||
dispatch('fetchList')
|
||||
dispatch('app/updateAddTaskOptions', {}, { root: true })
|
||||
})
|
||||
},
|
||||
addTorrent ({ dispatch }, data) {
|
||||
@@ -64,6 +65,7 @@ const actions = {
|
||||
return api.addTorrent({ torrent, options })
|
||||
.then(() => {
|
||||
dispatch('fetchList')
|
||||
dispatch('app/updateAddTaskOptions', {}, { root: true })
|
||||
})
|
||||
},
|
||||
addMetalink ({ dispatch }, data) {
|
||||
@@ -71,8 +73,17 @@ const actions = {
|
||||
return api.addMetalink({ metalink, options })
|
||||
.then(() => {
|
||||
dispatch('fetchList')
|
||||
dispatch('app/updateAddTaskOptions', {}, { root: true })
|
||||
})
|
||||
},
|
||||
getTaskOption (_, gid) {
|
||||
return new Promise((resolve) => {
|
||||
api.getOption({ gid })
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
removeTask ({ dispatch }, task) {
|
||||
const { gid } = task
|
||||
return api.forcePauseTask({ gid })
|
||||
@@ -124,7 +135,10 @@ const actions = {
|
||||
})
|
||||
},
|
||||
removeTaskRecord ({ dispatch }, task) {
|
||||
const { gid } = task
|
||||
const { gid, status } = task
|
||||
if (['error', 'complete', 'removed'].indexOf(status) === -1) {
|
||||
return
|
||||
}
|
||||
return api.removeTaskRecord({ gid })
|
||||
.finally(() => dispatch('fetchList'))
|
||||
},
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
const userKeys = [
|
||||
'locale',
|
||||
'cookie',
|
||||
'resume-all-when-app-launched',
|
||||
'task-notification',
|
||||
'hide-app-menu',
|
||||
'new-task-show-downloading',
|
||||
'use-proxy',
|
||||
'all-proxy-backup',
|
||||
'log-path',
|
||||
'session-path',
|
||||
'enable-egg-features',
|
||||
'theme',
|
||||
'auto-check-update',
|
||||
'last-check-update-time'
|
||||
'cookie',
|
||||
'enable-egg-features',
|
||||
'hide-app-menu',
|
||||
'keep-window-state',
|
||||
'last-check-update-time',
|
||||
'locale',
|
||||
'log-path',
|
||||
'new-task-show-downloading',
|
||||
'open-at-login',
|
||||
'resume-all-when-app-launched',
|
||||
'session-path',
|
||||
'task-notification',
|
||||
'theme',
|
||||
'use-proxy'
|
||||
]
|
||||
|
||||
const systemKeys = [
|
||||
'max-concurrent-downloads',
|
||||
'all-proxy',
|
||||
'all-proxy-passwd',
|
||||
'all-proxy-user',
|
||||
'all-proxy',
|
||||
'allow-overwrite',
|
||||
'allow-piece-length-change',
|
||||
'always-resume',
|
||||
@@ -42,10 +43,10 @@ const systemKeys = [
|
||||
'bt-save-metadata',
|
||||
'bt-seed-unverified',
|
||||
'bt-stop-timeout',
|
||||
'bt-tracker',
|
||||
'bt-tracker-connect-timeout',
|
||||
'bt-tracker-interval',
|
||||
'bt-tracker-timeout',
|
||||
'bt-tracker',
|
||||
'check-integrity',
|
||||
'checksum',
|
||||
'conditional-get',
|
||||
@@ -64,9 +65,9 @@ const systemKeys = [
|
||||
'force-save',
|
||||
'ftp-passwd',
|
||||
'ftp-pasv',
|
||||
'ftp-proxy',
|
||||
'ftp-proxy-passwd',
|
||||
'ftp-proxy-user',
|
||||
'ftp-proxy',
|
||||
'ftp-reuse-connection',
|
||||
'ftp-type',
|
||||
'ftp-user',
|
||||
@@ -77,15 +78,16 @@ const systemKeys = [
|
||||
'http-auth-challenge',
|
||||
'http-no-cache',
|
||||
'http-passwd',
|
||||
'http-proxy',
|
||||
'http-proxy-passwd',
|
||||
'http-proxy-user',
|
||||
'http-proxy',
|
||||
'http-user',
|
||||
'https-proxy',
|
||||
'https-proxy-passwd',
|
||||
'https-proxy-user',
|
||||
'https-proxy',
|
||||
'index-out',
|
||||
'lowest-speed-limit',
|
||||
'max-concurrent-downloads',
|
||||
'max-connection-per-server',
|
||||
'max-download-limit',
|
||||
'max-file-not-found',
|
||||
@@ -93,6 +95,8 @@ const systemKeys = [
|
||||
'max-resume-failure-tries',
|
||||
'max-tries',
|
||||
'max-upload-limit',
|
||||
'max-overall-download-limit',
|
||||
'max-overall-upload-limit',
|
||||
'metalink-base-uri',
|
||||
'metalink-enable-unique-protocol',
|
||||
'metalink-language',
|
||||
@@ -106,8 +110,8 @@ const systemKeys = [
|
||||
'no-proxy',
|
||||
'out',
|
||||
'parameterized-uri',
|
||||
'pause',
|
||||
'pause-metadata',
|
||||
'pause',
|
||||
'piece-length',
|
||||
'proxy-method',
|
||||
'realtime-chunk-checksum',
|
||||
|
||||
@@ -3,3 +3,9 @@ export const DARK_THEME = 'dark'
|
||||
|
||||
export const BEST_TRACKERS_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt'
|
||||
export const BEST_TRACKERS_IP_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best_ip.txt'
|
||||
|
||||
// One Week
|
||||
export const AUTO_CHECK_UPDATE_INTERVAL = 7 * 24 * 60 * 60 * 1000
|
||||
|
||||
export const NONE_SELECTED_FILES = 'none'
|
||||
export const SELECTED_ALL_FILES = 'all'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': 'Engine Version',
|
||||
'license': 'Lizenz',
|
||||
'about': 'Über',
|
||||
'release': 'Versionen',
|
||||
'support': 'Unterstützung anfordern'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'advanced': 'Erweitert',
|
||||
'lab': 'Experimentell',
|
||||
'save': 'Speichern & übernehmen',
|
||||
'save-success-message': 'Einstellungen erfolgreich speichern',
|
||||
'save-fail-message': 'Speichern der Einstellungen fehlgeschlagen',
|
||||
'discard': 'Verwerfen',
|
||||
'startup': 'Startup',
|
||||
'open-at-login': 'Beim Login öffnen',
|
||||
'keep-window-state': 'Stellen Sie die Größe und Position des Fensters wieder her',
|
||||
'auto-resume-all': 'Alle nicht abgeschlossenen Aufgaben automatisch fortsetzen',
|
||||
'default-dir': 'Standard Speicherort',
|
||||
'default-dir': 'Standardpfad',
|
||||
'mas-default-dir-tips': 'Aufgrund der Einschränkungen durch Sandbox-Berechtigungen im App Store wird der Download Ordner als Standard empfohlen',
|
||||
'transfer-settings': 'Übertragung',
|
||||
'transfer-speed-upload': 'Upload-Limit',
|
||||
'transfer-speed-download': 'Download-Limit',
|
||||
'transfer-speed-unlimited': 'Unbegrenzt',
|
||||
'task-manage': 'Aufgaben verwalten',
|
||||
'max-concurrent-downloads': 'Maximal aktive Aufgaben',
|
||||
'max-connection-per-server': 'Maximale Verbindungen pro Server',
|
||||
|
||||
@@ -9,8 +9,14 @@ export default {
|
||||
'torrent-task': 'Torrent',
|
||||
'uri-task-tips': 'Eine Download URL pro Zeile (magnet wird unterstützt)',
|
||||
'thunder-link-tips': 'Tipp: Thunder Links werden möglicherweise nach dem dekodieren nicht heruntergeladen',
|
||||
'new-task-uris-required': 'Bitte geben Sie mindestens eine gültige URL ein',
|
||||
'new-task-torrent-required': 'Bitte wählen Sie eine Torrent-Datei',
|
||||
'file-name': 'Dateiname',
|
||||
'file-extension': 'Dateityp',
|
||||
'file-size': 'Dateigröße',
|
||||
'selected-files-sum': 'Ausgewählt: {{selectedFilesCount}} Dateien, insgesamt {{selectedFilesTotalSize}}',
|
||||
'task-name': 'Aufgaben Name',
|
||||
'task-out': 'Umbenennen',
|
||||
'task-out': 'Dateiname',
|
||||
'task-out-tips': 'Optional, unterstützt nur Einzelaufgaben',
|
||||
'task-split': 'Splits',
|
||||
'task-dir': 'Ordner',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export default {
|
||||
'engine-version': 'Engine Version',
|
||||
'license': 'License',
|
||||
'about': 'About',
|
||||
'release': 'Release',
|
||||
'release': 'Releases',
|
||||
'support': 'Support'
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
'preferences': 'Preferences...',
|
||||
'check-for-updates': 'Check for Updates...',
|
||||
'check-for-updates-title': 'Check for Updates',
|
||||
'update-available-message': 'A new version of Motrix is available, update now?',
|
||||
'update-available-message': 'A newer version of Motrix is available, update now?',
|
||||
'update-not-available-message': 'You are up-to-date!',
|
||||
'update-downloaded-message': 'Ready to install...',
|
||||
'update-error-message': 'Update Error',
|
||||
|
||||
@@ -3,18 +3,26 @@ export default {
|
||||
'advanced': 'Advanced',
|
||||
'lab': 'Lab',
|
||||
'save': 'Save & Apply',
|
||||
'save-success-message': 'Save preferences successfully',
|
||||
'save-fail-message': 'Save preferences failed',
|
||||
'discard': 'Discard',
|
||||
'startup': 'Startup',
|
||||
'auto-resume-all': 'Auto resume all unfinished tasks',
|
||||
'default-dir': 'Default Dir',
|
||||
'mas-default-dir-tips': 'Due to the sandbox permissions restrictions of the App Store, the default download dir is recommended to be set to Downloads directory',
|
||||
'task-manage': 'Task Manage',
|
||||
'open-at-login': 'Open at login',
|
||||
'keep-window-state': 'Keep size and position of the window when exit',
|
||||
'auto-resume-all': 'Automatically resume all unfinished tasks',
|
||||
'default-dir': 'Default Path',
|
||||
'mas-default-dir-tips': 'Due to sandbox permission restrictions of the App Store, the default download directory is recommended to be set to ~/Downloads',
|
||||
'transfer-settings': 'Transmission',
|
||||
'transfer-speed-upload': 'Upload limit',
|
||||
'transfer-speed-download': 'Download limit',
|
||||
'transfer-speed-unlimited': 'Unlimited',
|
||||
'task-manage': 'Task Management',
|
||||
'max-concurrent-downloads': 'Maximum active tasks',
|
||||
'max-connection-per-server': 'Max connection per server',
|
||||
'new-task-show-downloading': 'Auto show downloading after add task',
|
||||
'max-connection-per-server': 'Maximum connection per server',
|
||||
'new-task-show-downloading': 'Automatically show downloading after adding task',
|
||||
'continue': 'Continue',
|
||||
'task-completed-notify': 'Notification after download is complete',
|
||||
'auto-purge-record': 'Auto purge download record when app exit',
|
||||
'auto-purge-record': 'Automatically purge download records when exiting app',
|
||||
'ui': 'UI',
|
||||
'appearance': 'Appearance',
|
||||
'theme-auto': 'Auto',
|
||||
@@ -26,22 +34,25 @@ export default {
|
||||
'proxy': 'Proxy',
|
||||
'use-proxy': 'Enable Proxy',
|
||||
'bt-tracker': 'Tracker Servers',
|
||||
'bt-tracker-input-tips': 'Tracker server, one per line',
|
||||
'bt-tracker-tips': 'Recommend: ',
|
||||
'bt-tracker-input-tips': 'Tracker servers, one per line',
|
||||
'bt-tracker-tips': 'Recommended: ',
|
||||
'sync-tracker-tips': 'Sync from ngosang/trackerslist',
|
||||
'security': 'Security',
|
||||
'rpc-secret': 'RPC Secret',
|
||||
'rpc-secret-tips': 'RPC Secret Manual',
|
||||
'developer': 'Developer',
|
||||
'mock-user-agent': 'Mock User-Agent',
|
||||
'app-log-path': 'App log path',
|
||||
'download-session-path': 'Download session path',
|
||||
'factory-reset': 'Factory Reset',
|
||||
'factory-reset-confirm': 'Are you sure you want to revert to factory settings?',
|
||||
'lab-warning': '⚠️ Enabling lab features may cause app crashes or data loss, decide for yourself!',
|
||||
'download-protocol': 'Protocol',
|
||||
'support-more-download-protocols': 'Enable more download protocols support',
|
||||
'lab-warning': '⚠️ Enabling lab features may result in app crash or data loss, decide at you own risk!',
|
||||
'download-protocol': 'Protocols',
|
||||
'support-more-download-protocols': 'Enable support for more download protocols',
|
||||
'browser-extensions': 'Extensions',
|
||||
'baidu-exporter': 'BaiduExporter',
|
||||
'browser-extensions-tips': 'Provided by the community, ',
|
||||
'baidu-exporter-help': 'Click here for usage',
|
||||
'auto-check-update': 'Auto check update',
|
||||
'last-check-update-time': 'Last Check Update Time'
|
||||
'auto-check-update': 'Automatically check for update',
|
||||
'last-check-update-time': 'Last Time Checking for Update'
|
||||
}
|
||||
|
||||
@@ -7,13 +7,19 @@ export default {
|
||||
'open-file': 'Open Torrent File...',
|
||||
'uri-task': 'URL',
|
||||
'torrent-task': 'Torrent',
|
||||
'uri-task-tips': 'One task url per line (support magnet)',
|
||||
'thunder-link-tips': 'Tip: Thunder links may not be downloaded after decoding',
|
||||
'uri-task-tips': 'One task url per line (supports magnet)',
|
||||
'thunder-link-tips': 'Tip: Thunder links may not be downloadable after decoding',
|
||||
'new-task-uris-required': 'Please enter at least one valid resource url',
|
||||
'new-task-torrent-required': 'Please select a torrent file',
|
||||
'file-name': 'File Name',
|
||||
'file-extension': 'File Type',
|
||||
'file-size': 'File Size',
|
||||
'selected-files-sum': 'Selected: {{selectedFilesCount}} files, total size {{selectedFilesTotalSize}}',
|
||||
'task-name': 'Task Name',
|
||||
'task-out': 'Rename',
|
||||
'task-out-tips': 'Optional, support single task',
|
||||
'task-out-tips': 'Optional (only supports single task)',
|
||||
'task-split': 'Splits',
|
||||
'task-dir': 'Dir',
|
||||
'task-dir': 'Save to',
|
||||
'pause-task': 'Pause Task',
|
||||
'task-ua': 'UA',
|
||||
'task-user-agent': 'User-Agent',
|
||||
@@ -22,61 +28,61 @@ export default {
|
||||
'navigate-to-downloading': 'Navigate to Downloading',
|
||||
'show-advanced-options': 'Advanced Options',
|
||||
'copyright-warning': 'Copyright Warning',
|
||||
'copyright-warning-message': 'The file you want to download may be copyrighted audio or video, please make sure you have the copyright license.',
|
||||
'copyright-yes': 'Yes, I have',
|
||||
'copyright-no': 'No',
|
||||
'copyright-error-message': 'Adding task failed due to copyright issues',
|
||||
'pause-task-success': 'Pause task "{{taskName}}" success',
|
||||
'pause-task-fail': 'Pause task "{{taskName}}" fail',
|
||||
'copyright-warning-message': 'The file you want to download may be copyrighted audio or video, please ensure that you have permission to access to it.',
|
||||
'copyright-yes': 'Yes, I have permission',
|
||||
'copyright-no': 'No, I don\'t have permission',
|
||||
'copyright-error-message': 'Failed to add task due to copyright issue',
|
||||
'pause-task-success': 'Successfully paused task "{{taskName}}"',
|
||||
'pause-task-fail': 'Failed to pause task "{{taskName}}"',
|
||||
'resume-task': 'Resume Task',
|
||||
'resume-task-success': 'Resume task "{{taskName}}" success',
|
||||
'resume-task-fail': 'Resume task "{{taskName}}" fail',
|
||||
'resume-task-success': 'Successfully resumed task "{{taskName}}"',
|
||||
'resume-task-fail': 'Failed to resume task "{{taskName}}"',
|
||||
'delete-task': 'Delete Task',
|
||||
'delete-selected-tasks': 'Delete Selected Tasks',
|
||||
'delete-task-confirm': 'Are you sure to remove the "{{taskName}}" download task?',
|
||||
'delete-task-confirm': 'Are you sure you want to remove download task "{{taskName}}"?',
|
||||
'delete-task-label': 'Delete with Files',
|
||||
'delete-task-success': 'Delete task "{{taskName}}" success',
|
||||
'delete-task-fail': 'Delete task "{{taskName}}" fail',
|
||||
'remove-task-file-fail': 'Failed to delete task file, please delete it manually',
|
||||
'delete-task-success': 'Successfully deleted task "{{taskName}}"',
|
||||
'delete-task-fail': 'Failed to delete task "{{taskName}}"',
|
||||
'remove-task-file-fail': 'Failed to delete task file(s), please delete them manually',
|
||||
'remove-task-config-file-fail': 'Failed to delete task config file, please delete it manually',
|
||||
'move-task-up': 'Move Task Up',
|
||||
'move-task-down': 'Move Task Down',
|
||||
'pause-all-task': 'Pause All Task',
|
||||
'pause-all-task-success': 'Pause all task success',
|
||||
'pause-all-task-fail': 'Pause all task fail',
|
||||
'resume-all-task': 'Resume All Task',
|
||||
'resume-all-task-success': 'Resume all task success',
|
||||
'resume-all-task-fail': 'Resume all task fail',
|
||||
'pause-all-task': 'Pause All Tasks',
|
||||
'pause-all-task-success': 'Successfully paused all tasks',
|
||||
'pause-all-task-fail': 'Failed to pause all tasks',
|
||||
'resume-all-task': 'Resume All Tasks',
|
||||
'resume-all-task-success': 'Successfully resumed all tasks',
|
||||
'resume-all-task-fail': 'Failed to resume all tasks',
|
||||
'clear-recent-tasks': 'Clear Recent Tasks',
|
||||
'purge-record': 'Purge Task Record',
|
||||
'purge-record-success': 'Purge task record success',
|
||||
'purge-record-fail': 'Purge task record fail',
|
||||
'purge-record-success': 'Successfully purged task records',
|
||||
'purge-record-fail': 'Failed to purge task records',
|
||||
'refresh-list': 'Refresh Task List',
|
||||
'no-task': 'There are no current downloads',
|
||||
'no-task': 'There are no current tasks',
|
||||
'copy-link': 'Copy Link',
|
||||
'copy-link-success': 'Copy link success',
|
||||
'copy-link-success': 'Successfully copied link',
|
||||
'remove-record': 'Remove Task Record',
|
||||
'remove-record-confirm': 'Are you sure to remove the "{{taskName}}" download record?',
|
||||
'remove-record-confirm': 'Are you sure you want to remove download record for "{{taskName}}"?',
|
||||
'remove-record-label': 'Delete with Files',
|
||||
'remove-record-success': 'Remove task "{{taskName}}" record success',
|
||||
'remove-record-fail': 'Remove task "{{taskName}}" record fail',
|
||||
'remove-record-success': 'Successfully removed task record for "{{taskName}}"',
|
||||
'remove-record-fail': 'Failed to remove task record for "{{taskName}}"',
|
||||
'show-in-folder': 'Show Task In Folder',
|
||||
'file-not-exist': 'File does not exist or has been deleted',
|
||||
'file-not-exist': 'Target file does not exist or has been deleted',
|
||||
'file-path-error': 'File path error',
|
||||
'opening-task-message': 'Opening "{{taskName}}" ...',
|
||||
'get-task-name': 'Get the task name...',
|
||||
'get-task-name': 'Getting task name...',
|
||||
'remaining-prefix': 'Remaining',
|
||||
'select-torrent': 'Drag the torrent here, or click to select',
|
||||
'task-info-dialog-title': '{{title}} Detail',
|
||||
'download-start-message': 'Start download {{taskName}}',
|
||||
'download-pause-message': 'Pause download {{taskName}}',
|
||||
'download-stop-message': '{{taskName}} download stopped',
|
||||
'download-error-message': '{{taskName}} download error occurred',
|
||||
'download-complete-message': '{{taskName}} download completed',
|
||||
'select-torrent': 'Drag torrent file here, or click to select',
|
||||
'task-info-dialog-title': '{{title}} Details',
|
||||
'download-start-message': 'Started downloading {{taskName}}',
|
||||
'download-pause-message': 'Paused downloading {{taskName}}',
|
||||
'download-stop-message': 'Stopped downloading {{taskName}}',
|
||||
'download-error-message': 'Error occurred when downloading {{taskName}}',
|
||||
'download-complete-message': 'Completed downloading {{taskName}}',
|
||||
'download-complete-notify': 'Download Completed',
|
||||
'bt-download-complete-message': '{{taskName}} download completed, seeding',
|
||||
'bt-download-complete-notify': 'BT Download Completed, Seeding...',
|
||||
'bt-download-complete-tips': 'Tips: You can stop the task to end the seeding',
|
||||
'download-fail-message': '{{taskName}} download failed',
|
||||
'bt-download-complete-message': 'Completed downloading {{taskName}}, seeding',
|
||||
'bt-download-complete-notify': 'BT Download Completed, seeding...',
|
||||
'bt-download-complete-tips': 'Tips: You can stop a task to end its seeding',
|
||||
'download-fail-message': 'Failed to download {{taskName}}',
|
||||
'download-fail-notify': 'Download Failed'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': 'نسخهی موتور',
|
||||
'license': 'مجوز منبع باز',
|
||||
'about': 'درباره ما',
|
||||
'release': 'نسخههای منتشر شده',
|
||||
'support': 'حمایت'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'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': 'بخاطر محدودیتهای دسترسی سندباکس از اپ استور، پیشنهاد میشود فولدر دانلود پیشفرض ست شده باشد.',
|
||||
'transfer-settings': 'انتقال',
|
||||
'transfer-speed-upload': 'محدودیت آپلود',
|
||||
'transfer-speed-download': 'محدودیت دانلود',
|
||||
'transfer-speed-unlimited': 'نامحدود',
|
||||
'task-manage': 'مدیریت تسک',
|
||||
'max-concurrent-downloads': 'حداکثر تسکهای فعال',
|
||||
'max-connection-per-server': 'حداکثر اتصال برای هر سرور',
|
||||
|
||||
@@ -9,6 +9,12 @@ export default {
|
||||
'torrent-task': 'تورنت',
|
||||
'uri-task-tips': 'یک آدرس دانلود در هر خط',
|
||||
'thunder-link-tips': 'نکته: لینکهای تاندر ممکن هست بعد دیکد کردن دانلود نشوند',
|
||||
'new-task-uris-required': 'لطفا حداقل یک آدرس دانلود معتبر وارد کنید',
|
||||
'new-task-torrent-required': 'لطفا فایل تورنت را انتخاب کنید',
|
||||
'file-name': 'نام فایل',
|
||||
'file-extension': 'امتداد',
|
||||
'file-size': 'اندازه',
|
||||
'selected-files-sum': 'انتخاب شده: {{selectedFilesCount}} فایلها، مجموع {{selectedFilesTotalSize}}',
|
||||
'task-name': 'اسم تسک',
|
||||
'task-out': 'تغییرنام',
|
||||
'task-out-tips': 'اختیاری، حمایت تک تسک',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': 'Version du moteur',
|
||||
'license': 'Licence',
|
||||
'about': 'À Propos',
|
||||
'release': 'Release',
|
||||
'support': 'Support'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'advanced': 'Avancé',
|
||||
'lab': 'Labo',
|
||||
'save': 'Sauver et appliquer',
|
||||
'save-success-message': 'Enregistrer les préférences avec succès',
|
||||
'save-fail-message': 'La sauvegarde des préférences a échoué',
|
||||
'discard': 'Annuler les changement',
|
||||
'startup': 'Démarrage',
|
||||
'open-at-login': 'Ouvrir à la connexion',
|
||||
'keep-window-state': 'Restaurez la taille et la position de la fenêtre',
|
||||
'auto-resume-all': 'Reprendre les tâches non terminées',
|
||||
'default-dir': 'Répertoire par défaut',
|
||||
'mas-default-dir-tips': 'En raison des restrictions d\'autorisations du bac à sable de l\'App Store, il est recommandé de définir le répertoire de téléchargement par défaut sur le répertoire Téléchargements.',
|
||||
'transfer-settings': 'Transmission',
|
||||
'transfer-speed-upload': 'Limite de téléchargement',
|
||||
'transfer-speed-download': 'Limite de téléchargement',
|
||||
'transfer-speed-unlimited': 'Unlimited',
|
||||
'task-manage': 'Tâches',
|
||||
'max-concurrent-downloads': 'Nombre de tâches active au maximum',
|
||||
'max-connection-per-server': 'Nombre maximum de connexions par serveurs',
|
||||
|
||||
@@ -9,6 +9,12 @@ export default {
|
||||
'torrent-task': 'Torrent',
|
||||
'uri-task-tips': 'Un lien par ligne (supporte les magnets)',
|
||||
'thunder-link-tips': 'Astuce: Les liens Thunder ne doivent pas être téléchargés après décodage',
|
||||
'new-task-uris-required': 'Veuillez entrer au moins une URL de ressource valide',
|
||||
'new-task-torrent-required': 'Veuillez sélectionner un fichier torrent',
|
||||
'file-name': 'Nom de fichier',
|
||||
'file-extension': 'Ext',
|
||||
'file-size': 'Taille',
|
||||
'selected-files-sum': 'Sélectionné: {{selectedFilesCount}} fichiers, total {{selectedFilesTotalSize}}',
|
||||
'task-name': 'Nom de la tâche',
|
||||
'task-out': 'Renommer',
|
||||
'task-out-tips': 'Optionel, supporte une seule tâche',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': 'バージョンを確認',
|
||||
'license': 'ライセンス',
|
||||
'about': '私たちについて',
|
||||
'release': 'リリースノート',
|
||||
'support': 'サポート'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'advanced': '詳細設定',
|
||||
'lab': '実験室',
|
||||
'save': '保存して適用',
|
||||
'save-success-message': '設定を保存します',
|
||||
'save-fail-message': '設定を保存できませんでした',
|
||||
'discard': '放棄',
|
||||
'startup': '起動',
|
||||
'open-at-login': '自動的に起動します',
|
||||
'auto-resume-all': '起動後自動的に未完了タスクを再開',
|
||||
'keep-window-state': 'ウィンドウのサイズと位置を元に戻します',
|
||||
'default-dir': '既定の保存先',
|
||||
'mas-default-dir-tips': 'App Store の Sandbox 制限のため,デフォルトディレクトリ設定は「Download」を推奨します。',
|
||||
'transfer-settings': '転送設定',
|
||||
'transfer-speed-upload': 'アップロード制限',
|
||||
'transfer-speed-download': 'ダウンロード制限',
|
||||
'transfer-speed-unlimited': '無制限',
|
||||
'task-manage': 'タスク管理',
|
||||
'max-concurrent-downloads': '最大同時タスク数',
|
||||
'max-connection-per-server': '最大サーバ接続数',
|
||||
|
||||
@@ -9,6 +9,12 @@ export default {
|
||||
'torrent-task': 'torrentタスク',
|
||||
'uri-task-tips': 'URLを複数追加したとき、一行につき一つのリンクとなります(マグネットリンクをサポート)',
|
||||
'thunder-link-tips': '注意:Thunder(Xunlei)リンク解析後、ダウンロードできるかは保証できません',
|
||||
'new-task-uris-required': '少なくとも1つの有効なリソースURLを入力してください',
|
||||
'new-task-torrent-required': 'トレントファイルを選択してください',
|
||||
'file-name': 'ファイル名',
|
||||
'file-extension': '拡張子',
|
||||
'file-size': 'サイズ',
|
||||
'selected-files-sum': '選択済み:{{selectedFilesCount}}ファイル、合計{{selectedFilesTotalSize}}',
|
||||
'task-name': 'タスク名',
|
||||
'task-out': '名前を変更',
|
||||
'task-out-tips': 'オプション(単独タスクのみサポート)',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': '엔진 버전',
|
||||
'license': '라이선스',
|
||||
'about': '정보',
|
||||
'release': '배포',
|
||||
'support': '지원'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'advanced': '고급',
|
||||
'lab': '실험실',
|
||||
'save': '저장 & 적용',
|
||||
'save-success-message': '환경 설정을 성공적으로 저장 합니다',
|
||||
'save-fail-message': '환경 설정 저장 실패',
|
||||
'discard': '포기',
|
||||
'startup': '시작',
|
||||
'open-at-login': '로그인 시 엽니다',
|
||||
'auto-resume-all': '완료되지 않은 모든 작업 자동 재개',
|
||||
'keep-window-state': '창의 크기와 위치를 복원합니다',
|
||||
'default-dir': '기본 폴더',
|
||||
'mas-default-dir-tips': '앱스토어의 샌드박스 사용 권한 제한으로 인해 기본 다운로드 폴더를 다운로드 폴더로 설정하는 것이 권장됩니다.',
|
||||
'transfer-settings': '전송 설정',
|
||||
'transfer-speed-upload': '업로드 제한',
|
||||
'transfer-speed-download': '다운로드 제한',
|
||||
'transfer-speed-unlimited': '무제한',
|
||||
'task-manage': '작업 관리',
|
||||
'max-concurrent-downloads': '최대 활성 작업',
|
||||
'max-connection-per-server': '서버당 최대 연결 수',
|
||||
@@ -26,9 +34,9 @@ export default {
|
||||
'proxy': '프록시',
|
||||
'use-proxy': '프록시 허용',
|
||||
'bt-tracker': '트래커 서버',
|
||||
'bt-tracker-input-tips': '추적 서버, 한 줄에 하나씩',
|
||||
'bt-tracker-input-tips': '트래커 서버, 한 줄에 하나씩',
|
||||
'bt-tracker-tips': '추천 :',
|
||||
'sync-tracker-tips': 'ngosang/trackerslist 에서 데이터 동기화',
|
||||
'sync-tracker-tips': 'ngosang/trackerslist에서 동기화',
|
||||
'developer': '개발자',
|
||||
'mock-user-agent': '사용자 에이전트 흉내',
|
||||
'app-log-path': '앱 로그 경로',
|
||||
|
||||
@@ -9,6 +9,12 @@ export default {
|
||||
'torrent-task': '토렌트',
|
||||
'uri-task-tips': '한 줄의 하나의 작업 URL (마그넷 지원)',
|
||||
'thunder-link-tips': '팁 : 디코딩 후 썬더 링크가 다운로드 되지 않을 수 있습니다.',
|
||||
'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': '선택적, 단일 작업 지원',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': 'Versão da Engine',
|
||||
'license': 'Licença',
|
||||
'about': 'Sobre',
|
||||
'release': 'Release',
|
||||
'support': 'Suporte'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'advanced': 'Avançado',
|
||||
'lab': 'Lab',
|
||||
'save': 'Salvar & Aplicar',
|
||||
'save-success-message': 'Salvar preferências com sucesso',
|
||||
'save-fail-message': 'Salvar preferências falhadas',
|
||||
'discard': 'Descartar',
|
||||
'startup': 'Inicialização',
|
||||
'open-at-login': 'Abra no login',
|
||||
'keep-window-state': 'Restaurar tamanho e posição da janela',
|
||||
'auto-resume-all': 'Auto resumir todas as tarefas não finalizadas',
|
||||
'default-dir': 'Diretório Padrão',
|
||||
'mas-default-dir-tips': 'Devido às restrições de permissões do sandbox da App Store, recomenda-se que o diretório de download padrão seja definido como o diretório de downloads',
|
||||
'transfer-settings': 'Transmissão',
|
||||
'transfer-speed-upload': 'limite de envio',
|
||||
'transfer-speed-download': 'limite de transferência',
|
||||
'transfer-speed-unlimited': 'Ilimitado',
|
||||
'task-manage': 'Gerenciador de Tarefas',
|
||||
'max-concurrent-downloads': 'Máximo de tarefas ativas',
|
||||
'max-connection-per-server': 'Máximo de coneções por servidor',
|
||||
|
||||
@@ -9,6 +9,12 @@ export default {
|
||||
'torrent-task': 'Torrent',
|
||||
'uri-task-tips': 'Uma url por linha (magnet links são aceitos)',
|
||||
'thunder-link-tips': 'Dica: Thunder links não podem ser baixados após decodificados',
|
||||
'new-task-uris-required': 'Por favor, insira pelo menos um URL de recurso válido',
|
||||
'new-task-torrent-required': 'Por favor, selecione um arquivo torrent',
|
||||
'file-name': 'Nome do arquivo',
|
||||
'file-extension': 'Ext',
|
||||
'file-size': 'Tamanho',
|
||||
'selected-files-sum': 'Selecionado: {{selectedFilesCount}} arquivos, total {{selectedFilesTotalSize}}',
|
||||
'task-name': 'Nome da Tarefa',
|
||||
'task-out': 'Renomear',
|
||||
'task-out-tips': 'Opcional, suporte únicc tarefa',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': 'Engine Versiyonu',
|
||||
'license': 'Lisans',
|
||||
'about': 'Hakkında',
|
||||
'release': 'Sürüm',
|
||||
'support': 'Destek'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'advanced': 'Gelişmiş',
|
||||
'lab': 'Deneysel',
|
||||
'save': 'Kaydet & Uygula',
|
||||
'save-success-message': 'Tercihleri başarıyla kaydedin',
|
||||
'save-fail-message': 'Tercihleri kaydetme başarısız oldu',
|
||||
'discard': 'İptal Et',
|
||||
'startup': 'Başlangıçta',
|
||||
'open-at-login': 'Giriş sırasında aç',
|
||||
'keep-window-state': 'Pencerenin boyutunu ve konumunu geri yükleyin',
|
||||
'auto-resume-all': 'Tüm bitmemiş görevleri otomatik olarak devam ettir',
|
||||
'default-dir': 'Varsayılan Klasör',
|
||||
'mas-default-dir-tips': 'App Store\'un sanal alan izinleri kısıtlamaları nedeniyle, varsayılan indirme dizininin İndirilenler dizinine ayarlanması önerilir.',
|
||||
'transfer-settings': 'İletim',
|
||||
'transfer-speed-upload': 'Yükleme limiti',
|
||||
'transfer-speed-download': 'İndirme limiti',
|
||||
'transfer-speed-unlimited': 'Sınırsız',
|
||||
'task-manage': 'Görev Yöneticisi',
|
||||
'max-concurrent-downloads': 'Maksimum aktif görev',
|
||||
'max-connection-per-server': 'Sunucu başına maksimum bağlantı',
|
||||
|
||||
@@ -9,8 +9,14 @@ export default {
|
||||
'torrent-task': 'Torrent',
|
||||
'uri-task-tips': 'Her bir satır için bir görev (magnet destekli)',
|
||||
'thunder-link-tips': 'İpucu: Thunder bağlantıları kod çözme işleminden sonra indirilemeyebilir',
|
||||
'new-task-uris-required': 'Lütfen en az bir geçerli kaynak URL adresi girin',
|
||||
'new-task-torrent-required': 'Lütfen bir torrent dosyası seçin',
|
||||
'file-name': 'Dosya Adı',
|
||||
'file-extension': 'uzantı',
|
||||
'file-size': 'Boyut',
|
||||
'selected-files-sum': 'Seçildi: {{selectedFilesCount}} dosya sayısı, total {{selectedFilesTotalSize}}',
|
||||
'task-name': 'Görev Adı',
|
||||
'task-out': 'Yeniden Adlandır',
|
||||
'task-out': 'Dosya Adı',
|
||||
'task-out-tips': 'Opsiyonel, tek görev destekle',
|
||||
'task-split': 'Parça',
|
||||
'task-dir': 'Yol',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': '引擎版本',
|
||||
'license': '开源许可',
|
||||
'about': '关于我们',
|
||||
'release': '更新日志',
|
||||
'support': '帮助支持'
|
||||
|
||||
@@ -3,11 +3,19 @@ export default {
|
||||
'advanced': '进阶设置',
|
||||
'lab': '实验室',
|
||||
'save': '保存并应用',
|
||||
'save-success-message': '偏好设置保存成功',
|
||||
'save-fail-message': '偏好设置保存失败',
|
||||
'discard': '放弃',
|
||||
'startup': '启动',
|
||||
'auto-resume-all': '启动后自动开始未完成任务',
|
||||
'open-at-login': '开机自动启动',
|
||||
'keep-window-state': '恢复上次退出时窗口的大小和位置',
|
||||
'auto-resume-all': '自动开始未完成的任务',
|
||||
'default-dir': '默认下载路径',
|
||||
'mas-default-dir-tips': '因 App Store 的沙箱权限限制,默认下载路径建议设置为您的「下载」目录',
|
||||
'transfer-settings': '传输设置',
|
||||
'transfer-speed-upload': '上传限速',
|
||||
'transfer-speed-download': '下载限速',
|
||||
'transfer-speed-unlimited': '不限速',
|
||||
'task-manage': '任务管理',
|
||||
'max-concurrent-downloads': '同时下载的最大任务数',
|
||||
'max-connection-per-server': '每个服务器最大连接数',
|
||||
|
||||
@@ -9,6 +9,12 @@ export default {
|
||||
'torrent-task': '种子任务',
|
||||
'uri-task-tips': '添加多个下载链接时,请确保每行只有一个链接(支持磁力链)',
|
||||
'thunder-link-tips': '友情提示:迅雷链接解码之后的资源不一定存在',
|
||||
'new-task-uris-required': '请至少输入一个有效的下载地址',
|
||||
'new-task-torrent-required': '请先选择种子文件',
|
||||
'file-name': '文件名',
|
||||
'file-extension': '类型',
|
||||
'file-size': '大小',
|
||||
'selected-files-sum': '已选:{{selectedFilesCount}}个文件,共 {{selectedFilesTotalSize}}',
|
||||
'task-name': '任务名',
|
||||
'task-out': '重命名',
|
||||
'task-out-tips': '选填(仅支持单任务)',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
'engine-version': '引擎版本',
|
||||
'license': '開源許可',
|
||||
'about': '關於',
|
||||
'release': '版本資訊',
|
||||
'support': '支援'
|
||||
|
||||
@@ -3,15 +3,23 @@ export default {
|
||||
'advanced': '進階設定',
|
||||
'lab': '實驗性功能',
|
||||
'save': '儲存並套用',
|
||||
'discard': '拋棄',
|
||||
'save-success-message': '偏好設定保存成功',
|
||||
'save-fail-message': '偏好設定保存失敗',
|
||||
'discard': '捨棄',
|
||||
'startup': '啟動',
|
||||
'auto-resume-all': '啟動後自動繼續未完成任務',
|
||||
'open-at-login': '開機自動啟動',
|
||||
'keep-window-state': '恢復上次退出時視窗的大小和位置',
|
||||
'auto-resume-all': '自動繼續未完成的任務',
|
||||
'default-dir': '預設下載路徑',
|
||||
'mas-default-dir-tips': '因 App Store 的沙盒權限限制,建議將預設下載路徑設定為您的「下載」資料夾',
|
||||
'transfer-settings': '傳輸設置',
|
||||
'transfer-speed-upload': '上傳限制',
|
||||
'transfer-speed-download': '下載限制',
|
||||
'transfer-speed-unlimited': '無限制',
|
||||
'task-manage': '任務管理',
|
||||
'max-concurrent-downloads': '最多可同時下載的任務數',
|
||||
'max-connection-per-server': '每台伺服器的最大連線數',
|
||||
'new-task-show-downloading': '新增任務後自動顯示下載頁',
|
||||
'new-task-show-downloading': '新增任務後自動顯示下載頁面',
|
||||
'continue': '中斷續傳',
|
||||
'task-completed-notify': '下載完成後通知',
|
||||
'auto-purge-record': '當結束程式時自動清除下載紀錄',
|
||||
@@ -32,16 +40,16 @@ export default {
|
||||
'developer': '開發者',
|
||||
'mock-user-agent': '模擬使用者代理(UA)',
|
||||
'app-log-path': '應用程式記錄檔位置',
|
||||
'download-session-path': '下載會談路徑',
|
||||
'factory-reset': '回復出廠預設值',
|
||||
'factory-reset-confirm': '您確定要回復成出廠預設值嗎?',
|
||||
'lab-warning': '⚠️開啟實驗性功能可能會造成程式崩潰或資料移失,請自己決定!',
|
||||
'download-session-path': '下載工作階段路徑',
|
||||
'factory-reset': '還原出廠預設值',
|
||||
'factory-reset-confirm': '您確定要還原為出廠預設值嗎?',
|
||||
'lab-warning': '⚠️開啟實驗性功能可能會造成程式當機或資料遺失,請自己決定!',
|
||||
'download-protocol': '下載協定',
|
||||
'support-more-download-protocols': '支援更多下載協定',
|
||||
'browser-extensions': '瀏覽器擴充功能',
|
||||
'baidu-exporter': '百度网盘助手',
|
||||
'baidu-exporter': '百度網盤助手',
|
||||
'browser-extensions-tips': '社群提供的瀏覽器擴充功能「不保證可用性」,',
|
||||
'baidu-exporter-help': '点此查看使用说明',
|
||||
'baidu-exporter-help': '點此檢視使用說明',
|
||||
'auto-check-update': '自動檢查更新',
|
||||
'last-check-update-time': '上次檢查更新時間'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default {
|
||||
'task-list': '任務列表',
|
||||
'task-list': '任務清單',
|
||||
'preferences': '偏好設定'
|
||||
}
|
||||
|
||||
@@ -4,11 +4,17 @@ export default {
|
||||
'stopped': '已停止',
|
||||
'new-task': '新增任務',
|
||||
'new-bt-task': '新增 BT 任務',
|
||||
'open-file': '打開種子文件...',
|
||||
'open-file': '開啟種子檔案...',
|
||||
'uri-task': '連結任務',
|
||||
'torrent-task': '種子任務',
|
||||
'uri-task-tips': '新增多個下載連結時,請確保每行只有一個連結(支援磁力連結)',
|
||||
'thunder-link-tips': '友情提示:迅雷連結解碼之後的資源不一定存在',
|
||||
'new-task-uris-required': '請至少輸入一個有效的資源網址',
|
||||
'new-task-torrent-required': '請先選擇種子文件',
|
||||
'file-name': '文件名',
|
||||
'file-extension': '擴展名',
|
||||
'file-size': '大小',
|
||||
'selected-files-sum': '選中:{{selectedFilesCount}}個文件,總計 {{selectedFilesTotalSize}}',
|
||||
'task-name': '任務名稱',
|
||||
'task-out': '重新命名',
|
||||
'task-out-tips': '選填(僅支援單一任務)',
|
||||
@@ -18,13 +24,13 @@ export default {
|
||||
'task-user-agent': 'User-Agent',
|
||||
'task-referer': 'Referer',
|
||||
'task-cookie': 'Cookie',
|
||||
'navigate-to-downloading': '跳轉到下載頁面',
|
||||
'navigate-to-downloading': '前往下載頁面',
|
||||
'show-advanced-options': '進階選項',
|
||||
'copyright-warning': '版權警告',
|
||||
'copyright-warning-message': '您要下載的檔案可能是有版權的音訊視訊,請確保您有相應的版權方授權。',
|
||||
'copyright-yes': '是,我有版權方授權',
|
||||
'copyright-no': '否',
|
||||
'copyright-error-message': '因版權问题,新增任務失敗',
|
||||
'copyright-error-message': '因版權問題,新增任務失敗',
|
||||
'pause-task': '暫停任務',
|
||||
'pause-task-success': '暫停任務 "{{taskName}}" 成功',
|
||||
'pause-task-fail': '暫停任務 "{{taskName}}" 失敗',
|
||||
@@ -34,11 +40,11 @@ export default {
|
||||
'delete-task': '移除任務',
|
||||
'delete-selected-tasks': '移除選取的任務',
|
||||
'delete-task-confirm': '你確定要移除 "{{taskName}}" 下載任務嗎?',
|
||||
'delete-task-label': '同時删除檔案',
|
||||
'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': '暫停所有任務',
|
||||
@@ -57,16 +63,16 @@ export default {
|
||||
'copy-link-success': '複製連結成功',
|
||||
'remove-record': '移除下載紀錄',
|
||||
'remove-record-confirm': '你確定要移除 "{{taskName}}" 下載紀錄吗?',
|
||||
'remove-record-label': '同时删除檔案',
|
||||
'remove-record-label': '同时刪除檔案',
|
||||
'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': '抓取任務名稱中...',
|
||||
'get-task-name': '取得任務名稱中...',
|
||||
'remaining-prefix': '剩下',
|
||||
'select-torrent': '將種子拖曳到此,或點選來選取',
|
||||
'select-torrent': '將種子拖曳至此,或點選來選取',
|
||||
'task-info-dialog-title': '{{title}} 詳細資訊',
|
||||
'download-start-message': '開始下載 {{taskName}}',
|
||||
'download-pause-message': '暫停下載 {{taskName}}',
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import {
|
||||
isEmpty,
|
||||
isNaN,
|
||||
camelCase,
|
||||
compact,
|
||||
difference,
|
||||
parseInt,
|
||||
isArray,
|
||||
isEmpty,
|
||||
isFunction,
|
||||
camelCase,
|
||||
kebabCase
|
||||
isNaN,
|
||||
kebabCase,
|
||||
omitBy,
|
||||
parseInt,
|
||||
pick
|
||||
} from 'lodash'
|
||||
import { resolve } from 'path'
|
||||
import { userKeys, systemKeys } from './configKeys'
|
||||
@@ -176,6 +179,11 @@ export function isMagnetTask (task) {
|
||||
return bittorrent && !bittorrent.info
|
||||
}
|
||||
|
||||
export function checkTaskIsSeeder (task) {
|
||||
const { bittorrent, seeder } = task
|
||||
return !!bittorrent && seeder
|
||||
}
|
||||
|
||||
export function getTaskUri (task, btTracker = []) {
|
||||
const { files } = task
|
||||
let result = ''
|
||||
@@ -301,6 +309,7 @@ export function separateConfig (options) {
|
||||
const system = {}
|
||||
// others
|
||||
const others = {}
|
||||
|
||||
for (const [k, v] of Object.entries(options)) {
|
||||
if (userKeys.indexOf(k) !== -1) {
|
||||
user[k] = v
|
||||
@@ -327,15 +336,77 @@ export function splitTextRows (text = '') {
|
||||
return result
|
||||
}
|
||||
|
||||
export function convertToTextRows (text = '') {
|
||||
export function convertCommaToLine (text = '') {
|
||||
let arr = text.split(',')
|
||||
arr = arr.map((row) => row.trim())
|
||||
const result = arr.join('\n')
|
||||
return result
|
||||
}
|
||||
|
||||
const audioSuffix = ['.aac', '.mp3', '.ogg', '.ape', '.flac', '.m4a', '.wav', '.wma', '.flav']
|
||||
const videoSuffix = ['.avi', '.mkv', '.rmvb', '.wmv', '.mp4', '.m4a', '.vob', '.mov', '.mpg']
|
||||
export function convertLineToComma (text = '') {
|
||||
const result = text.trim().replace(/(?:\r\n|\r|\n)/g, ',')
|
||||
return result
|
||||
}
|
||||
|
||||
export const imageSuffix = [
|
||||
'.ai',
|
||||
'.bmp',
|
||||
'.eps',
|
||||
'.gif',
|
||||
'.icn',
|
||||
'.ico',
|
||||
'.jpeg',
|
||||
'.jpg',
|
||||
'.png',
|
||||
'.psd',
|
||||
'.raw',
|
||||
'.sketch',
|
||||
'.svg',
|
||||
'.tif',
|
||||
'.webp',
|
||||
'.xd'
|
||||
]
|
||||
export const audioSuffix = [
|
||||
'.aac',
|
||||
'.ape',
|
||||
'.flac',
|
||||
'.flav',
|
||||
'.m4a',
|
||||
'.mp3',
|
||||
'.ogg',
|
||||
'.wav',
|
||||
'.wma'
|
||||
]
|
||||
export const videoSuffix = [
|
||||
'.avi',
|
||||
'.m4a',
|
||||
'.mkv',
|
||||
'.mov',
|
||||
'.mp4',
|
||||
'.mpg',
|
||||
'.rmvb',
|
||||
'.vob',
|
||||
'.wmv'
|
||||
]
|
||||
|
||||
export function filterVideoFiles (files = []) {
|
||||
return files.filter((item) => {
|
||||
return videoSuffix.includes(item.extension)
|
||||
})
|
||||
}
|
||||
|
||||
export function filterAudioFiles (files = []) {
|
||||
return files.filter((item) => {
|
||||
return audioSuffix.includes(item.extension)
|
||||
})
|
||||
}
|
||||
|
||||
export function filterImageFiles (files = []) {
|
||||
return files.filter((item) => {
|
||||
return imageSuffix.includes(item.extension)
|
||||
})
|
||||
}
|
||||
|
||||
export function isAudioOrVideo (uri = '') {
|
||||
const suffixs = [...audioSuffix, ...videoSuffix]
|
||||
const result = suffixs.some((suffix) => {
|
||||
@@ -394,3 +465,88 @@ export function buildFileList (rawFile) {
|
||||
const fileList = [file]
|
||||
return fileList
|
||||
}
|
||||
|
||||
const supportRtlLocales = [
|
||||
/* 'العربية', Arabic */
|
||||
'ar',
|
||||
/* 'فارسی', Persian */
|
||||
'fa',
|
||||
/* 'עברית', Hebrew */
|
||||
'he',
|
||||
/* 'Kurdî / كوردی', Kurdish */
|
||||
'ku',
|
||||
/* 'پنجابی', Western Punjabi */
|
||||
'pa',
|
||||
/* 'پښتو', Pashto, */
|
||||
'ps',
|
||||
/* 'سنڌي', Sindhi */
|
||||
'sd',
|
||||
/* 'اردو', Urdu */
|
||||
'ur',
|
||||
/* 'ייִדיש', Yiddish */
|
||||
'yi'
|
||||
]
|
||||
export function isRTL (locale = 'en-US') {
|
||||
return supportRtlLocales.includes(locale)
|
||||
}
|
||||
|
||||
export function getLangDirection (locale = 'en-US') {
|
||||
return isRTL(locale) ? 'rtl' : 'ltr'
|
||||
}
|
||||
|
||||
export function listTorrentFiles (files) {
|
||||
const result = files.map((file, index) => {
|
||||
const extension = getFileExtension(file.path)
|
||||
const item = {
|
||||
// aria2 select-file start index at 1
|
||||
// possible Values: 1-1048576
|
||||
idx: index + 1,
|
||||
extension: `.${extension}`,
|
||||
...file
|
||||
}
|
||||
return item
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export function getFileExtension (filename) {
|
||||
return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2)
|
||||
}
|
||||
|
||||
export function removeExtensionDot (extension = '') {
|
||||
return extension.replace('.', '')
|
||||
}
|
||||
|
||||
export function diffConfig (current = {}, next = {}) {
|
||||
const curr = pick(current, Object.keys(next))
|
||||
const result = omitBy(next, (val, key) => {
|
||||
if (isArray(val)) {
|
||||
return false
|
||||
}
|
||||
return curr[key] === val
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export function calcFormLabelWidth (locale) {
|
||||
return locale.startsWith('de') ? '28%' : '23%'
|
||||
}
|
||||
|
||||
export function parseHeader (header = '') {
|
||||
header = header.trim()
|
||||
let result = {}
|
||||
if (!header) {
|
||||
return result
|
||||
}
|
||||
|
||||
const headers = splitTextRows(header)
|
||||
headers.forEach((line) => {
|
||||
const index = line.indexOf(':')
|
||||
const name = line.substr(0, index)
|
||||
const value = line.substr(index + 1).trim()
|
||||
result[name] = value
|
||||
})
|
||||
result = changeKeysToCamelCase(result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||