diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a4b87342..512979c1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -63,12 +63,14 @@ All members of the `MangaParser` class are documented. Pay attention to some pec
- You can use _asserts_ to check some optional fields. For example, the `Manga.author` field is not required, but if
your source provides this information, add `assert(it != null)`. This will not have any effect on production but help
to find issues during unit testing.
+- Your parser may also implement the `Interceptor` interface for additional manipulation of all network requests and
+ responses, including image loading.
- If your source website (or its API) uses pages for pagination instead of offset you should extend `PagedMangaParser`
instead of `MangaParser`.
- If your source website (or its API) does not provide pagination (has only one page of content) you should extend
`SinglePageMangaParser` instead of `MangaParser` or `PagedMangaParser`.
-- Your parser may also implement the `Interceptor` interface for additional manipulation of all network requests and
- responses, including image loading.
+
+
## Development process
diff --git a/README.md b/README.md
index abfbf978..c1beaac2 100644
--- a/README.md
+++ b/README.md
@@ -53,11 +53,9 @@ JVM and Android applications.
`mangaLoaderContext` is an implementation of the `MangaLoaderContext` class.
See examples
of [Android](https://github.com/KotatsuApp/Kotatsu/blob/devel/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt)
- and [Non-Android](https://github.com/KotatsuApp/kotatsu-dl/blob/master/src/jvmMain/kotlin/org/koitharu/kotatsu_dl/logic/MangaLoaderContextImpl.kt)
+ and [Non-Android](https://github.com/KotatsuApp/kotatsu-dl/blob/master/src/main/kotlin/org/koitharu/kotatsu/dl/parsers/MangaLoaderContextImpl.kt)
implementation.
- Note that the `MangaParserSource.DUMMY` parsers cannot be instantiated.
-
## Projects that use the library
- [Kotatsu](https://github.com/KotatsuApp/Kotatsu)
diff --git a/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt b/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt
index 362a7ce5..c44795ca 100644
--- a/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt
+++ b/kotatsu-parsers-ksp/src/main/kotlin/org/koitharu/kotatsu/parsers/ksp/ParserProcessor.kt
@@ -98,7 +98,6 @@ class ParserProcessor(
factoryWriter?.write(
"""
- MangaParserSource.DUMMY -> throw NotImplementedError("Manga parser ${'$'}name cannot be instantiated")
}.let {
require(it.source == this) {
"Cannot instantiate manga parser: ${'$'}name mapped to ${'$'}{it.source}"
@@ -109,7 +108,6 @@ class ParserProcessor(
)
sourcesWriter?.write(
"""
- DUMMY("Dummy", "", ContentType.OTHER, false),
;
}
""".trimIndent(),
diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaSources.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaSources.kt
index 3b5081f5..75293076 100644
--- a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaSources.kt
+++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaSources.kt
@@ -1,8 +1,8 @@
package org.koitharu.kotatsu.parsers
import org.junit.jupiter.params.provider.EnumSource
-import org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE
import org.koitharu.kotatsu.parsers.model.MangaParserSource
-@EnumSource(MangaParserSource::class, names = ["DUMMY"], mode = EXCLUDE)
+// Change 'names' to test specified parsers
+@EnumSource(MangaParserSource::class, names = [], mode = EnumSource.Mode.INCLUDE)
internal annotation class MangaSources
diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/model/search/MangaSearchQueryTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/model/search/MangaSearchQueryTest.kt
index 6ec3409f..86b1757f 100644
--- a/src/test/kotlin/org/koitharu/kotatsu/parsers/model/search/MangaSearchQueryTest.kt
+++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/model/search/MangaSearchQueryTest.kt
@@ -12,61 +12,61 @@ import java.util.*
class MangaSearchQueryCapabilitiesTest {
- private val capabilities = MangaSearchQueryCapabilities(
- capabilities = setOf(
- SearchCapability(TITLE_NAME, setOf(Match::class), isMultiple = false, isExclusive = true),
- SearchCapability(TAG, setOf(Include::class, Exclude::class), isMultiple = true, isExclusive = false),
- SearchCapability(PUBLICATION_YEAR, setOf(Range::class), isMultiple = false, isExclusive = false),
- SearchCapability(STATE, setOf(Include::class), isMultiple = false, isExclusive = false),
- ),
- )
+ private val capabilities = MangaSearchQueryCapabilities(
+ capabilities = setOf(
+ SearchCapability(TITLE_NAME, setOf(Match::class), isMultiple = false, isExclusive = true),
+ SearchCapability(TAG, setOf(Include::class, Exclude::class), isMultiple = true, isExclusive = false),
+ SearchCapability(PUBLICATION_YEAR, setOf(Range::class), isMultiple = false, isExclusive = false),
+ SearchCapability(STATE, setOf(Include::class), isMultiple = false, isExclusive = false),
+ ),
+ )
- @Test
- fun validateValidSingleCriterionQuery() {
- val query = MangaSearchQuery.Builder()
- .criterion(Match(TITLE_NAME, "title"))
- .build()
+ @Test
+ fun validateValidSingleCriterionQuery() {
+ val query = MangaSearchQuery.Builder()
+ .criterion(Match(TITLE_NAME, "title"))
+ .build()
- assertDoesNotThrow { capabilities.validate(query) }
- }
+ assertDoesNotThrow { capabilities.validate(query) }
+ }
- @Test
- fun validateUnsupportedFieldThrowsException() {
- val query = MangaSearchQuery.Builder()
- .criterion(Include(ORIGINAL_LANGUAGE, setOf(Locale.ENGLISH)))
- .build()
+ @Test
+ fun validateUnsupportedFieldThrowsException() {
+ val query = MangaSearchQuery.Builder()
+ .criterion(Include(ORIGINAL_LANGUAGE, setOf(Locale.ENGLISH)))
+ .build()
- assertThrows(IllegalArgumentException::class.java) { capabilities.validate(query) }
- }
+ assertThrows(IllegalArgumentException::class.java) { capabilities.validate(query) }
+ }
- @Test
- fun validateUnsupportedMultiValueThrowsException() {
- val query = MangaSearchQuery.Builder()
- .criterion(Include(STATE, setOf(MangaState.ONGOING, MangaState.FINISHED)))
- .build()
+ @Test
+ fun validateUnsupportedMultiValueThrowsException() {
+ val query = MangaSearchQuery.Builder()
+ .criterion(Include(STATE, setOf(MangaState.ONGOING, MangaState.FINISHED)))
+ .build()
- assertThrows(IllegalArgumentException::class.java) { capabilities.validate(query) }
- }
+ assertThrows(IllegalArgumentException::class.java) { capabilities.validate(query) }
+ }
- @Test
- fun validateMultipleCriteriaWithOtherCriteriaAllowed() {
- val query = MangaSearchQuery.Builder()
- .criterion(Include(TAG, setOf(buildTag("tag1"), buildTag("tag2"))))
- .criterion(Exclude(TAG, setOf(buildTag("tag3"))))
- .build()
+ @Test
+ fun validateMultipleCriteriaWithOtherCriteriaAllowed() {
+ val query = MangaSearchQuery.Builder()
+ .criterion(Include(TAG, setOf(buildTag("tag1"), buildTag("tag2"))))
+ .criterion(Exclude(TAG, setOf(buildTag("tag3"))))
+ .build()
- assertDoesNotThrow { capabilities.validate(query) }
- }
+ assertDoesNotThrow { capabilities.validate(query) }
+ }
- @Test
- fun validateMultipleCriteriaWithStrictCapabilityThrowsException() {
- val query = MangaSearchQuery.Builder()
- .criterion(Match(TITLE_NAME, "title"))
- .criterion(Range(PUBLICATION_YEAR, 1990, 2000))
- .build()
+ @Test
+ fun validateMultipleCriteriaWithStrictCapabilityThrowsException() {
+ val query = MangaSearchQuery.Builder()
+ .criterion(Match(TITLE_NAME, "title"))
+ .criterion(Range(PUBLICATION_YEAR, 1990, 2000))
+ .build()
- assertThrows(IllegalArgumentException::class.java) { capabilities.validate(query) }
- }
+ assertThrows(IllegalArgumentException::class.java) { capabilities.validate(query) }
+ }
- private fun buildTag(name: String) = MangaTag(title = name, key = "${name}Key", source = MangaParserSource.DUMMY)
+ private fun buildTag(name: String) = MangaTag(title = name, key = "${name}Key", source = MangaParserSource.MANGADEX)
}
diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/util/IntentFilterGenerator.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/util/IntentFilterGenerator.kt
index 6b997776..9f4501f6 100644
--- a/src/test/kotlin/org/koitharu/kotatsu/parsers/util/IntentFilterGenerator.kt
+++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/util/IntentFilterGenerator.kt
@@ -8,33 +8,30 @@ import java.io.File
class IntentFilterGenerator {
- @Test
- fun generateIntentFilter() {
- val output = File("out/test/resources/intent-filter.xml")
- output.printWriter(Charsets.UTF_8).use { writer ->
- writer.appendLine("")
- writer.appendTab().appendLine("")
- writer.appendLine()
- writer.appendTab().appendLine("")
- writer.appendTab().appendLine("")
- writer.appendLine()
- writer.appendTab().appendLine("")
- writer.appendTab().appendLine("")
- writer.appendLine()
- for (source in MangaParserSource.entries) {
- if (source == MangaParserSource.DUMMY) {
- continue
- }
- val parser = source.newParser(MangaLoaderContextMock)
- parser.configKeyDomain.presetValues.forEach { domain ->
- writer.appendTab().append("")
- }
- }
- writer.appendLine()
- writer.appendLine("")
- }
- println(output.absolutePath)
- }
+ @Test
+ fun generateIntentFilter() {
+ val output = File("out/test/resources/intent-filter.xml")
+ output.printWriter(Charsets.UTF_8).use { writer ->
+ writer.appendLine("")
+ writer.appendTab().appendLine("")
+ writer.appendLine()
+ writer.appendTab().appendLine("")
+ writer.appendTab().appendLine("")
+ writer.appendLine()
+ writer.appendTab().appendLine("")
+ writer.appendTab().appendLine("")
+ writer.appendLine()
+ for (source in MangaParserSource.entries) {
+ val parser = source.newParser(MangaLoaderContextMock)
+ parser.configKeyDomain.presetValues.forEach { domain ->
+ writer.appendTab().append("")
+ }
+ }
+ writer.appendLine()
+ writer.appendLine("")
+ }
+ println(output.absolutePath)
+ }
- private fun Appendable.appendTab() = append('\t')
+ private fun Appendable.appendTab() = append('\t')
}
diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/util/ListFilterToSearchQueryConverterTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/util/ListFilterToSearchQueryConverterTest.kt
index 04530bec..a6ed694a 100644
--- a/src/test/kotlin/org/koitharu/kotatsu/parsers/util/ListFilterToSearchQueryConverterTest.kt
+++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/util/ListFilterToSearchQueryConverterTest.kt
@@ -13,65 +13,65 @@ import java.util.*
class ListFilterToSearchQueryConverterTest {
- @Test
- fun convertToMangaSearchQueryTest() {
- val tags = setOf(buildMangaTag("tag1"), buildMangaTag("tag2"))
- val excludedTags = setOf(buildMangaTag("exclude_tag"))
- val states = setOf(MangaState.ONGOING)
- val contentRatings = setOf(ContentRating.SAFE)
- val contentTypes = setOf(MANGA, MANHUA)
- val demographics = setOf(SEINEN)
+ @Test
+ fun convertToMangaSearchQueryTest() {
+ val tags = setOf(buildMangaTag("tag1"), buildMangaTag("tag2"))
+ val excludedTags = setOf(buildMangaTag("exclude_tag"))
+ val states = setOf(MangaState.ONGOING)
+ val contentRatings = setOf(ContentRating.SAFE)
+ val contentTypes = setOf(MANGA, MANHUA)
+ val demographics = setOf(SEINEN)
- val filter = MangaListFilter(
- query = "title_name",
- tags = tags,
- tagsExclude = excludedTags,
- locale = Locale.ENGLISH,
- originalLocale = Locale.JAPANESE,
- states = states,
- contentRating = contentRatings,
- types = contentTypes,
- demographics = demographics,
- year = 2020,
- yearFrom = 1997,
- yearTo = 2024,
- )
+ val filter = MangaListFilter(
+ query = "title_name",
+ tags = tags,
+ tagsExclude = excludedTags,
+ locale = Locale.ENGLISH,
+ originalLocale = Locale.JAPANESE,
+ states = states,
+ contentRating = contentRatings,
+ types = contentTypes,
+ demographics = demographics,
+ year = 2020,
+ yearFrom = 1997,
+ yearTo = 2024,
+ )
- val searchQuery = convertToMangaSearchQuery(0, SortOrder.NEWEST, filter)
+ val searchQuery = convertToMangaSearchQuery(0, SortOrder.NEWEST, filter)
- val expectedQuery = MangaSearchQuery.Builder()
- .offset(0)
- .order(SortOrder.NEWEST)
- .criterion(Match(TITLE_NAME, "title_name"))
- .criterion(Include(TAG, tags))
- .criterion(Exclude(TAG, excludedTags))
- .criterion(Include(LANGUAGE, setOf(Locale.ENGLISH)))
- .criterion(Include(ORIGINAL_LANGUAGE, setOf(Locale.JAPANESE)))
- .criterion(Include(STATE, states))
- .criterion(Include(CONTENT_RATING, contentRatings))
- .criterion(Include(CONTENT_TYPE, contentTypes))
- .criterion(Include(DEMOGRAPHIC, demographics))
- .criterion(Range(PUBLICATION_YEAR, 1997, 2024))
- .criterion(Match(PUBLICATION_YEAR, 2020))
- .build()
+ val expectedQuery = MangaSearchQuery.Builder()
+ .offset(0)
+ .order(SortOrder.NEWEST)
+ .criterion(Match(TITLE_NAME, "title_name"))
+ .criterion(Include(TAG, tags))
+ .criterion(Exclude(TAG, excludedTags))
+ .criterion(Include(LANGUAGE, setOf(Locale.ENGLISH)))
+ .criterion(Include(ORIGINAL_LANGUAGE, setOf(Locale.JAPANESE)))
+ .criterion(Include(STATE, states))
+ .criterion(Include(CONTENT_RATING, contentRatings))
+ .criterion(Include(CONTENT_TYPE, contentTypes))
+ .criterion(Include(DEMOGRAPHIC, demographics))
+ .criterion(Range(PUBLICATION_YEAR, 1997, 2024))
+ .criterion(Match(PUBLICATION_YEAR, 2020))
+ .build()
- assertEquals(expectedQuery, searchQuery)
- }
+ assertEquals(expectedQuery, searchQuery)
+ }
- @Test
- fun convertToMangaSearchQueryWithEmptyFieldsTest() {
- val filter = MangaListFilter()
+ @Test
+ fun convertToMangaSearchQueryWithEmptyFieldsTest() {
+ val filter = MangaListFilter()
- val searchQuery = convertToMangaSearchQuery(0, SortOrder.NEWEST, filter)
+ val searchQuery = convertToMangaSearchQuery(0, SortOrder.NEWEST, filter)
- assertEquals(MangaSearchQuery.Builder().offset(0).order(SortOrder.NEWEST).build(), searchQuery)
- }
+ assertEquals(MangaSearchQuery.Builder().offset(0).order(SortOrder.NEWEST).build(), searchQuery)
+ }
- private fun buildMangaTag(name: String): MangaTag {
- return MangaTag(
- key = "${name}Key",
- title = name,
- source = MangaParserSource.DUMMY,
- )
- }
+ private fun buildMangaTag(name: String): MangaTag {
+ return MangaTag(
+ key = "${name}Key",
+ title = name,
+ source = MangaParserSource.MANGADEX,
+ )
+ }
}
diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/util/SearchQueryToListFilterConverterTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/util/SearchQueryToListFilterConverterTest.kt
index ef282378..f9dfbc11 100644
--- a/src/test/kotlin/org/koitharu/kotatsu/parsers/util/SearchQueryToListFilterConverterTest.kt
+++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/util/SearchQueryToListFilterConverterTest.kt
@@ -17,78 +17,78 @@ import java.util.*
class ConvertToMangaListFilterTest {
- @Test
- fun convertToMangaListFilterTest() {
- val tags = setOf(buildMangaTag("tag1"), buildMangaTag("tag2"))
- val excludedTags = setOf(buildMangaTag("exclude_tag"))
- val states = setOf(MangaState.ONGOING)
- val contentRatings = setOf(ContentRating.SAFE)
- val contentTypes = setOf(MANGA, MANHUA)
- val demographics = setOf(SEINEN)
+ @Test
+ fun convertToMangaListFilterTest() {
+ val tags = setOf(buildMangaTag("tag1"), buildMangaTag("tag2"))
+ val excludedTags = setOf(buildMangaTag("exclude_tag"))
+ val states = setOf(MangaState.ONGOING)
+ val contentRatings = setOf(ContentRating.SAFE)
+ val contentTypes = setOf(MANGA, MANHUA)
+ val demographics = setOf(SEINEN)
- val query = MangaSearchQuery.Builder()
- .criterion(Match(TITLE_NAME, "title_name"))
- .criterion(Include(TAG, tags))
- .criterion(Exclude(TAG, excludedTags))
- .criterion(Include(LANGUAGE, setOf(Locale.ENGLISH)))
- .criterion(Include(ORIGINAL_LANGUAGE, setOf(Locale.JAPANESE)))
- .criterion(Include(STATE, states))
- .criterion(Include(CONTENT_RATING, contentRatings))
- .criterion(Include(CONTENT_TYPE, contentTypes))
- .criterion(Include(DEMOGRAPHIC, demographics))
- .criterion(Range(PUBLICATION_YEAR, 1997, 2024))
- .criterion(Match(PUBLICATION_YEAR, 2020))
- .build()
+ val query = MangaSearchQuery.Builder()
+ .criterion(Match(TITLE_NAME, "title_name"))
+ .criterion(Include(TAG, tags))
+ .criterion(Exclude(TAG, excludedTags))
+ .criterion(Include(LANGUAGE, setOf(Locale.ENGLISH)))
+ .criterion(Include(ORIGINAL_LANGUAGE, setOf(Locale.JAPANESE)))
+ .criterion(Include(STATE, states))
+ .criterion(Include(CONTENT_RATING, contentRatings))
+ .criterion(Include(CONTENT_TYPE, contentTypes))
+ .criterion(Include(DEMOGRAPHIC, demographics))
+ .criterion(Range(PUBLICATION_YEAR, 1997, 2024))
+ .criterion(Match(PUBLICATION_YEAR, 2020))
+ .build()
- val listFilter = convertToMangaListFilter(query)
+ val listFilter = convertToMangaListFilter(query)
- assertEquals(listFilter.query, "title_name")
- assertEquals(listFilter.tags, tags)
- assertEquals(listFilter.tagsExclude, excludedTags)
- assertEquals(listFilter.locale, Locale.ENGLISH)
- assertEquals(listFilter.originalLocale, Locale.JAPANESE)
- assertEquals(listFilter.states, states)
- assertEquals(listFilter.contentRating, contentRatings)
- assertEquals(listFilter.types, contentTypes)
- assertEquals(listFilter.demographics, demographics)
- assertEquals(listFilter.year, 2020)
- assertEquals(listFilter.yearFrom, 1997)
- assertEquals(listFilter.yearTo, 2024)
- }
+ assertEquals(listFilter.query, "title_name")
+ assertEquals(listFilter.tags, tags)
+ assertEquals(listFilter.tagsExclude, excludedTags)
+ assertEquals(listFilter.locale, Locale.ENGLISH)
+ assertEquals(listFilter.originalLocale, Locale.JAPANESE)
+ assertEquals(listFilter.states, states)
+ assertEquals(listFilter.contentRating, contentRatings)
+ assertEquals(listFilter.types, contentTypes)
+ assertEquals(listFilter.demographics, demographics)
+ assertEquals(listFilter.year, 2020)
+ assertEquals(listFilter.yearFrom, 1997)
+ assertEquals(listFilter.yearTo, 2024)
+ }
- @Test
- fun convertToMangaListFilterWithMultipleTagsIncludeTest() {
- val tags1 = setOf(buildMangaTag("tag1"), buildMangaTag("tag2"))
- val tags2 = setOf(buildMangaTag("tag3"), buildMangaTag("tag4"))
+ @Test
+ fun convertToMangaListFilterWithMultipleTagsIncludeTest() {
+ val tags1 = setOf(buildMangaTag("tag1"), buildMangaTag("tag2"))
+ val tags2 = setOf(buildMangaTag("tag3"), buildMangaTag("tag4"))
- val query = MangaSearchQuery.Builder()
- .criterion(Include(TAG, tags1))
- .criterion(Include(TAG, tags2))
- .build()
+ val query = MangaSearchQuery.Builder()
+ .criterion(Include(TAG, tags1))
+ .criterion(Include(TAG, tags2))
+ .build()
- val listFilter = convertToMangaListFilter(query)
+ val listFilter = convertToMangaListFilter(query)
- assertEquals(listFilter.tags, tags1 union tags2)
- }
+ assertEquals(listFilter.tags, tags1 union tags2)
+ }
- @Test
- fun convertToMangaListFilterWithUnsupportedFieldTest() {
- val query = MangaSearchQuery.Builder()
- .criterion(Include(AUTHOR, setOf(buildMangaTag("author"))))
- .build()
+ @Test
+ fun convertToMangaListFilterWithUnsupportedFieldTest() {
+ val query = MangaSearchQuery.Builder()
+ .criterion(Include(AUTHOR, setOf(buildMangaTag("author"))))
+ .build()
- val exception = assertThrows {
- convertToMangaListFilter(query)
- }
+ val exception = assertThrows {
+ convertToMangaListFilter(query)
+ }
- assert(exception.message!!.contains("Unsupported field for Include criterion: AUTHOR"))
- }
+ assert(exception.message!!.contains("Unsupported field for Include criterion: AUTHOR"))
+ }
- private fun buildMangaTag(name: String): MangaTag {
- return MangaTag(
- key = "${name}Key",
- title = name,
- source = MangaParserSource.DUMMY,
- )
- }
+ private fun buildMangaTag(name: String): MangaTag {
+ return MangaTag(
+ key = "${name}Key",
+ title = name,
+ source = MangaParserSource.MANGADEX,
+ )
+ }
}