forked from AlmightyHak/extensions-source
parent
5e0d900321
commit
c6ad6636ec
4 changed files with 50 additions and 55 deletions
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimeOwl'
|
||||
extClass = '.AnimeOwl'
|
||||
extVersionCode = 18
|
||||
extVersionCode = 19
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue