mirror of
https://github.com/gotson/komga.git
synced 2025-12-20 23:45:11 +01:00
fix: prevent transient scanning of directories that are part of existing libraries
This commit is contained in:
parent
e83eded07b
commit
8a92b84fd0
6 changed files with 46 additions and 13 deletions
|
|
@ -20,3 +20,4 @@ ERR_1013 | No unique match for book number within series
|
|||
ERR_1014 | No match for book number within series
|
||||
ERR_1015 | Error while deserializing ComicRack ReadingList
|
||||
ERR_1016 | Directory not accessible or not a directory
|
||||
ERR_1017 | Cannot scan folder that is part of an existing library
|
||||
|
|
|
|||
|
|
@ -434,7 +434,9 @@
|
|||
"ERR_1012": "No match for series",
|
||||
"ERR_1013": "No unique match for book number within series",
|
||||
"ERR_1014": "No match for book number within series",
|
||||
"ERR_1015": "Error while deserializing ComicRack ReadingList"
|
||||
"ERR_1015": "Error while deserializing ComicRack ReadingList",
|
||||
"ERR_1016": "Directory not accessible or not a directory",
|
||||
"ERR_1017": "Cannot scan folder that is part of an existing library"
|
||||
},
|
||||
"filter": {
|
||||
"age_rating": "age rating",
|
||||
|
|
|
|||
|
|
@ -6,25 +6,22 @@ const API_TRANSIENT_BOOKS = '/api/v1/transient-books'
|
|||
export default class KomgaTransientBooksService {
|
||||
private http: AxiosInstance
|
||||
|
||||
constructor (http: AxiosInstance) {
|
||||
constructor(http: AxiosInstance) {
|
||||
this.http = http
|
||||
}
|
||||
|
||||
async scanForTransientBooks (path: string): Promise<TransientBookDto[]> {
|
||||
async scanForTransientBooks(path: string): Promise<TransientBookDto[]> {
|
||||
try {
|
||||
return (await this.http.post(API_TRANSIENT_BOOKS, {
|
||||
path: path,
|
||||
})).data
|
||||
} catch (e) {
|
||||
let msg = `An error occurred while trying to scan for transient book`
|
||||
if (e.response.data.message) {
|
||||
msg += `: ${e.response.data.message}`
|
||||
}
|
||||
throw new Error(msg)
|
||||
if (e.response.data.message) throw new Error(e.response.data.message)
|
||||
throw new Error('An error occurred while trying to scan for transient book')
|
||||
}
|
||||
}
|
||||
|
||||
async analyze (id: string): Promise<TransientBookDto> {
|
||||
async analyze(id: string): Promise<TransientBookDto> {
|
||||
try {
|
||||
return (await this.http.post(`${API_TRANSIENT_BOOKS}/${id}/analyze`)).data
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -84,6 +84,19 @@
|
|||
</v-row>
|
||||
</template>
|
||||
|
||||
<v-snackbar
|
||||
v-model="snackbar"
|
||||
bottom
|
||||
color="error"
|
||||
>
|
||||
{{ snackText }}
|
||||
<v-btn
|
||||
text
|
||||
@click="snackbar = false"
|
||||
>{{ $t('common.close') }}
|
||||
</v-btn>
|
||||
</v-snackbar>
|
||||
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
|
|
@ -96,6 +109,7 @@ import SeriesPickerDialog from "@/components/dialogs/SeriesPickerDialog.vue";
|
|||
import {SeriesDto} from "@/types/komga-series";
|
||||
import {BookImportBatchDto, BookImportDto} from "@/types/komga-books";
|
||||
import {CopyMode} from "@/types/enum-books";
|
||||
import {convertErrorCodes} from "@/functions/error-codes";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BookImport',
|
||||
|
|
@ -110,6 +124,8 @@ export default Vue.extend({
|
|||
transientBooks: [] as TransientBookDto[],
|
||||
copyMode: CopyMode.HARDLINK,
|
||||
importFinished: false,
|
||||
snackbar: false,
|
||||
snackText: '',
|
||||
}),
|
||||
computed: {
|
||||
globalSelect: {
|
||||
|
|
@ -146,7 +162,11 @@ export default Vue.extend({
|
|||
methods: {
|
||||
async scanBooks() {
|
||||
this.transientBooks = []
|
||||
this.transientBooks = await this.$komgaTransientBooks.scanForTransientBooks(this.importPath)
|
||||
try {
|
||||
this.transientBooks = await this.$komgaTransientBooks.scanForTransientBooks(this.importPath)
|
||||
} catch (e) {
|
||||
this.showSnack(convertErrorCodes(e.message))
|
||||
}
|
||||
this.selected = this.$_.range(this.transientBooks.length)
|
||||
this.payloads = this.payloads.splice(this.transientBooks.length, this.payloads.length)
|
||||
this.importFinished = false
|
||||
|
|
@ -157,6 +177,10 @@ export default Vue.extend({
|
|||
this.importFinished = true
|
||||
}
|
||||
},
|
||||
showSnack(message: string) {
|
||||
this.snackText = message
|
||||
this.snackbar = true
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import org.gotson.komga.domain.model.BookPageContent
|
|||
import org.gotson.komga.domain.model.BookWithMedia
|
||||
import org.gotson.komga.domain.model.Media
|
||||
import org.gotson.komga.domain.model.MediaNotReadyException
|
||||
import org.gotson.komga.domain.model.PathContainedInPath
|
||||
import org.gotson.komga.domain.persistence.LibraryRepository
|
||||
import org.gotson.komga.domain.persistence.TransientBookRepository
|
||||
import org.springframework.stereotype.Service
|
||||
import java.nio.file.Paths
|
||||
|
|
@ -13,10 +15,17 @@ class TransientBookLifecycle(
|
|||
private val transientBookRepository: TransientBookRepository,
|
||||
private val bookAnalyzer: BookAnalyzer,
|
||||
private val fileSystemScanner: FileSystemScanner,
|
||||
private val libraryRepository: LibraryRepository,
|
||||
) {
|
||||
|
||||
fun scanAndPersist(filePath: String): List<BookWithMedia> {
|
||||
val books = fileSystemScanner.scanRootFolder(Paths.get(filePath)).values.flatten().map { BookWithMedia(it, Media()) }
|
||||
val folderToScan = Paths.get(filePath)
|
||||
|
||||
libraryRepository.findAll().forEach { library ->
|
||||
if (folderToScan.startsWith(library.path())) throw PathContainedInPath("Cannot scan folder that is part of an existing library", "ERR_1017")
|
||||
}
|
||||
|
||||
val books = fileSystemScanner.scanRootFolder(folderToScan).values.flatten().map { BookWithMedia(it, Media()) }
|
||||
|
||||
transientBookRepository.saveAll(books)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package org.gotson.komga.interfaces.rest
|
|||
import com.jakewharton.byteunits.BinaryByteUnit
|
||||
import mu.KotlinLogging
|
||||
import org.gotson.komga.domain.model.BookWithMedia
|
||||
import org.gotson.komga.domain.model.DirectoryNotFoundException
|
||||
import org.gotson.komga.domain.model.CodedException
|
||||
import org.gotson.komga.domain.model.MediaNotReadyException
|
||||
import org.gotson.komga.domain.model.ROLE_ADMIN
|
||||
import org.gotson.komga.domain.persistence.TransientBookRepository
|
||||
|
|
@ -43,7 +43,7 @@ class TransientBooksController(
|
|||
transientBookLifecycle.scanAndPersist(request.path)
|
||||
.sortedBy { it.book.path() }
|
||||
.map { it.toDto() }
|
||||
} catch (e: DirectoryNotFoundException) {
|
||||
} catch (e: CodedException) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, e.code)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue