mirror of
https://github.com/revanced/revanced-integrations.git
synced 2025-01-02 16:15:58 +01:00
feat(twitch): remove block-embedded-ads
patch (#230)
This commit is contained in:
parent
3a2536a86e
commit
2f32786661
@ -46,6 +46,4 @@ dependencies {
|
|||||||
compileOnly(project(mapOf("path" to ":dummy")))
|
compileOnly(project(mapOf("path" to ":dummy")))
|
||||||
compileOnly("androidx.annotation:annotation:1.5.0")
|
compileOnly("androidx.annotation:annotation:1.5.0")
|
||||||
compileOnly("androidx.appcompat:appcompat:1.5.1")
|
compileOnly("androidx.appcompat:appcompat:1.5.1")
|
||||||
compileOnly("com.squareup.okhttp3:okhttp:4.10.0")
|
|
||||||
compileOnly("com.squareup.retrofit2:retrofit:2.9.0")
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
package app.revanced.twitch.adblock
|
|
||||||
|
|
||||||
import okhttp3.Request
|
|
||||||
|
|
||||||
interface IAdblockService {
|
|
||||||
fun friendlyName(): String
|
|
||||||
fun maxAttempts(): Int
|
|
||||||
fun isAvailable(): Boolean
|
|
||||||
fun rewriteHlsRequest(originalRequest: Request): Request?
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun Request.isVod() = url.pathSegments.contains("vod")
|
|
||||||
fun Request.channelName() =
|
|
||||||
url.pathSegments
|
|
||||||
.firstOrNull { it.endsWith(".m3u8") }
|
|
||||||
.run { this?.replace(".m3u8", "") }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
package app.revanced.twitch.adblock
|
|
||||||
|
|
||||||
import app.revanced.twitch.adblock.IAdblockService.Companion.channelName
|
|
||||||
import app.revanced.twitch.api.RetrofitClient
|
|
||||||
import app.revanced.twitch.utils.LogHelper
|
|
||||||
import app.revanced.twitch.utils.ReVancedUtils
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
|
|
||||||
class PurpleAdblockService : IAdblockService {
|
|
||||||
private val tunnels = mutableMapOf(
|
|
||||||
/* tunnel url */ /* alive */
|
|
||||||
"https://eu1.jupter.ga" to false,
|
|
||||||
"https://eu2.jupter.ga" to false
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun friendlyName(): String = ReVancedUtils.getString("revanced_proxy_purpleadblock")
|
|
||||||
|
|
||||||
override fun maxAttempts(): Int = 3
|
|
||||||
|
|
||||||
override fun isAvailable(): Boolean {
|
|
||||||
for(tunnel in tunnels.keys) {
|
|
||||||
var success = true
|
|
||||||
try {
|
|
||||||
val response = RetrofitClient.getInstance().purpleAdblockApi.ping(tunnel).execute()
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
LogHelper.error("PurpleAdBlock tunnel $tunnel returned an error: HTTP code %d", response.code())
|
|
||||||
LogHelper.debug(response.message())
|
|
||||||
LogHelper.debug((response.errorBody() as ResponseBody).string())
|
|
||||||
success = false
|
|
||||||
}
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
LogHelper.printException("PurpleAdBlock tunnel $tunnel is unavailable", ex)
|
|
||||||
success = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache availability data
|
|
||||||
tunnels[tunnel] = success
|
|
||||||
|
|
||||||
if(success)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun rewriteHlsRequest(originalRequest: Request): Request? {
|
|
||||||
val server = tunnels.filter { it.value }.map { it.key }.firstOrNull()
|
|
||||||
server ?: run {
|
|
||||||
LogHelper.error("No tunnels are available")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compose new URL
|
|
||||||
val url = "$server/channel/${originalRequest.channelName()}".toHttpUrlOrNull()
|
|
||||||
if (url == null) {
|
|
||||||
LogHelper.error("Failed to parse rewritten URL")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite old request
|
|
||||||
return Request.Builder()
|
|
||||||
.get()
|
|
||||||
.url(url)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package app.revanced.twitch.adblock
|
|
||||||
|
|
||||||
import app.revanced.twitch.adblock.IAdblockService.Companion.channelName
|
|
||||||
import app.revanced.twitch.adblock.IAdblockService.Companion.isVod
|
|
||||||
import app.revanced.twitch.utils.LogHelper
|
|
||||||
import app.revanced.twitch.utils.ReVancedUtils
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
||||||
import okhttp3.Request
|
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
class TTVLolService : IAdblockService {
|
|
||||||
|
|
||||||
override fun friendlyName(): String = ReVancedUtils.getString("revanced_proxy_ttv_lol")
|
|
||||||
|
|
||||||
// TTV.lol is sometimes unstable
|
|
||||||
override fun maxAttempts(): Int = 4
|
|
||||||
|
|
||||||
override fun isAvailable(): Boolean = true
|
|
||||||
|
|
||||||
override fun rewriteHlsRequest(originalRequest: Request): Request? {
|
|
||||||
// Compose new URL
|
|
||||||
val url = "https://api.ttv.lol/${if (originalRequest.isVod()) "vod" else "playlist"}/${originalRequest.channelName()}.m3u8${nextQuery()}".toHttpUrlOrNull()
|
|
||||||
if (url == null) {
|
|
||||||
LogHelper.error("Failed to parse rewritten URL")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite old request
|
|
||||||
return Request.Builder()
|
|
||||||
.get()
|
|
||||||
.url(url)
|
|
||||||
.addHeader("X-Donate-To", "https://ttv.lol/donate")
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun nextQuery(): String {
|
|
||||||
return SAMPLE_QUERY.replace("<SESSION>", generateSessionId())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateSessionId() =
|
|
||||||
(1..32)
|
|
||||||
.map { "abcdef0123456789"[randomSource.nextInt(16)] }
|
|
||||||
.joinToString("")
|
|
||||||
|
|
||||||
private val randomSource = Random()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val SAMPLE_QUERY =
|
|
||||||
"%3Fallow_source%3Dtrue%26fast_bread%3Dtrue%26allow_audio_only%3Dtrue%26p%3D0%26play_session_id%3D<SESSION>%26player_backend%3Dmediaplayer%26warp%3Dfalse%26force_preroll%3Dfalse%26mobile_cellular%3Dfalse"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package app.revanced.twitch.api;
|
|
||||||
|
|
||||||
import okhttp3.ResponseBody;
|
|
||||||
import retrofit2.Call;
|
|
||||||
import retrofit2.http.GET;
|
|
||||||
import retrofit2.http.Url;
|
|
||||||
|
|
||||||
/* only used for service pings */
|
|
||||||
public interface PurpleAdblockApi {
|
|
||||||
@GET /* root */
|
|
||||||
Call<ResponseBody> ping(@Url String baseUrl);
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
package app.revanced.twitch.api
|
|
||||||
|
|
||||||
import app.revanced.twitch.adblock.IAdblockService
|
|
||||||
import app.revanced.twitch.adblock.IAdblockService.Companion.channelName
|
|
||||||
import app.revanced.twitch.adblock.IAdblockService.Companion.isVod
|
|
||||||
import app.revanced.twitch.adblock.PurpleAdblockService
|
|
||||||
import app.revanced.twitch.adblock.TTVLolService
|
|
||||||
import app.revanced.twitch.settings.SettingsEnum
|
|
||||||
import app.revanced.twitch.utils.LogHelper
|
|
||||||
import app.revanced.twitch.utils.ReVancedUtils
|
|
||||||
import okhttp3.*
|
|
||||||
|
|
||||||
class RequestInterceptor : Interceptor {
|
|
||||||
private var activeService: IAdblockService? = null
|
|
||||||
|
|
||||||
private fun updateActiveService() {
|
|
||||||
val current = SettingsEnum.BLOCK_EMBEDDED_ADS.string
|
|
||||||
activeService = if(current == ReVancedUtils.getString("key_revanced_proxy_ttv_lol") && activeService !is TTVLolService)
|
|
||||||
TTVLolService()
|
|
||||||
else if(current == ReVancedUtils.getString("key_revanced_proxy_purpleadblock") && activeService !is PurpleAdblockService)
|
|
||||||
PurpleAdblockService()
|
|
||||||
else if(current == ReVancedUtils.getString("key_revanced_proxy_disabled"))
|
|
||||||
null
|
|
||||||
else
|
|
||||||
activeService
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
val originalRequest = chain.request()
|
|
||||||
LogHelper.debug("Intercepted request to URL: %s", originalRequest.url.toString())
|
|
||||||
|
|
||||||
// Skip if not HLS manifest request
|
|
||||||
if (!originalRequest.url.host.contains("usher.ttvnw.net")) {
|
|
||||||
return chain.proceed(originalRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
LogHelper.debug("Found HLS manifest request. Is VOD? %s; Channel: %s",
|
|
||||||
if (originalRequest.isVod()) "yes" else "no", originalRequest.channelName())
|
|
||||||
|
|
||||||
// None of the services support VODs currently
|
|
||||||
if(originalRequest.isVod())
|
|
||||||
return chain.proceed(originalRequest)
|
|
||||||
|
|
||||||
updateActiveService()
|
|
||||||
|
|
||||||
activeService?.let {
|
|
||||||
val available = it.isAvailable()
|
|
||||||
val rewritten = it.rewriteHlsRequest(originalRequest)
|
|
||||||
|
|
||||||
if (!available || rewritten == null) {
|
|
||||||
ReVancedUtils.toast(
|
|
||||||
String.format(ReVancedUtils.getString("revanced_embedded_ads_service_unavailable"), it.friendlyName()),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
return chain.proceed(originalRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
LogHelper.debug("Rewritten HLS stream URL: %s", rewritten.url.toString())
|
|
||||||
|
|
||||||
val maxAttempts = it.maxAttempts()
|
|
||||||
for(i in 1..maxAttempts) {
|
|
||||||
// Execute rewritten request and close body to allow multiple proceed() calls
|
|
||||||
val response = chain.proceed(rewritten).apply { close() }
|
|
||||||
if(!response.isSuccessful) {
|
|
||||||
LogHelper.error("Request failed (attempt %d/%d): HTTP error %d (%s)",
|
|
||||||
i, maxAttempts, response.code, response.message)
|
|
||||||
Thread.sleep(50)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Accept response from ad blocker
|
|
||||||
LogHelper.debug("Ad-blocker used")
|
|
||||||
return chain.proceed(rewritten)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// maxAttempts exceeded; giving up on using the ad blocker
|
|
||||||
ReVancedUtils.toast(
|
|
||||||
String.format(ReVancedUtils.getString("revanced_embedded_ads_service_failed"), it.friendlyName()),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adblock disabled
|
|
||||||
return chain.proceed(originalRequest)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package app.revanced.twitch.api;
|
|
||||||
|
|
||||||
import retrofit2.Retrofit;
|
|
||||||
|
|
||||||
public class RetrofitClient {
|
|
||||||
|
|
||||||
private static RetrofitClient instance = null;
|
|
||||||
private final PurpleAdblockApi purpleAdblockApi;
|
|
||||||
|
|
||||||
private RetrofitClient() {
|
|
||||||
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://localhost" /* dummy */).build();
|
|
||||||
purpleAdblockApi = retrofit.create(PurpleAdblockApi.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized RetrofitClient getInstance() {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new RetrofitClient();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PurpleAdblockApi getPurpleAdblockApi() {
|
|
||||||
return purpleAdblockApi;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package app.revanced.twitch.patches;
|
|
||||||
|
|
||||||
import app.revanced.twitch.api.RequestInterceptor;
|
|
||||||
|
|
||||||
public class EmbeddedAdsPatch {
|
|
||||||
public static RequestInterceptor createRequestInterceptor() {
|
|
||||||
return new RequestInterceptor();
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ public enum SettingsEnum {
|
|||||||
/* Ads */
|
/* Ads */
|
||||||
BLOCK_VIDEO_ADS("revanced_block_video_ads", true, ReturnType.BOOLEAN),
|
BLOCK_VIDEO_ADS("revanced_block_video_ads", true, ReturnType.BOOLEAN),
|
||||||
BLOCK_AUDIO_ADS("revanced_block_audio_ads", true, ReturnType.BOOLEAN),
|
BLOCK_AUDIO_ADS("revanced_block_audio_ads", true, ReturnType.BOOLEAN),
|
||||||
BLOCK_EMBEDDED_ADS("revanced_block_embedded_ads", "ttv-lol", ReturnType.STRING),
|
|
||||||
|
|
||||||
/* Chat */
|
/* Chat */
|
||||||
SHOW_DELETED_MESSAGES("revanced_show_deleted_messages", "cross-out", ReturnType.STRING),
|
SHOW_DELETED_MESSAGES("revanced_show_deleted_messages", "cross-out", ReturnType.STRING),
|
||||||
|
@ -43,7 +43,7 @@ public class LogHelper {
|
|||||||
|
|
||||||
private static void showDebugToast(String msg) {
|
private static void showDebugToast(String msg) {
|
||||||
if(SettingsEnum.DEBUG_MODE.getBoolean()) {
|
if(SettingsEnum.DEBUG_MODE.getBoolean()) {
|
||||||
ReVancedUtils.toast(msg, false);
|
ReVancedUtils.ifContextAttached((c) -> Toast.makeText(c, msg, Toast.LENGTH_SHORT).show());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,6 @@ package app.revanced.twitch.utils;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
public class ReVancedUtils {
|
public class ReVancedUtils {
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
@ -56,10 +53,6 @@ public class ReVancedUtils {
|
|||||||
void run(Context ctx);
|
void run(Context ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void runOnMainThread(Runnable runnable) {
|
|
||||||
new Handler(Looper.getMainLooper()).post(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get resource id safely
|
* Get resource id safely
|
||||||
* @return May return 0 if resource not found or context not attached
|
* @return May return 0 if resource not found or context not attached
|
||||||
@ -91,13 +84,4 @@ public class ReVancedUtils {
|
|||||||
public static String getString(String name) {
|
public static String getString(String name) {
|
||||||
return ifContextAttached((c) -> c.getString(getStringId(name)), "");
|
return ifContextAttached((c) -> c.getString(getStringId(name)), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void toast(String message) {
|
|
||||||
toast(message, true);
|
|
||||||
}
|
|
||||||
public static void toast(String message, boolean longLength) {
|
|
||||||
ifContextAttached((c) -> {
|
|
||||||
runOnMainThread(() -> Toast.makeText(c, message, longLength ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user