Remove dead sources (#15016)

* Remove dead sources

* update multisrc
This commit is contained in:
stevenyomi 2023-01-19 04:32:53 +08:00 committed by GitHub
parent 1a872374c3
commit a37ff2559e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1 additions and 672 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,10 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.gemanga
import eu.kanade.tachiyomi.multisrc.madara.Madara
class Gemanga : Madara("Gemanga", "https://gemanga.com", "ar") {
// The website does not flag the content.
override val useLoadMoreSearch = false
override val filterNonMangaItems = false
}

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.all.flamescans
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
@ -13,21 +12,6 @@ class FlameScansFactory : SourceFactory {
)
}
class FlameScansAr : FlameScans("https://ar.flamescans.org", "ar", "/series") {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 1, TimeUnit.SECONDS)
.build()
override val id: Long = 6053688312544266540
override fun String?.parseStatus() = when {
this == null -> SManga.UNKNOWN
this.contains("مستمر") -> SManga.ONGOING
this.contains("مكتمل") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}
class FlameScansEn : FlameScans("https://flamescans.org", "en", "/series") {
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(2, 7, TimeUnit.SECONDS)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

View File

@ -1,33 +0,0 @@
package eu.kanade.tachiyomi.extension.id.gabutscans
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.source.model.Page
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonPrimitive
import org.jsoup.nodes.Document
import java.text.SimpleDateFormat
import java.util.Locale
class GabutScans : MangaThemesia(
"Gabut Scans", "https://gabutscans.com", "id",
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id"))
) {
override val hasProjectPage = true
override fun pageListParse(document: Document): List<Page> {
// Prefer using sources loaded from javascript
// because current CDN (Statically) can't load old images.
// Example: https://gabutscans.com/cinderella-wa-sagasanai-chapter-41-end-bahasa-indonesia/
val docString = document.toString()
val imageListRegex = Regex("\"images.*?:.*?(\\[.*?])")
val (imageListJson) = imageListRegex.find(docString)!!.destructured
val imageList = json.parseToJsonElement(imageListJson).jsonArray
return imageList.mapIndexed { i, jsonEl ->
Page(i, "", jsonEl.jsonPrimitive.content)
}
}
}

View File

@ -34,7 +34,6 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("Akuzenai Arts", "https://akuzenaiarts.org", "en"),
SingleLang("Aleatória Scan", "https://aleatoriascan.xyz", "pt-BR", className = "AleatoriaScan"),
SingleLang("AllPornComic", "https://allporncomic.com", "en", isNsfw = true),
SingleLang("AllTopManga", "https://alltopmanga.com", "en", isNsfw = true),
SingleLang("Aln Scans", "https://alnscans.com", "en"),
SingleLang("Amuy", "https://amuyscans.com", "pt-BR", isNsfw = true),
SingleLang("Anikiga", "https://anikiga.com", "tr"),
@ -113,7 +112,6 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("GalaxyDegenScans", "https://gdscans.com", "en", overrideVersionCode = 4),
SingleLang("Gatemanga", "https://gatemanga.com", "ar", overrideVersionCode = 1),
SingleLang("GeassToon", "https://geasstoon.com", "tr"),
SingleLang("Gemanga", "https://gemanga.com", "ar", overrideVersionCode = 2),
SingleLang("Glorious Scan", "https://gloriousscan.com", "pt-BR"),
SingleLang("Glory Manga", "https://glorymanga.com", "tr"),
SingleLang("Glory Scans", "https://gloryscans.com", "tr", isNsfw = true),

View File

@ -15,7 +15,7 @@ class MangaThemesiaGenerator : ThemeSourceGenerator {
override val sources = listOf(
MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 18),
MultiLang("Flame Scans", "https://flamescans.org", listOf("ar", "en"), className = "FlameScansFactory", pkgName = "flamescans", overrideVersionCode = 3),
MultiLang("Flame Scans", "https://flamescans.org", listOf("en"), className = "FlameScansFactory", pkgName = "flamescans", overrideVersionCode = 4),
MultiLang("Komik Lab", "https://komiklab.com", listOf("en", "id"), className = "KomikLabFactory", pkgName = "komiklab", overrideVersionCode = 1),
MultiLang("Miau Scan", "https://miauscan.com", listOf("es", "pt-BR")),
SingleLang("Animated Glitched Scans", "https://anigliscans.com", "en"),
@ -32,7 +32,6 @@ class MangaThemesiaGenerator : ThemeSourceGenerator {
SingleLang("DuniaKomik.id", "https://duniakomik.id", "id", className = "DuniaKomikId"),
SingleLang("FlameScans.fr", "https://flamescans.fr", "fr", className = "FlameScansFR"),
SingleLang("Franxx Mangás", "https://franxxmangas.net", "pt-BR", className = "FranxxMangas", isNsfw = true),
SingleLang("Gabut Scans", "https://gabutscans.com", "id", overrideVersionCode = 1),
SingleLang("Gecenin Lordu", "https://geceninlordu.com", "tr", overrideVersionCode = 1),
SingleLang("GoGoManga", "https://gogomanga.fun", "en", overrideVersionCode = 1),
SingleLang("Gremory Mangas", "https://gremorymangas.com", "es"),

View File

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

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Genkan.io'
pkgNameSuffix = "all.genkanio"
extClass = '.GenkanIOFactory'
extVersionCode = 5
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

View File

@ -1,348 +0,0 @@
package eu.kanade.tachiyomi.extension.all.genkanio
import android.util.Log
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
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 eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import okio.Buffer
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.util.Calendar
open class GenkanIO(override val lang: String) : ParsedHttpSource() {
final override val name = "Genkan.io"
final override val baseUrl = "https://genkan.io"
final override val supportsLatest = false
private val json: Json by injectLazy()
/** An interceptor which encapsulates the logic needed to interoperate with Genkan.io's
* livewire server, which uses a form a Remote Procedure call
*/
private val livewireInterceptor = object : Interceptor {
private lateinit var fingerprint: JsonElement
lateinit var serverMemo: JsonObject
private lateinit var csrf: String
var initialized = false
val serverUrl = "$baseUrl/livewire/message/manga.list-all-manga"
/**
* Given a string encoded with html entities and escape sequences, makes an attempt to decode
* and returns decoded string
*
* Warning: This is not all all exhaustive, and probably misses edge cases
*
* @Returns decoded string
*/
private fun htmlDecode(html: String): String {
return html.replace(Regex("&([A-Za-z]+);")) { match ->
mapOf(
"raquo" to "»",
"laquo" to "«",
"amp" to "&",
"lt" to "<",
"gt" to ">",
"quot" to "\""
)[match.groups[1]!!.value] ?: match.groups[0]!!.value
}.replace(Regex("\\\\(.)")) { match ->
mapOf(
"t" to "\t",
"n" to "\n",
"r" to "\r",
"b" to "\b"
)[match.groups[1]!!.value] ?: match.groups[1]!!.value
}
}
/**
* Recursively merges j2 onto j1 in place
* If j1 and j2 both contain keys whose values aren't both jsonObjects, j2's value overwrites j1's
*
*/
private fun mergeLeft(j1: JsonObject, j2: JsonObject): JsonObject = buildJsonObject {
j1.keys.forEach { put(it, j1[it]!!) }
j2.keys.forEach { k ->
when {
j1[k] !is JsonObject -> put(k, j2[k]!!)
j1[k] is JsonObject && j2[k] is JsonObject -> put(k, mergeLeft(j1[k]!!.jsonObject, j2[k]!!.jsonObject))
}
}
}
/**
* Initializes lateinit member vars
*/
private fun initLivewire(chain: Interceptor.Chain) {
val response = chain.proceed(GET("$baseUrl/manga", headers))
val soup = response.asJsoup()
response.body?.close()
val csrfToken = soup.selectFirst("meta[name=csrf-token]")?.attr("content")
val initialProps = soup.selectFirst("div[wire:initial-data]")?.attr("wire:initial-data")?.let {
json.parseToJsonElement(htmlDecode(it))
}
if (csrfToken != null && initialProps is JsonObject) {
csrf = csrfToken
serverMemo = initialProps["serverMemo"]!!.jsonObject
fingerprint = initialProps["fingerprint"]!!
initialized = true
} else {
Log.e("GenkanIo", soup.selectFirst("div[wire:initial-data]")?.toString() ?: "null")
}
}
/**
* Builds a request for livewire, augmenting the request with required body fields and headers
*
* @param req: Request - A request with a json encoded body, which represent the updates sent to server
*
*/
private fun livewireRequest(req: Request): Request {
val payload = buildJsonObject {
put("fingerprint", fingerprint)
put("serverMemo", serverMemo)
put("updates", json.parseToJsonElement(Buffer().apply { req.body!!.writeTo(this) }.readUtf8()))
}.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
return req.newBuilder()
.method(req.method, payload)
.addHeader("x-csrf-token", csrf)
.addHeader("x-livewire", "true")
.build()
}
/**
* Transforms json response from livewire server into a response which returns html
*
* @param response: Response - The response of sending a message to genkan's livewire server
*
* @return HTML Response - The html embedded within the provided response
*/
private fun livewireResponse(response: Response): Response {
if (!response.isSuccessful) return response
val body = response.body!!.string()
val responseJson = json.parseToJsonElement(body).jsonObject
// response contains state that we need to preserve
serverMemo = mergeLeft(serverMemo, responseJson["serverMemo"]!!.jsonObject)
// this seems to be an error state, so reset everything
if (responseJson["effects"]?.jsonObject?.get("html") is JsonNull) {
initialized = false
}
// Build html response
return response.newBuilder()
.body(htmlDecode("${responseJson["effects"]?.jsonObject?.get("html")}").toResponseBody("Content-Type: text/html; charset=UTF-8".toMediaTypeOrNull()))
.build()
}
override fun intercept(chain: Interceptor.Chain): Response {
if (chain.request().url.toString() != serverUrl)
return chain.proceed(chain.request())
if (!initialized) initLivewire(chain)
return livewireResponse(chain.proceed(livewireRequest(chain.request())))
}
}
override val client = super.client.newBuilder().addInterceptor(livewireInterceptor).build()
// popular manga
override fun fetchPopularManga(page: Int) = fetchSearchManga(page, "", FilterList(emptyList()))
override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException("Not used")
override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used")
override fun popularMangaSelector() = throw UnsupportedOperationException("Not used")
override fun popularMangaNextPageSelector() = throw UnsupportedOperationException("Not used")
// latest
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used")
override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException("Not used")
override fun latestUpdatesSelector() = throw UnsupportedOperationException("Not used")
override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException("Not used")
// search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val data = if (livewireInterceptor.initialized) livewireInterceptor.serverMemo["data"]!!.jsonObject else buildJsonObject {
put("readyToLoad", JsonPrimitive(false))
put("page", JsonPrimitive(1))
put("search", JsonPrimitive(""))
}
val updates = buildJsonArray {
if (data["readyToLoad"]?.jsonPrimitive?.boolean == false) {
add(json.parseToJsonElement("""{"type":"callMethod","payload":{"method":"loadManga","params":[]}}"""))
}
val isNewQuery = query != data["search"]?.jsonPrimitive?.content
if (isNewQuery) {
add(json.parseToJsonElement("""{"type": "syncInput", "payload": {"name": "search", "value": "$query"}}"""))
}
val currPage = if (isNewQuery) 1 else data["page"]!!.jsonPrimitive.int
for (i in (currPage + 1)..page)
add(json.parseToJsonElement("""{"type":"callMethod","payload":{"method":"nextPage","params":[]}}"""))
}
return POST(
livewireInterceptor.serverUrl,
headers,
updates.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
)
}
override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create()
element.select("a").let {
manga.url = it.attr("href").substringAfter(baseUrl)
manga.title = it.text()
}
manga.thumbnail_url = element.select("img").attr("src")
return manga
}
override fun searchMangaSelector() = "ul[role=list]:has(a)> li"
override fun searchMangaNextPageSelector() = "button[rel=next]"
// chapter list (is paginated),
override fun chapterListParse(response: Response): List<SChapter> = throw UnsupportedOperationException("Not used")
override fun chapterListRequest(manga: SManga): Request = throw UnsupportedOperationException("Not used")
data class ChapterPage(val chapters: List<SChapter>, val hasnext: Boolean)
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return if (manga.status != SManga.LICENSED) {
// Returns an observable which emits the list of chapters found on a page,
// for every page starting from specified page
fun getAllPagesFrom(page: Int, pred: Observable<List<SChapter>> = Observable.just(emptyList())): Observable<List<SChapter>> =
client.newCall(chapterListRequest(manga, page))
.asObservableSuccess()
.concatMap { response ->
val cp = chapterPageParse(response)
if (cp.hasnext)
getAllPagesFrom(page + 1, pred = pred.concatWith(Observable.just(cp.chapters))) // tail call to avoid blowing the stack
else
pred.concatWith(Observable.just(cp.chapters))
}
getAllPagesFrom(1).reduce(List<SChapter>::plus)
} else {
Observable.error(Exception("Licensed - No chapters to show"))
}
}
private fun chapterPageParse(response: Response): ChapterPage {
val document = response.asJsoup()
val manga = document.select(chapterListSelector()).map { element ->
chapterFromElement(element)
}
val hasNextPage = chapterListNextPageSelector()?.let { selector ->
document.select(selector).first()
} != null
return ChapterPage(manga, hasNextPage)
}
private fun chapterListRequest(manga: SManga, page: Int): Request {
val url = "$baseUrl${manga.url}".toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("page", "$page")
return GET("$url", headers)
}
override fun chapterFromElement(element: Element): SChapter = element.children().let { tableRow ->
val isTitleBlank: (String) -> Boolean = { s: String -> s == "-" || s.isBlank() }
val (numElem, nameElem, languageElem, groupElem, viewsElem) = tableRow
val (releasedElem, urlElem) = Pair(tableRow[5], tableRow[6])
SChapter.create().apply {
name = if (isTitleBlank(nameElem.text())) "Chapter ${numElem.text()}" else "Ch. ${numElem.text()}: ${nameElem.text()}"
url = urlElem.select("a").attr("href").substringAfter(baseUrl)
date_upload = parseRelativeDate(releasedElem.text())
scanlator = groupElem.text()
chapter_number = numElem.text().toFloat()
}
}
override fun chapterListSelector() = when (lang) {
"ar" -> "tbody > tr:contains(Arabic)"
"en" -> "tbody > tr:contains(English)"
"fr" -> "tbody > tr:contains(French)"
"pl" -> "tbody > tr:contains(Polish)"
"pt-BR" -> "tbody > tr:contains(Portuguese)"
"ru" -> "tbody > tr:contains(Russian)"
"es" -> "tbody > tr:contains(Spanish)"
"tr" -> "tbody > tr:contains(Turkish)"
else -> "tbody > tr"
}
private fun chapterListNextPageSelector() = "a[rel=next]"
// manga
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
thumbnail_url = document.selectFirst("section > div > img").attr("src")
status = SManga.UNKNOWN // unreported
artist = null // unreported
author = null // unreported
description = document.selectFirst("h2").nextElementSibling().text()
.plus("\n\n\n")
// Add additional details from info table
.plus(
document.select("ul.mt-1").joinToString("\n") {
"${it.previousElementSibling().text()}: ${it.text()}"
}
)
}
private fun parseRelativeDate(date: String): Long {
val trimmedDate = date.substringBefore(" ago").removeSuffix("s").split(" ")
val calendar = Calendar.getInstance()
when (trimmedDate[1]) {
"year" -> calendar.apply { add(Calendar.YEAR, -trimmedDate[0].toInt()) }
"month" -> calendar.apply { add(Calendar.MONTH, -trimmedDate[0].toInt()) }
"week" -> calendar.apply { add(Calendar.WEEK_OF_MONTH, -trimmedDate[0].toInt()) }
"day" -> calendar.apply { add(Calendar.DAY_OF_MONTH, -trimmedDate[0].toInt()) }
"hour" -> calendar.apply { add(Calendar.HOUR_OF_DAY, -trimmedDate[0].toInt()) }
"minute" -> calendar.apply { add(Calendar.MINUTE, -trimmedDate[0].toInt()) }
"second" -> calendar.apply { add(Calendar.SECOND, 0) }
}
return calendar.timeInMillis
}
// Pages
override fun pageListParse(document: Document): List<Page> = document.select("main > div > img").mapIndexed { index, img ->
Page(index, "", img.attr("src"))
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
}

