Skip to content

Commit cbc69a0

Browse files
Merge branch 'refactor' into Video-description-compose
2 parents f992132 + 47299c9 commit cbc69a0

366 files changed

Lines changed: 5925 additions & 1321 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ android {
2727
if (System.properties.containsKey('versionCodeOverride')) {
2828
versionCode System.getProperty('versionCodeOverride') as Integer
2929
} else {
30-
versionCode 1002
30+
versionCode 1003
3131
}
32-
versionName "0.27.5"
32+
versionName "0.27.6"
3333
if (System.properties.containsKey('versionNameSuffix')) {
3434
versionNameSuffix System.getProperty('versionNameSuffix')
3535
}
@@ -233,6 +233,7 @@ dependencies {
233233
implementation libs.androidx.work.runtime
234234
implementation libs.androidx.work.rxjava3
235235
implementation libs.androidx.material
236+
implementation libs.androidx.webkit
236237

237238
/** Third-party libraries **/
238239
// Instance state boilerplate elimination

app/proguard-rules.pro

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@
55

66
## Rules for NewPipeExtractor
77
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
8+
## Rules for Rhino and Rhino Engine
9+
-keep class org.mozilla.javascript.* { *; }
810
-keep class org.mozilla.javascript.** { *; }
11+
-keep class org.mozilla.javascript.engine.** { *; }
912
-keep class org.mozilla.classfile.ClassFileWriter
1013
-dontwarn org.mozilla.javascript.JavaToJSONConverters
1114
-dontwarn org.mozilla.javascript.tools.**
15+
-keep class javax.script.** { *; }
16+
-dontwarn javax.script.**
17+
-keep class jdk.dynalink.** { *; }
18+
-dontwarn jdk.dynalink.**
1219

1320
## Rules for ExoPlayer
1421
-keep class com.google.android.exoplayer2.** { *; }

app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
<intent-filter>
6565
<action android:name="android.intent.action.MEDIA_BUTTON" />
6666
</intent-filter>
67+
<intent-filter>
68+
<action android:name="android.media.browse.MediaBrowserService"/>
69+
</intent-filter>
6770
</service>
6871

6972
<activity
@@ -429,5 +432,10 @@
429432
<meta-data
430433
android:name="com.samsung.android.multidisplay.keep_process_alive"
431434
android:value="true" />
435+
<!-- Android Auto -->
436+
<meta-data android:name="com.google.android.gms.car.application"
437+
android:resource="@xml/automotive_app_desc" />
438+
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
439+
android:resource="@mipmap/ic_launcher" />
432440
</application>
433441
</manifest>

app/src/main/assets/po_token.html

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<!DOCTYPE html>
2+
<html lang="en"><head><title></title><script>
3+
/**
4+
* Factory method to create and load a BotGuardClient instance.
5+
* @param options - Configuration options for the BotGuardClient.
6+
* @returns A promise that resolves to a loaded BotGuardClient instance.
7+
*/
8+
function loadBotGuard(challengeData) {
9+
this.vm = this[challengeData.globalName];
10+
this.program = challengeData.program;
11+
this.vmFunctions = {};
12+
this.syncSnapshotFunction = null;
13+
14+
if (!this.vm)
15+
throw new Error('[BotGuardClient]: VM not found in the global object');
16+
17+
if (!this.vm.a)
18+
throw new Error('[BotGuardClient]: Could not load program');
19+
20+
const vmFunctionsCallback = function (
21+
asyncSnapshotFunction,
22+
shutdownFunction,
23+
passEventFunction,
24+
checkCameraFunction
25+
) {
26+
this.vmFunctions = {
27+
asyncSnapshotFunction: asyncSnapshotFunction,
28+
shutdownFunction: shutdownFunction,
29+
passEventFunction: passEventFunction,
30+
checkCameraFunction: checkCameraFunction
31+
};
32+
};
33+
34+
this.syncSnapshotFunction = this.vm.a(this.program, vmFunctionsCallback, true, this.userInteractionElement, function () {/** no-op */ }, [ [], [] ])[0]
35+
36+
// an asynchronous function runs in the background and it will eventually call
37+
// `vmFunctionsCallback`, however we need to manually tell JavaScript to pass
38+
// control to the things running in the background by interrupting this async
39+
// function in any way, e.g. with a delay of 1ms. The loop is most probably not
40+
// needed but is there just because.
41+
return new Promise(function (resolve, reject) {
42+
i = 0
43+
refreshIntervalId = setInterval(function () {
44+
if (!!this.vmFunctions.asyncSnapshotFunction) {
45+
resolve(this)
46+
clearInterval(refreshIntervalId);
47+
}
48+
if (i >= 10000) {
49+
reject("asyncSnapshotFunction is null even after 10 seconds")
50+
clearInterval(refreshIntervalId);
51+
}
52+
i += 1;
53+
}, 1);
54+
})
55+
}
56+
57+
/**
58+
* Takes a snapshot asynchronously.
59+
* @returns The snapshot result.
60+
* @example
61+
* ```ts
62+
* const result = await botguard.snapshot({
63+
* contentBinding: {
64+
* c: "a=6&a2=10&b=SZWDwKVIuixOp7Y4euGTgwckbJA&c=1729143849&d=1&t=7200&c1a=1&c6a=1&c6b=1&hh=HrMb5mRWTyxGJphDr0nW2Oxonh0_wl2BDqWuLHyeKLo",
65+
* e: "ENGAGEMENT_TYPE_VIDEO_LIKE",
66+
* encryptedVideoId: "P-vC09ZJcnM"
67+
* }
68+
* });
69+
*
70+
* console.log(result);
71+
* ```
72+
*/
73+
function snapshot(args) {
74+
return new Promise(function (resolve, reject) {
75+
if (!this.vmFunctions.asyncSnapshotFunction)
76+
return reject(new Error('[BotGuardClient]: Async snapshot function not found'));
77+
78+
this.vmFunctions.asyncSnapshotFunction(function (response) { resolve(response) }, [
79+
args.contentBinding,
80+
args.signedTimestamp,
81+
args.webPoSignalOutput,
82+
args.skipPrivacyBuffer
83+
]);
84+
});
85+
}
86+
87+
function runBotGuard(challengeData) {
88+
const interpreterJavascript = challengeData.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue;
89+
90+
if (interpreterJavascript) {
91+
new Function(interpreterJavascript)();
92+
} else throw new Error('Could not load VM');
93+
94+
const webPoSignalOutput = [];
95+
return loadBotGuard({
96+
globalName: challengeData.globalName,
97+
globalObj: this,
98+
program: challengeData.program
99+
}).then(function (botguard) {
100+
return botguard.snapshot({ webPoSignalOutput: webPoSignalOutput })
101+
}).then(function (botguardResponse) {
102+
return { webPoSignalOutput: webPoSignalOutput, botguardResponse: botguardResponse }
103+
})
104+
}
105+
106+
function obtainPoToken(webPoSignalOutput, integrityToken, identifier) {
107+
const getMinter = webPoSignalOutput[0];
108+
109+
if (!getMinter)
110+
throw new Error('PMD:Undefined');
111+
112+
const mintCallback = getMinter(integrityToken);
113+
114+
if (!(mintCallback instanceof Function))
115+
throw new Error('APF:Failed');
116+
117+
const result = mintCallback(identifier);
118+
119+
if (!result)
120+
throw new Error('YNJ:Undefined');
121+
122+
if (!(result instanceof Uint8Array))
123+
throw new Error('ODM:Invalid');
124+
125+
return result;
126+
}
127+
</script></head><body></body></html>

app/src/main/java/org/schabi/newpipe/App.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.acra.config.CoreConfigurationBuilder
2828
import org.schabi.newpipe.error.ReCaptchaActivity
2929
import org.schabi.newpipe.extractor.NewPipe
3030
import org.schabi.newpipe.extractor.downloader.Downloader
31+
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor
3132
import org.schabi.newpipe.ktx.hasAssignableCause
3233
import org.schabi.newpipe.settings.NewPipeSettings
3334
import org.schabi.newpipe.util.BridgeStateSaverInitializer
@@ -36,6 +37,7 @@ import org.schabi.newpipe.util.ServiceHelper
3637
import org.schabi.newpipe.util.StateSaver
3738
import org.schabi.newpipe.util.image.ImageStrategy
3839
import org.schabi.newpipe.util.image.PreferredImageQuality
40+
import org.schabi.newpipe.util.potoken.PoTokenProviderImpl
3941
import java.io.IOException
4042
import java.io.InterruptedIOException
4143
import java.net.SocketException
@@ -116,6 +118,8 @@ open class App :
116118
)
117119

118120
configureRxJavaErrorHandler()
121+
122+
YoutubeStreamExtractor.setPoTokenProvider(PoTokenProviderImpl)
119123
}
120124

