Fix MangaFast (#8638)

* Update build.gradle

* Update build.gradle

* Create MangaFastDto.kt

* Update MangaFast.kt

* Update MangaFast.kt

* Update MangaFastDto.kt

* Update build.gradle

* Update MangaFast.kt

* MangaFast: remove wildcard import

Co-authored-by: FourTOne5 <59261191+FourTOne5@users.noreply.github.com>
This commit is contained in:
David Ruppelt 2021-08-18 20:17:08 +02:00 committed by GitHub
parent e564383efa
commit 684eea5469
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 144 additions and 61 deletions

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'MangaFast' extName = 'MangaFast'
pkgNameSuffix = 'en.mangafast' pkgNameSuffix = 'en.mangafast'
extClass = '.MangaFast' extClass = '.MangaFast'
extVersionCode = 12 extVersionCode = 13
libVersion = '1.2' libVersion = '1.2'
} }

View File

@ -1,17 +1,28 @@
package eu.kanade.tachiyomi.extension.en.mangafast package eu.kanade.tachiyomi.extension.en.mangafast
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.Request import okhttp3.Request
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.Json
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.put
import okhttp3.Response
import org.jsoup.select.Elements
import uy.kohesive.injekt.injectLazy
class MangaFast : ParsedHttpSource() { class MangaFast : ParsedHttpSource() {
override val name = "MangaFast" override val name = "MangaFast"
@ -22,95 +33,125 @@ class MangaFast : ParsedHttpSource() {
override val supportsLatest = true override val supportsLatest = true
// popular private val json: Json by injectLazy()
// Popular
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/list-manga" + if (page > 1) "/page/$page" else "", headers) // Every thing is in a single page.
return GET("$baseUrl/list-manga", headers)
} }
override fun popularMangaSelector() = ".list-content .ls4 .ls4v" override fun popularMangaSelector(): String = "div#animelist li a"
override fun popularMangaFromElement(element: Element) = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
val a = element.select("a") element.select("a").let { a ->
setUrlWithoutDomain(a.attr("href")) setUrlWithoutDomain(a.attr("href"))
title = a.attr("title") title = a.attr("title")
thumbnail_url = a.select("img").attr("src") thumbnail_url = a.select("img").imageFromElement()
}
} }
override fun popularMangaNextPageSelector() = ".btn-w a:contains(Next »)" override fun popularMangaNextPageSelector(): String? = null
// latest // Latest
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers) override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/read", headers)
override fun latestUpdatesSelector() = ".ls8w div.ls8 .ls8v" override fun latestUpdatesSelector() = "div.ls5"
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element) override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector(): String? = null override fun latestUpdatesNextPageSelector(): String? = null
// search // Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = GET("$baseUrl/page/$page/?s=$query", headers) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val jsonPayload = buildJsonObject {
override fun searchMangaSelector() = popularMangaSelector() put("limit", 30)
put("q", query)
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
// manga details
override fun mangaDetailsParse(document: Document): SManga {
val articleTitle = document.select("article header[id=article-title]")
val articleInfo = document.select("article section[id=article-info]")
val manga = SManga.create().apply {
title = articleTitle.select("h1[itemprop=name]").text().trim()
description = articleTitle.select("p.desc").text().trim()
thumbnail_url = articleInfo.select("img.shadow").attr("src")
} }
articleInfo.select("table.inftable tbody tr").forEach { val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
val row = it.select("td")
when (row[0].text()) { val newHeaders = headersBuilder()
"Comic Title" -> manga.title = row[1].text().trim() .add("Content-Length", requestBody.contentLength().toString())
"Genre" -> manga.genre = row[1].text().trim().removeSuffix(",") .add("Content-Type", requestBody.contentType().toString())
"Author" -> manga.author = row[1].text().trim() .add("Accept", ACCEPT_JSON)
"Status" -> manga.status = parseStatus(row[1].text()) .add("Origin", baseUrl)
.add("Referer", "$baseUrl/")
.build()
return POST(
"https://search.${baseUrl.substringAfterLast("/")}/comics/ms",
headers = newHeaders,
body = requestBody
)
}
override fun searchMangaParse(response: Response): MangasPage {
val result = json.decodeFromString<SearchResultDto>(response.body!!.string())
val comicList = result.mangaList
.map(::searchMangaFromObject)
return MangasPage(comicList, hasNextPage = false)
}
private fun searchMangaFromObject(manga: MangaDto): SManga = SManga.create().apply {
title = manga.title
thumbnail_url = manga.thumbnail
url = "/read/${manga.slug}"
}
// Manga Details
override fun mangaDetailsParse(document: Document): SManga {
val sManga = SManga.create()
val infoTable = document.select("table.inftable tbody tr")
sManga.apply {
title = document.select("h1[itemprop=name]").text().trim()
description = document.select("h2[id^=Synopsis] + p").text().trim()
thumbnail_url = document.select("img#Thumbnail").imageFromElement()
}
infoTable.forEach { column ->
val rows = column.select("td")
when (rows[0].text().trim()) {
"Title" -> sManga.title = rows[1].text().trim()
"Genres" -> sManga.genre = rows[1].text().trim().removeSuffix(",")
"Author" -> sManga.author = rows[1].text().trim()
"Status" -> sManga.status = rows[1].text().parseStatus()
} }
} }
return manga return sManga
} }
private fun parseStatus(status: String) = when { private fun String?.parseStatus() = if (this == null) SManga.UNKNOWN else when {
status.contains("Ongoing") -> SManga.ONGOING this.toLowerCase(Locale.ENGLISH).contains("ongoing") -> SManga.ONGOING
status.contains("Completed") -> SManga.COMPLETED this.toLowerCase(Locale.ENGLISH).contains("completed") -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
// chapter list // Chapter List
override fun chapterListSelector() = "a.chapter-link:not(:has(i:contains(Spoiler & Release Date)))" override fun chapterListSelector() =
"tbody:has(th:contains(Chapter List)) tr[itemprop]:not(:contains(Spoiler & Release Date))"
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.attr("href")) val link = element.select("a.chapter-link")
name = element.select(".left").text() setUrlWithoutDomain(link.attr("href"))
date_upload = parseDate(element.select(".right").text()) name = link.text().trim()
date_upload = element.select("td:last-of-type").text().parseDate()
} }
private fun parseDate(text: String): Long { private fun String?.parseDate(): Long {
if (this == null) return 0L
return try { return try {
dateFormat.parse(text.trim())?.time ?: 0L dateFormat.parse(this.trim())?.time ?: 0L
} catch (pe: ParseException) { // this can happen for spoiler & release date entries } catch (pe: ParseException) { // this can happen for spoiler & release date entries
0L 0L
} }
} }
companion object { // Pages
val dateFormat by lazy {
SimpleDateFormat("MM/dd/yyyy", Locale.US)
}
}
// pages
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
return document.select(".content-comic > img").mapIndexed { i, element -> return document.select("div#Read img").mapIndexed { i, element ->
var url = element.attr("abs:data-src") var url = element.attr("abs:data-src")
if (url.isEmpty()) { if (url.isEmpty()) {
@ -121,7 +162,32 @@ class MangaFast : ParsedHttpSource() {
} }
} }
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used") private fun Elements.imageFromElement(): String? {
return when {
this.hasAttr("data-src") -> this.attr("abs:data-src")
this.hasAttr("abs:src") -> this.attr("abs:src")
else -> null
}
}
// Unused Function
override fun searchMangaSelector() = throw UnsupportedOperationException("Not Used")
override fun searchMangaFromElement(element: Element) =
throw UnsupportedOperationException("Not Used")
override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not Used")
override fun imageUrlParse(document: Document): String =
throw UnsupportedOperationException("Not Used")
override fun getFilterList() = FilterList() override fun getFilterList() = FilterList()
companion object {
val dateFormat by lazy {
SimpleDateFormat("MM/dd/yyyy", Locale.US)
}
private val JSON_MEDIA_TYPE = "application/json;charset=UTF-8".toMediaType()
private const val ACCEPT_JSON = "application/json, text/plain, */*"
}
} }

View File

@ -0,0 +1,16 @@
package eu.kanade.tachiyomi.extension.en.mangafast
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
@Serializable
data class SearchResultDto(
@SerialName("hits") val mangaList: List<MangaDto> = emptyList()
)
@Serializable
data class MangaDto(
val title: String = "",
val slug: String = "",
val thumbnail: String = "",
)