diff --git a/app/build.gradle b/app/build.gradle index 91c3d989..1f52cb85 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,6 +63,9 @@ android { debuggable true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } + debug { + applicationIdSuffix ".debug" + } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -106,6 +109,7 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.12.0' kapt 'com.github.bumptech.glide:compiler:4.12.0' + implementation 'com.github.bumptech.glide:okhttp3-integration:4.12.0' implementation 'jp.wasabeef:glide-transformations:4.0.0' @@ -141,5 +145,6 @@ dependencies { implementation "androidx.work:work-runtime-ktx:2.7.0-rc01" // Networking - implementation "com.squareup.okhttp3:okhttp:4.9.0" + implementation "com.squareup.okhttp3:okhttp:4.9.1" + implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1" } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index d1b01c48..78a3e923 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -26,6 +26,9 @@ import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.restrictedApis import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.network.get +import com.lagradost.cloudstream3.network.initRequestClient +import com.lagradost.cloudstream3.network.text import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO @@ -46,6 +49,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_result.* import java.util.* +import java.util.zip.GZIPInputStream import kotlin.concurrent.thread @@ -135,7 +139,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } companion object { - fun Activity?.getCastSession() : CastSession? { + fun Activity?.getCastSession(): CastSession? { return (this as MainActivity?)?.mSessionManager?.currentCastSession } @@ -290,6 +294,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { true ) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW updateLocale() + initRequestClient() super.onCreate(savedInstanceState) try { if (isCastApiAvailable()) { @@ -334,7 +339,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { }*/ // Fucks up anime info layout since that has its own layout - cast_mini_controller_holder?.isVisible = !listOf(R.id.navigation_results,R.id.navigation_player).contains(destination.id) + cast_mini_controller_holder?.isVisible = + !listOf(R.id.navigation_results, R.id.navigation_player).contains(destination.id) nav_view.isVisible = listOf( R.id.navigation_home, diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersToProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersToProvider.kt index a15d3053..ac7a3d1a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersToProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersToProvider.kt @@ -183,7 +183,7 @@ class TrailersToProvider : MainAPI() { return isSucc } else if (url.contains("/episode/")) { - val response = get(url).text + val response = get(url, params = mapOf("preview" to "1")).text val document = Jsoup.parse(response) // val qSub = document.select("subtitle-content") val subUrl = document.select("subtitle-content")?.attr("data-url") ?: "" diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/DohProviders.kt b/app/src/main/java/com/lagradost/cloudstream3/network/DohProviders.kt new file mode 100644 index 00000000..65f1a49d --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/network/DohProviders.kt @@ -0,0 +1,67 @@ +package com.lagradost.cloudstream3.network + +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.dnsoverhttps.DnsOverHttps +import java.net.InetAddress + +/** + * Based on https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt + */ + + fun OkHttpClient.Builder.addGenericDns(url: String, ips: List) = dns( + DnsOverHttps + .Builder() + .client(build()) + .url(url.toHttpUrl()) + .bootstrapDnsHosts( + ips.map { InetAddress.getByName(it) } + ) + .build() +) + +fun OkHttpClient.Builder.addGoogleDns() = ( + addGenericDns( + "https://dns.google/dns-query", + listOf( + "8.8.4.4", + "8.8.8.8" + ) + )) + +fun OkHttpClient.Builder.addCloudFlareDns() = ( + addGenericDns( + "https://cloudflare-dns.com/dns-query", + // https://www.cloudflare.com/ips/ + listOf( + "1.1.1.1", + "1.0.0.1", + "2606:4700:4700::1111", + "2606:4700:4700::1001" + ) + )) + +// Commented out as it doesn't work +//fun OkHttpClient.Builder.addOpenDns() = ( +// addGenericDns( +// "https://doh.opendns.com/dns-query", +// // https://support.opendns.com/hc/en-us/articles/360038086532-Using-DNS-over-HTTPS-DoH-with-OpenDNS +// listOf( +// "208.67.222.222", +// "208.67.220.220", +// "2620:119:35::35", +// "2620:119:53::53", +// ) +// )) + + +fun OkHttpClient.Builder.addAdGuardDns() = ( + addGenericDns( + "https://dns.adguard.com/dns-query", + // https://github.com/AdguardTeam/AdGuardDNS + listOf( + // "Non-filtering" + "94.140.14.140", + "94.140.14.141", + ) + )) \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt b/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt index 41e8b69a..7934e647 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt @@ -1,12 +1,20 @@ package com.lagradost.cloudstream3.network +import android.content.Context +import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.USER_AGENT import okhttp3.* import okhttp3.Headers.Companion.toHeaders +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.dnsoverhttps.DnsOverHttps +import java.io.File +import java.net.InetAddress import java.net.URI import java.util.* import java.util.concurrent.TimeUnit +var baseClient = OkHttpClient() private const val DEFAULT_TIME = 10 private val DEFAULT_TIME_UNIT = TimeUnit.MINUTES private const val DEFAULT_USER_AGENT = USER_AGENT @@ -15,6 +23,29 @@ private val DEFAULT_DATA: Map = mapOf() private val DEFAULT_COOKIES: Map = mapOf() private val DEFAULT_REFERER: String? = null +fun Context.initRequestClient(): OkHttpClient { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + val dns = settingsManager.getInt(this.getString(R.string.dns_pref), 0) + baseClient = OkHttpClient.Builder() + .cache( + // Note that you need to add a ResponseInterceptor to make this 100% active. + // The server response dictates if and when stuff should be cached. + Cache( + directory = File(cacheDir, "http_cache"), + maxSize = 50L * 1024L * 1024L // 50 MiB + ) + ).apply { + when (dns) { + 1 -> addGoogleDns() + 2 -> addCloudFlareDns() +// 3 -> addOpenDns() + 4 -> addAdGuardDns() + } + } + // Needs to be build as otherwise the other builders will change this object + .build() + return baseClient +} /** WARNING! CAN ONLY BE READ ONCE */ val Response.text: String @@ -93,14 +124,13 @@ fun get( timeout: Long = 0L, interceptor: Interceptor? = null ): Response { - - val client = OkHttpClient().newBuilder() + val client = baseClient + .newBuilder() .followRedirects(allowRedirects) .followSslRedirects(allowRedirects) .callTimeout(timeout, TimeUnit.SECONDS) if (interceptor != null) client.addInterceptor(interceptor) - val request = getRequestCreator(url, headers, referer, params, cookies, cacheTime, cacheUnit) return client.build().newCall(request).execute() } @@ -118,7 +148,8 @@ fun post( cacheUnit: TimeUnit = DEFAULT_TIME_UNIT, timeout: Long = 0L ): Response { - val client = OkHttpClient().newBuilder() + val client = baseClient + .newBuilder() .followRedirects(allowRedirects) .followSslRedirects(allowRedirects) .callTimeout(timeout, TimeUnit.SECONDS) diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt b/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt index c92b70c0..ca695c0b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.network import android.annotation.SuppressLint import android.net.http.SslError import android.webkit.* +import androidx.core.view.contains import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.utils.Coroutines.main import kotlinx.coroutines.delay @@ -10,6 +11,7 @@ import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response +import java.net.URI import java.util.concurrent.TimeUnit class WebViewResolver(val interceptUrl: Regex) : Interceptor { @@ -56,7 +58,7 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor { request: WebResourceRequest ): WebResourceResponse? { val webViewUrl = request.url.toString() -// println("Override url $webViewUrl") + if (interceptUrl.containsMatchIn(webViewUrl)) { fixedRequest = getRequestCreator( webViewUrl, @@ -71,7 +73,27 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor { println("Web-view request finished: $webViewUrl") destroyWebView() } - return super.shouldInterceptRequest(view, request) + + return try { + when { + // suppress favicon requests as we don't display them anywhere + webViewUrl.endsWith("/favicon.ico") -> WebResourceResponse("image/png", null, null) + webViewUrl.contains("recaptcha") -> super.shouldInterceptRequest(view, request) + + request.method == "GET" -> get( + webViewUrl, + headers = request.requestHeaders + ).toWebResourceResponse() + + request.method == "POST" -> post( + webViewUrl, + headers = request.requestHeaders + ).toWebResourceResponse() + else -> return super.shouldInterceptRequest(view, request) + } + } catch (e: Exception) { + null + } } override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) { @@ -99,4 +121,18 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor { return null } + fun Response.toWebResourceResponse(): WebResourceResponse { + val contentTypeValue = this.header("Content-Type") + // 1. contentType. 2. charset + val typeRegex = Regex("""(.*);(?:.*charset=(.*)(?:|;)|)""") + return if (contentTypeValue != null) { + val found = typeRegex.find(contentTypeValue ?: "") + val contentType = found?.groupValues?.getOrNull(1)?.ifBlank { null } ?: contentTypeValue + val charset = found?.groupValues?.getOrNull(2)?.ifBlank { null } + WebResourceResponse(contentType, charset, this.body?.byteStream()) + } else { + WebResourceResponse("application/octet-stream", null, this.body?.byteStream()) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index 89525ce7..6940ba4d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -16,6 +16,7 @@ import com.lagradost.cloudstream3.MainActivity.Companion.setLocale import com.lagradost.cloudstream3.MainActivity.Companion.showToast import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.network.initRequestClient import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate @@ -53,6 +54,7 @@ class SettingsFragment : PreferenceFragmentCompat() { val localePreference = findPreference(getString(R.string.locale_key))!! val benenePreference = findPreference(getString(R.string.benene_count))!! val watchQualityPreference = findPreference(getString(R.string.quality_pref_key))!! + val dnsPreference = findPreference(getString(R.string.dns_key))!! val legalPreference = findPreference(getString(R.string.legal_notice_key))!! val subdubPreference = findPreference(getString(R.string.display_sub_key))!! val providerLangPreference = findPreference(getString(R.string.provider_lang_key))!! @@ -154,6 +156,25 @@ class SettingsFragment : PreferenceFragmentCompat() { return@setOnPreferenceClickListener true } + dnsPreference.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.dns_pref) + val prefValues = resources.getIntArray(R.array.dns_pref_values) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) + + val currentDns = + settingsManager.getInt(getString(R.string.dns_pref), 0) + context?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentDns), + getString(R.string.dns_pref), + true, + {}) { + settingsManager.edit().putInt(getString(R.string.dns_pref), prefValues[it]).apply() + context?.initRequestClient() + } + return@setOnPreferenceClickListener true + } + try { val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt index b9731742..400ca9fa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt @@ -1,12 +1,18 @@ package com.lagradost.cloudstream3.utils import android.content.Context +import com.bumptech.glide.Glide import com.bumptech.glide.GlideBuilder +import com.bumptech.glide.Registry import com.bumptech.glide.annotation.GlideModule +import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.module.AppGlideModule import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.signature.ObjectKey +import com.lagradost.cloudstream3.network.initRequestClient +import java.io.InputStream @GlideModule class GlideModule : AppGlideModule() { @@ -18,4 +24,16 @@ class GlideModule : AppGlideModule() { .signature(ObjectKey(System.currentTimeMillis().toShort())) } } + + // Needed for DOH + // https://stackoverflow.com/a/61634041 + override fun registerComponents(context: Context, glide: Glide, registry: Registry) { + val client = context.initRequestClient() + registry.replace( + GlideUrl::class.java, + InputStream::class.java, + OkHttpUrlLoader.Factory(client) + ) + super.registerComponents(context, glide, registry) + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_dns_24.xml b/app/src/main/res/drawable/ic_baseline_dns_24.xml new file mode 100644 index 00000000..b2677f76 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_dns_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index febfea2d..c2a1504f 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -31,6 +31,21 @@ -2 + + None + Google + Cloudflare + + AdGuard + + + 0 + 1 + 2 + + 4 + + @string/episode_action_chomecast_episode @string/episode_action_chomecast_mirror diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cfc00ecd..e057a0f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,7 @@ display_sub_key show_fillers_key provider_lang_key + dns_key @@ -166,7 +167,9 @@ Tap twice on the right or left side to seek forwards or backwards Use system brightness - Use system brightness in the app player instead of an dark overlay + Use system brightness in the app player instead of an dark + overlay + Search Info @@ -261,6 +264,10 @@ Don\'t show again Update Preferred watch quality + DNS over HTTPS + Useful for bypassing ISP blocks + + Display Dubbed/Subbed Anime Fit to screen @@ -269,15 +276,21 @@ Disclaimer legal_notice_key - Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. + Any legal issues regarding the content on this application + should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. + CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. + CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or + manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, + user-friendly interface. - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. + It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the + responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use + CloudStream 3 at your own risk. General Provider Languages diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 1bdbce6c..c866a536 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -93,6 +93,12 @@ android:icon="@drawable/ic_baseline_skip_next_24" android:title="@string/show_fillers_settings" android:defaultValue="true"/> + +