diff --git a/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs b/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs
index 3d58659..378e136 100644
--- a/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs
+++ b/Roadie.Api.Library.Tests/ArtistLookupEngineTests.cs
@@ -1,27 +1,16 @@
-using Mapster;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
-using Roadie.Library.Data;
-using Roadie.Library.Engines;
-using Roadie.Library.Extensions;
-using Roadie.Library.MetaData.ID3Tags;
using Roadie.Library.Processors;
using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Text;
-using Xunit;
namespace Roadie.Library.Tests
{
public class ArtistLookupEngineTests
{
private IEventMessageLogger MessageLogger { get; }
+
private ILogger Logger
{
get
@@ -30,8 +19,6 @@ namespace Roadie.Library.Tests
}
}
-
-
private IRoadieSettings Configuration { get; }
public DictionaryCacheManager CacheManager { get; }
private Encoding.IHttpEncoder HttpEncoder { get; }
@@ -56,4 +43,4 @@ namespace Roadie.Library.Tests
Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
}
}
-}
+}
\ No newline at end of file
diff --git a/Roadie.Api.Library/Roadie.Library.csproj b/Roadie.Api.Library/Roadie.Library.csproj
index 7891660..e0ef1cd 100644
--- a/Roadie.Api.Library/Roadie.Library.csproj
+++ b/Roadie.Api.Library/Roadie.Library.csproj
@@ -11,11 +11,11 @@
-
-
-
+
+
+
-
+
@@ -36,7 +36,7 @@
-
+
diff --git a/Roadie.Api.Services/ImageService.cs b/Roadie.Api.Services/ImageService.cs
index 503196b..87403d6 100644
--- a/Roadie.Api.Services/ImageService.cs
+++ b/Roadie.Api.Services/ImageService.cs
@@ -356,16 +356,29 @@ namespace Roadie.Api.Services
{
try
{
- var playlist = await GetPlaylist(id).ConfigureAwait(false);
- if (playlist == null)
+ string playlistImageFilename = null;
+ IImage image = null;
+ if (id == PlaylistService.DynamicFavoritePlaylistId)
{
- return new FileOperationResult(true, $"Playlist Not Found [{id}]");
+ image = new Library.Imaging.Image(id)
+ {
+ CreatedDate = DateTime.UtcNow
+ };
+ playlistImageFilename = Path.Combine(Configuration.ContentPath, @"images/d.favorite.playlist.jpg");
}
- IImage image = new Library.Imaging.Image(id)
+ else
{
- CreatedDate = playlist.CreatedDate
- };
- var playlistImageFilename = playlist.PathToImage(Configuration);
+ var playlist = await GetPlaylist(id).ConfigureAwait(false);
+ if (playlist == null)
+ {
+ return new FileOperationResult(true, $"Playlist Not Found [{id}]");
+ }
+ image = new Library.Imaging.Image(id)
+ {
+ CreatedDate = playlist.CreatedDate
+ };
+ playlistImageFilename = playlist.PathToImage(Configuration);
+ }
try
{
if (File.Exists(playlistImageFilename))
diff --git a/Roadie.Api.Services/PlaylistService.cs b/Roadie.Api.Services/PlaylistService.cs
index c2b429a..8e56417 100644
--- a/Roadie.Api.Services/PlaylistService.cs
+++ b/Roadie.Api.Services/PlaylistService.cs
@@ -28,6 +28,8 @@ namespace Roadie.Api.Services
{
public class PlaylistService : ServiceBase, IPlaylistService
{
+ public static Guid DynamicFavoritePlaylistId = Guid.Parse("54BA96A4-DFB5-4970-A989-CBA4BF0EFE75");
+
private IBookmarkService BookmarkService { get; }
public PlaylistService(IRoadieSettings configuration,
@@ -85,7 +87,7 @@ namespace Roadie.Api.Services
{
ReleaseCount = result.ReleaseCount,
TrackCount = result.TrackCount,
- TrackSize = result.DurationTime,
+ TrackSize = result.Duration.ToString(),
FileSize = playlistTracks.Sum(x => (long?)x.t.FileSize).ToFileSize()
};
tsw.Stop();
@@ -232,11 +234,98 @@ namespace Roadie.Api.Services
{
var sw = Stopwatch.StartNew();
sw.Start();
- var cacheKey = $"urn:playlist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
- var result = await CacheManager.GetAsync(cacheKey, async () =>
+ OperationResult result = null;
+ var isPlaylistDynamic = id == PlaylistService.DynamicFavoritePlaylistId;
+ if (isPlaylistDynamic)
{
- return await PlaylistByIdAction(id, includes).ConfigureAwait(false);
- }, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
+ var now = DateTime.UtcNow;
+ var userFavoriteTracks = from ut in DbContext.UserTracks
+ join t in DbContext.Tracks on ut.TrackId equals t.Id
+ join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
+ join r in DbContext.Releases on rm.ReleaseId equals r.Id
+ join releaseArtist in DbContext.Artists on r.ArtistId equals releaseArtist.Id
+ join trackArtist in DbContext.Artists on t.ArtistId equals trackArtist.Id into tas
+ from trackArtist in tas.DefaultIfEmpty()
+ where ut.UserId == roadieUser.Id
+ where ut.IsFavorite == true
+ select new
+ {
+ r,
+ releaseArtist,
+ trackArtist,
+ rm,
+ t
+ };
+
+ var dynamicStatus = new ReleaseGroupingStatistics
+ {
+ ReleaseCount = SafeParser.ToNumber(await userFavoriteTracks.Select(x => x.rm.ReleaseId).Distinct().CountAsync().ConfigureAwait(false)),
+ TrackCount = SafeParser.ToNumber(await userFavoriteTracks.Select(x => x.t.Id).CountAsync().ConfigureAwait(false)),
+ TrackSize = (await userFavoriteTracks.SumAsync(x => x.t.Duration)).ToString(),
+ FileSize = SafeParser.ToNumber(await userFavoriteTracks.SumAsync(x => x.t.FileSize).ConfigureAwait(false)).ToFileSize()
+ };
+ var dynamicTracks = new List();
+ var looper = 1;
+ foreach(var td in (await userFavoriteTracks.ToArrayAsync().ConfigureAwait(false)))
+ {
+ dynamicTracks.Add(new PlaylistTrack
+ {
+ ListNumber = looper,
+ Track = TrackList.FromDataTrack(null,
+ td.t,
+ td.rm.MediaNumber,
+ td.r,
+ td.releaseArtist,
+ td.trackArtist,
+ HttpContext.BaseUrl,
+ ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, td.t.RoadieId),
+ ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, td.r.RoadieId),
+ ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, td.releaseArtist.RoadieId),
+ ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, td.trackArtist == null ? null : (Guid?)td.trackArtist.RoadieId))
+ });
+ looper++;
+ }
+ result = new OperationResult
+ {
+ Data = new Playlist
+ {
+ Id = id,
+ Name = "[r] Favorites",
+ Description = "Dynamic Playlist of Favorited Tracks",
+ CreatedDate = now,
+ LastUpdated = now,
+ UserCanEdit = false,
+ IsPublic = false,
+ Maintainer = new UserList
+ {
+ IsAdmin = false,
+ IsEditor = false,
+ IsPrivate = true,
+ User = new DataToken
+ {
+ Text = "Roadie System"
+ },
+ Thumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, roadieUser.UserId)
+ },
+ Statistics = dynamicStatus,
+ ReleaseCount = SafeParser.ToNumber(dynamicStatus.ReleaseCount),
+ TrackCount = SafeParser.ToNumber(dynamicStatus.TrackCount),
+ Duration = (await userFavoriteTracks.SumAsync(x => x.t.Duration).ConfigureAwait(false)),
+ Thumbnail = ImageHelper.MakePlaylistThumbnailImage(Configuration, HttpContext, id),
+ Tracks = dynamicTracks,
+ MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "playlist", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height)
+ },
+ IsSuccess = true
+ };
+ }
+ else
+ {
+ var cacheKey = $"urn:playlist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
+ result = await CacheManager.GetAsync(cacheKey, async () =>
+ {
+ return await PlaylistByIdAction(id, includes).ConfigureAwait(false);
+ }, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
+ }
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
@@ -248,27 +337,29 @@ namespace Roadie.Api.Services
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, HttpContext.BaseUrl, track.Track.Id);
}
}
-
- result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin;
- var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Playlist).ConfigureAwait(false);
- if (userBookmarkResult.IsSuccess)
+ if (!isPlaylistDynamic)
{
- result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
- }
- if (result.Data.Comments.Any())
- {
- var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
- var userCommentReactions = await (from cr in DbContext.CommentReactions
- where commentIds.Contains(cr.CommentId)
- where cr.UserId == roadieUser.Id
- select cr)
- .ToArrayAsync()
- .ConfigureAwait(false);
- foreach (var comment in result.Data.Comments)
+ result.Data.UserCanEdit = result.Data.Maintainer?.Id == roadieUser.UserId || roadieUser.IsAdmin;
+ var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Playlist).ConfigureAwait(false);
+ if (userBookmarkResult.IsSuccess)
{
- var userCommentReaction = Array.Find(userCommentReactions, x => x.CommentId == comment.DatabaseId);
- comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
- comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
+ result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
+ }
+ if (result.Data.Comments?.Any() == true)
+ {
+ var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
+ var userCommentReactions = await (from cr in DbContext.CommentReactions
+ where commentIds.Contains(cr.CommentId)
+ where cr.UserId == roadieUser.Id
+ select cr)
+ .ToArrayAsync()
+ .ConfigureAwait(false);
+ foreach (var comment in result.Data.Comments)
+ {
+ var userCommentReaction = Array.Find(userCommentReactions, x => x.CommentId == comment.DatabaseId);
+ comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
+ comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
+ }
}
}
}
@@ -344,12 +435,12 @@ namespace Roadie.Api.Services
if (request.FilterToReleaseId.HasValue)
{
playlistReleaseTrackIds = await (from pl in DbContext.Playlists
- join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
- join t in DbContext.Tracks on pltr.TrackId equals t.Id
- join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
- join r in DbContext.Releases on rm.ReleaseId equals r.Id
- where r.RoadieId == request.FilterToReleaseId
- select pl.Id
+ join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
+ join t in DbContext.Tracks on pltr.TrackId equals t.Id
+ join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
+ join r in DbContext.Releases on rm.ReleaseId equals r.Id
+ where r.RoadieId == request.FilterToReleaseId
+ select pl.Id
).ToArrayAsync().ConfigureAwait(false);
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
@@ -397,6 +488,46 @@ namespace Roadie.Api.Services
.Take(request.LimitValue)
.ToArrayAsync()
.ConfigureAwait(false);
+
+ // Dynamic list of favorites
+ if (!request.FilterToArtistId.HasValue && !request.FilterToReleaseId.HasValue && roadieUser != null)
+ {
+ var userFavoriteTracks = from ut in DbContext.UserTracks
+ join t in DbContext.Tracks on ut.TrackId equals t.Id
+ where ut.UserId == roadieUser.Id
+ where ut.IsFavorite == true
+ select t;
+
+ var numberOfFavorites = SafeParser.ToNumber(await userFavoriteTracks.CountAsync());
+
+ if (numberOfFavorites > 0)
+ {
+ var now = DateTime.UtcNow;
+ var dynamicPlaylist = new PlaylistList
+ {
+ Playlist = new DataToken
+ {
+ Text = "[r] Favorites",
+ Value = DynamicFavoritePlaylistId.ToString()
+ },
+ User = new DataToken
+ {
+ Text = roadieUser.UserName,
+ Value = roadieUser.UserId.ToString()
+ },
+ PlaylistCount = numberOfFavorites.Value,
+ IsPublic = false,
+ Duration = userFavoriteTracks.Sum(x => x.Duration),
+ TrackCount = numberOfFavorites.Value,
+ CreatedDate = now,
+ LastUpdated = now,
+ UserThumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, roadieUser.UserId),
+ Id = DynamicFavoritePlaylistId,
+ Thumbnail = ImageHelper.MakePlaylistThumbnailImage(Configuration, HttpContext, DynamicFavoritePlaylistId)
+ };
+ rows = new List(rows.Concat(new PlaylistList[1] { dynamicPlaylist })).ToArray();
+ }
+ }
sw.Stop();
return new Library.Models.Pagination.PagedResult
{
@@ -442,7 +573,7 @@ namespace Roadie.Api.Services
var sw = new Stopwatch();
sw.Start();
var errors = new List();
- var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false);
+ var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false);
if (playlist == null)
{
return new OperationResult(true, $"Playlist Not Found [{model.Id}]");
@@ -531,8 +662,8 @@ namespace Roadie.Api.Services
playlist.Tracks.Clear();
var tracks = await (from t in DbContext.Tracks
- join plt in request.Tracks on t.RoadieId equals plt.Track.Id
- select t).ToArrayAsync().ConfigureAwait(false);
+ join plt in request.Tracks on t.RoadieId equals plt.Track.Id
+ select t).ToArrayAsync().ConfigureAwait(false);
foreach (var newPlaylistTrack in request.Tracks.OrderBy(x => x.ListNumber))
{
var track = Array.Find(tracks, x => x.RoadieId == newPlaylistTrack.Track.Id);
diff --git a/Roadie.Api.Services/Roadie.Api.Services.csproj b/Roadie.Api.Services/Roadie.Api.Services.csproj
index a87e58f..b90349f 100644
--- a/Roadie.Api.Services/Roadie.Api.Services.csproj
+++ b/Roadie.Api.Services/Roadie.Api.Services.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Roadie.Api.Services/TrackService.cs b/Roadie.Api.Services/TrackService.cs
index 377a41b..bc23d7a 100644
--- a/Roadie.Api.Services/TrackService.cs
+++ b/Roadie.Api.Services/TrackService.cs
@@ -386,17 +386,25 @@ namespace Roadie.Api.Services
join t in DbContext.Tracks on plt.TrackId equals t.Id
where p.RoadieId == request.FilterToPlaylistId.Value
orderby plt.ListNumber
- select new
- {
- plt.ListNumber,
- t.Id
- }).ToArrayAsync().ConfigureAwait(false);
+ select new KeyValuePair(t.Id, plt.ListNumber)).ToArrayAsync().ConfigureAwait(false);
+
+ if(!request.FilterFavoriteOnly &&
+ request.FilterToPlaylistId == PlaylistService.DynamicFavoritePlaylistId)
+ {
+ var dynamicPlaylistFavoriteTrackIds = await (from ut in DbContext.UserTracks
+ join t in DbContext.Tracks on ut.TrackId equals t.Id
+ where ut.UserId == roadieUser.Id
+ where ut.IsFavorite == true
+ orderby t.CreatedDate descending
+ select t.Id).ToArrayAsync().ConfigureAwait(false);
+ playlistTrackInfos = dynamicPlaylistFavoriteTrackIds.Select((x,i) => new KeyValuePair(x, i+1)).ToArray();
+ }
rowCount = playlistTrackInfos.Length;
playListTrackPositions = playlistTrackInfos
.Skip(request.SkipValue)
.Take(request.LimitValue)
- .ToDictionary(x => x.Id, x => x.ListNumber);
+ .ToDictionary(x => x.Key, x => x.Value);
playlistTrackIds = playListTrackPositions.Select(x => x.Key).ToArray();
request.Sort = "TrackNumber";
request.Order = "ASC";
diff --git a/Roadie.Api/Roadie.Api.csproj b/Roadie.Api/Roadie.Api.csproj
index 0ffcb23..f9092af 100644
--- a/Roadie.Api/Roadie.Api.csproj
+++ b/Roadie.Api/Roadie.Api.csproj
@@ -41,18 +41,18 @@
-
+
-
+
-
+
-
+
diff --git a/Roadie.Api/wwwroot/Images/d.favorite.playlist.jpg b/Roadie.Api/wwwroot/Images/d.favorite.playlist.jpg
new file mode 100644
index 0000000..3dae3ab
Binary files /dev/null and b/Roadie.Api/wwwroot/Images/d.favorite.playlist.jpg differ