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