Skip to content
This repository was archived by the owner on Sep 28, 2020. It is now read-only.

Commit abdfebe

Browse files
committed
Decipher Performance Enhancement
Removed unnecessary code duplication in Decipherer.cs Added a loop breakout when all three functions have been determined. Rearranged the code so that operations is determined once and applied to each encrypted signature instead. This should greatly improve the performance of the decipher as it won't have to run though all the loops each time to decrypt a video/url. Renamed some variables and comments to clear up some of the trial fix I made.
1 parent 8b59d54 commit abdfebe

File tree

2 files changed

+50
-69
lines changed

2 files changed

+50
-69
lines changed

YoutubeExtractor/YoutubeExtractor/Decipherer.cs

Lines changed: 41 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,84 +7,54 @@ namespace YoutubeExtractor
77
{
88
internal static class Decipherer
99
{
10-
public static string DecipherWithVersion(string cipher, string cipherVersion)
10+
public static string DecipherOperations(string cipherVersion)
1111
{
12-
// NB: We intentionally don't cache the operations as it seems that the same operation
13-
// doesn't work if the URL below isn't called
14-
1512
string jsUrl = string.Format("http://s.ytimg.com/yts/jsbin/html5player-{0}.js", cipherVersion);
1613
string js = HttpHelper.DownloadString(jsUrl);
1714

1815
//Find "C" in this: var A = B.sig||C (B.s)
1916
string functNamePattern = @"\.sig\s*\|\|(\w+)\(";
2017
var funcName = Regex.Match(js, functNamePattern).Groups[1].Value;
21-
22-
//Match nested angle braces
23-
string funcBodyPattern = @"(?<brace>{([^{}]| ?(brace))*})";
24-
//Match the function function_name (that has one argument)
18+
string funcBodyPattern = @"(?<brace>{([^{}]| ?(brace))*})"; //Match nested angle braces
2519
string funcPattern = string.Format(@"{0}\(\w+\){1}", funcName, funcBodyPattern);
26-
var funcBody = Regex.Match(js, funcPattern).Groups["brace"].Value;
27-
var lines = funcBody.Split(';');
28-
29-
string operations = "";
20+
var funcBody = Regex.Match(js, funcPattern).Groups["brace"].Value; //Entire sig function
21+
var lines = funcBody.Split(';'); //Each line in sig function
3022

31-
32-
//CYNAO - 07/08/2014, Test Fix
33-
/* The previous algoritms used a.splice(), where the decipher used the method (.splice()), however it seems the new algoritm
34-
* renames the method to random but unique characters (example ab.dc() = splice). This code determines what each method code is,
35-
* as it is defined once using the original name.
36-
*/
37-
string id_Reverse = "" , id_Slice = "" , id_CharSwap = ""; //Holds the new method name for each.
23+
string id_Reverse = "", id_Slice = "", id_CharSwap = ""; //Hold name for each method
3824
string functionIdentifier = "";
25+
string operations = "";
3926

40-
string functIDPattern = @"\w+:\bfunction\b"; //Define as "NB:function(a,b)" where nb can be the three ciphers
41-
var funcID = Regex.Match(js, functIDPattern).Groups[1].Value;
42-
43-
///CODE ADDITION: Get the three ciphers by finding the definition
27+
//Match the code with each function. Only runs till all three are defined.
4428
foreach (var line in lines.Skip(1).Take(lines.Length - 2))
4529
{
46-
string newVarName; //Previous algoritms used to be just "a." - now stores temp var name as its uneccessary
47-
int locOfDot, locOfBracket, functionIDLength;
48-
locOfDot = line.IndexOf("."); // NB.AC( - gets location of the dot.
49-
locOfBracket = line.IndexOf("("); //NB.AC( - gets location of the bracet
50-
functionIDLength = locOfBracket - (locOfDot + 1);
51-
newVarName = line.Substring(0, locOfDot);
52-
functionIdentifier = line.Substring(locOfDot + 1, functionIDLength); //leaving us with the function AC
53-
54-
//This is what the definitions currently look like, could be changed so the regex needs improving. Messy fix.
55-
string tempReverse = string.Format(@"{0}:\bfunction\b\(\w+\)", functionIdentifier); //Reverse only one that doesnt have two parameters
56-
string tempSlice = string.Format(@"{0}:\bfunction\b\([a],b\).(\breturn\b)?.?\w+\.", functionIdentifier); //Regex for slice (return or not)
57-
string tempCharSwap = string.Format(@"{0}:\bfunction\b\(\w+\,\w\).\bvar\b.\bc=a\b", functionIdentifier); //Regex for the char swap.
58-
30+
if (!string.IsNullOrEmpty(id_Reverse) && !string.IsNullOrEmpty(id_Slice) &&
31+
!string.IsNullOrEmpty(id_CharSwap)) { break; } //Break out if all defined.
32+
33+
functionIdentifier = getFunctionFromLine(line);
34+
string re_Reverse = string.Format(@"{0}:\bfunction\b\(\w+\)", functionIdentifier); //Regex for reverse (one parameter)
35+
string re_Slice = string.Format(@"{0}:\bfunction\b\([a],b\).(\breturn\b)?.?\w+\.", functionIdentifier); //Regex for slice (return or not)
36+
string re_Swap = string.Format(@"{0}:\bfunction\b\(\w+\,\w\).\bvar\b.\bc=a\b", functionIdentifier); //Regex for the char swap.
37+
5938
Match me;
60-
if ((me = Regex.Match(js, tempReverse)).Success)
39+
if ((me = Regex.Match(js, re_Reverse)).Success)
6140
{ id_Reverse = functionIdentifier; } //If def matched the regex for reverse then the current function is a defined as the reverse cipher
6241

63-
if ((me = Regex.Match(js, tempSlice)).Success)
42+
if ((me = Regex.Match(js, re_Slice)).Success)
6443
{ id_Slice = functionIdentifier; } //If def matched the regex for slice then the current function is defined as the slice cipher.
6544

66-
if ((me = Regex.Match(js, tempCharSwap)).Success)
45+
if ((me = Regex.Match(js, re_Swap)).Success)
6746
{ id_CharSwap = functionIdentifier; } //If def matched the regex for charSwap then the current function is defined as swap cipher.
6847

6948
}
7049

71-
7250
foreach (var line in lines.Skip(1).Take(lines.Length - 2))
7351
{
7452
Match m;
75-
///DUPLICATE CODE! Improve.
76-
int locOfDot; int locOfBracket; int functionIDLength;
77-
locOfDot = line.IndexOf(".");
78-
locOfBracket = line.IndexOf("(");
79-
functionIDLength = locOfBracket - (locOfDot + 1);
80-
functionIdentifier = line.Substring(locOfDot + 1, functionIDLength); //Just needed this (define it as a member?)
81-
82-
string newSliceIDRegex = string.Format(@"(?<index>\d+)\)+", functionIdentifier);
53+
functionIdentifier = getFunctionFromLine(line);
8354

8455
if ((m = Regex.Match(line, @"\(\w+,(?<index>\d+)\)")).Success && functionIdentifier == id_CharSwap)
8556
{ operations += "w" + m.Groups["index"].Value + " "; } //Character swap regex appears to be the same as before
8657

87-
8858
if ((m = Regex.Match(line, @"\(\w+,(?<index>\d+)\)")).Success && functionIdentifier == id_Slice)
8959
{ operations += "s" + m.Groups["index"].Value + " "; } //Slice appears to have changed the index location???
9060
//Could be wrong and the regex needs improving, seems to work on the latest algorithm though.
@@ -93,9 +63,29 @@ public static string DecipherWithVersion(string cipher, string cipherVersion)
9363
{ operations += "r "; } //Reverse operation, no regex required
9464

9565
}
66+
9667
operations = operations.Trim();
68+
Console.WriteLine(operations);
69+
return operations;
70+
71+
}
72+
73+
public static string DecipherWithOperations(string cipher, string operations)
74+
{
75+
if (string.IsNullOrEmpty(operations))
76+
{ throw new NotImplementedException("No valid cipher operations found."); }
77+
78+
return operations.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
79+
.Aggregate(cipher, ApplyOperation);
80+
}
9781

98-
return DecipherWithOperations(cipher, operations);
82+
83+
private static string getFunctionFromLine(string currentLine)
84+
{
85+
Regex matchFunctionReg = new Regex(@"\w+\.(?<functionID>\w+)\("); //lc.ac(b,c) want ac.
86+
Match rgMatch = matchFunctionReg.Match(currentLine);
87+
string matchedFunction = rgMatch.Groups["functionID"].Value;
88+
return matchedFunction; //return 'ac'
9989
}
10090

10191
private static string ApplyOperation(string cipher, string op)
@@ -104,7 +94,7 @@ private static string ApplyOperation(string cipher, string op)
10494
{
10595
case 'r':
10696
return new string(cipher.ToCharArray().Reverse().ToArray());
107-
97+
10898
case 'w':
10999
{
110100
int index = GetOpIndex(op);
@@ -122,11 +112,6 @@ private static string ApplyOperation(string cipher, string op)
122112
}
123113
}
124114

125-
private static string DecipherWithOperations(string cipher, string operations)
126-
{
127-
return operations.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
128-
.Aggregate(cipher, ApplyOperation);
129-
}
130115

131116
private static int GetOpIndex(string op)
132117
{

YoutubeExtractor/YoutubeExtractor/DownloadUrlResolver.cs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static class DownloadUrlResolver
2424
/// <exception cref="YoutubeParseException">
2525
/// There was an error while deciphering the signature.
2626
/// </exception>
27-
public static void DecryptDownloadUrl(VideoInfo videoInfo)
27+
public static void DecryptDownloadUrl(VideoInfo videoInfo, string operations)
2828
{
2929
IDictionary<string, string> queries = HttpHelper.ParseQueryString(videoInfo.DownloadUrl);
3030

@@ -36,7 +36,10 @@ public static void DecryptDownloadUrl(VideoInfo videoInfo)
3636

3737
try
3838
{
39-
decrypted = GetDecipheredSignature(videoInfo.HtmlPlayerVersion, encryptedSignature);
39+
if (encryptedSignature.Length == CorrectSignatureLength)
40+
{ decrypted = encryptedSignature; } //Doesn't require decrypting
41+
else
42+
{ decrypted = Decipherer.DecipherWithOperations(encryptedSignature, operations); } //Use operations get decrypted cipher
4043
}
4144

4245
catch (Exception ex)
@@ -94,14 +97,17 @@ public static IEnumerable<VideoInfo> GetDownloadUrls(string videoUrl, bool decry
9497
IEnumerable<VideoInfo> infos = GetVideoInfos(downloadUrls, videoTitle).ToList();
9598

9699
string htmlPlayerVersion = GetHtml5PlayerVersion(json);
100+
string operations = ""; //Same operations for the same html5Player
97101

98102
foreach (VideoInfo info in infos)
99103
{
100104
info.HtmlPlayerVersion = htmlPlayerVersion;
101105

102106
if (decryptSignature && info.RequiresDecryption)
103107
{
104-
DecryptDownloadUrl(info);
108+
if (string.IsNullOrEmpty(operations)) //Get operations if we don't
109+
{ operations = Decipherer.DecipherOperations(htmlPlayerVersion); }
110+
DecryptDownloadUrl(info, operations);
105111
}
106112
}
107113

@@ -213,16 +219,6 @@ private static string GetAdaptiveStreamMap(JObject json)
213219
return streamMap.ToString();
214220
}
215221

216-
private static string GetDecipheredSignature(string htmlPlayerVersion, string signature)
217-
{
218-
if (signature.Length == CorrectSignatureLength)
219-
{
220-
return signature;
221-
}
222-
223-
return Decipherer.DecipherWithVersion(signature, htmlPlayerVersion);
224-
}
225-
226222
private static string GetHtml5PlayerVersion(JObject json)
227223
{
228224
var regex = new Regex(@"html5player-(.+?)\.js");

0 commit comments

Comments
 (0)