Updated NuGet packages, fixed LastFM integration, formatting and performance tweaks, bumped to v1.1.4

This commit is contained in:
Steven Hildreth 2022-03-08 21:23:46 -06:00
parent 7fc99893e3
commit 5768316587
24 changed files with 257 additions and 196 deletions

View file

@ -1,9 +1,6 @@
using System.Threading.Tasks;
using Roadie.Library.Models.Users;
namespace Roadie.Library.Scrobble
namespace Roadie.Library.Scrobble
{
public interface ILastFMScrobbler : IScrobblerIntegration
{
}
}
}

View file

@ -1,9 +1,6 @@
using System.Threading.Tasks;
using Roadie.Library.Models.Users;
namespace Roadie.Library.Scrobble
namespace Roadie.Library.Scrobble
{
public interface IRoadieScrobbler : IScrobblerIntegration
{
}
}
}

View file

@ -15,4 +15,4 @@ namespace Roadie.Library.Scrobble
/// </summary>
Task<OperationResult<bool>> Scrobble(User user, ScrobbleInfo scrobble);
}
}
}

View file

@ -5,8 +5,10 @@ namespace Roadie.Library.Scrobble
{
public interface IScrobblerIntegration
{
int SortOrder { get; }
Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble);
Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble);
}
}
}

View file

@ -6,7 +6,6 @@ using Roadie.Library.MetaData.LastFm;
using Roadie.Library.Models.Users;
using Roadie.Library.Utility;
using System.Threading.Tasks;
using data = Roadie.Library.Data;
namespace Roadie.Library.Scrobble
{
@ -16,10 +15,17 @@ namespace Roadie.Library.Scrobble
/// </summary>
public class LastFMScrobbler : ScrobblerIntegrationBase, ILastFMScrobbler
{
public override int SortOrder => 0;
private ILastFmHelper LastFmHelper { get; }
public LastFMScrobbler(IRoadieSettings configuration, ILogger<LastFMScrobbler> logger, IRoadieDbContext dbContext,
ICacheManager cacheManager, ILastFmHelper lastFmHelper, IHttpContext httpContext)
public LastFMScrobbler(
IRoadieSettings configuration,
ILogger<LastFMScrobbler> logger,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
ILastFmHelper lastFmHelper,
IHttpContext httpContext)
: base(configuration, logger, dbContext, cacheManager, httpContext)
{
LastFmHelper = lastFmHelper;
@ -43,6 +49,5 @@ namespace Roadie.Library.Scrobble
/// </remark>
/// </summary>
public override async Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.Scrobble(roadieUser, scrobble).ConfigureAwait(false);
}
}
}

View file

@ -7,7 +7,6 @@ using Roadie.Library.Models.Users;
using Roadie.Library.Utility;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using data = Roadie.Library.Data;
@ -15,19 +14,27 @@ namespace Roadie.Library.Scrobble
{
public class RoadieScrobbler : ScrobblerIntegrationBase, IRoadieScrobbler
{
public override int SortOrder => 99; // Ensure that the Roadie Scrobbler is last due to heavy database and cache manipulation
public RoadieScrobbler(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext, ICacheManager cacheManager)
public RoadieScrobbler(
IRoadieSettings configuration,
ILogger logger,
IRoadieDbContext dbContext,
ICacheManager cacheManager)
: base(configuration, logger, dbContext, cacheManager, null)
{
}
public RoadieScrobbler(IRoadieSettings configuration, ILogger<RoadieScrobbler> logger, IRoadieDbContext dbContext,
ICacheManager cacheManager, IHttpContext httpContext)
public RoadieScrobbler(
IRoadieSettings configuration,
ILogger<RoadieScrobbler> logger,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
IHttpContext httpContext)
: base(configuration, logger, dbContext, cacheManager, httpContext)
{
}
/// <summary>
/// For Roadie we only add a user play on the full scrobble event, otherwise we get double track play numbers.
/// </summary>
@ -48,7 +55,7 @@ namespace Roadie.Library.Scrobble
try
{
// If a user and If less than half of duration then do nothing
if (roadieUser != null &&
if (roadieUser != null &&
scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds < scrobble.TrackDuration.TotalSeconds / 2)
{
Logger.LogTrace("Skipping Scrobble, Playback did not exceed minimum elapsed time");
@ -81,22 +88,25 @@ namespace Roadie.Library.Scrobble
{
if (roadieUser != null)
{
var user = await DbContext.Users.FirstOrDefaultAsync(x => x.RoadieId == roadieUser.UserId).ConfigureAwait(false);
userTrack = await DbContext.UserTracks.FirstOrDefaultAsync(x => x.UserId == user.Id && x.TrackId == track.Id).ConfigureAwait(false);
if (userTrack == null)
// User should be cached if not then skip as probably a bad user
var user = CacheManager.Get<Identity.User>(Identity.User.CacheUrn(roadieUser.UserId));
if (user != null)
{
userTrack = new data.UserTrack(now)
userTrack = await DbContext.UserTracks.FirstOrDefaultAsync(x => x.UserId == user.Id && x.TrackId == track.Id).ConfigureAwait(false);
if (userTrack == null)
{
UserId = user.Id,
TrackId = track.Id
};
await DbContext.UserTracks.AddAsync(userTrack).ConfigureAwait(false);
userTrack = new data.UserTrack(now)
{
UserId = user.Id,
TrackId = track.Id
};
await DbContext.UserTracks.AddAsync(userTrack).ConfigureAwait(false);
}
userTrack.LastPlayed = now;
userTrack.PlayedCount = (userTrack.PlayedCount ?? 0) + 1;
CacheManager.ClearRegion(user.CacheRegion);
}
userTrack.LastPlayed = now;
userTrack.PlayedCount = (userTrack.PlayedCount ?? 0) + 1;
CacheManager.ClearRegion(user.CacheRegion);
}
track.PlayedCount = (track.PlayedCount ?? 0) + 1;
@ -139,7 +149,7 @@ namespace Roadie.Library.Scrobble
}
catch (Exception ex)
{
Logger.LogError(ex,$"Error in Scrobble, Creating UserTrack: User `{roadieUser}` TrackId [{track.Id}");
Logger.LogError(ex, $"Error in Scrobble, Creating UserTrack: User `{roadieUser}` TrackId [{track.Id}");
}
sw.Stop();
@ -159,4 +169,4 @@ namespace Roadie.Library.Scrobble
return new OperationResult<bool>();
}
}
}
}

