diff --git a/Manager.App/Components/Dialogs/AccountDialog.razor b/Manager.App/Components/Dialogs/AccountDialog.razor
index b7c0b53..3992946 100644
--- a/Manager.App/Components/Dialogs/AccountDialog.razor
+++ b/Manager.App/Components/Dialogs/AccountDialog.razor
@@ -26,19 +26,27 @@
Account handle: |
@Client.External.Information.AccountHandle |
-
- User agent: |
- @Client.UserAgent |
-
-
Logged in: |
@Client.External.State?.LoggedIn |
+
+ YouTube Premium: |
+ @Client.External.Information.IsPremiumUser |
+
+
+
+ User agent: |
+ @Client.UserAgent |
+
InnerTube API key: |
@Client.External.State?.InnertubeApiKey |
+
+ InnerTube client: |
+ @Client.External.State?.InnerTubeClient |
+
InnerTube client version: |
@Client.External.State?.InnerTubeClientVersion |
diff --git a/Manager.App/Components/Dialogs/AccountDialog.razor.cs b/Manager.App/Components/Dialogs/AccountDialog.razor.cs
index 1f2e5cc..363f816 100644
--- a/Manager.App/Components/Dialogs/AccountDialog.razor.cs
+++ b/Manager.App/Components/Dialogs/AccountDialog.razor.cs
@@ -67,22 +67,17 @@ namespace Manager.App.Components.Dialogs
foreach (var cookieStr in cookies)
{
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();
- var value = parts[1].Trim();
-
- var cookie = new Cookie(name, value)
- {
- Expires = DateTime.Now.AddDays(1),
- Path = "/",
- };
-
- if (!string.IsNullOrEmpty(domain))
- cookie.Domain = domain;
-
- collection.Add(cookie);
- }
+ Path = "/",
+ Domain = domain
+ };
+ collection.Add(cookie);
}
return collection;
diff --git a/Manager.YouTube/Models/ClientInformation.cs b/Manager.YouTube/Models/ClientInformation.cs
index b9e6aa8..607cf9e 100644
--- a/Manager.YouTube/Models/ClientInformation.cs
+++ b/Manager.YouTube/Models/ClientInformation.cs
@@ -5,4 +5,5 @@ public class ClientInformation
public string? AccountName { get; set; }
public string? AccountHandle { get; set; }
public string? Description { get; set; }
+ public bool IsPremiumUser { get; set; }
}
\ No newline at end of file
diff --git a/Manager.YouTube/Models/Innertube/ChannelFetch.cs b/Manager.YouTube/Models/Innertube/ChannelFetch.cs
index 9e212ef..149deea 100644
--- a/Manager.YouTube/Models/Innertube/ChannelFetch.cs
+++ b/Manager.YouTube/Models/Innertube/ChannelFetch.cs
@@ -4,7 +4,10 @@ public class ChannelFetch
{
public bool NoIndex { 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 AvailableCountries { get; set; } = [];
-
+ public List AvatarImages { get; set; } = [];
+ public List BannerImages { get; set; } = [];
}
\ No newline at end of file
diff --git a/Manager.YouTube/Models/Innertube/ClientState.cs b/Manager.YouTube/Models/Innertube/ClientState.cs
index ad69622..bc131c0 100644
--- a/Manager.YouTube/Models/Innertube/ClientState.cs
+++ b/Manager.YouTube/Models/Innertube/ClientState.cs
@@ -15,6 +15,9 @@ public class ClientState : AdditionalJsonData
[JsonPropertyName("SIGNIN_URL")]
public string? SigninUrl { get; set; }
+
+ [JsonPropertyName("INNERTUBE_CLIENT_NAME")]
+ public string? InnerTubeClient { get; set; }
[JsonPropertyName("INNERTUBE_CLIENT_VERSION")]
public string? InnerTubeClientVersion { get; set; }
diff --git a/Manager.YouTube/Models/Innertube/WebImage.cs b/Manager.YouTube/Models/Innertube/WebImage.cs
new file mode 100644
index 0000000..37e282b
--- /dev/null
+++ b/Manager.YouTube/Models/Innertube/WebImage.cs
@@ -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; } = "";
+}
\ No newline at end of file
diff --git a/Manager.YouTube/NetworkService.cs b/Manager.YouTube/NetworkService.cs
index 748f552..2826f75 100644
--- a/Manager.YouTube/NetworkService.cs
+++ b/Manager.YouTube/NetworkService.cs
@@ -5,6 +5,7 @@ using System.Text.Json.Nodes;
using DotBased.Monads;
using Manager.YouTube.Models.Innertube;
using Manager.YouTube.Parsers;
+using Manager.YouTube.Parsers.Json;
using Manager.YouTube.Util;
namespace Manager.YouTube;
@@ -52,6 +53,8 @@ public static class NetworkService
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;
}
@@ -125,7 +128,8 @@ public static class NetworkService
{
return ResultError.Fail("Unable to get http client!");
}
-
+
+ string json;
try
{
var response = await http.SendAsync(httpRequest);
@@ -135,31 +139,14 @@ public static class NetworkService
return ResultError.Fail(responseResult);
}
- var 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;
+ json = await response.Content.ReadAsStringAsync();
}
catch (Exception e)
{
return ResultError.Error(e);
}
+
+ return JsonAccountParser.ParseAccountId(json);
}
public static async Task> GetChannelAsync(string channelId, YouTubeClient client)
@@ -204,8 +191,10 @@ public static class NetworkService
}
var jsonContent = await response.Content.ReadAsStringAsync();
-
+ var parsed = ChannelJsonParser.ParseJsonToChannelData(jsonContent);
return ResultError.Fail("Not implemented!");
}
+
+
}
\ No newline at end of file
diff --git a/Manager.YouTube/Parsers/HtmlParser.cs b/Manager.YouTube/Parsers/HtmlParser.cs
index 844d4c2..b00429c 100644
--- a/Manager.YouTube/Parsers/HtmlParser.cs
+++ b/Manager.YouTube/Parsers/HtmlParser.cs
@@ -5,7 +5,7 @@ namespace Manager.YouTube.Parsers;
public static class HtmlParser
{
- public static Result<(string, string)> GetStateJson(string html)
+ public static Result<(string, bool)> GetStateJson(string html)
{
if (string.IsNullOrWhiteSpace(html))
{
@@ -21,14 +21,15 @@ public static class HtmlParser
return ResultError.Fail($"Could not find {setFunction} in html script nodes!");
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 (json, jsonText);
+ var isPremiumUser = html.Contains("logo-type=\"YOUTUBE_PREMIUM_LOGO\"", StringComparison.OrdinalIgnoreCase);
+
+ return (json, isPremiumUser);
}
static string? ExtractJson(string input, string marker)
diff --git a/Manager.YouTube/Parsers/Json/ChannelJsonParser.cs b/Manager.YouTube/Parsers/Json/ChannelJsonParser.cs
new file mode 100644
index 0000000..6a4172a
--- /dev/null
+++ b/Manager.YouTube/Parsers/Json/ChannelJsonParser.cs
@@ -0,0 +1,87 @@
+using System.Text.Json;
+using DotBased.Monads;
+using Manager.YouTube.Models.Innertube;
+
+namespace Manager.YouTube.Parsers.Json;
+
+///
+/// Parsing functionality for the response from the innertube browse endpoint.
+///
+public static class ChannelJsonParser
+{
+ public static Result 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().ToList(),
+ AvatarImages = avatars,
+ BannerImages = banners
+ };
+ return resultFetch;
+ }
+ catch (Exception e)
+ {
+ return ResultError.Error(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Manager.YouTube/Parsers/Json/JsonAccountParser.cs b/Manager.YouTube/Parsers/Json/JsonAccountParser.cs
new file mode 100644
index 0000000..d324056
--- /dev/null
+++ b/Manager.YouTube/Parsers/Json/JsonAccountParser.cs
@@ -0,0 +1,43 @@
+using System.Text.Json;
+using DotBased.Monads;
+
+namespace Manager.YouTube.Parsers.Json;
+
+///
+/// Parsing functionality for the response from endpoint: /youtubei/v1/account/account_menu
+///
+public static class JsonAccountParser
+{
+ public static Result 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Manager.YouTube/Parsers/Json/JsonParser.cs b/Manager.YouTube/Parsers/Json/JsonParser.cs
new file mode 100644
index 0000000..3ae103a
--- /dev/null
+++ b/Manager.YouTube/Parsers/Json/JsonParser.cs
@@ -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 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();
+}
\ No newline at end of file