diff --git a/src/all/netflixmirror/AndroidManifest.xml b/src/all/netflixmirror/AndroidManifest.xml deleted file mode 100644 index 8072ee00d..000000000 --- a/src/all/netflixmirror/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/all/netflixmirror/build.gradle b/src/all/netflixmirror/build.gradle deleted file mode 100644 index 21044713a..000000000 --- a/src/all/netflixmirror/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.kotlin.serialization) -} - -ext { - extName = 'NetFlix Mirror' - pkgNameSuffix = 'all.netflixmirror' - extClass = '.NetFlixMirror' - extVersionCode = 1 -} - -dependencies { - implementation(project(':lib-playlist-utils')) -} - -apply from: "$rootDir/common.gradle" diff --git a/src/all/netflixmirror/res/mipmap-hdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 68e5ac658..000000000 Binary files a/src/all/netflixmirror/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/netflixmirror/res/mipmap-mdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index b5e1811ca..000000000 Binary files a/src/all/netflixmirror/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/netflixmirror/res/mipmap-xhdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 838d63e3a..000000000 Binary files a/src/all/netflixmirror/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/netflixmirror/res/mipmap-xxhdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index f9599bf70..000000000 Binary files a/src/all/netflixmirror/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/netflixmirror/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/netflixmirror/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index ca41fc42a..000000000 Binary files a/src/all/netflixmirror/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/all/netflixmirror/res/web_hi_res_512.png b/src/all/netflixmirror/res/web_hi_res_512.png deleted file mode 100644 index fd4971093..000000000 Binary files a/src/all/netflixmirror/res/web_hi_res_512.png and /dev/null differ diff --git a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/CookieInterceptor.kt b/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/CookieInterceptor.kt deleted file mode 100644 index 01fd66e7e..000000000 --- a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/CookieInterceptor.kt +++ /dev/null @@ -1,45 +0,0 @@ -package eu.kanade.tachiyomi.animeextension.all.netflixmirror - -import android.util.Log -import android.webkit.CookieManager -import okhttp3.Interceptor -import okhttp3.Response - -class CookieInterceptor( - private val domain: String, - private val key: String, - private val value: String, -) : Interceptor { - - init { - val url = "https://$domain/" - val cookie = "$key=$value; Domain=$domain; Path=/" - setCookie(url, cookie) - } - - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - if (!request.url.host.endsWith(domain)) return chain.proceed(request) - - val cookie = "$key=$value" - val cookieList = request.header("Cookie")?.split("; ") ?: emptyList() - if (cookie in cookieList) return chain.proceed(request) - - setCookie("https://$domain/", "$cookie; Domain=$domain; Path=/") - val prefix = "$key=" - val newCookie = buildList(cookieList.size + 1) { - cookieList.filterNotTo(this) { it.startsWith(prefix) } - add(cookie) - }.joinToString("; ") - val newRequest = request.newBuilder().header("Cookie", newCookie).build() - return chain.proceed(newRequest) - } - - private fun setCookie(url: String, value: String) { - try { - CookieManager.getInstance().setCookie(url, value) - } catch (e: Exception) { - Log.e(domain, "failed to set cookie", e) - } - } -} diff --git a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/NetFlixMirror.kt b/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/NetFlixMirror.kt deleted file mode 100644 index fb5ad0e36..000000000 --- a/src/all/netflixmirror/src/eu/kanade/tachiyomi/animeextension/all/netflixmirror/NetFlixMirror.kt +++ /dev/null @@ -1,271 +0,0 @@ -package eu.kanade.tachiyomi.animeextension.all.netflixmirror - -import android.app.Application -import androidx.preference.ListPreference -import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.DetailsDto -import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.EpisodeUrl -import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.EpisodesDto -import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.SearchDto -import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.SeasonEpisodesDto -import eu.kanade.tachiyomi.animeextension.all.netflixmirror.dto.VideoList -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.Video -import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource -import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservableSuccess -import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.Request -import okhttp3.Response -import org.jsoup.select.Elements -import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -import kotlin.math.min - -class NetFlixMirror : AnimeHttpSource(), ConfigurableAnimeSource { - - override val name = "NetFlix Mirror" - - override val baseUrl = "https://m.netflixmirror.com" - - override val lang = "all" - - override val supportsLatest = false - - private val json: Json by injectLazy() - - override val client = network.cloudflareClient.newBuilder() - .addNetworkInterceptor( - CookieInterceptor(baseUrl.toHttpUrl().host, "hd", "on"), - ) - .build() - - override fun headersBuilder() = super.headersBuilder() - .add("Referer", "$baseUrl/") - - private val xhrHeaders by lazy { - headersBuilder() - .add("X-Requested-With", "XMLHttpRequest") - .build() - } - - private val preferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - - private val playListUtils by lazy { - PlaylistUtils(client, headers) - } - - private lateinit var pageElements: Elements - - override fun fetchPopularAnime(page: Int): Observable { - return if (page == 1) { - super.fetchPopularAnime(page) - } else { - Observable.just(paginatedAnimePageParse(page)) - } - } - - override fun popularAnimeRequest(page: Int): Request { - return GET("$baseUrl/home", headers) - } - - override fun popularAnimeParse(response: Response): AnimesPage { - pageElements = response.asJsoup().select("article > a.post-data") - - return paginatedAnimePageParse(1) - } - - private fun paginatedAnimePageParse(page: Int): AnimesPage { - val end = min(page * 20, pageElements.size) - val entries = pageElements.subList((page - 1) * 20, end).map { - SAnime.create().apply { - title = "" // no title here - url = it.attr("data-post") - thumbnail_url = it.selectFirst("img")?.attr("abs:data-src") - } - } - - return AnimesPage(entries, end < pageElements.size) - } - - override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { - return if (query.isNotEmpty()) { - super.fetchSearchAnime(page, query, filters) - } else { - if (page == 1) { - val pageFilter = filters.filterIsInstance().firstOrNull()?.selected ?: "/home" - val request = GET(baseUrl + pageFilter, headers) - - client.newCall(request) - .asObservableSuccess() - .map(::popularAnimeParse) - } else { - Observable.just(paginatedAnimePageParse(page)) - } - } - } - - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - val url = "$baseUrl/search.php".toHttpUrl().newBuilder().apply { - addQueryParameter("s", query.trim()) - addQueryParameter("t", System.currentTimeMillis().toString()) - }.build().toString() - - return GET(url, xhrHeaders) - } - - override fun getFilterList() = getFilters() - - override fun searchAnimeParse(response: Response): AnimesPage { - val result = response.parseAs() - - val entries = result.searchResult?.map { - SAnime.create().apply { - url = it.id - title = it.title - thumbnail_url = idToThumbnailUrl(it.id) - } - } ?: emptyList() - - return AnimesPage(entries, false) - } - - override fun animeDetailsRequest(anime: SAnime): Request { - val url = "$baseUrl/post.php?id=${anime.url}&t=${System.currentTimeMillis()}" - - return GET(url, xhrHeaders) - } - - override fun animeDetailsParse(response: Response): SAnime { - val result = response.parseAs() - val id = response.request.url.queryParameter("id")!! - - return SAnime.create().apply { - title = result.title - url = id - thumbnail_url = idToThumbnailUrl(id) - genre = "${result.genre}, ${result.cast}" - author = result.creator - artist = result.director - description = result.desc - if (!result.lang.isNullOrEmpty()) { - description += "\n\nAvailable Language(s): ${result.lang.joinToString { it.language }}" - } - status = result.status - } - } - - override fun episodeListRequest(anime: SAnime) = animeDetailsRequest(anime) - - override fun episodeListParse(response: Response): List { - val result = response.parseAs() - val id = response.request.url.queryParameter("id")!! - - if (result.episodes?.firstOrNull() == null) { - return SEpisode.create().apply { - name = "Movie" - url = EpisodeUrl(id, result.title).let(json::encodeToString) - }.let(::listOf) - } - - val episodes = result.episodes.mapNotNull { - if (it == null) return@mapNotNull null - - it.toSEpisode(result.title) - }.toMutableList() - - result.season?.reversed()?.drop(1)?.forEach { season -> - val seasonRequest = GET("$baseUrl/episodes.php?s=${season.id}&series=$id&t=${System.currentTimeMillis()}", xhrHeaders) - val seasonResponse = client.newCall(seasonRequest).execute().parseAs() - - episodes.addAll( - index = 0, - elements = seasonResponse.episodes?.map { - it.toSEpisode(result.title) - } ?: emptyList(), - ) - } - - return episodes.reversed() - } - - override fun videoListRequest(episode: SEpisode): Request { - val episodeUrl = episode.url.parseAs() - - val url = "$baseUrl/playlist.php".toHttpUrl().newBuilder().apply { - addQueryParameter("id", episodeUrl.id) - addQueryParameter("t", episodeUrl.title) - addQueryParameter("tm", System.currentTimeMillis().toString()) - }.build().toString() - - return GET(url, xhrHeaders) - } - - override fun videoListParse(response: Response): List