View file

@ -31,7 +31,12 @@ namespace Roadie.Library.Scrobble
private IEnumerable<IScrobblerIntegration> Scrobblers { get; }
public ScrobbleHandler(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext, ICacheManager cacheManager, RoadieScrobbler roadieScrobbler)
public ScrobbleHandler(
IRoadieSettings configuration,
ILogger logger,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
IRoadieScrobbler roadieScrobbler)
{
Logger = logger;
Configuration = configuration;
@ -43,9 +48,16 @@ namespace Roadie.Library.Scrobble
Scrobblers = scrobblers;
}
public ScrobbleHandler(IRoadieSettings configuration, ILogger<ScrobbleHandler> logger, IRoadieDbContext dbContext,
ICacheManager cacheManager, IHttpEncoder httpEncoder, IHttpContext httpContext,
ILastFmHelper lastFmHelper, IRoadieScrobbler roadieScrobbler, ILastFMScrobbler lastFMScrobbler)
public ScrobbleHandler(
IRoadieSettings configuration,
ILogger<ScrobbleHandler> logger,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
IHttpEncoder httpEncoder,
IHttpContext httpContext,
ILastFmHelper lastFmHelper,
IRoadieScrobbler roadieScrobbler,
ILastFMScrobbler lastFMScrobbler)
{
Logger = logger;
Configuration = configuration;
@ -110,7 +122,7 @@ namespace Roadie.Library.Scrobble
public async Task<OperationResult<bool>> Scrobble(User user, ScrobbleInfo scrobble)
{
var s = await GetScrobbleInfoDetailsAsync(scrobble).ConfigureAwait(false);
foreach (var scrobbler in Scrobblers)
foreach (var scrobbler in Scrobblers.OrderBy(x => x.SortOrder))
{
await scrobbler.Scrobble(user, s).ConfigureAwait(false);
}
@ -121,4 +133,4 @@ namespace Roadie.Library.Scrobble
};
}
}
}
}

View file

@ -6,14 +6,21 @@ namespace Roadie.Library.Scrobble
public class ScrobbleInfo
{
public string ArtistName { get; set; }
public TimeSpan ElapsedTimeOfTrackPlayed => DateTime.UtcNow.Subtract(TimePlayed);
public bool IsRandomizedScrobble { get; set; }
public string ReleaseTitle { get; set; }
public DateTime TimePlayed { get; set; }
public TimeSpan TrackDuration { get; set; }
public Guid TrackId { get; set; }
public string TrackNumber { get; set; }
public string TrackTitle { get; set; }
public override string ToString()
@ -21,4 +28,4 @@ namespace Roadie.Library.Scrobble
return $"Artist: [{ArtistName}], Release: [{ReleaseTitle}], Track# [{TrackNumber}], Track [{TrackTitle}]";
}
}
}
}

