feat(en/allanimechi): Add option to use hoster names & fix gogo (#2575)

This commit is contained in:
Secozzi 2023-11-29 15:38:28 +00:00 committed by GitHub
parent b2a68f135a
commit d60ff529e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 37 deletions

View File

@ -8,7 +8,7 @@ ext {
extName = 'AllAnimeChi' extName = 'AllAnimeChi'
pkgNameSuffix = 'en.allanimechi' pkgNameSuffix = 'en.allanimechi'
extClass = '.AllAnimeChi' extClass = '.AllAnimeChi'
extVersionCode = 1 extVersionCode = 2
libVersion = '13' libVersion = '13'
} }

View File

@ -38,6 +38,7 @@ import kotlinx.serialization.json.putJsonArray
import kotlinx.serialization.json.putJsonObject import kotlinx.serialization.json.putJsonObject
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
@ -46,6 +47,7 @@ import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Locale
class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() { class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
@ -252,8 +254,9 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
return GET(url, apiHeaders) return GET(url, apiHeaders)
} }
// TODO: replace with getAnimeUrl when new ext-lib is available
override fun animeDetailsRequest(anime: SAnime): Request { override fun animeDetailsRequest(anime: SAnime): Request {
return GET("data:text/plain,This%20extension%20does%20not%20exist%20as%20a%20website%21") return GET("data:text/plain,This%20extension%20does%20not%20have%20a%20website.")
} }
override fun animeDetailsParse(response: Response): SAnime { override fun animeDetailsParse(response: Response): SAnime {
@ -308,7 +311,7 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================ Video Links ============================= // ============================ Video Links =============================
private val internalExtractor by lazy { InternalExtractor(client, apiHeaders) } private val internalExtractor by lazy { InternalExtractor(client, apiHeaders, headers) }
override fun videoListRequest(episode: SEpisode): Request { override fun videoListRequest(episode: SEpisode): Request {
val variables = episode.url val variables = episode.url
@ -334,6 +337,7 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
val hosterBlackList = preferences.getHosterBlacklist val hosterBlackList = preferences.getHosterBlacklist
val altHosterBlackList = preferences.getAltHosterBlacklist val altHosterBlackList = preferences.getAltHosterBlacklist
val useHosterNames = preferences.useHosterName
val serverList = videoJson.data.episode.sourceUrls.mapNotNull { video -> val serverList = videoJson.data.episode.sourceUrls.mapNotNull { video ->
when { when {
@ -359,26 +363,32 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
} }
return prioritySort( return prioritySort(
serverList.parallelCatchingFlatMap(::getVideoFromServer), serverList.parallelCatchingFlatMap { getVideoFromServer(it, useHosterNames) },
) )
} }
private fun getVideoFromServer(server: Server): List<Pair<Video, Float>> { private fun getVideoFromServer(server: Server, useHosterName: Boolean): List<Pair<Video, Float>> {
return when (server.type) { return when (server.type) {
"player" -> getFromPlayer(server) "player" -> getFromPlayer(server, useHosterName)
"internal" -> internalExtractor.videosFromServer(server, removeRaw = preferences.removeRaw) "internal" -> internalExtractor.videosFromServer(server, useHosterName, removeRaw = preferences.removeRaw)
"external" -> getFromExternal(server) "external" -> getFromExternal(server, useHosterName)
else -> emptyList() else -> emptyList()
} }
} }
private fun getFromPlayer(server: Server): List<Pair<Video, Float>> { private fun getFromPlayer(server: Server, useHosterName: Boolean): List<Pair<Video, Float>> {
val name = if (useHosterName) {
getHostName(server.sourceUrl, server.sourceName)
} else {
server.sourceName
}
val videoHeaders = headers.newBuilder().apply { val videoHeaders = headers.newBuilder().apply {
add("origin", siteUrl) add("origin", siteUrl)
add("referer", "$siteUrl/") add("referer", "$siteUrl/")
}.build() }.build()
val video = Video(server.sourceUrl, server.sourceName, server.sourceUrl, headers = videoHeaders) val video = Video(server.sourceUrl, name, server.sourceUrl, headers = videoHeaders)
return listOf( return listOf(
Pair(video, server.priority), Pair(video, server.priority),
) )
@ -392,9 +402,14 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
private val allanimeExtractor by lazy { AllAnimeExtractor(client, headers) } private val allanimeExtractor by lazy { AllAnimeExtractor(client, headers) }
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) } private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private fun getFromExternal(server: Server): List<Pair<Video, Float>> { private fun getFromExternal(server: Server, useHosterName: Boolean): List<Pair<Video, Float>> {
val url = server.sourceUrl.replace(Regex("""^//"""), "https://") val url = server.sourceUrl.replace(Regex("""^//"""), "https://")
val prefix = "${server.sourceName} - " val prefix = if (useHosterName) {
"${getHostName(url, server.sourceName)} - "
} else {
"${server.sourceName} - "
}
val videoList = when { val videoList = when {
url.startsWith("https://ok") -> okruExtractor.videosFromUrl(url, prefix = prefix) url.startsWith("https://ok") -> okruExtractor.videosFromUrl(url, prefix = prefix)
url.startsWith("https://filemoon") -> filemoonExtractor.videosFromUrl(url, prefix = prefix) url.startsWith("https://filemoon") -> filemoonExtractor.videosFromUrl(url, prefix = prefix)
@ -411,6 +426,14 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================= Utilities ============================== // ============================= Utilities ==============================
private fun getHostName(host: String, fallback: String): String {
return host.toHttpUrlOrNull()?.host?.split(".")?.let {
it.getOrNull(it.size - 2)?.replaceFirstChar { c ->
if (c.isLowerCase()) c.titlecase(Locale.ROOT) else c.toString()
}
} ?: fallback
}
private fun String.decodeBase64(): String { private fun String.decodeBase64(): String {
return String(Base64.decode(this, Base64.DEFAULT)) return String(Base64.decode(this, Base64.DEFAULT))
} }
@ -468,6 +491,8 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
} }
companion object { companion object {
private const val PAGE_SIZE = 30 // number of items to retrieve when calling API
private const val POPULAR_HASH = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c" private const val POPULAR_HASH = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c"
private const val LATEST_HASH = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406" private const val LATEST_HASH = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406"
private const val DETAILS_HASH = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b" private const val DETAILS_HASH = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b"
@ -492,8 +517,6 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
"Yt-mp4", "Yt-mp4",
) )
private const val PAGE_SIZE = 30 // number of items to retrieve when calling API
private const val PREF_HOSTER_BLACKLIST_KEY = "pref_hoster_blacklist" private const val PREF_HOSTER_BLACKLIST_KEY = "pref_hoster_blacklist"
private val PREF_HOSTER_BLACKLIST_ENTRY_VALUES = INTERNAL_HOSTER_NAMES.map { private val PREF_HOSTER_BLACKLIST_ENTRY_VALUES = INTERNAL_HOSTER_NAMES.map {
it.lowercase().substringBefore(" (") it.lowercase().substringBefore(" (")
@ -542,6 +565,9 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
private const val PREF_SUB_KEY = "preferred_sub" private const val PREF_SUB_KEY = "preferred_sub"
private const val PREF_SUB_DEFAULT = "sub" private const val PREF_SUB_DEFAULT = "sub"
private const val PREF_USE_HOSTER_NAMES_KEY = "use_host_prefix"
private const val PREF_USE_HOSTER_NAMES_DEFAULT = false
} }
// ============================== Settings ============================== // ============================== Settings ==============================
@ -588,17 +614,6 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
} }
}.also(screen::addPreference) }.also(screen::addPreference)
SwitchPreferenceCompat(screen.context).apply {
key = PREF_REMOVE_RAW_KEY
title = "Attempt to filter out raw"
setDefaultValue(PREF_REMOVE_RAW_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
val new = newValue as Boolean
preferences.edit().putBoolean(key, new).commit()
}
}.also(screen::addPreference)
ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY key = PREF_QUALITY_KEY
title = "Preferred quality" title = "Preferred quality"
@ -646,6 +661,28 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
preferences.edit().putString(key, entry).commit() preferences.edit().putString(key, entry).commit()
} }
}.also(screen::addPreference) }.also(screen::addPreference)
SwitchPreferenceCompat(screen.context).apply {
key = PREF_REMOVE_RAW_KEY
title = "Attempt to filter out raw"
setDefaultValue(PREF_REMOVE_RAW_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
val new = newValue as Boolean
preferences.edit().putBoolean(key, new).commit()
}
}.also(screen::addPreference)
SwitchPreferenceCompat(screen.context).apply {
key = PREF_USE_HOSTER_NAMES_KEY
title = "Use names of video hoster"
setDefaultValue(PREF_USE_HOSTER_NAMES_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
val new = newValue as Boolean
preferences.edit().putBoolean(key, new).commit()
}
}.also(screen::addPreference)
} }
private val SharedPreferences.subPref private val SharedPreferences.subPref
@ -668,4 +705,7 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
private val SharedPreferences.removeRaw private val SharedPreferences.removeRaw
get() = getBoolean(PREF_REMOVE_RAW_KEY, PREF_REMOVE_RAW_DEFAULT) get() = getBoolean(PREF_REMOVE_RAW_KEY, PREF_REMOVE_RAW_DEFAULT)
private val SharedPreferences.useHosterName
get() = getBoolean(PREF_USE_HOSTER_NAMES_KEY, PREF_USE_HOSTER_NAMES_DEFAULT)
} }

