Skip to content

Commit aaab05c

Browse files
committed
[YouTube] Catch any exception in YoutubeThrottlingDecrypter.apply and improve docs
This will prevent any future extractor break due to decryption failure, like it was excepted to be the case before. Some documentation about the throttling decryption has been also improved.
1 parent 52ded6e commit aaab05c

File tree

2 files changed

+55
-27
lines changed

2 files changed

+55
-27
lines changed

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

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,24 @@
1212
import java.util.regex.Pattern;
1313

1414
/**
15+
* YouTube's streaming URLs of HTML5 clients are protected with a cipher, which modifies their
16+
* {@code n} query parameter.
17+
*
1518
* <p>
16-
* YouTube's media is protected with a cipher,
17-
* which modifies the "n" query parameter of it's video playback urls.
18-
* This class handles extracting that "n" query parameter,
19-
* applying the cipher on it and returning the resulting url which is not throttled.
19+
* This class handles extracting that {@code n} query parameter, applying the cipher on it and
20+
* returning the resulting URL which is not throttled.
2021
* </p>
2122
*
22-
* <pre>
23-
* https://r5---sn-4g5ednsz.googlevideo.com/videoplayback?n=VVF2xyZLVRZZxHXZ&amp;other=other
24-
* </pre>
23+
* <p>
24+
* For instance,
25+
* {@code https://r5---sn-4g5ednsz.googlevideo.com/videoplayback?n=VVF2xyZLVRZZxHXZ&other=other}
2526
* becomes
26-
* <pre>
27-
* https://r5---sn-4g5ednsz.googlevideo.com/videoplayback?n=iHywZkMipkszqA&amp;other=other
28-
* </pre>
29-
* <br>
27+
* {@code https://r5---sn-4g5ednsz.googlevideo.com/videoplayback?n=iHywZkMipkszqA&other=other}.
28+
* </p>
29+
*
3030
* <p>
31-
* Decoding the "n" parameter is time intensive. For this reason, the results are cached.
32-
* The cache can be cleared using {@link #clearCache()}
31+
* Decoding the {@code n} parameter is time intensive. For this reason, the results are cached.
32+
* The cache can be cleared using {@link #clearCache()}.
3333
* </p>
3434
*
3535
*/
@@ -73,13 +73,35 @@ public YoutubeThrottlingDecrypter() throws ParsingException {
7373
}
7474

7575
/**
76+
* Try to decrypt a YouTube streaming URL protected with a throttling parameter.
77+
*
7678
* <p>
77-
* The videoId is only used to fetch the decryption function.
78-
* It can be a constant value of any existing video.
79-
* A constant value is discouraged, because it could allow tracking.
79+
* If the streaming URL provided doesn't contain a throttling parameter, it is returned as it
80+
* is; otherwise, the encrypted value is decrypted and this value is replaced by the decrypted
81+
* one.
82+
* </p>
83+
*
84+
* <p>
85+
* If the JavaScript code has been not extracted, it is extracted with the given video ID using
86+
* {@link YoutubeJavaScriptExtractor#extractJavaScriptCode(String)}.
87+
* </p>
88+
*
89+
* @param streamingUrl The streaming URL to decrypt, if needed.
90+
* @param videoId A video ID, used to fetch the JavaScript code to get the decryption
91+
* function. It can be a constant value of any existing video, but a
92+
* constant value is discouraged, because it could allow tracking.
93+
* @return a streaming URL with the decrypted parameter or the streaming URL itself if no
94+
* throttling parameter has been found
95+
* @throws ParsingException if the streaming URL contains a throttling parameter and its
96+
* decryption failed
8097
*/
81-
public static String apply(final String url, final String videoId) throws ParsingException {
82-
if (containsNParam(url)) {
98+
public static String apply(@Nonnull final String streamingUrl,
99+
@Nonnull final String videoId) throws ParsingException {
100+
if (!containsNParam(streamingUrl)) {
101+
return streamingUrl;
102+
}
103+
104+
try {
83105
if (FUNCTION == null) {
84106
final String playerJsCode
85107
= YoutubeJavaScriptExtractor.extractJavaScriptCode(videoId);
@@ -88,11 +110,11 @@ public static String apply(final String url, final String videoId) throws Parsin
88110
FUNCTION = parseDecodeFunction(playerJsCode, FUNCTION_NAME);
89111
}
90112

91-
final String oldNParam = parseNParam(url);
113+
final String oldNParam = parseNParam(streamingUrl);
92114
final String newNParam = decryptNParam(FUNCTION, FUNCTION_NAME, oldNParam);
93-
return replaceNParam(url, oldNParam, newNParam);
94-
} else {
95-
return url;
115+
return replaceNParam(streamingUrl, oldNParam, newNParam);
116+
} catch (final Exception e) {
117+
throw new ParsingException("Could not parse, decrypt or replace n parameter", e);
96118
}
97119
}
98120

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -602,15 +602,21 @@ public List<VideoStream> getVideoOnlyStreams() throws ExtractionException {
602602
}
603603

604604
/**
605-
* Try to decrypt url and fallback to given url, because decryption is not
606-
* always needed.
605+
* Try to decrypt a streaming URL and fallback to the given URL, because decryption may fail if
606+
* YouTube does breaking changes.
607+
*
608+
* <p>
607609
* This way a breaking change from YouTube does not result in a broken extractor.
610+
* </p>
611+
*
612+
* @param streamUrl the stream URL to decrypt with {@link YoutubeThrottlingDecrypter}
613+
* @param videoId the video ID to use when extracting JavaScript player code, if needed
608614
*/
609-
private String tryDecryptUrl(final String url, final String videoId) {
615+
private String tryDecryptUrl(final String streamUrl, final String videoId) {
610616
try {
611-
return YoutubeThrottlingDecrypter.apply(url, videoId);
617+
return YoutubeThrottlingDecrypter.apply(streamUrl, videoId);
612618
} catch (final ParsingException e) {
613-
return url;
619+
return streamUrl;
614620
}
615621
}
616622

0 commit comments

Comments
 (0)