Fix TMO error. (#1175)

Fix TMO error #1166
This commit is contained in:
Edgar Mejía 2019-06-09 18:35:47 -06:00 committed by Eugene
parent bc861636fd
commit 5fda10228b
3 changed files with 155 additions and 110 deletions

View File

@ -23,4 +23,8 @@ public class Duktape implements Closeable {
throw new RuntimeException("Stub!"); throw new RuntimeException("Stub!");
} }
public synchronized <T> T get(final String name, final Class<T> type) {
throw new RuntimeException("Stub!");
}
} }

View File

@ -5,12 +5,13 @@ ext {
appName = 'Tachiyomi: TuMangaOnline' appName = 'Tachiyomi: TuMangaOnline'
pkgNameSuffix = 'es.tumangaonline' pkgNameSuffix = 'es.tumangaonline'
extClass = '.TuMangaOnline' extClass = '.TuMangaOnline'
extVersionCode = 4 extVersionCode = 5
libVersion = '1.2' libVersion = '1.2'
} }
dependencies { dependencies {
implementation project(':lib-ratelimit') implementation project(':lib-ratelimit')
compileOnly project(':duktape-stub')
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -11,6 +11,7 @@ import org.jsoup.nodes.Element
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import com.squareup.duktape.Duktape
class TuMangaOnline : ParsedHttpSource() { class TuMangaOnline : ParsedHttpSource() {
@ -25,32 +26,32 @@ class TuMangaOnline : ParsedHttpSource() {
private val rateLimitInterceptor = RateLimitInterceptor(4) private val rateLimitInterceptor = RateLimitInterceptor(4)
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addNetworkInterceptor(rateLimitInterceptor) .addNetworkInterceptor(rateLimitInterceptor)
.connectTimeout(1, TimeUnit.MINUTES) .connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES) .readTimeout(1, TimeUnit.MINUTES)
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
.followRedirects(true) .followRedirects(true)
.build()!! .build()!!
override fun headersBuilder(): Headers.Builder { override fun headersBuilder(): Headers.Builder {
return Headers.Builder() return Headers.Builder()
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) Gecko/20100101 Firefox/60") .add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) Gecko/20100101 Firefox/60")
} }
private fun getBuilder(url: String): String { private fun getBuilder(url: String): String {
val req = Request.Builder() val req = Request.Builder()
.headers(headersBuilder() .headers(headersBuilder()
.add("Referer", "$baseUrl/library/manga/") .add("Referer", "$baseUrl/library/manga/")
.add("Cache-mode", "no-cache") .add("Cache-mode", "no-cache")
.build()) .build())
.url(url) .url(url)
.build() .build()
return client.newCall(req) return client.newCall(req)
.execute() .execute()
.request() .request()
.url() .url()
.toString() .toString()
} }
override fun popularMangaSelector() = "div.element" override fun popularMangaSelector() = "div.element"
@ -149,8 +150,8 @@ class TuMangaOnline : ParsedHttpSource() {
} }
is GenreList -> { is GenreList -> {
filter.state filter.state
.filter { genre -> genre.state } .filter { genre -> genre.state }
.forEach { genre -> url.addQueryParameter("genders[]", genre.id) } .forEach { genre -> url.addQueryParameter("genders[]", genre.id) }
} }
} }
} }
@ -200,57 +201,92 @@ class TuMangaOnline : ParsedHttpSource() {
private fun parseChapterDate(date: String): Long = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(date).time private fun parseChapterDate(date: String): Long = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(date).time
override fun pageListRequest(chapter: SChapter): Request { override fun pageListRequest(chapter: SChapter): Request {
val url = getBuilder(baseUrl + chapter.url) val url = getBuilder(baseUrl + chapter.url).substringBeforeLast("/") + "/cascade"
// Get /cascade instead of /paginate to get all pages at once // Get /cascade instead of /paginate to get all pages at once
return GET(url.substringBeforeLast("/") + "/cascade", headers) return GET(url, headers)
} }
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply { override fun pageListParse(response: Response): List<Page> {
document.select("div#viewer-container > div.viewer-image-container > img.viewer-image")?.forEach { val pages = mutableListOf<Page>()
add(Page(size, "", it.attr("src"))) val body = response.body()!!.string()
var images = ""
val script = """
function chapterInit(data) {
pipe = "|";
idsR = /img_[^\.^;]+\.src\s*=\s*"([^"]+)/gm;
idData = ""
do {
m = idsR.exec(data);
if (m) {
idData = idData + pipe + m[1] ;
}
} while (m);
return idData;
}
"""
Duktape.create().use {
it.evaluate(script)
val chapterInit = it.get("chapterInit", JSIn::class.java)
images = chapterInit.call(true, body)
} }
images.split("|").forEachIndexed { index, element ->
if (element.isNotEmpty()) {
pages.add(Page(index, baseUrl, element))
}
}
return pages
}
override fun pageListParse(document: Document) = throw UnsupportedOperationException("Not used")
override fun imageUrlRequest(page: Page): Request {
return GET(page.url, headers)
} }
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used") override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
private class Types : UriPartFilter("Tipo", arrayOf( private class Types : UriPartFilter("Tipo", arrayOf(
Pair("Ver todo", ""), Pair("Ver todo", ""),
Pair("Manga", "manga"), Pair("Manga", "manga"),
Pair("Manhua", "manhua"), Pair("Manhua", "manhua"),
Pair("Manhwa", "manhwa"), Pair("Manhwa", "manhwa"),
Pair("Novela", "novel"), Pair("Novela", "novel"),
Pair("One shot", "one_shot"), Pair("One shot", "one_shot"),
Pair("Doujinshi", "doujinshi"), Pair("Doujinshi", "doujinshi"),
Pair("Oel", "oel") Pair("Oel", "oel")
)) ))
private class Demography : UriPartFilter("Demografía", arrayOf( private class Demography : UriPartFilter("Demografía", arrayOf(
Pair("Ver todo", ""), Pair("Ver todo", ""),
Pair("Seinen", "seinen"), Pair("Seinen", "seinen"),
Pair("Shoujo", "shoujo"), Pair("Shoujo", "shoujo"),
Pair("Shounen", "shounen"), Pair("Shounen", "shounen"),
Pair("Josei", "josei"), Pair("Josei", "josei"),
Pair("Kodomo", "kodomo") Pair("Kodomo", "kodomo")
)) ))
private class FilterBy : UriPartFilter("Ordenar por", arrayOf( private class FilterBy : UriPartFilter("Ordenar por", arrayOf(
Pair("Título", "title"), Pair("Título", "title"),
Pair("Autor", "author"), Pair("Autor", "author"),
Pair("Compañia", "company") Pair("Compañia", "company")
)) ))
private class OrderBy : UriPartFilter("Ordenar por", arrayOf( private class OrderBy : UriPartFilter("Ordenar por", arrayOf(
Pair("Me gusta", "likes_count"), Pair("Me gusta", "likes_count"),
Pair("Alfabético", "alphabetically"), Pair("Alfabético", "alphabetically"),
Pair("Puntuación", "score"), Pair("Puntuación", "score"),
Pair("Creación", "creation"), Pair("Creación", "creation"),
Pair("Fecha estreno", "release_date") Pair("Fecha estreno", "release_date")
)) ))
private class OrderDir : UriPartFilter("Ordenar por", arrayOf( private class OrderDir : UriPartFilter("Ordenar por", arrayOf(
Pair("ASC", "asc"), Pair("ASC", "asc"),
Pair("DESC", "desc") Pair("DESC", "desc")
)) ))
private class WebcomicFilter : Filter.TriState("Webcomic") private class WebcomicFilter : Filter.TriState("Webcomic")
@ -262,76 +298,80 @@ class TuMangaOnline : ParsedHttpSource() {
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Géneros", genres) private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Géneros", genres)
override fun getFilterList() = FilterList( override fun getFilterList() = FilterList(
Types(), Types(),
Demography(), Demography(),
Filter.Separator(), Filter.Separator(),
FilterBy(), FilterBy(),
OrderBy(), OrderBy(),
OrderDir(), OrderDir(),
Filter.Separator(), Filter.Separator(),
WebcomicFilter(), WebcomicFilter(),
FourKomaFilter(), FourKomaFilter(),
AmateurFilter(), AmateurFilter(),
EroticFilter(), EroticFilter(),
GenreList(getGenreList()) GenreList(getGenreList())
) )
// Array.from(document.querySelectorAll('#books-genders .col-auto .custom-control')).map(a => `Genre("${a.querySelector('label').innerText}", "${a.querySelector('input').value}")`).join(',\n') // Array.from(document.querySelectorAll('#books-genders .col-auto .custom-control')).map(a => `Genre("${a.querySelector('label').innerText}", "${a.querySelector('input').value}")`).join(',\n')
// on https://tumangaonline.me/library // on https://tumangaonline.me/library
private fun getGenreList() = listOf( private fun getGenreList() = listOf(
Genre("Acción", "1"), Genre("Acción", "1"),
Genre("Aventura", "2"), Genre("Aventura", "2"),
Genre("Comedia", "3"), Genre("Comedia", "3"),
Genre("Drama", "4"), Genre("Drama", "4"),
Genre("Recuentos de la vida", "5"), Genre("Recuentos de la vida", "5"),
Genre("Ecchi", "6"), Genre("Ecchi", "6"),
Genre("Fantasia", "7"), Genre("Fantasia", "7"),
Genre("Magia", "8"), Genre("Magia", "8"),
Genre("Sobrenatural", "9"), Genre("Sobrenatural", "9"),
Genre("Horror", "10"), Genre("Horror", "10"),
Genre("Misterio", "11"), Genre("Misterio", "11"),
Genre("Psicológico", "12"), Genre("Psicológico", "12"),
Genre("Romance", "13"), Genre("Romance", "13"),
Genre("Ciencia Ficción", "14"), Genre("Ciencia Ficción", "14"),
Genre("Thriller", "15"), Genre("Thriller", "15"),
Genre("Deporte", "16"), Genre("Deporte", "16"),
Genre("Girls Love", "17"), Genre("Girls Love", "17"),
Genre("Boys Love", "18"), Genre("Boys Love", "18"),
Genre("Harem", "19"), Genre("Harem", "19"),
Genre("Mecha", "20"), Genre("Mecha", "20"),
Genre("Supervivencia", "21"), Genre("Supervivencia", "21"),
Genre("Reencarnación", "22"), Genre("Reencarnación", "22"),
Genre("Gore", "23"), Genre("Gore", "23"),
Genre("Apocalíptico", "24"), Genre("Apocalíptico", "24"),
Genre("Tragedia", "25"), Genre("Tragedia", "25"),
Genre("Vida Escolar", "26"), Genre("Vida Escolar", "26"),
Genre("Historia", "27"), Genre("Historia", "27"),
Genre("Militar", "28"), Genre("Militar", "28"),
Genre("Policiaco", "29"), Genre("Policiaco", "29"),
Genre("Crimen", "30"), Genre("Crimen", "30"),
Genre("Superpoderes", "31"), Genre("Superpoderes", "31"),
Genre("Vampiros", "32"), Genre("Vampiros", "32"),
Genre("Artes Marciales", "33"), Genre("Artes Marciales", "33"),
Genre("Samurái", "34"), Genre("Samurái", "34"),
Genre("Género Bender", "35"), Genre("Género Bender", "35"),
Genre("Realidad Virtual", "36"), Genre("Realidad Virtual", "36"),
Genre("Ciberpunk", "37"), Genre("Ciberpunk", "37"),
Genre("Musica", "38"), Genre("Musica", "38"),
Genre("Parodia", "39"), Genre("Parodia", "39"),
Genre("Animación", "40"), Genre("Animación", "40"),
Genre("Demonios", "41"), Genre("Demonios", "41"),
Genre("Familia", "42"), Genre("Familia", "42"),
Genre("Extranjero", "43"), Genre("Extranjero", "43"),
Genre("Niños", "44"), Genre("Niños", "44"),
Genre("Realidad", "45"), Genre("Realidad", "45"),
Genre("Telenovela", "46"), Genre("Telenovela", "46"),
Genre("Guerra", "47"), Genre("Guerra", "47"),
Genre("Oeste", "48") Genre("Oeste", "48")
) )
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) : private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) { Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second fun toUriPart() = vals[state].second
} }
internal interface JSIn {
fun call(b: Boolean, data: String): String
}
} }