Skip to content

Commit f67f5f7

Browse files
committed
Merge branch 'dev' into extended
2 parents 7923cbb + 427bcbc commit f67f5f7

File tree

312 files changed

+4260
-797
lines changed

Some content is hidden

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

312 files changed

+4260
-797
lines changed

.github/CONTRIBUTING.md

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
PipeBender contribution guidelines
2+
===============================
3+
4+
## Crash reporting
5+
6+
Report crashes through the **automated crash report system** of PipeBender.
7+
This way all the data needed for debugging is included in your bug report for GitHub.
8+
You'll see *exactly* what is sent, be able to add **your comments**, and then send it.
9+
10+
## Issue reporting/feature requests
11+
12+
* **Already reported**? Browse the existing issues in [NewPipe](https://github.com/TeamNewPipe/NewPipe/issues), [Tubular] and [PipeBender] repositories to make sure your issue/feature hasn't been reported/requested.
13+
* **Already fixed**? Check whether your issue/feature is already fixed/implemented.
14+
* **Still relevant**? Check if the issue still exists in the latest release/beta version.
15+
* **Can you fix it**? If you are an Android/Java developer, you are always welcome to fix an issue or implement a feature yourself. PRs welcome! See [Code contribution](#code-contribution) for more info.
16+
* **Is it in English**? Issues in other languages will be ignored unless someone translates them.
17+
* **Is it one issue**? Multiple issues require multiple reports, that can be linked to track their statuses.
18+
* **The template**: Fill it out, everyone wins. Your issue has a chance of getting fixed.
19+
20+
21+
## Translation
22+
23+
* NewPipe is translated via [Weblate](https://hosted.weblate.org/projects/newpipe/strings/). Log in there with your GitHub account, or register.
24+
* as MaintainTeam organization we will set up a distinct weblate page later
25+
* Add the language you want to translate if it is not there already: see [How to add a new language](https://github.com/TeamNewPipe/NewPipe/wiki/How-to-add-a-new-language-to-NewPipe) in the wiki.
26+
* NewPipe uses the [PrettyTime](https://github.com/ocpsoft/prettytime) library to display localized versions of dates and times. It needs to be translated, too. Read [these instructions to add a new language](https://www.ocpsoft.org/prettytime/#section-14) and [this issue](https://github.com/TeamNewPipe/NewPipe/issues/9134) for more info.
27+
28+
## Code contribution
29+
30+
### Guidelines
31+
32+
* Stick to NewPipe's *style conventions* of [checkStyle](https://github.com/checkstyle/checkstyle) and [ktlint](https://github.com/pinterest/ktlint). They run each time you build the project.
33+
* Stick to [F-Droid contribution guidelines](https://f-droid.org/wiki/page/Inclusion_Policy).
34+
* In particular **do not bring non-free software** (e.g. binary blobs) into the project. Make sure you do not introduce any closed-source library from Google.
35+
36+
### Before starting development
37+
38+
* If you want to help out with an existing bug report or feature request, **leave a comment** on that issue saying you want to try your hand at it.
39+
* If there is no existing issue for what you want to work on, **open a new one** describing the changes you are planning to introduce. This gives the team and the community a chance to give **feedback** before you spend time on something that is already in development, should be done differently, or should be avoided completely.
40+
* Please show **intention to maintain your features** and code after you contribute a PR. Unmaintained code is a hassle for core developers. If you do not intend to maintain features you plan to contribute, please rethink your submission, or clearly state that in the PR description.
41+
* Create PRs that cover only **one specific issue/solution/bug**. Do not create PRs that are huge monoliths and could have been split into multiple independent contributions.
42+
* NewPipe uses [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) to fetch data from services. If you need to change something there, you must test your changes in NewPipe. Telling NewPipe to use your extractor version can be accomplished by editing the `app/build.gradle` file: the comments under the "NewPipe libraries" section of `dependencies` will help you out.
43+
* We use [LastPipeExtractor](https://github.com/MaintainTeam/LastPipeExtractor)
44+
45+
### Creating a Pull Request (PR)
46+
47+
* Make changes on a **separate branch** with a meaningful name, not on the _master_ branch or the _dev_ branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request (PR) on GitHub.
48+
* Please **test** (compile and run) your code before submitting changes! Ideally, provide test feedback in the PR description. Untested code will **not** be merged!
49+
* Respond if someone requests changes or otherwise raises issues about your PRs.
50+
* Try to figure out yourself why builds on our CI fail.
51+
* Make sure your PR is **up-to-date** with the rest of the code. Often, a simple click on "Update branch" will do the job, but if not, you must *rebase* your branch on the `dev` branch manually and resolve the conflicts on your own. You can find help [on the wiki](https://github.com/TeamNewPipe/NewPipe/wiki/How-to-merge-a-PR). Doing this makes the maintainers' job way easier.
52+
53+
## IDE setup & building the app
54+
55+
### Basic setup
56+
57+
NewPipe is developed using [Android Studio](https://developer.android.com/studio/). Learn more about how to install it and how it works in the [official documentation](https://developer.android.com/studio/intro). In particular, make sure you have accepted Android Studio's SDK licences. Once Android Studio is ready, setting up the NewPipe project is fairly simple:
58+
- Clone the NewPipe repository with `git clone https://github.com/TeamNewPipe/NewPipe.git` (or use the link from your own fork, if you want to open a PR).
59+
- Open the folder you just cloned with Android Studio.
60+
- Build and run it just like you would do with any other app, with the green triangle in the top bar.
61+
62+
You may find [SonarLint](https://www.sonarlint.org/intellij)'s **inspections** useful in helping you to write good code and prevent bugs.
63+
64+
### checkStyle setup
65+
66+
The [checkStyle](https://github.com/checkstyle/checkstyle) plugin verifies that Java code abides by the project style. It runs automatically each time you build the project. If you want to view errors directly in the editor, instead of having to skim through the build output, you can install an Android Studio plugin:
67+
- Go to `File -> Settings -> Plugins`, search for `checkstyle` and install `CheckStyle-IDEA`.
68+
- Go to `File -> Settings -> Tools -> Checkstyle`.
69+
- Add NewPipe's configuration file by clicking the `+` in the right toolbar of the "Configuration File" list.
70+
- Under the "Use a local Checkstyle file" bullet, click on `Browse` and, enter `checkstyle` folder under the project's root path and pick the file named `checkstyle.xml`.
71+
- Enable "Store relative to project location" so that moving the directory around does not create issues.
72+
- Insert a description in the top bar, then click `Next` and then `Finish`.
73+
- Activate the configuration file you just added by enabling the checkbox on the left.
74+
- Click `Ok` and you are done.
75+
76+
### ktlint setup
77+
78+
The [ktlint](https://github.com/pinterest/ktlint) plugin does the same job as checkStyle for Kotlin files. Installing the related plugin is as simple as going to `File -> Settings -> Plugins`, searching for `ktlint` and installing `Ktlint (unofficial)`.
79+
80+
## Communication
81+
82+
* You can use a Matrix account to join the NewPipe channel at [#newpipe:matrix.newpipe-ev.de](https://matrix.to/#/#newpipe:matrix.newpipe-ev.de). Some convenient clients, available both for phone and desktop, are listed at that link.
83+
* Alternatively, the #newpipe channel on Libera Chat (`ircs://irc.libera.chat:6697/newpipe`) can also be joined, as it is bridged to the Matrix room. [Click here for webchat](https://web.libera.chat/#newpipe)!
84+
* You can post your suggestions, changes, ideas etc. on either GitHub or Matrix (including via IRC).

.github/workflows/build-release-apk.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
- uses: actions/setup-java@v4
1515
with:
1616
distribution: 'temurin'
17-
java-version: '17'
17+
java-version: '21'
1818
cache: 'gradle'
1919

2020
- name: "Build release APK"

.github/workflows/release.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,12 @@ jobs:
176176
cd "${{ github.workspace }}/output/"
177177
version=$(cat version.num)
178178
echo $version
179-
gh release create "v${version}" --title "${{ inputs.title }}" --notes-file "./changelog.md" --prerelease="true" --repo MaintainTeam/LastPipeBender
179+
gh release create "v${version}" --title "${{ inputs.title }}" --notes-file "./changelog.md" --prerelease="true" --discussion-category "Announcements" --repo MaintainTeam/LastPipeBender
180180
gh release upload "v${version}" $(echo $(find . -name '*.apk' -type f -exec basename \{} \;) checksums.txt) --repo MaintainTeam/LastPipeBender
181181
182182
- name: Archive reports for job
183183
uses: actions/upload-artifact@v4
184184
with:
185185
name: reports
186186
path: '*/build/reports'
187-
if: ${{ always() }}
187+
if: ${{ always() }}

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ This is the SHA fingerprint of LastPipeBender's signing key to verify downloaded
4747
1B:00:8D:64:BB:95:AB:47:74:D6:8B:87:F2:2B:8B:E9:A2:72:F4:92:4D:F5:20:29:D7:E6:18:38:35:D9:18:CC
4848
```
4949

50+
You should also check checksums in releases. Also you can compare checksums with GitHub Actions notices to ensure the apk file built with CI
51+
52+
5053
### Project Management
5154

5255
```mermaid

app/build.gradle

+4-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ android {
2323
if (System.properties.containsKey('versionCodeOverride')) {
2424
versionCode System.getProperty('versionCodeOverride') as Integer
2525
} else {
26-
versionCode 1002
26+
versionCode 1003
2727
}
28-
versionName "0.27.5"
28+
versionName "0.27.6"
2929
if (System.properties.containsKey('versionNameSuffix')) {
3030
versionNameSuffix System.getProperty('versionNameSuffix')
3131
}
@@ -220,7 +220,7 @@ dependencies {
220220
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
221221
// WORKAROUND: if you get errors with the NewPipeExtractor dependency, replace `v0.24.3` with
222222
// the corresponding commit hash, since JitPack is sometimes buggy
223-
implementation 'com.github.MaintainTeam:LastPipeExtractor:d6fd0c38ca6ed4602cc8b3b0bdd012eb24d8e60c'
223+
implementation 'com.github.MaintainTeam:LastPipeExtractor:6bf7594fbc59ba2c6372b71addbe31aa2e33118f'
224224
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
225225

226226
/** Checkstyle **/
@@ -253,6 +253,7 @@ dependencies {
253253
implementation "androidx.work:work-runtime-ktx:${androidxWorkVersion}"
254254
implementation "androidx.work:work-rxjava3:${androidxWorkVersion}"
255255
implementation 'com.google.android.material:material:1.11.0'
256+
implementation "androidx.webkit:webkit:1.9.0"
256257

257258
/** Third-party libraries **/
258259
// Instance state boilerplate elimination

app/src/main/assets/po_token.html

+127
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.java

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.schabi.newpipe.error.ReCaptchaActivity;
1818
import org.schabi.newpipe.extractor.NewPipe;
1919
import org.schabi.newpipe.extractor.downloader.Downloader;
20+
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
2021
import org.schabi.newpipe.ktx.ExceptionUtils;
2122
import org.schabi.newpipe.settings.NewPipeSettings;
2223
import org.schabi.newpipe.util.BridgeStateSaverInitializer;
@@ -26,6 +27,7 @@
2627
import org.schabi.newpipe.util.image.ImageStrategy;
2728
import org.schabi.newpipe.util.image.PicassoHelper;
2829
import org.schabi.newpipe.util.image.PreferredImageQuality;
30+
import org.schabi.newpipe.util.potoken.PoTokenProviderImpl;
2931

3032
import java.io.IOException;
3133
import java.io.InterruptedIOException;
@@ -118,6 +120,8 @@ public void onCreate() {
118120
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false));
119121

120122
configureRxJavaErrorHandler();
123+
124+
YoutubeStreamExtractor.setPoTokenProvider(PoTokenProviderImpl.INSTANCE);
121125
}
122126

123127
@Override

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

+25-29
Original file line numberDiff line numberDiff line change
@@ -137,46 +137,42 @@ public Response execute(@NonNull final Request request)
137137
}
138138

139139
final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
140-
.method(httpMethod, requestBody).url(url)
140+
.method(httpMethod, requestBody)
141+
.url(url)
141142
.addHeader("User-Agent", USER_AGENT);
142143

143144
final String cookies = getCookies(url);
144145
if (!cookies.isEmpty()) {
145146
requestBuilder.addHeader("Cookie", cookies);
146147
}
147148

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

152-
if (headerValueList.size() > 1) {
153-
requestBuilder.removeHeader(headerName);
154-
for (final String headerValue : headerValueList) {
155-
requestBuilder.addHeader(headerName, headerValue);
162+
String responseBodyToReturn = null;
163+
try (ResponseBody body = response.body()) {
164+
if (body != null) {
165+
responseBodyToReturn = body.string();
156166
}
157-
} else if (headerValueList.size() == 1) {
158-
requestBuilder.header(headerName, headerValueList.get(0));
159167
}
160168

169+
final String latestUrl = response.request().url().toString();
170+
return new Response(
171+
response.code(),
172+
response.message(),
173+
response.headers().toMultimap(),
174+
responseBodyToReturn,
175+
latestUrl);
161176
}
162-
163-
final okhttp3.Response response = client.newCall(requestBuilder.build()).execute();
164-
165-
if (response.code() == 429) {
166-
response.close();
167-
168-
throw new ReCaptchaException("reCaptcha Challenge requested", url);
169-
}
170-
171-
final ResponseBody body = response.body();
172-
String responseBodyToReturn = null;
173-
174-
if (body != null) {
175-
responseBodyToReturn = body.string();
176-
}
177-
178-
final String latestUrl = response.request().url().toString();
179-
return new Response(response.code(), response.message(), response.headers().toMultimap(),
180-
responseBodyToReturn, latestUrl);
181177
}
182178
}

0 commit comments

Comments
 (0)