Compare commits
7 Commits
0.4.0
...
8b91a13095
Author | SHA1 | Date | |
---|---|---|---|
8b91a13095 | |||
27a5ca6b74 | |||
![]() |
8a36606dee | ||
![]() |
dfc02f6907 | ||
![]() |
887a8a7b6a | ||
![]() |
4b18003aa8 | ||
c0bee8fd3c |
28
HISTORY.md
28
HISTORY.md
@@ -4,6 +4,34 @@ Changelog
|
|||||||
|
|
||||||
(unreleased)
|
(unreleased)
|
||||||
------------
|
------------
|
||||||
|
- Feat: add Navidrome song validator, refs #5. [Simon Diesenreiter]
|
||||||
|
|
||||||
|
|
||||||
|
0.4.2 (2025-06-25)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Broken build, refs NOISSUE. [Simon Diesenreiter]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
|
||||||
|
0.4.1 (2025-06-25)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Fix formatting, refs NOISSUE. [Simon Diesenreiter]
|
||||||
|
- URL type, refs NOISSUE. [simon]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
|
||||||
|
0.4.0 (2025-06-04)
|
||||||
|
------------------
|
||||||
|
|
||||||
Fix
|
Fix
|
||||||
~~~
|
~~~
|
||||||
|
@@ -11,10 +11,9 @@ public class PhoneClaimCodeProviderService
|
|||||||
_signalIntegration = signalIntegration;
|
_signalIntegration = signalIntegration;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Random random = new Random();
|
|
||||||
|
|
||||||
private static string RandomString(int length)
|
private static string RandomString(int length)
|
||||||
{
|
{
|
||||||
|
Random random = new Random();
|
||||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
return new string(Enumerable.Repeat(chars, length)
|
return new string(Enumerable.Repeat(chars, length)
|
||||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||||
|
@@ -3,10 +3,10 @@ using song_of_the_day;
|
|||||||
|
|
||||||
public class LinkPreviewAttachment
|
public class LinkPreviewAttachment
|
||||||
{
|
{
|
||||||
public string Url { get; set; }
|
public required string Url { get; set; }
|
||||||
public string Title { get; set; }
|
public required string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public required string Description { get; set; }
|
||||||
public string Base64Image { get; set; }
|
public required string Base64Image { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SignalIntegration
|
public class SignalIntegration
|
||||||
@@ -150,12 +150,12 @@ public class SignalIntegration
|
|||||||
}
|
}
|
||||||
await this.SendMessageToUserAsync("Hi, my name is Proggy and I am your friendly neighborhood *Song of the Day* bot!", user.SignalMemberId);
|
await this.SendMessageToUserAsync("Hi, my name is Proggy and I am your friendly neighborhood *Song of the Day* bot!", user.SignalMemberId);
|
||||||
await this.SendMessageToUserAsync("You are receiving this message because you have been invited to a *Song of the Day* community group.", user.SignalMemberId);
|
await this.SendMessageToUserAsync("You are receiving this message because you have been invited to a *Song of the Day* community group.", user.SignalMemberId);
|
||||||
await this.SendMessageToUserAsync("In that community group I will pick a person at random each day at 8 AM and encourage them to share a song with the rest of the community.", user.SignalMemberId);
|
await this.SendMessageToUserAsync("In that community group I will pick a person at random every now and then and encourage them to share a song with the rest of the community.", user.SignalMemberId);
|
||||||
if (AppConfiguration.Instance.UseBotTag)
|
if (AppConfiguration.Instance.UseBotTag)
|
||||||
{
|
{
|
||||||
await this.SendMessageToUserAsync("You can always see which messages are sent by me rather than the community host by the **[Proggy]** tag at the beginning of the message", user.SignalMemberId);
|
await this.SendMessageToUserAsync("You can always see which messages are sent by me rather than the community host by the **[Proggy]** tag at the beginning of the message", user.SignalMemberId);
|
||||||
}
|
}
|
||||||
await this.SendMessageToUserAsync($"Not right now, but eventually you will be able to see more details about your community at {AppConfiguration.Instance.WebUIBaseURL}.", user.SignalMemberId);
|
await this.SendMessageToUserAsync($"You can find more details about your community at {AppConfiguration.Instance.WebUIBaseURL}.", user.SignalMemberId);
|
||||||
await this.SendMessageToUserAsync($"""You can navigate to {AppConfiguration.Instance.WebUIBaseURL + (AppConfiguration.Instance.WebUIBaseURL.EndsWith("/") ? "" : "/")}User/{user.UserId} to set your preferred display name for me to use.""", user.SignalMemberId);
|
await this.SendMessageToUserAsync($"""You can navigate to {AppConfiguration.Instance.WebUIBaseURL + (AppConfiguration.Instance.WebUIBaseURL.EndsWith("/") ? "" : "/")}User/{user.UserId} to set your preferred display name for me to use.""", user.SignalMemberId);
|
||||||
await this.SendMessageToUserAsync($"Now have fun and enjoy being a part of this community!", user.SignalMemberId);
|
await this.SendMessageToUserAsync($"Now have fun and enjoy being a part of this community!", user.SignalMemberId);
|
||||||
}
|
}
|
||||||
|
@@ -17,13 +17,14 @@ public class IndexModel : PageModel
|
|||||||
[BindProperty]
|
[BindProperty]
|
||||||
public List<SongSuggestion> SongSuggestions { get; set; } = new List<SongSuggestion>();
|
public List<SongSuggestion> SongSuggestions { get; set; } = new List<SongSuggestion>();
|
||||||
|
|
||||||
public async Task OnGet()
|
public Task OnGet()
|
||||||
{
|
{
|
||||||
using var dci = DataContext.Instance;
|
using var dci = DataContext.Instance;
|
||||||
this.SongSuggestions = dci.SongSuggestions.OrderByDescending(s => s.Date)
|
SongSuggestions = dci.SongSuggestions.OrderByDescending(s => s.Date)
|
||||||
.Take(50)
|
.Take(50)
|
||||||
.Include(s => s.Song)
|
.Include(s => s.Song)
|
||||||
.Include(s => s.User)
|
.Include(s => s.User)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
public class SongPartialModel
|
public class SongPartialModel
|
||||||
{
|
{
|
||||||
public Song InnerSong { get; set; }
|
public required Song InnerSong { get; set; }
|
||||||
|
|
||||||
public string? Artist => InnerSong.Artist;
|
public string? Artist => InnerSong.Artist;
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ public class SongSubmissionModel : PageModel
|
|||||||
public bool IsPageReadonly { get; set; } = false;
|
public bool IsPageReadonly { get; set; } = false;
|
||||||
|
|
||||||
[BindProperty]
|
[BindProperty]
|
||||||
public SuggestionHelper SuggestionHelper { get; set; } = null;
|
public SuggestionHelper? SuggestionHelper { get; set; } = null;
|
||||||
|
|
||||||
[BindProperty]
|
[BindProperty]
|
||||||
public List<SongSuggestion> UserSongSubmissions { get; set; } = [];
|
public List<SongSuggestion> UserSongSubmissions { get; set; } = [];
|
||||||
@@ -128,10 +128,10 @@ public class SongSubmissionModel : PageModel
|
|||||||
Url = suggestion.Song.Url,
|
Url = suggestion.Song.Url,
|
||||||
Base64Image = imageBuilder.ToString(),
|
Base64Image = imageBuilder.ToString(),
|
||||||
};
|
};
|
||||||
await signalIntegration.SendMessageToGroupAsync($"**{displayName}**'s " + dateString + $" is: \n\n {suggestion.Song.Url}", previewData);
|
await signalIntegration.SendMessageToGroupAsync($"**{displayName}**'s " + dateString + $" is: \n\n{suggestion.Song.Url}", previewData);
|
||||||
if (suggestion.HasUsedSuggestion)
|
if (suggestion.HasUsedSuggestion)
|
||||||
{
|
{
|
||||||
await signalIntegration.SendMessageToGroupAsync($"The suggestion used for this pick was: \n\n **{suggestion.SuggestionHelper.Title}**'s \n\n {suggestion.SuggestionHelper.Description}");
|
await signalIntegration.SendMessageToGroupAsync($"The suggestion used for this pick was: \n\n**{suggestion.SuggestionHelper.Title}** \n\n{suggestion.SuggestionHelper.Description}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ public class UnclaimedPhoneNumbersModel : PageModel
|
|||||||
{
|
{
|
||||||
using (var dci = DataContext.Instance)
|
using (var dci = DataContext.Instance)
|
||||||
{
|
{
|
||||||
this.UnclaimedUsers = dci.Users == null ? new List<User>(): dci.Users.Where(u => string.IsNullOrEmpty(u.LdapUserName)).ToList();
|
this.UnclaimedUsers = dci.Users == null ? new List<User>() : dci.Users.Where(u => string.IsNullOrEmpty(u.LdapUserName)).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -152,7 +152,7 @@ pickOfTheDayTimer.OnOccurence += async (s, ea) =>
|
|||||||
await signalIntegration.SendMessageToGroupAsync($"Today's chosen person to share a song is: **{userName}**");
|
await signalIntegration.SendMessageToGroupAsync($"Today's chosen person to share a song is: **{userName}**");
|
||||||
await signalIntegration.SendMessageToUserAsync($"Congratulations, you have been chosen to share a song today!", signalId);
|
await signalIntegration.SendMessageToUserAsync($"Congratulations, you have been chosen to share a song today!", signalId);
|
||||||
await signalIntegration.SendMessageToUserAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*", signalId);
|
await signalIntegration.SendMessageToUserAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*", signalId);
|
||||||
await signalIntegration.SendMessageToUserAsync($"Please navigate to https://sord.disi.dev/SongSubmission/{newSongSuggestion.Id} to submit your choice!", luckyUser.SignalMemberId);
|
await signalIntegration.SendMessageToUserAsync($"Please navigate to https://sotd.disi.dev/SongSubmission/{newSongSuggestion.Id} to submit your choice!", luckyUser.SignalMemberId);
|
||||||
}
|
}
|
||||||
await dci.DisposeAsync();
|
await dci.DisposeAsync();
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
public class Base64UrlImageBuilder
|
public class Base64UrlImageBuilder
|
||||||
{
|
{
|
||||||
public string ContentType { set; get; }
|
public required string ContentType { set; get; }
|
||||||
|
|
||||||
public string Url
|
public string Url
|
||||||
{
|
{
|
||||||
@@ -9,11 +9,17 @@ public class Base64UrlImageBuilder
|
|||||||
var httpClient = new HttpClient();
|
var httpClient = new HttpClient();
|
||||||
var response = (httpClient.GetAsync(new Uri($"{value}"))).Result;
|
var response = (httpClient.GetAsync(new Uri($"{value}"))).Result;
|
||||||
var bytes = (response.Content.ReadAsByteArrayAsync()).Result;
|
var bytes = (response.Content.ReadAsByteArrayAsync()).Result;
|
||||||
FileContents = Convert.ToBase64String(bytes);
|
_fileContents = Convert.ToBase64String(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FileContents { get; set; }
|
private string _fileContents = string.Empty;
|
||||||
|
|
||||||
|
public string FileContents {
|
||||||
|
get {
|
||||||
|
return _fileContents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,7 @@ public interface ISongValidator
|
|||||||
{
|
{
|
||||||
Task<Song> ValidateAsync(Uri songUri);
|
Task<Song> ValidateAsync(Uri songUri);
|
||||||
|
|
||||||
bool CanValidateUri(Uri songUri);
|
Task<bool> CanValidateUriAsync(Uri songUri);
|
||||||
|
|
||||||
Task<bool> CanExtractSongMetadataAsync(Uri songUri);
|
Task<bool> CanExtractSongMetadataAsync(Uri songUri);
|
||||||
|
|
||||||
|
121
song_of_the_day/SongValidators/NavidromeValidator.cs
Normal file
121
song_of_the_day/SongValidators/NavidromeValidator.cs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
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; }
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SpotifyAPI.Web;
|
using SpotifyAPI.Web;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ public class SongResolver
|
|||||||
{
|
{
|
||||||
foreach (var validator in _songValidators)
|
foreach (var validator in _songValidators)
|
||||||
{
|
{
|
||||||
if (validator.CanValidateUri(songUri))
|
if (await validator.CanValidateUriAsync(songUri))
|
||||||
{
|
{
|
||||||
if (!await validator.CanExtractSongMetadataAsync(songUri))
|
if (!await validator.CanExtractSongMetadataAsync(songUri))
|
||||||
{
|
{
|
||||||
@@ -62,7 +63,7 @@ public class SongResolver
|
|||||||
{
|
{
|
||||||
foreach (var validator in _songValidators)
|
foreach (var validator in _songValidators)
|
||||||
{
|
{
|
||||||
if (validator.CanValidateUri(songUri))
|
if (validator.CanValidateUriAsync(songUri).Result)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ public abstract class SongValidatorBase : ISongValidator
|
|||||||
|
|
||||||
public abstract Task<bool> CanExtractSongMetadataAsync(Uri songUri);
|
public abstract Task<bool> CanExtractSongMetadataAsync(Uri songUri);
|
||||||
|
|
||||||
public abstract bool CanValidateUri(Uri songUri);
|
public abstract Task<bool> CanValidateUriAsync(Uri songUri);
|
||||||
|
|
||||||
public abstract SongProvider GetSongProvider();
|
public abstract SongProvider GetSongProvider();
|
||||||
|
|
||||||
|
@@ -8,11 +8,11 @@ public class SpotifyValidator : UriBasedSongValidatorBase
|
|||||||
public override string UriValidatorRegex => @"^(https?://)?open.spotify.com/track/([a-zA-Z0-9_-]{22})(\?si=[a-zA-Z0-9_-]+)?$";
|
public override string UriValidatorRegex => @"^(https?://)?open.spotify.com/track/([a-zA-Z0-9_-]{22})(\?si=[a-zA-Z0-9_-]+)?$";
|
||||||
|
|
||||||
public SpotifyValidator(ILogger _logger, SpotifyApiClient spotifyApiClient) : base(_logger, spotifyApiClient)
|
public SpotifyValidator(ILogger _logger, SpotifyApiClient spotifyApiClient) : base(_logger, spotifyApiClient)
|
||||||
{}
|
{ }
|
||||||
|
|
||||||
public override Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||||
{
|
{
|
||||||
return Task.FromResult(this.CanValidateUri(songUri));
|
return await this.CanValidateUriAsync(songUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SongProvider GetSongProvider()
|
public override SongProvider GetSongProvider()
|
||||||
|
@@ -5,7 +5,7 @@ public abstract class UriBasedSongValidatorBase : SongValidatorBase
|
|||||||
public abstract string UriValidatorRegex { get; }
|
public abstract string UriValidatorRegex { get; }
|
||||||
|
|
||||||
public UriBasedSongValidatorBase(ILogger logger, SpotifyApiClient spotifyApiClient) : base(logger, spotifyApiClient)
|
public UriBasedSongValidatorBase(ILogger logger, SpotifyApiClient spotifyApiClient) : base(logger, spotifyApiClient)
|
||||||
{}
|
{ }
|
||||||
|
|
||||||
public Match GetUriMatch(Uri songUri)
|
public Match GetUriMatch(Uri songUri)
|
||||||
{
|
{
|
||||||
@@ -13,8 +13,12 @@ public abstract class UriBasedSongValidatorBase : SongValidatorBase
|
|||||||
return regexp.Match(songUri.ToString());
|
return regexp.Match(songUri.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanValidateUri(Uri songUri)
|
public override async Task<bool> CanValidateUriAsync(Uri songUri)
|
||||||
|
{
|
||||||
|
var result = await Task.Run(() =>
|
||||||
{
|
{
|
||||||
return GetUriMatch(songUri).Success;
|
return GetUriMatch(songUri).Success;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -18,9 +18,9 @@ public class YoutubeMusicValidator : UriBasedSongValidatorBase
|
|||||||
return SongProvider.YoutubeMusic;
|
return SongProvider.YoutubeMusic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||||
{
|
{
|
||||||
return Task.FromResult(this.CanValidateUri(songUri));
|
return await this.CanValidateUriAsync(songUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<Song> ValidateAsync(Uri songUri)
|
public override async Task<Song> ValidateAsync(Uri songUri)
|
||||||
|
@@ -16,7 +16,7 @@ public class YoutubeValidator : UriBasedSongValidatorBase
|
|||||||
|
|
||||||
public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||||
{
|
{
|
||||||
return this.CanValidateUri(songUri);
|
return await this.CanValidateUriAsync(songUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SongProvider GetSongProvider()
|
public override SongProvider GetSongProvider()
|
||||||
|
@@ -1 +1 @@
|
|||||||
0.4.0
|
0.5.0
|
||||||
|
Reference in New Issue
Block a user