diff --git a/.github/workflows/test-branch.yml b/.github/workflows/test-branch.yml index 49b3dc6d..a704944a 100644 --- a/.github/workflows/test-branch.yml +++ b/.github/workflows/test-branch.yml @@ -15,7 +15,7 @@ jobs: - name: Set up enviroment 🔧 uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Set up Gradle 📦 diff --git a/.github/workflows/test-parsers.yml b/.github/workflows/test-parsers.yml index 18d69cef..dc0d882b 100644 --- a/.github/workflows/test-parsers.yml +++ b/.github/workflows/test-parsers.yml @@ -19,7 +19,7 @@ jobs: - name: Set up enviroment 🔧 uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Set up Gradle 📦 diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MyReadingManga.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MyReadingManga.kt index 651df9d3..9d981696 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MyReadingManga.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MyReadingManga.kt @@ -1,8 +1,8 @@ package org.koitharu.kotatsu.parsers.site.all +import okhttp3.Headers import org.jsoup.nodes.Document import org.jsoup.nodes.Element -import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.config.ConfigKey @@ -23,34 +23,39 @@ import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl import org.koitharu.kotatsu.parsers.util.generateUid import org.koitharu.kotatsu.parsers.util.mapToSet +import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany import org.koitharu.kotatsu.parsers.util.parseHtml +import org.koitharu.kotatsu.parsers.util.splitByWhitespace import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl -import org.koitharu.kotatsu.parsers.util.urlEncoded import java.text.SimpleDateFormat import java.util.EnumSet import java.util.Locale import java.util.regex.Pattern +import kotlin.collections.joinToString -@Broken("Need to rewrite getListPage") @MangaSourceParser("MYREADINGMANGA", "MyReadingManga", type = ContentType.HENTAI) internal class MyReadingManga(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MYREADINGMANGA, 18) { override val configKeyDomain = ConfigKey.Domain("myreadingmanga.info") - override fun onCreateConfig(keys: MutableCollection>) { - super.onCreateConfig(keys) - keys.add(userAgentKey) - } + override fun getRequestHeaders(): Headers = Headers.Builder() + .add("User-Agent", "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Mobile Safari/537.36") + .add("X-Requested-With", randomString((1..20).random())) + .build() override val filterCapabilities: MangaListFilterCapabilities get() = MangaListFilterCapabilities( isSearchSupported = true, + isSearchWithFiltersSupported = true, + isMultipleTagsSupported = true, isOriginalLocaleSupported = true, ) override val availableSortOrders: Set = EnumSet.of( - SortOrder.UPDATED, + SortOrder.RELEVANCE, + SortOrder.NEWEST, + SortOrder.NEWEST_ASC, ) override suspend fun getFilterOptions() = MangaListFilterOptions( @@ -58,6 +63,8 @@ internal class MyReadingManga(context: MangaLoaderContext) : availableStates = EnumSet.of( MangaState.ONGOING, MangaState.FINISHED, + MangaState.PAUSED, + MangaState.ABANDONED, ), availableContentRating = EnumSet.of(ContentRating.ADULT), availableLocales = setOf( @@ -102,6 +109,8 @@ internal class MyReadingManga(context: MangaLoaderContext) : private fun getLanguageSlug(locale: Locale?): String? { return when { + locale?.language == "en" -> "english" + locale?.language == "ja" -> "japanese" locale?.language == "fr" -> "french" locale?.language == "ja" -> "jp" locale?.language == "zh" && locale.country == "TW" -> "traditional-chinese" @@ -141,75 +150,53 @@ internal class MyReadingManga(context: MangaLoaderContext) : } } - override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List { val url = buildString { append("https://") append(domain) - // Add language path if specified, need to fix - val langSlug = getLanguageSlug(filter.locale) - if (langSlug != null) { - append("/lang/") - append(langSlug) - } - - when { - !filter.query.isNullOrEmpty() -> { - // Search with language: /lang/french/page/2/?s=example - if (page > 1) { - append("/page/") - append(page) - } - append("/?s=") - append(filter.query.urlEncoded()) - } - - filter.tags.isNotEmpty() -> { - // Genre filtering doesn't work with language, so we ignore language for genre - if (langSlug == null) { - append("/genre/") - append(filter.tags.first().key) - append("/page/") - append(page) - append("/") - } else { - // If both language and genre are selected, just use language - append("/page/") - append(page) - append("/") - } - } - - filter.states.isNotEmpty() -> { - // Status filtering doesn't work with language either - if (langSlug == null) { - append("/status/") - append( - when (filter.states.first()) { - MangaState.ONGOING -> "ongoing" - MangaState.FINISHED -> "completed" - else -> "ongoing" - }, - ) - append("/page/") - append(page) - append("/") - } else { - // If both language and status are selected, just use language - append("/page/") - append(page) - append("/") - } - } - - else -> { - // Regular browsing with or without language - append("/page/") - append(page) - append("/") - } - } + if (page > 1) { + append("/page/") + append(page) + } + + append("/?ep_sort=") + when (order) { + SortOrder.NEWEST -> append("date") + SortOrder.NEWEST_ASC -> append("date_asc") + else -> append("") + } + + if (!filter.query.isNullOrEmpty()) { + append("&s=") + append(filter.query.splitByWhitespace().joinToString(separator = "+")) + } + + val langSlug = getLanguageSlug(filter.locale) + if (langSlug != null) { + append("&ep_filter_lang=") + append(langSlug) + } + + if (filter.states.isNotEmpty()) { + append("&ep_filter_status=") + filter.states.oneOrThrowIfMany()?.let { + append( + when (it) { + MangaState.ONGOING -> "ongoing" + MangaState.FINISHED -> "completed" + MangaState.PAUSED -> "hiatus" + MangaState.ABANDONED -> "dropped" + else -> "" + } + ) + } + } + + if (filter.tags.isNotEmpty()) { + append("&ep_filter_genre=") + append(filter.tags.joinToString(",") { it.key }) + } } val doc = webClient.httpGet(url).parseHtml() @@ -302,6 +289,8 @@ internal class MyReadingManga(context: MangaLoaderContext) : val state = when (doc.select("a[href*=status]").firstOrNull()?.text()) { "Ongoing" -> MangaState.ONGOING "Completed" -> MangaState.FINISHED + "Hiatus" -> MangaState.PAUSED + "Dropped" -> MangaState.ABANDONED else -> null } @@ -449,5 +438,10 @@ internal class MyReadingManga(context: MangaLoaderContext) : 0L } } + + private fun randomString(length: Int): String { + val charPool = ('a'..'z') + ('A'..'Z') + return List(length) { charPool.random() }.joinToString("") + } }