fix(rar): unsupported rar archives are marked as such

solid, encrypted, and multi-volumes rar4 archives are not supported

closes #147
This commit is contained in:
Gauthier Roebroeck 2020-05-02 22:20:22 +08:00
parent d275506eac
commit 6c0ebbeee1
8 changed files with 24 additions and 13 deletions

View file

@ -1,6 +1,7 @@
package org.gotson.komga.domain.model package org.gotson.komga.domain.model
class MediaNotReadyException : Exception() class MediaNotReadyException : Exception()
class MediaUnsupportedException(message: String) : Exception(message)
class ImageConversionException(message: String) : Exception(message) class ImageConversionException(message: String) : Exception(message)
class DirectoryNotFoundException(message: String) : Exception(message) class DirectoryNotFoundException(message: String) : Exception(message)
class DuplicateNameException(message: String) : Exception(message) class DuplicateNameException(message: String) : Exception(message)

View file

@ -5,6 +5,7 @@ import org.gotson.komga.domain.model.Book
import org.gotson.komga.domain.model.BookPage import org.gotson.komga.domain.model.BookPage
import org.gotson.komga.domain.model.Media import org.gotson.komga.domain.model.Media
import org.gotson.komga.domain.model.MediaNotReadyException import org.gotson.komga.domain.model.MediaNotReadyException
import org.gotson.komga.domain.model.MediaUnsupportedException
import org.gotson.komga.infrastructure.image.ImageConverter import org.gotson.komga.infrastructure.image.ImageConverter
import org.gotson.komga.infrastructure.mediacontainer.ContentDetector import org.gotson.komga.infrastructure.mediacontainer.ContentDetector
import org.gotson.komga.infrastructure.mediacontainer.MediaContainerExtractor import org.gotson.komga.infrastructure.mediacontainer.MediaContainerExtractor
@ -36,6 +37,8 @@ class BookAnalyzer(
val entries = try { val entries = try {
supportedMediaTypes.getValue(mediaType).getEntries(book.path()) supportedMediaTypes.getValue(mediaType).getEntries(book.path())
} catch (ex: MediaUnsupportedException) {
return Media(mediaType = mediaType, status = Media.Status.UNSUPPORTED, comment = ex.message)
} catch (ex: Exception) { } catch (ex: Exception) {
logger.error(ex) { "Error while analyzing book: $book" } logger.error(ex) { "Error while analyzing book: $book" }
return Media(mediaType = mediaType, status = Media.Status.ERROR, comment = ex.message) return Media(mediaType = mediaType, status = Media.Status.ERROR, comment = ex.message)

View file

@ -1,10 +1,14 @@
package org.gotson.komga.infrastructure.mediacontainer package org.gotson.komga.infrastructure.mediacontainer
import org.gotson.komga.domain.model.MediaContainerEntry import org.gotson.komga.domain.model.MediaContainerEntry
import org.gotson.komga.domain.model.MediaUnsupportedException
import java.nio.file.Path import java.nio.file.Path
interface MediaContainerExtractor { interface MediaContainerExtractor {
fun mediaTypes(): List<String> fun mediaTypes(): List<String>
@Throws(MediaUnsupportedException::class)
fun getEntries(path: Path): List<MediaContainerEntry> fun getEntries(path: Path): List<MediaContainerEntry>
fun getEntryStream(path: Path, entryName: String): ByteArray fun getEntryStream(path: Path, entryName: String): ByteArray
} }

View file

@ -4,6 +4,7 @@ import com.github.junrar.Archive
import mu.KotlinLogging import mu.KotlinLogging
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
import org.gotson.komga.domain.model.MediaContainerEntry import org.gotson.komga.domain.model.MediaContainerEntry
import org.gotson.komga.domain.model.MediaUnsupportedException
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@ -22,6 +23,9 @@ class RarExtractor(
override fun getEntries(path: Path): List<MediaContainerEntry> = override fun getEntries(path: Path): List<MediaContainerEntry> =
Archive(Files.newInputStream(path)).use { rar -> Archive(Files.newInputStream(path)).use { rar ->
if (rar.mainHeader.isEncrypted) throw MediaUnsupportedException("Encrypted RAR archives are not supported")
if (rar.mainHeader.isSolid) throw MediaUnsupportedException("Solid RAR archives are not supported")
if (rar.mainHeader.isMultiVolume) throw MediaUnsupportedException("Multi-Volume RAR archives are not supported")
rar.fileHeaders rar.fileHeaders
.filter { !it.isDirectory } .filter { !it.isDirectory }
.map { .map {
@ -38,6 +42,6 @@ class RarExtractor(
override fun getEntryStream(path: Path, entryName: String): ByteArray = override fun getEntryStream(path: Path, entryName: String): ByteArray =
Archive(Files.newInputStream(path)).use { rar -> Archive(Files.newInputStream(path)).use { rar ->
val header = rar.fileHeaders.find { it.fileNameString == entryName } val header = rar.fileHeaders.find { it.fileNameString == entryName }
rar.getInputStream(header).readBytes() rar.getInputStream(header).readBytes()
} }
} }

View file

@ -21,30 +21,30 @@ class BookAnalyzerTest(
@Autowired private val bookAnalyzer: BookAnalyzer @Autowired private val bookAnalyzer: BookAnalyzer
) { ) {
@ParameterizedTest @Test
@ValueSource(strings = [ fun `given rar4 archive when analyzing then media status is READY`() {
"rar4.rar", "rar4-solid.rar" val file = ClassPathResource("archives/rar4.rar")
])
fun `given rar4 archive when analyzing then media status is READY`(fileName: String) {
val file = ClassPathResource("archives/$fileName")
val book = Book("book", file.url, LocalDateTime.now()) val book = Book("book", file.url, LocalDateTime.now())
val media = bookAnalyzer.analyze(book) val media = bookAnalyzer.analyze(book)
assertThat(media.mediaType).isEqualTo("application/x-rar-compressed; version=4") assertThat(media.mediaType).isEqualTo("application/x-rar-compressed; version=4")
assertThat(media.status).isEqualTo(Media.Status.READY) assertThat(media.status).isEqualTo(Media.Status.READY)
assertThat(media.pages).hasSize(1) assertThat(media.pages).hasSize(3)
} }
@Test @ParameterizedTest
fun `given rar4 encrypted archive when analyzing then media status is ERROR`() { @ValueSource(strings = [
val file = ClassPathResource("archives/rar4-encrypted.rar") "rar4-solid.rar", "rar4-encrypted.rar"
])
fun `given rar4 solid or encrypted archive when analyzing then media status is UNSUPPORTED`(fileName: String) {
val file = ClassPathResource("archives/rar4-solid.rar")
val book = Book("book", file.url, LocalDateTime.now()) val book = Book("book", file.url, LocalDateTime.now())
val media = bookAnalyzer.analyze(book) val media = bookAnalyzer.analyze(book)
assertThat(media.mediaType).isEqualTo("application/x-rar-compressed; version=4") assertThat(media.mediaType).isEqualTo("application/x-rar-compressed; version=4")
assertThat(media.status).isEqualTo(Media.Status.ERROR) assertThat(media.status).isEqualTo(Media.Status.UNSUPPORTED)
} }
@ParameterizedTest @ParameterizedTest
@ -112,5 +112,4 @@ class BookAnalyzerTest(
assertThat(media.status).isEqualTo(Media.Status.READY) assertThat(media.status).isEqualTo(Media.Status.READY)
assertThat(media.pages).hasSize(1) assertThat(media.pages).hasSize(1)
} }
} }