[CHANGE] Split up json parsing, added getting account info
This commit is contained in:
@@ -26,19 +26,27 @@
|
|||||||
<td>Account handle:</td>
|
<td>Account handle:</td>
|
||||||
<td>@Client.External.Information.AccountHandle</td>
|
<td>@Client.External.Information.AccountHandle</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>User agent:</td>
|
|
||||||
<td>@Client.UserAgent</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Logged in:</td>
|
<td>Logged in:</td>
|
||||||
<td style="@($"color: {(Client.External.State?.LoggedIn ?? false ? "green" : "red")}")">@Client.External.State?.LoggedIn</td>
|
<td style="@($"color: {(Client.External.State?.LoggedIn ?? false ? "green" : "red")}")">@Client.External.State?.LoggedIn</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>YouTube Premium:</td>
|
||||||
|
<td style="@($"color: {(Client.External.Information.IsPremiumUser ? "green" : "red")}")">@Client.External.Information.IsPremiumUser</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>User agent:</td>
|
||||||
|
<td>@Client.UserAgent</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>InnerTube API key:</td>
|
<td>InnerTube API key:</td>
|
||||||
<td>@Client.External.State?.InnertubeApiKey</td>
|
<td>@Client.External.State?.InnertubeApiKey</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>InnerTube client:</td>
|
||||||
|
<td>@Client.External.State?.InnerTubeClient</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>InnerTube client version:</td>
|
<td>InnerTube client version:</td>
|
||||||
<td>@Client.External.State?.InnerTubeClientVersion</td>
|
<td>@Client.External.State?.InnerTubeClientVersion</td>
|
||||||
|
@@ -67,22 +67,17 @@ namespace Manager.App.Components.Dialogs
|
|||||||
foreach (var cookieStr in cookies)
|
foreach (var cookieStr in cookies)
|
||||||
{
|
{
|
||||||
var parts = cookieStr.Split('=', 2);
|
var parts = cookieStr.Split('=', 2);
|
||||||
if (parts.Length == 2)
|
if (parts.Length != 2) continue;
|
||||||
|
|
||||||
|
var name = parts[0].Trim();
|
||||||
|
var value = parts[1].Trim();
|
||||||
|
|
||||||
|
var cookie = new Cookie(name, value)
|
||||||
{
|
{
|
||||||
var name = parts[0].Trim();
|
Path = "/",
|
||||||
var value = parts[1].Trim();
|
Domain = domain
|
||||||
|
};
|
||||||
var cookie = new Cookie(name, value)
|
collection.Add(cookie);
|
||||||
{
|
|
||||||
Expires = DateTime.Now.AddDays(1),
|
|
||||||
Path = "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(domain))
|
|
||||||
cookie.Domain = domain;
|
|
||||||
|
|
||||||
collection.Add(cookie);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return collection;
|
return collection;
|
||||||
|
@@ -5,4 +5,5 @@ public class ClientInformation
|
|||||||
public string? AccountName { get; set; }
|
public string? AccountName { get; set; }
|
||||||
public string? AccountHandle { get; set; }
|
public string? AccountHandle { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
public bool IsPremiumUser { get; set; }
|
||||||
}
|
}
|
@@ -4,7 +4,10 @@ public class ChannelFetch
|
|||||||
{
|
{
|
||||||
public bool NoIndex { get; set; }
|
public bool NoIndex { get; set; }
|
||||||
public bool Unlisted { get; set; }
|
public bool Unlisted { get; set; }
|
||||||
public bool FamilyFriendly { get; set; }
|
public bool FamilySafe { get; set; }
|
||||||
|
public string? Handle { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
public List<string> AvailableCountries { get; set; } = [];
|
public List<string> AvailableCountries { get; set; } = [];
|
||||||
|
public List<WebImage> AvatarImages { get; set; } = [];
|
||||||
|
public List<WebImage> BannerImages { get; set; } = [];
|
||||||
}
|
}
|
@@ -15,6 +15,9 @@ public class ClientState : AdditionalJsonData
|
|||||||
|
|
||||||
[JsonPropertyName("SIGNIN_URL")]
|
[JsonPropertyName("SIGNIN_URL")]
|
||||||
public string? SigninUrl { get; set; }
|
public string? SigninUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("INNERTUBE_CLIENT_NAME")]
|
||||||
|
public string? InnerTubeClient { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("INNERTUBE_CLIENT_VERSION")]
|
[JsonPropertyName("INNERTUBE_CLIENT_VERSION")]
|
||||||
public string? InnerTubeClientVersion { get; set; }
|
public string? InnerTubeClientVersion { get; set; }
|
||||||
|
8
Manager.YouTube/Models/Innertube/WebImage.cs
Normal file
8
Manager.YouTube/Models/Innertube/WebImage.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Manager.YouTube.Models.Innertube;
|
||||||
|
|
||||||
|
public class WebImage
|
||||||
|
{
|
||||||
|
public int Width { get; set; }
|
||||||
|
public int Height { get; set; }
|
||||||
|
public string Url { get; set; } = "";
|
||||||
|
}
|
@@ -5,6 +5,7 @@ using System.Text.Json.Nodes;
|
|||||||
using DotBased.Monads;
|
using DotBased.Monads;
|
||||||
using Manager.YouTube.Models.Innertube;
|
using Manager.YouTube.Models.Innertube;
|
||||||
using Manager.YouTube.Parsers;
|
using Manager.YouTube.Parsers;
|
||||||
|
using Manager.YouTube.Parsers.Json;
|
||||||
using Manager.YouTube.Util;
|
using Manager.YouTube.Util;
|
||||||
|
|
||||||
namespace Manager.YouTube;
|
namespace Manager.YouTube;
|
||||||
@@ -52,6 +53,8 @@ public static class NetworkService
|
|||||||
return ResultError.Error(e, "Error while parsing JSON!");
|
return ResultError.Error(e, "Error while parsing JSON!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.External.Information.IsPremiumUser = clientStateResult.Value.Item2;
|
||||||
|
|
||||||
return clientState == null ? ResultError.Fail("Unable to parse client state!") : clientState;
|
return clientState == null ? ResultError.Fail("Unable to parse client state!") : clientState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +128,8 @@ public static class NetworkService
|
|||||||
{
|
{
|
||||||
return ResultError.Fail("Unable to get http client!");
|
return ResultError.Fail("Unable to get http client!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string json;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await http.SendAsync(httpRequest);
|
var response = await http.SendAsync(httpRequest);
|
||||||
@@ -135,31 +139,14 @@ public static class NetworkService
|
|||||||
return ResultError.Fail(responseResult);
|
return ResultError.Fail(responseResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
json = await response.Content.ReadAsStringAsync();
|
||||||
var jsonDocument = JsonDocument.Parse(json);
|
|
||||||
|
|
||||||
var matRuns = jsonDocument.RootElement
|
|
||||||
.GetProperty("actions")[0]
|
|
||||||
.GetProperty("openPopupAction")
|
|
||||||
.GetProperty("popup")
|
|
||||||
.GetProperty("multiPageMenuRenderer")
|
|
||||||
.GetProperty("header")
|
|
||||||
.GetProperty("activeAccountHeaderRenderer")
|
|
||||||
.GetProperty("manageAccountTitle")
|
|
||||||
.GetProperty("runs");
|
|
||||||
|
|
||||||
var firstElement = matRuns.EnumerateArray().FirstOrDefault();
|
|
||||||
var id = firstElement.GetProperty("navigationEndpoint").GetProperty("browseEndpoint").GetProperty("browseId").GetString();
|
|
||||||
if (string.IsNullOrWhiteSpace(id))
|
|
||||||
{
|
|
||||||
return ResultError.Fail("Unable to get account id!");
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return ResultError.Error(e);
|
return ResultError.Error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return JsonAccountParser.ParseAccountId(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Result<ChannelFetch>> GetChannelAsync(string channelId, YouTubeClient client)
|
public static async Task<Result<ChannelFetch>> GetChannelAsync(string channelId, YouTubeClient client)
|
||||||
@@ -204,8 +191,10 @@ public static class NetworkService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var jsonContent = await response.Content.ReadAsStringAsync();
|
var jsonContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var parsed = ChannelJsonParser.ParseJsonToChannelData(jsonContent);
|
||||||
|
|
||||||
return ResultError.Fail("Not implemented!");
|
return ResultError.Fail("Not implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@@ -5,7 +5,7 @@ namespace Manager.YouTube.Parsers;
|
|||||||
|
|
||||||
public static class HtmlParser
|
public static class HtmlParser
|
||||||
{
|
{
|
||||||
public static Result<(string, string)> GetStateJson(string html)
|
public static Result<(string, bool)> GetStateJson(string html)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(html))
|
if (string.IsNullOrWhiteSpace(html))
|
||||||
{
|
{
|
||||||
@@ -21,14 +21,15 @@ public static class HtmlParser
|
|||||||
return ResultError.Fail($"Could not find {setFunction} in html script nodes!");
|
return ResultError.Fail($"Could not find {setFunction} in html script nodes!");
|
||||||
|
|
||||||
var json = ExtractJson(scriptNode.InnerText, "ytcfg.set(");
|
var json = ExtractJson(scriptNode.InnerText, "ytcfg.set(");
|
||||||
var jsonText = ExtractJson(scriptNode.InnerText, "setMessage(");
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(json) || string.IsNullOrWhiteSpace(jsonText))
|
if (string.IsNullOrWhiteSpace(json))
|
||||||
{
|
{
|
||||||
return ResultError.Fail($"Could not find {setFunction} in html script nodes!");
|
return ResultError.Fail($"Could not find {setFunction} in html script nodes!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (json, jsonText);
|
var isPremiumUser = html.Contains("logo-type=\"YOUTUBE_PREMIUM_LOGO\"", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
return (json, isPremiumUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
static string? ExtractJson(string input, string marker)
|
static string? ExtractJson(string input, string marker)
|
||||||
|
87
Manager.YouTube/Parsers/Json/ChannelJsonParser.cs
Normal file
87
Manager.YouTube/Parsers/Json/ChannelJsonParser.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using DotBased.Monads;
|
||||||
|
using Manager.YouTube.Models.Innertube;
|
||||||
|
|
||||||
|
namespace Manager.YouTube.Parsers.Json;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parsing functionality for the response from the innertube browse endpoint.
|
||||||
|
/// </summary>
|
||||||
|
public static class ChannelJsonParser
|
||||||
|
{
|
||||||
|
public static Result<ChannelFetch> ParseJsonToChannelData(string json)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = JsonDocument.Parse(json);
|
||||||
|
var rootDoc = doc.RootElement;
|
||||||
|
|
||||||
|
var microformat = rootDoc.GetProperty("microformat").GetProperty("microformatDataRenderer");
|
||||||
|
|
||||||
|
var availableCountries = microformat
|
||||||
|
.GetProperty("availableCountries")
|
||||||
|
.EnumerateArray()
|
||||||
|
.Select(e => e.GetString())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var description = microformat.GetProperty("description").GetString();
|
||||||
|
var noIndex = microformat.GetProperty("noindex").GetBoolean();
|
||||||
|
var unlisted = microformat.GetProperty("unlisted").GetBoolean();
|
||||||
|
var familySafe = microformat.GetProperty("familySafe").GetBoolean();
|
||||||
|
|
||||||
|
var avatarThumbnails = rootDoc
|
||||||
|
.GetProperty("metadata")
|
||||||
|
.GetProperty("channelMetadataRenderer")
|
||||||
|
.GetProperty("avatar")
|
||||||
|
.GetProperty("thumbnails")
|
||||||
|
.EnumerateArray();
|
||||||
|
|
||||||
|
var avatars = JsonParser.ParseImages(avatarThumbnails);
|
||||||
|
|
||||||
|
var headerContent = rootDoc
|
||||||
|
.GetProperty("header")
|
||||||
|
.GetProperty("pageHeaderRenderer")
|
||||||
|
.GetProperty("content");
|
||||||
|
|
||||||
|
var metadataPartHandle = headerContent
|
||||||
|
.GetProperty("pageHeaderViewModel")
|
||||||
|
.GetProperty("metadata")
|
||||||
|
.GetProperty("contentMetadataViewModel")
|
||||||
|
.GetProperty("metadataRows")
|
||||||
|
.EnumerateArray()
|
||||||
|
.FirstOrDefault()
|
||||||
|
.GetProperty("metadataParts")
|
||||||
|
.EnumerateArray()
|
||||||
|
.FirstOrDefault()
|
||||||
|
.GetProperty("text")
|
||||||
|
.GetProperty("content").GetString();
|
||||||
|
|
||||||
|
var bannerImages = headerContent
|
||||||
|
.GetProperty("pageHeaderViewModel")
|
||||||
|
.GetProperty("banner")
|
||||||
|
.GetProperty("imageBannerViewModel")
|
||||||
|
.GetProperty("image")
|
||||||
|
.GetProperty("sources")
|
||||||
|
.EnumerateArray();
|
||||||
|
|
||||||
|
var banners = JsonParser.ParseImages(bannerImages);
|
||||||
|
|
||||||
|
var resultFetch = new ChannelFetch
|
||||||
|
{
|
||||||
|
NoIndex = noIndex,
|
||||||
|
Unlisted = unlisted,
|
||||||
|
FamilySafe = familySafe,
|
||||||
|
Handle = metadataPartHandle,
|
||||||
|
Description = description,
|
||||||
|
AvailableCountries = availableCountries.OfType<string>().ToList(),
|
||||||
|
AvatarImages = avatars,
|
||||||
|
BannerImages = banners
|
||||||
|
};
|
||||||
|
return resultFetch;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return ResultError.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
Manager.YouTube/Parsers/Json/JsonAccountParser.cs
Normal file
43
Manager.YouTube/Parsers/Json/JsonAccountParser.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using DotBased.Monads;
|
||||||
|
|
||||||
|
namespace Manager.YouTube.Parsers.Json;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parsing functionality for the response from endpoint: /youtubei/v1/account/account_menu
|
||||||
|
/// </summary>
|
||||||
|
public static class JsonAccountParser
|
||||||
|
{
|
||||||
|
public static Result<string> ParseAccountId(string json)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonDocument = JsonDocument.Parse(json);
|
||||||
|
|
||||||
|
var id = jsonDocument.RootElement
|
||||||
|
.GetProperty("actions")[0]
|
||||||
|
.GetProperty("openPopupAction")
|
||||||
|
.GetProperty("popup")
|
||||||
|
.GetProperty("multiPageMenuRenderer")
|
||||||
|
.GetProperty("header")
|
||||||
|
.GetProperty("activeAccountHeaderRenderer")
|
||||||
|
.GetProperty("manageAccountTitle")
|
||||||
|
.GetProperty("runs")
|
||||||
|
.EnumerateArray()
|
||||||
|
.FirstOrDefault()
|
||||||
|
.GetProperty("navigationEndpoint")
|
||||||
|
.GetProperty("browseEndpoint")
|
||||||
|
.GetProperty("browseId").GetString();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(id))
|
||||||
|
{
|
||||||
|
return ResultError.Fail("Unable to get account id!");
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return ResultError.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Manager.YouTube/Parsers/Json/JsonParser.cs
Normal file
12
Manager.YouTube/Parsers/Json/JsonParser.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Manager.YouTube.Models.Innertube;
|
||||||
|
|
||||||
|
namespace Manager.YouTube.Parsers.Json;
|
||||||
|
|
||||||
|
public static class JsonParser
|
||||||
|
{
|
||||||
|
public static List<WebImage> ParseImages(JsonElement.ArrayEnumerator array) =>
|
||||||
|
array
|
||||||
|
.Select(image => new WebImage { Width = image.GetProperty("width").GetInt32(), Height = image.GetProperty("height").GetInt32(), Url = image.GetProperty("url").GetString() ?? "" })
|
||||||
|
.ToList();
|
||||||
|
}
|
Reference in New Issue
Block a user