Initial commit
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 313 KiB |
@@ -0,0 +1,8 @@
|
||||
package eu.kanade.tachiyomi.extension.id.ainzscansid
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
|
||||
class AinzScansID : MangaThemesia("Ainz Scans ID", "https://ainzscans.site", "id", "/series") {
|
||||
|
||||
override val hasProjectPage = true
|
||||
}
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 280 KiB |
21
multisrc/overrides/mangathemesia/aiyumanga/src/AiYuManga.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package eu.kanade.tachiyomi.extension.es.aiyumanga
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class AiYuManga : MangaThemesia(
|
||||
"AiYuManga",
|
||||
"https://aiyumanhua.com",
|
||||
"es",
|
||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
||||
) {
|
||||
// Site moved from ZeistManga to MangaThemesia
|
||||
override val versionId = 3
|
||||
|
||||
override val client = super.client.newBuilder()
|
||||
.rateLimit(2, 1, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 377 KiB |
17
multisrc/overrides/mangathemesia/alceascan/src/Alceascan.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package eu.kanade.tachiyomi.extension.id.alceascan
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class Alceascan : MangaThemesia("Alceascan", "https://alceascan.my.id", "id") {
|
||||
|
||||
// Website theme changed from zManga to WPMangaThemesia.
|
||||
override val versionId = 2
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.rateLimit(20, 4)
|
||||
.build()
|
||||
|
||||
override val hasProjectPage = true
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 185 KiB |
@@ -0,0 +1,16 @@
|
||||
package eu.kanade.tachiyomi.extension.en.animatedglitchedcomics
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class AnimatedGlitchedComics : MangaThemesia(
|
||||
"Animated Glitched Comics",
|
||||
"https://agscomics.com",
|
||||
"en",
|
||||
mangaUrlDirectory = "/series",
|
||||
) {
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.rateLimit(1)
|
||||
.build()
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 185 KiB |
@@ -0,0 +1,5 @@
|
||||
package eu.kanade.tachiyomi.extension.en.animatedglitchedscans
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
|
||||
class AnimatedGlitchedScans : MangaThemesia("Animated Glitched Scans", "https://anigliscans.xyz", "en", mangaUrlDirectory = "/series")
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 80 KiB |
30
multisrc/overrides/mangathemesia/areamanga/src/AreaManga.kt
Normal file
@@ -0,0 +1,30 @@
|
||||
package eu.kanade.tachiyomi.extension.ar.areamanga
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class AreaManga : MangaThemesia(
|
||||
"أريا مانجا",
|
||||
"https://www.areascans.net",
|
||||
"ar",
|
||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||
) {
|
||||
override val seriesArtistSelector =
|
||||
".tsinfo .imptdt:contains(الرسام) i, ${super.seriesArtistSelector}"
|
||||
override val seriesAuthorSelector =
|
||||
".tsinfo .imptdt:contains(المؤلف) i, ${super.seriesAuthorSelector}"
|
||||
override val seriesStatusSelector =
|
||||
".tsinfo .imptdt:contains(الحالة) i, ${super.seriesStatusSelector}"
|
||||
override val seriesTypeSelector =
|
||||
".tsinfo .imptdt:contains(النوع) i, ${super.seriesTypeSelector}"
|
||||
|
||||
override fun String?.parseStatus() = when {
|
||||
this == null -> SManga.UNKNOWN
|
||||
this.contains("مستمر", ignoreCase = true) -> SManga.ONGOING
|
||||
this.contains("مكتمل", ignoreCase = true) -> SManga.COMPLETED
|
||||
this.contains("متوقف", ignoreCase = true) -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,7 @@
|
||||
package eu.kanade.tachiyomi.extension.en.arenascans
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
|
||||
class ArenaScans : MangaThemesia("Arena Scans", "https://arenascans.net", "en") {
|
||||
override val pageSelector = "div#readerarea img[data-src]"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package eu.kanade.tachiyomi.extension.pt.arkhamscan
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import okhttp3.OkHttpClient
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ArkhamScan : MangaThemesia(
|
||||
"Arkham Scan",
|
||||
"https://arkhamscan.com",
|
||||
"pt-BR",
|
||||
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
|
||||
) {
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.rateLimit(1, 2, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
override val altNamePrefix = "Nomes alternativos: "
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 105 KiB |
@@ -0,0 +1,13 @@
|
||||
package eu.kanade.tachiyomi.extension.en.arvenscans
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import okhttp3.OkHttpClient
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ArvenScans : MangaThemesia("Arven Scans", "https://arvenscans.com", "en", "/series") {
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.rateLimit(20, 5, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 116 KiB |
324
multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt
Normal file
@@ -0,0 +1,324 @@
|
||||
package eu.kanade.tachiyomi.extension.all.asurascans
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
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 kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class AsuraScansEn : MangaThemesia(
|
||||
"Asura Scans",
|
||||
"https://asuratoon.com",
|
||||
"en",
|
||||
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US),
|
||||
) {
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override val baseUrl by lazy {
|
||||
preferences.baseUrlHost.let { "https://$it" }
|
||||
}
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.addInterceptor(::urlChangeInterceptor)
|
||||
.addInterceptor(::domainChangeIntercept)
|
||||
.rateLimit(1, 3, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
override val seriesDescriptionSelector = "div.desc p, div.entry-content p, div[itemprop=description]:not(:has(p))"
|
||||
override val seriesArtistSelector = ".fmed b:contains(artist)+span, .infox span:contains(artist)"
|
||||
override val seriesAuthorSelector = ".fmed b:contains(author)+span, .infox span:contains(author)"
|
||||
|
||||
override val pageSelector = "div.rdminimal > img, div.rdminimal > p > img, div.rdminimal > a > img, div.rdminimal > p > a > img, " +
|
||||
"div.rdminimal > noscript > img, div.rdminimal > p > noscript > img, div.rdminimal > a > noscript > img, div.rdminimal > p > a > noscript > img"
|
||||
|
||||
// Permanent Url for Manga/Chapter End
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
return super.fetchPopularManga(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
return super.fetchLatestUpdates(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return super.fetchSearchManga(page, query, filters).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val request = super.searchMangaRequest(page, query, filters)
|
||||
if (query.isBlank()) return request
|
||||
|
||||
val url = request.url.newBuilder()
|
||||
.addPathSegment("page/$page/")
|
||||
.removeAllQueryParameters("page")
|
||||
.removeAllQueryParameters("title")
|
||||
.addQueryParameter("s", query)
|
||||
.build()
|
||||
|
||||
return request.newBuilder()
|
||||
.url(url)
|
||||
.build()
|
||||
}
|
||||
|
||||
// Temp Url for manga/chapter
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val newManga = manga.titleToUrlFrag()
|
||||
|
||||
return super.fetchChapterList(newManga)
|
||||
}
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
val newManga = manga.titleToUrlFrag()
|
||||
|
||||
return super.fetchMangaDetails(newManga)
|
||||
}
|
||||
|
||||
override fun getMangaUrl(manga: SManga): String {
|
||||
val dbSlug = manga.url
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val storedSlug = preferences.slugMap[dbSlug] ?: dbSlug
|
||||
|
||||
return "$baseUrl$mangaUrlDirectory/$storedSlug/"
|
||||
}
|
||||
|
||||
// Skip scriptPages
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select(pageSelector)
|
||||
.filterNot { it.attr("src").isNullOrEmpty() }
|
||||
.mapIndexed { i, img -> Page(i, document.location(), img.attr("abs:src")) }
|
||||
}
|
||||
|
||||
override fun Element.imgAttr(): String = when {
|
||||
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
|
||||
hasAttr("data-src") -> attr("abs:data-src")
|
||||
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
|
||||
else -> attr("abs:src")
|
||||
}
|
||||
|
||||
private fun Observable<MangasPage>.tempUrlToPermIfNeeded(): Observable<MangasPage> {
|
||||
return this.map { mangasPage ->
|
||||
MangasPage(
|
||||
mangasPage.mangas.map { it.tempUrlToPermIfNeeded() },
|
||||
mangasPage.hasNextPage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SManga.tempUrlToPermIfNeeded(): SManga {
|
||||
if (!preferences.permaUrlPref) return this
|
||||
|
||||
val slugMap = preferences.slugMap
|
||||
|
||||
val sMangaTitleFirstWord = this.title.split(" ")[0]
|
||||
if (!this.url.contains("/$sMangaTitleFirstWord", ignoreCase = true)) {
|
||||
val currentSlug = this.url
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val permaSlug = currentSlug.replaceFirst(TEMP_TO_PERM_REGEX, "")
|
||||
|
||||
slugMap[permaSlug] = currentSlug
|
||||
|
||||
this.url = "$mangaUrlDirectory/$permaSlug/"
|
||||
}
|
||||
preferences.slugMap = slugMap
|
||||
return this
|
||||
}
|
||||
|
||||
private fun SManga.titleToUrlFrag(): SManga {
|
||||
return try {
|
||||
this.apply {
|
||||
url = "$url#${title.toSearchQuery()}"
|
||||
}
|
||||
} catch (e: UninitializedPropertyAccessException) {
|
||||
// when called from deep link, title is not present
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
private fun urlChangeInterceptor(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
|
||||
val frag = request.url.fragment
|
||||
|
||||
if (frag.isNullOrEmpty()) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
val dbSlug = request.url.toString()
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val slugMap = preferences.slugMap
|
||||
|
||||
val storedSlug = slugMap[dbSlug] ?: dbSlug
|
||||
|
||||
val response = chain.proceed(
|
||||
request.newBuilder()
|
||||
.url("$baseUrl$mangaUrlDirectory/$storedSlug/")
|
||||
.build(),
|
||||
)
|
||||
|
||||
if (!response.isSuccessful && response.code == 404) {
|
||||
response.close()
|
||||
|
||||
val newSlug = getNewSlug(storedSlug, frag)
|
||||
?: throw IOException("Migrate from Asura to Asura")
|
||||
|
||||
slugMap[dbSlug] = newSlug
|
||||
preferences.slugMap = slugMap
|
||||
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.url("$baseUrl$mangaUrlDirectory/$newSlug/")
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
private fun getNewSlug(existingSlug: String, frag: String): String? {
|
||||
val permaSlug = existingSlug
|
||||
.replaceFirst(TEMP_TO_PERM_REGEX, "")
|
||||
|
||||
val search = frag.substringBefore("#")
|
||||
|
||||
val mangas = client.newCall(searchMangaRequest(1, search, FilterList()))
|
||||
.execute()
|
||||
.use {
|
||||
searchMangaParse(it)
|
||||
}
|
||||
|
||||
return mangas.mangas.firstOrNull { newManga ->
|
||||
newManga.url.contains(permaSlug, true)
|
||||
}
|
||||
?.url
|
||||
?.removeSuffix("/")
|
||||
?.substringAfterLast("/")
|
||||
}
|
||||
|
||||
private fun String.toSearchQuery(): String {
|
||||
return this.trim()
|
||||
.lowercase()
|
||||
.replace(titleSpecialCharactersRegex, "+")
|
||||
.replace(trailingPlusRegex, "")
|
||||
}
|
||||
|
||||
private var lastDomain = ""
|
||||
|
||||
private fun domainChangeIntercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
|
||||
if (request.url.host !in listOf(preferences.baseUrlHost, lastDomain)) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
if (lastDomain.isNotEmpty()) {
|
||||
val newUrl = request.url.newBuilder()
|
||||
.host(preferences.baseUrlHost)
|
||||
.build()
|
||||
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.url(newUrl)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
val response = chain.proceed(request)
|
||||
|
||||
if (request.url.host == response.request.url.host) return response
|
||||
|
||||
response.close()
|
||||
|
||||
preferences.baseUrlHost = response.request.url.host
|
||||
|
||||
lastDomain = request.url.host
|
||||
|
||||
val newUrl = request.url.newBuilder()
|
||||
.host(response.request.url.host)
|
||||
.build()
|
||||
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.url(newUrl)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
SwitchPreferenceCompat(screen.context).apply {
|
||||
key = PREF_PERM_MANGA_URL_KEY_PREFIX + lang
|
||||
title = PREF_PERM_MANGA_URL_TITLE
|
||||
summary = PREF_PERM_MANGA_URL_SUMMARY
|
||||
setDefaultValue(true)
|
||||
}.also(screen::addPreference)
|
||||
|
||||
super.setupPreferenceScreen(screen)
|
||||
}
|
||||
|
||||
private val SharedPreferences.permaUrlPref
|
||||
get() = getBoolean(PREF_PERM_MANGA_URL_KEY_PREFIX + lang, true)
|
||||
|
||||
private var SharedPreferences.slugMap: MutableMap<String, String>
|
||||
get() {
|
||||
val serialized = getString(PREF_URL_MAP, null) ?: return mutableMapOf()
|
||||
|
||||
return try {
|
||||
json.decodeFromString(serialized)
|
||||
} catch (e: Exception) {
|
||||
mutableMapOf()
|
||||
}
|
||||
}
|
||||
set(slugMap) {
|
||||
val serialized = json.encodeToString(slugMap)
|
||||
edit().putString(PREF_URL_MAP, serialized).commit()
|
||||
}
|
||||
|
||||
private var SharedPreferences.baseUrlHost
|
||||
get() = getString(BASE_URL_PREF, defaultBaseUrlHost) ?: defaultBaseUrlHost
|
||||
set(newHost) {
|
||||
edit().putString(BASE_URL_PREF, newHost).commit()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_PERM_MANGA_URL_KEY_PREFIX = "pref_permanent_manga_url_2_"
|
||||
private const val PREF_PERM_MANGA_URL_TITLE = "Permanent Manga URL"
|
||||
private const val PREF_PERM_MANGA_URL_SUMMARY = "Turns all manga urls into permanent ones."
|
||||
private const val PREF_URL_MAP = "pref_url_map"
|
||||
private const val BASE_URL_PREF = "pref_base_url_host"
|
||||
private const val defaultBaseUrlHost = "asuratoon.com"
|
||||
private val TEMP_TO_PERM_REGEX = Regex("""^\d+-""")
|
||||
private val titleSpecialCharactersRegex = Regex("""[^a-z0-9]+""")
|
||||
private val trailingPlusRegex = Regex("""\++$""")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package eu.kanade.tachiyomi.extension.all.asurascans
|
||||
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
|
||||
class AsuraScansFactory : SourceFactory {
|
||||
override fun createSources() = listOf(
|
||||
AsuraScansEn(),
|
||||
AsuraScansTr(),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package eu.kanade.tachiyomi.extension.all.asurascans
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class AsuraScansTr : MangaThemesia(
|
||||
"Asura Scans",
|
||||
"https://armoniscans.com",
|
||||
"tr",
|
||||
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr")),
|
||||
) {
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.rateLimit(1, 3, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
override val seriesArtistSelector = ".fmed b:contains(Çizer)+span"
|
||||
override val seriesAuthorSelector = ".fmed b:contains(Yazar)+span"
|
||||
override val seriesStatusSelector = ".imptdt:contains(Durum) i"
|
||||
override val seriesTypeSelector = ".imptdt:contains(Tür) a"
|
||||
|
||||
override val altNamePrefix: String = "Alternatif isim: "
|
||||
|
||||
override fun String?.parseStatus(): Int = when {
|
||||
this == null -> SManga.UNKNOWN
|
||||
this.contains("Devam Ediyor", ignoreCase = true) -> SManga.ONGOING
|
||||
this.contains("Tamamlandı", ignoreCase = true) -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun Element.imgAttr(): String = when {
|
||||
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
|
||||
hasAttr("data-src") -> attr("abs:data-src")
|
||||
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
|
||||
else -> attr("abs:src")
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val scriptContent = document.selectFirst("script:containsData(ts_reader)")?.data()
|
||||
?: return super.pageListParse(document)
|
||||
val jsonString = scriptContent.substringAfter("ts_reader.run(").substringBefore(");")
|
||||
val tsReader = json.decodeFromString<TSReader>(jsonString)
|
||||
val imageUrls = tsReader.sources.firstOrNull()?.images ?: return emptyList()
|
||||
return imageUrls.mapIndexed { index, imageUrl -> Page(index, document.location(), imageUrl) }
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class TSReader(
|
||||
val sources: List<ReaderImageSource>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ReaderImageSource(
|
||||
val source: String,
|
||||
val images: List<String>,
|
||||
)
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,7 @@
|
||||
package eu.kanade.tachiyomi.extension.fr.bananascan
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class BananaScan : MangaThemesia("Banana-Scan", "https://banana-scan.com", "fr", dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.FRENCH))
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,7 @@
|
||||
package eu.kanade.tachiyomi.extension.en.batotoscans
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
|
||||
class BatotoScans : MangaThemesia("Snudae Scans", "https://snudaescans.com", "en") {
|
||||
override val id = 4418350353197826372L
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 122 KiB |
@@ -0,0 +1,31 @@
|
||||
package eu.kanade.tachiyomi.extension.ar.beastscans
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class BeastScans : MangaThemesia(
|
||||
"Beast Scans",
|
||||
"https://beastscans.net",
|
||||
"ar",
|
||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||
) {
|
||||
override val seriesArtistSelector =
|
||||
".infox .fmed:contains(الرسام) span, ${super.seriesArtistSelector}"
|
||||
override val seriesAuthorSelector =
|
||||
".infox .fmed:contains(المؤلف) span, ${super.seriesAuthorSelector}"
|
||||
override val seriesStatusSelector =
|
||||
".tsinfo .imptdt:contains(الحالة) i, ${super.seriesStatusSelector}"
|
||||
override val seriesTypeSelector =
|
||||
".tsinfo .imptdt:contains(النوع) i, ${super.seriesTypeSelector}"
|
||||
|
||||
override fun String?.parseStatus() = when {
|
||||
this == null -> SManga.UNKNOWN
|
||||
listOf("مستمر", "ongoing", "publishing").any { this.contains(it, ignoreCase = true) } -> SManga.ONGOING
|
||||
listOf("متوقف", "hiatus").any { this.contains(it, ignoreCase = true) } -> SManga.ON_HIATUS
|
||||
listOf("مكتمل", "completed").any { this.contains(it, ignoreCase = true) } -> SManga.COMPLETED
|
||||
listOf("dropped", "cancelled").any { this.contains(it, ignoreCase = true) } -> SManga.CANCELLED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 115 KiB |
@@ -0,0 +1,18 @@
|
||||
package eu.kanade.tachiyomi.extension.es.berserkerscan
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class BerserkerScan : MangaThemesia(
|
||||
"Berserker Scan",
|
||||
"https://ragnascan.com",
|
||||
"es",
|
||||
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("es")),
|
||||
) {
|
||||
override val client = super.client.newBuilder()
|
||||
.rateLimit(2, 1, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||