diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/GroupleParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/GroupleParser.kt similarity index 71% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/GroupleParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/GroupleParser.kt index 8f771b25..44dffaf4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/GroupleParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/GroupleParser.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site +package org.koitharu.kotatsu.parsers.site.grouple import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl @@ -23,17 +23,17 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) : .build() override val sortOrders: Set = EnumSet.of( - SortOrder.UPDATED, - SortOrder.POPULARITY, - SortOrder.NEWEST, - SortOrder.RATING, - ) + SortOrder.UPDATED, + SortOrder.POPULARITY, + SortOrder.NEWEST, + SortOrder.RATING, + ) override suspend fun getList( - offset: Int, - query: String?, - tags: Set?, - sortOrder: SortOrder, + offset: Int, + query: String?, + tags: Set?, + sortOrder: SortOrder, ): List { val domain = getDomain() val doc = when { @@ -84,39 +84,40 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) : ?: return@mapNotNull null val tileInfo = descDiv.selectFirst("div.tile-info") val relUrl = href.toRelativeUrl(baseHost) - Manga( - id = generateUid(relUrl), - url = relUrl, - publicUrl = href, - title = title, - altTitle = descDiv.selectFirst("h4")?.text(), - coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original")?.replace("_p.", ".").orEmpty(), - rating = runCatching { - node.selectFirst("div.rating") - ?.attr("title") - ?.substringBefore(' ') - ?.toFloatOrNull() - ?.div(10f) - }.getOrNull() ?: RATING_UNKNOWN, - author = tileInfo?.selectFirst("a.person-link")?.text(), - isNsfw = false, - tags = runCatching { - tileInfo?.select("a.element-link") - ?.mapToSet { - MangaTag( - title = it.text().toTitleCase(), - key = it.attr("href").substringAfterLast('/'), - source = source, - ) - } - }.getOrNull().orEmpty(), - state = when { - node.selectFirst("div.tags") - ?.selectFirst("span.mangaCompleted") != null -> MangaState.FINISHED - else -> null - }, - source = source, - ) + Manga( + id = generateUid(relUrl), + url = relUrl, + publicUrl = href, + title = title, + altTitle = descDiv.selectFirst("h4")?.text(), + coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original")?.replace("_p.", ".").orEmpty(), + rating = runCatching { + node.selectFirst("div.rating") + ?.attr("title") + ?.substringBefore(' ') + ?.toFloatOrNull() + ?.div(10f) + }.getOrNull() ?: RATING_UNKNOWN, + author = tileInfo?.selectFirst("a.person-link")?.text(), + isNsfw = false, + tags = runCatching { + tileInfo?.select("a.element-link") + ?.mapToSet { + MangaTag( + title = it.text().toTitleCase(), + key = it.attr("href").substringAfterLast('/'), + source = source, + ) + } + }.getOrNull().orEmpty(), + state = when { + node.selectFirst("div.tags") + ?.selectFirst("span.mangaCompleted") != null -> MangaState.FINISHED + + else -> null + }, + source = source, + ) } } @@ -133,11 +134,11 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) : tags = manga.tags + root.select("div.subject-meta").select("span.elem_genre ") .mapNotNull { val a = it.selectFirst("a.element-link") ?: return@mapNotNull null - MangaTag( - title = a.text().toTitleCase(), - key = a.attr("href").substringAfterLast('/'), - source = source, - ) + MangaTag( + title = a.text().toTitleCase(), + key = a.attr("href").substringAfterLast('/'), + source = source, + ) }, isNsfw = root.select(".alert-warning").any { it.ownText().contains(NSFW_ALERT) }, chapters = root.selectFirst("div.chapters-link")?.selectFirst("table") @@ -151,16 +152,16 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) : .replace("(Переводчик),", "&") .removeSuffix(" (Переводчик)") } - MangaChapter( - id = generateUid(href), - name = tr.selectFirst("a")?.text().orEmpty().removePrefix(manga.title).trim(), - number = i + 1, - url = href, - uploadDate = dateFormat.tryParse(tr.selectFirst("td.d-none")?.text()), - scanlator = translators, - source = source, - branch = null, - ) + MangaChapter( + id = generateUid(href), + name = tr.selectFirst("a")?.text().orEmpty().removePrefix(manga.title).trim(), + number = i + 1, + url = href, + uploadDate = dateFormat.tryParse(tr.selectFirst("td.d-none")?.text()), + scanlator = translators, + source = source, + branch = null, + ) }, ) } @@ -189,13 +190,13 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) : val page = pages.getJSONArray(i) val primaryServer = page.getString(0) val url = page.getString(2) - MangaPage( - id = generateUid(url), - url = "$primaryServer|$serversStr|$url", - preview = null, - referer = chapter.url, - source = source, - ) + MangaPage( + id = generateUid(url), + url = "$primaryServer|$serversStr|$url", + preview = null, + referer = chapter.url, + source = source, + ) } } parseFailed("Pages list not found at ${chapter.url}") @@ -221,11 +222,11 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) : val root = doc.body().getElementById("mangaBox")?.selectFirst("div.leftContent") ?.selectFirst("table.table") ?: parseFailed("Cannot find root") return root.select("a.element-link").mapToSet { a -> - MangaTag( - title = a.text().toTitleCase(), - key = a.attr("href").substringAfterLast('/'), - source = source, - ) + MangaTag( + title = a.text().toTitleCase(), + key = a.attr("href").substringAfterLast('/'), + source = source, + ) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/MintMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/MintMangaParser.kt similarity index 69% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/MintMangaParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/MintMangaParser.kt index f30cdadc..8053907e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/MintMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/MintMangaParser.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site +package org.koitharu.kotatsu.parsers.site.grouple import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -7,11 +7,11 @@ import org.koitharu.kotatsu.parsers.model.MangaSource @MangaSourceParser("MINTMANGA", "MintManga", "ru") internal class MintMangaParser( - override val context: MangaLoaderContext, + override val context: MangaLoaderContext, ) : GroupleParser(MangaSource.MINTMANGA, "mintmangafun") { override val configKeyDomain = ConfigKey.Domain( - "mintmanga.live", - arrayOf("mintmanga.live", "mintmanga.com"), - ) + "mintmanga.live", + arrayOf("mintmanga.live", "mintmanga.com"), + ) } \ No newline at end of file diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ReadmangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/ReadmangaParser.kt similarity index 91% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/ReadmangaParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/ReadmangaParser.kt index 31d98a7d..25502347 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ReadmangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/ReadmangaParser.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site +package org.koitharu.kotatsu.parsers.site.grouple import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/SelfMangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/SelfMangaParser.kt similarity index 81% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/SelfMangaParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/SelfMangaParser.kt index 6a6280f8..c14faec2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/SelfMangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/grouple/SelfMangaParser.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site +package org.koitharu.kotatsu.parsers.site.grouple import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource @MangaSourceParser("SELFMANGA", "SelfManga", "ru") internal class SelfMangaParser( - override val context: MangaLoaderContext, + override val context: MangaLoaderContext, ) : GroupleParser(MangaSource.SELFMANGA, "selfmangafun") { override val configKeyDomain = ConfigKey.Domain("selfmanga.live", null) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ChanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/multichan/ChanParser.kt similarity index 86% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/ChanParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/multichan/ChanParser.kt index fff379f7..ea60b449 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ChanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/multichan/ChanParser.kt @@ -1,13 +1,15 @@ -package org.koitharu.kotatsu.parsers.site +package org.koitharu.kotatsu.parsers.site.multichan import org.koitharu.kotatsu.parsers.MangaParser +import org.koitharu.kotatsu.parsers.MangaParserAuthProvider +import org.koitharu.kotatsu.parsers.exception.AuthRequiredException import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.util.* import java.text.SimpleDateFormat import java.util.* -internal abstract class ChanParser(source: MangaSource) : MangaParser(source) { +internal abstract class ChanParser(source: MangaSource) : MangaParser(source), MangaParserAuthProvider { override val sortOrders: Set = EnumSet.of( SortOrder.NEWEST, @@ -15,6 +17,12 @@ internal abstract class ChanParser(source: MangaSource) : MangaParser(source) { SortOrder.ALPHABETICAL, ) + override val authUrl: String + get() = "https://${getDomain()}" + + override val isAuthorized: Boolean + get() = context.cookieJar.getCookies(getDomain()).any { it.name == "dle_user_id" } + override suspend fun getList( offset: Int, query: String?, @@ -29,11 +37,13 @@ internal abstract class ChanParser(source: MangaSource) : MangaParser(source) { } "https://$domain/?do=search&subaction=search&story=${query.urlEncoded()}" } + !tags.isNullOrEmpty() -> tags.joinToString( prefix = "https://$domain/tags/", postfix = "&n=${getSortKey2(sortOrder)}?offset=$offset", separator = "+", ) { tag -> tag.key } + else -> "https://$domain/${getSortKey(sortOrder)}?offset=$offset" } val doc = context.httpGet(url).parseHtml() @@ -143,6 +153,14 @@ internal abstract class ChanParser(source: MangaSource) : MangaParser(source) { } } + override suspend fun getUsername(): String { + val doc = context.httpGet("https://${getDomain()}").parseHtml().body() + val root = doc.requireElementById("top_user") + val a = root.getElementsByAttributeValueContaining("href", "/user/").firstOrNull() + ?: throw AuthRequiredException(source) + return a.attr("href").removeSuffix('/').substringAfterLast('/') + } + private fun getSortKey(sortOrder: SortOrder) = when (sortOrder) { SortOrder.ALPHABETICAL -> "catalog" diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/HenChanParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/multichan/HenChanParser.kt similarity index 86% rename from src/main/kotlin/org/koitharu/kotatsu/parsers/site/HenChanParser.kt rename to src/main/kotlin/org/koitharu/kotatsu/parsers/site/multichan/HenChanParser.kt index 629c14fe..7fd68684 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/HenChanParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/multichan/HenChanParser.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.parsers.site +package org.koitharu.kotatsu.parsers.site.multichan import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaSourceParser @@ -14,8 +14,8 @@ import org.koitharu.kotatsu.parsers.util.toTitleCase internal class HenChanParser(override val context: MangaLoaderContext) : ChanParser(MangaSource.HENCHAN) { override val configKeyDomain = ConfigKey.Domain( - "xxx.hentaichan.live", - arrayOf("xxx.hentaichan.live", "xx.hentaichan.live", "hentaichan.live", "hentaichan.pro"), + "y.hentaichan.live", + arrayOf("y.hentaichan.live", "xxx.hentaichan.live", "xx.hentaichan.live", "hentaichan.live", "hentaichan.pro"), ) override suspend fun getList( @@ -34,8 +34,7 @@ internal class HenChanParser(override val context: MangaLoaderContext) : ChanPar override suspend fun getDetails(manga: Manga): Manga { val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml() - val root = - doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root") + val root = doc.body().getElementById("dle-content") ?: throw ParseException("Cannot find root") val readLink = manga.url.replace("manga", "online") return manga.copy( description = root.getElementById("description")?.html()?.substringBeforeLast("