From 723dd523fe6f20e7d5e53b8e10a5a0bb4bccc8f2 Mon Sep 17 00:00:00 2001
From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
Date: Sat, 29 Jul 2023 07:33:07 +0500
Subject: [PATCH] Temple Scan: migrate to individual extension (#17294)
---
.../madara/templescan/src/TempleScan.kt | 47 -----
.../multisrc/madara/MadaraGenerator.kt | 1 -
src/en/templescan/AndroidManifest.xml | 24 +++
src/en/templescan/build.gradle | 12 ++
.../res/mipmap-hdpi/ic_launcher.png | Bin
.../res/mipmap-mdpi/ic_launcher.png | Bin
.../res/mipmap-xhdpi/ic_launcher.png | Bin
.../res/mipmap-xxhdpi/ic_launcher.png | Bin
.../res/mipmap-xxxhdpi/ic_launcher.png | Bin
.../en}/templescan/res/web_hi_res_512.png | Bin
.../extension/en/templescan/TempleScan.kt | 199 ++++++++++++++++++
.../en/templescan/TempleScanUrlActivity.kt | 34 +++
12 files changed, 269 insertions(+), 48 deletions(-)
delete mode 100644 multisrc/overrides/madara/templescan/src/TempleScan.kt
create mode 100644 src/en/templescan/AndroidManifest.xml
create mode 100644 src/en/templescan/build.gradle
rename {multisrc/overrides/madara => src/en}/templescan/res/mipmap-hdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/templescan/res/mipmap-mdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/templescan/res/mipmap-xhdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/templescan/res/mipmap-xxhdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/templescan/res/mipmap-xxxhdpi/ic_launcher.png (100%)
rename {multisrc/overrides/madara => src/en}/templescan/res/web_hi_res_512.png (100%)
create mode 100644 src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScan.kt
create mode 100644 src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScanUrlActivity.kt
diff --git a/multisrc/overrides/madara/templescan/src/TempleScan.kt b/multisrc/overrides/madara/templescan/src/TempleScan.kt
deleted file mode 100644
index a7537980a..000000000
--- a/multisrc/overrides/madara/templescan/src/TempleScan.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package eu.kanade.tachiyomi.extension.en.templescan
-
-import eu.kanade.tachiyomi.multisrc.madara.Madara
-import eu.kanade.tachiyomi.network.interceptor.rateLimit
-import eu.kanade.tachiyomi.source.model.SChapter
-import eu.kanade.tachiyomi.source.model.SManga
-import okhttp3.OkHttpClient
-import org.jsoup.nodes.Element
-import java.text.SimpleDateFormat
-import java.util.Locale
-
-class TempleScan : Madara(
- "Temple Scan",
- "https://templescan.net",
- "en",
- SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH),
-) {
- override val mangaSubString = "comic"
- override val useNewChapterEndpoint = true
- override fun popularMangaSelector() = "div.c-tabs-item > div > div"
- override val popularMangaUrlSelector = "div.series-box a"
- override val mangaDetailsSelectorStatus = ".post-content_item:contains(Status) .summary-content"
-
- override val client: OkHttpClient = super.client.newBuilder()
- .rateLimit(1)
- .build()
-
- override fun popularMangaFromElement(element: Element): SManga {
- return super.popularMangaFromElement(element).apply {
- title = element.select(popularMangaUrlSelector).text()
- }
- }
-
- override fun searchPage(page: Int): String {
- return if (page > 1) {
- "page/$page/"
- } else {
- ""
- }
- }
-
- override fun chapterFromElement(element: Element): SChapter {
- return super.chapterFromElement(element).apply {
- name = element.select(".chapter-manhwa-title").text()
- }
- }
-}
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
index 04406bb4d..b3e151d37 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt
@@ -407,7 +407,6 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("Taurus Fansub", "https://taurusfansub.com", "es"),
SingleLang("Tecno Scan", "https://tecnoscann.com", "es"),
SingleLang("TeenManhua", "https://teenmanhua.com", "en", overrideVersionCode = 1),
- SingleLang("Temple Scan", "https://templescan.net", "en"),
SingleLang("The Beginning After The End", "https://www.thebeginningaftertheend.fr", "fr", overrideVersionCode = 1),
SingleLang("The Guild", "https://theguildscans.com", "en"),
SingleLang("The Sugar", "https://thesugarscan.com", "pt-BR"),
diff --git a/src/en/templescan/AndroidManifest.xml b/src/en/templescan/AndroidManifest.xml
new file mode 100644
index 000000000..ee2d3d397
--- /dev/null
+++ b/src/en/templescan/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/en/templescan/build.gradle b/src/en/templescan/build.gradle
new file mode 100644
index 000000000..84d9cbb84
--- /dev/null
+++ b/src/en/templescan/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+ext {
+ extName = 'Temple Scan'
+ pkgNameSuffix = 'en.templescan'
+ extClass = '.TempleScan'
+ extVersionCode = 32
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/multisrc/overrides/madara/templescan/res/mipmap-hdpi/ic_launcher.png b/src/en/templescan/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/templescan/res/mipmap-hdpi/ic_launcher.png
rename to src/en/templescan/res/mipmap-hdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/templescan/res/mipmap-mdpi/ic_launcher.png b/src/en/templescan/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/templescan/res/mipmap-mdpi/ic_launcher.png
rename to src/en/templescan/res/mipmap-mdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/templescan/res/mipmap-xhdpi/ic_launcher.png b/src/en/templescan/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/templescan/res/mipmap-xhdpi/ic_launcher.png
rename to src/en/templescan/res/mipmap-xhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/templescan/res/mipmap-xxhdpi/ic_launcher.png b/src/en/templescan/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/templescan/res/mipmap-xxhdpi/ic_launcher.png
rename to src/en/templescan/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/templescan/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/templescan/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from multisrc/overrides/madara/templescan/res/mipmap-xxxhdpi/ic_launcher.png
rename to src/en/templescan/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/multisrc/overrides/madara/templescan/res/web_hi_res_512.png b/src/en/templescan/res/web_hi_res_512.png
similarity index 100%
rename from multisrc/overrides/madara/templescan/res/web_hi_res_512.png
rename to src/en/templescan/res/web_hi_res_512.png
diff --git a/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScan.kt b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScan.kt
new file mode 100644
index 000000000..60d08af61
--- /dev/null
+++ b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScan.kt
@@ -0,0 +1,199 @@
+package eu.kanade.tachiyomi.extension.en.templescan
+
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.interceptor.rateLimit
+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.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import okhttp3.Response
+import rx.Observable
+import uy.kohesive.injekt.injectLazy
+import java.util.Calendar
+import kotlin.math.min
+
+class TempleScan : HttpSource() {
+
+ override val name = "Temple Scan"
+
+ override val lang = "en"
+
+ override val baseUrl = "https://templescan.net"
+
+ override val supportsLatest = false
+
+ override val versionId = 2
+
+ override val client = network.cloudflareClient.newBuilder()
+ .rateLimit(1)
+ .build()
+
+ private val json: Json by injectLazy()
+
+ private val seriesCache: List by lazy {
+ val response = client.newCall(GET(baseUrl, headers)).execute()
+
+ if (response.isSuccessful.not()) {
+ response.close()
+ throw Exception("Http Error ${response.code}")
+ }
+
+ response.asJsoup()
+ .selectFirst("script:containsData(proyectos)")
+ ?.data()
+ ?.let { proyectosRegex.find(it)?.groupValues?.get(1) }
+ ?.let(json::decodeFromString)
+ ?: throw Exception(SeriesCacheFailureException)
+ }
+
+ private lateinit var filteredSeriesCache: List
+
+ private fun List.toMangasPage(page: Int): MangasPage {
+ val end = min(page * limit, this.size)
+ val entries = this.subList((page - 1) * limit, end)
+ .map(Series::toSManga)
+
+ return MangasPage(entries, end < this.size)
+ }
+
+ @Serializable
+ data class Series(
+ @SerialName("nombre") val name: String,
+ val slug: String,
+ @SerialName("portada") val cover: String,
+ ) {
+ fun toSManga() = SManga.create().apply {
+ url = "/comic/$slug"
+ title = name
+ thumbnail_url = cover
+ }
+ }
+
+ override fun fetchPopularManga(page: Int): Observable {
+ val mangasPage = seriesCache.toMangasPage(page)
+
+ return Observable.just(mangasPage)
+ }
+
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
+ if (query.startsWith(SEARCH_PREFIX)) {
+ val url = "/comic/${query.substringAfter(SEARCH_PREFIX)}"
+ val manga = SManga.create().apply { this.url = url }
+ return fetchMangaDetails(manga).map {
+ val newManga = it.apply { this.url = url }
+ MangasPage(listOf(newManga), false)
+ }
+ }
+
+ if (page == 1) {
+ filteredSeriesCache = seriesCache.filter {
+ it.name.contains(query.trim(), true)
+ }
+ }
+
+ val mangasPage = filteredSeriesCache.toMangasPage(page)
+
+ return Observable.just(mangasPage)
+ }
+
+ override fun mangaDetailsParse(response: Response): SManga {
+ val document = response.asJsoup()
+
+ return SManga.create().apply {
+ thumbnail_url = document.select(".max-w-80 > img").attr("abs:src")
+ description = document.select("section[id=section-sinopsis] p").text()
+ title = document.select("h1").text()
+ genre = document.select("div.flex div:contains(gen) + div a").joinToString { it.text().trim() }
+ author = document.selectFirst("div.flex div:contains(aut) + div")?.text()
+ }
+ }
+
+ override fun chapterListParse(response: Response): List {
+ val elements = response.asJsoup()
+ .select("div.contenedor-capitulo-miniatura a")
+
+ return elements.map { element ->
+ SChapter.create().apply {
+ setUrlWithoutDomain(element.attr("href"))
+ name = element.select("div[id=name]").text()
+ date_upload = element.select("time").text().let {
+ runCatching { it.parseRelativeDate() }.getOrDefault(0L)
+ }
+ }
+ }
+ }
+
+ override fun pageListParse(response: Response): List {
+ val elements = response.asJsoup()
+ .select("main div img")
+
+ return elements.mapIndexed { index, element ->
+ Page(index, "", element.attr("abs:src"))
+ }
+ }
+
+ private fun String.parseRelativeDate(): Long {
+ val now = Calendar.getInstance().apply {
+ set(Calendar.HOUR_OF_DAY, 0)
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }
+
+ var parsedDate = 0L
+
+ val relativeDate = try {
+ this.split(" ")[0].trim().toInt()
+ } catch (e: NumberFormatException) {
+ return 0L
+ }
+
+ when {
+ "second" in this -> {
+ parsedDate = now.apply { add(Calendar.SECOND, -relativeDate) }.timeInMillis
+ }
+ "minute" in this -> {
+ parsedDate = now.apply { add(Calendar.MINUTE, -relativeDate) }.timeInMillis
+ }
+ "hour" in this -> {
+ parsedDate = now.apply { add(Calendar.HOUR, -relativeDate) }.timeInMillis
+ }
+ "day" in this -> {
+ parsedDate = now.apply { add(Calendar.DAY_OF_YEAR, -relativeDate) }.timeInMillis
+ }
+ "week" in this -> {
+ parsedDate = now.apply { add(Calendar.WEEK_OF_YEAR, -relativeDate) }.timeInMillis
+ }
+ "month" in this -> {
+ parsedDate = now.apply { add(Calendar.MONTH, -relativeDate) }.timeInMillis
+ }
+ "year" in this -> {
+ parsedDate = now.apply { add(Calendar.YEAR, -relativeDate) }.timeInMillis
+ }
+ }
+ return parsedDate
+ }
+
+ companion object {
+ private val proyectosRegex = Regex("""proyectos\s*=\s*([^\;]+)""")
+ private const val SeriesCacheFailureException = "Unable to extract series information"
+
+ private const val limit = 20
+ const val SEARCH_PREFIX = "slug:"
+ }
+
+ override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException("Not Used")
+ override fun popularMangaParse(response: Response) = throw UnsupportedOperationException("Not Used")
+ override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not Used")
+ override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException("Not Used")
+ override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not Used")
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Not Used")
+ override fun searchMangaParse(response: Response) = throw UnsupportedOperationException("Not Used")
+}
diff --git a/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScanUrlActivity.kt b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScanUrlActivity.kt
new file mode 100644
index 000000000..df6c5a9e1
--- /dev/null
+++ b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScanUrlActivity.kt
@@ -0,0 +1,34 @@
+package eu.kanade.tachiyomi.extension.en.templescan
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import kotlin.system.exitProcess
+
+class TempleScanUrlActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+ if (pathSegments != null && pathSegments.size > 1) {
+ val slug = pathSegments[1]
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.SEARCH"
+ putExtra("query", "${TempleScan.SEARCH_PREFIX}$slug")
+ putExtra("filter", packageName)
+ }
+
+ try {
+ startActivity(mainIntent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e("TempleScanUrlActivity", e.toString())
+ }
+ } else {
+ Log.e("TempleScanUrlActivity", "could not parse uri from intent $intent")
+ }
+
+ finish()
+ exitProcess(0)
+ }
+}