Fix MangaHost not working (#3801)
* Fix MangaHost not working due to website redesign. * Change OkHttpClient to Cloudflare.
@ -5,7 +5,7 @@ ext {
 | 
			
		||||
    extName = 'MangaHost'
 | 
			
		||||
    pkgNameSuffix = 'pt.mangahost'
 | 
			
		||||
    extClass = '.MangaHost'
 | 
			
		||||
    extVersionCode = 9
 | 
			
		||||
    extVersionCode = 10
 | 
			
		||||
    libVersion = '1.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB  | 
| 
		 Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.9 KiB  | 
| 
		 Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB  | 
| 
		 Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 10 KiB  | 
| 
		 Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 49 KiB  | 
@ -11,6 +11,7 @@ import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
@ -30,15 +31,17 @@ class MangaHost : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override val client: OkHttpClient = network.cloudflareClient
 | 
			
		||||
 | 
			
		||||
    override fun headersBuilder(): Headers.Builder = Headers.Builder()
 | 
			
		||||
        .add("User-Agent", USER_AGENT)
 | 
			
		||||
        .add("Referer", baseUrl)
 | 
			
		||||
 | 
			
		||||
    private fun genericMangaFromElement(element: Element, lazy: Boolean = true): SManga =
 | 
			
		||||
    private fun genericMangaFromElement(element: Element): SManga =
 | 
			
		||||
        SManga.create().apply {
 | 
			
		||||
            title = element.attr("title").withoutLanguage()
 | 
			
		||||
            thumbnail_url = element.select("img.manga")
 | 
			
		||||
                .attr(if (lazy) "data-path" else "src")
 | 
			
		||||
            thumbnail_url = element.select("img")
 | 
			
		||||
                .attr("data-path")
 | 
			
		||||
                .toLargeUrl()
 | 
			
		||||
            setUrlWithoutDomain(element.attr("href").substringBeforeLast("-mh"))
 | 
			
		||||
        }
 | 
			
		||||
@ -52,7 +55,7 @@ class MangaHost : ParsedHttpSource() {
 | 
			
		||||
        return GET("$baseUrl/mangas/mais-visualizados$pageStr", newHeaders)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector(): String = "div.thumbnail div a.pull-left"
 | 
			
		||||
    override fun popularMangaSelector(): String = "div#dados div.manga-block div.manga-block-left a"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga = genericMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
@ -67,9 +70,9 @@ class MangaHost : ParsedHttpSource() {
 | 
			
		||||
        return GET("$baseUrl/lancamentos$pageStr", newHeaders)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "table.table-lancamentos > tbody > tr > td:eq(0) > a"
 | 
			
		||||
    override fun latestUpdatesSelector() = "div#dados div.line-lancamentos div.column-img a"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga = genericMangaFromElement(element, false)
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga = genericMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
@ -87,56 +90,32 @@ class MangaHost : ParsedHttpSource() {
 | 
			
		||||
    override fun searchMangaNextPageSelector(): String? = null
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val infoElement = document.select("div#page > section > div > div.pull-left")
 | 
			
		||||
        val infoElement = document.select("div.box-content div.w-row div.w-col:eq(1) article")
 | 
			
		||||
 | 
			
		||||
        return SManga.create().apply {
 | 
			
		||||
            author = infoElement.select("li:contains(Autor:)").textWithoutLabel()
 | 
			
		||||
            artist = infoElement.select("li:contains(Desenho (Art):)").textWithoutLabel()
 | 
			
		||||
            genre = infoElement.select("li:contains(Categoria(s):)").textWithoutLabel()
 | 
			
		||||
            description = infoElement.select("article").first()?.text()
 | 
			
		||||
            author = infoElement.select("div.text li div:contains(Autor:)").textWithoutLabel()
 | 
			
		||||
            artist = infoElement.select("div.text li div:contains(Arte:)").textWithoutLabel()
 | 
			
		||||
            genre = infoElement.select("h3.subtitle + div.tags a").joinToString { it.text() }
 | 
			
		||||
            description = infoElement.select("div.text div.paragraph").first()?.text()
 | 
			
		||||
                ?.substringBefore("Relacionados:")
 | 
			
		||||
            status = parseStatus(infoElement.select("li:contains(Status:)").text().orEmpty())
 | 
			
		||||
            thumbnail_url = document.select("div#page > section > div > img.thumbnail")
 | 
			
		||||
            status = infoElement.select("div.text li div:contains(Status:)").text().toStatus()
 | 
			
		||||
            thumbnail_url = document.select("div.box-content div.w-row div.w-col:eq(0) div.widget img")
 | 
			
		||||
                .attr("src")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(status: String) = when {
 | 
			
		||||
        status.contains("Ativo") -> SManga.ONGOING
 | 
			
		||||
        status.contains("Completo") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector(): String =
 | 
			
		||||
        "ul.list_chapters li a, table.table-hover:not(.table-mangas) > tbody > tr"
 | 
			
		||||
        "article.article > section.clearfix div.chapters div.cap div.card.pop"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val isNewLayout = element.tagName() == "a"
 | 
			
		||||
 | 
			
		||||
        if (isNewLayout) {
 | 
			
		||||
            val content = Jsoup.parse(element.attr("data-content"))
 | 
			
		||||
            val date = content.select("small.clearfix").text()
 | 
			
		||||
                .substringAfter("Adicionado em ")
 | 
			
		||||
 | 
			
		||||
            return SChapter.create().apply {
 | 
			
		||||
                name = element.attr("data-original-title").withoutLanguage()
 | 
			
		||||
                scanlator = content.select("small.clearfix strong").text()
 | 
			
		||||
                date_upload = DATE_FORMAT_NEW.tryParseTime(date)
 | 
			
		||||
                chapter_number = element.text().toFloatOrNull() ?: 1f
 | 
			
		||||
                setUrlWithoutDomain(content.select("div.clearfix a").attr("href"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val firstColumn = element.select("td:eq(0)")
 | 
			
		||||
        val secondColumn = element.select("td:eq(1)")
 | 
			
		||||
        val thirdColumn = element.select("td:eq(2)")
 | 
			
		||||
 | 
			
		||||
        return SChapter.create().apply {
 | 
			
		||||
            name = firstColumn.select("a").text().withoutLanguage()
 | 
			
		||||
            scanlator = secondColumn.text()
 | 
			
		||||
            date_upload = DATE_FORMAT_OLD.tryParseTime(thirdColumn.text())
 | 
			
		||||
            setUrlWithoutDomain(firstColumn.select("a").attr("href"))
 | 
			
		||||
        }
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
 | 
			
		||||
        name = element.select("div.pop-title").text().withoutLanguage()
 | 
			
		||||
        scanlator = element.select("div.pop-content small strong").text()
 | 
			
		||||
        date_upload = element.select("small.clearfix").text()
 | 
			
		||||
            .substringAfter("Adicionado em ")
 | 
			
		||||
            .let { DATE_FORMAT.tryParseTime(it) }
 | 
			
		||||
        chapter_number = element.select("div.pop-title span.btn-caps").text()
 | 
			
		||||
            .toFloatOrNull() ?: 1f
 | 
			
		||||
        setUrlWithoutDomain(element.select("div.tags a").attr("href"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListRequest(chapter: SChapter): Request {
 | 
			
		||||
@ -149,17 +128,17 @@ class MangaHost : ParsedHttpSource() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val documentStr = document.toString()
 | 
			
		||||
        val images = documentStr
 | 
			
		||||
            .substringAfter(SCRIPT_BEGIN)
 | 
			
		||||
            .substringBefore(SCRIPT_END)
 | 
			
		||||
        val imagesHtml = document.select("script:containsData(var images)").first()!!
 | 
			
		||||
            .data()
 | 
			
		||||
            .substringAfter("var images = [")
 | 
			
		||||
            .substringBefore("];")
 | 
			
		||||
            .replace(SCRIPT_REGEX, "")
 | 
			
		||||
 | 
			
		||||
        val newDocument = Jsoup.parse(images)
 | 
			
		||||
        val referer = document.select("link[rel='canonical']").first()
 | 
			
		||||
 | 
			
		||||
        return newDocument.select("a img")
 | 
			
		||||
            .mapIndexed { i, el -> Page(i, referer.attr("href"), el.attr("src")) }
 | 
			
		||||
        return Jsoup.parse(imagesHtml)
 | 
			
		||||
            .select("a img")
 | 
			
		||||
            .mapIndexed { i, el ->
 | 
			
		||||
                Page(i, document.location(), el.attr("src"))
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
@ -172,9 +151,15 @@ class MangaHost : ParsedHttpSource() {
 | 
			
		||||
        return GET(page.imageUrl!!, newHeaders)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun String.toStatus() = when {
 | 
			
		||||
        contains("Ativo") -> SManga.ONGOING
 | 
			
		||||
        contains("Completo") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun SimpleDateFormat.tryParseTime(date: String): Long {
 | 
			
		||||
        return try {
 | 
			
		||||
            parse(date).time
 | 
			
		||||
            parse(date)!!.time
 | 
			
		||||
        } catch (e: ParseException) {
 | 
			
		||||
            0L
 | 
			
		||||
        }
 | 
			
		||||
@ -184,19 +169,16 @@ class MangaHost : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    private fun String.toLargeUrl(): String = replace(IMAGE_REGEX, "_large.")
 | 
			
		||||
 | 
			
		||||
    private fun Elements.textWithoutLabel(): String = text()!!.substringAfter(":")
 | 
			
		||||
    private fun Elements.textWithoutLabel(): String = text()!!.substringAfter(":").trim()
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"
 | 
			
		||||
        private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
 | 
			
		||||
 | 
			
		||||
        private val LANG_REGEX = "( )?\\((PT-)?BR\\)".toRegex()
 | 
			
		||||
        private val IMAGE_REGEX = "_(small|medium)\\.".toRegex()
 | 
			
		||||
        private val IMAGE_REGEX = "_(small|medium|xmedium)\\.".toRegex()
 | 
			
		||||
 | 
			
		||||
        private val DATE_FORMAT_OLD by lazy { SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH) }
 | 
			
		||||
        private val DATE_FORMAT_NEW by lazy { SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH) }
 | 
			
		||||
        private val DATE_FORMAT by lazy { SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH) }
 | 
			
		||||
 | 
			
		||||
        private const val SCRIPT_BEGIN = "var images = ["
 | 
			
		||||
        private const val SCRIPT_END = "];"
 | 
			
		||||
        private val SCRIPT_REGEX = "[\",]".toRegex()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||