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:
TacoTheDank 2019-08-03 21:38:05 -04:00 committed by Eugene
parent 27175b3dee
commit c94cc58e7d
16 changed files with 188 additions and 214 deletions

View File

@ -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'
}

View File

@ -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!")
}
}

View File

@ -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", "/")

View File

@ -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'
}

View File

@ -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(),

View File

@ -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
}
}

View File

@ -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())
}
}

View File

@ -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))
}
}

View File

@ -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()) {

View File

@ -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'
}

View File

@ -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)
}
}
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: Genkan (multiple sources)'
pkgNameSuffix = 'all.genkan'
extClass = '.GenkanFactory'
extVersionCode = 2
extVersionCode = 3
libVersion = '1.2'
}

View File

@ -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()
}

View File

@ -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")