FlixScans: api changes (#2229)

* FlixScans: api changes

* review changes

* move filter fetching to `getFilterList`
This commit is contained in:
AwkwardPeak7 2024-04-03 17:28:00 +05:00 committed by Draff
parent ea833d9401
commit e8f8e9e8a9
5 changed files with 60 additions and 70 deletions

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc") id("lib-multisrc")
} }
baseVersionCode = 5 baseVersionCode = 6

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.multisrc.flixscans package eu.kanade.tachiyomi.multisrc.flixscans
import android.util.Log import android.util.Log
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -14,18 +14,17 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Call import okhttp3.Call
import okhttp3.Callback import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.IOException
abstract class FlixScans( abstract class FlixScans(
override val name: String, override val name: String,
override val baseUrl: String, override val baseUrl: String,
override val lang: String, override val lang: String,
protected val apiUrl: String = "$baseUrl/api/__api_party/noxApi", protected val apiUrl: String = "$baseUrl/api/v1",
protected val cdnUrl: String = baseUrl.replace("://", "://media.").plus("/"), protected val cdnUrl: String = baseUrl.replace("://", "://media.").plus("/"),
) : HttpSource() { ) : HttpSource() {
@ -38,22 +37,10 @@ abstract class FlixScans(
.build() .build()
override fun headersBuilder() = super.headersBuilder() override fun headersBuilder() = super.headersBuilder()
.add("Referer", baseUrl) .add("Referer", "$baseUrl/")
protected open fun postPath(path: String): Request {
val payload = """{"path":"$path","headers":{}}""".toRequestBody(JSON_MEDIA_TYPE)
return POST(apiUrl, headers, payload)
}
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
runCatching { fetchGenre() }
return super.fetchPopularManga(page)
}
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return postPath("webtoon/pages/home/romance") return GET("$apiUrl/webtoon/pages/home/romance", headers)
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
@ -66,21 +53,15 @@ abstract class FlixScans(
return MangasPage(entries, false) return MangasPage(entries, false)
} }
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
runCatching { fetchGenre() }
return super.fetchLatestUpdates(page)
}
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int): Request {
return postPath("search/advance?page=$page&serie_type=webtoon") return GET("$apiUrl/search/advance?page=$page&serie_type=webtoon", headers)
} }
override fun latestUpdatesParse(response: Response): MangasPage { override fun latestUpdatesParse(response: Response): MangasPage {
val result = response.parseAs<ApiResponse<BrowseSeries>>() val result = response.parseAs<ApiResponse<BrowseSeries>>()
val entries = result.data.map { it.toSManga(cdnUrl) } val entries = result.data.map { it.toSManga(cdnUrl) }
val hasNextPage = result.meta.lastPage > result.meta.currentPage val hasNextPage = result.lastPage > result.currentPage
return MangasPage(entries, hasNextPage) return MangasPage(entries, hasNextPage)
} }
@ -100,7 +81,7 @@ abstract class FlixScans(
} }
private val fetchGenreCallback = object : Callback { private val fetchGenreCallback = object : Callback {
override fun onFailure(call: Call, e: okio.IOException) { override fun onFailure(call: Call, e: IOException) {
fetchGenreAttempt++ fetchGenreAttempt++
fetchGenreFailed = true fetchGenreFailed = true
fetchGenreCallOngoing = false fetchGenreCallOngoing = false
@ -132,7 +113,7 @@ abstract class FlixScans(
} }
private fun fetchGenreRequest(): Request { private fun fetchGenreRequest(): Request {
return postPath("search/genres") return GET("$apiUrl/search/genres", headers)
} }
private fun fetchGenreParse(response: Response): List<GenreHolder> { private fun fetchGenreParse(response: Response): List<GenreHolder> {
@ -140,6 +121,8 @@ abstract class FlixScans(
} }
override fun getFilterList(): FilterList { override fun getFilterList(): FilterList {
fetchGenre()
val filters: MutableList<Filter<*>> = mutableListOf( val filters: MutableList<Filter<*>> = mutableListOf(
Filter.Header("Ignored when using Text Search"), Filter.Header("Ignored when using Text Search"),
MainGenreFilter(), MainGenreFilter(),
@ -161,60 +144,59 @@ abstract class FlixScans(
return FilterList(filters) return FilterList(filters)
} }
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
runCatching { fetchGenre() }
return super.fetchSearchManga(page, query, filters)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) { if (query.isNotEmpty()) {
return postPath("search/serie/${query.trim()}?page=$page") val url = "$apiUrl/search/serie".toHttpUrl().newBuilder()
.addPathSegment(query.trim())
.addQueryParameter("page", page.toString())
.build()
return GET(url, headers)
} }
val advSearchBody = buildString { val advSearchUrl = apiUrl.toHttpUrl().newBuilder().apply {
append("search/advance") addPathSegments("search/advance")
append("?page=", page) addQueryParameter("page", page.toString())
append("&serie_type=webtoon") addQueryParameter("serie_type", "webtoon")
filters.forEach { filter -> filters.forEach { filter ->
when (filter) { when (filter) {
is GenreFilter -> { is GenreFilter -> {
filter.checked.let { filter.checked.let {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
append("&genres=", it.joinToString(",")) addQueryParameter("genres", it.joinToString(","))
} }
} }
} }
is MainGenreFilter -> { is MainGenreFilter -> {
if (filter.state > 0) { if (filter.state > 0) {
append("&main_genres=", filter.selected) addQueryParameter("main_genres", filter.selected)
} }
} }
is TypeFilter -> { is TypeFilter -> {
if (filter.state > 0) { if (filter.state > 0) {
append("&type=", filter.selected) addQueryParameter("type", filter.selected)
} }
} }
is StatusFilter -> { is StatusFilter -> {
if (filter.state > 0) { if (filter.state > 0) {
append("&status=", filter.selected) addQueryParameter("status", filter.selected)
} }
} }
else -> {} else -> {}
} }
} }
} }.build()
return postPath(advSearchBody) return GET(advSearchUrl, headers)
} }
override fun searchMangaParse(response: Response) = latestUpdatesParse(response) override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
override fun mangaDetailsRequest(manga: SManga): Request { override fun mangaDetailsRequest(manga: SManga): Request {
val id = manga.url.split("-")[1] val (prefix, id) = getPrefixIdFromUrl(manga.url)
return postPath("webtoon/series/$id") return GET("$apiUrl/webtoon/series/$id/$prefix", headers)
} }
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
@ -226,23 +208,30 @@ abstract class FlixScans(
} }
override fun chapterListRequest(manga: SManga): Request { override fun chapterListRequest(manga: SManga): Request {
val id = manga.url.split("-")[1] val (prefix, id) = getPrefixIdFromUrl(manga.url)
return postPath("webtoon/chapters/$id-desc") return GET("$apiUrl/webtoon/chapters/$id-desc#$prefix", headers)
} }
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val chapters = response.parseAs<List<Chapter>>() val chapters = response.parseAs<List<Chapter>>()
val prefix = response.request.url.fragment!!
return chapters.map(Chapter::toSChapter) return chapters.map { it.toSChapter(prefix) }
} }
override fun pageListRequest(chapter: SChapter): Request { override fun pageListRequest(chapter: SChapter): Request {
val id = chapter.url val (prefix, id) = getPrefixIdFromUrl(chapter.url)
.substringAfterLast("/")
.substringBefore("-")
return postPath("webtoon/chapters/chapter/$id") return GET("$apiUrl/webtoon/chapters/chapter/$id/$prefix", headers)
}
protected fun getPrefixIdFromUrl(url: String): Pair<String, String> {
return with(url.substringAfterLast("/")) {
val split = split("-")
split[0] to split[1]
}
} }
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
@ -259,8 +248,4 @@ abstract class FlixScans(
protected inline fun <reified T> Response.parseAs(): T = protected inline fun <reified T> Response.parseAs(): T =
use { body.string() }.let(json::decodeFromString) use { body.string() }.let(json::decodeFromString)
companion object {
private val JSON_MEDIA_TYPE = "application/json".toMediaTypeOrNull()
}
} }

