Skip to content

Commit

Permalink
feat: better management of book analysis errors
Browse files Browse the repository at this point in the history
  • Loading branch information
gotson committed Jan 8, 2020
1 parent 4afa912 commit 8c26a31
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 99 deletions.
15 changes: 8 additions & 7 deletions komga-webui/src/components/BrowseBook.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,17 @@
<v-col cols="10" class="body-2">{{ book.size }}</v-col>
</v-row>

<v-row v-if="book.media.comment">
<v-col cols="2" md="1" lg="1" xl="1" class="body-2">COMMENT</v-col>
<v-col cols="10" class="body-2">
<span class="error--text font-weight-bold">{{ book.media.comment }}</span>
</v-col>
</v-row>

<v-row>
<v-col cols="2" md="1" lg="1" xl="1" class="body-2">FORMAT</v-col>
<v-col cols="10" class="body-2">
<template v-if="book.media.status === 'ERROR'">
<span class="error--text font-weight-bold">Book analysis failed</span>
</template>
<template v-else-if="book.media.status === 'UNSUPPORTED'">
<span class="error--text font-weight-bold">File type not supported: {{ book.media.mediaType }}</span>
</template>
<template v-else>{{ format.type }}</template>
<span>{{ format.type }}</span>
</v-col>
</v-row>

