mirror of
https://github.com/gotson/komga.git
synced 2026-05-08 12:35:30 +02:00
parent
3c97c20481
commit
38ad00c307
5 changed files with 117 additions and 0 deletions
|
|
@ -512,6 +512,12 @@
|
||||||
"title_comparison": "Book Comparison"
|
"title_comparison": "Book Comparison"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"duplicates": {
|
||||||
|
"file_hash": "File hash",
|
||||||
|
"size": "Size",
|
||||||
|
"title": "Duplicate Files",
|
||||||
|
"url": "URL"
|
||||||
|
},
|
||||||
"enums": {
|
"enums": {
|
||||||
"copy_mode": {
|
"copy_mode": {
|
||||||
"HARDLINK": "Hardlink/Copy Files",
|
"HARDLINK": "Hardlink/Copy Files",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,12 @@ const router = new Router({
|
||||||
beforeEnter: adminGuard,
|
beforeEnter: adminGuard,
|
||||||
component: () => import(/* webpackChunkName: "settings-analysis" */ './views/SettingsMediaAnalysis.vue'),
|
component: () => import(/* webpackChunkName: "settings-analysis" */ './views/SettingsMediaAnalysis.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/settings/duplicates',
|
||||||
|
name: 'settings-duplicates',
|
||||||
|
beforeEnter: adminGuard,
|
||||||
|
component: () => import(/* webpackChunkName: "settings-duplicates" */ './views/SettingsDuplicates.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/settings/server',
|
path: '/settings/server',
|
||||||
name: 'settings-server',
|
name: 'settings-server',
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,21 @@ export default class KomgaBooksService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDuplicateBooks(pageRequest?: PageRequest): Promise<Page<BookDto>> {
|
||||||
|
try {
|
||||||
|
return (await this.http.get(`${API_BOOKS}/duplicates`, {
|
||||||
|
params: pageRequest,
|
||||||
|
paramsSerializer: params => qs.stringify(params, {indices: false}),
|
||||||
|
})).data
|
||||||
|
} catch (e) {
|
||||||
|
let msg = 'An error occurred while trying to retrieve duplicate books'
|
||||||
|
if (e.response.data.message) {
|
||||||
|
msg += `: ${e.response.data.message}`
|
||||||
|
}
|
||||||
|
throw new Error(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getBooksOnDeck(libraryId?: string, pageRequest?: PageRequest): Promise<Page<BookDto>> {
|
async getBooksOnDeck(libraryId?: string, pageRequest?: PageRequest): Promise<Page<BookDto>> {
|
||||||
try {
|
try {
|
||||||
const params = {...pageRequest} as any
|
const params = {...pageRequest} as any
|
||||||
|
|
|
||||||
89
komga-webui/src/views/SettingsDuplicates.vue
Normal file
89
komga-webui/src/views/SettingsDuplicates.vue
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
<template>
|
||||||
|
<v-container fluid class="pa-6">
|
||||||
|
<v-data-table
|
||||||
|
:headers="headers"
|
||||||
|
:items="books"
|
||||||
|
:options.sync="options"
|
||||||
|
:server-items-length="totalBooks"
|
||||||
|
:loading="loading"
|
||||||
|
sort-by="fileHash"
|
||||||
|
multi-sort
|
||||||
|
show-group-by
|
||||||
|
group-by="fileHash"
|
||||||
|
class="elevation-1"
|
||||||
|
:footer-props="{
|
||||||
|
itemsPerPageOptions: [20, 50, 100]
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template v-slot:item.name="{ item }">
|
||||||
|
<router-link :to="{name:'browse-book', params: {bookId: item.id}}">{{ item.name }}</router-link>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue'
|
||||||
|
import {MediaStatus} from '@/types/enum-books'
|
||||||
|
import {BookDto} from '@/types/komga-books'
|
||||||
|
import {convertErrorCodes} from '@/functions/error-codes'
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: 'SettingsDuplicates',
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
books: [] as BookDto[],
|
||||||
|
totalBooks: 0,
|
||||||
|
loading: true,
|
||||||
|
options: {} as any,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
options: {
|
||||||
|
handler() {
|
||||||
|
this.loadBooks()
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
headers(): object[] {
|
||||||
|
return [
|
||||||
|
{text: this.$i18n.t('duplicates.file_hash').toString(), value: 'fileHash'},
|
||||||
|
{text: this.$i18n.t('duplicates.url').toString(), value: 'url', groupable: false},
|
||||||
|
{text: this.$i18n.t('duplicates.size').toString(), value: 'size', groupable: false},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
this.loadBooks()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadBooks() {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
const {sortBy, sortDesc, page, itemsPerPage} = this.options
|
||||||
|
|
||||||
|
const pageRequest = {
|
||||||
|
page: page - 1,
|
||||||
|
size: itemsPerPage,
|
||||||
|
sort: [],
|
||||||
|
} as PageRequest
|
||||||
|
|
||||||
|
for (let i = 0; i < sortBy.length; i++) {
|
||||||
|
pageRequest.sort!!.push(`${sortBy[i]},${sortDesc[i] ? 'desc' : 'asc'}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const booksPage = await this.$komgaBooks.getDuplicateBooks(pageRequest)
|
||||||
|
this.totalBooks = booksPage.totalElements
|
||||||
|
this.books = booksPage.content
|
||||||
|
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
{{ $t('media_analysis.media_analysis') }}
|
{{ $t('media_analysis.media_analysis') }}
|
||||||
</v-badge>
|
</v-badge>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
|
<v-tab :to="{name: 'settings-duplicates'}">{{ $t('duplicates.title') }}</v-tab>
|
||||||
<v-tab :to="{name: 'settings-users'}">{{ $t('users.users') }}</v-tab>
|
<v-tab :to="{name: 'settings-users'}">{{ $t('users.users') }}</v-tab>
|
||||||
<v-tab :to="{name: 'settings-server'}">{{ $t('server.tab_title') }}</v-tab>
|
<v-tab :to="{name: 'settings-server'}">{{ $t('server.tab_title') }}</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue