From 933413eaee06b3fe3713328da8a603cf5fe92e7c Mon Sep 17 00:00:00 2001 From: emo64 Date: Tue, 9 Mar 2021 23:58:44 +0800 Subject: [PATCH] Add settings Add more chinese localization Add an option to display speed in page title --- src/Api.ts | 12 +- src/App.vue | 14 +- src/components/Drawer.vue | 22 +-- src/components/Footer.vue | 4 +- src/components/Torrents.vue | 12 +- src/components/dialogs/TorrentInfo.vue | 19 +- .../settingsDialog/DownloadSettings.vue | 166 +++++++++++++--- .../dialogs/settingsDialog/PreferenceRow.vue | 32 ++++ .../dialogs/settingsDialog/SettingsDialog.vue | 75 ++++++-- .../dialogs/settingsDialog/SpeedSettings.vue | 157 +++++++++++++++ .../dialogs/settingsDialog/WebUISettings.vue | 179 ++++++++++++++++++ src/locale/en.ts | 85 ++++++++- src/locale/zh-CN.ts | 86 ++++++++- src/store/config.ts | 2 + src/store/index.ts | 7 +- src/types.ts | 3 + 16 files changed, 796 insertions(+), 79 deletions(-) create mode 100644 src/components/dialogs/settingsDialog/PreferenceRow.vue create mode 100644 src/components/dialogs/settingsDialog/SpeedSettings.vue create mode 100644 src/components/dialogs/settingsDialog/WebUISettings.vue diff --git a/src/Api.ts b/src/Api.ts index 911cbd4..05b86cc 100644 --- a/src/Api.ts +++ b/src/Api.ts @@ -1,6 +1,14 @@ /* eslint-disable @typescript-eslint/camelcase */ import Axios, { AxiosInstance, AxiosPromise, AxiosResponse } from 'axios'; -import { RssNode, RssRule, SearchPlugin, ApiCategory, SearchTaskResponse, Preferences } from '@/types'; +import { + RssNode, + RssRule, + SearchPlugin, + ApiCategory, + SearchTaskResponse, + Preferences, + MainData, +} from '@/types' const apiEndpoint = 'api/v2'; @@ -62,7 +70,7 @@ class Api { return this.axios.post('/app/shutdown'); } - public getMainData(rid?: number) { + public getMainData(rid?: number): AxiosPromise { const params = { rid, }; diff --git a/src/App.vue b/src/App.vue index a8cc8c7..047e952 100644 --- a/src/App.vue +++ b/src/App.vue @@ -82,6 +82,7 @@ import { Watch } from 'vue-property-decorator'; import { MainData } from './types'; import { Config } from './store/config'; import Api from './Api'; +import {formatSize} from '@/filters' let appWrapEl: HTMLElement; @@ -205,7 +206,18 @@ export default class App extends Vue { const mainData = resp.data; this.updateMainData(mainData); - + if(this.config.displaySpeedInTitle) { + const upInfoSpeed = mainData.server_state.up_info_speed + const dlInfoSpeed = mainData.server_state.dl_info_speed + let dl = '', up = '' + if (dlInfoSpeed > 1024) { + dl = `D ${formatSize(dlInfoSpeed)}/s` + } + if (upInfoSpeed > 1024) { + up = `U ${formatSize(upInfoSpeed)}/s` + } + document.title = `[${up} ${dl}] qBittorrent Web UI` + } this.task = setTimeout(this.getMainData, this.config.updateInterval); } diff --git a/src/components/Drawer.vue b/src/components/Drawer.vue index b922389..fb17657 100644 --- a/src/components/Drawer.vue +++ b/src/components/Drawer.vue @@ -68,50 +68,50 @@ import { tr } from '@/locale'; import { Torrent, Category } from '@/types'; import FilterGroup from './drawer/FilterGroup.vue'; import api from '../Api'; -import { formatSize } from '../filters'; -import { StateType } from '../consts'; +import { formatSize } from '@/filters'; +import { StateType } from '@/consts'; import SiteMap from '@/sites' import Component from 'vue-class-component'; import { Prop, Emit } from 'vue-property-decorator'; const stateList = [ { - title: tr('state.downloading'), + title: tr('category_state.downloading'), state: StateType.Downloading, icon: 'download', }, { - title: tr('state.seeding'), + title: tr('category_state.seeding'), state: StateType.Seeding, icon: 'upload', }, { - title: tr('state.completed'), + title: tr('category_state.completed'), state: StateType.Completed, icon: 'check', }, { - title: tr('state.resumed'), + title: tr('category_state.resumed'), state: StateType.Resumed, icon: 'play', }, { - title: tr('state.paused'), + title: tr('category_state.paused'), state: StateType.Paused, icon: 'pause', }, { - title: tr('state.active'), + title: tr('category_state.active'), state: StateType.Active, icon: 'filter', }, { - title: tr('state.inactive'), + title: tr('category_state.inactive'), state: StateType.Inactive, icon: 'filter-outline', }, { - title: tr('state.errored'), + title: tr('category_state.errored'), state: StateType.Errored, icon: 'alert', }, @@ -249,7 +249,7 @@ export default class Drawer extends Vue { filterGroups.push({ icon: 'mdi-menu-up', 'icon-alt': 'mdi-menu-down', - title: tr('state._'), + title: tr('category_state._'), model: null, select: 'state', children: [ diff --git a/src/components/Footer.vue b/src/components/Footer.vue index 8d38c88..5088c37 100644 --- a/src/components/Footer.vue +++ b/src/components/Footer.vue @@ -189,7 +189,7 @@ import api from '../Api'; import buildInfo from '@/buildInfo'; import Component from 'vue-class-component'; import { Prop, Watch } from 'vue-property-decorator'; -import { Torrent, ServerState } from '../types'; +import { Torrent, ServerState } from '@/types'; @Component({ @@ -314,7 +314,7 @@ export default class Footer extends Vue { align-items: center; .v-icon { - margin-right: 4px; + //margin-right: 4px; } } diff --git a/src/components/Torrents.vue b/src/components/Torrents.vue index cedf3dc..65724ad 100644 --- a/src/components/Torrents.vue +++ b/src/components/Torrents.vue @@ -106,7 +106,7 @@ icon @click="setTorrentLocation" :title="$t('title.set_location')" - :disabled="selectedRows.length == 0" + :disabled="selectedRows.length === 0" > mdi-folder-marker @@ -128,7 +128,7 @@ icon @click="recheckTorrents" :title="$t('recheck')" - :disabled="selectedRows.length == 0" + :disabled="selectedRows.length === 0" > mdi-backup-restore @@ -188,7 +188,7 @@ - {{ row.item.state }} + {{ $t('torrent_state.' + row.item.state) }} {{ row.item.num_seeds }}/{{ row.item.num_complete }} {{ row.item.num_leechs }}/{{ row.item.num_incomplete }} {{ row.item.dlspeed | formatNetworkSpeed }} @@ -237,10 +237,10 @@ import ConfirmSetCategoryDialog from './dialogs/ConfirmSetCategoryDialog.vue' import EditTrackerDialog from './dialogs/EditTrackerDialog.vue' import InfoDialog from './dialogs/InfoDialog.vue' import api from '../Api' -import { formatSize } from '../filters' -import { DialogType, TorrentFilter, ConfigPayload, DialogConfig, SnackBarConfig } from '../store/types' +import { formatSize } from '@/filters' +import { DialogType, TorrentFilter, ConfigPayload, DialogConfig, SnackBarConfig } from '@/store/types' import Component from 'vue-class-component' -import { Torrent, Category } from '../types' +import { Torrent, Category } from '@/types' import { Watch } from 'vue-property-decorator' function getStateInfo(state: string) { diff --git a/src/components/dialogs/TorrentInfo.vue b/src/components/dialogs/TorrentInfo.vue index 2f676f1..f5806ec 100644 --- a/src/components/dialogs/TorrentInfo.vue +++ b/src/components/dialogs/TorrentInfo.vue @@ -76,17 +76,15 @@ @@ -52,5 +157,14 @@ export default class DownloadSettings extends Vue { diff --git a/src/components/dialogs/settingsDialog/PreferenceRow.vue b/src/components/dialogs/settingsDialog/PreferenceRow.vue new file mode 100644 index 0000000..31b1c6c --- /dev/null +++ b/src/components/dialogs/settingsDialog/PreferenceRow.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/dialogs/settingsDialog/SettingsDialog.vue b/src/components/dialogs/settingsDialog/SettingsDialog.vue index 77656dc..25f57e2 100644 --- a/src/components/dialogs/settingsDialog/SettingsDialog.vue +++ b/src/components/dialogs/settingsDialog/SettingsDialog.vue @@ -19,39 +19,84 @@ - - - + + + {{ $t('preferences.' + item) }} + + + + + {{ $t('preferences.change_applied') }} + + + + + + + + + + + + + - diff --git a/src/components/dialogs/settingsDialog/SpeedSettings.vue b/src/components/dialogs/settingsDialog/SpeedSettings.vue new file mode 100644 index 0000000..95231b2 --- /dev/null +++ b/src/components/dialogs/settingsDialog/SpeedSettings.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/src/components/dialogs/settingsDialog/WebUISettings.vue b/src/components/dialogs/settingsDialog/WebUISettings.vue new file mode 100644 index 0000000..002edeb --- /dev/null +++ b/src/components/dialogs/settingsDialog/WebUISettings.vue @@ -0,0 +1,179 @@ + + + diff --git a/src/locale/en.ts b/src/locale/en.ts index 637d8b1..33f411e 100644 --- a/src/locale/en.ts +++ b/src/locale/en.ts @@ -67,6 +67,67 @@ export default { plugin_manager: 'Plugin manager', update_plugins: 'Update plugins', + preferences: { + change_applied: 'New preferences saved', + downloads: 'Downloads', + adding_torrent: 'When adding a torrent', + create_subfolder_enabled: 'Create subfolder for torrents with multiple files', + start_paused_enabled: 'Do not start the download automatically', + auto_delete_mode: 'Delete .torrent files afterwards', + preallocate_all: 'Pre-allocate disk space for all files', + incomplete_files_ext: 'Append .!qB extension to incomplete files', + saving_management: 'Saving Management', + auto_tmm_enabled: 'Default Torrent Management Mode', + torrent_changed_tmm_enabled: 'When Torrent Category changed', + save_path_changed_tmm_enabled: 'When Default Save Path changed', + category_changed_tmm_enabled: 'When Category Save Path changed', + auto_mode: 'Automatic', + manual_mode: 'Manual', + switch_torrent_mode_to_manual: 'Switch affected torrent to manual mode', + move_affected_torrent: 'Relocate affected torrents', + save_path: 'Default Save Path', + temp_path: 'Keep incomplete torrents in', + export_dir: 'Copy .torrent files to', + export_dir_fin: 'Copy .torrent files for finished downloads to', + + speed: 'Speed', + global_rate_limits: 'Global Rate Limits', + alternate_rate_limits: 'Alternative Rate Limits', + alternate_schedule_enable_time: 'Schedule the use of alternative rate limits', + apply_speed_limit: 'Rate Limits Settings', + dl_limit: 'Download (KiB/s)', + up_limit: 'Upload (KiB/s)', + zero_for_unlimited: '0 means unlimited', + schedule_from: 'From', + schedule_to: 'To', + scheduler_days: 'When', + limit_utp_rate: 'Apply rate limit to µTP protocol', + limit_tcp_overhead: 'Apply rate limit to transport overhead', + limit_lan_peers: 'Apply rate limit to peers on LAN', + + connection: 'Connections', + bittorrent: 'BitTorrent', + + webui: 'Web UI', + data_update_interval: 'Data Update Interval (ms)', + webui_remote_control: 'Web User Interface (Remote control)', + ip_address: 'IP address', + ip_port: 'Port', + enable_upnp: 'Use UPnP / NAT-PMP to forward the port from my router', + authentication: 'Authentication', + web_ui_username: 'Username', + web_ui_password: 'Password', + bypass_local_auth: 'Bypass authentication for clients on localhost', + bypass_auth_subnet_whitelist: 'Bypass authentication for clients on localhost', + web_ui_session_timeout: 'Session timeout', + web_ui_max_auth_fail_count: 'Ban client after consecutive failures', + web_ui_ban_duration: 'ban for', + web_ui_seconds: 'seconds', + new_password: 'Change current password...', + + display_speed_in_title: 'Display download speed in page title', + }, + title: { _: 'Title', add_torrents: 'Add Torrents', @@ -157,7 +218,7 @@ export default { }, }, - state: { + category_state: { _: 'State', downloading: 'Downloading', @@ -169,4 +230,26 @@ export default { inactive: 'Inactive', errored: 'Errored', }, + + torrent_state: { + error: 'error', + missingFiles: 'missingFiles', + uploading: 'uploading', + pausedUP: 'pausedUP', + queuedUP: 'queuedUP', + stalledUP: 'stalledUP', + checkingUP: 'checkingUP', + forcedUP: 'forcedUP', + allocating: 'allocating', + downloading: 'downloading', + metaDL: 'metaDL', + pausedDL: 'pausedDL', + queuedDL: 'queuedDL', + stalledDL: 'stalledDL', + checkingDL: 'checkingDL', + forceDL: 'forceDL', + checkingResumeData: 'checkingResumeData', + moving: 'moving', + unknown: 'unknown', + }, } diff --git a/src/locale/zh-CN.ts b/src/locale/zh-CN.ts index 11e46a3..581accb 100644 --- a/src/locale/zh-CN.ts +++ b/src/locale/zh-CN.ts @@ -45,6 +45,7 @@ export default { added_on: '添加时间', settings: '设置', + logs: '日志', light: '亮色', dark: '暗色', @@ -64,6 +65,67 @@ export default { action: '操作', search_engine: '搜索引擎', + preferences: { + change_applied: '配置已保存', + downloads: '下载', + adding_torrent: '添加 torrent 时', + create_subfolder_enabled: '为多个文件的 Torrent 创建子目录', + start_paused_enabled: '不要自动开始下载', + auto_delete_mode: '完成后删除 .torrent 文件', + preallocate_all: '为所有文件预分配磁盘空间', + incomplete_files_ext: '为不完整的文件添加扩展名 .!qB', + saving_management: '保存管理', + auto_tmm_enabled: '默认 Torrent 管理模式', + torrent_changed_tmm_enabled: '当 Torrent 分类修改时', + save_path_changed_tmm_enabled: '当默认保存路径修改时', + category_changed_tmm_enabled: '当分类保存路径修改时', + auto_mode: '自动', + manual_mode: '手动', + switch_torrent_mode_to_manual: '切换受影响的 Torrent 至手动模式', + move_affected_torrent: '重新定位受影响的 Torrent', + save_path: '默认保存路径', + temp_path: '保存未完成的 torrent 到', + export_dir: '复制 .torrent 文件到', + export_dir_fin: '复制下载完成的 .torrent 文件到', + + speed: '速度', + global_rate_limits: '全局速度限制', + alternate_rate_limits: '备用速度限制', + alternate_schedule_enable_time: '设置备用速度限制的启用时间', + apply_speed_limit: '设置速度限制', + dl_limit: '下载 (KiB/s)', + up_limit: '上传 (KiB/s)', + zero_for_unlimited: '0 为无限制', + schedule_from: '从', + schedule_to: '到', + scheduler_days: '时间', + limit_utp_rate: '对 µTP 协议进行速度限制', + limit_tcp_overhead: '对传送总开销进行速度限制', + limit_lan_peers: '对本地网络用户进行速度限制', + + connection: '连接', + bittorrent: 'BitTorrent', + + webui: 'Web UI', + data_update_interval: '数据更新频率(ms)', + webui_remote_control: 'Web 用户界面(远程控制)', + ip_address: 'IP 地址', + ip_port: '端口', + enable_upnp: '使用我的路由器的 UPnP / NAT-PMP 功能来转发端口', + authentication: '验证', + web_ui_username: '用户名', + web_ui_password: '密码', + bypass_local_auth: '对本地主机上的客户端跳过身份验证', + bypass_auth_subnet_whitelist: '对 IP 子网白名单中的客户端跳过身份验证', + web_ui_session_timeout: '会话超时', + web_ui_ban_duration: '禁止', + web_ui_max_auth_fail_count: '连续失败后禁止客户端次数', + web_ui_seconds: '秒', + new_password: '更改当前的密码...', + + display_speed_in_title: '在网页标题显示当前速度', + }, + title: { _: '标题', add_torrents: '添加种子', @@ -153,7 +215,7 @@ export default { }, }, - state: { + category_state: { _: '状态', downloading: '下载', @@ -165,4 +227,26 @@ export default { inactive: '空闲', errored: '错误', }, + + torrent_state: { + error: '错误', + missingFiles: '文件丢失', + uploading: '上传中', + pausedUP: '完成', + queuedUP: '排队上传', + stalledUP: '上传', + checkingUP: '上传校验', + forcedUP: '强制上传', + allocating: '分配空间', + downloading: '下载中', + metaDL: '获取信息', + pausedDL: '暂停下载', + queuedDL: '排队下载', + stalledDL: '下载', + checkingDL: '下载校验', + forceDL: '强制下载', + checkingResumeData: '快速校验', + moving: '移动中', + unknown: '未知', + }, } diff --git a/src/store/config.ts b/src/store/config.ts index d4be7b7..dc6a45e 100644 --- a/src/store/config.ts +++ b/src/store/config.ts @@ -17,6 +17,7 @@ export interface Config { }; locale: string | null; darkMode: string | null; + displaySpeedInTitle: boolean | null; } const defaultConfig = { @@ -33,6 +34,7 @@ const defaultConfig = { }, locale: null, darkMode: null, + displaySpeedInTitle: false, }; function saveConfig(obj: any) { diff --git a/src/store/index.ts b/src/store/index.ts index 97d41f2..bdd0d4b 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -131,9 +131,10 @@ const store = new Vuex.Store({ actions: { async updatePreferencesRequest({ dispatch }, preferences) { try { - const response = await api.setPreferences(preferences); - - dispatch("updatePreferencesRequestSuccess", response.data); + await api.setPreferences(preferences); + //setPreference api return a empty response. Need to update preference by another request. + const preferenceRes = await api.getAppPreferences(); + dispatch("updatePreferencesRequestSuccess", preferenceRes.data); } catch { dispatch("updatePreferencesRequestFailure"); } diff --git a/src/types.ts b/src/types.ts index c3a71ee..47739d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -284,6 +284,9 @@ export interface Preferences { web_ui_port: number; web_ui_upnp: boolean; web_ui_username: string; + web_ui_max_auth_fail_count: number; + web_ui_ban_duration: number; + web_ui_session_timeout: number; } export interface SearchPlugin {