using System.Net.Mime; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using DotBased.Monads; using Manager.YouTube.Models.Innertube; using Manager.YouTube.Parsers; using Manager.YouTube.Util; namespace Manager.YouTube; public static class NetworkService { private const string Origin = "https://www.youtube.com"; public static async Task> GetClientStateAsync(YouTubeClient client) { var httpRequest = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(Origin) }; httpRequest.Headers.Clear(); httpRequest.Headers.UserAgent.ParseAdd(client.UserAgent); var http = client.GetHttpClient(); if (http == null) { return ResultError.Fail("Unable to get http client!"); } var response = await http.SendAsync(httpRequest); if (!response.IsSuccessStatusCode) { var responseResult = await response.Content.ReadAsStringAsync(); return ResultError.Fail(responseResult); } var responseHtml = await response.Content.ReadAsStringAsync(); var clientStateResult = HtmlParser.GetStateJson(responseHtml); if (clientStateResult is { IsSuccess: false, Error: not null }) { return clientStateResult.Error; } ClientState? clientState; try { clientState = JsonSerializer.Deserialize(clientStateResult.Value.Item1); } catch (Exception e) { return ResultError.Error(e, "Error while parsing JSON!"); } return clientState == null ? ResultError.Fail("Unable to parse client state!") : clientState; } public static async Task> GetDatasyncIds(YouTubeClient client) { if (client.External.State is not { LoggedIn: true } || client.CookieContainer.Count == 0) { return ResultError.Fail("Client is not logged in, requires logged in client for this endpoint (/getDatasyncIdsEndpoint)."); } var httpClient = client.GetHttpClient(); if (httpClient == null) { return ResultError.Fail("Unable to get http client!"); } var httpRequest = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri($"{Origin}/getDatasyncIdsEndpoint") }; httpRequest.Headers.UserAgent.ParseAdd(client.UserAgent); httpRequest.Headers.Add("Origin", Origin); var response = await httpClient.SendAsync(httpRequest); if (!response.IsSuccessStatusCode) { var responseResult = await response.Content.ReadAsStringAsync(); return ResultError.Fail(responseResult); } var responseContent = await response.Content.ReadAsStringAsync(); var datasyncIdsJson = JsonNode.Parse(responseContent.Replace(")]}'", "")); var isLoggedOut = datasyncIdsJson?["responseContext"]?["mainAppWebResponseContext"]?["loggedOut"] .Deserialize() ?? true; if (!isLoggedOut) { return datasyncIdsJson?["datasyncIds"].Deserialize() ?? []; } return ResultError.Fail("Failed to get datasyncIds!"); } public static async Task> GetCurrentAccountIdAsync(YouTubeClient client) { if (client.External.State is not { LoggedIn: true }) { return ResultError.Fail("Client not logged in!"); } var httpRequest = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri($"{Origin}/youtubei/v1/account/account_menu") }; httpRequest.Headers.UserAgent.ParseAdd(client.UserAgent); httpRequest.Headers.Add("Origin", Origin); if (client.SapisidCookie != null) { httpRequest.Headers.Authorization = AuthenticationUtilities.GetSapisidHashHeader(client.External.GetDatasyncId(), client.SapisidCookie.Value, Origin); } var serializedContext = JsonSerializer.SerializeToNode(client.External.State.InnerTubeContext); var payload = new JsonObject { { "context", serializedContext } }; httpRequest.Content = new StringContent(payload.ToJsonString(), Encoding.UTF8, MediaTypeNames.Application.Json); var http = client.GetHttpClient(); if (http == null) { return ResultError.Fail("Unable to get http client!"); } try { var response = await http.SendAsync(httpRequest); if (!response.IsSuccessStatusCode) { var responseResult = await response.Content.ReadAsStringAsync(); 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; } catch (Exception e) { return ResultError.Error(e); } } public static async Task GetChannelAsync(string channelId, YouTubeClient client) { if (client.External.State == null) { return ResultError.Fail("No client state!"); } if (string.IsNullOrWhiteSpace(channelId)) { return ResultError.Fail("Channel id is empty!"); } var httpClient = client.GetHttpClient(); if (httpClient == null) { return ResultError.Fail("Unable to get http client!"); } var serializedContext = JsonSerializer.SerializeToNode(client.External.State.InnerTubeContext); var payload = new JsonObject { { "context", serializedContext }, { "browseId", channelId } }; var requestMessage = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri($"{Origin}/youtubei/v1/browse?key={client.External.State.InnertubeApiKey}"), Content = new StringContent(payload.ToJsonString(), Encoding.UTF8, MediaTypeNames.Application.Json) }; requestMessage.Headers.UserAgent.ParseAdd(client.UserAgent); requestMessage.Headers.Add("Origin", Origin); if (client.SapisidCookie != null) { requestMessage.Headers.Authorization = AuthenticationUtilities.GetSapisidHashHeader(client.External.GetDatasyncId(), client.SapisidCookie.Value, Origin); } var response = await httpClient.SendAsync(requestMessage); if (!response.IsSuccessStatusCode) { var responseResult = await response.Content.ReadAsStringAsync(); return ResultError.Fail(responseResult); } var jsonContent = await response.Content.ReadAsStringAsync(); return Result.Fail(ResultError.Fail("Not implemented!")); } }