121 lines
3.8 KiB
C#
121 lines
3.8 KiB
C#
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<bool> 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<bool> 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<Song> 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<NavidromeShareInfoData>(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<NavidromeTrackInfoData> 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; }
|
|
} |