mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-03-14 12:10:18 +03:00
perf(stores): Rework store structure to prevent circular imports (#1325)
This commit is contained in:
parent
f69851cc39
commit
179af5a1d6
88 changed files with 949 additions and 712 deletions
16
package-lock.json
generated
16
package-lock.json
generated
|
@ -8,9 +8,11 @@
|
|||
"name": "vuetorrent",
|
||||
"version": "2.1.1",
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^8.3.1",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"@fontsource/roboto-mono": "^5.0.14",
|
||||
"@mdi/js": "^7.2.96",
|
||||
"@mdi/font": "^7.3.67",
|
||||
"@mdi/js": "^7.3.67",
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"apexcharts": "^3.44.0",
|
||||
"axios": "^1.6.1",
|
||||
|
@ -29,8 +31,6 @@
|
|||
"vuetify": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.1.0",
|
||||
"@mdi/font": "^7.2.96",
|
||||
"@pinia/testing": "^0.1.3",
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "^20.9.0",
|
||||
|
@ -2196,10 +2196,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@faker-js/faker": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.2.0.tgz",
|
||||
"integrity": "sha512-VacmzZqVxdWdf9y64lDOMZNDMM/FQdtM9IsaOPKOm2suYwEatb8VkdHqOzXcDnZbk7YDE2BmsJmy/2Hmkn563g==",
|
||||
"dev": true,
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.3.1.tgz",
|
||||
"integrity": "sha512-FdgpFxY6V6rLZE9mmIBb9hM0xpfvQOSNOLnzolzKwsE1DH+gC7lEKV1p1IbR0lAYyvYd5a4u3qWJzowUkw1bIw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -2371,8 +2370,7 @@
|
|||
"node_modules/@mdi/font": {
|
||||
"version": "7.3.67",
|
||||
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.3.67.tgz",
|
||||
"integrity": "sha512-SWxvzRbUQRfewlIV+OF4/YF4DkeTjMWoT8Hh9yeU/5UBVdJZj9Uf4a9+cXjknSIhIaMxZ/4N1O/s7ojApOOGjg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-SWxvzRbUQRfewlIV+OF4/YF4DkeTjMWoT8Hh9yeU/5UBVdJZj9Uf4a9+cXjknSIhIaMxZ/4N1O/s7ojApOOGjg=="
|
||||
},
|
||||
"node_modules/@mdi/js": {
|
||||
"version": "7.3.67",
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
"coverage": "vitest run --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^8.3.1",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"@fontsource/roboto-mono": "^5.0.14",
|
||||
"@mdi/js": "^7.2.96",
|
||||
"@mdi/font": "^7.3.67",
|
||||
"@mdi/js": "^7.3.67",
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"apexcharts": "^3.44.0",
|
||||
"axios": "^1.6.1",
|
||||
|
@ -35,8 +37,6 @@
|
|||
"vuetify": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.1.0",
|
||||
"@mdi/font": "^7.2.96",
|
||||
"@pinia/testing": "^0.1.3",
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "^20.9.0",
|
||||
|
|
64
src/App.vue
64
src/App.vue
|
@ -2,16 +2,22 @@
|
|||
import AddPanel from '@/components/AddPanel.vue'
|
||||
import DnDZone from '@/components/DnDZone.vue'
|
||||
import Navbar from '@/components/Navbar/Navbar.vue'
|
||||
import { useAddTorrentStore } from '@/stores/addTorrents'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useLogStore } from '@/stores/logs'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { TitleOptions } from '@/constants/vuetorrent'
|
||||
import { formatPercent, formatSpeed } from '@/helpers'
|
||||
import {
|
||||
useAddTorrentStore,
|
||||
useAppStore,
|
||||
useAuthStore,
|
||||
useDialogStore,
|
||||
useLogStore,
|
||||
useMaindataStore,
|
||||
usePreferenceStore,
|
||||
useTorrentStore,
|
||||
useVueTorrentStore
|
||||
} from '@/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { onBeforeMount, watch } from 'vue'
|
||||
import { onBeforeMount, watch, watchEffect } from 'vue'
|
||||
|
||||
const addTorrentStore = useAddTorrentStore()
|
||||
const appStore = useAppStore()
|
||||
|
@ -19,8 +25,11 @@ const authStore = useAuthStore()
|
|||
const dialogStore = useDialogStore()
|
||||
const logStore = useLogStore()
|
||||
const maindataStore = useMaindataStore()
|
||||
const { serverState } = storeToRefs(maindataStore)
|
||||
const { torrents } = storeToRefs(useTorrentStore())
|
||||
const preferencesStore = usePreferenceStore()
|
||||
const vuetorrentStore = useVueTorrentStore()
|
||||
const { language, matchSystemTheme, uiTitleCustom, uiTitleType, useBitSpeed } = storeToRefs(vuetorrentStore)
|
||||
|
||||
const checkAuthentication = async () => {
|
||||
await authStore.updateAuthStatus()
|
||||
|
@ -43,12 +52,12 @@ const blockContextMenu = () => {
|
|||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (vuetorrentStore.matchSystemTheme) {
|
||||
if (matchSystemTheme.value) {
|
||||
vuetorrentStore.updateSystemTheme()
|
||||
} else {
|
||||
vuetorrentStore.updateTheme()
|
||||
}
|
||||
vuetorrentStore.setLanguage(vuetorrentStore.language)
|
||||
vuetorrentStore.setLanguage(language.value)
|
||||
checkAuthentication()
|
||||
blockContextMenu()
|
||||
})
|
||||
|
@ -72,6 +81,39 @@ watch(
|
|||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
watchEffect(() => {
|
||||
const mode = uiTitleType.value
|
||||
switch (mode) {
|
||||
case TitleOptions.GLOBAL_SPEED:
|
||||
document.title =
|
||||
'[' +
|
||||
`D: ${ formatSpeed(serverState.value?.dl_info_speed ?? 0, useBitSpeed.value) }, ` +
|
||||
`U: ${ formatSpeed(serverState.value?.up_info_speed ?? 0, useBitSpeed.value) }` +
|
||||
'] VueTorrent'
|
||||
break
|
||||
case TitleOptions.FIRST_TORRENT_STATUS:
|
||||
const torrent = torrents.value.at(0)
|
||||
if (torrent) {
|
||||
document.title =
|
||||
'[' +
|
||||
`D: ${ formatSpeed(torrent.dlspeed, useBitSpeed.value) }, ` +
|
||||
`U: ${ formatSpeed(torrent.upspeed, useBitSpeed.value) }, ` +
|
||||
`${ formatPercent(torrent.progress) }` +
|
||||
'] VueTorrent'
|
||||
} else {
|
||||
document.title = '[N/A] VueTorrent'
|
||||
}
|
||||
break
|
||||
case TitleOptions.CUSTOM:
|
||||
document.title = uiTitleCustom.value
|
||||
break
|
||||
case TitleOptions.DEFAULT:
|
||||
default:
|
||||
document.title = 'VueTorrent'
|
||||
break
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import AddTorrentDialog from '@/components/Dialogs/AddTorrentDialog.vue'
|
||||
import { useAddTorrentStore } from '@/stores/addTorrents'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useAddTorrentStore, useDialogStore } from '@/stores'
|
||||
|
||||
const addTorrentStore = useAddTorrentStore()
|
||||
const dialogStore = useDialogStore()
|
||||
|
@ -12,7 +11,8 @@ function openAddTorrentDialog() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<v-bottom-navigation :active="addTorrentStore.pendingTorrentsCount > 0" class="pointer" v-touch="{ up: openAddTorrentDialog }" @click="openAddTorrentDialog">
|
||||
<v-bottom-navigation :active="addTorrentStore.pendingTorrentsCount > 0" class="pointer"
|
||||
v-touch="{ up: openAddTorrentDialog }" @click="openAddTorrentDialog">
|
||||
<v-list-item :title="$t('navbar.addPanel.torrentsPendingCount', addTorrentStore.pendingTorrentsCount)" />
|
||||
<v-spacer />
|
||||
<v-list-item>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { formatDataUnit, formatDataValue } from '@/helpers'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useVueTorrentStore } from '@/stores'
|
||||
|
||||
defineProps<{ title: string; color: string; value: number }>()
|
||||
|
||||
|
@ -12,8 +12,11 @@ const vueTorrentStore = useVueTorrentStore()
|
|||
<v-row data-testid="card-wrapper" :class="[`text-${color}`]">
|
||||
<v-col data-testid="card-title" cols="7" class="text-subtitle-1">{{ title }}</v-col>
|
||||
<v-col cols="5" class="">
|
||||
<span data-testid="card-value" class="text-subtitle-1 roboto">{{ formatDataValue(value, vueTorrentStore.useBinarySize) }}</span>
|
||||
<span data-testid="card-unit" class="font-weight-light text-caption ml-1 text-subtitle-1">{{ formatDataUnit(value, vueTorrentStore.useBinarySize) }}</span>
|
||||
<span data-testid="card-value"
|
||||
class="text-subtitle-1 roboto">{{ formatDataValue(value, vueTorrentStore.useBinarySize) }}</span>
|
||||
<span data-testid="card-unit" class="font-weight-light text-caption ml-1 text-subtitle-1">{{
|
||||
formatDataUnit(value, vueTorrentStore.useBinarySize)
|
||||
}}</span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-sheet>
|
||||
|
@ -21,6 +24,7 @@ const vueTorrentStore = useVueTorrentStore()
|
|||
|
||||
<style scoped lang="scss">
|
||||
@import '@fontsource/roboto-mono';
|
||||
|
||||
.roboto {
|
||||
font-family: 'Roboto Mono', sans-serif !important;
|
||||
font-weight: 500;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { formatSpeedUnit, formatSpeedValue } from '@/helpers'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useVueTorrentStore } from '@/stores'
|
||||
|
||||
defineProps({
|
||||
icon: {
|
||||
|
@ -27,8 +27,11 @@ const vueTorrentStore = useVueTorrentStore()
|
|||
<v-icon class="" :icon="icon" :color="color" />
|
||||
</v-col>
|
||||
<v-col cols="8" class="d-flex flex-column align-center justify-center">
|
||||
<span class="text-subtitle-1 roboto" :class="`text-${color}`">{{ formatSpeedValue(value, vueTorrentStore.useBitSpeed) }}</span>
|
||||
<span class="text-caption" :class="`text-${color}`">{{ formatSpeedUnit(value, vueTorrentStore.useBitSpeed) }}</span>
|
||||
<span class="text-subtitle-1 roboto"
|
||||
:class="`text-${color}`">{{ formatSpeedValue(value, vueTorrentStore.useBitSpeed) }}</span>
|
||||
<span class="text-caption" :class="`text-${color}`">{{
|
||||
formatSpeedUnit(value, vueTorrentStore.useBitSpeed)
|
||||
}}</span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-sheet>
|
||||
|
@ -36,6 +39,7 @@ const vueTorrentStore = useVueTorrentStore()
|
|||
|
||||
<style scoped lang="scss">
|
||||
@import '@fontsource/roboto-mono';
|
||||
|
||||
.roboto {
|
||||
font-family: 'Roboto Mono', sans-serif !important;
|
||||
font-weight: 500;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { formatDataUnit, formatDataValue } from '@/helpers'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useVueTorrentStore } from '@/stores'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
|
||||
defineProps<{ torrent: Torrent; title: string; value: string }>()
|
||||
|
@ -11,7 +11,7 @@ const vuetorrentStore = useVueTorrentStore()
|
|||
<template>
|
||||
<div class="d-flex flex-column">
|
||||
<div class="text-caption text-grey">
|
||||
{{ $t(`torrent.properties.${title}`) }}
|
||||
{{ $t(`torrent.properties.${ title }`) }}
|
||||
</div>
|
||||
<div>
|
||||
{{ formatDataValue(torrent[value], vuetorrentStore.useBinarySize) }}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import dayjs from '@/plugins/dayjs'
|
||||
import { useVueTorrentStore } from '@/stores'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
|
||||
defineProps<{ torrent: Torrent; title: string; value: string }>()
|
||||
|
||||
|
@ -11,7 +11,7 @@ const vueTorrentStore = useVueTorrentStore()
|
|||
<template>
|
||||
<div class="d-flex flex-column">
|
||||
<div class="text-caption text-grey">
|
||||
{{ $t(`torrent.properties.${title}`) }}
|
||||
{{ $t(`torrent.properties.${ title }`) }}
|
||||
</div>
|
||||
<div>
|
||||
<span v-if="torrent[value] > 0">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { formatSpeedUnit, formatSpeedValue } from '@/helpers'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useVueTorrentStore } from '@/stores'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
|
||||
defineProps<{ torrent: Torrent; title: string; value: string }>()
|
||||
|
@ -11,7 +11,7 @@ const vuetorrentStore = useVueTorrentStore()
|
|||
<template>
|
||||
<div class="d-flex flex-column">
|
||||
<div class="text-caption text-grey">
|
||||
{{ $t(`torrent.properties.${title}`) }}
|
||||
{{ $t(`torrent.properties.${ title }`) }}
|
||||
</div>
|
||||
<div>
|
||||
{{ formatSpeedValue(torrent[value], vuetorrentStore.useBitSpeed) }}
|
||||
|
|
|
@ -5,10 +5,7 @@ import MoveTorrentDialog from '@/components/Dialogs/MoveTorrentDialog.vue'
|
|||
import RenameTorrentDialog from '@/components/Dialogs/RenameTorrentDialog.vue'
|
||||
import ShareLimitDialog from '@/components/Dialogs/ShareLimitDialog.vue'
|
||||
import SpeedLimitDialog from '@/components/Dialogs/SpeedLimitDialog.vue'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { useDashboardStore, useDialogStore, useMaindataStore, usePreferenceStore, useTorrentStore } from '@/stores'
|
||||
import { TRCMenuEntry } from '@/types/vuetorrent'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -25,6 +22,7 @@ const dashboardStore = useDashboardStore()
|
|||
const dialogStore = useDialogStore()
|
||||
const maindataStore = useMaindataStore()
|
||||
const preferenceStore = usePreferenceStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const trcVisible = computed({
|
||||
get: () => props.modelValue,
|
||||
|
@ -34,20 +32,20 @@ const trcVisible = computed({
|
|||
const isMultiple = computed(() => dashboardStore.selectedTorrents.length > 1)
|
||||
const hashes = computed(() => dashboardStore.selectedTorrents)
|
||||
const hash = computed(() => hashes.value[0])
|
||||
const torrent = computed(() => maindataStore.getTorrentByHash(hash.value))
|
||||
const torrents = computed(() => dashboardStore.selectedTorrents.map(maindataStore.getTorrentByHash).filter(torrent => !!torrent))
|
||||
const torrent = computed(() => torrentStore.getTorrentByHash(hash.value))
|
||||
const torrents = computed(() => dashboardStore.selectedTorrents.map(torrentStore.getTorrentByHash).filter(torrent => !!torrent))
|
||||
const availableCategories = computed(() => [{ name: '' }, ...maindataStore.categories])
|
||||
|
||||
async function resumeTorrents() {
|
||||
await maindataStore.resumeTorrents(hashes)
|
||||
await torrentStore.resumeTorrents(hashes)
|
||||
}
|
||||
|
||||
async function forceResumeTorrents() {
|
||||
await maindataStore.forceResumeTorrents(hashes)
|
||||
await torrentStore.forceResumeTorrents(hashes)
|
||||
}
|
||||
|
||||
async function pauseTorrents() {
|
||||
await maindataStore.pauseTorrents(hashes)
|
||||
await torrentStore.pauseTorrents(hashes)
|
||||
}
|
||||
|
||||
function deleteTorrents() {
|
||||
|
@ -63,7 +61,7 @@ function renameTorrents() {
|
|||
}
|
||||
|
||||
async function forceRecheck() {
|
||||
await maindataStore.recheckTorrents(hashes)
|
||||
await torrentStore.recheckTorrents(hashes)
|
||||
}
|
||||
|
||||
async function forceReannounce() {
|
||||
|
@ -87,8 +85,8 @@ function hasTag(tag: string) {
|
|||
}
|
||||
|
||||
async function toggleTag(tag: string) {
|
||||
if (hasTag(tag)) await maindataStore.removeTorrentTags(hashes.value, [tag])
|
||||
else await maindataStore.addTorrentTags(hashes.value, [tag])
|
||||
if (hasTag(tag)) await torrentStore.removeTorrentTags(hashes.value, [tag])
|
||||
else await torrentStore.addTorrentTags(hashes.value, [tag])
|
||||
}
|
||||
|
||||
async function copyValue(valueToCopy: string) {
|
||||
|
@ -109,12 +107,12 @@ function setShareLimit() {
|
|||
|
||||
async function exportTorrents() {
|
||||
hashes.value.forEach(hash => {
|
||||
maindataStore.exportTorrent(hash).then(blob => {
|
||||
torrentStore.exportTorrent(hash).then(blob => {
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.style.opacity = '0'
|
||||
link.setAttribute('download', `${hash}.torrent`)
|
||||
link.setAttribute('download', `${ hash }.torrent`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
|
@ -173,22 +171,22 @@ const menuData = computed<TRCMenuEntry[]>(() => [
|
|||
{
|
||||
text: t('dashboard.right_click.priority.top'),
|
||||
icon: 'mdi-priority-high',
|
||||
action: async () => await maindataStore.setTorrentPriority(hashes.value, 'topPrio')
|
||||
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'topPrio')
|
||||
},
|
||||
{
|
||||
text: t('dashboard.right_click.priority.increase'),
|
||||
icon: 'mdi-arrow-up',
|
||||
action: async () => await maindataStore.setTorrentPriority(hashes.value, 'increasePrio')
|
||||
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'increasePrio')
|
||||
},
|
||||
{
|
||||
text: t('dashboard.right_click.priority.decrease'),
|
||||
icon: 'mdi-arrow-down',
|
||||
action: async () => await maindataStore.setTorrentPriority(hashes.value, 'decreasePrio')
|
||||
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'decreasePrio')
|
||||
},
|
||||
{
|
||||
text: t('dashboard.right_click.priority.bottom'),
|
||||
icon: 'mdi-priority-low',
|
||||
action: async () => await maindataStore.setTorrentPriority(hashes.value, 'bottomPrio')
|
||||
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'bottomPrio')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -212,7 +210,7 @@ const menuData = computed<TRCMenuEntry[]>(() => [
|
|||
disabledIcon: 'mdi-label-off',
|
||||
children: availableCategories.value.map(category => ({
|
||||
text: category.name === '' ? t('dashboard.right_click.category.clear') : category.name,
|
||||
action: async () => await maindataStore.setTorrentCategory(hashes.value, category.name)
|
||||
action: async () => await torrentStore.setTorrentCategory(hashes.value, category.name)
|
||||
}))
|
||||
},
|
||||
{
|
||||
|
@ -273,7 +271,8 @@ const menuData = computed<TRCMenuEntry[]>(() => [
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<v-menu v-if="trcVisible" v-model="trcVisible" activator="parent" :close-on-content-click="true" transition="slide-y-transition" scroll-strategy="none">
|
||||
<v-menu v-if="trcVisible" v-model="trcVisible" activator="parent" :close-on-content-click="true"
|
||||
transition="slide-y-transition" scroll-strategy="none">
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<div class="d-flex justify-space-around">
|
||||
|
@ -286,7 +285,8 @@ const menuData = computed<TRCMenuEntry[]>(() => [
|
|||
|
||||
<v-tooltip location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn density="compact" variant="plain" icon="mdi-fast-forward" v-bind="props" @click="forceResumeTorrents" />
|
||||
<v-btn density="compact" variant="plain" icon="mdi-fast-forward" v-bind="props"
|
||||
@click="forceResumeTorrents" />
|
||||
</template>
|
||||
<span>Force Resume</span>
|
||||
</v-tooltip>
|
||||
|
@ -300,7 +300,8 @@ const menuData = computed<TRCMenuEntry[]>(() => [
|
|||
|
||||
<v-tooltip location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn color="red" density="compact" variant="plain" icon="mdi-delete-forever" v-bind="props" @click="deleteTorrents" />
|
||||
<v-btn color="red" density="compact" variant="plain" icon="mdi-delete-forever" v-bind="props"
|
||||
@click="deleteTorrents" />
|
||||
</template>
|
||||
<span>Delete</span>
|
||||
</v-tooltip>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { DashboardPropertyType } from '@/constants/vuetorrent'
|
||||
import { doesCommand } from '@/helpers'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useDashboardStore, useVueTorrentStore } from '@/stores'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { computed } from 'vue'
|
||||
import ItemAmount from './DashboardItems/ItemAmount.vue'
|
||||
|
@ -62,11 +61,13 @@ const isTorrentSelected = computed(() => dashboardStore.isTorrentInSelection(pro
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<v-card :class="`sideborder ${torrent.state} pointer`" :color="isTorrentSelected ? `torrent-${torrent.state}-darken-3` : undefined" width="100%" @click="onClick">
|
||||
<v-card :class="`sideborder ${torrent.state} pointer`"
|
||||
:color="isTorrentSelected ? `torrent-${torrent.state}-darken-3` : undefined" width="100%" @click="onClick">
|
||||
<v-card-title class="text-wrap text-subtitle-1 pt-1 pb-0">{{ torrent.name }}</v-card-title>
|
||||
<v-card-text class="pa-2 pt-0">
|
||||
<div class="d-flex gap flex-wrap">
|
||||
<component :is="getComponent(ppt.type)" :torrent="torrent" v-bind="ppt.props" v-for="ppt in torrentProperties" />
|
||||
<component :is="getComponent(ppt.type)" :torrent="torrent" v-bind="ppt.props"
|
||||
v-for="ppt in torrentProperties" />
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { useDialog } from '@/composables'
|
||||
import { AppPreferences } from '@/constants/qbit'
|
||||
import { useAddTorrentStore } from '@/stores/addTorrents'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useAddTorrentStore, useMaindataStore, usePreferenceStore, useTorrentStore, useVueTorrentStore } from '@/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -20,9 +17,10 @@ const props = withDefaults(defineProps<{
|
|||
const { isOpened } = useDialog(props.guid)
|
||||
const { t } = useI18n()
|
||||
const addTorrentStore = useAddTorrentStore()
|
||||
const maindataStore = useMaindataStore()
|
||||
const { urls, files, form } = storeToRefs(addTorrentStore)
|
||||
const maindataStore = useMaindataStore()
|
||||
const preferenceStore = usePreferenceStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
const vueTorrentStore = useVueTorrentStore()
|
||||
|
||||
const fileOverflowDisplayLimit = 2
|
||||
|
@ -139,7 +137,7 @@ const inactiveSeedingTimeLimit = computed({
|
|||
function submit() {
|
||||
if (!isFormValid.value) return
|
||||
|
||||
const promise = maindataStore.addTorrents(form.value, files.value)
|
||||
const promise = torrentStore.addTorrents(form.value, files.value)
|
||||
.then(() => {
|
||||
addTorrentStore.resetForm()
|
||||
close()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore } from '@/stores'
|
||||
import { Category } from '@/types/qbit/models'
|
||||
import { onBeforeMount, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -49,12 +49,15 @@ onBeforeMount(() => {
|
|||
<template>
|
||||
<v-dialog v-model="isOpened">
|
||||
<v-card>
|
||||
<v-card-title>{{ $t(`dialogs.category.title.${initialCategory ? 'edit' : 'create'}`) }}</v-card-title>
|
||||
<v-card-title>{{ $t(`dialogs.category.title.${ initialCategory ? 'edit' : 'create' }`) }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="isFormValid" ref="form" @submit.prevent @keydown.enter.prevent="submit">
|
||||
<v-text-field v-if="!!initialCategory" :model-value="initialCategory.name" disabled :label="$t('dialogs.category.oldName')" />
|
||||
<v-text-field v-model="formData.name" :rules="nameRules" :autofocus="!initialCategory" :label="$t('dialogs.category.name')" />
|
||||
<v-text-field v-model="formData.savePath" :autofocus="!!initialCategory" :label="$t('dialogs.category.savePath')" />
|
||||
<v-text-field v-if="!!initialCategory" :model-value="initialCategory.name" disabled
|
||||
:label="$t('dialogs.category.oldName')" />
|
||||
<v-text-field v-model="formData.name" :rules="nameRules" :autofocus="!initialCategory"
|
||||
:label="$t('dialogs.category.name')" />
|
||||
<v-text-field v-model="formData.savePath" :autofocus="!!initialCategory"
|
||||
:label="$t('dialogs.category.savePath')" />
|
||||
<v-scroll-x-transition>
|
||||
<div class="text-warning" v-if="!!initialCategory && initialCategory.name !== formData.name">
|
||||
<v-icon>mdi-alert</v-icon>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useDashboardStore, useTorrentStore, useVueTorrentStore } from '@/stores'
|
||||
import { computed, onBeforeMount, onUnmounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
@ -19,18 +17,18 @@ const route = useRoute()
|
|||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
const dashboardStore = useDashboardStore()
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
const vuetorrentStore = useVueTorrentStore()
|
||||
|
||||
const form = ref<VForm>()
|
||||
const isFormValid = ref(false)
|
||||
|
||||
const selection = computed(() => maindataStore.torrents.filter(t => props.hashes?.includes(t.hash)))
|
||||
const selection = computed(() => torrentStore.torrents.filter(t => props.hashes?.includes(t.hash)))
|
||||
|
||||
async function submit() {
|
||||
if (!isFormValid.value) return
|
||||
|
||||
await maindataStore.deleteTorrents(
|
||||
await torrentStore.deleteTorrents(
|
||||
selection.value.map(t => t.hash),
|
||||
vuetorrentStore.deleteWithFiles
|
||||
)
|
||||
|
@ -73,7 +71,8 @@ onUnmounted(() => {
|
|||
<div class="d-flex flex-wrap gap">
|
||||
<span class="pa-1 border wrap-anywhere" v-for="torrent in selection">{{ torrent.name }}</span>
|
||||
</div>
|
||||
<v-checkbox v-model="vuetorrentStore.deleteWithFiles" hide-details :label="$t('dialogs.delete.deleteWithFiles')" />
|
||||
<v-checkbox v-model="vuetorrentStore.deleteWithFiles" hide-details
|
||||
:label="$t('dialogs.delete.deleteWithFiles')" />
|
||||
<v-scroll-x-transition>
|
||||
<div class="text-red" v-show="vuetorrentStore.deleteWithFiles">
|
||||
<v-icon>mdi-alert</v-icon>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useAppStore, useAuthStore, useVueTorrentStore } from '@/stores'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { toast } from 'vue3-toastify'
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { ConnectionStatus } from '@/constants/qbit'
|
||||
import { useLogStore } from '@/stores/logs'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useLogStore, useMaindataStore } from '@/stores'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -38,7 +37,9 @@ const close = () => {
|
|||
<v-row>
|
||||
<v-col cols="12" sm="6" lg="3">
|
||||
<div>{{ $t('dialogs.connectionStatus.status') }}</div>
|
||||
<div :class="['ml-2', connectionStatusColor]">{{ $t('constants.connectionStatus.' + maindataStore.serverState?.connection_status) }}</div>
|
||||
<div :class="['ml-2', connectionStatusColor]">
|
||||
{{ $t('constants.connectionStatus.' + maindataStore.serverState?.connection_status) }}
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" lg="3">
|
||||
<div>{{ $t('dialogs.connectionStatus.externalIp') }}</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useTorrentStore } from '@/stores'
|
||||
import { computed, onBeforeMount, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { VForm } from 'vuetify/components'
|
||||
|
@ -12,7 +12,7 @@ const props = defineProps<{
|
|||
|
||||
const { isOpened } = useDialog(props.guid)
|
||||
const { t } = useI18n()
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const form = ref<VForm>()
|
||||
const isFormValid = ref(false)
|
||||
|
@ -22,14 +22,14 @@ const formData = reactive({
|
|||
|
||||
const rules = [(v: string) => !!v || t('dialogs.moveTorrent.required'), (v: string) => v !== oldPath.value || t('dialogs.moveTorrent.samePath')]
|
||||
|
||||
const torrents = computed(() => props.hashes.map(maindataStore.getTorrentByHash))
|
||||
const torrents = computed(() => props.hashes.map(torrentStore.getTorrentByHash))
|
||||
const oldPath = computed(() => torrents.value[0]?.savePath)
|
||||
|
||||
async function submit() {
|
||||
await form.value?.validate()
|
||||
if (!isFormValid.value) return
|
||||
|
||||
await maindataStore.moveTorrents(props.hashes, formData.newPath)
|
||||
await torrentStore.moveTorrents(props.hashes, formData.newPath)
|
||||
|
||||
close()
|
||||
}
|
||||
|
@ -50,7 +50,8 @@ onBeforeMount(() => {
|
|||
<v-card-text>
|
||||
<v-form v-model="isFormValid" ref="form" @submit.prevent>
|
||||
<v-text-field v-if="oldPath" :model-value="oldPath" disabled :label="$t('dialogs.moveTorrent.oldPath')" />
|
||||
<v-text-field v-model="formData.newPath" :rules="rules" autofocus :label="$t('dialogs.moveTorrent.newPath')" @keydown.enter="submit" />
|
||||
<v-text-field v-model="formData.newPath" :rules="rules" autofocus :label="$t('dialogs.moveTorrent.newPath')"
|
||||
@keydown.enter="submit" />
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore } from '@/stores'
|
||||
import { nextTick, onBeforeMount, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { VForm } from 'vuetify/components'
|
||||
|
@ -65,7 +65,8 @@ onBeforeMount(() => {
|
|||
<v-card-text>
|
||||
<v-form v-model="isFormValid" ref="form" @submit.prevent>
|
||||
<v-text-field v-if="oldName" :model-value="oldName" disabled :label="$t('dialogs.moveTorrentFile.oldName')" />
|
||||
<v-text-field v-model="formData.newName" ref="input" :rules="rules" autofocus :label="$t('dialogs.moveTorrent.newPath')" @keydown.enter="submit" />
|
||||
<v-text-field v-model="formData.newName" ref="input" :rules="rules" autofocus
|
||||
:label="$t('dialogs.moveTorrent.newPath')" @keydown.enter="submit" />
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useSearchEngineStore } from '@/stores/searchEngine'
|
||||
import { useSearchEngineStore } from '@/stores'
|
||||
import { SearchPlugin } from '@/types/qbit/models'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -83,7 +83,8 @@ function closeInstallDialog() {
|
|||
|
||||
<v-spacer />
|
||||
|
||||
<v-btn :text="$t('dialogs.pluginManager.update')" color="accent" class="mr-2" :loading="updateLoading" @click="updatePlugins" />
|
||||
<v-btn :text="$t('dialogs.pluginManager.update')" color="accent" class="mr-2" :loading="updateLoading"
|
||||
@click="updatePlugins" />
|
||||
<v-dialog v-model="installisOpened">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" color="primary">
|
||||
|
@ -106,7 +107,8 @@ function closeInstallDialog() {
|
|||
</v-dialog>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-data-table :headers="headers" items-per-page="-1" :items="searchEngineStore.searchPlugins" :sort-by="[{ key: 'fullName', order: 'asc' }]" :loading="loading">
|
||||
<v-data-table :headers="headers" items-per-page="-1" :items="searchEngineStore.searchPlugins"
|
||||
:sort-by="[{ key: 'fullName', order: 'asc' }]" :loading="loading">
|
||||
<template v-slot:item.enabled="{ item }">
|
||||
<v-checkbox-btn :model-value="item.enabled" @click="onTogglePlugin(item)" />
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useTorrentStore } from '@/stores'
|
||||
import { computed, onBeforeMount, onMounted, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { VForm } from 'vuetify/components'
|
||||
|
@ -12,7 +12,7 @@ const props = defineProps<{
|
|||
|
||||
const { isOpened } = useDialog(props.guid)
|
||||
const { t } = useI18n()
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const field = ref<HTMLInputElement>()
|
||||
const form = ref<VForm>()
|
||||
|
@ -23,14 +23,14 @@ const formData = reactive({
|
|||
|
||||
const rules = [(v: string) => !!v || t('dialogs.renameTorrent.required'), (v: string) => v !== oldName.value || t('dialogs.renameTorrent.sameName')]
|
||||
|
||||
const torrent = computed(() => maindataStore.getTorrentByHash(props.hash))
|
||||
const torrent = computed(() => torrentStore.getTorrentByHash(props.hash))
|
||||
const oldName = computed(() => torrent.value?.name)
|
||||
|
||||
async function submit() {
|
||||
await form.value?.validate()
|
||||
if (!isFormValid.value) return
|
||||
|
||||
await maindataStore.renameTorrent(props.hash, formData.newName)
|
||||
await torrentStore.renameTorrent(props.hash, formData.newName)
|
||||
|
||||
close()
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ onMounted(() => {
|
|||
<v-card-text>
|
||||
<v-form v-model="isFormValid" ref="form" @submit.prevent>
|
||||
<v-text-field v-if="oldName" :model-value="oldName" disabled :label="$t('dialogs.renameTorrent.oldName')" />
|
||||
<v-text-field v-model="formData.newName" ref="field" :rules="rules" autofocus :label="$t('dialogs.renameTorrent.newName')" @keydown.enter="submit" />
|
||||
<v-text-field v-model="formData.newName" ref="field" :rules="rules" autofocus
|
||||
:label="$t('dialogs.renameTorrent.newName')" @keydown.enter="submit" />
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useRssStore } from '@/stores/rss'
|
||||
import { useRssStore } from '@/stores'
|
||||
import { Feed } from '@/types/qbit/models'
|
||||
import { onBeforeMount, reactive, ref } from 'vue'
|
||||
import { VForm } from 'vuetify/components'
|
||||
|
@ -45,7 +45,7 @@ onBeforeMount(() => {
|
|||
<template>
|
||||
<v-dialog v-model="isOpened">
|
||||
<v-card>
|
||||
<v-card-title>{{ $t(`dialogs.rss.feed.title.${initialFeed ? 'edit' : 'create'}`) }}</v-card-title>
|
||||
<v-card-title>{{ $t(`dialogs.rss.feed.title.${ initialFeed ? 'edit' : 'create' }`) }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="isFormValid" ref="form" @submit.prevent>
|
||||
<v-text-field v-model="formData.name" :label="$t('dialogs.rss.feed.name')" />
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { ContentLayout } from '@/constants/qbit/AppPreferences'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useRssStore } from '@/stores/rss'
|
||||
import { useMaindataStore, useRssStore } from '@/stores'
|
||||
import { FeedRule } from '@/types/qbit/models'
|
||||
import { computed, onBeforeMount, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -51,7 +50,10 @@ const contentLayoutOptions = [
|
|||
{ title: t('constants.contentLayout.nosubfolder'), value: ContentLayout.NO_SUBFOLDER }
|
||||
]
|
||||
const categories = computed(() => {
|
||||
return [{ title: t('common.none'), value: '' }, ...maindataStore.categories.map(category => ({ title: category.name, value: category.name }))]
|
||||
return [{ title: t('common.none'), value: '' }, ...maindataStore.categories.map(category => ({
|
||||
title: category.name,
|
||||
value: category.name
|
||||
}))]
|
||||
})
|
||||
|
||||
const lastMatch = computed(() => {
|
||||
|
@ -143,19 +145,26 @@ onBeforeMount(async () => {
|
|||
<v-text-field v-model="formData.mustContain" :label="$t('dialogs.rss.rule.mustContain')" />
|
||||
<v-text-field v-model="formData.mustNotContain" :label="$t('dialogs.rss.rule.mustNotContain')" />
|
||||
<v-checkbox v-model="formData.smartFilter" hide-details :label="$t('dialogs.rss.rule.smartFilter')" />
|
||||
<v-text-field v-model="formData.episodeFilter" :placeholder="$t('dialogs.rss.rule.episodeFilterPlaceholder')" :label="$t('dialogs.rss.rule.episodeFilter')" />
|
||||
<v-text-field v-model="formData.episodeFilter"
|
||||
:placeholder="$t('dialogs.rss.rule.episodeFilterPlaceholder')"
|
||||
:label="$t('dialogs.rss.rule.episodeFilter')" />
|
||||
|
||||
<v-divider class="mb-4" />
|
||||
|
||||
<v-select v-model="formData.assignedCategory" :items="categories" :label="$t('dialogs.rss.rule.assignedCategory')" />
|
||||
<v-text-field v-model="formData.savePath" :placeholder="$t('dialogs.rss.rule.savePathPlaceholder')" :label="$t('dialogs.rss.rule.savePath')" />
|
||||
<v-text-field v-model="formData.ignoreDays" type="number" :hint="$t('dialogs.rss.rule.ignoreDaysHint')" :label="$t('dialogs.rss.rule.ignoreDays')" />
|
||||
<v-select v-model="formData.assignedCategory" :items="categories"
|
||||
:label="$t('dialogs.rss.rule.assignedCategory')" />
|
||||
<v-text-field v-model="formData.savePath" :placeholder="$t('dialogs.rss.rule.savePathPlaceholder')"
|
||||
:label="$t('dialogs.rss.rule.savePath')" />
|
||||
<v-text-field v-model="formData.ignoreDays" type="number" :hint="$t('dialogs.rss.rule.ignoreDaysHint')"
|
||||
:label="$t('dialogs.rss.rule.ignoreDays')" />
|
||||
<v-text-field v-model="lastMatch" disabled :label="$t('dialogs.rss.rule.lastMatch.label')" />
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-select v-model="formData.addPaused" :items="addPausedOptions" :label="$t('constants.addPaused.title')" />
|
||||
<v-select v-model="formData.torrentContentLayout" :items="contentLayoutOptions" :label="$t('constants.contentLayout.title')" />
|
||||
<v-select v-model="formData.addPaused" :items="addPausedOptions"
|
||||
:label="$t('constants.addPaused.title')" />
|
||||
<v-select v-model="formData.torrentContentLayout" :items="contentLayoutOptions"
|
||||
:label="$t('constants.contentLayout.title')" />
|
||||
|
||||
<v-list-subheader>{{ $t('dialogs.rss.rule.affectedFeedsSubheader') }}</v-list-subheader>
|
||||
|
||||
|
@ -168,7 +177,8 @@ onBeforeMount(async () => {
|
|||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-checkbox v-for="item in rssStore.feeds" v-model="formData.affectedFeeds" multiple hide-details :label="item.name" :value="item.url" />
|
||||
<v-checkbox v-for="item in rssStore.feeds" v-model="formData.affectedFeeds" multiple hide-details
|
||||
:label="item.name" :value="item.url" />
|
||||
</v-col>
|
||||
|
||||
<v-divider :vertical="!$vuetify.display.mobile" />
|
||||
|
@ -181,7 +191,8 @@ onBeforeMount(async () => {
|
|||
<v-list-subheader inset v-else-if="item.type === 'subheader'">{{ item.value }}</v-list-subheader>
|
||||
<v-list-item v-else class="mb-3">{{ item.value }}</v-list-item>
|
||||
</template>
|
||||
<v-list-item v-if="matchingArticles.length === 0" :title="$t('dialogs.rss.rule.matchingArticles.noMatch')" />
|
||||
<v-list-item v-if="matchingArticles.length === 0"
|
||||
:title="$t('dialogs.rss.rule.matchingArticles.noMatch')" />
|
||||
</v-list>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore, useTorrentStore } from '@/stores'
|
||||
import { computed, onBeforeMount, ref } from 'vue'
|
||||
|
||||
type ShareType = 'global' | 'disabled' | 'enabled'
|
||||
|
@ -14,6 +14,7 @@ const props = defineProps<{
|
|||
|
||||
const { isOpened } = useDialog(props.guid)
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const isFormValid = ref(false)
|
||||
|
||||
|
@ -53,7 +54,7 @@ async function submit() {
|
|||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const torrent = maindataStore.getTorrentByHash(props.hash)
|
||||
const torrent = torrentStore.getTorrentByHash(props.hash)
|
||||
if (!torrent) {
|
||||
return close()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useMaindataStore } from '@/stores/maindata.ts'
|
||||
import { useMaindataStore, useTorrentStore } from '@/stores'
|
||||
import { onBeforeMount, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -11,6 +11,7 @@ const props = defineProps<{
|
|||
|
||||
const { isOpened } = useDialog(props.guid)
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const isFormValid = ref(false)
|
||||
const value = ref(0)
|
||||
|
@ -33,7 +34,7 @@ async function submit() {
|
|||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const torrent = maindataStore.getTorrentByHash(props.hash)
|
||||
const torrent = torrentStore.getTorrentByHash(props.hash)
|
||||
if (!torrent) {
|
||||
return close()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useDialog } from '@/composables'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore } from '@/stores'
|
||||
import { onBeforeMount, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { VForm } from 'vuetify/components'
|
||||
|
@ -31,6 +31,7 @@ async function submit() {
|
|||
|
||||
close()
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
isOpened.value = false
|
||||
}
|
||||
|
@ -43,11 +44,12 @@ onBeforeMount(() => {
|
|||
<template>
|
||||
<v-dialog v-model="isOpened">
|
||||
<v-card>
|
||||
<v-card-title>{{ $t(`dialogs.tag.title.${initialTag ? 'rename' : 'create'}`) }}</v-card-title>
|
||||
<v-card-title>{{ $t(`dialogs.tag.title.${ initialTag ? 'rename' : 'create' }`) }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="isFormValid" ref="form" @submit.prevent @keydown.enter.prevent="submit">
|
||||
<v-text-field v-if="initialTag" :model-value="initialTag" disabled :label="$t('dialogs.tag.oldName')" />
|
||||
<v-text-field v-model="tagName" :rules="rules" autofocus :hint="$t('dialogs.tag.hint')" :label="$t('dialogs.tag.name')" />
|
||||
<v-text-field v-model="tagName" :rules="rules" autofocus :hint="$t('dialogs.tag.hint')"
|
||||
:label="$t('dialogs.tag.name')" />
|
||||
<v-scroll-x-transition>
|
||||
<div class="text-warning" v-if="!!initialTag && initialTag !== tagName">
|
||||
<v-icon>mdi-alert</v-icon>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import { useAddTorrentStore } from '@/stores/addTorrents'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useAddTorrentStore, useAuthStore } from '@/stores'
|
||||
import { useDropZone } from '@vueuse/core'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
@ -23,7 +22,7 @@ function onDrop(files: File[] | null, event: DragEvent) {
|
|||
|
||||
// Handle .torrent files
|
||||
const torrentFiles = (files || [])
|
||||
.filter(file => file.type === 'application/x-bittorrent' || file.name.endsWith('.torrent'))
|
||||
.filter(file => file.type === 'application/x-bittorrent' || file.name.endsWith('.torrent'))
|
||||
const links = event.dataTransfer.getData('text/plain').split('\n')
|
||||
.filter(link => link.startsWith('magnet:') || link.startsWith('http'))
|
||||
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import BottomActions from '@/components/Navbar/SideWidgets/BottomActions.vue'
|
||||
import CurrentSpeed from '@/components/Navbar/SideWidgets/CurrentSpeed.vue'
|
||||
import FilterSelect from '@/components/Navbar/SideWidgets/FilterSelect.vue'
|
||||
import FreeSpace from '@/components/Navbar/SideWidgets/FreeSpace.vue'
|
||||
import SpeedGraph from '@/components/Navbar/SideWidgets/SpeedGraph.vue'
|
||||
import TransferStats from '@/components/Navbar/SideWidgets/TransferStats.vue'
|
||||
import ActiveFilters from '@/components/Navbar/TopWidgets/ActiveFilters.vue'
|
||||
import TopContainer from '@/components/Navbar/TopWidgets/TopContainer.vue'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useDashboardStore, useVueTorrentStore } from '@/stores'
|
||||
import { ref } from 'vue'
|
||||
import BottomActions from './SideWidgets/BottomActions.vue'
|
||||
import CurrentSpeed from './SideWidgets/CurrentSpeed.vue'
|
||||
import FilterSelect from './SideWidgets/FilterSelect.vue'
|
||||
import FreeSpace from './SideWidgets/FreeSpace.vue'
|
||||
import SpeedGraph from './SideWidgets/SpeedGraph.vue'
|
||||
import TransferStats from './SideWidgets/TransferStats.vue'
|
||||
import ActiveFilters from './TopWidgets/ActiveFilters.vue'
|
||||
import TopContainer from './TopWidgets/TopContainer.vue'
|
||||
|
||||
const dashboardStore = useDashboardStore()
|
||||
const vueTorrentStore = useVueTorrentStore()
|
||||
|
@ -22,7 +21,8 @@ const toggleDrawer = () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<v-navigation-drawer v-model="isDrawerOpen" :location="vueTorrentStore.isDrawerRight ? 'right' : 'left'" color="primary" disable-route-watcher>
|
||||
<v-navigation-drawer v-model="isDrawerOpen" :location="vueTorrentStore.isDrawerRight ? 'right' : 'left'"
|
||||
color="primary" disable-route-watcher>
|
||||
<v-list class="clean-px px-2 pt-0">
|
||||
<v-list-item v-if="vueTorrentStore.showCurrentSpeed">
|
||||
<CurrentSpeed />
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
import ConfirmShutdownDialog from '@/components/Dialogs/ConfirmShutdownDialog.vue'
|
||||
import ConnectionStatusDialog from '@/components/Dialogs/ConnectionStatusDialog.vue'
|
||||
import { ConnectionStatus } from '@/constants/qbit'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useAppStore, useAuthStore, useDialogStore, useMaindataStore, useVueTorrentStore } from '@/stores'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -44,7 +40,7 @@ const connectionStatusText = computed(() => {
|
|||
key = 'unknown'
|
||||
}
|
||||
|
||||
return t('navbar.side.bottom_actions.conn_status', { status: t(`constants.connectionStatus.${key}`) })
|
||||
return t('navbar.side.bottom_actions.conn_status', { status: t(`constants.connectionStatus.${ key }`) })
|
||||
})
|
||||
|
||||
const logout = async () => {
|
||||
|
@ -58,6 +54,7 @@ const toggleAltSpeed = () => {
|
|||
function openConnectionStatusDialog() {
|
||||
dialogStore.createDialog(ConnectionStatusDialog)
|
||||
}
|
||||
|
||||
function openConfirmShutdownDialog() {
|
||||
dialogStore.createDialog(ConfirmShutdownDialog)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import SpeedCard from '@/components/Core/SpeedCard.vue'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore } from '@/stores'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
@ -16,7 +16,8 @@ const maindataStore = useMaindataStore()
|
|||
<v-sheet color="primary" class="mx-2">
|
||||
<v-row class="pt-0">
|
||||
<v-col class="px-1 pt-1">
|
||||
<SpeedCard icon="mdi-chevron-down" color="download" :value="maindataStore.serverState?.dl_info_speed ?? 0" />
|
||||
<SpeedCard icon="mdi-chevron-down" color="download"
|
||||
:value="maindataStore.serverState?.dl_info_speed ?? 0" />
|
||||
</v-col>
|
||||
<v-col class="px-1 pt-1">
|
||||
<SpeedCard icon="mdi-chevron-up" color="upload" :value="maindataStore.serverState?.up_info_speed ?? 0" />
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { TorrentState } from '@/constants/qbit'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useMaindataStore, useTorrentStore, useVueTorrentStore } from '@/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -10,16 +9,18 @@ const { t } = useI18n()
|
|||
const {
|
||||
categories: _categories,
|
||||
tags: _tags,
|
||||
trackers: _trackers,
|
||||
trackers: _trackers
|
||||
} = storeToRefs(useMaindataStore())
|
||||
const {
|
||||
statusFilter,
|
||||
categoryFilter,
|
||||
tagFilter,
|
||||
trackerFilter
|
||||
} = storeToRefs(useMaindataStore())
|
||||
} = storeToRefs(useTorrentStore())
|
||||
const vueTorrentStore = useVueTorrentStore()
|
||||
|
||||
const statuses = computed(() => Object.values(TorrentState).map(state => (
|
||||
{ title: t(`torrent.state.${ state }`), value: state }
|
||||
{ title: t(`torrent.state.${ state }`), value: state }
|
||||
)))
|
||||
const categories = computed(() => [
|
||||
{ title: t('navbar.side.filters.uncategorized'), value: '' },
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import DataCard from '@/components/Core/DataCard.vue'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore } from '@/stores'
|
||||
|
||||
const maindataStore = useMaindataStore()
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { formatSpeed } from '@/helpers'
|
||||
import { useNavbarStore } from '@/stores/navbar'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useNavbarStore, useVueTorrentStore } from '@/stores'
|
||||
import { ApexOptions } from 'apexcharts'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { computed } from 'vue'
|
||||
import DataCard from '@/components/Core/DataCard.vue'
|
||||
import StringCard from '@/components/Core/StringCard.vue'
|
||||
import { useMaindataStore } from '@/stores'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{ session: boolean }>()
|
||||
const maindataStore = useMaindataStore()
|
||||
|
@ -15,7 +15,10 @@ const ratio = computed(() => (props.session ? undefined : maindataStore.serverSt
|
|||
|
||||
<template>
|
||||
<v-card variant="flat" color="primary">
|
||||
<v-card-title class="px-0 pb-0 text-uppercase white--text ml-1 font-weight-normal text-caption">{{ title }}</v-card-title>
|
||||
<v-card-title class="px-0 pb-0 text-uppercase white--text ml-1 font-weight-normal text-caption">{{
|
||||
title
|
||||
}}
|
||||
</v-card-title>
|
||||
<v-card-text class="px-0 pb-0">
|
||||
<div class="d-flex flex-column gap">
|
||||
<DataCard title="Downloaded" :value="download" color="download" icon="mdi-arrow-down" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { useMaindataStore } from '@/stores/maindata.ts'
|
||||
import { useTorrentStore } from '@/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
@ -15,8 +15,8 @@ const {
|
|||
isTagFilterActive,
|
||||
tagFilter,
|
||||
isTrackerFilterActive,
|
||||
trackerFilter,
|
||||
} = storeToRefs(useMaindataStore())
|
||||
trackerFilter
|
||||
} = storeToRefs(useTorrentStore())
|
||||
|
||||
const globalFilterActive = computed(
|
||||
() =>
|
||||
|
@ -35,7 +35,7 @@ const isTrackerFilterPresent = computed(() => trackerFilter.value.length > 0)
|
|||
|
||||
const globalFilterColor = computed(() => globalFilterActive.value ? 'active-global' : 'active-global-disabled')
|
||||
const textFilterColor = computed(() => isTextFilterActive.value ? 'active-text' : 'active-text-disabled')
|
||||
const singleStatusFilterColor = computed(() => isStatusFilterActive.value ? `torrent-${statusFilter.value[0]}` : `torrent-${statusFilter.value[0]}-darken-2`)
|
||||
const singleStatusFilterColor = computed(() => isStatusFilterActive.value ? `torrent-${ statusFilter.value[0] }` : `torrent-${ statusFilter.value[0] }-darken-2`)
|
||||
const statusFilterColor = computed(() => isStatusFilterActive.value ? 'active-status' : 'active-status-disabled')
|
||||
const categoryFilterColor = computed(() => isCategoryFilterActive.value ? 'active-category' : 'active-category-disabled')
|
||||
const tagFilterColor = computed(() => isTagFilterActive.value ? 'active-tag' : 'active-tag-disabled')
|
||||
|
@ -131,7 +131,10 @@ function resetTrackerFilter() {
|
|||
<v-chip v-if="filterPresentCount > 0" v-bind="props" class="ml-6" :color="globalFilterColor" variant="elevated"
|
||||
closable @click:close="resetAllFilters()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleAllFilters()">{{ globalFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleAllFilters()">{{
|
||||
globalFilterActive ? 'mdi-filter' : 'mdi-filter-off'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.menu_label', filterActiveCount) }}
|
||||
</v-chip>
|
||||
|
@ -142,7 +145,10 @@ function resetTrackerFilter() {
|
|||
<v-chip v-if="isTextFilterPresent" :color="textFilterColor" variant="elevated"
|
||||
closable @click:close="resetTextFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTextFilter()">{{ isTextFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleTextFilter()">{{
|
||||
isTextFilterActive ? 'mdi-filter' : 'mdi-filter-off'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.text', { value: textFilter }) }}
|
||||
</v-chip>
|
||||
|
@ -151,14 +157,20 @@ function resetTrackerFilter() {
|
|||
<v-chip v-if="statusFilter.length === 1" :color="singleStatusFilterColor" variant="elevated"
|
||||
closable @click:close="resetStatusFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleStatusFilter()">{{ isStatusFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleStatusFilter()">{{
|
||||
isStatusFilterActive ? 'mdi-filter' : 'mdi-filter-off'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.state', { value: t(`torrent.state.${ statusFilter[0] }`) }) }}
|
||||
</v-chip>
|
||||
<v-chip v-else :color="statusFilterColor" variant="elevated"
|
||||
closable @click:close="resetStatusFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleStatusFilter()">{{ isStatusFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleStatusFilter()">{{
|
||||
isStatusFilterActive ? 'mdi-filter' : 'mdi-filter-off'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_state', statusFilter.length) }}
|
||||
</v-chip>
|
||||
|
@ -168,7 +180,9 @@ function resetTrackerFilter() {
|
|||
<v-chip v-if="categoryFilter.length === 1" :color="categoryFilterColor" variant="elevated"
|
||||
closable @click:close="resetCategoryFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleCategoryFilter()">{{ isCategoryFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleCategoryFilter()">
|
||||
{{ isCategoryFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{
|
||||
t('navbar.top.active_filters.category', { value: categoryFilter[0] === '' ? t('navbar.side.filters.uncategorized') : categoryFilter[0] })
|
||||
|
@ -177,7 +191,9 @@ function resetTrackerFilter() {
|
|||
<v-chip v-else :color="categoryFilterColor" variant="elevated"
|
||||
closable @click:close="resetCategoryFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleCategoryFilter()">{{ isCategoryFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleCategoryFilter()">
|
||||
{{ isCategoryFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_category', categoryFilter.length) }}
|
||||
</v-chip>
|
||||
|
@ -187,7 +203,10 @@ function resetTrackerFilter() {
|
|||
<v-chip v-if="tagFilter.length === 1" :color="tagFilterColor" variant="elevated"
|
||||
closable @click:close="resetTagFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTagFilter()">{{ isTagFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleTagFilter()">{{
|
||||
isTagFilterActive ? 'mdi-filter' : 'mdi-filter-off'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{
|
||||
t('navbar.top.active_filters.tag', { value: tagFilter[0] === null ? t('navbar.side.filters.untagged') : tagFilter[0] })
|
||||
|
@ -196,7 +215,10 @@ function resetTrackerFilter() {
|
|||
<v-chip v-else :color="tagFilterColor" variant="elevated"
|
||||
closable @click:close="resetTagFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTagFilter()">{{ isTagFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleTagFilter()">{{
|
||||
isTagFilterActive ? 'mdi-filter' : 'mdi-filter-off'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_tag', tagFilter.length) }}
|
||||
</v-chip>
|
||||
|
@ -206,7 +228,9 @@ function resetTrackerFilter() {
|
|||
<v-chip v-if="trackerFilter.length === 1" :color="trackerFilterColor" variant="elevated"
|
||||
closable @click:close="resetTrackerFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTrackerFilter()">{{ isTrackerFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleTrackerFilter()">
|
||||
{{ isTrackerFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{
|
||||
t('navbar.top.active_filters.tracker', { value: trackerFilter[0] === '' ? t('navbar.side.filters.untracked') : trackerFilter[0] })
|
||||
|
@ -215,7 +239,9 @@ function resetTrackerFilter() {
|
|||
<v-chip v-else :color="trackerFilterColor" variant="elevated"
|
||||
closable @click:close="resetTrackerFilter()">
|
||||
<template v-slot:prepend>
|
||||
<v-icon class="mr-1" @click="toggleTrackerFilter()">{{ isTrackerFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
||||
<v-icon class="mr-1" @click="toggleTrackerFilter()">
|
||||
{{ isTrackerFilterActive ? 'mdi-filter' : 'mdi-filter-off' }}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t('navbar.top.active_filters.multiple_tracker', trackerFilter.length) }}
|
||||
</v-chip>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
import AddTorrentDialog from '@/components/Dialogs/AddTorrentDialog.vue'
|
||||
import ConfirmDeleteDialog from '@/components/Dialogs/ConfirmDeleteDialog.vue'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useDashboardStore, useDialogStore, useTorrentStore } from '@/stores'
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import TopActions from './TopActions.vue'
|
||||
|
@ -13,7 +11,7 @@ const route = useRoute()
|
|||
const router = useRouter()
|
||||
const dashboardStore = useDashboardStore()
|
||||
const dialogStore = useDialogStore()
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const isOnTorrentDetail = computed(() => route.name === 'torrentDetail')
|
||||
const hashes = computed(() => (isOnTorrentDetail.value ? [route.params.hash as string] : dashboardStore.selectedTorrents))
|
||||
|
@ -23,11 +21,11 @@ function openAddTorrentDialog() {
|
|||
}
|
||||
|
||||
async function resumeTorrents() {
|
||||
await maindataStore.resumeTorrents(hashes.value)
|
||||
await torrentStore.resumeTorrents(hashes.value)
|
||||
}
|
||||
|
||||
async function pauseTorrents() {
|
||||
await maindataStore.pauseTorrents(hashes.value)
|
||||
await torrentStore.pauseTorrents(hashes.value)
|
||||
}
|
||||
|
||||
function deleteTorrents() {
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
UtpTcpMixedMode
|
||||
} from '@/constants/qbit/AppPreferences'
|
||||
import { qbit } from '@/services'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
import { computed, onBeforeMount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -339,7 +339,7 @@ onBeforeMount(async () => {
|
|||
<v-text-field v-model="preferenceStore.preferences!.socket_send_buffer_size" type="number"
|
||||
:label="t('settings.advanced.libtorrent.socketSendBufferSize')"
|
||||
:hint="$t('settings.advanced.libtorrent.socketSendBufferSizeHint')"
|
||||
suffix="kiB" />
|
||||
suffix="kiB" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="4">
|
||||
<v-text-field v-model="preferenceStore.preferences!.socket_receive_buffer_size" type="number"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { FileLogAgeType } from '@/constants/qbit/AppPreferences'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
|
||||
const preferenceStore = usePreferenceStore()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { Encryption, MaxRatioAction } from '@/constants/qbit/AppPreferences'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -25,23 +25,28 @@ const thenTypes = ref([
|
|||
<v-list-subheader>{{ t('settings.bittorrent.privacy.subheader') }}</v-list-subheader>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.dht" hide-details :label="t('settings.bittorrent.privacy.enableDHT')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.dht" hide-details
|
||||
:label="t('settings.bittorrent.privacy.enableDHT')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.pex" hide-details :label="t('settings.bittorrent.privacy.enablePeX')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.pex" hide-details
|
||||
:label="t('settings.bittorrent.privacy.enablePeX')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.lsd" hide-details :label="t('settings.bittorrent.privacy.enableLPD')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.lsd" hide-details
|
||||
:label="t('settings.bittorrent.privacy.enableLPD')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-select v-model="preferenceStore.preferences!.encryption" hide-details :items="encyptionModeOptions" :label="t('settings.bittorrent.privacy.encryptionMode')" />
|
||||
<v-select v-model="preferenceStore.preferences!.encryption" hide-details :items="encyptionModeOptions"
|
||||
:label="t('settings.bittorrent.privacy.encryptionMode')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.anonymous_mode" hide-details :label="t('settings.bittorrent.privacy.enableAnonymous')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.anonymous_mode" hide-details
|
||||
:label="t('settings.bittorrent.privacy.enableAnonymous')" />
|
||||
<a href="https://github.com/qbittorrent/qBittorrent/wiki/Anonymous-Mode" target="_blank">
|
||||
{{ t('settings.bittorrent.privacy.moreInfo') }}
|
||||
</a>
|
||||
|
@ -50,7 +55,8 @@ const thenTypes = ref([
|
|||
<v-divider />
|
||||
|
||||
<v-list-item class="my-3">
|
||||
<v-text-field v-model="preferenceStore.preferences!.max_active_checking_torrents" type="number" hide-details :label="t('settings.bittorrent.maxActiveCheckingTorrents')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.max_active_checking_torrents" type="number" hide-details
|
||||
:label="t('settings.bittorrent.maxActiveCheckingTorrents')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-divider />
|
||||
|
@ -58,7 +64,8 @@ const thenTypes = ref([
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" class="pb-0">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.queueing_enabled" hide-details :label="t('settings.bittorrent.torrentQueueing.subheader')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.queueing_enabled" hide-details
|
||||
:label="t('settings.bittorrent.torrentQueueing.subheader')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
|
@ -134,10 +141,12 @@ const thenTypes = ref([
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.max_ratio_enabled" hide-details :label="t('settings.bittorrent.seedLimits.whenRatioReaches')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.max_ratio_enabled" hide-details
|
||||
:label="t('settings.bittorrent.seedLimits.whenRatioReaches')" />
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-text-field v-model="preferenceStore.preferences!.max_ratio" :disabled="!preferenceStore.preferences!.max_ratio_enabled" type="number" hide-details />
|
||||
<v-text-field v-model="preferenceStore.preferences!.max_ratio"
|
||||
:disabled="!preferenceStore.preferences!.max_ratio_enabled" type="number" hide-details />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -145,7 +154,8 @@ const thenTypes = ref([
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.max_seeding_time_enabled" hide-details :label="t('settings.bittorrent.seedLimits.whenSeedingTimeReaches')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.max_seeding_time_enabled" hide-details
|
||||
:label="t('settings.bittorrent.seedLimits.whenSeedingTimeReaches')" />
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-text-field
|
||||
|
@ -161,7 +171,8 @@ const thenTypes = ref([
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.max_inactive_seeding_time_enabled" hide-details :label="t('settings.bittorrent.seedLimits.whenInactiveSeedingTimeReaches')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.max_inactive_seeding_time_enabled" hide-details
|
||||
:label="t('settings.bittorrent.seedLimits.whenInactiveSeedingTimeReaches')" />
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-text-field
|
||||
|
@ -191,7 +202,8 @@ const thenTypes = ref([
|
|||
<v-divider class="mt-3" />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.add_trackers_enabled" hide-details :label="t('settings.bittorrent.autoAddTrackers')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.add_trackers_enabled" hide-details
|
||||
:label="t('settings.bittorrent.autoAddTrackers')" />
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-textarea
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import PasswordField from '@/components/Core/PasswordField.vue'
|
||||
import { BitTorrentProtocol, ProxyType } from '@/constants/qbit/AppPreferences'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
import { computed, onBeforeMount, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { AppPreferences } from '@/constants/qbit'
|
||||
import { ScanDirs, ScanDirsEnum } from '@/constants/qbit/AppPreferences'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
import { nextTick, onBeforeMount, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -147,25 +147,33 @@ const closeDeleteDialog = async () => {
|
|||
<v-list-subheader>{{ t('settings.downloads.whenAddTorrent.subheader') }}</v-list-subheader>
|
||||
|
||||
<v-list-item>
|
||||
<v-select v-model="preferenceStore.preferences!.torrent_content_layout" hide-details :items="contentLayoutOptions" :label="t('constants.contentLayout.title')" />
|
||||
<v-select v-model="preferenceStore.preferences!.torrent_content_layout" hide-details :items="contentLayoutOptions"
|
||||
:label="t('constants.contentLayout.title')" />
|
||||
|
||||
<v-checkbox v-model="preferenceStore.preferences!.add_to_top_of_queue" hide-details :label="t('settings.downloads.whenAddTorrent.addToTopOfQueue')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.add_to_top_of_queue" hide-details
|
||||
:label="t('settings.downloads.whenAddTorrent.addToTopOfQueue')" />
|
||||
|
||||
<v-checkbox v-model="preferenceStore.preferences!.merge_trackers" hide-details :label="t('settings.downloads.whenAddTorrent.mergeTrackers')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.merge_trackers" hide-details
|
||||
:label="t('settings.downloads.whenAddTorrent.mergeTrackers')" />
|
||||
|
||||
<v-checkbox v-model="preferenceStore.preferences!.start_paused_enabled" hide-details :label="t('settings.downloads.whenAddTorrent.doNotAutoStart')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.start_paused_enabled" hide-details
|
||||
:label="t('settings.downloads.whenAddTorrent.doNotAutoStart')" />
|
||||
|
||||
<v-select v-model="preferenceStore.preferences!.torrent_stop_condition" hide-details :items="stopConditionOptions" :label="t('constants.stopCondition.title')" />
|
||||
<v-select v-model="preferenceStore.preferences!.torrent_stop_condition" hide-details :items="stopConditionOptions"
|
||||
:label="t('constants.stopCondition.title')" />
|
||||
|
||||
<v-checkbox v-model="preferenceStore.preferences!.auto_delete_mode" hide-details :label="t('settings.downloads.whenAddTorrent.autoDeleteMode')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.auto_delete_mode" hide-details
|
||||
:label="t('settings.downloads.whenAddTorrent.autoDeleteMode')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.preallocate_all" hide-details :label="t('settings.downloads.publicSettings.preAllocateDisk')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.preallocate_all" hide-details
|
||||
:label="t('settings.downloads.publicSettings.preAllocateDisk')" />
|
||||
|
||||
<v-checkbox v-model="preferenceStore.preferences!.incomplete_files_ext" hide-details :label="t('settings.downloads.publicSettings.appendQBExtension')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.incomplete_files_ext" hide-details
|
||||
:label="t('settings.downloads.publicSettings.appendQBExtension')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-divider />
|
||||
|
@ -204,7 +212,8 @@ const closeDeleteDialog = async () => {
|
|||
</v-col>
|
||||
|
||||
<v-col cols="12">
|
||||
<v-text-field v-model="preferenceStore.preferences!.save_path" hide-details :label="t('settings.downloads.saveManagement.defaultSavePath')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.save_path" hide-details
|
||||
:label="t('settings.downloads.saveManagement.defaultSavePath')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12">
|
||||
|
@ -265,7 +274,8 @@ const closeDeleteDialog = async () => {
|
|||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field v-model="monitoredFoldersEditedItem.monitoredFolderPath" :label="t('settings.downloads.monitoredFolders.monitoredFolderPath')" />
|
||||
<v-text-field v-model="monitoredFoldersEditedItem.monitoredFolderPath"
|
||||
:label="t('settings.downloads.monitoredFolders.monitoredFolderPath')" />
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-select
|
||||
|
@ -319,7 +329,8 @@ const closeDeleteDialog = async () => {
|
|||
<v-divider />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.excluded_file_names_enabled" hide-details :label="t('settings.downloads.excludedFileNames.label')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.excluded_file_names_enabled" hide-details
|
||||
:label="t('settings.downloads.excludedFileNames.label')" />
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-textarea
|
||||
|
@ -334,7 +345,8 @@ const closeDeleteDialog = async () => {
|
|||
<v-divider />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.mail_notification_enabled" hide-details :label="t('settings.downloads.mailNotification.enabled')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.mail_notification_enabled" hide-details
|
||||
:label="t('settings.downloads.mailNotification.enabled')" />
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-text-field
|
||||
|
@ -403,13 +415,15 @@ const closeDeleteDialog = async () => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.autorun_on_torrent_added_enabled" hide-details :label="t('settings.downloads.runExternalProgram.onAddedEnabled')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.autorun_on_torrent_added_enabled" hide-details
|
||||
:label="t('settings.downloads.runExternalProgram.onAddedEnabled')" />
|
||||
<v-text-field
|
||||
v-model="preferenceStore.preferences!.autorun_on_torrent_added_program"
|
||||
:disabled="!preferenceStore.preferences!.autorun_on_torrent_added_enabled"
|
||||
hide-details
|
||||
:label="t('settings.downloads.runExternalProgram.onAddedLabel')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.autorun_enabled" hide-details :label="t('settings.downloads.runExternalProgram.onFinishedEnabled')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.autorun_enabled" hide-details
|
||||
:label="t('settings.downloads.runExternalProgram.onFinishedEnabled')" />
|
||||
<v-text-field
|
||||
v-model="preferenceStore.preferences!.autorun_program"
|
||||
:disabled="!preferenceStore.preferences!.autorun_enabled"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import RssFeedDialog from '@/components/Dialogs/RssFeedDialog.vue'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useRssStore } from '@/stores/rss'
|
||||
import { useDialogStore, useRssStore } from '@/stores'
|
||||
import { Feed } from '@/types/qbit/models'
|
||||
import { useIntervalFn } from '@vueuse/core'
|
||||
import { onBeforeMount, ref, watch } from 'vue'
|
||||
|
@ -86,7 +85,8 @@ watch(
|
|||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="6" class="d-flex align-center justify-center">
|
||||
<v-btn color="accent" :loading="loading" :disabled="rssStore.feeds.length === 0" :text="$t('settings.rss.feeds.refreshAll')" @click="refreshAllFeeds" />
|
||||
<v-btn color="accent" :loading="loading" :disabled="rssStore.feeds.length === 0"
|
||||
:text="$t('settings.rss.feeds.refreshAll')" @click="refreshAllFeeds" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
|
||||
const preferenceStore = usePreferenceStore()
|
||||
</script>
|
||||
|
@ -10,7 +10,8 @@ const preferenceStore = usePreferenceStore()
|
|||
<v-list-subheader>{{ $t('settings.rss.general.reader.subheader') }}</v-list-subheader>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.rss_processing_enabled" hide-details :label="$t('settings.rss.general.reader.enableProcessing')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.rss_processing_enabled" hide-details
|
||||
:label="$t('settings.rss.general.reader.enableProcessing')" />
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
|
@ -22,7 +23,8 @@ const preferenceStore = usePreferenceStore()
|
|||
:label="$t('settings.rss.general.reader.feedsRefreshInterval')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field v-model="preferenceStore.preferences!.rss_max_articles_per_feed" type="number" :label="$t('settings.rss.general.reader.maximumArticlesPerFeed')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.rss_max_articles_per_feed" type="number"
|
||||
:label="$t('settings.rss.general.reader.maximumArticlesPerFeed')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -31,7 +33,8 @@ const preferenceStore = usePreferenceStore()
|
|||
<v-list-subheader>{{ $t('settings.rss.general.autoDownloader.subheader') }}</v-list-subheader>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.rss_auto_downloading_enabled" hide-details class="ma-0 pa-0" :label="$t('settings.rss.general.autoDownloader.enable')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.rss_auto_downloading_enabled" hide-details class="ma-0 pa-0"
|
||||
:label="$t('settings.rss.general.autoDownloader.enable')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-divider />
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import RssRuleDialog from '@/components/Dialogs/RssRuleDialog.vue'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useRssStore } from '@/stores/rss'
|
||||
import { useDialogStore, useRssStore } from '@/stores'
|
||||
import { FeedRule } from '@/types/qbit/models'
|
||||
import { useIntervalFn } from '@vueuse/core'
|
||||
import { onBeforeMount, ref, watch } from 'vue'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { SchedulerDays } from '@/constants/qbit/AppPreferences'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -30,10 +30,12 @@ const schedulerOptions = ref([
|
|||
|
||||
<v-row class="mx-1">
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="preferenceStore.preferences!.up_limit" hide-details suffix="kiB/s" :label="t('settings.speed.upload')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.up_limit" hide-details suffix="kiB/s"
|
||||
:label="t('settings.speed.upload')" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="preferenceStore.preferences!.dl_limit" hide-details suffix="kiB/s" :label="t('settings.speed.download')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.dl_limit" hide-details suffix="kiB/s"
|
||||
:label="t('settings.speed.download')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
@ -49,10 +51,12 @@ const schedulerOptions = ref([
|
|||
|
||||
<v-row class="mx-1">
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="preferenceStore.preferences!.alt_up_limit" hide-details suffix="kiB/s" :label="t('settings.speed.upload')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.alt_up_limit" hide-details suffix="kiB/s"
|
||||
:label="t('settings.speed.upload')" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="preferenceStore.preferences!.alt_dl_limit" hide-details suffix="kiB/s" :label="t('settings.speed.download')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.alt_dl_limit" hide-details suffix="kiB/s"
|
||||
:label="t('settings.speed.download')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
@ -66,7 +70,8 @@ const schedulerOptions = ref([
|
|||
<v-divider class="mt-2" />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.scheduler_enabled" hide-details :label="t('settings.speed.scheduler.subheader')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.scheduler_enabled" hide-details
|
||||
:label="t('settings.speed.scheduler.subheader')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
|
@ -75,10 +80,12 @@ const schedulerOptions = ref([
|
|||
<v-list-subheader>{{ t('settings.speed.scheduler.from') }}</v-list-subheader>
|
||||
</v-col>
|
||||
<v-col cols="4" md="2">
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_from_hour" :disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_from_hour"
|
||||
:disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
</v-col>
|
||||
<v-col cols="4" md="2">
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_from_min" :disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_from_min"
|
||||
:disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
</v-col>
|
||||
|
||||
<v-spacer />
|
||||
|
@ -87,10 +94,12 @@ const schedulerOptions = ref([
|
|||
<v-list-subheader>{{ t('settings.speed.scheduler.to') }}</v-list-subheader>
|
||||
</v-col>
|
||||
<v-col cols="4" md="2">
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_to_hour" :disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_to_hour"
|
||||
:disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
</v-col>
|
||||
<v-col cols="4" md="2">
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_to_min" :disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.schedule_to_min"
|
||||
:disabled="!preferenceStore.preferences!.scheduler_enabled" type="number" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -109,15 +118,18 @@ const schedulerOptions = ref([
|
|||
<v-list-subheader>{{ t('settings.speed.subheader.settings') }}</v-list-subheader>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.limit_utp_rate" hide-details :label="t('settings.speed.settings.applyToUtp')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.limit_utp_rate" hide-details
|
||||
:label="t('settings.speed.settings.applyToUtp')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.limit_tcp_overhead" hide-details :label="t('settings.speed.settings.applyToTransportOverhead')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.limit_tcp_overhead" hide-details
|
||||
:label="t('settings.speed.settings.applyToTransportOverhead')" />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.limit_lan_peers" hide-details :label="t('settings.speed.settings.applyToPeersOnLan')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.limit_lan_peers" hide-details
|
||||
:label="t('settings.speed.settings.applyToPeersOnLan')" />
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import CategoryFormDialog from '@/components/Dialogs/CategoryFormDialog.vue'
|
||||
import TagFormDialog from '@/components/Dialogs/TagFormDialog.vue'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useDialogStore, useMaindataStore } from '@/stores'
|
||||
import { Category } from '@/types/qbit/models'
|
||||
import { onBeforeMount, ref, watch } from 'vue'
|
||||
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import { TorrentProperty } from '@/types/vuetorrent'
|
||||
import { TorrentProperty } from '@/constants/vuetorrent'
|
||||
|
||||
defineProps<{ property: TorrentProperty }>()
|
||||
defineEmits<{ update: [value: void] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tr class="table-row">
|
||||
<td><v-icon icon="mdi-drag-vertical" class="dnd-handle" /></td>
|
||||
<td><v-btn density="compact" :icon="property.active ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline'" variant="flat" @click="$emit('update')" /></td>
|
||||
<td>{{ $t(`torrent.properties.${property.name}`) }}</td>
|
||||
<td>
|
||||
<v-icon icon="mdi-drag-vertical" class="dnd-handle" />
|
||||
</td>
|
||||
<td>
|
||||
<v-btn density="compact" :icon="property.active ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline'"
|
||||
variant="flat" @click="$emit('update')" />
|
||||
</td>
|
||||
<td>{{ $t(`torrent.properties.${ property.name }`) }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { TitleOptions } from '@/constants/vuetorrent'
|
||||
import { LOCALES } from '@/locales'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useAppStore, useVueTorrentStore } from '@/stores'
|
||||
import { computed, onBeforeMount, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { toast } from 'vue3-toastify'
|
||||
|
@ -86,45 +85,57 @@ onBeforeMount(() => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.showCurrentSpeed" hide-details density="compact" :label="t('settings.vuetorrent.general.showCurrentSpeed')" />
|
||||
<v-checkbox v-model="vueTorrentStore.showCurrentSpeed" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.showCurrentSpeed')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.showSpeedGraph" hide-details density="compact" :label="t('settings.vuetorrent.general.showSpeedGraph')" />
|
||||
<v-checkbox v-model="vueTorrentStore.showSpeedGraph" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.showSpeedGraph')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.showAlltimeStat" hide-details density="compact" :label="t('settings.vuetorrent.general.showAlltimeStat')" />
|
||||
<v-checkbox v-model="vueTorrentStore.showAlltimeStat" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.showAlltimeStat')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.showSessionStat" hide-details density="compact" :label="t('settings.vuetorrent.general.showSessionStat')" />
|
||||
<v-checkbox v-model="vueTorrentStore.showSessionStat" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.showSessionStat')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.showFreeSpace" hide-details density="compact" :label="t('settings.vuetorrent.general.showFreeSpace')" />
|
||||
<v-checkbox v-model="vueTorrentStore.showFreeSpace" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.showFreeSpace')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.showTrackerFilter" hide-details density="compact" :label="t('settings.vuetorrent.general.showTrackerFilter')" />
|
||||
<v-checkbox v-model="vueTorrentStore.showTrackerFilter" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.showTrackerFilter')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.isDrawerRight" hide-details density="compact" :label="t('settings.vuetorrent.general.isDrawerRight')" />
|
||||
<v-checkbox v-model="vueTorrentStore.isDrawerRight" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.isDrawerRight')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.isPaginationOnTop" hide-details density="compact" :label="t('settings.vuetorrent.general.isPaginationOnTop')" />
|
||||
<v-checkbox v-model="vueTorrentStore.isPaginationOnTop" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.isPaginationOnTop')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.openSideBarOnStart" hide-details density="compact" :label="t('settings.vuetorrent.general.openSideBarOnStart')" />
|
||||
<v-checkbox v-model="vueTorrentStore.openSideBarOnStart" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.openSideBarOnStart')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.isShutdownButtonVisible" hide-details density="compact" :label="t('settings.vuetorrent.general.isShutdownButtonVisible')" />
|
||||
<v-checkbox v-model="vueTorrentStore.isShutdownButtonVisible" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.isShutdownButtonVisible')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.useBinarySize" hide-details density="compact" :label="t('settings.vuetorrent.general.useBinarySize')" />
|
||||
<v-checkbox v-model="vueTorrentStore.useBinarySize" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.useBinarySize')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox v-model="vueTorrentStore.useBitSpeed" hide-details density="compact" :label="t('settings.vuetorrent.general.useBitSpeed')" />
|
||||
<v-checkbox v-model="vueTorrentStore.useBitSpeed" hide-details density="compact"
|
||||
:label="t('settings.vuetorrent.general.useBitSpeed')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -132,18 +143,22 @@ onBeforeMount(() => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="vueTorrentStore.refreshInterval" type="number" hide-details suffix="ms" :label="t('settings.vuetorrent.general.refreshInterval')" />
|
||||
<v-text-field v-model="vueTorrentStore.refreshInterval" type="number" hide-details suffix="ms"
|
||||
:label="t('settings.vuetorrent.general.refreshInterval')" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="vueTorrentStore.fileContentInterval" type="number" hide-details suffix="ms" :label="t('settings.vuetorrent.general.fileContentInterval')" />
|
||||
<v-text-field v-model="vueTorrentStore.fileContentInterval" type="number" hide-details suffix="ms"
|
||||
:label="t('settings.vuetorrent.general.fileContentInterval')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="vueTorrentStore.canvasRenderThreshold" type="number" :label="t('settings.vuetorrent.general.canvasRenderThreshold')" />
|
||||
<v-text-field v-model="vueTorrentStore.canvasRenderThreshold" type="number"
|
||||
:label="t('settings.vuetorrent.general.canvasRenderThreshold')" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field v-model="vueTorrentStore.canvasRefreshThreshold" type="number" :label="t('settings.vuetorrent.general.canvasRefreshThreshold')" />
|
||||
<v-text-field v-model="vueTorrentStore.canvasRefreshThreshold" type="number"
|
||||
:label="t('settings.vuetorrent.general.canvasRefreshThreshold')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -151,16 +166,20 @@ onBeforeMount(() => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<v-select v-model="vueTorrentStore.language" flat hide-details :items="LOCALES" :label="t('settings.vuetorrent.general.language')" />
|
||||
<v-select v-model="vueTorrentStore.language" flat hide-details :items="LOCALES"
|
||||
:label="t('settings.vuetorrent.general.language')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<v-select v-model="vueTorrentStore.paginationSize" flat hide-details :items="paginationSizes" :label="t('settings.vuetorrent.general.paginationSize.label')" />
|
||||
<v-select v-model="vueTorrentStore.paginationSize" flat hide-details :items="paginationSizes"
|
||||
:label="t('settings.vuetorrent.general.paginationSize.label')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<v-select v-model="vueTorrentStore.uiTitleType" flat hide-details :items="titleOptionsList" :label="t('settings.vuetorrent.general.vueTorrentTitle')" />
|
||||
<v-select v-model="vueTorrentStore.uiTitleType" flat hide-details :items="titleOptionsList"
|
||||
:label="t('settings.vuetorrent.general.vueTorrentTitle')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<v-text-field :disabled="vueTorrentStore.uiTitleType !== TitleOptions.CUSTOM" v-model="vueTorrentStore.uiTitleCustom" :label="t('settings.vuetorrent.general.customTitle')" />
|
||||
<v-text-field :disabled="vueTorrentStore.uiTitleType !== TitleOptions.CUSTOM"
|
||||
v-model="vueTorrentStore.uiTitleCustom" :label="t('settings.vuetorrent.general.customTitle')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -171,15 +190,21 @@ onBeforeMount(() => {
|
|||
<h3>
|
||||
{{ t('settings.vuetorrent.general.currentVersion') }}
|
||||
<span v-if="!vueTorrentVersion">undefined</span>
|
||||
<a v-else-if="vueTorrentVersion === 'DEV'" target="_blank" href="https://github.com/WDaan/VueTorrent/">{{ vueTorrentVersion }}</a>
|
||||
<a v-else target="_blank" :href="`https://github.com/WDaan/VueTorrent/releases/tag/v${vueTorrentVersion}`">{{ vueTorrentVersion }}</a>
|
||||
<a v-else-if="vueTorrentVersion === 'DEV'" target="_blank"
|
||||
href="https://github.com/WDaan/VueTorrent/">{{ vueTorrentVersion }}</a>
|
||||
<a v-else target="_blank" :href="`https://github.com/WDaan/VueTorrent/releases/tag/v${vueTorrentVersion}`">{{
|
||||
vueTorrentVersion
|
||||
}}</a>
|
||||
</h3>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="3" class="d-flex align-center justify-center">
|
||||
<h3>
|
||||
{{ t('settings.vuetorrent.general.qbittorrentVersion') }}
|
||||
<a target="_blank" :href="`https://github.com/qbittorrent/qBittorrent/releases/tag/release-${appStore.version}`">{{ appStore.version }}</a>
|
||||
<a target="_blank"
|
||||
:href="`https://github.com/qbittorrent/qBittorrent/releases/tag/release-${appStore.version}`">{{
|
||||
appStore.version
|
||||
}}</a>
|
||||
</h3>
|
||||
</v-col>
|
||||
|
||||
|
@ -188,7 +213,8 @@ onBeforeMount(() => {
|
|||
</v-col>
|
||||
|
||||
<v-col cols="12" md="3">
|
||||
<v-text-field v-model="vueTorrentStore.dateFormat" placeholder="DD/MM/YYYY, HH:mm:ss" hint="using Dayjs" :label="t('settings.vuetorrent.general.dateFormat')" />
|
||||
<v-text-field v-model="vueTorrentStore.dateFormat" placeholder="DD/MM/YYYY, HH:mm:ss" hint="using Dayjs"
|
||||
:label="t('settings.vuetorrent.general.dateFormat')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -196,7 +222,10 @@ onBeforeMount(() => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" class="d-flex align-center justify-center">
|
||||
<v-btn color="primary" @click="registerMagnetHandler">{{ t('settings.vuetorrent.general.registerMagnet') }}</v-btn>
|
||||
<v-btn color="primary" @click="registerMagnetHandler">{{
|
||||
t('settings.vuetorrent.general.registerMagnet')
|
||||
}}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="6" class="d-flex align-center justify-center">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import DashboardItem from '@/components/Settings/VueTorrent/DashboardItem.vue'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { TorrentProperty } from '@/types/vuetorrent'
|
||||
import { TorrentProperty } from '@/constants/vuetorrent'
|
||||
import { useVueTorrentStore } from '@/stores'
|
||||
import { computed } from 'vue'
|
||||
import Draggable from 'vuedraggable'
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import PasswordField from '@/components/Core/PasswordField.vue'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from '@/stores'
|
||||
import { ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -36,14 +36,17 @@ watch(webUiPassword, newValue => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="9">
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_address" hide-details :label="t('settings.webUI.interface.ipAddress')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_address" hide-details
|
||||
:label="t('settings.webUI.interface.ipAddress')" />
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_port" hide-details :label="t('settings.webUI.interface.port')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_port" hide-details
|
||||
:label="t('settings.webUI.interface.port')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" class="pt-0">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_upnp" hide-details :label="t('settings.webUI.interface.useUPnP')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_upnp" hide-details
|
||||
:label="t('settings.webUI.interface.useUPnP')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
@ -69,7 +72,8 @@ watch(webUiPassword, newValue => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_username" hide-details :label="t('settings.webUI.authentication.username')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_username" hide-details
|
||||
:label="t('settings.webUI.authentication.username')" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<PasswordField
|
||||
|
@ -82,10 +86,12 @@ watch(webUiPassword, newValue => {
|
|||
</v-col>
|
||||
|
||||
<v-col cols="12" class="py-0">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.bypass_local_auth" hide-details :label="t('settings.webUI.authentication.bypassLocalhost')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.bypass_local_auth" hide-details
|
||||
:label="t('settings.webUI.authentication.bypassLocalhost')" />
|
||||
</v-col>
|
||||
<v-col cols="12" class="pt-0">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.bypass_auth_subnet_whitelist_enabled" hide-details :label="t('settings.webUI.authentication.bypassWhitelist')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.bypass_auth_subnet_whitelist_enabled" hide-details
|
||||
:label="t('settings.webUI.authentication.bypassWhitelist')" />
|
||||
<v-textarea
|
||||
v-model="preferenceStore.preferences!.bypass_auth_subnet_whitelist"
|
||||
:disabled="!preferenceStore.preferences!.bypass_auth_subnet_whitelist_enabled"
|
||||
|
@ -99,7 +105,8 @@ watch(webUiPassword, newValue => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="4">
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_max_auth_fail_count" type="number" hide-details :label="t('settings.webUI.authentication.maxAttempts')" />
|
||||
<v-text-field v-model="preferenceStore.preferences!.web_ui_max_auth_fail_count" type="number" hide-details
|
||||
:label="t('settings.webUI.authentication.maxAttempts')" />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" sm="4">
|
||||
|
@ -127,7 +134,8 @@ watch(webUiPassword, newValue => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="12" class="pb-0">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.use_https" hide-details :label="t('settings.webUI.https.subheader')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.use_https" hide-details
|
||||
:label="t('settings.webUI.https.subheader')" />
|
||||
</v-col>
|
||||
<v-col cols="12" class="pt-0">
|
||||
<v-text-field
|
||||
|
@ -147,7 +155,8 @@ watch(webUiPassword, newValue => {
|
|||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<a href="https://httpd.apache.org/docs/current/ssl/ssl_faq.html#aboutcerts" target="_blank">{{ t('settings.webUI.https.tip') }}</a>
|
||||
<a href="https://httpd.apache.org/docs/current/ssl/ssl_faq.html#aboutcerts"
|
||||
target="_blank">{{ t('settings.webUI.https.tip') }}</a>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider />
|
||||
|
@ -163,7 +172,8 @@ watch(webUiPassword, newValue => {
|
|||
:label="t('settings.webUI.security.clickjacking')" />
|
||||
</v-col>
|
||||
<v-col cols="12" class="py-0">
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_csrf_protection_enabled" hide-details density="compact" :label="t('settings.webUI.security.csrf')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_csrf_protection_enabled" hide-details
|
||||
density="compact" :label="t('settings.webUI.security.csrf')" />
|
||||
</v-col>
|
||||
<v-col cols="12" class="py-0">
|
||||
<v-checkbox
|
||||
|
@ -195,7 +205,8 @@ watch(webUiPassword, newValue => {
|
|||
<v-divider />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_use_custom_http_headers_enabled" hide-details :label="t('settings.webUI.customHeaders')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_use_custom_http_headers_enabled" hide-details
|
||||
:label="t('settings.webUI.customHeaders')" />
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-textarea
|
||||
|
@ -211,7 +222,8 @@ watch(webUiPassword, newValue => {
|
|||
<v-divider />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_reverse_proxy_enabled" hide-details :label="t('settings.webUI.reverseProxySupport')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.web_ui_reverse_proxy_enabled" hide-details
|
||||
:label="t('settings.webUI.reverseProxySupport')" />
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-text-field
|
||||
|
@ -226,15 +238,19 @@ watch(webUiPassword, newValue => {
|
|||
<v-divider />
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox v-model="preferenceStore.preferences!.dyndns_enabled" hide-details :label="t('settings.webUI.dynDns.subheader')" />
|
||||
<v-checkbox v-model="preferenceStore.preferences!.dyndns_enabled" hide-details
|
||||
:label="t('settings.webUI.dynDns.subheader')" />
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="8">
|
||||
<v-select v-model="dynDnsProvider" :disabled="!preferenceStore.preferences!.dyndns_enabled" density="compact" hide-details :items="dynDnsProviderOptions" />
|
||||
<v-select v-model="dynDnsProvider" :disabled="!preferenceStore.preferences!.dyndns_enabled" density="compact"
|
||||
hide-details :items="dynDnsProviderOptions" />
|
||||
</v-col>
|
||||
<v-col cols="4">
|
||||
<v-btn :disabled="!preferenceStore.preferences!.dyndns_enabled" @click="registerDynDNS">{{ $t('settings.webUI.dynDns.registerBtn') }}</v-btn>
|
||||
<v-btn :disabled="!preferenceStore.preferences!.dyndns_enabled" @click="registerDynDNS">
|
||||
{{ $t('settings.webUI.dynDns.registerBtn') }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
|
|
@ -3,9 +3,7 @@ import MoveTorrentFileDialog from '@/components/Dialogs/MoveTorrentFileDialog.vu
|
|||
import RootNode from '@/components/TorrentDetail/Content/RootNode.vue'
|
||||
import { useTreeBuilder } from '@/composables'
|
||||
import { FilePriority } from '@/constants/qbit'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useDialogStore, useMaindataStore, useVueTorrentStore } from '@/stores'
|
||||
import { TorrentFile } from '@/types/qbit/models'
|
||||
import { Torrent, TreeNode } from '@/types/vuetorrent'
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||
|
@ -35,15 +33,15 @@ const fileSelection = computed({
|
|||
const oldValue = cachedFiles.value.filter(f => f.priority !== FilePriority.DO_NOT_DOWNLOAD).map(f => f.index)
|
||||
|
||||
const filesToExclude = oldValue
|
||||
.filter(index => !newValue.includes(index))
|
||||
.map(index => cachedFiles.value.find(f => f.index === index))
|
||||
.filter(f => f && f.priority !== FilePriority.DO_NOT_DOWNLOAD)
|
||||
.map(f => (f as TorrentFile).index)
|
||||
.filter(index => !newValue.includes(index))
|
||||
.map(index => cachedFiles.value.find(f => f.index === index))
|
||||
.filter(f => f && f.priority !== FilePriority.DO_NOT_DOWNLOAD)
|
||||
.map(f => (f as TorrentFile).index)
|
||||
const filesToInclude = newValue
|
||||
.filter(index => !oldValue.includes(index))
|
||||
.map(index => cachedFiles.value.find(f => f.index === index))
|
||||
.filter(f => f && f.priority === FilePriority.DO_NOT_DOWNLOAD)
|
||||
.map(f => (f as TorrentFile).index)
|
||||
.filter(index => !oldValue.includes(index))
|
||||
.map(index => cachedFiles.value.find(f => f.index === index))
|
||||
.filter(f => f && f.priority === FilePriority.DO_NOT_DOWNLOAD)
|
||||
.map(f => (f as TorrentFile).index)
|
||||
|
||||
if (filesToExclude.length) {
|
||||
await maindataStore.setTorrentFilePriority(props.torrent.hash, filesToExclude, FilePriority.DO_NOT_DOWNLOAD)
|
||||
|
@ -106,7 +104,8 @@ onMounted(() => {
|
|||
|
||||
<template>
|
||||
<v-card :loading="loading" flat>
|
||||
<RootNode v-model:opened="openedItems" v-model:selected="fileSelection" :root="tree" @renameFolder="renameNode" @renameFile="renameNode" />
|
||||
<RootNode v-model:opened="openedItems" v-model:selected="fileSelection" :root="tree" @renameFolder="renameNode"
|
||||
@renameFile="renameNode" />
|
||||
<!--
|
||||
TODO: add treeview after merge
|
||||
https://github.com/vuetifyjs/vuetify/issues/13518
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import { FilePriority } from '@/constants/qbit'
|
||||
import { getFileIcon } from '@/constants/vuetorrent'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { formatData, formatPercent } from '@/helpers'
|
||||
import { useVueTorrentStore } from '@/stores'
|
||||
import { TreeFile } from '@/types/vuetorrent'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { formatData, formatPercent } from '@/helpers'
|
||||
|
||||
defineProps<{
|
||||
node: TreeFile
|
||||
|
@ -29,7 +29,7 @@ function getNodePriority(node: TreeFile) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<v-list-item :title="node.name" :value="node.index" :prepend-icon="getFileIcon(node)">
|
||||
<v-list-item :title="node.name" :value="node.index" :prepend-icon="getFileIcon(node.name)">
|
||||
<template v-slot:append>
|
||||
<span class="mr-2">[ {{ formatData(node.size, vuetorrentStore.useBinarySize) }} ]</span>
|
||||
<span class="mr-2">{{ formatPercent(node.progress) }}</span>
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import InfoBase from '@/components/TorrentDetail/InfoBase.vue'
|
||||
import { formatData, formatSpeed } from '@/helpers'
|
||||
import dayjs from '@/plugins/dayjs'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useMaindataStore, useTorrentStore, useVueTorrentStore } from '@/stores'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { computed } from 'vue'
|
||||
import { formatData, formatSpeed } from '@/helpers'
|
||||
|
||||
const props = defineProps<{ torrent: Torrent; isActive: boolean }>()
|
||||
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
const vuetorrentStore = useVueTorrentStore()
|
||||
|
||||
const auto_tmm = computed({
|
||||
|
@ -26,9 +26,9 @@ const forced = computed({
|
|||
get: () => props.torrent.forced,
|
||||
set: value => {
|
||||
if (value) {
|
||||
maindataStore.forceResumeTorrents([props.torrent.hash])
|
||||
torrentStore.forceResumeTorrents([props.torrent.hash])
|
||||
} else {
|
||||
maindataStore.resumeTorrents([props.torrent.hash])
|
||||
torrentStore.resumeTorrents([props.torrent.hash])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -113,9 +113,11 @@ const longTextPpts = [
|
|||
<v-expansion-panel-text>
|
||||
<v-row>
|
||||
<InfoBase v-for="ppt in datetimePpts">
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ppt.title}`) }}</template>
|
||||
<template v-if="torrent[ppt.text] > 0" v-slot:text>{{ dayjs(torrent[ppt.text] * 1000).format(vuetorrentStore.dateFormat ?? 'DD/MM/YYYY, HH:mm:ss') }} </template>
|
||||
<template v-else v-slot:text> {{ $t('common.NA') }} </template>
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ ppt.title }`) }}</template>
|
||||
<template v-if="torrent[ppt.text] > 0" v-slot:text>
|
||||
{{ dayjs(torrent[ppt.text] * 1000).format(vuetorrentStore.dateFormat ?? 'DD/MM/YYYY, HH:mm:ss') }}
|
||||
</template>
|
||||
<template v-else v-slot:text> {{ $t('common.NA') }}</template>
|
||||
</InfoBase>
|
||||
</v-row>
|
||||
</v-expansion-panel-text>
|
||||
|
@ -125,7 +127,7 @@ const longTextPpts = [
|
|||
<v-expansion-panel-text>
|
||||
<v-row>
|
||||
<InfoBase v-for="ppt in durationPpts">
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ppt.title}`) }}</template>
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ ppt.title }`) }}</template>
|
||||
<template v-slot:text>{{ dayjs.duration(torrent[ppt.text], 's').humanize() }}</template>
|
||||
</InfoBase>
|
||||
</v-row>
|
||||
|
@ -137,31 +139,36 @@ const longTextPpts = [
|
|||
<v-row>
|
||||
<InfoBase>
|
||||
<template v-slot:title>
|
||||
<v-checkbox v-model="auto_tmm" hide-details density="compact" :label="$t('torrent.properties.auto_tmm')" />
|
||||
<v-checkbox v-model="auto_tmm" hide-details density="compact"
|
||||
:label="$t('torrent.properties.auto_tmm')" />
|
||||
</template>
|
||||
</InfoBase>
|
||||
|
||||
<InfoBase>
|
||||
<template v-slot:title>
|
||||
<v-checkbox v-model="f_l_piece_prio" hide-details density="compact" :label="$t('torrent.properties.f_l_piece_prio')" />
|
||||
<v-checkbox v-model="f_l_piece_prio" hide-details density="compact"
|
||||
:label="$t('torrent.properties.f_l_piece_prio')" />
|
||||
</template>
|
||||
</InfoBase>
|
||||
|
||||
<InfoBase>
|
||||
<template v-slot:title>
|
||||
<v-checkbox v-model="forced" hide-details density="compact" :label="$t('torrent.properties.forced')" />
|
||||
<v-checkbox v-model="forced" hide-details density="compact"
|
||||
:label="$t('torrent.properties.forced')" />
|
||||
</template>
|
||||
</InfoBase>
|
||||
|
||||
<InfoBase>
|
||||
<template v-slot:title>
|
||||
<v-checkbox v-model="seq_dl" hide-details density="compact" :label="$t('torrent.properties.seq_dl')" />
|
||||
<v-checkbox v-model="seq_dl" hide-details density="compact"
|
||||
:label="$t('torrent.properties.seq_dl')" />
|
||||
</template>
|
||||
</InfoBase>
|
||||
|
||||
<InfoBase>
|
||||
<template v-slot:title>
|
||||
<v-checkbox v-model="super_seeding" hide-details density="compact" :label="$t('torrent.properties.super_seeding')" />
|
||||
<v-checkbox v-model="super_seeding" hide-details density="compact"
|
||||
:label="$t('torrent.properties.super_seeding')" />
|
||||
</template>
|
||||
</InfoBase>
|
||||
</v-row>
|
||||
|
@ -172,7 +179,7 @@ const longTextPpts = [
|
|||
<v-expansion-panel-text>
|
||||
<v-row>
|
||||
<InfoBase v-for="ppt in dataPpts">
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ppt.title}`) }}</template>
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ ppt.title }`) }}</template>
|
||||
<template v-slot:text>{{ formatData(torrent[ppt.text], vuetorrentStore.useBinarySize) }}</template>
|
||||
</InfoBase>
|
||||
</v-row>
|
||||
|
@ -183,7 +190,7 @@ const longTextPpts = [
|
|||
<v-expansion-panel-text>
|
||||
<v-row>
|
||||
<InfoBase v-for="ppt in speedPpts">
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ppt.title}`) }}</template>
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ ppt.title }`) }}</template>
|
||||
<template v-slot:text>{{ formatSpeed(torrent[ppt.text], vuetorrentStore.useBitSpeed) }}</template>
|
||||
</InfoBase>
|
||||
</v-row>
|
||||
|
@ -194,7 +201,7 @@ const longTextPpts = [
|
|||
<v-expansion-panel-text>
|
||||
<v-row>
|
||||
<InfoBase v-for="ppt in textPpts">
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ppt.title}`) }}</template>
|
||||
<template v-slot:title>{{ $t(`torrent.properties.${ ppt.title }`) }}</template>
|
||||
<template v-slot:text>{{ torrent[ppt.text] }}</template>
|
||||
</InfoBase>
|
||||
</v-row>
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import MoveTorrentDialog from '@/components/Dialogs/MoveTorrentDialog.vue'
|
||||
import MoveTorrentFileDialog from '@/components/Dialogs/MoveTorrentFileDialog.vue'
|
||||
import { PieceState, FilePriority, TorrentState } from '@/constants/qbit'
|
||||
import { formatData, formatDataUnit, formatDataValue, formatPercent, formatSpeed, getDomainBody, splitByUrl, stringContainsUrl } from '@/helpers'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { FilePriority, PieceState, TorrentState } from '@/constants/qbit'
|
||||
import {
|
||||
formatData,
|
||||
formatDataUnit,
|
||||
formatDataValue,
|
||||
formatPercent,
|
||||
formatSpeed,
|
||||
getDomainBody,
|
||||
splitByUrl,
|
||||
stringContainsUrl
|
||||
} from '@/helpers'
|
||||
import { useDialogStore, useMaindataStore, useTorrentStore, useVueTorrentStore } from '@/stores'
|
||||
import { TorrentFile } from '@/types/qbit/models'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
@ -18,6 +25,7 @@ const { t } = useI18n()
|
|||
const theme = useTheme()
|
||||
const dialogStore = useDialogStore()
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
const vuetorrentStore = useVueTorrentStore()
|
||||
|
||||
const canvas = ref<HTMLCanvasElement>()
|
||||
|
@ -33,14 +41,14 @@ const torrentPieceOwned = ref(0)
|
|||
const torrentPieceCount = ref(0)
|
||||
const uploadSpeedAvg = ref(0)
|
||||
|
||||
const torrentStateColor = computed(() => `torrent-${props.torrent.state}`)
|
||||
const pieceSize = computed(() => `${parseInt(formatDataValue(torrentPieceSize.value, true))} ${formatDataUnit(torrentPieceSize.value, true)}`)
|
||||
const torrentStateColor = computed(() => `torrent-${ props.torrent.state }`)
|
||||
const pieceSize = computed(() => `${ parseInt(formatDataValue(torrentPieceSize.value, true)) } ${ formatDataUnit(torrentPieceSize.value, true) }`)
|
||||
const isFetchingMetadata = computed(() => props.torrent.state === TorrentState.META_DL)
|
||||
const shouldRenderPieceState = computed(() => !isFetchingMetadata.value && torrentPieceCount.value > 0 && torrentPieceCount.value < vuetorrentStore.canvasRenderThreshold)
|
||||
const shouldRefreshPieceState = computed(() => shouldRenderPieceState.value && torrentPieceCount.value < vuetorrentStore.canvasRefreshThreshold)
|
||||
|
||||
async function getTorrentProperties() {
|
||||
const ppts = await maindataStore.getTorrentProperties(props.torrent.hash)
|
||||
const ppts = await torrentStore.getTorrentProperties(props.torrent.hash)
|
||||
comment.value = ppts.comment
|
||||
downloadSpeedAvg.value = ppts.dl_speed_avg
|
||||
torrentPieceCount.value = ppts.pieces_num
|
||||
|
@ -122,7 +130,11 @@ function openMoveTorrentDialog() {
|
|||
}
|
||||
|
||||
function openMoveTorrentFileDialog() {
|
||||
dialogStore.createDialog(MoveTorrentFileDialog, { hash: props.torrent.hash, isFolder: false, oldName: torrentFileName.value })
|
||||
dialogStore.createDialog(MoveTorrentFileDialog, {
|
||||
hash: props.torrent.hash,
|
||||
isFolder: false,
|
||||
oldName: torrentFileName.value
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
|
@ -157,7 +169,8 @@ watch(
|
|||
<v-col cols="12" md="6">
|
||||
<v-row>
|
||||
<v-col cols="4" md="4">
|
||||
<v-progress-circular :color="torrentStateColor" :indeterminate="isFetchingMetadata" :size="100" :model-value="torrent?.progress * 100 ?? 0" :width="15">
|
||||
<v-progress-circular :color="torrentStateColor" :indeterminate="isFetchingMetadata" :size="100"
|
||||
:model-value="torrent?.progress * 100 ?? 0" :width="15">
|
||||
<template v-slot>
|
||||
<span v-if="isFetchingMetadata">{{ $t('torrentDetail.overview.fetchingMetadata') }}</span>
|
||||
<v-icon v-else-if="torrent.progress === 1" icon="mdi-check" size="x-large" />
|
||||
|
@ -212,7 +225,8 @@ watch(
|
|||
<div>{{ $t('torrentDetail.overview.fileCount') }}:</div>
|
||||
<div>{{ selectedFileCount }} / {{ torrentFileCount }}</div>
|
||||
<div v-if="selectedFileCount === 1">{{ torrentFileName }}</div>
|
||||
<v-btn v-if="selectedFileCount === 1" icon="mdi-pencil" color="accent" size="x-small" @click="openMoveTorrentFileDialog" />
|
||||
<v-btn v-if="selectedFileCount === 1" icon="mdi-pencil" color="accent" size="x-small"
|
||||
@click="openMoveTorrentFileDialog" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
|
@ -220,7 +234,7 @@ watch(
|
|||
<v-row>
|
||||
<v-col cols="6">
|
||||
<div>{{ $t('torrent.properties.state') }}:</div>
|
||||
<v-chip variant="flat" :color="torrentStateColor">{{ $t(`torrent.state.${torrent.state}`) }}</v-chip>
|
||||
<v-chip variant="flat" :color="torrentStateColor">{{ $t(`torrent.state.${ torrent.state }`) }}</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<div>{{ $t('torrent.properties.category') }}:</div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { codeToFlag, formatData, formatPercent, formatSpeed, isWindows } from '@/helpers'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useMaindataStore, useVueTorrentStore } from '@/stores'
|
||||
import { Peer } from '@/types/qbit/models'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { onBeforeMount, onUnmounted, ref, watch } from 'vue'
|
||||
|
@ -73,7 +72,8 @@ watch(() => props.isActive, setupTimer)
|
|||
<div>
|
||||
<v-list-item-title class="overflow-visible text-select">
|
||||
<span v-if="peer.country_code">
|
||||
<img v-if="isWindows" :alt="codeToFlag(peer.country_code).char" :src="codeToFlag(peer.country_code).url" :title="peer.country" style="max-width: 32px" />
|
||||
<img v-if="isWindows" :alt="codeToFlag(peer.country_code).char" :src="codeToFlag(peer.country_code).url"
|
||||
:title="peer.country" style="max-width: 32px" />
|
||||
<span v-else :title="peer.country">{{ codeToFlag(peer.country_code).char }}</span>
|
||||
</span>
|
||||
<span>{{ peer.ip }}</span>
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore, useTorrentStore } from '@/stores'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { computed, onBeforeMount } from 'vue'
|
||||
|
||||
const props = defineProps<{ torrent: Torrent; isActive: boolean }>()
|
||||
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const activeCategory = computed(() => maindataStore.categories.map(cat => cat.name).indexOf(props.torrent.category))
|
||||
const activeTags = computed(() => maindataStore.tags.filter(tag => props.torrent.tags?.includes(tag)))
|
||||
|
||||
async function setCategory(category: string) {
|
||||
if (props.torrent.category === category) {
|
||||
await maindataStore.setTorrentCategory([props.torrent.hash], '')
|
||||
await torrentStore.setTorrentCategory([props.torrent.hash], '')
|
||||
} else {
|
||||
await maindataStore.setTorrentCategory([props.torrent.hash], category)
|
||||
await torrentStore.setTorrentCategory([props.torrent.hash], category)
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleTag(tag: string) {
|
||||
if (props.torrent.tags?.includes(tag)) {
|
||||
await maindataStore.removeTorrentTags([props.torrent.hash], [tag])
|
||||
await torrentStore.removeTorrentTags([props.torrent.hash], [tag])
|
||||
} else {
|
||||
await maindataStore.addTorrentTags([props.torrent.hash], [tag])
|
||||
await torrentStore.addTorrentTags([props.torrent.hash], [tag])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { TrackerStatus } from '@/constants/qbit'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useMaindataStore } from '@/stores'
|
||||
import { Tracker } from '@/types/qbit/models'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { nextTick, onBeforeMount, onUnmounted, reactive, ref, watch } from 'vue'
|
||||
|
@ -162,7 +162,8 @@ watch(() => props.isActive, setupTimer)
|
|||
|
||||
<v-card-text>
|
||||
<v-form v-model="editTrackerDialog.isFormValid" @submit.prevent>
|
||||
<v-text-field :model-value="editTrackerDialog.oldUrl" disabled :label="$t('torrentDetail.trackers.editTracker.oldUrl')" />
|
||||
<v-text-field :model-value="editTrackerDialog.oldUrl" disabled
|
||||
:label="$t('torrentDetail.trackers.editTracker.oldUrl')" />
|
||||
<v-text-field
|
||||
v-model="editTrackerDialog.newUrl"
|
||||
id="input"
|
||||
|
@ -175,7 +176,9 @@ watch(() => props.isActive, setupTimer)
|
|||
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn color="error" :disabled="!editTrackerDialog.isFormValid" @click="editTrackerDialog.isVisible = false">{{ t('common.cancel') }}</v-btn>
|
||||
<v-btn color="error" :disabled="!editTrackerDialog.isFormValid"
|
||||
@click="editTrackerDialog.isVisible = false">{{ t('common.cancel') }}
|
||||
</v-btn>
|
||||
<v-btn color="accent" @click="editTracker">{{ t('common.ok') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
@ -218,7 +221,8 @@ watch(() => props.isActive, setupTimer)
|
|||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-btn variant="flat" :disabled="torrentTrackers.length === 3" :text="t('torrentDetail.trackers.reannounce')" color="primary" @click="reannounceTrackers" />
|
||||
<v-btn variant="flat" :disabled="torrentTrackers.length === 3" :text="t('torrentDetail.trackers.reannounce')"
|
||||
color="primary" @click="reannounceTrackers" />
|
||||
</div>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
@ -233,15 +237,19 @@ watch(() => props.isActive, setupTimer)
|
|||
.tracker-disabled {
|
||||
color: darken(lightgrey, 5%);
|
||||
}
|
||||
|
||||
.tracker-not_yet_contacted {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.tracker-working {
|
||||
color: lightgreen;
|
||||
}
|
||||
|
||||
.tracker-not_working {
|
||||
color: lightcoral;
|
||||
}
|
||||
|
||||
.tracker-updating {
|
||||
color: lightblue;
|
||||
}
|
||||
|
@ -251,15 +259,19 @@ watch(() => props.isActive, setupTimer)
|
|||
.tracker-disabled {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.tracker-not_yet_contacted {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.tracker-working {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.tracker-not_working {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.tracker-updating {
|
||||
color: dodgerblue;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
import { FilePriority, TorrentState } from '@/constants/qbit'
|
||||
import { formatEta, getDomainBody } from '@/helpers'
|
||||
import { useMaindataStore } from '@/stores/maindata.ts'
|
||||
import { Torrent as QbitTorrent } from '@/types/qbit/models'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { faker } from '@faker-js/faker/locale/en'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
type StaticTorrent = Omit<Torrent, 'avgDownloadSpeed' | 'avgUploadSpeed' | 'globalSpeed' | 'globalVolume'>
|
||||
|
||||
export function useTorrentBuilder() {
|
||||
const { t } = useI18n()
|
||||
const maindataStore = useMaindataStore()
|
||||
|
||||
const categories = computed(() => maindataStore.categories.map(value => value.name) || ['ISO', 'Other', 'Movie', 'Music', 'TV'])
|
||||
|
||||
const computedValues = ['avgDownloadSpeed', 'avgUploadSpeed', 'globalSpeed', 'globalVolume', 'priority']
|
||||
|
||||
|
@ -86,7 +81,7 @@ export function useTorrentBuilder() {
|
|||
availability: data.availability || faker.number.float({ min: 0, max: 100, precision: 0.01 }),
|
||||
available_peers,
|
||||
available_seeds,
|
||||
category: data.category || faker.helpers.arrayElement(categories.value),
|
||||
category: data.category || faker.helpers.arrayElement(['ISO', 'Other', 'Movie', 'Music', 'TV']),
|
||||
completed_on: data.completed_on || faker.date.between({ from: added_on, to: Date.now() }),
|
||||
content_path: data.content_path || faker.system.filePath(),
|
||||
dl_limit: data.dl_limit || faker.number.float({ min: 0, max: 1, precision: 0.01 }),
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
import * as AppPreferences from './AppPreferences'
|
||||
import { ConnectionStatus } from './ConnectionStatus'
|
||||
import { FilePriority } from './FilePriority'
|
||||
import { FilterState } from './FilterState'
|
||||
import { LogType } from './LogType'
|
||||
import { PieceState } from './PieceState'
|
||||
import { FilePriority } from './FilePriority'
|
||||
import { SortOptions } from './SortOptions'
|
||||
import { TorrentOperatingMode } from './TorrentOperatingMode'
|
||||
import { TorrentState } from './TorrentState'
|
||||
import { TrackerStatus } from './TrackerStatus'
|
||||
|
||||
export { AppPreferences, ConnectionStatus, FilterState, LogType, PieceState, FilePriority, TrackerStatus, TorrentOperatingMode, TorrentState }
|
||||
export {
|
||||
AppPreferences,
|
||||
ConnectionStatus,
|
||||
FilterState,
|
||||
LogType,
|
||||
PieceState,
|
||||
FilePriority,
|
||||
SortOptions,
|
||||
TrackerStatus,
|
||||
TorrentOperatingMode,
|
||||
TorrentState
|
||||
}
|
||||
|
|
|
@ -1,6 +1,27 @@
|
|||
import { DashboardProperty } from './DashboardProperty'
|
||||
import { DashboardPropertyType } from './DashboardPropertyType'
|
||||
import { PropertyData, PropertyMetadata } from '@/types/vuetorrent'
|
||||
|
||||
type pptData = { active: boolean; order: number }
|
||||
type pptMetadata =
|
||||
| { type: DashboardPropertyType.AMOUNT; props: { title: string; value: string; total: string } }
|
||||
| { type: DashboardPropertyType.CHIP; props: { title: string; value: string; color: string } }
|
||||
| { type: DashboardPropertyType.DATA; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.DATETIME; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.DURATION; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.PERCENT; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.RELATIVE; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.SPEED; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.TEXT; props: { title: string; value: string } }
|
||||
|
||||
export type TorrentProperty = { name: DashboardProperty } & pptData & pptMetadata
|
||||
|
||||
export interface PropertyData {
|
||||
[key: DashboardProperty | string]: pptData
|
||||
}
|
||||
|
||||
export interface PropertyMetadata {
|
||||
[key: DashboardProperty | string]: pptMetadata
|
||||
}
|
||||
|
||||
export const propsData: PropertyData = {
|
||||
[DashboardProperty.ADDED_ON]: {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { TreeFile } from '@/types/vuetorrent'
|
||||
|
||||
enum FileIcon {
|
||||
PDF = 'mdi-file-pdf-box',
|
||||
IMAGE = 'mdi-file-image',
|
||||
|
@ -53,7 +51,7 @@ export const typesMap: Record<string, FileIcon> = {
|
|||
jar: FileIcon.EXECUTABLE
|
||||
}
|
||||
|
||||
export function getFileIcon(file: TreeFile) {
|
||||
const type = file.name.split('.').pop()?.toLowerCase() || ''
|
||||
export function getFileIcon(filename: string) {
|
||||
const type = filename.split('.').pop()?.toLowerCase() || ''
|
||||
return typesMap[type] || 'mdi-file'
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import type { TorrentProperty, PropertyData, PropertyMetadata } from './DashboardDefaults'
|
||||
import { propsData, propsMetadata } from './DashboardDefaults'
|
||||
import { DashboardProperty } from './DashboardProperty'
|
||||
import { DashboardPropertyType } from './DashboardPropertyType'
|
||||
import { typesMap, getFileIcon } from './FileIcon'
|
||||
import { TitleOptions } from './TitleOptions'
|
||||
|
||||
export { propsData, propsMetadata, DashboardProperty, DashboardPropertyType, typesMap, getFileIcon, TitleOptions }
|
||||
export { TorrentProperty, PropertyData, PropertyMetadata, propsData, propsMetadata, DashboardProperty, DashboardPropertyType, typesMap, getFileIcon, TitleOptions }
|
||||
|
|
|
@ -4,10 +4,7 @@ import RightClickMenu from '@/components/Dashboard/TRC/RightClickMenu.vue'
|
|||
import ConfirmDeleteDialog from '@/components/Dialogs/ConfirmDeleteDialog.vue'
|
||||
import { useArrayPagination } from '@/composables'
|
||||
import { doesCommand } from '@/helpers'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useDashboardStore, useDialogStore, useMaindataStore, useTorrentStore, useVueTorrentStore } from '@/stores'
|
||||
import { Torrent as TorrentType } from '@/types/vuetorrent'
|
||||
import debounce from 'lodash.debounce'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
@ -23,13 +20,13 @@ const {
|
|||
currentPage: dashboardPage,
|
||||
isSelectionMultiple,
|
||||
selectedTorrents,
|
||||
sortOptions,
|
||||
torrentCountString
|
||||
} = storeToRefs(useDashboardStore())
|
||||
const dashboardStore = useDashboardStore()
|
||||
const dialogStore = useDialogStore()
|
||||
const { filteredTorrents } = storeToRefs(useMaindataStore())
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
const { filteredTorrents, sortOptions } = storeToRefs(torrentStore)
|
||||
const vuetorrentStore = useVueTorrentStore()
|
||||
|
||||
const torrentSortOptions = [
|
||||
|
@ -96,9 +93,9 @@ const trcProperties = reactive({
|
|||
})
|
||||
|
||||
const torrentTitleFilter = computed({
|
||||
get: () => maindataStore.textFilter,
|
||||
get: () => torrentStore.textFilter,
|
||||
set: debounce((newValue: string | null) => {
|
||||
maindataStore.textFilter = newValue ?? ''
|
||||
torrentStore.textFilter = newValue ?? ''
|
||||
}, 300)
|
||||
})
|
||||
|
||||
|
@ -107,7 +104,7 @@ const {
|
|||
currentPage,
|
||||
pageCount
|
||||
} = useArrayPagination(filteredTorrents, vuetorrentStore.paginationSize, dashboardPage)
|
||||
const hasSearchFilter = computed(() => !!maindataStore.textFilter && maindataStore.textFilter.length > 0)
|
||||
const hasSearchFilter = computed(() => !!torrentStore.textFilter && torrentStore.textFilter.length > 0)
|
||||
|
||||
const isAllTorrentsSelected = computed(() => filteredTorrents.value.length <= selectedTorrents.value.length)
|
||||
|
||||
|
@ -132,7 +129,7 @@ function goToInfo(hash: string) {
|
|||
}
|
||||
|
||||
function resetInput() {
|
||||
maindataStore.textFilter = ''
|
||||
torrentStore.textFilter = ''
|
||||
}
|
||||
|
||||
function scrollToTop() {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import PasswordField from '@/components/Core/PasswordField.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { onMounted, reactive, ref, watchEffect } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { toast } from 'vue3-toastify'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAuthStore } from '@/stores'
|
||||
|
||||
import { LoginPayload } from '@/types/qbit/payloads'
|
||||
import { onMounted, reactive, ref, watchEffect } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { toast } from 'vue3-toastify'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
|
@ -68,7 +68,8 @@ watchEffect(() => {
|
|||
<v-card-subtitle>{{ t('login.subtitle') }}</v-card-subtitle>
|
||||
<v-card-text>
|
||||
<v-form v-model="rulesOk" @submit.prevent="login">
|
||||
<v-text-field v-model="loginForm.username" :label="t('login.username')" autofocus :rules="rules.username" @keydown.enter.prevent="login" variant="outlined">
|
||||
<v-text-field v-model="loginForm.username" :label="t('login.username')" autofocus :rules="rules.username"
|
||||
@keydown.enter.prevent="login" variant="outlined">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="accent" icon="mdi-account" />
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useArrayPagination } from '@/composables'
|
||||
import { LogType } from '@/constants/qbit'
|
||||
import { useLogStore } from '@/stores/logs'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { useLogStore, useVueTorrentStore } from '@/stores'
|
||||
import { Log } from '@/types/qbit/models'
|
||||
import { useIntervalFn } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
|
@ -42,7 +41,7 @@ const goHome = () => {
|
|||
router.push({ name: 'dashboard' })
|
||||
}
|
||||
const getLogTypeClassName = (log: Log) => {
|
||||
return `logtype-${LogType[log?.type]?.toLowerCase()}`
|
||||
return `logtype-${ LogType[log?.type]?.toLowerCase() }`
|
||||
}
|
||||
const getLogTypeName = (log: Log) => {
|
||||
return LogType[log.type]
|
||||
|
@ -92,11 +91,13 @@ onUnmounted(() => {
|
|||
<v-list-item>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-select v-model="logTypeFilter" :items="logTypeOptions" :label="$t('logs.filters.type')" hide-details multiple chips>
|
||||
<v-select v-model="logTypeFilter" :items="logTypeOptions" :label="$t('logs.filters.type')" hide-details
|
||||
multiple chips>
|
||||
<template v-slot:prepend-item>
|
||||
<v-list-item :title="$t('common.selectAll')" @click="toggleSelectAll">
|
||||
<template v-slot:prepend>
|
||||
<v-checkbox-btn :indeterminate="someTypesSelected && !allTypesSelected" :model-value="someTypesSelected" />
|
||||
<v-checkbox-btn :indeterminate="someTypesSelected && !allTypesSelected"
|
||||
:model-value="someTypesSelected" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
|
@ -105,7 +106,8 @@ onUnmounted(() => {
|
|||
</v-col>
|
||||
|
||||
<v-col cols="6">
|
||||
<v-select v-model="sortBy" :items="headers" :label="$t('logs.filters.sortBy.label')" hide-details multiple chips />
|
||||
<v-select v-model="sortBy" :items="headers" :label="$t('logs.filters.sortBy.label')" hide-details multiple
|
||||
chips />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import AddTorrentDialog from '@/components/Dialogs/AddTorrentDialog.vue'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useAddTorrentStore } from '@/stores/addTorrents'
|
||||
import { useAddTorrentStore, useDialogStore } from '@/stores'
|
||||
import { onBeforeMount } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { useArrayPagination, useSearchQuery } from '@/composables'
|
||||
import { useAddTorrentStore } from '@/stores/addTorrents'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useRssStore } from '@/stores/rss'
|
||||
import { useAddTorrentStore, useDialogStore, useRssStore } from '@/stores'
|
||||
import { RssArticle } from '@/types/vuetorrent'
|
||||
import debounce from 'lodash.debounce'
|
||||
import { computed, onBeforeMount, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||
|
@ -124,16 +122,26 @@ onUnmounted(() => {
|
|||
<template v-for="(article, index) in paginatedResults">
|
||||
<v-divider v-if="index > 0" color="white" />
|
||||
|
||||
<v-list-item :class="{ 'rss-read': article.isRead }" @click="showDescription(article)" @contextmenu="markAsRead(article)">
|
||||
<v-list-item :class="{ 'rss-read': article.isRead }" @click="showDescription(article)"
|
||||
@contextmenu="markAsRead(article)">
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
<v-list-item-title class="wrap-anywhere" style="white-space: unset">{{ article.title }}</v-list-item-title>
|
||||
<v-list-item-title class="wrap-anywhere" style="white-space: unset">{{
|
||||
article.title
|
||||
}}
|
||||
</v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle class="d-block">
|
||||
<div>{{ article.parsedDate.toLocaleString() }}</div>
|
||||
<div>{{ t('rssArticles.item.feedName', { name: rssStore.getFeedNames(article.id).join(' | ') }) }}</div>
|
||||
<div>{{
|
||||
t('rssArticles.item.feedName', { name: rssStore.getFeedNames(article.id).join(' | ') })
|
||||
}}
|
||||
</div>
|
||||
<div v-if="article.author">{{ t('rssArticles.item.author', { author: article.author }) }}</div>
|
||||
<div v-if="article.category">{{ t('rssArticles.item.category', { category: article.category }) }}</div>
|
||||
<div v-if="article.category">{{
|
||||
t('rssArticles.item.category', { category: article.category })
|
||||
}}
|
||||
</div>
|
||||
</v-list-item-subtitle>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import PluginManagerDialog from '@/components/Dialogs/PluginManagerDialog.vue'
|
||||
import { useSearchQuery } from '@/composables'
|
||||
import { useAddTorrentStore } from '@/stores/addTorrents'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useSearchEngineStore } from '@/stores/searchEngine'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { formatData } from '@/helpers'
|
||||
import { useAddTorrentStore, useDialogStore, useSearchEngineStore, useVueTorrentStore } from '@/stores'
|
||||
import { SearchPlugin, SearchResult } from '@/types/qbit/models'
|
||||
import { SearchData } from '@/types/vuetorrent'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { formatData } from '@/helpers'
|
||||
import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
@ -54,10 +51,10 @@ const plugins = computed(() => {
|
|||
]
|
||||
|
||||
searchEngineStore.searchPlugins
|
||||
.filter((plugin: SearchPlugin) => plugin.enabled)
|
||||
.forEach((plugin: SearchPlugin) => {
|
||||
plugins.push({ title: plugin.name, value: plugin.name })
|
||||
})
|
||||
.filter((plugin: SearchPlugin) => plugin.enabled)
|
||||
.forEach((plugin: SearchPlugin) => {
|
||||
plugins.push({ title: plugin.name, value: plugin.name })
|
||||
})
|
||||
|
||||
return plugins
|
||||
})
|
||||
|
@ -173,7 +170,8 @@ onBeforeUnmount(() => {
|
|||
<v-spacer />
|
||||
|
||||
<v-btn icon="mdi-plus-circle-outline" variant="plain" color="accent" @click="createNewTab" />
|
||||
<v-btn icon="mdi-minus-circle-outline" variant="plain" color="error" :disabled="searchData.length === 1" @click="deleteTab" />
|
||||
<v-btn icon="mdi-minus-circle-outline" variant="plain" color="error" :disabled="searchData.length === 1"
|
||||
@click="deleteTab" />
|
||||
</v-container>
|
||||
</v-row>
|
||||
|
||||
|
@ -227,11 +225,14 @@ onBeforeUnmount(() => {
|
|||
<v-divider class="my-3" />
|
||||
|
||||
<v-list-item>
|
||||
<v-data-table :headers="headers" :items="filteredResults" :footer-props="{ itemsPerPageOptions: [10, 25, 50, 100, -1] }" :items-per-page.sync="selectedTab.itemsPerPage">
|
||||
<v-data-table :headers="headers" :items="filteredResults"
|
||||
:footer-props="{ itemsPerPageOptions: [10, 25, 50, 100, -1] }"
|
||||
:items-per-page.sync="selectedTab.itemsPerPage">
|
||||
<template v-slot:top>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field v-model="selectedTab.filters.title" density="compact" hide-details :label="$t('searchEngine.filters.title.label')" />
|
||||
<v-text-field v-model="selectedTab.filters.title" density="compact" hide-details
|
||||
:label="$t('searchEngine.filters.title.label')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
@ -239,7 +240,7 @@ onBeforeUnmount(() => {
|
|||
{{ formatData(item.fileSize, vuetorrentStore.useBinarySize) }}
|
||||
</template>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn icon="mdi-download" variant="flat" density="compact" @click="downloadTorrent(item)"> </v-btn>
|
||||
<v-btn icon="mdi-download" variant="flat" density="compact" @click="downloadTorrent(item)"></v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-list-item>
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import Behavior from '@/components/Settings/Behavior.vue'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { toast } from 'vue3-toastify'
|
||||
|
||||
import Advanced from '@/components/Settings/Advanced.vue'
|
||||
import Behavior from '@/components/Settings/Behavior.vue'
|
||||
import BitTorrent from '@/components/Settings/BitTorrent.vue'
|
||||
import Connection from '@/components/Settings/Connection.vue'
|
||||
import Downloads from '@/components/Settings/Downloads.vue'
|
||||
import RFeeds from '@/components/Settings/RSS/Feeds.vue'
|
||||
import RGeneral from '@/components/Settings/RSS/General.vue'
|
||||
import RRules from '@/components/Settings/RSS/Rules.vue'
|
||||
import Speed from '@/components/Settings/Speed.vue'
|
||||
import TagsAndCategories from '@/components/Settings/TagsAndCategories.vue'
|
||||
import WebUI from '@/components/Settings/WebUI.vue'
|
||||
import RGeneral from '@/components/Settings/RSS/General.vue'
|
||||
import RFeeds from '@/components/Settings/RSS/Feeds.vue'
|
||||
import RRules from '@/components/Settings/RSS/Rules.vue'
|
||||
import VGeneral from '@/components/Settings/VueTorrent/General.vue'
|
||||
import VTorrentCard from '@/components/Settings/VueTorrent/TorrentCard.vue'
|
||||
import WebUI from '@/components/Settings/WebUI.vue'
|
||||
import { useDialogStore, usePreferenceStore } from '@/stores'
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { toast } from 'vue3-toastify'
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
|
|
|
@ -6,8 +6,7 @@ import Overview from '@/components/TorrentDetail/Overview.vue'
|
|||
import Peers from '@/components/TorrentDetail/Peers.vue'
|
||||
import TagsAndCategories from '@/components/TorrentDetail/TagsAndCategories.vue'
|
||||
import Trackers from '@/components/TorrentDetail/Trackers.vue'
|
||||
import { useDialogStore } from '@/stores/dialog'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useDialogStore, useTorrentStore } from '@/stores'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
@ -16,12 +15,12 @@ const route = useRoute()
|
|||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
const dialogStore = useDialogStore()
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
|
||||
const tab = ref('overview')
|
||||
|
||||
const hash = computed(() => route.params.hash as string)
|
||||
const torrent = computed(() => maindataStore.getTorrentByHash(hash.value))
|
||||
const torrent = computed(() => torrentStore.getTorrentByHash(hash.value))
|
||||
|
||||
const goHome = () => {
|
||||
router.push({ name: 'dashboard' })
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useAuthStore } from '@/stores/auth'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { routes } from '@/pages'
|
||||
import { useAuthStore } from '@/stores'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { NetworkInterface } from '@/types/qbit/models/AppPreferences.ts'
|
||||
import type { AxiosInstance } from 'axios'
|
||||
import axios from 'axios'
|
||||
import type {
|
||||
|
@ -7,7 +8,6 @@ import type {
|
|||
Feed,
|
||||
FeedRule,
|
||||
Log,
|
||||
NetworkInterface,
|
||||
SearchJob,
|
||||
SearchPlugin,
|
||||
SearchStatus,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { usePreferenceStore } from '@/stores/preferences'
|
||||
import { usePreferenceStore } from './preferences'
|
||||
import { AddTorrentPayload } from '@/types/qbit/payloads'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { qbit } from '@/services'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useAppStore = defineStore('app', () => {
|
||||
const intervals = ref<number[]>([])
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { SortOptions } from '@/constants/qbit/SortOptions'
|
||||
import { formatData } from '@/helpers'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { GetTorrentPayload } from '@/types/qbit/payloads'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useTorrentStore } from './torrents'
|
||||
import { useVueTorrentStore } from './vuetorrent'
|
||||
|
||||
export const useDashboardStore = defineStore(
|
||||
'dashboard',
|
||||
|
@ -14,38 +12,26 @@ export const useDashboardStore = defineStore(
|
|||
const isSelectionMultiple = ref(false)
|
||||
const selectedTorrents = ref<string[]>([])
|
||||
const latestSelectedTorrent = ref<string>()
|
||||
const sortOptions = reactive({
|
||||
isCustomSortEnabled: false,
|
||||
sortBy: SortOptions.DEFAULT,
|
||||
reverseOrder: false
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const maindataStore = useMaindataStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
const vuetorrentStore = useVueTorrentStore()
|
||||
|
||||
const torrentCountString = computed(() => {
|
||||
if (selectedTorrents.value.length) {
|
||||
const selectedSize = selectedTorrents.value
|
||||
.map(hash => maindataStore.getTorrentByHash(hash))
|
||||
.map(hash => torrentStore.getTorrentByHash(hash))
|
||||
.filter(torrent => torrent !== undefined)
|
||||
.map(torrent => torrent!.size)
|
||||
.reduce((partial, size) => partial + size, 0)
|
||||
|
||||
return t('dashboard.selectedTorrentsCount', {
|
||||
count: selectedTorrents.value.length,
|
||||
total: maindataStore.filteredTorrents.length,
|
||||
total: torrentStore.filteredTorrents.length,
|
||||
size: formatData(selectedSize, vuetorrentStore.useBinarySize)
|
||||
})
|
||||
} else {
|
||||
return t('dashboard.torrentsCount', maindataStore.filteredTorrents.length)
|
||||
}
|
||||
})
|
||||
|
||||
const getTorrentsPayload = computed<GetTorrentPayload>(() => {
|
||||
return {
|
||||
sort: sortOptions.isCustomSortEnabled ? SortOptions.DEFAULT : sortOptions.sortBy,
|
||||
reverse: sortOptions.reverseOrder
|
||||
return t('dashboard.torrentsCount', torrentStore.filteredTorrents.length)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -84,19 +70,19 @@ export const useDashboardStore = defineStore(
|
|||
function spanTorrentSelection(endHash: string) {
|
||||
if (!latestSelectedTorrent.value) return
|
||||
|
||||
const latestIndex = maindataStore.getTorrentIndexByHash(latestSelectedTorrent.value)
|
||||
const endIndex = maindataStore.getTorrentIndexByHash(endHash)
|
||||
const latestIndex = torrentStore.getTorrentIndexByHash(latestSelectedTorrent.value)
|
||||
const endIndex = torrentStore.getTorrentIndexByHash(endHash)
|
||||
|
||||
const start = Math.min(endIndex, latestIndex)
|
||||
const end = Math.max(endIndex, latestIndex)
|
||||
const hashes = maindataStore.filteredTorrents.slice(start, end + 1).map(t => t.hash)
|
||||
const hashes = torrentStore.filteredTorrents.slice(start, end + 1).map(t => t.hash)
|
||||
selectTorrents(...hashes)
|
||||
}
|
||||
|
||||
function selectAllTorrents() {
|
||||
isSelectionMultiple.value = true
|
||||
selectedTorrents.value.splice(0, selectedTorrents.value.length, ...maindataStore.torrents.map(t => t.hash))
|
||||
latestSelectedTorrent.value = maindataStore.torrents[0]?.hash
|
||||
selectedTorrents.value.splice(0, selectedTorrents.value.length, ...torrentStore.torrents.map(t => t.hash))
|
||||
latestSelectedTorrent.value = torrentStore.torrents[0]?.hash
|
||||
}
|
||||
|
||||
function unselectAllTorrents() {
|
||||
|
@ -109,7 +95,7 @@ export const useDashboardStore = defineStore(
|
|||
}
|
||||
})
|
||||
|
||||
watch(() => maindataStore.filteredTorrents, newValue => {
|
||||
watch(() => torrentStore.filteredTorrents, newValue => {
|
||||
const pageCount = Math.ceil(newValue.length / vuetorrentStore.paginationSize)
|
||||
if (pageCount < currentPage.value) {
|
||||
currentPage.value = Math.max(1, pageCount)
|
||||
|
@ -121,7 +107,6 @@ export const useDashboardStore = defineStore(
|
|||
isSelectionMultiple,
|
||||
selectedTorrents,
|
||||
latestSelectedTorrent,
|
||||
sortOptions,
|
||||
torrentCountString,
|
||||
isTorrentInSelection,
|
||||
selectTorrent,
|
||||
|
@ -130,20 +115,8 @@ export const useDashboardStore = defineStore(
|
|||
spanTorrentSelection,
|
||||
selectAllTorrents,
|
||||
unselectAllTorrents,
|
||||
toggleSelect,
|
||||
getTorrentsPayload
|
||||
toggleSelect
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
storage: localStorage,
|
||||
key: 'vuetorrent_dashboard',
|
||||
paths: ['sortOptions']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
|
|
29
src/stores/index.ts
Normal file
29
src/stores/index.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { useAddTorrentStore } from './addTorrents'
|
||||
import { useAppStore } from './app'
|
||||
import { useAuthStore } from './auth'
|
||||
import { useDashboardStore } from './dashboard'
|
||||
import { useDialogStore } from './dialog'
|
||||
import { useLogStore } from './logs'
|
||||
import { useMaindataStore } from './maindata'
|
||||
import { useNavbarStore } from './navbar'
|
||||
import { usePreferenceStore } from './preferences'
|
||||
import { useRssStore } from './rss'
|
||||
import { useSearchEngineStore } from './searchEngine'
|
||||
import { useTorrentStore } from './torrents'
|
||||
import { useVueTorrentStore } from './vuetorrent'
|
||||
|
||||
export {
|
||||
useAddTorrentStore,
|
||||
useAppStore,
|
||||
useAuthStore,
|
||||
useDashboardStore,
|
||||
useDialogStore,
|
||||
useLogStore,
|
||||
useMaindataStore,
|
||||
useNavbarStore,
|
||||
usePreferenceStore,
|
||||
useRssStore,
|
||||
useSearchEngineStore,
|
||||
useTorrentStore,
|
||||
useVueTorrentStore
|
||||
}
|
|
@ -1,18 +1,17 @@
|
|||
import { useSearchQuery, useTorrentBuilder } from '@/composables'
|
||||
import { FilePriority, TorrentState } from '@/constants/qbit'
|
||||
import { SortOptions } from '@/constants/qbit/SortOptions'
|
||||
import { useTorrentBuilder } from '@/composables'
|
||||
import { FilePriority, SortOptions } from '@/constants/qbit'
|
||||
import { extractHostname } from '@/helpers'
|
||||
import { uuidFromRaw } from '@/helpers/text'
|
||||
import { qbit } from '@/services'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useDashboardStore } from '@/stores/dashboard'
|
||||
import { useNavbarStore } from '@/stores/navbar'
|
||||
import { useVueTorrentStore } from '@/stores/vuetorrent'
|
||||
import { Category, ServerState } from '@/types/qbit/models'
|
||||
import { AddTorrentPayload } from '@/types/qbit/payloads'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, MaybeRefOrGetter, ref, toValue } from 'vue'
|
||||
import { defineStore, storeToRefs } from 'pinia'
|
||||
import { MaybeRefOrGetter, ref, toValue } from 'vue'
|
||||
import { useAuthStore } from './auth'
|
||||
import { useDashboardStore } from './dashboard'
|
||||
import { useNavbarStore } from './navbar'
|
||||
import { useTorrentStore } from './torrents'
|
||||
import { useVueTorrentStore } from './vuetorrent'
|
||||
|
||||
export const useMaindataStore = defineStore('maindata', () => {
|
||||
const categories = ref<Category[]>([])
|
||||
|
@ -20,64 +19,17 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
const rid = ref<number>()
|
||||
const serverState = ref<ServerState>()
|
||||
const tags = ref<string[]>([])
|
||||
const torrents = ref<Torrent[]>([])
|
||||
|
||||
const trackers = ref<string[]>([])
|
||||
|
||||
const isTextFilterActive = ref(true)
|
||||
const isStatusFilterActive = ref(true)
|
||||
const isCategoryFilterActive = ref(true)
|
||||
const isTagFilterActive = ref(true)
|
||||
const isTrackerFilterActive = ref(true)
|
||||
|
||||
const textFilter = ref('')
|
||||
const statusFilter = ref<TorrentState[]>([])
|
||||
const categoryFilter = ref<string[]>([])
|
||||
const tagFilter = ref<(string | null)[]>([])
|
||||
const trackerFilter = ref<(string | null)[]>([])
|
||||
|
||||
const torrentsWithFilters = computed(() => {
|
||||
return torrents.value.filter(torrent => {
|
||||
if (statusFilter.value.length > 0 && isStatusFilterActive.value && !statusFilter.value.includes(torrent.state)) return false
|
||||
if (categoryFilter.value.length > 0 && isCategoryFilterActive.value && !categoryFilter.value.includes(torrent.category)) return false
|
||||
if (tagFilter.value.length > 0 && isTagFilterActive.value) {
|
||||
if (torrent.tags.length === 0 && tagFilter.value.includes(null)) return true
|
||||
if (!torrent.tags.some(tag => tagFilter.value.includes(tag))) return false
|
||||
}
|
||||
if (trackerFilter.value.length > 0 && isTrackerFilterActive.value && !trackerFilter.value.includes(extractHostname(torrent.tracker))) return false
|
||||
|
||||
return true
|
||||
})
|
||||
})
|
||||
const filteredTorrents = computed(() => searchQuery.results.value)
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const dashboardStore = useDashboardStore()
|
||||
const navbarStore = useNavbarStore()
|
||||
const torrentStore = useTorrentStore()
|
||||
const { torrents } = storeToRefs(torrentStore)
|
||||
const vueTorrentStore = useVueTorrentStore()
|
||||
const torrentBuilder = useTorrentBuilder()
|
||||
|
||||
const searchQuery = useSearchQuery(
|
||||
torrentsWithFilters,
|
||||
() => isTextFilterActive.value ? textFilter.value : null,
|
||||
torrent => torrent.name,
|
||||
results => {
|
||||
if (dashboardStore.sortOptions.isCustomSortEnabled) {
|
||||
if (dashboardStore.sortOptions.sortBy === 'priority') {
|
||||
results.sort((a, b) => {
|
||||
if (a.priority > 0 && b.priority > 0) return a.priority - b.priority
|
||||
else if (a.priority <= 0 && b.priority <= 0) return a.added_on - b.added_on
|
||||
else if (a.priority <= 0) return 1
|
||||
else return -1
|
||||
})
|
||||
} else {
|
||||
results.sort((a, b) => a[dashboardStore.sortOptions.sortBy] - b[dashboardStore.sortOptions.sortBy] || a.added_on - b.added_on)
|
||||
}
|
||||
if (dashboardStore.sortOptions.reverseOrder) results.reverse()
|
||||
}
|
||||
return results
|
||||
}
|
||||
)
|
||||
|
||||
async function fetchCategories() {
|
||||
categories.value = await qbit.getCategories()
|
||||
}
|
||||
|
@ -90,10 +42,6 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
await qbit.createCategory(category)
|
||||
}
|
||||
|
||||
async function setTorrentCategory(hashes: string[], category: string) {
|
||||
await qbit.setCategory(hashes, category)
|
||||
}
|
||||
|
||||
async function editCategory(category: Category, oldCategory?: string) {
|
||||
if (oldCategory) {
|
||||
// Create new category
|
||||
|
@ -131,14 +79,6 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
await qbit.createTag(tags)
|
||||
}
|
||||
|
||||
async function addTorrentTags(hashes: string[], tags: string[]) {
|
||||
await qbit.addTorrentTag(hashes, tags)
|
||||
}
|
||||
|
||||
async function removeTorrentTags(hashes: string[], tags: string[]) {
|
||||
await qbit.removeTorrentTag(hashes, tags)
|
||||
}
|
||||
|
||||
async function editTag(oldTag: string, newTag: string) {
|
||||
if (oldTag === newTag) return
|
||||
|
||||
|
@ -162,22 +102,6 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
await qbit.deleteTags(tags)
|
||||
}
|
||||
|
||||
function getTorrentByHash(hash: string) {
|
||||
return torrents.value.find(t => t.hash === hash)
|
||||
}
|
||||
|
||||
function getTorrentIndexByHash(hash: string) {
|
||||
return filteredTorrents.value.findIndex(t => t.hash === hash)
|
||||
}
|
||||
|
||||
async function deleteTorrents(hashes: string[], deleteWithFiles: boolean) {
|
||||
await qbit.deleteTorrents(hashes, deleteWithFiles)
|
||||
}
|
||||
|
||||
async function moveTorrents(hashes: string[], newPath: string) {
|
||||
await qbit.setTorrentLocation(hashes, newPath)
|
||||
}
|
||||
|
||||
async function updateMaindata() {
|
||||
if (isUpdatingMaindata.value) return
|
||||
isUpdatingMaindata.value = true
|
||||
|
@ -193,8 +117,8 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
}
|
||||
|
||||
// fetch torrent data
|
||||
dashboardStore.sortOptions.isCustomSortEnabled = torrentBuilder.computedValues.indexOf(dashboardStore.sortOptions.sortBy) !== -1
|
||||
let data = await qbit.getTorrents(dashboardStore.getTorrentsPayload)
|
||||
torrentStore.sortOptions.isCustomSortEnabled = torrentBuilder.computedValues.indexOf(torrentStore.sortOptions.sortBy) !== -1
|
||||
let data = await qbit.getTorrents(torrentStore.getTorrentsPayload)
|
||||
|
||||
if (vueTorrentStore.showTrackerFilter) {
|
||||
trackers.value = data
|
||||
|
@ -219,8 +143,6 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
// filter out deleted torrents from selection
|
||||
const hash_index = torrents.value.map(torrent => torrent.hash)
|
||||
dashboardStore.selectedTorrents = dashboardStore.selectedTorrents.filter(hash => hash_index.includes(hash))
|
||||
|
||||
vueTorrentStore.updateTitle()
|
||||
} catch (error: any) {
|
||||
if (error?.response?.status === 403) {
|
||||
console.error('No longer authenticated, logging out...')
|
||||
|
@ -234,22 +156,10 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
}
|
||||
}
|
||||
|
||||
async function addTorrents(payload: AddTorrentPayload, torrents: File[]) {
|
||||
return await qbit.addTorrents(payload, torrents)
|
||||
}
|
||||
|
||||
async function getTorrentProperties(hash: string) {
|
||||
return await qbit.getTorrentProperties(hash)
|
||||
}
|
||||
|
||||
async function fetchFiles(hash: string, indexes?: number[]) {
|
||||
return await qbit.getTorrentFiles(hash, indexes)
|
||||
}
|
||||
|
||||
async function renameTorrent(hash: string, newName: string) {
|
||||
await qbit.setTorrentName(hash, newName)
|
||||
}
|
||||
|
||||
async function renameTorrentFile(hash: string, oldPath: string, newPath: string) {
|
||||
await qbit.renameFile(hash, oldPath, newPath)
|
||||
}
|
||||
|
@ -262,22 +172,6 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
return await qbit.getTorrentPieceStates(hash)
|
||||
}
|
||||
|
||||
async function resumeTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.resumeTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function forceResumeTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.forceStartTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function pauseTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.pauseTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function recheckTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.recheckTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function reannounceTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.reannounceTorrents(toValue(hashes))
|
||||
}
|
||||
|
@ -298,10 +192,6 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
await qbit.setSuperSeeding(toValue(hashes), toValue(enable))
|
||||
}
|
||||
|
||||
async function setTorrentPriority(hashes: string[], priority: 'increasePrio' | 'decreasePrio' | 'topPrio' | 'bottomPrio') {
|
||||
await qbit.setTorrentPriority(hashes, priority)
|
||||
}
|
||||
|
||||
async function getTorrentTrackers(hash: string) {
|
||||
return await qbit.getTorrentTrackers(hash)
|
||||
}
|
||||
|
@ -334,10 +224,6 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
await qbit.setTorrentFilePriority(hash, ids, priority)
|
||||
}
|
||||
|
||||
async function exportTorrent(hash: string) {
|
||||
return await qbit.exportTorrent(hash)
|
||||
}
|
||||
|
||||
async function setDownloadLimit(limit: number, hashes: string[]) {
|
||||
return await qbit.setDownloadLimit(hashes, limit)
|
||||
}
|
||||
|
@ -356,53 +242,26 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
rid,
|
||||
serverState,
|
||||
tags,
|
||||
torrents,
|
||||
filteredTorrents,
|
||||
trackers,
|
||||
isTextFilterActive,
|
||||
textFilter,
|
||||
isStatusFilterActive,
|
||||
statusFilter,
|
||||
isCategoryFilterActive,
|
||||
categoryFilter,
|
||||
isTagFilterActive,
|
||||
tagFilter,
|
||||
isTrackerFilterActive,
|
||||
trackerFilter,
|
||||
getTorrentByHash,
|
||||
getTorrentIndexByHash,
|
||||
deleteTorrents,
|
||||
fetchCategories,
|
||||
getCategoryFromName,
|
||||
createCategory,
|
||||
setTorrentCategory,
|
||||
editCategory,
|
||||
deleteCategories,
|
||||
fetchTags,
|
||||
createTags,
|
||||
addTorrentTags,
|
||||
removeTorrentTags,
|
||||
editTag,
|
||||
deleteTags,
|
||||
moveTorrents,
|
||||
updateMaindata,
|
||||
addTorrents,
|
||||
getTorrentProperties,
|
||||
fetchFiles,
|
||||
renameTorrent,
|
||||
renameTorrentFile,
|
||||
renameTorrentFolder,
|
||||
fetchPieceState,
|
||||
resumeTorrents,
|
||||
forceResumeTorrents,
|
||||
pauseTorrents,
|
||||
recheckTorrents,
|
||||
reannounceTorrents,
|
||||
toggleSeqDl,
|
||||
toggleFLPiecePrio,
|
||||
toggleAutoTmm,
|
||||
setSuperSeeding,
|
||||
setTorrentPriority,
|
||||
getTorrentTrackers,
|
||||
addTorrentTrackers,
|
||||
editTorrentTracker,
|
||||
|
@ -411,31 +270,8 @@ export const useMaindataStore = defineStore('maindata', () => {
|
|||
addTorrentPeers,
|
||||
banPeers,
|
||||
setTorrentFilePriority,
|
||||
exportTorrent,
|
||||
setDownloadLimit,
|
||||
setUploadLimit,
|
||||
setShareLimit
|
||||
}
|
||||
}, {
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
storage: localStorage,
|
||||
key: 'vuetorrent_maindata',
|
||||
paths: [
|
||||
'isTextFilterActive',
|
||||
'textFilter',
|
||||
'isStatusFilterActive',
|
||||
'statusFilter',
|
||||
'isCategoryFilterActive',
|
||||
'categoryFilter',
|
||||
'isTagFilterActive',
|
||||
'tagFilter',
|
||||
'isTrackerFilterActive',
|
||||
'trackerFilter'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
|
195
src/stores/torrents.ts
Normal file
195
src/stores/torrents.ts
Normal file
|
@ -0,0 +1,195 @@
|
|||
import { useSearchQuery } from '@/composables'
|
||||
import { SortOptions, TorrentState } from '@/constants/qbit'
|
||||
import { extractHostname } from '@/helpers'
|
||||
import { qbit } from '@/services'
|
||||
import { AddTorrentPayload, GetTorrentPayload } from '@/types/qbit/payloads'
|
||||
import { Torrent } from '@/types/vuetorrent'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, MaybeRefOrGetter, reactive, ref, toValue } from 'vue'
|
||||
|
||||
export const useTorrentStore = defineStore('torrents', () => {
|
||||
const torrents = ref<Torrent[]>([])
|
||||
|
||||
const isTextFilterActive = ref(true)
|
||||
const isStatusFilterActive = ref(true)
|
||||
const isCategoryFilterActive = ref(true)
|
||||
const isTagFilterActive = ref(true)
|
||||
const isTrackerFilterActive = ref(true)
|
||||
|
||||
const textFilter = ref('')
|
||||
const statusFilter = ref<TorrentState[]>([])
|
||||
const categoryFilter = ref<string[]>([])
|
||||
const tagFilter = ref<(string | null)[]>([])
|
||||
const trackerFilter = ref<(string | null)[]>([])
|
||||
|
||||
const torrentsWithFilters = computed(() => {
|
||||
return torrents.value.filter(torrent => {
|
||||
if (statusFilter.value.length > 0 && isStatusFilterActive.value && !statusFilter.value.includes(torrent.state)) return false
|
||||
if (categoryFilter.value.length > 0 && isCategoryFilterActive.value && !categoryFilter.value.includes(torrent.category)) return false
|
||||
if (tagFilter.value.length > 0 && isTagFilterActive.value) {
|
||||
if (torrent.tags.length === 0 && tagFilter.value.includes(null)) return true
|
||||
if (!torrent.tags.some(tag => tagFilter.value.includes(tag))) return false
|
||||
}
|
||||
if (trackerFilter.value.length > 0 && isTrackerFilterActive.value && !trackerFilter.value.includes(extractHostname(torrent.tracker))) return false
|
||||
|
||||
return true
|
||||
})
|
||||
})
|
||||
const filteredTorrents = computed(() => searchQuery.results.value)
|
||||
|
||||
const sortOptions = reactive({
|
||||
isCustomSortEnabled: false,
|
||||
sortBy: SortOptions.DEFAULT,
|
||||
reverseOrder: false
|
||||
})
|
||||
const getTorrentsPayload = computed<GetTorrentPayload>(() => {
|
||||
return {
|
||||
sort: sortOptions.isCustomSortEnabled ? SortOptions.DEFAULT : sortOptions.sortBy,
|
||||
reverse: sortOptions.reverseOrder
|
||||
}
|
||||
})
|
||||
|
||||
const searchQuery = useSearchQuery(
|
||||
torrentsWithFilters,
|
||||
() => isTextFilterActive.value ? textFilter.value : null,
|
||||
torrent => torrent.name,
|
||||
results => {
|
||||
if (sortOptions.isCustomSortEnabled) {
|
||||
if (sortOptions.sortBy === 'priority') {
|
||||
results.sort((a, b) => {
|
||||
if (a.priority > 0 && b.priority > 0) return a.priority - b.priority
|
||||
else if (a.priority <= 0 && b.priority <= 0) return a.added_on - b.added_on
|
||||
else if (a.priority <= 0) return 1
|
||||
else return -1
|
||||
})
|
||||
} else {
|
||||
results.sort((a, b) => a[sortOptions.sortBy] - b[sortOptions.sortBy] || a.added_on - b.added_on)
|
||||
}
|
||||
if (sortOptions.reverseOrder) results.reverse()
|
||||
}
|
||||
return results
|
||||
}
|
||||
)
|
||||
|
||||
async function setTorrentCategory(hashes: string[], category: string) {
|
||||
await qbit.setCategory(hashes, category)
|
||||
}
|
||||
|
||||
async function addTorrentTags(hashes: string[], tags: string[]) {
|
||||
await qbit.addTorrentTag(hashes, tags)
|
||||
}
|
||||
|
||||
async function removeTorrentTags(hashes: string[], tags: string[]) {
|
||||
await qbit.removeTorrentTag(hashes, tags)
|
||||
}
|
||||
|
||||
function getTorrentByHash(hash: string) {
|
||||
return torrents.value.find(t => t.hash === hash)
|
||||
}
|
||||
|
||||
function getTorrentIndexByHash(hash: string) {
|
||||
return filteredTorrents.value.findIndex(t => t.hash === hash)
|
||||
}
|
||||
|
||||
async function deleteTorrents(hashes: string[], deleteWithFiles: boolean) {
|
||||
await qbit.deleteTorrents(hashes, deleteWithFiles)
|
||||
}
|
||||
|
||||
async function moveTorrents(hashes: string[], newPath: string) {
|
||||
await qbit.setTorrentLocation(hashes, newPath)
|
||||
}
|
||||
|
||||
async function addTorrents(payload: AddTorrentPayload, torrents: File[]) {
|
||||
return await qbit.addTorrents(payload, torrents)
|
||||
}
|
||||
|
||||
async function getTorrentProperties(hash: string) {
|
||||
return await qbit.getTorrentProperties(hash)
|
||||
}
|
||||
|
||||
async function renameTorrent(hash: string, newName: string) {
|
||||
await qbit.setTorrentName(hash, newName)
|
||||
}
|
||||
|
||||
async function resumeTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.resumeTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function forceResumeTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.forceStartTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function pauseTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.pauseTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function recheckTorrents(hashes: MaybeRefOrGetter<string[]>) {
|
||||
await qbit.recheckTorrents(toValue(hashes))
|
||||
}
|
||||
|
||||
async function setTorrentPriority(hashes: string[], priority: 'increasePrio' | 'decreasePrio' | 'topPrio' | 'bottomPrio') {
|
||||
await qbit.setTorrentPriority(hashes, priority)
|
||||
}
|
||||
|
||||
async function exportTorrent(hash: string) {
|
||||
return await qbit.exportTorrent(hash)
|
||||
}
|
||||
|
||||
return {
|
||||
torrents,
|
||||
isTextFilterActive,
|
||||
isStatusFilterActive,
|
||||
isCategoryFilterActive,
|
||||
isTagFilterActive,
|
||||
isTrackerFilterActive,
|
||||
textFilter,
|
||||
statusFilter,
|
||||
categoryFilter,
|
||||
tagFilter,
|
||||
trackerFilter,
|
||||
torrentsWithFilters,
|
||||
filteredTorrents,
|
||||
sortOptions,
|
||||
getTorrentsPayload,
|
||||
searchQuery,
|
||||
setTorrentCategory,
|
||||
addTorrentTags,
|
||||
removeTorrentTags,
|
||||
getTorrentByHash,
|
||||
getTorrentIndexByHash,
|
||||
deleteTorrents,
|
||||
moveTorrents,
|
||||
addTorrents,
|
||||
getTorrentProperties,
|
||||
renameTorrent,
|
||||
resumeTorrents,
|
||||
forceResumeTorrents,
|
||||
pauseTorrents,
|
||||
recheckTorrents,
|
||||
setTorrentPriority,
|
||||
exportTorrent
|
||||
}
|
||||
}, {
|
||||
persist: {
|
||||
enabled: true,
|
||||
strategies: [
|
||||
{
|
||||
storage: localStorage,
|
||||
key: 'vuetorrent_torrents',
|
||||
paths: [
|
||||
'isTextFilterActive',
|
||||
'textFilter',
|
||||
'isStatusFilterActive',
|
||||
'statusFilter',
|
||||
'isCategoryFilterActive',
|
||||
'categoryFilter',
|
||||
'isTagFilterActive',
|
||||
'tagFilter',
|
||||
'isTrackerFilterActive',
|
||||
'trackerFilter',
|
||||
'sortOptions'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
|
@ -1,8 +1,12 @@
|
|||
import { propsData, propsMetadata, DashboardProperty, TitleOptions } from '@/constants/vuetorrent'
|
||||
import { formatPercent, formatSpeed } from '@/helpers'
|
||||
import {
|
||||
DashboardProperty,
|
||||
PropertyData,
|
||||
propsData,
|
||||
propsMetadata,
|
||||
TitleOptions,
|
||||
TorrentProperty
|
||||
} from '@/constants/vuetorrent'
|
||||
import { Theme } from '@/plugins/vuetify'
|
||||
import { useMaindataStore } from '@/stores/maindata'
|
||||
import { PropertyData, TorrentProperty } from '@/types/vuetorrent'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
@ -73,7 +77,6 @@ export const useVueTorrentStore = defineStore(
|
|||
const i18n = useI18n()
|
||||
const router = useRouter()
|
||||
const theme = useTheme()
|
||||
const maindataStore = useMaindataStore()
|
||||
|
||||
watch(language, setLanguage)
|
||||
watch(darkMode, updateTheme)
|
||||
|
@ -111,45 +114,13 @@ export const useVueTorrentStore = defineStore(
|
|||
await router.push({ name: 'login', query: { redirect: router.currentRoute.value.path } })
|
||||
}
|
||||
|
||||
function updateTitle() {
|
||||
const mode = uiTitleType.value
|
||||
switch (mode) {
|
||||
case TitleOptions.GLOBAL_SPEED:
|
||||
document.title =
|
||||
'[' +
|
||||
`D: ${formatSpeed(maindataStore.serverState?.dl_info_speed ?? 0, useBitSpeed.value)}, ` +
|
||||
`U: ${formatSpeed(maindataStore.serverState?.up_info_speed ?? 0, useBitSpeed.value)}` +
|
||||
'] VueTorrent'
|
||||
break
|
||||
case TitleOptions.FIRST_TORRENT_STATUS:
|
||||
const torrent = maindataStore.torrents.at(0)
|
||||
if (torrent) {
|
||||
document.title =
|
||||
'[' +
|
||||
`D: ${formatSpeed(torrent.dlspeed, useBitSpeed.value)}, ` +
|
||||
`U: ${formatSpeed(torrent.upspeed, useBitSpeed.value)}, ` +
|
||||
`${formatPercent(torrent.progress)}` +
|
||||
'] VueTorrent'
|
||||
} else {
|
||||
document.title = '[N/A] VueTorrent'
|
||||
}
|
||||
break
|
||||
case TitleOptions.CUSTOM:
|
||||
document.title = uiTitleCustom.value
|
||||
break
|
||||
case TitleOptions.DEFAULT:
|
||||
default:
|
||||
document.title = 'VueTorrent'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function updateBusyProperties(values: TorrentProperty[]) {
|
||||
values.forEach((ppt, index) => {
|
||||
_busyProperties.value[ppt.name].active = ppt.active
|
||||
_busyProperties.value[ppt.name].order = index + 1
|
||||
})
|
||||
}
|
||||
|
||||
function updateDoneProperties(values: TorrentProperty[]) {
|
||||
values.forEach((ppt, index) => {
|
||||
_doneProperties.value[ppt.name].active = ppt.active
|
||||
|
@ -160,6 +131,7 @@ export const useVueTorrentStore = defineStore(
|
|||
function toggleBusyProperty(name: DashboardProperty) {
|
||||
_busyProperties.value[name].active = !_busyProperties.value[name].active
|
||||
}
|
||||
|
||||
function toggleDoneProperty(name: DashboardProperty) {
|
||||
_doneProperties.value[name].active = !_doneProperties.value[name].active
|
||||
}
|
||||
|
@ -201,7 +173,6 @@ export const useVueTorrentStore = defineStore(
|
|||
updateSystemTheme,
|
||||
toggleTheme,
|
||||
redirectToLogin,
|
||||
updateTitle,
|
||||
updateBusyProperties,
|
||||
updateDoneProperties,
|
||||
toggleBusyProperty,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FeedArticle } from '@/types/qbit/models'
|
||||
import FeedArticle from './FeedArticle'
|
||||
|
||||
export default interface Feed {
|
||||
name: string
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export interface FeedArticle {
|
||||
export default interface FeedArticle {
|
||||
author: string
|
||||
category: string
|
||||
date: string
|
||||
|
|
|
@ -12,8 +12,7 @@ import type SearchPlugin from './SearchPlugin'
|
|||
import type SearchJob from './SearchJob'
|
||||
import type SearchStatus from './SearchStatus'
|
||||
import type SearchResult from './SearchResult'
|
||||
import { FeedArticle } from './FeedArticle'
|
||||
import { NetworkInterface } from './AppPreferences'
|
||||
import type FeedArticle from './FeedArticle'
|
||||
import type Log from './Log'
|
||||
|
||||
type ApplicationVersion = string
|
||||
|
@ -21,7 +20,6 @@ type ApplicationVersion = string
|
|||
export type {
|
||||
ApplicationVersion,
|
||||
AppPreferences,
|
||||
NetworkInterface,
|
||||
Category,
|
||||
ServerState,
|
||||
Tracker,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { BasePayload } from './index'
|
||||
import type BasePayload from './BasePayload'
|
||||
|
||||
export default interface CreateFeedPayload extends BasePayload {
|
||||
url: string
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { FilterState } from '@/constants/qbit'
|
||||
import { SortOptions } from '@/constants/qbit/SortOptions'
|
||||
import { FilterState, SortOptions } from '@/constants/qbit'
|
||||
|
||||
export default interface GetTorrentPayload {
|
||||
filter?: FilterState
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { BasePayload } from './index'
|
||||
import type BasePayload from './BasePayload'
|
||||
|
||||
export default interface LoginPayload extends BasePayload {
|
||||
/** Username used to access the WebUI */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { Optional } from '@/types/global'
|
||||
import type { BasePayload } from './index'
|
||||
import type BasePayload from './BasePayload'
|
||||
|
||||
export default interface PeerLogPayload extends BasePayload {
|
||||
// Exclude messages with "message id" <= last_known_id (default: -1)
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { DashboardProperty, DashboardPropertyType } from '@/constants/vuetorrent'
|
||||
|
||||
type pptData = { active: boolean; order: number }
|
||||
type pptMetadata =
|
||||
| { type: DashboardPropertyType.AMOUNT; props: { title: string; value: string; total: string } }
|
||||
| { type: DashboardPropertyType.CHIP; props: { title: string; value: string; color: string } }
|
||||
| { type: DashboardPropertyType.DATA; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.DATETIME; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.DURATION; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.PERCENT; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.RELATIVE; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.SPEED; props: { title: string; value: string } }
|
||||
| { type: DashboardPropertyType.TEXT; props: { title: string; value: string } }
|
||||
|
||||
export interface PropertyData {
|
||||
[key: DashboardProperty | string]: pptData
|
||||
}
|
||||
|
||||
export interface PropertyMetadata {
|
||||
[key: DashboardProperty | string]: pptMetadata
|
||||
}
|
||||
|
||||
export type TorrentProperty = { name: DashboardProperty } & pptData & pptMetadata
|
|
@ -1,8 +1,7 @@
|
|||
import type { RssArticle } from './RssArticle'
|
||||
import type { SearchData } from './SearchData'
|
||||
import type Torrent from './Torrent'
|
||||
import type { PropertyData, PropertyMetadata, TorrentProperty } from './TorrentProperty'
|
||||
import type { TreeNode, TreeFile, TreeFolder, TreeRoot } from './TreeObjects'
|
||||
import type { TRCMenuEntry } from './TRCMenuEntry'
|
||||
|
||||
export { RssArticle, SearchData, Torrent, PropertyData, PropertyMetadata, TorrentProperty, TreeNode, TreeFile, TreeFolder, TreeRoot, TRCMenuEntry }
|
||||
export { RssArticle, SearchData, Torrent, TreeNode, TreeFile, TreeFolder, TreeRoot, TRCMenuEntry }
|
||||
|
|
12
src/vite-env.d.ts
vendored
12
src/vite-env.d.ts
vendored
|
@ -1 +1,13 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_QBITTORRENT_TARGET: string
|
||||
readonly VITE_QBITTORRENT_PORT: number
|
||||
|
||||
readonly VITE_USE_FAKE_TORRENTS: string
|
||||
readonly VITE_FAKE_TORRENT_COUNT: number
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue