diff --git a/komga/src/flyway/resources/db/migration/sqlite/V20211228152113__book_metadata_link.sql b/komga/src/flyway/resources/db/migration/sqlite/V20211228152113__book_metadata_link.sql new file mode 100644 index 000000000..104b34677 --- /dev/null +++ b/komga/src/flyway/resources/db/migration/sqlite/V20211228152113__book_metadata_link.sql @@ -0,0 +1,10 @@ +CREATE TABLE BOOK_METADATA_LINK +( + LABEL varchar NOT NULL, + URL varchar NOT NULL, + BOOK_ID varchar NOT NULL, + FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID) +); + +alter table book_metadata + add column LINKS_LOCK boolean NOT NULL DEFAULT 0; diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt index 694e7b968..dabdf2995 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt @@ -12,6 +12,7 @@ class BookMetadata( val authors: List = emptyList(), tags: Set = emptySet(), val isbn: String = "", + val links: List = emptyList(), val titleLock: Boolean = false, val summaryLock: Boolean = false, @@ -21,6 +22,7 @@ class BookMetadata( val authorsLock: Boolean = false, val tagsLock: Boolean = false, val isbnLock: Boolean = false, + val linksLock: Boolean = false, val bookId: String = "", @@ -42,6 +44,7 @@ class BookMetadata( authors: List = this.authors.toList(), tags: Set = this.tags, isbn: String = this.isbn, + links: List = this.links, titleLock: Boolean = this.titleLock, summaryLock: Boolean = this.summaryLock, numberLock: Boolean = this.numberLock, @@ -50,9 +53,10 @@ class BookMetadata( authorsLock: Boolean = this.authorsLock, tagsLock: Boolean = this.tagsLock, isbnLock: Boolean = this.isbnLock, + linksLock: Boolean = this.linksLock, bookId: String = this.bookId, createdDate: LocalDateTime = this.createdDate, - lastModifiedDate: LocalDateTime = this.lastModifiedDate + lastModifiedDate: LocalDateTime = this.lastModifiedDate, ) = BookMetadata( title = title, @@ -63,6 +67,7 @@ class BookMetadata( authors = authors, tags = tags, isbn = isbn, + links = links, titleLock = titleLock, summaryLock = summaryLock, numberLock = numberLock, @@ -71,11 +76,12 @@ class BookMetadata( authorsLock = authorsLock, tagsLock = tagsLock, isbnLock = isbnLock, + linksLock = linksLock, bookId = bookId, createdDate = createdDate, - lastModifiedDate = lastModifiedDate + lastModifiedDate = lastModifiedDate, ) override fun toString(): String = - "BookMetadata(numberSort=$numberSort, releaseDate=$releaseDate, authors=$authors, isbn='$isbn', titleLock=$titleLock, summaryLock=$summaryLock, numberLock=$numberLock, numberSortLock=$numberSortLock, releaseDateLock=$releaseDateLock, authorsLock=$authorsLock, tagsLock=$tagsLock, isbnLock=$isbnLock, bookId='$bookId', createdDate=$createdDate, lastModifiedDate=$lastModifiedDate, title='$title', summary='$summary', number='$number', tags=$tags)" + "BookMetadata(numberSort=$numberSort, releaseDate=$releaseDate, authors=$authors, isbn='$isbn', links=$links, titleLock=$titleLock, summaryLock=$summaryLock, numberLock=$numberLock, numberSortLock=$numberSortLock, releaseDateLock=$releaseDateLock, authorsLock=$authorsLock, tagsLock=$tagsLock, isbnLock=$isbnLock, linksLock=$linksLock, bookId='$bookId', createdDate=$createdDate, lastModifiedDate=$lastModifiedDate, title='$title', summary='$summary', number='$number', tags=$tags)" } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadataPatch.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadataPatch.kt index 1af57b341..875366401 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadataPatch.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadataPatch.kt @@ -10,6 +10,7 @@ data class BookMetadataPatch( val releaseDate: LocalDate? = null, val authors: List? = null, val isbn: String? = null, + val links: List? = null, val readLists: List = emptyList() ) { @@ -30,4 +31,5 @@ enum class BookMetadataPatchCapability { ISBN, READ_LISTS, THUMBNAILS, + LINKS, } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/WebLink.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/WebLink.kt new file mode 100644 index 000000000..ea87b828b --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/WebLink.kt @@ -0,0 +1,8 @@ +package org.gotson.komga.domain.model + +import java.net.URI + +data class WebLink( + val label: String, + val url: URI, +) diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataApplier.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataApplier.kt index 043ab3fcf..6f8ed77f6 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataApplier.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/MetadataApplier.kt @@ -23,6 +23,7 @@ class MetadataApplier { releaseDate = getIfNotLocked(releaseDate, patch.releaseDate, releaseDateLock), authors = getIfNotLocked(authors, patch.authors, authorsLock), isbn = getIfNotLocked(isbn, patch.isbn, isbnLock), + links = getIfNotLocked(links, patch.links, linksLock), ) } @@ -38,7 +39,7 @@ class MetadataApplier { publisher = getIfNotLocked(publisher, patch.publisher, publisherLock), language = getIfNotLocked(language, patch.language, languageLock), genres = getIfNotLocked(genres, patch.genres, genresLock), - totalBookCount = getIfNotLocked(totalBookCount, patch.totalBookCount, totalBookCountLock) + totalBookCount = getIfNotLocked(totalBookCount, patch.totalBookCount, totalBookCountLock), ) } } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt index 8f73ed8dc..021728b48 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDao.kt @@ -2,6 +2,7 @@ package org.gotson.komga.infrastructure.jooq import org.gotson.komga.domain.model.Author import org.gotson.komga.domain.model.BookMetadata +import org.gotson.komga.domain.model.WebLink import org.gotson.komga.domain.persistence.BookMetadataRepository import org.gotson.komga.jooq.Tables import org.gotson.komga.jooq.tables.records.BookMetadataAuthorRecord @@ -10,6 +11,7 @@ import org.jooq.DSLContext import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional +import java.net.URI import java.time.LocalDateTime import java.time.ZoneId @@ -22,6 +24,7 @@ class BookMetadataDao( private val d = Tables.BOOK_METADATA private val a = Tables.BOOK_METADATA_AUTHOR private val bt = Tables.BOOK_METADATA_TAG + private val bl = Tables.BOOK_METADATA_LINK private val groupFields = arrayOf(*d.fields(), *a.fields()) @@ -41,9 +44,9 @@ class BookMetadataDao( .where(d.BOOK_ID.`in`(bookIds)) .groupBy(*groupFields) .fetchGroups( - { it.into(d) }, { it.into(a) } + { it.into(d) }, { it.into(a) }, ).map { (dr, ar) -> - dr.toDomain(ar.filterNot { it.name == null }.map { it.toDomain() }, findTags(dr.bookId)) + dr.toDomain(ar.filterNot { it.name == null }.map { it.toDomain() }, findTags(dr.bookId), findLinks(dr.bookId)) } private fun findTags(bookId: String) = @@ -54,6 +57,13 @@ class BookMetadataDao( .mapNotNull { it.tag } .toSet() + private fun findLinks(bookId: String) = + dsl.select(bl.LABEL, bl.URL) + .from(bl) + .where(bl.BOOK_ID.eq(bookId)) + .fetchInto(bl) + .map { WebLink(it.label, URI(it.url)) } + @Transactional override fun insert(metadata: BookMetadata) { insert(listOf(metadata)) @@ -80,8 +90,9 @@ class BookMetadataDao( d.AUTHORS_LOCK, d.TAGS_LOCK, d.ISBN, - d.ISBN_LOCK - ).values(null as String?, null, null, null, null, null, null, null, null, null, null, null, null, null, null) + d.ISBN_LOCK, + d.LINKS_LOCK, + ).values(null as String?, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null), ).also { step -> chunk.forEach { step.bind( @@ -99,7 +110,8 @@ class BookMetadataDao( it.authorsLock, it.tagsLock, it.isbn, - it.isbnLock + it.isbnLock, + it.linksLock, ) } }.execute() @@ -107,6 +119,7 @@ class BookMetadataDao( insertAuthors(metadatas) insertTags(metadatas) + insertLinks(metadatas) } } @@ -136,6 +149,7 @@ class BookMetadataDao( .set(d.TAGS_LOCK, metadata.tagsLock) .set(d.ISBN, metadata.isbn) .set(d.ISBN_LOCK, metadata.isbnLock) + .set(d.LINKS_LOCK, metadata.linksLock) .set(d.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) .where(d.BOOK_ID.eq(metadata.bookId)) .execute() @@ -146,9 +160,13 @@ class BookMetadataDao( dsl.deleteFrom(bt) .where(bt.BOOK_ID.eq(metadata.bookId)) .execute() + dsl.deleteFrom(bl) + .where(bl.BOOK_ID.eq(metadata.bookId)) + .execute() insertAuthors(listOf(metadata)) insertTags(listOf(metadata)) + insertLinks(listOf(metadata)) } private fun insertAuthors(metadatas: Collection) { @@ -156,7 +174,7 @@ class BookMetadataDao( metadatas.chunked(batchSize).forEach { chunk -> dsl.batch( dsl.insertInto(a, a.BOOK_ID, a.NAME, a.ROLE) - .values(null as String?, null, null) + .values(null as String?, null, null), ).also { step -> chunk.forEach { metadata -> metadata.authors.forEach { @@ -173,7 +191,7 @@ class BookMetadataDao( metadatas.chunked(batchSize).forEach { chunk -> dsl.batch( dsl.insertInto(bt, bt.BOOK_ID, bt.TAG) - .values(null as String?, null) + .values(null as String?, null), ).also { step -> chunk.forEach { metadata -> metadata.tags.forEach { @@ -185,10 +203,28 @@ class BookMetadataDao( } } + private fun insertLinks(metadatas: Collection) { + if (metadatas.any { it.links.isNotEmpty() }) { + metadatas.chunked(batchSize).forEach { chunk -> + dsl.batch( + dsl.insertInto(bl, bl.BOOK_ID, bl.LABEL, bl.URL) + .values(null as String?, null, null), + ).also { step -> + chunk.forEach { metadata -> + metadata.links.forEach { + step.bind(metadata.bookId, it.label, it.url.toString()) + } + } + }.execute() + } + } + } + @Transactional override fun delete(bookId: String) { dsl.deleteFrom(a).where(a.BOOK_ID.eq(bookId)).execute() dsl.deleteFrom(bt).where(bt.BOOK_ID.eq(bookId)).execute() + dsl.deleteFrom(bl).where(bl.BOOK_ID.eq(bookId)).execute() dsl.deleteFrom(d).where(d.BOOK_ID.eq(bookId)).execute() } @@ -198,12 +234,13 @@ class BookMetadataDao( dsl.deleteFrom(a).where(a.BOOK_ID.`in`(dsl.selectTempStrings())).execute() dsl.deleteFrom(bt).where(bt.BOOK_ID.`in`(dsl.selectTempStrings())).execute() + dsl.deleteFrom(bl).where(bl.BOOK_ID.`in`(dsl.selectTempStrings())).execute() dsl.deleteFrom(d).where(d.BOOK_ID.`in`(dsl.selectTempStrings())).execute() } override fun count(): Long = dsl.fetchCount(d).toLong() - private fun BookMetadataRecord.toDomain(authors: List, tags: Set) = + private fun BookMetadataRecord.toDomain(authors: List, tags: Set, links: List) = BookMetadata( title = title, summary = summary, @@ -213,6 +250,7 @@ class BookMetadataDao( authors = authors, tags = tags, isbn = isbn, + links = links, bookId = bookId, @@ -227,11 +265,12 @@ class BookMetadataDao( authorsLock = authorsLock, tagsLock = tagsLock, isbnLock = isbnLock, + linksLock = linksLock, ) private fun BookMetadataAuthorRecord.toDomain() = Author( name = name, - role = role + role = role, ) } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProvider.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProvider.kt index 85683f2e6..377ba18a2 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProvider.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProvider.kt @@ -8,6 +8,7 @@ import org.gotson.komga.domain.model.BookMetadataPatchCapability import org.gotson.komga.domain.model.BookWithMedia import org.gotson.komga.domain.model.SeriesMetadata import org.gotson.komga.domain.model.SeriesMetadataPatch +import org.gotson.komga.domain.model.WebLink import org.gotson.komga.domain.service.BookAnalyzer import org.gotson.komga.infrastructure.metadata.BookMetadataProvider import org.gotson.komga.infrastructure.metadata.SeriesMetadataFromBookProvider @@ -16,6 +17,7 @@ import org.gotson.komga.infrastructure.metadata.comicrack.dto.Manga import org.gotson.komga.infrastructure.validation.BCP47TagValidator import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service +import java.net.URI import java.time.LocalDate private val logger = KotlinLogging.logger {} @@ -37,6 +39,7 @@ class ComicInfoProvider( BookMetadataPatchCapability.RELEASE_DATE, BookMetadataPatchCapability.AUTHORS, BookMetadataPatchCapability.READ_LISTS, + BookMetadataPatchCapability.LINKS, ) override fun getBookMetadataFromBook(book: BookWithMedia): BookMetadataPatch? { @@ -81,6 +84,16 @@ class ComicInfoProvider( } } + val link = comicInfo.web?.let { + try { + val uri = URI(it) + listOf(WebLink(uri.host, uri)) + } catch (e: Exception) { + logger.error(e) { "Could not parse Web element as valid URI: $it" } + null + } + } + return BookMetadataPatch( title = comicInfo.title?.ifBlank { null }, summary = comicInfo.summary?.ifBlank { null }, @@ -88,7 +101,8 @@ class ComicInfoProvider( numberSort = comicInfo.number?.toFloatOrNull(), releaseDate = releaseDate, authors = authors.ifEmpty { null }, - readLists = readLists + readLists = readLists, + links = link, ) } return null diff --git a/komga/src/test/kotlin/org/gotson/komga/domain/service/MetadataApplierTest.kt b/komga/src/test/kotlin/org/gotson/komga/domain/service/MetadataApplierTest.kt new file mode 100644 index 000000000..f86f1c88e --- /dev/null +++ b/komga/src/test/kotlin/org/gotson/komga/domain/service/MetadataApplierTest.kt @@ -0,0 +1,190 @@ +package org.gotson.komga.domain.service + +import org.assertj.core.api.Assertions.assertThat +import org.gotson.komga.domain.model.Author +import org.gotson.komga.domain.model.BookMetadata +import org.gotson.komga.domain.model.BookMetadataPatch +import org.gotson.komga.domain.model.SeriesMetadata +import org.gotson.komga.domain.model.SeriesMetadataPatch +import org.gotson.komga.domain.model.WebLink +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import java.net.URI +import java.time.LocalDate + +class MetadataApplierTest { + + private val metadataApplier = MetadataApplier() + + @Nested + inner class Book { + + @Test + fun `given locked metadata when applying patch then metadata is not changed`() { + val metadata = BookMetadata( + title = "title", + number = "1", + numberSort = 1F, + titleLock = true, + summaryLock = true, + numberLock = true, + numberSortLock = true, + releaseDateLock = true, + authorsLock = true, + tagsLock = true, + isbnLock = true, + linksLock = true, + ) + + val patch = BookMetadataPatch( + title = "new title", + summary = "new summary", + number = "2", + numberSort = 2F, + releaseDate = LocalDate.of(2020, 12, 2), + authors = listOf(Author("Marcel", "writer")), + isbn = "9782811632397", + links = listOf(WebLink("Comixology", URI("https://www.comixology.com/Sandman/digital-comic/727888"))), + ) + + val patched = metadataApplier.apply(patch, metadata) + + assertThat(patched.title).isEqualTo(metadata.title) + assertThat(patched.number).isEqualTo(metadata.number) + assertThat(patched.numberSort).isEqualTo(metadata.numberSort) + assertThat(patched.summary).isEqualTo("") + assertThat(patched.authors).isEmpty() + assertThat(patched.releaseDate).isNull() + assertThat(patched.tags).isEmpty() + assertThat(patched.isbn).isEqualTo("") + assertThat(patched.links).isEmpty() + } + + @Test + fun `given unlocked metadata when applying patch then metadata is changed`() { + val metadata = BookMetadata( + title = "title", + number = "1", + numberSort = 1F, + ) + + val patch = BookMetadataPatch( + title = "new title", + summary = "new summary", + number = "2", + numberSort = 2F, + releaseDate = LocalDate.of(2020, 12, 2), + authors = listOf(Author("Marcel", "writer")), + isbn = "9782811632397", + links = listOf(WebLink("Comixology", URI("https://www.comixology.com/Sandman/digital-comic/727888"))), + ) + + val patched = metadataApplier.apply(patch, metadata) + + assertThat(patched.title).isEqualTo(patch.title) + assertThat(patched.number).isEqualTo(patch.number) + assertThat(patched.numberSort).isEqualTo(patch.numberSort) + assertThat(patched.summary).isEqualTo(patch.summary) + assertThat(patched.authors) + .hasSize(1) + .containsExactlyInAnyOrder( + Author("Marcel", "writer"), + ) + assertThat(patched.releaseDate).isEqualTo(patch.releaseDate) + assertThat(patched.tags).isEmpty() + assertThat(patched.isbn).isEqualTo(patch.isbn) + assertThat(patched.links) + .hasSize(1) + .containsExactlyInAnyOrder( + WebLink("Comixology", URI("https://www.comixology.com/Sandman/digital-comic/727888")), + ) + } + } + + @Nested + inner class Series { + + @Test + fun `given locked metadata when applying patch then metadata is not changed`() { + val metadata = SeriesMetadata( + title = "title", + statusLock = true, + titleLock = true, + titleSortLock = true, + summaryLock = true, + readingDirectionLock = true, + publisherLock = true, + ageRatingLock = true, + languageLock = true, + genresLock = true, + tagsLock = true, + totalBookCountLock = true, + ) + + val patch = SeriesMetadataPatch( + title = "new title", + titleSort = "new title sort", + status = SeriesMetadata.Status.ENDED, + summary = "new summary", + readingDirection = SeriesMetadata.ReadingDirection.VERTICAL, + publisher = "new publisher", + ageRating = 12, + language = "en", + genres = setOf("shonen"), + totalBookCount = 12, + collections = emptyList(), + ) + + val patched = metadataApplier.apply(patch, metadata) + + assertThat(patched.title).isEqualTo(metadata.title) + assertThat(patched.titleSort).isEqualTo(metadata.titleSort) + assertThat(patched.status).isEqualTo(metadata.status) + assertThat(patched.summary).isEqualTo(metadata.summary) + assertThat(patched.readingDirection).isEqualTo(metadata.readingDirection) + assertThat(patched.publisher).isEqualTo(metadata.publisher) + assertThat(patched.ageRating).isEqualTo(metadata.ageRating) + assertThat(patched.language).isEqualTo(metadata.language) + assertThat(patched.genres).isEmpty() + assertThat(patched.totalBookCount).isNull() + assertThat(patched.tags).isEmpty() + } + + @Test + fun `given unlocked metadata when applying patch then metadata is changed`() { + val metadata = SeriesMetadata( + title = "title", + ) + + val patch = SeriesMetadataPatch( + title = "new title", + titleSort = "new title sort", + status = SeriesMetadata.Status.ENDED, + summary = "new summary", + readingDirection = SeriesMetadata.ReadingDirection.VERTICAL, + publisher = "new publisher", + ageRating = 12, + language = "en", + genres = setOf("shonen"), + totalBookCount = 12, + collections = emptyList(), + ) + + val patched = metadataApplier.apply(patch, metadata) + + assertThat(patched.title).isEqualTo(patch.title) + assertThat(patched.titleSort).isEqualTo(patch.titleSort) + assertThat(patched.status).isEqualTo(patch.status) + assertThat(patched.summary).isEqualTo(patch.summary) + assertThat(patched.readingDirection).isEqualTo(patch.readingDirection) + assertThat(patched.publisher).isEqualTo(patch.publisher) + assertThat(patched.ageRating).isEqualTo(patch.ageRating) + assertThat(patched.language).isEqualTo(patch.language) + assertThat(patched.totalBookCount).isEqualTo(patch.totalBookCount) + assertThat(patched.genres) + .hasSize(1) + .containsExactlyInAnyOrder("shonen") + assertThat(patched.tags).isEmpty() + } + } +} diff --git a/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDaoTest.kt b/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDaoTest.kt index 61e5f2c38..e40d75550 100644 --- a/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDaoTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/BookMetadataDaoTest.kt @@ -4,6 +4,7 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.catchThrowable import org.gotson.komga.domain.model.Author import org.gotson.komga.domain.model.BookMetadata +import org.gotson.komga.domain.model.WebLink import org.gotson.komga.domain.model.makeBook import org.gotson.komga.domain.model.makeLibrary import org.gotson.komga.domain.model.makeSeries @@ -18,6 +19,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.junit.jupiter.SpringExtension +import java.net.URI import java.time.LocalDate import java.time.LocalDateTime @@ -27,7 +29,7 @@ class BookMetadataDaoTest( @Autowired private val bookMetadataDao: BookMetadataDao, @Autowired private val bookRepository: BookRepository, @Autowired private val seriesRepository: SeriesRepository, - @Autowired private val libraryRepository: LibraryRepository + @Autowired private val libraryRepository: LibraryRepository, ) { private val library = makeLibrary() private val series = makeSeries("Series") @@ -68,6 +70,7 @@ class BookMetadataDaoTest( authors = listOf(Author("author", "role")), tags = setOf("tag", "another"), isbn = "987654321", + links = listOf(WebLink("Comicvine", URI("https://comicvine.gamespot.com/doctor-strange-30-a-gathering-of-fear/4000-18731/"))), bookId = book.id, titleLock = true, summaryLock = true, @@ -98,6 +101,10 @@ class BookMetadataDaoTest( } assertThat(created.tags).containsAll(metadata.tags) assertThat(created.isbn).isEqualTo(metadata.isbn) + with(created.links.first()) { + assertThat(label).isEqualTo(metadata.links.first().label) + assertThat(url).isEqualTo(metadata.links.first().url) + } assertThat(created.titleLock).isEqualTo(metadata.titleLock) assertThat(created.summaryLock).isEqualTo(metadata.summaryLock) @@ -107,6 +114,7 @@ class BookMetadataDaoTest( assertThat(created.authorsLock).isEqualTo(metadata.authorsLock) assertThat(created.tagsLock).isEqualTo(metadata.tagsLock) assertThat(created.isbnLock).isEqualTo(metadata.isbnLock) + assertThat(created.linksLock).isEqualTo(metadata.linksLock) } @Test @@ -115,7 +123,7 @@ class BookMetadataDaoTest( title = "Book", number = "1", numberSort = 1F, - bookId = book.id + bookId = book.id, ) bookMetadataDao.insert(metadata) @@ -131,6 +139,7 @@ class BookMetadataDaoTest( assertThat(created.authors).isEmpty() assertThat(created.tags).isEmpty() assertThat(created.isbn).isBlank + assertThat(created.links).isEmpty() assertThat(created.titleLock).isFalse assertThat(created.summaryLock).isFalse @@ -140,6 +149,7 @@ class BookMetadataDaoTest( assertThat(created.authorsLock).isFalse assertThat(created.tagsLock).isFalse assertThat(created.isbnLock).isFalse + assertThat(created.linksLock).isFalse } @Test @@ -152,7 +162,8 @@ class BookMetadataDaoTest( releaseDate = LocalDate.now(), authors = listOf(Author("author", "role")), tags = setOf("tag"), - bookId = book.id + links = listOf(WebLink("Comicvine", URI("https://comicvine.gamespot.com/doctor-strange-30-a-gathering-of-fear/4000-18731/"))), + bookId = book.id, ) bookMetadataDao.insert(metadata) @@ -167,6 +178,7 @@ class BookMetadataDaoTest( authors = listOf(Author("author2", "role2")), tags = setOf("another"), isbn = "987654321", + links = listOf(WebLink("Bedetheque", URI("https://www.bedetheque.com/BD-AD-Grand-Riviere-Tome-1-Terre-d-election-12596.html"))), titleLock = true, summaryLock = true, numberLock = true, @@ -175,6 +187,7 @@ class BookMetadataDaoTest( authorsLock = true, tagsLock = true, isbnLock = true, + linksLock = true, ) } @@ -201,10 +214,13 @@ class BookMetadataDaoTest( assertThat(modified.authorsLock).isEqualTo(updated.authorsLock) assertThat(modified.tagsLock).isEqualTo(updated.tagsLock) assertThat(modified.isbnLock).isEqualTo(updated.isbnLock) + assertThat(modified.linksLock).isEqualTo(updated.linksLock) assertThat(modified.tags).containsAll(updated.tags) assertThat(modified.authors.first().name).isEqualTo(updated.authors.first().name) assertThat(modified.authors.first().role).isEqualTo(updated.authors.first().role) + assertThat(modified.links.first().label).isEqualTo(updated.links.first().label) + assertThat(modified.links.first().url).isEqualTo(updated.links.first().url) } @Test @@ -216,7 +232,7 @@ class BookMetadataDaoTest( numberSort = 1F, releaseDate = LocalDate.now(), authors = listOf(Author("author", "role")), - bookId = book.id + bookId = book.id, ) bookMetadataDao.insert(metadata) diff --git a/komga/src/test/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProviderTest.kt b/komga/src/test/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProviderTest.kt index 2614c6ef3..3232b8882 100644 --- a/komga/src/test/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProviderTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/infrastructure/metadata/comicrack/ComicInfoProviderTest.kt @@ -8,6 +8,7 @@ import org.gotson.komga.domain.model.BookMetadataPatch import org.gotson.komga.domain.model.BookWithMedia import org.gotson.komga.domain.model.Media import org.gotson.komga.domain.model.SeriesMetadata +import org.gotson.komga.domain.model.WebLink import org.gotson.komga.domain.model.makeBook import org.gotson.komga.domain.service.BookAnalyzer import org.gotson.komga.infrastructure.metadata.comicrack.dto.AgeRating @@ -18,6 +19,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import java.net.URI import java.time.LocalDate import java.util.stream.Stream @@ -34,7 +36,7 @@ class ComicInfoProviderTest { private val media = Media( status = Media.Status.READY, mediaType = "application/zip", - files = listOf("ComicInfo.xml") + files = listOf("ComicInfo.xml"), ) @Nested @@ -51,6 +53,7 @@ class ComicInfoProviderTest { alternateSeries = "story arc" alternateNumber = "5" storyArc = "one, two, three" + web = "https://www.comixology.com/Sandman/digital-comic/727888" } every { mockMapper.readValue(any(), ComicInfo::class.java) } returns comicInfo @@ -71,6 +74,11 @@ class ComicInfoProviderTest { BookMetadataPatch.ReadListEntry("two"), BookMetadataPatch.ReadListEntry("three"), ) + + assertThat(links).hasSize(1) + assertThat(links).containsExactlyInAnyOrder( + WebLink("www.comixology.com", URI("https://www.comixology.com/Sandman/digital-comic/727888")), + ) } } @@ -88,7 +96,7 @@ class ComicInfoProviderTest { with(patch!!) { assertThat(readLists).hasSize(1) assertThat(readLists).containsExactlyInAnyOrder( - BookMetadataPatch.ReadListEntry("one", 6) + BookMetadataPatch.ReadListEntry("one", 6), ) } } @@ -419,13 +427,16 @@ class ComicInfoProviderTest { } } - fun computeSeriesFromSeriesAndVolumeArguments() = Stream.of( - Arguments.of("", null, null), - Arguments.of(null, null, null), - Arguments.of("Series", null, "Series"), - Arguments.of("Series", 1, "Series"), - Arguments.of("Series", 10, "Series (10)"), - ) + companion object { + @JvmStatic + fun computeSeriesFromSeriesAndVolumeArguments(): Stream = Stream.of( + Arguments.of("", null, null), + Arguments.of(null, null, null), + Arguments.of("Series", null, "Series"), + Arguments.of("Series", 1, "Series"), + Arguments.of("Series", 10, "Series (10)"), + ) + } @ParameterizedTest @MethodSource("computeSeriesFromSeriesAndVolumeArguments")