Fix Philiascans (#10322)

This commit is contained in:
manti 2025-09-01 11:06:55 +02:00 committed by Draff
parent 79bdda34b2
commit efe09f539b
Signed by: Draff
GPG Key ID: E8A89F3211677653
2 changed files with 216 additions and 23 deletions

View File

@ -1,9 +1,7 @@
ext {
extName = 'Philia Scans'
extClass = '.PhiliaScans'
themePkg = 'madara'
baseUrl = "https://philiascans.org"
overrideVersionCode = 1
extVersionCode = 44
isNsfw = false
}

View File

@ -1,44 +1,239 @@
package eu.kanade.tachiyomi.extension.en.philiascans
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
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.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import java.text.SimpleDateFormat
import java.util.Locale
import okhttp3.Response
import org.jsoup.nodes.Element
class PhiliaScans : Madara(
"Philia Scans",
"https://philiascans.org",
"en",
SimpleDateFormat("dd/MMM", Locale.US),
) {
override val versionId: Int = 2
override val useNewChapterEndpoint = true
class PhiliaScans : HttpSource() {
override fun popularMangaSelector(): String = ".manga__item"
override fun latestUpdatesSelector(): String = popularMangaSelector()
override fun searchMangaSelector(): String = popularMangaSelector()
override val name = "Philia Scans"
override val baseUrl = "https://philiascans.org"
override val lang = "en"
override val supportsLatest = true
override val versionId: Int = 3
override val mangaDetailsSelectorTitle: String = "h1.post-title"
override val mangaDetailsSelectorStatus: String = "div.summary-heading:contains(Status) + div"
override val client: OkHttpClient = network.cloudflareClient
override fun popularMangaRequest(page: Int): Request {
val url = baseUrl.toHttpUrl().newBuilder().apply {
addQueryParameter("post_type", "wp-manga")
addQueryParameter("m_orderby", "views")
addQueryParameter("s", "")
addQueryParameter("sort", "most_viewed")
addQueryParameter("paged", page.toString())
}.build()
return GET(url, headers)
}
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.select("div.unit").map { popularMangaFromElement(it) }
val hasNextPage = document.selectFirst("li.page-item:not(.disabled) a.page-link[rel=next]") != null
return MangasPage(mangas, hasNextPage)
}
private fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
val titleLink = element.selectFirst("a.c-title")!!
title = titleLink.text()
setUrlWithoutDomain(titleLink.attr("href"))
thumbnail_url = element.selectFirst("a.poster div.poster-image-wrapper > img")?.attr("abs:src")
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/recently-updated/?page=$page", headers)
}
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = baseUrl.toHttpUrl().newBuilder().apply {
addQueryParameter("post_type", "wp-manga")
addQueryParameter("m_orderby", "latest")
addQueryParameter("s", query)
addQueryParameter("paged", page.toString())
filters.forEach { filter ->
when (filter) {
is SortBy -> {
val sort = filter.toUriPart()
if (sort.isNotEmpty()) addQueryParameter("sort", sort)
}
is TypeList ->
filter.state
.filter { it.state }
.forEach { addQueryParameter("type[]", it.value) }
is YearList ->
filter.state
.filter { it.state }
.forEach { addQueryParameter("release[]", it.name) }
is GenreList ->
filter.state
.filter { it.state }
.forEach { addQueryParameter("genre[]", it.id) }
is GenreInclusion -> {
if (filter.state) addQueryParameter("genre_mode", "and")
}
else -> {}
}
}
}.build()
return GET(url, headers)
}
// needed to exclude paid chapters
override fun chapterListSelector(): String = """li.wp-manga-chapter:not(:has(a[href="#"]))"""
override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response)
override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup()
return SManga.create().apply {
title = document.selectFirst("h1.serie-title")!!.text()
author = document.selectFirst(".stat-item:has(.stat-label:contains(Author)) .stat-value")?.text()
artist = document.selectFirst(".stat-item:has(.stat-label:contains(Artist)) .stat-value")?.text()
genre = document.select("div.genre-list a").joinToString { it.text() }
status = parseStatus(document.selectFirst(".stat-item:has(.stat-label:contains(Status)) span:not(.stat-label)")?.text())
description = document.selectFirst("div.description-content")?.text()
thumbnail_url = imageFromElement(document.selectFirst("div.main-cover img.cover"))
}
}
private fun parseStatus(status: String?): Int = when {
status == null -> SManga.UNKNOWN
status.contains("Releasing", true) -> SManga.ONGOING
status.contains("Completed", true) -> SManga.COMPLETED
status.contains("On Hold", true) -> SManga.ON_HIATUS
status.contains("Canceled", true) -> SManga.CANCELLED
else -> SManga.UNKNOWN
}
override fun chapterListParse(response: Response): List<SChapter> {
return response.asJsoup().select("li.item")
.toList()
.filter { it.selectFirst("a")?.attr("href")?.contains("#") == false } // Filter Coin Chapters
.map { chapterFromElement(it) }
}
private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
val urlElement = element.selectFirst("a")!!
setUrlWithoutDomain(urlElement.attr("href"))
name = element.selectFirst("zebi")!!.text().trim()
}
override fun pageListParse(response: Response): List<Page> {
return response.asJsoup().select("div#ch-images img")
.mapIndexed { index, element ->
Page(index, imageUrl = imageFromElement(element))
}
}
private fun imageFromElement(element: Element?): String? {
return when {
element == null -> null
element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
element.hasAttr("data-src") -> element.attr("abs:data-src")
else -> element.attr("abs:src")
}
}
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
override fun getFilterList(): FilterList {
return FilterList(
Filter.Header("NOTE: Filters are applied when you search."),
SortBy(),
TypeList(getTypeList()),
YearList(getYearList()),
Filter.Separator(),
GenreList(getGenreList()),
GenreInclusion(),
)
}
private class SortBy : UriPartFilter(
"Sort by",
arrayOf(
Pair("Default", ""),
Pair("Newest", "recently_added"),
Pair("Alphabetical", "title_az"),
Pair("Most Viewed", "most_viewed"),
),
)
private class Type(name: String, val value: String) : Filter.CheckBox(name)
private class TypeList(types: List<Type>) : Filter.Group<Type>("Type", types)
private fun getTypeList() = listOf(
Type("Manga", "manga"),
Type("Manhua", "manhua"),
Type("Manhwa", "manhwa"),
Type("Seinen", "seinen"),
)
private class Year(name: String) : Filter.CheckBox(name)
private class YearList(years: List<Year>) : Filter.Group<Year>("Year", years)
private fun getYearList() = listOf(
Year("2025"),
Year("2024"),
Year("2023"),
Year("2022"),
Year("2021"),
Year("2020"),
Year("2019"),
)
private class Genre(name: String, val id: String) : Filter.CheckBox(name)
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
private class GenreInclusion : Filter.CheckBox("Must include all selected genres")
private fun getGenreList() = listOf(
Genre("Action", "29"),
Genre("Adventure", "38"),
Genre("Comedy", "42"),
Genre("Crime", "30"),
Genre("Drama", "34"),
Genre("Ecchi", "39"),
Genre("Fantasy", "43"),
Genre("Gore", "157"),
Genre("Gourmet", "188"),
Genre("Harem", "46"),
Genre("Historical", "40"),
Genre("Horror", "44"),
Genre("Isekai", "31"),
Genre("Josei", "173"),
Genre("Josei-None", "302"),
Genre("Josei-Seinen", "174"),
Genre("Magic", "87"),
Genre("Martial Arts", "61"),
Genre("Medical", "32"),
Genre("Monsters", "99"),
Genre("Music", "303"),
Genre("Mystery", "35"),
Genre("Psychological", "62"),
Genre("Regression", "122"),
Genre("Romance", "33"),
Genre("School Life", "47"),
Genre("Sci-Fi", "36"),
Genre("Seinen", "48"),
Genre("Shoujo", "69"),
Genre("Shounen", "55"),
Genre("Slice of Life", "37"),
Genre("Sports", "45"),
Genre("Supernatural", "49"),
Genre("Survival", "121"),
Genre("Tragedy", "41"),
Genre("Villainess", "253"),
Genre("War", "120"),
Genre("Yuri", "195"),
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
}