using System.Runtime.Serialization; using System.Text.Json; using System.Text.Json.Serialization; using DotBased.Logging; using Manager.YouTube.Models; using Manager.YouTube.Models.Innertube; using Manager.YouTube.Parsers.Json; namespace Manager.YouTube.Util.Converters; public class YouTubeVideoJsonConverter : JsonConverter { private readonly ILogger _logger = LogService.RegisterLogger(); public override YouTubeVideo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var document = JsonDocument.ParseValue(ref reader); var root = document.RootElement; var playabilityStatus = root.GetProperty("playabilityStatus"); var streamingData = root.GetProperty("streamingData"); var videoDetails = root.GetProperty("videoDetails"); var playerConfigJson = root.GetProperty("playerConfig"); var microformat = root.GetProperty("microformat").GetProperty("playerMicroformatRenderer"); var videoId = videoDetails.GetProperty("videoId").GetString() ?? microformat.GetProperty("externalVideoId").GetString(); if (string.IsNullOrEmpty(videoId)) { throw new SerializationException("Failed to get videoId"); } var thumbnails = JsonParser.ExtractWebImages(videoDetails.GetProperty("thumbnail")); thumbnails.AddRange(JsonParser.ExtractWebImages(microformat.GetProperty("thumbnail"))); var video = new YouTubeVideo { VideoId = videoId, Title = JsonParser.ExtractTextOrHtml(microformat.GetProperty("title")), Description = JsonParser.ExtractTextOrHtml(microformat.GetProperty("description")), ViewCount = videoDetails.GetProperty("viewCount").GetInt32(), LikeCount = videoDetails.GetProperty("likeCount").GetInt32(), ChannelId = videoDetails.GetProperty("channelId").GetString() ?? "", Author = JsonParser.ExtractTextOrHtml(videoDetails.GetProperty("author")), PlayabilityStatus = playabilityStatus.GetProperty("status").GetString() ?? "", LengthSeconds = videoDetails.GetProperty("lengthSeconds").GetInt32(), Keywords = videoDetails.GetProperty("keywords").EnumerateArray().Select(v => v.GetString()).Cast().ToArray(), IsOwnerViewing = videoDetails.GetProperty("isOwnerViewing").GetBoolean(), AllowRating = videoDetails.GetProperty("allowRating").GetBoolean(), IsCrawlable = videoDetails.GetProperty("isCrawlable").GetBoolean(), IsPrivate = videoDetails.GetProperty("isPrivate").GetBoolean(), IsUnpluggedCorpus = videoDetails.GetProperty("isUnpluggedCorpus").GetBoolean(), IsLive = videoDetails.GetProperty("isLiveContent").GetBoolean(), IsFamilySave = microformat.GetProperty("isFamilySave").GetBoolean(), AvailableCountries = microformat.GetProperty("availableCountries").EnumerateArray().Select(v => v.GetString()).Cast().ToArray(), IsUnlisted = microformat.GetProperty("isUnlisted").GetBoolean(), HasYpcMetadata = microformat.GetProperty("hasYpcMetadata").GetBoolean(), PublishDate = microformat.GetProperty("publishDate").GetDateTime(), UploadDate = microformat.GetProperty("uploadDate").GetDateTime(), IsShortsEligible = microformat.GetProperty("isShortsEligible").GetBoolean(), Category = microformat.GetProperty("category").GetString() ?? "", StreamingData = streamingData.Deserialize(), Thumbnails = thumbnails, PlayerConfig = ExtractPlayerConfig(playerConfigJson) }; return video; } public override void Write(Utf8JsonWriter writer, YouTubeVideo value, JsonSerializerOptions options) { throw new NotImplementedException("Converter only supports reading."); } private PlayerConfig? ExtractPlayerConfig(JsonElement element) { try { var playerConfig = new PlayerConfig { AudioLoudnessDb = element.GetProperty("audioConfig").GetProperty("loudnessDb").GetDouble(), AudioPerceptualLoudnessDb = element.GetProperty("audioConfig").GetProperty("perceptualLoudnessDb").GetDouble(), AudioEnablePerFormatLoudness = element.GetProperty("audioConfig").GetProperty("enablePerFormatLoudness") .GetBoolean(), MaxBitrate = element.GetProperty("streamSelectionConfig").GetProperty("maxBitrate").GetUInt32(), MaxReadAheadMediaTimeMs = element.GetProperty("mediaCommonConfig").GetProperty("dynamicReadaheadConfig") .GetProperty("maxReadAheadMediaTimeMs").GetUInt32(), MinReadAheadMediaTimeMs = element.GetProperty("mediaCommonConfig").GetProperty("dynamicReadaheadConfig") .GetProperty("minReadAheadMediaTimeMs").GetUInt32(), ReadAheadGrowthRateMs = element.GetProperty("mediaCommonConfig").GetProperty("dynamicReadaheadConfig") .GetProperty("readAheadGrowthRateMs").GetUInt32(), }; return playerConfig; } catch (Exception e) { _logger.Error(e, "Failed to extract player config from JSON."); return null; } } }