View File

@ -9,11 +9,13 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Locale
class InternalExtractor(private val client: OkHttpClient, private val apiHeaders: Headers) { class InternalExtractor(private val client: OkHttpClient, private val apiHeaders: Headers, private val headers: Headers) {
private val blogUrl = "aHR0cHM6Ly9ibG9nLmFsbGFuaW1lLnBybw==".decodeBase64() private val blogUrl = "aHR0cHM6Ly9ibG9nLmFsbGFuaW1lLnBybw==".decodeBase64()
@ -22,11 +24,11 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders
"Dalvik/2.1.0 (Linux; U; Android 13; Pixel 5 Build/TQ3A.230705.001.B4)", "Dalvik/2.1.0 (Linux; U; Android 13; Pixel 5 Build/TQ3A.230705.001.B4)",
) )
private val playlistUtils by lazy { PlaylistUtils(client, playlistHeaders) } private val playlistUtils by lazy { PlaylistUtils(client, headers) }
private val json: Json by injectLazy() private val json: Json by injectLazy()
fun videosFromServer(server: AllAnimeChi.Server, removeRaw: Boolean): List<Pair<Video, Float>> { fun videosFromServer(server: AllAnimeChi.Server, useHosterName: Boolean, removeRaw: Boolean): List<Pair<Video, Float>> {
val blogHeaders = apiHeaders.newBuilder().apply { val blogHeaders = apiHeaders.newBuilder().apply {
set("host", blogUrl.toHttpUrl().host) set("host", blogUrl.toHttpUrl().host)
}.build() }.build()
@ -37,8 +39,8 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders
val videoList = videoData.links.flatMap { val videoList = videoData.links.flatMap {
when { when {
it.hls == true -> getFromHls(server, it, removeRaw) it.hls == true -> getFromHls(server, it, useHosterName, removeRaw)
it.mp4 == true -> getFromMp4(server, it) it.mp4 == true -> getFromMp4(server, it, useHosterName)
else -> emptyList() else -> emptyList()
} }
} }
@ -46,20 +48,26 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders
return videoList return videoList
} }
private fun getFromMp4(server: AllAnimeChi.Server, data: VideoData.LinkObject): List<Pair<Video, Float>> { private fun getFromMp4(server: AllAnimeChi.Server, data: VideoData.LinkObject, useHosterName: Boolean): List<Pair<Video, Float>> {
val baseName = "${server.sourceName} - ${data.resolutionStr}" val host = if (useHosterName) getHostName(data.link, server.sourceName) else server.sourceName
val baseName = "$host - ${data.resolutionStr}"
val video = Video(data.link, baseName, data.link, headers = playlistHeaders) val video = Video(data.link, baseName, data.link, headers = playlistHeaders)
return listOf( return listOf(
Pair(video, server.priority), Pair(video, server.priority),
) )
} }
private fun getFromHls(server: AllAnimeChi.Server, data: VideoData.LinkObject, removeRaw: Boolean): List<Pair<Video, Float>> { private fun getFromHls(server: AllAnimeChi.Server, data: VideoData.LinkObject, useHosterName: Boolean, removeRaw: Boolean): List<Pair<Video, Float>> {
if (removeRaw && data.resolutionStr.contains("raw", true)) return emptyList() if (removeRaw && data.resolutionStr.contains("raw", true)) return emptyList()
val host = if (useHosterName) getHostName(data.link, server.sourceName) else server.sourceName
val linkHost = data.link.toHttpUrl().host val linkHost = data.link.toHttpUrl().host
// Doesn't seem to work // Doesn't seem to work
if (server.sourceName.equals("Luf-mp4", true) && linkHost.contains("maverickki")) return emptyList() if (server.sourceName.equals("Luf-mp4", true)) {
return getFromGogo(server, data, host)
}
// Hardcode some names // Hardcode some names
val baseName = if (linkHost.contains("crunchyroll")) { val baseName = if (linkHost.contains("crunchyroll")) {
@ -73,8 +81,7 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders
.replace("vo_SUB", "Sub") .replace("vo_SUB", "Sub")
.replace("SUB", "Sub") .replace("SUB", "Sub")
} else { } else {
"${server.sourceName} - ${data.resolutionStr}" "$host - ${data.resolutionStr}"
.replace("Luf-mp4", "Luf-mp4 (gogo)")
} }
// Get stuff // Get stuff
@ -91,7 +98,7 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders
val videoList = playlistUtils.extractFromHls( val videoList = playlistUtils.extractFromHls(
data.link, data.link,
videoNameGen = { q -> "$baseName - $q" }, videoNameGen = { q -> "$baseName - ${data.resolutionStr} - $q" },
masterHeadersGen = { _, _ -> masterHeaders }, masterHeadersGen = { _, _ -> masterHeaders },
) )
@ -100,8 +107,33 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders
} }
} }
private fun getFromGogo(server: AllAnimeChi.Server, data: VideoData.LinkObject, hostName: String): List<Pair<Video, Float>> {
val host = data.link.toHttpUrl().host
// Seems to be dead
if (host.contains("maverickki", true)) return emptyList()
val videoList = playlistUtils.extractFromHls(
data.link,
videoNameGen = { q -> "$hostName - ${data.resolutionStr} - $q" },
referer = "https://playtaku.net/",
)
return videoList.map {
Pair(it, server.priority)
}
}
// ============================= Utilities ============================== // ============================= Utilities ==============================
private fun getHostName(host: String, fallback: String): String {
return host.toHttpUrlOrNull()?.host?.split(".")?.let {
it.getOrNull(it.size - 2)?.replaceFirstChar { c ->
if (c.isLowerCase()) c.titlecase(Locale.ROOT) else c.toString()
}
} ?: fallback
}
@Serializable @Serializable
data class VideoData( data class VideoData(
val links: List<LinkObject>, val links: List<LinkObject>,