diff --git a/komf-core/src/commonMain/kotlin/snd/komf/util/BookNameParser.kt b/komf-core/src/commonMain/kotlin/snd/komf/util/BookNameParser.kt index 9b75a243..6339f672 100644 --- a/komf-core/src/commonMain/kotlin/snd/komf/util/BookNameParser.kt +++ b/komf-core/src/commonMain/kotlin/snd/komf/util/BookNameParser.kt @@ -18,6 +18,7 @@ object BookNameParser { "(?i)(?:\\s|#|no\\.)(?[0-9]+[AB]?([.x#][0-9]+)?)(?-[0-9]+([.x#][0-9]+)?)?(?:\\s\\(.*\\)\\s*)*$".toRegex(), "Issue (?[0-9]+[AB]?([.x#][0-9]+)?)(?-[0-9]+([.x#][0-9]+)?)?".toRegex(), "Volume (?[0-9]+[AB]?([.x#][0-9]+)?)(?-[0-9]+([.x#][0-9]+)?)?".toRegex(), + "\\s-\\s(?[0-9]+[AB]?([.x#][0-9]+)?)(?-[0-9]+([.x#][0-9]+)?)?\\s-\\s".toRegex(), ) private val extraDataRegex = "\\[(?.*?)]".toRegex() diff --git a/komf-core/src/commonTest/kotlin/snd/komf/util/BookNameParserTest.kt b/komf-core/src/commonTest/kotlin/snd/komf/util/BookNameParserTest.kt new file mode 100644 index 00000000..5f454b5e --- /dev/null +++ b/komf-core/src/commonTest/kotlin/snd/komf/util/BookNameParserTest.kt @@ -0,0 +1,77 @@ +package snd.komf.util + +import snd.komf.model.BookRange +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class BookNameParserTest { + + @Test + fun `getBookNumber parses number at end of string`() { + assertEquals(BookRange(123.0), BookNameParser.getBookNumber("Batman 123")) + } + + @Test + fun `getBookNumber parses hash-prefixed number`() { + assertEquals(BookRange(5.0), BookNameParser.getBookNumber("Spider-Man #5")) + } + + @Test + fun `getBookNumber parses Issue prefix`() { + assertEquals(BookRange(42.0), BookNameParser.getBookNumber("Issue 42")) + } + + @Test + fun `getBookNumber parses Volume prefix`() { + assertEquals(BookRange(3.0), BookNameParser.getBookNumber("Volume 3")) + } + + @Test + fun `getBookNumber parses number with parenthetical suffix`() { + assertEquals(BookRange(10.0), BookNameParser.getBookNumber("Series 10 (2020)")) + } + + @Test + fun `getBookNumber parses dash-delimited number`() { + assertEquals( + BookRange(195.0), + BookNameParser.getBookNumber("Suske En Wiske HQ - 195 - De Hippe Heksen") + ) + } + + @Test + fun `getBookNumber parses dash-delimited leading zeros`() { + assertEquals( + BookRange(1.0), + BookNameParser.getBookNumber("The Walking Dead - 001 - Days Gone Bye") + ) + } + + @Test + fun `getBookNumber parses dash-delimited decimal number`() { + assertEquals( + BookRange(12.5), + BookNameParser.getBookNumber("Series - 12.5 - Special Edition") + ) + } + + @Test + fun `getBookNumber parses dash-delimited range`() { + assertEquals( + BookRange(1.0, 5.0), + BookNameParser.getBookNumber("Series - 1-5 - Omnibus") + ) + } + + @Test + fun `getBookNumber prefers existing regex over dash-delimited`() { + // "Issue 5" should match the Issue regex, not the dash pattern + assertEquals(BookRange(5.0), BookNameParser.getBookNumber("Series - 2099 - Issue 5")) + } + + @Test + fun `getBookNumber returns null for unrecognized format`() { + assertNull(BookNameParser.getBookNumber("No Number Here")) + } +} diff --git a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/metadata/MetadataService.kt b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/metadata/MetadataService.kt index ef1827a7..891c7102 100644 --- a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/metadata/MetadataService.kt +++ b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/metadata/MetadataService.kt @@ -296,6 +296,7 @@ class MetadataService( else mapOf(books.first() to null) } + logger.debug { "Matching ${books.size} books against ${providerBooks.size} provider books" } return books.associateWith { book -> val bookExtraData = BookNameParser.getExtraData(book.name).map { it.lowercase() } editionBooks.keys.firstOrNull { bookExtraData.contains(it) } @@ -303,6 +304,7 @@ class MetadataService( val bookNumber = getBookNumber(book.name) val providerBook = editionBooks[edition] ?.firstOrNull { it.number != null && it.number == bookNumber } + logger.debug { "Book '${book.name}': parsed number=$bookNumber, matched=${providerBook != null}" } book to providerBook }.toMap() }