Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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