Expand Down
3 changes: 2 additions & 1 deletion komga-webui/src/types/komga-books.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ interface BookDto {
interface MediaDto {
status: string,
mediaType: string,
pagesCount: number
pagesCount: number,
comment: string
}

interface PageDto {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.gotson.komga.domain.model

class MediaNotReadyException : Exception()
class EmptyBookException(val mediaType: String) : Exception()
class UnsupportedMediaTypeException(message: String, val mediaType: String) : Exception(message)
class ImageConversionException(message: String) : Exception(message)
class DirectoryNotFoundException(message: String) : Exception(message)
class DuplicateNameException(message: String) : Exception(message)
class PathContainedInPath(message: String) : Exception(message)
5 changes: 4 additions & 1 deletion komga/src/main/kotlin/org/gotson/komga/domain/model/Media.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ class Media(
@Lob
var thumbnail: ByteArray? = null,

pages: Iterable<BookPage> = emptyList()
pages: Iterable<BookPage> = emptyList(),

@Column(name = "comment")
var comment: String? = null
) : AuditableEntity() {
@Id
@GeneratedValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import mu.KotlinLogging
import net.coobird.thumbnailator.Thumbnails
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
import org.gotson.komga.domain.model.Book
import org.gotson.komga.domain.model.EmptyBookException
import org.gotson.komga.domain.model.Media
import org.gotson.komga.domain.model.MediaNotReadyException
import org.gotson.komga.domain.model.UnsupportedMediaTypeException
import org.gotson.komga.infrastructure.archive.ContentDetector
import org.gotson.komga.infrastructure.archive.PdfExtractor
import org.gotson.komga.infrastructure.archive.RarExtractor
Expand Down Expand Up @@ -37,21 +35,25 @@ class BookAnalyzer(
private val thumbnailSize = 300
private val thumbnailFormat = "jpeg"

@Throws(
UnsupportedMediaTypeException::class,
EmptyBookException::class
)
fun analyze(book: Book): Media {
logger.info { "Trying to analyze book: $book" }

val mediaType = contentDetector.detectMediaType(book.path())
logger.info { "Detected media type: $mediaType" }
if (!supportedMediaTypes.keys.contains(mediaType))
throw UnsupportedMediaTypeException("Unsupported mime type: $mediaType. File: $book", mediaType)
return Media(mediaType = mediaType, status = Media.Status.UNSUPPORTED, comment = "Media type $mediaType is not supported")

val pages = try {
supportedMediaTypes.getValue(mediaType).getPagesList(book.path()).sortedWith(compareBy(natSortComparator) { it.fileName })
} catch (ex: Exception) {
logger.error(ex) { "Error while analyzing book: $book" }
return Media(mediaType = mediaType, status = Media.Status.ERROR, comment = ex.message)
}

val pages = supportedMediaTypes.getValue(mediaType).getPagesList(book.path())
.sortedWith(compareBy(natSortComparator) { it.fileName })
if (pages.isEmpty()) throw EmptyBookException(mediaType)
if (pages.isEmpty()) {
logger.warn { "Book $book does not contain any pages" }
return Media(mediaType = mediaType, status = Media.Status.ERROR, comment = "Book does not contain any pages")
}
logger.info { "Book has ${pages.size} pages" }

logger.info { "Trying to generate cover for book: $book" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import mu.KotlinLogging
import org.apache.commons.lang3.time.DurationFormatUtils
import org.gotson.komga.domain.model.Book
import org.gotson.komga.domain.model.BookPageContent
import org.gotson.komga.domain.model.EmptyBookException
import org.gotson.komga.domain.model.ImageConversionException
import org.gotson.komga.domain.model.Media
import org.gotson.komga.domain.model.MediaNotReadyException
import org.gotson.komga.domain.model.UnsupportedMediaTypeException
import org.gotson.komga.domain.persistence.BookRepository
import org.gotson.komga.infrastructure.image.ImageConverter
import org.gotson.komga.infrastructure.image.ImageType
Expand All @@ -34,15 +33,9 @@ class BookLifecycle(
return AsyncResult(measureTimeMillis {
try {
book.media = bookAnalyzer.analyze(book)
} catch (ex: UnsupportedMediaTypeException) {
logger.warn { "Unsupported media type: ${ex.mediaType}. Book: $book" }
book.media = Media(status = Media.Status.UNSUPPORTED, mediaType = ex.mediaType)
} catch (ex: EmptyBookException) {
logger.warn { "Book does not contain any images: $book" }
book.media = Media(status = Media.Status.ERROR, mediaType = ex.mediaType)
} catch (ex: Exception) {
logger.error(ex) { "Error while parsing. Book: $book" }
book.media = Media(status = Media.Status.ERROR)
logger.error(ex) { "Error while analyzing book: $book" }
book.media = Media(status = Media.Status.ERROR, comment = ex.message)
}
bookRepository.save(book)
}.also { logger.info { "Parsing finished in ${DurationFormatUtils.formatDurationHMS(it)}" } })
Expand All @@ -64,9 +57,9 @@ class BookLifecycle(
}

@Throws(
UnsupportedMediaTypeException::class,
MediaNotReadyException::class,
IndexOutOfBoundsException::class
ImageConversionException::class,
MediaNotReadyException::class,
IndexOutOfBoundsException::class
)
fun getBookPage(book: Book, number: Int, convertTo: ImageType? = null): BookPageContent {
val pageContent = bookAnalyzer.getPageContent(book, number)
Expand All @@ -75,10 +68,10 @@ class BookLifecycle(
convertTo?.let {
val msg = "Convert page #$number of book $book from $pageMediaType to ${it.mediaType}"
if (!imageConverter.supportedReadMediaTypes.contains(pageMediaType)) {
throw UnsupportedMediaTypeException("$msg: unsupported read format $pageMediaType", pageMediaType)
throw ImageConversionException("$msg: unsupported read format $pageMediaType")
}
if (!imageConverter.supportedWriteMediaTypes.contains(it.mediaType)) {
throw UnsupportedMediaTypeException("$msg: unsupported cannot write format ${it.mediaType}", it.mediaType)
throw ImageConversionException("$msg: unsupported write format ${it.mediaType}")
}
if (pageMediaType == it.mediaType) {
logger.warn { "$msg: same format, no need for conversion" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.github.klinq.jpaspec.`in`
import com.github.klinq.jpaspec.likeLower
import mu.KotlinLogging
import org.gotson.komga.domain.model.Book
import org.gotson.komga.domain.model.ImageConversionException
import org.gotson.komga.domain.model.Media
import org.gotson.komga.domain.model.MediaNotReadyException
import org.gotson.komga.domain.persistence.BookRepository
Expand Down Expand Up @@ -227,6 +228,8 @@ class BookController(
.body(pageContent.content)
} catch (ex: IndexOutOfBoundsException) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Page number does not exist")
} catch (ex: ImageConversionException) {
throw ResponseStatusException(HttpStatus.NOT_FOUND, ex.message)
} catch (ex: MediaNotReadyException) {
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book analysis failed")
} catch (ex: NoSuchFileException) {
Expand Down
129 changes: 66 additions & 63 deletions komga/src/main/kotlin/org/gotson/komga/interfaces/rest/Dto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,84 +9,87 @@ import java.time.ZoneId
import java.time.ZoneOffset

data class SeriesDto(
val id: Long,
val libraryId: Long,
val name: String,
val url: String,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val created: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val lastModified: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val fileLastModified: LocalDateTime,
val booksCount: Int
val id: Long,
val libraryId: Long,
val name: String,
val url: String,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val created: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val lastModified: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val fileLastModified: LocalDateTime,
val booksCount: Int
)

fun Series.toDto(includeUrl: Boolean) = SeriesDto(
id = id,
libraryId = library.id,
name = name,
url = if (includeUrl) url.toURI().path else "",
created = createdDate?.toUTC(),
lastModified = lastModifiedDate?.toUTC(),
fileLastModified = fileLastModified.toUTC(),
booksCount = books.size
id = id,
libraryId = library.id,
name = name,
url = if (includeUrl) url.toURI().path else "",
created = createdDate?.toUTC(),
lastModified = lastModifiedDate?.toUTC(),
fileLastModified = fileLastModified.toUTC(),
booksCount = books.size
)

data class BookDto(
val id: Long,
val seriesId: Long,
val name: String,
val url: String,
val number: Float,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val created: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val lastModified: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val fileLastModified: LocalDateTime,
val sizeBytes: Long,
val size: String,
@Deprecated("Deprecated since 0.10", ReplaceWith("media"))
val metadata: MediaDto,
val media: MediaDto
val id: Long,
val seriesId: Long,
val name: String,
val url: String,
val number: Float,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val created: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val lastModified: LocalDateTime?,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val fileLastModified: LocalDateTime,
val sizeBytes: Long,
val size: String,
@Deprecated("Deprecated since 0.10", ReplaceWith("media"))
val metadata: MediaDto,
val media: MediaDto
)

data class MediaDto(
val status: String,
val mediaType: String,
val pagesCount: Int
val status: String,
val mediaType: String,
val pagesCount: Int,
val comment: String
)

fun Book.toDto(includeFullUrl: Boolean) =
BookDto(
id = id,
seriesId = series.id,
name = name,
url = if (includeFullUrl) url.toURI().path else FilenameUtils.getName(url.toURI().path),
number = number,
created = createdDate?.toUTC(),
lastModified = lastModifiedDate?.toUTC(),
fileLastModified = fileLastModified.toUTC(),
sizeBytes = fileSize,
size = fileSizeHumanReadable(),
metadata = MediaDto(
status = media.status.toString(),
mediaType = media.mediaType ?: "",
pagesCount = media.pages.size
),
media = MediaDto(
status = media.status.toString(),
mediaType = media.mediaType ?: "",
pagesCount = media.pages.size
)
BookDto(
id = id,
seriesId = series.id,
name = name,
url = if (includeFullUrl) url.toURI().path else FilenameUtils.getName(url.toURI().path),
number = number,
created = createdDate?.toUTC(),
lastModified = lastModifiedDate?.toUTC(),
fileLastModified = fileLastModified.toUTC(),
sizeBytes = fileSize,
size = fileSizeHumanReadable(),
metadata = MediaDto(
status = media.status.toString(),
mediaType = media.mediaType ?: "",
pagesCount = media.pages.size,
comment = media.comment ?: ""
),
media = MediaDto(
status = media.status.toString(),
mediaType = media.mediaType ?: "",
pagesCount = media.pages.size,
comment = media.comment ?: ""
)
)

data class PageDto(
val number: Int,
val fileName: String,
val mediaType: String
val number: Int,
val fileName: String,
val mediaType: String
)

fun LocalDateTime.toUTC(): LocalDateTime =
atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime()
atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
alter table media
add (comment varchar);

0 comments on commit 8c26a31

Please sign in to comment.