[CHANGE] Updated cipher implementation and added decipher function
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Frozen;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Manager.YouTube.Util.Cipher.Operations;
|
using Manager.YouTube.Util.Cipher.Operations;
|
||||||
@@ -8,11 +9,11 @@ public partial class CipherDecoder
|
|||||||
{
|
{
|
||||||
public required string Version { get; init; }
|
public required string Version { get; init; }
|
||||||
public required string OriginalRelativeUrl { get; init; }
|
public required string OriginalRelativeUrl { get; init; }
|
||||||
public readonly IReadOnlyCollection<ICipherOperation> Operations;
|
public readonly IReadOnlySet<ICipherOperation> Operations;
|
||||||
|
|
||||||
private CipherDecoder(IEnumerable<ICipherOperation> operations)
|
private CipherDecoder(IEnumerable<ICipherOperation> operations)
|
||||||
{
|
{
|
||||||
Operations = operations.ToList();
|
Operations = operations.ToFrozenSet();
|
||||||
if (Operations.Count == 0)
|
if (Operations.Count == 0)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(operations), "No decipher operations given.");
|
throw new ArgumentNullException(nameof(operations), "No decipher operations given.");
|
||||||
@@ -30,6 +31,37 @@ public partial class CipherDecoder
|
|||||||
return decoder;
|
return decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Decipher(string signatureCipher)
|
||||||
|
{
|
||||||
|
var urlBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
var indexStart = signatureCipher.IndexOf("s=", StringComparison.Ordinal);
|
||||||
|
var indexEnd = signatureCipher.IndexOf("&", StringComparison.Ordinal);
|
||||||
|
var signature = signatureCipher.Substring(indexStart, indexEnd);
|
||||||
|
|
||||||
|
indexStart = signatureCipher.IndexOf("&sp", StringComparison.Ordinal);
|
||||||
|
indexEnd = signatureCipher.IndexOf("&url", StringComparison.Ordinal);
|
||||||
|
var spParam = signatureCipher.Substring(indexStart, indexEnd - indexStart);
|
||||||
|
|
||||||
|
indexStart = signatureCipher.IndexOf("&url", StringComparison.Ordinal);
|
||||||
|
var videoUrl = signatureCipher[indexStart..];
|
||||||
|
|
||||||
|
|
||||||
|
signature = signature[(signature.IndexOf('=') + 1)..];
|
||||||
|
spParam = spParam[(spParam.IndexOf('=') + 1)..];
|
||||||
|
videoUrl = videoUrl[(videoUrl.IndexOf('=') + 1)..];
|
||||||
|
if (string.IsNullOrWhiteSpace(signature))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid signature.");
|
||||||
|
}
|
||||||
|
var signatureDeciphered = Operations.Aggregate(signature, (acc, op) => op.Decipher(acc));
|
||||||
|
|
||||||
|
urlBuilder.Append(videoUrl);
|
||||||
|
urlBuilder.Append($"&{spParam}=");
|
||||||
|
urlBuilder.Append(signatureDeciphered);
|
||||||
|
return urlBuilder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task<IEnumerable<ICipherOperation>> GetCipherOperations(string relativeUrl, YouTubeClient? client = null)
|
private static async Task<IEnumerable<ICipherOperation>> GetCipherOperations(string relativeUrl, YouTubeClient? client = null)
|
||||||
{
|
{
|
||||||
var downloadRequest = new HttpRequestMessage(HttpMethod.Get, new Uri($"{NetworkService.Origin}/{relativeUrl}"));
|
var downloadRequest = new HttpRequestMessage(HttpMethod.Get, new Uri($"{NetworkService.Origin}/{relativeUrl}"));
|
||||||
@@ -49,7 +81,7 @@ public partial class CipherDecoder
|
|||||||
var definitionBody = DefinitionBodyRegex().Match(functionBody).Groups[1].Value;
|
var definitionBody = DefinitionBodyRegex().Match(functionBody).Groups[1].Value;
|
||||||
var decipherDefinition = Regex.Match(playerJs, $@"var\s+{definitionBody}=\{{(\w+:function\(\w+(,\w+)?\)\{{(.*?)\}}),?\}};", RegexOptions.Singleline).Groups[0].ToString();
|
var decipherDefinition = Regex.Match(playerJs, $@"var\s+{definitionBody}=\{{(\w+:function\(\w+(,\w+)?\)\{{(.*?)\}}),?\}};", RegexOptions.Singleline).Groups[0].ToString();
|
||||||
|
|
||||||
List<ICipherOperation> operations = [];
|
SortedSet<ICipherOperation> operations = [];
|
||||||
foreach (var statement in functionBody.Split(';'))
|
foreach (var statement in functionBody.Split(';'))
|
||||||
{
|
{
|
||||||
// Get the name of the function called in this statement
|
// Get the name of the function called in this statement
|
||||||
@@ -73,7 +105,7 @@ public partial class CipherDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return operations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public static class CipherManager
|
|||||||
private static readonly CipherDecoderCollection LoadedCiphers = [];
|
private static readonly CipherDecoderCollection LoadedCiphers = [];
|
||||||
private static readonly ILogger Logger = LogService.RegisterLogger(typeof(CipherManager));
|
private static readonly ILogger Logger = LogService.RegisterLogger(typeof(CipherManager));
|
||||||
|
|
||||||
public static async Task<Result<CipherDecoder>> GetDecoder(YouTubeClient client)
|
public static async Task<Result<CipherDecoder>> GetDecoderAsync(YouTubeClient client)
|
||||||
{
|
{
|
||||||
var relativePlayerJsUrl = client.State?.PlayerJsUrl;
|
var relativePlayerJsUrl = client.State?.PlayerJsUrl;
|
||||||
if (string.IsNullOrEmpty(relativePlayerJsUrl))
|
if (string.IsNullOrEmpty(relativePlayerJsUrl))
|
||||||
|
|||||||
Reference in New Issue
Block a user