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'
pkgNameSuffix = 'th.nekopost'
extClass = '.Nekopost'
extVersionCode = 9
extVersionCode = 10
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.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.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.MangasPage
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.source.online.HttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
class Nekopost : ParsedHttpSource() {
class Nekopost : HttpSource() {
private val json: Json by injectLazy()
override val baseUrl: String = "https://www.nekopost.net/manga/"
private val latestMangaEndpoint: String =
"https://api.osemocphoto.com/frontAPI/getLatestChapter/m"
private val projectDataEndpoint: String =
"https://api.osemocphoto.com/frontAPI/getProjectInfo"
private val latestMangaEndpoint: String = "https://api.osemocphoto.com/frontAPI/getLatestChapter/m"
private val projectDataEndpoint: String = "https://api.osemocphoto.com/frontAPI/getProjectInfo"
private val fileHost: String = "https://www.osemocphoto.com"
private val nekopostUrl = "https://www.nekopost.net"
override val client: OkHttpClient = network.cloudflareClient
@ -57,118 +54,96 @@ class Nekopost : ParsedHttpSource() {
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 =
throw NotImplementedError("Unused")
override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used.")
override fun chapterListSelector(): String = throw NotImplementedError("Unused")
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used.")
override fun chapterFromElement(element: Element): SChapter =
throw NotImplementedError("Unused")
override fun imageUrlRequest(page: Page): Request = throw UnsupportedOperationException("Not used.")
override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl)
override fun imageUrlParse(document: Document): String = throw NotImplementedError("Unused")
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 projectInfo: RawProjectInfo = json.decodeFromString(responseBody.string())
manga.apply {
projectInfo.projectInfo.let {
url = it.projectId
title = it.projectName
artist = it.artistName
author = it.authorName
description = it.info
status = getStatus(it.status)
thumbnail_url =
"$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
initialized = true
}
genre = if (projectInfo.projectCategoryUsed != null) {
projectInfo.projectCategoryUsed.joinToString(", ") { it.categoryName }
} else {
""
}
}
}
override fun mangaDetailsRequest(manga: SManga): Request {
return GET("$projectDataEndpoint/${manga.url}", headers)
}
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return if (manga.status != SManga.LICENSED) {
client.newCall(GET("$projectDataEndpoint/${manga.url}", headers))
.asObservableSuccess()
.map { response ->
val responseBody = response.body
val projectInfo: RawProjectInfo = json.decodeFromString(responseBody.string())
override fun mangaDetailsParse(response: Response): SManga {
val responseBody = response.body
val projectInfo: RawProjectInfo = json.decodeFromString(responseBody.string())
val manga = SManga.create()
manga.apply {
projectInfo.projectInfo.let {
url = it.projectId
title = it.projectName
artist = it.artistName
author = it.authorName
description = it.info
status = getStatus(it.status)
thumbnail_url = "$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
initialized = true
}
manga.status = getStatus(projectInfo.projectInfo.status)
genre = if (projectInfo.projectCategoryUsed != null) {
projectInfo.projectCategoryUsed.joinToString(", ") { it.categoryName }
} else {
""
}
}
return manga
}
if (manga.status == SManga.LICENSED) {
throw Exception("Licensed - No chapter to show")
}
override fun chapterListRequest(manga: SManga): Request {
val headers = Headers.headersOf("accept", "*/*", "content-type", "text/plain;charset=UTF-8", "origin", nekopostUrl)
return GET("$projectDataEndpoint/${manga.url}", headers)
}
projectInfo.projectChapterList!!.map { chapter ->
SChapter.create().apply {
url =
"${manga.url}/${chapter.chapterId}/${manga.url}_${chapter.chapterId}.json"
name = chapter.chapterName
date_upload = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss",
Locale("th"),
).parse(chapter.createDate)?.time
?: 0L
chapter_number = chapter.chapterNo.toFloat()
scanlator = chapter.providerName
}
}
}
} else {
Observable.error(Exception("Licensed - No chapter to show"))
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)
if (manga.status == SManga.LICENSED) {
throw Exception("Licensed - No chapter to show")
}
return projectInfo.projectChapterList!!.map { chapter ->
SChapter.create().apply {
url = "${projectInfo.projectInfo.projectId.toInt()}/${chapter.chapterId}/${projectInfo.projectInfo.projectId.toInt()}_${chapter.chapterId}.json"
name = chapter.chapterName
date_upload = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss",
Locale("th"),
).parse(chapter.createDate)?.time ?: 0L
chapter_number = chapter.chapterNo.toFloat()
scanlator = chapter.providerName
}
}
}
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
return client.newCall(GET("$fileHost/collectManga/${chapter.url}", headers))
.asObservableSuccess()
.map { response ->
val responseBody = response.body
val chapterInfo: RawChapterInfo = json.decodeFromString(responseBody.string())
chapterInfo.pageItem.map { page ->
val imgUrl: String = if (page.pageName != null) {
"$fileHost/collectManga/${chapterInfo.projectId}/${chapterInfo.chapterId}/${page.pageName}"
} else {
"$fileHost/collectManga/${chapterInfo.projectId}/${chapterInfo.chapterId}/${page.fileName}"
}
Page(
index = page.pageNo,
imageUrl = imgUrl,
)
}
}
override fun pageListRequest(chapter: SChapter): Request {
return GET("$fileHost/collectManga/${chapter.url}", headers)
}
override fun pageListParse(document: Document): List<Page> = throw NotImplementedError("Unused")
override fun pageListParse(response: Response): List<Page> {
val responseBody = response.body
val chapterInfo: RawChapterInfo = json.decodeFromString(responseBody.string())
return chapterInfo.pageItem.map { page ->
val imgUrl: String = if (page.pageName != null) {
"$fileHost/collectManga/${chapterInfo.projectId}/${chapterInfo.chapterId}/${page.pageName}"
} else {
"$fileHost/collectManga/${chapterInfo.projectId}/${chapterInfo.chapterId}/${page.fileName}"
}
Page(
index = page.pageNo,
imageUrl = imgUrl,
)
}
}
override fun popularMangaRequest(page: Int): Request {
if (page <= 1) existingProject.clear()
// API has a bug that sometime it returns null on first page
return GET("$latestMangaEndpoint/${if (firstPageNulled) page else page - 1 }", headers)
return GET("$latestMangaEndpoint/${if (firstPageNulled) page else page - 1}", headers)
}
override fun popularMangaParse(response: Response): MangasPage {
@ -176,18 +151,15 @@ class Nekopost : ParsedHttpSource() {
val projectList: RawProjectSummaryList = json.decodeFromString(responseBody.string())
val mangaList: List<SManga> = if (projectList.listChapter != null) {
projectList.listChapter
.filter { !existingProject.contains(it.projectId) }
.map {
SManga.create().apply {
url = it.projectId
title = it.projectName
thumbnail_url =
"$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
initialized = false
status = 0
}
projectList.listChapter.filter { !existingProject.contains(it.projectId) }.map {
SManga.create().apply {
url = it.projectId
title = it.projectName
thumbnail_url = "$fileHost/collectManga/${it.projectId}/${it.projectId}_cover.jpg"
initialized = false
status = 0
}
}
} else {
firstPageNulled = true // API has a bug that sometime it returns null on first page
return MangasPage(emptyList(), hasNextPage = false)
@ -198,51 +170,27 @@ class Nekopost : ParsedHttpSource() {
return MangasPage(mangaList, hasNextPage = true)
}
override fun popularMangaFromElement(element: Element): SManga =
throw NotImplementedError("Unused")
override fun popularMangaNextPageSelector(): String = throw Exception("Unused")
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 mangaList: List<SManga> = projectList.filter { project ->
Regex(
query,
setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE),
).find(project.npName) != null
}.map { project ->
SManga.create().apply {
url = project.npProjectId
title = project.npName
status = getStatus(project.npStatus)
initialized = false
}
}
MangasPage(mangaList, false)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
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 searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
throw Exception("Unused")
override fun searchMangaParse(response: Response): MangasPage {
val responseBody = response.body.string()
override fun searchMangaParse(response: Response): MangasPage = throw Exception("Unused")
val projectList: List<RawProjectSearchSummary> = json.decodeFromString(responseBody)
val mangaList: List<SManga> = projectList.filter { project ->
project.projectType == "m"
}.map { project ->
SManga.create().apply {
url = project.projectId.toString()
title = project.projectName
status = project.status
initialized = false
}
}
override fun searchMangaSelector(): String = throw Exception("Unused")
return MangasPage(mangaList, false)
}
}

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,
)