[CHANGE] Updated cipher implementation and added decipher function

This commit is contained in:
max
2025-10-23 19:45:36 +02:00
parent 41f880cfef
commit e87e1c57f9
2 changed files with 37 additions and 5 deletions

View File

@@ -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;
} }

View File

@@ -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))