Skip to content

Commit 8866a95

Browse files
Rewrite PoToken functionality using coroutines
1 parent 99aae7e commit 8866a95

3 files changed

Lines changed: 134 additions & 186 deletions

File tree

app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenGenerator.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.schabi.newpipe.util.potoken
22

33
import android.content.Context
4-
import io.reactivex.rxjava3.core.Single
54
import java.io.Closeable
65

76
/**
@@ -14,13 +13,13 @@ interface PoTokenGenerator : Closeable {
1413
* `webPoSignalOutput` previously obtained in the initialization of [PoTokenWebView]. Can be
1514
* called multiple times.
1615
*/
17-
fun generatePoToken(identifier: String): Single<String>
16+
suspend fun generatePoToken(identifier: String): String
1817

1918
/**
2019
* @return whether the `integrityToken` is expired, in which case all tokens generated by
2120
* [generatePoToken] will be invalid
2221
*/
23-
fun isExpired(): Boolean
22+
val isExpired: Boolean
2423

2524
interface Factory {
2625
/**
@@ -30,6 +29,6 @@ interface PoTokenGenerator : Closeable {
3029
*
3130
* @param context used e.g. to load the HTML asset or to instantiate a WebView
3231
*/
33-
fun newPoTokenGenerator(context: Context): Single<PoTokenGenerator>
32+
suspend fun getNewPoTokenGenerator(context: Context): PoTokenGenerator
3433
}
3534
}

app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package org.schabi.newpipe.util.potoken
22

