From 040ed6e023f8a6efe2a90d71272b733107effc49 Mon Sep 17 00:00:00 2001 From: Steven Hildreth Date: Wed, 8 Dec 2021 22:20:13 -0600 Subject: [PATCH] resolves #42 --- .../ArtistLookupEngineTests.cs | 19 +- Roadie.Api.Library/Roadie.Library.csproj | 10 +- Roadie.Api.Services/ImageService.cs | 27 ++- Roadie.Api.Services/PlaylistService.cs | 197 +++++++++++++++--- .../Roadie.Api.Services.csproj | 2 +- Roadie.Api.Services/TrackService.cs | 20 +- Roadie.Api/Roadie.Api.csproj | 8 +- .../wwwroot/Images/d.favorite.playlist.jpg | Bin 0 -> 31676 bytes 8 files changed, 211 insertions(+), 72 deletions(-) create mode 100644 Roadie.Api/wwwroot/Images/d.favorite.playlist.jpg 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 0000000000000000000000000000000000000000..3dae3abc9b7e65b5120c7125e25c0bb601c8c7ec GIT binary patch literal 31676 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 z

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 J`pl2X{{XG@gbe@y literal 0 HcmV?d00001