fix(src/en): Fix AnimeOwl (#241)

Closes #199
This commit is contained in:
imper1aldev 2024-09-14 19:56:41 -06:00 committed by GitHub
parent 5e0d900321
commit c6ad6636ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 50 additions and 55 deletions

View file

@ -1,7 +1,7 @@
ext {
extName = 'AnimeOwl'
extClass = '.AnimeOwl'
extVersionCode = 18
extVersionCode = 19
}
apply from: "$rootDir/common.gradle"

View file

@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelFlatMap
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
@ -35,12 +34,11 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import kotlin.math.ceil
@ExperimentalSerializationApi
class AnimeOwl : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "AnimeOwl"
override val baseUrl = "https://animeowl.us"
override val baseUrl = "https://animeowl.live"
override val lang = "en"
@ -109,6 +107,7 @@ class AnimeOwl : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
genre = document.select("div.genre > a").joinToString { it.text() }
author = document.select("div.type > a").text()
status = parseStatus(document.select("div.status > span").text())
thumbnail_url = document.selectFirst(".cover-img-container > img")?.attr("abs:src")
description = buildString {
document.select("div.anime-desc.desc-content").text()
.takeIf { it.isNotBlank() }
@ -127,32 +126,33 @@ class AnimeOwl : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
val animeId = response.asJsoup().select("div#unq-anime-id").attr("animeId")
val episodes = client.newCall(
GET("$baseUrl/api/anime/$animeId/episodes"),
).execute()
.parseAs<EpisodeResponse>()
val document = response.asJsoup()
val sub = document.select("#anime-cover-sub-content .episode-node").mapIndexed { idx, it ->
EpisodeResponse.Episode(
id = it.text().toDouble(),
episodeIndex = idx.toString(),
name = it.text(),
lang = "Sub",
href = it.attr("abs:href"),
)
}
val dub = document.select("#anime-cover-dub-content .episode-node").mapIndexed { idx, it ->
EpisodeResponse.Episode(
id = it.text().toDouble(),
episodeIndex = idx.toString(),
name = it.text(),
lang = "Dub",
href = it.attr("abs:href"),
)
}
return listOf(
episodes.sub.map { it.copy(lang = "Sub") },
episodes.dub.map { it.copy(lang = "Dub") },
).flatten()
.groupBy { it.name }
.map { (epNum, epList) ->
SEpisode.create().apply {
url = LinkData(
epList.map { ep ->
Link(
ep.buildUrl(episodes.subSlug, episodes.dubSlug),
ep.lang!!,
)
},
).toJsonString()
episode_number = epNum.toFloatOrNull() ?: 0F
name = "Episode $epNum"
}
return listOf(sub, dub).flatten().groupBy { it.name }.map { (epNum, epList) ->
SEpisode.create().apply {
url = LinkData(epList.map { ep -> Link(ep.href!!, ep.lang!!) }).toJsonString()
episode_number = epNum.toFloatOrNull() ?: 0F
name = "Episode $epNum"
}
.sortedByDescending { it.episode_number }
}.sortedByDescending { it.episode_number }
}
override fun episodeListSelector(): String = throw UnsupportedOperationException()
@ -160,9 +160,9 @@ class AnimeOwl : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException()
// ============================ Video Links =============================
override suspend fun getVideoList(episode: SEpisode): List<Video> =
json.decodeFromString<LinkData>(episode.url)
.links.parallelFlatMap { owlServersExtractor.extractOwlVideo(it) }.sort()
override suspend fun getVideoList(episode: SEpisode): List<Video> {
return json.decodeFromString<LinkData>(episode.url).links.parallelFlatMap { owlServersExtractor.extractOwlVideo(it) }.sort()
}
override fun videoFromElement(element: Element): Video = throw UnsupportedOperationException()
@ -228,15 +228,7 @@ class AnimeOwl : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
)
}
private fun LinkData.toJsonString(): String {
return json.encodeToString(this)
}
private fun EpisodeResponse.Episode.buildUrl(subSlug: String, dubSlug: String): String =
when (lang) {
"dub" -> dubSlug
else -> subSlug
}.let { "$baseUrl/watch/$it/$episodeIndex" }
private fun LinkData.toJsonString(): String = json.encodeToString(this)
private fun parseStatus(statusString: String): Int =
when (statusString) {

View file

@ -31,11 +31,12 @@ data class EpisodeResponse(
) {
@Serializable
data class Episode(
val id: Int,
val id: Double? = null,
val name: String,
val lang: String? = null,
@SerialName("episode_index")
val episodeIndex: String,
val href: String? = null,
)
}

View file

@ -36,18 +36,18 @@ class OwlExtractor(private val client: OkHttpClient, private val baseUrl: String
}
.let(Deobfuscator::deobfuscateScript)
?: throw Exception("Unable to get clean JS")
val jwt = JWT_REGEX.find(epJS)?.groupValues?.get(1) ?: throw Exception("Unable to get jwt")
val jwt = findFirstJwt(epJS) ?: throw Exception("Unable to get jwt")
val videoList = mutableListOf<Video>()
val servers = client.newCall(GET("$baseUrl$dataSrc")).execute()
.parseAs<OwlServers>()
coroutineScope {
val lufDeferred = async {
servers.luffy?.let { luffy ->
noRedirectClient.newCall(GET("${luffy}$jwt")).execute()
.use { it.headers["Location"] }
?.let { videoList.add(Video(it, "Luffy - ${link.lang} - 1080p", it)) }
?.let { videoList.add(Video(it, "${link.lang} Luffy:1080p", it)) }
}
}
val kaiDeferred = async {
@ -70,18 +70,20 @@ class OwlExtractor(private val client: OkHttpClient, private val baseUrl: String
return videoList
}
private fun getHLS(url: String, server: String, lang: String): List<Video> =
client.newCall(GET(url)).execute()
.parseAs<Stream>()
.url
.let {
playlistUtils.extractFromHls(
it,
videoNameGen = { qty -> "$server - $lang - $qty" },
)
private fun getHLS(url: String, server: String, lang: String): List<Video> {
return client.newCall(GET(url)).execute().let {
if (it.isSuccessful) {
it.parseAs<Stream>().url.let {
playlistUtils.extractFromHls(it, videoNameGen = { qty -> "$lang $server:$qty" })
}
} else {
emptyList()
}
}
}
companion object {
private val JWT_REGEX by lazy { "const\\s+(?:[A-Za-z0-9_]*)\\s*=\\s*'([^']+)'".toRegex() }
private fun findFirstJwt(text: String): String? {
val jwtPattern = Regex("['\"]([A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+)['\"]")
return jwtPattern.find(text)?.groupValues?.get(1)
}
}