Simply Hentai - new extension (#1838)

This commit is contained in:
Mike 2019-11-30 17:07:54 -05:00 committed by arkon
parent b6c602545c
commit 1e041cd669
9 changed files with 245 additions and 0 deletions

View File

@ -0,0 +1,17 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: Simply Hentai'
pkgNameSuffix = 'all.simplyhentai'
extClass = '.SimplyHentaiFactory'
extVersionCode = 1
libVersion = '1.2'
}
dependencies {
compileOnly 'com.google.code.gson:gson:2.8.2'
compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -0,0 +1,199 @@
package eu.kanade.tachiyomi.extension.all.simplyhentai
import com.github.salomonbrys.kotson.forEach
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
abstract class SimplyHentai (
override val lang: String,
private val urlLang: String,
private val searchLang: String
) : ParsedHttpSource() {
override val name = "Simply Hentai"
override val baseUrl = "https://www.simply-hentai.com"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val gson = Gson()
// Popular
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/album/language/$urlLang/$page/popularity/desc", headers)
}
override fun popularMangaSelector() = "div.col-sm-3"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
element.select("h3.object-title a").let {
manga.url = it.attr("href").substringAfter(baseUrl)
manga.title = it.text()
}
manga.thumbnail_url = element.select("img.img-responsive").attr("abs:data-src")
return manga
}
override fun popularMangaNextPageSelector() = "a[rel=next]"
// Latest
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/album/language/$urlLang/$page", headers)
}
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = HttpUrl.parse("$baseUrl/search")!!.newBuilder()
.addQueryParameter("query", query)
.addQueryParameter("language_ids[$searchLang]", searchLang)
.addQueryParameter("page", page.toString())
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is GenreList -> {
filter.state.forEach {
if (it.state) url.addQueryParameter("tag_ids[${it.id}]", it.id)
}
}
is SeriesList -> {
filter.state.forEach {
if (it.state) url.addQueryParameter("series_id[${it.id}]", it.id)
}
}
is SortOrder -> {
url.addQueryParameter("sort", getSortOrder()[filter.state].second)
}
}
}
return GET(url.toString(), headers)
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
// Details
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
document.select("div.padding-md-right-8").let { info ->
manga.artist = info.select("div.box-title:contains(Artists) + a").text()
manga.author = manga.artist
manga.genre = info.select("a[rel=tag]").joinToString { it.text() }
manga.description = info.select("div.link-box > div.box-title:contains(Series) ~ a").let { e ->
if (e.text().isNotEmpty()) "Series: ${e.joinToString { it.text() }}\n\n" else ""
}
manga.description += info.select("div.link-box > div.box-title:contains(Characters) ~ a").let { e ->
if (e.text().isNotEmpty()) ("Characters: ${e.joinToString { it.text() }}\n\n") else ""
}
}
manga.thumbnail_url = document.select("div.col-xs-12 img.img-responsive").attr("abs:data-src")
return manga
}
// Chapters
override fun chapterListParse(response: Response): List<SChapter> {
val chapter = SChapter.create().apply {
name = "Chapter"
url = response.request().url().toString().removeSuffix("/").substringAfterLast("/")
}
return listOf(chapter)
}
override fun chapterListSelector() = "not used"
override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used")
// Pages
override fun pageListRequest(chapter: SChapter): Request {
return GET("https://api.simply-hentai.com/v1/images/album/${chapter.url}", headersBuilder().add("X-Requested-With", "XMLHttpRequest").build())
}
override fun pageListParse(response: Response): List<Page> {
val pages = mutableListOf<Page>()
gson.fromJson<JsonObject>(response.body()!!.string()).forEach { _, jsonElement ->
pages.add(Page(pages.size, "", jsonElement["sizes"]["full"].string))
}
return pages
}
override fun pageListParse(document: Document): List<Page> = throw UnsupportedOperationException("Not used")
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
// Filters
private class SortOrder(sortPairs: List<Pair<String, String>>): Filter.Select<String>("Sort By", sortPairs.map { it.first }.toTypedArray())
private class SearchPair(name: String, val id: String = name) : Filter.CheckBox(name)
private class GenreList(searchVal: List<SearchPair>) : Filter.Group<SearchPair>("Genres", searchVal)
private class SeriesList(searchVal: List<SearchPair>) : Filter.Group<SearchPair>("Series", searchVal)
override fun getFilterList() = FilterList(
SortOrder(getSortOrder()),
GenreList(getGenreList()),
SeriesList(getSeriesList())
)
// "Relevance" should be empty, don't add a "Views" sort order
private fun getSortOrder() = listOf(
Pair("Relevance", ""),
Pair("Popularity", "sort_value"),
Pair("Upload Date", "created_at")
)
// TODO: add more to getGenreList and getSeriesList
private fun getGenreList() = listOf(
SearchPair("Solo Female", "4807"),
SearchPair("Solo Male", "4805"),
SearchPair("Big Breasts", "2528"),
SearchPair("Nakadashi", "2418"),
SearchPair("Blowjob", "64"),
SearchPair("Schoolgirl Uniform", "2522"),
SearchPair("Stockings", "33")
)
private fun getSeriesList() = listOf(
SearchPair("Original Work", "1093"),
SearchPair("Kantai Collection", "1316"),
SearchPair("Touhou", "747"),
SearchPair("Fate Grand Order", "2171"),
SearchPair("Idolmaster", "306"),
SearchPair("Granblue Fantasy", "2041"),
SearchPair("Girls Und Panzer", "1324")
)
}

View File

@ -0,0 +1,29 @@
package eu.kanade.tachiyomi.extension.all.simplyhentai
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class SimplyHentaiFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
SimplyHentaiEN(),
SimplyHentaiJA(),
SimplyHentaiZH(),
SimplyHentaiKO(),
SimplyHentaiES(),
SimplyHentaiRU(),
SimplyHentaiFR(),
SimplyHentaiDE(),
SimplyHentaiPT()
)
}
class SimplyHentaiEN: SimplyHentai("en", "english", "1")
class SimplyHentaiJA: SimplyHentai("ja", "japanese", "2")
class SimplyHentaiZH: SimplyHentai("zh", "chinese", "11")
class SimplyHentaiKO: SimplyHentai("ko", "korean", "9")
class SimplyHentaiES: SimplyHentai("es", "spanish", "8")
class SimplyHentaiRU: SimplyHentai("ru", "russian", "7")
class SimplyHentaiFR: SimplyHentai("fr", "french", "3")
class SimplyHentaiDE: SimplyHentai("de", "german", "4")
class SimplyHentaiPT: SimplyHentai("pt", "portuguese", "6")