Rawkuma: move to WPMangaStream (closes #4509)

This commit is contained in:
arkon 2020-10-24 18:03:13 -04:00
parent bb131790df
commit 6e3141397a
10 changed files with 5 additions and 304 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'WP MangaStream (multiple sources)'
pkgNameSuffix = 'all.wpmangastream'
extClass = '.WPMangaStreamFactory'
extVersionCode = 29
extVersionCode = 30
libVersion = '1.2'
}

View File

@ -53,10 +53,13 @@ class WPMangaStreamFactory : SourceFactory {
SilenceScan(),
SheaManga(),
FlameScans(),
GURUKomik()
GURUKomik(),
Rawkuma()
)
}
class Rawkuma : WPMangaStream("Rawkuma", "https://rawkuma.com/", "ja")
class GURUKomik : WPMangaStream("GURU Komik", "https://gurukomik.com", "id", SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("id")))
class FlameScans : WPMangaStream("Flame Scans", "https://www.flame-scans.com", "en")

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Rawkuma'
pkgNameSuffix = 'ja.rawkuma'
extClass = '.Rawkuma'
extVersionCode = 3
libVersion = '1.2'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

View File

@ -1,290 +0,0 @@
package eu.kanade.tachiyomi.extension.ja.rawkuma
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
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.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale
class Rawkuma : ParsedHttpSource() {
override val name = "Rawkuma"
override val baseUrl = "https://rawkuma.com"
override val lang = "ja"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
override fun latestUpdatesSelector() = "div.bsx a"
override fun latestUpdatesRequest(page: Int): Request {
// The site redirects page 1 -> url-without-page so we do this redirect early for optimization
val builtUrl = if (page == 1) "$baseUrl/manga/?order=update" else "$baseUrl/manga/page/$page/?order=update"
return GET(builtUrl)
}
override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create()
val imgurl = element.select("img").attr("src").substringBefore("?resize")
manga.url = element.attr("href")
manga.title = element.attr("title")
manga.thumbnail_url = imgurl
return manga
}
override fun latestUpdatesNextPageSelector() = "a.next"
override fun popularMangaRequest(page: Int): Request {
val builtUrl = if (page == 1) "$baseUrl/manga/?order=popular" else "$baseUrl/manga/page/$page/?order=popular"
return GET(builtUrl)
}
override fun popularMangaSelector() = latestUpdatesSelector()
override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element)
override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val builtUrl = if (page == 1) "$baseUrl/manga/" else "$baseUrl/manga/page/$page/"
val url = HttpUrl.parse(builtUrl)!!.newBuilder()
url.addQueryParameter("title", query)
url.addQueryParameter("page", page.toString())
filters.forEach { filter ->
when (filter) {
is AuthorFilter -> {
url.addQueryParameter("author", filter.state)
}
is YearFilter -> {
url.addQueryParameter("yearx", filter.state)
}
is StatusFilter -> {
val status = when (filter.state) {
Filter.TriState.STATE_INCLUDE -> "completed"
Filter.TriState.STATE_EXCLUDE -> "ongoing"
else -> ""
}
url.addQueryParameter("status", status)
}
is TypeFilter -> {
url.addQueryParameter("type", filter.toUriPart())
}
is OrderByFilter -> {
url.addQueryParameter("order", filter.toUriPart())
}
is GenreList -> {
filter.state
.filter { it.state != Filter.TriState.STATE_IGNORE }
.forEach { url.addQueryParameter("genre[]", it.id) }
}
}
}
return GET(url.build().toString(), headers)
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector()
override fun mangaDetailsRequest(manga: SManga): Request {
if (manga.url.startsWith("http")) {
return GET(manga.url, headers)
}
return super.mangaDetailsRequest(manga)
}
override fun mangaDetailsParse(document: Document): SManga {
val stringBuilder = StringBuilder()
val infoElement = document.select("div.infox")
val author = infoElement.select("span:contains(author)").text().substringAfter("Author: ")
val manga = SManga.create()
val genres = mutableListOf<String>()
val status = infoElement.select("span:contains(Status)").text()
val desc = infoElement.select("span.desc p")
infoElement.select("span:contains(Genres) a").forEach { element ->
val genre = element.text()
genres.add(genre)
}
if (desc.size > 0) {
desc.forEach {
stringBuilder.append(it.text())
if (it != desc.last())
stringBuilder.append("\n\n")
}
manga.description = stringBuilder.toString()
} else
manga.description = infoElement.select("span.desc").text()
manga.title = infoElement.select("h1").text()
manga.author = author
manga.artist = author
manga.status = parseStatus(status)
manga.genre = genres.joinToString(", ")
manga.thumbnail_url = document.select("div.bigcontent img").attr("src")
return manga
}
private fun parseStatus(status: String?) = when {
status == null -> SManga.UNKNOWN
status.contains("Status: Ongoing") -> SManga.ONGOING
status.contains("Status: Completed") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun chapterListRequest(manga: SManga): Request {
if (manga.url.startsWith("http")) {
return GET(manga.url, headers)
}
return super.chapterListRequest(manga)
}
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val chapters = mutableListOf<SChapter>()
document.select(chapterListSelector()).map { chapters.add(chapterFromElement(it)) }
// Add date for latest chapter only
document.select("time[itemprop=dateModified]").text()
.let {
chapters[0].date_upload = parseDate(it)
}
return chapters
}
private fun parseDate(date: String): Long {
return try {
SimpleDateFormat("MMM dd, yyyy", Locale.US).parse(date)?.time ?: 0L
} catch (e: ParseException) {
0L
}
}
override fun chapterListSelector() = ".lchx"
override fun chapterFromElement(element: Element): SChapter {
val urlElement = element.select("a")
val chapter = SChapter.create()
chapter.url = urlElement.attr("href")
chapter.name = urlElement.text()
return chapter
}
override fun pageListRequest(chapter: SChapter): Request {
if (chapter.url.startsWith("http")) {
return GET(chapter.url, headers)
}
return super.pageListRequest(chapter)
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("div#readerarea img").forEach {
val url = it.attr("src")
pages.add(Page(pages.size, "", url))
}
return pages
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
override fun getFilterList() = FilterList(
Filter.Header("You can combine filter."),
Filter.Separator(),
AuthorFilter(),
YearFilter(),
StatusFilter(),
TypeFilter(),
OrderByFilter(),
GenreList(getGenreList())
)
private class AuthorFilter : Filter.Text("Author")
private class YearFilter : Filter.Text("Year")
private class StatusFilter : Filter.TriState("Completed")
private class TypeFilter : UriPartFilter(
"Type",
arrayOf(
Pair("All", ""),
Pair("Manga", "Manga"),
Pair("Manhwa", "Manhwa"),
Pair("Manhua", "Manhua")
)
)
private class OrderByFilter : UriPartFilter(
"Order By",
arrayOf(
Pair("<select>", ""),
Pair("A-Z", "title"),
Pair("Z-A", "titlereverse"),
Pair("Latest Update", "update"),
Pair("Latest Added", "latest"),
Pair("Popular", "popular")
)
)
private fun getGenreList() = listOf(
Tag("action", "Action"),
Tag("adult", "Adult"),
Tag("adventure", "Adventure"),
Tag("blood", "Blood"),
Tag("comedy", "Comedy"),
Tag("drama", "Drama"),
Tag("ecchi", "Ecchi"),
Tag("fanta", "Fanta"),
Tag("fantasy", "Fantasy"),
Tag("gender-bender", "Gender Bender"),
Tag("harem", "Harem"),
Tag("historical", "Historical"),
Tag("horror", "Horror"),
Tag("isekai", "Isekai"),
Tag("josei", "Josei"),
Tag("lolicon", "Lolicon"),
Tag("martial-arts", "Martial Arts"),
Tag("mature", "Mature"),
Tag("mecha", "Mecha"),
Tag("mystery", "Mystery"),
Tag("parody", "Parody"),
Tag("psychological", "Psychological"),
Tag("romance", "Romance"),
Tag("school-life", "School Life"),
Tag("sci-fi", "Sci-fi"),
Tag("seinen", "Seinen"),
Tag("shoujo", "Shoujo"),
Tag("shoujo-ai", "Shoujo Ai"),
Tag("shounen", "Shounen"),
Tag("slice-of-life", "Slice of Life"),
Tag("sports", "Sports"),
Tag("supernatural", "Supernatural"),
Tag("thriller", "Thriller"),
Tag("tragedy", "Tragedy"),
Tag("yuri", "Yuri")
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
private class Tag(val id: String, name: String) : Filter.TriState(name)
private class GenreList(genres: List<Tag>) : Filter.Group<Tag>("Genres", genres)
}