View File

@ -1,17 +0,0 @@
package eu.kanade.tachiyomi.extension.all.genkanio
import eu.kanade.tachiyomi.source.SourceFactory
class GenkanIOFactory : SourceFactory {
override fun createSources() = listOf(
GenkanIO("all"),
GenkanIO("ar"),
GenkanIO("en"),
GenkanIO("fr"),
GenkanIO("pl"),
GenkanIO("pt-BR"),
GenkanIO("ru"),
GenkanIO("es"),
GenkanIO("tr"),
)
}

View File

@ -1,44 +0,0 @@
package eu.kanade.tachiyomi.extension.all.genkanio
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
class GenkanIOUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size >= 2) {
// url scheme is of the form "/manga/ID-MANGANAME"
val (_, titleComponent) = pathSegments
// This is essentially substringBefore(titleComponent, '-'), don't have access to stdlib
var titleId = ""
for (i in 0 until titleComponent.length) {
if (titleComponent[i] == '-') break
titleId = titleId.plus(titleComponent[i])
}
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", titleId)
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("GenkanIOUrlActivity", e.toString())
}
} else {
Log.e("GenkanIOUrlActivity", "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -1,11 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'ManhuaID'
pkgNameSuffix = 'id.manhuaid'
extClass = '.ManhuaID'
extVersionCode = 8
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

View File

@ -1,151 +0,0 @@
package eu.kanade.tachiyomi.extension.id.manhuaid
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 eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
class ManhuaID : ParsedHttpSource() {
override val name = "ManhuaID"
override val baseUrl = "https://manhuaid.com"
override val lang = "id"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
// popular
override fun popularMangaSelector() = "a:has(img.card-img)"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/index.php/project?page_project=$page", headers)
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.attr("href"))
title = element.select("img").attr("alt")
thumbnail_url = element.select("img").attr("abs:src")
}
override fun popularMangaNextPageSelector() = "[rel=nofollow]"
// latest
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/index.php/project?page_project=$page", headers)
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
// search
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/search?".toHttpUrlOrNull()!!.newBuilder()
url.addQueryParameter("q", query)
filters.forEach { filter ->
when (filter) {
is ProjectFilter -> {
if (filter.toUriPart() == "project-filter-on") {
url = "$baseUrl/project?page_project=$page".toHttpUrlOrNull()!!.newBuilder()
}
}
}
}
return GET(url.toString(), headers)
}
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = "li:last-child:not(.active) [rel=nofollow]"
// manga details
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
author = document.select("table").first().select("td")[3].text()
title = document.select("h1").text()
description = document.select(".text-justify").text()
genre = document.select("span.badge.badge-success.mr-1.mb-1").joinToString { it.text() }
status = document.select("td > span.badge.badge-success").text().let {
parseStatus(it)
}
thumbnail_url = document.select("img.img-fluid").attr("abs:src")
// add series type(manga/manhwa/manhua/other) thinggy to genre
document.select("table tr:contains(Type) a, table a[href*=type]").firstOrNull()?.ownText()?.let {
if (it.isEmpty().not() && genre!!.contains(it, true).not()) {
genre += if (genre!!.isEmpty()) it else ", $it"
}
}
}
private fun parseStatus(status: String) = when {
status.contains("Ongoing") -> SManga.ONGOING
status.contains("Completed") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
// chapters
override fun chapterListSelector() = "table.table tr td:first-of-type a"
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val chapters = document.select(chapterListSelector()).map { chapterFromElement(it) }
// Add timestamp to latest chapter, taken from "Updated On". so source which not provide chapter timestamp will have atleast one
val date = document.select("table tr:contains(update) td").text()
val checkChapter = document.select(chapterListSelector()).firstOrNull()
if (date != "" && checkChapter != null) chapters[0].date_upload = parseDate(date)
return chapters
}
private fun parseDate(date: String): Long {
return SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH).parse(date)?.time ?: 0L
}
override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.attr("href"))
name = element.text()
}
// pages
override fun pageListParse(document: Document): List<Page> {
return document.select("img.img-fluid.mb-0.mt-0").mapIndexed { i, element ->
Page(i, "", element.attr("src"))
}
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used")
override fun getFilterList() = FilterList(
Filter.Header("NOTE: cant be used with search or other filter!"),
Filter.Header("$name Project List page"),
ProjectFilter(),
)
private class ProjectFilter : UriPartFilter(
"Filter Project",
arrayOf(
Pair("Show all manga", ""),
Pair("Show only project manga", "project-filter-on")
)
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
}