Manga-TR: Fix popular/latest, chapter list and page loading (#11620)

* Manga-TR: Fix popular manga parsing

* Manga-TR: Fix manga details parsing

* MangaTR: Fetch only manga

* MangaTR: Update chapter parsing

* MangaTR: update page list parsing

* MangaTR: bump version

* MangaTR: Fix manga detail description parsing

* MangaTR: Fix genre parsing
This commit is contained in:
Ata 2025-11-14 20:27:59 +03:00 committed by Draff
parent f17d40a1c1
commit cc885e7353
Signed by: Draff
GPG Key ID: E8A89F3211677653
2 changed files with 101 additions and 24 deletions

View File

@ -3,7 +3,7 @@ ext {
extClass = '.MangaTR' extClass = '.MangaTR'
themePkg = 'fmreader' themePkg = 'fmreader'
baseUrl = 'https://manga-tr.com' baseUrl = 'https://manga-tr.com'
overrideVersionCode = 5 overrideVersionCode = 6
isNsfw = true isNsfw = true
} }

View File

@ -38,11 +38,40 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
// ============================== Popular =============================== // ============================== Popular ===============================
override fun popularMangaNextPageSelector() = "div.btn-group:not(div.btn-block) button.btn-info" override fun popularMangaNextPageSelector() = "div.btn-group:not(div.btn-block) button.btn-info"
override fun popularMangaSelector() = "div.row a[data-toggle]" override fun popularMangaSelector() = "div.col-md-12 > span.thumbnail"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
setUrlWithoutDomain(element.absUrl("href")) val link = element.selectFirst("a.pull-left")!!
title = element.text() setUrlWithoutDomain(link.absUrl("href"))
title = element.selectFirst("h3.media-heading a")?.text()?.trim() ?: ""
thumbnail_url = link.selectFirst("img.media-object")?.absUrl("src")
}
override fun popularMangaRequest(page: Int): Request {
val url = "$baseUrl/$requestPath".toHttpUrl().newBuilder()
.addQueryParameter("sort", "views")
.addQueryParameter("sort_type", "DESC")
.addQueryParameter("page", page.toString())
.addQueryParameter("listType", "pagination")
.addQueryParameter("icerik", "1")
.build()
return GET(url, headers)
}
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request {
val url = "$baseUrl/$requestPath".toHttpUrl().newBuilder()
.addQueryParameter("sort", "last_update")
.addQueryParameter("sort_type", "DESC")
.addQueryParameter("page", page.toString())
.addQueryParameter("listType", "pagination")
.addQueryParameter("icerik", "1")
.build()
return GET(url, headers)
} }
// =============================== Search =============================== // =============================== Search ===============================
@ -147,25 +176,44 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
// =========================== Manga Details ============================ // =========================== Manga Details ============================
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
val infoElement = document.selectFirst("div#tab1")!! val infoElement = document.selectFirst("div.manga-meta-card")!!
infoElement.selectFirst("table + table tr + tr")?.run { title = document.selectFirst("div.info-body h1")?.text()?.trim() ?: ""
author = selectFirst("td:nth-child(1) a")?.text() author = infoElement.selectFirst("div.manga-meta-item:has(div.manga-meta-label:contains(Yazar)) div.manga-meta-value")
artist = selectFirst("td:nth-child(2) a")?.text() ?.select("a")
genre = selectFirst("td:nth-child(3)")?.text() ?.joinToString { it.text().trim() }
}
description = infoElement.selectFirst("div.well")?.ownText()?.trim() artist = infoElement.selectFirst("div.manga-meta-item:has(div.manga-meta-label:contains(Sanatçı)) div.manga-meta-value")
?.select("a")
?.joinToString { it.text().trim() }
val contentType = infoElement.selectFirst("div.manga-meta-item:has(div.manga-meta-label:contains(İçerik Türü)) div.manga-meta-value")
?.text()
?.trim()
val genres = infoElement.selectFirst("div.manga-meta-item:has(div.manga-meta-label:contains(Tür(ler))) div.manga-meta-value")
?.select("a")
?.joinToString { it.text().trim() }
genre = listOfNotNull(contentType, genres)
.filter { it.isNotEmpty() }
.joinToString(", ")
description = document.selectFirst("div.info-card div.info-desc")?.text()?.trim()
thumbnail_url = document.selectFirst("img.thumbnail")?.absUrl("src") thumbnail_url = document.selectFirst("img.thumbnail")?.absUrl("src")
status = infoElement.selectFirst("tr:contains(Çeviri Durumu) + tr > td:nth-child(2)") status = infoElement.selectFirst("div.manga-meta-item:has(div.manga-meta-label:contains(Çeviri Durumu)) div.manga-meta-value")
.let { parseStatus(it?.text()) } ?.text()
?.trim()
.let { parseStatus(it) }
} }
// ============================== Chapters ============================== // ============================== Chapters ==============================
override fun chapterListSelector() = "tr.table-bordered" override fun chapterListSelector() = "div.chapter-item"
override val chapterUrlSelector = "td[align=left] > a" override val chapterUrlSelector = "div.chapter-title a"
override val chapterTimeSelector = "td[align=right]" override val chapterTimeSelector = "div.stats"
private val chapterListHeaders by lazy { private val chapterListHeaders by lazy {
headersBuilder().add("X-Requested-With", "XMLHttpRequest").build() headersBuilder().add("X-Requested-With", "XMLHttpRequest").build()
@ -202,10 +250,19 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
return chapters return chapters
} }
override fun pageListRequest(chapter: SChapter) = override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
GET("$baseUrl/${chapter.url.substringAfter("cek/")}", headers) val link = element.selectFirst("div.chapter-title a")!!
setUrlWithoutDomain(link.attr("href"))
name = link.text().trim()
date_upload = parseRelativeDate(element.selectFirst("div.stats")?.ownText() ?: "")
}
override val pageListImageSelector = "div.chapter-content img.chapter-img" override fun pageListRequest(chapter: SChapter): Request {
val url = "$baseUrl/${chapter.url}"
return GET(url, headers)
}
override val pageListImageSelector = "div#chapter-images img.chapter-img"
// Manga-TR: image URL resolution with Base64-decoded data-src // Manga-TR: image URL resolution with Base64-decoded data-src
override fun getImgAttr(element: Element?): String? { override fun getImgAttr(element: Element?): String? {
@ -217,20 +274,40 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
val decodedBytes = Base64.decode(encodedUrl, Base64.DEFAULT) val decodedBytes = Base64.decode(encodedUrl, Base64.DEFAULT)
String(decodedBytes, StandardCharsets.UTF_8) String(decodedBytes, StandardCharsets.UTF_8)
} catch (e: Exception) { } catch (e: Exception) {
element.attr("abs:src") null
} }
} }
element.hasAttr("src") -> element.attr("abs:src") element.hasAttr("data-original") -> element.absUrl("data-original")
element.hasAttr("data-original") -> element.attr("abs:data-original")
else -> null else -> null
} }
} }
// Simple pageListParse - relies on the selector above // Simple pageListParse - relies on the selector above
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
return document.select(pageListImageSelector).mapIndexed { i, img -> val scripts = document.select("script:not([src])")
Page(i, imageUrl = getImgAttr(img))
for (script in scripts) {
val content = script.html()
val queueMatch = Regex("""var\s+imageQueue\s*=\s*\[(.*?)\]""", RegexOption.DOT_MATCHES_ALL)
.find(content) ?: continue
val encodedUrls = queueMatch.groupValues[1]
.split(",")
.map { it.trim().removeSurrounding("\"") }
.filter { it.isNotEmpty() && it != "LOGO" }
return encodedUrls.mapIndexedNotNull { index, encoded ->
try {
val decoded = String(Base64.decode(encoded, Base64.DEFAULT), StandardCharsets.UTF_8)
Page(index, imageUrl = decoded)
} catch (e: Exception) {
null
}
}
} }
return emptyList()
} }
// =========================== List Parse =========================== // =========================== List Parse ===========================