Compare commits

..

No commits in common. "e0bb39f99adb84553c28dce5f190fbeb60fb80b9" and "5ecf338be0dd21937706e3284fc9d0edc5950e62" have entirely different histories.

721 changed files with 3476 additions and 5057 deletions

View File

@ -1,14 +1,12 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
insert_final_newline = true
end_of_line = lf
[*.kt]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
@ -17,3 +15,5 @@ ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
[*.properties]
charset = utf-8
end_of_line = lf
insert_final_newline = true

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -9,7 +9,6 @@ assert !ext.has("libVersion")
assert extName.chars().max().asInt < 0x180 : "Extension name should be romanized"
Project theme = ext.has("themePkg") ? project(":lib-multisrc:$themePkg") : null
if (theme != null) evaluationDependsOn(theme.path)
android {
compileSdk AndroidConfig.compileSdk

View File

@ -25,5 +25,3 @@ android.useAndroidX=true
android.enableBuildConfigAsBytecode=true
android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
org.gradle.configureondemand=true

View File

@ -18,7 +18,7 @@ kotlin-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", ver
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines_version" }
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines_version" }
injekt-core = { module = "com.github.null2264.injekt:injekt-core", version = "4135455a2a" }
injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" }
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
jsoup = { module = "org.jsoup:jsoup", version = "1.15.1" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" }

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -2,7 +2,7 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 4
baseVersionCode = 3
dependencies {
api(project(":lib:synchrony"))

View File

@ -276,7 +276,7 @@ abstract class ColaManga(
}.also(screen::addPreference)
}
private val keyMappingRegex = Regex("""if\s*\(\s*([a-zA-Z0-9_]+)\s*==\s*(?<keyType>\d+)\s*\)\s*\{\s*return\s*'(?<key>[a-zA-Z0-9_]+)'\s*;""")
private val keyMappingRegex = Regex("""[0-9A-Za-z_]+\s*==\s*['"](?<keyType>\d+)['"]\s*&&\s*\([0-9A-Za-z_]+\s*=\s*['"](?<key>[a-zA-Z0-9]+)['"]\)""")
private val keyMapping by lazy {
val obfuscatedReadJs = client.newCall(GET("$baseUrl/js/manga.read.js")).execute().body.string()

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 24
baseVersionCode = 23

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 4
baseVersionCode = 3

View File

@ -83,8 +83,8 @@ class Post<T>(val post: T)
@Serializable
class ChapterListResponse(
val isNovel: Boolean = false,
val slug: String? = null,
val isNovel: Boolean,
val slug: String,
val chapters: List<Chapter>,
)
@ -96,13 +96,11 @@ class Chapter(
private val createdBy: Name,
private val createdAt: String,
private val chapterStatus: String,
private val mangaPost: ChapterPostDetails,
) {
fun isPublic() = chapterStatus == "PUBLIC"
fun toSChapter(mangaSlug: String?) = SChapter.create().apply {
val seriesSlug = mangaSlug ?: mangaPost.slug
url = "/series/$seriesSlug/$slug#$id"
fun toSChapter(mangaSlug: String) = SChapter.create().apply {
url = "/series/$mangaSlug/$slug#$id"
name = "Chapter $number"
scanlator = createdBy.name
date_upload = try {
@ -113,9 +111,4 @@ class Chapter(
}
}
@Serializable
class ChapterPostDetails(
val slug: String,
)
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH)

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 7
baseVersionCode = 4

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.multisrc.keyoapp
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
@ -30,7 +31,9 @@ abstract class Keyoapp(
) : ParsedHttpSource() {
override val supportsLatest = true
override val client = network.cloudflareClient
override val client = network.cloudflareClient.newBuilder()
.rateLimit(2)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
@ -194,18 +197,7 @@ abstract class Keyoapp(
status = document.selectFirst("div[alt=Status]").parseStatus()
author = document.selectFirst("div[alt=Author]")?.text()
artist = document.selectFirst("div[alt=Artist]")?.text()
genre = buildList {
document.selectFirst("div[alt='Series Type']")?.text()?.replaceFirstChar {
if (it.isLowerCase()) {
it.titlecase(
Locale.getDefault(),
)
} else {
it.toString()
}
}.let(::add)
document.select("div.grid:has(>h1) > div > a").forEach { add(it.text()) }
}.joinToString()
genre = document.select("div.grid:has(>h1) > div > a").joinToString { it.text() }
}
private fun Element?.parseStatus(): Int = when (this?.text()?.lowercase()) {
@ -231,27 +223,15 @@ abstract class Keyoapp(
// Image list
override fun pageListParse(document: Document): List<Page> {
document.select("#pages > img")
.map { it.attr("uid") }
.filter { it.isNotEmpty() }
.mapIndexed { index, img ->
Page(index, document.location(), "$cdnUrl/uploads/$img")
}
.takeIf { it.isNotEmpty() }
?.also { return it }
// Fallback, old method
return document.select("#pages > img")
.map { it.imgAttr() }
.filter { it.contains(oldImgCdnRegex) }
.filter { it.contains(imgCdnRegex) }
.mapIndexed { index, img ->
Page(index, document.location(), img)
}
}
protected val cdnUrl = "https://cdn.igniscans.com"
private val oldImgCdnRegex = Regex("""^(https?:)?//cdn\d*\.keyoapp\.com""")
private val imgCdnRegex = Regex("""^(https?:)?//cdn\d*\.keyoapp\.com""")
override fun imageUrlParse(document: Document) = ""
@ -267,7 +247,7 @@ abstract class Keyoapp(
return url
}
protected open fun Element.getImageUrl(selector: String): String? {
private fun Element.getImageUrl(selector: String): String? {
return this.selectFirst(selector)?.let { element ->
element.attr("style")
.substringAfter(":url(", "")

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 31
baseVersionCode = 30

View File

@ -622,7 +622,7 @@ abstract class Madara(
"OnGoing", "Продолжается", "Updating", "Em Lançamento", "Em lançamento", "Em andamento",
"Em Andamento", "En cours", "En Cours", "En cours de publication", "Ativo", "Lançando", "Đang Tiến Hành", "Devam Ediyor",
"Devam ediyor", "In Corso", "In Arrivo", "مستمرة", "مستمر", "En Curso", "En curso", "Emision",
"Curso", "En marcha", "Publicandose", "Publicándose", "En emision", "连载中", "Em Lançamento", "Devam Ediyo",
"Curso", "En marcha", "Publicandose", "En emision", "连载中", "Em Lançamento", "Devam Ediyo",
"Đang làm", "Em postagem", "Devam Eden", "Em progresso", "Em curso",
)

View File

@ -490,8 +490,8 @@ abstract class MangaThemesia(
Pair(intl["order_by_filter_popular"], "popular"),
)
protected open val popularFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "popular")) }
protected open val latestFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "update")) }
protected val popularFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "popular")) }
protected val latestFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "update")) }
protected class ProjectFilter(
name: String,
@ -603,7 +603,7 @@ abstract class MangaThemesia(
(!strict && url.pathSegments.size == n + 1 && url.pathSegments[n].isEmpty())
}
protected open fun parseGenres(document: Document): List<GenreData>? {
private fun parseGenres(document: Document): List<GenreData>? {
return document.selectFirst("ul.genrez")?.select("li")?.map { li ->
GenreData(
li.selectFirst("label")!!.text(),

View File

@ -3,7 +3,7 @@
<application>
<activity
android:name="eu.kanade.tachiyomi.extension.tr.sadscans.SadscansUrlActivity"
android:name="eu.kanade.tachiyomi.multisrc.po2scans.PO2ScansUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
@ -14,9 +14,9 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="sadscans.com"
android:host="${SOURCEHOST}"
android:pathPattern="/series/..*"
android:scheme="https" />
android:scheme="${SOURCESCHEME}" />
</intent-filter>
</activity>
</application>

View File

@ -0,0 +1,9 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 1
dependencies {
api(project(":lib:dataimage"))
}

View File

@ -0,0 +1,141 @@
package eu.kanade.tachiyomi.multisrc.po2scans
import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor
import eu.kanade.tachiyomi.lib.dataimage.dataImageAsUrl
import eu.kanade.tachiyomi.network.GET
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.ParsedHttpSource
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.SimpleDateFormat
import java.util.Locale
abstract class PO2Scans(
override val name: String,
override val baseUrl: String,
override val lang: String,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("dd MMMM, yy", Locale.ENGLISH),
) : ParsedHttpSource() {
override val supportsLatest = true
override val client = network.cloudflareClient.newBuilder()
.addInterceptor(DataImageInterceptor())
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
// popular
override fun popularMangaRequest(page: Int) = GET("$baseUrl/series", headers)
override fun popularMangaSelector() = "div.series-list"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("div > a")!!.absUrl("href"))
title = element.selectFirst("div > h2")!!.text()
thumbnail_url = element.selectFirst("img")?.absUrl("data-src")
}
// TODO: add page selectors & url parameters when site have enough series for pagination
override fun popularMangaNextPageSelector() = null
// latest
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
override fun latestUpdatesSelector() = "div.chap"
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
element.selectFirst("div.chap-title a")!!.let {
setUrlWithoutDomain(it.absUrl("href"))
title = it.text()
}
thumbnail_url = element.selectFirst("img")?.absUrl("data-src")
}
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
// search
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
if (!query.startsWith(SLUG_SEARCH_PREFIX)) {
return super.fetchSearchManga(page, query, filters)
}
val url = "/series/${query.substringAfter(SLUG_SEARCH_PREFIX)}"
return fetchMangaDetails(SManga.create().apply { this.url = url })
.map {
it.url = url
MangasPage(listOf(it), false)
}
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
GET("$baseUrl/series?search=$query", headers)
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
// manga details
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
title = document.selectFirst(".title")!!.text()
author = document.select(".author > span:nth-child(2)").text()
artist = author
status = document.select(".status > span:nth-child(2)").text().parseStatus()
description = document.select(".summary p").text()
thumbnail_url = document.select("div.series-image img").attr("abs:src")
}
}
// chapter list
override fun chapterListSelector() = "div.chap"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
element.selectFirst("a")!!.let {
setUrlWithoutDomain(it.absUrl("href"))
name = it.text()
}
date_upload = parseDate(element.select("div > div > span:nth-child(2)").text())
}
// page list
override fun pageListParse(document: Document) =
document.select(".swiper-slide img").mapIndexed { index, img ->
Page(index, imageUrl = img.imgAttr())
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
private val statusOngoing = listOf("ongoing", "devam ediyor")
private val statusCompleted = listOf("complete", "tamamlandı", "bitti")
private fun String.parseStatus(): Int {
return when (this.lowercase()) {
in statusOngoing -> SManga.ONGOING
in statusCompleted -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}
private fun Element.imgAttr(): String = when {
hasAttr("data-pagespeed-high-res-src") -> dataImageAsUrl("data-pagespeed-high-res-src")
hasAttr("data-pagespeed-lazy-src") -> dataImageAsUrl("data-pagespeed-lazy-src")
else -> dataImageAsUrl("src")
}
private fun parseDate(dateStr: String) =
runCatching { dateFormat.parse(dateStr)!!.time }
.getOrDefault(0L)
companion object {
const val SLUG_SEARCH_PREFIX = "slug:"
}
}

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.extension.tr.sadscans
package eu.kanade.tachiyomi.multisrc.po2scans
import android.app.Activity
import android.content.ActivityNotFoundException
@ -7,7 +7,7 @@ import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
class SadscansUrlActivity : Activity() {
class PO2ScansUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
@ -15,17 +15,17 @@ class SadscansUrlActivity : Activity() {
val slug = pathSegments[1]
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "${Sadscans.SLUG_SEARCH_PREFIX}$slug")
putExtra("query", "${PO2Scans.SLUG_SEARCH_PREFIX}$slug")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("SadscansUrlActivity", "Could not start activity", e)
Log.e("PO2ScansUrlActivity", "Could not start activity", e)
}
} else {
Log.e("SadscansUrlActivity", "could not parse URI from intent $intent")
Log.e("PO2ScansUrlActivity", "could not parse URI from intent $intent")
}
finish()

