feat(en/oppaistream): Fetch Anilist covers (#2055)
This commit is contained in:
@ -5,7 +5,7 @@ ext {
|
|||||||
extName = 'Oppai Stream'
|
extName = 'Oppai Stream'
|
||||||
pkgNameSuffix = 'en.oppaistream'
|
pkgNameSuffix = 'en.oppaistream'
|
||||||
extClass = '.OppaiStream'
|
extClass = '.OppaiStream'
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
containsNsfw = true
|
containsNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
|||||||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import okhttp3.FormBody
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -131,16 +137,92 @@ class OppaiStream : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to fetch thumbnail URL using AniList GraphQL API
|
||||||
|
// Only use in animeDetailsParse.
|
||||||
|
private fun fetchThumbnailUrlByTitle(title: String): Pair<String?, MutableList<String>>? {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val query = """
|
||||||
|
query {
|
||||||
|
Media(search: "$title", type: ANIME, isAdult: true) {
|
||||||
|
coverImage {
|
||||||
|
extraLarge
|
||||||
|
large
|
||||||
|
}
|
||||||
|
studios {
|
||||||
|
nodes {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val requestBody = FormBody.Builder()
|
||||||
|
.add("query", query)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://graphql.anilist.co")
|
||||||
|
.post(requestBody)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
val responseString = response.body?.string()
|
||||||
|
|
||||||
|
return parseThumbnailUrlFromResponse(responseString)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseThumbnailUrlFromResponse(responseString: String?): Pair<String?, MutableList<String>>? {
|
||||||
|
val responseJson = Json.parseToJsonElement(responseString ?: "") as? JsonObject ?: return null
|
||||||
|
val data = responseJson["data"] as? JsonObject ?: return null
|
||||||
|
val media = data["Media"] as? JsonObject ?: return null
|
||||||
|
val coverImage = media["coverImage"] as? JsonObject ?: return null
|
||||||
|
|
||||||
|
val coverURL = when (preferences.getString(PREF_COVER_QUALITY, "large")) {
|
||||||
|
"extraLarge" -> coverImage["extraLarge"]?.jsonPrimitive?.content
|
||||||
|
"large" -> coverImage["large"]?.jsonPrimitive?.content
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val studiosList = mutableListOf<String>()
|
||||||
|
val studios = media["studios"]?.jsonObject?.get("nodes")?.jsonArray
|
||||||
|
studios?.forEach { studio ->
|
||||||
|
val name = studio.jsonObject["name"]?.jsonPrimitive?.content
|
||||||
|
if (!name.isNullOrEmpty()) {
|
||||||
|
studiosList.add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(coverURL, studiosList)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getFilterList() = FILTERS
|
override fun getFilterList() = FILTERS
|
||||||
|
|
||||||
// details
|
// details
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
return SAnime.create().apply {
|
return SAnime.create().apply {
|
||||||
|
// Fetch from from Anilist when "Anilist Cover" is selected in settings
|
||||||
|
val selectedCoverSource = preferences.getString(PREF_COVER_SOURCE, "Anilist-Cover")
|
||||||
|
val newTitle = document.select("div.effect-title").text().replace(Regex("[^a-zA-Z0-9\\s!.:\"]"), " ")
|
||||||
|
val thumbnailUrl = if (selectedCoverSource == "Anilist-Cover") {
|
||||||
|
fetchThumbnailUrlByTitle(newTitle)
|
||||||
|
} else {
|
||||||
|
null // Use default cover
|
||||||
|
}
|
||||||
|
|
||||||
title = document.select("div.effect-title").text()
|
title = document.select("div.effect-title").text()
|
||||||
description = document.select("div.description").text()
|
description = document.select("div.description").text()
|
||||||
genre = document.select("div.tags a").joinToString { it.text() }
|
genre = document.select("div.tags a").joinToString { it.text() }
|
||||||
author = document.select("div.content a.red").joinToString { it.text() }
|
author = document.select("div.content a.red").joinToString { it.text() }
|
||||||
thumbnail_url = document.select("#player").attr("data-poster")
|
|
||||||
|
// thumbnail_url = document.select("#player").attr("data-poster")
|
||||||
|
|
||||||
|
// Match local studios with anilist studios to increase the accuracy of the poster
|
||||||
|
val matchingStudios = document.select("div.content a.red").map { it.text() }
|
||||||
|
val matchedStudio = thumbnailUrl?.second?.find { it in matchingStudios }
|
||||||
|
|
||||||
|
thumbnail_url = if (matchedStudio != null) thumbnailUrl.first else document.select("#player").attr("data-poster")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +280,30 @@ class OppaiStream : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||||||
}.let {
|
}.let {
|
||||||
screen.addPreference(it)
|
screen.addPreference(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add cover source preference
|
||||||
|
ListPreference(screen.context).apply {
|
||||||
|
key = PREF_COVER_SOURCE
|
||||||
|
title = PREF_COVER_SOURCE_TITLE
|
||||||
|
entries = arrayOf("Default Cover", "Anilist Cover")
|
||||||
|
entryValues = arrayOf("Default-Cover", "Anilist-Cover")
|
||||||
|
summary = "This feature is experimental. It uses a covers for Anilist. If you see the default cover after switching to AniList cover, try clearing the cache in Settings > Advanced > Clear Anime Database > Oppai Steam. It only fetch Anilist covers in anime details page."
|
||||||
|
setDefaultValue("Anilist-Cover")
|
||||||
|
}.let {
|
||||||
|
screen.addPreference(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add cover source preference
|
||||||
|
ListPreference(screen.context).apply {
|
||||||
|
key = PREF_COVER_QUALITY
|
||||||
|
title = PREF_COVER_QUALITY_TITLE
|
||||||
|
entries = arrayOf("Extra Large", "Large")
|
||||||
|
entryValues = arrayOf("extraLarge", "large")
|
||||||
|
summary = "%s"
|
||||||
|
setDefaultValue("large")
|
||||||
|
}.let {
|
||||||
|
screen.addPreference(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -206,5 +312,11 @@ class OppaiStream : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||||||
|
|
||||||
private const val PREF_QUALITY = "preferred_quality"
|
private const val PREF_QUALITY = "preferred_quality"
|
||||||
private const val PREF_QUALITY_TITLE = "Preferred quality"
|
private const val PREF_QUALITY_TITLE = "Preferred quality"
|
||||||
|
|
||||||
|
private const val PREF_COVER_SOURCE = "preferred_cover_source"
|
||||||
|
private const val PREF_COVER_SOURCE_TITLE = "Preferred cover source - Beta"
|
||||||
|
|
||||||
|
private const val PREF_COVER_QUALITY = "preferred_cover_quality"
|
||||||
|
private const val PREF_COVER_QUALITY_TITLE = "Preferred cover quality - Beta"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user