mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-03-14 12:10:18 +03:00
perf: full Typescript migration + restructure (#612) @Larsluph
This commit is contained in:
parent
6cabef1b56
commit
e09e8a0300
105 changed files with 2091 additions and 805 deletions
|
@ -19,6 +19,6 @@
|
|||
<strong>We're sorry but Vuetorrent doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
44
package-lock.json
generated
44
package-lock.json
generated
|
@ -19,6 +19,7 @@
|
|||
"vue": "^2.7.14",
|
||||
"vue-apexcharts": "^1.6.2",
|
||||
"vue-i18n": "^8",
|
||||
"vue-property-decorator": "^9.1.2",
|
||||
"vue-router": "^3.6.5",
|
||||
"vue-toastification": "^1.7.11",
|
||||
"vuedraggable": "^2.24.3",
|
||||
|
@ -30,6 +31,8 @@
|
|||
"@faker-js/faker": "^7.6.0",
|
||||
"@mdi/js": "^7",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5",
|
||||
"@typescript-eslint/parser": "^5",
|
||||
"@vitejs/plugin-vue2": "^2",
|
||||
|
@ -46,8 +49,9 @@
|
|||
"vite": "^3",
|
||||
"vite-plugin-eslint": "^1",
|
||||
"vite-plugin-pwa": "^0.13",
|
||||
"vitest": "^0.25",
|
||||
"vue-template-compiler": "^2"
|
||||
"vitest": "^0.25.8",
|
||||
"vue-template-compiler": "^2",
|
||||
"vue-typed-mixins": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
|
@ -2260,6 +2264,12 @@
|
|||
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.191",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
|
||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
|
@ -2293,6 +2303,12 @@
|
|||
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.48.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.0.tgz",
|
||||
|
@ -7244,6 +7260,15 @@
|
|||
"apexcharts": "^3.26.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-class-component": {
|
||||
"version": "7.2.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz",
|
||||
"integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"vue": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-eslint-parser": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz",
|
||||
|
@ -7295,6 +7320,15 @@
|
|||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.28.2.tgz",
|
||||
"integrity": "sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA=="
|
||||
},
|
||||
"node_modules/vue-property-decorator": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz",
|
||||
"integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==",
|
||||
"peerDependencies": {
|
||||
"vue": "*",
|
||||
"vue-class-component": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz",
|
||||
|
@ -7318,6 +7352,12 @@
|
|||
"vue": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-typed-mixins": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-typed-mixins/-/vue-typed-mixins-0.2.0.tgz",
|
||||
"integrity": "sha512-0OxuinandPWv3nm5k/reYkuKtX3jjPZ40Sy9roJz0ih8PUzmI7zSRiXFEJ62LsyRegw9Tqy+qMkajk7ipKP8Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vuedraggable": {
|
||||
"version": "2.24.3",
|
||||
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"vue": "^2.7.14",
|
||||
"vue-apexcharts": "^1.6.2",
|
||||
"vue-i18n": "^8",
|
||||
"vue-property-decorator": "^9.1.2",
|
||||
"vue-router": "^3.6.5",
|
||||
"vue-toastification": "^1.7.11",
|
||||
"vuedraggable": "^2.24.3",
|
||||
|
@ -33,6 +34,8 @@
|
|||
"@faker-js/faker": "^7.6.0",
|
||||
"@mdi/js": "^7",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5",
|
||||
"@typescript-eslint/parser": "^5",
|
||||
"@vitejs/plugin-vue2": "^2",
|
||||
|
@ -49,8 +52,9 @@
|
|||
"vite": "^3",
|
||||
"vite-plugin-eslint": "^1",
|
||||
"vite-plugin-pwa": "^0.13",
|
||||
"vitest": "^0.25",
|
||||
"vue-template-compiler": "^2"
|
||||
"vitest": "^0.25.8",
|
||||
"vue-template-compiler": "^2",
|
||||
"vue-typed-mixins": "^0.2.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import { formatBytes } from '@/helpers'
|
||||
import store from '../store'
|
||||
import store from '@/store'
|
||||
|
||||
export class DocumentTitle {
|
||||
static setDefault() {
|
||||
private static setDefault() {
|
||||
this.set('qBittorrent')
|
||||
}
|
||||
|
||||
static setGlobalSpeed() {
|
||||
private static setGlobalSpeed() {
|
||||
const status = store.getters.getStatus()
|
||||
this.set(`[D: ${formatBytes(status.dlspeed)}/s, U: ${formatBytes(status.upspeed)}/s] VueTorrent`)
|
||||
}
|
||||
|
||||
static setFirstTorrentStatus() {
|
||||
private static setFirstTorrentStatus() {
|
||||
const torrents = store.getters.getTorrents()
|
||||
if (!torrents && !torrents.length) return
|
||||
const torrent = torrents[0]
|
||||
this.set(`[D: ${formatBytes(torrent.dlspeed)}/s, U: ${formatBytes(torrent.upspeed)}/s] ${torrent.progress}%`)
|
||||
}
|
||||
|
||||
static update() {
|
||||
public static update() {
|
||||
const mode = store.getters.getWebuiSettings().title
|
||||
switch (mode) {
|
||||
case 'Default':
|
||||
|
@ -32,7 +32,7 @@ export class DocumentTitle {
|
|||
}
|
||||
}
|
||||
|
||||
static set(title) {
|
||||
private static set(title: string) {
|
||||
document.title = title
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import store from '../store'
|
||||
import store from '@/store'
|
||||
|
||||
export class Graph {
|
||||
static update() {
|
||||
public static shiftValues() {
|
||||
const state = store.state
|
||||
state.download_data.shift()
|
||||
state.download_data.push(state.status.dlspeedRaw || 0)
|
|
@ -1,10 +0,0 @@
|
|||
import store from '../store'
|
||||
import Status from '@/models/Status'
|
||||
|
||||
export class ServerStatus {
|
||||
static update(response) {
|
||||
if (response.server_state) {
|
||||
store.state.status = new Status(response.server_state)
|
||||
}
|
||||
}
|
||||
}
|
12
src/actions/ServerStatus.ts
Normal file
12
src/actions/ServerStatus.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import store from '@/store'
|
||||
import {Status} from '@/models'
|
||||
import type {ServerState} from "@/types/qbit/models";
|
||||
import type {Optional} from "@/global";
|
||||
|
||||
export class ServerStatus {
|
||||
static update(server_state: Optional<ServerState>) {
|
||||
if (server_state) {
|
||||
store.state.status = new Status(server_state)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import store from '../store'
|
||||
import { ArrayHelper } from '@/helpers'
|
||||
|
||||
export class Tags {
|
||||
static update(response) {
|
||||
if (response?.full_update === true) {
|
||||
store.state.tags = response.tags
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (response.tags_removed) {
|
||||
store.state.tags = ArrayHelper.remove(store.state.tags, response.tags_removed)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (response.tags) {
|
||||
store.state.tags = ArrayHelper.concat(store.state.tags, response.tags)
|
||||
}
|
||||
}
|
||||
}
|
23
src/actions/Tags.ts
Normal file
23
src/actions/Tags.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import store from '@/store'
|
||||
import {ArrayHelper} from '@/helpers'
|
||||
import type {MainDataResponse} from "@/types/qbit/responses";
|
||||
|
||||
export class Tags {
|
||||
static update(response: MainDataResponse) {
|
||||
if (response?.fullUpdate === true) {
|
||||
store.state.tags = response.tags || []
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (response.tags_removed) {
|
||||
store.state.tags = ArrayHelper.remove(store.state.tags, ...response.tags_removed)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (response.tags) {
|
||||
store.state.tags = ArrayHelper.concat(store.state.tags, response.tags)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
import store from '../store'
|
||||
import store from '@/store'
|
||||
import { Hostname } from '@/helpers'
|
||||
import Torrent from '@/models/Torrent'
|
||||
import {Torrent as VtTorrent} from '@/models'
|
||||
import type {Torrent as QbitTorrent} from '@/types/qbit/models'
|
||||
import { isProduction } from '@/utils'
|
||||
import { generateMultiple } from '@/utils/faker'
|
||||
|
||||
export class Torrents {
|
||||
static update(data) {
|
||||
static update(data: QbitTorrent[]) {
|
||||
if (store.state.webuiSettings.showTrackerFilter) {
|
||||
// don't calculate trackers when disabled
|
||||
|
||||
|
@ -15,7 +16,7 @@ export class Torrents {
|
|||
}
|
||||
|
||||
// update torrents
|
||||
store.state.torrents = data.map(t => new Torrent(t, store.state.webuiSettings.dateFormat))
|
||||
store.state.torrents = data.map(t => new VtTorrent(t, store.state.webuiSettings.dateFormat))
|
||||
|
||||
// load fake torrents if enabled
|
||||
if (isProduction()) return
|
|
@ -1,8 +1,9 @@
|
|||
import store from '../store'
|
||||
import store from '@/store'
|
||||
import { Hostname } from '@/helpers'
|
||||
import type {Torrent} from "@/types/qbit/models";
|
||||
|
||||
export class Trackers {
|
||||
static update(data) {
|
||||
static update(data: Torrent[]) {
|
||||
if (store.state.webuiSettings.showTrackerFilter) {
|
||||
store.state.trackers = data
|
||||
.map(t => t.tracker)
|
|
@ -70,7 +70,7 @@ export default {
|
|||
this.dialog = false
|
||||
},
|
||||
edit() {
|
||||
qbit.editfeed(this.feed)
|
||||
qbit.editFeed(this.feed)
|
||||
Vue.$toast.success(this.$t('toast.feedSaved'))
|
||||
this.cancel()
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ export default {
|
|||
async addTrackers() {
|
||||
if (!this.newTrackers.length) return (this.trackerDialog = false)
|
||||
|
||||
qbit.addTorrenTrackers(this.hash, this.newTrackers)
|
||||
qbit.addTorrentTrackers(this.hash, this.newTrackers)
|
||||
this.newTrackers = ''
|
||||
await this.getTorrentTrackers()
|
||||
this.trackerDialog = false
|
||||
|
|
67
src/enums/qbit/AppPreferences.ts
Normal file
67
src/enums/qbit/AppPreferences.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
export enum BitTorrentProtocol {
|
||||
TCP_uTP,
|
||||
TCP,
|
||||
uTP
|
||||
}
|
||||
|
||||
export enum DynDnsService {
|
||||
USE_DYNDNS,
|
||||
USE_NOIP
|
||||
}
|
||||
|
||||
export enum Encryption {
|
||||
PREFER_ENCRYPTION,
|
||||
FORCE_ON,
|
||||
FORCE_OFF
|
||||
}
|
||||
|
||||
export enum MaxRatioAction {
|
||||
PAUSE_TORRENT,
|
||||
REMOVE_TORRENT,
|
||||
REMOVE_TORRENT_AND_FILES,
|
||||
ENABLE_SUPERSEEDING
|
||||
}
|
||||
|
||||
export enum ProxyType {
|
||||
DISABLED = -1,
|
||||
HTTP_WITHOUT_AUTH = 1,
|
||||
SOCKS5_WITHOUT_AUTH,
|
||||
HTTP_WITH_AUTH,
|
||||
SOCKS5_WITH_AUTH,
|
||||
SOCKS4_WITHOUT_AUTH
|
||||
}
|
||||
|
||||
enum ScanDirsEnum {
|
||||
MONITORED_FOLDER,
|
||||
DEFAULT_SAVE_PATH
|
||||
}
|
||||
export type ScanDirs = ScanDirsEnum | string
|
||||
|
||||
export enum SchedulerDays {
|
||||
EVERY_DAY,
|
||||
EVERY_WEEKDAY,
|
||||
EVERY_WEEKEND,
|
||||
EVERY_MONDAY,
|
||||
EVERY_TUESDAY,
|
||||
EVERY_WEDNESDAY,
|
||||
EVERY_THURSDAY,
|
||||
EVERY_FRIDAY,
|
||||
EVERY_SATURDAY,
|
||||
EVERY_SUNDAY
|
||||
}
|
||||
|
||||
export enum UploadChokingAlgorithm {
|
||||
ROUND_ROBIN,
|
||||
FASTEST_UPLOAD,
|
||||
ANTI_LEECH
|
||||
}
|
||||
|
||||
export enum UploadSlotsBehavior {
|
||||
FIXED_SLOTS,
|
||||
UPLOAD_RATE_BASED
|
||||
}
|
||||
|
||||
export enum UtpTcpMixedMode {
|
||||
PREFER_TCP,
|
||||
PEER_PROPORTIONAL
|
||||
}
|
5
src/enums/qbit/ConnectionStatus.ts
Normal file
5
src/enums/qbit/ConnectionStatus.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export enum ConnectionStatus {
|
||||
CONNECTED = 'connected',
|
||||
FIREWALLED = 'firewalled',
|
||||
DISCONNECTED = 'disconnected'
|
||||
}
|
6
src/enums/qbit/LogType.ts
Normal file
6
src/enums/qbit/LogType.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export enum LogType {
|
||||
NORMAL = 1,
|
||||
INFO = 2,
|
||||
WARNING = 4,
|
||||
CRITICAL = 8
|
||||
}
|
6
src/enums/qbit/Priority.ts
Normal file
6
src/enums/qbit/Priority.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export enum Priority {
|
||||
DO_NOT_DOWNLOAD = 0,
|
||||
NORMAL = 1,
|
||||
HIGH = 6,
|
||||
MAXIMAL = 7
|
||||
}
|
40
src/enums/qbit/TorrentState.ts
Normal file
40
src/enums/qbit/TorrentState.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
export enum TorrentState {
|
||||
/** Some error occurred, applies to paused torrents */
|
||||
ERROR = 'error',
|
||||
/** Torrent data files is missing */
|
||||
MISSING_FILES = 'missingFiles',
|
||||
/** Torrent is being seeded and data is being transferred */
|
||||
UPLOADING = 'uploading',
|
||||
/** Torrent is paused and has finished downloading */
|
||||
PAUSED_UP = 'pausedUP',
|
||||
/** Queuing is enabled and torrent is queued for upload */
|
||||
QUEUED_UP = 'queuedUP',
|
||||
/** Torrent is being seeded, but no connection were made */
|
||||
STALLED_UP = 'stalledUP',
|
||||
/** Torrent has finished downloading and is being checked */
|
||||
CHECKING_UP = 'checkingUP',
|
||||
/** Torrent is forced to uploading and ignore queue limit */
|
||||
FORCED_UP = 'forcedUP',
|
||||
/** Torrent is allocating disk space for download */
|
||||
ALLOCATING = 'allocating',
|
||||
/** Torrent is being downloaded and data is being transferred */
|
||||
DOWNLOADING = 'downloading',
|
||||
/** Torrent has just started downloading and is fetching metadata */
|
||||
META_DL = 'metaDL',
|
||||
/** Torrent is paused and has NOT finished downloading */
|
||||
PAUSED_DL = 'pausedDL',
|
||||
/** Queuing is enabled and torrent is queued for download */
|
||||
QUEUED_DL = 'queuedDL',
|
||||
/** Torrent is being downloaded, but no connection were made */
|
||||
STALLED_DL = 'stalledDL',
|
||||
/** Same as checkingUP, but torrent has NOT finished downloading */
|
||||
CHECKING_DL = 'checkingDL',
|
||||
/** Torrent is forced to downloading to ignore queue limit */
|
||||
FORCED_DL = 'forcedDL',
|
||||
/** Checking resume data on qBt startup */
|
||||
CHECKING_RESUME_DATA = 'checkingResumeData',
|
||||
/** Torrent is moving to another location */
|
||||
MOVING = 'moving',
|
||||
/** Unknown status */
|
||||
UNKNOWN = 'unknown'
|
||||
}
|
12
src/enums/qbit/TrackerStatus.ts
Normal file
12
src/enums/qbit/TrackerStatus.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export enum TrackerStatus {
|
||||
/** Tracker is disabled (used for DHT, PeX, and LSD) */
|
||||
DISABLED,
|
||||
/** Tracker has not been contacted yet */
|
||||
NOT_YET_CONTACTED,
|
||||
/** Tracker has been contacted and is working */
|
||||
WORKING,
|
||||
/** Tracker is updating */
|
||||
UPDATING,
|
||||
/** Tracker has been contacted, but it is not working (or doesn't send proper replies) */
|
||||
NOT_WORKING
|
||||
}
|
8
src/enums/qbit/index.ts
Normal file
8
src/enums/qbit/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import * as AppPreferences from './AppPreferences'
|
||||
import { LogType } from './LogType'
|
||||
import { Priority } from './Priority'
|
||||
import { TrackerStatus } from './TrackerStatus'
|
||||
import { ConnectionStatus } from './ConnectionStatus'
|
||||
import {TorrentState} from './TorrentState'
|
||||
|
||||
export { AppPreferences, ConnectionStatus, LogType, Priority, TrackerStatus, TorrentState }
|
5
src/enums/vuetorrent/TitleOptions.ts
Normal file
5
src/enums/vuetorrent/TitleOptions.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export enum TitleOptions {
|
||||
DEFAULT = 'Default',
|
||||
GLOBAL_SPEED = 'Global Speed',
|
||||
FIRST_TORRENT_STATUS = 'First Torrent Status'
|
||||
}
|
12
src/enums/vuetorrent/TorrentState.ts
Normal file
12
src/enums/vuetorrent/TorrentState.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export enum TorrentState {
|
||||
DOWNLOADING = 'Downloading',
|
||||
SEEDING = 'Seeding',
|
||||
PAUSED = 'Paused',
|
||||
STALLED = 'Stalled',
|
||||
METADATA = 'Metadata',
|
||||
DONE = 'Done',
|
||||
QUEUED = 'Queued',
|
||||
CHECKING = 'Checking',
|
||||
MOVING = 'Moving',
|
||||
FAIL = 'Fail'
|
||||
}
|
4
src/enums/vuetorrent/index.ts
Normal file
4
src/enums/vuetorrent/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import {TorrentState} from "./TorrentState"
|
||||
import {TitleOptions} from "./TitleOptions"
|
||||
|
||||
export {TorrentState, TitleOptions}
|
|
@ -1,7 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
export function toPrecision(value, precision) {
|
||||
export function toPrecision(value: number, precision: number): string {
|
||||
if (value >= 10 ** precision) {
|
||||
return value.toString()
|
||||
}
|
||||
|
@ -12,7 +11,7 @@ export function toPrecision(value, precision) {
|
|||
return value.toFixed(precision - 1)
|
||||
}
|
||||
|
||||
export function formatSize(value) {
|
||||
export function formatSize(value: number): string {
|
||||
const units = 'KMGTP'
|
||||
let index = -1
|
||||
|
||||
|
@ -33,7 +32,7 @@ export function formatSize(value) {
|
|||
Vue.filter('formatSize', formatSize)
|
||||
Vue.filter('size', formatSize)
|
||||
|
||||
export function formatProgress(progress) {
|
||||
export function formatProgress(progress: number): string {
|
||||
progress *= 100
|
||||
|
||||
return `${toPrecision(progress, 3)}%`
|
||||
|
@ -41,17 +40,7 @@ export function formatProgress(progress) {
|
|||
|
||||
Vue.filter('progress', formatProgress)
|
||||
|
||||
export function parseDate(str) {
|
||||
if (!str) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Date.parse(str) / 1000
|
||||
}
|
||||
|
||||
Vue.filter('parseDate', parseDate)
|
||||
|
||||
export function formatNetworkSpeed(speed) {
|
||||
export function formatNetworkSpeed(speed: number): string|null {
|
||||
if (speed === 0) {
|
||||
return null
|
||||
}
|
||||
|
@ -61,7 +50,7 @@ export function formatNetworkSpeed(speed) {
|
|||
|
||||
Vue.filter('networkSpeed', formatNetworkSpeed)
|
||||
|
||||
export function networkSize(size) {
|
||||
export function networkSize(size: number) {
|
||||
if (size === 0) {
|
||||
return null
|
||||
}
|
||||
|
@ -71,42 +60,41 @@ export function networkSize(size) {
|
|||
|
||||
Vue.filter('networkSize', networkSize)
|
||||
|
||||
export function getDataUnit(a, b) {
|
||||
if (a === -1) return null
|
||||
if (!a) return 'B'
|
||||
export function getDataUnit(data: number) {
|
||||
if (data === -1) return null
|
||||
if (!data) return 'B'
|
||||
const c = 1024
|
||||
const e = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
const f = Math.floor(Math.log(a) / Math.log(c))
|
||||
const f = Math.floor(Math.log(data) / Math.log(c))
|
||||
|
||||
return `${e[f]}`
|
||||
}
|
||||
|
||||
Vue.filter('getDataUnit', getDataUnit)
|
||||
|
||||
export function getDataValue(a, b) {
|
||||
if (a === -1) return 'None'
|
||||
if (!a) return '0'
|
||||
export function getDataValue(data: number, precision: number = 2) {
|
||||
if (data === -1) return 'None'
|
||||
if (!data) return '0'
|
||||
const c = 1024
|
||||
const d = b || 2
|
||||
const f = Math.floor(Math.log(a) / Math.log(c))
|
||||
const f = Math.floor(Math.log(data) / Math.log(c))
|
||||
|
||||
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))}`
|
||||
return `${parseFloat((data / Math.pow(c, f)).toFixed(precision))}`
|
||||
}
|
||||
|
||||
Vue.filter('getDataValue', getDataValue)
|
||||
|
||||
export function titleCase(str) {
|
||||
if (!str) return
|
||||
export function titleCase(str: string): string {
|
||||
if (str.length == 0) return str
|
||||
|
||||
return str
|
||||
.split(' ')
|
||||
.map(w => w[0].toUpperCase() + w.substr(1).toLowerCase())
|
||||
.map(w => w[0] && w[0].toUpperCase() + w.substring(1).toLowerCase())
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
Vue.filter('titleCase', titleCase)
|
||||
|
||||
export function limitToValue(value) {
|
||||
export function limitToValue(value: number): string {
|
||||
if (value === -2) {
|
||||
return 'global'
|
||||
}
|
||||
|
@ -114,7 +102,7 @@ export function limitToValue(value) {
|
|||
return 'unlimited'
|
||||
}
|
||||
|
||||
return value
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
Vue.filter('limitToValue', limitToValue)
|
4
src/global.d.ts
vendored
Normal file
4
src/global.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
export type Optional<T> = T | null | undefined
|
||||
|
||||
export type $TSFixMe = any
|
||||
export type $TSFixMeFunction = (...args: any[]) => any
|
|
@ -195,4 +195,4 @@ export class Hostname {
|
|||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,20 +14,20 @@ import vi from './vi.json'
|
|||
import zh_hans from './zh-hans.json'
|
||||
import zh_hant from './zh-hant.json'
|
||||
|
||||
export const messages = {
|
||||
[Locales.EN]: en,
|
||||
[Locales.ES]: es,
|
||||
[Locales.FR]: fr,
|
||||
[Locales.ID]: id,
|
||||
[Locales.IT]: it,
|
||||
[Locales.JA]: ja,
|
||||
[Locales.NL]: nl,
|
||||
[Locales.PT_BR]: pt_br,
|
||||
[Locales.RU]: ru,
|
||||
[Locales.UK]: uk,
|
||||
[Locales.VI]: vi,
|
||||
[Locales.ZH_HANS]: zh_hans,
|
||||
[Locales.ZH_HANT]: zh_hant
|
||||
export const messages: Record<Locales, any> = {
|
||||
[Locales.EN]: en,
|
||||
[Locales.ES]: es,
|
||||
[Locales.FR]: fr,
|
||||
[Locales.ID]: id,
|
||||
[Locales.IT]: it,
|
||||
[Locales.JA]: ja,
|
||||
[Locales.NL]: nl,
|
||||
[Locales.PT_BR]: pt_br,
|
||||
[Locales.RU]: ru,
|
||||
[Locales.UK]: uk,
|
||||
[Locales.VI]: vi,
|
||||
[Locales.ZH_HANS]: zh_hans,
|
||||
[Locales.ZH_HANT]: zh_hant
|
||||
}
|
||||
|
||||
export const defaultLocale = Locales.EN
|
||||
|
|
|
@ -16,11 +16,7 @@ Vue.use(toast, config)
|
|||
// register modals
|
||||
const components = import.meta.glob('./components/Modals/**/*.vue')
|
||||
Object.entries(components).forEach(([path, definition]) => {
|
||||
const componentName = path
|
||||
.split('/')
|
||||
.pop()
|
||||
.replace(/\.\w+$/, '')
|
||||
|
||||
const componentName = (path.split('/').pop() as string).replace(/\.\w+$/, '')
|
||||
Vue.component(componentName, definition)
|
||||
})
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
export default {
|
||||
computed: {
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '80%'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dialog(visible) {
|
||||
if (!visible) {
|
||||
this.tab = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
src/mixins/FullScreenModal.ts
Normal file
18
src/mixins/FullScreenModal.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {Component, Vue, Watch} from "vue-property-decorator";
|
||||
|
||||
@Component
|
||||
export default class FullScreenModal extends Vue {
|
||||
tab!: string|null
|
||||
|
||||
get phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
}
|
||||
get dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '80%'
|
||||
}
|
||||
|
||||
@Watch('dialog')
|
||||
onDialogChanged(visible: boolean) {
|
||||
if (!visible) this.tab = null
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
return this.getTheme()
|
||||
},
|
||||
isMobile() {
|
||||
return this.$vuetify.breakpoint.smAndDown
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createModal(name, props) {
|
||||
const component = {
|
||||
component: name,
|
||||
props,
|
||||
guid: uuidv4()
|
||||
}
|
||||
|
||||
this.$store.commit('ADD_MODAL', component)
|
||||
}
|
||||
}
|
||||
}
|
27
src/mixins/General.ts
Normal file
27
src/mixins/General.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { mapGetters } from 'vuex'
|
||||
import {Component, Vue} from "vue-property-decorator";
|
||||
|
||||
@Component({
|
||||
computed: mapGetters(['getTheme'])
|
||||
})
|
||||
export default class General extends Vue {
|
||||
getTheme!: () => string
|
||||
|
||||
get theme() {
|
||||
return this.getTheme()
|
||||
}
|
||||
get isMobile() {
|
||||
return this.$vuetify.breakpoint.smAndDown
|
||||
}
|
||||
|
||||
createModal(name: string, props: any) {
|
||||
const component = {
|
||||
component: name,
|
||||
props,
|
||||
guid: uuidv4()
|
||||
}
|
||||
|
||||
this.$store.commit('ADD_MODAL', component)
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
props: ['guid'],
|
||||
data() {
|
||||
return {
|
||||
hndlDialog: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getModalState']),
|
||||
dialog: {
|
||||
get() {
|
||||
return this.hndlDialog
|
||||
},
|
||||
set(val) {
|
||||
this.hndlDialog = val
|
||||
if (!val) this.deleteModal()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
deleteModal() {
|
||||
//this.hndlDialog = false
|
||||
setTimeout(
|
||||
function () {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}.bind(this),
|
||||
300
|
||||
)
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
35
src/mixins/Modal.ts
Normal file
35
src/mixins/Modal.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import {Component, Prop, Vue} from "vue-property-decorator";
|
||||
|
||||
@Component({
|
||||
computed: mapGetters(['getModalState'])
|
||||
})
|
||||
export default class Modal extends Vue {
|
||||
//data
|
||||
hndlDialog = true
|
||||
|
||||
//props
|
||||
@Prop() guid!: string
|
||||
|
||||
// mapGetters
|
||||
getModalState!: () => any
|
||||
|
||||
// computed
|
||||
get dialog() {
|
||||
return this.hndlDialog
|
||||
}
|
||||
set dialog(val) {
|
||||
this.hndlDialog = val
|
||||
if (!val) this.deleteModal()
|
||||
}
|
||||
|
||||
// methods
|
||||
deleteModal() {
|
||||
//this.hndlDialog = false
|
||||
setTimeout(() => { this.$store.commit('DELETE_MODAL', this.guid) }, 300)
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import Vue from 'vue'
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['getSettings']),
|
||||
settings() {
|
||||
return this.getSettings()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async saveSettings() {
|
||||
qbit.setPreferences(this.getSettings()).then(() => {
|
||||
Vue.$toast.success(this.$t('toast.settingsSaved'))
|
||||
})
|
||||
await this.$store.dispatch('FETCH_SETTINGS')
|
||||
await this.$store.commit('SET_LANGUAGE')
|
||||
this.close()
|
||||
if (!this.settings.alternative_webui_enabled) {
|
||||
navigator.serviceWorker.getRegistrations().then(function (registrations) {
|
||||
for (const registration of registrations) {
|
||||
registration.unregister()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
src/mixins/SettingsTab.ts
Normal file
33
src/mixins/SettingsTab.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import {Component, Vue} from "vue-property-decorator";
|
||||
import type {AppPreferences} from "@/types/qbit/models";
|
||||
|
||||
@Component({
|
||||
computed: mapGetters(['getSettings'])
|
||||
})
|
||||
export default class SettingsTab extends Vue {
|
||||
getSettings!: () => AppPreferences
|
||||
|
||||
get settings() {
|
||||
return this.getSettings()
|
||||
}
|
||||
|
||||
close!: () => void
|
||||
|
||||
async saveSettings() {
|
||||
qbit.setPreferences(this.getSettings()).then(() => {
|
||||
Vue.$toast.success(this.$t('toast.settingsSaved').toString())
|
||||
})
|
||||
await this.$store.dispatch('FETCH_SETTINGS')
|
||||
await this.$store.commit('SET_LANGUAGE')
|
||||
this.close()
|
||||
if (!this.settings.alternative_webui_enabled) {
|
||||
navigator.serviceWorker.getRegistrations().then(function (registrations) {
|
||||
for (const registration of registrations) {
|
||||
registration.unregister()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
export default {
|
||||
props: {
|
||||
hash: String,
|
||||
isActive: Boolean
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.activeMethod()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
src/mixins/Tab.ts
Normal file
14
src/mixins/Tab.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {Component, Prop, Vue, Watch} from "vue-property-decorator";
|
||||
|
||||
@Component
|
||||
export default class Tab extends Vue {
|
||||
@Prop() hash!: string
|
||||
@Prop() isActive!: boolean
|
||||
|
||||
activeMethod!: () => void
|
||||
|
||||
@Watch('isActive')
|
||||
isActiveChanged(active: boolean) {
|
||||
if (active) this.activeMethod()
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
theme() {
|
||||
return this.getTheme()
|
||||
},
|
||||
state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
}
|
||||
}
|
||||
}
|
22
src/mixins/TorrentDashboardItem.ts
Normal file
22
src/mixins/TorrentDashboardItem.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import {Component, Prop, Vue} from "vue-property-decorator";
|
||||
import type {Torrent} from "@/models";
|
||||
|
||||
@Component({
|
||||
computed: mapGetters(['getTheme'])
|
||||
})
|
||||
export default class TorrentDashboardItem extends Vue {
|
||||
getTheme!: () => string
|
||||
|
||||
@Prop() torrent!: Torrent
|
||||
|
||||
get phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
}
|
||||
get theme() {
|
||||
return this.getTheme()
|
||||
}
|
||||
get state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
export default {
|
||||
methods: {
|
||||
isAlreadySelected(hash) {
|
||||
return this.$store.getters.containsTorrent(hash)
|
||||
},
|
||||
selectTorrent(hash) {
|
||||
if (!this.$store.state.selectMode) this.$store.state.selectMode = true
|
||||
if (this.isAlreadySelected(hash)) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'remove', hash })
|
||||
} else {
|
||||
this.$store.commit('SET_SELECTED', { type: 'add', hash })
|
||||
}
|
||||
},
|
||||
selectUntil(hash, index) {
|
||||
if (!this.$store.state.selectMode) return
|
||||
this.$store.commit('SET_SELECTED', { type: 'until', hash, index })
|
||||
}
|
||||
}
|
||||
}
|
20
src/mixins/TorrentSelect.ts
Normal file
20
src/mixins/TorrentSelect.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import {Component, Vue} from "vue-property-decorator";
|
||||
|
||||
@Component
|
||||
export default class TorrentSelect extends Vue {
|
||||
isAlreadySelected(hash: string) {
|
||||
return this.$store.getters.containsTorrent(hash)
|
||||
}
|
||||
selectTorrent(hash: string) {
|
||||
if (!this.$store.state.selectMode) this.$store.state.selectMode = true
|
||||
if (this.isAlreadySelected(hash)) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'remove', hash })
|
||||
} else {
|
||||
this.$store.commit('SET_SELECTED', { type: 'add', hash })
|
||||
}
|
||||
}
|
||||
selectUntil(hash: string, index: number) {
|
||||
if (!this.$store.state.selectMode) return
|
||||
this.$store.commit('SET_SELECTED', { type: 'until', hash, index })
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import store from '../store'
|
||||
|
||||
export default class Status {
|
||||
constructor({ connection_status, dl_info_data, up_info_data, alltime_dl, alltime_ul, dl_info_speed, up_info_speed, free_space_on_disk, use_alt_speed_limits }) {
|
||||
const previous = store.state.status
|
||||
|
||||
this.status = connection_status || previous.status
|
||||
this.sessionDownloaded = dl_info_data || previous.sessionDownloaded
|
||||
this.sessionUploaded = up_info_data || previous.sessionUploaded
|
||||
this.alltimeDownloaded = alltime_dl || previous.alltimeDownloaded
|
||||
this.alltimeUploaded = alltime_ul || previous.alltimeUploaded
|
||||
this.dlspeed = dl_info_speed || 0
|
||||
this.upspeed = up_info_speed || 0
|
||||
this.freeDiskSpace = free_space_on_disk || previous.freeDiskSpace
|
||||
this.altSpeed = use_alt_speed_limits !== undefined ? use_alt_speed_limits : previous.altSpeed
|
||||
this.dlspeedRaw = this.formatSpeed(dl_info_speed) || 0
|
||||
this.upspeedRaw = this.formatSpeed(up_info_speed) || 0
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
formatSpeed(value) {
|
||||
if (!value) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return Math.round(value)
|
||||
}
|
||||
}
|
47
src/models/Status.ts
Normal file
47
src/models/Status.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import store from '@/store'
|
||||
import type { ServerState } from '@/types/qbit/models'
|
||||
import { ConnectionStatus } from '@/enums/qbit'
|
||||
|
||||
export default class Status {
|
||||
alltimeDownloaded: number = 0
|
||||
alltimeUploaded: number = 0
|
||||
altSpeed: boolean = false
|
||||
dlspeed: number = 0
|
||||
dlspeedRaw: number = 0
|
||||
freeDiskSpace: number = 0
|
||||
sessionDownloaded: number = 0
|
||||
sessionUploaded: number = 0
|
||||
status: ConnectionStatus = ConnectionStatus.DISCONNECTED
|
||||
upspeed: number = 0
|
||||
upspeedRaw: number = 0
|
||||
|
||||
constructor(in_state?: ServerState) {
|
||||
if (!in_state) {
|
||||
return
|
||||
}
|
||||
|
||||
const previous = store.state.status
|
||||
|
||||
this.alltimeDownloaded = in_state.alltime_dl || previous.alltimeDownloaded
|
||||
this.alltimeUploaded = in_state.alltime_ul || previous.alltimeUploaded
|
||||
this.altSpeed = in_state.use_alt_speed_limits !== undefined ? in_state.use_alt_speed_limits : previous.altSpeed
|
||||
this.dlspeed = in_state.dl_info_speed || 0
|
||||
this.dlspeedRaw = this.formatSpeed(in_state.dl_info_speed) || 0
|
||||
this.freeDiskSpace = in_state.free_space_on_disk || previous.freeDiskSpace
|
||||
this.sessionDownloaded = in_state.dl_info_data || previous.sessionDownloaded
|
||||
this.sessionUploaded = in_state.up_info_data || previous.sessionUploaded
|
||||
this.status = in_state.connection_status || previous.status
|
||||
this.upspeed = in_state.up_info_speed || 0
|
||||
this.upspeedRaw = this.formatSpeed(in_state.up_info_speed) || 0
|
||||
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
formatSpeed(value: number): number {
|
||||
if (!value) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return Math.round(value)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
import dayjs from 'dayjs'
|
||||
import duration from 'dayjs/plugin/duration'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import {TorrentState as QbitTorrentState} from "@/enums/qbit";
|
||||
import {TorrentState as VtTorrentState} from "@/enums/vuetorrent";
|
||||
import type {Torrent as QbitTorrent} from '@/types/qbit/models'
|
||||
|
||||
dayjs.extend(duration)
|
||||
dayjs.extend(relativeTime)
|
||||
|
@ -9,7 +12,45 @@ const durationFormat = 'D[d] H[h] m[m] s[s]'
|
|||
|
||||
export default class Torrent {
|
||||
static computedValues = ['globalSpeed', 'globalVolume']
|
||||
constructor(data, format = 'DD/MM/YYYY, HH:mm:ss') {
|
||||
|
||||
name: string
|
||||
size: number
|
||||
added_on: string
|
||||
completed_on: string
|
||||
dlspeed: number
|
||||
dloaded: number
|
||||
upspeed: number
|
||||
uploaded: number
|
||||
uploaded_session: number
|
||||
eta: string
|
||||
num_leechs: number
|
||||
num_seeds: number
|
||||
state: VtTorrentState
|
||||
hash: string
|
||||
available_seeds: number
|
||||
available_peers: number
|
||||
savePath: string
|
||||
progress: number
|
||||
ratio: number
|
||||
tags: string[] | null
|
||||
category: string
|
||||
tracker: string
|
||||
f_l_piece_prio: boolean
|
||||
seq_dl: boolean
|
||||
auto_tmm: boolean
|
||||
dl_limit: number
|
||||
up_limit: number
|
||||
ratio_limit: number
|
||||
ratio_time_limit: number
|
||||
availability: number
|
||||
forced: boolean
|
||||
magnet: string
|
||||
time_active: string
|
||||
seeding_time: string | null
|
||||
last_activity: string
|
||||
globalSpeed: number
|
||||
globalVolume: number
|
||||
constructor(data: QbitTorrent, format = 'DD/MM/YYYY, HH:mm:ss') {
|
||||
this.name = data.name
|
||||
this.size = data.size
|
||||
this.added_on = dayjs(data.added_on * 1000).format(format)
|
||||
|
@ -22,7 +63,6 @@ export default class Torrent {
|
|||
this.eta = this.formatEta(data.eta)
|
||||
this.num_leechs = data.num_leechs
|
||||
this.num_seeds = data.num_seeds
|
||||
this.path = data.path === undefined ? '/downloads' : data.path
|
||||
this.state = this.formatState(data.state)
|
||||
this.hash = data.hash
|
||||
this.available_seeds = data.num_complete
|
||||
|
@ -54,42 +94,41 @@ export default class Torrent {
|
|||
Object.freeze(this)
|
||||
}
|
||||
|
||||
formatState(state) {
|
||||
formatState(state: QbitTorrentState): VtTorrentState {
|
||||
switch (state) {
|
||||
case 'forcedDL':
|
||||
case 'downloading':
|
||||
return 'Downloading'
|
||||
case 'metaDL':
|
||||
return 'Metadata'
|
||||
case 'forcedUP':
|
||||
case 'uploading':
|
||||
case 'stalledUP':
|
||||
return 'Seeding'
|
||||
case 'pausedDL':
|
||||
return 'Paused'
|
||||
case 'pausedUP':
|
||||
return 'Done'
|
||||
case 'queuedDL':
|
||||
case 'queuedUP':
|
||||
return 'Queued'
|
||||
case 'allocating':
|
||||
case 'checkingDL':
|
||||
case 'checkingUP':
|
||||
case 'checkingResumeData':
|
||||
return 'Checking'
|
||||
case 'moving':
|
||||
return 'Moving'
|
||||
case 'unknown':
|
||||
case 'missingFiles':
|
||||
return 'Fail'
|
||||
case 'stalledDL':
|
||||
return 'Stalled'
|
||||
case QbitTorrentState.FORCED_DL:
|
||||
case QbitTorrentState.DOWNLOADING:
|
||||
return VtTorrentState.DOWNLOADING
|
||||
case QbitTorrentState.META_DL:
|
||||
return VtTorrentState.METADATA
|
||||
case QbitTorrentState.FORCED_UP:
|
||||
case QbitTorrentState.UPLOADING:
|
||||
case QbitTorrentState.STALLED_UP:
|
||||
return VtTorrentState.SEEDING
|
||||
case QbitTorrentState.PAUSED_DL:
|
||||
return VtTorrentState.PAUSED
|
||||
case QbitTorrentState.PAUSED_UP:
|
||||
return VtTorrentState.DONE
|
||||
case QbitTorrentState.QUEUED_DL:
|
||||
case QbitTorrentState.QUEUED_UP:
|
||||
return VtTorrentState.QUEUED
|
||||
case QbitTorrentState.ALLOCATING:
|
||||
case QbitTorrentState.CHECKING_DL:
|
||||
case QbitTorrentState.CHECKING_UP:
|
||||
case QbitTorrentState.CHECKING_RESUME_DATA:
|
||||
return VtTorrentState.CHECKING
|
||||
case QbitTorrentState.MOVING:
|
||||
return VtTorrentState.MOVING
|
||||
case QbitTorrentState.UNKNOWN:
|
||||
case QbitTorrentState.STALLED_DL:
|
||||
return VtTorrentState.STALLED
|
||||
case QbitTorrentState.MISSING_FILES:
|
||||
default:
|
||||
return 'Fail'
|
||||
return VtTorrentState.FAIL
|
||||
}
|
||||
}
|
||||
|
||||
formatEta(value) {
|
||||
formatEta(value: number): string {
|
||||
const options = { dayLimit: 100 }
|
||||
const minute = 60
|
||||
const hour = minute * 60
|
4
src/models/index.ts
Normal file
4
src/models/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Status from './Status'
|
||||
import Torrent from './Torrent'
|
||||
|
||||
export {Status, Torrent}
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue'
|
||||
import type { Framework } from 'vuetify'
|
||||
import Vuetify from 'vuetify/lib'
|
||||
import { Ripple } from 'vuetify/lib/directives'
|
||||
|
||||
|
@ -46,45 +47,22 @@ export default new Vuetify({
|
|||
background: colors.grey.lighten4,
|
||||
selected: colors.grey.lighten2,
|
||||
red: colors.red.accent2,
|
||||
primary: '#35495e',
|
||||
secondary: '#3e556d',
|
||||
download: '#64CEAA',
|
||||
upload: '#00b3fa',
|
||||
// Torrent status colors
|
||||
'torrent-done': '#16573e',
|
||||
'torrent-downloading': '#5bb974',
|
||||
'torrent-fail': '#f83e70',
|
||||
'torrent-paused': '#9CA3AF',
|
||||
'torrent-queued': '#2e5eaa',
|
||||
'torrent-seeding': '#4ecde6',
|
||||
'torrent-checking': '#ff7043',
|
||||
'torrent-stalled': '#4ADE80',
|
||||
'torrent-metadata': '#7e57c2',
|
||||
'torrent-moving': '#ffaa2c',
|
||||
...variables
|
||||
},
|
||||
dark: {
|
||||
accent: '#64CEAA',
|
||||
background: colors.black,
|
||||
background: colors.shades.black,
|
||||
selected: colors.grey.darken1,
|
||||
red: colors.red.accent3,
|
||||
primary: '#35495e',
|
||||
secondary: '#3e556d',
|
||||
download: '#64CEAA',
|
||||
upload: '#00b3fa',
|
||||
// Torrent status colors
|
||||
'torrent-done': '#16573e',
|
||||
'torrent-downloading': '#5bb974',
|
||||
'torrent-fail': '#f83e70',
|
||||
'torrent-paused': '#9CA3AF',
|
||||
'torrent-queued': '#2e5eaa',
|
||||
'torrent-seeding': '#4ecde6',
|
||||
'torrent-checking': '#ff7043',
|
||||
'torrent-stalled': '#4ADE80',
|
||||
'torrent-metadata': '#7e57c2',
|
||||
'torrent-moving': '#ffaa2c',
|
||||
...variables
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
// this.$vuetify inside Vue components
|
||||
interface Vue {
|
||||
$vuetify: Framework
|
||||
}
|
||||
}
|
|
@ -1,4 +1,25 @@
|
|||
import axios, { AxiosInstance } from 'axios'
|
||||
import axios from 'axios'
|
||||
import type { AxiosInstance } from 'axios'
|
||||
import type {
|
||||
ApplicationVersion,
|
||||
AppPreferences,
|
||||
Category,
|
||||
Feed,
|
||||
FeedRule,
|
||||
SearchJob,
|
||||
SearchPlugin,
|
||||
SearchStatus,
|
||||
TorrentFile,
|
||||
TorrentProperties,
|
||||
Tracker,
|
||||
Torrent
|
||||
} from '@/types/qbit/models'
|
||||
import type { MainDataResponse, SearchResultsResponse, TorrentPeersResponse } from '@/types/qbit/responses'
|
||||
import type { AddTorrentPayload, AppPreferencesPayload, LoginPayload } from '@/types/qbit/payloads'
|
||||
import type { SortOptions } from '@/types/vuetorrent'
|
||||
import type { Priority } from '@/enums/qbit'
|
||||
|
||||
type Parameters = Record<string, any>
|
||||
|
||||
export class QBitApi {
|
||||
private axios: AxiosInstance
|
||||
|
@ -11,92 +32,63 @@ export class QBitApi {
|
|||
this.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
|
||||
}
|
||||
|
||||
execute(method, action, params) {
|
||||
if (method === 'post') {
|
||||
const data = new URLSearchParams(params)
|
||||
|
||||
return this.axios.post(action, data).then(res => res.data)
|
||||
}
|
||||
async execute(action: string, params?: Parameters): Promise<any> {
|
||||
const data = new URLSearchParams(params)
|
||||
return this.axios.post(action, data).then(res => res.data)
|
||||
}
|
||||
|
||||
/** Begin General functions * */
|
||||
getAppVersion(): Promise<string> {
|
||||
async getAppVersion(): Promise<ApplicationVersion> {
|
||||
return this.axios
|
||||
.get('/app/version')
|
||||
.then(res => res.data)
|
||||
.then(version => (version.includes('v') ? version.substring(1) : version))
|
||||
.get('/app/version')
|
||||
.then(res => res.data)
|
||||
.then(version => (version.includes('v') ? version.substring(1) : version))
|
||||
}
|
||||
|
||||
getApiVersion() {
|
||||
return this.axios.get('/app/webapiVersion')
|
||||
}
|
||||
|
||||
async login(params) {
|
||||
async login(params: LoginPayload): Promise<string> {
|
||||
const payload = new URLSearchParams(params)
|
||||
const { data } = await this.axios
|
||||
.post('/auth/login', payload, {
|
||||
validateStatus(status) {
|
||||
return status === 200 || status === 403
|
||||
}
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
const res = await this.axios
|
||||
.post('/auth/login', payload, {
|
||||
validateStatus: (status: number) => status === 200 || status === 403
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
|
||||
return data
|
||||
return res?.data
|
||||
}
|
||||
|
||||
async getAuthenticationStatus() {
|
||||
async getAuthenticationStatus(): Promise<boolean> {
|
||||
return this.axios
|
||||
.get('/app/version')
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
.get('/app/version')
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
}
|
||||
|
||||
async logout() {
|
||||
this.axios.post('/auth/logout')
|
||||
async logout(): Promise<void> {
|
||||
await this.axios.post('/auth/logout')
|
||||
}
|
||||
|
||||
getGlobalTransferInfo() {
|
||||
return this.axios.get('/transfer/info')
|
||||
async getAppPreferences(): Promise<AppPreferences> {
|
||||
return this.axios.get('/app/preferences').then(r => r.data)
|
||||
}
|
||||
|
||||
getAppPreferences() {
|
||||
return this.axios.get('/app/preferences')
|
||||
}
|
||||
|
||||
setPreferences(params) {
|
||||
const data = new URLSearchParams({
|
||||
async setPreferences(params: AppPreferencesPayload): Promise<void> {
|
||||
const data = {
|
||||
json: JSON.stringify(params)
|
||||
})
|
||||
}
|
||||
|
||||
return this.axios.post('/app/setPreferences', data)
|
||||
await this.execute('/app/setPreferences', data)
|
||||
}
|
||||
|
||||
getMainData(rid) {
|
||||
async getMainData(rid?: number): Promise<MainDataResponse> {
|
||||
return this.axios.get('/sync/maindata', { params: { rid } }).then(res => res.data)
|
||||
}
|
||||
|
||||
switchToOldUi() {
|
||||
return this.setPreferences({
|
||||
alternative_webui_enabled: false
|
||||
})
|
||||
async toggleSpeedLimitsMode(): Promise<void> {
|
||||
await this.execute('/transfer/toggleSpeedLimitsMode')
|
||||
}
|
||||
|
||||
toggleSpeedLimitsMode() {
|
||||
return this.axios.post('/transfer/toggleSpeedLimitsMode')
|
||||
}
|
||||
|
||||
/** Begin Torrent functions * */
|
||||
|
||||
// Get
|
||||
|
||||
getLogs(lastId) {
|
||||
return this.axios.get('/log/main', {
|
||||
last_known_id: lastId
|
||||
})
|
||||
}
|
||||
|
||||
getTorrents(payload) {
|
||||
const params = {
|
||||
async getTorrents(payload: SortOptions): Promise<Torrent[]> {
|
||||
const params: Parameters = {
|
||||
sort: !payload.isCustomSortEnabled ? payload.sort : null,
|
||||
reverse: !payload.isCustomSortEnabled ? payload.reverse : null,
|
||||
hashes: payload.hashes.length > 0 ? payload.hashes.join('|') : null,
|
||||
|
@ -110,104 +102,111 @@ export class QBitApi {
|
|||
|
||||
const data = new URLSearchParams(params)
|
||||
|
||||
return this.axios.get(`/torrents/info?${data.toString()}`)
|
||||
return this.axios.get(`/torrents/info?${data.toString()}`).then(r => r.data)
|
||||
}
|
||||
|
||||
getTorrentTrackers(hash) {
|
||||
return this.axios.get('/torrents/trackers', {
|
||||
params: { hash }
|
||||
})
|
||||
async getTorrentTrackers(hash: string): Promise<Tracker[]> {
|
||||
return this.axios
|
||||
.get('/torrents/trackers', {
|
||||
params: { hash }
|
||||
})
|
||||
.then(r => r.data)
|
||||
}
|
||||
|
||||
getTorrentPeers(hash, rid) {
|
||||
async getTorrentPeers(hash: string, rid?: number): Promise<TorrentPeersResponse> {
|
||||
return this.axios.get('/sync/torrentPeers', {
|
||||
params: { hash, rid }
|
||||
})
|
||||
}
|
||||
|
||||
setTorrentName(hash, name) {
|
||||
return this.execute('post', '/torrents/rename', { hash, name })
|
||||
async setTorrentName(hash: string, name: string): Promise<void> {
|
||||
await this.execute('/torrents/rename', { hash, name })
|
||||
}
|
||||
|
||||
getTorrentPieceStates(hash) {
|
||||
return this.axios.get('/torrents/pieceStates', {
|
||||
params: { hash }
|
||||
})
|
||||
async getTorrentPieceStates(hash: string): Promise<number[]> {
|
||||
return this.axios
|
||||
.get('/torrents/pieceStates', {
|
||||
params: { hash }
|
||||
})
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
getTorrentFiles(hash) {
|
||||
return this.axios.get('/torrents/files', {
|
||||
params: { hash }
|
||||
})
|
||||
async getTorrentFiles(hash: string, indexes?: number[]): Promise<TorrentFile[]> {
|
||||
return this.axios
|
||||
.get('/torrents/files', {
|
||||
params: { hash, indexes: indexes?.join('|') }
|
||||
})
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
getAvailableTags() {
|
||||
async getAvailableTags(): Promise<string[]> {
|
||||
return this.axios.get('/torrents/tags').then(res => res.data)
|
||||
}
|
||||
|
||||
getTorrentProperties(hash) {
|
||||
async getTorrentProperties(hash: string): Promise<TorrentProperties> {
|
||||
return this.axios
|
||||
.get('/torrents/properties', {
|
||||
params: { hash }
|
||||
})
|
||||
.then(res => res.data)
|
||||
.get('/torrents/properties', {
|
||||
params: { hash }
|
||||
})
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
// RSS
|
||||
|
||||
createFeed(feed) {
|
||||
return this.execute('post', '/rss/addFeed', {
|
||||
url: feed.url,
|
||||
path: feed.url
|
||||
async createFeed(url: string, path?: string): Promise<void> {
|
||||
await this.execute('/rss/addFeed', {
|
||||
url: url,
|
||||
path: path
|
||||
})
|
||||
}
|
||||
|
||||
createRule(ruleName, defs) {
|
||||
return this.execute('post', '/rss/setRule', {
|
||||
async createRule(ruleName: string, ruleDef: FeedRule) {
|
||||
return this.execute('/rss/setRule', {
|
||||
ruleName: ruleName,
|
||||
ruleDef: JSON.stringify(defs)
|
||||
ruleDef: JSON.stringify(ruleDef, ['enabled', 'mustContain', 'mustNotContain', 'useRegex', 'affectedFeeds'])
|
||||
})
|
||||
}
|
||||
|
||||
getFeeds() {
|
||||
return this.axios
|
||||
.get('/rss/items')
|
||||
.then(res => res.data)
|
||||
.then(data =>
|
||||
Object.entries(data).map(feed => {
|
||||
return { name: feed[0], ...feed[1] }
|
||||
})
|
||||
)
|
||||
async getFeeds(): Promise<Record<string, Feed>> {
|
||||
return this.axios.get('/rss/items').then(res => res.data)
|
||||
}
|
||||
|
||||
getRules() {
|
||||
return this.axios
|
||||
.get('/rss/rules')
|
||||
.then(res => res.data)
|
||||
.then(data =>
|
||||
Object.entries(data).map(rule => {
|
||||
return { name: rule[0], ...rule[1] }
|
||||
})
|
||||
)
|
||||
async getRules(): Promise<Record<string, FeedRule>> {
|
||||
return this.axios.get('/rss/rules').then(res => res.data)
|
||||
}
|
||||
|
||||
deleteRule(ruleName) {
|
||||
return this.execute('post', 'rss/removeRule', {
|
||||
async editFeed(itemPath: string, destPath: string): Promise<void> {
|
||||
await this.execute('/rss/moveItem', {
|
||||
itemPath,
|
||||
destPath
|
||||
})
|
||||
}
|
||||
|
||||
async editRule(ruleName: string, newRuleName: string): Promise<void> {
|
||||
await this.execute('/rss/renameRule', {
|
||||
ruleName,
|
||||
newRuleName
|
||||
})
|
||||
}
|
||||
|
||||
async deleteRule(ruleName: string): Promise<void> {
|
||||
await this.execute('rss/removeRule', {
|
||||
ruleName
|
||||
})
|
||||
}
|
||||
|
||||
deleteFeed(name) {
|
||||
return this.execute('post', 'rss/removeItem', {
|
||||
async deleteFeed(name: string): Promise<void> {
|
||||
await this.execute('rss/removeItem', {
|
||||
path: name
|
||||
})
|
||||
}
|
||||
|
||||
// Post
|
||||
|
||||
addTorrents(params, torrents) {
|
||||
async addTorrents(params: AddTorrentPayload, torrents: File[]): Promise<void> {
|
||||
let data
|
||||
if (torrents) {
|
||||
// torrent files
|
||||
const formData = new FormData()
|
||||
if (params) {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
|
@ -221,283 +220,269 @@ export class QBitApi {
|
|||
|
||||
data = formData
|
||||
} else {
|
||||
data = new URLSearchParams(params)
|
||||
// magnet links
|
||||
data = new URLSearchParams(params as Parameters)
|
||||
}
|
||||
|
||||
return this.axios.post('/torrents/add', data)
|
||||
await this.axios.post('/torrents/add', data)
|
||||
}
|
||||
|
||||
setTorrentFilePriority(hash, idList, priority) {
|
||||
async setTorrentFilePriority(hash: string, idList: number[], priority: Priority): Promise<void> {
|
||||
const params = {
|
||||
hash,
|
||||
id: idList.join('|'),
|
||||
priority
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/filePrio', params)
|
||||
await this.execute('/torrents/filePrio', params)
|
||||
}
|
||||
|
||||
deleteTorrents(hashes, deleteFiles) {
|
||||
async deleteTorrents(hashes: string[], deleteFiles: boolean): Promise<void> {
|
||||
if (!hashes.length) return
|
||||
|
||||
return this.torrentAction('delete', hashes, { deleteFiles })
|
||||
await this.torrentAction('delete', hashes, { deleteFiles })
|
||||
}
|
||||
|
||||
pauseTorrents(hashes) {
|
||||
return this.torrentAction('pause', hashes)
|
||||
async pauseTorrents(hashes: string[]): Promise<void> {
|
||||
await this.torrentAction('pause', hashes)
|
||||
}
|
||||
|
||||
resumeTorrents(hashes) {
|
||||
return this.torrentAction('resume', hashes)
|
||||
async resumeTorrents(hashes: string[]): Promise<void> {
|
||||
await this.torrentAction('resume', hashes)
|
||||
}
|
||||
|
||||
forceStartTorrents(hashes) {
|
||||
return this.torrentAction('setForceStart', hashes, { value: true })
|
||||
async forceStartTorrents(hashes: string[]): Promise<void> {
|
||||
await this.torrentAction('setForceStart', hashes, { value: true })
|
||||
}
|
||||
|
||||
toggleSequentialDownload(hashes) {
|
||||
return this.torrentAction('toggleSequentialDownload', hashes)
|
||||
async toggleSequentialDownload(hashes: string[]): Promise<void> {
|
||||
await this.torrentAction('toggleSequentialDownload', hashes)
|
||||
}
|
||||
|
||||
toggleFirstLastPiecePriority(hashes) {
|
||||
return this.torrentAction('toggleFirstLastPiecePrio', hashes)
|
||||
async toggleFirstLastPiecePriority(hashes: string[]): Promise<void> {
|
||||
await this.torrentAction('toggleFirstLastPiecePrio', hashes)
|
||||
}
|
||||
|
||||
setAutoTMM(hashes, enable) {
|
||||
return this.torrentAction('setAutoManagement', hashes, { enable })
|
||||
async setAutoTMM(hashes: string[], enable: boolean): Promise<void> {
|
||||
await this.torrentAction('setAutoManagement', hashes, { enable })
|
||||
}
|
||||
|
||||
setDownloadLimit(hashes, limit) {
|
||||
return this.torrentAction('setDownloadLimit', hashes, { limit })
|
||||
async setDownloadLimit(hashes: string[], limit: number): Promise<void> {
|
||||
await this.torrentAction('setDownloadLimit', hashes, { limit })
|
||||
}
|
||||
|
||||
setUploadLimit(hashes, limit) {
|
||||
return this.torrentAction('setUploadLimit', hashes, { limit })
|
||||
async setUploadLimit(hashes: string[], limit: number): Promise<void> {
|
||||
await this.torrentAction('setUploadLimit', hashes, { limit })
|
||||
}
|
||||
|
||||
async getGlobalDownloadLimit() {
|
||||
const { data } = await this.axios.get('/transfer/downloadLimit')
|
||||
|
||||
return data
|
||||
/**
|
||||
* @return current global download speed limit in bytes/second; this value will be zero if no limit is applied.
|
||||
*/
|
||||
async getGlobalDownloadLimit(): Promise<number> {
|
||||
return this.axios.get('/transfer/downloadLimit').then(res => res.data)
|
||||
}
|
||||
|
||||
async getGlobalUploadLimit() {
|
||||
const { data } = await this.axios.get('/transfer/uploadLimit')
|
||||
|
||||
return data
|
||||
/**
|
||||
* @return current global upload speed limit in bytes/second; this value will be zero if no limit is applied.
|
||||
*/
|
||||
async getGlobalUploadLimit(): Promise<number> {
|
||||
return this.axios.get('/transfer/uploadLimit').then(res => res.data)
|
||||
}
|
||||
|
||||
setGlobalDownloadLimit(limit) {
|
||||
const formData = new FormData()
|
||||
formData.append('limit', limit)
|
||||
/**
|
||||
* @param limit - The global download speed limit to set in bytes/second
|
||||
*/
|
||||
async setGlobalDownloadLimit(limit: number): Promise<void> {
|
||||
const data = {
|
||||
limit
|
||||
}
|
||||
|
||||
return this.axios.post('/transfer/setDownloadLimit', formData)
|
||||
await this.execute('/transfer/setDownloadLimit', data)
|
||||
}
|
||||
|
||||
setGlobalUploadLimit(limit) {
|
||||
const formData = new FormData()
|
||||
formData.append('limit', limit)
|
||||
/**
|
||||
* @param limit - The global upload speed limit to set in bytes/second
|
||||
*/
|
||||
async setGlobalUploadLimit(limit: number): Promise<void> {
|
||||
const data = {
|
||||
limit
|
||||
}
|
||||
|
||||
return this.axios.post('/transfer/setUploadLimit', formData)
|
||||
await this.execute('/transfer/setUploadLimit', data)
|
||||
}
|
||||
|
||||
setShareLimit(hashes, ratioLimit, seedingTimeLimit) {
|
||||
return this.torrentAction('setShareLimits', hashes, {
|
||||
async setShareLimit(hashes: string[], ratioLimit: number, seedingTimeLimit: number): Promise<void> {
|
||||
await this.torrentAction('setShareLimits', hashes, {
|
||||
ratioLimit,
|
||||
seedingTimeLimit
|
||||
})
|
||||
}
|
||||
|
||||
reannounceTorrents(hashes) {
|
||||
return this.torrentAction('reannounce', hashes)
|
||||
async reannounceTorrents(hashes: string[]): Promise<void> {
|
||||
await this.torrentAction('reannounce', hashes)
|
||||
}
|
||||
|
||||
recheckTorrents(hashes) {
|
||||
return this.torrentAction('recheck', hashes)
|
||||
async recheckTorrents(hashes: string[]): Promise<void> {
|
||||
await this.torrentAction('recheck', hashes)
|
||||
}
|
||||
|
||||
setTorrentsCategory(hashes, category) {
|
||||
return this.torrentAction('setCategory', hashes, { category })
|
||||
async setTorrentLocation(hashes: string[], location: string): Promise<void> {
|
||||
await this.torrentAction('setLocation', hashes, { location })
|
||||
}
|
||||
|
||||
editTracker(hash, origUrl, newUrl) {
|
||||
return this.torrentAction('editTracker', [hash], { origUrl, newUrl })
|
||||
async addTorrentTrackers(hash: string, trackers: string): Promise<void> {
|
||||
await this.torrentAction('addTrackers', [hash], { urls: trackers })
|
||||
}
|
||||
|
||||
setTorrentLocation(hashes, location) {
|
||||
return this.torrentAction('setLocation', hashes, { location })
|
||||
async removeTorrentTrackers(hash: string, trackers: string[]): Promise<void> {
|
||||
await this.torrentAction('removeTrackers', [hash], { urls: trackers.join('|') })
|
||||
}
|
||||
|
||||
addTorrenTrackers(hash, trackers) {
|
||||
const params = {
|
||||
hash,
|
||||
urls: trackers
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/addTrackers', params)
|
||||
async addTorrentPeers(hashes: string[], peers: string[]): Promise<void> {
|
||||
await this.torrentAction('addPeers', hashes, { peers: peers.join('|') })
|
||||
}
|
||||
|
||||
removeTorrentTrackers(hash, trackers) {
|
||||
const params = {
|
||||
hash,
|
||||
urls: trackers.join('|')
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/removeTrackers', params)
|
||||
}
|
||||
|
||||
addTorrentPeers(hashes: Array<string>, peers: Array<string>) {
|
||||
const params = {
|
||||
hashes: hashes.join('|'),
|
||||
peers: peers.join('|')
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/addPeers', params)
|
||||
}
|
||||
|
||||
banPeers(peers: Array<string>) {
|
||||
async banPeers(peers: string[]): Promise<void> {
|
||||
const params = {
|
||||
peers: peers.join('|')
|
||||
}
|
||||
|
||||
return this.execute('post', '/transfer/banPeers', params)
|
||||
await this.execute('/transfer/banPeers', params)
|
||||
}
|
||||
|
||||
torrentAction(action, hashes, extra) {
|
||||
async torrentAction(action: string, hashes: string[], extra?: Record<string, any>): Promise<any> {
|
||||
const params = {
|
||||
hashes: hashes.length ? hashes.join('|') : 'all',
|
||||
...extra
|
||||
}
|
||||
|
||||
return this.execute('post', `/torrents/${action}`, params)
|
||||
return this.execute(`/torrents/${action}`, params)
|
||||
}
|
||||
|
||||
renameFile(hash, oldPath, newPath) {
|
||||
async renameFile(hash: string, oldPath: string, newPath: string): Promise<void> {
|
||||
const params = {
|
||||
hash,
|
||||
oldPath,
|
||||
newPath
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/renameFile', params)
|
||||
await this.execute('/torrents/renameFile', params)
|
||||
}
|
||||
|
||||
renameFolder(hash, oldPath, newPath) {
|
||||
async renameFolder(hash: string, oldPath: string, newPath: string): Promise<void> {
|
||||
const params = {
|
||||
hash,
|
||||
oldPath,
|
||||
newPath
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/renameFolder', params)
|
||||
await this.execute('/torrents/renameFolder', params)
|
||||
}
|
||||
|
||||
/** Torrent Priority **/
|
||||
setTorrentPriority(hashes, priority) {
|
||||
if (['increasePrio', 'decreasePrio', 'topPrio', 'bottomPrio'].includes(priority)) {
|
||||
return this.execute('post', `/torrents/${priority}`, {
|
||||
hashes: hashes.join('|')
|
||||
})
|
||||
}
|
||||
async setTorrentPriority(hashes: string[], priority: 'increasePrio' | 'decreasePrio' | 'topPrio' | 'bottomPrio'): Promise<void> {
|
||||
await this.execute(`/torrents/${priority}`, {
|
||||
hashes: hashes.join('|')
|
||||
})
|
||||
}
|
||||
|
||||
/** Begin Torrent Tags **/
|
||||
removeTorrentTag(hashes, tag) {
|
||||
return this.execute('post', '/torrents/removeTags', {
|
||||
hashes: hashes.join('|'),
|
||||
tags: tag
|
||||
async removeTorrentTag(hashes: string[], tags: string[]): Promise<void> {
|
||||
await this.torrentAction('removeTags', hashes, { tags: tags.join('|') })
|
||||
}
|
||||
|
||||
async addTorrentTag(hashes: string[], tags: string[]): Promise<void> {
|
||||
await this.torrentAction('addTags', hashes, { tags: tags.join('|') })
|
||||
}
|
||||
|
||||
async createTag(tags: string[]): Promise<void> {
|
||||
await this.execute('/torrents/createTags', {
|
||||
tags: tags.join(',')
|
||||
})
|
||||
}
|
||||
|
||||
addTorrentTag(hashes, tag) {
|
||||
return this.execute('post', '/torrents/addTags ', {
|
||||
hashes: hashes.join('|'),
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
createTag(tag) {
|
||||
return this.execute('post', '/torrents/createTags ', {
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
deleteTag(tag) {
|
||||
return this.execute('post', '/torrents/deleteTags', {
|
||||
tags: tag
|
||||
async deleteTag(tags: string[]): Promise<void> {
|
||||
await this.execute('/torrents/deleteTags', {
|
||||
tags: tags.join(',')
|
||||
})
|
||||
}
|
||||
|
||||
/** Begin Categories **/
|
||||
getCategories() {
|
||||
return this.axios.get('/torrents/categories').then(res => res.data)
|
||||
async getCategories(): Promise<Category[]> {
|
||||
return this.axios
|
||||
.get('/torrents/categories')
|
||||
.then(res => res.data)
|
||||
.then(data => Object.values(data))
|
||||
}
|
||||
|
||||
deleteCategory(categories) {
|
||||
return this.execute('post', '/torrents/removeCategories', {
|
||||
categories
|
||||
async deleteCategory(categories: string[]): Promise<void> {
|
||||
await this.execute('/torrents/removeCategories', {
|
||||
categories: categories.join('\n')
|
||||
})
|
||||
}
|
||||
|
||||
createCategory(cat) {
|
||||
return this.execute('post', '/torrents/createCategory', {
|
||||
async createCategory(cat: Category): Promise<void> {
|
||||
await this.execute('/torrents/createCategory', {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
})
|
||||
}
|
||||
|
||||
setCategory(hashes, category) {
|
||||
return this.torrentAction('setCategory', hashes, { category })
|
||||
async setCategory(hashes: string[], category: string): Promise<void> {
|
||||
await this.torrentAction('setCategory', hashes, { category })
|
||||
}
|
||||
|
||||
editCategory(cat) {
|
||||
async editCategory(cat: Category): Promise<void> {
|
||||
const params = {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/editCategory', params)
|
||||
await this.execute('/torrents/editCategory', params)
|
||||
}
|
||||
|
||||
/** Search **/
|
||||
getSearchPlugins() {
|
||||
async getSearchPlugins(): Promise<SearchPlugin[]> {
|
||||
return this.axios.get('/search/plugins').then(res => res.data)
|
||||
}
|
||||
|
||||
updateSearchPlugins() {
|
||||
return this.execute('post', '/search/updatePlugins')
|
||||
async updateSearchPlugins(): Promise<void> {
|
||||
await this.execute('/search/updatePlugins')
|
||||
}
|
||||
|
||||
enableSearchPlugin(plugins, enable) {
|
||||
async enableSearchPlugin(pluginNames: string[], enable: boolean): Promise<void> {
|
||||
const params = {
|
||||
names: plugins.join('|'),
|
||||
names: pluginNames.join('|'),
|
||||
enable
|
||||
}
|
||||
|
||||
return this.execute('post', '/search/enablePlugin', params)
|
||||
await this.execute('/search/enablePlugin', params)
|
||||
}
|
||||
|
||||
startSearch(pattern, plugins) {
|
||||
async startSearch(pattern: string, plugins: string[]): Promise<SearchJob> {
|
||||
const params = {
|
||||
pattern,
|
||||
plugins: Array.isArray(plugins) ? plugins.join('|') : 'all',
|
||||
plugins: plugins.length ? plugins.join('|') : 'enabled',
|
||||
category: 'all'
|
||||
}
|
||||
|
||||
return this.execute('post', '/search/start', params)
|
||||
return this.execute('/search/start', params)
|
||||
}
|
||||
|
||||
stopSearch(id) {
|
||||
return this.execute('post', '/search/stop', { id })
|
||||
async stopSearch(id: number): Promise<void> {
|
||||
await this.execute('/search/stop', { id })
|
||||
}
|
||||
|
||||
getSearchStatus(id) {
|
||||
return this.execute('post', '/search/status', { id })
|
||||
async getSearchStatus(id?: number): Promise<SearchStatus[]> {
|
||||
const params = id !== undefined ? { id } : undefined
|
||||
return this.execute('/search/status', params)
|
||||
}
|
||||
|
||||
getSearchResults(id) {
|
||||
return this.execute('post', '/search/results', {
|
||||
id
|
||||
async getSearchResults(id: number, limit?: number, offset?: number): Promise<SearchResultsResponse> {
|
||||
return this.execute('/search/results', {
|
||||
id,
|
||||
limit,
|
||||
offset
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import qbit from '../services/qbit'
|
||||
import { i18n } from '@/plugins/i18n'
|
||||
|
||||
export default {
|
||||
INIT_INTERVALS: async context => {
|
||||
context.state.intervals[0] = setInterval(() => {
|
||||
context.commit('updateMainData')
|
||||
}, 2000)
|
||||
},
|
||||
LOGIN: async (context, payload) => {
|
||||
const res = await qbit.login(payload)
|
||||
console.log(res)
|
||||
if (res === 'Ok.') {
|
||||
Vue.$toast.success(i18n.t('toast.loginSuccess'))
|
||||
context.commit('LOGIN', true)
|
||||
context.commit('updateMainData')
|
||||
await context.dispatch('FETCH_SETTINGS')
|
||||
context.commit('FETCH_CATEGORIES')
|
||||
context.commit('FETCH_TAGS')
|
||||
|
||||
return true
|
||||
}
|
||||
Vue.$toast.error(i18n.t('toast.loginFailed'))
|
||||
|
||||
return false
|
||||
},
|
||||
FETCH_SETTINGS: async context => {
|
||||
const { data } = await qbit.getAppPreferences()
|
||||
context.commit('FETCH_SETTINGS', data)
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
37
src/store/actions.ts
Normal file
37
src/store/actions.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import Vue from 'vue'
|
||||
import qbit from '@/services/qbit'
|
||||
import { i18n } from '@/plugins/i18n'
|
||||
import type { StoreState } from '@/types/vuetorrent'
|
||||
import type { Store } from 'vuex'
|
||||
import type { LoginPayload } from '@/types/qbit/payloads'
|
||||
|
||||
export default {
|
||||
INIT_INTERVALS: async (store: Store<StoreState>) => {
|
||||
store.state.intervals[0] = setInterval(() => {
|
||||
store.commit('updateMainData')
|
||||
}, 2000)
|
||||
},
|
||||
LOGIN: async (store: Store<StoreState>, payload: LoginPayload) => {
|
||||
const res = await qbit.login(payload)
|
||||
console.log(res)
|
||||
if (res === 'Ok.') {
|
||||
Vue.$toast.success(i18n.t('toast.loginSuccess').toString())
|
||||
store.commit('LOGIN', true)
|
||||
store.commit('updateMainData')
|
||||
await store.dispatch('FETCH_SETTINGS')
|
||||
store.commit('FETCH_CATEGORIES')
|
||||
store.commit('FETCH_TAGS')
|
||||
|
||||
return true
|
||||
}
|
||||
Vue.$toast.error(i18n.t('toast.loginFailed').toString())
|
||||
|
||||
return false
|
||||
},
|
||||
FETCH_SETTINGS: async (store: Store<StoreState>) => {
|
||||
const data = await qbit.getAppPreferences()
|
||||
store.commit('FETCH_SETTINGS', data)
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import { i18n } from '@/plugins/i18n'
|
||||
|
||||
export default {
|
||||
getAppVersion: state => () => state.version,
|
||||
containsTorrent: state => hash => state.selected_torrents.includes(hash),
|
||||
isDarkMode: state => () => state.webuiSettings.darkTheme,
|
||||
getTheme: state => () => state.webuiSettings.darkTheme ? 'dark' : 'light',
|
||||
getModalState: state => guid => state.modals.filter(m => m.guid === guid)[0],
|
||||
getSettings: state => () => state.settings,
|
||||
getStatus: state => () => state.status,
|
||||
getTorrent: state => hash => state.torrents.filter(el => el.hash === hash)[0],
|
||||
getWebuiSettings: state => () => state.webuiSettings,
|
||||
getAvailableTags: state => () => state.tags,
|
||||
getCategories: state => () => state.categories,
|
||||
getFeeds: state => () => state.rss.feeds,
|
||||
getRules: state => () => state.rss.rules,
|
||||
getModals: state => () => state.modals,
|
||||
getTorrents: state => () => state.torrents,
|
||||
getTrackers: state => () => state.trackers,
|
||||
getAuthenticated: state => () => state.authenticated,
|
||||
getTorrentCountString: state => () => {
|
||||
if (state.selected_torrents && state.selected_torrents.length) {
|
||||
return `${state.selected_torrents.length} ${i18n.t('of')} ${i18n.tc('navbar.torrentsCount', state.filteredTorrentsCount)}`
|
||||
}
|
||||
|
||||
return i18n.tc('navbar.torrentsCount', state.filteredTorrentsCount)
|
||||
},
|
||||
getSearchPlugins: state => () => state.searchPlugins
|
||||
}
|
30
src/store/getters.ts
Normal file
30
src/store/getters.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { i18n } from '@/plugins/i18n'
|
||||
import type { StoreState } from '@/types/vuetorrent'
|
||||
|
||||
export default {
|
||||
getAppVersion: (state: StoreState) => () => state.version,
|
||||
containsTorrent: (state: StoreState) => (hash: string) => state.selected_torrents.includes(hash),
|
||||
isDarkMode: (state: StoreState) => () => state.webuiSettings.darkTheme,
|
||||
getTheme: (state: StoreState) => () => state.webuiSettings.darkTheme ? 'dark' : 'light',
|
||||
getModalState: (state: StoreState) => (guid: string) => state.modals.filter(m => m.guid === guid)[0],
|
||||
getSettings: (state: StoreState) => () => state.settings,
|
||||
getStatus: (state: StoreState) => () => state.status,
|
||||
getTorrent: (state: StoreState) => (hash: string) => state.torrents.filter(el => el.hash === hash)[0],
|
||||
getWebuiSettings: (state: StoreState) => () => state.webuiSettings,
|
||||
getAvailableTags: (state: StoreState) => () => state.tags,
|
||||
getCategories: (state: StoreState) => () => state.categories,
|
||||
getFeeds: (state: StoreState) => () => state.rss.feeds,
|
||||
getRules: (state: StoreState) => () => state.rss.rules,
|
||||
getModals: (state: StoreState) => () => state.modals,
|
||||
getTorrents: (state: StoreState) => () => state.torrents,
|
||||
getTrackers: (state: StoreState) => () => state.trackers,
|
||||
getAuthenticated: (state: StoreState) => () => state.authenticated,
|
||||
getTorrentCountString: (state: StoreState) => () => {
|
||||
if (state.selected_torrents && state.selected_torrents.length) {
|
||||
return `${state.selected_torrents.length} ${i18n.t('of')} ${i18n.tc('navbar.torrentsCount', state.filteredTorrentsCount)}`
|
||||
}
|
||||
|
||||
return i18n.tc('navbar.torrentsCount', state.filteredTorrentsCount)
|
||||
},
|
||||
getSearchPlugins: (state: StoreState) => () => state.searchPlugins
|
||||
}
|
|
@ -4,8 +4,11 @@ import VuexPersist from 'vuex-persist'
|
|||
import actions from './actions'
|
||||
import getters from './getters'
|
||||
import mutations from './mutations'
|
||||
import type { StoreState } from '@/types/vuetorrent'
|
||||
import { Status } from '@/models'
|
||||
import {TitleOptions} from "@/enums/vuetorrent";
|
||||
|
||||
const vuexPersist = new VuexPersist({
|
||||
const vuexPersist = new VuexPersist<StoreState>({
|
||||
key: 'vuetorrent',
|
||||
storage: window.localStorage,
|
||||
reducer: state => ({
|
||||
|
@ -17,6 +20,7 @@ const vuexPersist = new VuexPersist({
|
|||
|
||||
Vue.use(Vuex)
|
||||
|
||||
// noinspection DuplicatedCode
|
||||
const propertiesTemplate = [
|
||||
{ name: 'Size', active: true },
|
||||
{ name: 'Progress', active: true },
|
||||
|
@ -40,32 +44,29 @@ const propertiesTemplate = [
|
|||
{ name: 'GlobalVolume', active: false }
|
||||
]
|
||||
|
||||
export default new Vuex.Store({
|
||||
export default new Vuex.Store<StoreState>({
|
||||
plugins: [vuexPersist.plugin],
|
||||
state: {
|
||||
version: 0,
|
||||
authenticated: false,
|
||||
categories: [],
|
||||
dashboard: {
|
||||
currentPage: 1,
|
||||
searchFilter: ''
|
||||
},
|
||||
download_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
filteredTorrentsCount: 0,
|
||||
intervals: [],
|
||||
latestSelectedTorrent: -1,
|
||||
modals: [],
|
||||
rid: 0,
|
||||
rss: {
|
||||
feeds: [],
|
||||
rules: []
|
||||
},
|
||||
status: {
|
||||
status: '',
|
||||
downloaded: '',
|
||||
uploaded: '',
|
||||
dlspeed: '',
|
||||
upspeed: '',
|
||||
freeDiskSpace: '',
|
||||
altSpeed: '',
|
||||
dlspeedRaw: '',
|
||||
upspeedRaw: '',
|
||||
tags: ''
|
||||
},
|
||||
upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
download_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
torrents: [],
|
||||
searchPlugins: [],
|
||||
selectMode: false,
|
||||
selected_torrents: [],
|
||||
authenticated: false,
|
||||
settings: null,
|
||||
sort_options: {
|
||||
isCustomSortEnabled: false,
|
||||
sort: 'priority',
|
||||
|
@ -76,10 +77,12 @@ export default new Vuex.Store({
|
|||
tag: null,
|
||||
tracker: null
|
||||
},
|
||||
rid: 0,
|
||||
pasteUrl: null,
|
||||
modals: [],
|
||||
settings: {},
|
||||
status: new Status(),
|
||||
tags: [],
|
||||
torrents: [],
|
||||
trackers: [],
|
||||
upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
version: '',
|
||||
webuiSettings: {
|
||||
lang: 'en',
|
||||
darkTheme: false,
|
||||
|
@ -91,27 +94,17 @@ export default new Vuex.Store({
|
|||
showTrackerFilter: false,
|
||||
showSpeedInTitle: false,
|
||||
deleteWithFiles: false,
|
||||
title: 'Default',
|
||||
title: TitleOptions.DEFAULT,
|
||||
rightDrawer: false,
|
||||
topPagination: false,
|
||||
paginationSize: 15,
|
||||
dateFormat: 'DD/MM/YYYY, HH:mm:ss',
|
||||
openSideBarOnStart: true,
|
||||
busyTorrentProperties: JSON.parse(JSON.stringify(propertiesTemplate)),
|
||||
doneTorrentProperties: JSON.parse(JSON.stringify(propertiesTemplate))
|
||||
},
|
||||
categories: [],
|
||||
trackers: [],
|
||||
tags: [],
|
||||
filteredTorrentsCount: 0,
|
||||
latestSelectedTorrent: null,
|
||||
selectMode: false,
|
||||
searchPlugins: [],
|
||||
dashboard: {
|
||||
currentPage: 1,
|
||||
searchFilter: ''
|
||||
busyTorrentProperties: [...propertiesTemplate],
|
||||
doneTorrentProperties: [...propertiesTemplate]
|
||||
}
|
||||
},
|
||||
// @ts-expect-error
|
||||
actions: {
|
||||
...actions
|
||||
},
|
|
@ -1,84 +0,0 @@
|
|||
import qbit from '../services/qbit'
|
||||
import { DocumentTitle, Tags, Trackers, Torrents, Graph, ServerStatus } from '@/actions'
|
||||
import { setLanguage } from '@/plugins/i18n'
|
||||
import Torrent from "@/models/Torrent";
|
||||
|
||||
export default {
|
||||
SET_APP_VERSION(state, version) {
|
||||
state.version = version
|
||||
},
|
||||
REMOVE_INTERVALS: state => {
|
||||
state.intervals.forEach(el => clearInterval(el))
|
||||
},
|
||||
ADD_MODAL(state, modal) {
|
||||
state.modals.push(modal)
|
||||
},
|
||||
DELETE_MODAL(state, guid) {
|
||||
state.modals = state.modals.filter(m => m.guid !== guid)
|
||||
},
|
||||
SET_SELECTED: (state, { type, hash, index }) => {
|
||||
if (type === 'add') {
|
||||
state.selected_torrents.push(hash)
|
||||
state.latestSelectedTorrent = state.torrents.map(t => t.hash).indexOf(hash)
|
||||
} else if (type === 'remove') {
|
||||
state.selected_torrents.splice(state.selected_torrents.indexOf(hash), 1)
|
||||
} else if (type === 'until') {
|
||||
let from
|
||||
let until
|
||||
if (state.latestSelectedTorrent > index) {
|
||||
from = index
|
||||
until = state.latestSelectedTorrent + 1 // include latest selected
|
||||
} else {
|
||||
from = state.latestSelectedTorrent
|
||||
until = index + 1
|
||||
}
|
||||
state.selected_torrents = state.torrents.map(t => t.hash).slice(from, until)
|
||||
}
|
||||
},
|
||||
RESET_SELECTED: state => {
|
||||
state.selected_torrents = []
|
||||
},
|
||||
TOGGLE_THEME(state) {
|
||||
state.webuiSettings.darkTheme = !state.webuiSettings.darkTheme
|
||||
},
|
||||
LOGOUT: async state => {
|
||||
await qbit.logout()
|
||||
state.authenticated = false
|
||||
},
|
||||
LOGIN: async (state, payload) => {
|
||||
state.authenticated = payload
|
||||
},
|
||||
updateMainData: async state => {
|
||||
const response = await qbit.getMainData(state.rid || undefined)
|
||||
state.rid = response.rid || undefined
|
||||
|
||||
ServerStatus.update(response)
|
||||
Tags.update(response)
|
||||
Graph.update()
|
||||
|
||||
// fetch torrent data
|
||||
state.sort_options.isCustomSortEnabled = Torrent.computedValues.indexOf(state.sort_options.sort) !== -1
|
||||
const { data } = await qbit.getTorrents(state.sort_options)
|
||||
|
||||
Trackers.update(data)
|
||||
Torrents.update(data)
|
||||
DocumentTitle.update()
|
||||
},
|
||||
FETCH_SETTINGS: async (state, settings) => {
|
||||
state.settings = settings
|
||||
},
|
||||
UPDATE_SORT_OPTIONS: (state, { hashes = [], filter = null, category = null, tag = null, tracker = null }) => {
|
||||
state.sort_options.hashes = hashes
|
||||
state.sort_options.filter = filter
|
||||
state.sort_options.category = category
|
||||
state.sort_options.tag = tag
|
||||
state.sort_options.tracker = tracker
|
||||
},
|
||||
FETCH_CATEGORIES: async state => (state.categories = Object.values(await qbit.getCategories())),
|
||||
FETCH_TAGS: async state => (state.tags = await qbit.getAvailableTags()),
|
||||
FETCH_FEEDS: async state => (state.rss.feeds = await qbit.getFeeds()),
|
||||
FETCH_RULES: async state => (state.rss.rules = await qbit.getRules()),
|
||||
FETCH_SEARCH_PLUGINS: async state => (state.searchPlugins = await qbit.getSearchPlugins()),
|
||||
SET_CURRENT_ITEM_COUNT: (state, count) => (state.filteredTorrentsCount = count),
|
||||
SET_LANGUAGE: async state => setLanguage(state.webuiSettings.lang)
|
||||
}
|
86
src/store/mutations.ts
Normal file
86
src/store/mutations.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import qbit from '../services/qbit'
|
||||
import { DocumentTitle, Tags, Trackers, Torrents, Graph, ServerStatus } from '@/actions'
|
||||
import { setLanguage } from '@/plugins/i18n'
|
||||
import type { ModalTemplate, StoreState } from '@/types/vuetorrent'
|
||||
import Torrent from '@/models/Torrent'
|
||||
import type { AppPreferences } from '@/types/qbit/models'
|
||||
|
||||
export default {
|
||||
SET_APP_VERSION(state: StoreState, version: string) {
|
||||
state.version = version
|
||||
},
|
||||
REMOVE_INTERVALS: (state: StoreState) => {
|
||||
state.intervals.forEach(el => clearInterval(el))
|
||||
},
|
||||
ADD_MODAL(state: StoreState, modal: ModalTemplate) {
|
||||
state.modals.push(modal)
|
||||
},
|
||||
DELETE_MODAL(state: StoreState, guid: string) {
|
||||
state.modals = state.modals.filter(m => m.guid !== guid)
|
||||
},
|
||||
SET_SELECTED: (state: StoreState, data: { type: string; hash: string; index: number }) => {
|
||||
const { type, hash, index } = data
|
||||
if (type === 'add') {
|
||||
state.selected_torrents.push(hash)
|
||||
state.latestSelectedTorrent = state.torrents.map(t => t.hash).indexOf(hash)
|
||||
} else if (type === 'remove') {
|
||||
state.selected_torrents.splice(state.selected_torrents.indexOf(hash), 1)
|
||||
} else if (type === 'until') {
|
||||
let from, until
|
||||
if (state.latestSelectedTorrent > index) {
|
||||
from = index
|
||||
until = state.latestSelectedTorrent + 1 // include latest selected
|
||||
} else {
|
||||
from = state.latestSelectedTorrent
|
||||
until = index + 1
|
||||
}
|
||||
state.selected_torrents = state.torrents.map(t => t.hash).slice(from, until)
|
||||
}
|
||||
},
|
||||
RESET_SELECTED: (state: StoreState) => {
|
||||
state.selected_torrents = []
|
||||
},
|
||||
TOGGLE_THEME(state: StoreState) {
|
||||
state.webuiSettings.darkTheme = !state.webuiSettings.darkTheme
|
||||
},
|
||||
LOGOUT: async (state: StoreState) => {
|
||||
await qbit.logout()
|
||||
state.authenticated = false
|
||||
},
|
||||
LOGIN: async (state: StoreState, payload: boolean) => {
|
||||
state.authenticated = payload
|
||||
},
|
||||
updateMainData: async (state: StoreState) => {
|
||||
const response = await qbit.getMainData(state.rid || undefined)
|
||||
state.rid = response.rid || undefined
|
||||
|
||||
ServerStatus.update(response.server_state)
|
||||
Tags.update(response)
|
||||
Graph.shiftValues()
|
||||
|
||||
// fetch torrent data
|
||||
state.sort_options.isCustomSortEnabled = Torrent.computedValues.indexOf(state.sort_options.sort) !== -1
|
||||
const data = await qbit.getTorrents(state.sort_options)
|
||||
|
||||
Trackers.update(data)
|
||||
Torrents.update(data)
|
||||
DocumentTitle.update()
|
||||
},
|
||||
FETCH_SETTINGS: async (state: StoreState, settings: AppPreferences) => {
|
||||
state.settings = settings
|
||||
},
|
||||
UPDATE_SORT_OPTIONS: (state: StoreState, { hashes = [], filter = null, category = null, tag = null, tracker = null }) => {
|
||||
state.sort_options.hashes = hashes
|
||||
state.sort_options.filter = filter
|
||||
state.sort_options.category = category
|
||||
state.sort_options.tag = tag
|
||||
state.sort_options.tracker = tracker
|
||||
},
|
||||
FETCH_CATEGORIES: async (state: StoreState) => (state.categories = Object.values(await qbit.getCategories())),
|
||||
FETCH_TAGS: async (state: StoreState) => (state.tags = await qbit.getAvailableTags()),
|
||||
FETCH_FEEDS: async (state: StoreState) => (state.rss.feeds = Object.entries(await qbit.getFeeds()).map(([key, value]) => ({ name: key, ...value }))),
|
||||
FETCH_RULES: async (state: StoreState) => (state.rss.rules = Object.entries(await qbit.getRules()).map(([key, value]) => ({ name: key, ...value }))),
|
||||
FETCH_SEARCH_PLUGINS: async (state: StoreState) => (state.searchPlugins = await qbit.getSearchPlugins()),
|
||||
SET_CURRENT_ITEM_COUNT: (state: StoreState, count: number) => (state.filteredTorrentsCount = count),
|
||||
SET_LANGUAGE: async (state: StoreState) => setLanguage(state.webuiSettings.lang)
|
||||
}
|
39
src/types/mappers/FeedRuleMapper.ts
Normal file
39
src/types/mappers/FeedRuleMapper.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import type {FeedRule as QbitFeedRule} from '@/types/qbit/models'
|
||||
import type {FeedRule as VtFeedRule} from '@/types/vuetorrent'
|
||||
import type {BaseMapper} from "@/types/mappers/index";
|
||||
|
||||
export default class FeedRuleMapper implements BaseMapper<QbitFeedRule, VtFeedRule> {
|
||||
public toQbit(rule: VtFeedRule): QbitFeedRule {
|
||||
return <QbitFeedRule>{
|
||||
addPaused: rule.addPaused,
|
||||
affectedFeeds: rule.affectedFeeds,
|
||||
assignedCategory: rule.assignedCategory,
|
||||
enabled: rule.enabled,
|
||||
episodeFilter: rule.episodeFilter,
|
||||
ignoreDays: rule.ignoreDays,
|
||||
lastMatch: rule.lastMatch,
|
||||
mustContain: rule.mustContain,
|
||||
mustNotContain: rule.mustNotContain,
|
||||
savePath: rule.savePath,
|
||||
smartFilter: rule.smartFilter,
|
||||
useRegex: rule.useRegex
|
||||
}
|
||||
}
|
||||
|
||||
public toVt(rule: QbitFeedRule): VtFeedRule {
|
||||
return <VtFeedRule>{
|
||||
addPaused: rule.addPaused,
|
||||
affectedFeeds: rule.affectedFeeds,
|
||||
assignedCategory: rule.assignedCategory,
|
||||
enabled: rule.enabled,
|
||||
episodeFilter: rule.episodeFilter,
|
||||
ignoreDays: rule.ignoreDays,
|
||||
lastMatch: rule.lastMatch,
|
||||
mustContain: rule.mustContain,
|
||||
mustNotContain: rule.mustNotContain,
|
||||
savePath: rule.savePath,
|
||||
smartFilter: rule.smartFilter,
|
||||
useRegex: rule.useRegex
|
||||
}
|
||||
}
|
||||
}
|
32
src/types/mappers/TrackerMapper.ts
Normal file
32
src/types/mappers/TrackerMapper.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import type {Tracker as QbitTracker} from '@/types/qbit/models'
|
||||
import type {Tracker as VtTracker} from '@/types/vuetorrent'
|
||||
import type {BaseMapper} from "@/types/mappers/index"
|
||||
|
||||
export default class TrackerMapper implements BaseMapper<QbitTracker, VtTracker> {
|
||||
public toVt(tracker: QbitTracker): VtTracker {
|
||||
return {
|
||||
isSelectable: tracker.tier >= 0,
|
||||
msg: tracker.msg,
|
||||
num_downloaded: tracker.num_downloaded,
|
||||
num_leeches: tracker.num_leeches,
|
||||
num_peers: tracker.num_peers,
|
||||
num_seeds: tracker.num_seeds,
|
||||
status: tracker.status,
|
||||
tier: tracker.tier,
|
||||
url: tracker.url
|
||||
}
|
||||
}
|
||||
|
||||
toQbit(tracker: VtTracker): QbitTracker {
|
||||
return {
|
||||
msg: tracker.msg,
|
||||
num_downloaded: tracker.num_downloaded,
|
||||
num_leeches: tracker.num_leeches,
|
||||
num_peers: tracker.num_peers,
|
||||
num_seeds: tracker.num_seeds,
|
||||
status: tracker.status,
|
||||
tier: tracker.tier,
|
||||
url: tracker.url
|
||||
}
|
||||
}
|
||||
}
|
9
src/types/mappers/index.ts
Normal file
9
src/types/mappers/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import FeedRuleMapper from "./FeedRuleMapper"
|
||||
import TrackerMapper from "./TrackerMapper"
|
||||
|
||||
export {FeedRuleMapper, TrackerMapper}
|
||||
|
||||
export interface BaseMapper<QbitType, VtType> {
|
||||
toVt(data: QbitType): VtType
|
||||
toQbit(data: VtType): QbitType
|
||||
}
|
309
src/types/qbit/models/AppPreferences.ts
Normal file
309
src/types/qbit/models/AppPreferences.ts
Normal file
|
@ -0,0 +1,309 @@
|
|||
import type {
|
||||
BitTorrentProtocol,
|
||||
DynDnsService,
|
||||
Encryption,
|
||||
MaxRatioAction,
|
||||
ProxyType,
|
||||
ScanDirs,
|
||||
SchedulerDays,
|
||||
UploadChokingAlgorithm,
|
||||
UploadSlotsBehavior,
|
||||
UtpTcpMixedMode
|
||||
} from '@/enums/qbit/AppPreferences'
|
||||
|
||||
export default interface AppPreferences {
|
||||
/** List of trackers to add to new torrent */
|
||||
add_trackers: string
|
||||
/** Enable automatic adding of trackers to new torrents */
|
||||
add_trackers_enabled: boolean
|
||||
/** Alternative global download speed limit in KiB/s */
|
||||
alt_dl_limit: number
|
||||
/** Alternative global upload speed limit in KiB/s */
|
||||
alt_up_limit: number
|
||||
/** True if an alternative WebUI should be used */
|
||||
alternative_webui_enabled: boolean
|
||||
/** File path to the alternative WebUI */
|
||||
alternative_webui_path: string
|
||||
announce_ip: string
|
||||
/** True always announce to all tiers */
|
||||
announce_to_all_tiers: boolean
|
||||
/** True always announce to all trackers in a tier */
|
||||
announce_to_all_trackers: boolean
|
||||
/** If true anonymous mode will be enabled; read more here; this option is only available in qBittorent built against libtorrent version 0.16.X and higher */
|
||||
anonymous_mode: boolean
|
||||
/** Number of asynchronous I/O threads */
|
||||
async_io_threads: number
|
||||
auto_delete_mode: number
|
||||
/** True if Automatic Torrent Management is enabled by default */
|
||||
auto_tmm_enabled: boolean
|
||||
/** True if external program should be run after torrent has finished downloading */
|
||||
autorun_enabled: boolean
|
||||
/** Program path/name/arguments to run if autorun_enabled is enabled; path is separated by slashes; you can use %f and %n arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively */
|
||||
autorun_program: string
|
||||
/** True if external program should be run after torrent has been added */
|
||||
autorun_on_torrent_added_enabled: boolean
|
||||
/** Program path/name/arguments to run if autorun_on_torrent_added_enabled is enabled; path is separated by slashes; you can use %f and %n arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively */
|
||||
autorun_on_torrent_added_program: string
|
||||
/** List of banned IPs */
|
||||
banned_IPs: string
|
||||
/** Bittorrent Protocol to use (see list of possible values below) */
|
||||
bittorrent_protocol: BitTorrentProtocol
|
||||
/** (White)list of ipv4/ipv6 subnets for which webui authentication should be bypassed; list entries are separated by commas */
|
||||
bypass_auth_subnet_whitelist: string
|
||||
/** True if webui authentication should be bypassed for clients whose ip resides within (at least) one of the subnets on the whitelist */
|
||||
bypass_auth_subnet_whitelist_enabled: boolean
|
||||
/** True if authentication challenge for loopback address (127.0.0.1) should be disabled */
|
||||
bypass_local_auth: boolean
|
||||
/** True if torrent should be relocated when its Category's save path changes */
|
||||
category_changed_tmm_enabled: boolean
|
||||
/** Outstanding memory when checking torrents in MiB */
|
||||
checking_memory_use: number
|
||||
/** True if a subfolder should be created when adding a torrent */
|
||||
create_subfolder_enabled: boolean
|
||||
/** IP Address to bind to. Empty String means All addresses */
|
||||
current_interface_address: string
|
||||
/** Network Interface used */
|
||||
current_network_interface: string
|
||||
/** True if DHT is enabled */
|
||||
dht: boolean
|
||||
/** Disk cache used in MiB */
|
||||
disk_cache: number
|
||||
/** Disk cache expiry interval in seconds */
|
||||
disk_cache_ttl: number
|
||||
/** Global download speed limit in KiB/s; -1 means no limit is applied */
|
||||
dl_limit: number
|
||||
/** If true torrents w/o any activity (stalled ones) will not be counted towards max_active_* limits; see dont_count_slow_torrents for more information */
|
||||
dont_count_slow_torrents: boolean
|
||||
/** Your DDNS domain name */
|
||||
dyndns_domain: string
|
||||
/** True if server DNS should be updated dynamically */
|
||||
dyndns_enabled: boolean
|
||||
/** Password for DDNS service */
|
||||
dyndns_password: string
|
||||
/** See list of possible values here below */
|
||||
dyndns_service: DynDnsService
|
||||
/** Username for DDNS service */
|
||||
dyndns_username: string
|
||||
/** Port used for embedded tracker */
|
||||
embedded_tracker_port: number
|
||||
/** True enables coalesce reads & writes */
|
||||
enable_coalesce_read_write: boolean
|
||||
/** True enables embedded tracker */
|
||||
enable_embedded_tracker: boolean
|
||||
/** True allows multiple connections from the same IP address */
|
||||
enable_multi_connections_from_same_ip: boolean
|
||||
/** True enables os cache */
|
||||
enable_os_cache: boolean
|
||||
/** True if the advanced libtorrent option piece_extent_affinity is enabled */
|
||||
enable_piece_extent_affinity: boolean
|
||||
/** True enables sending of upload piece suggestions */
|
||||
enable_upload_suggestions: boolean
|
||||
/** See list of possible values here below */
|
||||
encryption: Encryption
|
||||
/** Path to directory to copy .torrent files to. Slashes are used as path separators */
|
||||
export_dir: string
|
||||
/** Path to directory to copy .torrent files of completed downloads to. Slashes are used as path separators */
|
||||
export_dir_fin: string
|
||||
/** File pool size */
|
||||
file_pool_size: number
|
||||
/** True if ".!qB" should be appended to incomplete files */
|
||||
incomplete_files_ext: boolean
|
||||
/** True if external IP filter should be enabled */
|
||||
ip_filter_enabled: boolean
|
||||
/** Path to IP filter file (.dat, .p2p, .p2b files are supported); path is separated by slashes */
|
||||
ip_filter_path: string
|
||||
/** True if IP filters are applied to trackers */
|
||||
ip_filter_trackers: boolean
|
||||
/** True if [du]l_limit should be applied to peers on the LAN */
|
||||
limit_lan_peers: boolean
|
||||
/** True if [du]l_limit should be applied to estimated TCP overhead (service data: e.g. packet headers) */
|
||||
limit_tcp_overhead: boolean
|
||||
/** True if [du]l_limit should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher */
|
||||
limit_utp_rate: boolean
|
||||
/** Port for incoming connections */
|
||||
listen_port: number
|
||||
/** Currently selected language (e.g. en_GB for English) */
|
||||
locale: string
|
||||
/** True if LSD is enabled */
|
||||
lsd: boolean
|
||||
/** True if smtp server requires authentication */
|
||||
mail_notification_auth_enabled: boolean
|
||||
/** e-mail to send notifications to */
|
||||
mail_notification_email: string
|
||||
/** True if e-mail notification should be enabled */
|
||||
mail_notification_enabled: boolean
|
||||
/** Password for smtp authentication */
|
||||
mail_notification_password: string
|
||||
/** e-mail where notifications should originate from */
|
||||
mail_notification_sender: string
|
||||
/** smtp server for e-mail notifications */
|
||||
mail_notification_smtp: string
|
||||
/** True if smtp server requires SSL connection */
|
||||
mail_notification_ssl_enabled: boolean
|
||||
/** Username for smtp authentication */
|
||||
mail_notification_username: string
|
||||
/** Maximum number of active simultaneous downloads */
|
||||
max_active_downloads: number
|
||||
/** Maximum number of active simultaneous downloads and uploads */
|
||||
max_active_torrents: number
|
||||
/** Maximum number of active simultaneous uploads */
|
||||
max_active_uploads: number
|
||||
/** Maximum global number of simultaneous connections */
|
||||
max_connec: number
|
||||
/** Maximum number of simultaneous connections per torrent */
|
||||
max_connec_per_torrent: number
|
||||
/** Get the global share ratio limit */
|
||||
max_ratio: number
|
||||
/** Action performed when a torrent reaches the maximum share ratio. See list of possible values here below. */
|
||||
max_ratio_act: MaxRatioAction
|
||||
/** True if share ratio limit is enabled */
|
||||
max_ratio_enabled: boolean
|
||||
/** Number of minutes to seed a torrent */
|
||||
max_seeding_time: number
|
||||
/** True enables max seeding time */
|
||||
max_seeding_time_enabled: boolean
|
||||
/** Maximum number of upload slots */
|
||||
max_uploads: number
|
||||
/** Maximum number of upload slots per torrent */
|
||||
max_uploads_per_torrent: number
|
||||
/** Maximal outgoing port (0: Disabled) */
|
||||
outgoing_ports_max: number
|
||||
/** Minimal outgoing port (0: Disabled) */
|
||||
outgoing_ports_min: number
|
||||
/** True if PeX is enabled */
|
||||
pex: boolean
|
||||
/** True if disk space should be pre-allocated for all files */
|
||||
preallocate_all: boolean
|
||||
/** True proxy requires authentication; doesn't apply to SOCKS4 proxies */
|
||||
proxy_auth_enabled: boolean
|
||||
/** Proxy IP address or domain name */
|
||||
proxy_ip: string
|
||||
/** Password for proxy authentication */
|
||||
proxy_password: string
|
||||
/** True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher */
|
||||
proxy_peer_connections: boolean
|
||||
/** Proxy port */
|
||||
proxy_port: number
|
||||
/** True if proxy is only used for torrents */
|
||||
proxy_torrents_only: boolean
|
||||
/** See list of possible values here below */
|
||||
proxy_type: ProxyType
|
||||
/** Username for proxy authentication */
|
||||
proxy_username: string
|
||||
/** True if torrent queuing is enabled */
|
||||
queueing_enabled: boolean
|
||||
/** True if the port is randomly selected */
|
||||
random_port: boolean
|
||||
/** True rechecks torrents on completion */
|
||||
recheck_completed_torrents: boolean
|
||||
/** True resolves peer countries */
|
||||
resolve_peer_countries: boolean
|
||||
/** Enable auto-downloading of torrents from the RSS feeds */
|
||||
rss_auto_downloading_enabled: boolean
|
||||
/** For API ≥ v2.5.1: Enable downloading of repack/proper Episodes */
|
||||
rss_download_repack_proper_episodes: boolean
|
||||
/** Max stored articles per RSS feed */
|
||||
rss_max_articles_per_feed: number
|
||||
/** Enable processing of RSS feeds */
|
||||
rss_processing_enabled: boolean
|
||||
/** RSS refresh interval */
|
||||
rss_refresh_interval: number
|
||||
/** For API ≥ v2.5.1: List of RSS Smart Episode Filters */
|
||||
rss_smart_episode_filters: string
|
||||
/** Default save path for torrents, separated by slashes */
|
||||
save_path: string
|
||||
/** True if torrent should be relocated when the default save path changes */
|
||||
save_path_changed_tmm_enabled: boolean
|
||||
/** Save resume data interval in min */
|
||||
save_resume_data_interval: number
|
||||
/** Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified */
|
||||
scan_dirs: Record<string, ScanDirs>
|
||||
/** Scheduler starting hour */
|
||||
schedule_from_hour: number
|
||||
/** Scheduler starting minute */
|
||||
schedule_from_min: number
|
||||
/** Scheduler ending hour */
|
||||
schedule_to_hour: number
|
||||
/** Scheduler ending minute */
|
||||
schedule_to_min: number
|
||||
/** Scheduler days. See possible values here below */
|
||||
scheduler_days: SchedulerDays
|
||||
/** True if alternative limits should be applied according to schedule */
|
||||
scheduler_enabled: boolean
|
||||
/** Send buffer low watermark in KiB */
|
||||
send_buffer_low_watermark: number
|
||||
/** Send buffer watermark in KiB */
|
||||
send_buffer_watermark: number
|
||||
/** Send buffer watermark factor in percent */
|
||||
send_buffer_watermark_factor: number
|
||||
/** Download rate in KiB/s for a torrent to be considered "slow" */
|
||||
slow_torrent_dl_rate_threshold: number
|
||||
/** Seconds a torrent should be inactive before considered "slow" */
|
||||
slow_torrent_inactive_timer: number
|
||||
/** Upload rate in KiB/s for a torrent to be considered "slow" */
|
||||
slow_torrent_ul_rate_threshold: number
|
||||
/** Socket backlog size */
|
||||
socket_backlog_size: number
|
||||
/** For API < v2.0.1: SSL certificate contents (this is a not a path) */
|
||||
ssl_cert: string
|
||||
/** For API < v2.0.1: SSL keyfile contents (this is a not a path) */
|
||||
ssl_key: string
|
||||
/** True if torrents should be added in a Paused state */
|
||||
start_paused_enabled: boolean
|
||||
/** Timeout in seconds for a stopped announce request to trackers */
|
||||
stop_tracker_timeout: number
|
||||
/** Path for incomplete torrents, separated by slashes */
|
||||
temp_path: string
|
||||
/** True if folder for incomplete torrents is enabled */
|
||||
temp_path_enabled: boolean
|
||||
/** True if torrent should be relocated when its Category changes */
|
||||
torrent_changed_tmm_enabled: boolean
|
||||
/** Global upload speed limit in KiB/s; -1 means no limit is applied */
|
||||
up_limit: number
|
||||
/** Upload choking algorithm used (see list of possible values below) */
|
||||
upload_choking_algorithm: UploadChokingAlgorithm
|
||||
/** Upload slots behavior used (see list of possible values below) */
|
||||
upload_slots_behavior: UploadSlotsBehavior
|
||||
/** True if UPnP/NAT-PMP is enabled */
|
||||
upnp: boolean
|
||||
/** UPnP lease duration (0: Permanent lease) */
|
||||
upnp_lease_duration: number
|
||||
/** True if WebUI HTTPS access is enabled */
|
||||
use_https: boolean
|
||||
/** μTP-TCP mixed mode algorithm (see list of possible values below) */
|
||||
utp_tcp_mixed_mode: UtpTcpMixedMode
|
||||
/** IP address to use for the WebUI */
|
||||
web_ui_address: string
|
||||
/** WebUI access ban duration in seconds */
|
||||
web_ui_ban_duration: number
|
||||
/** True if WebUI clickjacking protection is enabled */
|
||||
web_ui_clickjacking_protection_enabled: boolean
|
||||
/** True if WebUI CSRF protection is enabled */
|
||||
web_ui_csrf_protection_enabled: boolean
|
||||
/** For API ≥ v2.5.1: List of custom http headers */
|
||||
web_ui_custom_http_headers: string
|
||||
/** Comma-separated list of domains to accept when performing Host header validation */
|
||||
web_ui_domain_list: string
|
||||
/** True if WebUI host header validation is enabled */
|
||||
web_ui_host_header_validation_enabled: boolean
|
||||
/** For API ≥ v2.0.1: Path to SSL certificate */
|
||||
web_ui_https_cert_path: string
|
||||
/** For API ≥ v2.0.1: Path to SSL keyfile */
|
||||
web_ui_https_key_path: string
|
||||
/** Maximum number of authentication failures before WebUI access ban */
|
||||
web_ui_max_auth_fail_count: number
|
||||
/** For API ≥ v2.3.0: Plaintext WebUI password, not readable, write-only. For API < v2.3.0: MD5 hash of WebUI password, hash is generated from the following string: username:Web UI Access:plain_text_web_ui_password */
|
||||
web_ui_password: string
|
||||
/** WebUI port */
|
||||
web_ui_port: number
|
||||
/** True if WebUI cookie Secure flag is enabled */
|
||||
web_ui_secure_cookie_enabled: boolean
|
||||
/** Seconds until WebUI is automatically signed off */
|
||||
web_ui_session_timeout: number
|
||||
/** True if UPnP is used for the WebUI port */
|
||||
web_ui_upnp: boolean
|
||||
/** For API ≥ v2.5.1: Enable custom http headers */
|
||||
web_ui_use_custom_http_headers_enabled: boolean
|
||||
/** WebUI username */
|
||||
web_ui_username: string
|
||||
}
|
4
src/types/qbit/models/Category.ts
Normal file
4
src/types/qbit/models/Category.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default interface Category {
|
||||
name: string
|
||||
savePath: string
|
||||
}
|
4
src/types/qbit/models/Feed.ts
Normal file
4
src/types/qbit/models/Feed.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default interface Feed {
|
||||
uid: string
|
||||
url: string
|
||||
}
|
29
src/types/qbit/models/FeedRule.ts
Normal file
29
src/types/qbit/models/FeedRule.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
export default interface FeedRule {
|
||||
/** Add matched torrent in paused mode */
|
||||
addPaused: boolean
|
||||
/** The feed URLs the rule applied to */
|
||||
affectedFeeds: string[]
|
||||
/** Assign category to the torrent */
|
||||
assignedCategory: string
|
||||
/** Whether the rule is enabled */
|
||||
enabled: boolean
|
||||
/** Episode filter definition */
|
||||
episodeFilter: string
|
||||
/** Ignore sunsequent rule matches */
|
||||
ignoreDays: number
|
||||
/** The rule last match time */
|
||||
lastMatch: string
|
||||
/** The substring that the torrent name must contain */
|
||||
mustContain: string
|
||||
/** The substring that the torrent name must not contain */
|
||||
mustNotContain: string
|
||||
/** The list of episode IDs already matched by smart filter */
|
||||
previouslyMatchedEpisodes?: unknown[]
|
||||
/** Save torrent to the given directory */
|
||||
savePath: string
|
||||
/** Enable smart episode filter */
|
||||
smartFilter: boolean
|
||||
torrentContentLayout?: unknown
|
||||
/** Enable regex mode in "mustContain" and "mustNotContain" */
|
||||
useRegex: boolean
|
||||
}
|
18
src/types/qbit/models/Peer.ts
Normal file
18
src/types/qbit/models/Peer.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
export default interface Peer {
|
||||
client: string
|
||||
connection: string
|
||||
country: string
|
||||
country_code: string
|
||||
dl_speed: number
|
||||
downloaded: number
|
||||
files: string
|
||||
flags: string
|
||||
flags_desc: string
|
||||
ip: string
|
||||
peer_id_client: string
|
||||
port: number
|
||||
progress: number
|
||||
relevance: number
|
||||
up_speed: number
|
||||
uploaded: number
|
||||
}
|
4
src/types/qbit/models/SearchJob.ts
Normal file
4
src/types/qbit/models/SearchJob.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default interface SearchJob {
|
||||
/** ID of the search job */
|
||||
id: number
|
||||
}
|
16
src/types/qbit/models/SearchPlugin.ts
Normal file
16
src/types/qbit/models/SearchPlugin.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
type PluginCategory = { id: string; name: string }
|
||||
|
||||
export default interface SearchPlugin {
|
||||
/** Whether the plugin is enabled */
|
||||
enabled: boolean
|
||||
/** Full name of the plugin */
|
||||
fullName: string
|
||||
/** Short name of the plugin */
|
||||
name: string
|
||||
/** List of category objects */
|
||||
supportedCategories: PluginCategory[]
|
||||
/** URL of the torrent site */
|
||||
url: string
|
||||
/** Installed version of the plugin */
|
||||
version: string
|
||||
}
|
16
src/types/qbit/models/SearchResult.ts
Normal file
16
src/types/qbit/models/SearchResult.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export default interface SearchResult {
|
||||
/** URL of the torrent's description page */
|
||||
descrLink: string
|
||||
/** Name of the file */
|
||||
fileName: string
|
||||
/** Size of the file in Bytes */
|
||||
fileSize: number
|
||||
/** Torrent download link (usually either .torrent file or magnet link) */
|
||||
fileUrl: string
|
||||
/** Number of leechers */
|
||||
nbLeechers: number
|
||||
/** Number of seeders */
|
||||
nbSeeders: number
|
||||
/** URL of the torrent site */
|
||||
siteUrl: string
|
||||
}
|
8
src/types/qbit/models/SearchStatus.ts
Normal file
8
src/types/qbit/models/SearchStatus.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export default interface SearchStatus {
|
||||
/** ID of the search job */
|
||||
id: number
|
||||
/** Current status of the search job (either Running or Stopped) */
|
||||
status: 'Running' | 'Stopped'
|
||||
/** Total number of results. If the status is Running this number may contineu to increase */
|
||||
total: number
|
||||
}
|
28
src/types/qbit/models/ServerState.ts
Normal file
28
src/types/qbit/models/ServerState.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import type { ConnectionStatus } from '@/enums/qbit'
|
||||
|
||||
export default interface ServerState {
|
||||
alltime_dl: number
|
||||
alltime_ul: number
|
||||
average_time_queue: number
|
||||
connection_status: ConnectionStatus
|
||||
dht_nodes: number
|
||||
dl_info_data: number
|
||||
dl_info_speed: number
|
||||
dl_rate_limit: number
|
||||
free_space_on_disk: number
|
||||
global_ratio: string
|
||||
queued_io_jobs: number
|
||||
queueing: boolean
|
||||
read_cache_hits: string
|
||||
read_cache_overload: string
|
||||
refresh_interval: number
|
||||
total_buffers_size: number
|
||||
total_peer_connections: number
|
||||
total_queued_size: number
|
||||
total_wasted_session: number
|
||||
up_info_data: number
|
||||
up_info_speed: number
|
||||
up_rate_limit: number
|
||||
use_alt_speed_limits: boolean
|
||||
write_cache_overload: string
|
||||
}
|
92
src/types/qbit/models/Torrent.ts
Normal file
92
src/types/qbit/models/Torrent.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
import type {TorrentState} from "@/enums/qbit";
|
||||
|
||||
export default interface Torrent {
|
||||
// Time (Unix Epoch) when the torrent was added to the client
|
||||
added_on: number
|
||||
// Amount of data left to download (bytes)
|
||||
amount_left: number
|
||||
// Whether this torrent is managed by Automatic Torrent Management
|
||||
auto_tmm: boolean
|
||||
// Percentage of file pieces currently available
|
||||
availability: number
|
||||
// Category of the torrent
|
||||
category: string
|
||||
// Amount of transfer data completed (bytes)
|
||||
completed: number
|
||||
// Time (Unix Epoch) when the torrent completed
|
||||
completion_on: number
|
||||
// Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents)
|
||||
content_path: string
|
||||
// Torrent download speed limit (bytes/s). -1 if ulimited.
|
||||
dl_limit: number
|
||||
// Torrent download speed (bytes/s)
|
||||
dlspeed: number
|
||||
// Amount of data downloaded
|
||||
downloaded: number
|
||||
// Amount of data downloaded this session
|
||||
downloaded_session: number
|
||||
// Torrent ETA (seconds)
|
||||
eta: number
|
||||
// True if first last piece are prioritized
|
||||
f_l_piece_prio: boolean
|
||||
// True if force start is enabled for this torrent
|
||||
force_start: boolean
|
||||
// Torrent hash
|
||||
hash: string
|
||||
// Last time (Unix Epoch) when a chunk was downloaded/uploaded
|
||||
last_activity: number
|
||||
// Magnet URI corresponding to this torrent
|
||||
magnet_uri: string
|
||||
// Maximum share ratio until torrent is stopped from seeding/uploading
|
||||
max_ratio: number
|
||||
// Maximum seeding time (seconds) until torrent is stopped from seeding
|
||||
max_seeding_time: number
|
||||
// Torrent name
|
||||
name: string
|
||||
// Number of seeds in the swarm
|
||||
num_complete: number
|
||||
// Number of leechers in the swarm
|
||||
num_incomplete: number
|
||||
// Number of leechers connected to
|
||||
num_leechs: number
|
||||
// Number of seeds connected to
|
||||
num_seeds: number
|
||||
// Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode
|
||||
priority: number
|
||||
// Torrent progress (percentage/100)
|
||||
progress: number
|
||||
// Torrent share ratio. Max ratio value: 9999.
|
||||
ratio: number
|
||||
ratio_limit: number
|
||||
// Path where this torrent's data is stored
|
||||
save_path: string
|
||||
// Torrent elapsed time while complete (seconds)
|
||||
seeding_time: number
|
||||
seeding_time_limit: number
|
||||
// Time (Unix Epoch) when this torrent was last seen complete
|
||||
seen_complete: number
|
||||
// True if sequential download is enabled
|
||||
seq_dl: boolean
|
||||
// Total size (bytes) of files selected for download
|
||||
size: number
|
||||
// Torrent state. See table here below for the possible values
|
||||
state: TorrentState
|
||||
// True if super seeding is enabled
|
||||
super_seeding: boolean
|
||||
// Comma-concatenated tag list of the torrent
|
||||
tags: string
|
||||
// Total active time (seconds)
|
||||
time_active: number
|
||||
// Total size (bytes) of all file in this torrent (including unselected ones)
|
||||
total_size: number
|
||||
// The first tracker with working status. Returns empty string if no tracker is working.
|
||||
tracker: string
|
||||
// Torrent upload speed limit (bytes/s). -1 if unlimited.
|
||||
up_limit: number
|
||||
// Amount of data uploaded
|
||||
uploaded: number
|
||||
// Amount of data uploaded this session
|
||||
uploaded_session: number
|
||||
// Torrent upload speed (bytes/s)
|
||||
upspeed: number
|
||||
}
|
20
src/types/qbit/models/TorrentFile.ts
Normal file
20
src/types/qbit/models/TorrentFile.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import type { Priority } from '@/enums/qbit'
|
||||
|
||||
export default interface TorrentFile {
|
||||
/** Percentage of file pieces currently available (percentage/100) */
|
||||
availability: number
|
||||
/** File index */
|
||||
index: number
|
||||
/** True if file is seeding/complete */
|
||||
is_seed: boolean
|
||||
/** File name (including relative path) */
|
||||
name: string
|
||||
/** The first number is the starting piece index and the second number is the ending piece index (inclusive) */
|
||||
piece_range: [number, number]
|
||||
/** File priority. See possible values here below */
|
||||
priority: Priority
|
||||
/** File progress (percentage/100) */
|
||||
progress: number
|
||||
/** File size (bytes) */
|
||||
size: number
|
||||
}
|
68
src/types/qbit/models/TorrentProperties.ts
Normal file
68
src/types/qbit/models/TorrentProperties.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
export default interface TorrentProperties {
|
||||
/** When this torrent was added (unix timestamp) */
|
||||
addition_date: number
|
||||
/** Torrent comment */
|
||||
comment: string
|
||||
/** Torrent completion date (unix timestamp) */
|
||||
completion_date: number
|
||||
/** Torrent creator */
|
||||
created_by: string
|
||||
/** Torrent creation date (Unix timestamp) */
|
||||
creation_date: number
|
||||
/** Torrent download limit (bytes/s) */
|
||||
dl_limit: number
|
||||
/** Torrent download speed (bytes/second) */
|
||||
dl_speed: number
|
||||
/** Torrent average download speed (bytes/second) */
|
||||
dl_speed_avg: number
|
||||
/** Torrent ETA (seconds) */
|
||||
eta: number
|
||||
/** Last seen complete date (unix timestamp) */
|
||||
last_seen: number
|
||||
/** Torrent connection count */
|
||||
nb_connections: number
|
||||
/** Torrent connection count limit */
|
||||
nb_connections_limit: number
|
||||
/** Number of peers connected to */
|
||||
peers: number
|
||||
/** Number of peers in the swarm */
|
||||
peers_total: number
|
||||
/** Torrent piece size (bytes) */
|
||||
piece_size: number
|
||||
/** Number of pieces owned */
|
||||
pieces_have: number
|
||||
/** Number of pieces of the torrent */
|
||||
pieces_num: number
|
||||
/** Number of seconds until the next announce */
|
||||
reannounce: number
|
||||
/** Torrent save path */
|
||||
save_path: string
|
||||
/** Torrent elapsed time while complete (seconds) */
|
||||
seeding_time: number
|
||||
/** Number of seeds connected to */
|
||||
seeds: number
|
||||
/** Number of seeds in the swarm */
|
||||
seeds_total: number
|
||||
/** Torrent share ratio */
|
||||
share_ratio: number
|
||||
/** Torrent elapsed time (seconds) */
|
||||
time_elapsed: number
|
||||
/** Total data downloaded for torrent (bytes) */
|
||||
total_downloaded: number
|
||||
/** Total data downloaded this session (bytes) */
|
||||
total_downloaded_session: number
|
||||
/** Torrent total size (bytes) */
|
||||
total_size: number
|
||||
/** Total data uploaded for torrent (bytes) */
|
||||
total_uploaded: number
|
||||
/** Total data uploaded this session (bytes) */
|
||||
total_uploaded_session: number
|
||||
/** Total data wasted for torrent (bytes) */
|
||||
total_wasted: number
|
||||
/** Torrent upload limit (bytes/s) */
|
||||
up_limit: number
|
||||
/** Torrent upload speed (bytes/second) */
|
||||
up_speed: number
|
||||
/** Torrent average upload speed (bytes/second) */
|
||||
up_speed_avg: number
|
||||
}
|
20
src/types/qbit/models/Tracker.ts
Normal file
20
src/types/qbit/models/Tracker.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import type { TrackerStatus } from '@/enums/qbit'
|
||||
|
||||
export default interface Tracker {
|
||||
/** Tracker message (there is no way of knowing what this message is - it's up to tracker admins) */
|
||||
msg: string
|
||||
/** Number of completed downlods for current torrent, as reported by the tracker */
|
||||
num_downloaded: number
|
||||
/** Number of leeches for current torrent, as reported by the tracker */
|
||||
num_leeches: number
|
||||
/** Number of peers for current torrent, as reported by the tracker */
|
||||
num_peers: number
|
||||
/** Number of seeds for current torrent, asreported by the tracker */
|
||||
num_seeds: number
|
||||
/** Tracker status. See the table below for possible values */
|
||||
status: TrackerStatus
|
||||
/** Tracker priority tier. Lower tier trackers are tried before higher tiers. Tier numbers are valid when >= 0, < 0 is used as placeholder when tier does not exist for special entries (such as DHT). */
|
||||
tier: number
|
||||
/** Tracker url */
|
||||
url: string
|
||||
}
|
34
src/types/qbit/models/index.ts
Normal file
34
src/types/qbit/models/index.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import type AppPreferences from './AppPreferences'
|
||||
import type Category from './Category'
|
||||
import type ServerState from './ServerState'
|
||||
import type Torrent from './Torrent'
|
||||
import type Tracker from './Tracker'
|
||||
import type Peer from './Peer'
|
||||
import type TorrentFile from './TorrentFile'
|
||||
import type TorrentProperties from './TorrentProperties'
|
||||
import type FeedRule from './FeedRule'
|
||||
import type Feed from './Feed'
|
||||
import type SearchPlugin from './SearchPlugin'
|
||||
import type SearchJob from './SearchJob'
|
||||
import type SearchStatus from './SearchStatus'
|
||||
import type SearchResult from './SearchResult'
|
||||
|
||||
type ApplicationVersion = string
|
||||
|
||||
export type {
|
||||
ApplicationVersion,
|
||||
AppPreferences,
|
||||
Category,
|
||||
ServerState,
|
||||
Tracker,
|
||||
Torrent,
|
||||
Peer,
|
||||
TorrentFile,
|
||||
TorrentProperties,
|
||||
FeedRule,
|
||||
Feed,
|
||||
SearchPlugin,
|
||||
SearchJob,
|
||||
SearchStatus,
|
||||
SearchResult
|
||||
}
|
36
src/types/qbit/payloads/AddTorrentPayload.ts
Normal file
36
src/types/qbit/payloads/AddTorrentPayload.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import type { BasePayload } from '.'
|
||||
|
||||
export default interface AddTorrentPayload extends BasePayload {
|
||||
/** Whether Automatic Torrent Management should be used */
|
||||
autoTMM?: boolean
|
||||
/** Category for the torrent */
|
||||
category?: string
|
||||
/** Cookie sent to download the .torrent file */
|
||||
cookie?: string
|
||||
/** Set torrent download speed limit. Unit in bytes/second */
|
||||
dlLimit?: number
|
||||
/** Prioritize download first last piece. Possible values are true, false (default) */
|
||||
firstLastPiecePrio?: boolean
|
||||
/** Add torrents in the paused state. Possible values are true, false (default) */
|
||||
paused?: boolean
|
||||
/** Set torrent share ratio limit */
|
||||
ratioLimit?: number
|
||||
/** Rename torrent */
|
||||
rename?: string
|
||||
/** Create the root folder. Possible values are true, false, unset (default) */
|
||||
root_folder?: boolean
|
||||
/** Download folder */
|
||||
savepath?: string
|
||||
/** Set torrent seeding time limit. Unit in minutes */
|
||||
seedingTimeLimit?: number
|
||||
/** Enable sequential download. Possible values are true, false (default) */
|
||||
sequentialDownload?: boolean
|
||||
/** Skip hash checking. Possible values are true, false (default) */
|
||||
skip_checking?: boolean
|
||||
/** Tags for the torrent, split by ',' */
|
||||
tags?: string
|
||||
/** Set torrent upload speed limit. Unit in bytes/second */
|
||||
upLimit?: number
|
||||
/** URLs separated with newlines */
|
||||
urls?: string
|
||||
}
|
3
src/types/qbit/payloads/AppPreferencesPayload.ts
Normal file
3
src/types/qbit/payloads/AppPreferencesPayload.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import type { AppPreferences } from '@/types/qbit/models'
|
||||
|
||||
export type AppPreferencesPayload = Partial<AppPreferences>
|
1
src/types/qbit/payloads/BasePayload.ts
Normal file
1
src/types/qbit/payloads/BasePayload.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export default interface BasePayload extends Record<string, any> {}
|
8
src/types/qbit/payloads/LoginPayload.ts
Normal file
8
src/types/qbit/payloads/LoginPayload.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import type { BasePayload } from '.'
|
||||
|
||||
export default interface LoginPayload extends BasePayload {
|
||||
/** Username used to access the WebUI */
|
||||
username: string
|
||||
/** Password used to access the WebUI */
|
||||
password: string
|
||||
}
|
7
src/types/qbit/payloads/PeerLogPayload.ts
Normal file
7
src/types/qbit/payloads/PeerLogPayload.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import type { Optional } from '@/global'
|
||||
import type { BasePayload } from '.'
|
||||
|
||||
export default interface PeerLogPayload extends BasePayload {
|
||||
// Exclude messages with "message id" <= last_known_id (default: -1)
|
||||
last_known_id: Optional<number>
|
||||
}
|
7
src/types/qbit/payloads/index.ts
Normal file
7
src/types/qbit/payloads/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import type LoginPayload from './LoginPayload'
|
||||
import type AddTorrentPayload from './AddTorrentPayload'
|
||||
import type PeerLogPayload from './PeerLogPayload'
|
||||
import type { AppPreferencesPayload } from './AppPreferencesPayload'
|
||||
import type BasePayload from './BasePayload'
|
||||
|
||||
export { AppPreferencesPayload, LoginPayload, AddTorrentPayload, PeerLogPayload, BasePayload }
|
17
src/types/qbit/responses/MainDataResponse.ts
Normal file
17
src/types/qbit/responses/MainDataResponse.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import type { Category, Torrent, ServerState } from '@/types/qbit/models'
|
||||
import type { Optional } from '@/global'
|
||||
|
||||
export default interface MainDataResponse {
|
||||
// Response ID
|
||||
rid: number
|
||||
// Whether the response contains all the data or partial data
|
||||
fullUpdate: Optional<boolean>
|
||||
torrents: Optional<Record<string, Torrent>>
|
||||
torrents_removed: Optional<string[]>
|
||||
categories: Optional<Record<string, Category>>
|
||||
categories_removed: Optional<Category[]>
|
||||
tags: Optional<string[]>
|
||||
tags_removed: Optional<string[]>
|
||||
trackers: Optional<Record<string, string[]>>
|
||||
server_state?: Optional<ServerState>
|
||||
}
|
14
src/types/qbit/responses/PeerLogResponse.ts
Normal file
14
src/types/qbit/responses/PeerLogResponse.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
interface PeerLog {
|
||||
// ID of the peer
|
||||
id: number
|
||||
// IP of the peer
|
||||
ip: string
|
||||
// Milliseconds since epoch
|
||||
timestamp: number
|
||||
// Whether or not the peer was blocked
|
||||
blocked: boolean
|
||||
// Reason of the block
|
||||
reason: string
|
||||
}
|
||||
|
||||
export type PeerLogResponse = PeerLog[]
|
10
src/types/qbit/responses/SearchResultsResponse.ts
Normal file
10
src/types/qbit/responses/SearchResultsResponse.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import type { SearchResult } from '@/types/qbit/models'
|
||||
|
||||
export default interface SearchResultsResponse {
|
||||
/** Array of result objects- see table below */
|
||||
results: SearchResult[]
|
||||
/** Current status of the search job (either Running or Stopped) */
|
||||
status: 'Running' | 'Stopped'
|
||||
/** Total number of results. If the status is Running this number may continue to increase */
|
||||
total: number
|
||||
}
|
8
src/types/qbit/responses/TorrentPeersResponse.ts
Normal file
8
src/types/qbit/responses/TorrentPeersResponse.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import type { Peer } from '@/types/qbit/models'
|
||||
|
||||
export default interface TorrentPeersResponse {
|
||||
full_update?: boolean
|
||||
rid: number
|
||||
peers: Record<string, Peer>
|
||||
show_flags: boolean
|
||||
}
|
6
src/types/qbit/responses/index.ts
Normal file
6
src/types/qbit/responses/index.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import type MainDataResponse from './MainDataResponse'
|
||||
import type { PeerLogResponse } from './PeerLogResponse'
|
||||
import type TorrentPeersResponse from './TorrentPeersResponse'
|
||||
import type SearchResultsResponse from './SearchResultsResponse'
|
||||
|
||||
export { MainDataResponse, PeerLogResponse, TorrentPeersResponse, SearchResultsResponse }
|
4
src/types/vuetorrent/Category.ts
Normal file
4
src/types/vuetorrent/Category.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default interface Category {
|
||||
name: string
|
||||
savePath: string
|
||||
}
|
5
src/types/vuetorrent/ModalTemplate.ts
Normal file
5
src/types/vuetorrent/ModalTemplate.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default interface ModalTemplate {
|
||||
component: string
|
||||
props: Object
|
||||
guid: string
|
||||
}
|
13
src/types/vuetorrent/SortOptions.ts
Normal file
13
src/types/vuetorrent/SortOptions.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import type { Optional } from '@/global'
|
||||
import type {TorrentState} from "@/enums/vuetorrent"
|
||||
|
||||
export default interface SortOptions {
|
||||
isCustomSortEnabled: boolean
|
||||
sort: string
|
||||
reverse: boolean
|
||||
hashes: string[]
|
||||
filter: Optional<TorrentState>
|
||||
category: Optional<string>
|
||||
tag: Optional<string>
|
||||
tracker: Optional<string>
|
||||
}
|
42
src/types/vuetorrent/StoreState.ts
Normal file
42
src/types/vuetorrent/StoreState.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import type Feed from './rss/Feed'
|
||||
import type FeedRule from './rss/FeedRule'
|
||||
import type Category from '../qbit/models/Category'
|
||||
import type Torrent from '@/models/Torrent'
|
||||
import type SortOptions from './SortOptions'
|
||||
import type { AppPreferences } from '../qbit/models'
|
||||
import type { Optional } from '@/global'
|
||||
import type ModalTemplate from './ModalTemplate'
|
||||
import type {Status} from '@/models'
|
||||
import type WebUISettings from '@/types/vuetorrent/WebUISettings'
|
||||
import type {SearchPlugin} from "@/types/qbit/models";
|
||||
|
||||
export default interface StoreState {
|
||||
authenticated: boolean
|
||||
categories: Category[]
|
||||
dashboard: {
|
||||
currentPage: number
|
||||
searchFilter: string
|
||||
}
|
||||
download_data: number[]
|
||||
filteredTorrentsCount: number
|
||||
intervals: NodeJS.Timer[]
|
||||
latestSelectedTorrent: number
|
||||
modals: ModalTemplate[]
|
||||
rid?: number
|
||||
rss: {
|
||||
feeds: Feed[]
|
||||
rules: FeedRule[]
|
||||
}
|
||||
searchPlugins: SearchPlugin[]
|
||||
selectMode: boolean
|
||||
selected_torrents: string[]
|
||||
settings: Optional<AppPreferences>
|
||||
sort_options: SortOptions
|
||||
status: Status
|
||||
tags: string[]
|
||||
torrents: Torrent[]
|
||||
trackers: string[]
|
||||
upload_data: number[]
|
||||
version: string
|
||||
webuiSettings: WebUISettings
|
||||
}
|
5
src/types/vuetorrent/Tracker.ts
Normal file
5
src/types/vuetorrent/Tracker.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import type {Tracker as QbitTracker} from '../qbit/models'
|
||||
|
||||
export default interface Tracker extends QbitTracker {
|
||||
isSelectable: boolean
|
||||
}
|
17
src/types/vuetorrent/TreeObjects.ts
Normal file
17
src/types/vuetorrent/TreeObjects.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
export interface TreeNode {
|
||||
name: string
|
||||
fullName: string
|
||||
children: TreeNode[]
|
||||
}
|
||||
|
||||
export interface TreeFile extends TreeNode {
|
||||
id: number
|
||||
progress: number
|
||||
size: string
|
||||
icon: string
|
||||
priority: number
|
||||
}
|
||||
|
||||
export interface TreeFolder extends TreeNode {
|
||||
type: 'directory'
|
||||
}
|
31
src/types/vuetorrent/WebUISettings.ts
Normal file
31
src/types/vuetorrent/WebUISettings.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import type {TitleOptions} from "@/enums/vuetorrent"
|
||||
|
||||
export interface TorrentProperty {
|
||||
name: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export interface TorrentPropertyLocalized extends TorrentProperty {
|
||||
label: string
|
||||
}
|
||||
|
||||
export default interface WebUISettings {
|
||||
lang: string
|
||||
darkTheme: boolean
|
||||
showFreeSpace: boolean
|
||||
showSpeedGraph: boolean
|
||||
showSessionStat: boolean
|
||||
showAlltimeStat: boolean
|
||||
showCurrentSpeed: boolean
|
||||
showTrackerFilter: boolean
|
||||
showSpeedInTitle: boolean
|
||||
deleteWithFiles: boolean
|
||||
title: TitleOptions
|
||||
rightDrawer: boolean
|
||||
topPagination: boolean
|
||||
paginationSize: number
|
||||
dateFormat: string
|
||||
openSideBarOnStart: boolean
|
||||
busyTorrentProperties: TorrentProperty[]
|
||||
doneTorrentProperties: TorrentProperty[]
|
||||
}
|
15
src/types/vuetorrent/index.ts
Normal file
15
src/types/vuetorrent/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import type Category from "./Category"
|
||||
import type Feed from './rss/Feed'
|
||||
import type FeedRule from './rss/FeedRule'
|
||||
import type SearchStatus from "./search/SearchStatus"
|
||||
import type SearchResult from "./search/SearchResult"
|
||||
import type ModalTemplate from './ModalTemplate'
|
||||
import type SortOptions from './SortOptions'
|
||||
import type StoreState from './StoreState'
|
||||
import type { TreeNode, TreeFile, TreeFolder } from './TreeObjects'
|
||||
import type {TorrentProperty, TorrentPropertyLocalized} from "@/types/vuetorrent/WebUISettings"
|
||||
import type Tracker from './Tracker'
|
||||
|
||||
export type ComponentRule = (value: string) => boolean|string
|
||||
|
||||
export { Category, Feed, FeedRule, SearchStatus, SearchResult, ModalTemplate, SortOptions, StoreState, TreeNode, TreeFile, TreeFolder, TorrentProperty, TorrentPropertyLocalized, Tracker }
|
5
src/types/vuetorrent/rss/Feed.ts
Normal file
5
src/types/vuetorrent/rss/Feed.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default interface Feed {
|
||||
name: string
|
||||
uid?: string
|
||||
url: string
|
||||
}
|
16
src/types/vuetorrent/rss/FeedRule.ts
Normal file
16
src/types/vuetorrent/rss/FeedRule.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export default interface FeedRule {
|
||||
addPaused?: boolean
|
||||
affectedFeeds?: string[]
|
||||
assignedCategory?: string
|
||||
enabled: boolean
|
||||
episodeFilter?: string
|
||||
ignoreDays?: number
|
||||
lastMatch?: string
|
||||
mustContain?: string
|
||||
mustNotContain?: string
|
||||
name: string
|
||||
previouslyMatchedEpisodes?: unknown[]
|
||||
savePath?: string
|
||||
smartFilter?: boolean
|
||||
useRegex?: boolean
|
||||
}
|
9
src/types/vuetorrent/search/SearchResult.ts
Normal file
9
src/types/vuetorrent/search/SearchResult.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export default interface SearchResult {
|
||||
descrLink: string
|
||||
fileName: string
|
||||
fileSize: number
|
||||
fileUrl: string
|
||||
nbLeechers: number
|
||||
nbSeeders: number
|
||||
siteUrl: string
|
||||
}
|
9
src/types/vuetorrent/search/SearchStatus.ts
Normal file
9
src/types/vuetorrent/search/SearchStatus.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import type {SearchResult} from "..";
|
||||
import type {Optional} from "@/global";
|
||||
|
||||
export default interface SearchStatus {
|
||||
id: number
|
||||
status: 'Running' | 'Stopped'
|
||||
interval: Optional<NodeJS.Timer>
|
||||
results: SearchResult[]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue