Aniwave updates (#98)

* new encryption keys

* updated extVersionCode to 73

* fixed null exception in episodeListRequest

* removed old domains

* added custom domain preference

* added new episode path id resolution

* updated/fixed custom domain preference setting

*updated build_push.yml
This commit is contained in:
almightyhak 2024-08-04 00:32:19 +07:00 committed by almightyhak
parent 42065686b3
commit dcb5c7ef2f
3 changed files with 85 additions and 28 deletions

View file

@ -44,7 +44,7 @@ jobs:
- name: Bump extensions that uses a modified lib - name: Bump extensions that uses a modified lib
if: steps.modified-libs.outputs.any_changed == 'true' if: steps.modified-libs.outputs.any_changed == 'true'
run: | run: |
./.github/scripts/bump-versions.py ${{ steps.modified-libs.outputs.all_changed_files }} chmod +x ./.github/scripts/bump-versions.py ${{ steps.modified-libs.outputs.all_changed_files }}
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@a494d935f4b56874c4a5a87d19af7afcf3a163d0 # v2 uses: gradle/wrapper-validation-action@a494d935f4b56874c4a5a87d19af7afcf3a163d0 # v2

View file

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

View file

@ -2,7 +2,10 @@ package eu.kanade.tachiyomi.animeextension.en.nineanime
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Log
import android.webkit.URLUtil
import android.widget.Toast import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
@ -38,7 +41,12 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val id: Long = 98855593379717478 override val id: Long = 98855593379717478
override val baseUrl by lazy { override val baseUrl by lazy {
preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! val customDomain = preferences.getString(PREF_CUSTOM_DOMAIN_KEY, null)
if (customDomain.isNullOrBlank()) {
preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!!
} else {
customDomain
}
} }
override val lang = "en" override val lang = "en"
@ -89,7 +97,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filters = AniwaveFilters.getSearchParameters(filters) val filters = AniwaveFilters.getSearchParameters(filters)
val vrf = if (query.isNotBlank()) utils.vrfEncrypt(KEY_ENCRYPT, query) else "" val vrf = if (query.isNotBlank()) utils.vrfEncrypt(ENCRYPTION_KEY, query) else ""
var url = "$baseUrl/filter?keyword=$query" var url = "$baseUrl/filter?keyword=$query"
if (filters.genre.isNotBlank()) url += filters.genre if (filters.genre.isNotBlank()) url += filters.genre
@ -116,30 +124,39 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime = SAnime.create().apply { override fun animeDetailsParse(document: Document): SAnime {
title = document.select("h1.title").text() val anime = SAnime.create()
genre = document.select("div:contains(Genre) > span > a").joinToString { it.text() } val newDocument = resolveSearchAnime(anime, document)
description = document.select("div.synopsis > div.shorting > div.content").text() anime.apply {
author = document.select("div:contains(Studio) > span > a").text() title = newDocument.select("h1.title").text()
status = parseStatus(document.select("div:contains(Status) > span").text()) genre = newDocument.select("div:contains(Genre) > span > a").joinToString { it.text() }
description = newDocument.select("div.synopsis > div.shorting > div.content").text()
author = newDocument.select("div:contains(Studio) > span > a").text()
status = parseStatus(newDocument.select("div:contains(Status) > span").text())
val altName = "Other name(s): " val altName = "Other name(s): "
document.select("h1.title").attr("data-jp").let { newDocument.select("h1.title").attr("data-jp").let {
if (it.isNotBlank()) { if (it.isNotBlank()) {
description = when { description = when {
description.isNullOrBlank() -> altName + it description.isNullOrBlank() -> altName + it
else -> description + "\n\n$altName" + it else -> description + "\n\n$altName" + it
}
} }
} }
} }
return anime
} }
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun episodeListRequest(anime: SAnime): Request { override fun episodeListRequest(anime: SAnime): Request {
val id = client.newCall(GET(baseUrl + anime.url)).execute().asJsoup() Log.i(name, "episodeListRequest")
.selectFirst("div[data-id]")!!.attr("data-id") val response = client.newCall(GET(baseUrl + anime.url)).execute()
val vrf = utils.vrfEncrypt(KEY_ENCRYPT, id) var document = response.asJsoup()
document = resolveSearchAnime(anime, document)
val id = document.selectFirst("div[data-id]")?.attr("data-id") ?: throw Exception("ID not found")
val vrf = utils.vrfEncrypt(ENCRYPTION_KEY, id)
val listHeaders = headers.newBuilder().apply { val listHeaders = headers.newBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01") add("Accept", "application/json, text/javascript, */*; q=0.01")
@ -195,7 +212,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListRequest(episode: SEpisode): Request { override fun videoListRequest(episode: SEpisode): Request {
val ids = episode.url.substringBefore("&") val ids = episode.url.substringBefore("&")
val vrf = utils.vrfEncrypt(KEY_ENCRYPT, ids) val vrf = utils.vrfEncrypt(ENCRYPTION_KEY, ids)
val url = "/ajax/server/list/$ids?vrf=$vrf" val url = "/ajax/server/list/$ids?vrf=$vrf"
val epurl = episode.url.substringAfter("epurl=") val epurl = episode.url.substringAfter("epurl=")
@ -248,7 +265,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) } private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
private fun extractVideo(server: VideoData, epUrl: String): List<Video> { private fun extractVideo(server: VideoData, epUrl: String): List<Video> {
val vrf = utils.vrfEncrypt(KEY_ENCRYPT, server.serverId) val vrf = utils.vrfEncrypt(ENCRYPTION_KEY, server.serverId)
val listHeaders = headers.newBuilder().apply { val listHeaders = headers.newBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01") add("Accept", "application/json, text/javascript, */*; q=0.01")
@ -263,7 +280,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return runCatching { return runCatching {
val parsed = response.parseAs<ServerResponse>() val parsed = response.parseAs<ServerResponse>()
val embedLink = utils.vrfDecrypt(KEY_DECRYPT, parsed.result.url) val embedLink = utils.vrfDecrypt(DECRYPTION_KEY, parsed.result.url)
when (server.serverName) { when (server.serverName) {
"vidstream", "megaf" -> { "vidstream", "megaf" -> {
vidsrcExtractor.videosFromUrl(embedLink, server.serverName, server.type) vidsrcExtractor.videosFromUrl(embedLink, server.serverName, server.type)
@ -308,6 +325,16 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
} }
private fun resolveSearchAnime(anime: SAnime, document: Document): Document {
if (document.location().startsWith("$baseUrl/filter?keyword=")) { // redirected to search
val element = document.selectFirst(searchAnimeSelector())
val foundAnimePath = element?.selectFirst("a[href]")?.attr("href") ?: throw Exception("Search element not found (resolveSearch)")
anime.url = foundAnimePath // probably doesn't work as intended
return client.newCall(GET(baseUrl + foundAnimePath)).execute().asJsoup()
}
return document
}
private fun getHosters(): Set<String> { private fun getHosters(): Set<String> {
val hosterSelection = preferences.getStringSet(PREF_HOSTER_KEY, PREF_HOSTER_DEFAULT)!! val hosterSelection = preferences.getStringSet(PREF_HOSTER_KEY, PREF_HOSTER_DEFAULT)!!
var invalidRecord = false var invalidRecord = false
@ -338,6 +365,8 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private const val PREF_DOMAIN_KEY = "preferred_domain" private const val PREF_DOMAIN_KEY = "preferred_domain"
private const val PREF_DOMAIN_DEFAULT = "https://aniwave.to" private const val PREF_DOMAIN_DEFAULT = "https://aniwave.to"
private const val PREF_CUSTOM_DOMAIN_KEY = "custom_domain"
private const val PREF_QUALITY_KEY = "preferred_quality" private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "1080" private const val PREF_QUALITY_DEFAULT = "1080"
@ -371,22 +400,25 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private val TYPES = arrayOf("Sub", "Softsub", "Dub") private val TYPES = arrayOf("Sub", "Softsub", "Dub")
private val PREF_TYPES_TOGGLE_DEFAULT = TYPES.toSet() private val PREF_TYPES_TOGGLE_DEFAULT = TYPES.toSet()
// https://rowdy-avocado.github.io/multi-keys/ private const val DECRYPTION_KEY = "ctpAbOz5u7S6OMkx"
private const val KEY_DECRYPT = "ctpAbOz5u7S6OMkx" private const val ENCRYPTION_KEY = "T78s2WjTc7hSIZZR"
private const val KEY_ENCRYPT = "p01EDKu734HJP1Tm"
} }
// ============================== Settings ============================== // ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
// validate hosters preferences and if invalid reset // validate hosters preferences and if invalid reset
getHosters() try {
getHosters()
} catch (e: Exception) {
Log.w(name, e.toString())
}
ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = PREF_DOMAIN_KEY key = PREF_DOMAIN_KEY
title = "Preferred domain" title = "Preferred domain"
entries = arrayOf("aniwave.to", "aniwave.li", "aniwave.ws", "aniwave.vc") entries = arrayOf("aniwave.to", "aniwavetv.to (unofficial)")
entryValues = arrayOf("https://aniwave.to", "https://aniwave.li", "https://aniwave.ws", "https://aniwave.vc") entryValues = arrayOf("https://aniwave.to", "https://aniwavetv.to")
setDefaultValue(PREF_DOMAIN_DEFAULT) setDefaultValue(PREF_DOMAIN_DEFAULT)
summary = "%s" summary = "%s"
@ -482,5 +514,30 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
preferences.edit().putStringSet(key, newValue as Set<String>).commit() preferences.edit().putStringSet(key, newValue as Set<String>).commit()
} }
}.also(screen::addPreference) }.also(screen::addPreference)
EditTextPreference(screen.context).apply {
key = PREF_CUSTOM_DOMAIN_KEY
title = "Custom domain"
setDefaultValue(null)
val currentValue = preferences.getString(PREF_CUSTOM_DOMAIN_KEY, null)
summary = if (currentValue.isNullOrBlank()) {
"Custom domain of your choosing"
} else {
"Domain: \"$currentValue\". \nLeave blank to disable. Overrides any domain preferences!"
}
setOnPreferenceChangeListener { _, newValue ->
val newDomain = newValue as String
if (newDomain.isBlank() || URLUtil.isValidUrl(newDomain)) {
summary = "Restart to apply changes"
Toast.makeText(screen.context, "Restart Aniyomi to apply changes", Toast.LENGTH_LONG).show()
preferences.edit().putString(key, newDomain).apply()
true
} else {
Toast.makeText(screen.context, "Invalid url. Url example: https://aniwave.to", Toast.LENGTH_LONG).show()
false
}
}
}.also(screen::addPreference)
} }
} }