Temple Scan: migrate to individual extension (#17294)

This commit is contained in:
AwkwardPeak7 2023-07-29 07:33:07 +05:00 committed by GitHub
parent 276e464021
commit 723dd523fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 269 additions and 48 deletions

View File

@ -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()
}
}
}

View File

@ -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"),

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.kanade.tachiyomi.extension">
<application>
<activity
android:name=".en.templescan.TempleScanUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="templescan.net"
android:pathPattern="/comic/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -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"

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -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<Series> 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<Series>
private fun List<Series>.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<MangasPage> {
val mangasPage = seriesCache.toMangasPage(page)
return Observable.just(mangasPage)
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
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<SChapter> {
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<Page> {
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")
}

View File

@ -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)
}
}