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:
parent
42065686b3
commit
dcb5c7ef2f
3 changed files with 85 additions and 28 deletions
2
.github/workflows/build_push.yml
vendored
2
.github/workflows/build_push.yml
vendored
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue