Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ar/mangadar/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {

keiyoushi {
name = "MangaDar"
versionCode = 1
versionCode = 2
contentWarning = ContentWarning.SAFE
libVersion = "1.6"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package eu.kanade.tachiyomi.extension.ar.mangadar

import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.Serializable
import okhttp3.HttpUrl.Companion.toHttpUrl

@Serializable
data class SearchResponse(
Expand All @@ -17,21 +15,3 @@ data class SearchMangaItemDto(
val url: String,
val cover: String,
)

@Serializable
data class ChapterDto(
val id: Int,
val number: String,
val title: String,
val url: String,
val date: String,
val timestamp: Long,
val ago: String,
) {
fun toSChapter(): SChapter = SChapter.create().apply {
name = this@ChapterDto.title
chapter_number = number.toFloatOrNull() ?: 0f
url = this@ChapterDto.url.toHttpUrl().encodedPath
date_upload = timestamp * 1000
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import keiyoushi.network.rateLimit
import keiyoushi.source.KeiSource
import keiyoushi.utils.firstInstanceOrNull
import keiyoushi.utils.parseAs
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.contentOrNull
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
Expand All @@ -23,7 +26,7 @@ import org.jsoup.nodes.Document
@Source
abstract class MangaDar : KeiSource() {

override val supportsLatest = false
override val supportsLatest = true

override fun OkHttpClient.Builder.configureClient() = rateLimit(2)

Expand All @@ -37,7 +40,16 @@ abstract class MangaDar : KeiSource() {

// ============================== Latest ===============================

override suspend fun getLatestUpdates(page: Int): MangasPage = getPopularManga(page)
override suspend fun getLatestUpdates(page: Int): MangasPage {
val urlBuilder = "$baseUrl/manga/".toHttpUrl().newBuilder()
.addQueryParameter("sort", "new")
if (page > 1) {
urlBuilder.addPathSegment("page")
urlBuilder.addPathSegment(page.toString())
}
val response = client.get(urlBuilder.build())
return parseMangaListPage(response)
}

// ============================== Search ===============================

Expand Down Expand Up @@ -147,8 +159,10 @@ abstract class MangaDar : KeiSource() {
private fun parseMangaListPage(response: Response): MangasPage {
val doc = response.asJsoup()
val mangas = doc.select("a[href*=/manga/]").mapNotNull { element ->
val slug = element.absUrl("href").toHttpUrl()
.pathSegments.last { it.isNotEmpty() }
val href = element.absUrl("href")
val segments = href.toHttpUrl().pathSegments.filter { it.isNotEmpty() }
if (segments.size != 2 || segments[0] != "manga") return@mapNotNull null
val slug = segments[1]
if (slug.isBlank() || slug == "page") return@mapNotNull null
val title = element.select("img").attr("alt")
.ifBlank { element.select("h3, h4, .font-semibold").text() }
Expand Down Expand Up @@ -200,23 +214,44 @@ abstract class MangaDar : KeiSource() {
}

private fun parseChaptersFromDoc(doc: Document): List<SChapter> {
val chaptersContainer = doc.selectFirst("div[x-data]:containsData(chapters)")
if (chaptersContainer != null) {
val xData = chaptersContainer.attr("x-data")
val chaptersStart = xData.indexOf("chapters:")
if (chaptersStart != -1) {
val jsonStart = xData.indexOf("[", chaptersStart)
val jsonEnd = findMatchingBracket(xData, jsonStart)
if (jsonStart != -1 && jsonEnd != -1) {
val chaptersJson = xData.substring(jsonStart, jsonEnd + 1)
val chapters = chaptersJson.parseAs<List<ChapterDto>>()
return chapters.map { it.toSChapter() }
val containers = doc.select("div[x-data]")
for (container in containers) {
val xData = container.attr("x-data")
val rowsStart = xData.indexOf("rows:")
if (rowsStart != -1) {
val bracketStart = xData.indexOf("[", rowsStart)
val bracketEnd = findMatchingBracket(xData, bracketStart)
if (bracketStart != -1 && bracketEnd != -1) {
val rowsJson = xData.substring(bracketStart, bracketEnd + 1)
return parseRowsJson(rowsJson)
}
}
}
return emptyList()
}

private fun parseRowsJson(rowsJson: String): List<SChapter> {
// Parse the compact array format: [[id, number, url, timestamp, num], ...]
val rows = rowsJson.parseAs<JsonArray>()
return rows.mapNotNull { element ->
val row = element as? JsonArray ?: return@mapNotNull null
if (row.size < 4) return@mapNotNull null

val chapterNumber = (row[1] as? JsonPrimitive)?.contentOrNull.orEmpty()
val chapterUrl = (row[2] as? JsonPrimitive)?.contentOrNull.orEmpty()
val timestamp = (row[3] as? JsonPrimitive)?.contentOrNull?.toLongOrNull() ?: 0L

if (chapterUrl.isBlank()) return@mapNotNull null

SChapter.create().apply {
this.url = chapterUrl.removePrefix(baseUrl)
this.name = "الفصل $chapterNumber"
chapter_number = chapterNumber.toFloatOrNull() ?: 0f
date_upload = timestamp * 1000
}
}
}

private fun findMatchingBracket(str: String, startIndex: Int): Int {
if (startIndex < 0 || startIndex >= str.length) return -1
var depth = 0
Expand Down