Do some code improvements
Use final where possible, annotate some methods and parameters as Nonnull and format new code to be in the 100 characters limit per line.
This commit is contained in:
parent
1c30a2725e
commit
32055147e0
|
@ -12,11 +12,12 @@ import org.schabi.newpipe.extractor.utils.Parser;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* YouTube restricts streaming their media in multiple ways by requiring clients to apply a cipher function
|
* YouTube restricts streaming their media in multiple ways by requiring clients to apply a cipher
|
||||||
* on parameters of requests.
|
* function on parameters of requests.
|
||||||
* The cipher function is sent alongside as a JavaScript function.
|
* The cipher function is sent alongside as a JavaScript function.
|
||||||
* <p>
|
* <p>
|
||||||
* This class handling fetching the JavaScript file in order to allow other classes to extract the needed functions.
|
* This class handling fetching the JavaScript file in order to allow other classes to extract the
|
||||||
|
* needed functions.
|
||||||
*/
|
*/
|
||||||
public class YoutubeJavaScriptExtractor {
|
public class YoutubeJavaScriptExtractor {
|
||||||
|
|
||||||
|
@ -27,14 +28,16 @@ public class YoutubeJavaScriptExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the JavaScript file. The result is cached, so subsequent calls use the result of previous calls.
|
* Extracts the JavaScript file. The result is cached, so subsequent calls use the result of
|
||||||
|
* previous calls.
|
||||||
*
|
*
|
||||||
* @param videoId Does not influence the result, but a valid video id may help in the chance that YouTube tracks it.
|
* @param videoId Does not influence the result, but a valid video id may help in the chance
|
||||||
* @return The whole javascript file as a string.
|
* that YouTube tracks it.
|
||||||
|
* @return The whole JavaScript file as a string.
|
||||||
* @throws ParsingException If the extraction failed.
|
* @throws ParsingException If the extraction failed.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static String extractJavaScriptCode(String videoId) throws ParsingException {
|
public static String extractJavaScriptCode(final String videoId) throws ParsingException {
|
||||||
if (cachedJavaScriptCode == null) {
|
if (cachedJavaScriptCode == null) {
|
||||||
final String playerJsUrl = YoutubeJavaScriptExtractor.cleanJavaScriptUrl(
|
final String playerJsUrl = YoutubeJavaScriptExtractor.cleanJavaScriptUrl(
|
||||||
YoutubeJavaScriptExtractor.extractJavaScriptUrl(videoId));
|
YoutubeJavaScriptExtractor.extractJavaScriptUrl(videoId));
|
||||||
|
@ -45,18 +48,19 @@ public class YoutubeJavaScriptExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link YoutubeJavaScriptExtractor#extractJavaScriptCode(String)} but with a constant value for videoId.
|
* Same as {@link YoutubeJavaScriptExtractor#extractJavaScriptCode(String)} but with a constant
|
||||||
|
* value for videoId.
|
||||||
* Possible because the videoId has no influence on the result.
|
* Possible because the videoId has no influence on the result.
|
||||||
* <p>
|
* <p>
|
||||||
* In the off chance that YouTube tracks with which video id the request is made, it may make sense to pass in
|
* In the off chance that YouTube tracks with which video id the request is made, it may make
|
||||||
* video ids.
|
* sense to pass in video ids.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static String extractJavaScriptCode() throws ParsingException {
|
public static String extractJavaScriptCode() throws ParsingException {
|
||||||
return extractJavaScriptCode("d4IGg5dqeO8");
|
return extractJavaScriptCode("d4IGg5dqeO8");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String extractJavaScriptUrl(String videoId) throws ParsingException {
|
private static String extractJavaScriptUrl(final String videoId) throws ParsingException {
|
||||||
try {
|
try {
|
||||||
final String embedUrl = "https://www.youtube.com/embed/" + videoId;
|
final String embedUrl = "https://www.youtube.com/embed/" + videoId;
|
||||||
final String embedPageContent = NewPipe.getDownloader()
|
final String embedPageContent = NewPipe.getDownloader()
|
||||||
|
@ -84,7 +88,8 @@ public class YoutubeJavaScriptExtractor {
|
||||||
throw new ParsingException("Embedded info did not provide YouTube player js url");
|
throw new ParsingException("Embedded info did not provide YouTube player js url");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String cleanJavaScriptUrl(String playerJsUrl) {
|
@Nonnull
|
||||||
|
private static String cleanJavaScriptUrl(@Nonnull final String playerJsUrl) {
|
||||||
if (playerJsUrl.startsWith("//")) {
|
if (playerJsUrl.startsWith("//")) {
|
||||||
return HTTPS + playerJsUrl;
|
return HTTPS + playerJsUrl;
|
||||||
} else if (playerJsUrl.startsWith("/")) {
|
} else if (playerJsUrl.startsWith("/")) {
|
||||||
|
@ -95,10 +100,12 @@ public class YoutubeJavaScriptExtractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String downloadJavaScriptCode(String playerJsUrl) throws ParsingException {
|
@Nonnull
|
||||||
|
private static String downloadJavaScriptCode(final String playerJsUrl)
|
||||||
|
throws ParsingException {
|
||||||
try {
|
try {
|
||||||
return NewPipe.getDownloader().get(playerJsUrl, Localization.DEFAULT).responseBody();
|
return NewPipe.getDownloader().get(playerJsUrl, Localization.DEFAULT).responseBody();
|
||||||
} catch (Exception e) {
|
} catch (final Exception e) {
|
||||||
throw new ParsingException("Could not get player js code from url: " + playerJsUrl);
|
throw new ParsingException("Could not get player js code from url: " + playerJsUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.utils.JavaScript;
|
import org.schabi.newpipe.extractor.utils.JavaScript;
|
||||||
import org.schabi.newpipe.extractor.utils.Parser;
|
import org.schabi.newpipe.extractor.utils.Parser;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -33,11 +34,12 @@ public class YoutubeThrottlingDecrypter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Use this if you care about the off chance that YouTube tracks with which videoId the cipher is requested.
|
* Use this if you care about the off chance that YouTube tracks with which videoId the cipher
|
||||||
|
* is requested.
|
||||||
* </p>
|
* </p>
|
||||||
* Otherwise use the no-arg constructor which uses a constant value.
|
* Otherwise use the no-arg constructor which uses a constant value.
|
||||||
*/
|
*/
|
||||||
public YoutubeThrottlingDecrypter(String videoId) throws ParsingException {
|
public YoutubeThrottlingDecrypter(final String videoId) throws ParsingException {
|
||||||
final String playerJsCode = YoutubeJavaScriptExtractor.extractJavaScriptCode(videoId);
|
final String playerJsCode = YoutubeJavaScriptExtractor.extractJavaScriptCode(videoId);
|
||||||
|
|
||||||
functionName = parseDecodeFunctionName(playerJsCode);
|
functionName = parseDecodeFunctionName(playerJsCode);
|
||||||
|
@ -51,17 +53,22 @@ public class YoutubeThrottlingDecrypter {
|
||||||
function = parseDecodeFunction(playerJsCode, functionName);
|
function = parseDecodeFunction(playerJsCode, functionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String parseDecodeFunctionName(String playerJsCode) throws Parser.RegexException {
|
private String parseDecodeFunctionName(final String playerJsCode)
|
||||||
Pattern pattern = Pattern.compile("b=a\\.get\\(\"n\"\\)\\)&&\\(b=(\\w+)\\(b\\),a\\.set\\(\"n\",b\\)");
|
throws Parser.RegexException {
|
||||||
|
Pattern pattern = Pattern.compile(
|
||||||
|
"b=a\\.get\\(\"n\"\\)\\)&&\\(b=(\\w+)\\(b\\),a\\.set\\(\"n\",b\\)");
|
||||||
return Parser.matchGroup1(pattern, playerJsCode);
|
return Parser.matchGroup1(pattern, playerJsCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String parseDecodeFunction(String playerJsCode, String functionName) throws Parser.RegexException {
|
@Nonnull
|
||||||
Pattern functionPattern = Pattern.compile(functionName + "=function(.*?;)\n", Pattern.DOTALL);
|
private String parseDecodeFunction(final String playerJsCode, final String functionName)
|
||||||
return "function " + functionName + Parser.matchGroup1(functionPattern, playerJsCode);
|
throws Parser.RegexException {
|
||||||
|
Pattern functionPattern = Pattern.compile(functionName + "=function(.*?;)\n",
|
||||||
|
Pattern.DOTALL);
|
||||||
|
return "function " + functionName + Parser.matchGroup1(functionPattern, playerJsCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String apply(String url) throws Parser.RegexException {
|
public String apply(final String url) throws Parser.RegexException {
|
||||||
if (containsNParam(url)) {
|
if (containsNParam(url)) {
|
||||||
String oldNParam = parseNParam(url);
|
String oldNParam = parseNParam(url);
|
||||||
String newNParam = decryptNParam(oldNParam);
|
String newNParam = decryptNParam(oldNParam);
|
||||||
|
@ -71,16 +78,16 @@ public class YoutubeThrottlingDecrypter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsNParam(String url) {
|
private boolean containsNParam(final String url) {
|
||||||
return Parser.isMatch(N_PARAM_REGEX, url);
|
return Parser.isMatch(N_PARAM_REGEX, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String parseNParam(String url) throws Parser.RegexException {
|
private String parseNParam(final String url) throws Parser.RegexException {
|
||||||
Pattern nValuePattern = Pattern.compile(N_PARAM_REGEX);
|
Pattern nValuePattern = Pattern.compile(N_PARAM_REGEX);
|
||||||
return Parser.matchGroup1(nValuePattern, url);
|
return Parser.matchGroup1(nValuePattern, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String decryptNParam(String nParam) {
|
private String decryptNParam(final String nParam) {
|
||||||
if (nParams.containsKey(nParam)) {
|
if (nParams.containsKey(nParam)) {
|
||||||
return nParams.get(nParam);
|
return nParams.get(nParam);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +96,10 @@ public class YoutubeThrottlingDecrypter {
|
||||||
return decryptedNParam;
|
return decryptedNParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String replaceNParam(String url, String oldValue, String newValue) {
|
@Nonnull
|
||||||
|
private String replaceNParam(@Nonnull final String url,
|
||||||
|
final String oldValue,
|
||||||
|
final String newValue) {
|
||||||
return url.replace(oldValue, newValue);
|
return url.replace(oldValue, newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,17 @@ public class JavaScript {
|
||||||
private JavaScript() {
|
private JavaScript() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String run(String function, String functionName, String... parameters) {
|
public static String run(final String function,
|
||||||
|
final String functionName,
|
||||||
|
final String... parameters) {
|
||||||
try {
|
try {
|
||||||
Context context = Context.enter();
|
final Context context = Context.enter();
|
||||||
context.setOptimizationLevel(-1);
|
context.setOptimizationLevel(-1);
|
||||||
ScriptableObject scope = context.initSafeStandardObjects();
|
final ScriptableObject scope = context.initSafeStandardObjects();
|
||||||
|
|
||||||
context.evaluateString(scope, function, functionName, 1, null);
|
context.evaluateString(scope, function, functionName, 1, null);
|
||||||
Function jsFunction = (Function) scope.get(functionName, scope);
|
final Function jsFunction = (Function) scope.get(functionName, scope);
|
||||||
Object result = jsFunction.call(context, scope, scope, parameters);
|
final Object result = jsFunction.call(context, scope, scope, parameters);
|
||||||
return result.toString();
|
return result.toString();
|
||||||
} finally {
|
} finally {
|
||||||
Context.exit();
|
Context.exit();
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class YoutubeJavaScriptExtractorTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPlayerJsCode(String playerJsCode) {
|
private void assertPlayerJsCode(final String playerJsCode) {
|
||||||
assertThat(playerJsCode, allOf(
|
assertThat(playerJsCode, allOf(
|
||||||
containsString(" Copyright The Closure Library Authors.\n"
|
containsString(" Copyright The Closure Library Authors.\n"
|
||||||
+ " SPDX-License-Identifier: Apache-2.0"),
|
+ " SPDX-License-Identifier: Apache-2.0"),
|
||||||
|
|
Loading…
Reference in New Issue