Myreadingmanga - add more languages (#3088)

* working

* integration

* cleanup, finalization
This commit is contained in:
Mike 2020-05-10 17:47:38 -04:00 committed by GitHub
parent 99ee039d09
commit a6c2ed3aae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 137 deletions

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: MyReadingManga' appName = 'Tachiyomi: MyReadingManga'
pkgNameSuffix = 'all.myreadingmanga' pkgNameSuffix = 'all.myreadingmanga'
extClass = '.MyReadingMangaFactory' extClass = '.MyReadingMangaFactory'
extVersionCode = 33 extVersionCode = 34
libVersion = '1.2' libVersion = '1.2'
} }

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.extension.all.myreadingmanga package eu.kanade.tachiyomi.extension.all.myreadingmanga
import android.annotation.SuppressLint
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
@ -22,11 +23,11 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
open class MyReadingManga(override val lang: String) : ParsedHttpSource() { open class MyReadingManga(override val lang: String, private val siteLang: String, private val latestLang: String) : ParsedHttpSource() {
// Basic Info // Basic Info
override val name = "MyReadingManga" override val name = "MyReadingManga"
override val baseUrl = "https://myreadingmanga.info" final override val baseUrl = "https://myreadingmanga.info"
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(1, TimeUnit.MINUTES) .connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES) .readTimeout(1, TimeUnit.MINUTES)
@ -37,106 +38,57 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
// Popular - Random // Popular - Random
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/search/?wpsolr_sort=sort_by_random&wpsolr_page=$page", headers) // Random Manga as returned by search return GET("$baseUrl/search/?wpsolr_sort=sort_by_random&wpsolr_page=$page&wpsolr_fq[0]=lang_str:$siteLang", headers) // Random Manga as returned by search
} }
override fun popularMangaNextPageSelector() = searchMangaNextPageSelector() override fun popularMangaParse(response: Response): MangasPage {
override fun popularMangaSelector() = searchMangaSelector() if (!filtersCached) {
override fun popularMangaParse(response: Response) = searchMangaParse(response) cachedPagesUrls.onEach { filterAssist(it.value) }
override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element) filtersCached = true
}
return searchMangaParse(response)
}
override fun popularMangaNextPageSelector() = throw Exception("Not used")
override fun popularMangaSelector() = throw Exception("Not used")
override fun popularMangaFromElement(element: Element) = throw Exception("Not used")
// Latest // Latest
@SuppressLint("DefaultLocale")
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/page/$page/", headers) // Home Page - Latest Manga return GET("$baseUrl/lang/${latestLang.toLowerCase()}" + if (page > 1) "/page/$page/" else "", headers) // Home Page - Latest Manga
} }
override fun latestUpdatesNextPageSelector() = "li.pagination-next" override fun latestUpdatesNextPageSelector() = "li.pagination-next"
override fun latestUpdatesSelector() = "article" override fun latestUpdatesSelector() = "article"
override fun latestUpdatesParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = mutableListOf<SManga>()
val list = document.select(latestUpdatesSelector()).filter { element ->
val select = element.select("a[rel=bookmark]")
select.text().contains("[$lang", true)
}
for (element in list) {
mangas.add(latestUpdatesFromElement(element))
}
val hasNextPage = latestUpdatesNextPageSelector().let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
}
override fun latestUpdatesFromElement(element: Element) = buildManga(element.select("a[rel]").first(), element.select("a.entry-image-link img").first()) override fun latestUpdatesFromElement(element: Element) = buildManga(element.select("a[rel]").first(), element.select("a.entry-image-link img").first())
// Search // Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val uri = if (query.isNotBlank()) { var fqIndex = 0
Uri.parse("$baseUrl/search/").buildUpon() val uri = Uri.parse("$baseUrl/search/").buildUpon()
.appendQueryParameter("search", query) .appendQueryParameter("wpsolr_q", query)
.appendQueryParameter("wpsolr_page", page.toString()) .appendQueryParameter("wpsolr_fq[$fqIndex]", "lang_str:$siteLang")
} else { .appendQueryParameter("wpsolr_page", page.toString())
val uri = Uri.parse("$baseUrl/").buildUpon() filters.forEach {
// Append uri filters if (it is UriFilter) {
filters.forEach { fqIndex++
if (it is UriFilter) it.addToUri(uri, "wpsolr_fq[$fqIndex]")
it.addToUri(uri)
} }
uri.appendPath("page").appendPath("$page")
} }
return GET(uri.toString(), headers) return GET(uri.toString(), headers)
} }
override fun searchMangaNextPageSelector(): String? = null override fun searchMangaNextPageSelector(): String? = throw Exception("Not used")
override fun searchMangaSelector() = "div.results-by-facets div[id*=res]" override fun searchMangaSelector() = "div.results-by-facets div[id*=res]"
private var mangaParsedSoFar = 0
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
// Filter Assist - Caches Pages required for filter parsing
if (!filtersCached) {
filterAssist(baseUrl)
filterAssist("$baseUrl/cats/")
filterAssist("$baseUrl/pairing/")
filterAssist("$baseUrl/group/")
filtersCached = true
}
val document = response.asJsoup() val document = response.asJsoup()
val mangas = mutableListOf<SManga>() if (document.location().contains("page=1")) mangaParsedSoFar = 0
// Process Search Results val mangas = document.select(searchMangaSelector()).map { searchMangaFromElement(it) }
if (document.baseUri().contains("search", true)) { .also { mangaParsedSoFar += it.count() }
val elements = document.select(searchMangaSelector()) val totalResults = Regex("""(\d+)""").find(document.select("div.res_info").text())?.groupValues?.get(1)?.toIntOrNull() ?: 0
for (element in elements) { return MangasPage(mangas, mangaParsedSoFar < totalResults)
if (element.text().contains("[$lang", true)) {
mangas.add(searchMangaFromElement(element))
}
}
val hasNextPage = searchMangaSelector().let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
}
// Process Filter Results / Same theme as home page
else {
// return popularMangaParse(response)
val list = document.select(latestUpdatesSelector()).filter { element ->
val select = element.select("a[rel=bookmark]")
select.text().contains("[$lang", true)
}
for (element in list) {
mangas.add(latestUpdatesFromElement(element))
}
val hasNextPage = latestUpdatesNextPageSelector().let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
}
} }
override fun searchMangaFromElement(element: Element) = buildManga(element.select("a").first(), element.select("img")?.first()) override fun searchMangaFromElement(element: Element) = buildManga(element.select("a").first(), element.select("img")?.first())
// Build Manga From Element // Build Manga From Element
@ -184,8 +136,7 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
val manga = SManga.create() val manga = SManga.create()
manga.author = cleanAuthor(document.select("h1").text()) manga.author = cleanAuthor(document.select("h1").text())
manga.artist = cleanAuthor(document.select("h1").text()) manga.artist = cleanAuthor(document.select("h1").text())
val glist = document.select(".entry-header p a[href*=genre]").map { it.text() } manga.genre = document.select(".entry-header p a[href*=genre]").joinToString(", ") { it.text() }
manga.genre = glist.joinToString(", ")
val extendedDescription = document.select(".entry-content p:not(p:containsOwn(|)):not(.chapter-class + p)")?.joinToString("\n") { it.text() } val extendedDescription = document.select(".entry-content p:not(p:containsOwn(|)):not(.chapter-class + p)")?.joinToString("\n") { it.text() }
manga.description = document.select("h1").text() + if (extendedDescription.isNullOrEmpty()) "" else "\n\n$extendedDescription" manga.description = document.select("h1").text() + if (extendedDescription.isNullOrEmpty()) "" else "\n\n$extendedDescription"
manga.status = when (document.select("a[href*=status]")?.first()?.text()) { manga.status = when (document.select("a[href*=status]")?.first()?.text()) {
@ -207,6 +158,7 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
// Start Chapter Get // Start Chapter Get
override fun chapterListSelector() = ".entry-pagination a" override fun chapterListSelector() = ".entry-pagination a"
@SuppressLint("DefaultLocale")
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup() val document = response.asJsoup()
val chapters = mutableListOf<SChapter>() val chapters = mutableListOf<SChapter>()
@ -285,63 +237,53 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
} }
// Parses page for filter // Parses page for filter
private fun returnFilter(document: Document?, css: String, attributekey: String): Array<Pair<String, String>> { private fun returnFilter(document: Document?, css: String): Array<String> {
return document?.select(css)?.map { Pair(it.attr(attributekey).substringBeforeLast("/").substringAfterLast("/"), it.text()) }?.toTypedArray() return document?.select(css)?.map { it.text() }?.toTypedArray()
?: arrayOf(Pair("", "Press 'Reset' to try again")) ?: arrayOf("Press 'Reset' to try again")
} }
// URLs for the pages we need to cache
private val cachedPagesUrls = hashMapOf(
Pair("genres", baseUrl),
Pair("tags", baseUrl),
Pair("categories", "$baseUrl/cats/"),
Pair("pairings", "$baseUrl/pairing/"),
Pair("groups", "$baseUrl/group/")
)
// Generates the filter lists for app // Generates the filter lists for app
override fun getFilterList(): FilterList { override fun getFilterList(): FilterList {
return FilterList( return FilterList(
// MRM does not support genre filtering and text search at the same time GenreFilter(returnFilter(getCache(cachedPagesUrls["genres"]!!), ".tagcloud a[href*=/genre/]")),
Filter.Header("NOTE: Filters are ignored if using text search."), TagFilter(returnFilter(getCache(cachedPagesUrls["tags"]!!), ".tagcloud a[href*=/tag/]")),
Filter.Header("Only one filter can be used at a time."), CatFilter(returnFilter(getCache(cachedPagesUrls["categories"]!!), ".links a")),
GenreFilter(returnFilter(getCache(baseUrl), ".tagcloud a[href*=/genre/]", "href")), PairingFilter(returnFilter(getCache(cachedPagesUrls["pairings"]!!), ".links a")),
TagFilter(returnFilter(getCache(baseUrl), ".tagcloud a[href*=/tag/]", "href")), ScanGroupFilter(returnFilter(getCache(cachedPagesUrls["groups"]!!), ".links a"))
CatFilter(returnFilter(getCache("$baseUrl/cats/"), ".links a", "abs:href")),
PairingFilter(returnFilter(getCache("$baseUrl/pairing/"), ".links a", "abs:href")),
ScanGroupFilter(returnFilter(getCache("$baseUrl/group/"), ".links a", "abs:href"))
) )
} }
private class GenreFilter(GENRES: Array<Pair<String, String>>) : UriSelectFilterPath("Genre", "genre", arrayOf(Pair("", "Any"), *GENRES)) private class GenreFilter(GENRES: Array<String>) : UriSelectFilter("Genre", "genre_str", arrayOf("Any", *GENRES))
private class TagFilter(POPTAG: Array<Pair<String, String>>) : UriSelectFilterPath("Popular Tags", "tag", arrayOf(Pair("", "Any"), *POPTAG)) private class TagFilter(POPTAG: Array<String>) : UriSelectFilter("Popular Tags", "tags", arrayOf("Any", *POPTAG))
private class CatFilter(CATID: Array<Pair<String, String>>) : UriSelectFilterShortPath("Categories", "cat", arrayOf(Pair("", "Any"), *CATID)) private class CatFilter(CATID: Array<String>) : UriSelectFilter("Categories", "categories", arrayOf("Any", *CATID))
private class PairingFilter(PAIR: Array<Pair<String, String>>) : UriSelectFilterPath("Pairing", "pairing", arrayOf(Pair("", "Any"), *PAIR)) private class PairingFilter(PAIR: Array<String>) : UriSelectFilter("Pairing", "pairing_str", arrayOf("Any", *PAIR))
private class ScanGroupFilter(GROUP: Array<Pair<String, String>>) : UriSelectFilterPath("Scanlation Group", "group", arrayOf(Pair("", "Any"), *GROUP)) private class ScanGroupFilter(GROUP: Array<String>) : UriSelectFilter("Scanlation Group", "group_str", arrayOf("Any", *GROUP))
/** /**
* Class that creates a select filter. Each entry in the dropdown has a name and a display name. * Class that creates a select filter. Each entry in the dropdown has a name and a display name.
* If an entry is selected it is appended as a query parameter onto the end of the URI. * If an entry is selected it is appended as a query parameter onto the end of the URI.
* If `firstIsUnspecified` is set to true, if the first entry is selected, nothing will be appended on the the URI. * If `firstIsUnspecified` is set to true, if the first entry is selected, nothing will be appended on the the URI.
*/ */
// vals: <name, display> private open class UriSelectFilter(
private open class UriSelectFilterPath(
displayName: String, displayName: String,
val uriParam: String, val uriValuePrefix: String,
val vals: Array<Pair<String, String>>, val vals: Array<String>,
val firstIsUnspecified: Boolean = true, val firstIsUnspecified: Boolean = true,
defaultValue: Int = 0 defaultValue: Int = 0
) : ) :
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter { Filter.Select<String>(displayName, vals.map { it }.toTypedArray(), defaultValue), UriFilter {
override fun addToUri(uri: Uri.Builder) { override fun addToUri(uri: Uri.Builder, uriParam: String) {
if (state != 0 || !firstIsUnspecified) if (state != 0 || !firstIsUnspecified)
uri.appendPath(uriParam) uri.appendQueryParameter(uriParam, "$uriValuePrefix:${vals[state]}")
.appendPath(vals[state].first)
}
}
private open class UriSelectFilterShortPath(
displayName: String,
val uriParam: String,
val vals: Array<Pair<String, String>>,
val firstIsUnspecified: Boolean = true,
defaultValue: Int = 0
) :
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter {
override fun addToUri(uri: Uri.Builder) {
if (state != 0 || !firstIsUnspecified)
uri.appendPath(vals[state].first)
} }
} }
@ -349,6 +291,6 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
* Represents a filter that is able to modify a URI. * Represents a filter that is able to modify a URI.
*/ */
private interface UriFilter { private interface UriFilter {
fun addToUri(uri: Uri.Builder) fun addToUri(uri: Uri.Builder, uriParam: String)
} }
} }

