diff --git a/Roadie.Api.Library/Factories/FactoryBase.cs b/Roadie.Api.Library/Factories/FactoryBase.cs index 6def8e0..854fe32 100644 --- a/Roadie.Api.Library/Factories/FactoryBase.cs +++ b/Roadie.Api.Library/Factories/FactoryBase.cs @@ -36,6 +36,7 @@ namespace Roadie.Library.Factories this.ReleaseLookupEngine = releaseLookupEngine; } + [Obsolete("Use Service Methods")] protected IEnumerable ArtistIdsForRelease(int releaseId) { var trackArtistIds = (from r in this.DbContext.Releases @@ -48,6 +49,7 @@ namespace Roadie.Library.Factories return trackArtistIds.Distinct().ToArray(); } + [Obsolete("Use Service Methods")] /// /// Update the counts for all artists on a release (both track and release artists) /// @@ -59,6 +61,7 @@ namespace Roadie.Library.Factories } } + [Obsolete("Use Service Methods")] protected async Task UpdateArtistCounts(int artistId, DateTime now) { var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == artistId); @@ -77,6 +80,7 @@ namespace Roadie.Library.Factories } } + [Obsolete("Use Service Methods")] protected async Task UpdateLabelCounts(int labelId, DateTime now) { var label = this.DbContext.Labels.FirstOrDefault(x => x.Id == labelId); @@ -100,6 +104,7 @@ namespace Roadie.Library.Factories } } + [Obsolete("Use Service Methods")] protected async Task UpdateReleaseCounts(int releaseId, DateTime now) { var release = this.DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); diff --git a/Roadie.Api.Library/Factories/PlaylistFactory.cs b/Roadie.Api.Library/Factories/PlaylistFactory.cs index 6347499..53a3e1e 100644 --- a/Roadie.Api.Library/Factories/PlaylistFactory.cs +++ b/Roadie.Api.Library/Factories/PlaylistFactory.cs @@ -19,6 +19,7 @@ namespace Roadie.Library.Factories { } + [Obsolete("Use PlaylistService")] public async Task> AddTracksToPlaylist(Playlist playlist, IEnumerable trackIds) { var sw = new Stopwatch(); @@ -57,6 +58,7 @@ namespace Roadie.Library.Factories }; } + [Obsolete("Use PlaylistService")] public async Task> ReorderPlaylist(Playlist playlist) { var sw = new Stopwatch(); diff --git a/Roadie.Api.Services/IPlaylistService.cs b/Roadie.Api.Services/IPlaylistService.cs index b99b3ab..df7beef 100644 --- a/Roadie.Api.Services/IPlaylistService.cs +++ b/Roadie.Api.Services/IPlaylistService.cs @@ -5,13 +5,20 @@ using Roadie.Library.Models.Users; using System; using System.Collections.Generic; using System.Threading.Tasks; +using data = Roadie.Library.Data; namespace Roadie.Api.Services { public interface IPlaylistService { + Task> AddNewPlaylist(User user, Playlist model); + + Task> AddTracksToPlaylist(data.Playlist playlist, IEnumerable trackIds); + Task> ById(User roadieUser, Guid id, IEnumerable includes = null); Task> List(PagedRequest request, User roadieUser = null); + + Task> ReorderPlaylist(data.Playlist playlist); } } \ No newline at end of file diff --git a/Roadie.Api.Services/PlaylistService.cs b/Roadie.Api.Services/PlaylistService.cs index 950143a..5a56f43 100644 --- a/Roadie.Api.Services/PlaylistService.cs +++ b/Roadie.Api.Services/PlaylistService.cs @@ -10,7 +10,6 @@ using Roadie.Library.Extensions; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Playlists; -using Roadie.Library.Models.Releases; using Roadie.Library.Models.Users; using Roadie.Library.Utility; using System; @@ -39,6 +38,70 @@ namespace Roadie.Api.Services this.BookmarkService = bookmarkService; } + public async Task> AddNewPlaylist(User user, Playlist model) + { + var playlist = new data.Playlist + { + IsPublic = model.IsPublic, + Description = model.Description, + Name = model.Name, + UserId = user.Id + }; + this.DbContext.Playlists.Add(playlist); + await this.DbContext.SaveChangesAsync(); + var r = await this.AddTracksToPlaylist(playlist, model.Tracks.OrderBy(x => x.ListNumber).Select(x => x.Track.Id)); + var request = new PagedRequest + { + FilterToPlaylistId = playlist.RoadieId + }; + var result = await this.List(request, user); + return new OperationResult + { + Data = result.Rows.First(), + IsSuccess = true + }; + } + + public async Task> AddTracksToPlaylist(data.Playlist playlist, IEnumerable trackIds) + { + var sw = new Stopwatch(); + sw.Start(); + + var result = false; + var now = DateTime.UtcNow; + + var existingTracksForPlaylist = (from plt in this.DbContext.PlaylistTracks + join t in this.DbContext.Tracks on plt.TrackId equals t.Id + where plt.PlayListId == playlist.Id + select t); + var newTracksForPlaylist = (from t in this.DbContext.Tracks + where (from x in trackIds select x).Contains(t.RoadieId) + where !(from x in existingTracksForPlaylist select x.RoadieId).Contains(t.RoadieId) + select t).ToArray(); + foreach (var newTrackForPlaylist in newTracksForPlaylist) + { + this.DbContext.PlaylistTracks.Add(new data.PlaylistTrack + { + TrackId = newTrackForPlaylist.Id, + PlayListId = playlist.Id + }); + } + playlist.LastUpdated = now; + await this.DbContext.SaveChangesAsync(); + result = true; + + var r = await this.ReorderPlaylist(playlist); + result = result && r.IsSuccess; + + await base.UpdatePlaylistCounts(playlist.Id, now); + + return new OperationResult + { + IsSuccess = result, + Data = result + }; + } + public async Task> ById(User roadieUser, Guid id, IEnumerable includes = null) { var sw = Stopwatch.StartNew(); @@ -68,6 +131,87 @@ namespace Roadie.Api.Services }; } + public Task> List(PagedRequest request, User roadieUser = null) + { + var sw = new Stopwatch(); + sw.Start(); + + int[] playlistWithArtistTrackIds = new int[0]; + if (request.FilterToArtistId.HasValue) + { + playlistWithArtistTrackIds = (from pl in this.DbContext.Playlists + join pltr in this.DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId + join t in this.DbContext.Tracks on pltr.TrackId equals t.Id + join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + join r in this.DbContext.Releases on rm.ReleaseId equals r.Id + join a in this.DbContext.Artists on r.ArtistId equals a.Id + where a.RoadieId == request.FilterToArtistId + select pl.Id + ).ToArray(); + } + int[] playlistReleaseTrackIds = new int[0]; + if (request.FilterToReleaseId.HasValue) + { + playlistReleaseTrackIds = (from pl in this.DbContext.Playlists + join pltr in this.DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId + join t in this.DbContext.Tracks on pltr.TrackId equals t.Id + join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + join r in this.DbContext.Releases on rm.ReleaseId equals r.Id + where r.RoadieId == request.FilterToReleaseId + select pl.Id + ).ToArray(); + } + + var result = (from pl in this.DbContext.Playlists + join u in this.DbContext.Users on pl.UserId equals u.Id + where (request.FilterToPlaylistId == null || pl.RoadieId == request.FilterToPlaylistId) + where (request.FilterToArtistId == null || playlistWithArtistTrackIds.Contains(pl.Id)) + where (request.FilterToReleaseId == null || playlistReleaseTrackIds.Contains(pl.Id)) + where ((roadieUser == null && pl.IsPublic) || (roadieUser != null && u.RoadieId == roadieUser.UserId || pl.IsPublic)) + where (request.FilterValue.Length == 0 || (request.FilterValue.Length > 0 && (pl.Name != null && pl.Name.Contains(request.FilterValue)))) + select PlaylistList.FromDataPlaylist(pl, u, this.MakePlaylistThumbnailImage(pl.RoadieId), this.MakeUserThumbnailImage(u.RoadieId))); + var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "Playlist.Text", "ASC" } }) : request.OrderValue(null); + var rowCount = result.Count(); + var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); + sw.Stop(); + return Task.FromResult(new Library.Models.Pagination.PagedResult + { + TotalCount = rowCount, + CurrentPage = request.PageValue, + TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue), + OperationTime = sw.ElapsedMilliseconds, + Rows = rows + }); + } + + public async Task> ReorderPlaylist(data.Playlist playlist) + { + var sw = new Stopwatch(); + sw.Start(); + + var result = false; + var now = DateTime.UtcNow; + + if (playlist != null) + { + var looper = 0; + foreach (var playlistTrack in this.DbContext.PlaylistTracks.Where(x => x.PlayListId == playlist.Id).OrderBy(x => x.CreatedDate)) + { + looper++; + playlistTrack.ListNumber = looper; + playlistTrack.LastUpdated = now; + } + await this.DbContext.SaveChangesAsync(); + result = true; + } + + return new OperationResult + { + IsSuccess = result, + Data = result + }; + } + private Task> PlaylistByIdAction(Guid id, IEnumerable includes = null) { var sw = Stopwatch.StartNew(); @@ -109,27 +253,26 @@ namespace Roadie.Api.Services if (includes.Contains("tracks")) { result.Tracks = (from plt in playlistTracks - join rm in this.DbContext.ReleaseMedias on plt.t.ReleaseMediaId equals rm.Id - join r in this.DbContext.Releases on rm.ReleaseId equals r.Id - join releaseArtist in this.DbContext.Artists on r.ArtistId equals releaseArtist.Id - join trackArtist in this.DbContext.Artists on plt.t.ArtistId equals trackArtist.Id into tas - from trackArtist in tas.DefaultIfEmpty() - select new PlaylistTrack - { - ListNumber = plt.pltr.ListNumber, - Track = TrackList.FromDataTrack(plt.t, - rm.MediaNumber, - r, - releaseArtist, - trackArtist, - this.HttpContext.BaseUrl, - this.MakeTrackThumbnailImage(plt.t.RoadieId), - this.MakeReleaseThumbnailImage(r.RoadieId), - this.MakeArtistThumbnailImage(releaseArtist.RoadieId), - this.MakeArtistThumbnailImage(trackArtist == null ? null : (Guid?)trackArtist.RoadieId)) - }).ToArray(); + join rm in this.DbContext.ReleaseMedias on plt.t.ReleaseMediaId equals rm.Id + join r in this.DbContext.Releases on rm.ReleaseId equals r.Id + join releaseArtist in this.DbContext.Artists on r.ArtistId equals releaseArtist.Id + join trackArtist in this.DbContext.Artists on plt.t.ArtistId equals trackArtist.Id into tas + from trackArtist in tas.DefaultIfEmpty() + select new PlaylistTrack + { + ListNumber = plt.pltr.ListNumber, + Track = TrackList.FromDataTrack(plt.t, + rm.MediaNumber, + r, + releaseArtist, + trackArtist, + this.HttpContext.BaseUrl, + this.MakeTrackThumbnailImage(plt.t.RoadieId), + this.MakeReleaseThumbnailImage(r.RoadieId), + this.MakeArtistThumbnailImage(releaseArtist.RoadieId), + this.MakeArtistThumbnailImage(trackArtist == null ? null : (Guid?)trackArtist.RoadieId)) + }).ToArray(); } - } sw.Stop(); @@ -139,61 +282,6 @@ namespace Roadie.Api.Services IsSuccess = result != null, OperationTime = sw.ElapsedMilliseconds }); - - } - - - public Task> List(PagedRequest request, User roadieUser = null) - { - var sw = new Stopwatch(); - sw.Start(); - - int[] playlistWithArtistTrackIds = new int[0]; - if(request.FilterToArtistId.HasValue) - { - playlistWithArtistTrackIds = (from pl in this.DbContext.Playlists - join pltr in this.DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId - join t in this.DbContext.Tracks on pltr.TrackId equals t.Id - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id - join r in this.DbContext.Releases on rm.ReleaseId equals r.Id - join a in this.DbContext.Artists on r.ArtistId equals a.Id - where a.RoadieId == request.FilterToArtistId - select pl.Id - ).ToArray(); - } - int[] playlistReleaseTrackIds = new int[0]; - if(request.FilterToReleaseId.HasValue) - { - playlistReleaseTrackIds = (from pl in this.DbContext.Playlists - join pltr in this.DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId - join t in this.DbContext.Tracks on pltr.TrackId equals t.Id - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id - join r in this.DbContext.Releases on rm.ReleaseId equals r.Id - where r.RoadieId == request.FilterToReleaseId - select pl.Id - ).ToArray(); - } - - var result = (from pl in this.DbContext.Playlists - join u in this.DbContext.Users on pl.UserId equals u.Id - where (request.FilterToPlaylistId == null || pl.RoadieId == request.FilterToPlaylistId) - where (request.FilterToArtistId == null || playlistWithArtistTrackIds.Contains(pl.Id)) - where (request.FilterToReleaseId == null || playlistReleaseTrackIds.Contains(pl.Id)) - where ((roadieUser == null && pl.IsPublic) || (roadieUser != null && u.RoadieId == roadieUser.UserId || pl.IsPublic)) - where (request.FilterValue.Length == 0 || (request.FilterValue.Length > 0 && (pl.Name != null && pl.Name.Contains(request.FilterValue)))) - select PlaylistList.FromDataPlaylist(pl, u, this.MakePlaylistThumbnailImage(pl.RoadieId), this.MakeUserThumbnailImage(u.RoadieId))); - var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "Playlist.Text", "ASC" } }) : request.OrderValue(null); - var rowCount = result.Count(); - var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); - sw.Stop(); - return Task.FromResult(new Library.Models.Pagination.PagedResult - { - TotalCount = rowCount, - CurrentPage = request.PageValue, - TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue), - OperationTime = sw.ElapsedMilliseconds, - Rows = rows - }); } } } \ No newline at end of file diff --git a/Roadie.Api.Services/ServiceBase.cs b/Roadie.Api.Services/ServiceBase.cs index 3d83a5a..6fb6f00 100644 --- a/Roadie.Api.Services/ServiceBase.cs +++ b/Roadie.Api.Services/ServiceBase.cs @@ -8,6 +8,7 @@ using Roadie.Library.Identity; using Roadie.Library.Models; using Roadie.Library.Utility; using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using data = Roadie.Library.Data; @@ -603,6 +604,100 @@ namespace Roadie.Api.Services return new Image($"{this.HttpContext.ImageBaseUrl }/{type}/{id}", caption, null); } - + protected IEnumerable ArtistIdsForRelease(int releaseId) + { + var trackArtistIds = (from r in this.DbContext.Releases + join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join tr in this.DbContext.Tracks on rm.Id equals tr.ReleaseMediaId + where r.Id == releaseId + where tr.ArtistId != null + select tr.ArtistId.Value).ToList(); + trackArtistIds.Add(this.DbContext.Releases.FirstOrDefault(x => x.Id == releaseId).ArtistId); + return trackArtistIds.Distinct().ToArray(); + } + + /// + /// Update the counts for all artists on a release (both track and release artists) + /// + protected async Task UpdateArtistCountsForRelease(int releaseId, DateTime now) + { + foreach (var artistId in this.ArtistIdsForRelease(releaseId)) + { + await this.UpdateArtistCounts(artistId, now); + } + } + + protected async Task UpdateArtistCounts(int artistId, DateTime now) + { + var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == artistId); + if (artist != null) + { + artist.ReleaseCount = this.DbContext.Releases.Where(x => x.ArtistId == artistId).Count(); + artist.TrackCount = (from r in this.DbContext.Releases + join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join tr in this.DbContext.Tracks on rm.Id equals tr.ReleaseMediaId + where (tr.ArtistId == artistId || r.ArtistId == artistId) + select tr).Count(); + + artist.LastUpdated = now; + await this.DbContext.SaveChangesAsync(); + this.CacheManager.ClearRegion(artist.CacheRegion); + } + } + + protected async Task UpdatePlaylistCounts(int playlistId, DateTime now) + { + var playlist = this.DbContext.Playlists.FirstOrDefault(x => x.Id == playlistId); + if(playlist != null) + { + var playlistTracks = this.DbContext.PlaylistTracks + .Include(x => x.Track) + .Include("Track.ReleaseMedia") + .Where(x => x.PlayListId == playlist.Id).ToArray(); + playlist.TrackCount = (short)playlistTracks.Count(); + playlist.Duration = playlistTracks.Sum(x => x.Track.Duration); + playlist.ReleaseCount = (short)playlistTracks.Select(x => x.Track.ReleaseMedia.ReleaseId).Distinct().Count(); + playlist.LastUpdated = now; + await this.DbContext.SaveChangesAsync(); + this.CacheManager.ClearRegion(playlist.CacheRegion); + } + } + + protected async Task UpdateLabelCounts(int labelId, DateTime now) + { + var label = this.DbContext.Labels.FirstOrDefault(x => x.Id == labelId); + if (label != null) + { + label.ReleaseCount = this.DbContext.ReleaseLabels.Where(x => x.LabelId == label.Id).Count(); + label.ArtistCount = (from r in this.DbContext.Releases + join rl in this.DbContext.ReleaseLabels on r.Id equals rl.ReleaseId + join a in this.DbContext.Artists on r.ArtistId equals a.Id + where rl.LabelId == label.Id + group a by a.Id into artists + select artists).Select(x => x.Key).Count(); + label.TrackCount = (from r in this.DbContext.Releases + join rl in this.DbContext.ReleaseLabels on r.Id equals rl.ReleaseId + join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId + where rl.LabelId == label.Id + select t).Count(); + await this.DbContext.SaveChangesAsync(); + this.CacheManager.ClearRegion(label.CacheRegion); + } + } + + protected async Task UpdateReleaseCounts(int releaseId, DateTime now) + { + var release = this.DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); + if (release != null) + { + release.Duration = (from t in this.DbContext.Tracks + join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + where rm.ReleaseId == releaseId + select t).Sum(x => x.Duration); + await this.DbContext.SaveChangesAsync(); + this.CacheManager.ClearRegion(release.CacheRegion); + } + } } } \ No newline at end of file diff --git a/Roadie.Api/Controllers/PlaylistController.cs b/Roadie.Api/Controllers/PlaylistController.cs index 407cea9..cd232a9 100644 --- a/Roadie.Api/Controllers/PlaylistController.cs +++ b/Roadie.Api/Controllers/PlaylistController.cs @@ -6,8 +6,11 @@ using Microsoft.Extensions.Logging; using Roadie.Api.Services; using Roadie.Library.Caching; using Roadie.Library.Identity; +using Roadie.Library.Models; using Roadie.Library.Models.Pagination; +using Roadie.Library.Models.Playlists; using System; +using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using models = Roadie.Library.Models; @@ -59,5 +62,18 @@ namespace Roadie.Api.Controllers return Ok(result); } + [HttpPost("add")] + [ProducesResponseType(200)] + [ProducesResponseType(404)] + public async Task AddNewPlaylist([FromBody]Playlist model) + { + var result = await this.PlaylistService.AddNewPlaylist(await this.CurrentUserModel(), model); + if (!result.IsSuccess) + { + return StatusCode((int)HttpStatusCode.InternalServerError); + } + return Ok(result); + } + } } \ No newline at end of file