Compare commits
2 Commits
a84195aefa
...
4c04378080
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c04378080 | ||
|
|
b5c701b971 |
13
Manager.App/Components/Application/Dev/CipherDev.razor
Normal file
13
Manager.App/Components/Application/Dev/CipherDev.razor
Normal file
@@ -0,0 +1,13 @@
|
||||
@using Manager.App.Models.System
|
||||
@using Manager.App.Services.System
|
||||
|
||||
@inject ISnackbar Snackbar
|
||||
@inject ClientService ClientService
|
||||
|
||||
<MudText>Cipher manager</MudText>
|
||||
<MudStack Row Spacing="2">
|
||||
<MudAutocomplete T="YouTubeClientItem" Label="Client" @bind-Value="@_selectedClient" SearchFunc="SearchClientsAsync" ToStringFunc="@(i => i == null ? "null?" : $"{i.Name} ({i.Handle})")"
|
||||
Variant="Variant.Outlined" ShowProgressIndicator ProgressIndicatorColor="Color.Primary">
|
||||
</MudAutocomplete>
|
||||
<MudButton OnClick="ExecCipher">Exec</MudButton>
|
||||
</MudStack>
|
||||
43
Manager.App/Components/Application/Dev/CipherDev.razor.cs
Normal file
43
Manager.App/Components/Application/Dev/CipherDev.razor.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Manager.App.Models.System;
|
||||
using Manager.YouTube.Util.Cipher;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Manager.App.Components.Application.Dev;
|
||||
|
||||
public partial class CipherDev : ComponentBase
|
||||
{
|
||||
private YouTubeClientItem? _selectedClient;
|
||||
|
||||
private async Task ExecCipher(MouseEventArgs obj)
|
||||
{
|
||||
if (_selectedClient == null)
|
||||
{
|
||||
Snackbar.Add("No client selected", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var ytClientResult = await ClientService.LoadClientByIdAsync(_selectedClient.Id);
|
||||
if (!ytClientResult.IsSuccess)
|
||||
{
|
||||
Snackbar.Add(ytClientResult.Error?.Description ?? "Failed to get the client!", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var ytClient = ytClientResult.Value;
|
||||
if (ytClient.State == null)
|
||||
{
|
||||
Snackbar.Add("Client state is null!", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var decoder = await CipherManager.GetDecoderAsync(ytClient.State, ytClient);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<YouTubeClientItem>> SearchClientsAsync(string? search, CancellationToken cancellationToken)
|
||||
{
|
||||
var searchResults = await ClientService.GetClientsAsync(search, cancellationToken: cancellationToken);
|
||||
return !searchResults.IsSuccess ? [] : searchResults.Value;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
@inject ClientService ClientService
|
||||
|
||||
<MudText>Video data</MudText>
|
||||
<MudStack Spacing="2">
|
||||
<MudStack Row Spacing="2">
|
||||
<MudAutocomplete T="YouTubeClientItem" Label="Client" @bind-Value="@_selectedClient" SearchFunc="SearchClientsAsync" ToStringFunc="@(i => i == null ? "null?" : $"{i.Name} ({i.Handle})")"
|
||||
Variant="Variant.Outlined" ShowProgressIndicator ProgressIndicatorColor="Color.Primary">
|
||||
</MudAutocomplete>
|
||||
|
||||
@@ -9,4 +9,7 @@
|
||||
<MudTabPanel Text="Video">
|
||||
<DevelopmentVideo />
|
||||
</MudTabPanel>
|
||||
<MudTabPanel Text="Cipher">
|
||||
<CipherDev />
|
||||
</MudTabPanel>
|
||||
</MudTabs>
|
||||
@@ -144,7 +144,7 @@ public class LibraryService : ILibraryService
|
||||
try
|
||||
{
|
||||
await using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
var channel = await context.Channels
|
||||
var channel = await context.Channels.AsSplitQuery()
|
||||
.Include(c => c.ClientAccount)
|
||||
.ThenInclude(p => p!.HttpCookies)
|
||||
.Include(f => f.Files)
|
||||
|
||||
58
Manager.YouTube/Interpreter/JavaScriptEngineManager.cs
Normal file
58
Manager.YouTube/Interpreter/JavaScriptEngineManager.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Text;
|
||||
using DotBased.Monads;
|
||||
|
||||
namespace Manager.YouTube.Interpreter;
|
||||
|
||||
public class JavaScriptEngineManager
|
||||
{
|
||||
private readonly PlayerEngineCollection _engines = [];
|
||||
|
||||
public async Task<Result<PlayerEngine>> GetPlayerEngine(string playerUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(playerUrl))
|
||||
{
|
||||
return ResultError.Fail("player url is empty or null!");
|
||||
}
|
||||
|
||||
var version = GetScriptVersion(playerUrl);
|
||||
|
||||
if (_engines.TryGetValue(version, out var engine))
|
||||
{
|
||||
return engine;
|
||||
}
|
||||
|
||||
var playerJsSourceResult = await DownloadPlayerScriptAsync(playerUrl);
|
||||
if (!playerJsSourceResult.IsSuccess)
|
||||
{
|
||||
return playerJsSourceResult.Error ?? ResultError.Fail("Download player script failed!");
|
||||
}
|
||||
|
||||
return new PlayerEngine(version, playerJsSourceResult.Value);
|
||||
}
|
||||
|
||||
private static string GetScriptVersion(string relativePlayerUrl)
|
||||
{
|
||||
var split = relativePlayerUrl.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var v = split[2];
|
||||
var lang = split[4];
|
||||
return $"{v}-{lang}";
|
||||
}
|
||||
|
||||
private static async Task<Result<string>> DownloadPlayerScriptAsync(string relativeUrl, YouTubeClient? client = null)
|
||||
{
|
||||
var downloadRequest = new HttpRequestMessage(HttpMethod.Get, new Uri($"{NetworkService.Origin}/{relativeUrl}"));
|
||||
var downloadResponse = await NetworkService.DownloadBytesAsync(downloadRequest, client);
|
||||
if (!downloadResponse.IsSuccess)
|
||||
{
|
||||
return downloadResponse.Error ?? ResultError.Fail($"Failed to download script from url: {relativeUrl}");
|
||||
}
|
||||
|
||||
var playerJs = Encoding.UTF8.GetString(downloadResponse.Value.Data);
|
||||
if (string.IsNullOrWhiteSpace(playerJs))
|
||||
{
|
||||
return ResultError.Fail("Script value is empty!");
|
||||
}
|
||||
|
||||
return playerJs;
|
||||
}
|
||||
}
|
||||
42
Manager.YouTube/Interpreter/PlayerEngine.cs
Normal file
42
Manager.YouTube/Interpreter/PlayerEngine.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using DotBased.Logging;
|
||||
using Jint;
|
||||
|
||||
namespace Manager.YouTube.Interpreter;
|
||||
|
||||
public class PlayerEngine
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public Engine JsEngine { get; set; }
|
||||
private ILogger Logger { get; set; }
|
||||
|
||||
public PlayerEngine(string version, string script)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(script))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(script));
|
||||
}
|
||||
|
||||
Logger = LogService.RegisterLogger(typeof(PlayerEngine), version);
|
||||
Version = version;
|
||||
JsEngine = new Engine().Execute(script).SetValue("log", new Action<object>(obj =>
|
||||
{
|
||||
var logStr = obj.ToString();
|
||||
if (string.IsNullOrEmpty(logStr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Logger.Information(logStr);
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
public void InitializePlayer()
|
||||
{
|
||||
JsEngine.Execute("createPlayer");
|
||||
}
|
||||
}
|
||||
11
Manager.YouTube/Interpreter/PlayerEngineCollection.cs
Normal file
11
Manager.YouTube/Interpreter/PlayerEngineCollection.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Manager.YouTube.Interpreter;
|
||||
|
||||
public class PlayerEngineCollection : KeyedCollection<string, PlayerEngine>
|
||||
{
|
||||
protected override string GetKeyForItem(PlayerEngine item)
|
||||
{
|
||||
return item.Version;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotBased" Version="1.0.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.2" />
|
||||
<PackageReference Include="Jint" Version="4.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Manager.YouTube.Util.Converters;
|
||||
|
||||
namespace Manager.YouTube.Models.Innertube;
|
||||
|
||||
@@ -8,6 +9,7 @@ public class StreamingData
|
||||
[JsonPropertyName("expiresInSeconds")]
|
||||
public int ExpiresInSeconds { get; set; }
|
||||
[JsonPropertyName("serverAbrStreamingUrl")]
|
||||
[JsonConverter(typeof(JsonUrlEscapeConverter))]
|
||||
public string ServerAbrStreamingUrl { get; set; } = "";
|
||||
[JsonPropertyName("formats")]
|
||||
public List<StreamingFormat> Formats { get; set; } = [];
|
||||
|
||||
@@ -112,7 +112,7 @@ public partial class CipherDecoder
|
||||
}
|
||||
|
||||
|
||||
[GeneratedRegex(@"(\w+)=function\(\w+\){(\w+)=\2\.split\(\x22{2}\);.*?return\s+\2\.join\(\x22{2}\)}")]
|
||||
[GeneratedRegex(@"([A-Za-z_$][A-Za-z0-9_$]*)=function\([A-Za-z_$][A-Za-z0-9_$]*\)\{\s*([A-Za-z_$][A-Za-z0-9_$]*)=\2\.split\(\x22\x22\);[\s\S]*?return\s+\2\.join\(\x22\x22\)\s*\}")]
|
||||
private static partial Regex FunctionBodyRegex();
|
||||
[GeneratedRegex("([\\$_\\w]+).\\w+\\(\\w+,\\d+\\);")]
|
||||
private static partial Regex DefinitionBodyRegex();
|
||||
|
||||
27
Manager.YouTube/Util/Converters/JsonUrlEscapeConverter.cs
Normal file
27
Manager.YouTube/Util/Converters/JsonUrlEscapeConverter.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Manager.YouTube.Util.Converters;
|
||||
|
||||
public partial class JsonUrlEscapeConverter : JsonConverter<string>
|
||||
{
|
||||
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var url = reader.GetString();
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
return UrlPatternRegex().IsMatch(url) ? Uri.UnescapeDataString(url) : url;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value);
|
||||
}
|
||||
|
||||
[GeneratedRegex("^(https?|ftp)://", RegexOptions.IgnoreCase | RegexOptions.Compiled, "nl-NL")]
|
||||
private static partial Regex UrlPatternRegex();
|
||||
}
|
||||
Reference in New Issue
Block a user