From 5292a9ff0ae31b660f7ce28e33478a24f121c465 Mon Sep 17 00:00:00 2001 From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Date: Tue, 12 Mar 2024 16:19:43 +0500 Subject: [PATCH] Gmanga multisrc: Add Dilar & MangaTales (#1767) * gmanga multisrc * search payload and filters refactor * ratelimit * distinct * dynamic filters * dilar * gmanga multisrc: latest * gmanga multisrc: search & filter * gmanga multisrc: chapters & pages * small cleanup * remove obsolete preferences * small cleanup & arabic tl deepl * Dilar: filter paid chapters * GManga: use unencrypted alt api for chapters * abstract away sort of chapters and pages * remove chapters logic from multisrc class since all three have different logic * remove `this` --- lib-multisrc/gmanga/build.gradle.kts | 5 + .../tachiyomi/multisrc/gmanga/CryptoUtils.kt | 10 +- .../kanade/tachiyomi/multisrc/gmanga/Dto.kt | 147 ++++++++ .../tachiyomi/multisrc/gmanga/Filters.kt | 94 +++++ .../tachiyomi/multisrc/gmanga/Gmanga.kt | 297 +++++++++++++++ .../tachiyomi/multisrc/gmanga/PayLoadDto.kt | 40 ++ src/ar/dilar/build.gradle | 8 + src/ar/dilar/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3227 bytes src/ar/dilar/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1874 bytes src/ar/dilar/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3996 bytes .../dilar/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6942 bytes .../dilar/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9173 bytes .../tachiyomi/extension/ar/dilar/Dilar.kt | 26 ++ .../tachiyomi/extension/ar/dilar/Dto.kt | 36 ++ src/ar/gmanga/build.gradle | 3 +- .../tachiyomi/extension/ar/gmanga/Dto.kt | 33 ++ .../tachiyomi/extension/ar/gmanga/Gmanga.kt | 345 +++--------------- .../extension/ar/gmanga/GmangaFilters.kt | 291 --------------- .../extension/ar/gmanga/GmangaPreferences.kt | 81 ---- .../extension/ar/gmanga/dto/ChapterDto.kt | 13 - .../extension/ar/gmanga/dto/ChapterListDto.kt | 10 - .../extension/ar/gmanga/dto/ReleaseDto.kt | 15 - .../extension/ar/gmanga/dto/TableDto.kt | 61 ---- .../extension/ar/gmanga/dto/TeamDto.kt | 9 - src/ar/mangatales/build.gradle | 8 + .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3513 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2014 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 5041 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 8738 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 12267 bytes .../tachiyomi/extension/ar/mangatales/Dto.kt | 67 ++++ .../extension/ar/mangatales/MangaTales.kt | 50 +++ 32 files changed, 878 insertions(+), 771 deletions(-) create mode 100644 lib-multisrc/gmanga/build.gradle.kts rename src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaCryptoUtils.kt => lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/CryptoUtils.kt (81%) create mode 100644 lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Dto.kt create mode 100644 lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Filters.kt create mode 100644 lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Gmanga.kt create mode 100644 lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/PayLoadDto.kt create mode 100644 src/ar/dilar/build.gradle create mode 100644 src/ar/dilar/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/ar/dilar/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/ar/dilar/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/ar/dilar/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/ar/dilar/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dilar.kt create mode 100644 src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dto.kt create mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Dto.kt delete mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaFilters.kt delete mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaPreferences.kt delete mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterDto.kt delete mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterListDto.kt delete mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ReleaseDto.kt delete mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TableDto.kt delete mode 100644 src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TeamDto.kt create mode 100644 src/ar/mangatales/build.gradle create mode 100644 src/ar/mangatales/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/ar/mangatales/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/ar/mangatales/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/ar/mangatales/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/ar/mangatales/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/Dto.kt create mode 100644 src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/MangaTales.kt diff --git a/lib-multisrc/gmanga/build.gradle.kts b/lib-multisrc/gmanga/build.gradle.kts new file mode 100644 index 000000000..dc076cc37 --- /dev/null +++ b/lib-multisrc/gmanga/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("lib-multisrc") +} + +baseVersionCode = 1 diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaCryptoUtils.kt b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/CryptoUtils.kt similarity index 81% rename from src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaCryptoUtils.kt rename to lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/CryptoUtils.kt index ad20bf9f8..70642e109 100644 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaCryptoUtils.kt +++ b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/CryptoUtils.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga +package eu.kanade.tachiyomi.multisrc.gmanga import android.util.Base64 import java.security.MessageDigest @@ -14,7 +14,7 @@ fun decrypt(responseData: String): String { } private fun String.hexStringToByteArray(): ByteArray { - val len = this.length + val len = length val data = ByteArray(len / 2) var i = 0 while (i < len) { @@ -30,8 +30,8 @@ private fun String.hexStringToByteArray(): ByteArray { private fun String.sha256(): String { return MessageDigest .getInstance("SHA-256") - .digest(this.toByteArray()) - .fold("", { str, it -> str + "%02x".format(it) }) + .digest(toByteArray()) + .fold("") { str, it -> str + "%02x".format(it) } } private fun String.aesDecrypt(secretKey: ByteArray, ivString: String): String { @@ -40,6 +40,6 @@ private fun String.aesDecrypt(secretKey: ByteArray, ivString: String): String { val iv = IvParameterSpec(Base64.decode(ivString.toByteArray(Charsets.UTF_8), Base64.DEFAULT)) c.init(Cipher.DECRYPT_MODE, sk, iv) - val byteStr = Base64.decode(this.toByteArray(Charsets.UTF_8), Base64.DEFAULT) + val byteStr = Base64.decode(toByteArray(Charsets.UTF_8), Base64.DEFAULT) return String(c.doFinal(byteStr)) } diff --git a/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Dto.kt b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Dto.kt new file mode 100644 index 000000000..3a0943c62 --- /dev/null +++ b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Dto.kt @@ -0,0 +1,147 @@ +package eu.kanade.tachiyomi.multisrc.gmanga + +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +class EncryptedResponse(val data: String) + +@Serializable +class MangaDataAction(val mangaDataAction: T) + +@Serializable +class LatestChaptersDto( + val releases: List, +) + +@Serializable +class LatestReleaseDto( + val manga: BrowseManga, +) + +@Serializable +class SearchMangaDto( + val mangas: List, +) + +@Serializable +class BrowseManga( + private val id: Int, + private val title: String, + private val cover: String, +) { + fun toSManga(createThumbnail: (String, String) -> String) = SManga.create().apply { + url = "/mangas/$id" + title = this@BrowseManga.title + thumbnail_url = createThumbnail(id.toString(), cover) + } +} + +@Serializable +class FiltersDto( + val categoryTypes: List? = null, + val categories: List? = null, +) + +@Serializable +class FilterDto( + val name: String, + val id: Int, +) + +@Serializable +class MangaDetailsDto( + val mangaData: Manga, +) + +@Serializable +class Manga( + private val id: Int, + private val cover: String, + private val title: String, + private val summary: String? = null, + private val artists: List, + private val authors: List, + @SerialName("story_status") private val status: Int, + private val type: TypeDto, + private val categories: List, + @SerialName("translation_status") private val tlStatus: Int, + private val synonyms: String? = null, + @SerialName("arabic_title") private val arTitle: String? = null, + @SerialName("japanese") private val jpTitle: String? = null, + @SerialName("english") private val enTitle: String? = null, +) { + fun toSManga(createThumbnail: (String, String) -> String) = SManga.create().apply { + title = this@Manga.title + thumbnail_url = createThumbnail(id.toString(), cover) + artist = artists.joinToString { it.name } + author = authors.joinToString { it.name } + status = when (this@Manga.status) { + 2 -> SManga.ONGOING + 3 -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + genre = buildList { + add(type.title) + add(type.name) + categories.forEach { add(it.name) } + }.joinToString() + description = buildString { + summary.orEmpty() + .ifEmpty { "لم يتم اضافة قصة بعد" } + .also { append(it) } + + when (tlStatus) { + 0 -> "منتهية" + 1 -> "مستمرة" + 2 -> "متوقفة" + else -> "مجهول" + }.also { + append("\n\n") + append("حالة الترجمة") + append(":\n• ") + append(it) + } + + val titles = listOfNotNull(synonyms, arTitle, jpTitle, enTitle) + if (titles.isNotEmpty()) { + append("\n\n") + append("مسميّات أخرى") + append(":\n• ") + append(titles.joinToString("\n• ")) + } + } + } +} + +@Serializable +class NameDto(val name: String) + +@Serializable +class TypeDto( + val name: String, + val title: String, +) + +@Serializable +class ReaderDto( + val readerDataAction: ReaderData, +) + +@Serializable +class ReaderData( + val readerData: ReaderChapter, +) + +@Serializable +class ReaderChapter( + val release: ReaderPages, +) + +@Serializable +class ReaderPages( + @SerialName("webp_pages") val webpPages: List, + val pages: List, + @SerialName("storage_key") val key: String, +) diff --git a/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Filters.kt b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Filters.kt new file mode 100644 index 000000000..0dbc06204 --- /dev/null +++ b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Filters.kt @@ -0,0 +1,94 @@ +package eu.kanade.tachiyomi.multisrc.gmanga + +import eu.kanade.tachiyomi.source.model.Filter +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.Locale + +class TagFilterData( + private val id: String, + private val name: String, + private val state: Int = Filter.TriState.STATE_IGNORE, +) { + fun toTagFilter() = TagFilter(id, name, state) +} + +class TagFilter( + val id: String, + name: String, + state: Int = STATE_IGNORE, +) : Filter.TriState(name, state) + +abstract class ValidatingTextFilter(name: String) : Filter.Text(name) { + abstract fun isValid(): Boolean +} + +private val DATE_FITLER_FORMAT = SimpleDateFormat("yyyy/MM/dd", Locale.ENGLISH).apply { + isLenient = false +} + +private fun SimpleDateFormat.isValid(date: String): Boolean { + return try { + parse(date) + true + } catch (e: ParseException) { + false + } +} + +class DateFilter(val id: String, name: String) : ValidatingTextFilter("(yyyy/MM/dd) $name)") { + override fun isValid(): Boolean = DATE_FITLER_FORMAT.isValid(state) +} + +class IntFilter(val id: String, name: String) : ValidatingTextFilter(name) { + override fun isValid(): Boolean = state.toIntOrNull() != null +} + +class MangaTypeFilter(types: List) : Filter.Group( + "الأصل", + types.map { it.toTagFilter() }, +) + +class OneShotFilter : Filter.Group( + "ونشوت؟", + listOf( + TagFilter("oneshot", "نعم", TriState.STATE_EXCLUDE), + ), +) + +class StoryStatusFilter(status: List) : Filter.Group( + "حالة القصة", + status.map { it.toTagFilter() }, +) + +class TranslationStatusFilter(tlStatus: List) : Filter.Group( + "حالة الترجمة", + tlStatus.map { it.toTagFilter() }, +) + +class ChapterCountFilter : Filter.Group( + "عدد الفصول", + listOf( + IntFilter("min", "على الأقل"), + IntFilter("max", "على الأكثر"), + ), +) { + val min get() = state.first { it.id == "min" } + val max get() = state.first { it.id == "max" } +} + +class DateRangeFilter : Filter.Group( + "تاريخ النشر", + listOf( + DateFilter("start", "تاريخ النشر"), + DateFilter("end", "تاريخ الإنتهاء"), + ), +) { + val start get() = state.first { it.id == "start" } + val end get() = state.first { it.id == "end" } +} + +class CategoryFilter(categories: List) : Filter.Group( + "التصنيفات", + categories.map { it.toTagFilter() }, +) diff --git a/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Gmanga.kt b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Gmanga.kt new file mode 100644 index 000000000..77370ff80 --- /dev/null +++ b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/Gmanga.kt @@ -0,0 +1,297 @@ +package eu.kanade.tachiyomi.multisrc.gmanga + +import android.util.Log +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.await +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +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.online.HttpSource +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import uy.kohesive.injekt.injectLazy + +abstract class Gmanga( + override val name: String, + override val baseUrl: String, + final override val lang: String, + protected val cdnUrl: String = baseUrl, +) : HttpSource() { + + override val supportsLatest = true + + protected val json: Json by injectLazy() + + override val client = network.cloudflareClient + + override fun headersBuilder() = super.headersBuilder() + .set("Referer", "$baseUrl/") + + override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", getFilterList()) + override fun popularMangaParse(response: Response) = searchMangaParse(response) + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$baseUrl/api/releases?page=$page", headers) + } + + override fun latestUpdatesParse(response: Response): MangasPage { + val releases = response.parseAs().releases + + val entries = releases.map { it.manga.toSManga(::createThumbnail) } + .distinctBy { it.url } + + return MangasPage( + entries, + hasNextPage = (releases.size >= 30), + ) + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val filterList = if (filters.isEmpty()) getFilterList() else filters + + val mangaTypeFilter = filterList.findInstance()!! + val oneShotFilter = filterList.findInstance()!! + val storyStatusFilter = filterList.findInstance()!! + val translationStatusFilter = filterList.findInstance()!! + val chapterCountFilter = filterList.findInstance()!! + val dateRangeFilter = filterList.findInstance()!! + val categoryFilter = filterList.findInstance() ?: CategoryFilter(emptyList()) + + val body = SearchPayload( + oneshot = OneShot( + value = oneShotFilter.state.first().run { + when { + isIncluded() -> true + else -> false + } + }, + ), + title = query, + page = page, + mangaTypes = IncludeExclude( + include = mangaTypeFilter.state.filter { it.isIncluded() }.map { it.id }, + exclude = mangaTypeFilter.state.filter { it.isExcluded() }.map { it.id }, + ), + storyStatus = IncludeExclude( + include = storyStatusFilter.state.filter { it.isIncluded() }.map { it.id }, + exclude = storyStatusFilter.state.filter { it.isExcluded() }.map { it.id }, + ), + tlStatus = IncludeExclude( + include = translationStatusFilter.state.filter { it.isIncluded() }.map { it.id }, + exclude = translationStatusFilter.state.filter { it.isExcluded() }.map { it.id }, + ), + categories = IncludeExclude( + // always include null, maybe to avoid shifting index in the backend + include = listOf(null) + categoryFilter.state.filter { it.isIncluded() }.map { it.id }, + exclude = categoryFilter.state.filter { it.isExcluded() }.map { it.id }, + ), + chapters = MinMax( + min = chapterCountFilter.min.run { + when { + state == "" -> "" + isValid() -> state + else -> throw Exception("الحد الأدنى لعدد الفصول غير صالح") + } + }, + max = chapterCountFilter.max.run { + when { + state == "" -> "" + isValid() -> state + else -> throw Exception("الحد الأقصى لعدد الفصول غير صالح") + } + }, + ), + dates = StartEnd( + start = dateRangeFilter.start.run { + when { + state == "" -> "" + isValid() -> state + else -> throw Exception("تاريخ بداية غير صالح") + } + }, + end = dateRangeFilter.end.run { + when { + state == "" -> "" + isValid() -> state + else -> throw Exception("تاريخ نهاية غير صالح") + } + }, + ), + ).let(json::encodeToString).toRequestBody(MEDIA_TYPE) + + return POST("$baseUrl/api/mangas/search", headers, body) + } + + private var categories: List = emptyList() + private var filtersState = FilterState.Unfetched + private var filterAttempts = 0 + + private enum class FilterState { + Fetching, Fetched, Unfetched + } + + private suspend fun fetchFilters() { + if (filtersState == FilterState.Unfetched && filterAttempts < 3) { + filtersState = FilterState.Fetching + filterAttempts++ + + try { + categories = client.newCall(GET("$baseUrl/mangas/", headers)) + .await() + .asJsoup() + .select(".js-react-on-rails-component").html() + .parseAs() + .run { + categories ?: categoryTypes!!.flatMap { it.categories!! } + } + .map { TagFilterData(it.id.toString(), it.name) } + + filtersState = FilterState.Fetched + } catch (e: Exception) { + Log.e(name, e.stackTraceToString()) + filtersState = FilterState.Unfetched + } + } + } + + protected open fun getTypesFilter() = listOf( + TagFilterData("1", "يابانية", Filter.TriState.STATE_INCLUDE), + TagFilterData("2", "كورية", Filter.TriState.STATE_INCLUDE), + TagFilterData("3", "صينية", Filter.TriState.STATE_INCLUDE), + TagFilterData("4", "عربية", Filter.TriState.STATE_INCLUDE), + TagFilterData("5", "كوميك", Filter.TriState.STATE_INCLUDE), + TagFilterData("6", "هواة", Filter.TriState.STATE_INCLUDE), + TagFilterData("7", "إندونيسية", Filter.TriState.STATE_INCLUDE), + TagFilterData("8", "روسية", Filter.TriState.STATE_INCLUDE), + ) + + protected open fun getStatusFilter() = listOf( + TagFilterData("2", "مستمرة"), + TagFilterData("3", "منتهية"), + ) + + protected open fun getTranslationFilter() = listOf( + TagFilterData("0", "منتهية"), + TagFilterData("1", "مستمرة"), + TagFilterData("2", "متوقفة"), + TagFilterData("3", "غير مترجمة", Filter.TriState.STATE_EXCLUDE), + ) + + override fun getFilterList(): FilterList { + CoroutineScope(Dispatchers.IO).launch { fetchFilters() } + + val filters = mutableListOf>( + MangaTypeFilter(getTypesFilter()), + OneShotFilter(), + StoryStatusFilter(getStatusFilter()), + TranslationStatusFilter(getTranslationFilter()), + ChapterCountFilter(), + DateRangeFilter(), + ) + + filters += if (filtersState == FilterState.Fetched) { + listOf( + CategoryFilter(categories), + ) + } else { + listOf( + Filter.Separator(), + Filter.Header("اضغط على\"إعادة تعيين\"لمحاولة تحميل التصنيفات"), + ) + } + + return FilterList(filters) + } + + override fun searchMangaParse(response: Response): MangasPage { + val data = response.decryptAs() + return MangasPage( + data.mangas.map { it.toSManga(::createThumbnail) }, + hasNextPage = data.mangas.size == 50, + ) + } + + override fun mangaDetailsParse(response: Response): SManga { + return response.asJsoup() + .select(".js-react-on-rails-component").html() + .parseAs>() + .mangaDataAction.mangaData + .toSManga(::createThumbnail) + } + + abstract fun chaptersRequest(manga: SManga): Request + abstract fun chaptersParse(response: Response): List + + final override fun chapterListRequest(manga: SManga) = chaptersRequest(manga) + final override fun chapterListParse(response: Response) = chaptersParse(response).sortChapters() + + private fun List.sortChapters() = + sortedWith( + compareBy( + { -it.chapter_number }, + { -it.date_upload }, + ), + ) + + override fun pageListParse(response: Response): List { + val data = response.asJsoup() + .select(".js-react-on-rails-component").html() + .parseAs() + .readerDataAction.readerData.release + + val hasWebP = data.webpPages.isNotEmpty() + + val (pages, directory) = when { + hasWebP -> data.webpPages to "hq_webp" + else -> data.pages to "hq" + } + + return pages.sortedWith(pageSort).mapIndexed { index, pageUri -> + Page( + index = index, + imageUrl = "$cdnUrl/uploads/releases/${data.key}/$directory/$pageUri", + ) + } + } + + private val pageSort = + compareBy({ parseNumber(0, it) ?: Double.MAX_VALUE }, { parseNumber(1, it) }, { parseNumber(2, it) }) + + private fun parseNumber(index: Int, string: String): Double? = + Regex("\\d+").findAll(string).map { it.value }.toList().getOrNull(index)?.toDoubleOrNull() + + protected inline fun Response.decryptAs(): T = + decrypt(parseAs().data).parseAs() + + protected inline fun Response.parseAs(): T = body.string().parseAs() + + protected inline fun String.parseAs(): T = json.decodeFromString(this) + + protected inline fun Iterable<*>.findInstance() = find { it is T } as? T + + protected open fun createThumbnail(mangaId: String, cover: String): String { + val thumbnail = "large_${cover.substringBeforeLast(".")}.webp" + + return "$cdnUrl/uploads/manga/cover/$mangaId/$thumbnail" + } + + override fun imageUrlParse(response: Response): String = + throw UnsupportedOperationException() + + companion object { + private val MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() + } +} diff --git a/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/PayLoadDto.kt b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/PayLoadDto.kt new file mode 100644 index 000000000..9a346ab8b --- /dev/null +++ b/lib-multisrc/gmanga/src/eu/kanade/tachiyomi/multisrc/gmanga/PayLoadDto.kt @@ -0,0 +1,40 @@ +package eu.kanade.tachiyomi.multisrc.gmanga + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +class SearchPayload( + private val oneshot: OneShot, + private val title: String, + private val page: Int, + @SerialName("manga_types") private val mangaTypes: IncludeExclude, + @SerialName("story_status") private val storyStatus: IncludeExclude, + @SerialName("translation_status") val tlStatus: IncludeExclude, + private val categories: IncludeExclude, + private val chapters: MinMax, + private val dates: StartEnd, +) + +@Serializable +class OneShot( + private val value: Boolean, +) + +@Serializable +class IncludeExclude( + private val include: List, + private val exclude: List, +) + +@Serializable +class MinMax( + private val min: String, + private val max: String, +) + +@Serializable +class StartEnd( + private val start: String, + private val end: String, +) diff --git a/src/ar/dilar/build.gradle b/src/ar/dilar/build.gradle new file mode 100644 index 000000000..8d9d35146 --- /dev/null +++ b/src/ar/dilar/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'Dilar' + extClass = '.Dilar' + themePkg = 'gmanga' + overrideVersionCode = 0 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/ar/dilar/res/mipmap-hdpi/ic_launcher.png b/src/ar/dilar/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8a754798b7e9933bb54257f683947632d45fd329 GIT binary patch literal 3227 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 N002ovPDHLkV1m*yC?x;@ literal 0 HcmV?d00001 diff --git a/src/ar/dilar/res/mipmap-mdpi/ic_launcher.png b/src/ar/dilar/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..ab92db00446806300bd1566bdece615e8f3c69bc GIT binary patch literal 1874 zcmV-Y2d(&tP)3prp5M6= zc*;fcl;ekI1bnT*{xl#(aP*x1`cllec`x^Iz~PiYVPT+~jitd#;zF4VY0A5?C}c z1=b}@28C26NaTqiP}9}`J1WjXOLr#&OvW6q3WZ&1b77djs$UYK3H0=upr^MNOlAtl z7T1xY@sa(pU(9%#QYpys6zNoj#9%T)QC%ez-TDKJxF%^MToC|!r@RcqhX%QvE{cG` z)C0>)4_n+GfuetK93)&Q!0+@E;+En+K|>Uv#*<7841?5=VK6m322?V+Q&2ZrAHj~w zv(U_GBL=}2q`%^Fyk`Wgz&W*B5z*=K#X8LTy9^^v_ON(ZIcg%r1Oz(}`LCl5HeEYu zA%RQ17@Y`P#%DPtESkWwi-*{P6#iyKjRqNKCfvcZXJiVxo-SzY>V&%pN@wh1zgy`M zk=60f!HT#E6isXZR<%Ba&1EOiL@lE~pSBRj1xGrN@Q45{twd8O%3LXA;tX_sdkb7? zsE1Q`Yv6vTzK=;pWE{MknvI8GTVn0IaRrXwsbX_ys^eg1+8pl@$mKLZwtUzP0{orH z@lV{Tf&Dkjz|d=CkLE@tLjL$n8-Pt+9guhF2-Bn#HFPv{6$H!uZN`Y!01+UY4nzk% zoFS|MXDT9xeXslsv>Pp@%$Af)n2Yw#mz|ZxQ2MZr?X?yCU|w{RcL*%KsAUTU5Ag?W z#tK0bay8)cqg(I+f+Pu*4}}9WmtpAj>l=RN?%%Md%97+33?B=dQ!+d!(1~F**GmKl z&9?Gi;P=LQP77Hq>R9DYM=Pwoe2jga7#aniPFXPc1X#Fa;Yi}v{q$F+g>wv8R%ASE zpYTE-NJ^OVi@#yXk@Cq;*{i)rfFfK7Iu+l9YV}R9@#+bNe_XH{_Dr5HXkP8cZdhFM zJtG~73GZOWa+^8ytPR641==&SH6X(bZxvBaOeVCn#Nq??1a9gY;mxZ*vu9%hBUt=) zM{>@82UJdw{hyq@)@F+4w;$jZhG5G2KcWWsk;o$gWGY195HAY-g@xIMvJ(tIQeZd~ zOv`ZxKfH78G@i175!i>N17Fsm58OsyAOc6T@>q@Ju?Ai(Imp(aD8$bLR9S8$0%#75 zK)QPb`eT`M;Q+LACUkm^#wJRM)&LP8J0}ACOk9nzeG`@deH!qP0GaE8;&0h{VHjqQ zWxr;_RU83KJWDP<)_^U6he+TQi`(P_o@$`H`5vo@NYW!l!tO~gd659BfI{1mtW|xB zQW5r80}*J?o|(|sSZ-+lEoa((J#iZ3#ix0J0IGG#1&an;5ugpnpbywPq+1@%4I(Y( zl@`GRgPsxCKVvDosqsae32_8y1M&fT4ZO{%+*1NnMtpp|gn>^EQo|R|FLK~7n!w@< zn0PoRc1FPB1L07Z?zpKtTF_&1(_r{)z>1WF?#G#tqr6985s$!8o)1)`4_NbnSORp3 z*@5-Z<%Zh~a2nM>Qx`fdOGIga7B%99o`fz^bStw8h0zUVn2AyEV}c@31GY&?==7=f z+5Kk;6hEkCL+Qfw)$}D0Jv7kiRM7-n0bvBVQerF;7>EY7;Te51|ML!)lSM``n5 zx_YEb-h)Pf9FGVHX@JIEZ@CZ0>#N~Bp1So8OeMv()LD>)m;e4`Pzb!mX&@Fm4x3VD zQVE0Quo-&sKV*9Crf$3+w?du187eVRHSro+1a>%@%l3)0S>zFtK_$R%uh=$P5SEjc z3|&uiV^dj|Lg6ReRnd_gz4pz7-C&ytgqZLdlqH}=-FnwlCdVtNDDYvWt1vsSWyLPA1}R;%5Wl$4Z% zuXXzGd3y_?i*@jIm%gs9?zBdudB38fg3`6Y#;lyoBT{saBNL*OIAoA93N26VJRN5` z^irYEd}yMpWz>% literal 0 HcmV?d00001 diff --git a/src/ar/dilar/res/mipmap-xhdpi/ic_launcher.png b/src/ar/dilar/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..c42f39585729a832ae03a29a14570caeb26ac4d1 GIT binary patch literal 3996 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

@-G@8~gX~ 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 literal 0 HcmV?d00001 diff --git a/src/ar/dilar/res/mipmap-xxxhdpi/ic_launcher.png b/src/ar/dilar/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..c5333f03e21e18d4f39fa5adec60590846e117de GIT binary patch literal 9173 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 literal 0 HcmV?d00001 diff --git a/src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dilar.kt b/src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dilar.kt new file mode 100644 index 000000000..fe013a50d --- /dev/null +++ b/src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dilar.kt @@ -0,0 +1,26 @@ +package eu.kanade.tachiyomi.extension.ar.dilar + +import eu.kanade.tachiyomi.multisrc.gmanga.Gmanga +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Request +import okhttp3.Response + +class Dilar : Gmanga( + "Dilar", + "https://dilar.tube", + "ar", +) { + override fun chaptersRequest(manga: SManga): Request { + val mangaId = manga.url.substringAfterLast("/") + return GET("$baseUrl/api/mangas/$mangaId/releases", headers) + } + + override fun chaptersParse(response: Response): List { + val releases = response.parseAs().releases + .filterNot { it.isMonetized } + + return releases.map { it.toSChapter() } + } +} diff --git a/src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dto.kt b/src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dto.kt new file mode 100644 index 000000000..ef2bb5e7b --- /dev/null +++ b/src/ar/dilar/src/eu/kanade/tachiyomi/extension/ar/dilar/Dto.kt @@ -0,0 +1,36 @@ +package eu.kanade.tachiyomi.extension.ar.dilar + +import eu.kanade.tachiyomi.source.model.SChapter +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.float + +@Serializable +class ChapterListDto( + val releases: List, +) + +@Serializable +class ChapterRelease( + private val id: Int, + private val chapter: JsonPrimitive, + private val title: String, + @SerialName("team_name") private val teamName: String, + @SerialName("time_stamp") private val timestamp: Long, + + @SerialName("has_rev_link") private val hasRevLink: Boolean, + @SerialName("support_link") private val supportLink: String, +) { + val isMonetized get() = hasRevLink && supportLink.isNotEmpty() + + fun toSChapter() = SChapter.create().apply { + url = "/r/$id" + chapter_number = chapter.float + date_upload = timestamp * 1000 + scanlator = teamName + + val chapterName = title.let { if (it.trim() != "") " - $it" else "" } + name = "${chapter_number.let { if (it % 1 > 0) it else it.toInt() }}$chapterName" + } +} diff --git a/src/ar/gmanga/build.gradle b/src/ar/gmanga/build.gradle index b03647d68..be9a621e6 100644 --- a/src/ar/gmanga/build.gradle +++ b/src/ar/gmanga/build.gradle @@ -1,7 +1,8 @@ ext { extName = 'GMANGA' extClass = '.Gmanga' - extVersionCode = 13 + themePkg = 'gmanga' + overrideVersionCode = 13 } apply from: "$rootDir/common.gradle" diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Dto.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Dto.kt new file mode 100644 index 000000000..5c2cd71f1 --- /dev/null +++ b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Dto.kt @@ -0,0 +1,33 @@ +package eu.kanade.tachiyomi.extension.ar.gmanga + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonPrimitive + +@Serializable +class ChapterListResponse( + val releases: List, + val chapterizations: List, + val teams: List, +) + +@Serializable +class ChapterRelease( + val id: Int, + @SerialName("chapterization_id") val chapId: Int, + @SerialName("team_id") val teamId: Int, + val chapter: JsonPrimitive, + @SerialName("time_stamp") val timestamp: Long, +) + +@Serializable +class Chapterization( + val id: Int, + val title: String, +) + +@Serializable +class Team( + val id: Int, + val name: String, +) diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Gmanga.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Gmanga.kt index 838e4bc2a..ad0d19bd9 100644 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Gmanga.kt +++ b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/Gmanga.kt @@ -1,315 +1,90 @@ package eu.kanade.tachiyomi.extension.ar.gmanga -import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_CHAPTER_LISTING -import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_CHAPTER_LISTING_SHOW_ALL -import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_CHAPTER_LISTING_SHOW_POPULAR -import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_LASTETS_LISTING -import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_LASTETS_LISTING_SHOW_LASTETS_CHAPTER -import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_LASTETS_LISTING_SHOW_LASTETS_MANGA -import eu.kanade.tachiyomi.extension.ar.gmanga.dto.TableDto -import eu.kanade.tachiyomi.extension.ar.gmanga.dto.asChapterList +import android.app.Application +import eu.kanade.tachiyomi.multisrc.gmanga.BrowseManga +import eu.kanade.tachiyomi.multisrc.gmanga.Gmanga import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.ConfigurableSource -import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage -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.online.HttpSource -import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.buildJsonArray -import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.float import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import okhttp3.Headers -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response -import uy.kohesive.injekt.injectLazy -import java.text.SimpleDateFormat -import java.util.Locale +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get -class Gmanga : ConfigurableSource, HttpSource() { - - private val domain: String = "gmanga.org" - - override val baseUrl: String = "https://$domain" - - override val lang: String = "ar" - - override val name: String = "GMANGA" - - override val supportsLatest: Boolean = true - - private val json: Json by injectLazy() - - private val preferences = GmangaPreferences(id) - - override val client: OkHttpClient = network.client.newBuilder() +class Gmanga : Gmanga( + "GMANGA", + "https://gmanga.org", + "ar", + "https://media.gmanga.me", +) { + override val client = super.client.newBuilder() .rateLimit(4) .build() - private val parsedDatePattern: SimpleDateFormat = SimpleDateFormat( - "yyyy-MM-dd HH:mm:ss ZZZ zzz", - Locale.ENGLISH, - ) - private val formattedDatePattern: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) - override fun headersBuilder() = Headers.Builder().apply { - add("User-Agent", USER_AGENT) + init { + // remove obsolete preferences + Injekt.get().getSharedPreferences("source_$id", 0x0000).run { + if (contains("gmanga_chapter_listing")) { + edit().remove("gmanga_chapter_listing").apply() + } + if (contains("gmanga_last_listing")) { + edit().remove("gmanga_last_listing").apply() + } + } } - override fun setupPreferenceScreen(screen: PreferenceScreen) = - preferences.setupPreferenceScreen(screen) - - override fun chapterListRequest(manga: SManga): Request { - val mangaId = manga.url.substringAfterLast("/") - return GET("$baseUrl/api/mangas/$mangaId/releases", headers) - } - - override fun chapterListParse(response: Response): List { - val data = decryptResponse(response) - - val table = json.decodeFromJsonElement(data) - val chapterList = table.asChapterList() - - val releases = when (preferences.getString(PREF_CHAPTER_LISTING)) { - PREF_CHAPTER_LISTING_SHOW_POPULAR -> - chapterList.releases - .groupBy { release -> release.chapterizationId } - .mapNotNull { (_, releases) -> releases.maxByOrNull { it.views } } - PREF_CHAPTER_LISTING_SHOW_ALL -> chapterList.releases - else -> emptyList() + override fun latestUpdatesParse(response: Response): MangasPage { + val decMga = response.decryptAs() + val selectedManga = decMga["rows"]!!.jsonArray[0].jsonObject["rows"]!!.jsonArray + val manags = selectedManga.map { + json.decodeFromJsonElement(it.jsonArray[17]) } - return releases.map { release -> - SChapter.create().apply { - val chapter = chapterList.chapters.first { it.id == release.chapterizationId } - val team = chapterList.teams.firstOrNull { it.id == release.teamId } + val entries = manags.map { it.toSManga(::createThumbnail) } + .distinctBy { it.url } - url = "/r/${release.id}" - chapter_number = chapter.chapter - date_upload = release.timestamp * 1000 + return MangasPage( + entries, + hasNextPage = (manags.size >= 30), + ) + } + + override fun fetchChapterList(manga: SManga): Observable> { + return client.newCall(chapterListRequest(manga)) + .asObservable() // sites returns false 302 code + .map(::chapterListParse) + } + + override fun chaptersRequest(manga: SManga): Request { + val mangaId = manga.url.substringAfterLast("/") + return GET("https://api2.gmanga.me/api/mangas/$mangaId/releases", headers) + } + + override fun chaptersParse(response: Response): List { + val chapterList = response.parseAs() + + return chapterList.releases.map { + SChapter.create().apply { + val chapter = chapterList.chapterizations.first { chap -> chap.id == it.chapId } + val team = chapterList.teams.firstOrNull { team -> team.id == it.teamId } + + url = "/r/${it.id}" + chapter_number = it.chapter.float + date_upload = it.timestamp * 1000 scanlator = team?.name val chapterName = chapter.title.let { if (it.trim() != "") " - $it" else "" } name = "${chapter_number.let { if (it % 1 > 0) it else it.toInt() }}$chapterName" } - }.sortedWith(compareBy({ -it.chapter_number }, { -it.date_upload })) - } - - override fun imageUrlParse(response: Response): String = - throw UnsupportedOperationException() - - override fun latestUpdatesParse(response: Response): MangasPage { - val isLatest = when (preferences.getString(PREF_LASTETS_LISTING)) { - PREF_LASTETS_LISTING_SHOW_LASTETS_MANGA -> true - PREF_LASTETS_LISTING_SHOW_LASTETS_CHAPTER -> false - else -> true } - - val mangas = if (!isLatest) { - val decMga = decryptResponse(response) - val selectedManga = decMga["rows"]!!.jsonArray[0].jsonObject["rows"]!!.jsonArray - buildJsonArray { - for (i in 0 until selectedManga.size) { - add(selectedManga[i].jsonArray[17]) - } - } - } else { - val data = json.decodeFromString( - response.asJsoup().select(".js-react-on-rails-component").html(), - ) - data["mangaDataAction"]!!.jsonObject["newMangas"]!!.jsonArray - } - return MangasPage( - mangas.jsonArray.map { - SManga.create().apply { - url = "/mangas/${it.jsonObject["id"]!!.jsonPrimitive.content}" - title = it.jsonObject["title"]!!.jsonPrimitive.content - val thumbnail = "medium_${ - it.jsonObject["cover"]!!.jsonPrimitive.content.substringBeforeLast(".") - }.webp" - thumbnail_url = - "https://media.gmanga.me/uploads/manga/cover/${it.jsonObject["id"]!!.jsonPrimitive.content}/$thumbnail" - } - }, - (mangas.size >= 30) && !isLatest, - ) - } - - override fun latestUpdatesRequest(page: Int): Request { - val latestUrl = when (preferences.getString(PREF_LASTETS_LISTING)) { - PREF_LASTETS_LISTING_SHOW_LASTETS_MANGA -> "$baseUrl/mangas/latest" - PREF_LASTETS_LISTING_SHOW_LASTETS_CHAPTER -> "https://api.gmanga.me/api/releases?page=$page" - else -> "$baseUrl/mangas/latest" - } - return GET(latestUrl, headers) - } - - override fun mangaDetailsParse(response: Response): SManga { - val altNamePrefix = "مسميّات أخرى" - val translationStatusPrefix = "حالة الترجمة" - val startedDayPrefix = "تاريخ النشر" - val endedDayPrefix = "تاريخ الانتهاء" - val data = json.decodeFromString( - response.asJsoup().select(".js-react-on-rails-component").html(), - ) - val mangaData = data["mangaDataAction"]!!.jsonObject["mangaData"]!!.jsonObject - return SManga.create().apply { - description = - mangaData["summary"]!!.jsonPrimitive.contentOrNull?.ifEmpty { "لم يتم اضافة قصة بعد" } - artist = - mangaData["artists"]!!.jsonArray.joinToString(", ") { it.jsonObject["name"]!!.jsonPrimitive.content } - author = - mangaData["authors"]!!.jsonArray.joinToString(", ") { it.jsonObject["name"]!!.jsonPrimitive.content } - status = parseStatus(mangaData["story_status"].toString()) - genre = listOfNotNull( - mangaData["type"]!!.jsonObject["title"]!!.jsonPrimitive.content, - mangaData["type"]!!.jsonObject["name"]!!.jsonPrimitive.content, - mangaData["categories"]!!.jsonArray.joinToString(", ") { it.jsonObject["name"]!!.jsonPrimitive.content }, - ).joinToString(", ") - - parseTranslationStatus(mangaData["translation_status"].toString()).let { - description = "$description\n\n:$translationStatusPrefix ᗏ \n$it •" - } - var startedDate = - mangaData["s_date"]!!.jsonPrimitive.content.takeIf { it.isBlank().not() } - startedDate = if (startedDate.isNullOrBlank().not()) { - parsedDatePattern.parse(startedDate!!)?.let { formattedDatePattern.format(it) } - } else { - null - } - var endedDay = mangaData["e_date"]!!.jsonPrimitive.content.takeIf { it.isBlank().not() } - endedDay = if (endedDay.isNullOrBlank().not()) { - parsedDatePattern.parse(endedDay!!)?.let { formattedDatePattern.format(it) } - } else { - null - } - - val alternativeName = listOfNotNull( - mangaData["synonyms"]!!.jsonPrimitive.content.takeIf { it.isBlank().not() }, - mangaData["arabic_title"]!!.jsonPrimitive.content.takeIf { it.isBlank().not() }, - mangaData["japanese"]!!.jsonPrimitive.content.takeIf { it.isBlank().not() }, - mangaData["english"]!!.jsonPrimitive.content.takeIf { it.isBlank().not() }, - ).joinToString("\n").trim() - - val additionalInformation = listOfNotNull( - startedDate, - endedDay, - alternativeName, - ) - additionalInformation.forEach { info -> - when (info) { - startedDate -> - description = - "$description\n\n:$startedDayPrefix ᗏ \n$startedDate •" - endedDay -> description = "$description\n\n:$endedDayPrefix ᗏ \n$endedDay •" - alternativeName -> - description = - "$description\n\n:$altNamePrefix ᗏ \n$alternativeName •" - else -> description - } - } - } - } - - private fun parseStatus(status: String?) = when { - status == null -> SManga.UNKNOWN - status.contains("2") -> SManga.ONGOING - status.contains("3") -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - - private fun parseTranslationStatus(status: String?) = when { - status == null -> "مجهول" - status.contains("0") -> "منتهية" - status.contains("1") -> "مستمرة" - status.contains("2") -> "متوقفة" - else -> "مجهول" - } - - override fun pageListParse(response: Response): List { - val url = response.request.url.toString() - val data = json.decodeFromString( - response.asJsoup().select(".js-react-on-rails-component").html(), - ) - val releaseData = - data["readerDataAction"]!!.jsonObject["readerData"]!!.jsonObject["release"]!!.jsonObject - - val hasWebP = releaseData["webp_pages"]!!.jsonArray.size > 0 - return releaseData[if (hasWebP) "webp_pages" else "pages"]!!.jsonArray.map { it.jsonPrimitive.content } - .sortedWith(pageSort) - .mapIndexed { index, pageUri -> - Page( - index, - "$url#page_$index", - "https://media.gmanga.me/uploads/releases/${releaseData["storage_key"]!!.jsonPrimitive.content}/hq${if (hasWebP) "_webp" else ""}/$pageUri", - ) - } - } - - private val pageSort = - compareBy({ parseNumber(0, it) ?: Double.MAX_VALUE }, { parseNumber(1, it) }, { parseNumber(2, it) }) - - private fun parseNumber(index: Int, string: String): Double? = - Regex("\\d+").findAll(string).map { it.value }.toList().getOrNull(index)?.toDoubleOrNull() - - override fun popularMangaParse(response: Response) = searchMangaParse(response) - - override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", getFilterList()) - - override fun searchMangaParse(response: Response): MangasPage { - val data = decryptResponse(response) - val mangas = data["mangas"]!!.jsonArray - return MangasPage( - mangas.jsonArray.map { - SManga.create().apply { - url = "/mangas/${it.jsonObject["id"]!!.jsonPrimitive.content}" - title = it.jsonObject["title"]!!.jsonPrimitive.content - val thumbnail = "medium_${ - it.jsonObject["cover"]!!.jsonPrimitive.content.substringBeforeLast(".") - }.webp" - thumbnail_url = - "https://media.gmanga.me/uploads/manga/cover/${it.jsonObject["id"]!!.jsonPrimitive.content}/$thumbnail" - } - }, - mangas.size == 50, - ) - } - - private fun decryptResponse(response: Response): JsonObject { - val encryptedData = - json.decodeFromString(response.body.string())["data"]!!.jsonPrimitive.content - val decryptedData = decrypt(encryptedData) - return json.decodeFromString(decryptedData) - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return GmangaFilters.buildSearchPayload( - page, - query, - if (filters.isEmpty()) getFilterList() else filters, - ).let { - val body = it.toString().toRequestBody(MEDIA_TYPE) - POST("$baseUrl/api/mangas/search", headers, body) - } - } - - override fun getFilterList() = GmangaFilters.getFilterList() - - companion object { - private const val USER_AGENT = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36" - private val MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() } } diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaFilters.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaFilters.kt deleted file mode 100644 index 63983dcc5..000000000 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaFilters.kt +++ /dev/null @@ -1,291 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga - -import android.annotation.SuppressLint -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.source.model.FilterList -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonObjectBuilder -import kotlinx.serialization.json.add -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonArray -import kotlinx.serialization.json.putJsonObject -import java.text.ParseException -import java.text.SimpleDateFormat - -class GmangaFilters() { - - companion object { - - fun getFilterList() = FilterList( - MangaTypeFilter(), - OneShotFilter(), - StoryStatusFilter(), - TranslationStatusFilter(), - ChapterCountFilter(), - DateRangeFilter(), - CategoryFilter(), - ) - - fun buildSearchPayload(page: Int, query: String = "", filters: FilterList): JsonObject { - val mangaTypeFilter = filters.findInstance()!! - val oneShotFilter = filters.findInstance()!! - val storyStatusFilter = filters.findInstance()!! - val translationStatusFilter = filters.findInstance()!! - val chapterCountFilter = filters.findInstance()!! - val dateRangeFilter = filters.findInstance()!! - val categoryFilter = filters.findInstance()!! - - return buildJsonObject { - oneShotFilter.state.first().let { - putJsonObject("oneshot") { - when { - it.isIncluded() -> put("value", true) - it.isExcluded() -> put("value", false) - else -> put("value", JsonNull) - } - } - } - - put("title", query) - put("page", page) - putJsonObject("manga_types") { - putJsonArray("include") { - mangaTypeFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) } - } - - putJsonArray("exclude") { - mangaTypeFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) } - } - } - putJsonObject("story_status") { - putJsonArray("include") { - storyStatusFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) } - } - - putJsonArray("exclude") { - storyStatusFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) } - } - } - putJsonObject("translation_status") { - putJsonArray("include") { - translationStatusFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) } - } - - putJsonArray("exclude") { - translationStatusFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) } - } - } - putJsonObject("categories") { - putJsonArray("include") { - add(JsonNull) // always included, maybe to avoid shifting index in the backend - categoryFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) } - } - - putJsonArray("exclude") { - categoryFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) } - } - } - putJsonObject("chapters") { - putFromValidatingTextFilter( - chapterCountFilter.state.first { - it.id == FILTER_ID_MIN_CHAPTER_COUNT - }, - "min", - ERROR_INVALID_MIN_CHAPTER_COUNT, - "", - ) - - putFromValidatingTextFilter( - chapterCountFilter.state.first { - it.id == FILTER_ID_MAX_CHAPTER_COUNT - }, - "max", - ERROR_INVALID_MAX_CHAPTER_COUNT, - "", - ) - } - putJsonObject("dates") { - putFromValidatingTextFilter( - dateRangeFilter.state.first { - it.id == FILTER_ID_START_DATE - }, - "start", - ERROR_INVALID_START_DATE, - ) - - putFromValidatingTextFilter( - dateRangeFilter.state.first { - it.id == FILTER_ID_END_DATE - }, - "end", - ERROR_INVALID_END_DATE, - ) - } - } - } - - // filter IDs - private const val FILTER_ID_ONE_SHOT = "oneshot" - private const val FILTER_ID_START_DATE = "start" - private const val FILTER_ID_END_DATE = "end" - private const val FILTER_ID_MIN_CHAPTER_COUNT = "min" - private const val FILTER_ID_MAX_CHAPTER_COUNT = "max" - - // error messages - private const val ERROR_INVALID_START_DATE = "تاريخ بداية غير صالح" - private const val ERROR_INVALID_END_DATE = " تاريخ نهاية غير صالح" - private const val ERROR_INVALID_MIN_CHAPTER_COUNT = "الحد الأدنى لعدد الفصول غير صالح" - private const val ERROR_INVALID_MAX_CHAPTER_COUNT = "الحد الأقصى لعدد الفصول غير صالح" - - private class MangaTypeFilter() : Filter.Group( - "الأصل", - listOf( - TagFilter("1", "يابانية", TriState.STATE_INCLUDE), - TagFilter("2", "كورية", TriState.STATE_INCLUDE), - TagFilter("3", "صينية", TriState.STATE_INCLUDE), - TagFilter("4", "عربية", TriState.STATE_INCLUDE), - TagFilter("5", "كوميك", TriState.STATE_INCLUDE), - TagFilter("6", "هواة", TriState.STATE_INCLUDE), - TagFilter("7", "إندونيسية", TriState.STATE_INCLUDE), - TagFilter("8", "روسية", TriState.STATE_INCLUDE), - ), - ) - - private class OneShotFilter() : Filter.Group( - "ونشوت؟", - listOf( - TagFilter(FILTER_ID_ONE_SHOT, "نعم", TriState.STATE_EXCLUDE), - ), - ) - - private class StoryStatusFilter() : Filter.Group( - "حالة القصة", - listOf( - TagFilter("2", "مستمرة"), - TagFilter("3", "منتهية"), - ), - ) - - private class TranslationStatusFilter() : Filter.Group( - "حالة الترجمة", - listOf( - TagFilter("0", "منتهية"), - TagFilter("1", "مستمرة"), - TagFilter("2", "متوقفة"), - TagFilter("3", "غير مترجمة", TriState.STATE_EXCLUDE), - ), - ) - - private class ChapterCountFilter() : Filter.Group( - "عدد الفصول", - listOf( - IntFilter(FILTER_ID_MIN_CHAPTER_COUNT, "على الأقل"), - IntFilter(FILTER_ID_MAX_CHAPTER_COUNT, "على الأكثر"), - ), - ) - - private class DateRangeFilter() : Filter.Group( - "تاريخ النشر", - listOf( - DateFilter(FILTER_ID_START_DATE, "تاريخ النشر"), - DateFilter(FILTER_ID_END_DATE, "تاريخ الإنتهاء"), - ), - ) - - private class CategoryFilter() : Filter.Group( - "التصنيفات", - listOf( - TagFilter("1", "إثارة"), - TagFilter("2", "أكشن"), - TagFilter("3", "الحياة المدرسية"), - TagFilter("4", "الحياة اليومية"), - TagFilter("5", "آليات"), - TagFilter("6", "تاريخي"), - TagFilter("7", "تراجيدي"), - TagFilter("8", "جوسيه"), - TagFilter("9", "حربي"), - TagFilter("10", "خيال"), - TagFilter("11", "خيال علمي"), - TagFilter("12", "دراما"), - TagFilter("13", "رعب"), - TagFilter("14", "رومانسي"), - TagFilter("15", "رياضة"), - TagFilter("16", "ساموراي"), - TagFilter("17", "سحر"), - TagFilter("18", "سينين"), - TagFilter("19", "شوجو"), - TagFilter("20", "شونين"), - TagFilter("21", "عنف"), - TagFilter("22", "غموض"), - TagFilter("23", "فنون قتال"), - TagFilter("24", "قوى خارقة"), - TagFilter("25", "كوميدي"), - TagFilter("26", "لعبة"), - TagFilter("27", "مسابقة"), - TagFilter("28", "مصاصي الدماء"), - TagFilter("29", "مغامرات"), - TagFilter("30", "موسيقى"), - TagFilter("31", "نفسي"), - TagFilter("32", "نينجا"), - TagFilter("33", "وحوش"), - TagFilter("34", "حريم"), - TagFilter("35", "راشد"), - TagFilter("38", "ويب-تون"), - TagFilter("39", "زمنكاني"), - ), - ) - - private const val DATE_FILTER_PATTERN = "yyyy/MM/dd" - - @SuppressLint("SimpleDateFormat") - private val DATE_FITLER_FORMAT = SimpleDateFormat(DATE_FILTER_PATTERN).apply { - isLenient = false - } - - private fun SimpleDateFormat.isValid(date: String): Boolean { - return try { - this.parse(date) - true - } catch (e: ParseException) { - false - } - } - - private fun JsonObjectBuilder.putFromValidatingTextFilter( - filter: ValidatingTextFilter, - property: String, - invalidErrorMessage: String, - default: String? = null, - ) { - filter.let { - when { - it.state == "" -> if (default == null) { - put(property, JsonNull) - } else { - put(property, default) - } - it.isValid() -> put(property, it.state) - else -> throw Exception(invalidErrorMessage) - } - } - } - - private inline fun Iterable<*>.findInstance() = find { it is T } as? T - - private class TagFilter(val id: String, name: String, state: Int = STATE_IGNORE) : Filter.TriState(name, state) - - private abstract class ValidatingTextFilter(name: String) : Filter.Text(name) { - abstract fun isValid(): Boolean - } - - private class DateFilter(val id: String, name: String) : ValidatingTextFilter("($DATE_FILTER_PATTERN) $name)") { - override fun isValid(): Boolean = DATE_FITLER_FORMAT.isValid(this.state) - } - - private class IntFilter(val id: String, name: String) : ValidatingTextFilter(name) { - override fun isValid(): Boolean = state.toIntOrNull() != null - } - } -} diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaPreferences.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaPreferences.kt deleted file mode 100644 index 1e17d1cea..000000000 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/GmangaPreferences.kt +++ /dev/null @@ -1,81 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga - -import android.app.Application -import android.content.SharedPreferences -import androidx.preference.ListPreference -import androidx.preference.PreferenceScreen -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get - -class GmangaPreferences(id: Long) { - - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - - fun setupPreferenceScreen(screen: PreferenceScreen) { - STRING_PREFERENCES.forEach { - val preference = ListPreference(screen.context).apply { - key = it.key - title = it.title - entries = it.entries() - entryValues = it.entryValues() - summary = "%s" - } - - if (!preferences.contains(it.key)) { - preferences.edit().putString(it.key, it.default().key).apply() - } - - screen.addPreference(preference) - } - } - - fun getString(pref: StringPreference): String { - return preferences.getString(pref.key, pref.default().key)!! - } - - companion object { - - class StringPreferenceOption(val key: String, val title: String) - - class StringPreference( - val key: String, - val title: String, - private val options: List, - private val defaultOptionIndex: Int = 0, - ) { - fun entries(): Array = options.map { it.title }.toTypedArray() - fun entryValues(): Array = options.map { it.key }.toTypedArray() - fun default(): StringPreferenceOption = options[defaultOptionIndex] - } - - // preferences - const val PREF_CHAPTER_LISTING_SHOW_ALL = "gmanga_gmanga_chapter_listing_show_all" - const val PREF_CHAPTER_LISTING_SHOW_POPULAR = "gmanga_chapter_listing_most_viewed" - const val PREF_LASTETS_LISTING_SHOW_LASTETS_CHAPTER = "gmanga_Last_listing_last_chapter_added" - const val PREF_LASTETS_LISTING_SHOW_LASTETS_MANGA = "gmanga_chapter_listing_last_manga_added" - - val PREF_CHAPTER_LISTING = StringPreference( - "gmanga_chapter_listing", - "كيفية عرض الفصل بقائمة الفصول", - listOf( - StringPreferenceOption(PREF_CHAPTER_LISTING_SHOW_POPULAR, "اختيار النسخة الأكثر مشاهدة"), - StringPreferenceOption(PREF_CHAPTER_LISTING_SHOW_ALL, "عرض جميع النسخ"), - ), - ) - val PREF_LASTETS_LISTING = StringPreference( - "gmanga_last_listing", - "كيفية عرض بقائمة الأعمال الجديدة ", - listOf( - StringPreferenceOption(PREF_LASTETS_LISTING_SHOW_LASTETS_CHAPTER, "اختيار آخر الإضافات"), - StringPreferenceOption(PREF_LASTETS_LISTING_SHOW_LASTETS_MANGA, "اختيار لمانجات الجديدة"), - ), - ) - - private val STRING_PREFERENCES = listOf( - PREF_CHAPTER_LISTING, - PREF_LASTETS_LISTING, - ) - } -} diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterDto.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterDto.kt deleted file mode 100644 index 64e536fc4..000000000 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterDto.kt +++ /dev/null @@ -1,13 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga.dto - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class ChapterDto( - val id: Int, - val chapter: Float, - val volume: Int, - val title: String, - @SerialName("time_stamp") val timestamp: Long, -) diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterListDto.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterListDto.kt deleted file mode 100644 index 4be58257f..000000000 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ChapterListDto.kt +++ /dev/null @@ -1,10 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga.dto - -import kotlinx.serialization.Serializable - -@Serializable -data class ChapterListDto( - val releases: List, - val teams: List, - val chapters: List, -) diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ReleaseDto.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ReleaseDto.kt deleted file mode 100644 index d200c8a92..000000000 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/ReleaseDto.kt +++ /dev/null @@ -1,15 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga.dto - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class ReleaseDto( - val id: Int, - @SerialName("created_at") val createdAt: String, - @SerialName("timestamp") val timestamp: Long, - val views: Int, - @SerialName("chapterization_id") val chapterizationId: Int, - @SerialName("team_id") val teamId: Int, - val teams: List, -) diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TableDto.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TableDto.kt deleted file mode 100644 index 5ea91e079..000000000 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TableDto.kt +++ /dev/null @@ -1,61 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga.dto - -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.decodeFromJsonElement -import kotlinx.serialization.json.float -import kotlinx.serialization.json.int -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.long -import uy.kohesive.injekt.injectLazy - -@Serializable -data class TableDto( - val cols: List, - val rows: List, - val isCompact: Boolean, - val maxLevel: Int, - val isArray: Boolean? = null, - val isObject: Boolean? = null, -) - -private val json: Json by injectLazy() - -private fun TableDto.get(key: String): TableDto? { - isObject ?: return null - - val index = cols.indexOf(key) - return json.decodeFromJsonElement(rows[index]) -} - -fun TableDto.asChapterList() = ChapterListDto( - // YOLO - get("releases")!!.rows.map { - ReleaseDto( - it.jsonArray[0].jsonPrimitive.int, - it.jsonArray[1].jsonPrimitive.content, - it.jsonArray[2].jsonPrimitive.long, - it.jsonArray[3].jsonPrimitive.int, - it.jsonArray[4].jsonPrimitive.int, - it.jsonArray[5].jsonPrimitive.int, - it.jsonArray[6].jsonArray.map { it.jsonPrimitive.int }, - ) - }, - get("teams")!!.rows.map { - TeamDto( - it.jsonArray[0].jsonPrimitive.int, - it.jsonArray[1].jsonPrimitive.content, - ) - }, - get("chapterizations")!!.rows.map { - ChapterDto( - it.jsonArray[0].jsonPrimitive.int, - it.jsonArray[1].jsonPrimitive.float, - it.jsonArray[2].jsonPrimitive.int, - it.jsonArray[3].jsonPrimitive.content, - it.jsonArray[4].jsonPrimitive.long, - ) - }, -) diff --git a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TeamDto.kt b/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TeamDto.kt deleted file mode 100644 index 0942c64a9..000000000 --- a/src/ar/gmanga/src/eu/kanade/tachiyomi/extension/ar/gmanga/dto/TeamDto.kt +++ /dev/null @@ -1,9 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.gmanga.dto - -import kotlinx.serialization.Serializable - -@Serializable -data class TeamDto( - val id: Int, - val name: String, -) diff --git a/src/ar/mangatales/build.gradle b/src/ar/mangatales/build.gradle new file mode 100644 index 000000000..186d336a7 --- /dev/null +++ b/src/ar/mangatales/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'Manga Tales' + extClass = '.MangaTales' + themePkg = 'gmanga' + overrideVersionCode = 0 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/ar/mangatales/res/mipmap-hdpi/ic_launcher.png b/src/ar/mangatales/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1af117696143f227fe6e3544052a60de6ef02e GIT binary patch literal 3513 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 literal 0 HcmV?d00001 diff --git a/src/ar/mangatales/res/mipmap-mdpi/ic_launcher.png b/src/ar/mangatales/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a32b489e0205e859df0fc0fbc0c17dca740ab4e9 GIT binary patch literal 2014 zcmV<42O;>0P)gKV^BpVX zt8zlK9MJIPi z|M(F7$Nk6u0RHU^TFZa~Imdj@d~%OBXnNB_hoC15y3WVU7$<<7oE%wtdip9yN5^E5 zNaVpvF=}`Sr=v9v9BcSQt$nZ8>l@T+U4C} zSgj^?^$7-30(`Cds|FHBF?>dN^{S5gBm<@vvy*nB^=>;df6ZW0 z%z

{RlBRuGQ-i89xfiD_<7Cee-Da34ogGcd&6OOT?jjIC&t<&>(A7PS@=9~N7Vn4RNCjrD zkB4`#7q(^W#gYSQoqRS3(N2z|+t1*q&yK+FNpHlx{Twzf{1MZZ#UeI2g5zK-p!YJG z?cK?E`T4pS=lJnS8 z#s~W|sQ*h0Yx&OD967QJfSwNf@;Q4@eWe!jf1V9@e>W)4-az?DB__T-o^5mLHZ^v= ze+ZWjmm%)GXhhG6z`oUmP+hG>_~cNCDbTmd>QGnJKn2|nb^CpY`#C`BCB@(oo*4R+ z4?@Nc$G|}{PKwz9KnK<3T5M1J0b}PsiwTROSOXQsf1|GQ4kBkfjRsW{au@x~IyP&~ z6xNxW)c22XF2RlSDm2x#Ku;aZh3dgK>~Z*w@+POf;O66s{;tlQMC31%8x6d)9LWoNN5_+4|o9_x~Faj)Y(;xl6qJ!_0@ z86YW608G&_-NiI2%pBXX?^E%D}2WdK}~f7(sB|YAxD|bFDL$cbkOJG zf>g*n%v-6NoM(okbHBc>fo^@4n#ZZ^Vw}n;!SL}z@yZvIk(06;udj&3uyB7?_`pYp zaVqx$k~bv~)dD#kPZSV;6M10N^NSq{SZET0st-qmG zh&t=1?m+0IU?gTuwW7)TSGEJ-*=KZGJ$A(9B5v(uc+u6MZ-Ugj2W|5$IlO44oIi zN=hg_ws#?`f$m0XD`s8*3?u1r@95~b=H%o=joVgJmV#{!p@r`{r`2k83;8sRG&j)0 z%(8>)4UMZpq4+vDBqW9RG+`4d1PF0pDJv_>4+#l*lcbyIvG|6D(e4}&5a5@epZ{@C zP|y^5t;2o87T`x;ETPw3E!Qh652dH3eo$IkT17fLjP-K;oXpHI*Cl4}XMXhOz8>|3 zcfxdNXjyY7b=r;T!$qNPYK23BbM%B!U0f1o$#hj1O>~Je*Mu6wiXQX)s)DP;j`Aybq*w62k$9xt?~`|j|1QFR025RiUeExiA^-pY07*qoM6N<$f`MSn4gdfE literal 0 HcmV?d00001 diff --git a/src/ar/mangatales/res/mipmap-xhdpi/ic_launcher.png b/src/ar/mangatales/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9791404eb8151126d7b5ce5e0fa48a98827e74e0 GIT binary patch literal 5041 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 M07*qoM6N<$f`6HmOaK4? literal 0 HcmV?d00001 diff --git a/src/ar/mangatales/res/mipmap-xxxhdpi/ic_launcher.png b/src/ar/mangatales/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..673aaa7759811a10693f060a053a124b07245f6e GIT binary patch literal 12267 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 AL;wH) literal 0 HcmV?d00001 diff --git a/src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/Dto.kt b/src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/Dto.kt new file mode 100644 index 000000000..3fedac59a --- /dev/null +++ b/src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/Dto.kt @@ -0,0 +1,67 @@ +package eu.kanade.tachiyomi.extension.ar.mangatales + +import eu.kanade.tachiyomi.source.model.SChapter +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.float +import java.text.SimpleDateFormat +import java.util.Locale + +@Serializable +class ChapterListDto( + val mangaReleases: List, +) + +@Serializable +class ChapterRelease( + private val id: Int, + private val chapter: JsonPrimitive, + private val title: String, + @SerialName("team_name") private val teamName: String, + @SerialName("created_at") private val createdAt: String, +) { + fun toSChapter() = SChapter.create().apply { + url = "/r/$id" + chapter_number = chapter.float + date_upload = try { + dateFormat.parse(createdAt)!!.time + } catch (_: Exception) { + 0L + } + scanlator = teamName + + val chapterName = title.let { if (it.trim() != "") " - $it" else "" } + name = "${chapter_number.let { if (it % 1 > 0) it else it.toInt() }}$chapterName" + } +} + +private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) + +@Serializable +class ReaderDto( + val readerDataAction: ReaderData, + val globals: Globals, +) + +@Serializable +class Globals( + val mediaKey: String, +) + +@Serializable +class ReaderData( + val readerData: ReaderChapter, +) + +@Serializable +class ReaderChapter( + val release: ReaderPages, +) + +@Serializable +class ReaderPages( + @SerialName("hq_pages") private val page: String, +) { + val pages get() = page.split("\r\n") +} diff --git a/src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/MangaTales.kt b/src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/MangaTales.kt new file mode 100644 index 000000000..22d30806b --- /dev/null +++ b/src/ar/mangatales/src/eu/kanade/tachiyomi/extension/ar/mangatales/MangaTales.kt @@ -0,0 +1,50 @@ +package eu.kanade.tachiyomi.extension.ar.mangatales + +import eu.kanade.tachiyomi.multisrc.gmanga.Gmanga +import eu.kanade.tachiyomi.multisrc.gmanga.TagFilterData +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.Filter +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.util.asJsoup +import okhttp3.Request +import okhttp3.Response + +class MangaTales : Gmanga( + "Manga Tales", + "https://www.mangatales.com", + "ar", + "https://media.mangatales.com", +) { + override fun createThumbnail(mangaId: String, cover: String): String { + return "$cdnUrl/uploads/manga/cover/$mangaId/large_$cover" + } + + override fun getTypesFilter() = listOf( + TagFilterData("1", "عربية", Filter.TriState.STATE_INCLUDE), + TagFilterData("2", "إنجليزي", Filter.TriState.STATE_INCLUDE), + ) + + override fun chaptersRequest(manga: SManga): Request { + val mangaId = manga.url.substringAfterLast("/") + return GET("$baseUrl/api/mangas/$mangaId", headers) + } + + override fun chaptersParse(response: Response): List { + val releases = response.parseAs().mangaReleases + + return releases.map { it.toSChapter() } + } + + override fun pageListParse(response: Response): List { + val data = response.asJsoup() + .select(".js-react-on-rails-component").html() + .parseAs() + + return data.readerDataAction.readerData.release.pages + .mapIndexed { idx, img -> + Page(idx, imageUrl = "$cdnUrl/uploads/releases/$img?ak=${data.globals.mediaKey}") + } + } +}