121 lines
6.3 KiB
C#
121 lines
6.3 KiB
C#
using System.Runtime.Serialization;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
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<YouTubeVideo>
|
|
{
|
|
private readonly ILogger _logger = LogService.RegisterLogger<YouTubeVideoJsonConverter>();
|
|
private readonly JsonSerializerOptions _serializerOptions = new()
|
|
{
|
|
Converters = {
|
|
new NumericJsonConverter<int>(),
|
|
new NumericJsonConverter<uint>(),
|
|
new NumericJsonConverter<long>(),
|
|
new NumericJsonConverter<double>(),
|
|
new NumericJsonConverter<decimal>() },
|
|
PropertyNameCaseInsensitive = true
|
|
};
|
|
|
|
public override YouTubeVideo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
{
|
|
var node = JsonNode.Parse(ref reader);
|
|
if (node == null)
|
|
{
|
|
throw new SerializationException("Failed to parse JSON reader.");
|
|
}
|
|
|
|
var rootObject = node.AsObject();
|
|
|
|
var playabilityStatus = rootObject["playabilityStatus"];
|
|
var streamingDataJson = rootObject["streamingData"];
|
|
var videoDetails = rootObject["videoDetails"];
|
|
var playerConfigJson = rootObject["playerConfig"];
|
|
var microformat = rootObject["microformat"]?["playerMicroformatRenderer"];
|
|
|
|
var videoId = videoDetails?["videoId"]?.GetValue<string>() ?? microformat?["externalVideoId"]?.GetValue<string>();
|
|
if (string.IsNullOrEmpty(videoId))
|
|
{
|
|
throw new SerializationException("Failed to get videoId");
|
|
}
|
|
|
|
var thumbnails = JsonParser.ExtractWebImages(videoDetails?["thumbnail"]);
|
|
thumbnails.AddRange(JsonParser.ExtractWebImages(microformat?["thumbnail"]));
|
|
|
|
var streamingData = streamingDataJson.Deserialize<StreamingData>(_serializerOptions);
|
|
var playerConfig = ExtractPlayerConfig(playerConfigJson);
|
|
|
|
var video = new YouTubeVideo
|
|
{
|
|
VideoId = videoId,
|
|
Title = JsonParser.ExtractTextOrHtml(microformat?["title"]),
|
|
Description = JsonParser.ExtractTextOrHtml(microformat?["description"]),
|
|
ViewCount = long.TryParse(microformat?["viewCount"]?.GetValue<string>(), out var viewCountParsed) ? viewCountParsed : -1,
|
|
LikeCount = long.TryParse(microformat?["likeCount"]?.GetValue<string>(), out var likeCountParsed) ? likeCountParsed : -1,
|
|
ChannelId = videoDetails?["channelId"]?.GetValue<string>() ?? "",
|
|
Author = videoDetails?["author"]?.GetValue<string>() ?? "",
|
|
PlayabilityStatus = playabilityStatus?["status"]?.GetValue<string>() ?? "",
|
|
LengthSeconds = long.TryParse(videoDetails?["lengthSeconds"]?.GetValue<string>(), out var lengthSecondsParsed) ? lengthSecondsParsed : -1,
|
|
Keywords = videoDetails?["keywords"]?.AsArray().Select(v => v?.GetValue<string>() ?? "").ToArray() ?? [],
|
|
IsOwnerViewing = videoDetails?["isOwnerViewing"]?.GetValue<bool>() ?? false,
|
|
AllowRating = videoDetails?["allowRating"]?.GetValue<bool>() ?? false,
|
|
IsCrawlable = videoDetails?["isCrawlable"]?.GetValue<bool>() ?? false,
|
|
IsPrivate = videoDetails?["isPrivate"]?.GetValue<bool>() ?? false,
|
|
IsUnpluggedCorpus = videoDetails?["isUnpluggedCorpus"]?.GetValue<bool>() ?? false,
|
|
IsLive = videoDetails?["isLiveContent"]?.GetValue<bool>() ?? false,
|
|
IsFamilySave = microformat?["isFamilySave"]?.GetValue<bool>() ?? false,
|
|
AvailableCountries = microformat?["availableCountries"]?.AsArray().Select(v => v?.GetValue<string>() ?? "").ToArray() ?? [],
|
|
IsUnlisted = microformat?["isUnlisted"]?.GetValue<bool>() ?? false,
|
|
HasYpcMetadata = microformat?["hasYpcMetadata"]?.GetValue<bool>() ?? false,
|
|
PublishDate = DateTime.TryParse(microformat?["publishDate"]?.GetValue<string>(), out var parsedPublishDate) ? parsedPublishDate : DateTime.MinValue,
|
|
UploadDate = DateTime.TryParse(microformat?["uploadDate"]?.GetValue<string>(), out var parsedUploadDate) ? parsedUploadDate : DateTime.MinValue,
|
|
IsShortsEligible = microformat?["isShortsEligible"]?.GetValue<bool>() ?? false,
|
|
Category = microformat?["category"]?.GetValue<string>() ?? "",
|
|
StreamingData = streamingData,
|
|
Thumbnails = thumbnails,
|
|
PlayerConfig = playerConfig
|
|
};
|
|
|
|
return video;
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, YouTubeVideo value, JsonSerializerOptions options)
|
|
{
|
|
throw new NotImplementedException("Converter only supports reading.");
|
|
}
|
|
|
|
private PlayerConfig? ExtractPlayerConfig(JsonNode? playerConfigNode)
|
|
{
|
|
if (playerConfigNode == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
var playerConfigObj = playerConfigNode.AsObject();
|
|
var playerConfig = new PlayerConfig
|
|
{
|
|
AudioLoudnessDb = playerConfigObj["audioConfig"]?["loudnessDb"]?.GetValue<double>() ?? 0,
|
|
AudioPerceptualLoudnessDb = playerConfigObj["audioConfig"]?["perceptualLoudnessDb"]?.GetValue<double>() ?? 0,
|
|
AudioEnablePerFormatLoudness = playerConfigObj["audioConfig"]?["enablePerFormatLoudness"]?.GetValue<bool>() ?? false,
|
|
MaxBitrate = uint.TryParse(playerConfigObj["streamSelectionConfig"]?["maxBitrate"]?.GetValue<string>(), out var parsedMaxBitrate) ? parsedMaxBitrate : 0,
|
|
MaxReadAheadMediaTimeMs = playerConfigObj["mediaCommonConfig"]?["dynamicReadaheadConfig"]?["maxReadAheadMediaTimeMs"]?.GetValue<uint>() ?? 0,
|
|
MinReadAheadMediaTimeMs = playerConfigObj["mediaCommonConfig"]?["dynamicReadaheadConfig"]?["minReadAheadMediaTimeMs"]?.GetValue<uint>() ?? 0,
|
|
ReadAheadGrowthRateMs = playerConfigObj["mediaCommonConfig"]?["dynamicReadaheadConfig"]?["readAheadGrowthRateMs"]?.GetValue<uint>() ?? 0,
|
|
};
|
|
return playerConfig;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.Error(e, "Failed to extract player config from JSON.");
|
|
return null;
|
|
}
|
|
}
|
|
} |