feat(webui): library option to automatically empty trash after scan

This commit is contained in:
Gauthier Roebroeck 2021-07-09 15:21:50 +08:00
parent 21781a3a23
commit fc06b4a987
3 changed files with 244 additions and 214 deletions

View file

@ -2,7 +2,8 @@
<v-dialog v-model="modal" <v-dialog v-model="modal"
:fullscreen="this.$vuetify.breakpoint.xsOnly" :fullscreen="this.$vuetify.breakpoint.xsOnly"
:hide-overlay="this.$vuetify.breakpoint.xsOnly" :hide-overlay="this.$vuetify.breakpoint.xsOnly"
max-width="600" max-width="700"
scrollable
> >
<form novalidate> <form novalidate>
<v-card> <v-card>
@ -13,217 +14,244 @@
<v-toolbar-title>{{ dialogTitle }}</v-toolbar-title> <v-toolbar-title>{{ dialogTitle }}</v-toolbar-title>
<v-spacer/> <v-spacer/>
<v-toolbar-items> <v-toolbar-items>
<v-btn text color="primary" @click="dialogConfirm">{{ confirmText }}</v-btn> <v-btn text color="primary" @click="nextTab" v-if="showNext">
{{ $t('dialog.edit_library.button_next') }}
</v-btn>
<v-btn text color="primary" @click="dialogConfirm" v-else>{{ confirmText }}</v-btn>
</v-toolbar-items> </v-toolbar-items>
</v-toolbar> </v-toolbar>
<v-card-title class="hidden-xs-only">{{ dialogTitle }}</v-card-title> <v-card-title class="hidden-xs-only">{{ dialogTitle }}</v-card-title>
<v-tabs :vertical="$vuetify.breakpoint.smAndUp" v-model="tab"> <v-card-text class="pa-0">
<v-tab class="justify-start"> <v-tabs :vertical="$vuetify.breakpoint.smAndUp" v-model="tab">
<v-icon left class="hidden-xs-only">mdi-bookshelf</v-icon> <v-tab class="justify-start">
{{ $t('dialog.edit_library.tab_general') }} <v-icon left class="hidden-xs-only">mdi-bookshelf</v-icon>
</v-tab> {{ $t('dialog.edit_library.tab_general') }}
<v-tab class="justify-start"> </v-tab>
<v-icon left class="hidden-xs-only">mdi-tune</v-icon> <v-tab class="justify-start">
{{ $t('dialog.edit_library.tab_options') }} <v-icon left class="hidden-xs-only">mdi-tune</v-icon>
</v-tab> {{ $t('dialog.edit_library.tab_options') }}
</v-tab>
<v-tab class="justify-start">
<v-icon left class="hidden-xs-only">mdi-book-information-variant</v-icon>
{{ $t('dialog.edit_library.tab_metadata') }}
</v-tab>
<!-- Tab: General --> <!-- Tab: General -->
<v-tab-item> <v-tab-item>
<v-card flat> <v-card flat :min-height="$vuetify.breakpoint.xs ? $vuetify.breakpoint.height * .8 : undefined">
<v-container fluid> <v-container fluid>
<v-row> <v-row>
<v-col> <v-col>
<v-text-field v-model="form.name" <v-text-field v-model="form.name"
autofocus autofocus
:label="$t('dialog.edit_library.field_name')" :label="$t('dialog.edit_library.field_name')"
:error-messages="getErrors('name')" :error-messages="getErrors('name')"
@input="$v.form.name.$touch()" @input="$v.form.name.$touch()"
@blur="$v.form.name.$touch()" @blur="$v.form.name.$touch()"
/> />
</v-col> </v-col>
</v-row> </v-row>
<v-row justify="center"> <v-row justify="center">
<v-col cols="8" align-self="center"> <v-col cols="8" align-self="center">
<file-browser-dialog <file-browser-dialog
v-model="modalFileBrowser" v-model="modalFileBrowser"
:path.sync="form.path" :path.sync="form.path"
:confirm-text="$t('dialog.edit_library.file_browser_dialog_button_confirm')" :confirm-text="$t('dialog.edit_library.file_browser_dialog_button_confirm')"
:dialog-title="$t('dialog.edit_library.file_browser_dialog_title')" :dialog-title="$t('dialog.edit_library.file_browser_dialog_title')"
/> />
<v-text-field v-model="form.path" <v-text-field v-model="form.path"
:label="$t('dialog.edit_library.field_root_folder')" :label="$t('dialog.edit_library.field_root_folder')"
:error-messages="getErrors('path')" :error-messages="getErrors('path')"
@input="$v.form.path.$touch()" @input="$v.form.path.$touch()"
@blur="$v.form.path.$touch()" @blur="$v.form.path.$touch()"
/> />
</v-col> </v-col>
<v-col cols="4" align-self="center"> <v-col cols="4" align-self="center">
<v-btn @click="modalFileBrowser = true">{{ $t('dialog.edit_library.button_browse') }}</v-btn> <v-btn @click="modalFileBrowser = true">{{ $t('dialog.edit_library.button_browse') }}</v-btn>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</v-card> </v-card>
</v-tab-item> </v-tab-item>
<!-- Tab: Options --> <!-- Tab: Options -->
<v-tab-item> <v-tab-item>
<v-card flat> <v-card flat :min-height="$vuetify.breakpoint.xs ? $vuetify.breakpoint.height * .8 : undefined">
<v-container fluid> <v-container fluid>
<v-row> <v-row>
<v-col> <v-col cols="auto">
<v-checkbox <span class="text-subtitle-1 text--primary">{{ $t('dialog.edit_library.label_scanner') }}</span>
v-model="importComicInfo" <v-checkbox
:indeterminate="importComicInfo === 1" v-model="form.emptyTrashAfterScan"
hide-details :label="$t('dialog.edit_library.field_scanner_empty_trash_after_scan')"
> hide-details
<template v-slot:label> class="mx-4"
<span class="text-subtitle-2 text--primary">{{ />
<v-checkbox
v-model="form.scanForceModifiedTime"
:label="$t('dialog.edit_library.field_scanner_force_directory_modified_time')"
hide-details
class="mx-4"
/>
<v-checkbox
v-model="form.scanDeep"
:label="$t('dialog.edit_library.field_scanner_deep_scan')"
hide-details
class="mx-4"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-checkbox
v-model="fileManagement"
:indeterminate="fileManagement === 1"
hide-details
:label="$t('dialog.edit_library.label_file_management')"
>
<template v-slot:label>
<span class="text-subtitle-1 text--primary">{{
$t('dialog.edit_library.label_file_management')
}}</span>
</template>
</v-checkbox>
<v-checkbox
v-model="form.repairExtensions"
:label="$t('dialog.edit_library.field_repair_extensions')"
hide-details
class="mx-4"
/>
<v-checkbox
v-model="form.convertToCbz"
:label="$t('dialog.edit_library.field_convert_to_cbz')"
hide-details
class="mx-4"
/>
</v-col>
</v-row>
</v-container>
</v-card>
</v-tab-item>
<!-- Tab: Metadata -->
<v-tab-item>
<v-card flat :min-height="$vuetify.breakpoint.xs ? $vuetify.breakpoint.height * .8 : undefined">
<v-container fluid>
<v-row>
<v-col>
<v-checkbox
v-model="importComicInfo"
:indeterminate="importComicInfo === 1"
hide-details
>
<template v-slot:label>
<span class="text-subtitle-1 text--primary">{{
$t('dialog.edit_library.label_import_comicinfo') $t('dialog.edit_library.label_import_comicinfo')
}}</span> }}</span>
</template> </template>
</v-checkbox> </v-checkbox>
<v-checkbox <v-checkbox
v-model="form.importComicInfoBook" v-model="form.importComicInfoBook"
:label="$t('dialog.edit_library.field_import_comicinfo_book')" :label="$t('dialog.edit_library.field_import_comicinfo_book')"
hide-details hide-details
class="mx-4" class="mx-4"
/> />
<v-checkbox <v-checkbox
v-model="form.importComicInfoSeries" v-model="form.importComicInfoSeries"
:label="$t('dialog.edit_library.field_import_comicinfo_series')" :label="$t('dialog.edit_library.field_import_comicinfo_series')"
hide-details hide-details
class="mx-4" class="mx-4"
/> />
<v-checkbox <v-checkbox
v-model="form.importComicInfoCollection" v-model="form.importComicInfoCollection"
:label="$t('dialog.edit_library.field_import_comicinfo_collections')" :label="$t('dialog.edit_library.field_import_comicinfo_collections')"
hide-details hide-details
class="mx-4" class="mx-4"
/> />
<v-checkbox <v-checkbox
v-model="form.importComicInfoReadList" v-model="form.importComicInfoReadList"
:label="$t('dialog.edit_library.field_import_comicinfo_readlists')" :label="$t('dialog.edit_library.field_import_comicinfo_readlists')"
hide-details hide-details
class="mx-4" class="mx-4"
/> />
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col> <v-col>
<v-checkbox <v-checkbox
v-model="importEpub" v-model="importEpub"
:indeterminate="importEpub === 1" :indeterminate="importEpub === 1"
hide-details hide-details
> >
<template v-slot:label> <template v-slot:label>
<span class="text-subtitle-2 text--primary">{{ <span class="text-subtitle-1 text--primary">{{
$t('dialog.edit_library.label_import_epub') $t('dialog.edit_library.label_import_epub')
}}</span> }}</span>
</template> </template>
</v-checkbox> </v-checkbox>
<v-checkbox <v-checkbox
v-model="form.importEpubBook" v-model="form.importEpubBook"
:label="$t('dialog.edit_library.field_import_epub_book')" :label="$t('dialog.edit_library.field_import_epub_book')"
hide-details hide-details
class="mx-4" class="mx-4"
/> />
<v-checkbox <v-checkbox
v-model="form.importEpubSeries" v-model="form.importEpubSeries"
:label="$t('dialog.edit_library.field_import_epub_series')" :label="$t('dialog.edit_library.field_import_epub_series')"
hide-details hide-details
class="mx-4" class="mx-4"
/> />
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col> <v-col>
<span class="text-subtitle-2">{{ $t('dialog.edit_library.label_import_local') }}</span> <span class="text-subtitle-1 text--primary">{{
<v-checkbox $t('dialog.edit_library.label_import_local')
v-model="form.importLocalArtwork" }}</span>
:label="$t('dialog.edit_library.field_import_local_artwork')" <v-checkbox
hide-details v-model="form.importLocalArtwork"
class="mx-4" :label="$t('dialog.edit_library.field_import_local_artwork')"
/> hide-details
</v-col> class="mx-4"
</v-row> />
<v-row> </v-col>
<v-col> </v-row>
<span class="text-subtitle-2">{{ $t('dialog.edit_library.label_import_barcode_isbn') }}</span> <v-row>
<v-checkbox <v-col>
v-model="form.importBarcodeIsbn" <span class="text-subtitle-1 text--primary">{{
:label="$t('dialog.edit_library.field_import_barcode_isbn')" $t('dialog.edit_library.label_import_barcode_isbn')
hide-details }}</span>
class="mx-4" <v-checkbox
/> v-model="form.importBarcodeIsbn"
</v-col> :label="$t('dialog.edit_library.field_import_barcode_isbn')"
</v-row> hide-details
<v-row> class="mx-4"
<v-col cols="auto"> />
<v-checkbox </v-col>
v-model="scanner" </v-row>
:indeterminate="scanner === 1"
hide-details
>
<template v-slot:label>
<span class="text-subtitle-2 text--primary">{{ $t('dialog.edit_library.label_scanner') }}</span>
</template>
</v-checkbox>
<v-checkbox
v-model="form.scanForceModifiedTime"
:label="$t('dialog.edit_library.field_scanner_force_directory_modified_time')"
hide-details
class="mx-4"
/>
<v-checkbox
v-model="form.scanDeep"
:label="$t('dialog.edit_library.field_scanner_deep_scan')"
hide-details
class="mx-4"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-checkbox
v-model="fileManagement"
:indeterminate="fileManagement === 1"
hide-details
>
<template v-slot:label>
<span class="text-subtitle-2">{{ $t('dialog.edit_library.label_file_management') }}</span>
</template>
</v-checkbox>
<v-checkbox </v-container>
v-model="form.repairExtensions" </v-card>
:label="$t('dialog.edit_library.field_repair_extensions')" </v-tab-item>
hide-details
class="mx-4"
/>
<v-checkbox </v-tabs>
v-model="form.convertToCbz" </v-card-text>
:label="$t('dialog.edit_library.field_convert_to_cbz')"
hide-details
class="mx-4"
/>
</v-col>
</v-row>
</v-container>
</v-card>
</v-tab-item>
</v-tabs>
<v-card-actions class="hidden-xs-only"> <v-card-actions class="hidden-xs-only">
<v-spacer/> <v-spacer/>
<v-btn text @click="dialogClose">{{ $t('dialog.edit_library.button_cancel') }}</v-btn> <v-btn text @click="dialogClose">{{ $t('dialog.edit_library.button_cancel') }}</v-btn>
<v-btn color="primary" @click="dialogConfirm">{{ confirmText }}</v-btn> <v-btn color="primary" @click="nextTab" v-if="showNext">
{{ $t('dialog.edit_library.button_next') }}
</v-btn>
<v-btn color="primary" @click="dialogConfirm" v-else>{{ confirmText }}</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</form> </form>
@ -259,6 +287,7 @@ export default Vue.extend({
scanDeep: false, scanDeep: false,
repairExtensions: false, repairExtensions: false,
convertToCbz: false, convertToCbz: false,
emptyTrashAfterScan: false,
}, },
validationFieldNames: new Map([]), validationFieldNames: new Map([]),
} }
@ -270,6 +299,9 @@ export default Vue.extend({
confirmText(): string { confirmText(): string {
return this.library ? this.$t('dialog.edit_library.button_confirm_edit').toString() : this.$t('dialog.edit_library.button_confirm_add').toString() return this.library ? this.$t('dialog.edit_library.button_confirm_edit').toString() : this.$t('dialog.edit_library.button_confirm_add').toString()
}, },
showNext(): boolean {
return !this.library && this.tab !== 2
},
importComicInfo: { importComicInfo: {
get: function (): number { get: function (): number {
@ -301,20 +333,6 @@ export default Vue.extend({
}, },
}, },
scanner: {
get: function (): number {
const val = [this.form.scanDeep, this.form.scanForceModifiedTime]
const count = val.filter(Boolean).length
if (count === val.length) return 2
if (count === 0) return 0
return 1
},
set: function (value: boolean): void {
this.form.scanDeep = value
this.form.scanForceModifiedTime = value
},
},
fileManagement: { fileManagement: {
get: function (): number { get: function (): number {
const val = [this.form.repairExtensions, this.form.convertToCbz] const val = [this.form.repairExtensions, this.form.convertToCbz]
@ -352,6 +370,10 @@ export default Vue.extend({
}, },
}, },
methods: { methods: {
nextTab() {
this.$v.$touch()
if (!this.$v.$invalid) this.tab += 1
},
getErrors(fieldName: string) { getErrors(fieldName: string) {
const errors = [] const errors = []
@ -385,6 +407,7 @@ export default Vue.extend({
this.form.scanDeep = library ? library.scanDeep : false this.form.scanDeep = library ? library.scanDeep : false
this.form.repairExtensions = library ? library.repairExtensions : false this.form.repairExtensions = library ? library.repairExtensions : false
this.form.convertToCbz = library ? library.convertToCbz : false this.form.convertToCbz = library ? library.convertToCbz : false
this.form.emptyTrashAfterScan = library ? library.emptyTrashAfterScan : false
this.$v.$reset() this.$v.$reset()
}, },
validateLibrary() { validateLibrary() {
@ -406,6 +429,7 @@ export default Vue.extend({
scanDeep: this.form.scanDeep, scanDeep: this.form.scanDeep,
repairExtensions: this.form.repairExtensions, repairExtensions: this.form.repairExtensions,
convertToCbz: this.form.convertToCbz, convertToCbz: this.form.convertToCbz,
emptyTrashAfterScan: this.form.emptyTrashAfterScan,
} }
} }
return null return null

View file

@ -322,6 +322,7 @@
"button_cancel": "Cancel", "button_cancel": "Cancel",
"button_confirm_add": "Add", "button_confirm_add": "Add",
"button_confirm_edit": "Edit", "button_confirm_edit": "Edit",
"button_next": "Next",
"dialog_title_add": "Add Library", "dialog_title_add": "Add Library",
"dialot_title_edit": "Edit Library", "dialot_title_edit": "Edit Library",
"field_convert_to_cbz": "Automatically convert to CBZ", "field_convert_to_cbz": "Automatically convert to CBZ",
@ -337,6 +338,7 @@
"field_repair_extensions": "Automatically repair incorrect file extensions", "field_repair_extensions": "Automatically repair incorrect file extensions",
"field_root_folder": "Root folder", "field_root_folder": "Root folder",
"field_scanner_deep_scan": "Deep scan", "field_scanner_deep_scan": "Deep scan",
"field_scanner_empty_trash_after_scan": "Empty trash automatically after every scan",
"field_scanner_force_directory_modified_time": "Force directory modified time", "field_scanner_force_directory_modified_time": "Force directory modified time",
"file_browser_dialog_button_confirm": "Choose", "file_browser_dialog_button_confirm": "Choose",
"file_browser_dialog_title": "Library's root folder", "file_browser_dialog_title": "Library's root folder",
@ -347,6 +349,7 @@
"label_import_local": "Import local media assets", "label_import_local": "Import local media assets",
"label_scanner": "Scanner", "label_scanner": "Scanner",
"tab_general": "General", "tab_general": "General",
"tab_metadata": "Metadata",
"tab_options": "Options" "tab_options": "Options"
}, },
"edit_readlist": { "edit_readlist": {
@ -389,6 +392,11 @@
"field_all_libraries": "All libraries", "field_all_libraries": "All libraries",
"label_shared_with": "Shared with {name}" "label_shared_with": "Shared with {name}"
}, },
"empty_trash": {
"body": "By default the media server doesn't remove information for media right away. This helps if a drive is temporarily disconnected. When you empty the trash for a library, all information about missing media is deleted.",
"button_confirm": "Empty",
"title": "Empty trash for library"
},
"file_browser": { "file_browser": {
"button_cancel": "Cancel", "button_cancel": "Cancel",
"button_confirm_default": "Choose", "button_confirm_default": "Choose",
@ -455,11 +463,6 @@
"page_of_pages": "{page} / {pages}", "page_of_pages": "{page} / {pages}",
"title": "Inspect Book", "title": "Inspect Book",
"title_comparison": "Book Comparison" "title_comparison": "Book Comparison"
},
"empty_trash": {
"title": "Empty trash for library",
"body": "By default the media server doesn't remove information for media right away. This helps if a drive is temporarily disconnected. When you empty the trash for a library, all information about missing media is deleted.",
"button_confirm": "Empty"
} }
}, },
"enums": { "enums": {

View file

@ -12,7 +12,8 @@ interface LibraryCreationDto {
scanForceModifiedTime: boolean, scanForceModifiedTime: boolean,
scanDeep: boolean, scanDeep: boolean,
repairExtensions: boolean, repairExtensions: boolean,
convertToCbz: boolean convertToCbz: boolean,
emptyTrashAfterScan: boolean,
} }
interface LibraryUpdateDto { interface LibraryUpdateDto {
@ -29,7 +30,8 @@ interface LibraryUpdateDto {
scanForceModifiedTime: boolean, scanForceModifiedTime: boolean,
scanDeep: boolean, scanDeep: boolean,
repairExtensions: boolean, repairExtensions: boolean,
convertToCbz: boolean convertToCbz: boolean,
emptyTrashAfterScan: boolean,
} }
interface LibraryDto { interface LibraryDto {
@ -47,5 +49,6 @@ interface LibraryDto {
scanForceModifiedTime: boolean, scanForceModifiedTime: boolean,
scanDeep: boolean, scanDeep: boolean,
repairExtensions: boolean, repairExtensions: boolean,
convertToCbz: boolean convertToCbz: boolean,
emptyTrashAfterScan: boolean,
} }