View File

@ -1,7 +1,7 @@
ext {
extName = 'Akuma'
extClass = '.AkumaFactory'
extVersionCode = 5
extVersionCode = 4
isNsfw = true
}

View File

@ -1,13 +1,8 @@
package eu.kanade.tachiyomi.extension.all.akuma
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
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.MangasPage
import eu.kanade.tachiyomi.source.model.Page
@ -25,8 +20,6 @@ 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.ParseException
import java.text.SimpleDateFormat
@ -36,7 +29,7 @@ import java.util.TimeZone
class Akuma(
override val lang: String,
private val akumaLang: String,
) : ConfigurableSource, ParsedHttpSource() {
) : ParsedHttpSource() {
override val name = "Akuma"
@ -112,23 +105,6 @@ class Akuma(
return storedToken!!
}
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val displayFullTitle: Boolean get() = preferences.getBoolean(PREF_TITLE, false)
private val shortenTitleRegex = Regex("""(\[[^]]*]|[({][^)}]*[)}])""")
private fun String.shortenTitle() = this.replace(shortenTitleRegex, "").trim()
override fun setupPreferenceScreen(screen: PreferenceScreen) {
SwitchPreferenceCompat(screen.context).apply {
key = PREF_TITLE
title = "Display manga title as full title"
setDefaultValue(false)
}.also(screen::addPreference)
}
override fun popularMangaRequest(page: Int): Request {
val payload = FormBody.Builder()
.add("view", "3")
@ -173,9 +149,7 @@ class Akuma(
override fun popularMangaFromElement(element: Element): SManga {
return SManga.create().apply {
setUrlWithoutDomain(element.select("a").attr("href"))
title = element.select(".overlay-title").text().replace("\"", "").let {
if (displayFullTitle) it.trim() else it.shortenTitle()
}
title = element.select(".overlay-title").text()
thumbnail_url = element.select("img").attr("abs:src")
}
}
@ -246,9 +220,7 @@ class Akuma(
override fun mangaDetailsParse(document: Document) = with(document) {
SManga.create().apply {
title = select(".entry-title").text().replace("\"", "").let {
if (displayFullTitle) it.trim() else it.shortenTitle()
}
title = select(".entry-title").text()
thumbnail_url = select(".img-thumbnail").attr("abs:src")
author = select(".group~.value").eachText().joinToString()
@ -330,7 +302,6 @@ class Akuma(
companion object {
const val PREFIX_ID = "id:"
private const val PREF_TITLE = "pref_title"
}
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()

View File

@ -1,7 +0,0 @@
ext {
extName = 'ComicsKingdom'
extClass = '.ComicsKingdomFactory'
extVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,44 +0,0 @@
package eu.kanade.tachiyomi.extension.all.comicskingdom
import kotlinx.serialization.Serializable
@Serializable
class Chapter(
val id: Int,
val date: String,
val assets: Assets?,
val link: String,
)
@Serializable
class Assets(
val single: AssetData,
)
@Serializable
class AssetData(
val url: String,
)
@Serializable
class Manga(
val id: Int,
val link: String,
val title: Rendered,
val content: Rendered,
val meta: MangaMeta,
val yoast_head: String,
)
@Serializable
class MangaMeta(
val ck_byline_on_app: String,
)
@Serializable
class Rendered(
val rendered: String,
)
val ChapterFields = Chapter.javaClass.fields.joinToString(",") { it.name }
val MangaFields = Manga.javaClass.fields.joinToString(",") { it.name }

View File

@ -1,311 +0,0 @@
package eu.kanade.tachiyomi.extension.all.comicskingdom
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter
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
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.math.ceil
import kotlin.math.roundToInt
class ComicsKingdom(override val lang: String) : ConfigurableSource, HttpSource() {
override val name = "Comics Kingdom"
override val baseUrl = "https://wp.comicskingdom.com"
override val supportsLatest = true
private val json: Json by injectLazy()
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault())
private val compactChapterCountRegex = Regex("\"totalItems\":(\\d+)")
private val thumbnailUrlRegex = Regex("thumbnailUrl\":\"(\\S+)\",\"dateP")
private val mangaPerPage = 20
private val chapterPerPage = 100
private fun mangaApiUrl(): HttpUrl.Builder =
baseUrl.toHttpUrl().newBuilder().apply {
addPathSegments("wp-json/wp/v2")
addPathSegment("ck_feature")
addQueryParameter("per_page", mangaPerPage.toString())
addQueryParameter("_fields", MangaFields)
addQueryParameter("ck_language", if (lang == "es") "spanish" else "english")
}
private fun chapterApiUrl(): HttpUrl.Builder = baseUrl.toHttpUrl().newBuilder().apply {
addPathSegments("wp-json/wp/v2")
addPathSegment("ck_comic")
addQueryParameter("per_page", chapterPerPage.toString())
addQueryParameter("_fields", ChapterFields)
}
private fun getReq(orderBy: String, page: Int): Request = GET(
mangaApiUrl().apply {
addQueryParameter("orderBy", orderBy)
addQueryParameter("page", page.toString())
}.build(),
headers,
)
override fun popularMangaRequest(page: Int): Request = getReq("relevance", page)
override fun latestUpdatesRequest(page: Int): Request = getReq("modified", page)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
GET(
mangaApiUrl().apply {
addQueryParameter("search", query)
addQueryParameter("page", page.toString())
if (!filters.isEmpty()) {
for (filter in filters) {
when (filter) {
is OrderFilter -> {
addQueryParameter("orderby", filter.getValue())
}
is GenreList -> {
if (filter.included.isNotEmpty()) {
addQueryParameter(
"ck_genre",
filter.included.joinToString(","),
)
}
if (filter.excluded.isNotEmpty()) {
addQueryParameter(
"ck_genre_exclude",
filter.excluded.joinToString(","),
)
}
}
else -> {}
}
}
}
}.build(),
headers,
)
override fun searchMangaParse(response: Response): MangasPage {
val list = json.decodeFromString<List<Manga>>(response.body.string())
return MangasPage(
list.map {
SManga.create().apply {
thumbnail_url = thumbnailUrlRegex.find(it.yoast_head)?.groupValues?.get(1)
setUrlWithoutDomain(
mangaApiUrl().apply {
addPathSegment(it.id.toString())
addQueryParameter("slug", it.link.toHttpUrl().pathSegments.last())
}
.build().toString(),
)
title = it.title.rendered
}
},
list.count() == mangaPerPage,
)
}
override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response)
override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response)
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val mangaData = json.decodeFromString<Manga>(response.body.string())
title = mangaData.title.rendered
author = mangaData.meta.ck_byline_on_app.substringAfter("By").trim()
description = Jsoup.parse(mangaData.content.rendered).text()
status = SManga.UNKNOWN
thumbnail_url = thumbnailUrlRegex.find(mangaData.yoast_head)?.groupValues?.get(1)
}
override fun getMangaUrl(manga: SManga): String =
"$baseUrl/${(baseUrl + manga.url).toHttpUrl().queryParameter("slug")}"
override fun chapterListParse(response: Response): List<SChapter> {
val mangaData = json.decodeFromString<Manga>(response.body.string())
val mangaName = mangaData.link.toHttpUrl().pathSegments.last()
if (shouldCompact()) {
val res = client.newCall(GET(mangaData.link)).execute()
val postCount = compactChapterCountRegex.findAll(res.body.string())
.find { result -> result.groupValues[1].toDouble() > 0 }!!.groupValues[1].toDouble()
res.close()
val maxPage = ceil(postCount / chapterPerPage)
return List(maxPage.roundToInt()) { idx ->
SChapter.create().apply {
chapter_number = idx * 0.01F
name =
"${idx * chapterPerPage + 1}-${if (postCount - (idx + 1) * chapterPerPage < 0) postCount.toInt() else (idx + 1) * chapterPerPage}"
setUrlWithoutDomain(
chapterApiUrl().apply {
addQueryParameter("orderBy", "date")
addQueryParameter("order", "asc")
addQueryParameter("ck_feature", mangaName)
addQueryParameter("page", (idx + 1).toString())
}.build().toString(),
)
}
}.reversed()
}
val chapters = mutableListOf<SChapter>()
var pageNum = 1
var chapterData = getChapterList(mangaName, pageNum)
var chapterNum = 0.0F
while (chapterData != null) {
val list = chapterData.map {
chapterNum += 0.01F
SChapter.create().apply {
chapter_number = chapterNum
setUrlWithoutDomain(
chapterApiUrl().apply {
addPathSegment(it.id.toString())
addQueryParameter("slug", it.link.substringAfter(baseUrl))
}
.toString(),
)
date_upload = dateFormat.parse(it.date).time
name = it.date.substringBefore("T")
}
}
chapters.addAll(list)
if (list.count() < 100) {
break
}
pageNum++
try {
chapterData = getChapterList(mangaName, pageNum)
} catch (exception: Exception) {
if (chapters.isNotEmpty()) {
return chapters
}
}
}
return chapters
}
private fun getChapterList(mangaName: String, page: Int): List<Chapter> {
val url = chapterApiUrl().apply {
addQueryParameter("order", "desc")
addQueryParameter("ck_feature", mangaName)
addQueryParameter("page", page.toString())
}.build()
val call = client.newCall(GET(url, headers)).execute()
val body = call.body.string()
call.close()
return json.decodeFromString<List<Chapter>>(body)
}
override fun getChapterUrl(chapter: SChapter): String {
if (shouldCompact()) {
return "$baseUrl/${(baseUrl + chapter.url).toHttpUrl().queryParameter("ck_feature")}"
}
return "$baseUrl/${(baseUrl + chapter.url).toHttpUrl().queryParameter("slug")}"
}
override fun pageListParse(response: Response): List<Page> {
if (shouldCompact()) {
return json.decodeFromString<List<Chapter>>(response.body.string())
.mapIndexed { idx, chapter ->
Page(idx, imageUrl = chapter.assets!!.single.url)
}
}
val chapter = json.decodeFromString<Chapter>(response.body.string())
return listOf(Page(0, imageUrl = chapter.assets!!.single.url))
}
private class OrderFilter :
Filter.Select<String>(
"Order by",
arrayOf(
"author",
"date",
"id",
"include",
"modified",
"parent",
"relevance",
"title",
"rand",
),
) {
fun getValue(): String = values[state]
}
private class Genre(name: String, val gid: String) : Filter.TriState(name)
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres) {
val included: List<String>
get() = state.filter { it.isIncluded() }.map { it.gid }
val excluded: List<String>
get() = state.filter { it.isExcluded() }.map { it.gid }
}
override fun getFilterList() = FilterList(
OrderFilter(),
GenreList(getGenreList()),
)
private fun getGenreList() = listOf(
Genre("Action", "action"),
Genre("Adventure", "adventure"),
Genre("Classic", "classic"),
Genre("Comedy", "comedy"),
Genre("Crime", "crime"),
Genre("Fantasy", "fantasy"),
Genre("Gag Cartoons", "gag-cartoons"),
Genre("Mystery", "mystery"),
Genre("New Arrivals", "new-arrivals"),
Genre("Non-Fiction", "non-fiction"),
Genre("OffBeat", "offbeat"),
Genre("Political Cartoons", "political-cartoons"),
Genre("Romance", "romance"),
Genre("Sci-Fi", "sci-fi"),
Genre("Slice Of Life", "slice-of-life"),
Genre("Superhero", "superhero"),
Genre("Vintage", "vintage"),
)
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val compactpref = androidx.preference.CheckBoxPreference(screen.context).apply {
key = "compactPref"
title = "Compact chapters"
summary =
"Unchecking this will make each daily/weekly upload into a chapter which can be very slow because some comics have 8000+ uploads"
isChecked = true
}
screen.addPreference(compactpref)
}
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private fun shouldCompact() = preferences.getBoolean("compactPref", true)
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
}

View File

@ -1,8 +0,0 @@
package eu.kanade.tachiyomi.extension.all.comicskingdom
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class ComicsKingdomFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(ComicsKingdom("en"), ComicsKingdom("es"))
}

View File

@ -1,14 +0,0 @@
package eu.kanade.tachiyomi.extension.all.comicsvalley
import eu.kanade.tachiyomi.multisrc.madara.Madara
class ComicsValley : Madara(
"Comics Valley",
"https://comicsvalley.com",
"all",
) {
override val mangaSubString = "comics-new"
override val useNewChapterEndpoint = true
override val useLoadMoreRequest = LoadMoreStrategy.Always
override val id = 1103204227230640533
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,12 +0,0 @@
package eu.kanade.tachiyomi.extension.all.eromanhwa
import eu.kanade.tachiyomi.multisrc.madara.Madara
class Eromanhwa : Madara(
"Eromanhwa",
"https://eromanhwa.org",
"all",
) {
override val id = 3597355706480775153 // accidently set lang to en...
override val useNewChapterEndpoint = true
}

View File

@ -1,7 +1,7 @@
ext {
extName = 'Hitomi'
extClass = '.HitomiFactory'
extVersionCode = 33
extVersionCode = 32
isNsfw = true
}

View File

@ -517,9 +517,6 @@ class Hitomi(
"https://${subDomain}tn.$domain/webpbigtn/${thumbPathFromHash(hash)}/$hash.webp"
}
description = buildString {
japaneseTitle?.let {
append("Japanese title: ", it, "\n")
}
parodys?.joinToString { it.formatted }?.let {
append("Series: ", it, "\n")
}

View File

@ -7,7 +7,6 @@ import kotlinx.serialization.json.JsonPrimitive
class Gallery(
val galleryurl: String,
val title: String,
val japaneseTitle: String?,
val date: String,
val type: String?,
val language: String?,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,41 +0,0 @@
package eu.kanade.tachiyomi.extension.all.kdtscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
class KdtScans : Madara(
"KDT Scans",
"https://kdtscans.com",
"all",
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
) {
override val useNewChapterEndpoint = true
override val fetchGenres = false
override fun popularMangaFromElement(element: Element): SManga {
return super.popularMangaFromElement(element).apply {
title = title.cleanupTitle()
}
}
override fun searchMangaFromElement(element: Element): SManga {
return super.searchMangaFromElement(element).apply {
title = title.cleanupTitle()
}
}
override fun mangaDetailsParse(document: Document): SManga {
return super.mangaDetailsParse(document).apply {
title = title.cleanupTitle()
}
}
private fun String.cleanupTitle() = replace(titleCleanupRegex, "").trim()
private val titleCleanupRegex =
Regex("""^\[(ESPAÑOL|English)\]\s+(\s+)?""", RegexOption.IGNORE_CASE)
}

View File

@ -1,7 +1,7 @@
ext {
extName = 'LANraragi'
extClass = '.LANraragiFactory'
extVersionCode = 18
extVersionCode = 17
}
apply from: "$rootDir/common.gradle"

View File

@ -7,7 +7,6 @@ data class Archive(
val arcid: String,
val isnew: String,
val tags: String?,
val summary: String?,
val title: String,
)
@ -26,6 +25,7 @@ data class ArchiveSearchResult(
@Serializable
data class Category(
val id: String,
val last_used: String,
val name: String,
val pinned: String,
)

View File

@ -242,7 +242,7 @@ open class LANraragi(private val suffix: String = "") : ConfigurableSource, Unme
private fun archiveToSManga(archive: Archive) = SManga.create().apply {
url = "/reader?id=${archive.arcid}"
title = archive.title
description = if (archive.summary.isNullOrBlank()) archive.title else archive.summary
description = archive.title
thumbnail_url = getThumbnailUri(archive.arcid)
genre = archive.tags?.replace(",", ", ")
artist = getArtist(archive.tags)
@ -406,10 +406,12 @@ open class LANraragi(private val suffix: String = "") : ConfigurableSource, Unme
private fun getCategoryPairs(categories: List<Category>): Array<Pair<String?, String>> {
// Empty pair to disable. Sort by pinned status then name for convenience.
// Web client sort is pinned > last_used but reflects between page changes.
val pin = "\uD83D\uDCCC "
// Maintain categories sync for next FilterList reset.
// Maintain categories sync for next FilterList reset. If there's demand for it, it's now
// possible to sort by last_used similar to the web client. Maybe an option toggle?
getCategories()
return listOf(Pair("", ""))

View File

@ -1,7 +1,7 @@
ext {
extName = 'MangaDex'
extClass = '.MangaDexFactory'
extVersionCode = 194
extVersionCode = 193
isNsfw = true
}

View File

@ -138,6 +138,13 @@ object MDConstants {
return "${altTitlesInDescPref}_$dexLang"
}
private const val customUserAgentPref = "customUserAgent"
fun getCustomUserAgentPrefKey(dexLang: String): String {
return "${customUserAgentPref}_$dexLang"
}
val defaultUserAgent = "Tachiyomi " + System.getProperty("http.agent")
private const val tagGroupContent = "content"
private const val tagGroupFormat = "format"
private const val tagGroupGenre = "genre"

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension.all.mangadex
import android.app.Application
import android.content.SharedPreferences
import android.os.Build
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
@ -55,7 +56,6 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
.sanitizeExistingUuidPrefs()
}
private val helper = MangaDexHelper(lang)
@ -67,7 +67,6 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
"Keiyoushi"
val builder = super.headersBuilder().apply {
set("User-Agent", "Tachiyomi " + System.getProperty("http.agent"))
set("Referer", "$baseUrl/")
set("Origin", baseUrl)
set("Extra", extraHeader)
@ -79,8 +78,13 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
override val client = network.client.newBuilder()
.rateLimit(3)
.addInterceptor(MdAtHomeReportInterceptor(network.client, headers))
.addInterceptor(MdUserAgentInterceptor(preferences, dexLang))
.build()
init {
preferences.sanitizeExistingUuidPrefs()
}
// Popular manga section
override fun popularMangaRequest(page: Int): Request {
@ -391,7 +395,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
// Manga Details section
override fun getMangaUrl(manga: SManga): String {
return baseUrl + manga.url.replace("/manga/", "/title/") + "/" + helper.titleToSlug(manga.title)
return baseUrl + manga.url + "/" + helper.titleToSlug(manga.title)
}
/**
@ -757,6 +761,30 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
}
}
val userAgentPref = EditTextPreference(screen.context).apply {
key = MDConstants.getCustomUserAgentPrefKey(dexLang)
title = helper.intl["set_custom_useragent"]
summary = helper.intl["set_custom_useragent_summary"]
dialogMessage = helper.intl.format(
"set_custom_useragent_dialog",
MDConstants.defaultUserAgent,
)
setDefaultValue(MDConstants.defaultUserAgent)
setOnPreferenceChangeListener { _, newValue ->
try {
Headers.Builder().add("User-Agent", newValue as String)
summary = newValue
true
} catch (e: Throwable) {
val errorMessage = helper.intl.format("set_custom_useragent_error_invalid", e.message)
Toast.makeText(screen.context, errorMessage, Toast.LENGTH_LONG).show()
false
}
}
}
screen.addPreference(coverQualityPref)
screen.addPreference(tryUsingFirstVolumeCoverPref)
screen.addPreference(dataSaverPref)
@ -766,6 +794,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
screen.addPreference(originalLanguagePref)
screen.addPreference(blockedGroupsPref)
screen.addPreference(blockedUploaderPref)
screen.addPreference(userAgentPref)
}
override fun getFilterList(): FilterList =
@ -840,14 +869,20 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
private val SharedPreferences.altTitlesInDesc
get() = getBoolean(MDConstants.getAltTitlesInDescPrefKey(dexLang), false)
private val SharedPreferences.customUserAgent
get() = getString(
MDConstants.getCustomUserAgentPrefKey(dexLang),
MDConstants.defaultUserAgent,
)
/**
* Previous versions of the extension allowed invalid UUID values to be stored in the
* preferences. This method clear invalid UUIDs in case the user have updated from
* a previous version with that behaviour.
*/
private fun SharedPreferences.sanitizeExistingUuidPrefs(): SharedPreferences {
private fun SharedPreferences.sanitizeExistingUuidPrefs() {
if (getBoolean(MDConstants.getHasSanitizedUuidsPrefKey(dexLang), false)) {
return this
return
}
val blockedGroups = getString(MDConstants.getBlockedGroupsPrefKey(dexLang), "")!!
@ -867,7 +902,5 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
.putString(MDConstants.getBlockedUploaderPrefKey(dexLang), blockedUploaders)
.putBoolean(MDConstants.getHasSanitizedUuidsPrefKey(dexLang), true)
.apply()
return this
}
}

View File

@ -0,0 +1,39 @@
package eu.kanade.tachiyomi.extension.all.mangadex
import android.content.SharedPreferences
import okhttp3.Interceptor
import okhttp3.Response
/**
* Interceptor to set custom useragent for MangaDex
*/
class MdUserAgentInterceptor(
private val preferences: SharedPreferences,
private val dexLang: String,
) : Interceptor {
private val SharedPreferences.customUserAgent
get() = getString(
MDConstants.getCustomUserAgentPrefKey(dexLang),
MDConstants.defaultUserAgent,
)
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val newUserAgent = preferences.customUserAgent
?: return chain.proceed(originalRequest)
val originalHeaders = originalRequest.headers
val modifiedHeaders = originalHeaders.newBuilder()
.set("User-Agent", newUserAgent)
.build()
val modifiedRequest = originalRequest.newBuilder()
.headers(modifiedHeaders)
.build()
return chain.proceed(modifiedRequest)
}
}

View File

@ -17,8 +17,6 @@ schedule_monthly=Monthly
schedule_other=Other
schedule_trimonthly=Trimonthly
schedule_weekly=Weekly
subtitle_only=Show subtitle only
subtitle_only_summary=Removes the redundant chapter number from the chapter name.
serialization=Serialization: %s
split_double_pages=Split double pages
split_double_pages_summary=Only a few titles supports disabling this setting.

View File

@ -1,7 +1,7 @@
ext {
extName = 'MANGA Plus by SHUEISHA'
extClass = '.MangaPlusFactory'
extVersionCode = 53
extVersionCode = 52
}
apply from: "$rootDir/common.gradle"

View File

@ -289,10 +289,9 @@ class MangaPlus(
}
val titleDetailView = result.success.titleDetailView!!
val subtitleOnly = preferences.subtitleOnly()
return titleDetailView.chapterList
.map { it.toSChapter(subtitleOnly) }
.map(Chapter::toSChapter)
.reversed()
}
@ -308,8 +307,8 @@ class MangaPlus(
private fun pageListRequest(chapterId: String): Request {
val url = "$APP_API_URL/manga_viewer".toHttpUrl().newBuilder()
.addQueryParameter("chapter_id", chapterId)
.addQueryParameter("split", if (preferences.splitImages()) "yes" else "no")
.addQueryParameter("img_quality", preferences.imageQuality())
.addQueryParameter("split", if (preferences.splitImages) "yes" else "no")
.addQueryParameter("img_quality", preferences.imageQuality)
.addQueryParameter("ticket_reading", "no")
.addQueryParameter("free_reading", "yes")
.addQueryParameter("subscription_reading", "no")
@ -379,16 +378,8 @@ class MangaPlus(
key = "${VER_PREF_KEY}_$lang",
)
val titlePref = SwitchPreferenceCompat(screen.context).apply {
key = "${SUBTITLE_ONLY_KEY}_$lang"
title = intl["subtitle_only"]
summary = intl["subtitle_only_summary"]
setDefaultValue(SUBTITLE_ONLY_DEFAULT_VALUE)
}
screen.addPreference(qualityPref)
screen.addPreference(splitPref)
screen.addPreference(titlePref)
}
private fun PreferenceScreen.addEditTextPreference(
@ -503,11 +494,11 @@ class MangaPlus(
json.decodeFromString(body.string())
}
private fun SharedPreferences.imageQuality(): String = getString("${QUALITY_PREF_KEY}_$lang", QUALITY_PREF_DEFAULT_VALUE)!!
private val SharedPreferences.imageQuality: String
get() = getString("${QUALITY_PREF_KEY}_$lang", QUALITY_PREF_DEFAULT_VALUE)!!
private fun SharedPreferences.splitImages(): Boolean = getBoolean("${SPLIT_PREF_KEY}_$lang", SPLIT_PREF_DEFAULT_VALUE)
private fun SharedPreferences.subtitleOnly(): Boolean = getBoolean("${SUBTITLE_ONLY_KEY}_$lang", SUBTITLE_ONLY_DEFAULT_VALUE)
private val SharedPreferences.splitImages: Boolean
get() = getBoolean("${SPLIT_PREF_KEY}_$lang", SPLIT_PREF_DEFAULT_VALUE)
private val SharedPreferences.appVersion: String?
get() = getString("${VER_PREF_KEY}_$lang", VER_PREF_DEFAULT_VALUE)
@ -535,9 +526,6 @@ private val QUALITY_PREF_DEFAULT_VALUE = QUALITY_PREF_ENTRY_VALUES[2]
private const val SPLIT_PREF_KEY = "splitImage"
private const val SPLIT_PREF_DEFAULT_VALUE = true
private const val SUBTITLE_ONLY_KEY = "subtitleOnly"
private const val SUBTITLE_ONLY_DEFAULT_VALUE = false
private const val VER_PREF_KEY = "appVer"
private const val VER_PREF_DEFAULT_VALUE = ""
private const val SECRET_PREF_KEY = "accountSecret"

View File

@ -322,16 +322,11 @@ class Chapter(
val isExpired: Boolean
get() = subTitle == null
fun toSChapter(subtitlePref: Boolean): SChapter = SChapter.create().apply {
name = if (subtitlePref && subTitle != null) {
subTitle
} else {
"${this@Chapter.name} - $subTitle"
}
fun toSChapter(): SChapter = SChapter.create().apply {
name = "${this@Chapter.name} - $subTitle"
date_upload = 1000L * startTimeStamp
url = "#/viewer/$chapterId"
chapter_number = this@Chapter.name.substringAfter("#").toFloatOrNull() ?: -1f
scanlator = "MANGA Plus"
}
}

View File

@ -0,0 +1,8 @@
ext {
extName = 'Meitua.top'
extClass = '.MeituaTop'
extVersionCode = 6
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,111 @@
package eu.kanade.tachiyomi.extension.all.meituatop
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.select.Evaluator
import rx.Observable
import java.text.SimpleDateFormat
import java.util.Locale
// Uses MACCMS http://www.maccms.la/
class MeituaTop : HttpSource() {
override val name = "Meitua.top"
override val lang = "all"
override val supportsLatest = false
override val baseUrl = "https://mt1.meitu1.sbs"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/arttype/0b-$page.html", headers)
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.selectFirst(Evaluator.Class("thumbnail-group"))!!.children().map {
SManga.create().apply {
url = it.selectFirst(Evaluator.Tag("a"))!!.attr("href")
val image = it.selectFirst(Evaluator.Tag("img"))!!
title = image.attr("alt")
thumbnail_url = image.attr("src")
val info = it.selectFirst(Evaluator.Tag("p"))!!.ownText().split(" - ")
genre = info[0]
description = info[1]
status = SManga.COMPLETED
initialized = true
}
}
val pageLinks = document.select(Evaluator.Class("page_link"))
if (pageLinks.isEmpty()) return MangasPage(mangas, false)
val lastPage = pageLinks[3].attr("href")
val hasNextPage = document.location().pageNumber() != lastPage.pageNumber()
return MangasPage(mangas, hasNextPage)
}
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) {
val url = "$baseUrl/artsearch/-------.html".toHttpUrl().newBuilder()
.addQueryParameter("wd", query)
.addQueryParameter("page", page.toString())
.toString()
return GET(url, headers)
}
val filter = filters.filterIsInstance<RegionFilter>().firstOrNull() ?: return popularMangaRequest(page)
return GET("$baseUrl/arttype/${21 + filter.state}b-$page.html", headers)
}
override fun searchMangaParse(response: Response) = popularMangaParse(response)
override fun fetchMangaDetails(manga: SManga): Observable<SManga> = Observable.just(manga)
override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException()
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
val chapter = SChapter.create().apply {
url = manga.url
name = "Gallery"
date_upload = dateFormat.parse(manga.description!!)!!.time
chapter_number = -2f
}
return Observable.just(listOf(chapter))
}
override fun chapterListParse(response: Response) = throw UnsupportedOperationException()
override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup()
val images = document.selectFirst(Evaluator.Class("ttnr"))!!.select(Evaluator.Tag("img"))
.map { it.attr("src") }.distinct()
return images.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) }
}
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
override fun getFilterList() = FilterList(
Filter.Header("Category (ignored for text search)"),
RegionFilter(),
)
private class RegionFilter : Filter.Select<String>(
"Region",
arrayOf("All", "国产美女", "韩国美女", "台湾美女", "日本美女", "欧美美女", "泰国美女"),
)
private fun String.pageNumber() = numberRegex.findAll(this).last().value.toInt()
private val numberRegex by lazy { Regex("""\d+""") }
private val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) }
}

View File

@ -1,8 +0,0 @@
ext {
extName = 'Mitaku'
extClass = '.Mitaku'
extVersionCode = 1
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@ -1,171 +0,0 @@
package eu.kanade.tachiyomi.extension.all.mitaku
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
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.model.UpdateStrategy
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
class Mitaku : ParsedHttpSource() {
override val name = "Mitaku"
override val baseUrl = "https://mitaku.net"
override val lang = "all"
override val supportsLatest = false
// ============================== Popular ===============================
override fun popularMangaRequest(page: Int) = GET("$baseUrl/category/ero-cosplay/page/$page", headers)
override fun popularMangaSelector() = "div.article-container article"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
title = element.selectFirst("a")!!.attr("title")
thumbnail_url = element.selectFirst("img")?.absUrl("src")
}
override fun popularMangaNextPageSelector() = "div.wp-pagenavi a.page.larger"
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request {
throw UnsupportedOperationException()
}
override fun latestUpdatesSelector(): String {
throw UnsupportedOperationException()
}
override fun latestUpdatesFromElement(element: Element): SManga {
throw UnsupportedOperationException()
}
override fun latestUpdatesNextPageSelector(): String? {
throw UnsupportedOperationException()
}
// =============================== Search ===============================
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val tagFilter = filterList.findInstance<TagFilter>()!!
val categoryFilter = filterList.findInstance<CategoryFilter>()!!
return when {
query.isEmpty() && categoryFilter.state != 0 -> {
val url = "$baseUrl/category/${categoryFilter.toUriPart()}/page/$page/"
GET(url, headers)
}
query.isEmpty() && tagFilter.state.isNotEmpty() -> {
val url = baseUrl.toHttpUrl().newBuilder()
.addPathSegment("tag")
.addPathSegment(tagFilter.toUriPart())
.addPathSegment("page")
.addPathSegment(page.toString())
.build()
GET(url, headers)
}
query.isNotEmpty() -> {
val url = "$baseUrl/page/$page/".toHttpUrl().newBuilder()
.addQueryParameter("s", query)
.build()
GET(url, headers)
}
else -> latestUpdatesRequest(page)
}
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
status = SManga.COMPLETED
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
with(document.selectFirst("article")!!) {
title = selectFirst("h1")!!.text()
val catGenres = select("span.cat-links a").joinToString { it.text() }
val tagGenres = select("span.tag-links a").joinToString { it.text() }
genre = listOf(catGenres, tagGenres).filter { it.isNotEmpty() }.joinToString()
}
}
// ============================== Chapters ==============================
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
val chapter = SChapter.create().apply {
url = manga.url
chapter_number = 1F
name = "Chapter"
}
return Observable.just(listOf(chapter))
}
override fun chapterListSelector(): String {
throw UnsupportedOperationException()
}
override fun chapterFromElement(element: Element): SChapter {
throw UnsupportedOperationException()
}
// =============================== Pages ================================
override fun pageListParse(document: Document): List<Page> {
val imageElements = document.select("a.msacwl-img-link")
return imageElements.mapIndexed { index, element ->
val imageUrl = element.absUrl("data-mfp-src")
Page(index, imageUrl = imageUrl)
}
}
override fun imageUrlParse(document: Document): String {
throw UnsupportedOperationException()
}
open class UriPartFilter(
displayName: String,
private val valuePair: Array<Pair<String, String>>,
) : Filter.Select<String>(displayName, valuePair.map { it.first }.toTypedArray()) {
fun toUriPart() = valuePair[state].second
}
class CategoryFilter : UriPartFilter(
"Category",
arrayOf(
Pair("Any", ""),
Pair("Ero Cosplay", "/ero-cosplay"),
Pair("Nude", "/nude"),
Pair("Sexy Set", "/sexy-set"),
Pair("Online Video", "/online-video"),
),
)
override fun getFilterList(): FilterList = FilterList(
Filter.Header("NOTE: Only one tag search"),
Filter.Separator(),
CategoryFilter(),
TagFilter(),
)
class TagFilter : Filter.Text("Tag") {
fun toUriPart(): String {
return state.trim().lowercase().replace(" ", "-")
}
}
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
}

