From 1b5132534445a5f4323d788076797d77fbd9a43c Mon Sep 17 00:00:00 2001 From: Secozzi <49240133+Secozzi@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:49:27 +0000 Subject: [PATCH] fix(en/fmovies): Update enimax's vrf helper & add subtitle support for streamtape (#2227) --- .../StreamTapeExtractor.kt | 5 +- src/en/fmovies/build.gradle | 13 +- .../animeextension/en/fmovies/FMovies.kt | 228 +++++------------- .../animeextension/en/fmovies/FMoviesDto.kt | 15 ++ .../en/fmovies/FMoviesHelper.kt | 73 ++++++ .../fmovies/extractors/StreamtapeExtractor.kt | 54 ----- .../en/fmovies/extractors/VidsrcExtractor.kt | 60 +++++ 7 files changed, 214 insertions(+), 234 deletions(-) create mode 100644 src/en/fmovies/src/eu/kanade/tachiyomi/animeextension/en/fmovies/FMoviesHelper.kt delete mode 100644 src/en/fmovies/src/eu/kanade/tachiyomi/animeextension/en/fmovies/extractors/StreamtapeExtractor.kt create mode 100644 src/en/fmovies/src/eu/kanade/tachiyomi/animeextension/en/fmovies/extractors/VidsrcExtractor.kt diff --git a/lib/streamtape-extractor/src/main/java/eu/kanade/tachiyomi/lib/streamtapeextractor/StreamTapeExtractor.kt b/lib/streamtape-extractor/src/main/java/eu/kanade/tachiyomi/lib/streamtapeextractor/StreamTapeExtractor.kt index 8e1fb2a680..d8bee40675 100644 --- a/lib/streamtape-extractor/src/main/java/eu/kanade/tachiyomi/lib/streamtapeextractor/StreamTapeExtractor.kt +++ b/lib/streamtape-extractor/src/main/java/eu/kanade/tachiyomi/lib/streamtapeextractor/StreamTapeExtractor.kt @@ -1,12 +1,13 @@ package eu.kanade.tachiyomi.lib.streamtapeextractor +import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.util.asJsoup import okhttp3.OkHttpClient class StreamTapeExtractor(private val client: OkHttpClient) { - fun videoFromUrl(url: String, quality: String = "StreamTape"): Video? { + fun videoFromUrl(url: String, quality: String = "StreamTape", subtitleList: List = emptyList()): Video? { val baseUrl = "https://streamtape.com/e/" val newUrl = if (!url.startsWith(baseUrl)) { // ["https", "", "", "", "", ...] @@ -21,6 +22,6 @@ class StreamTapeExtractor(private val client: OkHttpClient) { ?: return null val videoUrl = "https:" + script.substringBefore("'") + script.substringAfter("+ ('xcd").substringBefore("'") - return Video(videoUrl, quality, videoUrl) + return Video(videoUrl, quality, videoUrl, subtitleTracks = subtitleList) } } diff --git a/src/en/fmovies/build.gradle b/src/en/fmovies/build.gradle index d482cee1e9..1909108504 100644 --- a/src/en/fmovies/build.gradle +++ b/src/en/fmovies/build.gradle @@ -1,18 +1,21 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) +} ext { extName = 'FMovies' pkgNameSuffix = 'en.fmovies' extClass = '.FMovies' - extVersionCode = 5 + extVersionCode = 6 libVersion = '13' } dependencies { implementation(project(':lib-filemoon-extractor')) - implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1" + implementation(project(':lib-streamtape-extractor')) + implementation(project(':lib-playlist-utils')) } diff --git a/src/en/fmovies/src/eu/kanade/tachiyomi/animeextension/en/fmovies/FMovies.kt b/src/en/fmovies/src/eu/kanade/tachiyomi/animeextension/en/fmovies/FMovies.kt index cab7a318ce..500ad7033b 100644 --- a/src/en/fmovies/src/eu/kanade/tachiyomi/animeextension/en/fmovies/FMovies.kt +++ b/src/en/fmovies/src/eu/kanade/tachiyomi/animeextension/en/fmovies/FMovies.kt @@ -5,16 +5,16 @@ import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.animeextension.en.fmovies.extractors.StreamtapeExtractor +import eu.kanade.tachiyomi.animeextension.en.fmovies.extractors.VidsrcExtractor import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.model.AnimeFilterList -import eu.kanade.tachiyomi.animesource.model.AnimesPage import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor +import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.util.asJsoup @@ -24,8 +24,6 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import okhttp3.Headers -import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.Request @@ -42,7 +40,7 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val name = "FMovies" - override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! } + override val baseUrl = "https://fmoviesz.to" override val lang = "en" @@ -56,20 +54,21 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { Injekt.get().getSharedPreferences("source_$id", 0x0000) } + private val vrfHelper by lazy { FMoviesHelper(client, headers) } + // ============================== Popular =============================== override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/trending${page.toPageQuery()}", headers) override fun popularAnimeSelector(): String = "div.items > div.item" - override fun popularAnimeFromElement(element: Element): SAnime { - val a = element.selectFirst("div.meta a")!! - - return SAnime.create().apply { - setUrlWithoutDomain(a.attr("abs:href")) - thumbnail_url = element.select("div.poster img").attr("data-src") + override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply { + element.selectFirst("div.meta a")!!.let { a -> title = a.text() + setUrlWithoutDomain(a.attr("abs:href")) } + + thumbnail_url = element.select("div.poster img").attr("data-src") } override fun popularAnimeNextPageSelector(): String = "ul.pagination > li.active + li" @@ -87,19 +86,11 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // =============================== Search =============================== - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw Exception("Not used") - - override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { val params = FMoviesFilters.getSearchParameters(filters) - return client.newCall(searchAnimeRequest(page, query, params)) - .asObservableSuccess() - .map { response -> - searchAnimeParse(response) - } - } - private fun searchAnimeRequest(page: Int, query: String, filters: FMoviesFilters.FilterSearchParams): Request = - GET("$baseUrl/filter?keyword=$query${filters.filter}${page.toPageQuery(false)}", headers) + return GET("$baseUrl/filter?keyword=$query${params.filter}${page.toPageQuery(false)}", headers) + } override fun searchAnimeSelector(): String = popularAnimeSelector() @@ -139,16 +130,17 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun episodeListRequest(anime: SAnime): Request { val id = client.newCall(GET(baseUrl + anime.url)).execute().asJsoup() .selectFirst("div[data-id]")!!.attr("data-id") - val vrf = callConsumet(id, "fmovies-vrf") - val vrfHeaders = headers.newBuilder() - .add("Accept", "application/json, text/javascript, */*; q=0.01") - .add("Host", baseUrl.toHttpUrl().host) - .add("Referer", baseUrl + anime.url) - .add("X-Requested-With", "XMLHttpRequest") - .build() + val vrf = vrfHelper.getVrf(id) - return GET("$baseUrl/ajax/episode/list/$id?$vrf", headers = vrfHeaders) + val vrfHeaders = headers.newBuilder().apply { + add("Accept", "application/json, text/javascript, */*; q=0.01") + add("Host", baseUrl.toHttpUrl().host) + add("Referer", baseUrl + anime.url) + add("X-Requested-With", "XMLHttpRequest") + }.build() + + return GET("$baseUrl/ajax/episode/list/$id?vrf=$vrf", headers = vrfHeaders) } override fun episodeListParse(response: Response): List { @@ -165,17 +157,19 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } season.select("li").forEach { ep -> - val a = ep.selectFirst("a")!! episodeList.add( SEpisode.create().apply { name = "$seasonPrefix${ep.text().trim()}".replace("Episode ", "Ep. ") - episode_number = a.attr("data-num").toFloatOrNull() ?: 0F - url = json.encodeToString( - EpisodeInfo( - id = a.attr("data-id"), - url = "$baseUrl${a.attr("href")}", - ), - ) + + ep.selectFirst("a")!!.let { a -> + episode_number = a.attr("data-num").toFloatOrNull() ?: 0F + url = json.encodeToString( + EpisodeInfo( + id = a.attr("data-id"), + url = "$baseUrl${a.attr("href")}", + ), + ) + } }, ) } @@ -200,8 +194,7 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun videoListRequest(episode: SEpisode): Request { val data = json.decodeFromString(episode.url) - - val vrf = callConsumet(data.id, "fmovies-vrf") + val vrf = vrfHelper.getVrf(data.id) val vrfHeaders = headers.newBuilder() .add("Accept", "application/json, text/javascript, */*; q=0.01") @@ -210,9 +203,13 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { .add("X-Requested-With", "XMLHttpRequest") .build() - return GET("$baseUrl/ajax/server/list/${data.id}?$vrf", headers = vrfHeaders) + return GET("$baseUrl/ajax/server/list/${data.id}?vrf=$vrf", headers = vrfHeaders) } + private val vidsrcExtractor by lazy { VidsrcExtractor(client, headers) } + private val filemoonExtractor by lazy { FilemoonExtractor(client) } + private val streamtapeExtractor by lazy { StreamTapeExtractor(client) } + private fun videoListParse(response: Response, episode: SEpisode): List