View File

@ -3,9 +3,43 @@ package eu.kanade.tachiyomi.extension.all.myreadingmanga
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.SourceFactory
/**
*
*/
class MyReadingMangaFactory : SourceFactory { class MyReadingMangaFactory : SourceFactory {
override fun createSources(): List<Source> = getAllMyReadingMangaLanguages() override fun createSources(): List<Source> = languageList.map { MyReadingManga(it.tachiLang, it.siteLang, it.latestLang) }
} }
private data class Source(val tachiLang: String, val siteLang: String, val latestLang: String = siteLang)
// These should all be valid. Add a language code and uncomment to enable
private val languageList = listOf(
Source("ar", "Arabic"),
// Source("", "Bahasa"),
Source("id", "Indonesia"),
// Source("", "Bulgarian"),
Source("zh", "Chinese"),
// Source("", "Croatian"),
// Source("", "Czech"),
Source("en", "English"),
// Source("", "Filipino"),
// Source("", "Finnish"),
// Source("", "Flemish", "flemish-dutch"),
// Source("", "Dutch"),
Source("fr", "French"),
Source("de", "German"),
// Source("", "Greek"),
// Source("", "Hebrew"),
// Source("", "Hindi"),
// Source("", "Hungarian"),
Source("it", "Italian"),
Source("ja", "Japanese", "jp"),
Source("ko", "Korean"),
// Source("", "Polish"),
Source("pt-BR", "Portuguese"),
// Source("", "Romanian"),
Source("ru", "Russian"),
// Source("", "Slovak"),
Source("es", "Spanish"),
// Source("", "Swedish"),
// Source("", "Thai"),
Source("tr", "Turkish"),
Source("vi", "Vietnamese")
)

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.all.myreadingmanga
/**
* MyReadingManga languages
*/
class MyReadingMangaEnglish : MyReadingManga("en")
fun getAllMyReadingMangaLanguages() = listOf(
MyReadingMangaEnglish()
)