perf: Add Monitored folders (#802)

This commit is contained in:
Rémi Marseault 2023-05-08 20:40:07 +02:00 committed by GitHub
parent 3e7f35107b
commit 0deb6f8e47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 245 additions and 86 deletions

View file

@ -6,7 +6,7 @@
</v-list-item>
<v-list-item>
<v-checkbox v-model="settings.start_paused_enabled" hide-details class="ma-0 pa-0" :label="$t('modals.settings.downloads.whenAddTorrent.donotAutoStart')" />
<v-checkbox v-model="settings.start_paused_enabled" hide-details class="ma-0 pa-0" :label="$t('modals.settings.downloads.whenAddTorrent.doNotAutoStart')" />
</v-list-item>
<v-list-item>
@ -104,17 +104,87 @@
</v-list-item>
<v-divider />
<v-subheader>{{ $t('modals.settings.downloads.saveManagement.monitoredFolders.subheader') }}</v-subheader>
<v-list-item>
<!-- TODO: Monitored folder -->
<v-btn :disabled="true">Coming soon!</v-btn>
</v-list-item>
<v-data-table class="mt-5" :headers="monitoredFoldersHeaders" :items="monitoredFoldersData">
<template v-slot:top>
<v-toolbar flat>
<v-toolbar-title>{{ $t('modals.settings.downloads.monitoredFolders.subheader') }}</v-toolbar-title>
<v-divider class="mx-4" inset vertical />
<v-spacer></v-spacer>
<v-dialog v-model="monitoredFoldersDialog" max-width="500px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">{{ $t('modals.settings.downloads.monitoredFolders.newItem') }}</v-btn>
</template>
<v-card>
<v-card-title>
<h5>{{ $t('modals.settings.downloads.monitoredFolders.editItem') }}</h5>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field v-model="monitoredFoldersEditedItem.monitoredFolderPath" :label="$t('modals.settings.downloads.monitoredFolders.monitoredFolderPath')" />
</v-col>
<v-col cols="12">
<v-select
v-model="monitoredFoldersEditedItem.saveType"
height="1"
flat
dense
hide-details
outlined
:items="monitoredFoldersMonitorTypeOptions"
:label="$t('modals.settings.downloads.monitoredFolders.saveType')"
/>
</v-col>
<v-col cols="12">
<v-text-field
:disabled="monitoredFoldersEditedItem.saveType !== -1"
v-model="monitoredFoldersEditedItem.otherPath"
:label="$t('modals.settings.downloads.monitoredFolders.otherPath')"
/>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn color="accent darken-1" text @click="close">{{ $t('cancel') }}</v-btn>
<v-btn color="accent darken-1" text @click="save">{{ $t('save') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="monitoredFoldersDialogDelete" max-width="500px">
<v-card>
<v-card-title class="text-break">{{ $t('modals.settings.downloads.monitoredFolders.confirmDelete') }}</v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="closeDelete">{{ $t('cancel') }}</v-btn>
<v-btn color="blue darken-1" text @click="deleteItemConfirm">{{ $t('ok') }}</v-btn>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.saveType="{ item }">
{{ monitoredFoldersMonitorTypeOptions.filter(value => value.value === item.saveType)[0].text }}
</template>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" @click="editItem(item)">{{ mdiPencil }}</v-icon>
<v-icon small @click="deleteItem(item)">{{ mdiDelete }}</v-icon>
</template>
<template v-slot:no-data>
{{ $t('modals.settings.downloads.monitoredFolders.noData') }}
</template>
</v-data-table>
<v-divider />
<v-list-item>
<v-checkbox v-model="settings.excluded_file_names_enabled" hide-details class="ma-0 pa-0" :label="$t('modals.settings.downloads.saveManagement.excludedFileNames.label')" />
<v-checkbox v-model="settings.excluded_file_names_enabled" hide-details class="ma-0 pa-0" :label="$t('modals.settings.downloads.excludedFileNames.label')" />
</v-list-item>
<v-list-item>
<v-textarea
@ -125,14 +195,14 @@
auto-grow
clearable
persistent-hint
:hint="$t('modals.settings.downloads.saveManagement.excludedFileNames.hint')"
:hint="$t('modals.settings.downloads.excludedFileNames.hint')"
/>
</v-list-item>
<v-divider />
<v-list-item>
<v-checkbox v-model="settings.mail_notification_enabled" hide-details class="ma-0 pa-0" :label="$t('modals.settings.downloads.saveManagement.mailNotification.enabled')" />
<v-checkbox v-model="settings.mail_notification_enabled" hide-details class="ma-0 pa-0" :label="$t('modals.settings.downloads.mailNotification.enabled')" />
</v-list-item>
<v-list-item>
<v-text-field
@ -141,7 +211,7 @@
dense
hide-details
outlined
:label="$t('modals.settings.downloads.saveManagement.mailNotification.from')"
:label="$t('modals.settings.downloads.mailNotification.from')"
/>
</v-list-item>
<v-list-item>
@ -151,7 +221,7 @@
dense
hide-details
outlined
:label="$t('modals.settings.downloads.saveManagement.mailNotification.to')"
:label="$t('modals.settings.downloads.mailNotification.to')"
/>
</v-list-item>
<v-list-item>
@ -161,7 +231,7 @@
dense
hide-details
outlined
:label="$t('modals.settings.downloads.saveManagement.mailNotification.smtpServer')"
:label="$t('modals.settings.downloads.mailNotification.smtpServer')"
/>
</v-list-item>
<v-list-item>
@ -170,7 +240,7 @@
v-model="settings.mail_notification_ssl_enabled"
hide-details
class="ma-0 pa-0"
:label="$t('modals.settings.downloads.saveManagement.mailNotification.sslEnabled')"
:label="$t('modals.settings.downloads.mailNotification.sslEnabled')"
/>
</v-list-item>
@ -180,7 +250,7 @@
v-model="settings.mail_notification_auth_enabled"
hide-details
class="ma-0 pa-0"
:label="$t('modals.settings.downloads.saveManagement.mailNotification.authEnabled')"
:label="$t('modals.settings.downloads.mailNotification.authEnabled')"
/>
<v-row class="ms-6">
<v-col>
@ -190,14 +260,14 @@
dense
hide-details
class="mb-5"
:label="$t('modals.settings.downloads.saveManagement.mailNotification.username')"
:label="$t('modals.settings.downloads.mailNotification.username')"
/>
<v-text-field
:disabled="!settings.mail_notification_enabled || !settings.mail_notification_auth_enabled"
v-model="settings.mail_notification_password"
dense
hide-details
:label="$t('modals.settings.downloads.saveManagement.mailNotification.password')"
:label="$t('modals.settings.downloads.mailNotification.password')"
:type="showPassword ? 'text' : 'password'"
:append-icon="!settings.mail_notification_enabled || !settings.mail_notification_auth_enabled ? '' : showPassword ? mdiEye : mdiEyeOff"
@click:append="showPassword = !showPassword"
@ -207,7 +277,7 @@
</v-list-item>
<v-divider />
<v-subheader>{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.subheader') }}</v-subheader>
<v-subheader>{{ $t('modals.settings.downloads.runExternalProgram.subheader') }}</v-subheader>
<v-row>
<v-col cols="12" md="6">
@ -216,7 +286,7 @@
v-model="settings.autorun_on_torrent_added_enabled"
hide-details
class="ma-0 pa-0"
:label="$t('modals.settings.downloads.saveManagement.runExternalProgram.onAddedEnabled')"
:label="$t('modals.settings.downloads.runExternalProgram.onAddedEnabled')"
/>
</v-list-item>
<v-list-item>
@ -226,17 +296,12 @@
class="mb-2"
outlined
dense
:label="$t('modals.settings.downloads.saveManagement.runExternalProgram.onAddedLabel')"
:label="$t('modals.settings.downloads.runExternalProgram.onAddedLabel')"
hide-details
/>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="settings.autorun_enabled"
hide-details
class="ma-0 pa-0"
:label="$t('modals.settings.downloads.saveManagement.runExternalProgram.onFinishedEnabled')"
/>
<v-checkbox v-model="settings.autorun_enabled" hide-details class="ma-0 pa-0" :label="$t('modals.settings.downloads.runExternalProgram.onFinishedEnabled')" />
</v-list-item>
<v-list-item>
<v-text-field
@ -245,7 +310,7 @@
class="mb-2"
outlined
dense
:label="$t('modals.settings.downloads.saveManagement.runExternalProgram.onFinishedLabel')"
:label="$t('modals.settings.downloads.runExternalProgram.onFinishedLabel')"
hide-details
/>
</v-list-item>
@ -255,47 +320,47 @@
<v-card flat color="grey--text selected">
<v-card-text>
<h5>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamTitle') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamTitle') }}
</h5>
<ul>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamN') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamN') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamL') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamL') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamG') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamG') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamF') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamF') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamR') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamR') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamD') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamD') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamC') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamC') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamZ') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamZ') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamT') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamT') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamI') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamI') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamJ') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamJ') }}
</li>
<li>
{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.supportParamK') }}
{{ $t('modals.settings.downloads.runExternalProgram.supportParamK') }}
</li>
</ul>
<h5>{{ $t('modals.settings.downloads.saveManagement.runExternalProgram.tip') }}</h5>
<h5>{{ $t('modals.settings.downloads.runExternalProgram.tip') }}</h5>
</v-card-text>
</v-card>
</v-col>
@ -307,7 +372,10 @@
import { defineComponent } from 'vue'
import { FullScreenModal, SettingsTab } from '@/mixins'
import { AppPreferences } from '@/enums/qbit'
import { mdiEye, mdiEyeOff } from '@mdi/js'
import { mdiDelete, mdiEye, mdiEyeOff, mdiPencil } from '@mdi/js'
import { ScanDirs, ScanDirsEnum } from '@/enums/qbit/AppPreferences'
type MonitoredFolder = { monitoredFolderPath: string; saveType: ScanDirs | -1; otherPath: string }
export default defineComponent({
name: 'Downloads',
@ -335,16 +403,47 @@ export default defineComponent({
export_dir_enabled: false,
export_dir_fin_enabled: false,
showPassword: false,
monitoredFoldersDialog: false,
monitoredFoldersDialogDelete: false,
monitoredFoldersEditedIndex: -1,
monitoredFoldersEditedItem: { monitoredFolderPath: '', saveType: ScanDirsEnum.MONITORED_FOLDER, otherPath: '' } as MonitoredFolder,
monitoredFoldersDefaultItem: { monitoredFolderPath: '', saveType: ScanDirsEnum.MONITORED_FOLDER, otherPath: '' } as MonitoredFolder,
monitoredFoldersHeaders: [
{ text: this.$t('modals.settings.downloads.monitoredFolders.monitoredFolderPath'), value: 'monitoredFolderPath', sortable: false },
{ text: this.$t('modals.settings.downloads.monitoredFolders.saveType'), value: 'saveType', sortable: false },
{ text: this.$t('modals.settings.downloads.monitoredFolders.otherPath'), value: 'otherPath', sortable: false },
{ text: this.$t('modals.settings.downloads.monitoredFolders.actions'), value: 'actions', sortable: false }
],
monitoredFoldersData: [] as MonitoredFolder[],
monitoredFoldersMonitorTypeOptions: [
{ text: this.$t('enums.monitoredFolderSaveLocation.monitoredFolder'), value: ScanDirsEnum.MONITORED_FOLDER },
{ text: this.$t('enums.monitoredFolderSaveLocation.defaultSavePath'), value: ScanDirsEnum.DEFAULT_SAVE_PATH },
{ text: this.$t('enums.monitoredFolderSaveLocation.other'), value: -1 }
],
mdiEye,
mdiEyeOff
mdiEyeOff,
mdiPencil,
mdiDelete
}
},
mounted() {
this.export_dir_enabled = this.settings.export_dir.length > 0
this.export_dir_fin_enabled = this.settings.export_dir_fin.length > 0
},
Object.entries(this.settings.scan_dirs).forEach(entry => {
const [k, v] = entry
let saveType, otherPath
if (typeof v === 'string') {
saveType = -1
otherPath = v
} else {
saveType = v
otherPath = ''
}
this.monitoredFoldersData.push({ monitoredFolderPath: k, saveType, otherPath })
})
},
watch: {
export_dir_enabled(newValue) {
this.settings.export_dir = newValue ? this.settings.export_dir : ''
@ -352,6 +451,52 @@ export default defineComponent({
export_dir_fin_enabled(newValue) {
this.settings.export_dir_fin = newValue ? this.settings.export_dir_fin : ''
}
},
methods: {
editItem(item: MonitoredFolder) {
this.monitoredFoldersEditedIndex = this.monitoredFoldersData.indexOf(item)
this.monitoredFoldersEditedItem = { ...item }
this.monitoredFoldersDialog = true
},
deleteItem(item: MonitoredFolder) {
this.monitoredFoldersEditedIndex = this.monitoredFoldersData.indexOf(item)
this.monitoredFoldersEditedItem = { ...item }
this.monitoredFoldersDialogDelete = true
},
deleteItemConfirm() {
this.monitoredFoldersData.splice(this.monitoredFoldersEditedIndex, 1)
this.convertSettings()
this.closeDelete()
},
save() {
if (this.monitoredFoldersEditedIndex > -1) {
Object.assign(this.monitoredFoldersData[this.monitoredFoldersEditedIndex], this.monitoredFoldersEditedItem)
} else {
this.monitoredFoldersData.push(this.monitoredFoldersEditedItem)
}
this.convertSettings()
this.close()
},
convertSettings() {
this.settings.scan_dirs = {}
this.monitoredFoldersData.forEach(folder => {
this.settings.scan_dirs[folder.monitoredFolderPath] = folder.saveType === -1 ? folder.otherPath : folder.saveType
})
},
close() {
this.monitoredFoldersDialog = false
this.$nextTick(() => {
this.monitoredFoldersEditedItem = { ...this.monitoredFoldersDefaultItem }
this.monitoredFoldersEditedIndex = -1
})
},
closeDelete() {
this.monitoredFoldersDialogDelete = false
this.$nextTick(() => {
this.monitoredFoldersEditedItem = { ...this.monitoredFoldersDefaultItem }
this.monitoredFoldersEditedIndex = -1
})
}
}
})
</script>

View file

@ -35,7 +35,7 @@ export enum ProxyType {
SOCKS4 = 5
}
enum ScanDirsEnum {
export enum ScanDirsEnum {
MONITORED_FOLDER,
DEFAULT_SAVE_PATH
}

View file

@ -3,6 +3,7 @@
"settings": "Settings",
"pause": "Pause",
"delete": "Delete",
"ok": "OK",
"save": "Save",
"cancel": "Cancel",
"confirm": "Confirm",
@ -257,7 +258,7 @@
"downloads": {
"whenAddTorrent": {
"subheader": "When adding a torrent",
"donotAutoStart": "Do not start the download automatically",
"doNotAutoStart": "Do not start the download automatically",
"autoDeleteMode": "Delete .torrent files afterwards"
},
"publicSettings": {
@ -281,45 +282,53 @@
"defaultSavePath": "Default Save Path",
"keepIncompleteIn": "Keep incomplete torrents in",
"exportDir": "Copy .torrent files to",
"exportDirFinished": "Copy .torrent files for finished downloads to",
"monitoredFolders": {
"subheader": "Monitored Folders"
},
"excludedFileNames": {
"label": "Excluded file names",
"hint": "One file name per line"
},
"mailNotification": {
"enabled": "Email notification upon download completion",
"from": "From",
"to": "To",
"smtpServer": "SMTP server",
"sslEnabled": "This server requires a secure connection (SSL)",
"authEnabled": "Authentication",
"username": "Username",
"password": "Password"
},
"runExternalProgram": {
"subheader": "Run external program",
"onAddedEnabled": "Run external program on torrent added:",
"onAddedLabel": "Command",
"onFinishedEnabled": "Run external program on torrent finished:",
"onFinishedLabel": "Command",
"supportParamTitle": "Supported parameters (case sensitive):",
"supportParamN": "%N: Torrent name",
"supportParamL": "%L: Category",
"supportParamG": "%G: Tags (separated by comma)",
"supportParamF": "%F: Content path (same as root path for multi-file torrent)",
"supportParamR": "%R: Root path (first torrent subdirectory path)",
"supportParamD": "%D: Save path",
"supportParamC": "%C: Number of files",
"supportParamZ": "%Z: Torrent size (bytes)",
"supportParamT": "%T: Current tracker",
"supportParamI": "%I: Info hash v1",
"supportParamJ": "%J: Info hash v2",
"supportParamK": "%K: Torrent ID",
"tip": "Tip: Encapsulate parameter with quotation marks to avoid text being cut off at whitespace (e.g., \"%N\")"
}
"exportDirFinished": "Copy .torrent files for finished downloads to"
},
"monitoredFolders": {
"subheader": "Monitored Folders",
"newItem": "New Item",
"editItem": "Edit Item",
"monitoredFolderPath": "Monitored Folder",
"saveType": "Override Save Location",
"otherPath": "Other Path",
"actions": "Actions",
"confirmDelete": "Are you sure you want to delete this item?",
"noData": "No monitored folder yet"
},
"excludedFileNames": {
"label": "Excluded file names",
"hint": "One file name per line"
},
"mailNotification": {
"enabled": "Email notification upon download completion",
"from": "From",
"to": "To",
"smtpServer": "SMTP server",
"sslEnabled": "This server requires a secure connection (SSL)",
"authEnabled": "Authentication",
"username": "Username",
"password": "Password"
},
"runExternalProgram": {
"subheader": "Run external program",
"onAddedEnabled": "Run external program on torrent added:",
"onAddedLabel": "Command",
"onFinishedEnabled": "Run external program on torrent finished:",
"onFinishedLabel": "Command",
"supportParamTitle": "Supported parameters (case sensitive):",
"supportParamN": "%N: Torrent name",
"supportParamL": "%L: Category",
"supportParamG": "%G: Tags (separated by comma)",
"supportParamF": "%F: Content path (same as root path for multi-file torrent)",
"supportParamR": "%R: Root path (first torrent subdirectory path)",
"supportParamD": "%D: Save path",
"supportParamC": "%C: Number of files",
"supportParamZ": "%Z: Torrent size (bytes)",
"supportParamT": "%T: Current tracker",
"supportParamI": "%I: Info hash v1",
"supportParamJ": "%J: Info hash v2",
"supportParamK": "%K: Torrent ID",
"tip": "Tip: Encapsulate parameter with quotation marks to avoid text being cut off at whitespace (e.g., \"%N\")"
}
},
"connection": {
@ -867,6 +876,11 @@
"roundRobin": "Round-robin",
"fastestUpload": "Fastest upload",
"antiLeech": "Anti-leech"
},
"monitoredFolderSaveLocation": {
"monitoredFolder": "Monitored Folder Location",
"defaultSavePath": "Default Save Path",
"other": "Other"
}
}
}

View file

@ -11,9 +11,9 @@ import type {
UploadChokingAlgorithm,
UploadSlotsBehavior,
UtpTcpMixedMode,
DiskIOMode,
DiskIOType
} from '@/enums/qbit/AppPreferences'
import { DiskIOMode } from '@/enums/qbit/AppPreferences'
export interface NetworkInterface {
name: string