View File

@ -1,7 +1,7 @@
ext {
extName = 'NHentai'
extClass = '.NHFactory'
extVersionCode = 46
extVersionCode = 41
isNsfw = true
}

View File

@ -136,51 +136,57 @@ open class NHentai(
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val fixedQuery = query.ifEmpty { "\"\"" }
val filterList = if (filters.isEmpty()) getFilterList() else filters
val nhLangSearch = if (nhLang.isBlank()) "" else "language:$nhLang "
val nhLangSearch = if (nhLang.isBlank()) "" else "+$nhLang "
val advQuery = combineQuery(filterList)
val favoriteFilter = filterList.findInstance<FavoriteFilter>()
val isOkayToSort = filterList.findInstance<UploadedFilter>()?.state?.isBlank() ?: true
val offsetPage =
filterList.findInstance<OffsetPageFilter>()?.state?.toIntOrNull()?.plus(page) ?: page
if (favoriteFilter?.state == true) {
val url = "$baseUrl/favorites/".toHttpUrl().newBuilder()
.addQueryParameter("q", "$query $advQuery")
val url = "$baseUrl/favorites".toHttpUrl().newBuilder()
.addQueryParameter("q", "$fixedQuery $advQuery")
.addQueryParameter("page", offsetPage.toString())
return GET(url.build(), headers)
} else {
val url = "$baseUrl/search/".toHttpUrl().newBuilder()
// Blank query (Multi + sort by popular month/week/day) shows a 404 page
// Searching for `""` is a hacky way to return everything without any filtering
.addQueryParameter("q", "$query $nhLangSearch$advQuery".ifBlank { "\"\"" })
val url = "$baseUrl/search".toHttpUrl().newBuilder()
.addQueryParameter("q", "$fixedQuery $nhLangSearch$advQuery")
.addQueryParameter("page", offsetPage.toString())
if (isOkayToSort) {
filterList.findInstance<SortFilter>()?.let { f ->
url.addQueryParameter("sort", f.toUriPart())
}
}
return GET(url.build(), headers)
}
}
private fun combineQuery(filters: FilterList): String = buildString {
filters.filterIsInstance<AdvSearchEntryFilter>().forEach { filter ->
filter.state.split(",")
.map(String::trim)
.filterNot(String::isBlank)
.forEach { tag ->
val y = !(filter.name == "Pages" || filter.name == "Uploaded")
if (tag.startsWith("-")) append("-")
append(filter.name, ':')
if (y) append('"')
append(tag.removePrefix("-"))
if (y) append('"')
append(" ")
private fun combineQuery(filters: FilterList): String {
val stringBuilder = StringBuilder()
val advSearch = filters.filterIsInstance<AdvSearchEntryFilter>().flatMap { filter ->
val splitState = filter.state.split(",").map(String::trim).filterNot(String::isBlank)
splitState.map {
AdvSearchEntry(filter.name, it.removePrefix("-"), it.startsWith("-"))
}
}
advSearch.forEach { entry ->
if (entry.exclude) stringBuilder.append("-")
stringBuilder.append("${entry.name}:")
stringBuilder.append(entry.text)
stringBuilder.append(" ")
}
return stringBuilder.toString()
}
data class AdvSearchEntry(val name: String, val text: String, val exclude: Boolean)
private fun searchMangaByIdRequest(id: String) = GET("$baseUrl/g/$id", headers)
private fun searchMangaByIdParse(response: Response, id: String): MangasPage {
@ -220,7 +226,7 @@ open class NHentai(
.plus("$fullTitle\n")
.plus("${document.select("div#info h2").text()}\n\n")
.plus("Pages: ${getNumPages(document)}\n")
.plus("Favorited by: ${document.select("div#info i.fa-heart ~ span span").text().removeSurrounding("(", ")")}\n")
.plus("Favorited by: ${document.select("div#info i.fa-heart + span span").text().removeSurrounding("(", ")")}\n")
.plus(getTagDescription(document))
genre = getTags(document)
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE

View File

@ -3,7 +3,7 @@ ext {
extClass = '.ThunderScansFactory'
themePkg = 'mangathemesia'
baseUrl = 'https://en-thunderscans.com'
overrideVersionCode = 5
overrideVersionCode = 4
}
apply from: "$rootDir/common.gradle"

View File

@ -14,7 +14,7 @@ class ThunderScansFactory : SourceFactory {
class ThunderScansAR : MangaThemesiaAlt(
"Thunder Scans",
"https://ar-thunderepic.com",
"https://thunderscans.com",
"ar",
dateFormat = SimpleDateFormat("MMM d, yyy", Locale("ar")),
)

View File

@ -2,8 +2,8 @@ ext {
extName = 'Empire Webtoon'
extClass = '.EmpireWebtoon'
themePkg = 'madara'
baseUrl = 'https://webtoonempire-ron.com'
overrideVersionCode = 4
baseUrl = 'https://webtoonsempireron.com'
overrideVersionCode = 3
isNsfw = true
}

View File

@ -4,6 +4,7 @@ import android.app.Application
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.extension.BuildConfig
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.source.ConfigurableSource
import uy.kohesive.injekt.Injekt
@ -14,56 +15,46 @@ import java.util.Locale
class EmpireWebtoon :
Madara(
"Empire Webtoon",
"https://webtoonempire-ron.com",
"https://webtoonsempireron.com",
"ar",
SimpleDateFormat("d MMMM، yyyy", Locale("ar")),
),
ConfigurableSource {
private val preferences: SharedPreferences =
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
init {
preferences.getString(DEFAULT_BASE_URL_PREF, null).let { prefDefaultBaseUrl ->
if (prefDefaultBaseUrl != super.baseUrl) {
preferences.edit()
.putString(BASE_URL_PREF, super.baseUrl)
.putString(DEFAULT_BASE_URL_PREF, super.baseUrl)
.apply()
}
}
}
private val defaultBaseUrl = "https://webtoonsempireron.com"
override val baseUrl by lazy { getPrefBaseUrl() }
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override val mangaSubString = "webtoon"
override val useNewChapterEndpoint = false
companion object {
private const val RESTART_TACHIYOMI = "Restart Tachiyomi to apply new setting."
private const val BASE_URL_PREF_TITLE = "Override BaseUrl"
private const val BASE_URL_PREF = "overrideBaseUrl_v${BuildConfig.VERSION_CODE}"
private const val BASE_URL_PREF_SUMMARY = "For temporary uses. Updating the extension will erase this setting."
}
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
setDefaultValue(super.baseUrl)
this.setDefaultValue(defaultBaseUrl)
dialogTitle = BASE_URL_PREF_TITLE
dialogMessage = "Default: ${super.baseUrl}"
setOnPreferenceChangeListener { _, _ ->
Toast.makeText(screen.context, RESTART_APP, Toast.LENGTH_LONG).show()
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
true
}
}
screen.addPreference(baseUrlPref)
}
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, super.baseUrl)!!
companion object {
private const val DEFAULT_BASE_URL_PREF = "defaultBaseUrl"
private const val RESTART_APP = "Restart app to apply new setting."
private const val BASE_URL_PREF_TITLE = "Override BaseUrl"
private const val BASE_URL_PREF = "overrideBaseUrl"
private const val BASE_URL_PREF_SUMMARY = "For temporary uses. Updating the extension will erase this setting."
}
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!!
}

View File

@ -2,8 +2,8 @@ ext {
extName = 'MangaNoon'
extClass = '.MangaNoon'
themePkg = 'mangathemesia'
baseUrl = 'https://manjanoon.xyz'
overrideVersionCode = 5
baseUrl = 'https://noonscan.net'
overrideVersionCode = 4
isNsfw = false
}

View File

@ -7,7 +7,7 @@ import java.util.Calendar
class MangaNoon : MangaThemesia(
"مانجا نون",
"https://manjanoon.xyz",
"https://noonscan.net",
"ar",
) {

View File

@ -2,8 +2,8 @@ ext {
extName = 'MangaSwat'
extClass = '.MangaSwat'
themePkg = 'mangathemesia'
baseUrl = 'https://healteer.com'
overrideVersionCode = 23
baseUrl = 'https://tatwt.com'
overrideVersionCode = 21
}
apply from: "$rootDir/common.gradle"

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension.ar.mangaswat
import android.app.Application
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
@ -11,6 +12,7 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
@ -22,7 +24,7 @@ import java.util.Locale
class MangaSwat :
MangaThemesia(
"MangaSwat",
"https://healteer.com",
"https://tatwt.com",
"ar",
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
),
@ -30,53 +32,29 @@ class MangaSwat :
override val baseUrl by lazy { getPrefBaseUrl() }
private val preferences by lazy {
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override val client = super.client.newBuilder()
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1)
.build()
override fun latestUpdatesRequest(page: Int): Request {
val filter = FilterList(OrderByFilter("", orderByFilterOptions, "added"))
return searchMangaRequest(page, "", filter)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val request = super.searchMangaRequest(page, query, filters)
val urlBuilder = request.url.newBuilder()
if (query.isBlank()) return request
// remove trailing slash
if (request.url.pathSegments.last().isBlank()) {
urlBuilder.removePathSegment(
request.url.pathSegments.lastIndex,
)
}
if (query.isNotBlank()) {
urlBuilder
val url = request.url.newBuilder()
.removePathSegment(0)
.removeAllQueryParameters("title")
.addQueryParameter("s", query)
.build()
}
return request.newBuilder()
.url(urlBuilder.build())
.url(url)
.build()
}
override val orderByFilterOptions = arrayOf(
Pair(intl["order_by_filter_default"], ""),
Pair(intl["order_by_filter_az"], "a-z"),
Pair(intl["order_by_filter_za"], "z-a"),
Pair(intl["order_by_filter_latest_update"], "update"),
Pair(intl["order_by_filter_latest_added"], "added"),
Pair(intl["order_by_filter_popular"], "popular"),
)
override fun searchMangaNextPageSelector() = "a[rel=next]"
override val seriesTitleSelector = "h1[itemprop=headline]"
@ -104,12 +82,13 @@ class MangaSwat :
}
@Serializable
class TSReader(
data class TSReader(
val sources: List<ReaderImageSource>,
)
@Serializable
class ReaderImageSource(
data class ReaderImageSource(
val source: String,
val images: List<String>,
)

View File

@ -3,7 +3,7 @@ ext {
extClass = '.RocksManga'
themePkg = 'madara'
baseUrl = 'https://rocksmanga.com'
overrideVersionCode = 2
overrideVersionCode = 1
isNsfw = false
}

View File

@ -49,7 +49,7 @@ class RocksManga : Madara(
override val mangaDetailsSelectorThumbnail = ".manga-poster img"
override val mangaDetailsSelectorGenre = "div.meta span:contains(التصنيف:) + span a"
override val altNameSelector = "div.alternative"
override fun chapterListSelector() = "li.chapter-item"
override fun chapterListSelector() = ".chapters-list li.chapter-item"
override fun chapterDateSelector() = ".chapter-release-date"
override val pageListParseSelector = ".chapter-reading-page img"

View File

@ -1,10 +0,0 @@
ext {
extName = 'Scans 4u'
extClass = '.Scans4u'
themePkg = 'keyoapp'
baseUrl = 'https://4uscans.com'
overrideVersionCode = 0
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,5 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.scans4u
import eu.kanade.tachiyomi.multisrc.keyoapp.Keyoapp
class Scans4u : Keyoapp("Scans 4u", "https://4uscans.com", "ar")

View File

@ -2,8 +2,8 @@ ext {
extName = 'YonaBar'
extClass = '.YonaBar'
themePkg = 'madara'
baseUrl = 'https://yonabar.xyz'
overrideVersionCode = 3
baseUrl = 'https://yonabar.com'
overrideVersionCode = 2
isNsfw = true
}

View File

@ -1,19 +1,7 @@
package eu.kanade.tachiyomi.extension.ar.yonabar
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import java.text.SimpleDateFormat
import java.util.Locale
class YonaBar : Madara(
"YonaBar",
"https://yonabar.xyz",
"ar",
SimpleDateFormat("MMM dd, yyyy", Locale("ar")),
) {
override val client = super.client.newBuilder()
.rateLimit(3)
.build()
override val mangaSubString = "yaoi"
}
class YonaBar : Madara("YonaBar", "https://yonabar.com", "ar", SimpleDateFormat("yyyy-MM-dd", Locale("ar")))

View File

@ -3,7 +3,7 @@ ext {
extClass = '.YuriMoonSub'
themePkg = 'zeistmanga'
baseUrl = 'https://yurimoonsub.blogspot.com'
overrideVersionCode = 1
overrideVersionCode = 0
isNsfw = true
}

View File

@ -2,30 +2,9 @@ package eu.kanade.tachiyomi.extension.ar.yurimoonsub
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import org.jsoup.nodes.Document
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
class YuriMoonSub : ZeistManga(
"Yuri Moon Sub",
"https://yurimoonsub.blogspot.com",
"ar",
) {
class YuriMoonSub : ZeistManga("Yuri Moon Sub", "https://yurimoonsub.blogspot.com", "ar") {
override val client = super.client.newBuilder()
.rateLimit(2)
.build()
override fun getChapterFeedUrl(doc: Document): String {
return URLDecoder.decode(super.getChapterFeedUrl(doc), StandardCharsets.UTF_8.toString())
.removeArabicChars()
}
private fun String.removeArabicChars() =
this.replace(ARABIC_CHARS_REGEX, "")
.replace(EXTRA_SPACES_REGEX, "")
companion object {
val ARABIC_CHARS_REGEX = "[\\u0600-\\u06FF]".toRegex()
val EXTRA_SPACES_REGEX = "\\s{2,}".toRegex()
}
}

View File

@ -0,0 +1,7 @@
ext {
extName = 'WieManga'
extClass = '.WieManga'
extVersionCode = 5
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Some files were not shown because too many files have changed in this diff Show More