using System.Threading.Tasks; using AngleSharp; using AngleSharp.Dom; using AngleSharp.Html.Dom; using YouTubeMusicAPI.Client; using System.Text.Json; using Acornima.Ast; using AngleSharp.Text; using System.Text.RegularExpressions; using System.Text.Json.Serialization; public class NavidromeValidator : SongValidatorBase { private YouTubeMusicClient youtubeClient; public NavidromeValidator(ILogger logger, SpotifyApiClient spotifyApiClient) : base(logger, spotifyApiClient) { youtubeClient = new("AT"); } public override async Task CanValidateUriAsync(Uri songUri) { // Check if behind this URL there is a public navidrome share var config = Configuration.Default.WithDefaultLoader(); var address = songUri.ToString(); var context = BrowsingContext.New(config); var document = await context.OpenAsync(address); var titleCell = (document.DocumentElement.GetDescendants().First() as HtmlElement) .GetElementsByTagName("title").First(); return "Navidrome".Equals(titleCell.TextContent); } public override async Task CanExtractSongMetadataAsync(Uri songUri) { return await this.CanValidateUriAsync(songUri); } public override SongProvider GetSongProvider() { return SongProvider.NavidromeSharedLink; } private static Stream GenerateStreamFromString(string s) { var stream = new MemoryStream(); var writer = new StreamWriter(stream); writer.Write(s); writer.Flush(); stream.Position = 0; return stream; } public override async Task ValidateAsync(Uri songUri) { var config = Configuration.Default.WithDefaultLoader(); var address = songUri.ToString(); var context = BrowsingContext.New(config); var document = await context.OpenAsync(address); var infoScriptNode = document.GetElementsByTagName("script").Where(e => e.TextContent.Contains("__SHARE_INFO__")).First(); var manipulatedValue = infoScriptNode.TextContent.Replace("window.__SHARE_INFO__ = \"", "").StripLeadingTrailingSpaces().StripLineBreaks(); manipulatedValue = manipulatedValue.Remove(manipulatedValue.Length - 1); var infoScriptJsonData = Regex.Unescape(Regex.Unescape(manipulatedValue)); var title = string.Empty; var artist = string.Empty; using (var stream = GenerateStreamFromString(infoScriptJsonData)) { var jsonContent = await JsonSerializer.DeserializeAsync(stream); title = jsonContent.Tracks[0].Title; artist = jsonContent.Tracks[0].Artist; } var song = new Song { Name = title, Artist = artist, Url = songUri.ToString(), Provider = SongProvider.NavidromeSharedLink, SpotifyId = await this.LookupSpotifyIdAsync(title, artist) }; return song; } } public class NavidromeShareInfoData { [JsonPropertyName("id")] public string Id { get; set; } [JsonPropertyName("description")] public string Description { get; set; } [JsonPropertyName("downloadable")] public bool Downloadable { get; set; } [JsonPropertyName("tracks")] public List Tracks { get; set; } } public class NavidromeTrackInfoData { [JsonPropertyName("id")] public string Id { get; set; } [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("artist")] public string Artist { get; set; } [JsonPropertyName("album")] public string Album { get; set; } [JsonPropertyName("updatedAt")] public DateTime UpdatedAt { get; set; } [JsonPropertyName("duration")] public float Duration { get; set; } }