mirror of
https://git.mihon.tech/mihonapp/mihon
synced 2024-11-27 17:49:16 +03:00
update Mangasee due to webpage changes (#521)
This commit is contained in:
parent
b716a2f8ac
commit
36d4e1f7ef
1 changed files with 117 additions and 64 deletions
|
@ -1,17 +1,21 @@
|
||||||
package eu.kanade.tachiyomi.data.source.online.english
|
package eu.kanade.tachiyomi.data.source.online.english
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.network.POST
|
||||||
import eu.kanade.tachiyomi.data.source.EN
|
import eu.kanade.tachiyomi.data.source.EN
|
||||||
import eu.kanade.tachiyomi.data.source.Language
|
import eu.kanade.tachiyomi.data.source.Language
|
||||||
|
import eu.kanade.tachiyomi.data.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.data.source.model.Page
|
import eu.kanade.tachiyomi.data.source.model.Page
|
||||||
import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource
|
import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import okhttp3.FormBody
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import java.util.*
|
import java.text.SimpleDateFormat
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class Mangasee(override val id: Int) : ParsedOnlineSource() {
|
class Mangasee(override val id: Int) : ParsedOnlineSource() {
|
||||||
|
@ -22,104 +26,128 @@ class Mangasee(override val id: Int) : ParsedOnlineSource() {
|
||||||
|
|
||||||
override val lang: Language get() = EN
|
override val lang: Language get() = EN
|
||||||
|
|
||||||
override val supportsLatest = false
|
override val supportsLatest = true
|
||||||
|
|
||||||
private val datePattern = Pattern.compile("(\\d+)\\s+(.*?)s? (from now|ago).*")
|
private val recentUpdatesPattern = Pattern.compile("(.*?)\\s(\\d+)")
|
||||||
|
|
||||||
private val dateFields = HashMap<String, Int>().apply {
|
override fun popularMangaInitialUrl() = "$baseUrl/search/request.php?sortBy=popularity&sortOrder=descending"
|
||||||
put("second", Calendar.SECOND)
|
|
||||||
put("minute", Calendar.MINUTE)
|
override fun popularMangaSelector() = "div.requested > div.row"
|
||||||
put("hour", Calendar.HOUR)
|
|
||||||
put("day", Calendar.DATE)
|
override fun popularMangaRequest(page: MangasPage): Request {
|
||||||
put("week", Calendar.WEEK_OF_YEAR)
|
if (page.page == 1) {
|
||||||
put("month", Calendar.MONTH)
|
page.url = popularMangaInitialUrl()
|
||||||
put("year", Calendar.YEAR)
|
}
|
||||||
|
val (body, requestUrl) = convertQueryToPost(page)
|
||||||
|
return POST(requestUrl, headers, body.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
private val dateRelationFields = HashMap<String, Int>().apply {
|
override fun popularMangaParse(response: Response, page: MangasPage) {
|
||||||
put("from now", 1)
|
val document = response.asJsoup()
|
||||||
put("ago", -1)
|
for (element in document.select(popularMangaSelector())) {
|
||||||
|
Manga.create(id).apply {
|
||||||
|
popularMangaFromElement(element, this)
|
||||||
|
page.mangas.add(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page.nextPageUrl = page.url
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaInitialUrl() = "$baseUrl/search_result.php?Action=Yes&order=popularity&numResultPerPage=20&sort=desc"
|
|
||||||
|
|
||||||
override fun popularMangaSelector() = "div.well > table > tbody > tr"
|
|
||||||
|
|
||||||
override fun popularMangaFromElement(element: Element, manga: Manga) {
|
override fun popularMangaFromElement(element: Element, manga: Manga) {
|
||||||
element.select("td > h2 > a").first().let {
|
element.select("a.resultLink").first().let {
|
||||||
manga.setUrlWithoutDomain("/${it.attr("href")}")
|
manga.setUrlWithoutDomain(it.attr("href"))
|
||||||
manga.title = it.text()
|
manga.title = it.text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaNextPageSelector() = "ul.pagination > li > a:contains(Next)"
|
// Not used, overrides parent.
|
||||||
|
override fun popularMangaNextPageSelector() = ""
|
||||||
|
|
||||||
override fun searchMangaInitialUrl(query: String, filters: List<Filter>) =
|
override fun searchMangaInitialUrl(query: String, filters: List<Filter>) =
|
||||||
"$baseUrl/advanced-search/result.php?sortBy=alphabet&direction=ASC&textOnly=no&resPerPage=20&page=1&seriesName=$query&${filters.map { it.id + "=Yes" }.joinToString("&")}"
|
"$baseUrl/search/request.php?sortBy=popularity&sortOrder=descending&keyword=$query&genre=${filters.map { it.id }.joinToString(",")}"
|
||||||
|
|
||||||
override fun searchMangaSelector() = "div.row > div > div > div > h1"
|
override fun searchMangaSelector() = "div.searchResults > div.requested > div.row"
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: MangasPage, query: String, filters: List<Filter>): Request {
|
||||||
|
if (page.page == 1) {
|
||||||
|
page.url = searchMangaInitialUrl(query, filters)
|
||||||
|
}
|
||||||
|
val (body, requestUrl) = convertQueryToPost(page)
|
||||||
|
return POST(requestUrl, headers, body.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertQueryToPost(page: MangasPage): Pair<FormBody.Builder, String> {
|
||||||
|
val url = HttpUrl.parse(page.url)
|
||||||
|
val body = FormBody.Builder().add("page", page.page.toString())
|
||||||
|
for (i in 0..url.querySize() - 1) {
|
||||||
|
body.add(url.queryParameterName(i), url.queryParameterValue(i))
|
||||||
|
}
|
||||||
|
val requestUrl = url.scheme() + "://" + url.host() + url.encodedPath()
|
||||||
|
return Pair(body, requestUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response, page: MangasPage, query: String, filters: List<Filter>) {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
for (element in document.select(popularMangaSelector())) {
|
||||||
|
Manga.create(id).apply {
|
||||||
|
popularMangaFromElement(element, this)
|
||||||
|
page.mangas.add(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page.nextPageUrl = page.url
|
||||||
|
}
|
||||||
|
|
||||||
override fun searchMangaFromElement(element: Element, manga: Manga) {
|
override fun searchMangaFromElement(element: Element, manga: Manga) {
|
||||||
element.select("a").first().let {
|
element.select("a.resultLink").first().let {
|
||||||
manga.setUrlWithoutDomain("/${it.attr("href")}")
|
manga.setUrlWithoutDomain(it.attr("href"))
|
||||||
manga.title = it.text()
|
manga.title = it.text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaNextPageSelector() = "ul.pagination > li > a:contains(Next)"
|
// Not used, overrides parent.
|
||||||
|
override fun searchMangaNextPageSelector() = ""
|
||||||
|
|
||||||
override fun mangaDetailsParse(document: Document, manga: Manga) {
|
override fun mangaDetailsParse(document: Document, manga: Manga) {
|
||||||
val detailElement = document.select("div.well > div.row").first()
|
val detailElement = document.select("div.well > div.row").first()
|
||||||
|
|
||||||
manga.author = detailElement.select("a[href^=../search_result.php?author_name=]").first()?.text()
|
manga.author = detailElement.select("a[href^=/search/?author=]").first()?.text()
|
||||||
manga.genre = detailElement.select("div > div.row > div:has(b:contains(Genre:)) > a").map { it.text() }.joinToString()
|
manga.genre = detailElement.select("span.details > div.row > div:has(b:contains(Genre(s))) > a").map { it.text() }.joinToString()
|
||||||
manga.description = detailElement.select("strong:contains(Description:) + div").first()?.text()
|
manga.description = detailElement.select("strong:contains(Description:) + div").first()?.text()
|
||||||
manga.status = detailElement.select("div > div.row > div:has(b:contains(Scanlation Status:))").first()?.text().orEmpty().let { parseStatus(it) }
|
manga.status = detailElement.select("a[href^=/search/?status=]").first()?.text().orEmpty().let { parseStatus(it) }
|
||||||
manga.thumbnail_url = detailElement.select("div > img").first()?.absUrl("src")
|
manga.thumbnail_url = detailElement.select("div > img").first()?.absUrl("src")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseStatus(status: String) = when {
|
private fun parseStatus(status: String) = when {
|
||||||
status.contains("Ongoing") -> Manga.ONGOING
|
status.contains("Ongoing (Scan)") -> Manga.ONGOING
|
||||||
status.contains("Completed") -> Manga.COMPLETED
|
status.contains("Complete (Scan)") -> Manga.COMPLETED
|
||||||
else -> Manga.UNKNOWN
|
else -> Manga.UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListSelector() = "div.row > div > div.row > div > div.row:has(a.chapter_link[alt])"
|
override fun chapterListSelector() = "div.chapter-list > a"
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element, chapter: Chapter) {
|
override fun chapterFromElement(element: Element, chapter: Chapter) {
|
||||||
val urlElement = element.select("a").first()
|
val urlElement = element.select("a").first()
|
||||||
|
|
||||||
chapter.setUrlWithoutDomain("/${urlElement.attr("href")}")
|
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
||||||
chapter.name = urlElement.text()
|
chapter.name = element.select("span.chapterLabel").first().text()?.let { it } ?: ""
|
||||||
chapter.date_upload = element.select("span").first()?.text()?.let { parseChapterDate(it) } ?: 0
|
chapter.date_upload = element.select("time").first()?.attr("datetime")?.let { parseChapterDate(it) } ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseChapterDate(dateAsString: String): Long {
|
private fun parseChapterDate(dateAsString: String): Long {
|
||||||
val m = datePattern.matcher(dateAsString)
|
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(dateAsString).time
|
||||||
|
|
||||||
if (m.matches()) {
|
|
||||||
val amount = Integer.parseInt(m.group(1))
|
|
||||||
val unit = m.group(2)
|
|
||||||
val relation = m.group(3)
|
|
||||||
|
|
||||||
return Calendar.getInstance().apply {
|
|
||||||
add(dateFields[unit]!!, dateRelationFields[relation]!! * amount)
|
|
||||||
}.time.time
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListParse(response: Response, pages: MutableList<Page>) {
|
override fun pageListParse(response: Response, pages: MutableList<Page>) {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val url = response.request().url().toString().substringBeforeLast('/')
|
val url = response.request().url().toString().substringBeforeLast('/')
|
||||||
|
|
||||||
val series = document.select("input[name=series]").first().attr("value")
|
val series = document.select("input.IndexName").first().attr("value")
|
||||||
val chapter = document.select("input[name=chapter]").first().attr("value")
|
val chapter = document.select("span.CurChapter").first().text()
|
||||||
val index = document.select("input[name=index]").first().attr("value")
|
|
||||||
|
|
||||||
document.select("select[name=page] > option").forEach {
|
document.select("div.ContainerNav").first().select("select.PageSelect > option").forEach {
|
||||||
pages.add(Page(pages.size, "$url/?series=$series&chapter=$chapter&index=$index&page=${pages.size + 1}"))
|
pages.add(Page(pages.size, "$url/$series-chapter-$chapter-page-${pages.size + 1}.html"))
|
||||||
}
|
}
|
||||||
pages.getOrNull(0)?.imageUrl = imageUrlParse(document)
|
pages.getOrNull(0)?.imageUrl = imageUrlParse(document)
|
||||||
}
|
}
|
||||||
|
@ -128,7 +156,7 @@ class Mangasee(override val id: Int) : ParsedOnlineSource() {
|
||||||
override fun pageListParse(document: Document, pages: MutableList<Page>) {
|
override fun pageListParse(document: Document, pages: MutableList<Page>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document) = document.select("div > a > img").attr("src")
|
override fun imageUrlParse(document: Document): String = document.select("img.CurImage").attr("src")
|
||||||
|
|
||||||
// [...document.querySelectorAll("label.triStateCheckBox input")].map(el => `Filter("${el.getAttribute('name')}", "${el.nextSibling.textContent.trim()}")`).join(',\n')
|
// [...document.querySelectorAll("label.triStateCheckBox input")].map(el => `Filter("${el.getAttribute('name')}", "${el.nextSibling.textContent.trim()}")`).join(',\n')
|
||||||
// http://mangasee.co/advanced-search/
|
// http://mangasee.co/advanced-search/
|
||||||
|
@ -171,20 +199,45 @@ class Mangasee(override val id: Int) : ParsedOnlineSource() {
|
||||||
Filter("Yuri", "Yuri")
|
Filter("Yuri", "Yuri")
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun latestUpdatesInitialUrl(): String {
|
override fun latestUpdatesInitialUrl(): String = "http://mangaseeonline.net/home/latest.request.php"
|
||||||
throw UnsupportedOperationException("not implemented")
|
|
||||||
|
// Not used, overrides parent.
|
||||||
|
override fun latestUpdatesNextPageSelector(): String = ""
|
||||||
|
|
||||||
|
override fun latestUpdatesSelector(): String = "a.latestSeries"
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: MangasPage): Request {
|
||||||
|
if (page.page == 1) {
|
||||||
|
page.url = latestUpdatesInitialUrl()
|
||||||
|
}
|
||||||
|
val (body, requestUrl) = convertQueryToPost(page)
|
||||||
|
return POST(requestUrl, headers, body.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesNextPageSelector(): String {
|
override fun latestUpdatesParse(response: Response, page: MangasPage) {
|
||||||
throw UnsupportedOperationException("not implemented")
|
val document = response.asJsoup()
|
||||||
|
for (element in document.select(latestUpdatesSelector())) {
|
||||||
|
Manga.create(id).apply {
|
||||||
|
latestUpdatesFromElement(element, this)
|
||||||
|
page.mangas.add(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page.nextPageUrl = page.url
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesFromElement(element: Element, manga: Manga) {
|
override fun latestUpdatesFromElement(element: Element, manga: Manga) {
|
||||||
throw UnsupportedOperationException("not implemented")
|
element.select("a.latestSeries").first().let {
|
||||||
}
|
val chapterUrl = it.attr("href")
|
||||||
|
val indexOfMangaUrl = chapterUrl.indexOf("-chapter-")
|
||||||
override fun latestUpdatesSelector(): String {
|
val indexOfLastPath = chapterUrl.lastIndexOf("/")
|
||||||
throw UnsupportedOperationException("not implemented")
|
val mangaUrl = chapterUrl.substring(indexOfLastPath, indexOfMangaUrl)
|
||||||
|
val defaultText = it.select("p.clamp2").text();
|
||||||
|
val m = recentUpdatesPattern.matcher(defaultText)
|
||||||
|
val title = if (m.matches()) m.group(1) else defaultText
|
||||||
|
manga.setUrlWithoutDomain("/manga" + mangaUrl)
|
||||||
|
manga.title = title
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue