Compare commits

..

No commits in common. "1b722d308d2a14ffe2edc7f7b26c387ea37f2b3d" and "480cb9d780ba4c8d24393340a5b13a1e2b74f3ee" have entirely different histories.

147 changed files with 699 additions and 1475 deletions

View File

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

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.multisrc.flixscans
import android.util.Log
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
@ -14,17 +14,18 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Call
import okhttp3.Callback
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.io.IOException
abstract class FlixScans(
override val name: String,
override val baseUrl: String,
override val lang: String,
protected val apiUrl: String = "$baseUrl/api/v1",
protected val apiUrl: String = "$baseUrl/api/__api_party/noxApi",
protected val cdnUrl: String = baseUrl.replace("://", "://media.").plus("/"),
) : HttpSource() {
@ -37,10 +38,22 @@ abstract class FlixScans(
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
.add("Referer", baseUrl)
protected open fun postPath(path: String): Request {
val payload = """{"path":"$path","headers":{}}""".toRequestBody(JSON_MEDIA_TYPE)
return POST(apiUrl, headers, payload)
}
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
runCatching { fetchGenre() }
return super.fetchPopularManga(page)
}
override fun popularMangaRequest(page: Int): Request {
return GET("$apiUrl/webtoon/pages/home/romance", headers)
return postPath("webtoon/pages/home/romance")
}
override fun popularMangaParse(response: Response): MangasPage {
@ -53,15 +66,21 @@ abstract class FlixScans(
return MangasPage(entries, false)
}
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
runCatching { fetchGenre() }
return super.fetchLatestUpdates(page)
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$apiUrl/search/advance?page=$page&serie_type=webtoon", headers)
return postPath("search/advance?page=$page&serie_type=webtoon")
}
override fun latestUpdatesParse(response: Response): MangasPage {
val result = response.parseAs<ApiResponse<BrowseSeries>>()
val entries = result.data.map { it.toSManga(cdnUrl) }
val hasNextPage = result.lastPage > result.currentPage
val hasNextPage = result.meta.lastPage > result.meta.currentPage
return MangasPage(entries, hasNextPage)
}
@ -81,7 +100,7 @@ abstract class FlixScans(
}
private val fetchGenreCallback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
override fun onFailure(call: Call, e: okio.IOException) {
fetchGenreAttempt++
fetchGenreFailed = true
fetchGenreCallOngoing = false
@ -113,7 +132,7 @@ abstract class FlixScans(
}
private fun fetchGenreRequest(): Request {
return GET("$apiUrl/search/genres", headers)
return postPath("search/genres")
}
private fun fetchGenreParse(response: Response): List<GenreHolder> {
@ -121,8 +140,6 @@ abstract class FlixScans(
}
override fun getFilterList(): FilterList {
fetchGenre()
val filters: MutableList<Filter<*>> = mutableListOf(
Filter.Header("Ignored when using Text Search"),
MainGenreFilter(),
@ -144,59 +161,60 @@ abstract class FlixScans(
return FilterList(filters)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) {
val url = "$apiUrl/search/serie".toHttpUrl().newBuilder()
.addPathSegment(query.trim())
.addQueryParameter("page", page.toString())
.build()
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
runCatching { fetchGenre() }
return GET(url, headers)
return super.fetchSearchManga(page, query, filters)
}
val advSearchUrl = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("search/advance")
addQueryParameter("page", page.toString())
addQueryParameter("serie_type", "webtoon")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) {
return postPath("search/serie/${query.trim()}?page=$page")
}
val advSearchBody = buildString {
append("search/advance")
append("?page=", page)
append("&serie_type=webtoon")
filters.forEach { filter ->
when (filter) {
is GenreFilter -> {
filter.checked.let {
if (it.isNotEmpty()) {
addQueryParameter("genres", it.joinToString(","))
append("&genres=", it.joinToString(","))
}
}
}
is MainGenreFilter -> {
if (filter.state > 0) {
addQueryParameter("main_genres", filter.selected)
append("&main_genres=", filter.selected)
}
}
is TypeFilter -> {
if (filter.state > 0) {
addQueryParameter("type", filter.selected)
append("&type=", filter.selected)
}
}
is StatusFilter -> {
if (filter.state > 0) {
addQueryParameter("status", filter.selected)
append("&status=", filter.selected)
}
}
else -> {}
}
}
}.build()
}
return GET(advSearchUrl, headers)
return postPath(advSearchBody)
}
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
override fun mangaDetailsRequest(manga: SManga): Request {
val (prefix, id) = getPrefixIdFromUrl(manga.url)
val id = manga.url.split("-")[1]
return GET("$apiUrl/webtoon/series/$id/$prefix", headers)
return postPath("webtoon/series/$id")
}
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
@ -208,30 +226,23 @@ abstract class FlixScans(
}
override fun chapterListRequest(manga: SManga): Request {
val (prefix, id) = getPrefixIdFromUrl(manga.url)
val id = manga.url.split("-")[1]
return GET("$apiUrl/webtoon/chapters/$id-desc#$prefix", headers)
return postPath("webtoon/chapters/$id-desc")
}
override fun chapterListParse(response: Response): List<SChapter> {
val chapters = response.parseAs<List<Chapter>>()
val prefix = response.request.url.fragment!!
return chapters.map { it.toSChapter(prefix) }
return chapters.map(Chapter::toSChapter)
}
override fun pageListRequest(chapter: SChapter): Request {
val (prefix, id) = getPrefixIdFromUrl(chapter.url)
val id = chapter.url
.substringAfterLast("/")
.substringBefore("-")
return GET("$apiUrl/webtoon/chapters/chapter/$id/$prefix", headers)
}
protected fun getPrefixIdFromUrl(url: String): Pair<String, String> {
return with(url.substringAfterLast("/")) {
val split = split("-")
split[0] to split[1]
}
return postPath("webtoon/chapters/chapter/$id")
}
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
@ -248,4 +259,8 @@ abstract class FlixScans(
protected inline fun <reified T> Response.parseAs(): T =
use { body.string() }.let(json::decodeFromString)
companion object {
private val JSON_MEDIA_TYPE = "application/json".toMediaTypeOrNull()
}
}

View File

@ -11,8 +11,13 @@ import java.util.Locale
@Serializable
data class ApiResponse<T>(
val data: List<T>,
@SerialName("current_page") val currentPage: Int,
val meta: PageInfo,
)
@Serializable
data class PageInfo(
@SerialName("last_page") val lastPage: Int,
@SerialName("current_page") val currentPage: Int,
)
@Serializable
@ -115,8 +120,8 @@ data class Chapter(
val slug: String,
val createdAt: String? = null,
) {
fun toSChapter(prefix: String) = SChapter.create().apply {
url = "/read/webtoon/$prefix-$id-$slug"
fun toSChapter() = SChapter.create().apply {
url = "/read/webtoon/$id-$slug"
name = this@Chapter.name
date_upload = runCatching { dateFormat.parse(createdAt!!)!!.time }.getOrDefault(0L)
}

View File

@ -28,7 +28,7 @@ abstract class Gmanga(
override val name: String,
override val baseUrl: String,
final override val lang: String,
protected open val cdnUrl: String = baseUrl,
protected val cdnUrl: String = baseUrl,
) : HttpSource() {
override val supportsLatest = true

View File

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

View File

@ -1,158 +0,0 @@
package eu.kanade.tachiyomi.multisrc.hotcomics
import eu.kanade.tachiyomi.lib.cookieinterceptor.CookieInterceptor
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.nodes.Element
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale
abstract class HotComics(
final override val name: String,
final override val lang: String,
final override val baseUrl: String,
) : HttpSource() {
override val supportsLatest = true
override val client = network.cloudflareClient.newBuilder()
.addNetworkInterceptor(
CookieInterceptor(baseUrl.removePrefix("https://"), "hc_vfs" to "Y"),
)
.build()
override fun headersBuilder() = super.headersBuilder()
.set("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int) = GET("$baseUrl/en", headers)
override fun popularMangaParse(response: Response) = searchMangaParse(response)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/en/new", headers)
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = baseUrl.toHttpUrl().newBuilder().apply {
if (query.isNotEmpty()) {
addEncodedPathSegments("en/search")
addQueryParameter("keyword", query.trim())
} else {
val filter = filters.filterIsInstance<BrowseFilter>().first()
addEncodedPathSegments(filter.selected)
addQueryParameter("page", page.toString())
}
}.build()
return GET(url, headers)
}
abstract class SelectFilter(
name: String,
private val options: List<Pair<String, String>>,
) : Filter.Select<String>(
name,
options.map { it.first }.toTypedArray(),
) {
val selected get() = options[state].second
}
abstract val browseList: List<Pair<String, String>>
class BrowseFilter(browseList: List<Pair<String, String>>) : SelectFilter("Browse", browseList)
override fun getFilterList() = FilterList(
Filter.Header("Doesn't work with Text search"),
Filter.Separator(),
BrowseFilter(browseList),
)
override fun searchMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val entries = document.select("li[itemtype*=ComicSeries]:not(.no-comic) > a").map { element ->
SManga.create().apply {
setUrlWithoutDomain(element.absUrl("href"))
thumbnail_url = element.selectFirst("div.visual img")?.imgAttr()
title = element.selectFirst("div.main-text > h4.title")!!.text()
}
}.distinctBy { it.url }
val hasNextPage = document.selectFirst("div.pagination a.vnext:not(.disabled)") != null
return MangasPage(entries, hasNextPage)
}
override fun mangaDetailsParse(response: Response) = SManga.create().apply {
val document = response.asJsoup()
title = document.selectFirst("h2.episode-title")!!.text()
with(document.selectFirst("p.type_box")!!) {
author = selectFirst("span.writer")?.text()
?.substringAfter("")?.trim()
genre = selectFirst("span.type")?.text()
?.split("/")?.joinToString { it.trim() }
status = when (selectFirst("span.date")?.text()) {
"End", "Ende" -> SManga.COMPLETED
null -> SManga.UNKNOWN
else -> SManga.ONGOING
}
}
description = buildString {
document.selectFirst("div.episode-contents header")
?.text()?.let {
append(it)
append("\n\n")
}
document.selectFirst("div.title_content > h2:not(.episode-title)")
?.text()?.let { append(it) }
}.trim()
}
override fun chapterListParse(response: Response): List<SChapter> {
return response.asJsoup().select("#tab-chapter a").map { element ->
SChapter.create().apply {
setUrlWithoutDomain(element.absUrl("href"))
name = element.selectFirst(".cell-num")!!.text()
date_upload = parseDate(element.selectFirst(".cell-time")?.text())
}
}.reversed()
}
private val dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH)
private fun parseDate(date: String?): Long {
date ?: return 0L
return try {
dateFormat.parse(date)!!.time
} catch (_: ParseException) {
0L
}
}
override fun pageListParse(response: Response): List<Page> {
return response.asJsoup().select("#viewer-img img").mapIndexed { idx, img ->
Page(idx, imageUrl = img.imgAttr())
}
}
private fun Element.imgAttr(): String {
return when {
hasAttr("data-src") -> absUrl("data-src")
else -> absUrl("src")
}
}
override fun imageUrlParse(response: Response): String {
throw UnsupportedOperationException()
}
}

View File

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

View File

@ -27,7 +27,7 @@ abstract class ZeistManga(
override val supportsLatest = true
protected val json: Json by injectLazy()
private val json: Json by injectLazy()
private val intl by lazy { ZeistMangaIntl(lang) }
@ -63,7 +63,23 @@ abstract class ZeistManga(
return GET(url, headers)
}
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
override fun latestUpdatesParse(response: Response): MangasPage {
val jsonString = response.body.string()
val result = json.decodeFromString<ZeistMangaDto>(jsonString)
val mangas = result.feed?.entry.orEmpty()
.filter { it.category.orEmpty().any { category -> category.term == "Series" } }
.filter { !it.category.orEmpty().any { category -> category.term == "Anime" } }
.map { it.toSManga(baseUrl) }
val mangalist = mangas.toMutableList()
if (mangas.size == maxMangaResults + 1) {
mangalist.removeLast()
return MangasPage(mangalist, true)
}
return MangasPage(mangalist, false)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val startIndex = maxMangaResults * (page - 1) + 1
@ -107,25 +123,7 @@ abstract class ZeistManga(
return GET(url.build(), headers)
}
protected open val excludedCategories: List<String> = listOf("Anime")
override fun searchMangaParse(response: Response): MangasPage {
val jsonString = response.body.string()
val result = json.decodeFromString<ZeistMangaDto>(jsonString)
val mangas = result.feed?.entry.orEmpty()
.filter { it.category.orEmpty().any { category -> category.term == "Series" } } // Default category for all series
.filterNot { it.category.orEmpty().any { category -> excludedCategories.contains(category.term) } }
.map { it.toSManga(baseUrl) }
val mangalist = mangas.toMutableList()
if (mangas.size == maxMangaResults + 1) {
mangalist.removeLast()
return MangasPage(mangalist, true)
}
return MangasPage(mangalist, false)
}
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
protected open val statusSelectorList = listOf(
"Status",
@ -201,9 +199,13 @@ abstract class ZeistManga(
val document = response.asJsoup()
val url = getChapterFeedUrl(document)
val res = client.newCall(GET(url, headers)).execute()
val result = json.decodeFromString<ZeistMangaDto>(res.body.string())
val req = GET(url, headers)
val res = client.newCall(req).execute()
val jsonString = res.body.string()
val result = json.decodeFromString<ZeistMangaDto>(jsonString)
return result.feed?.entry?.filter { it.category.orEmpty().any { category -> category.term == chapterCategory } }
?.map { it.toSChapter(baseUrl) }
?: throw Exception("Failed to parse from chapter API")
@ -390,7 +392,6 @@ abstract class ZeistManga(
"ongoing",
"en curso",
"en emisión",
"activo",
"ativo",
"lançando",
"مستمر",
@ -399,12 +400,10 @@ abstract class ZeistManga(
protected open val statusCompletedList = listOf(
"completed",
"completo",
"finalizado",
)
protected open val statusHiatusList = listOf(
"hiatus",
"pausado",
)
protected open val statusCancelledList = listOf(

View File

@ -1,9 +1,9 @@
ext {
extName = 'HNI-Scantrad'
extClass = '.HNIScantrad'
themePkg = 'pizzareader'
baseUrl = 'https://hni-scantrad.net'
overrideVersionCode = 5
extClass = '.HNIScantradFactory'
themePkg = 'foolslide'
baseUrl = 'https://hni-scantrad.com'
overrideVersionCode = 1
}
apply from: "$rootDir/common.gradle"

View File

@ -1,29 +0,0 @@
package eu.kanade.tachiyomi.extension.all.hniscantrad
import eu.kanade.tachiyomi.multisrc.pizzareader.PizzaReader
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.json.Json
class HNIScantrad : PizzaReader("HNI-Scantrad", "https://hni-scantrad.net", "all") {
override val json = Json {
ignoreUnknownKeys = true
coerceInputValues = true
}
override fun String.toStatus(): Int {
return if (isEmpty()) {
SManga.UNKNOWN
} else {
when (substring(0, 7)) {
"In cors" -> SManga.ONGOING
"On goin" -> SManga.ONGOING
"Complet" -> SManga.COMPLETED
"Conclus" -> SManga.COMPLETED
"Conclud" -> SManga.COMPLETED
"Licenzi" -> SManga.LICENSED
"License" -> SManga.LICENSED
else -> SManga.UNKNOWN
}
}
}
}

View File

@ -0,0 +1,19 @@
package eu.kanade.tachiyomi.extension.all.hniscantrad
import eu.kanade.tachiyomi.multisrc.foolslide.FoolSlide
import eu.kanade.tachiyomi.source.SourceFactory
import okhttp3.Response
class HNIScantradFactory : SourceFactory {
override fun createSources() = listOf(HNIScantradFR(), HNIScantradEN())
}
class HNIScantradFR : FoolSlide("HNI-Scantrad", "https://hni-scantrad.com", "fr", "/lel") {
override fun chapterListParse(response: Response) =
super.chapterListParse(response).filter { "/fr/" in it.url }
}
class HNIScantradEN : FoolSlide("HNI-Scantrad", "https://hni-scantrad.com", "en", "/lel") {
override fun chapterListParse(response: Response) =
super.chapterListParse(response).filter { "/en-us/" in it.url }
}

View File

@ -2,7 +2,7 @@ ext {
extName = 'Dilar'
extClass = '.Dilar'
themePkg = 'gmanga'
overrideVersionCode = 2
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

View File

@ -1,30 +1,15 @@
package eu.kanade.tachiyomi.extension.ar.dilar
import android.app.Application
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.multisrc.gmanga.Gmanga
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
private const val MIRROR_PREF_KEY = "MIRROR"
private const val MIRROR_PREF_TITLE = "Dilar : Mirror Urls"
private val MIRROR_PREF_ENTRY_VALUES = arrayOf("https://dilar.tube", "https://golden.rest")
private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0]
private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل"
class Dilar :
ConfigurableSource, Gmanga(
class Dilar : Gmanga(
"Dilar",
MIRROR_PREF_DEFAULT_VALUE,
"https://dilar.tube",
"ar",
) {
override fun chaptersRequest(manga: SManga): Request {
@ -38,34 +23,4 @@ class Dilar :
return releases.map { it.toSChapter() }
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val mirrorPref = ListPreference(screen.context).apply {
key = MIRROR_PREF_KEY
title = MIRROR_PREF_TITLE
entries = MIRROR_PREF_ENTRY_VALUES
entryValues = MIRROR_PREF_ENTRY_VALUES
setDefaultValue(MIRROR_PREF_DEFAULT_VALUE)
summary = "%s"
setOnPreferenceChangeListener { _, _ ->
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
true
}
}
screen.addPreference(mirrorPref)
}
private fun mirrorPref() = when {
System.getenv("CI") == "true" -> MIRROR_PREF_ENTRY_VALUES.joinToString("#, ")
else -> preferences.getString(MIRROR_PREF_KEY, MIRROR_PREF_DEFAULT_VALUE)!!
}
override val baseUrl by lazy { mirrorPref() }
override val cdnUrl by lazy { baseUrl }
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
}

View File

@ -2,11 +2,6 @@ package eu.kanade.tachiyomi.extension.ar.galaxymanga
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
class GalaxyManga : FlixScans(
"جالاكسي مانجا",
"https://flixscans.com",
"ar",
"https://ar.flixscans.site/api/v1",
) {
class GalaxyManga : FlixScans("جالاكسي مانجا", "https://flixscans.com", "ar") {
override val versionId = 2
}

View File

@ -3,8 +3,7 @@ ext {
extClass = '.Mangalek'
themePkg = 'madara'
baseUrl = 'https://lekmanga.net'
overrideVersionCode = 6
isNsfw = false
overrideVersionCode = 5
}
apply from: "$rootDir/common.gradle"

View File

@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.extension.ar.mangalek
import android.app.Application
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.extension.BuildConfig
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.ConfigurableSource
@ -18,25 +18,14 @@ import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale
private const val MIRROR_PREF_KEY = "MIRROR"
private const val MIRROR_PREF_TITLE = "تعديل رابط مانجا ليك"
internal val MIRROR_PREF_ENTRY_VALUES = arrayOf(
"https://lekmanga.net",
"https://lekmanga.org",
"https://like-manga.net",
"https://lekmanga.com",
)
private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0]
private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل"
private const val mangalekUrl = "https://lekmanga.net"
class Mangalek :
Madara(
"مانجا ليك",
MIRROR_PREF_DEFAULT_VALUE,
mangalekUrl,
"ar",
SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
),
@ -46,50 +35,38 @@ class Mangalek :
override val useLoadMoreRequest = LoadMoreStrategy.Always
override val chapterUrlSuffix = ""
override val baseUrl by lazy {
when {
System.getenv("CI") == "true" -> MIRROR_PREF_ENTRY_VALUES.joinToString("#, ")
else -> preferences.getString(MIRROR_PREF_KEY, MIRROR_PREF_DEFAULT_VALUE)!!
}
}
private val defaultBaseUrl = mangalekUrl
override val baseUrl by lazy { getPrefBaseUrl() }
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
companion object {
private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل"
private const val BASE_URL_PREF_TITLE = "تعديل الرابط"
private const val BASE_URL_PREF = "overrideBaseUrl_v${BuildConfig.VERSION_CODE}"
private const val BASE_URL_PREF_SUMMARY = ".للاستخدام المؤقت. تحديث التطبيق سيؤدي الى حذف الإعدادات"
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val mirrorPref = ListPreference(screen.context).apply {
key = MIRROR_PREF_KEY
title = MIRROR_PREF_TITLE
entries = MIRROR_PREF_ENTRY_VALUES
entryValues = MIRROR_PREF_ENTRY_VALUES
setDefaultValue(MIRROR_PREF_DEFAULT_VALUE)
summary = "%s"
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_TACHIYOMI, Toast.LENGTH_LONG).show()
true
}
}
screen.addPreference(mirrorPref)
screen.addPreference(baseUrlPref)
}
private val formatOne = SimpleDateFormat("MMMM dd, yyyy", Locale("ar"))
private val formatTwo = SimpleDateFormat("yyyy-MM-dd", Locale.US)
override fun parseChapterDate(date: String?): Long {
date ?: return 0L
return try {
formatOne.parse(date)!!.time
} catch (_: ParseException) {
try {
formatTwo.parse(date)!!.time
} catch (_: ParseException) {
0L
}
}
}
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!!
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
POST(

View File

@ -5,10 +5,13 @@ import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaDto
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
class Yokai : ZeistManga("Yokai", "https://yokai-team.blogspot.com", "ar") {
private val json: Json by injectLazy()
// ============================== Chapters ==============================
override fun chapterListParse(response: Response): List<SChapter> {

View File

@ -1,9 +0,0 @@
ext {
extName = 'Toomics.Top'
extClass = '.ToomicsTop'
themePkg = 'hotcomics'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,44 +0,0 @@
package eu.kanade.tachiyomi.extension.de.toomicstop
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
import eu.kanade.tachiyomi.source.model.MangasPage
import okhttp3.Response
class ToomicsTop : HotComics(
"Toomics.Top",
"de",
"https://toomics.top",
) {
override fun searchMangaParse(response: Response): MangasPage {
val mangasPage = super.searchMangaParse(response)
mangasPage.mangas.apply {
for (i in indices) {
this[i].url = this[i].url.replace(urlIdRegex, ".html")
}
}
return mangasPage
}
private val urlIdRegex = Regex("""(/\w+).html""")
override val browseList = listOf(
Pair("Home", "en"),
Pair("Weekly", "en/weekly"),
Pair("New", "en/new"),
Pair("Genre: All", "en/genres"),
Pair("Genre: Romantik", "en/genres/Romantik"),
Pair("Genre: Drama", "en/genres/Drama"),
Pair("Genre: BL", "en/genres/BL"),
Pair("Genre: Action", "en/genres/Action"),
Pair("Genre: Schulleben", "en/genres/Schulleben"),
Pair("Genre: Fantasy", "en/genres/Fantasy"),
Pair("Genre: Comedy", "en/genres/Comedy"),
Pair("Genre: Historisch", "en/genres/Historisch"),
Pair("Genre: Sci-Fi", "en/genres/Sci-Fi"),
Pair("Genre: Thriller", "en/genres/Thriller"),
Pair("Genre: Horror", "en/genres/Horror"),
Pair("Genre: Sport", "en/genres/Sport"),
Pair("Genre: GL", "en/genres/GL"),
)
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'DAYcomics.me'
extClass = '.DAYcomicsMe'
themePkg = 'hotcomics'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,30 +0,0 @@
package eu.kanade.tachiyomi.extension.en.daycomicsme
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
class DAYcomicsMe : HotComics(
"DAYcomics.me",
"en",
"https://daycomics.me",
) {
override val browseList = listOf(
Pair("Home", "en"),
Pair("Weekly", "en/weekly"),
Pair("New", "en/new"),
Pair("Genre: All", "en/genres"),
Pair("Genre: Romance", "en/genres/Romance"),
Pair("Genre: Office", "en/genres/Office"),
Pair("Genre: College", "en/genres/College"),
Pair("Genre: Drama", "en/genres/Drama"),
Pair("Genre: Isekai", "en/genres/Isekai"),
Pair("Genre: UNCENSORED", "en/genres/UNCENSORED"),
Pair("Genre: Action", "en/genres/Action"),
Pair("Genre: BL", "en/genres/BL"),
Pair("Genre: New", "en/genres/New"),
Pair("Genre: Slice of Life", "en/genres/Slice_of_Life"),
Pair("Genre: Supernatural", "en/genres/Supernatural"),
Pair("Genre: Historical", "en/genres/Historical"),
Pair("Genre: School Life", "en/genres/School_Life"),
Pair("Genre: Horror Thriller", "en/genres/Horror_Thriller"),
)
}

View File

@ -2,9 +2,4 @@ package eu.kanade.tachiyomi.extension.en.flixscans
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
class FlixScansNet : FlixScans(
"Flix Scans",
"https://flixscans.org",
"en",
"https://flixscans.site/api/v1",
)
class FlixScansNet : FlixScans("Flix Scans", "https://flixscans.org", "en")

View File

@ -1,9 +0,0 @@
ext {
extName = 'HotComics'
extClass = '.HotComics'
themePkg = 'hotcomics'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,29 +0,0 @@
package eu.kanade.tachiyomi.extension.en.hotcomics
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
class HotComics : HotComics(
"HotComics",
"en",
"https://hotcomics.me",
) {
override val browseList = listOf(
Pair("Home", "en"),
Pair("Weekly", "en/weekly"),
Pair("New", "en/new"),
Pair("Genre: All", "en/genres"),
Pair("Genre: Sports", "en/genres/Sports"),
Pair("Genre: Historical", "en/genres/Historical"),
Pair("Genre: Drama", "en/genres/Drama"),
Pair("Genre: BL", "en/genres/BL"),
Pair("Genre: Thriller", "en/genres/Thriller"),
Pair("Genre: School life", "en/genres/School_life"),
Pair("Genre: Comedy", "en/genres/Comedy"),
Pair("Genre: GL", "en/genres/GL"),
Pair("Genre: Action", "en/genres/Action"),
Pair("Genre: Sci-fi", "en/genres/Sci-fi"),
Pair("Genre: Horror", "en/genres/Horror"),
Pair("Genre: Fantasy", "en/genres/Fantasy"),
Pair("Genre: Romance", "en/genres/Romance"),
)
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'Laid Back Scans'
extClass = '.LaidBackScans'
themePkg = 'keyoapp'
baseUrl = 'https://laidbackscans.org'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,5 +0,0 @@
package eu.kanade.tachiyomi.extension.en.laidbackscans
import eu.kanade.tachiyomi.multisrc.keyoapp.Keyoapp
class LaidBackScans : Keyoapp("Laid Back Scans", "https://laidbackscans.org", "en")

View File

@ -2,8 +2,8 @@ ext {
extName = 'Magus Manga'
extClass = '.MagusManga'
themePkg = 'mangathemesia'
baseUrl = 'https://neroscans.com'
overrideVersionCode = 4
baseUrl = 'https://vofeg.com'
overrideVersionCode = 3
}
apply from: "$rootDir/common.gradle"

View File

@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit
class MagusManga : MangaThemesia(
"Magus Manga",
"https://neroscans.com",
"https://vofeg.com",
"en",
mangaUrlDirectory = "/series",
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("en")),

View File

@ -2,8 +2,8 @@ ext {
extName = 'Read Attack on Titan Shingeki no Kyojin Manga'
extClass = '.ReadAttackOnTitanShingekiNoKyojinManga'
themePkg = 'mangacatalog'
baseUrl = 'https://ww9.readsnk.com'
overrideVersionCode = 5
baseUrl = 'https://ww8.readsnk.com'
overrideVersionCode = 4
}
apply from: "$rootDir/common.gradle"

View File

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.multisrc.mangacatalog.MangaCatalog
import eu.kanade.tachiyomi.source.model.SChapter
import org.jsoup.nodes.Element
class ReadAttackOnTitanShingekiNoKyojinManga : MangaCatalog("Read Attack on Titan Shingeki no Kyojin Manga", "https://ww9.readsnk.com", "en") {
class ReadAttackOnTitanShingekiNoKyojinManga : MangaCatalog("Read Attack on Titan Shingeki no Kyojin Manga", "https://ww8.readsnk.com", "en") {
override val sourceList = listOf(
Pair("Shingeki No Kyojin", "$baseUrl/manga/shingeki-no-kyojin/"),
Pair("Colored", "$baseUrl/manga/shingeki-no-kyojin-colored/"),
@ -20,7 +20,7 @@ class ReadAttackOnTitanShingekiNoKyojinManga : MangaCatalog("Read Attack on Tita
Pair("No Regrets Colored", "$baseUrl/manga/attack-on-titan-no-regrets-colored/"),
).sortedBy { it.first }.distinctBy { it.second }
override fun chapterListSelector(): String = "div.w-full div.grid div.col-span-4"
override fun chapterListSelector(): String = "div.w-full div.grid div.col-span-3"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
val urlElement = element.selectFirst("a")!!

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -1,234 +0,0 @@
package eu.kanade.tachiyomi.extension.en.readcomictop
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.online.ParsedHttpSource
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
class ReadComicTop : ParsedHttpSource() {
override val name = "ReadComic.Top"
override val baseUrl = "https://readcomic.top"
override val lang = "en"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
override fun popularMangaSelector() = "div.eg-box"
override fun latestUpdatesSelector() = "ul.line-list"
override fun popularMangaRequest(page: Int): Request {
val url = "$baseUrl/popular-comics".toHttpUrl().newBuilder().apply {
if (page > 1) addQueryParameter("page", page.toString())
}.build()
return GET(url, headers)
}
override fun latestUpdatesRequest(page: Int): Request {
val url = "$baseUrl/comic-updates".toHttpUrl().newBuilder().apply {
if (page > 1) addQueryParameter("page", page.toString())
}.build()
return GET(url, headers)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/advanced-search".toHttpUrl().newBuilder().apply {
addQueryParameter("key", query)
filters.forEach { filter ->
when (filter) {
is GenreFilter -> {
addQueryParameter("wg", filter.included.joinToString("%2C"))
addQueryParameter("wog", filter.excluded.joinToString("%2C"))
}
is StatusFilter -> if (filter.toUriPart().isNotBlank()) {
addQueryParameter("status", filter.toUriPart())
}
else -> {}
}
}
if (page > 1) addQueryParameter("page", page.toString())
}.build()
return GET(url, headers)
}
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.select("div.egb-right > a.egb-serie").attr("href"))
title = element.select("div.egb-right > a.egb-serie").text()
thumbnail_url = element.select("a.eg-image > img").attr("src")
}
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
with(element.select("ul.line-list > li > a.big-link")) {
setUrlWithoutDomain(attr("href"))
title = text()
}
thumbnail_url = "https://fakeimg.pl/200x300/?text=No%20Cover&font_size=62"
}
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
with(element.select("div.dlb-right > a.dlb-title")) {
setUrlWithoutDomain(attr("href"))
title = text()
}
thumbnail_url = element.select("a.dlb-image > img").attr("src")
}
override fun popularMangaNextPageSelector() = "div.general-nav > a:contains(Next)"
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaSelector() = "div.dl-box"
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
title = document.select("h1.title").text()
thumbnail_url = document.select("div.anime-image > img").attr("src")
status = parseStatus(document.select("ul.anime-genres li.status").text())
author = document.select("td:contains(Author:) + td").text()
description = document.select(".detail-desc-content > p").text()
genre = document.select("ul.anime-genres > li > a[href*='genre']").joinToString { it.text() }
}
}
private fun parseStatus(element: String): Int = when {
element.contains("Completed") -> SManga.COMPLETED
element.contains("Ongoing") -> SManga.ONGOING
else -> SManga.UNKNOWN
}
override fun chapterListSelector() = "ul.basic-list > li"
override fun chapterFromElement(element: Element): SChapter {
return SChapter.create().apply {
with(element.select("a.ch-name")) {
setUrlWithoutDomain(attr("href"))
name = text()
}
date_upload = dateParse(element.select("span").text())
}
}
private val dateFormat by lazy { SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH) }
private fun dateParse(dateStr: String): Long {
return try {
dateFormat.parse(dateStr)!!.time
} catch (_: Exception) {
0L
}
}
override fun pageListRequest(chapter: SChapter): Request {
return GET(baseUrl + chapter.url + "/full", headers)
}
override fun pageListParse(document: Document): List<Page> {
return document.select("div.chapter-container img").mapIndexed { index, img ->
Page(index, "", img.attr("abs:src"))
}
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
// Filters
override fun getFilterList() = FilterList(
Filter.Header("Note: can't leave both filters as Any with a blank search string"),
Filter.Separator(),
GenreFilter(getGenreList),
StatusFilter(getStatusList),
)
private class Genre(name: String, val toUriPart: String) : Filter.TriState(name)
private class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres) {
val included: List<String>
get() = state.filter { it.isIncluded() }.map { it.toUriPart }
val excluded: List<String>
get() = state.filter { it.isExcluded() }.map { it.toUriPart }
}
private class StatusFilter(statusPairs: Array<Pair<String, String>>) : UriPartFilter("Status", statusPairs)
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
private val getStatusList = arrayOf(
Pair("Any", ""), // You might want an option for any status
Pair("Ongoing", "ONG"),
Pair("Completed", "CMP"),
)
private val getGenreList = listOf(
Genre("Any", ""),
Genre("Marvel", "Marvel"),
Genre("DC Comics", "DC%20Comics"),
Genre("Action", "Action"),
Genre("Adventure", "Adventure"),
Genre("Anthology", "Anthology"),
Genre("Anthropomorphic", "Anthropomorphic"),
Genre("Biography", "Biography"),
Genre("Children", "Children"),
Genre("Comedy", "Comedy"),
Genre("Crime", "Crime"),
Genre("Cyborgs", "Cyborgs"),
Genre("Dark Horse", "Dark%20Horse"),
Genre("Demons", "Demons"),
Genre("Drama", "Drama"),
Genre("Fantasy", "Fantasy"),
Genre("Family", "Family"),
Genre("Fighting", "Fighting"),
Genre("Gore", "Gore"),
Genre("Graphic Novels", "Graphic%20Novels"),
Genre("Historical", "Historical"),
Genre("Horror", "Horror"),
Genre("Leading Ladies", "Leading%20Ladies"),
Genre("Literature", "Literature"),
Genre("Magic", "Magic"),
Genre("Manga", "Manga"),
Genre("Martial Arts", "Martial%20Arts"),
Genre("Mature", "Mature"),
Genre("Mecha", "Mecha"),
Genre("Military", "Military"),
Genre("Movie Cinematic Link", "Movie%20Cinematic%20Link"),
Genre("Mystery", "Mystery"),
Genre("Mythology", "Mythology"),
Genre("Psychological", "Psychological"),
Genre("Personal", "Personal"),
Genre("Political", "Political"),
Genre("Post-Apocalyptic", "Post-Apocalyptic"),
Genre("Pulp", "Pulp"),
Genre("Robots", "Robots"),
Genre("Romance", "Romance"),
Genre("Sci-Fi", "Sci-Fi"),
Genre("Slice of Life", "Slice%20of%20Life"),
Genre("Science Fiction", "Science%20Fiction"),
Genre("Sports", "Sports"),
Genre("Spy", "Spy"),
Genre("Superhero", "Superhero"),
Genre("Supernatural", "Supernatural"),
Genre("Suspense", "Suspense"),
Genre("Thriller", "Thriller"),
Genre("Tragedy", "Tragedy"),
Genre("Vampires", "Vampires"),
Genre("Vertigo", "Vertigo"),
Genre("Video Games", "Video%20Games"),
Genre("War", "War"),
Genre("Western", "Western"),
Genre("Zombies", "Zombies"),
)
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'ToomicsFree.com'
extClass = '.ToomicsFree'
themePkg = 'hotcomics'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,29 +0,0 @@
package eu.kanade.tachiyomi.extension.en.toomicsfree
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
class ToomicsFree : HotComics(
"ToomicsFree.com",
"en",
"https://toomicsfree.com",
) {
override val browseList = listOf(
Pair("Home", "en"),
Pair("Weekly", "en/weekly"),
Pair("New", "en/new"),
Pair("Genre: All", "en/genres"),
Pair("Genre: Sports", "en/genres/Sports"),
Pair("Genre: Historical", "en/genres/Historical"),
Pair("Genre: Drama", "en/genres/Drama"),
Pair("Genre: BL", "en/genres/BL"),
Pair("Genre: Thriller", "en/genres/Thriller"),
Pair("Genre: School life", "en/genres/School_life"),
Pair("Genre: Comedy", "en/genres/Comedy"),
Pair("Genre: GL", "en/genres/GL"),
Pair("Genre: Action", "en/genres/Action"),
Pair("Genre: Sci-fi", "en/genres/Sci-fi"),
Pair("Genre: Horror", "en/genres/Horror"),
Pair("Genre: Fantasy", "en/genres/Fantasy"),
Pair("Genre: Romance", "en/genres/Romance"),
)
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'ToomicsFree.info'
extClass = '.ToomicsFreeInfo'
themePkg = 'hotcomics'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,29 +0,0 @@
package eu.kanade.tachiyomi.extension.en.toomicsfreeinfo
import eu.kanade.tachiyomi.multisrc.hotcomics.HotComics
class ToomicsFreeInfo : HotComics(
"ToomicsFree.info",
"en",
"https://toomicsfree.info",
) {
override val browseList = listOf(
Pair("Home", "en"),
Pair("Weekly", "en/weekly"),
Pair("New", "en/new"),
Pair("Genre: All", "en/genres"),
Pair("Genre: Sports", "en/genres/Sports"),
Pair("Genre: Historical", "en/genres/Historical"),
Pair("Genre: Drama", "en/genres/Drama"),
Pair("Genre: BL", "en/genres/BL"),
Pair("Genre: Thriller", "en/genres/Thriller"),
Pair("Genre: School life", "en/genres/School_life"),
Pair("Genre: Comedy", "en/genres/Comedy"),
Pair("Genre: GL", "en/genres/GL"),
Pair("Genre: Action", "en/genres/Action"),
Pair("Genre: Sci-fi", "en/genres/Sci-fi"),
Pair("Genre: Horror", "en/genres/Horror"),
Pair("Genre: Fantasy", "en/genres/Fantasy"),
Pair("Genre: Romance", "en/genres/Romance"),
)
}

View File

@ -1,9 +1,7 @@
ext {
extName = 'Cerberus Series'
extClass = '.CerberusSeries'
themePkg = 'mangathemesia'
baseUrl = 'https://cerberuseries.xyz'
overrideVersionCode = 0
extVersionCode = 1
isNsfw = false
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,12 +1,107 @@
package eu.kanade.tachiyomi.extension.es.cerberusseries
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
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.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.io.IOException
import java.util.Calendar
class CerberusSeries : MangaThemesia(
"Cerberus Series",
"https://cerberuseries.xyz",
"es",
) {
// Moved from custom to MangaThemesia
override val versionId = 2
class CerberusSeries : ParsedHttpSource() {
override val name = "Cerberus Series"
override val baseUrl = "https://cerberuseries.xyz"
override val lang = "es"
override val supportsLatest = true
override val client: OkHttpClient = network.client.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 2)
.build()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Referer", baseUrl)
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/comics?page=$page", headers)
override fun popularMangaSelector(): String = "div.grid > div:has(> div.c-iZMlIN)"
override fun popularMangaNextPageSelector(): String = "nav[role=navigation] a:contains(»), nav[role=navigation] a:contains(Next)"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
setUrlWithoutDomain(element.select("div.c-hCLgme a").attr("href"))
title = element.select("div.c-hCLgme a").text()
thumbnail_url = element.selectFirst("div.c-iZMlIN img")?.attr("abs:src")
}
override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl, headers)
override fun latestUpdatesSelector(): String = popularMangaSelector()
override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw IOException("Esta funcionalidad aún no esta implementada.")
override fun searchMangaSelector(): String = throw UnsupportedOperationException()
override fun searchMangaNextPageSelector(): String = throw UnsupportedOperationException()
override fun searchMangaFromElement(element: Element): SManga = throw UnsupportedOperationException()
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
thumbnail_url = document.selectFirst("div.thumb-wrapper img")!!.attr("abs:src")
title = document.selectFirst("div.series-title")!!.text()
genre = document.select("div.tags-container span").joinToString { it.text() }
description = document.selectFirst("div.description-container")!!.text()
author = document.select("div.useful-container p:containsOwn(Autor) strong").text()
}
override fun chapterListSelector(): String = "div.chapters-list-wrapper ul a"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
setUrlWithoutDomain(element.attr("href"))
name = element.selectFirst("li span")!!.text()
date_upload = parseRelativeDate(element.selectFirst("li p")!!.text())
}
override fun pageListParse(document: Document): List<Page> {
return document.select("div.main-content p > img").mapIndexed { i, element ->
Page(i, "", element.attr("abs:src"))
}
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
private fun parseRelativeDate(date: String): Long {
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
val cal = Calendar.getInstance()
return when {
WordSet("segundo").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
WordSet("minuto").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
WordSet("hora").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
WordSet("día", "dia").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
WordSet("semana").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number * 7) }.timeInMillis
WordSet("mes").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
WordSet("año").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
else -> 0
}
}
class WordSet(private vararg val words: String) {
fun anyWordIn(dateString: String): Boolean = words.any { dateString.contains(it, ignoreCase = true) }
}
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'De Todo Un Poco Scan'
extClass = '.DtupScan'
themePkg = 'mangathemesia'
baseUrl = 'https://dtupscan.com'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 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: 31 KiB

View File

@ -1,18 +0,0 @@
package eu.kanade.tachiyomi.extension.es.dtupscan
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.text.SimpleDateFormat
import java.util.Locale
class DtupScan : MangaThemesia(
"De Todo Un Poco Scan",
"https://dtupscan.com",
"es",
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
) {
override val client = super.client.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 3, 1)
.build()
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'Foy Scan'
extClass = '.FoyScan'
themePkg = 'mangaesp'
baseUrl = 'https://foyscan.xyz'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,5 +0,0 @@
package eu.kanade.tachiyomi.extension.es.foyscan
import eu.kanade.tachiyomi.multisrc.mangaesp.MangaEsp
class FoyScan : MangaEsp("Foy Scan", "https://foyscan.xyz", "es")

View File

@ -1,10 +0,0 @@
ext {
extName = 'Gistamis House'
extClass = '.GistamisHouse'
themePkg = 'zeistmanga'
baseUrl = 'https://gistamishousefansub.blogspot.com'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 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: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,154 +0,0 @@
package eu.kanade.tachiyomi.extension.es.gistamishouse
import eu.kanade.tachiyomi.multisrc.zeistmanga.Genre
import eu.kanade.tachiyomi.multisrc.zeistmanga.Status
import eu.kanade.tachiyomi.multisrc.zeistmanga.Type
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaDto
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import okhttp3.Response
class GistamisHouse : ZeistManga(
"Gistamis House",
"https://gistamishousefansub.blogspot.com",
"es",
) {
override val useNewChapterFeed = true
override val hasFilters = true
override val hasLanguageFilter = false
override val client = super.client.newBuilder()
.rateLimit(2)
.build()
override val excludedCategories = listOf("Anime", "Novela")
override val popularMangaSelector = "div.PopularPosts div.grid > figure:not(:has(span[data=Capitulo]))"
override val authorSelectorList = listOf(
"Author",
"Autor",
"Mangaka",
)
override val mangaDetailsSelectorAltName = "div.y6x11p:contains(Otros Nombres) > span.dt"
override val mangaDetailsSelectorInfoTitle = ""
override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup()
val profileManga = document.selectFirst(mangaDetailsSelector)!!
return SManga.create().apply {
thumbnail_url = profileManga.selectFirst("img")!!.attr("abs:src")
description = buildString {
append(profileManga.select(mangaDetailsSelectorDescription).text())
append("\n\n")
profileManga.selectFirst(mangaDetailsSelectorAltName)?.text()?.takeIf { it.isNotBlank() }?.let {
append("Otros Nombres: ")
append(it)
}
}.trim()
genre = profileManga.select(mangaDetailsSelectorGenres)
.joinToString { it.text() }
val infoElement = profileManga.select(mangaDetailsSelectorInfo)
var statusFound = false
infoElement.forEach { element ->
val infoText = element.ownText().trim().ifEmpty { element.selectFirst(mangaDetailsSelectorInfoTitle)?.text()?.trim() ?: "" }
val descText = element.select(mangaDetailsSelectorInfoDescription).text().trim()
when {
statusSelectorList.any { infoText.contains(it) } -> {
if (!statusFound) status = parseStatus(descText)
statusFound = true
}
authorSelectorList.any { infoText.contains(it) } -> {
author = descText
}
artisSelectorList.any { infoText.contains(it) } -> {
artist = descText
}
}
}
}
}
override val chapterCategory = ""
private val chapterCategories = listOf("Capitulo", "Cap")
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val url = getChapterFeedUrl(document)
val res = client.newCall(GET(url, headers)).execute()
val result = json.decodeFromString<ZeistMangaDto>(res.body.string())
return result.feed?.entry?.filter { it.category.orEmpty().any { category -> chapterCategories.contains(category.term) } }
?.map { it.toSChapter(baseUrl) }
?: throw Exception("Failed to parse from chapter API")
}
override val pageListSelector = "article.oh div.post p"
override fun getGenreList(): List<Genre> = listOf(
Genre("Acción", "Acción"),
Genre("Aventura", "Aventura"),
Genre("Comedia", "Comedia"),
Genre("Dementia", "Dementia"),
Genre("Demonios", "Demonios"),
Genre("Drama", "Drama"),
Genre("Ecchi", "Ecchi"),
Genre("Fantasía", "Fantasía"),
Genre("Videojuegos", "Videojuegos"),
Genre("Harem", "Harem"),
Genre("Histórico", "Histórico"),
Genre("Horror", "Horror"),
Genre("Josei", "Josei"),
Genre("Magia", "Magia"),
Genre("Arte marcial", "Arte marcial"),
Genre("Mecha", "Mecha"),
Genre("Militar", "Militar"),
Genre("Música", "Música"),
Genre("Misterio", "Misterio"),
Genre("Parody", "Parody"),
Genre("Policia", "Policia"),
Genre("Filosófico", "Filosófico"),
Genre("Romance", "Romance"),
Genre("Samurai", "Samurai"),
Genre("Escolar", "Escolar"),
Genre("Sci-Fi", "Sci-Fi"),
Genre("Seinen", "Seinen"),
Genre("Shoujo", "Shoujo"),
Genre("GL", "GL"),
Genre("BL", "BL"),
Genre("HET", "HET"),
Genre("Shounen", "Shounen"),
Genre("Vida cotidiana", "Vida cotidiana"),
Genre("Espacio", "Espacio"),
Genre("Deportes", "Deportes"),
Genre("Super poderes", "Super poderes"),
Genre("Sobrenatural", "Sobrenatural"),
Genre("Thriller", "Thriller"),
Genre("Vampiro", "Vampiro"),
Genre("Vida laboral", "Vida laboral"),
)
override fun getStatusList(): List<Status> = listOf(
Status("Activo", "Activo"),
Status("Completo", "Completo"),
Status("Cancelado", "Cancelado"),
Status("Futuro", "Futuro"),
Status("Pausado", "Pausado"),
)
override fun getTypeList(): List<Type> = listOf(
Type("Manga", "Manga"),
Type("Manhua", "Manhua"),
Type("Manhwa", "Manhwa"),
)
}

View File

@ -1,7 +1,7 @@
ext {
extName = 'Ikigai Mangas'
extClass = '.IkigaiMangas'
extVersionCode = 4
extVersionCode = 3
isNsfw = true
}

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