Skip to content

Commit 5de75b4

Browse files
committed
Merge origin/main into main
2 parents 9f69bb2 + 274ba4d commit 5de75b4

403 files changed

Lines changed: 60 additions & 92872 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.

build.gradle

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@ allprojects {
33
apply plugin: 'maven-publish'
44

55
compileJava.options.encoding = 'UTF-8'
6-
compileTestJava.options.encoding = 'UTF-8'
7-
86
sourceCompatibility = 1.8
97
targetCompatibility = 1.8
108

11-
version 'v4.9.0'
9+
version 'v4.9.2'
1210
group 'com.github.TeamNewPipe'
1311

1412
repositories {
@@ -29,7 +27,6 @@ allprojects {
2927
ext {
3028
nanojsonVersion = "1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
3129
spotbugsVersion = "4.6.0"
32-
junitVersion = "5.8.2"
3330
checkstyleVersion = "9.3" // do not use latest version (10.0) as it requires compile JDK 11
3431
}
3532
}
@@ -45,14 +42,6 @@ subprojects {
4542
from sourceSets.main.allSource
4643
}
4744

48-
tasks.withType(Test) {
49-
testLogging {
50-
events "skipped", "failed"
51-
showStandardStreams = true
52-
exceptionFormat = 'full'
53-
}
54-
}
55-
5645
artifacts {
5746
archives sourcesJar
5847
}

extractor/build.gradle

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,6 @@ plugins {
33
id 'com.squareup.wire' version '4.4.0'
44
}
55

6-
test {
7-
// Pass on downloader type to tests for different CI jobs. See DownloaderFactory.java and ci.yml
8-
if (System.properties.containsKey('downloader')) {
9-
systemProperty('downloader', System.getProperty('downloader'))
10-
}
11-
useJUnitPlatform()
12-
dependsOn checkstyleMain // run checkstyle when testing
13-
}
14-
156
checkstyle {
167
getConfigDirectory().set(rootProject.file("checkstyle"))
178
ignoreFailures false
@@ -24,10 +15,6 @@ wire {
2415
}
2516
}
2617

27-
checkstyleTest {
28-
enabled false // do not checkstyle test files
29-
}
30-
3118
dependencies {
3219
implementation project(':timeago-parser')
3320

@@ -51,12 +38,4 @@ dependencies {
5138
implementation 'org.mozilla:rhino:1.7.13'
5239

5340
checkstyle "com.puppycrawl.tools:checkstyle:$checkstyleVersion"
54-
55-
testImplementation platform("org.junit:junit-bom:$junitVersion")
56-
testImplementation 'org.junit.jupiter:junit-jupiter-api'
57-
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
58-
testImplementation 'org.junit.jupiter:junit-jupiter-params'
59-
60-
61-
testImplementation 'com.google.code.gson:gson:2.9.1'
6241
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeApiDecoder.java

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.net.URLEncoder;
1616
import java.nio.charset.StandardCharsets;
1717
import java.util.ArrayList;
18+
import java.util.Collections;
1819
import java.util.HashMap;
1920
import java.util.List;
2021
import java.util.Map;
@@ -50,52 +51,50 @@ private YoutubeApiDecoder() {
5051
@Nonnull
5152
static String decodeSignature(@Nonnull final String playerId,
5253
@Nonnull final String signature) throws ParsingException {
53-
return decode(playerId, "sig", signature);
54+
return decode("sig", signature);
5455
}
5556

5657
/**
5758
* Decode a throttling parameter (n parameter) using the PipePipe API.
5859
*
5960
* @param playerId the YouTube player ID (8-character hash)
60-
* @param nParameter the obfuscated n parameter to decode
61+
* @param nParameter the obfuscated n parameter to decode
6162
* @return the deobfuscated n parameter
6263
* @throws ParsingException if the API call fails or returns invalid data
6364
*/
6465
@Nonnull
6566
static String decodeThrottlingParameter(@Nonnull final String playerId,
6667
@Nonnull final String nParameter)
6768
throws ParsingException {
68-
return decode(playerId, "n", nParameter);
69+
return decode( "n", nParameter);
6970
}
7071

7172
/**
7273
* Generic decode method that calls the PipePipe API.
7374
*
74-
* @param playerId the YouTube player ID (8-character hash)
75-
* @param paramType the parameter type ("sig" or "n")
76-
* @param value the obfuscated value to decode
75+
* @param paramType the parameter type ("sig" or "n")
76+
* @param value the obfuscated value to decode
7777
* @return the deobfuscated value
7878
* @throws ParsingException if the API call fails or returns invalid data
7979
*/
8080
@Nonnull
81-
private static String decode(@Nonnull final String playerId,
82-
@Nonnull final String paramType,
81+
private static String decode(@Nonnull final String paramType,
8382
@Nonnull final String value) throws ParsingException {
8483
// Check cache first
85-
final String cacheKey = playerId + ":" + paramType + ":" + value;
84+
final String cacheKey = paramType + ":" + value;
8685
final String cachedResult = DECODE_CACHE.get(cacheKey);
8786
if (cachedResult != null) {
8887
return cachedResult;
8988
}
9089

9190
try {
92-
// Build API URL
91+
// Build API URL (no player parameter)
9392
final String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8.name());
94-
final String url = API_BASE_URL + "?player=" + playerId + "&" + paramType + "=" + encodedValue;
93+
final String url = API_BASE_URL + "?" + paramType + "=" + encodedValue;
9594

9695
// Set headers
97-
final Map<String, java.util.List<String>> headers = new HashMap<>();
98-
headers.put("User-Agent", java.util.Collections.singletonList(USER_AGENT));
96+
final Map<String, List<String>> headers = new HashMap<>();
97+
headers.put("User-Agent", Collections.singletonList(USER_AGENT));
9998

10099
// Make API call
101100
final Response response = NewPipe.getDownloader().get(url, headers, Localization.DEFAULT);
@@ -106,20 +105,24 @@ private static String decode(@Nonnull final String playerId,
106105

107106
// Validate response structure
108107
if (!"result".equals(jsonResponse.getString("type"))) {
109-
throw new ParsingException("API returned unexpected type: " + jsonResponse.getString("type"));
108+
throw new ParsingException(
109+
"API returned unexpected type: " + jsonResponse.getString("type"));
110110
}
111111

112112
// Extract decoded value
113113
final JsonObject firstResponse = jsonResponse.getArray("responses").getObject(0);
114114
if (!"result".equals(firstResponse.getString("type"))) {
115-
throw new ParsingException("API response item has unexpected type: " + firstResponse.getString("type"));
115+
throw new ParsingException(
116+
"API response item has unexpected type: "
117+
+ firstResponse.getString("type"));
116118
}
117119

118120
final JsonObject data = firstResponse.getObject("data");
119121
final String decodedValue = data.getString(value);
120122

121123
if (decodedValue == null || decodedValue.isEmpty()) {
122-
throw new ParsingException("API returned empty decoded value for: " + value);
124+
throw new ParsingException(
125+
"API returned empty decoded value for: " + value);
123126
}
124127

125128
// Cache the result
@@ -130,6 +133,8 @@ private static String decode(@Nonnull final String playerId,
130133
throw new ParsingException("Failed to call decode API", e);
131134
} catch (final JsonParserException e) {
132135
throw new ParsingException("Failed to parse API response", e);
136+
} catch (final ParsingException e) {
137+
throw e;
133138
} catch (final Exception e) {
134139
throw new ParsingException("Unexpected error during decoding", e);
135140
}
@@ -154,18 +159,15 @@ static int getCacheSize() {
154159
/**
155160
* Batch decode multiple signatures and throttling parameters in a single API call.
156161
*
157-
* @param playerId the YouTube player ID (8-character hash)
158162
* @param signatureParams list of obfuscated signatures to decode (can be null or empty)
159163
* @param nParams list of obfuscated n parameters to decode (can be null or empty)
160164
* @return a BatchDecodeResult containing the decoded values
161165
* @throws ParsingException if the API call fails or returns invalid data
162166
*/
163167
@Nonnull
164-
static BatchDecodeResult decodeBatch(@Nonnull final String playerId,
165-
@Nullable final List<String> signatureParams,
168+
static BatchDecodeResult decodeBatch(@Nullable final List<String> signatureParams,
166169
@Nullable final List<String> nParams)
167170
throws ParsingException {
168-
// Validate input
169171
final boolean hasSigs = signatureParams != null && !signatureParams.isEmpty();
170172
final boolean hasNs = nParams != null && !nParams.isEmpty();
171173

@@ -181,7 +183,7 @@ static BatchDecodeResult decodeBatch(@Nonnull final String playerId,
181183

182184
if (hasSigs) {
183185
for (final String sig : signatureParams) {
184-
final String cacheKey = playerId + ":sig:" + sig;
186+
final String cacheKey = "sig:" + sig;
185187
final String cachedResult = DECODE_CACHE.get(cacheKey);
186188
if (cachedResult != null) {
187189
sigResults.put(sig, cachedResult);
@@ -193,7 +195,7 @@ static BatchDecodeResult decodeBatch(@Nonnull final String playerId,
193195

194196
if (hasNs) {
195197
for (final String n : nParams) {
196-
final String cacheKey = playerId + ":n:" + n;
198+
final String cacheKey = "n:" + n;
197199
final String cachedResult = DECODE_CACHE.get(cacheKey);
198200
if (cachedResult != null) {
199201
nResults.put(n, cachedResult);
@@ -209,44 +211,50 @@ static BatchDecodeResult decodeBatch(@Nonnull final String playerId,
209211
}
210212

211213
try {
212-
// Build API URL with batch parameters
214+
// Build API URL without player parameter
213215
final StringBuilder urlBuilder = new StringBuilder(API_BASE_URL);
214-
urlBuilder.append("?player=").append(playerId);
216+
boolean firstParam = true;
215217

216218
if (!uncachedNs.isEmpty()) {
217-
urlBuilder.append("&n=");
219+
urlBuilder.append(firstParam ? '?' : '&');
220+
firstParam = false;
221+
urlBuilder.append("n=");
218222
for (int i = 0; i < uncachedNs.size(); i++) {
219223
if (i > 0) {
220224
urlBuilder.append(',');
221225
}
222-
urlBuilder.append(URLEncoder.encode(uncachedNs.get(i), StandardCharsets.UTF_8.name()));
226+
urlBuilder.append(URLEncoder.encode(
227+
uncachedNs.get(i), StandardCharsets.UTF_8.name()));
223228
}
224229
}
225230

226231
if (!uncachedSigs.isEmpty()) {
227-
urlBuilder.append("&sig=");
232+
urlBuilder.append(firstParam ? '?' : '&');
233+
urlBuilder.append("sig=");
228234
for (int i = 0; i < uncachedSigs.size(); i++) {
229235
if (i > 0) {
230236
urlBuilder.append(',');
231237
}
232-
urlBuilder.append(URLEncoder.encode(uncachedSigs.get(i), StandardCharsets.UTF_8.name()));
238+
urlBuilder.append(URLEncoder.encode(
239+
uncachedSigs.get(i), StandardCharsets.UTF_8.name()));
233240
}
234241
}
235242

236243
// Set headers
237-
final Map<String, java.util.List<String>> headers = new HashMap<>();
238-
headers.put("User-Agent", java.util.Collections.singletonList(USER_AGENT));
244+
final Map<String, List<String>> headers = new HashMap<>();
245+
headers.put("User-Agent", Collections.singletonList(USER_AGENT));
239246

240247
// Make API call
241-
final Response response = NewPipe.getDownloader().get(urlBuilder.toString(), headers, Localization.DEFAULT);
248+
final Response response = NewPipe.getDownloader().get(
249+
urlBuilder.toString(), headers, Localization.DEFAULT);
242250

243251
// Parse response
244252
final String responseBody = response.responseBody();
245253
final JsonObject jsonResponse = JsonParser.object().from(responseBody);
246254

247-
// Validate response structure
248255
if (!"result".equals(jsonResponse.getString("type"))) {
249-
throw new ParsingException("API returned unexpected type: " + jsonResponse.getString("type"));
256+
throw new ParsingException(
257+
"API returned unexpected type: " + jsonResponse.getString("type"));
250258
}
251259

252260
final JsonArray responses = jsonResponse.getArray("responses");
@@ -256,37 +264,41 @@ static BatchDecodeResult decodeBatch(@Nonnull final String playerId,
256264
if (!uncachedNs.isEmpty()) {
257265
final JsonObject nResponse = responses.getObject(responseIndex++);
258266
if (!"result".equals(nResponse.getString("type"))) {
259-
throw new ParsingException("N parameter response has unexpected type: " + nResponse.getString("type"));
267+
throw new ParsingException(
268+
"N parameter response has unexpected type: "
269+
+ nResponse.getString("type"));
260270
}
261271

262272
final JsonObject nData = nResponse.getObject("data");
263273
for (final String nParam : uncachedNs) {
264274
final String decodedValue = nData.getString(nParam);
265275
if (decodedValue == null || decodedValue.isEmpty()) {
266-
throw new ParsingException("API returned empty decoded value for n parameter: " + nParam);
276+
throw new ParsingException(
277+
"API returned empty decoded value for n parameter: " + nParam);
267278
}
268279
nResults.put(nParam, decodedValue);
269-
// Cache the result
270-
DECODE_CACHE.put(playerId + ":n:" + nParam, decodedValue);
280+
DECODE_CACHE.put("n:" + nParam, decodedValue);
271281
}
272282
}
273283

274284
// Process signature parameters (if present)
275285
if (!uncachedSigs.isEmpty()) {
276286
final JsonObject sigResponse = responses.getObject(responseIndex);
277287
if (!"result".equals(sigResponse.getString("type"))) {
278-
throw new ParsingException("Signature response has unexpected type: " + sigResponse.getString("type"));
288+
throw new ParsingException(
289+
"Signature response has unexpected type: "
290+
+ sigResponse.getString("type"));
279291
}
280292

281293
final JsonObject sigData = sigResponse.getObject("data");
282294
for (final String sig : uncachedSigs) {
283295
final String decodedValue = sigData.getString(sig);
284296
if (decodedValue == null || decodedValue.isEmpty()) {
285-
throw new ParsingException("API returned empty decoded value for signature: " + sig);
297+
throw new ParsingException(
298+
"API returned empty decoded value for signature: " + sig);
286299
}
287300
sigResults.put(sig, decodedValue);
288-
// Cache the result
289-
DECODE_CACHE.put(playerId + ":sig:" + sig, decodedValue);
301+
DECODE_CACHE.put("sig:" + sig, decodedValue);
290302
}
291303
}
292304

@@ -295,6 +307,8 @@ static BatchDecodeResult decodeBatch(@Nonnull final String playerId,
295307
throw new ParsingException("Failed to call batch decode API", e);
296308
} catch (final JsonParserException e) {
297309
throw new ParsingException("Failed to parse batch API response", e);
310+
} catch (final ParsingException e) {
311+
throw e;
298312
} catch (final Exception e) {
299313
throw new ParsingException("Unexpected error during batch decoding", e);
300314
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeJavaScriptPlayerManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ public static YoutubeApiDecoder.BatchDecodeResult deobfuscateBatch(
202202
@Nullable final List<String> signatures,
203203
@Nullable final List<String> throttlingParams) throws ParsingException {
204204
extractPlayerIdIfNeeded(videoId);
205-
return YoutubeApiDecoder.decodeBatch(cachedPlayerId, signatures, throttlingParams);
205+
return YoutubeApiDecoder.decodeBatch(signatures, throttlingParams);
206206
}
207207

208208
/**

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,9 @@ public List<StaffInfoItem> getStaffs() {
706706
@Override
707707
public String getDashMpdUrl() throws ParsingException {
708708
assertPageFetched();
709-
709+
if (streamType == StreamType.LIVE_STREAM && !StringUtils.isBlank(ServiceList.YouTube.getTokens())) {
710+
return "";
711+
}
710712
String dashUrl = getManifestUrl(
711713
"dash",
712714
Arrays.asList(safariStreamingData, androidStreamingData));

0 commit comments

Comments
 (0)