3-
import android.os.Handler
4-
import android.os.Looper
53
import android.util.Log
4+
import kotlinx.coroutines.Dispatchers
5+
import kotlinx.coroutines.runBlocking
6+
import kotlinx.coroutines.sync.Mutex
7+
import kotlinx.coroutines.sync.withLock
8+
import kotlinx.coroutines.withContext
69
import org.schabi.newpipe.App
710
import org.schabi.newpipe.BuildConfig
811
import org.schabi.newpipe.extractor.NewPipe
@@ -17,7 +20,7 @@ object PoTokenProviderImpl : PoTokenProvider {
1720
private val webViewSupported by lazy { DeviceUtils.supportsWebView() }
1821
private var webViewBadImpl = false // whether the system has a bad WebView implementation
1922

20-
private object WebPoTokenGenLock
23+
private val webPoTokenGenLock = Mutex()
2124
private var webPoTokenVisitorData: String? = null
2225
private var webPoTokenStreamingPot: String? = null
2326
private var webPoTokenGenerator: PoTokenGenerator? = null
@@ -27,18 +30,16 @@ object PoTokenProviderImpl : PoTokenProvider {
2730
return null
2831
}
2932

30-
try {
31-
return getWebClientPoToken(videoId = videoId, forceRecreate = false)
32-
} catch (e: RuntimeException) {
33-
// RxJava's Single wraps exceptions into RuntimeErrors, so we need to unwrap them here
34-
when (val cause = e.cause) {
33+
return try {
34+
runBlocking { getWebClientPoToken(videoId, forceRecreate = false) }
35+
} catch (e: Exception) {
36+
when (e) {
3537
is BadWebViewException -> {
3638
Log.e(TAG, "Could not obtain poToken because WebView is broken", e)
3739
webViewBadImpl = true
38-
return null
40+
null
3941
}
40-
null -> throw e
41-
else -> throw cause // includes PoTokenException
42+
else -> throw e // includes PoTokenException
4243
}
4344
}
4445
}
@@ -48,56 +49,52 @@ object PoTokenProviderImpl : PoTokenProvider {
4849
* case the current [webPoTokenGenerator] threw an error last time
4950
* [PoTokenGenerator.generatePoToken] was called
5051
*/
51-
private fun getWebClientPoToken(videoId: String, forceRecreate: Boolean): PoTokenResult {
52+
private suspend fun getWebClientPoToken(videoId: String, forceRecreate: Boolean): PoTokenResult {
5253
// just a helper class since Kotlin does not have builtin support for 4-tuples
5354
data class Quadruple<T1, T2, T3, T4>(val t1: T1, val t2: T2, val t3: T3, val t4: T4)
5455

5556
val (poTokenGenerator, visitorData, streamingPot, hasBeenRecreated) =
56-
synchronized(WebPoTokenGenLock) {
57-
val shouldRecreate = webPoTokenGenerator == null || forceRecreate ||
58-
webPoTokenGenerator!!.isExpired()
57+
webPoTokenGenLock.withLock {
58+
val gen = webPoTokenGenerator
59+
val shouldRecreate = forceRecreate || gen == null || gen.isExpired
5960

6061
if (shouldRecreate) {
61-
62-
val innertubeClientRequestInfo = InnertubeClientRequestInfo.ofWebClient()
63-
innertubeClientRequestInfo.clientInfo.clientVersion =
64-
YoutubeParsingHelper.getClientVersion()
65-
66-
webPoTokenVisitorData = YoutubeParsingHelper.getVisitorDataFromInnertube(
67-
innertubeClientRequestInfo,
68-
NewPipe.getPreferredLocalization(),
69-
NewPipe.getPreferredContentCountry(),
70-
YoutubeParsingHelper.getYouTubeHeaders(),
71-
YoutubeParsingHelper.YOUTUBEI_V1_URL,
72-
null,
73-
false
74-
)
75-
// close the current webPoTokenGenerator on the main thread
76-
webPoTokenGenerator?.let { Handler(Looper.getMainLooper()).post { it.close() } }
62+
webPoTokenVisitorData = withContext(Dispatchers.IO) {
63+
val innertubeClientRequestInfo = InnertubeClientRequestInfo.ofWebClient()
64+
innertubeClientRequestInfo.clientInfo.clientVersion =
65+
YoutubeParsingHelper.getClientVersion()
66+
67+
YoutubeParsingHelper.getVisitorDataFromInnertube(
68+
innertubeClientRequestInfo,
69+
NewPipe.getPreferredLocalization(),
70+
NewPipe.getPreferredContentCountry(),
71+
YoutubeParsingHelper.getYouTubeHeaders(),
72+
YoutubeParsingHelper.YOUTUBEI_V1_URL,
73+
null,
74+
false
75+
)
76+
}
77+
78+
withContext(Dispatchers.Main) {
79+
webPoTokenGenerator?.close()
80+
}
7781

7882
// create a new webPoTokenGenerator
79-
webPoTokenGenerator = PoTokenWebView
80-
.newPoTokenGenerator(App.instance).blockingGet()
83+
webPoTokenGenerator = PoTokenWebView.getNewPoTokenGenerator(App.instance)
8184

8285
// The streaming poToken needs to be generated exactly once before generating
8386
// any other (player) tokens.
84-
webPoTokenStreamingPot = webPoTokenGenerator!!
85-
.generatePoToken(webPoTokenVisitorData!!).blockingGet()
87+
webPoTokenStreamingPot = webPoTokenGenerator!!.generatePoToken(webPoTokenVisitorData!!)
8688
}
8789

88-
return@synchronized Quadruple(
89-
webPoTokenGenerator!!,
90-
webPoTokenVisitorData!!,
91-
webPoTokenStreamingPot!!,
92-
shouldRecreate
93-
)
90+
Quadruple(webPoTokenGenerator!!, webPoTokenVisitorData!!, webPoTokenStreamingPot!!, shouldRecreate)
9491
}
9592

9693
val playerPot = try {
9794
// Not using synchronized here, since poTokenGenerator would be able to generate
9895
// multiple poTokens in parallel if needed. The only important thing is for exactly one
9996
// visitorData/streaming poToken to be generated before anything else.
100-
poTokenGenerator.generatePoToken(videoId).blockingGet()
97+
poTokenGenerator.generatePoToken(videoId)
10198
} catch (throwable: Throwable) {
10299
if (hasBeenRecreated) {
103100
// the poTokenGenerator has just been recreated (and possibly this is already the
@@ -108,7 +105,7 @@ object PoTokenProviderImpl : PoTokenProvider {
108105
// this might happen for example if NewPipe goes in the background and the WebView
109106
// content is lost
110107
Log.e(TAG, "Failed to obtain poToken, retrying", throwable)
111-
return getWebClientPoToken(videoId = videoId, forceRecreate = true)
108+
return getWebClientPoToken(videoId, forceRecreate = true)
112109
}
113110
}
114111

0 commit comments

Comments
 (0)