View File

@ -11,13 +11,8 @@ import java.util.Locale
@Serializable @Serializable
data class ApiResponse<T>( data class ApiResponse<T>(
val data: List<T>, val data: List<T>,
val meta: PageInfo,
)
@Serializable
data class PageInfo(
@SerialName("last_page") val lastPage: Int,
@SerialName("current_page") val currentPage: Int, @SerialName("current_page") val currentPage: Int,
@SerialName("last_page") val lastPage: Int,
) )
@Serializable @Serializable
@ -120,8 +115,8 @@ data class Chapter(
val slug: String, val slug: String,
val createdAt: String? = null, val createdAt: String? = null,
) { ) {
fun toSChapter() = SChapter.create().apply { fun toSChapter(prefix: String) = SChapter.create().apply {
url = "/read/webtoon/$id-$slug" url = "/read/webtoon/$prefix-$id-$slug"
name = this@Chapter.name name = this@Chapter.name
date_upload = runCatching { dateFormat.parse(createdAt!!)!!.time }.getOrDefault(0L) date_upload = runCatching { dateFormat.parse(createdAt!!)!!.time }.getOrDefault(0L)
} }

View File

@ -2,6 +2,11 @@ package eu.kanade.tachiyomi.extension.ar.galaxymanga
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
class GalaxyManga : FlixScans("جالاكسي مانجا", "https://flixscans.com", "ar") { class GalaxyManga : FlixScans(
"جالاكسي مانجا",
"https://flixscans.com",
"ar",
"https://ar.flixscans.site/api/v1",
) {
override val versionId = 2 override val versionId = 2
} }

View File

@ -2,4 +2,9 @@ package eu.kanade.tachiyomi.extension.en.flixscans
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
class FlixScansNet : FlixScans("Flix Scans", "https://flixscans.org", "en") class FlixScansNet : FlixScans(
"Flix Scans",
"https://flixscans.org",
"en",
"https://flixscans.site/api/v1",
)