NineHentai: Tags, Sorting, more manga details (#981)

This commit is contained in:
krogon500 2019-03-30 03:35:17 +05:00 committed by Eugene
parent 3014f5b3f6
commit fc3a2bf425
4 changed files with 1650 additions and 93 deletions

View File

@ -5,11 +5,11 @@ ext {
appName = 'Tachiyomi: NineHentai' appName = 'Tachiyomi: NineHentai'
pkgNameSuffix = 'all.ninehentai' pkgNameSuffix = 'all.ninehentai'
extClass = '.NineHentai' extClass = '.NineHentai'
extVersionCode = 3 extVersionCode = 4
libVersion = '1.2' libVersion = '1.2'
} }
dependencies { dependencies {
compileOnly 'com.google.code.gson:gson:2.8.2' compileOnly 'com.google.code.gson:gson:2.8.5'
compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0' compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0'
compileOnly 'com.github.inorichi.injekt:injekt-core:65b0440' compileOnly 'com.github.inorichi.injekt:injekt-core:65b0440'
} }

View File

@ -1,5 +1,32 @@
package eu.kanade.tachiyomi.extension.all.ninehentai package eu.kanade.tachiyomi.extension.all.ninehentai
data class id( import eu.kanade.tachiyomi.source.model.Filter
val id : Int
data class Manga(
val id : Int,
var title: String,
val image_server: String,
val tags: List<Tag>,
val total_page: Int
)
class Tag(
val id: Int,
name: String,
val description: String = "null",
val type: Int = 1
): Filter.TriState(name)
data class SearchRequest(
val text: String,
val page: Int,
val sort: Int,
val pages: Map<String, IntArray> = mapOf("range" to intArrayOf(0, 2000)),
val tag: Map<String, Items>
)
data class Items(
val included: MutableList<Tag>,
val excluded: MutableList<Tag>
) )

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
package eu.kanade.tachiyomi.extension.all.ninehentai package eu.kanade.tachiyomi.extension.all.ninehentai
import com.github.salomonbrys.kotson.get import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.int import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.string import com.github.salomonbrys.kotson.string
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
@ -15,7 +15,6 @@ import okhttp3.*
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import java.net.URLEncoder
import java.util.* import java.util.*
open class NineHentai : ParsedHttpSource() { open class NineHentai : ParsedHttpSource() {
@ -30,6 +29,8 @@ open class NineHentai : ParsedHttpSource() {
override val client: OkHttpClient = network.cloudflareClient override val client: OkHttpClient = network.cloudflareClient
private val gson = Gson()
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return POST(baseUrl + SEARCH_URL, headers, buildRequestBody(page = page, sort = 1)) return POST(baseUrl + SEARCH_URL, headers, buildRequestBody(page = page, sort = 1))
} }
@ -42,105 +43,92 @@ open class NineHentai : ParsedHttpSource() {
return client.newCall(popularMangaRequest(page)) return client.newCall(popularMangaRequest(page))
.asObservableSuccess() .asObservableSuccess()
.map { response -> .map { response ->
popularMangaParse(response) getMangaList(response, page)
} }
} }
override fun popularMangaParse(response: Response): MangasPage {
val list = getMangaList(response)
return MangasPage(list, list.isNotEmpty())
}
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> { override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
return client.newCall(latestUpdatesRequest(page)) return client.newCall(latestUpdatesRequest(page))
.asObservableSuccess() .asObservableSuccess()
.map { response -> .map { response ->
latestUpdatesParse(response) getMangaList(response, page)
} }
} }
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
private fun getMangaList(response: Response): List<SManga> {
val jsonData = response.body()!!.string()
val jsonObject = JsonParser().parse(jsonData).asJsonObject
val results = jsonObject.getAsJsonArray("results")
return parseSearch(results.toList())
}
private fun parseSearch(jsonArray: List<JsonElement>): List<SManga> {
val mutableList = mutableListOf<SManga>()
jsonArray.forEach { json ->
val manga = SManga.create()
val id = json["id"].string
manga.url = "$baseUrl/g/$id"
manga.title = json["title"].string
manga.thumbnail_url = json["image_server"].string + id + "/" + "cover.jpg"
mutableList.add(manga)
}
return mutableList
}
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.map { response ->
chapterListParse(response)
}
}
private fun getChapter(response: Response): SChapter {
val jsonData = response.body()!!.string()
val jsonObject = JsonParser().parse(jsonData).asJsonObject
val jsonArray = jsonObject.getAsJsonObject("results")
val sChapter = SChapter.create()
jsonArray.let { json ->
val id = json["id"].string
sChapter.url = "$baseUrl/g/$id"
sChapter.name = "chapter"
//api doesnt return date so setting to current date for now
sChapter.date_upload = Date().time
}
return sChapter
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return POST(baseUrl + SEARCH_URL, headers, buildRequestBody(query, page))
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return client.newCall(searchMangaRequest(page, query, filters)) return client.newCall(searchMangaRequest(page, query, filters))
.asObservableSuccess() .asObservableSuccess()
.map { response -> .map { response ->
searchMangaParse(response) getMangaList(response, page)
} }
} }
override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response) private fun getMangaList(response: Response, page: Int): MangasPage {
override fun mangaDetailsParse(response: Response): SManga {
val jsonData = response.body()!!.string() val jsonData = response.body()!!.string()
val jsonObject = JsonParser().parse(jsonData).asJsonObject val jsonObject = JsonParser().parse(jsonData).asJsonObject
val results = jsonObject.getAsJsonObject("results") val totalPages = jsonObject["total_count"].int
return parseSearch(listOf(results))[0] val results = jsonObject["results"].array
return MangasPage(parseSearch(results.toList()), page < totalPages)
} }
override fun chapterListParse(response: Response): List<SChapter> = listOf(getChapter(response)) private fun parseSearch(jsonArray: List<JsonElement>): List<SManga> {
val mutableList = mutableListOf<SManga>()
jsonArray.forEach { json ->
val manga = SManga.create()
val gsonManga = gson.fromJson<Manga>(json)
manga.url = "/g/${gsonManga.id}"
manga.title = gsonManga.title
manga.thumbnail_url = gsonManga.image_server + gsonManga.id + "/cover.jpg"
manga.genre = gsonManga.tags.filter { it.type == 1 }.joinToString { it.name }
manga.artist = gsonManga.tags.firstOrNull { it.type == 4 }?.name
manga.initialized = true
mutableList.add(manga)
}
return mutableList
}
override fun pageListParse(document: Document) = throw Exception("Not used") override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
val chapter = SChapter.create()
val chapterId = manga.url.substringAfter("/g/").toInt()
chapter.url = "/g/$chapterId"
chapter.name = "chapter"
//api doesnt return date so setting to current date for now
chapter.date_upload = Date().time
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { return Observable.just(listOf(chapter))
val mangaId = chapter.url.substringAfter("/g/").toInt() }
return client.newCall(POST(baseUrl + MANGA_URL, headers, buildIdBody(mangaId))) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
.asObservableSuccess() val includedTags = mutableListOf<Tag>()
.map { response -> val excludedTags = mutableListOf<Tag>()
pageListParse(response) var sort = 0
for (filter in if (filters.isEmpty()) getFilterList() else filters) {
when(filter){
is GenreList -> {
filter.state.forEach { f ->
if (!f.isIgnored()) {
if (f.isExcluded())
excludedTags.add(f)
else
includedTags.add(f)
}
}
} }
is Sorting -> {
sort = filter.state!!.index
}
}
}
return POST(baseUrl + SEARCH_URL, headers, buildRequestBody(query, page, sort, includedTags, excludedTags))
}
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return Observable.just(manga)
}
override fun pageListRequest(chapter: SChapter): Request {
val mangaId = chapter.url.substringAfter("/g/").toInt()
return POST(baseUrl + MANGA_URL, headers, buildIdBody(mangaId))
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
@ -152,38 +140,54 @@ open class NineHentai : ParsedHttpSource() {
var mangaId: String var mangaId: String
jsonArray.let { json -> jsonArray.let { json ->
mangaId = json["id"].string mangaId = json["id"].string
imageUrl = json["image_server"].string + mangaId + "/" imageUrl = json["image_server"].string + mangaId
totalPages = json["total_page"].int totalPages = json["total_page"].int
} }
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
for (i in 1..totalPages) { for (i in 1..totalPages) {
pages.add(Page(pages.size, "", "$imageUrl$i.jpg")) pages.add(Page(pages.size, "", "$imageUrl/$i.jpg"))
} }
return pages return pages
} }
private fun buildRequestBody(searchText: String = "", page: Int = 0, sort: Int = 0): RequestBody { private fun buildRequestBody(searchText: String = "", page: Int, sort: Int = 0, includedTags: MutableList<Tag> = arrayListOf(), excludedTags: MutableList<Tag> = arrayListOf()): RequestBody {
val gson = GsonBuilder().create() val json = gson.toJson(mapOf("search" to SearchRequest(text = searchText, page = page-1, sort = sort, tag = mapOf("items" to Items(includedTags, excludedTags)))))
val json = gson.toJson(mapOf("search" to mapOf("text" to searchText, "page" to page, "sort" to sort, "pages" to mapOf("range" to intArrayOf(0, 2000)), "tag" to mapOf("text" to "", "type" to 1, "tags" to arrayOf<String>(), "items" to mapOf("included" to arrayOf<String>(), "excluded" to arrayOf())))))
return RequestBody.create(MEDIA_TYPE, json) return RequestBody.create(MEDIA_TYPE, json)
} }
override fun mangaDetailsRequest(manga: SManga): Request { private fun buildIdBody(id: Int): RequestBody {
val id = manga.url.substringAfter("/g/").toInt() return RequestBody.create(MEDIA_TYPE, gson.toJson(mapOf("id" to id)))
return POST(baseUrl + MANGA_URL, headers, buildIdBody(id))
} }
private fun buildIdBody(id: Int): RequestBody { private class GenreList(tags: List<Tag>) : Filter.Group<Tag>("Tags", tags)
val dto = eu.kanade.tachiyomi.extension.all.ninehentai.id(id)
return RequestBody.create(MEDIA_TYPE, Gson().toJson(dto)) private class Sorting : Filter.Sort("Sorting",
} arrayOf("Newest", "Popular Rightnow", "Most Fapped", "Most Viewed", "By Title"),
Filter.Sort.Selection(1, false))
override fun getFilterList() = FilterList(
Sorting(),
GenreList(NHTags.getTagsList())
)
override fun imageUrlParse(document: Document): String = "" override fun imageUrlParse(document: Document): String = ""
override fun chapterListRequest(manga: SManga): Request = throw Exception("Not Used")
override fun popularMangaParse(response: Response): MangasPage = throw Exception("Not Used")
override fun latestUpdatesParse(response: Response): MangasPage = throw Exception("Not Used")
override fun searchMangaParse(response: Response): MangasPage = throw Exception("Not Used")
override fun chapterListParse(response: Response): List<SChapter> = throw Exception("Not Used")
override fun chapterFromElement(element: Element): SChapter = throw Exception("Not used") override fun chapterFromElement(element: Element): SChapter = throw Exception("Not used")
override fun pageListParse(document: Document) = throw Exception("Not used")
override fun chapterListSelector(): String = throw Exception("Not used") override fun chapterListSelector(): String = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element): SManga = throw Exception("Not used") override fun latestUpdatesFromElement(element: Element): SManga = throw Exception("Not used")
@ -192,6 +196,8 @@ open class NineHentai : ParsedHttpSource() {
override fun latestUpdatesSelector(): String = throw Exception("Not used") override fun latestUpdatesSelector(): String = throw Exception("Not used")
override fun mangaDetailsParse(response: Response): SManga = throw Exception("Not Used")
override fun mangaDetailsParse(document: Document): SManga = throw Exception("Not used") override fun mangaDetailsParse(document: Document): SManga = throw Exception("Not used")
override fun popularMangaFromElement(element: Element): SManga = throw Exception("Not used") override fun popularMangaFromElement(element: Element): SManga = throw Exception("Not used")