FoolSlide enchancements (#292)

* Add more FoolSlide sources
Fix spacing in FoolSlide extension name

* Fix broken chapter dates on Jaiminibox

* Many fixes to new Foolslide sources
This commit is contained in:
Andy Bao 2018-04-17 10:52:57 -04:00 committed by Carlos
parent b2850ec5c4
commit bbda7fd772
3 changed files with 244 additions and 23 deletions

View File

@ -5,8 +5,8 @@ ext {
appName = 'Tachiyomi: FoolSlide'
pkgNameSuffix = "all.foolslide"
extClass = '.FoolSlideFactory'
extVersionCode = 1
extVersionSuffix = 1
extVersionCode = 2
extVersionSuffix = 2
libVersion = '1.2'
}
dependencies {

View File

@ -15,9 +15,10 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
open class FoolSlide(override val name: String, override val baseUrl: String, override val lang: String, private val urlModifier: String = "") : ParsedHttpSource() {
open class FoolSlide(override val name: String, override val baseUrl: String, override val lang: String, val urlModifier: String = "") : ParsedHttpSource() {
override val supportsLatest = true
@ -41,8 +42,8 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
manga.title = it.text()
}
element.select("img").first().let {
manga.thumbnail_url = it.attr("src").replace("/thumb_", "/")
element.select("img").first()?.let {
manga.thumbnail_url = it.absUrl("src").replace("/thumb_", "/")
}
return manga
@ -62,9 +63,8 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
override fun latestUpdatesNextPageSelector() = "div.next"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val form = FormBody.Builder().apply {
add("search", query)
}
val form = FormBody.Builder()
.add("search", query)
return POST("$baseUrl$urlModifier/search/", headers, form.build())
}
@ -82,22 +82,45 @@ 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))
open val mangaDetailsInfoSelector = "div.info"
open val mangaDetailsThumbnailSelector = "div.thumbnail img"
override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.info").first().text()
val infoElement = document.select(mangaDetailsInfoSelector).first().text()
val manga = SManga.create()
manga.author = infoElement.substringAfter("Author:").substringBefore("Artist:")
manga.artist = infoElement.substringAfter("Artist:").substringBefore("Synopsis:")
manga.description = infoElement.substringAfter("Synopsis:")
manga.thumbnail_url = document.select("div.thumbnail img").first()?.attr("src")
manga.thumbnail_url = document.select(mangaDetailsThumbnailSelector).first()?.absUrl("src")
return manga
}
override fun chapterListSelector() = "div.group div.element"
/**
* Transform a GET request into a POST request that automatically authorizes all adult content
*/
private fun allowAdult(request: Request): Request {
return POST(request.url().toString(), body = FormBody.Builder()
.add("adult", "true")
.build())
}
override fun chapterListRequest(manga: SManga)
= allowAdult(super.chapterListRequest(manga))
override fun chapterListSelector() = "div.group div.element, div.list div.element"
open val chapterDateSelector = "div.meta_r"
open val chapterUrlSelector = "a[title]"
override fun chapterFromElement(element: Element): SChapter {
val urlElement = element.select("a[title]").first()
val dateElement = element.select("div.meta_r").first()
val urlElement = element.select(chapterUrlSelector).first()
val dateElement = element.select(chapterDateSelector).first()
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href"))
chapter.name = urlElement.text()
@ -105,26 +128,33 @@ open class FoolSlide(override val name: String, override val baseUrl: String, ov
return chapter
}
private fun parseChapterDate(date: String): Long {
open fun parseChapterDate(date: String): Long {
return try {
SimpleDateFormat("yyyy.MM.dd").parse(date).time
SimpleDateFormat("yyyy.MM.dd", Locale.US).parse(date).time
} catch (e: ParseException) {
0L
}
}
override fun pageListParse(document: Document): List<Page> {
override fun pageListRequest(chapter: SChapter)
= allowAdult(super.pageListRequest(chapter))
override fun pageListParse(document: Document): List<Page> {
val doc = document.toString()
val jsonstr = doc.substringAfter("var pages = ").substringBefore(";")
val json = JsonParser().parse(jsonstr).asJsonArray
val pages = mutableListOf<Page>()
json.forEach {
pages.add(Page(pages.size, "", it.get("url").asString))
// Create dummy element to resolve relative URL
val absUrl = document.createElement("a")
.attr("href", it["url"].asString)
.absUrl("href")
pages.add(Page(pages.size, "", absUrl))
}
return pages
}
override fun imageUrlParse(document: Document) = throw Exception("Not used")
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
}

View File

@ -3,19 +3,54 @@ package eu.kanade.tachiyomi.extension.all.foolslide
import android.util.Base64
import com.github.salomonbrys.kotson.get
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.Page
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Request
import org.jsoup.nodes.Document
import java.util.*
class FoolSlideFactory : SourceFactory {
override fun createSources(): List<Source> = getAllFoolSlide()
}
fun getAllFoolSlide(): List<Source> {
return listOf(JaminisBox(), ChampionScans(), HelveticaScans())
return listOf(
JaminisBox(),
ChampionScans(),
HelveticaScans(),
SenseScans(),
SeaOtterScans(),
KireiCake(),
HiranoMoeScansBureau(),
SilentSky(),
Mangatellers(),
IskultripScans(),
PinkFatale(),
AnataNoMotokare(),
HatigarmScans(),
DeathTollScans(),
DKThias(),
MangaichiScanlationDivision(),
WorldThree(),
TheCatScans(),
AngelicScanlations(),
DokiFansubs(),
YuriIsm(),
AjiaNoScantrad(),
OneTimeScans(),
TsubasaSociety(),
Helheim(),
MangaScouts(),
StormInHeaven(),
Lilyreader(),
MidnightHaven(),
Russification(),
NieznaniReader(),
EvilFlowers()
)
}
class JaminisBox : FoolSlide("Jaimini's Box", "https://jaiminisbox.com", "en", "/reader") {
@ -27,12 +62,168 @@ class JaminisBox : FoolSlide("Jaimini's Box", "https://jaiminisbox.com", "en", "
val json = JsonParser().parse(decodeJson).asJsonArray
val pages = mutableListOf<Page>()
json.forEach {
pages.add(Page(pages.size, "", it.get("url").asString))
pages.add(Page(pages.size, "", it["url"].asString))
}
return pages
}
override fun parseChapterDate(date: String): Long {
try {
val lcDate = date.toLowerCase()
if (lcDate.endsWith("ago"))
return parseRelativeDate(lcDate)
//Handle 'yesterday' and 'today'
var relativeDate: Calendar? = null
if (lcDate.startsWith("yesterday")) {
relativeDate = Calendar.getInstance()
relativeDate.add(Calendar.DAY_OF_MONTH, -1) //yesterday
} else if (lcDate.startsWith("today")) {
relativeDate = Calendar.getInstance()
}
return relativeDate?.timeInMillis
?: super.parseChapterDate(date)
} catch(t: Throwable) {
return 0L
}
}
/**
* Parses dates in this form:
* `11 days ago`
*/
private fun parseRelativeDate(date: String): Long {
val trimmedDate = date.split(" ")
if (trimmedDate[2] != "ago") return 0
val number = trimmedDate[0].toIntOrNull() ?: return 0
val unit = trimmedDate[1].removeSuffix("s") //Remove 's' suffix
val now = Calendar.getInstance()
//Map English unit to Java unit
val javaUnit = when (unit) {
"year", "yr" -> Calendar.YEAR
"month" -> Calendar.MONTH
"week" -> Calendar.WEEK_OF_MONTH
"day" -> Calendar.DAY_OF_MONTH
"hour", "hr" -> Calendar.HOUR
"minute", "min" -> Calendar.MINUTE
"second", "sec" -> Calendar.SECOND
else -> return 0
}
now.add(javaUnit, -number)
return now.timeInMillis
}
}
class ChampionScans : FoolSlide("Champion Scans", "http://reader.championscans.com", "en")
class HelveticaScans : FoolSlide("Helvetica Scans", "http://helveticascans.com", "en", "/r")
class SenseScans : FoolSlide("Sense-Scans", "http://sensescans.com", "en", "/reader")
class SeaOtterScans : FoolSlide("Sea Otter Scans", "https://reader.seaotterscans.com", "en")
class KireiCake : FoolSlide("Kirei Cake", "https://reader.kireicake.com", "en")
class HiranoMoeScansBureau : FoolSlide("HiranoMoe Scans Bureau", "http://hiranomoe.com", "en", "/r")
class SilentSky : FoolSlide("Silent Sky", "http://reader.silentsky-scans.net", "en")
class Mangatellers : FoolSlide("Mangatellers", "http://www.mangatellers.gr", "en", "/reader/reader") {
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl$urlModifier/list/$page/", headers)
}
}
class IskultripScans : FoolSlide("Iskultrip Scans", "http://www.maryfaye.net", "en", "/reader")
class PinkFatale : FoolSlide("PinkFatale", "http://manga.pinkfatale.net", "en")
class AnataNoMotokare : FoolSlide("Anata no Motokare", "https://motokare.maos.ca", "en")
// Has other languages too but it is difficult to differentiate between them
class HatigarmScans : FoolSlide("Hatigarm Scans", "http://hatigarmscans.eu", "en", "/hs") {
override fun chapterListSelector() = "div.list-group div.list-group-item:not(.active)"
override val chapterDateSelector = "div.label"
override val chapterUrlSelector = ".title > a"
override fun popularMangaSelector() = ".well > a"
override fun latestUpdatesSelector() = "div.latest > div.row"
override val mangaDetailsInfoSelector = "div.col-md-9"
override val mangaDetailsThumbnailSelector = "div.thumb > img"
}
class DeathTollScans : FoolSlide("Death Toll Scans", "https://reader.deathtollscans.net", "en")
class DKThias : FoolSlide("DKThias Scanlations", "http://reader.dkthias.com", "en", "/reader") {
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl$urlModifier/list/$page/", headers)
}
}
class MangaichiScanlationDivision : FoolSlide("Mangaichi Scanlation Division", "http://mangaichiscans.mokkori.fr", "en", "/fs")
class WorldThree : FoolSlide("World Three", "http://www.slide.world-three.org", "en")
class TheCatScans : FoolSlide("The Cat Scans", "https://reader.thecatscans.com", "en")
class AngelicScanlations : FoolSlide("Angelic Scanlations", "http://www.angelicscans.net", "en", "/foolslide") {
override fun latestUpdatesSelector() = "div.list > div.releases"
override fun popularMangaSelector() = ".grouped > .series-block"
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
thumbnail_url = document.select(".preview > img").attr("src")
val info = document.select(".data").first()
title = info.select("h2.title").text().trim()
val authorArtist = info.select(".author").text().split("/")
author = authorArtist.getOrNull(0)?.trim()
artist = authorArtist.getOrNull(1)?.trim()
description = info.ownText().trim()
}
}
override fun chapterListSelector() = ".list > .release"
override val chapterDateSelector = ".metadata"
}
class DokiFansubs : FoolSlide("Doki Fansubs", "https://kobato.hologfx.com", "en", "/reader")
class YuriIsm : FoolSlide("Yuri-ism", "http://reader.yuri-ism.com", "en", "/slide")
class AjiaNoScantrad : FoolSlide("Ajia no Scantrad", "http://ajianoscantrad.fr", "fr", "/reader")
class OneTimeScans : FoolSlide("One Time Scans", "https://otscans.com", "en", "/foolslide")
class TsubasaSociety : FoolSlide("Tsubasa Society", "https://www.tsubasasociety.com", "en", "/reader/master/Xreader")
class Helheim : FoolSlide("Helheim", "http://helheim.pl", "pl", "/reader")
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 Lilyreader : FoolSlide("Lilyreader", "https://manga.smuglo.li", "en")
class MidnightHaven : FoolSlide("Midnight Haven", "http://midnighthaven.shounen-ai.net", "en", "/reader")
class Russification : FoolSlide("Русификация", "http://rusmanga.ru", "ru")
class NieznaniReader : FoolSlide("Nieznani", "http://reader.nieznani.mynindo.pl", "pl")
class EvilFlowers : FoolSlide("Evil Flowers", "http://reader.evilflowers.com", "en")