diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt index bf815d97..0eb6764c 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt @@ -114,6 +114,11 @@ abstract class MangaParser @InternalParsersApi constructor(val source: MangaSour return config[configKeyDomain] } + fun getDomain(subdomain: String): String { + val domain = getDomain() + return subdomain + "." + domain.removePrefix("www.") + } + /** * Create a unique id for [Manga]/[MangaChapter]/[MangaPage]. * @param url must be relative url, without a domain @@ -150,34 +155,6 @@ abstract class MangaParser @InternalParsersApi constructor(val source: MangaSour return h } - /** - * Convert relative url to an absolute using [getDomain] - */ - @Deprecated( - message = "Use toAbsoluteUrl() instead", - replaceWith = ReplaceWith( - "toAbsoluteUrl(getDomain(), subdomain)", - "org.koitharu.kotatsu.parsers.util.toAbsoluteUrl", - ), - ) - protected fun String.withDomain(subdomain: String): String { - return toAbsoluteUrl(getDomain(), subdomain) - } - - /** - * Convert relative url to an absolute using [getDomain] - */ - @Deprecated( - message = "Use toAbsoluteUrl() instead", - replaceWith = ReplaceWith( - "toAbsoluteUrl(getDomain())", - "org.koitharu.kotatsu.parsers.util.toAbsoluteUrl", - ), - ) - protected fun String.withDomain(): String { - return toAbsoluteUrl(getDomain()) - } - @InternalParsersApi @Suppress("NOTHING_TO_INLINE") protected inline fun parseFailed(message: String? = null): Nothing { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/AnibelParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/AnibelParser.kt index 5ff44bd1..8436ee1d 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/AnibelParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/AnibelParser.kt @@ -72,7 +72,7 @@ internal class AnibelParser(override val context: MangaLoaderContext) : MangaPar id = generateUid(mediaId), title = title.getString("be"), coverUrl = jo.getString("poster").removePrefix("/cdn") - .toAbsoluteUrl(getDomain(), "cdn") + "?width=200&height=280", + .toAbsoluteUrl(getDomain("cdn")) + "?width=200&height=280", altTitle = title.getString("alt").takeUnless(String::isEmpty), author = null, isNsfw = false, @@ -112,7 +112,7 @@ internal class AnibelParser(override val context: MangaLoaderContext) : MangaPar ).getJSONObject("media") val title = details.getJSONObject("title") val poster = details.getString("poster").removePrefix("/cdn") - .toAbsoluteUrl(getDomain(), "cdn") + .toAbsoluteUrl(getDomain("cdn")) val chapters = apiCall( """ chapters(mediaId: "${details.getString("mediaId")}") { @@ -213,7 +213,7 @@ internal class AnibelParser(override val context: MangaLoaderContext) : MangaPar id = generateUid(mediaId), title = title.getString("be"), coverUrl = jo.getString("poster").removePrefix("/cdn") - .toAbsoluteUrl(getDomain(), "cdn") + "?width=200&height=280", + .toAbsoluteUrl(getDomain("cdn")) + "?width=200&height=280", altTitle = title.getString("en").takeUnless(String::isEmpty), author = null, isNsfw = false, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/MangaTownParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/MangaTownParser.kt index ad840e88..6042656a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/MangaTownParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/MangaTownParser.kt @@ -190,7 +190,7 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga } private suspend fun bypassLicensedChapters(manga: Manga): List { - val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain(), "m")).parseHtml() + val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain("m"))).parseHtml() val list = doc.body().selectFirst("ul.detail-ch-list") ?: return emptyList() val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US) return list.select("li").asReversed().mapIndexedNotNull { i, li -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/NicovideoSeigaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/NicovideoSeigaParser.kt index 603d4f19..cee01510 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/NicovideoSeigaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/NicovideoSeigaParser.kt @@ -1,9 +1,6 @@ package org.koitharu.kotatsu.parsers.site -import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaParser -import org.koitharu.kotatsu.parsers.MangaParserAuthProvider -import org.koitharu.kotatsu.parsers.MangaSourceParser +import org.koitharu.kotatsu.parsers.* import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.exception.AuthRequiredException import org.koitharu.kotatsu.parsers.model.* @@ -13,22 +10,21 @@ import java.util.* private const val STATUS_ONGOING = "連載" private const val STATUS_FINISHED = "完結" -@MangaSourceParser("NICOVIDEOSEIGA", "Nicovideo Seiga", "ja") -class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.NICOVIDEOSEIGA), +@MangaSourceParser("NICOVIDEO_SEIGA", "Nicovideo Seiga", "ja") +class NicovideoSeigaParser(override val context: MangaLoaderContext) : + MangaParser(MangaSource.NICOVIDEO_SEIGA), MangaParserAuthProvider { override val authUrl: String - get() = "https://account.nicovideo.jp/login?site=seiga" + get() = "https://${getDomain("account")}/login?site=seiga" override val isAuthorized: Boolean - get() { - return context.cookieJar.getCookies(getDomain()).any { - it.name == "user_session" - } + get() = context.cookieJar.getCookies(getDomain("seiga")).any { + it.name == "user_session" } override suspend fun getUsername(): String { - val body = context.httpGet("https://app.nicovideo.jp/my/apps").parseHtml().body() + val body = context.httpGet("https://${getDomain("app")}/my/apps").parseHtml().body() return body.selectFirst("#userinfo > div > div > strong")?.text() ?: throw AuthRequiredException(source) } @@ -37,21 +33,23 @@ class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaPars SortOrder.POPULARITY, ) - override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("seiga.nicovideo.jp", null) + override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("nicovideo.jp", null) + @InternalParsersApi override suspend fun getList( offset: Int, query: String?, tags: Set?, - sortOrder: SortOrder?, + sortOrder: SortOrder, ): List { val page = (offset / 20f).toIntUp().inc() + val domain = getDomain("seiga") val url = when { !query.isNullOrEmpty() -> return if (offset == 0) getSearchList(query, page) else emptyList() - tags.isNullOrEmpty() -> "https://${getDomain()}/manga/list?page=$page&sort=${getSortKey(sortOrder)}" - tags.size == 1 -> "https://${getDomain()}${tags.first().key}?page=$page&sort=${getSortKey(sortOrder)}" + tags.isNullOrEmpty() -> "https://$domain/manga/list?page=$page&sort=${getSortKey(sortOrder)}" + tags.size == 1 -> "https://$domain/manga/list?category=${tags.first().key}&page=$page&sort=${getSortKey(sortOrder)}" tags.size > 1 -> throw IllegalArgumentException("This source supports only 1 category") - else -> "https://${getDomain()}/manga/list?page=$page&sort=${getSortKey(sortOrder)}" + else -> "https://$domain/manga/list?page=$page&sort=${getSortKey(sortOrder)}" } val doc = context.httpGet(url).parseHtml() val comicList = doc.body().select("#comic_list > ul > li") ?: parseFailed("Container not found") @@ -80,16 +78,18 @@ class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaPars STATUS_FINISHED -> MangaState.FINISHED else -> null }, - publicUrl = href.toAbsoluteUrl(item.host ?: getDomain()), + publicUrl = href.toAbsoluteUrl(item.host ?: getDomain("seiga")), source = source, ) } } override suspend fun getDetails(manga: Manga): Manga { - val doc = context.httpGet(manga.url.withDomain()).parseHtml() + val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain("seiga"))).parseHtml() val contents = doc.body().selectFirst("#contents") ?: parseFailed("Cannot find root") - val statusText = contents.select("div.mg_work_detail > div > div:nth-child(2) > div.tip.content_status.status_series > span").text() + val statusText = contents + .select("div.mg_work_detail > div > div:nth-child(2) > div.tip.content_status.status_series > span") + .text() return manga.copy( description = contents.selectFirst("div.mg_work_detail > div > div.row > div.description_text")?.html(), largeCoverUrl = contents.selectFirst("div.primaries > div.main_visual > a > img")?.attrAsAbsoluteUrlOrNull("src"), @@ -116,7 +116,7 @@ class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaPars } override suspend fun getPages(chapter: MangaChapter): List { - val fullUrl = chapter.url.withDomain() + val fullUrl = chapter.url.toAbsoluteUrl(getDomain("seiga")) val doc = context.httpGet(fullUrl).parseHtml() if (!doc.select("#login_manga").isEmpty()) throw AuthRequiredException(source) @@ -134,7 +134,7 @@ class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaPars } override suspend fun getTags(): Set { - val doc = context.httpGet("https://${getDomain()}/manga/list").parseHtml() + val doc = context.httpGet("https://${getDomain("seiga")}/manga/list").parseHtml() val root = doc.body().select("#mg_category_list > ul > li") ?: parseFailed("Cannot find tags") return root.mapToSet { li -> val a = li.selectFirst("a") ?: parseFailed("a is null") @@ -147,7 +147,7 @@ class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaPars } private suspend fun getSearchList(query: String, page: Int): List { - val domain = getDomain() + val domain = getDomain("seiga") val doc = context.httpGet("https://$domain/manga/search/?q=$query&page=$page&sort=score").parseHtml() val root = doc.body().select(".search_result__item") return root.mapNotNull { item -> @@ -155,7 +155,7 @@ class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaPars Manga( id = generateUid(href), url = href, - publicUrl = href.toAbsoluteUrl(item.host ?: getDomain()), + publicUrl = href.toAbsoluteUrl(item.host ?: domain), title = item.selectFirst(".search_result__item__info > .search_result__item__info--title > a") ?.text()?.trim() ?: return@mapNotNull null, altTitle = null, @@ -171,10 +171,9 @@ class NicovideoSeigaParser(override val context: MangaLoaderContext) : MangaPars } } - private fun getSortKey(sortOrder: SortOrder?) = - when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) { - SortOrder.POPULARITY -> "manga_view" - SortOrder.UPDATED -> "manga_updated" - else -> "manga_view" - } + private fun getSortKey(sortOrder: SortOrder) = when (sortOrder) { + SortOrder.POPULARITY -> "manga_view" + SortOrder.UPDATED -> "manga_updated" + else -> "manga_view" + } } \ No newline at end of file diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/RemangaParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/RemangaParser.kt index f81b093f..7dc191c4 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/RemangaParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/RemangaParser.kt @@ -171,7 +171,7 @@ internal class RemangaParser( override suspend fun getPages(chapter: MangaChapter): List { val referer = "https://${getDomain()}/" - val content = context.httpGet(chapter.url.toAbsoluteUrl(getDomain(), "api"), getApiHeaders()) + val content = context.httpGet(chapter.url.toAbsoluteUrl(getDomain("api")), getApiHeaders()) .handle401() .parseJson() .getJSONObject("content") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt index 01cee9af..2bac7814 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt @@ -8,11 +8,7 @@ import okhttp3.internal.closeQuietly import org.json.JSONArray import org.json.JSONObject import org.jsoup.Jsoup -import org.jsoup.internal.StringUtil import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.select.Elements import java.text.DateFormat /** @@ -50,49 +46,6 @@ fun Response.parseJsonArray(): JSONArray = try { closeQuietly() } -@Deprecated( - message = "", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("firstNotNullOfOrNull { it.ownText().takeIf(predicate) }"), -) -inline fun Elements.findOwnText(predicate: (String) -> Boolean): String? { - for (x in this) { - val ownText = x.ownText() - if (predicate(ownText)) { - return ownText - } - } - return null -} - -@Deprecated( - message = "", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("firstNotNullOfOrNull { it.text().takeIf(predicate) }"), -) -inline fun Elements.findText(predicate: (String) -> Boolean): String? { - for (x in this) { - val text = x.text() - if (predicate(text)) { - return text - } - } - return null -} - -@Deprecated( - message = "Use toAbsoluteUrl() instead", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("toAbsoluteUrl(node.host)"), -) -fun String.inContextOf(node: Node): String { - return if (this.isEmpty()) { - "" - } else { - StringUtil.resolve(node.baseUri(), this) - } -} - /** * Convert url to relative if it is on [domain] * @return an url relative to the [domain] or absolute, if domain is mismatching @@ -114,41 +67,6 @@ fun String.toAbsoluteUrl(domain: String): String = when { else -> this } -/** - * Convert url to absolute with specified domain and subdomain - * @return an absolute url with [subdomain].[domain] if this is relative - */ -fun String.toAbsoluteUrl(domain: String, subdomain: String): String { - if (!this.startsWith('/')) return this - return toAbsoluteUrl(subdomain + "." + domain.removePrefix("www.")) -} - -@Deprecated( - message = "", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("attrAsRelativeUrl(attributeKey)"), -) -fun Element.relUrl(attributeKey: String): String { - val attr = attr(attributeKey).trim() - if (attr.isEmpty()) { - return "" - } - if (attr.startsWith("/")) { - return attr - } - val baseUrl = REGEX_URL_BASE.find(baseUri())?.value ?: return attr - return attr.removePrefix(baseUrl.dropLast(1)) -} - -private val REGEX_URL_BASE = Regex("^[^/]{2,6}://[^/]+/", RegexOption.IGNORE_CASE) - -@Deprecated( - message = "", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("styleValueOrNull(property)"), -) -fun Element.css(property: String): String? = styleValueOrNull(property) - fun DateFormat.tryParse(str: String?): Long = if (str.isNullOrEmpty()) { 0L } else { diff --git a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt index 22255e80..e0ac6d02 100644 --- a/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt +++ b/src/test/kotlin/org/koitharu/kotatsu/parsers/MangaParserTest.kt @@ -135,7 +135,7 @@ internal class MangaParserTest { .scheme("https") .toString() val response = context.doRequest(url) - val realDomain = response.request.url.host + val realDomain = response.request.url.topPrivateDomain() assertEquals(defaultDomain, realDomain) }