Update four "all" sources (#1314)
* ComiCake: Some cleanup work Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com> * E-Hentai: Some cleanup work Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com> * FoolSlide: Some cleanup work and source updates Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com> * Genkan: Some cleanup work Signed-off-by: TacoTheDank <SkytkRSfan3895@gmail.com>
This commit is contained in:
parent
27175b3dee
commit
c94cc58e7d
|
@ -3,9 +3,9 @@ apply plugin: 'kotlin-android'
|
|||
|
||||
ext {
|
||||
appName = 'Tachiyomi: ComiCake'
|
||||
pkgNameSuffix = "all.comicake"
|
||||
pkgNameSuffix = 'all.comicake'
|
||||
extClass = '.ComiCakeFactory'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
|
|
@ -2,21 +2,24 @@ package eu.kanade.tachiyomi.extension.all.comicake
|
|||
|
||||
import android.os.Build
|
||||
import eu.kanade.tachiyomi.extension.BuildConfig
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.text.SimpleDateFormat
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
const val COMICAKE_DEFAULT_API_ENDPOINT = "/api" // Highly unlikely to change
|
||||
const val COMICAKE_DEFAULT_READER_ENDPOINT = "/r" // Can change based on CC config
|
||||
|
||||
open class ComiCake(override val name: String, override val baseUrl: String, override val lang: String, val readerEndpoint: String = COMICAKE_DEFAULT_READER_ENDPOINT, val apiEndpoint: String = COMICAKE_DEFAULT_API_ENDPOINT) : HttpSource() {
|
||||
open class ComiCake(override val name: String,
|
||||
final override val baseUrl: String,
|
||||
override val lang: String,
|
||||
readerEndpoint: String = COMICAKE_DEFAULT_READER_ENDPOINT,
|
||||
apiEndpoint: String = COMICAKE_DEFAULT_API_ENDPOINT) : HttpSource() {
|
||||
override val versionId = 1
|
||||
override val supportsLatest = true
|
||||
private val readerBase = baseUrl + readerEndpoint
|
||||
|
@ -40,18 +43,18 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
|||
return getMangasPageFromComicsResponse(res)
|
||||
}
|
||||
|
||||
private fun getMangasPageFromComicsResponse(json: String, nested: Boolean = false) : MangasPage {
|
||||
var response = JSONObject(json)
|
||||
var results = response.getJSONArray("results")
|
||||
private fun getMangasPageFromComicsResponse(json: String, nested: Boolean = false): MangasPage {
|
||||
val response = JSONObject(json)
|
||||
val results = response.getJSONArray("results")
|
||||
val mangas = ArrayList<SManga>()
|
||||
val ids = mutableListOf<Int>();
|
||||
val ids = mutableListOf<Int>()
|
||||
|
||||
for (i in 0 until results.length()) {
|
||||
val obj = results.getJSONObject(i)
|
||||
if(nested) {
|
||||
val nestedComic = obj.getJSONObject("comic");
|
||||
if (nested) {
|
||||
val nestedComic = obj.getJSONObject("comic")
|
||||
val id = nestedComic.getInt("id")
|
||||
if(ids.contains(id))
|
||||
if (ids.contains(id))
|
||||
continue
|
||||
ids.add(id)
|
||||
val manga = SManga.create()
|
||||
|
@ -60,13 +63,13 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
|||
mangas.add(manga)
|
||||
} else {
|
||||
val id = obj.getInt("id")
|
||||
if(ids.contains(id))
|
||||
if (ids.contains(id))
|
||||
continue
|
||||
ids.add(id)
|
||||
mangas.add(parseComicJson(obj))
|
||||
}
|
||||
}
|
||||
return MangasPage(mangas, if (response.getString("next").isNullOrEmpty() || response.getString("next") == "null") false else true)
|
||||
return MangasPage(mangas, !(response.getString("next").isNullOrEmpty() || response.getString("next") == "null"))
|
||||
}
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
|
@ -78,11 +81,11 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
|||
return parseComicJson(comicJson, true)
|
||||
}
|
||||
|
||||
private fun parseComicJson(obj: JSONObject, human: Boolean = false) = SManga.create().apply {
|
||||
if(human) {
|
||||
url = "$readerBase/series/${obj.getString("slug")}/"
|
||||
private fun parseComicJson(obj: JSONObject, human: Boolean = false) = SManga.create().apply {
|
||||
url = if (human) {
|
||||
"$readerBase/series/${obj.getString("slug")}/"
|
||||
} else {
|
||||
url = obj.getInt("id").toString() // Yeah, I know... Feel free to improve on this
|
||||
obj.getInt("id").toString() // Yeah, I know... Feel free to improve on this
|
||||
}
|
||||
title = obj.getString("name")
|
||||
thumbnail_url = obj.getString("cover")
|
||||
|
@ -93,9 +96,9 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
|||
status = SManga.UNKNOWN
|
||||
}
|
||||
|
||||
private fun parseListNames(arr: JSONArray) : String {
|
||||
var hold = ArrayList<String>(arr.length())
|
||||
for(i in 0 until arr.length())
|
||||
private fun parseListNames(arr: JSONArray): String {
|
||||
val hold = ArrayList<String>(arr.length())
|
||||
for (i in 0 until arr.length())
|
||||
hold.add(arr.getJSONObject(i).getString("name"))
|
||||
return hold.sorted().joinToString(", ")
|
||||
}
|
||||
|
@ -133,7 +136,7 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
|||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val chapterJson = JSONObject(response.body()!!.string())
|
||||
var results = chapterJson.getJSONArray("results")
|
||||
val results = chapterJson.getJSONArray("results")
|
||||
val ret = ArrayList<SChapter>()
|
||||
for (i in 0 until results.length()) {
|
||||
ret.add(parseChapterJson(results.getJSONObject(i)))
|
||||
|
@ -144,13 +147,13 @@ open class ComiCake(override val name: String, override val baseUrl: String, ove
|
|||
override fun pageListParse(response: Response): List<Page> {
|
||||
val webPub = JSONObject(response.body()!!.string())
|
||||
val readingOrder = webPub.getJSONArray("readingOrder")
|
||||
val ret = ArrayList<Page>();
|
||||
val ret = ArrayList<Page>()
|
||||
for (i in 0 until readingOrder.length()) {
|
||||
var pageUrl = readingOrder.getJSONObject(i).getString("href")
|
||||
val pageUrl = readingOrder.getJSONObject(i).getString("href")
|
||||
ret.add(Page(i, "", pageUrl))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("This method should not be called!")
|
||||
}
|
||||
}
|
|
@ -9,11 +9,11 @@ class ComiCakeFactory : SourceFactory {
|
|||
|
||||
fun getAllComiCake(): List<Source> {
|
||||
return listOf(
|
||||
WhimSubs(),
|
||||
ChampionScans()
|
||||
WhimSubs(),
|
||||
ChampionScans()
|
||||
)
|
||||
}
|
||||
|
||||
class WhimSubs : ComiCake("WhimSubs", "https://whimsubs.xyz", "en")
|
||||
|
||||
class ChampionScans : ComiCake("Champion Scans", "https://reader.championscans.com", "en", "/")
|
||||
class ChampionScans : ComiCake("Champion Scans", "https://reader.championscans.com", "en", "/")
|
|
@ -4,8 +4,8 @@ apply plugin: 'kotlin-android'
|
|||
ext {
|
||||
appName = 'Tachiyomi: E-Hentai'
|
||||
pkgNameSuffix = 'all.ehentai'
|
||||
extClass = '.EHJapanese; .EHEnglish; .EHChinese; .EHDutch; .EHFrench; .EHGerman; .EHHungarian; .EHItalian; .EHKorean; .EHPolish; .EHPolish; .EHPortuguese; .EHRussian; .EHSpanish; .EHThai; .EHVietnamese; .EHSpeechless; .EHOther'
|
||||
extVersionCode = 4
|
||||
extClass = '.EHJapanese; .EHEnglish; .EHChinese; .EHDutch; .EHFrench; .EHGerman; .EHHungarian; .EHItalian; .EHKorean; .EHPolish; .EHPortuguese; .EHRussian; .EHSpanish; .EHThai; .EHVietnamese; .EHSpeechless; .EHOther'
|
||||
extVersionCode = 5
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
|
|
@ -3,23 +3,23 @@ package eu.kanade.tachiyomi.extension.all.ehentai
|
|||
/**
|
||||
* E-Hentai languages
|
||||
*/
|
||||
class EHJapanese: EHentai("ja", "japanese")
|
||||
class EHEnglish: EHentai("en", "english")
|
||||
class EHChinese: EHentai("zh", "chinese")
|
||||
class EHDutch: EHentai("nl", "dutch")
|
||||
class EHFrench: EHentai("fr", "french")
|
||||
class EHGerman: EHentai("de", "german")
|
||||
class EHHungarian: EHentai("hu", "hungarian")
|
||||
class EHItalian: EHentai("it", "italian")
|
||||
class EHKorean: EHentai("ko", "korean")
|
||||
class EHPolish: EHentai("pl", "polish")
|
||||
class EHPortuguese: EHentai("pt", "portuguese")
|
||||
class EHRussian: EHentai("ru", "russian")
|
||||
class EHSpanish: EHentai("es", "spanish")
|
||||
class EHThai: EHentai("th", "thai")
|
||||
class EHVietnamese: EHentai("vi", "vietnamese")
|
||||
class EHSpeechless: EHentai("none", "n/a")
|
||||
class EHOther: EHentai("other", "other")
|
||||
class EHJapanese : EHentai("ja", "japanese")
|
||||
class EHEnglish : EHentai("en", "english")
|
||||
class EHChinese : EHentai("zh", "chinese")
|
||||
class EHDutch : EHentai("nl", "dutch")
|
||||
class EHFrench : EHentai("fr", "french")
|
||||
class EHGerman : EHentai("de", "german")
|
||||
class EHHungarian : EHentai("hu", "hungarian")
|
||||
class EHItalian : EHentai("it", "italian")
|
||||
class EHKorean : EHentai("ko", "korean")
|
||||
class EHPolish : EHentai("pl", "polish")
|
||||
class EHPortuguese : EHentai("pt", "portuguese")
|
||||
class EHRussian : EHentai("ru", "russian")
|
||||
class EHSpanish : EHentai("es", "spanish")
|
||||
class EHThai : EHentai("th", "thai")
|
||||
class EHVietnamese : EHentai("vi", "vietnamese")
|
||||
class EHSpeechless : EHentai("none", "n/a")
|
||||
class EHOther : EHentai("other", "other")
|
||||
|
||||
fun getAllEHentaiLanguages() = listOf(
|
||||
EHJapanese(),
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package eu.kanade.tachiyomi.extension.all.ehentai
|
||||
|
||||
import kotlin.math.ln
|
||||
import kotlin.math.pow
|
||||
|
||||
/**
|
||||
* Various utility methods used in the E-Hentai source
|
||||
*/
|
||||
|
@ -36,18 +39,18 @@ operator fun StringBuilder.plusAssign(other: String) {
|
|||
*/
|
||||
fun humanReadableByteCount(bytes: Long, si: Boolean): String {
|
||||
val unit = if (si) 1000 else 1024
|
||||
if (bytes < unit) return bytes.toString() + " B"
|
||||
val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt()
|
||||
if (bytes < unit) return "$bytes B"
|
||||
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
||||
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
|
||||
return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre)
|
||||
return String.format("%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
|
||||
}
|
||||
|
||||
private val KB_FACTOR = 1000
|
||||
private val KIB_FACTOR = 1024
|
||||
private val MB_FACTOR = 1000 * KB_FACTOR
|
||||
private val MIB_FACTOR = 1024 * KIB_FACTOR
|
||||
private val GB_FACTOR = 1000 * MB_FACTOR
|
||||
private val GIB_FACTOR = 1024 * MIB_FACTOR
|
||||
private const val KB_FACTOR = 1000
|
||||
private const val KIB_FACTOR = 1024
|
||||
private const val MB_FACTOR = 1000 * KB_FACTOR
|
||||
private const val MIB_FACTOR = 1024 * KIB_FACTOR
|
||||
private const val GB_FACTOR = 1000 * MB_FACTOR
|
||||
private const val GIB_FACTOR = 1024 * MIB_FACTOR
|
||||
|
||||
/**
|
||||
* Parse human readable size Strings
|
||||
|
@ -64,4 +67,4 @@ fun parseHumanReadableByteCount(arg0: String): Double? {
|
|||
"KiB" -> return ret * KIB_FACTOR
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,24 +3,15 @@ package eu.kanade.tachiyomi.extension.all.ehentai
|
|||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
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.model.*
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.*
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import java.net.URLEncoder
|
||||
|
||||
open class EHentai(override val lang: String, val ehLang: String) : HttpSource() {
|
||||
open class EHentai(override val lang: String, private val ehLang: String) : HttpSource() {
|
||||
|
||||
override val name = "E-Hentai"
|
||||
|
||||
|
@ -51,15 +42,13 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
return MangasPage(parsedMangas, hasNextPage)
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
|
||||
= Observable.just(listOf(SChapter.create().apply {
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = Observable.just(listOf(SChapter.create().apply {
|
||||
url = manga.url
|
||||
name = "Chapter"
|
||||
chapter_number = 1f
|
||||
}))
|
||||
|
||||
override fun fetchPageList(chapter: SChapter)
|
||||
= fetchChapterPage(chapter, "$baseUrl/${chapter.url}").map {
|
||||
override fun fetchPageList(chapter: SChapter) = fetchChapterPage(chapter, "$baseUrl/${chapter.url}").map {
|
||||
it.mapIndexed { i, s ->
|
||||
Page(i, s)
|
||||
}
|
||||
|
@ -80,8 +69,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
}
|
||||
}
|
||||
|
||||
private fun parseChapterPage(response: Element)
|
||||
= with(response) {
|
||||
private fun parseChapterPage(response: Element) = with(response) {
|
||||
select(".gdtm a").map {
|
||||
Pair(it.child(0).attr("alt").toInt(), it.attr("href"))
|
||||
}.sortedBy(Pair<Int, String>::first).map { it.second }
|
||||
|
@ -90,8 +78,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
private fun chapterPageCall(np: String) = client.newCall(chapterPageRequest(np)).asObservableSuccess()
|
||||
private fun chapterPageRequest(np: String) = exGet(np, null, headers)
|
||||
|
||||
private fun nextPageUrl(element: Element)
|
||||
= element.select("a[onclick=return false]").last()?.let {
|
||||
private fun nextPageUrl(element: Element) = element.select("a[onclick=return false]").last()?.let {
|
||||
if (it.text() == ">") it.attr("href") else null
|
||||
}
|
||||
|
||||
|
@ -115,8 +102,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
override fun searchMangaParse(response: Response) = genericMangaParse(response)
|
||||
override fun latestUpdatesParse(response: Response) = genericMangaParse(response)
|
||||
|
||||
private fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true)
|
||||
= GET(page?.let {
|
||||
private fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true) = GET(page?.let {
|
||||
addParam(url, "page", (it - 1).toString())
|
||||
} ?: url, additionalHeaders?.let {
|
||||
val headers = headers.newBuilder()
|
||||
|
@ -204,7 +190,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
Tag(it.text().trim(),
|
||||
it.hasClass("gtl"))
|
||||
}
|
||||
tags.put(namespace, currentTags)
|
||||
tags[namespace] = currentTags
|
||||
}
|
||||
|
||||
//Copy metadata to manga
|
||||
|
@ -214,19 +200,15 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
}
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
|
||||
override fun pageListParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
override fun pageListParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
|
||||
override fun fetchImageUrl(page: Page)
|
||||
= client.newCall(imageUrlRequest(page))
|
||||
override fun fetchImageUrl(page: Page) = client.newCall(imageUrlRequest(page))
|
||||
.asObservableSuccess()
|
||||
.map { realImageUrlParse(it, page) }!!
|
||||
|
||||
private fun realImageUrlParse(response: Response, page: Page)
|
||||
= with(response.asJsoup()) {
|
||||
private fun realImageUrlParse(response: Response, page: Page) = with(response.asJsoup()) {
|
||||
val currentImage = getElementById("img").attr("src")
|
||||
//TODO We cannot currently do this as page.url is immutable
|
||||
//Each press of the retry button will choose another server
|
||||
|
@ -236,8 +218,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
currentImage
|
||||
}!!
|
||||
|
||||
override fun imageUrlParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
|
||||
private val cookiesHeader by lazy {
|
||||
val cookies = mutableMapOf<String, String>()
|
||||
|
@ -253,25 +234,21 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
.flatMap { it.second }
|
||||
.joinToString("x")
|
||||
|
||||
cookies.put("uconfig", buildSettings(settings))
|
||||
cookies["uconfig"] = buildSettings(settings)
|
||||
|
||||
buildCookies(cookies)
|
||||
}
|
||||
|
||||
//Headers
|
||||
override fun headersBuilder()
|
||||
= super.headersBuilder().add("Cookie", cookiesHeader)!!
|
||||
override fun headersBuilder() = super.headersBuilder().add("Cookie", cookiesHeader)!!
|
||||
|
||||
private fun buildSettings(settings: List<String?>)
|
||||
= settings.filterNotNull().joinToString(separator = "-")
|
||||
private fun buildSettings(settings: List<String?>) = settings.filterNotNull().joinToString(separator = "-")
|
||||
|
||||
private fun buildCookies(cookies: Map<String, String>)
|
||||
= cookies.entries.map {
|
||||
private fun buildCookies(cookies: Map<String, String>) = cookies.entries.joinToString(separator = "; ", postfix = ";") {
|
||||
"${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}"
|
||||
}.joinToString(separator = "; ", postfix = ";")
|
||||
}
|
||||
|
||||
private fun addParam(url: String, param: String, value: String)
|
||||
= Uri.parse(url)
|
||||
private fun addParam(url: String, param: String, value: String) = Uri.parse(url)
|
||||
.buildUpon()
|
||||
.appendQueryParameter(param, value)
|
||||
.toString()
|
||||
|
@ -295,9 +272,9 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
AdvancedGroup()
|
||||
)
|
||||
|
||||
class GenreOption(name: String, val genreId: String) : Filter.CheckBox(name, false), UriFilter {
|
||||
class GenreOption(name: String, private val genreId: String) : Filter.CheckBox(name, false), UriFilter {
|
||||
override fun addToUri(builder: Uri.Builder) {
|
||||
builder.appendQueryParameter("f_" + genreId, if (state) "1" else "0")
|
||||
builder.appendQueryParameter("f_$genreId", if (state) "1" else "0")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,7 +291,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
GenreOption("Misc", "misc")
|
||||
))
|
||||
|
||||
class AdvancedOption(name: String, val param: String, defValue: Boolean = false) : Filter.CheckBox(name, defValue), UriFilter {
|
||||
class AdvancedOption(name: String, private val param: String, defValue: Boolean = false) : Filter.CheckBox(name, defValue), UriFilter {
|
||||
override fun addToUri(builder: Uri.Builder) {
|
||||
if (state)
|
||||
builder.appendQueryParameter(param, "on")
|
||||
|
@ -329,7 +306,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
|
|||
"5 stars"
|
||||
)), UriFilter {
|
||||
override fun addToUri(builder: Uri.Builder) {
|
||||
if (state > 0) builder.appendQueryParameter("f_srdd", Integer.toString(state + 1))
|
||||
if (state > 0) builder.appendQueryParameter("f_srdd", (state + 1).toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package eu.kanade.tachiyomi.extension.all.ehentai;
|
||||
package eu.kanade.tachiyomi.extension.all.ehentai
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
|
@ -32,25 +32,21 @@ class ExGalleryMetadata {
|
|||
val tags: MutableMap<String, List<Tag>> = mutableMapOf()
|
||||
|
||||
companion object {
|
||||
private fun splitGalleryUrl(url: String)
|
||||
= url.let {
|
||||
private fun splitGalleryUrl(url: String) = url.let {
|
||||
//Only parse URL if is full URL
|
||||
val pathSegments = if(it.startsWith("http"))
|
||||
val pathSegments = if (it.startsWith("http"))
|
||||
Uri.parse(it).pathSegments
|
||||
else
|
||||
it.split('/')
|
||||
pathSegments.filterNot(String::isNullOrBlank)
|
||||
}
|
||||
|
||||
fun galleryId(url: String) = splitGalleryUrl(url)[1]
|
||||
private fun galleryId(url: String) = splitGalleryUrl(url)[1]
|
||||
|
||||
fun galleryToken(url: String) =
|
||||
splitGalleryUrl(url)[2]
|
||||
private fun galleryToken(url: String) = splitGalleryUrl(url)[2]
|
||||
|
||||
fun normalizeUrl(id: String, token: String)
|
||||
= "/g/$id/$token/?nw=always"
|
||||
private fun normalizeUrl(id: String, token: String) = "/g/$id/$token/?nw=always"
|
||||
|
||||
fun normalizeUrl(url: String)
|
||||
= normalizeUrl(galleryId(url), galleryToken(url))
|
||||
fun normalizeUrl(url: String) = normalizeUrl(galleryId(url), galleryToken(url))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
|
|||
manga.status = SManga.COMPLETED
|
||||
title?.let { t ->
|
||||
if (ONGOING_SUFFIX.any {
|
||||
t.endsWith(it, ignoreCase = true)
|
||||
}) manga.status = SManga.ONGOING
|
||||
t.endsWith(it, ignoreCase = true)
|
||||
}) manga.status = SManga.ONGOING
|
||||
}
|
||||
|
||||
//Build a nice looking description out of what we know
|
||||
|
@ -71,8 +71,7 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
|
|||
.joinToString(separator = "\n")
|
||||
}
|
||||
|
||||
private fun buildTagsDescription(metadata: ExGalleryMetadata)
|
||||
= StringBuilder("Tags:\n").apply {
|
||||
private fun buildTagsDescription(metadata: ExGalleryMetadata) = StringBuilder("Tags:\n").apply {
|
||||
//BiConsumer only available in Java 8, we have to use destructuring here
|
||||
metadata.tags.forEach { (namespace, tags) ->
|
||||
if (tags.isNotEmpty()) {
|
||||
|
|
|
@ -3,9 +3,9 @@ apply plugin: 'kotlin-android'
|
|||
|
||||
ext {
|
||||
appName = 'Tachiyomi: FoolSlide'
|
||||
pkgNameSuffix = "all.foolslide"
|
||||
pkgNameSuffix = 'all.foolslide'
|
||||
extClass = '.FoolSlideFactory'
|
||||
extVersionCode = 21
|
||||
extVersionCode = 22
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,10 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
|
||||
|
||||
open class FoolSlide(override val name: String, override val baseUrl: String, override val lang: String, val urlModifier: String = "") : ParsedHttpSource() {
|
||||
open class FoolSlide(override val name: String,
|
||||
override val baseUrl: String,
|
||||
override val lang: String,
|
||||
val urlModifier: String = "") : ParsedHttpSource() {
|
||||
|
||||
protected open val dedupeLatestUpdates = true
|
||||
|
||||
|
@ -32,7 +35,7 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
|||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val mp = super.latestUpdatesParse(response)
|
||||
return if(dedupeLatestUpdates) {
|
||||
return if (dedupeLatestUpdates) {
|
||||
val mangas = mp.mangas.distinctBy { it.url }.filterNot { latestUpdatesUrls.contains(it.url) }
|
||||
latestUpdatesUrls.addAll(mangas.map { it.url })
|
||||
MangasPage(mangas, mp.hasNextPage)
|
||||
|
@ -96,8 +99,7 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
|||
|
||||
override fun searchMangaNextPageSelector() = "a:has(span.next)"
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga)
|
||||
= allowAdult(super.mangaDetailsRequest(manga))
|
||||
override fun mangaDetailsRequest(manga: SManga) = allowAdult(super.mangaDetailsRequest(manga))
|
||||
|
||||
open val mangaDetailsInfoSelector = "div.info"
|
||||
open val mangaDetailsThumbnailSelector = "div.thumbnail img"
|
||||
|
@ -119,14 +121,13 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
|||
*/
|
||||
private fun allowAdult(request: Request) = allowAdult(request.url().toString())
|
||||
|
||||
protected fun allowAdult(url: String): Request {
|
||||
private fun allowAdult(url: String): Request {
|
||||
return POST(url, body = FormBody.Builder()
|
||||
.add("adult", "true")
|
||||
.build())
|
||||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga)
|
||||
= allowAdult(super.chapterListRequest(manga))
|
||||
override fun chapterListRequest(manga: SManga) = allowAdult(super.chapterListRequest(manga))
|
||||
|
||||
override fun chapterListSelector() = "div.group div.element, div.list div.element"
|
||||
|
||||
|
@ -140,7 +141,8 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
|||
val chapter = SChapter.create()
|
||||
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
||||
chapter.name = urlElement.text()
|
||||
chapter.date_upload = dateElement.text()?.let { parseChapterDate(it.substringAfter(", ")) } ?: 0
|
||||
chapter.date_upload = dateElement.text()?.let { parseChapterDate(it.substringAfter(", ")) }
|
||||
?: 0
|
||||
return chapter
|
||||
}
|
||||
|
||||
|
@ -172,18 +174,18 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
|||
|
||||
var result = DATE_FORMAT_1.parseOrNull(date)
|
||||
|
||||
for(dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES) {
|
||||
for (dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES) {
|
||||
if (result == null)
|
||||
result = dateFormat.parseOrNull(date)
|
||||
else
|
||||
break
|
||||
}
|
||||
|
||||
for(dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES_NO_YEAR) {
|
||||
for (dateFormat in DATE_FORMATS_WITH_ORDINAL_SUFFIXES_NO_YEAR) {
|
||||
if (result == null) {
|
||||
result = dateFormat.parseOrNull(date)
|
||||
|
||||
if(result != null) {
|
||||
if (result != null) {
|
||||
// Result parsed but no year, copy current year over
|
||||
result = Calendar.getInstance().apply {
|
||||
time = result
|
||||
|
@ -230,13 +232,12 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
|||
private fun SimpleDateFormat.parseOrNull(string: String): Date? {
|
||||
return try {
|
||||
parse(string)
|
||||
} catch(e: ParseException) {
|
||||
} catch (e: ParseException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter)
|
||||
= allowAdult(super.pageListRequest(chapter))
|
||||
override fun pageListRequest(chapter: SChapter) = allowAdult(super.pageListRequest(chapter))
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val doc = document.toString()
|
||||
|
@ -266,4 +267,4 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
|
|||
SimpleDateFormat("dd'$it' MMMM", Locale.US)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,8 @@ import com.google.gson.JsonParser
|
|||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
|
@ -17,34 +18,33 @@ class FoolSlideFactory : SourceFactory {
|
|||
|
||||
fun getAllFoolSlide(): List<Source> {
|
||||
return listOf(
|
||||
JaminisBox(),
|
||||
HelveticaScans(),
|
||||
SenseScans(),
|
||||
KireiCake(),
|
||||
SilentSky(),
|
||||
Mangatellers(),
|
||||
IskultripScans(),
|
||||
AnataNoMotokare(),
|
||||
DeathTollScans(),
|
||||
DKThias(),
|
||||
WorldThree(),
|
||||
DokiFansubs(),
|
||||
YuriIsm(),
|
||||
AjiaNoScantrad(),
|
||||
OneTimeScans(),
|
||||
TsubasaSociety(),
|
||||
MangaScouts(),
|
||||
StormInHeaven(),
|
||||
Lilyreader(),
|
||||
MidnightHaven(),
|
||||
Russification(),
|
||||
EvilFlowers(),
|
||||
AkaiYuhiMunTeam(),
|
||||
LupiTeam(),
|
||||
HentaiCafe(),
|
||||
ShoujoSense(),
|
||||
TheCatScans(),
|
||||
ShoujoHearts()
|
||||
JaminisBox(),
|
||||
HelveticaScans(),
|
||||
SenseScans(),
|
||||
KireiCake(),
|
||||
SilentSky(),
|
||||
Mangatellers(),
|
||||
IskultripScans(),
|
||||
AnataNoMotokare(),
|
||||
DeathTollScans(),
|
||||
DKThias(),
|
||||
WorldThree(),
|
||||
DokiFansubs(),
|
||||
YuriIsm(),
|
||||
AjiaNoScantrad(),
|
||||
OneTimeScans(),
|
||||
TsubasaSociety(),
|
||||
MangaScouts(),
|
||||
StormInHeaven(),
|
||||
Lilyreader(),
|
||||
Russification(),
|
||||
EvilFlowers(),
|
||||
AkaiYuhiMunTeam(),
|
||||
LupiTeam(),
|
||||
HentaiCafe(),
|
||||
ShoujoSense(),
|
||||
TheCatScans(),
|
||||
ShoujoHearts()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ class SenseScans : FoolSlide("Sense-Scans", "https://sensescans.com", "en", "/re
|
|||
|
||||
class KireiCake : FoolSlide("Kirei Cake", "https://reader.kireicake.com", "en")
|
||||
|
||||
class SilentSky : FoolSlide("Silent Sky", "http://reader.silentsky-scans.net", "en")
|
||||
class SilentSky : FoolSlide("Silent Sky", "https://reader.silentsky-scans.net", "en")
|
||||
|
||||
class Mangatellers : FoolSlide("Mangatellers", "http://www.mangatellers.gr", "en", "/reader/reader") {
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
|
@ -108,12 +108,10 @@ class TsubasaSociety : FoolSlide("Tsubasa Society", "https://www.tsubasasociety.
|
|||
|
||||
class MangaScouts : FoolSlide("MangaScouts", "http://onlinereader.mangascouts.org", "de")
|
||||
|
||||
class StormInHeaven : FoolSlide("Storm in Heaven", "http://www.storm-in-heaven.net", "it", "/reader-sih")
|
||||
class StormInHeaven : FoolSlide("Storm in Heaven", "https://www.storm-in-heaven.net", "it", "/reader-sih")
|
||||
|
||||
class Lilyreader : FoolSlide("Lilyreader", "https://manga.smuglo.li", "en")
|
||||
|
||||
class MidnightHaven : FoolSlide("Midnight Haven", "http://midnighthaven.shounen-ai.net", "en", "/reader")
|
||||
|
||||
class Russification : FoolSlide("Русификация", "https://rusmanga.ru", "ru")
|
||||
|
||||
class EvilFlowers : FoolSlide("Evil Flowers", "http://reader.evilflowers.com", "en")
|
||||
|
@ -158,5 +156,3 @@ class ShoujoHearts : FoolSlide("ShoujoHearts", "http://shoujohearts.com", "en",
|
|||
return manga
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
}
|
||||
|
||||
filters.findInstance<ArtistFilter>()?.let { f ->
|
||||
if(f.state.isNotBlank()) {
|
||||
if (f.state.isNotBlank()) {
|
||||
requireNoUrl()
|
||||
url = "/artist/${f.state
|
||||
.trim()
|
||||
|
@ -72,19 +72,19 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
}
|
||||
}
|
||||
filters.findInstance<BookFilter>()?.let { f ->
|
||||
if(f.state) {
|
||||
if (f.state) {
|
||||
requireNoUrl()
|
||||
url = "/category/book/"
|
||||
}
|
||||
}
|
||||
filters.findInstance<TagFilter>()?.let { f ->
|
||||
if(f.state != 0) {
|
||||
if (f.state != 0) {
|
||||
requireNoUrl()
|
||||
url = "/tag/${f.values[f.state].name}/"
|
||||
}
|
||||
}
|
||||
|
||||
if(query.isNotBlank()) {
|
||||
if (query.isNotBlank()) {
|
||||
requireNoUrl()
|
||||
url = "/"
|
||||
queryString = "s=" + URLEncoder.encode(query, "UTF-8")
|
||||
|
@ -97,8 +97,8 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
|
||||
private fun pagedRequest(url: String, page: Int, queryString: String? = null): Request {
|
||||
// The site redirects page 1 -> url-without-page so we do this redirect early for optimization
|
||||
val builtUrl = if(page == 1) url else "${url}page/$page/"
|
||||
return GET(if(queryString != null) "$builtUrl?$queryString" else builtUrl)
|
||||
val builtUrl = if (page == 1) url else "${url}page/$page/"
|
||||
return GET(if (queryString != null) "$builtUrl?$queryString" else builtUrl)
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
||||
|
@ -106,7 +106,7 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return client.newCall(searchMangaRequest(page, query, filters))
|
||||
.asObservable().doOnNext { response ->
|
||||
if(!response.isSuccessful) {
|
||||
if (!response.isSuccessful) {
|
||||
response.close()
|
||||
// Better error message for invalid artist
|
||||
if (response.code() == 404
|
||||
|
@ -132,7 +132,6 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
class ArtistFilter : Filter.Text("Artist (must be exact match)")
|
||||
class BookFilter : Filter.CheckBox("Show books only", false)
|
||||
class TagFilter : Filter.Select<Tag>("Tag", arrayOf(
|
||||
Tag("all", "All"),
|
||||
Tag("ahegao", "Ahegao"),
|
||||
Tag("anal", "Anal"),
|
||||
Tag("big-ass", "Big ass"),
|
||||
|
@ -155,9 +154,9 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
Tag("group", "Group"),
|
||||
Tag("hairy", "Hairy"),
|
||||
Tag("handjob", "Handjob"),
|
||||
Tag("heart-pupils", "Heart pupils"),
|
||||
Tag("housewife", "Housewife"),
|
||||
Tag("incest", "Incest"),
|
||||
Tag("large-breast", "Large breast"),
|
||||
Tag("lingerie", "Lingerie"),
|
||||
Tag("loli", "Loli"),
|
||||
Tag("masturbation", "Masturbation"),
|
||||
|
@ -167,7 +166,7 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
Tag("pettanko", "Pettanko"),
|
||||
Tag("rape", "Rape"),
|
||||
Tag("schoolgirl", "Schoolgirl"),
|
||||
Tag("sex-toys", "Sex Toys"),
|
||||
Tag("sex-toys", "Sex toys"),
|
||||
Tag("shota", "Shota"),
|
||||
Tag("socks", "Socks"),
|
||||
Tag("stocking", "Stocking"),
|
||||
|
@ -175,11 +174,12 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
Tag("swimsuit", "Swimsuit"),
|
||||
Tag("teacher", "Teacher"),
|
||||
Tag("tsundere", "Tsundere"),
|
||||
Tag("uncensored", "uncensored"),
|
||||
Tag("uncensored", "Uncensored"),
|
||||
Tag("vanilla", "Vanilla"),
|
||||
Tag("x-ray", "X-ray")
|
||||
Tag("x-ray", "X-Ray")
|
||||
))
|
||||
class Tag(val name: String, val displayName: String) {
|
||||
|
||||
class Tag(val name: String, private val displayName: String) {
|
||||
override fun toString() = displayName
|
||||
}
|
||||
|
||||
|
@ -189,4 +189,4 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
|
|||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
|
||||
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
|
|
@ -5,7 +5,7 @@ ext {
|
|||
appName = 'Tachiyomi: Genkan (multiple sources)'
|
||||
pkgNameSuffix = 'all.genkan'
|
||||
extClass = '.GenkanFactory'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
|
||||
abstract class Genkan(
|
||||
override val name: String,
|
||||
override val baseUrl: String,
|
||||
override val lang: String
|
||||
override val name: String,
|
||||
final override val baseUrl: String,
|
||||
override val lang: String
|
||||
) : ParsedHttpSource() {
|
||||
|
||||
override val supportsLatest = true
|
||||
|
@ -27,7 +27,7 @@ abstract class Genkan(
|
|||
|
||||
private val popularMangaUrl = "$baseUrl/comics?page=" // Search is also based off this val
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
return GET("$popularMangaUrl$page")
|
||||
return GET("$popularMangaUrl$page")
|
||||
}
|
||||
|
||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||
|
@ -36,8 +36,8 @@ abstract class Genkan(
|
|||
private val latestUpdatesTitles = mutableSetOf<String>()
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request {
|
||||
if (page == 1) latestUpdatesTitles.clear()
|
||||
return GET("$baseUrl/latest?page=$page")
|
||||
if (page == 1) latestUpdatesTitles.clear()
|
||||
return GET("$baseUrl/latest?page=$page")
|
||||
}
|
||||
|
||||
// To prevent dupes
|
||||
|
@ -165,10 +165,10 @@ abstract class Genkan(
|
|||
|
||||
// If the date string contains the word "ago" send it off for relative date parsing otherwise use dateFormat
|
||||
private fun parseChapterDate(string: String): Long? {
|
||||
if ("ago" in string) {
|
||||
return parseRelativeDate(string) ?: 0
|
||||
return if ("ago" in string) {
|
||||
parseRelativeDate(string) ?: 0
|
||||
} else {
|
||||
return dateFormat.parse(string).time
|
||||
dateFormat.parse(string).time
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,14 +177,14 @@ abstract class Genkan(
|
|||
val trimmedDate = date.substringBefore(" ago").removeSuffix("s").split(" ")
|
||||
|
||||
val calendar = Calendar.getInstance()
|
||||
when (trimmedDate[1]){
|
||||
"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)}
|
||||
}
|
||||
when (trimmedDate[1]) {
|
||||
"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
|
||||
}
|
||||
|
@ -192,10 +192,10 @@ abstract class Genkan(
|
|||
override fun pageListParse(document: Document): List<Page> {
|
||||
val pages = mutableListOf<Page>()
|
||||
|
||||
val allImages = document.select("div#pages-container + script").first().data()
|
||||
.substringAfter("[").substringBefore("];")
|
||||
.replace(Regex("""["\\]"""), "")
|
||||
.split(",")
|
||||
val allImages = document.select("div#pages-container + script").first().data()
|
||||
.substringAfter("[").substringBefore("];")
|
||||
.replace(Regex("""["\\]"""), "")
|
||||
.split(",")
|
||||
|
||||
for (i in 0 until allImages.size) {
|
||||
pages.add(Page(i, "", allImages[i]))
|
||||
|
@ -207,5 +207,4 @@ abstract class Genkan(
|
|||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun getFilterList() = FilterList()
|
||||
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@ import eu.kanade.tachiyomi.source.SourceFactory
|
|||
|
||||
class GenkanFactory : SourceFactory {
|
||||
override fun createSources(): List<Source> = listOf(
|
||||
LeviatanScans(),
|
||||
PsychoPlay(),
|
||||
OneShotScans(),
|
||||
KaguyaDex(),
|
||||
KomiScans(),
|
||||
HunlightScans())
|
||||
LeviatanScans(),
|
||||
PsychoPlay(),
|
||||
OneShotScans(),
|
||||
KaguyaDex(),
|
||||
KomiScans(),
|
||||
HunlightScans())
|
||||
}
|
||||
|
||||
class LeviatanScans : Genkan("Leviatan Scans", "https://leviatanscans.com", "en")
|
||||
|
|
Loading…
Reference in New Issue