From 1d1ed728c4e35d89e9602d7687d3286b678d5ea9 Mon Sep 17 00:00:00 2001 From: SKProCH Date: Mon, 15 Jan 2024 02:59:45 +0300 Subject: [PATCH] Add YandexId type to handle cursed Yandex ids (long/guid) --- .../AudioItems/YandexMusicTrack.cs | 5 +- YandexMusicResolver/Ids/YandexId.cs | 128 ++++++++++++++++++ YandexMusicResolver/Ids/YandexIdConverter.cs | 36 +++++ YandexMusicResolver/Responses/MetaTrack.cs | 5 +- 4 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 YandexMusicResolver/Ids/YandexId.cs create mode 100644 YandexMusicResolver/Ids/YandexIdConverter.cs diff --git a/YandexMusicResolver/AudioItems/YandexMusicTrack.cs b/YandexMusicResolver/AudioItems/YandexMusicTrack.cs index f84213f..34a7f40 100644 --- a/YandexMusicResolver/AudioItems/YandexMusicTrack.cs +++ b/YandexMusicResolver/AudioItems/YandexMusicTrack.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using YandexMusicResolver.Ids; namespace YandexMusicResolver.AudioItems { /// /// AudioTrackInfo wrapper to resolve track direct url /// public class YandexMusicTrack { - internal YandexMusicTrack(string title, List authors, TimeSpan length, long id, string? uri, bool isAvailable, string? artworkUrl = null) { + internal YandexMusicTrack(string title, List authors, TimeSpan length, YandexId id, string? uri, bool isAvailable, string? artworkUrl = null) { Title = title; Authors = authors; Length = length; @@ -40,7 +41,7 @@ internal YandexMusicTrack(string title, List authors, TimeSpa /// /// Track id /// - public long Id { get; } + public YandexId Id { get; } /// /// Track link diff --git a/YandexMusicResolver/Ids/YandexId.cs b/YandexMusicResolver/Ids/YandexId.cs new file mode 100644 index 0000000..f073233 --- /dev/null +++ b/YandexMusicResolver/Ids/YandexId.cs @@ -0,0 +1,128 @@ +using System; + +namespace YandexMusicResolver.Ids; + +/// +/// Holds the yandex track id +/// +/// +/// Since Yandex fucked up as usual, it will return an ids for yandex's track, but will return ids for user uploaded tracks +/// +public struct YandexId { + private long? _longId; + private Guid? _guidId; + + /// + /// Gets the id + /// + /// Current Id is not an Guid type. You can look at to determine proper id type + public Guid GuidId { + get => _guidId ?? throw new InvalidOperationException($"Current Id is not an Guid type. Current type is {IdType}"); + set => _guidId = value; + } + + /// + /// Gets the id + /// + /// Current Id is not an long type. You can look at to determine proper id type + public long LongId { + get => _longId ?? throw new InvalidOperationException($"Current Id is not an long type. Current type is {IdType}"); + set => _longId = value; + } + + /// + /// Gets the current id type + /// + public YandexIdType IdType { get; } + + /// + /// Creates with id + /// + /// The id + public YandexId(Guid id) { + IdType = YandexIdType.Guid; + _guidId = id; + } + + /// + /// Creates with id + /// + /// The id + public YandexId(long id) { + IdType = YandexIdType.Long; + _longId = id; + } + + /// + /// Allows to implicitly cast to + /// + /// Instance of + /// The value + public static implicit operator Guid(YandexId yandexId) => yandexId.GuidId; + + /// + /// Allows to implicitly cast to + /// + /// Instance of + /// The value + public static implicit operator long(YandexId yandexId) => yandexId.LongId; + + /// + /// Allows to implicitly cast to + /// + /// value + /// The intance + public static implicit operator YandexId(long id) => new(id); + + /// + /// Allows to implicitly cast to + /// + /// value + /// The intance + public static implicit operator YandexId(Guid id) => new(id); + + /// + public override string ToString() { + if (_longId is not null) { + return _longId.ToString(); + } + + if (_guidId is not null) { + return _guidId.ToString(); + } + + throw new NotSupportedException("Current Id type doesn't support to string conversion. Report this to the library author"); + } + + /// + /// Tries to parse from the string + /// + /// to parse the Id + /// Instance of + /// Current string cannot be converted to any known Id type. Check the string + public static YandexId Parse(string s) { + if (Guid.TryParse(s, out var guid)) { + return new YandexId(guid); + } + + if (long.TryParse(s, out var l)) { + return new YandexId(l); + } + + throw new NotSupportedException("Current string cannot be converted to any known Id type. Check the string"); + } + + /// + /// Available types of Yandex ids + /// + public enum YandexIdType { + /// + /// Current id is long + /// + Long, + /// + /// Current id is Guid + /// + Guid + } +} diff --git a/YandexMusicResolver/Ids/YandexIdConverter.cs b/YandexMusicResolver/Ids/YandexIdConverter.cs new file mode 100644 index 0000000..e27d333 --- /dev/null +++ b/YandexMusicResolver/Ids/YandexIdConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace YandexMusicResolver.Ids; + +/// +/// Allows to properly serialize/deserialize the from JSON +/// +public class YandexIdConverter : JsonConverter { + /// + public override YandexId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TryGetInt64(out var l)) { + return new YandexId(l); + } + + if (reader.TryGetGuid(out var guid)) { + return new YandexId(guid); + } + + throw new NotSupportedException("Current value seems doesn't look like any known YandexId type"); + } + /// + public override void Write(Utf8JsonWriter writer, YandexId value, JsonSerializerOptions options) { + switch (value.IdType) { + case YandexId.YandexIdType.Long: + writer.WriteNumberValue(value.LongId); + break; + case YandexId.YandexIdType.Guid: + writer.WriteStringValue(value.GuidId); + break; + default: + throw new ArgumentOutOfRangeException(nameof(value.IdType), "Current YandexId type can't be wrote to JSON"); + } + } +} diff --git a/YandexMusicResolver/Responses/MetaTrack.cs b/YandexMusicResolver/Responses/MetaTrack.cs index 0670668..ce29e82 100644 --- a/YandexMusicResolver/Responses/MetaTrack.cs +++ b/YandexMusicResolver/Responses/MetaTrack.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text.Json.Serialization; using YandexMusicResolver.AudioItems; +using YandexMusicResolver.Ids; namespace YandexMusicResolver.Responses { /// @@ -11,8 +12,8 @@ namespace YandexMusicResolver.Responses { internal class MetaTrack { private const string TrackUrlFormat = "https://music.yandex.ru/album/{0}/track/{1}"; - [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] - public long Id { get; set; } + [JsonConverter(typeof(YandexIdConverter))] + public YandexId Id { get; set; } public string Title { get; set; } = null!;