feat(src/de): Remove login for serienstream and aniworld (#3201)
This commit is contained in:
parent
79c7f052d2
commit
ebb2163465
@ -1,5 +1,5 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'AniWorld (experimental)'
|
extName = 'AniWorld'
|
||||||
extClass = '.AniWorld'
|
extClass = '.AniWorld'
|
||||||
extVersionCode = 23
|
extVersionCode = 23
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.animeextension.de.aniworld
|
package eu.kanade.tachiyomi.animeextension.de.aniworld
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
|
|
||||||
object AWConstants {
|
object AWConstants {
|
||||||
const val NAME_DOOD = "Doodstream"
|
const val NAME_DOOD = "Doodstream"
|
||||||
const val NAME_STAPE = "Streamtape"
|
const val NAME_STAPE = "Streamtape"
|
||||||
@ -29,14 +27,4 @@ object AWConstants {
|
|||||||
const val PREFERRED_HOSTER = "preferred_hoster"
|
const val PREFERRED_HOSTER = "preferred_hoster"
|
||||||
const val PREFERRED_LANG = "preferred_lang"
|
const val PREFERRED_LANG = "preferred_lang"
|
||||||
const val HOSTER_SELECTION = "hoster_selection"
|
const val HOSTER_SELECTION = "hoster_selection"
|
||||||
|
|
||||||
const val LOGIN_TITLE = "E-Mail-Adresse"
|
|
||||||
const val LOGIN_DEFAULT = ""
|
|
||||||
const val PASSWORD_TITLE = "Passwort"
|
|
||||||
const val PASSWORD_DEFAULT = ""
|
|
||||||
|
|
||||||
const val LOGIN_URL = "https://aniworld.to/login"
|
|
||||||
|
|
||||||
fun getPrefBaseLogin(preferences: SharedPreferences): String = preferences.getString(LOGIN_TITLE, LOGIN_DEFAULT)!!
|
|
||||||
fun getPrefBasePassword(preferences: SharedPreferences): String = preferences.getString(PASSWORD_TITLE, PASSWORD_DEFAULT)!!
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,6 @@ package eu.kanade.tachiyomi.animeextension.de.aniworld
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.text.InputType
|
|
||||||
import android.util.Log
|
|
||||||
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
|
||||||
@ -30,7 +26,6 @@ import kotlinx.serialization.json.jsonObject
|
|||||||
import kotlinx.serialization.json.jsonPrimitive
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
@ -41,15 +36,14 @@ import uy.kohesive.injekt.injectLazy
|
|||||||
|
|
||||||
class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||||
|
|
||||||
override val name = "AniWorld (experimental)"
|
override val name = "AniWorld"
|
||||||
|
|
||||||
override val baseUrl = "https://aniworld.to"
|
override val baseUrl = "https://aniworld.to"
|
||||||
|
|
||||||
private val baseLogin by lazy { AWConstants.getPrefBaseLogin(preferences) }
|
|
||||||
private val basePassword by lazy { AWConstants.getPrefBasePassword(preferences) }
|
|
||||||
|
|
||||||
override val lang = "de"
|
override val lang = "de"
|
||||||
|
|
||||||
|
override val id: Long = 8286900189409315836
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
private val preferences: SharedPreferences by lazy {
|
private val preferences: SharedPreferences by lazy {
|
||||||
@ -60,14 +54,8 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
.addInterceptor(DdosGuardInterceptor(network.client))
|
.addInterceptor(DdosGuardInterceptor(network.client))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val authClient = network.client.newBuilder()
|
|
||||||
.addInterceptor(AniWorldInterceptor(client, preferences))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
val context = Injekt.get<Application>()
|
|
||||||
|
|
||||||
// ===== POPULAR ANIME =====
|
// ===== POPULAR ANIME =====
|
||||||
override fun popularAnimeSelector(): String = "div.seriesListContainer div"
|
override fun popularAnimeSelector(): String = "div.seriesListContainer div"
|
||||||
|
|
||||||
@ -78,7 +66,6 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeFromElement(element: Element): SAnime {
|
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||||
context
|
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
val linkElement = element.selectFirst("a")!!
|
val linkElement = element.selectFirst("a")!!
|
||||||
anime.url = linkElement.attr("href")
|
anime.url = linkElement.attr("href")
|
||||||
@ -190,14 +177,14 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
private fun parseEpisodesFromSeries(element: Element): List<SEpisode> {
|
private fun parseEpisodesFromSeries(element: Element): List<SEpisode> {
|
||||||
val seasonId = element.attr("abs:href")
|
val seasonId = element.attr("abs:href")
|
||||||
val episodesHtml = authClient.newCall(GET(seasonId)).execute().asJsoup()
|
val episodesHtml = client.newCall(GET(seasonId)).execute().asJsoup()
|
||||||
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
||||||
return episodeElements.map { episodeFromElement(it) }
|
return episodeElements.map { episodeFromElement(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMoviesFromSeries(element: Element): List<SEpisode> {
|
private fun parseMoviesFromSeries(element: Element): List<SEpisode> {
|
||||||
val seasonId = element.attr("abs:href")
|
val seasonId = element.attr("abs:href")
|
||||||
val episodesHtml = authClient.newCall(GET(seasonId)).execute().asJsoup()
|
val episodesHtml = client.newCall(GET(seasonId)).execute().asJsoup()
|
||||||
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
||||||
return episodeElements.map { episodeFromElement(it) }
|
return episodeElements.map { episodeFromElement(it) }
|
||||||
}
|
}
|
||||||
@ -228,8 +215,6 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val redirectlink = document.select("ul.row li")
|
val redirectlink = document.select("ul.row li")
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val hosterSelection = preferences.getStringSet(AWConstants.HOSTER_SELECTION, null)
|
val hosterSelection = preferences.getStringSet(AWConstants.HOSTER_SELECTION, null)
|
||||||
val redirectInterceptor = client.newBuilder().addInterceptor(RedirectInterceptor()).build()
|
|
||||||
val jsInterceptor = client.newBuilder().addInterceptor(JsInterceptor()).build()
|
|
||||||
redirectlink.forEach {
|
redirectlink.forEach {
|
||||||
val langkey = it.attr("data-lang-key")
|
val langkey = it.attr("data-lang-key")
|
||||||
val language = getlanguage(langkey)
|
val language = getlanguage(langkey)
|
||||||
@ -238,19 +223,13 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
if (hosterSelection != null) {
|
if (hosterSelection != null) {
|
||||||
when {
|
when {
|
||||||
hoster.contains("VOE") && hosterSelection.contains(AWConstants.NAME_VOE) -> {
|
hoster.contains("VOE") && hosterSelection.contains(AWConstants.NAME_VOE) -> {
|
||||||
var url = redirectInterceptor.newCall(GET(redirectgs)).execute().request.url.toString()
|
val url = client.newCall(GET(redirectgs)).execute().request.url.toString()
|
||||||
if (url.contains("payload") || url.contains(redirectgs)) {
|
|
||||||
url = recapbypass(jsInterceptor, redirectgs)
|
|
||||||
}
|
|
||||||
videoList.addAll(VoeExtractor(client).videosFromUrl(url, "($language) "))
|
videoList.addAll(VoeExtractor(client).videosFromUrl(url, "($language) "))
|
||||||
}
|
}
|
||||||
|
|
||||||
hoster.contains("Doodstream") && hosterSelection.contains(AWConstants.NAME_DOOD) -> {
|
hoster.contains("Doodstream") && hosterSelection.contains(AWConstants.NAME_DOOD) -> {
|
||||||
val quality = "Doodstream $language"
|
val quality = "Doodstream $language"
|
||||||
var url = redirectInterceptor.newCall(GET(redirectgs)).execute().request.url.toString()
|
val url = client.newCall(GET(redirectgs)).execute().request.url.toString()
|
||||||
if (url.contains("payload") || url.contains(redirectgs)) {
|
|
||||||
url = recapbypass(jsInterceptor, redirectgs)
|
|
||||||
}
|
|
||||||
val video = DoodExtractor(client).videoFromUrl(url, quality)
|
val video = DoodExtractor(client).videoFromUrl(url, quality)
|
||||||
if (video != null) {
|
if (video != null) {
|
||||||
videoList.add(video)
|
videoList.add(video)
|
||||||
@ -259,10 +238,7 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
hoster.contains("Streamtape") && hosterSelection.contains(AWConstants.NAME_STAPE) -> {
|
hoster.contains("Streamtape") && hosterSelection.contains(AWConstants.NAME_STAPE) -> {
|
||||||
val quality = "Streamtape $language"
|
val quality = "Streamtape $language"
|
||||||
var url = redirectInterceptor.newCall(GET(redirectgs)).execute().request.url.toString()
|
val url = client.newCall(GET(redirectgs)).execute().request.url.toString()
|
||||||
if (url.contains("payload") || url.contains(redirectgs)) {
|
|
||||||
url = recapbypass(jsInterceptor, redirectgs)
|
|
||||||
}
|
|
||||||
val video = StreamTapeExtractor(client).videoFromUrl(url, quality)
|
val video = StreamTapeExtractor(client).videoFromUrl(url, quality)
|
||||||
if (video != null) {
|
if (video != null) {
|
||||||
videoList.add(video)
|
videoList.add(video)
|
||||||
@ -270,10 +246,7 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
hoster.contains("Vidoza") && hosterSelection.contains(AWConstants.NAME_VIZ) -> {
|
hoster.contains("Vidoza") && hosterSelection.contains(AWConstants.NAME_VIZ) -> {
|
||||||
val quality = "Vidoza $language"
|
val quality = "Vidoza $language"
|
||||||
var url = redirectInterceptor.newCall(GET(redirectgs)).execute().request.url.toString()
|
val url = client.newCall(GET(redirectgs)).execute().request.url.toString()
|
||||||
if (url.contains("payload") || url.contains(redirectgs)) {
|
|
||||||
url = recapbypass(jsInterceptor, redirectgs)
|
|
||||||
}
|
|
||||||
val video = VidozaExtractor(client).videoFromUrl(url, quality)
|
val video = VidozaExtractor(client).videoFromUrl(url, quality)
|
||||||
if (video != null) {
|
if (video != null) {
|
||||||
videoList.add(video)
|
videoList.add(video)
|
||||||
@ -285,12 +258,6 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return videoList
|
return videoList
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recapbypass(jsInterceptor: OkHttpClient, redirectgs: String): String {
|
|
||||||
val token = jsInterceptor.newCall(GET(redirectgs)).execute().request.header("url").toString()
|
|
||||||
val url = client.newCall(GET("$redirectgs?token=$token&original=")).execute().request.url.toString()
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getlanguage(langkey: String): String? {
|
private fun getlanguage(langkey: String): String? {
|
||||||
when {
|
when {
|
||||||
langkey.contains("${AWConstants.KEY_GER_SUB}") -> {
|
langkey.contains("${AWConstants.KEY_GER_SUB}") -> {
|
||||||
@ -394,37 +361,8 @@ class AniWorld : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
|
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
screen.addPreference(screen.editTextPreference(AWConstants.LOGIN_TITLE, AWConstants.LOGIN_DEFAULT, baseLogin, false, ""))
|
|
||||||
screen.addPreference(screen.editTextPreference(AWConstants.PASSWORD_TITLE, AWConstants.PASSWORD_DEFAULT, basePassword, true, ""))
|
|
||||||
screen.addPreference(subPref)
|
screen.addPreference(subPref)
|
||||||
screen.addPreference(hosterPref)
|
screen.addPreference(hosterPref)
|
||||||
screen.addPreference(hosterSelection)
|
screen.addPreference(hosterSelection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceScreen.editTextPreference(title: String, default: String, value: String, isPassword: Boolean = false, placeholder: String): EditTextPreference {
|
|
||||||
return EditTextPreference(context).apply {
|
|
||||||
key = title
|
|
||||||
this.title = title
|
|
||||||
summary = value.ifEmpty { placeholder }
|
|
||||||
this.setDefaultValue(default)
|
|
||||||
dialogTitle = title
|
|
||||||
|
|
||||||
if (isPassword) {
|
|
||||||
setOnBindEditTextListener {
|
|
||||||
it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
try {
|
|
||||||
val res = preferences.edit().putString(title, newValue as String).commit()
|
|
||||||
Toast.makeText(context, "Starte Aniyomi neu, um die Einstellungen zu übernehmen.", Toast.LENGTH_LONG).show()
|
|
||||||
res
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Anicloud", "Fehler beim festlegen der Einstellung.", e)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.animeextension.de.aniworld
|
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.util.Log
|
|
||||||
import android.webkit.CookieManager
|
|
||||||
import eu.kanade.tachiyomi.network.POST
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import okhttp3.FormBody
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.Interceptor
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Response
|
|
||||||
|
|
||||||
class AniWorldInterceptor(private val client: OkHttpClient, private val preferences: SharedPreferences) : Interceptor {
|
|
||||||
|
|
||||||
private val cookieManager by lazy { CookieManager.getInstance() }
|
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
val originalRequest = chain.request()
|
|
||||||
val cookies = cookieManager.getCookie(originalRequest.url.toString())
|
|
||||||
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
|
|
||||||
cookies.split(";").mapNotNull { Cookie.parse(originalRequest.url, it) }
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
val sessionCookie = oldCookie.firstOrNull { it.name == "rememberLogin" }
|
|
||||||
if (!sessionCookie?.value.isNullOrEmpty()) {
|
|
||||||
return chain.proceed(originalRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
val newCookie = getNewCookie(originalRequest.url)
|
|
||||||
// ?: throw Exception("Bitte im Browser oder in den Erweiterungs-Einstellungen einloggen.")
|
|
||||||
val newCookieHeader = buildString {
|
|
||||||
(oldCookie + newCookie).forEachIndexed { index, cookie ->
|
|
||||||
if (index > 0) append("; ")
|
|
||||||
if (cookie != null) {
|
|
||||||
append(cookie.name).append('=').append(cookie.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chain.proceed(
|
|
||||||
originalRequest
|
|
||||||
.newBuilder()
|
|
||||||
.addHeader("cookie", newCookieHeader)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNewCookie(url: HttpUrl): Cookie? {
|
|
||||||
val cookies = cookieManager.getCookie(url.toString())
|
|
||||||
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
|
|
||||||
cookies.split(";").mapNotNull { Cookie.parse(url, it) }
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
val sessionCookie = oldCookie.firstOrNull { it.name == "rememberLogin" }
|
|
||||||
if (!sessionCookie?.value.isNullOrEmpty()) {
|
|
||||||
return sessionCookie
|
|
||||||
}
|
|
||||||
val email = AWConstants.getPrefBaseLogin(preferences)
|
|
||||||
val password = AWConstants.getPrefBasePassword(preferences)
|
|
||||||
if (email.isEmpty() || password.isEmpty()) return null
|
|
||||||
val payload = FormBody.Builder()
|
|
||||||
.add("email", email)
|
|
||||||
.add("password", password)
|
|
||||||
.add("autoLogin", "on")
|
|
||||||
.build()
|
|
||||||
val headers = Headers.Builder()
|
|
||||||
.add("Upgrade-Insecure-Requests", "1")
|
|
||||||
.add("Referer", "https://aniworld.to")
|
|
||||||
// .add("user-agent", "Mozilla/5.0 (Linux; Android 12; Pixel 5 Build/SP2A.220405.004; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Safari/537.36")
|
|
||||||
.build()
|
|
||||||
return client.newCall(POST(AWConstants.LOGIN_URL, body = payload, headers = headers)).execute().header("set-cookie")?.let {
|
|
||||||
Log.i("bruh", it)
|
|
||||||
Cookie.parse(url, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
@ -1,99 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.animeextension.de.aniworld
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Application
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.webkit.WebResourceRequest
|
|
||||||
import android.webkit.WebResourceResponse
|
|
||||||
import android.webkit.WebView
|
|
||||||
import android.webkit.WebViewClient
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import okhttp3.Headers.Companion.toHeaders
|
|
||||||
import okhttp3.Interceptor
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class RedirectInterceptor : Interceptor {
|
|
||||||
|
|
||||||
private val context = Injekt.get<Application>()
|
|
||||||
private val handler by lazy { Handler(Looper.getMainLooper()) }
|
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
val originalRequest = chain.request()
|
|
||||||
|
|
||||||
val newRequest = resolveWithWebView(originalRequest) ?: throw Exception("Versuche es später nochmal")
|
|
||||||
|
|
||||||
return chain.proceed(newRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
private fun resolveWithWebView(request: Request): Request? {
|
|
||||||
// We need to lock this thread until the WebView finds the challenge solution url, because
|
|
||||||
// OkHttp doesn't support asynchronous interceptors.
|
|
||||||
val latch = CountDownLatch(1)
|
|
||||||
|
|
||||||
var webView: WebView? = null
|
|
||||||
|
|
||||||
val origRequestUrl = request.url.toString()
|
|
||||||
|
|
||||||
val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
|
|
||||||
|
|
||||||
var newRequest: Request? = null
|
|
||||||
|
|
||||||
var test = true
|
|
||||||
|
|
||||||
handler.post {
|
|
||||||
val webview = WebView(context)
|
|
||||||
webView = webview
|
|
||||||
with(webview.settings) {
|
|
||||||
javaScriptEnabled = true
|
|
||||||
domStorageEnabled = true
|
|
||||||
databaseEnabled = true
|
|
||||||
useWideViewPort = false
|
|
||||||
loadWithOverviewMode = false
|
|
||||||
userAgentString = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:106.0) Gecko/20100101 Firefox/106.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
webview.webViewClient = object : WebViewClient() {
|
|
||||||
override fun shouldInterceptRequest(
|
|
||||||
view: WebView,
|
|
||||||
request: WebResourceRequest,
|
|
||||||
): WebResourceResponse? {
|
|
||||||
if (request.url.toString().contains("payload")) {
|
|
||||||
newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders())
|
|
||||||
latch.countDown()
|
|
||||||
} else if (request.url.toString().contains("https://aniworld.to/redirect/") && request.url.toString().contains("token")) {
|
|
||||||
newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders())
|
|
||||||
latch.countDown()
|
|
||||||
} else {
|
|
||||||
test = false
|
|
||||||
}
|
|
||||||
if (test == false) {
|
|
||||||
newRequest = GET(origRequestUrl, headers.toHeaders())
|
|
||||||
latch.countDown()
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.shouldInterceptRequest(view, request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
webView?.loadUrl(origRequestUrl, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait a reasonable amount of time to retrieve the solution. The minimum should be
|
|
||||||
// around 4 seconds but it can take more due to slow networks or server issues.
|
|
||||||
latch.await(12, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
handler.post {
|
|
||||||
webView?.stopLoading()
|
|
||||||
webView?.destroy()
|
|
||||||
webView = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return newRequest
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Serienstream'
|
extName = 'Serienstream'
|
||||||
extClass = '.Serienstream'
|
extClass = '.Serienstream'
|
||||||
extVersionCode = 17
|
extVersionCode = 18
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.animeextension.de.serienstream
|
package eu.kanade.tachiyomi.animeextension.de.serienstream
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
|
|
||||||
object SConstants {
|
object SConstants {
|
||||||
const val NAME_DOOD = "Doodstream"
|
const val NAME_DOOD = "Doodstream"
|
||||||
const val NAME_STAPE = "Streamtape"
|
const val NAME_STAPE = "Streamtape"
|
||||||
@ -27,14 +25,4 @@ object SConstants {
|
|||||||
const val PREFERRED_HOSTER = "preferred_hoster"
|
const val PREFERRED_HOSTER = "preferred_hoster"
|
||||||
const val PREFERRED_LANG = "preferred_lang"
|
const val PREFERRED_LANG = "preferred_lang"
|
||||||
const val HOSTER_SELECTION = "hoster_selection"
|
const val HOSTER_SELECTION = "hoster_selection"
|
||||||
|
|
||||||
const val LOGIN_TITLE = "E-Mail-Adresse"
|
|
||||||
const val LOGIN_DEFAULT = ""
|
|
||||||
const val PASSWORD_TITLE = "Passwort"
|
|
||||||
const val PASSWORD_DEFAULT = ""
|
|
||||||
|
|
||||||
const val LOGIN_URL = "http://186.2.175.5/login"
|
|
||||||
|
|
||||||
fun getPrefBaseLogin(preferences: SharedPreferences): String = preferences.getString(LOGIN_TITLE, LOGIN_DEFAULT)!!
|
|
||||||
fun getPrefBasePassword(preferences: SharedPreferences): String = preferences.getString(PASSWORD_TITLE, PASSWORD_DEFAULT)!!
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,6 @@ package eu.kanade.tachiyomi.animeextension.de.serienstream
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.text.InputType
|
|
||||||
import android.util.Log
|
|
||||||
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
|
||||||
@ -22,7 +18,6 @@ import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
|
|||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
@ -44,9 +39,6 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override val baseUrl = "http://186.2.175.5"
|
override val baseUrl = "http://186.2.175.5"
|
||||||
|
|
||||||
private val baseLogin by lazy { SConstants.getPrefBaseLogin(preferences) }
|
|
||||||
private val basePassword by lazy { SConstants.getPrefBasePassword(preferences) }
|
|
||||||
|
|
||||||
override val lang = "de"
|
override val lang = "de"
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
@ -59,14 +51,8 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
.addInterceptor(DdosGuardInterceptor(network.client))
|
.addInterceptor(DdosGuardInterceptor(network.client))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val authClient = network.client.newBuilder()
|
|
||||||
.addInterceptor(SerienstreamInterceptor(client, preferences))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
val context = Injekt.get<Application>()
|
|
||||||
|
|
||||||
// ===== POPULAR ANIME =====
|
// ===== POPULAR ANIME =====
|
||||||
override fun popularAnimeSelector(): String = "div.seriesListContainer div"
|
override fun popularAnimeSelector(): String = "div.seriesListContainer div"
|
||||||
|
|
||||||
@ -77,7 +63,6 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeFromElement(element: Element): SAnime {
|
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||||
context
|
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
val linkElement = element.selectFirst("a")!!
|
val linkElement = element.selectFirst("a")!!
|
||||||
anime.url = linkElement.attr("href")
|
anime.url = linkElement.attr("href")
|
||||||
@ -187,14 +172,14 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
private fun parseEpisodesFromSeries(element: Element): List<SEpisode> {
|
private fun parseEpisodesFromSeries(element: Element): List<SEpisode> {
|
||||||
val seasonId = element.attr("abs:href")
|
val seasonId = element.attr("abs:href")
|
||||||
val episodesHtml = authClient.newCall(GET(seasonId)).execute().asJsoup()
|
val episodesHtml = client.newCall(GET(seasonId)).execute().asJsoup()
|
||||||
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
||||||
return episodeElements.map { episodeFromElement(it) }
|
return episodeElements.map { episodeFromElement(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMoviesFromSeries(element: Element): List<SEpisode> {
|
private fun parseMoviesFromSeries(element: Element): List<SEpisode> {
|
||||||
val seasonId = element.attr("abs:href")
|
val seasonId = element.attr("abs:href")
|
||||||
val episodesHtml = authClient.newCall(GET(seasonId)).execute().asJsoup()
|
val episodesHtml = client.newCall(GET(seasonId)).execute().asJsoup()
|
||||||
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
val episodeElements = episodesHtml.select("table.seasonEpisodesList tbody tr")
|
||||||
return episodeElements.map { episodeFromElement(it) }
|
return episodeElements.map { episodeFromElement(it) }
|
||||||
}
|
}
|
||||||
@ -222,7 +207,7 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val redirectlink = document.select("ul.row li")
|
val redirectlink = document.select("div.hosterSiteVideo ul.row li")
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val hosterSelection = preferences.getStringSet(SConstants.HOSTER_SELECTION, null)
|
val hosterSelection = preferences.getStringSet(SConstants.HOSTER_SELECTION, null)
|
||||||
redirectlink.forEach {
|
redirectlink.forEach {
|
||||||
@ -363,37 +348,8 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
|
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
screen.addPreference(screen.editTextPreference(SConstants.LOGIN_TITLE, SConstants.LOGIN_DEFAULT, baseLogin, false, ""))
|
|
||||||
screen.addPreference(screen.editTextPreference(SConstants.PASSWORD_TITLE, SConstants.PASSWORD_DEFAULT, basePassword, true, ""))
|
|
||||||
screen.addPreference(subPref)
|
screen.addPreference(subPref)
|
||||||
screen.addPreference(hosterPref)
|
screen.addPreference(hosterPref)
|
||||||
screen.addPreference(hosterSelection)
|
screen.addPreference(hosterSelection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceScreen.editTextPreference(title: String, default: String, value: String, isPassword: Boolean = false, placeholder: String): EditTextPreference {
|
|
||||||
return EditTextPreference(context).apply {
|
|
||||||
key = title
|
|
||||||
this.title = title
|
|
||||||
summary = value.ifEmpty { placeholder }
|
|
||||||
this.setDefaultValue(default)
|
|
||||||
dialogTitle = title
|
|
||||||
|
|
||||||
if (isPassword) {
|
|
||||||
setOnBindEditTextListener {
|
|
||||||
it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
try {
|
|
||||||
val res = preferences.edit().putString(title, newValue as String).commit()
|
|
||||||
Toast.makeText(context, "Starte Aniyomi neu, um die Einstellungen zu übernehmen.", Toast.LENGTH_LONG).show()
|
|
||||||
res
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("SerienStream", "Fehler beim festlegen der Einstellung.", e)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.animeextension.de.serienstream
|
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.util.Log
|
|
||||||
import android.webkit.CookieManager
|
|
||||||
import eu.kanade.tachiyomi.network.POST
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import okhttp3.FormBody
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.Interceptor
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Response
|
|
||||||
|
|
||||||
class SerienstreamInterceptor(private val client: OkHttpClient, private val preferences: SharedPreferences) : Interceptor {
|
|
||||||
|
|
||||||
private val cookieManager by lazy { CookieManager.getInstance() }
|
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
val originalRequest = chain.request()
|
|
||||||
val cookies = cookieManager.getCookie(originalRequest.url.toString())
|
|
||||||
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
|
|
||||||
cookies.split(";").mapNotNull { Cookie.parse(originalRequest.url, it) }
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
val sessionCookie = oldCookie.firstOrNull { it.name == "rememberLogin" }
|
|
||||||
if (!sessionCookie?.value.isNullOrEmpty()) {
|
|
||||||
return chain.proceed(originalRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
val newCookie = getNewCookie(originalRequest.url)
|
|
||||||
// ?: throw Exception("Bitte im Browser oder in den Erweiterungs-Einstellungen einloggen.")
|
|
||||||
val newCookieHeader = buildString {
|
|
||||||
(oldCookie + newCookie).forEachIndexed { index, cookie ->
|
|
||||||
if (index > 0) append("; ")
|
|
||||||
if (cookie != null) {
|
|
||||||
append(cookie.name).append('=').append(cookie.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chain.proceed(
|
|
||||||
originalRequest
|
|
||||||
.newBuilder()
|
|
||||||
.addHeader("cookie", newCookieHeader)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNewCookie(url: HttpUrl): Cookie? {
|
|
||||||
val cookies = cookieManager.getCookie(url.toString())
|
|
||||||
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
|
|
||||||
cookies.split(";").mapNotNull { Cookie.parse(url, it) }
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
val sessionCookie = oldCookie.firstOrNull { it.name == "rememberLogin" }
|
|
||||||
if (!sessionCookie?.value.isNullOrEmpty()) {
|
|
||||||
return sessionCookie
|
|
||||||
}
|
|
||||||
val email = SConstants.getPrefBaseLogin(preferences)
|
|
||||||
val password = SConstants.getPrefBasePassword(preferences)
|
|
||||||
if (email.isEmpty() || password.isEmpty()) return null
|
|
||||||
val payload = FormBody.Builder()
|
|
||||||
.add("email", email)
|
|
||||||
.add("password", password)
|
|
||||||
.add("autoLogin", "on")
|
|
||||||
.build()
|
|
||||||
val headers = Headers.Builder()
|
|
||||||
.add("Upgrade-Insecure-Requests", "1")
|
|
||||||
.add("Referer", "http://186.2.175.5")
|
|
||||||
// .add("user-agent", "Mozilla/5.0 (Linux; Android 12; Pixel 5 Build/SP2A.220405.004; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Safari/537.36")
|
|
||||||
.build()
|
|
||||||
return client.newCall(POST(SConstants.LOGIN_URL, body = payload, headers = headers)).execute().header("set-cookie")?.let {
|
|
||||||
Log.i("bruh", it)
|
|
||||||
Cookie.parse(url, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user