121125
override fun newImageLoader(context: Context): ImageLoader =

app/src/main/java/org/schabi/newpipe/DownloaderImpl.java

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -142,46 +142,42 @@ public Response execute(@NonNull final Request request)
142142
}
143143

144144
final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
145-
.method(httpMethod, requestBody).url(url)
145+
.method(httpMethod, requestBody)
146+
.url(url)
146147
.addHeader("User-Agent", USER_AGENT);
147148

148149
final String cookies = getCookies(url);
149150
if (!cookies.isEmpty()) {
150151
requestBuilder.addHeader("Cookie", cookies);
151152
}
152153

153-
for (final Map.Entry<String, List<String>> pair : headers.entrySet()) {
154-
final String headerName = pair.getKey();
155-
final List<String> headerValueList = pair.getValue();
154+
headers.forEach((headerName, headerValueList) -> {
155+
requestBuilder.removeHeader(headerName);
156+
headerValueList.forEach(headerValue ->
157+
requestBuilder.addHeader(headerName, headerValue));
158+
});
159+
160+
try (
161+
okhttp3.Response response = client.newCall(requestBuilder.build()).execute()
162+
) {
163+
if (response.code() == 429) {
164+
throw new ReCaptchaException("reCaptcha Challenge requested", url);
165+
}
156166

157-
if (headerValueList.size() > 1) {
158-
requestBuilder.removeHeader(headerName);
159-
for (final String headerValue : headerValueList) {
160-
requestBuilder.addHeader(headerName, headerValue);
167+
String responseBodyToReturn = null;
168+
try (ResponseBody body = response.body()) {
169+
if (body != null) {
170+
responseBodyToReturn = body.string();
161171
}
162-
} else if (headerValueList.size() == 1) {
163-
requestBuilder.header(headerName, headerValueList.get(0));
164172
}
165173

174+
final String latestUrl = response.request().url().toString();
175+
return new Response(
176+
response.code(),
177+
response.message(),
178+
response.headers().toMultimap(),
179+
responseBodyToReturn,
180+
latestUrl);
166181
}
167-
168-
final okhttp3.Response response = client.newCall(requestBuilder.build()).execute();
169-
170-
if (response.code() == 429) {
171-
response.close();
172-
173-
throw new ReCaptchaException("reCaptcha Challenge requested", url);
174-
}
175-
176-
final ResponseBody body = response.body();
177-
String responseBodyToReturn = null;
178-
179-
if (body != null) {
180-
responseBodyToReturn = body.string();
181-
}
182-
183-
final String latestUrl = response.request().url().toString();
184-
return new Response(response.code(), response.message(), response.headers().toMultimap(),
185-
responseBodyToReturn, latestUrl);
186182
}
187183
}

app/src/main/java/org/schabi/newpipe/MainActivity.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import android.view.MenuItem;
3939
import android.view.View;
4040
import android.view.ViewGroup;
41+
import android.webkit.WebView;
4142
import android.widget.AdapterView;
4243
import android.widget.ArrayAdapter;
4344
import android.widget.FrameLayout;
@@ -88,6 +89,7 @@
8889
import org.schabi.newpipe.util.ServiceHelper;
8990
import org.schabi.newpipe.util.StateSaver;
9091
import org.schabi.newpipe.util.ThemeHelper;
92+
import org.schabi.newpipe.util.external_communication.ShareUtils;
9193
import org.schabi.newpipe.views.FocusOverlayView;
9294

9395
import java.util.ArrayList;
@@ -116,7 +118,8 @@ public class MainActivity extends AppCompatActivity {
116118
private static final int ITEM_ID_DOWNLOADS = -4;
117119
private static final int ITEM_ID_HISTORY = -5;
118120
private static final int ITEM_ID_SETTINGS = 0;
119-
private static final int ITEM_ID_ABOUT = 1;
121+
private static final int ITEM_ID_DONATION = 1;
122+
private static final int ITEM_ID_ABOUT = 2;
120123

121124
private static final int ORDER = 0;
122125

@@ -134,6 +137,19 @@ protected void onCreate(final Bundle savedInstanceState) {
134137
ThemeHelper.setDayNightMode(this);
135138
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
136139

140+
// Fixes text color turning black in dark/black mode:
141+
// https://github.com/TeamNewPipe/NewPipe/issues/12016
142+
// For further reference see: https://issuetracker.google.com/issues/37124582
143+
if (DeviceUtils.supportsWebView()) {
144+
try {
145+
new WebView(this);
146+
} catch (final Throwable e) {
147+
if (DEBUG) {
148+
Log.e(TAG, "Failed to create WebView", e);
149+
}
150+
}
151+
}
152+
137153
assureCorrectAppLanguage(this);
138154
super.onCreate(savedInstanceState);
139155

@@ -258,6 +274,10 @@ private void addDrawerMenuForCurrentService() throws ExtractionException {
258274
drawerLayoutBinding.navigation.getMenu()
259275
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
260276
.setIcon(R.drawable.ic_settings);
277+
drawerLayoutBinding.navigation.getMenu()
278+
.add(R.id.menu_options_about_group, ITEM_ID_DONATION, ORDER,
279+
R.string.donation_title)
280+
.setIcon(R.drawable.volunteer_activism_ic);
261281
drawerLayoutBinding.navigation.getMenu()
262282
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
263283
.setIcon(R.drawable.ic_info_outline);
@@ -333,6 +353,9 @@ private void optionsAboutSelected(final MenuItem item) {
333353
case ITEM_ID_SETTINGS:
334354
NavigationHelper.openSettings(this);
335355
break;
356+
case ITEM_ID_DONATION:
357+
ShareUtils.openUrlInBrowser(this, getString(R.string.donation_url));
358+
break;
336359
case ITEM_ID_ABOUT:
337360
NavigationHelper.openAbout(this);
338361
break;
@@ -569,8 +592,8 @@ public void onBackPressed() {
569592
if (player instanceof BackPressable backPressable && !backPressable.onBackPressed()) {
570593
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder)
571594
.setState(BottomSheetBehavior.STATE_COLLAPSED);
572-
return;
573595
}
596+
return;
574597
}
575598

576599
if (fragmentManager.getBackStackEntryCount() == 1) {
@@ -817,7 +840,8 @@ private void openMiniPlayerUponPlayerStarted() {
817840
@Override
818841
public void onReceive(final Context context, final Intent intent) {
819842
if (Objects.equals(intent.getAction(),
820-
VideoDetailFragment.ACTION_PLAYER_STARTED)) {
843+
VideoDetailFragment.ACTION_PLAYER_STARTED)
844+
&& PlayerHolder.getInstance().isPlayerOpen()) {
821845
openMiniPlayerIfMissing();
822846
// At this point the player is added 100%, we can unregister. Other actions
823847
// are useless since the fragment will not be removed after that.
@@ -829,6 +853,10 @@ public void onReceive(final Context context, final Intent intent) {
829853
final IntentFilter intentFilter = new IntentFilter();
830854
intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED);
831855
registerReceiver(broadcastReceiver, intentFilter);
856+
857+
// If the PlayerHolder is not bound yet, but the service is running, try to bind to it.
858+
// Once the connection is established, the ACTION_PLAYER_STARTED will be sent.
859+
PlayerHolder.getInstance().tryBindIfNeeded(this);
832860
}
833861
}
834862

@@ -840,4 +868,5 @@ private boolean bottomSheetHiddenOrCollapsed() {
840868
return sheetState == BottomSheetBehavior.STATE_HIDDEN
841869
|| sheetState == BottomSheetBehavior.STATE_COLLAPSED;
842870
}
871+
843872
}

0 commit comments

Comments
 (0)