From 9b206fc0921fbe48b648d3149364f2f60772a177 Mon Sep 17 00:00:00 2001 From: CriosChan <36739192+CriosChan@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:12:08 +0100 Subject: [PATCH] Add ScanR (#11245) * ScanR: V1 closes (#11244) * ScanR: Changes asked by AwkwardPeak7 + NSFW in manga name --- src/fr/scanr/AndroidManifest.xml | 23 +++ src/fr/scanr/build.gradle | 8 + src/fr/scanr/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2909 bytes src/fr/scanr/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1557 bytes src/fr/scanr/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4172 bytes .../scanr/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7767 bytes .../scanr/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10967 bytes .../tachiyomi/extension/fr/scanr/ScanR.kt | 195 ++++++++++++++++++ .../tachiyomi/extension/fr/scanr/ScanRDto.kt | 43 ++++ .../extension/fr/scanr/ScanRUrlActivity.kt | 38 ++++ 10 files changed, 307 insertions(+) create mode 100644 src/fr/scanr/AndroidManifest.xml create mode 100644 src/fr/scanr/build.gradle create mode 100644 src/fr/scanr/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/fr/scanr/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/fr/scanr/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/fr/scanr/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/fr/scanr/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanR.kt create mode 100644 src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRDto.kt create mode 100644 src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRUrlActivity.kt diff --git a/src/fr/scanr/AndroidManifest.xml b/src/fr/scanr/AndroidManifest.xml new file mode 100644 index 000000000..07b46f265 --- /dev/null +++ b/src/fr/scanr/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/src/fr/scanr/build.gradle b/src/fr/scanr/build.gradle new file mode 100644 index 000000000..b93015e55 --- /dev/null +++ b/src/fr/scanr/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'ScanR' + extClass = '.ScanR' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/fr/scanr/res/mipmap-hdpi/ic_launcher.png b/src/fr/scanr/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..be7d05be0d48342db9e018727ececa6dfaa43459 GIT binary patch literal 2909 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 Hu0mjffgjyNc|gLY6Ew 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

|H<3ZaZ@d+!MRA>3MYhz{b65CDqrgu&IZ4 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 Y{5g0OU}5ve2de;O1$Fsyq}j*+2ci;() + + // Popular + override fun popularMangaRequest(page: Int): Request { + return GET("$cdnUrl/index.json", headers) + } + + override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response) + + // Search + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = if (query.isNotBlank()) { + "$cdnUrl/index.json#$query" + } else { + "$cdnUrl/index.json" + } + return GET(url, headers) + } + + override fun searchMangaParse(response: Response): MangasPage { + val series = response.parseAs>() + val mangaList = mutableListOf() + + val fragment = response.request.url.fragment + val searchQuery = fragment ?: "" + + if (searchQuery.startsWith("SLUG:")) { + val filename = series.get(searchQuery.removePrefix("SLUG:")) + if (filename != null) { + val serie = fetchSeriesData(filename) + mangaList.add(serie.toDetailedSManga()) + } + return MangasPage(mangaList, false) + } + + for ((slug, filename) in series) { + val serie = fetchSeriesData(filename) + + if (searchQuery.isBlank() || serie.title.contains(searchQuery, ignoreCase = true)) { + mangaList.add(serie.toDetailedSManga()) + } + } + + return MangasPage(mangaList, false) + } + + // Details + override fun fetchMangaDetails(manga: SManga): Observable { + val splitedPath = URI(manga.url).path.split("/") + val slug = splitedPath[1] + return client.newCall(GET("$cdnUrl/index.json", headers)) + .asObservableSuccess() + .map { response -> + mangaDetailsParse(response, slug) + } + } + + private fun mangaDetailsParse(response: Response, slug: String = ""): SManga { + val map = response.parseAs>() + val serie = fetchSeriesData(map.get(slug)!!, false) + return serie.toDetailedSManga() + } + + // Pages + override fun pageListRequest(chapter: SChapter): Request { + val splitedPath = URI(chapter.url).path.split("/") + val slug = splitedPath[1] + val chapterId = splitedPath[2] + val serie = getSerieFromSlug(slug) + val chapterDetails = serie.chapters[chapterId.replace("-", ".")] + val cubariProxy = chapterDetails!!.groups.getValue(chapterDetails.groups.keys.first()) + return GET("https://cubari.moe$cubariProxy", headers) + } + + override fun pageListParse(response: Response): List { + val images = response.parseAs>() + return images.mapIndexed { index, pageData -> + Page(index, imageUrl = pageData) + } + } + + // Chapters + override fun fetchChapterList(manga: SManga): Observable> { + val splitedPath = URI(manga.url).path.split("/") + val slug = splitedPath[1] + return client.newCall(GET("$cdnUrl/index.json", headers)) + .asObservableSuccess() + .map { response -> + chapterListParse(response, slug) + } + } + + private fun chapterListParse(response: Response, slug: String = ""): List { + val filename = response.parseAs>().get(slug)!! + val series = fetchSeriesData(filename) + return buildChapterList(series) + } + + private fun buildChapterList(serie: Serie): List { + val chapters = serie.chapters + val chapterList = mutableListOf() + + for ((chapterNumber, chapterData) in chapters) { + val title = chapterData.title + val volumeNumber = chapterData.volume + + val baseName = if (!serie.os) { + buildString { + if (volumeNumber.isNotBlank()) append("Vol. $volumeNumber ") + append("Ch. $chapterNumber") + if (title.isNotBlank()) append(" – $title") + } + } else { + if (title.isNotBlank()) "One Shot – $title" else "One Shot" + } + + val chapter = SChapter.create().apply { + name = baseName + setUrlWithoutDomain("$baseUrl/${serie.slug}/${chapterNumber.replace(".","-")}") + chapter_number = chapterNumber.toFloatOrNull() ?: -1f + scanlator = chapterData.groups.keys.first() + date_upload = chapterData.lastUpdated.toLong() * 1000L + } + chapterList.add(chapter) + } + + return chapterList.sortedByDescending { it.chapter_number } + } + + // Series utils + private fun fetchSeriesData(filename: String, forceReload: Boolean = false): Serie { + val cachedSerie = seriesDataCache[filename] + if (!forceReload && cachedSerie != null) { + return cachedSerie + } + + val response = client.newCall(GET("$cdnUrl/$filename", headers)).execute() + val seriesData = response.parseAs() + + seriesDataCache[filename] = seriesData + return seriesData + } + + private fun getSerieFromSlug(slug: String, forceReload: Boolean = false): Serie { + val serieList = + client.newCall(GET("$cdnUrl/index.json", headers)) + .execute().parseAs>() + + return fetchSeriesData(serieList[slug] ?: "", forceReload) + } + + // Unsupported stuff + override fun imageUrlParse(response: Response): String { + throw UnsupportedOperationException() + } + + override fun latestUpdatesParse(response: Response): MangasPage { + throw UnsupportedOperationException() + } + + override fun latestUpdatesRequest(page: Int): Request { + throw UnsupportedOperationException() + } + + override fun chapterListParse(response: Response): List { + throw UnsupportedOperationException() + } + + override fun mangaDetailsParse(response: Response): SManga { + throw UnsupportedOperationException() + } +} diff --git a/src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRDto.kt b/src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRDto.kt new file mode 100644 index 000000000..731034a1d --- /dev/null +++ b/src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRDto.kt @@ -0,0 +1,43 @@ +package eu.kanade.tachiyomi.extension.fr.scanr + +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Data Transfer Objects for TeamScanR extension + */ + +@Serializable +class Serie( + val slug: String, + val title: String, + val description: String, + val artist: String, + val author: String, + val cover: String, + val os: Boolean = false, + val chapters: Map, + val completed: Boolean = false, + val konami: Boolean = false, +) + +@Serializable +class Chapter( + val title: String, + val volume: String, + @SerialName("last_updated") + val lastUpdated: String, + val groups: Map, +) + +// DTO to SManga extension functions +fun Serie.toDetailedSManga(): SManga = SManga.create().apply { + title = (if (this@toDetailedSManga.konami == true) "[+18] " else "") + this@toDetailedSManga.title + description = this@toDetailedSManga.description + artist = this@toDetailedSManga.artist + author = this@toDetailedSManga.author + status = if (this@toDetailedSManga.os || this@toDetailedSManga.completed) SManga.COMPLETED else SManga.ONGOING + thumbnail_url = this@toDetailedSManga.cover + url = "/${this@toDetailedSManga.slug}" +} diff --git a/src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRUrlActivity.kt b/src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRUrlActivity.kt new file mode 100644 index 000000000..1b85c0111 --- /dev/null +++ b/src/fr/scanr/src/eu/kanade/tachiyomi/extension/fr/scanr/ScanRUrlActivity.kt @@ -0,0 +1,38 @@ +package eu.kanade.tachiyomi.extension.fr.scanr + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +/** + * Springboard that accepts https://teamscanr.fr/xxxxxx intents and redirects them to + * the main Tachiyomi process. + */ +class ScanRUrlActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size >= 1) { + val slug = pathSegments[0] + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "SLUG:$slug") + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("ScanRUrlActivity", e.toString()) + } + } else { + Log.e("ScanRUrlActivity", "could not parse uri from intent $intent") + } + + finish() + exitProcess(0) + } +}