Merge pull request #158 from Blatzar/master

Added DNS over HTTPS and cache
This commit is contained in:
Osten 2021-10-19 16:08:57 +02:00 committed by GitHub
commit 12977e1788
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 242 additions and 14 deletions

View file

@ -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"
}

View file

@ -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,

View file

@ -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") ?: ""

View file

@ -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<String>) = 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",
)
))

View file

@ -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<String, String> = mapOf()
private val DEFAULT_COOKIES: Map<String, String> = 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)

View file

@ -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())
}
}
}

View file

@ -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<Preference>(getString(R.string.locale_key))!!
val benenePreference = findPreference<Preference>(getString(R.string.benene_count))!!
val watchQualityPreference = findPreference<Preference>(getString(R.string.quality_pref_key))!!
val dnsPreference = findPreference<Preference>(getString(R.string.dns_key))!!
val legalPreference = findPreference<Preference>(getString(R.string.legal_notice_key))!!
val subdubPreference = findPreference<Preference>(getString(R.string.display_sub_key))!!
val providerLangPreference = findPreference<Preference>(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)

View file

@ -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)
}
}

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/white">
<path
android:fillColor="@android:color/white"
android:pathData="M20,13H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zM7,19c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM20,3H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1zM7,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
</vector>

View file

@ -31,6 +31,21 @@
<item>-2</item>
</array>
<array name="dns_pref">
<item>None</item>
<item>Google</item>
<item>Cloudflare</item>
<!-- <item>OpenDns</item>-->
<item>AdGuard</item>
</array>
<array name="dns_pref_values">
<item>0</item>
<item>1</item>
<item>2</item>
<!-- <item>3</item>-->
<item>4</item>
</array>
<array name="episode_long_click_options">
<item>@string/episode_action_chomecast_episode</item>
<item>@string/episode_action_chomecast_mirror</item>

View file

@ -23,6 +23,7 @@
<string name="display_sub_key" translatable="false">display_sub_key</string>
<string name="show_fillers_key" translatable="false">show_fillers_key</string>
<string name="provider_lang_key" translatable="false">provider_lang_key</string>
<string name="dns_key" translatable="false">dns_key</string>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
@ -166,7 +167,9 @@
<string name="double_tap_to_seek_settings_des">Tap twice on the right or left side to seek forwards or backwards
</string>
<string name="use_system_brightness_settings">Use system brightness</string>
<string name="use_system_brightness_settings_des">Use system brightness in the app player instead of an dark overlay</string>
<string name="use_system_brightness_settings_des">Use system brightness in the app player instead of an dark
overlay
</string>
<string name="search">Search</string>
<string name="settings_info">Info</string>
@ -261,6 +264,10 @@
<string name="dont_show_again">Don\'t show again</string>
<string name="update">Update</string>
<string name="watch_quality_pref">Preferred watch quality</string>
<string name="dns_pref">DNS over HTTPS</string>
<string name="dns_pref_summary">Useful for bypassing ISP blocks</string>
<string name="display_subbed_dubbed_settings">Display Dubbed/Subbed Anime</string>
<string name="resize_fit">Fit to screen</string>
@ -269,15 +276,21 @@
<string name="legal_notice" translatable="false">Disclaimer</string>
<string name="legal_notice_key" translatable="false">legal_notice_key</string>
<string name="legal_notice_text" translatable="false">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.
<string name="legal_notice_text" translatable="false">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.
</string>
<string name="general">General</string>
<string name="provider_lang_settings">Provider Languages</string>

View file

@ -93,6 +93,12 @@
android:icon="@drawable/ic_baseline_skip_next_24"
android:title="@string/show_fillers_settings"
android:defaultValue="true"/>
<Preference
android:key="@string/dns_key"
android:title="@string/dns_pref"
android:summary="@string/dns_pref_summary"
android:icon="@drawable/ic_baseline_dns_24">
</Preference>
</PreferenceCategory>
<PreferenceCategory
android:key="search"