@@ -9,8 +9,10 @@ import android.os.Bundle
9
9
import android.view.View
10
10
import android.view.ViewGroup
11
11
import android.view.ViewTreeObserver
12
+ import android.webkit.ConsoleMessage
12
13
import android.webkit.WebChromeClient
13
14
import android.webkit.WebResourceRequest
15
+ import android.webkit.WebResourceResponse
14
16
import android.webkit.WebView
15
17
import android.webkit.WebViewClient
16
18
import android.widget.FrameLayout
@@ -24,9 +26,15 @@ import androidx.core.view.WindowCompat
24
26
import androidx.core.view.WindowInsetsCompat
25
27
import androidx.core.view.WindowInsetsControllerCompat
26
28
import io.freetubeapp.freetube.databinding.ActivityMainBinding
29
+ import org.json.JSONObject
30
+ import java.io.Serializable
31
+ import java.net.HttpURLConnection
32
+ import java.net.URL
27
33
import java.net.URLEncoder
28
34
import java.util.Base64
35
+ import java.util.UUID
29
36
import java.util.concurrent.BlockingQueue
37
+ import java.util.concurrent.ConcurrentHashMap.KeySetView
30
38
import java.util.concurrent.LinkedBlockingQueue
31
39
import java.util.concurrent.ThreadPoolExecutor
32
40
import java.util.concurrent.TimeUnit
@@ -44,10 +52,12 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback {
44
52
lateinit var jsInterface: FreeTubeJavaScriptInterface
45
53
lateinit var activityResultLauncher: ActivityResultLauncher <Intent >
46
54
lateinit var content: View
55
+ var consoleMessages: MutableList <JSONObject > = mutableListOf ()
47
56
var showSplashScreen: Boolean = true
48
57
var darkMode: Boolean = false
49
58
var paused: Boolean = false
50
59
var isInAPrompt: Boolean = false
60
+ var pendingRequestBodies: MutableMap <String , String > = mutableMapOf ()
51
61
/*
52
62
* Gets the number of available cores
53
63
* (not always the same as the maximum number of cores)
@@ -164,6 +174,20 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback {
164
174
webView.addJavascriptInterface(jsInterface, " Android" )
165
175
webView.webChromeClient = object : WebChromeClient () {
166
176
177
+ override fun onConsoleMessage (consoleMessage : ConsoleMessage ): Boolean {
178
+ val messageData = JSONObject ()
179
+ messageData.put(" content" , consoleMessage.message())
180
+ messageData.put(" level" , consoleMessage.messageLevel())
181
+ messageData.put(" timestamp" , System .currentTimeMillis())
182
+ messageData.put(" id" , UUID .randomUUID())
183
+ messageData.put(" key" , " ${messageData[" id" ]} -${messageData[" timestamp" ]} " )
184
+ messageData.put(" sourceId" , consoleMessage.sourceId())
185
+ messageData.put(" lineNumber" , consoleMessage.lineNumber())
186
+ consoleMessages.add(messageData)
187
+ webView.loadUrl(" javascript: var event = new Event(\" console-message\" ); event.data = JSON.parse(${btoa(messageData.toString())} ); window.dispatchEvent(event)" )
188
+ return super .onConsoleMessage(consoleMessage);
189
+ }
190
+
167
191
override fun onShowCustomView (view : View ? , callback : CustomViewCallback ? ) {
168
192
windowInsetsController.hide(WindowInsetsCompat .Type .systemBars())
169
193
fullscreenView = view!!
@@ -186,6 +210,72 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback {
186
210
}
187
211
}
188
212
webView.webViewClient = object : WebViewClient () {
213
+ override fun shouldInterceptRequest (
214
+ view : WebView ? ,
215
+ request : WebResourceRequest ?
216
+ ): WebResourceResponse ? {
217
+ if (request!! .requestHeaders.containsKey(" x-user-agent" )) {
218
+ with (URL (request!! .url.toString()).openConnection() as HttpURLConnection ) {
219
+ requestMethod = request.method
220
+ val isClient5 = request.requestHeaders.containsKey(" x-youtube-client-name" ) && request.requestHeaders[" x-youtube-client-name" ] == " 5"
221
+ // map headers
222
+ for (header in request!! .requestHeaders) {
223
+ fun getReal (key : String , value : String ): Array <String >? {
224
+ if (key == " x-user-agent" ) {
225
+ return arrayOf(" User-Agent" , value)
226
+ }
227
+ if (key == " User-Agent" ) {
228
+ return null
229
+ }
230
+ if (key == " x-fta-request-id" ) {
231
+ return null
232
+ }
233
+ if (isClient5) {
234
+ if (key == " referrer" ) {
235
+ return null
236
+ }
237
+ if (key == " origin" ) {
238
+ return null
239
+ }
240
+ if (key == " Sec-Fetch-Site" ) {
241
+ return null
242
+ }
243
+ if (key == " Sec-Fetch-Mode" ) {
244
+ return null
245
+ }
246
+ if (key == " Sec-Fetch-Dest" ) {
247
+ return null
248
+ }
249
+ if (key == " sec-ch-ua" ) {
250
+ return null
251
+ }
252
+ if (key == " sec-ch-ua-mobile" ) {
253
+ return null
254
+ }
255
+ if (key == " sec-ch-ua-platform" ) {
256
+ return null
257
+ }
258
+ }
259
+ return arrayOf(key, value)
260
+ }
261
+ val real = getReal(header.key, header.value)
262
+ if (real != = null ) {
263
+ setRequestProperty(real[0 ], real[1 ])
264
+ }
265
+ }
266
+ if (request.requestHeaders.containsKey(" x-fta-request-id" )) {
267
+ if (pendingRequestBodies.containsKey(request.requestHeaders[" x-fta-request-id" ])) {
268
+ val body = pendingRequestBodies[request.requestHeaders[" x-fta-request-id" ]]
269
+ pendingRequestBodies.remove(request.requestHeaders[" x-fta-request-id" ])
270
+ outputStream.write(body!! .toByteArray())
271
+ }
272
+ }
273
+ // 🧝♀️ magic
274
+ return WebResourceResponse (this .contentType, this .contentEncoding, inputStream!! )
275
+ }
276
+ }
277
+ return super .shouldInterceptRequest(view, request)
278
+ }
189
279
override fun shouldOverrideUrlLoading (view : WebView ? , request : WebResourceRequest ? ): Boolean {
190
280
if (request!! .url!! .scheme == " file" ) {
191
281
// don't send file url requests to a web browser (it will crash the app)
0 commit comments