Nekopost extension: Search fix (HTTP error 520) (#18931)

* format the code and change the throw exception

* fix nekopost search

* switch to HttpSource
use request/parse instead of fetch

* Remove commented code.
This commit is contained in:
Taihenc 2023-11-15 23:27:39 +07:00 committed by GitHub
parent d044170507
commit 11a8fc6e5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 160 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Nekopost' extName = 'Nekopost'
pkgNameSuffix = 'th.nekopost' pkgNameSuffix = 'th.nekopost'
extClass = '.Nekopost' extClass = '.Nekopost'
extVersionCode = 9 extVersionCode = 10
isNsfw = true isNsfw = true
} }

View File

@ -2,38 +2,35 @@ package eu.kanade.tachiyomi.extension.th.nekopost
import eu.kanade.tachiyomi.extension.th.nekopost.model.RawChapterInfo import eu.kanade.tachiyomi.extension.th.nekopost.model.RawChapterInfo
import eu.kanade.tachiyomi.extension.th.nekopost.model.RawProjectInfo import eu.kanade.tachiyomi.extension.th.nekopost.model.RawProjectInfo
import eu.kanade.tachiyomi.extension.th.nekopost.model.RawProjectNameListItem import eu.kanade.tachiyomi.extension.th.nekopost.model.RawProjectSearchSummary
import eu.kanade.tachiyomi.extension.th.nekopost.model.RawProjectSummaryList import eu.kanade.tachiyomi.extension.th.nekopost.model.RawProjectSummaryList
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class Nekopost : ParsedHttpSource() { class Nekopost : HttpSource() {
private val json: Json by injectLazy() private val json: Json by injectLazy()
override val baseUrl: String = "https://www.nekopost.net/manga/" override val baseUrl: String = "https://www.nekopost.net/manga/"
private val latestMangaEndpoint: String = private val latestMangaEndpoint: String = "https://api.osemocphoto.com/frontAPI/getLatestChapter/m"
"https://api.osemocphoto.com/frontAPI/getLatestChapter/m" private val projectDataEndpoint: String = "https://api.osemocphoto.com/frontAPI/getProjectInfo"
private val projectDataEndpoint: String =
"https://api.osemocphoto.com/frontAPI/getProjectInfo"
private val fileHost: String = "https://www.osemocphoto.com" private val fileHost: String = "https://www.osemocphoto.com"
private val nekopostUrl = "https://www.nekopost.net"
override val client: OkHttpClient = network.cloudflareClient override val client: OkHttpClient = network.cloudflareClient
@ -57,35 +54,22 @@ class Nekopost : ParsedHttpSource() {
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
override fun latestUpdatesRequest(page: Int): Request = throw NotImplementedError("Unused") override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException("Not used.")
override fun latestUpdatesParse(response: Response): MangasPage = override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used.")
throw NotImplementedError("Unused")
override fun chapterListSelector(): String = throw NotImplementedError("Unused") override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used.")
override fun chapterFromElement(element: Element): SChapter = override fun imageUrlRequest(page: Page): Request = throw UnsupportedOperationException("Not used.")
throw NotImplementedError("Unused")
override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl) override fun mangaDetailsRequest(manga: SManga): Request {
return GET("$projectDataEndpoint/${manga.url}", headers)
}
override fun imageUrlParse(document: Document): String = throw NotImplementedError("Unused") override fun mangaDetailsParse(response: Response): SManga {
override fun latestUpdatesFromElement(element: Element): SManga = throw Exception("Unused")
override fun latestUpdatesNextPageSelector(): String = throw Exception("Unused")
override fun latestUpdatesSelector(): String = throw Exception("Unused")
override fun mangaDetailsParse(document: Document): SManga = throw NotImplementedError("Unused")
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(GET("$projectDataEndpoint/${manga.url}", headers))
.asObservableSuccess()
.map { response ->
val responseBody = response.body val responseBody = response.body
val projectInfo: RawProjectInfo = json.decodeFromString(responseBody.string()) val projectInfo: RawProjectInfo = json.decodeFromString(responseBody.string())
val manga = SManga.create()
manga.apply { manga.apply {
projectInfo.projectInfo.let { projectInfo.projectInfo.let {
url = it.projectId url = it.projectId
@ -94,8 +78,7 @@ class Nekopost : ParsedHttpSource() {
author = it.authorName author = it.authorName
description = it.info description = it.info
status = getStatus(it.status) status = getStatus(it.status)
thumbnail_url = thumbnail_url = "$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
"$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
initialized = true initialized = true
} }
@ -105,51 +88,47 @@ class Nekopost : ParsedHttpSource() {
"" ""
} }
} }
} return manga
} }
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { override fun chapterListRequest(manga: SManga): Request {
return if (manga.status != SManga.LICENSED) { val headers = Headers.headersOf("accept", "*/*", "content-type", "text/plain;charset=UTF-8", "origin", nekopostUrl)
client.newCall(GET("$projectDataEndpoint/${manga.url}", headers)) return GET("$projectDataEndpoint/${manga.url}", headers)
.asObservableSuccess() }
.map { response ->
val responseBody = response.body
val projectInfo: RawProjectInfo = json.decodeFromString(responseBody.string())
override fun chapterListParse(response: Response): List<SChapter> {
val responseBody = response.body.string()
val projectInfo: RawProjectInfo = json.decodeFromString(responseBody)
val manga = SManga.create()
manga.status = getStatus(projectInfo.projectInfo.status) manga.status = getStatus(projectInfo.projectInfo.status)
if (manga.status == SManga.LICENSED) { if (manga.status == SManga.LICENSED) {
throw Exception("Licensed - No chapter to show") throw Exception("Licensed - No chapter to show")
} }
projectInfo.projectChapterList!!.map { chapter -> return projectInfo.projectChapterList!!.map { chapter ->
SChapter.create().apply { SChapter.create().apply {
url = url = "${projectInfo.projectInfo.projectId.toInt()}/${chapter.chapterId}/${projectInfo.projectInfo.projectId.toInt()}_${chapter.chapterId}.json"
"${manga.url}/${chapter.chapterId}/${manga.url}_${chapter.chapterId}.json"
name = chapter.chapterName name = chapter.chapterName
date_upload = SimpleDateFormat( date_upload = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss",
Locale("th"), Locale("th"),
).parse(chapter.createDate)?.time ).parse(chapter.createDate)?.time ?: 0L
?: 0L
chapter_number = chapter.chapterNo.toFloat() chapter_number = chapter.chapterNo.toFloat()
scanlator = chapter.providerName scanlator = chapter.providerName
} }
} }
} }
} else {
Observable.error(Exception("Licensed - No chapter to show")) override fun pageListRequest(chapter: SChapter): Request {
} return GET("$fileHost/collectManga/${chapter.url}", headers)
} }
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { override fun pageListParse(response: Response): List<Page> {
return client.newCall(GET("$fileHost/collectManga/${chapter.url}", headers))
.asObservableSuccess()
.map { response ->
val responseBody = response.body val responseBody = response.body
val chapterInfo: RawChapterInfo = json.decodeFromString(responseBody.string()) val chapterInfo: RawChapterInfo = json.decodeFromString(responseBody.string())
chapterInfo.pageItem.map { page -> return chapterInfo.pageItem.map { page ->
val imgUrl: String = if (page.pageName != null) { val imgUrl: String = if (page.pageName != null) {
"$fileHost/collectManga/${chapterInfo.projectId}/${chapterInfo.chapterId}/${page.pageName}" "$fileHost/collectManga/${chapterInfo.projectId}/${chapterInfo.chapterId}/${page.pageName}"
} else { } else {
@ -161,10 +140,6 @@ class Nekopost : ParsedHttpSource() {
) )
} }
} }
}
override fun pageListParse(document: Document): List<Page> = throw NotImplementedError("Unused")
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
if (page <= 1) existingProject.clear() if (page <= 1) existingProject.clear()
// API has a bug that sometime it returns null on first page // API has a bug that sometime it returns null on first page
@ -176,14 +151,11 @@ class Nekopost : ParsedHttpSource() {
val projectList: RawProjectSummaryList = json.decodeFromString(responseBody.string()) val projectList: RawProjectSummaryList = json.decodeFromString(responseBody.string())
val mangaList: List<SManga> = if (projectList.listChapter != null) { val mangaList: List<SManga> = if (projectList.listChapter != null) {
projectList.listChapter projectList.listChapter.filter { !existingProject.contains(it.projectId) }.map {
.filter { !existingProject.contains(it.projectId) }
.map {
SManga.create().apply { SManga.create().apply {
url = it.projectId url = it.projectId
title = it.projectName title = it.projectName
thumbnail_url = thumbnail_url = "$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
"$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
initialized = false initialized = false
status = 0 status = 0
} }
@ -198,51 +170,27 @@ class Nekopost : ParsedHttpSource() {
return MangasPage(mangaList, hasNextPage = true) return MangasPage(mangaList, hasNextPage = true)
} }
override fun popularMangaFromElement(element: Element): SManga = override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
throw NotImplementedError("Unused") val headers = Headers.headersOf("accept", "*/*", "content-type", "text/plain;charset=UTF-8", "origin", nekopostUrl)
val requestBody = "{\"keyword\":\"$query\"}".toRequestBody()
return POST("$nekopostUrl/api/explore/search", headers, requestBody)
}
override fun popularMangaNextPageSelector(): String = throw Exception("Unused") override fun searchMangaParse(response: Response): MangasPage {
val responseBody = response.body.string()
override fun popularMangaSelector(): String = throw Exception("Unused")
override fun searchMangaFromElement(element: Element): SManga = throw Exception("Unused")
override fun searchMangaNextPageSelector(): String = throw Exception("Unused")
override fun fetchSearchManga(
page: Int,
query: String,
filters: FilterList,
): Observable<MangasPage> {
return client.newCall(GET("$fileHost/dataJson/dataProjectName.json"))
.asObservableSuccess()
.map { response ->
val responseBody = response.body
val projectList: List<RawProjectNameListItem> =
json.decodeFromString(responseBody.string())
val projectList: List<RawProjectSearchSummary> = json.decodeFromString(responseBody)
val mangaList: List<SManga> = projectList.filter { project -> val mangaList: List<SManga> = projectList.filter { project ->
Regex( project.projectType == "m"
query,
setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE),
).find(project.npName) != null
}.map { project -> }.map { project ->
SManga.create().apply { SManga.create().apply {
url = project.npProjectId url = project.projectId.toString()
title = project.npName title = project.projectName
status = getStatus(project.npStatus) status = project.status
initialized = false initialized = false
} }
} }
MangasPage(mangaList, false) return MangasPage(mangaList, false)
} }
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
throw Exception("Unused")
override fun searchMangaParse(response: Response): MangasPage = throw Exception("Unused")
override fun searchMangaSelector(): String = throw Exception("Unused")
}

View File

@ -0,0 +1,19 @@
package eu.kanade.tachiyomi.extension.th.nekopost.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class RawProjectSearchSummary(
val projectId: Int,
val projectName: String,
val projectType: String,
@SerialName("STATUS")
val status: Int,
val noChapter: Int,
val coverVersion: Int,
val info: String,
val views: Int,
@SerialName("lastUpdate")
val lastUpdateDate: String,
)