Skip to content

Commit

Permalink
feat(en/allanimechi): Add option to use hoster names & fix gogo (#2575)
Browse files Browse the repository at this point in the history
  • Loading branch information
Secozzi authored Nov 29, 2023
1 parent b2a68f1 commit d60ff52
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/en/allanimechi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ext {
extName = 'AllAnimeChi'
pkgNameSuffix = 'en.allanimechi'
extClass = '.AllAnimeChi'
extVersionCode = 1
extVersionCode = 2
libVersion = '13'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import kotlinx.serialization.json.putJsonArray
import kotlinx.serialization.json.putJsonObject
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
Expand All @@ -46,6 +47,7 @@ import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.Locale

class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {

Expand Down Expand Up @@ -252,8 +254,9 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
return GET(url, apiHeaders)
}

// TODO: replace with getAnimeUrl when new ext-lib is available
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 {
Expand Down Expand Up @@ -308,7 +311,7 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {

// ============================ 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 {
val variables = episode.url
Expand All @@ -334,6 +337,7 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {

val hosterBlackList = preferences.getHosterBlacklist
val altHosterBlackList = preferences.getAltHosterBlacklist
val useHosterNames = preferences.useHosterName

val serverList = videoJson.data.episode.sourceUrls.mapNotNull { video ->
when {
Expand All @@ -359,26 +363,32 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
}

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) {
"player" -> getFromPlayer(server)
"internal" -> internalExtractor.videosFromServer(server, removeRaw = preferences.removeRaw)
"external" -> getFromExternal(server)
"player" -> getFromPlayer(server, useHosterName)
"internal" -> internalExtractor.videosFromServer(server, useHosterName, removeRaw = preferences.removeRaw)
"external" -> getFromExternal(server, useHosterName)
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 {
add("origin", siteUrl)
add("referer", "$siteUrl/")
}.build()

val video = Video(server.sourceUrl, server.sourceName, server.sourceUrl, headers = videoHeaders)
val video = Video(server.sourceUrl, name, server.sourceUrl, headers = videoHeaders)
return listOf(
Pair(video, server.priority),
)
Expand All @@ -392,9 +402,14 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
private val allanimeExtractor by lazy { AllAnimeExtractor(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 prefix = "${server.sourceName} - "
val prefix = if (useHosterName) {
"${getHostName(url, server.sourceName)} - "
} else {
"${server.sourceName} - "
}

val videoList = when {
url.startsWith("https://ok") -> okruExtractor.videosFromUrl(url, prefix = prefix)
url.startsWith("https://filemoon") -> filemoonExtractor.videosFromUrl(url, prefix = prefix)
Expand All @@ -411,6 +426,14 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {

// ============================= 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 {
return String(Base64.decode(this, Base64.DEFAULT))
}
Expand Down Expand Up @@ -468,6 +491,8 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
}

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 LATEST_HASH = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406"
private const val DETAILS_HASH = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b"
Expand All @@ -492,8 +517,6 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
"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 val PREF_HOSTER_BLACKLIST_ENTRY_VALUES = INTERNAL_HOSTER_NAMES.map {
it.lowercase().substringBefore(" (")
Expand Down Expand Up @@ -542,6 +565,9 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {

private const val PREF_SUB_KEY = "preferred_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 ==============================
Expand Down Expand Up @@ -588,17 +614,6 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
}
}.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 {
key = PREF_QUALITY_KEY
title = "Preferred quality"
Expand Down Expand Up @@ -646,6 +661,28 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {
preferences.edit().putString(key, entry).commit()
}
}.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
Expand All @@ -668,4 +705,7 @@ class AllAnimeChi : ConfigurableAnimeSource, AnimeHttpSource() {

private val SharedPreferences.removeRaw
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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Response
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()

Expand All @@ -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)",
)

private val playlistUtils by lazy { PlaylistUtils(client, playlistHeaders) }
private val playlistUtils by lazy { PlaylistUtils(client, headers) }

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 {
set("host", blogUrl.toHttpUrl().host)
}.build()
Expand All @@ -37,29 +39,35 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders

val videoList = videoData.links.flatMap {
when {
it.hls == true -> getFromHls(server, it, removeRaw)
it.mp4 == true -> getFromMp4(server, it)
it.hls == true -> getFromHls(server, it, useHosterName, removeRaw)
it.mp4 == true -> getFromMp4(server, it, useHosterName)
else -> emptyList()
}
}

return videoList
}

private fun getFromMp4(server: AllAnimeChi.Server, data: VideoData.LinkObject): List<Pair<Video, Float>> {
val baseName = "${server.sourceName} - ${data.resolutionStr}"
private fun getFromMp4(server: AllAnimeChi.Server, data: VideoData.LinkObject, useHosterName: Boolean): List<Pair<Video, Float>> {
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)
return listOf(
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()
val host = if (useHosterName) getHostName(data.link, server.sourceName) else server.sourceName

val linkHost = data.link.toHttpUrl().host

// 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
val baseName = if (linkHost.contains("crunchyroll")) {
Expand All @@ -73,8 +81,7 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders
.replace("vo_SUB", "Sub")
.replace("SUB", "Sub")
} else {
"${server.sourceName} - ${data.resolutionStr}"
.replace("Luf-mp4", "Luf-mp4 (gogo)")
"$host - ${data.resolutionStr}"
}

// Get stuff
Expand All @@ -91,7 +98,7 @@ class InternalExtractor(private val client: OkHttpClient, private val apiHeaders

val videoList = playlistUtils.extractFromHls(
data.link,
videoNameGen = { q -> "$baseName - $q" },
videoNameGen = { q -> "$baseName - ${data.resolutionStr} - $q" },
masterHeadersGen = { _, _ -> masterHeaders },
)

Expand All @@ -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 ==============================

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
data class VideoData(
val links: List<LinkObject>,
Expand Down

0 comments on commit d60ff52

Please sign in to comment.