Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/com/github/felipeucelli/javatube/Cipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public Cipher(String js, String ytPlayerJs) throws Exception {
}
private static String getInitialFunctionName(String js) throws Exception {
String[] functionPattern = {
"(?<sig>[a-zA-Z0-9_$]+)\\s*=\\s*function\\(\\s*(?<arg>[a-zA-Z0-9_$]+)\\s*\\)\\s*\\{\\s*(\\k<arg>)\\s*=\\s*(\\k<arg>)\\.split\\(\\s*\\\"\\\"\\s*\\)\\s*;\\s*[^}]+;\\s*return\\s+(\\k<arg>)\\.join\\(\\s*\\\"\\\"\\s*\\)\\}",
"(?<sig>[a-zA-Z0-9_$]+)\\s*=\\s*function\\(\\s*(?<arg>[a-zA-Z0-9_$]+)\\s*\\)\\s*\\{\\s*(\\k<arg>)\\s*=\\s*(\\k<arg>)\\.split\\(\\s*[a-zA-Z0-9_\\$\"\\[\\]]+\\s*\\)\\s*;\\s*[^}]+;\\s*return\\s+(\\k<arg>)\\.join\\(\\s*[a-zA-Z0-9_\\$\"\\[\\]]+\\s*\\)\\}",
"\\b[cs]\\s*&&\\s*[adf]\\.set\\([^,]+\\s*,\\s*encodeURIComponent\\s*\\(\\s*([a-zA-Z0-9$]+)\\(",
"(?<sig>[a-zA-Z0-9$]+)\\s*=\\s*function\\(\\s*(?<arg>[a-zA-Z0-9$]+)\\s*\\)\\s*\\{\\s*(?=arg)\\s*=\\s*(?=arg)\\.split\\(\\s*\\\"\\\"\\s*\\)\\s*;\\s*[^}]+;\\s*return\\s+(?=arg)\\.join\\(\\s*\\\"\\\"\\s*\\)",
"\\b[a-zA-Z0-9]+\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*encodeURIComponent\\s*\\(\\s*([a-zA-Z0-9$]+)\\(",
Expand Down Expand Up @@ -71,7 +71,7 @@ private String getThrottlingFunctionName(String js) throws Exception {

// New pattern used in player "2f238d39" on October 10, 2024
// a.D && (b = "nn" [+a.D], zM(a), c = a.j[b] || null) && (c = XDa[0](c), a.set(b, c))
";[a-zA-Z]\\.[a-zA-Z]&&\\((?<arg>[a-zA-Z])=(?:\\\"nn\\\"|[a-zA-Z0-9]+\\(\\)).*?&&\\((?<func>[a-zA-Z])=(?<nfunc>[a-zA-Z0-9-_$]{3})\\[(?<idx>\\d{1})\\].*?[a-zA-Z].set\\(\\k<arg>.\\k<func>\\).*?\\}\\};"
";[a-zA-Z]\\.[a-zA-Z]&&\\((?<arg>[a-zA-Z])=(?:\\\"nn\\\"|[a-zA-Z0-9$]+\\(\\)).*?&&\\((?<func>[a-zA-Z])=(?<nfunc>[a-zA-Z0-9-_$]{3})\\[(?<idx>\\d{1})\\].*?[a-zA-Z].set\\(\\k<arg>.\\k<func>\\).*?\\}\\};"
};

for(String pattern : functionPatterns){
Expand Down
155 changes: 129 additions & 26 deletions src/main/java/com/github/felipeucelli/javatube/JsInterpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@ public class JsInterpreter {
private static final String QUOTES = "'\"/";
private int namedObjectCounter = 0;
private static final Map<String, BiFunction<Object, Object, Object>> OPERATORS = createOperatorsMap();
private static final Map<String, BiFunction<Object, Object, Object>> UNARY_OPERATORS_X = createUnaryXOperatorsMap();
private static final Map<String, BiFunction<Object, Object, Object>> ALL_OPERATORS = mergeOperators();

private static Map<String, BiFunction<Object, Object, Object>> mergeOperators() {
Map<String, BiFunction<Object, Object, Object>> mergedMap = new LinkedHashMap<>();
mergedMap.putAll(OPERATORS);
mergedMap.putAll(UNARY_OPERATORS_X);
return mergedMap;
}


private static final Object JS_Undefined = new Object();
private static final Map<Character, Integer> RE_FLAGS = new HashMap<>();
static {
Expand Down Expand Up @@ -274,6 +285,25 @@ private static Map<String, BiFunction<Object, Object, Object>> createOperatorsMa

return OPERATORS;
}
private static Map<String, BiFunction<Object, Object, Object>> createUnaryXOperatorsMap() {
Map<String, BiFunction<Object, Object, Object>> OPERATORS = new LinkedHashMap<>();

OPERATORS.put("typeof", JsInterpreter::jsTypeof);

return OPERATORS;
}

private static Object jsTypeof(Object expr, Object o) {
if (expr == null) return "object";
if (expr instanceof Boolean) return "boolean";
if (expr instanceof Number)
return "number";
if (expr instanceof String) return "string";
if (expr instanceof Runnable) return "function";

return "object";
}

private static int jsBitOpOr(Object a, Object b) {
return zeroise(a) | zeroise(b);
}
Expand Down Expand Up @@ -583,11 +613,11 @@ private Object[] interpretStatement(String stmt, LocalNameSpace localVars, int a
String obj = expr.substring(4);
if (obj.startsWith("Date(")){
List<String> result = separateAtParen(obj.substring(4), null);
String left = result.get(0).substring(1).replace("\"", "");
String left = result.get(0).substring(1);
String right = result.get(1);

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
ZonedDateTime zonedDateTime = ZonedDateTime.parse(left, formatter);
ZonedDateTime zonedDateTime = ZonedDateTime.parse((String) interpretExpression(left, localVars, allowRecursion), formatter);
Instant instant = zonedDateTime.toInstant();

expr = dump(instant.toEpochMilli(), localVars) + right;
Expand All @@ -601,6 +631,21 @@ private Object[] interpretStatement(String stmt, LocalNameSpace localVars, int a
return new Object[]{0, shouldReturn};
}


for (String op : UNARY_OPERATORS_X.keySet()) {
if (!expr.startsWith(op)) {
continue;
}
String operand = expr.substring(op.length());
if (operand.isEmpty() || operand.charAt(0) != ' ') {
continue;
}
Object[] opResult = handleOperators(expr, localVars, allowRecursion);
if (opResult.length > 0) {
return new Object[]{opResult[0], shouldReturn};
}
}

if (expr.startsWith("{")){
List<String> result = separateAtParen(expr, "}");
String inner = result.get(0).substring(1);
Expand Down Expand Up @@ -692,7 +737,12 @@ Object[] dictItem(Object key, Object val) throws Exception {
List<String> result = separateAtParen(expr.substring(m.end() - 1), null);
Object cndn = result.get(0).substring(1);
expr = result.get(1);
List<String> result2 = separateAtParen(expr, null);
List<String> result2;
if (expr.startsWith("{")){
result2 = separateAtParen(expr, null);
}else{
result2 = separateAtParen(String.format(" %s;", expr), ";");
}
String ifExpr = result2.get(0).substring(1);
expr = result2.get(1);
String elseExpr = "";
Expand Down Expand Up @@ -957,7 +1007,12 @@ Object[] dictItem(Object key, Object val) throws Exception {
return new Object[]{Double.NaN, shouldReturn};

}else if(find && m2.group("return") != null){
return new Object[]{localVars.getValue(m2.group("name")), shouldReturn};
Object r = localVars.getValue(m2.group("name"));
if (r == null){
return new Object[]{extractGlobalVar(m2.group("name"), localVars), shouldReturn};
}else {
return new Object[]{r, shouldReturn};
}
}

if (find && m2.group("indexing") != null && m2.start() == 0){
Expand All @@ -966,25 +1021,9 @@ Object[] dictItem(Object key, Object val) throws Exception {
return new Object[]{index(val, idx, false), shouldReturn};
}

for(String op : OPERATORS.keySet()){
List<String> separated = _separate(expr, op, null);
StringBuilder rightExpr = new StringBuilder(separated.remove(separated.size() - 1));
while (true){
if ((op.equals("?") || op.equals("<") || op.equals(">") || op.equals("*") || op.equals("-")) && separated.size() > 1 && separated.get(separated.size() - 1).isEmpty()){
separated.remove(separated.size() - 1);
}else if (!(!separated.isEmpty() && op.equals("?") && rightExpr.toString().startsWith("."))){
break;
}
rightExpr.insert(0, op);
if (!op.equals("-")){
rightExpr.insert(0, separated.remove(separated.size() - 1) + op);
}
}
if (separated.isEmpty()){
continue;
}
Object leftVal = interpretExpression(String.join(op, separated), localVars, allowRecursion);
return new Object[]{operator(op, leftVal, rightExpr.toString(), expr, localVars, allowRecursion), shouldReturn};
Object[] opResult = handleOperators(expr, localVars, allowRecursion);
if(opResult.length > 0){
return new Object[]{opResult[0], shouldReturn};
}

try{
Expand Down Expand Up @@ -1105,7 +1144,7 @@ Object evalMethod() throws Exception {
assert obj != null;

String arg = (String) argvals.get(0);
return new ArrayList<>(Arrays.asList(((String) obj).split(arg)));
return new ArrayList<>(Arrays.asList(((String) obj).split(Pattern.quote(arg))));
}
case "join" -> {
assertion(obj instanceof List<?>, "must be applied on a list");
Expand Down Expand Up @@ -1245,6 +1284,18 @@ Object evalMethod() throws Exception {
}
throw new Exception("Unsupported JS expression: " + expr);
}

private Object extractGlobalVar(String var, LocalNameSpace localVars) {
Matcher matcher = Pattern.compile("var\\s?" + Pattern.quote(var) + "=(?<var>.*?)[,;]").matcher(code);
if (matcher.find()){
Object code = matcher.group("var");
localVars.put(var, code);
return code;
}else {
return null;
}
}

private Object extractObject(String objName) throws Exception {
Map<Object, FunctionWithRepr> obj = new HashMap<>();
Pattern pattern = Pattern.compile("(?x)" +
Expand Down Expand Up @@ -1293,6 +1344,30 @@ private static int customIndexOf(List<Object> list, Object value, int start) {
return -1;
}

private Object[] handleOperators(String expr, LocalNameSpace localVars, int allowRecursion) throws Exception {
for(String op : ALL_OPERATORS.keySet()){
List<String> separated = _separate(expr, op, null);
StringBuilder rightExpr = new StringBuilder(separated.remove(separated.size() - 1));
while (true){
if ((op.equals("?") || op.equals("<") || op.equals(">") || op.equals("*") || op.equals("-")) && separated.size() > 1 && separated.get(separated.size() - 1).isEmpty()){
separated.remove(separated.size() - 1);
}else if (!(!separated.isEmpty() && op.equals("?") && rightExpr.toString().startsWith("."))){
break;
}
rightExpr.insert(0, op);
if (!op.equals("-")){
rightExpr.insert(0, separated.remove(separated.size() - 1) + op);
}
}
if (separated.isEmpty()){
continue;
}
Object leftVal = interpretExpression(String.join(op, separated), localVars, allowRecursion);
return new Object[]{operator(op, leftVal, rightExpr.toString(), expr, localVars, allowRecursion), true};
}
return new Object[]{};
}

private Object operator(String op, Object leftVal, Object rightExpr, String expr, LocalNameSpace localVars, int allowRecursion) throws Exception {
if(Objects.equals(op, "||") || Objects.equals(op, "&&")){
if((Objects.equals(op, "&&") ^ (boolean) jsTernary(leftVal, true, false))){
Expand Down Expand Up @@ -1454,7 +1529,35 @@ private Map<String, String> extractFunctionCode(String funName) throws Exception
return r;
}

private String fixup_n_function_code(String[] argnames, String code){
private String extractPlayerJsGlobalVar(String jsCode){
Pattern pattern1 = Pattern.compile("""
(?x)
(?<q1>[\\"\\'])use\\s+strict(\\k<q1>);\\s*
(?<code>
var\\s+(?<name>[a-zA-Z0-9_$]+)\\s*=\\s*
(?<value>
(?<q2>[\\"\\'])(?:(?!(\\k<q2>)).|\\.)+(\\k<q2>)
\\.split\\((?<q3>[\\"\\'])(?:(?!(\\k<q3>)).)+(\\k<q3>)\\)
|\\[\\s*(?:(?<q4>[\\"\\'])(?:(?!(\\k<q4>)).|\\.)*(\\k<q4>)\\s*,?\\s*)+\\]
)
)[;,]
""");
Matcher matcher = pattern1.matcher(jsCode);
if (matcher.find()){
String value = matcher.group("value");
String code = matcher.group("code");
return code;
}
else {
return null;
}
}

private String fixup_n_function_code(String[] argnames, String code, String fullCode){
String globalVar = extractPlayerJsGlobalVar(fullCode);
if (globalVar != null){
code = globalVar + "; " + code;
}
String regex = ";\\s*if\\s*\\(\\s*typeof\\s+[a-zA-Z0-9_$]+\\s*===?\\s*(['\"])undefined\\1\\s*\\)\\s*return\\s+" + Pattern.quote(argnames[0]) + ";";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(code);
Expand All @@ -1465,7 +1568,7 @@ private String fixup_n_function_code(String[] argnames, String code){

private FunctionWithRepr extractFuName(String funName) throws Exception {
Map<String, String> code = extractFunctionCode(funName);
Object obj = extractFunctionFromCode(List.of(code.get("args").split(",")), fixup_n_function_code(code.get("args").split(","), code.get("code")), new HashMap<>());
Object obj = extractFunctionFromCode(List.of(code.get("args").split(",")), fixup_n_function_code(code.get("args").split(","), code.get("code"), this.code), new HashMap<>());
return new FunctionWithRepr(obj, "F<" + funName +">");
}
public Object callFunction(String funName, Object arg) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ static java.util.stream.Stream<String> fileNames() {
"b7240855-player_ias.vflset-en_US.txt",
"3bb1f723-player_ias.vflset-en_US.txt",
"2f1832d2-player_ias.vflset-en_US.txt",
"f3d47b5a-player_ias.vflset-en_US.txt"
"f3d47b5a-player_ias.vflset-en_US.txt",
"e7567ecf-player_ias_tce.vflset-en_US.txt",
"91201489-player_ias_tce.vflset-en_US.txt"
);
}
private String readFileContent(String fileName) throws IOException {
Expand Down Expand Up @@ -87,6 +89,8 @@ private String getNSigFunName(String fileName) {
case "3bb1f723-player_ias.vflset-en_US.txt" -> "fyn";
case "2f1832d2-player_ias.vflset-en_US.txt" -> "dCH";
case "f3d47b5a-player_ias.vflset-en_US.txt" -> "xyN";
case "e7567ecf-player_ias_tce.vflset-en_US.txt" -> "X_S";
case "91201489-player_ias_tce.vflset-en_US.txt" -> "K48";
default -> "";
};
}
Expand All @@ -111,6 +115,8 @@ private String getSigFunName(String fileName) {
case "3bb1f723-player_ias.vflset-en_US.txt" -> "pen";
case "2f1832d2-player_ias.vflset-en_US.txt" -> "B_H";
case "f3d47b5a-player_ias.vflset-en_US.txt" -> "ouU";
case "e7567ecf-player_ias_tce.vflset-en_US.txt" -> "$oW";
case "91201489-player_ias_tce.vflset-en_US.txt" -> "N4a";
default -> "";
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ static Stream<String> fileNames() {
"20dfca59-player_ias.vflset-en_US.txt",
"b12cc44b-player_ias.vflset-en_US.txt",
"3bb1f723-player_ias.vflset-en_US.txt",
"2f1832d2-player_ias.vflset-en_US.txt"
"2f1832d2-player_ias.vflset-en_US.txt",
"e7567ecf-player_ias_tce.vflset-en_US.txt",
"74e4bb46-player_ias_tce.vflset-en_US.txt",
"4fcd6e4a-player_ias.vflset-en_US.txt"
);
}
private String readFileContent(String fileName) throws IOException {
Expand Down Expand Up @@ -112,6 +115,9 @@ private List<String> getNSigParams(String fileName) {
case "b12cc44b-player_ias.vflset-en_US.txt" -> List.of("FnDBX5UEM1NHNyr", "Ema", "70QzMb0nhneLLS6BN");
case "3bb1f723-player_ias.vflset-en_US.txt" -> List.of("-zeqduSAj2ON5", "fyn", "70QzMb0nhneLLS6BN");
case "2f1832d2-player_ias.vflset-en_US.txt" -> List.of("e7hn0bMSQ", "B_H", "70QzMb0nhneLLS6BN");
case "e7567ecf-player_ias_tce.vflset-en_US.txt" -> List.of("eGJT0Dt7IGpKgk", "X_S", "70QzMb0nhneLLS6BN");
case "74e4bb46-player_ias_tce.vflset-en_US.txt" -> List.of("YRvnhiNcKsUj", "Bzs", "70QzMb0nhneLLS6BN");
case "4fcd6e4a-player_ias.vflset-en_US.txt" -> List.of("DtBH24Jm4Vu4ga", "gGu", "70QzMb0nhneLLS6BN");
default -> List.of("", "", "");
};
}
Expand Down Expand Up @@ -148,6 +154,9 @@ private List<String> getSigParams(String fileName) {
case "b12cc44b-player_ias.vflset-en_US.txt" -> List.of("EJ8wRAIgINWRWqXhcJg0Am3IRnTm5qrUo93yib6IL45hGtp70P4CQEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDoOU", "PBa", "AOq0QJ8wRAIgINWRWqXhcJg0Em3IRnTm5qrUo93yib6IL45hGtp70P4CIEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDogUxTO");
case "3bb1f723-player_ias.vflset-en_US.txt" -> List.of("iwR8IgIN5RWqXhcJg0Em3IRATm5qrUo93yAb6IL40hGtp70P4CIEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDogUxTO", "pen", "AOq0QJ8wRAIgINWRWqXhcJg0Em3IRnTm5qrUo93yib6IL45hGtp70P4CIEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDogUxTO");
case "2f1832d2-player_ias.vflset-en_US.txt" -> List.of("KpC5ut2EHrCarJWeXtsVBjE-IM3YQwOpfz8kcrr8emyEL62UySqmFPy6MPf8wVOXd41yKQhDHvEx_8f-Gu75ltKNohkk-", "dCH", "AOq0QJ8wRAIgINWRWqXhcJg0Em3IRnTm5qrUo93yib6IL45hGtp70P4CIEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDogUxTO");
case "e7567ecf-player_ias_tce.vflset-en_US.txt" -> List.of("UgoDDREOR36Kj5wrO_NjqCHEmy6PGNIGWF9GKnFEIC4P0eptGh54LI6biy39oUrq5mTnRI3mE0gJchXqWRWNIgIARw8JQ0qO", "$oW", "AOq0QJ8wRAIgINWRWqXhcJg0Em3IRnTm5qrUo93yib6IL45hGtp70P4CIEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDogUxTO");
case "74e4bb46-player_ias_tce.vflset-en_US.txt" -> List.of("TxUgoDDREeR36Kj5wrO_NjqCHEmy6PGNIGWF9GKnFEIC4P07ptGh54LI6biy39oUrqAmTnRIOmE0gJchXqWRWNIgIARw8JQ0q3", "bol", "AOq0QJ8wRAIgINWRWqXhcJg0Em3IRnTm5qrUo93yib6IL45hGtp70P4CIEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDogUxTO");
case "4fcd6e4a-player_ias.vflset-en_US.txt" -> List.of("eTxUgoDDREOR36Kj5wrO_NjqCHEmy6PGNIGWq9GKnFEIC4P07ptGh54LI6biy39oUrq5mTnRI3mE0gJchXqWRWNIgIARw8JQ0", "YqR", "AOq0QJ8wRAIgINWRWqXhcJg0Em3IRnTm5qrUo93yib6IL45hGtp70P4CIEFnKG9FWGINGP6ymEHCqjN_Orw5jK63ReERDDogUxTO");
default -> List.of("", "", "");
};

Expand Down
Loading