View file

@ -5,7 +5,6 @@ using Roadie.Library.Data.Context;
using Roadie.Library.Models.Users;
using Roadie.Library.Utility;
using System.Threading.Tasks;
using data = Roadie.Library.Data;
namespace Roadie.Library.Scrobble
{
@ -21,8 +20,14 @@ namespace Roadie.Library.Scrobble
protected ILogger Logger { get; }
public ScrobblerIntegrationBase(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext,
ICacheManager cacheManager, IHttpContext httpContext)
public abstract int SortOrder { get; }
public ScrobblerIntegrationBase(
IRoadieSettings configuration,
ILogger logger,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
IHttpContext httpContext)
{
Logger = logger;
Configuration = configuration;
@ -35,4 +40,4 @@ namespace Roadie.Library.Scrobble
public abstract Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble);
}
}
}

View file

@ -1,5 +1,6 @@
using IF.Lastfm.Core.Api;
using IF.Lastfm.Core.Objects;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
@ -14,14 +15,12 @@ using Roadie.Library.SearchEngines.MetaData.LastFm;
using Roadie.Library.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using static System.Net.Mime.MediaTypeNames;
@ -46,6 +45,14 @@ namespace Roadie.Library.MetaData.LastFm
private IHttpEncoder HttpEncoder { get; }
private LastAuth LastFmTrackApiAuth { get; }
private IF.Lastfm.Core.Api.TrackApi LastFmTrackApi { get; }
private IF.Lastfm.Core.Scrobblers.MemoryScrobbler LastFmScrobbler { get; }
public int SortOrder => 0;
public LastFmHelper(
IRoadieSettings configuration,
ICacheManager cacheManager,
@ -55,10 +62,13 @@ namespace Roadie.Library.MetaData.LastFm
IHttpClientFactory httpClientFactory)
: base(configuration, cacheManager, logger, httpClientFactory)
{
_apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "LastFMApiKey") ??
new ApiKey();
_apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "LastFMApiKey") ?? new ApiKey();
DbContext = dbContext;
HttpEncoder = httpEncoder;
LastFmTrackApiAuth = new LastAuth(ApiKey.Key, ApiKey.KeySecret);
LastFmTrackApi = new IF.Lastfm.Core.Api.TrackApi(LastFmTrackApiAuth, HttpClient);
LastFmScrobbler = new IF.Lastfm.Core.Scrobblers.MemoryScrobbler(LastFmTrackApiAuth, HttpClient);
}
public static void CheckLastFmStatus(XPathNavigator navigator)
@ -89,70 +99,70 @@ namespace Roadie.Library.MetaData.LastFm
public async Task<OperationResult<string>> GetSessionKeyForUserToken(string token)
{
var parameters = new Dictionary<string, string>
var result = false;
string sessionKey = null;
try
{
{"token", token}
};
string responseXML = null;
var request = new HttpRequestMessage(HttpMethod.Get, BuildUrl("auth.getSession", parameters));
request.Headers.Add("User-Agent", WebHelper.UserAgent);
var response = await HttpClient.SendAsync(request).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
responseXML = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var parameters = new Dictionary<string, string>
{
{"token", token}
};
string responseXML = null;
var request = new HttpRequestMessage(HttpMethod.Get, BuildUrl("auth.getSession", parameters));
request.Headers.Add("User-Agent", WebHelper.UserAgent);
var response = await HttpClient.SendAsync(request).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
responseXML = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
var doc = new XmlDocument();
doc.LoadXml(responseXML);
sessionKey = doc.GetElementsByTagName("key")[0].InnerText;
result = true;
}
catch (Exception ex)
{
Logger.LogError(ex, $"Error in LastFmHelper GetSessionKeyForUserToken: token [{token}]");
}
var doc = new XmlDocument();
doc.LoadXml(responseXML);
var sessionKey = doc.GetElementsByTagName("key")[0].InnerText;
return new OperationResult<string>
{
Data = sessionKey,
IsSuccess = true
IsSuccess = result
};
}
public async Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble)
{
var result = false;
string msg = null;
try
{
if (!IsEnabled) return new OperationResult<bool>("LastFM Integation Disabled");
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId);
if (user == null || string.IsNullOrEmpty(user.LastFMSessionKey))
if (!IsEnabled)
{
return new OperationResult<bool>("LastFM Integation Disabled");
}
// User should be cached if not then skip as probably a bad user
var user = CacheManager.Get<Identity.User>(Identity.User.CacheUrn(roadieUser.UserId));
if (string.IsNullOrEmpty(user?.LastFMSessionKey))
{
return new OperationResult<bool>("User does not have LastFM Integration setup");
}
var method = "track.updateNowPlaying";
var parameters = new RequestParameters
LastFmTrackApiAuth.LoadSession(new LastUserSession { Token = user.LastFMSessionKey });
var lastFmScrobble = new IF.Lastfm.Core.Objects.Scrobble(
scrobble.ArtistName,
scrobble.ReleaseTitle,
scrobble.TrackTitle,
DateTimeOffset.UtcNow)
{
{"artist", scrobble.ArtistName},
{"track", scrobble.TrackTitle},
{"album", scrobble.ReleaseTitle},
{"duration", ((int) scrobble.TrackDuration.TotalSeconds).ToString()}
Duration = scrobble.TrackDuration
};
var url = "http://ws.audioscrobbler.com/2.0/";
var signature = GenerateMethodSignature(method, parameters, user.LastFMSessionKey);
parameters.Add("api_sig", signature);
ServicePointManager.Expect100Continue = false;
XPathNavigator xp = null;
var parametersJson = new StringContent(CacheManager.CacheSerializer.Serialize(parameters), System.Text.Encoding.UTF8, Application.Json);
using (var httpResponseMessage = await HttpClient.PostAsync(url, parametersJson).ConfigureAwait(false))
{
if (httpResponseMessage.IsSuccessStatusCode)
{
xp = await GetResponseAsXml(httpResponseMessage).ConfigureAwait(false);
result = true;
}
}
Logger.LogTrace($"LastFmHelper: Success [{ result }] RoadieUser `{roadieUser}` NowPlaying `{scrobble}` LastFmResult [{xp.InnerXml}]");
var nowPlayingResponse = await LastFmTrackApi.UpdateNowPlayingAsync(lastFmScrobble).ConfigureAwait(false);
result = nowPlayingResponse?.Success ?? false;
Logger.LogTrace($"LastFmHelper: NowPlaying : Success [{ result }] RoadieUser `{roadieUser}` NowPlaying `{scrobble}` LastFMResponse `{ CacheManager.CacheSerializer.Serialize(nowPlayingResponse) }`");
}
catch (Exception ex)
{
Logger.LogError(ex,
$"Error in LastFmHelper NowPlaying: RoadieUser `{roadieUser}` Scrobble `{scrobble}`");
Logger.LogError(ex, $"Error in LastFmHelper NowPlaying: RoadieUser `{roadieUser}` Scrobble `{scrobble}` Http Msg [{ msg }]");
}
return new OperationResult<bool>
{
@ -188,7 +198,7 @@ namespace Roadie.Library.MetaData.LastFm
{
result.Tags = lastFmArtist.Tags.Select(x => x.Name).ToList();
}
if(lastFmArtist.MainImage != null)
if (lastFmArtist.MainImage != null)
{
result.ImageUrls = new string[1] { lastFmArtist.MainImage.Largest?.AbsoluteUri };
}
@ -219,7 +229,7 @@ namespace Roadie.Library.MetaData.LastFm
var data = await CacheManager.GetAsync<ReleaseSearchResult>(cacheKey, async () =>
{
var auth = new LastAuth(ApiKey.Key, ApiKey.KeySecret);
var releaseApi =new IF.Lastfm.Core.Api.AlbumApi(auth, HttpClient);
var releaseApi = new IF.Lastfm.Core.Api.AlbumApi(auth, HttpClient);
var sendResponse = await releaseApi.GetInfoAsync(artistName, query).ConfigureAwait(false);
if (!sendResponse.Success)
{
@ -237,9 +247,8 @@ namespace Roadie.Library.MetaData.LastFm
MusicBrainzId = lastFmAlbum.Mbid
};
if(lastFmAlbum.Images != null)
if (lastFmAlbum.Images != null)
{
result.ImageUrls = new string[1] { lastFmAlbum.Images.Largest.AbsoluteUri };
}
@ -250,7 +259,7 @@ namespace Roadie.Library.MetaData.LastFm
else
{
var tagResult = await releaseApi.GetTopTagsAsync(artistName, query).ConfigureAwait(false);
if(tagResult != null)
if (tagResult != null)
{
result.Tags = FilterTags(tagResult.Select(x => x.Name).Distinct().Take(25).ToArray());
}
@ -313,7 +322,7 @@ namespace Roadie.Library.MetaData.LastFm
private List<string> FilterTags(IEnumerable<string> tags)
{
if(tags.Any() == false)
if (tags.Any() == false)
{
return null;
}
@ -326,51 +335,42 @@ namespace Roadie.Library.MetaData.LastFm
var result = false;
try
{
if (!IsEnabled) return new OperationResult<bool>("LastFM Integation Disabled");
if (!IsEnabled)
{
return new OperationResult<bool>("LastFM Integation Disabled");
}
// LastFM Rules on scrobbling:
// * The track must be longer than 30 seconds.
// * And the track has been played for at least half its duration, or for 4 minutes(whichever occurs earlier.)
if (scrobble.TrackDuration.TotalSeconds < 30)
return new OperationResult<bool>(
"Track duration or elapsed time does not qualify for LastFM Scrobbling");
{
return new OperationResult<bool>("Track duration or elapsed time does not qualify for LastFM Scrobbling");
}
// If less than half of duration then create a NowPlaying
if (scrobble.ElapsedTimeOfTrackPlayed.TotalMinutes < 4 ||
scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds < scrobble.TrackDuration.TotalSeconds / 2)
{
return await NowPlaying(roadieUser, scrobble).ConfigureAwait(false);
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId);
if (user == null || string.IsNullOrEmpty(user.LastFMSessionKey))
}
// User should be cached if not then skip as probably a bad user
var user = CacheManager.Get<Identity.User>(Identity.User.CacheUrn(roadieUser.UserId));
if (string.IsNullOrEmpty(user?.LastFMSessionKey))
{
return new OperationResult<bool>("User does not have LastFM Integration setup");
}
var parameters = new RequestParameters
LastFmTrackApiAuth.LoadSession(new LastUserSession { Token = user.LastFMSessionKey });
var lastFmScrobble = new IF.Lastfm.Core.Objects.Scrobble(
scrobble.ArtistName,
scrobble.ReleaseTitle,
scrobble.TrackTitle,
scrobble.TimePlayed)
{
{"artist", scrobble.ArtistName},
{"track", scrobble.TrackTitle},
{"timestamp", scrobble.TimePlayed.ToUnixTime().ToString()},
{"album", scrobble.ReleaseTitle},
{"chosenByUser", scrobble.IsRandomizedScrobble ? "1" : "0"},
{"duration", ((int) scrobble.TrackDuration.TotalSeconds).ToString()}
Duration = scrobble.TrackDuration,
ChosenByUser = !scrobble.IsRandomizedScrobble
};
var method = "track.scrobble";
var url = "http://ws.audioscrobbler.com/2.0/";
var signature = GenerateMethodSignature(method, parameters, user.LastFMSessionKey);
parameters.Add("api_sig", signature);
ServicePointManager.Expect100Continue = false;
XPathNavigator xp = null;
var parametersJson = new StringContent(CacheManager.CacheSerializer.Serialize(parameters), System.Text.Encoding.UTF8, Application.Json);
using (var httpResponseMessage = await HttpClient.PostAsync(url, parametersJson).ConfigureAwait(false))
{
if (httpResponseMessage.IsSuccessStatusCode)
{
xp = await GetResponseAsXml(httpResponseMessage).ConfigureAwait(false);
result = true;
}
}
Logger.LogTrace($"LastFmHelper: RoadieUser `{roadieUser}` Scrobble `{scrobble}` LastFmResult [{xp.InnerXml}]");
var scrobbleResponse = await LastFmScrobbler.ScrobbleAsync(lastFmScrobble);
result = scrobbleResponse?.Success ?? false;
Logger.LogTrace($"LastFmHelper: Scrobble : Success [{ result }] RoadieUser `{roadieUser}` Scrobble `{scrobble}`");
result = true;
}
catch (Exception ex)
@ -566,10 +566,22 @@ namespace Roadie.Library.MetaData.LastFm
private string GenerateMethodSignature(string method, IDictionary<string, string> parameters = null, string sk = null)
{
if (parameters == null) parameters = new Dictionary<string, string>();
if (!parameters.ContainsKey("method")) parameters.Add("method", method);
if (!parameters.ContainsKey("api_key")) parameters.Add("api_key", _apiKey.Key);
if (!string.IsNullOrEmpty(sk) && !parameters.ContainsKey("sk")) parameters.Add("sk", sk);
if (parameters == null)
{
parameters = new Dictionary<string, string>();
}
if (!parameters.ContainsKey("method"))
{
parameters.Add("method", method);
}
if (!parameters.ContainsKey("api_key"))
{
parameters.Add("api_key", _apiKey.Key);
}
if (!string.IsNullOrEmpty(sk) && !parameters.ContainsKey("sk"))
{
parameters.Add("sk", sk);
}
var builder = new StringBuilder();
foreach (var kv in parameters.OrderBy(kv => kv.Key, StringComparer.Ordinal))
{

View file

@ -70,7 +70,7 @@ namespace Roadie.Api.Services
var rowCount = result.Count();
var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
foreach (var row in rows)
{

View file

@ -10,6 +10,8 @@ namespace Roadie.Api.Services
{
public interface ITrackService
{
Task<Roadie.Library.Identity.User> GetUserByUserNameAsync(string username);
Task<OperationResult<Track>> ByIdAsyncAsync(User roadieUser, Guid id, IEnumerable<string> includes);
Task<PagedResult<TrackList>> ListAsync(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null);

View file

@ -564,7 +564,7 @@ namespace Roadie.Api.Services
{
try
{
var user = await GetUser(id).ConfigureAwait(false);
var user = await GetUserAsync(id).ConfigureAwait(false);
if (user == null)
{
return new FileOperationResult<IImage>(true, $"User Not Found [{id}]");

View file

@ -28,16 +28,26 @@ namespace Roadie.Api.Services
protected IScrobbleHandler ScrobblerHandler { get; }
public PlayActivityService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager,
ILogger logger, ScrobbleHandler scrobbleHandler)
public PlayActivityService(
IRoadieSettings configuration,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
ILogger logger,
ScrobbleHandler scrobbleHandler)
: base(configuration, null, dbContext, cacheManager, logger, null)
{
ScrobblerHandler = scrobbleHandler;
}
public PlayActivityService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext,
IRoadieDbContext dbContext, ICacheManager cacheManager, ILogger<PlayActivityService> logger,
IScrobbleHandler scrobbleHandler, IHubContext<PlayActivityHub> playActivityHub)
public PlayActivityService(
IRoadieSettings configuration,
IHttpEncoder httpEncoder,
IHttpContext httpContext,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
ILogger<PlayActivityService> logger,
IScrobbleHandler scrobbleHandler,
IHubContext<PlayActivityHub> playActivityHub)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
{
PlayActivityHub = playActivityHub;
@ -195,7 +205,7 @@ namespace Roadie.Api.Services
? request.OrderValue(new Dictionary<string, string> { { "PlayedDateDateTime", "DESC" } })
: request.OrderValue();
var rowCount = result.Count();
var rows = await result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArrayAsync().ConfigureAwait(false);
var rows = await result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArrayAsync().ConfigureAwait(false);
sw.Stop();
return new Library.Models.Pagination.PagedResult<PlayActivityList>
{
@ -237,4 +247,4 @@ namespace Roadie.Api.Services
return scrobbleResult;
}
}
}
}

View file

@ -336,7 +336,7 @@ namespace Roadie.Api.Services
{
if (result?.Data?.Tracks != null)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
foreach (var track in result.Data.Tracks)
{
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, HttpContext.BaseUrl, track.Track.Id);

View file

@ -487,7 +487,7 @@ namespace Roadie.Api.Services
if (result.Data.Medias != null)
{
tsw.Restart();
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
tsw.Stop();
timings.Add("getUser", tsw.ElapsedMilliseconds);

View file

@ -195,7 +195,7 @@ namespace Roadie.Api.Services
}, data.Track.CacheRegionUrn(id));
}
protected async Task<User> GetUser(string username)
public async Task<User> GetUserByUserNameAsync(string username)
{
if (string.IsNullOrEmpty(username))
{
@ -205,10 +205,10 @@ namespace Roadie.Api.Services
{
return DbContext.Users.FirstOrDefaultAsync(x => x.UserName == username);
}, null).ConfigureAwait(false);
return await GetUser(userByUsername?.RoadieId).ConfigureAwait(false);
return await GetUserAsync(userByUsername?.RoadieId).ConfigureAwait(false);
}
protected Task<User> GetUser(Guid? id)
public Task<User> GetUserAsync(Guid? id)
{
return CacheManager.GetAsync(User.CacheUrn(id.Value), async () =>
{

View file

@ -110,13 +110,12 @@ namespace Roadie.Api.Services
{
if (request == null || string.IsNullOrEmpty(request?.u))
{
return new subsonic.SubsonicOperationResult<subsonic.SubsonicAuthenticateResponse>(
subsonic.ErrorCodes.WrongUsernameOrPassword, "Unknown Username");
return new subsonic.SubsonicOperationResult<subsonic.SubsonicAuthenticateResponse>(subsonic.ErrorCodes.WrongUsernameOrPassword, "Unknown Username");
}
try
{
var user = DbContext.Users.FirstOrDefault(x => x.UserName == request.u);
var user = await GetUserByUserNameAsync(request.u).ConfigureAwait(false);
if (user == null)
{
Logger.LogTrace($"Unknown User [{request.u}]");
@ -144,8 +143,7 @@ namespace Roadie.Api.Services
{
try
{
var hashCheck =
UserManger.PasswordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
var hashCheck = UserManger.PasswordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
if (hashCheck == PasswordVerificationResult.Failed)
{
user = null;
@ -235,7 +233,7 @@ namespace Roadie.Api.Services
await DbContext.SaveChangesAsync().ConfigureAwait(false);
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion);
Logger.LogTrace(
@ -371,7 +369,7 @@ namespace Roadie.Api.Services
DbContext.Bookmarks.Remove(userBookmark);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion);
Logger.LogTrace($"Subsonic: Deleted Bookmark `{userBookmark}` for User `{roadieUser}`");
@ -847,7 +845,7 @@ namespace Roadie.Api.Services
}
else if (!string.IsNullOrEmpty(request.u))
{
var user = await GetUser(request.u).ConfigureAwait(false);
var user = await GetUserByUserNameAsync(request.u).ConfigureAwait(false);
if (user == null)
{
return new subsonic.SubsonicFileOperationResult<Library.Models.Image>(
@ -990,7 +988,7 @@ namespace Roadie.Api.Services
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetMusicDirectoryAsync(subsonic.Request request, Library.Models.Users.User roadieUser)
{
var directory = new subsonic.Directory();
var user = await GetUser(roadieUser?.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser?.UserId).ConfigureAwait(false);
// Request to get albums for an Artist
if (request.ArtistId != null)
@ -1232,7 +1230,7 @@ namespace Roadie.Api.Services
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetPlayQueueAsync(subsonic.Request request,
Library.Models.Users.User roadieUser)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
subsonic.PlayQueue playQue = null;
@ -1546,7 +1544,7 @@ namespace Roadie.Api.Services
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetUserAsync(subsonic.Request request,
string username)
{
var user = await GetUser(username).ConfigureAwait(false);
var user = await GetUserByUserNameAsync(username).ConfigureAwait(false);
if (user == null)
{
return new subsonic.SubsonicOperationResult<subsonic.Response>(
@ -1612,7 +1610,7 @@ namespace Roadie.Api.Services
Library.Models.Users.User roadieUser, string current, long? position)
{
// Remove any existing Que for User
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user.UserQues?.Any() == true)
{
DbContext.UserQues.RemoveRange(user.UserQues);
@ -1744,7 +1742,7 @@ namespace Roadie.Api.Services
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> SetRatingAsync(subsonic.Request request,
Library.Models.Users.User roadieUser, short rating)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new subsonic.SubsonicOperationResult<subsonic.Response>(
@ -1800,7 +1798,7 @@ namespace Roadie.Api.Services
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> ToggleStarAsync(subsonic.Request request,
Library.Models.Users.User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new subsonic.SubsonicOperationResult<subsonic.Response>(
@ -1954,7 +1952,7 @@ namespace Roadie.Api.Services
await DbContext.SaveChangesAsync().ConfigureAwait(false);
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion);
return new subsonic.SubsonicOperationResult<subsonic.Response>

View file

@ -229,7 +229,7 @@ namespace Roadie.Api.Services
if (result?.Data != null && roadieUser != null)
{
tsw.Restart();
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
tsw.Stop();
timings.Add("getUser", tsw.ElapsedMilliseconds);
@ -620,7 +620,7 @@ namespace Roadie.Api.Services
resultQuery = resultQuery.Where(x => x.ti.Tags != null && x.ti.Tags.Contains(tagValue));
}
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
var result = resultQuery.Select(x =>
new TrackList
{

View file

@ -128,7 +128,7 @@ namespace Roadie.Api.Services
var tsw = new Stopwatch();
tsw.Restart();
var user = await GetUser(id).ConfigureAwait(false);
var user = await GetUserAsync(id).ConfigureAwait(false);
tsw.Stop();
timings.Add("getUser", tsw.ElapsedMilliseconds);
@ -257,7 +257,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> DeleteAllBookmarksAsync(User roadieUser)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -356,7 +356,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetArtistBookmarkAsync(Guid artistId, User roadieUser, bool isBookmarked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -380,7 +380,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetArtistDislikedAsync(Guid artistId, User roadieUser, bool isDisliked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -390,7 +390,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetArtistFavoriteAsync(Guid artistId, User roadieUser, bool isFavorite)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -400,7 +400,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<short>> SetArtistRatingAsync(Guid artistId, User roadieUser, short rating)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<short>(true, $"Invalid User [{roadieUser}]");
@ -410,7 +410,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetCollectionBookmarkAsync(Guid collectionId, User roadieUser, bool isBookmarked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -433,7 +433,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetLabelBookmarkAsync(Guid labelId, User roadieUser, bool isBookmarked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -457,7 +457,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetPlaylistBookmarkAsync(Guid playlistId, User roadieUser,
bool isBookmarked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -480,7 +480,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetReleaseBookmarkAsync(Guid releaseid, User roadieUser, bool isBookmarked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -503,7 +503,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetReleaseDislikedAsync(Guid releaseId, User roadieUser, bool isDisliked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -513,7 +513,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetReleaseFavoriteAsync(Guid releaseId, User roadieUser, bool isFavorite)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -523,7 +523,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<short>> SetReleaseRatingAsync(Guid releaseId, User roadieUser, short rating)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<short>(true, $"Invalid User [{roadieUser}]");
@ -533,7 +533,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetTrackBookmarkAsync(Guid trackId, User roadieUser, bool isBookmarked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -556,7 +556,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetTrackDislikedAsync(Guid trackId, User roadieUser, bool isDisliked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -566,7 +566,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<bool>> SetTrackFavoriteAsync(Guid trackId, User roadieUser, bool isFavorite)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
@ -578,9 +578,9 @@ namespace Roadie.Api.Services
{
var timings = new Dictionary<string, long>();
var sw = Stopwatch.StartNew();
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
var user = await GetUserAsync(roadieUser.UserId).ConfigureAwait(false);
sw.Stop();
timings.Add(nameof(GetUser), sw.ElapsedMilliseconds);
timings.Add(nameof(GetUserByUserNameAsync), sw.ElapsedMilliseconds);
if (user == null)
{

View file

@ -155,7 +155,6 @@ namespace Roadie.Api.Controllers
{
return null;
}
var result = user.Adapt<models.User>();
result.IsAdmin = User.IsInRole("Admin");
result.IsEditor = User.IsInRole("Editor") || result.IsAdmin;

View file

@ -88,7 +88,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)]
public async Task<IActionResult> Scrobble(Guid id, string startedPlaying, bool isRandom)
{
var result = await PlayActivityService.ScrobbleAsync(await CurrentUserModel().ConfigureAwait(false), new ScrobbleInfo
var user = await CurrentUserModel().ConfigureAwait(false);
// Put user in cache (if not already) for future track related API operations.
await TrackService.GetUserByUserNameAsync(user.UserName).ConfigureAwait(false);
var result = await PlayActivityService.ScrobbleAsync(user, new ScrobbleInfo
{
TrackId = id,
TimePlayed = SafeParser.ToDateTime(startedPlaying) ?? DateTime.UtcNow,
@ -119,7 +124,8 @@ namespace Roadie.Api.Controllers
{
return StatusCode((int)HttpStatusCode.Unauthorized);
}
// Put user in cache (if not already) for future track related API operations.
await System.Threading.Tasks.Task.Run(async () => await TrackService.GetUserByUserNameAsync(user.UserName).ConfigureAwait(false));
if (!ServiceBase.ConfirmTrackPlayToken(user, id, trackPlayToken))
{
return StatusCode((int)HttpStatusCode.Unauthorized);

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<VersionPrefix>1.1.3</VersionPrefix>
<VersionPrefix>1.1.4</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
@ -9,7 +9,7 @@
<TargetFramework>net6.0</TargetFramework>
<Platforms>AnyCPU;x64</Platforms>
<UserSecretsId>3f484b72-52aa-42ae-938d-4635f9511319</UserSecretsId>
<Version>1.1.3</Version>
<Version>1.1.4</Version>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>

View file

@ -265,10 +265,10 @@ namespace Roadie.Api
services.AddSingleton<IWikipediaHelper, WikipediaHelper>();
services.AddSingleton<IFileNameHelper, FileNameHelper>();
services.AddSingleton<IID3TagsHelper, ID3TagsHelper>();
services.AddSingleton<ILastFmHelper, LastFmHelper>();
services.AddSingleton<IRoadieScrobbler, RoadieScrobbler>();
services.AddSingleton<ILastFMScrobbler, LastFMScrobbler>();
services.AddScoped<ILastFmHelper, LastFmHelper>();
services.AddScoped<IRoadieScrobbler, RoadieScrobbler>();
services.AddScoped<ILastFMScrobbler, LastFMScrobbler>();
services.AddScoped<IStatisticsService, StatisticsService>();
services.AddScoped<ICollectionService, CollectionService>();
services.AddScoped<IPlaylistService, PlaylistService>();
@ -361,7 +361,6 @@ namespace Roadie.Api
}
return new HttpContext(factory.GetService<IRoadieSettings>(), new UrlHelper(actionContext));
});
}
private static string _roadieApiVersion = null;