Compare commits
130 Commits
ac57f5e3dd
...
5ecf338be0
Author | SHA1 | Date |
---|---|---|
Chopper | 5ecf338be0 | |
子斗子 | d2ad77c0a3 | |
Vetle Ledaal | ac55757327 | |
KenjieDec | a1c813a40d | |
AwkwardPeak7 | 9211ccee37 | |
AwkwardPeak7 | 10a0b67bf4 | |
Vetle Ledaal | e67653580e | |
Vetle Ledaal | 69ace8bba9 | |
Vetle Ledaal | b1f0d14217 | |
Vetle Ledaal | 9ee1be41b8 | |
Vetle Ledaal | 89dbb360d3 | |
Vetle Ledaal | 15aebe8edb | |
Vetle Ledaal | b4dd656175 | |
AwkwardPeak7 | c62cec150f | |
AwkwardPeak7 | 77ac036786 | |
AwkwardPeak7 | 39b1e20401 | |
KenjieDec | b65681b8b7 | |
KenjieDec | db149972bd | |
AwkwardPeak7 | b99fc8af0d | |
AwkwardPeak7 | 41cebfbf1d | |
AwkwardPeak7 | ca739c2278 | |
AwkwardPeak7 | 10a970a56d | |
AwkwardPeak7 | 32220c1673 | |
KenjieDec | d63c947ca7 | |
AwkwardPeak7 | 48b0101075 | |
AwkwardPeak7 | a14bf9cc8b | |
AwkwardPeak7 | fc27271192 | |
AwkwardPeak7 | 18863bcd7f | |
AwkwardPeak7 | 292bcc93bd | |
AwkwardPeak7 | 6b764484d6 | |
AwkwardPeak7 | 490a7b1c0c | |
AwkwardPeak7 | afe2bf4e55 | |
AwkwardPeak7 | e0a7051166 | |
AwkwardPeak7 | f4124bb944 | |
KenjieDec | 159bee785a | |
TheKingTermux | 2bab0cccdb | |
KenjieDec | a27b852546 | |
Chopper | 4955c0f1e5 | |
AwkwardPeak7 | fa0910b72c | |
AwkwardPeak7 | 425d6e839d | |
AwkwardPeak7 | 93c5a106c4 | |
AwkwardPeak7 | 4a7de77df7 | |
AwkwardPeak7 | 847f8d084f | |
AwkwardPeak7 | 6711c29e74 | |
KenjieDec | b6cf811a63 | |
Chopper | 18b6668b54 | |
AwkwardPeak7 | 987aa83570 | |
AwkwardPeak7 | 7ac206a61a | |
AwkwardPeak7 | fdcae35d12 | |
AwkwardPeak7 | 5fca0cb71f | |
AwkwardPeak7 | 271855037e | |
AwkwardPeak7 | 0ea2b99616 | |
AwkwardPeak7 | c16c663fba | |
AwkwardPeak7 | b50b2c3275 | |
AwkwardPeak7 | 6f51e9d50f | |
are-are-are | 45ec1a302d | |
Wackery | 6f98841b14 | |
KenjieDec | cdb8de9fbf | |
Chopper | 9730a445c8 | |
Pedro Azevedo | a4558c60eb | |
bapeey | 6b8d072c6c | |
bapeey | 8b0ff6e537 | |
Chopper | 99caea527a | |
Chopper | f765a61aa4 | |
Vetle Ledaal | fb6bd6a041 | |
Chaos Pjeles | 39f83d1d77 | |
Chopper | c78f0e45f1 | |
kana-shii | cea1f3c81c | |
bapeey | cca5e276d2 | |
jckli | 118a905cef | |
lamaxama | c02273905e | |
lamaxama | 56aa26d330 | |
Chopper | 2d41904b79 | |
Chopper | 3030e0c74f | |
Chopper | 8c4ee90ccc | |
Chopper | 90ba562e46 | |
Chopper | 329738e39d | |
Vetle Ledaal | ebdcdea164 | |
Vetle Ledaal | aece7b3d39 | |
Vetle Ledaal | 4dac2439f8 | |
Vetle Ledaal | a60cd07d45 | |
KenjieDec | da46ebfa9a | |
AwkwardPeak7 | 1a1566be23 | |
AwkwardPeak7 | 7a4fa5d46a | |
Wackery | 4554a0c717 | |
CriosChan | 7e3d185dab | |
Chopper | 5824d4adfa | |
Yush0DAN | ea99a44cf3 | |
AwkwardPeak7 | a82548860a | |
Secozzi | 20b9eff851 | |
Chopper | e8d27b655e | |
KenjieDec | 869afb9534 | |
KirinRaikage | 311bea3a8a | |
KenjieDec | 860155e34d | |
Chopper | 5ba43beecc | |
Chopper | 709769b171 | |
Secozzi | e9d0013df3 | |
Norsze | 0251a55109 | |
Norsze | 530e5500da | |
Norsze | 5ae345113f | |
AwkwardPeak7 | fa37d45021 | |
Luqman | 4fc5107823 | |
Chopper | 01f3fa0191 | |
AwkwardPeak7 | 902e0242dd | |
Chopper | 9e8187cd1f | |
Chopper | 362b739c88 | |
Vetle Ledaal | e171d141f0 | |
AwkwardPeak7 | 401e53d45a | |
AwkwardPeak7 | e5a68f8ba4 | |
Chopper | d07fbd5294 | |
Chopper | ee3d182a5f | |
Chopper | 40f2170702 | |
Chopper | f0e17a6531 | |
Chopper | 5dbcb16307 | |
KenjieDec | ef7ff81f1b | |
AwkwardPeak7 | 8cb6533dcd | |
Vetle Ledaal | 0e2d9bf970 | |
Vetle Ledaal | 7dbe265e52 | |
Vetle Ledaal | 2623bf72c5 | |
Vetle Ledaal | 13fcfc6996 | |
Vetle Ledaal | 0bf59dd5e8 | |
KenjieDec | 38ef5386a6 | |
AwkwardPeak7 | 4eacdd057e | |
KenjieDec | a5e5ccceec | |
kana-shii | 10ddb3734f | |
AwkwardPeak7 | 3e6c9170ab | |
bapeey | 4d291d571d | |
bapeey | 303f6d7737 | |
KenjieDec | 88fffed4f9 | |
KenjieDec | 2f9ebadb08 |
|
@ -2,4 +2,4 @@ plugins {
|
||||||
id("lib-multisrc")
|
id("lib-multisrc")
|
||||||
}
|
}
|
||||||
|
|
||||||
baseVersionCode = 1
|
baseVersionCode = 2
|
||||||
|
|
|
@ -291,7 +291,7 @@ abstract class BlogTruyen(
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
val pages = mutableListOf<Page>()
|
val pages = mutableListOf<Page>()
|
||||||
|
|
||||||
document.select("#content > img").forEachIndexed { i, e ->
|
document.select(".content > img, #content > img").forEachIndexed { i, e ->
|
||||||
pages.add(Page(i, imageUrl = e.absUrl("src")))
|
pages.add(Page(i, imageUrl = e.absUrl("src")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
/**
|
/**
|
||||||
* Springboard that accepts https://readmanga.live/xxx intents and redirects them to
|
* Springboard that accepts https://1.readmanga.io/xxx intents and redirects them to
|
||||||
* the main tachiyomi process. The idea is to not install the intent filter unless
|
* the main tachiyomi process. The idea is to not install the intent filter unless
|
||||||
* you have this extension installed, but still let the main tachiyomi app control
|
* you have this extension installed, but still let the main tachiyomi app control
|
||||||
* things.
|
* things.
|
||||||
|
|
|
@ -2,7 +2,7 @@ plugins {
|
||||||
id("lib-multisrc")
|
id("lib-multisrc")
|
||||||
}
|
}
|
||||||
|
|
||||||
baseVersionCode = 25
|
baseVersionCode = 27
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":lib:i18n"))
|
api(project(":lib:i18n"))
|
||||||
|
|
|
@ -72,6 +72,8 @@ abstract class HeanCms(
|
||||||
|
|
||||||
protected open val coverPath: String = ""
|
protected open val coverPath: String = ""
|
||||||
|
|
||||||
|
protected open val cdnUrl = apiUrl
|
||||||
|
|
||||||
protected open val mangaSubDirectory: String = "series"
|
protected open val mangaSubDirectory: String = "series"
|
||||||
|
|
||||||
protected open val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US)
|
protected open val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US)
|
||||||
|
@ -206,7 +208,7 @@ abstract class HeanCms(
|
||||||
|
|
||||||
val result = json.parseAs<HeanCmsQuerySearchDto>()
|
val result = json.parseAs<HeanCmsQuerySearchDto>()
|
||||||
val mangaList = result.data.map {
|
val mangaList = result.data.map {
|
||||||
it.toSManga(apiUrl, coverPath, mangaSubDirectory)
|
it.toSManga(cdnUrl, coverPath, mangaSubDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
return MangasPage(mangaList, result.meta?.hasNextPage() ?: false)
|
return MangasPage(mangaList, result.meta?.hasNextPage() ?: false)
|
||||||
|
@ -242,7 +244,7 @@ abstract class HeanCms(
|
||||||
val seriesResult = result.getOrNull()
|
val seriesResult = result.getOrNull()
|
||||||
?: throw Exception(intl.format("url_changed_error", name, name))
|
?: throw Exception(intl.format("url_changed_error", name, name))
|
||||||
|
|
||||||
val seriesDetails = seriesResult.toSManga(apiUrl, coverPath, mangaSubDirectory)
|
val seriesDetails = seriesResult.toSManga(cdnUrl, coverPath, mangaSubDirectory)
|
||||||
|
|
||||||
return seriesDetails.apply {
|
return seriesDetails.apply {
|
||||||
status = status.takeUnless { it == SManga.UNKNOWN }
|
status = status.takeUnless { it == SManga.UNKNOWN }
|
||||||
|
@ -345,8 +347,8 @@ abstract class HeanCms(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.toAbsoluteUrl(): String {
|
protected open fun String.toAbsoluteUrl(): String {
|
||||||
return if (startsWith("https://") || startsWith("http://")) this else "$apiUrl/$coverPath$this"
|
return if (startsWith("https://") || startsWith("http://")) this else "$cdnUrl/$coverPath$this"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
|
override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
|
||||||
|
|
|
@ -63,7 +63,7 @@ class HeanCmsSeriesDto(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun toSManga(
|
fun toSManga(
|
||||||
apiUrl: String,
|
cdnUrl: String,
|
||||||
coverPath: String,
|
coverPath: String,
|
||||||
mangaSubDirectory: String,
|
mangaSubDirectory: String,
|
||||||
): SManga = SManga.create().apply {
|
): SManga = SManga.create().apply {
|
||||||
|
@ -79,7 +79,7 @@ class HeanCmsSeriesDto(
|
||||||
.sortedBy(HeanCmsTagDto::name)
|
.sortedBy(HeanCmsTagDto::name)
|
||||||
.joinToString { it.name }
|
.joinToString { it.name }
|
||||||
thumbnail_url = thumbnail.ifEmpty { null }
|
thumbnail_url = thumbnail.ifEmpty { null }
|
||||||
?.toAbsoluteThumbnailUrl(apiUrl, coverPath)
|
?.toAbsoluteThumbnailUrl(cdnUrl, coverPath)
|
||||||
status = this@HeanCmsSeriesDto.status?.toStatus() ?: SManga.UNKNOWN
|
status = this@HeanCmsSeriesDto.status?.toStatus() ?: SManga.UNKNOWN
|
||||||
url = "/$mangaSubDirectory/$slug#$id"
|
url = "/$mangaSubDirectory/$slug#$id"
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,7 @@ class HeanCmsChapterPayloadDto(
|
||||||
class HeanCmsChapterDto(
|
class HeanCmsChapterDto(
|
||||||
private val id: Int,
|
private val id: Int,
|
||||||
@SerialName("chapter_name") private val name: String,
|
@SerialName("chapter_name") private val name: String,
|
||||||
|
@SerialName("chapter_title") private val title: String? = null,
|
||||||
@SerialName("chapter_slug") private val slug: String,
|
@SerialName("chapter_slug") private val slug: String,
|
||||||
@SerialName("created_at") private val createdAt: String,
|
@SerialName("created_at") private val createdAt: String,
|
||||||
val price: Int? = null,
|
val price: Int? = null,
|
||||||
|
@ -114,6 +115,10 @@ class HeanCmsChapterDto(
|
||||||
): SChapter = SChapter.create().apply {
|
): SChapter = SChapter.create().apply {
|
||||||
name = this@HeanCmsChapterDto.name.trim()
|
name = this@HeanCmsChapterDto.name.trim()
|
||||||
|
|
||||||
|
if (title != null) {
|
||||||
|
name += " - ${title.trim()}"
|
||||||
|
}
|
||||||
|
|
||||||
if (price != 0) {
|
if (price != 0) {
|
||||||
name += " \uD83D\uDD12"
|
name += " \uD83D\uDD12"
|
||||||
}
|
}
|
||||||
|
@ -161,8 +166,8 @@ class HeanCmsGenreDto(
|
||||||
val name: String,
|
val name: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun String.toAbsoluteThumbnailUrl(apiUrl: String, coverPath: String): String {
|
private fun String.toAbsoluteThumbnailUrl(cdnUrl: String, coverPath: String): String {
|
||||||
return if (startsWith("https://") || startsWith("http://")) this else "$apiUrl/$coverPath$this"
|
return if (startsWith("https://") || startsWith("http://")) this else "$cdnUrl/$coverPath$this"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.toStatus(): Int = when (this) {
|
fun String.toStatus(): Int = when (this) {
|
||||||
|
|
|
@ -2,4 +2,4 @@ plugins {
|
||||||
id("lib-multisrc")
|
id("lib-multisrc")
|
||||||
}
|
}
|
||||||
|
|
||||||
baseVersionCode = 1
|
baseVersionCode = 3
|
||||||
|
|
|
@ -4,7 +4,6 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -31,14 +30,10 @@ class Manga(
|
||||||
private val seriesStatus: String? = null,
|
private val seriesStatus: String? = null,
|
||||||
val genres: List<Genre> = emptyList(),
|
val genres: List<Genre> = emptyList(),
|
||||||
) {
|
) {
|
||||||
fun toSManga(baseUrl: String) = SManga.create().apply {
|
fun toSManga() = SManga.create().apply {
|
||||||
url = "$slug#$id"
|
url = "$slug#$id"
|
||||||
title = postTitle
|
title = postTitle
|
||||||
thumbnail_url = "$baseUrl/_next/image".toHttpUrl().newBuilder().apply {
|
thumbnail_url = featuredImage
|
||||||
addQueryParameter("url", featuredImage)
|
|
||||||
addQueryParameter("w", "828")
|
|
||||||
addQueryParameter("q", "75")
|
|
||||||
}.toString()
|
|
||||||
author = this@Manga.author?.takeUnless { it.isEmpty() }
|
author = this@Manga.author?.takeUnless { it.isEmpty() }
|
||||||
artist = this@Manga.artist?.takeUnless { it.isEmpty() }
|
artist = this@Manga.artist?.takeUnless { it.isEmpty() }
|
||||||
description = buildString {
|
description = buildString {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import kotlinx.serialization.json.Json
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
abstract class Iken(
|
abstract class Iken(
|
||||||
|
@ -43,7 +44,7 @@ abstract class Iken(
|
||||||
it.genres.map { genre ->
|
it.genres.map { genre ->
|
||||||
genre.name to genre.id.toString()
|
genre.name to genre.id.toString()
|
||||||
}
|
}
|
||||||
}
|
}.distinct()
|
||||||
}
|
}
|
||||||
.associateBy { it.slug }
|
.associateBy { it.slug }
|
||||||
}
|
}
|
||||||
|
@ -56,7 +57,7 @@ abstract class Iken(
|
||||||
.map { it.absUrl("href").substringAfterLast("/series/") }
|
.map { it.absUrl("href").substringAfterLast("/series/") }
|
||||||
|
|
||||||
val entries = slugs.mapNotNull {
|
val entries = slugs.mapNotNull {
|
||||||
titleCache[it]?.toSManga(baseUrl)
|
titleCache[it]?.toSManga()
|
||||||
}
|
}
|
||||||
|
|
||||||
return MangasPage(entries, false)
|
return MangasPage(entries, false)
|
||||||
|
@ -84,7 +85,7 @@ abstract class Iken(
|
||||||
|
|
||||||
val entries = data.posts
|
val entries = data.posts
|
||||||
.filterNot { it.isNovel }
|
.filterNot { it.isNovel }
|
||||||
.map { it.toSManga(baseUrl) }
|
.map { it.toSManga() }
|
||||||
|
|
||||||
val hasNextPage = data.totalCount > (page * perPage)
|
val hasNextPage = data.totalCount > (page * perPage)
|
||||||
|
|
||||||
|
@ -98,32 +99,28 @@ abstract class Iken(
|
||||||
Filter.Header("Open popular mangas if genre filter is empty"),
|
Filter.Header("Open popular mangas if genre filter is empty"),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
|
||||||
val id = manga.url.substringAfterLast("#")
|
|
||||||
val url = "$baseUrl/api/chapters?postId=$id&skip=0&take=1000&order=desc&userid="
|
|
||||||
|
|
||||||
return GET(url, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga): String {
|
override fun getMangaUrl(manga: SManga): String {
|
||||||
val slug = manga.url.substringBeforeLast("#")
|
val slug = manga.url.substringBeforeLast("#")
|
||||||
|
|
||||||
return "$baseUrl/series/$slug"
|
return "$baseUrl/series/$slug"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response): SManga {
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||||
val data = response.parseAs<Post<Manga>>()
|
val slug = manga.url.substringBeforeLast("#")
|
||||||
|
val update = titleCache[slug]?.toSManga() ?: manga
|
||||||
|
|
||||||
assert(!data.post.isNovel) { "Novels are unsupported" }
|
return Observable.just(update)
|
||||||
|
|
||||||
// genres are only returned in search call
|
|
||||||
// and not when fetching details
|
|
||||||
return data.post.toSManga(baseUrl).apply {
|
|
||||||
genre = titleCache[data.post.slug]?.getGenres()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
override fun mangaDetailsParse(response: Response) =
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
val id = manga.url.substringAfterLast("#")
|
||||||
|
val url = "$baseUrl/api/chapters?postId=$id&skip=0&take=1000&order=desc&userid="
|
||||||
|
|
||||||
|
return GET(url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val data = response.parseAs<Post<ChapterListResponse>>()
|
val data = response.parseAs<Post<ChapterListResponse>>()
|
||||||
|
@ -138,7 +135,7 @@ abstract class Iken(
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
return document.select("main section > img").mapIndexed { idx, img ->
|
return document.select("main section img").mapIndexed { idx, img ->
|
||||||
Page(idx, imageUrl = img.absUrl("src"))
|
Page(idx, imageUrl = img.absUrl("src"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
plugins {
|
|
||||||
id("lib-multisrc")
|
|
||||||
}
|
|
||||||
|
|
||||||
baseVersionCode = 6
|
|
|
@ -5,7 +5,6 @@ import android.content.SharedPreferences
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.await
|
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
@ -32,8 +31,18 @@ abstract class MangaThemesiaAlt(
|
||||||
private val randomUrlPrefKey: String = "pref_auto_random_url",
|
private val randomUrlPrefKey: String = "pref_auto_random_url",
|
||||||
) : MangaThemesia(name, baseUrl, lang, mangaUrlDirectory, dateFormat), ConfigurableSource {
|
) : MangaThemesia(name, baseUrl, lang, mangaUrlDirectory, dateFormat), ConfigurableSource {
|
||||||
|
|
||||||
|
protected open val listUrl = "$mangaUrlDirectory/list-mode/"
|
||||||
|
protected open val listSelector = "div#content div.soralist ul li a.series"
|
||||||
|
|
||||||
protected val preferences: SharedPreferences by lazy {
|
protected val preferences: SharedPreferences by lazy {
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000).also {
|
||||||
|
if (it.contains("__random_part_cache")) {
|
||||||
|
it.edit().remove("__random_part_cache").apply()
|
||||||
|
}
|
||||||
|
if (it.contains("titles_without_random_part")) {
|
||||||
|
it.edit().remove("titles_without_random_part").apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
@ -47,183 +56,11 @@ abstract class MangaThemesiaAlt(
|
||||||
|
|
||||||
private fun getRandomUrlPref() = preferences.getBoolean(randomUrlPrefKey, true)
|
private fun getRandomUrlPref() = preferences.getBoolean(randomUrlPrefKey, true)
|
||||||
|
|
||||||
private var randomPartCache = SuspendLazy(::getUpdatedRandomPart) { preferences.randomPartCache = it }
|
|
||||||
|
|
||||||
// cache in preference for webview urls
|
|
||||||
private var SharedPreferences.randomPartCache: String
|
|
||||||
get() = getString("__random_part_cache", "")!!
|
|
||||||
set(newValue) = edit().putString("__random_part_cache", newValue).apply()
|
|
||||||
|
|
||||||
// some new titles don't have random part
|
|
||||||
// se we save their slug and when they
|
|
||||||
// finally add it, we remove the slug in the interceptor
|
|
||||||
private var SharedPreferences.titlesWithoutRandomPart: MutableSet<String>
|
|
||||||
get() {
|
|
||||||
val value = getString("titles_without_random_part", null)
|
|
||||||
?: return mutableSetOf()
|
|
||||||
|
|
||||||
return json.decodeFromString(value)
|
|
||||||
}
|
|
||||||
set(newValue) {
|
|
||||||
val encodedValue = json.encodeToString(newValue)
|
|
||||||
|
|
||||||
edit().putString("titles_without_random_part", encodedValue).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun getRandomPartFromUrl(url: String): String {
|
|
||||||
val slug = url
|
|
||||||
.removeSuffix("/")
|
|
||||||
.substringAfterLast("/")
|
|
||||||
|
|
||||||
return slugRegex.find(slug)?.groupValues?.get(1) ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun getRandomPartFromResponse(response: Response): String {
|
|
||||||
return response.asJsoup()
|
|
||||||
.selectFirst(searchMangaSelector())!!
|
|
||||||
.select("a").attr("href")
|
|
||||||
.let(::getRandomPartFromUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected suspend fun getUpdatedRandomPart(): String =
|
|
||||||
client.newCall(GET("$baseUrl$mangaUrlDirectory/", headers))
|
|
||||||
.await()
|
|
||||||
.use(::getRandomPartFromResponse)
|
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage {
|
|
||||||
val mp = super.searchMangaParse(response)
|
|
||||||
|
|
||||||
if (!getRandomUrlPref()) return mp
|
|
||||||
|
|
||||||
// extract random part during browsing to avoid extra call
|
|
||||||
mp.mangas.firstOrNull()?.run {
|
|
||||||
val randomPart = getRandomPartFromUrl(url)
|
|
||||||
|
|
||||||
if (randomPart.isNotEmpty()) {
|
|
||||||
randomPartCache.set(randomPart)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mangas = mp.mangas.toPermanentMangaUrls()
|
|
||||||
|
|
||||||
return MangasPage(mangas, mp.hasNextPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun List<SManga>.toPermanentMangaUrls(): List<SManga> {
|
|
||||||
// some absolutely new titles don't have the random part yet
|
|
||||||
// save them so we know where to not apply it
|
|
||||||
val foundTitlesWithoutRandomPart = mutableSetOf<String>()
|
|
||||||
|
|
||||||
for (i in indices) {
|
|
||||||
val slug = this[i].url
|
|
||||||
.removeSuffix("/")
|
|
||||||
.substringAfterLast("/")
|
|
||||||
|
|
||||||
if (slugRegex.find(slug)?.groupValues?.get(1) == null) {
|
|
||||||
foundTitlesWithoutRandomPart.add(slug)
|
|
||||||
}
|
|
||||||
|
|
||||||
val permaSlug = slug
|
|
||||||
.replaceFirst(slugRegex, "")
|
|
||||||
|
|
||||||
this[i].url = "$mangaUrlDirectory/$permaSlug/"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundTitlesWithoutRandomPart.isNotEmpty()) {
|
|
||||||
foundTitlesWithoutRandomPart.addAll(preferences.titlesWithoutRandomPart)
|
|
||||||
|
|
||||||
preferences.titlesWithoutRandomPart = foundTitlesWithoutRandomPart
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open val slugRegex = Regex("""^(\d+-)""")
|
|
||||||
|
|
||||||
override val client = super.client.newBuilder()
|
|
||||||
.addInterceptor { chain ->
|
|
||||||
val request = chain.request()
|
|
||||||
val response = chain.proceed(request)
|
|
||||||
|
|
||||||
if (request.url.fragment != "titlesWithoutRandomPart") {
|
|
||||||
return@addInterceptor response
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.isSuccessful && response.code == 404) {
|
|
||||||
response.close()
|
|
||||||
|
|
||||||
val slug = request.url.toString()
|
|
||||||
.substringBefore("#")
|
|
||||||
.removeSuffix("/")
|
|
||||||
.substringAfterLast("/")
|
|
||||||
|
|
||||||
preferences.titlesWithoutRandomPart.run {
|
|
||||||
remove(slug)
|
|
||||||
|
|
||||||
preferences.titlesWithoutRandomPart = this
|
|
||||||
}
|
|
||||||
|
|
||||||
val randomPart = randomPartCache.blockingGet()
|
|
||||||
val newRequest = request.newBuilder()
|
|
||||||
.url("$baseUrl$mangaUrlDirectory/$randomPart$slug/")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return@addInterceptor chain.proceed(newRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
return@addInterceptor response
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
|
||||||
if (!getRandomUrlPref()) return super.mangaDetailsRequest(manga)
|
|
||||||
|
|
||||||
val slug = manga.url
|
|
||||||
.substringBefore("#")
|
|
||||||
.removeSuffix("/")
|
|
||||||
.substringAfterLast("/")
|
|
||||||
.replaceFirst(slugRegex, "")
|
|
||||||
|
|
||||||
if (preferences.titlesWithoutRandomPart.contains(slug)) {
|
|
||||||
return GET("$baseUrl${manga.url}#titlesWithoutRandomPart")
|
|
||||||
}
|
|
||||||
|
|
||||||
val randomPart = randomPartCache.blockingGet()
|
|
||||||
|
|
||||||
return GET("$baseUrl$mangaUrlDirectory/$randomPart$slug/", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga): String {
|
|
||||||
if (!getRandomUrlPref()) return super.getMangaUrl(manga)
|
|
||||||
|
|
||||||
val slug = manga.url
|
|
||||||
.substringBefore("#")
|
|
||||||
.removeSuffix("/")
|
|
||||||
.substringAfterLast("/")
|
|
||||||
.replaceFirst(slugRegex, "")
|
|
||||||
|
|
||||||
if (preferences.titlesWithoutRandomPart.contains(slug)) {
|
|
||||||
return "$baseUrl${manga.url}"
|
|
||||||
}
|
|
||||||
|
|
||||||
val randomPart = randomPartCache.peek() ?: preferences.randomPartCache
|
|
||||||
|
|
||||||
return "$baseUrl$mangaUrlDirectory/$randomPart$slug/"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class SuspendLazy(
|
|
||||||
private val initializer: suspend () -> String,
|
|
||||||
private val saveCache: (String) -> Unit,
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
private var cachedValue: SoftReference<String>? = null
|
private var cachedValue: SoftReference<Map<String, String>>? = null
|
||||||
private var fetchTime = 0L
|
private var fetchTime = 0L
|
||||||
|
|
||||||
suspend fun get(): String {
|
private suspend fun getUrlMapInternal(): Map<String, String> {
|
||||||
if (fetchTime + 3600000 < System.currentTimeMillis()) {
|
if (fetchTime + 3600000 < System.currentTimeMillis()) {
|
||||||
// reset cache
|
// reset cache
|
||||||
cachedValue = null
|
cachedValue = null
|
||||||
|
@ -238,22 +75,104 @@ internal class SuspendLazy(
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
initializer().also { set(it) }
|
fetchUrlMap().also {
|
||||||
|
cachedValue = SoftReference(it)
|
||||||
|
fetchTime = System.currentTimeMillis()
|
||||||
|
preferences.urlMapCache = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun set(newVal: String) {
|
protected open fun fetchUrlMap(): Map<String, String> {
|
||||||
cachedValue = SoftReference(newVal)
|
client.newCall(GET("$baseUrl$listUrl", headers)).execute().use { response ->
|
||||||
fetchTime = System.currentTimeMillis()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
saveCache(newVal)
|
return document.select(listSelector).associate {
|
||||||
|
val url = it.absUrl("href")
|
||||||
|
|
||||||
|
val slug = url.removeSuffix("/")
|
||||||
|
.substringAfterLast("/")
|
||||||
|
|
||||||
|
val permaSlug = slug
|
||||||
|
.replaceFirst(slugRegex, "")
|
||||||
|
|
||||||
|
permaSlug to slug
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun peek(): String? {
|
protected fun getUrlMap(cached: Boolean = false): Map<String, String> {
|
||||||
return cachedValue?.get()
|
return if (cached && cachedValue == null) {
|
||||||
|
preferences.urlMapCache
|
||||||
|
} else {
|
||||||
|
runBlocking { getUrlMapInternal() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun blockingGet(): String {
|
// cache in preference for webview urls
|
||||||
return runBlocking { get() }
|
private var SharedPreferences.urlMapCache: Map<String, String>
|
||||||
|
get(): Map<String, String> {
|
||||||
|
val value = getString("url_map_cache", "{}")!!
|
||||||
|
return try {
|
||||||
|
json.decodeFromString(value)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set(newMap) = edit().putString("url_map_cache", json.encodeToString(newMap)).apply()
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
val mp = super.searchMangaParse(response)
|
||||||
|
|
||||||
|
if (!getRandomUrlPref()) return mp
|
||||||
|
|
||||||
|
val mangas = mp.mangas.toPermanentMangaUrls()
|
||||||
|
|
||||||
|
return MangasPage(mangas, mp.hasNextPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun List<SManga>.toPermanentMangaUrls(): List<SManga> {
|
||||||
|
return onEach {
|
||||||
|
val slug = it.url
|
||||||
|
.removeSuffix("/")
|
||||||
|
.substringAfterLast("/")
|
||||||
|
|
||||||
|
val permaSlug = slug
|
||||||
|
.replaceFirst(slugRegex, "")
|
||||||
|
|
||||||
|
it.url = "$mangaUrlDirectory/$permaSlug/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open val slugRegex = Regex("""^(\d+-)""")
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
if (!getRandomUrlPref()) return super.mangaDetailsRequest(manga)
|
||||||
|
|
||||||
|
val slug = manga.url
|
||||||
|
.substringBefore("#")
|
||||||
|
.removeSuffix("/")
|
||||||
|
.substringAfterLast("/")
|
||||||
|
.replaceFirst(slugRegex, "")
|
||||||
|
|
||||||
|
val randomSlug = getUrlMap()[slug] ?: slug
|
||||||
|
|
||||||
|
return GET("$baseUrl$mangaUrlDirectory/$randomSlug/", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMangaUrl(manga: SManga): String {
|
||||||
|
if (!getRandomUrlPref()) return super.getMangaUrl(manga)
|
||||||
|
|
||||||
|
val slug = manga.url
|
||||||
|
.substringBefore("#")
|
||||||
|
.removeSuffix("/")
|
||||||
|
.substringAfterLast("/")
|
||||||
|
.replaceFirst(slugRegex, "")
|
||||||
|
|
||||||
|
val randomSlug = getUrlMap(true)[slug] ?: slug
|
||||||
|
|
||||||
|
return "$baseUrl$mangaUrlDirectory/$randomSlug/"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Bato.to'
|
extName = 'Bato.to'
|
||||||
extClass = '.BatoToFactory'
|
extClass = '.BatoToFactory'
|
||||||
extVersionCode = 36
|
extVersionCode = 37
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -384,11 +384,14 @@ open class BatoTo(
|
||||||
val chapter = SChapter.create()
|
val chapter = SChapter.create()
|
||||||
val urlElement = element.select("a.chapt")
|
val urlElement = element.select("a.chapt")
|
||||||
val group = element.select("div.extra > a:not(.ps-3)").text()
|
val group = element.select("div.extra > a:not(.ps-3)").text()
|
||||||
|
val user = element.select("div.extra > a.ps-3").text()
|
||||||
val time = element.select("div.extra > i.ps-3").text()
|
val time = element.select("div.extra > i.ps-3").text()
|
||||||
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
||||||
chapter.name = urlElement.text()
|
chapter.name = urlElement.text()
|
||||||
if (group != "") {
|
chapter.scanlator = when {
|
||||||
chapter.scanlator = group
|
group.isNotBlank() -> group
|
||||||
|
user.isNotBlank() -> user
|
||||||
|
else -> "Unknown"
|
||||||
}
|
}
|
||||||
if (time != "") {
|
if (time != "") {
|
||||||
chapter.date_upload = parseChapterDate(time)
|
chapter.date_upload = parseChapterDate(time)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Comick'
|
extName = 'Comick'
|
||||||
extClass = '.ComickFactory'
|
extClass = '.ComickFactory'
|
||||||
extVersionCode = 46
|
extVersionCode = 47
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -463,7 +463,16 @@ abstract class Comick(
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
val result = response.parseAs<PageList>()
|
val result = response.parseAs<PageList>()
|
||||||
return result.chapter.images.mapIndexedNotNull { index, data ->
|
val images = result.chapter.images.ifEmpty {
|
||||||
|
// cache busting
|
||||||
|
val url = response.request.url.newBuilder()
|
||||||
|
.addQueryParameter("_", System.currentTimeMillis().toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(GET(url, headers)).execute()
|
||||||
|
.parseAs<PageList>().chapter.images
|
||||||
|
}
|
||||||
|
return images.mapIndexedNotNull { index, data ->
|
||||||
if (data.url == null) null else Page(index = index, imageUrl = data.url)
|
if (data.url == null) null else Page(index = index, imageUrl = data.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'MangaHot'
|
extName = 'FoamGirl'
|
||||||
extClass = '.MangaHot'
|
extClass = '.FoamGirl'
|
||||||
extVersionCode = 1
|
extVersionCode = 1
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,127 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.foamgirl
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
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.ParsedHttpSource
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.Request
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import rx.Observable
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class FoamGirl() : ParsedHttpSource() {
|
||||||
|
override val baseUrl = "https://foamgirl.net"
|
||||||
|
override val lang = "all"
|
||||||
|
override val name = "FoamGirl"
|
||||||
|
override val supportsLatest = false
|
||||||
|
|
||||||
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
|
.add("Referer", "$baseUrl/")
|
||||||
|
|
||||||
|
// Popular
|
||||||
|
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
|
||||||
|
thumbnail_url = element.select("img").attr("data-original")
|
||||||
|
title = element.select("a.meta-title").text()
|
||||||
|
setUrlWithoutDomain(element.select("a").attr("href"))
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||||
|
return Observable.just(manga)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(document: Document): SManga {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
override fun popularMangaNextPageSelector() = "a.next"
|
||||||
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
return GET("$baseUrl/page/$page", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaSelector() = ".update_area .i_list"
|
||||||
|
|
||||||
|
// Search
|
||||||
|
|
||||||
|
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
|
||||||
|
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
return GET(
|
||||||
|
baseUrl.toHttpUrl().newBuilder().apply {
|
||||||
|
addPathSegment("page")
|
||||||
|
addPathSegment("$page")
|
||||||
|
addQueryParameter("post_type", "post")
|
||||||
|
addQueryParameter("s", query)
|
||||||
|
}.build(),
|
||||||
|
headers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaSelector() = popularMangaSelector()
|
||||||
|
|
||||||
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
val pages = mutableListOf<Page>()
|
||||||
|
val imageCount = document.select(".post_title_topimg").text().substringAfter("(").substringBefore("P").toInt()
|
||||||
|
val imageUrl = document.select(".imageclick-imgbox").attr("href").toHttpUrl()
|
||||||
|
val imagePrefix = imageUrl.pathSegments.last().substringBefore(".").toLong() / 10
|
||||||
|
for (i in 0 until imageCount) {
|
||||||
|
pages.add(
|
||||||
|
Page(
|
||||||
|
i,
|
||||||
|
imageUrl = imageUrl.newBuilder().apply {
|
||||||
|
removePathSegment(imageUrl.pathSize - 1)
|
||||||
|
addPathSegment("${imagePrefix}${i + 2}.jpg")
|
||||||
|
}.build().toString(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
||||||
|
setUrlWithoutDomain(element.select("link[rel=canonical]").attr("abs:href"))
|
||||||
|
chapter_number = 0F
|
||||||
|
name = "GALLERY"
|
||||||
|
date_upload = getDate(element.select("span.image-info-time").text().substring(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListSelector() = "html"
|
||||||
|
|
||||||
|
// Pages
|
||||||
|
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun latestUpdatesFromElement(element: Element): SManga {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesNextPageSelector(): String? {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesSelector(): String {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDate(str: String): Long {
|
||||||
|
return try {
|
||||||
|
DATE_FORMAT.parse(str)?.time ?: 0L
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val DATE_FORMAT by lazy {
|
||||||
|
SimpleDateFormat("yyyy.M.d", Locale.ENGLISH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Hentai Cosplay'
|
extName = 'Hentai Cosplay'
|
||||||
extClass = '.HentaiCosplay'
|
extClass = '.HentaiCosplay'
|
||||||
extVersionCode = 3
|
extVersionCode = 4
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class HentaiCosplay : HttpSource() {
|
||||||
|
|
||||||
override val name = "Hentai Cosplay"
|
override val name = "Hentai Cosplay"
|
||||||
|
|
||||||
override val baseUrl = "https://hentai-cosplays.com"
|
override val baseUrl = "https://hentai-cosplay-xxx.com"
|
||||||
|
|
||||||
override val lang = "all"
|
override val lang = "all"
|
||||||
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.all.mangatopsite
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class MangaTopSite : Madara(
|
|
||||||
"MangaTop.site",
|
|
||||||
"https://mangatop.site",
|
|
||||||
"all",
|
|
||||||
dateFormat = SimpleDateFormat("d MMM yyyy", Locale.ENGLISH),
|
|
||||||
) {
|
|
||||||
override val useNewChapterEndpoint = false
|
|
||||||
override val chapterUrlSuffix = ""
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'NHentai'
|
extName = 'NHentai'
|
||||||
extClass = '.NHFactory'
|
extClass = '.NHFactory'
|
||||||
extVersionCode = 40
|
extVersionCode = 41
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,7 @@ open class NHentai(
|
||||||
thumbnail_url = document.select("#cover > a > img").attr("data-src")
|
thumbnail_url = document.select("#cover > a > img").attr("data-src")
|
||||||
status = SManga.COMPLETED
|
status = SManga.COMPLETED
|
||||||
artist = getArtists(document)
|
artist = getArtists(document)
|
||||||
author = artist
|
author = getGroups(document)
|
||||||
// Some people want these additional details in description
|
// Some people want these additional details in description
|
||||||
description = "Full English and Japanese titles:\n"
|
description = "Full English and Japanese titles:\n"
|
||||||
.plus("$fullTitle\n")
|
.plus("$fullTitle\n")
|
||||||
|
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'Thunder Scans'
|
extName = 'Thunder Scans'
|
||||||
extClass = '.ThunderScansFactory'
|
extClass = '.ThunderScansFactory'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://en-thunderepic.com'
|
baseUrl = 'https://en-thunderscans.com'
|
||||||
overrideVersionCode = 2
|
overrideVersionCode = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -14,14 +14,14 @@ class ThunderScansFactory : SourceFactory {
|
||||||
|
|
||||||
class ThunderScansAR : MangaThemesiaAlt(
|
class ThunderScansAR : MangaThemesiaAlt(
|
||||||
"Thunder Scans",
|
"Thunder Scans",
|
||||||
"https://ar-thunderepic.com",
|
"https://thunderscans.com",
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMM d, yyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMM d, yyy", Locale("ar")),
|
||||||
)
|
)
|
||||||
|
|
||||||
class ThunderScansEN : MangaThemesiaAlt(
|
class ThunderScansEN : MangaThemesiaAlt(
|
||||||
"Thunder Scans",
|
"Thunder Scans",
|
||||||
"https://en-thunderepic.com",
|
"https://en-thunderscans.com",
|
||||||
"en",
|
"en",
|
||||||
mangaUrlDirectory = "/comics",
|
mangaUrlDirectory = "/comics",
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
ext {
|
||||||
|
extName = 'MangaHub'
|
||||||
|
extClass = '.MangaHub'
|
||||||
|
themePkg = 'zeistmanga'
|
||||||
|
baseUrl = 'https://www.mangahub.link'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
isNsfw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.ar.mangahub
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
|
||||||
|
class MangaHub : ZeistManga(
|
||||||
|
"MangaHub",
|
||||||
|
"https://www.mangahub.link",
|
||||||
|
"ar",
|
||||||
|
) {
|
||||||
|
override val client = super.client.newBuilder()
|
||||||
|
.rateLimit(3)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override val popularMangaSelector = "div#PopularPosts2 article"
|
||||||
|
override val popularMangaSelectorTitle = "h3"
|
||||||
|
override val popularMangaSelectorUrl = "a"
|
||||||
|
|
||||||
|
override val mangaDetailsSelector = ".grid.gap-5.gta-series"
|
||||||
|
override val mangaDetailsSelectorGenres = "dt:contains(التصنيف) + dd a[rel=tag]"
|
||||||
|
|
||||||
|
override val pageListSelector = "article#reader .separator, div.image-container"
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'MangaNoon'
|
extName = 'MangaNoon'
|
||||||
extClass = '.MangaNoon'
|
extClass = '.MangaNoon'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://manjanoon.co'
|
baseUrl = 'https://noonscan.net'
|
||||||
overrideVersionCode = 3
|
overrideVersionCode = 4
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.util.Calendar
|
||||||
|
|
||||||
class MangaNoon : MangaThemesia(
|
class MangaNoon : MangaThemesia(
|
||||||
"مانجا نون",
|
"مانجا نون",
|
||||||
"https://manjanoon.co",
|
"https://noonscan.net",
|
||||||
"ar",
|
"ar",
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
||||||
extClass = '.MangaPro'
|
extClass = '.MangaPro'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://promanga.pro'
|
baseUrl = 'https://promanga.pro'
|
||||||
overrideVersionCode = 2
|
overrideVersionCode = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package eu.kanade.tachiyomi.extension.ar.mangapro
|
package eu.kanade.tachiyomi.extension.ar.mangapro
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
@ -11,4 +14,30 @@ class MangaPro : MangaThemesia(
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||||
) {
|
) {
|
||||||
override val versionId = 3
|
override val versionId = 3
|
||||||
|
|
||||||
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
return super.pageListParse(document).onEach {
|
||||||
|
val httpUrl = it.imageUrl!!.toHttpUrl()
|
||||||
|
|
||||||
|
if (wpImgRegex.containsMatchIn(httpUrl.host)) {
|
||||||
|
it.imageUrl = StringBuilder().apply {
|
||||||
|
val ssl = httpUrl.queryParameter("ssl")
|
||||||
|
when (ssl) {
|
||||||
|
null -> append(httpUrl.scheme)
|
||||||
|
"0" -> append("http")
|
||||||
|
else -> append("https")
|
||||||
|
}
|
||||||
|
append("://")
|
||||||
|
append(httpUrl.pathSegments.joinToString("/"))
|
||||||
|
val search = httpUrl.queryParameter("q")
|
||||||
|
if (search != null) {
|
||||||
|
append("?q=")
|
||||||
|
append(search)
|
||||||
|
}
|
||||||
|
}.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val wpImgRegex = Regex("""i\d+\.wp\.com""")
|
||||||
|
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'MangaSwat'
|
extName = 'MangaSwat'
|
||||||
extClass = '.MangaSwat'
|
extClass = '.MangaSwat'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://maxlevelteam.com'
|
baseUrl = 'https://tatwt.com'
|
||||||
overrideVersionCode = 20
|
overrideVersionCode = 21
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -24,7 +24,7 @@ import java.util.Locale
|
||||||
class MangaSwat :
|
class MangaSwat :
|
||||||
MangaThemesia(
|
MangaThemesia(
|
||||||
"MangaSwat",
|
"MangaSwat",
|
||||||
"https://maxlevelteam.com",
|
"https://tatwt.com",
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'Rocks Manga'
|
extName = 'Rocks Manga'
|
||||||
extClass = '.RocksManga'
|
extClass = '.RocksManga'
|
||||||
themePkg = 'madara'
|
themePkg = 'madara'
|
||||||
baseUrl = 'https://rocks-manga.com'
|
baseUrl = 'https://rocksmanga.com'
|
||||||
overrideVersionCode = 0
|
overrideVersionCode = 1
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,39 +2,65 @@ package eu.kanade.tachiyomi.extension.ar.rocksmanga
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class RocksManga : Madara(
|
class RocksManga : Madara(
|
||||||
"Rocks Manga",
|
"Rocks Manga",
|
||||||
"https://rocks-manga.com",
|
"https://rocksmanga.com",
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("ar")),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun popularMangaSelector() = ".shido-manga"
|
override fun popularMangaSelector() = "div.page-content-listing > .manga"
|
||||||
override val popularMangaUrlSelector = "a.s-manga-title"
|
override val popularMangaUrlSelector = "div.manga-poster a"
|
||||||
override val mangaDetailsSelectorTitle = ".title"
|
|
||||||
override val mangaDetailsSelectorAuthor = ".heading:contains(المؤلف:) + .content a"
|
override fun popularMangaFromElement(element: Element): SManga {
|
||||||
override val mangaDetailsSelectorArtist = ".heading:contains(الرسام:) + .content a"
|
return super.popularMangaFromElement(element).apply {
|
||||||
|
title = element.selectFirst(popularMangaUrlSelector)!!.attr("title")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaSelector() = "#manga-search-results .manga-item"
|
||||||
|
|
||||||
|
override fun searchMangaFromElement(element: Element): SManga {
|
||||||
|
val manga = SManga.create()
|
||||||
|
|
||||||
|
with(element) {
|
||||||
|
selectFirst("a.cover")!!.let {
|
||||||
|
manga.setUrlWithoutDomain(it.attr("abs:href"))
|
||||||
|
manga.title = it.attr("title")
|
||||||
|
}
|
||||||
|
selectFirst("img")?.let {
|
||||||
|
manga.thumbnail_url = imageFromElement(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return manga
|
||||||
|
}
|
||||||
|
|
||||||
|
override val mangaDetailsSelectorTitle = ".manga-title"
|
||||||
|
override val mangaDetailsSelectorAuthor = "div.meta span:contains(المؤلف:) + span a"
|
||||||
|
override val mangaDetailsSelectorArtist = "div.meta span:contains(الرسام:) + span a"
|
||||||
override val mangaDetailsSelectorStatus = ".status"
|
override val mangaDetailsSelectorStatus = ".status"
|
||||||
override val mangaDetailsSelectorDescription = ".story"
|
override val mangaDetailsSelectorDescription = "div.description"
|
||||||
override val mangaDetailsSelectorThumbnail = ".profile-manga .poster img"
|
override val mangaDetailsSelectorThumbnail = ".manga-poster img"
|
||||||
override val mangaDetailsSelectorGenre = ".heading:contains(التصنيف:) + .content a"
|
override val mangaDetailsSelectorGenre = "div.meta span:contains(التصنيف:) + span a"
|
||||||
override val altNameSelector = ".other-name"
|
override val altNameSelector = "div.alternative"
|
||||||
override fun chapterListSelector() = "#chapter-list li.chapter-item"
|
override fun chapterListSelector() = ".chapters-list li.chapter-item"
|
||||||
override fun chapterDateSelector() = ".ch-post-time"
|
override fun chapterDateSelector() = ".chapter-release-date"
|
||||||
override val pageListParseSelector = ".reading-content img"
|
override val pageListParseSelector = ".chapter-reading-page img"
|
||||||
|
|
||||||
override val useLoadMoreRequest = LoadMoreStrategy.Never
|
override val useLoadMoreRequest = LoadMoreStrategy.Never
|
||||||
override val useNewChapterEndpoint = false
|
override val useNewChapterEndpoint = false
|
||||||
|
override val fetchGenres = false
|
||||||
override val filterNonMangaItems = false
|
override val filterNonMangaItems = false
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element): SChapter {
|
override fun chapterFromElement(element: Element): SChapter {
|
||||||
val chapter = super.chapterFromElement(element)
|
val chapter = super.chapterFromElement(element)
|
||||||
chapter.name = element.selectFirst(".detail-ch")!!.text()
|
chapter.name = element.selectFirst(".num")!!.text()
|
||||||
return chapter
|
return chapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Team X'
|
extName = 'Team X'
|
||||||
extClass = '.TeamX'
|
extClass = '.TeamX'
|
||||||
extVersionCode = 17
|
extVersionCode = 18
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
package eu.kanade.tachiyomi.extension.ar.teamx
|
package eu.kanade.tachiyomi.extension.ar.teamx
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
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.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
@ -14,15 +19,19 @@ import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class TeamX : ParsedHttpSource() {
|
class TeamX : ParsedHttpSource(), ConfigurableSource {
|
||||||
|
|
||||||
override val name = "Team X"
|
override val name = "Team X"
|
||||||
|
|
||||||
override val baseUrl = "https://teamxnovel.com"
|
private val defaultBaseUrl = "https://teamoney.site"
|
||||||
|
|
||||||
|
override val baseUrl by lazy { getPrefBaseUrl() }
|
||||||
|
|
||||||
override val lang = "ar"
|
override val lang = "ar"
|
||||||
|
|
||||||
|
@ -34,6 +43,10 @@ class TeamX : ParsedHttpSource() {
|
||||||
.rateLimit(10, 1, TimeUnit.SECONDS)
|
.rateLimit(10, 1, TimeUnit.SECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
private val preferences: SharedPreferences by lazy {
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
}
|
||||||
|
|
||||||
// Popular
|
// Popular
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
@ -130,6 +143,14 @@ class TeamX : ParsedHttpSource() {
|
||||||
}
|
}
|
||||||
genre = document.select("div.review-author-info a").joinToString { it.text() }
|
genre = document.select("div.review-author-info a").joinToString { it.text() }
|
||||||
thumbnail_url = document.select("div.text-right img").first()!!.absUrl("src")
|
thumbnail_url = document.select("div.text-right img").first()!!.absUrl("src")
|
||||||
|
status = document
|
||||||
|
.selectFirst(".full-list-info > small:first-child:contains(الحالة) + small")
|
||||||
|
?.text()
|
||||||
|
.toStatus()
|
||||||
|
author = document
|
||||||
|
.selectFirst(".full-list-info > small:first-child:contains(الرسام) + small")
|
||||||
|
?.text()
|
||||||
|
?.takeIf { it != "غير معروف" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +208,14 @@ class TeamX : ParsedHttpSource() {
|
||||||
}.getOrNull() ?: 0
|
}.getOrNull() ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun String?.toStatus() = when (this) {
|
||||||
|
"مستمرة" -> SManga.ONGOING
|
||||||
|
"قادم قريبًا" -> SManga.ONGOING // "coming soon"
|
||||||
|
"مكتمل" -> SManga.COMPLETED
|
||||||
|
"متوقف" -> SManga.ON_HIATUS
|
||||||
|
else -> SManga.UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
@ -196,4 +225,41 @@ class TeamX : ParsedHttpSource() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val RESTART_APP = ".لتطبيق الإعدادات الجديدة أعد تشغيل التطبيق"
|
||||||
|
private const val BASE_URL_PREF_TITLE = "تعديل الرابط"
|
||||||
|
private const val BASE_URL_PREF = "overrideBaseUrl"
|
||||||
|
private const val BASE_URL_PREF_SUMMARY = ".للاستخدام المؤقت. تحديث التطبيق سيؤدي الى حذف الإعدادات"
|
||||||
|
private const val DEFAULT_BASE_URL_PREF = "defaultBaseUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
val baseUrlPref = androidx.preference.EditTextPreference(screen.context).apply {
|
||||||
|
key = BASE_URL_PREF
|
||||||
|
title = BASE_URL_PREF_TITLE
|
||||||
|
summary = BASE_URL_PREF_SUMMARY
|
||||||
|
this.setDefaultValue(defaultBaseUrl)
|
||||||
|
dialogTitle = BASE_URL_PREF_TITLE
|
||||||
|
dialogMessage = "Default: $defaultBaseUrl"
|
||||||
|
|
||||||
|
setOnPreferenceChangeListener { _, _ ->
|
||||||
|
Toast.makeText(screen.context, RESTART_APP, Toast.LENGTH_LONG).show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
screen.addPreference(baseUrlPref)
|
||||||
|
}
|
||||||
|
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!!
|
||||||
|
|
||||||
|
init {
|
||||||
|
preferences.getString(DEFAULT_BASE_URL_PREF, null).let { prefDefaultBaseUrl ->
|
||||||
|
if (prefDefaultBaseUrl != defaultBaseUrl) {
|
||||||
|
preferences.edit()
|
||||||
|
.putString(BASE_URL_PREF, defaultBaseUrl)
|
||||||
|
.putString(DEFAULT_BASE_URL_PREF, defaultBaseUrl)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ext {
|
||||||
|
extName = 'Altay Scans'
|
||||||
|
extClass = '.AltayScans'
|
||||||
|
themePkg = 'mangathemesia'
|
||||||
|
baseUrl = 'https://altayscans.com'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,14 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.altayscans
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
|
||||||
|
class AltayScans : MangaThemesia(
|
||||||
|
"Altay Scans",
|
||||||
|
"https://altayscans.com",
|
||||||
|
"en",
|
||||||
|
) {
|
||||||
|
override val client = super.client.newBuilder()
|
||||||
|
.rateLimit(3)
|
||||||
|
.build()
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'Ansh Scans'
|
|
||||||
extClass = '.AnshScans'
|
|
||||||
themePkg = 'madara'
|
|
||||||
baseUrl = 'https://anshscans.org'
|
|
||||||
overrideVersionCode = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 18 KiB |
|
@ -1,11 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.anshscans
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
|
||||||
|
|
||||||
class AnshScans : Madara("Ansh Scans", "https://anshscans.org", "en") {
|
|
||||||
override val useNewChapterEndpoint = true
|
|
||||||
|
|
||||||
override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content"
|
|
||||||
|
|
||||||
override val mangaSubString = "comic"
|
|
||||||
}
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ext {
|
||||||
|
extName = 'Astra Scans'
|
||||||
|
extClass = '.AstraScans'
|
||||||
|
themePkg = 'mangathemesia'
|
||||||
|
baseUrl = 'https://astrascans.org'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 23 KiB |
|
@ -0,0 +1,15 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.astrascans
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
|
||||||
|
class AstraScans : MangaThemesia(
|
||||||
|
"Astra Scans",
|
||||||
|
"https://astrascans.org",
|
||||||
|
"en",
|
||||||
|
"/series",
|
||||||
|
) {
|
||||||
|
override val client = super.client.newBuilder()
|
||||||
|
.rateLimit(3)
|
||||||
|
.build()
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Asura Scans'
|
extName = 'Asura Scans'
|
||||||
extClass = '.AsuraScans'
|
extClass = '.AsuraScans'
|
||||||
extVersionCode = 36
|
extVersionCode = 39
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -205,7 +205,12 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
|
||||||
description = document.selectFirst("span.font-medium.text-sm")?.text()
|
description = document.selectFirst("span.font-medium.text-sm")?.text()
|
||||||
author = document.selectFirst("div.grid > div:has(h3:eq(0):containsOwn(Author)) > h3:eq(1)")?.ownText()
|
author = document.selectFirst("div.grid > div:has(h3:eq(0):containsOwn(Author)) > h3:eq(1)")?.ownText()
|
||||||
artist = document.selectFirst("div.grid > div:has(h3:eq(0):containsOwn(Artist)) > h3:eq(1)")?.ownText()
|
artist = document.selectFirst("div.grid > div:has(h3:eq(0):containsOwn(Artist)) > h3:eq(1)")?.ownText()
|
||||||
genre = document.select("div[class^=space] > div.flex > button.text-white").joinToString { it.ownText() }
|
genre = buildList {
|
||||||
|
document.selectFirst("div.flex:has(h3:eq(0):containsOwn(type)) > h3:eq(1)")
|
||||||
|
?.ownText()?.let(::add)
|
||||||
|
document.select("div[class^=space] > div.flex > button.text-white")
|
||||||
|
.forEach { add(it.ownText()) }
|
||||||
|
}.joinToString()
|
||||||
status = parseStatus(document.selectFirst("div.flex:has(h3:eq(0):containsOwn(Status)) > h3:eq(1)")?.ownText())
|
status = parseStatus(document.selectFirst("div.flex:has(h3:eq(0):containsOwn(Status)) > h3:eq(1)")?.ownText())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,10 +234,10 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
||||||
|
|
||||||
override fun chapterListSelector() = "div.scrollbar-thumb-themecolor > a.block"
|
override fun chapterListSelector() = "div.scrollbar-thumb-themecolor > div.group"
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
||||||
setUrlWithoutDomain(element.attr("abs:href").toPermSlugIfNeeded())
|
setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href").toPermSlugIfNeeded())
|
||||||
name = element.selectFirst("h3:eq(0)")!!.text()
|
name = element.selectFirst("h3:eq(0)")!!.text()
|
||||||
date_upload = try {
|
date_upload = try {
|
||||||
val text = element.selectFirst("h3:eq(1)")!!.ownText()
|
val text = element.selectFirst("h3:eq(1)")!!.ownText()
|
||||||
|
@ -253,7 +258,7 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
return document.select("div > img[alt=chapter]").mapIndexed { i, element ->
|
return document.select("div > img[alt*=chapter]").mapIndexed { i, element ->
|
||||||
Page(i, imageUrl = element.attr("abs:src"))
|
Page(i, imageUrl = element.attr("abs:src"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'AgiToon'
|
extName = 'Atsumaru'
|
||||||
extClass = '.AgiToon'
|
extClass = '.Atsumaru'
|
||||||
extVersionCode = 1
|
extVersionCode = 1
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,156 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.atsumaru
|
||||||
|
|
||||||
|
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 kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
class Atsumaru : HttpSource() {
|
||||||
|
|
||||||
|
override val name = "Atsumaru"
|
||||||
|
|
||||||
|
override val baseUrl = "https://atsu.moe"
|
||||||
|
private val apiUrl = "$baseUrl/api/v1"
|
||||||
|
|
||||||
|
override val lang = "en"
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
override val client = network.cloudflareClient.newBuilder()
|
||||||
|
.rateLimit(2)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
|
.add("Referer", "$baseUrl/")
|
||||||
|
|
||||||
|
private fun apiHeadersBuilder() = headersBuilder().apply {
|
||||||
|
add("Accept", "*/*")
|
||||||
|
add("Host", apiUrl.toHttpUrl().host)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val apiHeaders by lazy { apiHeadersBuilder().build() }
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
// ============================== Popular ===============================
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
return GET("$apiUrl/layouts/s1/sliders/hotUpdates", apiHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
|
val data = response.parseAs<BrowseMangaDto>().items
|
||||||
|
|
||||||
|
return MangasPage(data.map { it.manga.toSManga() }, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================== Latest ===============================
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
|
return GET("$apiUrl/layouts/s1/latest-updates", apiHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
|
return popularMangaParse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================== Search ===============================
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val url = "$apiUrl/search".toHttpUrl().newBuilder()
|
||||||
|
.addPathSegment(query)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return GET(url, apiHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
val data = response.parseAs<SearchResultsDto>().hits
|
||||||
|
|
||||||
|
return MangasPage(data.map { it.info.toSManga() }, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================== Manga Details ============================
|
||||||
|
|
||||||
|
override fun getMangaUrl(manga: SManga): String {
|
||||||
|
return baseUrl + manga.url
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
return GET(apiUrl + manga.url, apiHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
|
return response.parseAs<MangaObjectDto>().manga.toSManga()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================== Chapters ==============================
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
return mangaDetailsRequest(manga)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
val chapterList = response.parseAs<MangaObjectDto>().manga.chapters!!.map {
|
||||||
|
it.toSChapter(response.request.url.pathSegments.last())
|
||||||
|
}
|
||||||
|
|
||||||
|
return chapterList.sortedWith(
|
||||||
|
compareBy(
|
||||||
|
{ it.chapter_number },
|
||||||
|
{ it.scanlator },
|
||||||
|
),
|
||||||
|
).reversed()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChapterUrl(chapter: SChapter): String {
|
||||||
|
val (slug, name) = chapter.url.split("/")
|
||||||
|
return "$baseUrl/read/s1/$slug/$name/1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================== Pages ================================
|
||||||
|
|
||||||
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
|
val (slug, name) = chapter.url.split("/")
|
||||||
|
return GET("$apiUrl/manga/s1/$slug#$name", apiHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val chapter = response.parseAs<MangaObjectDto>().manga.chapters!!.first {
|
||||||
|
it.name == response.request.url.fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
return chapter.pages.map { page ->
|
||||||
|
Page(page.name.toInt(), imageUrl = page.pageURLs.first())
|
||||||
|
}.sortedBy { it.index }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageRequest(page: Page): Request {
|
||||||
|
val imgHeaders = headersBuilder().apply {
|
||||||
|
add("Accept", "image/avif,image/webp,*/*")
|
||||||
|
add("Host", page.imageUrl!!.toHttpUrl().host)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return GET(page.imageUrl!!, imgHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response): String {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
|
private inline fun <reified T> Response.parseAs(): T {
|
||||||
|
return json.decodeFromString(body.string())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.atsumaru
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class BrowseMangaDto(
|
||||||
|
val items: List<MangaObjectDto>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class MangaObjectDto(
|
||||||
|
val manga: MangaDto,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class SearchResultsDto(
|
||||||
|
val hits: List<SearchMangaDto>,
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
class SearchMangaDto(
|
||||||
|
val info: MangaDto,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class MangaDto(
|
||||||
|
// Common
|
||||||
|
private val title: String,
|
||||||
|
private val cover: String,
|
||||||
|
private val slug: String,
|
||||||
|
|
||||||
|
// Details
|
||||||
|
private val authors: List<String>? = null,
|
||||||
|
private val description: String? = null,
|
||||||
|
private val genres: List<String>? = null,
|
||||||
|
private val statuses: List<String>? = null,
|
||||||
|
|
||||||
|
// Chapters
|
||||||
|
val chapters: List<ChapterDto>? = null,
|
||||||
|
) {
|
||||||
|
fun toSManga(): SManga = SManga.create().apply {
|
||||||
|
title = this@MangaDto.title
|
||||||
|
thumbnail_url = cover
|
||||||
|
url = "/manga/s1/$slug"
|
||||||
|
|
||||||
|
authors?.let {
|
||||||
|
author = it.joinToString()
|
||||||
|
}
|
||||||
|
description = this@MangaDto.description
|
||||||
|
genres?.let {
|
||||||
|
genre = it.joinToString()
|
||||||
|
}
|
||||||
|
statuses?.let {
|
||||||
|
status = when (it.first().lowercase().substringBefore(" ")) {
|
||||||
|
"ongoing" -> SManga.ONGOING
|
||||||
|
"complete" -> SManga.COMPLETED
|
||||||
|
else -> SManga.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class ChapterDto(
|
||||||
|
val pages: List<PageDto>,
|
||||||
|
val name: String,
|
||||||
|
private val type: String,
|
||||||
|
private val title: String? = null,
|
||||||
|
private val date: String? = null,
|
||||||
|
) {
|
||||||
|
fun toSChapter(slug: String): SChapter = SChapter.create().apply {
|
||||||
|
val chapterNumber = this@ChapterDto.name.replace("_", ".")
|
||||||
|
.filter { it.isDigit() || it == '.' }
|
||||||
|
|
||||||
|
name = buildString {
|
||||||
|
append("Chapter ")
|
||||||
|
append(chapterNumber)
|
||||||
|
if (title != null) {
|
||||||
|
append(" - ")
|
||||||
|
append(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url = "$slug/${this@ChapterDto.name}"
|
||||||
|
chapter_number = chapterNumber.toFloat()
|
||||||
|
scanlator = type.takeUnless { it == "Chapter" }
|
||||||
|
date?.let {
|
||||||
|
date_upload = parseDate(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseDate(dateStr: String): Long {
|
||||||
|
return try {
|
||||||
|
DATE_FORMAT.parse(dateStr)!!.time
|
||||||
|
} catch (_: ParseException) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val DATE_FORMAT by lazy {
|
||||||
|
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class PageDto(
|
||||||
|
val pageURLs: List<String>,
|
||||||
|
val name: String,
|
||||||
|
)
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'ColoredManga'
|
extName = 'ColoredManga'
|
||||||
extClass = '.ColoredManga'
|
extClass = '.ColoredManga'
|
||||||
extVersionCode = 3
|
extVersionCode = 35
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'Comic Scans'
|
|
||||||
extClass = '.ComicScans'
|
|
||||||
themePkg = 'madara'
|
|
||||||
baseUrl = 'https://www.comicscans.org'
|
|
||||||
overrideVersionCode = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
|
@ -1,7 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.comicscans
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
|
||||||
|
|
||||||
class ComicScans : Madara("Comic Scans", "https://www.comicscans.org", "en") {
|
|
||||||
override val useNewChapterEndpoint = true
|
|
||||||
}
|
|
|
@ -2,8 +2,8 @@ ext {
|
||||||
extName = 'EnryuManga'
|
extName = 'EnryuManga'
|
||||||
extClass = '.EnryuManga'
|
extClass = '.EnryuManga'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://enryumanga.com'
|
baseUrl = 'https://enryumanga.net'
|
||||||
overrideVersionCode = 0
|
overrideVersionCode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -2,4 +2,4 @@ package eu.kanade.tachiyomi.extension.en.enryumanga
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
|
||||||
class EnryuManga : MangaThemesia("EnryuManga", "https://enryumanga.com", "en")
|
class EnryuManga : MangaThemesia("EnryuManga", "https://enryumanga.net", "en")
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
ext {
|
||||||
|
extName = 'Eros Scans'
|
||||||
|
extClass = '.ErosScans'
|
||||||
|
themePkg = 'mangathemesia'
|
||||||
|
baseUrl = 'https://erosscans.xyz'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 40 KiB |
|
@ -0,0 +1,14 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.erosscans
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
|
||||||
|
class ErosScans : MangaThemesia(
|
||||||
|
"Eros Scans",
|
||||||
|
"https://erosscans.xyz",
|
||||||
|
"en",
|
||||||
|
) {
|
||||||
|
override val client = super.client.newBuilder()
|
||||||
|
.rateLimit(3)
|
||||||
|
.build()
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Fire Scans'
|
extName = 'Firecomics'
|
||||||
extClass = '.FireScans'
|
extClass = '.Firecomics'
|
||||||
themePkg = 'madara'
|
themePkg = 'madara'
|
||||||
baseUrl = 'https://firescans.xyz'
|
baseUrl = 'https://firecomics.org'
|
||||||
overrideVersionCode = 1
|
overrideVersionCode = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 68 KiB |
|
@ -11,8 +11,9 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
|
|
||||||
class FireScans : Madara("Fire Scans", "https://firescans.xyz", "en") {
|
class Firecomics : Madara("Firecomics", "https://firecomics.org", "en") {
|
||||||
|
|
||||||
|
override val id: Long = 5761461704760730187
|
||||||
override val client: OkHttpClient = super.client.newBuilder()
|
override val client: OkHttpClient = super.client.newBuilder()
|
||||||
.rateLimit(20, 5)
|
.rateLimit(20, 5)
|
||||||
.build()
|
.build()
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Hentai2Read'
|
extName = 'Hentai2Read'
|
||||||
extClass = '.Hentai2Read'
|
extClass = '.Hentai2Read'
|
||||||
extVersionCode = 16
|
extVersionCode = 17
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
ext {
|
||||||
|
extName = 'HentaiDex'
|
||||||
|
extClass = '.HentaiDex'
|
||||||
|
themePkg = 'mangathemesia'
|
||||||
|
baseUrl = 'https://dexhentai.com'
|
||||||
|
overrideVersionCode = 0
|
||||||
|
isNsfw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 3.6 KiB |