From 1402cfbb72a1df6d61a66f04d94721c8554538c4 Mon Sep 17 00:00:00 2001 From: Steven Hildreth Date: Sun, 30 Jun 2019 17:14:36 -0500 Subject: [PATCH] Cleaning up files. --- Inspector/appsettings.json | 6 +- Roadie.Api.Hubs/PlayActivityHub.cs | 5 +- Roadie.Api.Hubs/ScanActivityHub.cs | 6 +- .../Roadie.Library.Tests.csproj | 2 +- Roadie.Api.Library/AppException.cs | 9 +- Roadie.Api.Library/Data/CollectionMissing.cs | 28 + Roadie.Api.Library/Data/IRoadieDbContext.cs | 1 + Roadie.Api.Library/Data/RoadieDbContext.cs | 1 + Roadie.Api.Library/OperationResult.cs | 149 +- Roadie.Api.Library/Roadie.Library.csproj | 6 +- Roadie.Api.Services/AdminService.cs | 602 ++++---- Roadie.Api.Services/ArtistService.cs | 760 +++++----- Roadie.Api.Services/BookmarkService.cs | 199 ++- Roadie.Api.Services/CollectionService.cs | 199 +-- Roadie.Api.Services/CommentService.cs | 191 +-- Roadie.Api.Services/EmailSenderService.cs | 21 +- Roadie.Api.Services/GenreService.cs | 61 +- Roadie.Api.Services/HttpContext.cs | 17 +- Roadie.Api.Services/HttpEncoder.cs | 3 +- Roadie.Api.Services/IAdminService.cs | 12 +- Roadie.Api.Services/IArtistService.cs | 7 +- Roadie.Api.Services/IBookmarkService.cs | 3 +- Roadie.Api.Services/ICollectionService.cs | 3 +- Roadie.Api.Services/IImageService.cs | 27 +- Roadie.Api.Services/IPlayActivityService.cs | 5 +- Roadie.Api.Services/IReleaseService.cs | 11 +- Roadie.Api.Services/ISubsonicService.cs | 77 +- Roadie.Api.Services/ITrackService.cs | 6 +- Roadie.Api.Services/IUserService.cs | 4 +- Roadie.Api.Services/ImageService.cs | 538 +++---- Roadie.Api.Services/LabelService.cs | 215 +-- Roadie.Api.Services/LookupService.cs | 33 +- Roadie.Api.Services/PlayActivityService.cs | 240 +-- Roadie.Api.Services/PlaylistService.cs | 266 ++-- Roadie.Api.Services/ReleaseService.cs | 658 ++++---- .../Roadie.Api.Services.csproj | 2 +- Roadie.Api.Services/ServiceBase.cs | 794 +++++----- Roadie.Api.Services/StatisticsService.cs | 37 +- Roadie.Api.Services/SubsonicService.cs | 1326 +++++++++-------- Roadie.Api.Services/TokenService.cs | 20 +- Roadie.Api.Services/TrackService.cs | 628 ++++---- Roadie.Api.Services/UserService.cs | 570 ++++--- Roadie.Api/Roadie.Api.csproj | 4 +- Roadie.sln | 1 + Upgrade0003.sql | 11 + roadie.sql | 29 +- 46 files changed, 3967 insertions(+), 3826 deletions(-) create mode 100644 Roadie.Api.Library/Data/CollectionMissing.cs diff --git a/Inspector/appsettings.json b/Inspector/appsettings.json index 770edfe..63f7644 100644 --- a/Inspector/appsettings.json +++ b/Inspector/appsettings.json @@ -18,7 +18,7 @@ "Width": 320 }, "DontDoMetaDataProvidersSearchArtists": [ "Various Artists", "Sound Tracks" ], - "FileExtensionsToDelete": [ ".accurip", ".cue", ".dat", ".db", ".exe", ".htm", ".html", ".ini", ".log", ".jpg", ".jpeg", ".par", ".par2", ".pdf", ".png", ".md5", ".mht", ".mpg", ".m3u", ".nfo", ".nzb", ".pls", ".sfv", ".srr", ".txt", ".url" ], + "FileExtensionsToDelete": [ ".accurip", ".bmp", ".cue", ".dat", ".db", ".exe", ".htm", ".html", ".ini", ".log", ".jpg", ".jpeg", ".par", ".par2", ".pdf", ".png", ".md5", ".mht", ".mpg", ".m3u", ".nfo", ".nzb", ".pls", ".sfv", ".srr", ".txt", ".url" ], "RecordNoResultSearches": true, "ArtistNameReplace": { "AC/DC": [ "AC; DC", "AC;DC", "AC/ DC", "AC DC" ], @@ -100,6 +100,4 @@ ] } } -} - - +} \ No newline at end of file diff --git a/Roadie.Api.Hubs/PlayActivityHub.cs b/Roadie.Api.Hubs/PlayActivityHub.cs index 05afde5..a40f6a0 100644 --- a/Roadie.Api.Hubs/PlayActivityHub.cs +++ b/Roadie.Api.Hubs/PlayActivityHub.cs @@ -1,8 +1,5 @@ using Microsoft.AspNetCore.SignalR; using Roadie.Library.Models; -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace Roadie.Api.Hubs @@ -14,4 +11,4 @@ namespace Roadie.Api.Hubs await Clients.All.SendAsync("PlayActivity", playActivity); } } -} +} \ No newline at end of file diff --git a/Roadie.Api.Hubs/ScanActivityHub.cs b/Roadie.Api.Hubs/ScanActivityHub.cs index 936fa29..5f2f40f 100644 --- a/Roadie.Api.Hubs/ScanActivityHub.cs +++ b/Roadie.Api.Hubs/ScanActivityHub.cs @@ -1,8 +1,4 @@ using Microsoft.AspNetCore.SignalR; -using Roadie.Library.Models; -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace Roadie.Api.Hubs @@ -14,4 +10,4 @@ namespace Roadie.Api.Hubs await Clients.All.SendAsync("SendSystemActivity", scanActivity); } } -} +} \ No newline at end of file diff --git a/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj b/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj index aeb26c5..140d421 100644 --- a/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj +++ b/Roadie.Api.Library.Tests/Roadie.Library.Tests.csproj @@ -20,7 +20,7 @@ - + all diff --git a/Roadie.Api.Library/AppException.cs b/Roadie.Api.Library/AppException.cs index 5563ca6..ca57b4d 100644 --- a/Roadie.Api.Library/AppException.cs +++ b/Roadie.Api.Library/AppException.cs @@ -1,7 +1,12 @@ +using System; + namespace Roadie.Library { - public class AppException + [Serializable] + public class AppException : Exception { - + public AppException(string message) : base(message) + { + } } } \ No newline at end of file diff --git a/Roadie.Api.Library/Data/CollectionMissing.cs b/Roadie.Api.Library/Data/CollectionMissing.cs new file mode 100644 index 0000000..4f15c2b --- /dev/null +++ b/Roadie.Api.Library/Data/CollectionMissing.cs @@ -0,0 +1,28 @@ +using Roadie.Library.Enums; +using Roadie.Library.Identity; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Roadie.Library.Data +{ + [Table("collectionMissing")] + public partial class CollectionMissing + { + [Column("id")] + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + [Column("collectionId")] + public int CollectionId { get; set; } + [Column("isArtistFound")] + public bool IsArtistFound { get; set; } + [Column("position")] + public int Position { get; set; } + [Column("artist")] + [MaxLength(1000)] + public string Artist { get; set; } + [Column("release")] + [MaxLength(1000)] + public string Release { get; set; } + } +} diff --git a/Roadie.Api.Library/Data/IRoadieDbContext.cs b/Roadie.Api.Library/Data/IRoadieDbContext.cs index 84be9bb..47f938e 100644 --- a/Roadie.Api.Library/Data/IRoadieDbContext.cs +++ b/Roadie.Api.Library/Data/IRoadieDbContext.cs @@ -18,6 +18,7 @@ namespace Roadie.Library.Data DbSet Bookmarks { get; set; } ChangeTracker ChangeTracker { get; } DbSet ChatMessages { get; set; } + DbSet CollectionMissings { get; set; } DbSet CollectionReleases { get; set; } DbSet Collections { get; set; } DbSet Comments { get; set; } diff --git a/Roadie.Api.Library/Data/RoadieDbContext.cs b/Roadie.Api.Library/Data/RoadieDbContext.cs index e3dc1bc..9f16288 100644 --- a/Roadie.Api.Library/Data/RoadieDbContext.cs +++ b/Roadie.Api.Library/Data/RoadieDbContext.cs @@ -13,6 +13,7 @@ namespace Roadie.Library.Data public DbSet Bookmarks { get; set; } public DbSet ChatMessages { get; set; } public DbSet Comments { get; set; } + public DbSet CollectionMissings { get; set; } public DbSet CommentReactions { get; set; } public DbSet CollectionReleases { get; set; } public DbSet Collections { get; set; } diff --git a/Roadie.Api.Library/OperationResult.cs b/Roadie.Api.Library/OperationResult.cs index 4c51b49..cdbdee2 100644 --- a/Roadie.Api.Library/OperationResult.cs +++ b/Roadie.Api.Library/OperationResult.cs @@ -1,83 +1,18 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections.Generic; -using System.Globalization; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Xml.Serialization; +using Newtonsoft.Json; namespace Roadie.Library { - [Serializable] - public class AppException : Exception - { - public AppException() : base() - { - } - - public AppException(string message) : base(message) - { - } - - public AppException(string message, params object[] args) - : base(String.Format(CultureInfo.CurrentCulture, message, args)) - { - } - } - [Serializable] public class OperationResult { private List _errors; private List _messages; - [JsonIgnore] - [XmlIgnore] - public Dictionary AdditionalData { get; set; } = new Dictionary(); - - public Dictionary AdditionalClientData { get; set; } = new Dictionary(); - - /// - /// Client friendly exceptions - /// - [JsonProperty("errors")] - public IEnumerable AppExceptions - { - get - { - if (this.Errors == null || !this.Errors.Any()) - { - return null; - } - return this.Errors.Select(x => new AppException(x.Message)); - } - } - - public T Data { get; set; } - - /// - /// Server side visible exceptions - /// - [JsonIgnore] - public IEnumerable Errors { get; set; } - - [JsonIgnore] - public bool IsNotFoundResult { get; set; } - - [JsonIgnore] - public bool IsAccessDeniedResult { get; set; } - - public bool IsSuccess { get; set; } - - public IEnumerable Messages - { - get - { - return this._messages; - } - } - - public long OperationTime { get; set; } - public OperationResult() { } @@ -86,53 +21,89 @@ namespace Roadie.Library { if (messages != null && messages.Any()) { - this.AdditionalData = new Dictionary(); - messages.ToList().ForEach(x => this.AddMessage(x)); + AdditionalData = new Dictionary(); + messages.ToList().ForEach(AddMessage); } } public OperationResult(bool isNotFoundResult, IEnumerable messages = null) { - this.IsNotFoundResult = isNotFoundResult; + IsNotFoundResult = isNotFoundResult; if (messages != null && messages.Any()) { - this.AdditionalData = new Dictionary(); - messages.ToList().ForEach(x => this.AddMessage(x)); + AdditionalData = new Dictionary(); + messages.ToList().ForEach(AddMessage); } } public OperationResult(bool isNotFoundResult, string message) { - this.IsNotFoundResult = isNotFoundResult; - this.AddMessage(message); + IsNotFoundResult = isNotFoundResult; + AddMessage(message); } public OperationResult(string message = null) { - this.AdditionalData = new Dictionary(); - this.AddMessage(message); + AdditionalData = new Dictionary(); + AddMessage(message); } public OperationResult(Exception error = null) { - this.AddError(error); + AddError(error); } public OperationResult(string message = null, Exception error = null) { - this.AddMessage(message); - this.AddError(error); + AddMessage(message); + AddError(error); } + [JsonIgnore] + [XmlIgnore] + public Dictionary AdditionalData { get; set; } = new Dictionary(); + + public Dictionary AdditionalClientData { get; set; } = new Dictionary(); + + /// + /// Client friendly exceptions + /// + [JsonProperty("errors")] + public IEnumerable AppExceptions + { + get + { + if (Errors == null || !Errors.Any()) return null; + + return Errors.Select(x => new AppException(x.Message)); + } + } + + public T Data { get; set; } + + /// + /// Server side visible exceptions + /// + [JsonIgnore] + public IEnumerable Errors { get; set; } + + [JsonIgnore] public bool IsNotFoundResult { get; set; } + + [JsonIgnore] public bool IsAccessDeniedResult { get; set; } + + public bool IsSuccess { get; set; } + + public IEnumerable Messages => _messages; + + public long OperationTime { get; set; } + public void AddError(Exception exception) { if (exception != null) { - if (this._errors == null) - { - this._errors = new List(); - } - this._errors.Add(exception); + if (_errors == null) _errors = new List(); + + _errors.Add(exception); } } @@ -140,11 +111,9 @@ namespace Roadie.Library { if (!string.IsNullOrEmpty(message)) { - if (this._messages == null) - { - this._messages = new List(); - } - this._messages.Add(message); + if (_messages == null) _messages = new List(); + + _messages.Add(message); } } } diff --git a/Roadie.Api.Library/Roadie.Library.csproj b/Roadie.Api.Library/Roadie.Library.csproj index 217b1b5..4157f5e 100644 --- a/Roadie.Api.Library/Roadie.Library.csproj +++ b/Roadie.Api.Library/Roadie.Library.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.2 @@ -11,9 +11,9 @@ - + - + diff --git a/Roadie.Api.Services/AdminService.cs b/Roadie.Api.Services/AdminService.cs index 1ba57cf..f483c48 100644 --- a/Roadie.Api.Services/AdminService.cs +++ b/Roadie.Api.Services/AdminService.cs @@ -1,5 +1,4 @@ -using Mapster; -using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -7,7 +6,6 @@ using Roadie.Api.Hubs; using Roadie.Library; using Roadie.Library.Caching; using Roadie.Library.Configuration; -using Roadie.Library.Data; using Roadie.Library.Encoding; using Roadie.Library.Engines; using Roadie.Library.Enums; @@ -20,6 +18,7 @@ using Roadie.Library.MetaData.FileName; using Roadie.Library.MetaData.ID3Tags; using Roadie.Library.MetaData.LastFm; using Roadie.Library.MetaData.MusicBrainz; +using Roadie.Library.Models.Collections; using Roadie.Library.Processors; using Roadie.Library.Utility; using System; @@ -28,7 +27,6 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; - using data = Roadie.Library.Data; namespace Roadie.Api.Services @@ -36,57 +34,70 @@ namespace Roadie.Api.Services public class AdminService : ServiceBase, IAdminService { protected IHubContext ScanActivityHub { get; } + private IArtistFactory ArtistFactory { get; } + private IArtistLookupEngine ArtistLookupEngine { get; } + private IAudioMetaDataHelper AudioMetaDataHelper { get; } + private IEventMessageLogger EventMessageLogger { get; } + private IFileNameHelper FileNameHelper { get; } + private IID3TagsHelper ID3TagsHelper { get; } + private IImageFactory ImageFactory { get; } + private ILabelFactory LabelFactory { get; } + private ILabelLookupEngine LabelLookupEngine { get; } + private ILastFmHelper LastFmHelper { get; } - private ILogger MessageLogger - { - get - { - return this.EventMessageLogger as ILogger; - } - } + private ILogger MessageLogger => EventMessageLogger as ILogger; private IMusicBrainzProvider MusicBrainzProvider { get; } + private IReleaseFactory ReleaseFactory { get; } + private IReleaseLookupEngine ReleaseLookupEngine { get; } public AdminService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger, - IHubContext scanActivityHub - ) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger, + IHubContext scanActivityHub + ) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { - this.ScanActivityHub = scanActivityHub; - this.EventMessageLogger = new EventMessageLogger(); - this.EventMessageLogger.Messages += EventMessageLogger_Messages; + ScanActivityHub = scanActivityHub; + EventMessageLogger = new EventMessageLogger(); + EventMessageLogger.Messages += EventMessageLogger_Messages; - this.MusicBrainzProvider = new MusicBrainzProvider(configuration, cacheManager, MessageLogger); - this.LastFmHelper = new LastFmHelper(configuration, cacheManager, MessageLogger, context, httpEncoder); - this.FileNameHelper = new FileNameHelper(configuration, cacheManager, MessageLogger); - this.ID3TagsHelper = new ID3TagsHelper(configuration, cacheManager, MessageLogger); + MusicBrainzProvider = new MusicBrainzProvider(configuration, cacheManager, MessageLogger); + LastFmHelper = new LastFmHelper(configuration, cacheManager, MessageLogger, context, httpEncoder); + FileNameHelper = new FileNameHelper(configuration, cacheManager, MessageLogger); + ID3TagsHelper = new ID3TagsHelper(configuration, cacheManager, MessageLogger); - this.ArtistLookupEngine = new ArtistLookupEngine(configuration, httpEncoder, context, cacheManager, MessageLogger); - this.LabelLookupEngine = new LabelLookupEngine(configuration, httpEncoder, context, cacheManager, MessageLogger); - this.ReleaseLookupEngine = new ReleaseLookupEngine(configuration, httpEncoder, context, cacheManager, MessageLogger, this.ArtistLookupEngine, this.LabelLookupEngine); - this.ImageFactory = new ImageFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, this.ArtistLookupEngine, this.ReleaseLookupEngine); - this.LabelFactory = new LabelFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, this.ArtistLookupEngine, this.ReleaseLookupEngine); - this.AudioMetaDataHelper = new AudioMetaDataHelper(configuration, httpEncoder, context, this.MusicBrainzProvider, this.LastFmHelper, cacheManager, - MessageLogger, this.ArtistLookupEngine, this.ImageFactory, this.FileNameHelper, this.ID3TagsHelper); - this.ReleaseFactory = new ReleaseFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, this.ArtistLookupEngine, this.LabelFactory, this.AudioMetaDataHelper, this.ReleaseLookupEngine); - this.ArtistFactory = new ArtistFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, this.ArtistLookupEngine, this.ReleaseFactory, this.ImageFactory, this.ReleaseLookupEngine, this.AudioMetaDataHelper); + ArtistLookupEngine = + new ArtistLookupEngine(configuration, httpEncoder, context, cacheManager, MessageLogger); + LabelLookupEngine = new LabelLookupEngine(configuration, httpEncoder, context, cacheManager, MessageLogger); + ReleaseLookupEngine = new ReleaseLookupEngine(configuration, httpEncoder, context, cacheManager, + MessageLogger, ArtistLookupEngine, LabelLookupEngine); + ImageFactory = new ImageFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, + ArtistLookupEngine, ReleaseLookupEngine); + LabelFactory = new LabelFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, + ArtistLookupEngine, ReleaseLookupEngine); + AudioMetaDataHelper = new AudioMetaDataHelper(configuration, httpEncoder, context, MusicBrainzProvider, + LastFmHelper, cacheManager, + MessageLogger, ArtistLookupEngine, ImageFactory, FileNameHelper, ID3TagsHelper); + ReleaseFactory = new ReleaseFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, + ArtistLookupEngine, LabelFactory, AudioMetaDataHelper, ReleaseLookupEngine); + ArtistFactory = new ArtistFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, + ArtistLookupEngine, ReleaseFactory, ImageFactory, ReleaseLookupEngine, AudioMetaDataHelper); } public async Task> DeleteArtist(ApplicationUser user, Guid artistId) @@ -94,31 +105,31 @@ namespace Roadie.Api.Services var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); + var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) { - await this.LogAndPublish($"DeleteArtist Unknown Artist [{ artistId}]", LogLevel.Warning); - return new OperationResult(true, $"Artist Not Found [{ artistId }]"); + await LogAndPublish($"DeleteArtist Unknown Artist [{artistId}]", LogLevel.Warning); + return new OperationResult(true, $"Artist Not Found [{artistId}]"); } + try { - var result = await this.ArtistFactory.Delete(artist); + var result = await ArtistFactory.Delete(artist); if (!result.IsSuccess) - { return new OperationResult { Errors = result.Errors }; - } } catch (Exception ex) { - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting artist."); + Logger.LogError(ex); + await LogAndPublish("Error deleting artist."); errors.Add(ex); } + sw.Stop(); - await this.LogAndPublish($"DeleteArtist `{ artist }`, By User `{user }`", LogLevel.Information); + await LogAndPublish($"DeleteArtist `{artist}`, By User `{user}`", LogLevel.Information); return new OperationResult { IsSuccess = !errors.Any(), @@ -128,30 +139,35 @@ namespace Roadie.Api.Services }; } - public async Task> DeleteArtistReleases(ApplicationUser user, Guid artistId, bool doDeleteFiles = false) + public async Task> DeleteArtistReleases(ApplicationUser user, Guid artistId, + bool doDeleteFiles = false) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); + var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) { - await this.LogAndPublish($"DeleteArtistReleases Unknown Artist [{ artistId}]", LogLevel.Warning); - return new OperationResult(true, $"Artist Not Found [{ artistId }]"); + await LogAndPublish($"DeleteArtistReleases Unknown Artist [{artistId}]", LogLevel.Warning); + return new OperationResult(true, $"Artist Not Found [{artistId}]"); } + try { - await this.ReleaseFactory.DeleteReleases(this.DbContext.Releases.Where(x => x.ArtistId == artist.Id).Select(x => x.RoadieId).ToArray(), doDeleteFiles); - await this.DbContext.SaveChangesAsync(); + await ReleaseFactory.DeleteReleases( + DbContext.Releases.Where(x => x.ArtistId == artist.Id).Select(x => x.RoadieId).ToArray(), + doDeleteFiles); + await DbContext.SaveChangesAsync(); } catch (Exception ex) { - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting artist."); + Logger.LogError(ex); + await LogAndPublish("Error deleting artist."); errors.Add(ex); } + sw.Stop(); - await this.LogAndPublish($"DeleteArtistReleases `{ artist }`, By User `{user }`", LogLevel.Information); + await LogAndPublish($"DeleteArtistReleases `{artist}`, By User `{user}`", LogLevel.Information); return new OperationResult { IsSuccess = !errors.Any(), @@ -161,36 +177,38 @@ namespace Roadie.Api.Services }; } - public async Task> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index) + public async Task> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, + int index) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); + var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) { - await this.LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{ artistId}]", LogLevel.Warning); - return new OperationResult(true, $"Artist Not Found [{ artistId }]"); + await LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{artistId}]", LogLevel.Warning); + return new OperationResult(true, $"Artist Not Found [{artistId}]"); } + try { - var artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); - var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly); + var artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); + var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), + ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly); var artistImageFilename = artistImagesInFolder.Skip(index).FirstOrDefault(); - if (artistImageFilename.Exists) - { - artistImageFilename.Delete(); - } - this.CacheManager.ClearRegion(artist.CacheRegion); + if (artistImageFilename.Exists) artistImageFilename.Delete(); + CacheManager.ClearRegion(artist.CacheRegion); } catch (Exception ex) { - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting artist secondary image."); + Logger.LogError(ex); + await LogAndPublish("Error deleting artist secondary image."); errors.Add(ex); } + sw.Stop(); - await this.LogAndPublish($"DeleteArtistSecondaryImage `{ artist }` Index [{ index }], By User `{user }`", LogLevel.Information); + await LogAndPublish($"DeleteArtistSecondaryImage `{artist}` Index [{index}], By User `{user}`", + LogLevel.Information); return new OperationResult { IsSuccess = !errors.Any(), @@ -200,32 +218,35 @@ namespace Roadie.Api.Services }; } - public async Task> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles) + public async Task> DeleteRelease(ApplicationUser user, Guid releaseId, + bool? doDeleteFiles) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var release = this.DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId); + var release = DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId); try { if (release == null) { - await this.LogAndPublish($"DeleteRelease Unknown Release [{ releaseId}]", LogLevel.Warning); - return new OperationResult(true, $"Release Not Found [{ releaseId }]"); + await LogAndPublish($"DeleteRelease Unknown Release [{releaseId}]", LogLevel.Warning); + return new OperationResult(true, $"Release Not Found [{releaseId}]"); } - await this.ReleaseFactory.Delete(release, doDeleteFiles ?? false); + + await ReleaseFactory.Delete(release, doDeleteFiles ?? false); } catch (Exception ex) { - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting release."); + Logger.LogError(ex); + await LogAndPublish("Error deleting release."); errors.Add(ex); } + sw.Stop(); - await this.LogAndPublish($"DeleteRelease `{ release }`, By User `{ user}`", LogLevel.Information); - this.CacheManager.Clear(); + await LogAndPublish($"DeleteRelease `{release}`, By User `{user}`", LogLevel.Information); + CacheManager.Clear(); return new OperationResult { IsSuccess = !errors.Any(), @@ -235,36 +256,40 @@ namespace Roadie.Api.Services }; } - public async Task> DeleteReleaseSecondaryImage(ApplicationUser user, Guid releaseId, int index) + public async Task> DeleteReleaseSecondaryImage(ApplicationUser user, Guid releaseId, + int index) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var release = this.DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId); + var release = DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId); if (release == null) { - await this.LogAndPublish($"DeleteReleaseSecondaryImage Unknown Release [{ releaseId}]", LogLevel.Warning); - return new OperationResult(true, $"Release Not Found [{ releaseId }]"); + await LogAndPublish($"DeleteReleaseSecondaryImage Unknown Release [{releaseId}]", LogLevel.Warning); + return new OperationResult(true, $"Release Not Found [{releaseId}]"); } + try { - var releaseFolder = release.ReleaseFileFolder(release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder)); - var releaseImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary, SearchOption.TopDirectoryOnly); + var releaseFolder = + release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration, + Configuration.LibraryFolder)); + var releaseImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), + ImageType.ReleaseSecondary, SearchOption.TopDirectoryOnly); var releaseImageFilename = releaseImagesInFolder.Skip(index).FirstOrDefault(); - if (releaseImageFilename.Exists) - { - releaseImageFilename.Delete(); - } - this.CacheManager.ClearRegion(release.CacheRegion); + if (releaseImageFilename.Exists) releaseImageFilename.Delete(); + CacheManager.ClearRegion(release.CacheRegion); } catch (Exception ex) { - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting release secondary image."); + Logger.LogError(ex); + await LogAndPublish("Error deleting release secondary image."); errors.Add(ex); } + sw.Stop(); - await this.LogAndPublish($"DeleteReleaseSecondaryImage `{ release }` Index [{ index }], By User `{user }`", LogLevel.Information); + await LogAndPublish($"DeleteReleaseSecondaryImage `{release}` Index [{index}], By User `{user}`", + LogLevel.Information); return new OperationResult { IsSuccess = !errors.Any(), @@ -281,53 +306,60 @@ namespace Roadie.Api.Services var errors = new List(); - var track = this.DbContext.Tracks.Include(x => x.ReleaseMedia) - .Include(x => x.ReleaseMedia.Release) - .Include(x => x.ReleaseMedia.Release.Artist) - .FirstOrDefault(x => x.RoadieId == trackId); + var track = DbContext.Tracks.Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .FirstOrDefault(x => x.RoadieId == trackId); try { if (track == null) { - await this.LogAndPublish($"DeleteTrack Unknown Track [{ trackId}]", LogLevel.Warning); - return new OperationResult(true, $"Track Not Found [{ trackId }]"); + await LogAndPublish($"DeleteTrack Unknown Track [{trackId}]", LogLevel.Warning); + return new OperationResult(true, $"Track Not Found [{trackId}]"); } - this.DbContext.Tracks.Remove(track); - await this.DbContext.SaveChangesAsync(); + + DbContext.Tracks.Remove(track); + await DbContext.SaveChangesAsync(); if (doDeleteFile ?? false) { string trackPath = null; try { - trackPath = track.PathToTrack(this.Configuration, this.Configuration.LibraryFolder); + trackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder); if (File.Exists(trackPath)) { File.Delete(trackPath); - this.Logger.LogWarning($"x For Track `{ track }`, Deleted File [{ trackPath }]"); + Logger.LogWarning($"x For Track `{track}`, Deleted File [{trackPath}]"); } - var trackThumbnailName = track.PathToTrackThumbnail(this.Configuration, this.Configuration.LibraryFolder); + + var trackThumbnailName = track.PathToTrackThumbnail(Configuration, Configuration.LibraryFolder); if (File.Exists(trackThumbnailName)) { File.Delete(trackThumbnailName); - this.Logger.LogWarning($"x For Track `{ track }`, Deleted Thumbnail File [{ trackThumbnailName }]"); + Logger.LogWarning($"x For Track `{track}`, Deleted Thumbnail File [{trackThumbnailName}]"); } } catch (Exception ex) { - this.Logger.LogError(ex, string.Format("Error Deleting File [{0}] For Track [{1}] Exception [{2}]", trackPath, track.Id, ex.Serialize())); + Logger.LogError(ex, + string.Format("Error Deleting File [{0}] For Track [{1}] Exception [{2}]", trackPath, + track.Id, ex.Serialize())); } } - await this.ReleaseFactory.ScanReleaseFolder(track.ReleaseMedia.Release.RoadieId, this.Configuration.LibraryFolder, false, track.ReleaseMedia.Release); + + await ReleaseFactory.ScanReleaseFolder(track.ReleaseMedia.Release.RoadieId, Configuration.LibraryFolder, + false, track.ReleaseMedia.Release); } catch (Exception ex) { - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting track."); + Logger.LogError(ex); + await LogAndPublish("Error deleting track."); errors.Add(ex); } + sw.Stop(); - await this.LogAndPublish($"DeleteTrack `{ track }`, By User `{ user}`", LogLevel.Information); - this.CacheManager.Clear(); + await LogAndPublish($"DeleteTrack `{track}`, By User `{user}`", LogLevel.Information); + CacheManager.Clear(); return new OperationResult { IsSuccess = !errors.Any(), @@ -343,34 +375,36 @@ namespace Roadie.Api.Services sw.Start(); var errors = new List(); - var user = this.DbContext.Users.FirstOrDefault(x => x.RoadieId == userId); + var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userId); if (user.Id == applicationUser.Id) { var ex = new Exception("User cannot self."); - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting user."); + Logger.LogError(ex); + await LogAndPublish("Error deleting user."); errors.Add(ex); } + try { if (user == null) { - await this.LogAndPublish($"DeleteUser Unknown User [{ userId}]", LogLevel.Warning); - return new OperationResult(true, $"User Not Found [{ userId }]"); + await LogAndPublish($"DeleteUser Unknown User [{userId}]", LogLevel.Warning); + return new OperationResult(true, $"User Not Found [{userId}]"); } - this.DbContext.Users.Remove(user); - await this.DbContext.SaveChangesAsync(); + + DbContext.Users.Remove(user); + await DbContext.SaveChangesAsync(); } catch (Exception ex) { - this.Logger.LogError(ex); - await this.LogAndPublish("Error deleting user."); + Logger.LogError(ex); + await LogAndPublish("Error deleting user."); errors.Add(ex); } sw.Stop(); - await this.LogAndPublish($"DeleteUser `{ user }`, By User `{ user}`", LogLevel.Information); - this.CacheManager.Clear(); + await LogAndPublish($"DeleteUser `{user}`, By User `{user}`", LogLevel.Information); + CacheManager.Clear(); return new OperationResult { IsSuccess = !errors.Any(), @@ -381,55 +415,61 @@ namespace Roadie.Api.Services } /// - /// This is a very simple way to seed the database or setup configuration when the first (who becomes "Admin") user registers + /// This is a very simple way to seed the database or setup configuration when the first (who becomes "Admin") user + /// registers /// - public async Task> DoInitialSetup(ApplicationUser user, UserManager userManager) + public async Task> DoInitialSetup(ApplicationUser user, + UserManager userManager) { var sw = new Stopwatch(); sw.Start(); // Create user roles - this.DbContext.UserRoles.Add(new ApplicationRole + DbContext.UserRoles.Add(new ApplicationRole { Name = "Admin", Description = "Users with Administrative (full) access", NormalizedName = "ADMIN" }); - this.DbContext.UserRoles.Add(new ApplicationRole + DbContext.UserRoles.Add(new ApplicationRole { Name = "Editor", Description = "Users who have Edit Permissions", NormalizedName = "EDITOR" }); - await this.DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync(); // Add given user to Admin role await userManager.AddToRoleAsync(user, "Admin"); // Create special system artists of 'Sound Tracks' and 'Various Artists' - this.DbContext.Artists.Add(new data.Artist + DbContext.Artists.Add(new data.Artist { - AlternateNames = "Sound Track|Film Sound Track|Film Sound Tracks|Les Sound Track|Motion Picture Soundtrack|Original Motion Picture SoundTrack|Original Motion Picture SoundTracks|Original Cast Album|Original Soundtrack|Soundtracks|SoundTrack|soundtracks|Original Cast|Original Cast Soundtrack|Motion Picture Cast Recording|Cast Recording", + AlternateNames = + "Sound Track|Film Sound Track|Film Sound Tracks|Les Sound Track|Motion Picture Soundtrack|Original Motion Picture SoundTrack|Original Motion Picture SoundTracks|Original Cast Album|Original Soundtrack|Soundtracks|SoundTrack|soundtracks|Original Cast|Original Cast Soundtrack|Motion Picture Cast Recording|Cast Recording", ArtistType = "Meta", - BioContext = "A soundtrack, also written sound track, can be recorded music accompanying and synchronized to the images of a motion picture, book, television program or video game; a commercially released soundtrack album of music as featured in the soundtrack of a film or TV show; or the physical area of a film that contains the synchronized recorded sound.", + BioContext = + "A soundtrack, also written sound track, can be recorded music accompanying and synchronized to the images of a motion picture, book, television program or video game; a commercially released soundtrack album of music as featured in the soundtrack of a film or TV show; or the physical area of a film that contains the synchronized recorded sound.", Name = "Sound Tracks", SortName = "Sound Tracks", Status = Statuses.Ok, - Tags = "movie and television soundtracks|video game soundtracks|book soundstracks|composite|compilations", + Tags = + "movie and television soundtracks|video game soundtracks|book soundstracks|composite|compilations", URLs = "https://en.wikipedia.org/wiki/Soundtrack" }); - this.DbContext.Artists.Add(new data.Artist + DbContext.Artists.Add(new data.Artist { AlternateNames = "Various Artists|Various BNB artist|variousartist|va", ArtistType = "Meta", - BioContext = "Songs included on a compilation album may be previously released or unreleased, usually from several separate recordings by either one or several performers. If by one artist, then generally the tracks were not originally intended for release together as a single work, but may be collected together as a greatest hits album or box set. If from several performers, there may be a theme, topic, or genre which links the tracks, or they may have been intended for release as a single work—such as a tribute album. When the tracks are by the same recording artist, the album may be referred to as a retrospective album or an anthology. Compilation albums may employ traditional product bundling strategies", + BioContext = + "Songs included on a compilation album may be previously released or unreleased, usually from several separate recordings by either one or several performers. If by one artist, then generally the tracks were not originally intended for release together as a single work, but may be collected together as a greatest hits album or box set. If from several performers, there may be a theme, topic, or genre which links the tracks, or they may have been intended for release as a single work—such as a tribute album. When the tracks are by the same recording artist, the album may be referred to as a retrospective album or an anthology. Compilation albums may employ traditional product bundling strategies", Name = "Various Artists", SortName = "Various Artist", Status = Statuses.Ok, Tags = "compilations|various", URLs = "https://en.wikipedia.org/wiki/Compilation_album" }); - await this.DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync(); return new OperationResult { @@ -445,12 +485,12 @@ namespace Roadie.Api.Services sw.Start(); var missingData = new Dictionary>(); - foreach (var collection in this.DbContext.Collections.Where(x => x.Status != Statuses.Complete)) + foreach (var collection in DbContext.Collections.Where(x => x.Status != Statuses.Complete)) { - var collectionReleases = (from cr in this.DbContext.CollectionReleases - where cr.CollectionId == collection.Id - select cr); - PositionArtistRelease[] pars = null; + var collectionReleases = from cr in DbContext.CollectionReleases + where cr.CollectionId == collection.Id + select cr; + data.PositionArtistRelease[] pars = null; try { @@ -458,24 +498,23 @@ namespace Roadie.Api.Services } catch (Exception ex) { - missingData.Add($"CSV Error [{ collection.Name }]", new List { ex.Message }); + missingData.Add($"CSV Error [{collection.Name}]", new List { ex.Message }); continue; } + foreach (var par in pars) { var cr = collectionReleases.FirstOrDefault(x => x.ListNumber == par.Position); if (cr == null) { // If artist is already in result then add missing album to artist, if not then add artist then add missing album - if (!missingData.ContainsKey(par.Artist)) - { - missingData.Add(par.Artist, new List()); - } - var r = $"[{ collection.Name }]:[{ par.Release }]"; + if (!missingData.ContainsKey(par.Artist)) missingData.Add(par.Artist, new List()); + var r = $"[{collection.Name}]:[{par.Release}]"; missingData[par.Artist].Add(r); } } } + sw.Stop(); return Task.FromResult(new OperationResult>> { @@ -485,37 +524,33 @@ namespace Roadie.Api.Services }); } - public async Task> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = false) + public async Task> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, + bool doPurgeFirst = false) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var collections = this.DbContext.Collections.Where(x => x.IsLocked == false).ToArray(); + var collections = DbContext.Collections.Where(x => x.IsLocked == false).ToArray(); var updatedReleaseIds = new List(); foreach (var collection in collections) - { try { - var result = await this.ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst, false); - if (!result.IsSuccess) - { - errors.AddRange(result.Errors); - } + var result = await ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst, false); + if (!result.IsSuccess) errors.AddRange(result.Errors); updatedReleaseIds.AddRange((int[])result.AdditionalData["updatedReleaseIds"]); } catch (Exception ex) { - await this.LogAndPublish(ex.ToString(), LogLevel.Error); + await LogAndPublish(ex.ToString(), LogLevel.Error); errors.Add(ex); } - } - foreach (var updatedReleaseId in updatedReleaseIds.Distinct()) - { - await this.UpdateReleaseRank(updatedReleaseId); - } + + foreach (var updatedReleaseId in updatedReleaseIds.Distinct()) await UpdateReleaseRank(updatedReleaseId); sw.Stop(); - await this.LogAndPublish($"ScanAllCollections, By User `{user}`, Updated Release Count [{ updatedReleaseIds.Distinct().Count() }], ElapsedTime [{ sw.ElapsedMilliseconds}]", LogLevel.Information); + await LogAndPublish( + $"ScanAllCollections, By User `{user}`, Updated Release Count [{updatedReleaseIds.Distinct().Count()}], ElapsedTime [{sw.ElapsedMilliseconds}]", + LogLevel.Information); return new OperationResult { IsSuccess = !errors.Any(), @@ -525,40 +560,45 @@ namespace Roadie.Api.Services }; } - public async Task> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false) + public async Task> ScanArtist(ApplicationUser user, Guid artistId, + bool isReadOnly = false) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); + var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) { - await this.LogAndPublish($"ScanArtist Unknown Release [{ artistId}]", LogLevel.Warning); - return new OperationResult(true, $"Artist Not Found [{ artistId }]"); + await LogAndPublish($"ScanArtist Unknown Release [{artistId}]", LogLevel.Warning); + return new OperationResult(true, $"Artist Not Found [{artistId}]"); } + try { - var result = await this.ArtistFactory.ScanArtistReleasesFolders(artist.RoadieId, this.Configuration.LibraryFolder, isReadOnly); - this.CacheManager.ClearRegion(artist.CacheRegion); + var result = + await ArtistFactory.ScanArtistReleasesFolders(artist.RoadieId, Configuration.LibraryFolder, + isReadOnly); + CacheManager.ClearRegion(artist.CacheRegion); } catch (Exception ex) { - await this.LogAndPublish(ex.ToString(), LogLevel.Error); + await LogAndPublish(ex.ToString(), LogLevel.Error); errors.Add(ex); } + sw.Stop(); - this.DbContext.ScanHistories.Add(new data.ScanHistory + DbContext.ScanHistories.Add(new data.ScanHistory { UserId = user.Id, ForArtistId = artist.Id, - NewReleases = this.ReleaseLookupEngine.AddedReleaseIds.Count(), - NewTracks = this.ReleaseFactory.AddedTrackIds.Count(), + NewReleases = ReleaseLookupEngine.AddedReleaseIds.Count(), + NewTracks = ReleaseFactory.AddedTrackIds.Count(), TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds }); - await this.DbContext.SaveChangesAsync(); - await this.UpdateArtistRank(artist.Id, true); - await this.LogAndPublish($"ScanArtist `{artist}`, By User `{user}`", LogLevel.Information); + await DbContext.SaveChangesAsync(); + await UpdateArtistRank(artist.Id, true); + await LogAndPublish($"ScanArtist `{artist}`, By User `{user}`", LogLevel.Information); return new OperationResult { IsSuccess = !errors.Any(), @@ -568,30 +608,37 @@ namespace Roadie.Api.Services }; } - public async Task> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true) + public async Task> ScanCollection(ApplicationUser user, Guid collectionId, + bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true) { var sw = new Stopwatch(); sw.Start(); var releaseIdsInCollection = new List(); var updatedReleaseIds = new List(); - var result = new List(); + var result = new List(); var errors = new List(); - var collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == collectionId); + var collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == collectionId); if (collection == null) { - await this.LogAndPublish($"ScanCollection Unknown Collection [{ collectionId}]", LogLevel.Warning); - return new OperationResult(true, $"Collection Not Found [{ collectionId }]"); + await LogAndPublish($"ScanCollection Unknown Collection [{collectionId}]", LogLevel.Warning); + return new OperationResult(true, $"Collection Not Found [{collectionId}]"); } + try { if (doPurgeFirst) { - await this.LogAndPublish($"ScanCollection Purgeing Collection [{ collectionId}]", LogLevel.Warning); - var crs = this.DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArray(); - this.DbContext.CollectionReleases.RemoveRange(crs); - await this.DbContext.SaveChangesAsync(); + await LogAndPublish($"ScanCollection Purgeing Collection [{collectionId}]", LogLevel.Warning); + var crs = DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArray(); + DbContext.CollectionReleases.RemoveRange(crs); + await DbContext.SaveChangesAsync(); } + + var collectionMissingRecords = DbContext.CollectionMissings.Where(x => x.CollectionId == collection.Id); + DbContext.CollectionMissings.RemoveRange(collectionMissingRecords); + await DbContext.SaveChangesAsync(); + var par = collection.PositionArtistReleases(); if (par != null) { @@ -604,53 +651,73 @@ namespace Roadie.Api.Services var searchName = csvRelease.Artist.NormalizeName(); var specialSearchName = csvRelease.Artist.ToAlphanumericName(); - var artistResults = (from a in this.DbContext.Artists - where (a.Name.Contains(searchName) || - a.SortName.Contains(searchName) || - a.AlternateNames.Contains(searchName) || - a.AlternateNames.Contains(specialSearchName)) + var artistResults = (from a in DbContext.Artists + where a.Name.Contains(searchName) || + a.SortName.Contains(searchName) || + a.AlternateNames.Contains(searchName) || + a.AlternateNames.Contains(specialSearchName) select a).ToArray(); if (!artistResults.Any()) { - await this.LogAndPublish($"Unable To Find Artist [{csvRelease.Artist }], SearchName [{ searchName}]", LogLevel.Warning); - csvRelease.Status = Library.Enums.Statuses.Missing; + await LogAndPublish( + $"Unable To Find Artist [{csvRelease.Artist}], SearchName [{searchName}]", + LogLevel.Warning); + csvRelease.Status = Statuses.Missing; + DbContext.CollectionMissings.Add(new data.CollectionMissing + { + CollectionId = collection.Id, + Position = csvRelease.Position, + Artist = csvRelease.Artist, + Release = searchName + }); continue; } + foreach (var artistResult in artistResults) { artist = artistResult; searchName = csvRelease.Release.NormalizeName().ToLower(); specialSearchName = csvRelease.Release.ToAlphanumericName(); - release = (from r in this.DbContext.Releases - where (r.ArtistId == artist.Id) - where (r.Title.Contains(searchName) || - r.AlternateNames.Contains(searchName) || - r.AlternateNames.Contains(specialSearchName)) + release = (from r in DbContext.Releases + where r.ArtistId == artist.Id + where r.Title.Contains(searchName) || + r.AlternateNames.Contains(searchName) || + r.AlternateNames.Contains(specialSearchName) select r - ).FirstOrDefault(); - if (release != null) - { - break; - } + ).FirstOrDefault(); + if (release != null) break; } + if (release == null) { - await this.LogAndPublish($"Unable To Find Release [{csvRelease.Release}] for Artist [{csvRelease.Artist}], SearchName [{searchName}]", LogLevel.Warning ); - csvRelease.Status = Library.Enums.Statuses.Missing; + await LogAndPublish( + $"Unable To Find Release [{csvRelease.Release}] for Artist [{csvRelease.Artist}], SearchName [{searchName}]", + LogLevel.Warning); + csvRelease.Status = Statuses.Missing; + DbContext.CollectionMissings.Add(new data.CollectionMissing + { + CollectionId = collection.Id, + IsArtistFound = true, + Position = csvRelease.Position, + Artist = csvRelease.Artist, + Release = searchName + }); continue; } - var isInCollection = this.DbContext.CollectionReleases.FirstOrDefault(x => x.CollectionId == collection.Id && - x.ListNumber == csvRelease.Position && - x.ReleaseId == release.Id); + + var isInCollection = DbContext.CollectionReleases.FirstOrDefault(x => + x.CollectionId == collection.Id && + x.ListNumber == csvRelease.Position && + x.ReleaseId == release.Id); var updated = false; // Found in Database but not in collection add to Collection if (isInCollection == null) { - this.DbContext.CollectionReleases.Add(new CollectionRelease + DbContext.CollectionReleases.Add(new data.CollectionRelease { CollectionId = collection.Id, ReleaseId = release.Id, - ListNumber = csvRelease.Position, + ListNumber = csvRelease.Position }); updated = true; } @@ -661,18 +728,17 @@ namespace Roadie.Api.Services isInCollection.ListNumber = csvRelease.Position; updated = true; } - if(updated && !updatedReleaseIds.Any(x => x == release.Id)) - { - updatedReleaseIds.Add(release.Id); - } + + if (updated && !updatedReleaseIds.Any(x => x == release.Id)) updatedReleaseIds.Add(release.Id); releaseIdsInCollection.Add(release.Id); } + collection.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); - var dto = new Library.Models.Collections.CollectionList + await DbContext.SaveChangesAsync(); + var dto = new CollectionList { CollectionCount = collection.CollectionCount, - CollectionFoundCount = (from cr in this.DbContext.CollectionReleases + CollectionFoundCount = (from cr in DbContext.CollectionReleases where cr.CollectionId == collection.Id select cr.CollectionId).Count() }; @@ -686,33 +752,35 @@ namespace Roadie.Api.Services { collection.Status = Statuses.Incomplete; } - var collectionReleasesToRemove = (from cr in this.DbContext.CollectionReleases + + var collectionReleasesToRemove = (from cr in DbContext.CollectionReleases where cr.CollectionId == collection.Id where !releaseIdsInCollection.Contains(cr.ReleaseId) select cr).ToArray(); if (collectionReleasesToRemove.Any()) { - await this.LogAndPublish($"Removing [{ collectionReleasesToRemove.Count() }] Stale Release Records from Collection.", LogLevel.Information); - this.DbContext.CollectionReleases.RemoveRange(collectionReleasesToRemove); + await LogAndPublish( + $"Removing [{collectionReleasesToRemove.Count()}] Stale Release Records from Collection.", + LogLevel.Information); + DbContext.CollectionReleases.RemoveRange(collectionReleasesToRemove); } - await this.DbContext.SaveChangesAsync(); + + await DbContext.SaveChangesAsync(); if (doUpdateRanks) - { - foreach(var updatedReleaseId in updatedReleaseIds) - { - await this.UpdateReleaseRank(updatedReleaseId); - } - } - this.CacheManager.ClearRegion(collection.CacheRegion); + foreach (var updatedReleaseId in updatedReleaseIds) + await UpdateReleaseRank(updatedReleaseId); + CacheManager.ClearRegion(collection.CacheRegion); } } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); errors.Add(ex); } + sw.Stop(); - this.Logger.LogInformation(string.Format("RescanCollection `{0}`, By User `{1}`, ElapsedTime [{2}]", collection, user, sw.ElapsedMilliseconds)); + Logger.LogInformation(string.Format("RescanCollection `{0}`, By User `{1}`, ElapsedTime [{2}]", collection, + user, sw.ElapsedMilliseconds)); return new OperationResult { @@ -726,55 +794,61 @@ namespace Roadie.Api.Services public async Task> ScanInboundFolder(ApplicationUser user, bool isReadOnly = false) { - var d = new DirectoryInfo(this.Configuration.InboundFolder); - var dest = new DirectoryInfo(this.Configuration.LibraryFolder); - return await this.ScanFolder(d, dest, user, isReadOnly); + var d = new DirectoryInfo(Configuration.InboundFolder); + var dest = new DirectoryInfo(Configuration.LibraryFolder); + return await ScanFolder(d, dest, user, isReadOnly); } public async Task> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false) { - var d = new DirectoryInfo(this.Configuration.LibraryFolder); - var dest = new DirectoryInfo(this.Configuration.LibraryFolder); - return await this.ScanFolder(d, dest, user, isReadOnly); + var d = new DirectoryInfo(Configuration.LibraryFolder); + var dest = new DirectoryInfo(Configuration.LibraryFolder); + return await ScanFolder(d, dest, user, isReadOnly); } - public async Task> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) + public async Task> ScanRelease(ApplicationUser user, Guid releaseId, + bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var release = this.DbContext.Releases - .Include(x => x.Artist) - .Include(x => x.Labels) - .FirstOrDefault(x => x.RoadieId == releaseId); + var release = DbContext.Releases + .Include(x => x.Artist) + .Include(x => x.Labels) + .FirstOrDefault(x => x.RoadieId == releaseId); if (release == null) { - await this.LogAndPublish($"ScanRelease Unknown Release [{ releaseId}]", LogLevel.Warning); - return new OperationResult(true, $"Release Not Found [{ releaseId }]"); + await LogAndPublish($"ScanRelease Unknown Release [{releaseId}]", LogLevel.Warning); + return new OperationResult(true, $"Release Not Found [{releaseId}]"); } + try { - var result = await this.ReleaseFactory.ScanReleaseFolder(release.RoadieId, this.Configuration.LibraryFolder, isReadOnly, release); - await this.UpdateReleaseRank(release.Id); - this.CacheManager.ClearRegion(release.CacheRegion); + var result = await ReleaseFactory.ScanReleaseFolder(release.RoadieId, Configuration.LibraryFolder, + isReadOnly, release); + await UpdateReleaseRank(release.Id); + CacheManager.ClearRegion(release.CacheRegion); } catch (Exception ex) { - await this.LogAndPublish(ex.ToString(), LogLevel.Error); + await LogAndPublish(ex.ToString(), LogLevel.Error); errors.Add(ex); } + sw.Stop(); - this.DbContext.ScanHistories.Add(new data.ScanHistory + DbContext.ScanHistories.Add(new data.ScanHistory { UserId = user.Id, ForReleaseId = release.Id, - NewTracks = this.ReleaseFactory.AddedTrackIds.Count(), + NewTracks = ReleaseFactory.AddedTrackIds.Count(), TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds }); - await this.DbContext.SaveChangesAsync(); - await this.LogAndPublish($"ScanRelease `{release}`, By User `{user}`, WasDoneForInvalidTrackPlay [{ wasDoneForInvalidTrackPlay }]", LogLevel.Information); + await DbContext.SaveChangesAsync(); + await LogAndPublish( + $"ScanRelease `{release}`, By User `{user}`, WasDoneForInvalidTrackPlay [{wasDoneForInvalidTrackPlay}]", + LogLevel.Information); return new OperationResult { IsSuccess = !errors.Any(), @@ -786,7 +860,7 @@ namespace Roadie.Api.Services private void EventMessageLogger_Messages(object sender, EventMessage e) { - Task.WaitAll(this.LogAndPublish(e.Message, e.Level)); + Task.WaitAll(LogAndPublish(e.Message, e.Level)); } private async Task LogAndPublish(string message, LogLevel level = LogLevel.Trace) @@ -794,38 +868,42 @@ namespace Roadie.Api.Services switch (level) { case LogLevel.Trace: - this.Logger.LogTrace(message); + Logger.LogTrace(message); break; case LogLevel.Debug: - this.Logger.LogDebug(message); + Logger.LogDebug(message); break; case LogLevel.Information: - this.Logger.LogInformation(message); + Logger.LogInformation(message); break; case LogLevel.Warning: - this.Logger.LogWarning(message); + Logger.LogWarning(message); break; case LogLevel.Critical: - this.Logger.LogCritical(message); + Logger.LogCritical(message); break; } - await this.ScanActivityHub.Clients.All.SendAsync("SendSystemActivity", message); + + await ScanActivityHub.Clients.All.SendAsync("SendSystemActivity", message); } - private async Task> ScanFolder(DirectoryInfo d, DirectoryInfo dest, ApplicationUser user, bool isReadOnly) + private async Task> ScanFolder(DirectoryInfo d, DirectoryInfo dest, ApplicationUser user, + bool isReadOnly) { var sw = new Stopwatch(); sw.Start(); long processedFiles = 0; - await this.LogAndPublish($"** Processing Folder: [{d.FullName}]"); + await LogAndPublish($"** Processing Folder: [{d.FullName}]"); long processedFolders = 0; - var folderProcessor = new FolderProcessor(this.Configuration, this.HttpEncoder, this.Configuration.LibraryFolder, this.DbContext, this.CacheManager, this.MessageLogger, this.ArtistLookupEngine, this.ArtistFactory, this.ReleaseFactory, this.ImageFactory, this.ReleaseLookupEngine, this.AudioMetaDataHelper); + var folderProcessor = new FolderProcessor(Configuration, HttpEncoder, Configuration.LibraryFolder, + DbContext, CacheManager, MessageLogger, ArtistLookupEngine, ArtistFactory, ReleaseFactory, ImageFactory, + ReleaseLookupEngine, AudioMetaDataHelper); var newArtists = 0; var newReleases = 0; @@ -835,21 +913,20 @@ namespace Roadie.Api.Services { result = await folderProcessor.Process(new DirectoryInfo(folder), isReadOnly); // Between folders flush cache, the caching for folder processing was intended for caching artist metadata lookups. Most of the time artists are in the same folder. - this.CacheManager.Clear(); + CacheManager.Clear(); processedFolders++; } + if (result.AdditionalData != null) { newArtists = SafeParser.ToNumber(result.AdditionalData["newArtists"]); newReleases = SafeParser.ToNumber(result.AdditionalData["newReleases"]); newTracks = SafeParser.ToNumber(result.AdditionalData["newTracks"]); } - if (!isReadOnly) - { - FolderProcessor.DeleteEmptyFolders(d, this.Logger); - } + + if (!isReadOnly) FolderProcessor.DeleteEmptyFolders(d, Logger); sw.Stop(); - this.DbContext.ScanHistories.Add(new data.ScanHistory + DbContext.ScanHistories.Add(new data.ScanHistory { UserId = user.Id, NewArtists = newArtists, @@ -857,9 +934,10 @@ namespace Roadie.Api.Services NewTracks = newTracks, TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds }); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.Clear(); - await this.LogAndPublish($"**Completed!Processed Folders[{ processedFolders }], Processed Files[{ processedFiles}] : Elapsed Time[{ sw.Elapsed}]"); + await DbContext.SaveChangesAsync(); + CacheManager.Clear(); + await LogAndPublish( + $"**Completed!Processed Folders[{processedFolders}], Processed Files[{processedFiles}] : Elapsed Time[{sw.Elapsed}]"); return new OperationResult { Data = true, diff --git a/Roadie.Api.Services/ArtistService.cs b/Roadie.Api.Services/ArtistService.cs index 65ecd0e..3316378 100644 --- a/Roadie.Api.Services/ArtistService.cs +++ b/Roadie.Api.Services/ArtistService.cs @@ -38,51 +38,71 @@ namespace Roadie.Api.Services public class ArtistService : ServiceBase, IArtistService { private IArtistFactory ArtistFactory { get; } + private IArtistLookupEngine ArtistLookupEngine { get; } + private IAudioMetaDataHelper AudioMetaDataHelper { get; } - private IBookmarkService BookmarkService { get; } = null; - private ICollectionService CollectionService { get; } = null; + + private IBookmarkService BookmarkService { get; } + + private ICollectionService CollectionService { get; } + private IFileNameHelper FileNameHelper { get; } + private IID3TagsHelper ID3TagsHelper { get; } + private IImageFactory ImageFactory { get; } + private ILabelFactory LabelFactory { get; } + private ILabelLookupEngine LabelLookupEngine { get; } + private ILastFmHelper LastFmHelper { get; } + private mb.IMusicBrainzProvider MusicBrainzProvider { get; } - private IPlaylistService PlaylistService { get; } = null; + + private IPlaylistService PlaylistService { get; } + private IReleaseFactory ReleaseFactory { get; } + private IReleaseLookupEngine ReleaseLookupEngine { get; } public ArtistService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext dbContext, - ICacheManager cacheManager, - ILogger logger, - ICollectionService collectionService, - IPlaylistService playlistService, - IBookmarkService bookmarkService - ) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext dbContext, + ICacheManager cacheManager, + ILogger logger, + ICollectionService collectionService, + IPlaylistService playlistService, + IBookmarkService bookmarkService + ) : base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext) { - this.CollectionService = collectionService; - this.PlaylistService = playlistService; - this.BookmarkService = bookmarkService; + CollectionService = collectionService; + PlaylistService = playlistService; + BookmarkService = bookmarkService; - this.MusicBrainzProvider = new mb.MusicBrainzProvider(configuration, cacheManager, logger); - this.LastFmHelper = new LastFmHelper(configuration, cacheManager, logger, dbContext, httpEncoder); - this.FileNameHelper = new FileNameHelper(configuration, cacheManager, logger); - this.ID3TagsHelper = new ID3TagsHelper(configuration, cacheManager, logger); - this.ArtistLookupEngine = new ArtistLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger); - this.LabelLookupEngine = new LabelLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger); - this.ReleaseLookupEngine = new ReleaseLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger, this.ArtistLookupEngine, this.LabelLookupEngine); - this.ImageFactory = new ImageFactory(configuration, httpEncoder, dbContext, cacheManager, logger, this.ArtistLookupEngine, this.ReleaseLookupEngine); - this.LabelFactory = new LabelFactory(configuration, httpEncoder, dbContext, cacheManager, logger, this.ArtistLookupEngine, this.ReleaseLookupEngine); - this.AudioMetaDataHelper = new AudioMetaDataHelper(configuration, httpEncoder, dbContext, this.MusicBrainzProvider, this.LastFmHelper, cacheManager, - logger, this.ArtistLookupEngine, this.ImageFactory, this.FileNameHelper, this.ID3TagsHelper); + MusicBrainzProvider = new mb.MusicBrainzProvider(configuration, cacheManager, logger); + LastFmHelper = new LastFmHelper(configuration, cacheManager, logger, dbContext, httpEncoder); + FileNameHelper = new FileNameHelper(configuration, cacheManager, logger); + ID3TagsHelper = new ID3TagsHelper(configuration, cacheManager, logger); + ArtistLookupEngine = new ArtistLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger); + LabelLookupEngine = new LabelLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger); + ReleaseLookupEngine = new ReleaseLookupEngine(configuration, httpEncoder, dbContext, cacheManager, logger, + ArtistLookupEngine, LabelLookupEngine); + ImageFactory = new ImageFactory(configuration, httpEncoder, dbContext, cacheManager, logger, + ArtistLookupEngine, ReleaseLookupEngine); + LabelFactory = new LabelFactory(configuration, httpEncoder, dbContext, cacheManager, logger, + ArtistLookupEngine, ReleaseLookupEngine); + AudioMetaDataHelper = new AudioMetaDataHelper(configuration, httpEncoder, dbContext, MusicBrainzProvider, + LastFmHelper, cacheManager, + logger, ArtistLookupEngine, ImageFactory, FileNameHelper, ID3TagsHelper); - this.ReleaseFactory = new ReleaseFactory(configuration, httpEncoder, dbContext, cacheManager, logger, this.ArtistLookupEngine, this.LabelFactory, this.AudioMetaDataHelper, this.ReleaseLookupEngine); - this.ArtistFactory = new ArtistFactory(configuration, httpEncoder, dbContext, cacheManager, logger, this.ArtistLookupEngine, this.ReleaseFactory, this.ImageFactory, this.ReleaseLookupEngine, this.AudioMetaDataHelper); + ReleaseFactory = new ReleaseFactory(configuration, httpEncoder, dbContext, cacheManager, logger, + ArtistLookupEngine, LabelFactory, AudioMetaDataHelper, ReleaseLookupEngine); + ArtistFactory = new ArtistFactory(configuration, httpEncoder, dbContext, cacheManager, logger, + ArtistLookupEngine, ReleaseFactory, ImageFactory, ReleaseLookupEngine, AudioMetaDataHelper); } public async Task> ById(User roadieUser, Guid id, IEnumerable includes) @@ -92,11 +112,11 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); sw.Start(); - var cacheKey = string.Format("urn:artist_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes)); - var result = await this.CacheManager.GetAsync>(cacheKey, async () => + var cacheKey = $"urn:artist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}"; + var result = await CacheManager.GetAsync(cacheKey, async () => { tsw.Restart(); - var rr = await this.ArtistByIdAction(id, includes); + var rr = await ArtistByIdAction(id, includes); tsw.Stop(); timings.Add("ArtistByIdAction", tsw.ElapsedMilliseconds); return rr; @@ -104,52 +124,56 @@ namespace Roadie.Api.Services if (result?.Data != null && roadieUser != null) { tsw.Restart(); - var artist = this.GetArtist(id); + var artist = GetArtist(id); tsw.Stop(); timings.Add("GetArtist", tsw.ElapsedMilliseconds); tsw.Restart(); - var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Artist); + var userBookmarkResult = + await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Artist); if (userBookmarkResult.IsSuccess) - { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == artist.RoadieId.ToString()) != null; - } + result.Data.UserBookmarked = + userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == artist.RoadieId.ToString()) != + null; tsw.Stop(); timings.Add("userBookmarkResult", tsw.ElapsedMilliseconds); tsw.Restart(); - var userArtist = this.DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == roadieUser.Id); + var userArtist = + DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == roadieUser.Id); if (userArtist != null) - { result.Data.UserRating = new UserArtist { IsDisliked = userArtist.IsDisliked ?? false, IsFavorite = userArtist.IsFavorite ?? false, Rating = userArtist.Rating }; - } tsw.Stop(); timings.Add("userArtist", tsw.ElapsedMilliseconds); - - if(result.Data.Comments.Any()) + + if (result.Data.Comments.Any()) { tsw.Restart(); var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + var userCommentReactions = (from cr in DbContext.CommentReactions where commentIds.Contains(cr.CommentId) where cr.UserId == roadieUser.Id select cr).ToArray(); - foreach(var comment in result.Data.Comments) + foreach (var comment in result.Data.Comments) { - var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); + var userCommentReaction = + userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; } + tsw.Stop(); timings.Add("commentReactions", tsw.ElapsedMilliseconds); } } + sw.Stop(); timings.Add("operation", sw.ElapsedMilliseconds); - this.Logger.LogDebug("ById Timings: id [{0}], includes [{1}], timings [{3}]", id, includes, JsonConvert.SerializeObject(timings)); + Logger.LogDebug("ById Timings: id [{0}], includes [{1}], timings [{3}]", id, includes, + JsonConvert.SerializeObject(timings)); return new OperationResult(result.Messages) { Data = result?.Data, @@ -160,44 +184,42 @@ namespace Roadie.Api.Services }; } - public async Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true) + public async Task> List(User roadieUser, PagedRequest request, + bool? doRandomize = false, bool? onlyIncludeWithReleases = true) { var sw = new Stopwatch(); sw.Start(); IQueryable favoriteArtistIds = null; if (request.FilterFavoriteOnly) - { - favoriteArtistIds = (from a in this.DbContext.Artists - join ua in this.DbContext.UserArtists on a.Id equals ua.ArtistId - where ua.IsFavorite ?? false - where (roadieUser == null || ua.UserId == roadieUser.Id) - select a.Id - ); - } + favoriteArtistIds = from a in DbContext.Artists + join ua in DbContext.UserArtists on a.Id equals ua.ArtistId + where ua.IsFavorite ?? false + where roadieUser == null || ua.UserId == roadieUser.Id + select a.Id; IQueryable labelArtistIds = null; if (request.FilterToLabelId.HasValue) - { - labelArtistIds = (from l in this.DbContext.Labels - join rl in this.DbContext.ReleaseLabels on l.Id equals rl.LabelId - join r in this.DbContext.Releases on rl.ReleaseId equals r.Id + labelArtistIds = (from l in DbContext.Labels + join rl in DbContext.ReleaseLabels on l.Id equals rl.LabelId + join r in DbContext.Releases on rl.ReleaseId equals r.Id where l.RoadieId == request.FilterToLabelId select r.ArtistId) - .Distinct(); - } + .Distinct(); IQueryable genreArtistIds = null; var isFilteredToGenre = false; - if (!string.IsNullOrEmpty(request.Filter) && request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(request.Filter) && + request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase)) { var genreFilter = request.Filter.Replace(":genre ", ""); - genreArtistIds = (from ag in this.DbContext.ArtistGenres - join g in this.DbContext.Genres on ag.GenreId equals g.Id + genreArtistIds = (from ag in DbContext.ArtistGenres + join g in DbContext.Genres on ag.GenreId equals g.Id where g.Name.Contains(genreFilter) select ag.ArtistId) - .Distinct(); + .Distinct(); isFilteredToGenre = true; request.Filter = null; } + var onlyWithReleases = onlyIncludeWithReleases ?? true; var isEqualFilter = false; if (!string.IsNullOrEmpty(request.FilterValue)) @@ -210,22 +232,22 @@ namespace Roadie.Api.Services request.Filter = filter.Substring(1, filter.Length - 2); } } - var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) ? request.FilterValue.ToAlphanumericName() : null; - var result = (from a in this.DbContext.Artists - where (!onlyWithReleases || a.ReleaseCount > 0) - where (request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId) - where (request.FilterMinimumRating == null || a.Rating >= request.FilterMinimumRating.Value) - where (request.FilterValue == "" || (a.Name.Contains(request.FilterValue) || - a.SortName.Contains(request.FilterValue) || - a.AlternateNames.Contains(request.FilterValue) || - a.AlternateNames.Contains(normalizedFilterValue))) - where (!isEqualFilter || (a.Name.Equals(request.FilterValue) || - a.SortName.Equals(request.FilterValue) || - a.AlternateNames.Equals(request.FilterValue) || - a.AlternateNames.Equals(normalizedFilterValue))) - where (!request.FilterFavoriteOnly || favoriteArtistIds.Contains(a.Id)) - where (request.FilterToLabelId == null || labelArtistIds.Contains(a.Id)) - where (!isFilteredToGenre || genreArtistIds.Contains(a.Id)) + + var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) + ? request.FilterValue.ToAlphanumericName() + : null; + var result = (from a in DbContext.Artists + where !onlyWithReleases || a.ReleaseCount > 0 + where request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId + where request.FilterMinimumRating == null || a.Rating >= request.FilterMinimumRating.Value + where request.FilterValue == "" || a.Name.Contains(request.FilterValue) || + a.SortName.Contains(request.FilterValue) || a.AlternateNames.Contains(request.FilterValue) || + a.AlternateNames.Contains(normalizedFilterValue) + where !isEqualFilter || a.Name.Equals(request.FilterValue) || a.SortName.Equals(request.FilterValue) || + a.AlternateNames.Equals(request.FilterValue) || a.AlternateNames.Equals(normalizedFilterValue) + where !request.FilterFavoriteOnly || favoriteArtistIds.Contains(a.Id) + where request.FilterToLabelId == null || labelArtistIds.Contains(a.Id) + where !isFilteredToGenre || genreArtistIds.Contains(a.Id) select new ArtistList { DatabaseId = a.Id, @@ -235,7 +257,7 @@ namespace Roadie.Api.Services Text = a.Name, Value = a.RoadieId.ToString() }, - Thumbnail = this.MakeArtistThumbnailImage(a.RoadieId), + Thumbnail = MakeArtistThumbnailImage(a.RoadieId), Rating = a.Rating, Rank = a.Rank, CreatedDate = a.CreatedDate, @@ -247,7 +269,7 @@ namespace Roadie.Api.Services SortName = a.SortName }).Distinct(); - ArtistList[] rows = null; + ArtistList[] rows; var rowCount = result.Count(); if (doRandomize ?? false) { @@ -257,30 +279,31 @@ namespace Roadie.Api.Services } else { - string sortBy = "Id"; + string sortBy; if (request.ActionValue == User.ActionKeyUserRated) - { - sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "Rating", "DESC" }, { "Artist.Text", "ASC" } }) : request.OrderValue(null); - } + sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue( + new Dictionary { { "Rating", "DESC" }, { "Artist.Text", "ASC" } }) + : request.OrderValue(); else - { - sortBy = request.OrderValue(new Dictionary { { "SortName", "ASC" }, { "Artist.Text", "ASC" } }); - } + sortBy = request.OrderValue(new Dictionary + {{"SortName", "ASC"}, {"Artist.Text", "ASC"}}); rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); } + if (rows.Any() && roadieUser != null) { var rowIds = rows.Select(x => x.DatabaseId).ToArray(); - var userArtistRatings = (from ua in this.DbContext.UserArtists + var userArtistRatings = (from ua in DbContext.UserArtists where ua.UserId == roadieUser.Id where rowIds.Contains(ua.ArtistId) select ua).ToArray(); - foreach (var userArtistRating in userArtistRatings.Where(x => rows.Select(r => r.DatabaseId).Contains(x.ArtistId))) + foreach (var userArtistRating in userArtistRatings.Where(x => + rows.Select(r => r.DatabaseId).Contains(x.ArtistId))) { var row = rows.FirstOrDefault(x => x.DatabaseId == userArtistRating.ArtistId); if (row != null) - { row.UserRating = new UserArtist { IsDisliked = userArtistRating.IsDisliked ?? false, @@ -288,12 +311,11 @@ namespace Roadie.Api.Services Rating = userArtistRating.Rating, RatedDate = userArtistRating.LastUpdated ?? userArtistRating.CreatedDate }; - } } } + sw.Stop(); if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0) - { if (Configuration.RecordNoResultSearches) { // Create request for no artist found @@ -302,10 +324,10 @@ namespace Roadie.Api.Services UserId = roadieUser?.Id, Description = request.Filter }; - this.DbContext.Requests.Add(req); - await this.DbContext.SaveChangesAsync(); + DbContext.Requests.Add(req); + await DbContext.SaveChangesAsync(); } - } + return new Library.Models.Pagination.PagedResult { TotalCount = rowCount, @@ -322,40 +344,43 @@ namespace Roadie.Api.Services sw.Start(); var errors = new List(); - var artistToMerge = this.DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == artistToMergeId); + var artistToMerge = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == artistToMergeId); if (artistToMerge == null) { - this.Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeId); - return new OperationResult(true, string.Format("Artist Not Found [{0}]", artistToMergeId)); + Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeId); + return new OperationResult(true, $"Artist Not Found [{artistToMergeId}]"); } - var mergeIntoArtist = this.DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == artistToMergeIntoId); + + var mergeIntoArtist = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == artistToMergeIntoId); if (mergeIntoArtist == null) { - this.Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeIntoId); - return new OperationResult(true, string.Format("Artist Not Found [{0}]", artistToMergeIntoId)); + Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeIntoId); + return new OperationResult(true, $"Artist Not Found [{artistToMergeIntoId}]"); } try { - var result = await this.ArtistFactory.MergeArtists(artistToMerge, mergeIntoArtist, true); + var result = await ArtistFactory.MergeArtists(artistToMerge, mergeIntoArtist, true); if (!result.IsSuccess) { - this.CacheManager.ClearRegion(artistToMerge.CacheRegion); - this.CacheManager.ClearRegion(mergeIntoArtist.CacheRegion); - this.Logger.LogInformation("MergeArtists `{0}` => `{1}`, By User `{2}`", artistToMerge, mergeIntoArtist, user); + CacheManager.ClearRegion(artistToMerge.CacheRegion); + CacheManager.ClearRegion(mergeIntoArtist.CacheRegion); + Logger.LogInformation("MergeArtists `{0}` => `{1}`, By User `{2}`", artistToMerge, mergeIntoArtist, + user); } } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); errors.Add(ex); } + sw.Stop(); return new OperationResult @@ -367,102 +392,9 @@ namespace Roadie.Api.Services }; } - public async Task> ScanArtistReleasesFolders(Guid artistId, string destinationFolder, bool doJustInfo) + public async Task> SetReleaseImageByUrl(User user, Guid id, string imageUrl) { - SimpleContract.Requires(artistId != Guid.Empty, "Invalid ArtistId"); - - var result = true; - var resultErrors = new List(); - var sw = new Stopwatch(); - sw.Start(); - try - { - var artist = this.DbContext.Artists - .Include("Releases") - .Include("Releases.Labels") - .FirstOrDefault(x => x.RoadieId == artistId); - if (artist == null) - { - this.Logger.LogWarning("Unable To Find Artist [{0}]", artistId); - return new OperationResult(); - } - var releaseScannedCount = 0; - var artistFolder = artist.ArtistFileFolder(this.Configuration, destinationFolder); - var scannedArtistFolders = new List(); - // Scan known releases for changes - if (artist.Releases != null) - { - foreach (var release in artist.Releases) - { - try - { - result = result && (await this.ReleaseFactory.ScanReleaseFolder(Guid.Empty, destinationFolder, doJustInfo, release)).Data; - releaseScannedCount++; - scannedArtistFolders.Add(release.ReleaseFileFolder(artistFolder)); - } - catch (Exception ex) - { - this.Logger.LogError(ex, ex.Serialize()); - } - } - } - // Any folder found in Artist folder not already scanned scan - var folderProcessor = new FolderProcessor(this.Configuration, this.HttpEncoder, destinationFolder, this.DbContext, this.CacheManager, this.Logger, this.ArtistLookupEngine, this.ArtistFactory, this.ReleaseFactory, this.ImageFactory, this.ReleaseLookupEngine, this.AudioMetaDataHelper); - var nonReleaseFolders = (from d in Directory.EnumerateDirectories(artistFolder) - where !(from r in scannedArtistFolders select r).Contains(d) - orderby d - select d); - foreach (var folder in nonReleaseFolders) - { - await folderProcessor.Process(new DirectoryInfo(folder), doJustInfo); - } - if (!doJustInfo) - { - FolderProcessor.DeleteEmptyFolders(new DirectoryInfo(artistFolder), this.Logger); - } - - // Always update artist image if artist image is found on an artist rescan - var imageFiles = ImageHelper.ImageFilesInFolder(artistFolder, SearchOption.AllDirectories); - if (imageFiles != null && imageFiles.Any()) - { - var imageFile = imageFiles.First(); - var i = new FileInfo(imageFile); - var iName = i.Name.ToLower().Trim(); - var isArtistImage = iName.Contains("artist"); - if (isArtistImage) - { - // Read image and convert to jpeg - artist.Thumbnail = File.ReadAllBytes(i.FullName); - artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); - artist.Thumbnail = ImageHelper.ConvertToJpegFormat(artist.Thumbnail); - artist.LastUpdated = DateTime.UtcNow; - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(artist.CacheRegion); - this.Logger.LogInformation("Update Thumbnail using Artist File [{0}]", iName); - } - } - - sw.Stop(); - this.CacheManager.ClearRegion(artist.CacheRegion); - this.Logger.LogInformation("Scanned Artist [{0}], Releases Scanned [{1}], OperationTime [{2}]", artist.ToString(), releaseScannedCount, sw.ElapsedMilliseconds); - } - catch (Exception ex) - { - this.Logger.LogError(ex, ex.Serialize()); - resultErrors.Add(ex); - } - return new OperationResult - { - Data = result, - IsSuccess = result, - Errors = resultErrors, - OperationTime = sw.ElapsedMilliseconds - }; - } - - public async Task> SetReleaseImageByUrl(User user, Guid id, string imageUrl) - { - return await this.SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); + return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl)); } public async Task> UpdateArtist(User user, Artist model) @@ -472,24 +404,20 @@ namespace Roadie.Api.Services var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = this.DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == model.Id); + var artist = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == model.Id); if (artist == null) - { - return new OperationResult(true, string.Format("Artist Not Found [{0}]", model.Id)); - } + return new OperationResult(true, $"Artist Not Found [{model.Id}]"); try { var now = DateTime.UtcNow; - var originalArtistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); + var originalArtistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); var specialArtistName = model.Name.ToAlphanumericName(); var alt = new List(model.AlternateNamesList); if (!model.AlternateNamesList.Contains(specialArtistName, StringComparer.OrdinalIgnoreCase)) - { alt.Add(specialArtistName); - } artist.AlternateNames = alt.ToDelimitedList(); artist.ArtistType = model.ArtistType; artist.AmgId = model.AmgId; @@ -512,15 +440,16 @@ namespace Roadie.Api.Services artist.Tags = model.TagsList.ToDelimitedList(); artist.URLs = model.URLsList.ToDelimitedList(); - var newArtistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); + var newArtistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); if (!newArtistFolder.Equals(originalArtistFolder, StringComparison.OrdinalIgnoreCase)) { didRenameArtist = true; // Rename artist folder to reflect new artist name - this.Logger.LogTrace("Moving Artist From Folder [{0}] To [{1}]", originalArtistFolder, newArtistFolder); + Logger.LogTrace("Moving Artist From Folder [{0}] To [{1}]", originalArtistFolder, newArtistFolder); Directory.Move(originalArtistFolder, newArtistFolder); } + var artistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData); if (artistImage != null) { @@ -532,7 +461,8 @@ namespace Roadie.Api.Services File.WriteAllBytes(artistImageName, artist.Thumbnail); // Resize to store in database as thumbnail - artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); + artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail, Configuration.MediumImageSize.Width, + Configuration.MediumImageSize.Height); didChangeThumbnail = true; } @@ -548,14 +478,18 @@ namespace Roadie.Api.Services // Ensure is jpeg first artistSecondaryImage = ImageHelper.ConvertToJpegFormat(artistSecondaryImage); - var artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + var artistImageFilename = Path.Combine(newArtistFolder, + string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); while (File.Exists(artistImageFilename)) { looper++; - artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); + artistImageFilename = Path.Combine(newArtistFolder, + string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00"))); } + File.WriteAllBytes(artistImageFilename, artistSecondaryImage); } + looper++; } } @@ -565,11 +499,9 @@ namespace Roadie.Api.Services // Remove existing Genres not in model list foreach (var genre in artist.Genres.ToList()) { - var doesExistInModel = model.Genres.Any(x => SafeParser.ToGuid(x.Value) == genre.Genre.RoadieId); - if (!doesExistInModel) - { - artist.Genres.Remove(genre); - } + var doesExistInModel = + model.Genres.Any(x => SafeParser.ToGuid(x.Value) == genre.Genre.RoadieId); + if (!doesExistInModel) artist.Genres.Remove(genre); } // Add new Genres in model not in data @@ -579,16 +511,14 @@ namespace Roadie.Api.Services var doesExistInData = artist.Genres.Any(x => x.Genre.RoadieId == genreId); if (!doesExistInData) { - var g = this.DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId); + var g = DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId); if (g != null) - { artist.Genres.Add(new data.ArtistGenre { ArtistId = artist.Id, GenreId = g.Id, Genre = g }); - } } } } @@ -599,34 +529,32 @@ namespace Roadie.Api.Services if (model.AssociatedArtistsTokens != null && model.AssociatedArtistsTokens.Any()) { - var associatedArtists = this.DbContext.ArtistAssociations.Include(x => x.AssociatedArtist).Where(x => x.ArtistId == artist.Id).ToList(); + var associatedArtists = DbContext.ArtistAssociations.Include(x => x.AssociatedArtist) + .Where(x => x.ArtistId == artist.Id).ToList(); // Remove existing AssociatedArtists not in model list foreach (var associatedArtist in associatedArtists) { - var doesExistInModel = model.AssociatedArtistsTokens.Any(x => SafeParser.ToGuid(x.Value) == associatedArtist.AssociatedArtist.RoadieId); - if (!doesExistInModel) - { - this.DbContext.ArtistAssociations.Remove(associatedArtist); - } + var doesExistInModel = model.AssociatedArtistsTokens.Any(x => + SafeParser.ToGuid(x.Value) == associatedArtist.AssociatedArtist.RoadieId); + if (!doesExistInModel) DbContext.ArtistAssociations.Remove(associatedArtist); } // Add new AssociatedArtists in model not in data foreach (var associatedArtist in model.AssociatedArtistsTokens) { var associatedArtistId = SafeParser.ToGuid(associatedArtist.Value); - var doesExistInData = associatedArtists.Any(x => x.AssociatedArtist.RoadieId == associatedArtistId); + var doesExistInData = + associatedArtists.Any(x => x.AssociatedArtist.RoadieId == associatedArtistId); if (!doesExistInData) { - var a = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == associatedArtistId); + var a = DbContext.Artists.FirstOrDefault(x => x.RoadieId == associatedArtistId); if (a != null) - { - this.DbContext.ArtistAssociations.Add(new data.ArtistAssociation + DbContext.ArtistAssociations.Add(new data.ArtistAssociation { ArtistId = artist.Id, AssociatedArtistId = a.Id }); - } } } } @@ -641,30 +569,34 @@ namespace Roadie.Api.Services } artist.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync(); if (didRenameArtist) { // Update artist tracks to have new artist name in ID3 metadata foreach (var mp3 in Directory.GetFiles(newArtistFolder, "*.mp3", SearchOption.AllDirectories)) { var trackFileInfo = new FileInfo(mp3); - var audioMetaData = await this.AudioMetaDataHelper.GetInfo(trackFileInfo); + var audioMetaData = await AudioMetaDataHelper.GetInfo(trackFileInfo); if (audioMetaData != null) { audioMetaData.Artist = artist.Name; - this.AudioMetaDataHelper.WriteTags(audioMetaData, trackFileInfo); + AudioMetaDataHelper.WriteTags(audioMetaData, trackFileInfo); } } - await this.ScanArtistReleasesFolders(artist.RoadieId, this.Configuration.LibraryFolder, false); + + await ScanArtistReleasesFolders(artist.RoadieId, Configuration.LibraryFolder, false); } - this.CacheManager.ClearRegion(artist.CacheRegion); - this.Logger.LogInformation($"UpdateArtist `{ artist }` By User `{ user }`: Renamed Artist [{ didRenameArtist }], Uploaded new image [{ didChangeThumbnail }]"); + + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogInformation( + $"UpdateArtist `{artist}` By User `{user}`: Renamed Artist [{didRenameArtist}], Uploaded new image [{didChangeThumbnail}]"); } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); errors.Add(ex); } + sw.Stop(); return new OperationResult @@ -676,7 +608,7 @@ namespace Roadie.Api.Services }; } - public async Task> UploadArtistImage(User user, Guid id, IFormFile file) + public async Task> UploadArtistImage(User user, Guid id, IFormFile file) { var bytes = new byte[0]; using (var ms = new MemoryStream()) @@ -684,7 +616,8 @@ namespace Roadie.Api.Services file.CopyTo(ms); bytes = ms.ToArray(); } - return await this.SaveImageBytes(user, id, bytes); + + return await SaveImageBytes(user, id, bytes); } private async Task> ArtistByIdAction(Guid id, IEnumerable includes) @@ -696,27 +629,32 @@ namespace Roadie.Api.Services sw.Start(); tsw.Restart(); - var artist = this.GetArtist(id); + var artist = GetArtist(id); tsw.Stop(); timings.Add("getArtist", tsw.ElapsedMilliseconds); - if (artist == null) - { - return new OperationResult(true, string.Format("Artist Not Found [{0}]", id)); - } + if (artist == null) return new OperationResult(true, $"Artist Not Found [{id}]"); tsw.Restart(); var result = artist.Adapt(); result.BandStatus = result.BandStatus ?? BandStatus.Unknown.ToString(); - result.BeginDate = result.BeginDate == null || result.BeginDate == DateTime.MinValue ? null : result.BeginDate; + result.BeginDate = result.BeginDate == null || result.BeginDate == DateTime.MinValue + ? null + : result.BeginDate; result.EndDate = result.EndDate == null || result.EndDate == DateTime.MinValue ? null : result.EndDate; - result.BirthDate = result.BirthDate == null || result.BirthDate == DateTime.MinValue ? null : result.BirthDate; - result.RankPosition = result.Rank > 0 ? SafeParser.ToNumber(this.DbContext.Artists.Count(x => x.Rank > result.Rank) + 1) : null; + result.BirthDate = result.BirthDate == null || result.BirthDate == DateTime.MinValue + ? null + : result.BirthDate; + result.RankPosition = result.Rank > 0 + ? SafeParser.ToNumber(DbContext.Artists.Count(x => x.Rank > result.Rank) + 1) + : null; tsw.Stop(); timings.Add("adaptArtist", tsw.ElapsedMilliseconds); - result.Thumbnail = base.MakeArtistThumbnailImage(id); - result.MediumThumbnail = base.MakeThumbnailImage(id, "artist", this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); + result.Thumbnail = MakeArtistThumbnailImage(id); + result.MediumThumbnail = MakeThumbnailImage(id, "artist", Configuration.MediumImageSize.Width, + Configuration.MediumImageSize.Height); tsw.Restart(); - result.Genres = artist.Genres.Select(x => new DataToken { Text = x.Genre.Name, Value = x.Genre.RoadieId.ToString() }); + result.Genres = artist.Genres.Select(x => new DataToken + { Text = x.Genre.Name, Value = x.Genre.RoadieId.ToString() }); tsw.Stop(); timings.Add("genres", tsw.ElapsedMilliseconds); @@ -725,69 +663,74 @@ namespace Roadie.Api.Services if (includes.Contains("releases")) { var dtoReleases = new List(); - foreach (var release in this.DbContext.Releases.Include("Medias").Include("Medias.Tracks").Include("Medias.Tracks").Where(x => x.ArtistId == artist.Id).ToArray()) + foreach (var release in DbContext.Releases.Include("Medias").Include("Medias.Tracks") + .Include("Medias.Tracks").Where(x => x.ArtistId == artist.Id).ToArray()) { var releaseList = release.Adapt(); - releaseList.Thumbnail = base.MakeReleaseThumbnailImage(release.RoadieId); + releaseList.Thumbnail = MakeReleaseThumbnailImage(release.RoadieId); var dtoReleaseMedia = new List(); if (includes.Contains("tracks")) - { foreach (var releasemedia in release.Medias.OrderBy(x => x.MediaNumber).ToArray()) { var dtoMedia = releasemedia.Adapt(); var tracks = new List(); - foreach (var t in this.DbContext.Tracks.Where(x => x.ReleaseMediaId == releasemedia.Id).OrderBy(x => x.TrackNumber).ToArray()) + foreach (var t in DbContext.Tracks.Where(x => x.ReleaseMediaId == releasemedia.Id) + .OrderBy(x => x.TrackNumber).ToArray()) { var track = t.Adapt(); ArtistList trackArtist = null; if (t.ArtistId.HasValue) { - var ta = this.DbContext.Artists.FirstOrDefault(x => x.Id == t.ArtistId.Value); + var ta = DbContext.Artists.FirstOrDefault(x => x.Id == t.ArtistId.Value); if (ta != null) - { - trackArtist = ArtistList.FromDataArtist(ta, this.MakeArtistThumbnailImage(ta.RoadieId)); - } + trackArtist = ArtistList.FromDataArtist(ta, + MakeArtistThumbnailImage(ta.RoadieId)); } + track.TrackArtist = trackArtist; tracks.Add(track); } + dtoMedia.Tracks = tracks; dtoReleaseMedia.Add(dtoMedia); } - } + releaseList.Media = dtoReleaseMedia; dtoReleases.Add(releaseList); } + result.Releases = dtoReleases; } + if (includes.Contains("stats")) - { try { tsw.Restart(); - var artistTracks = (from r in this.DbContext.Releases - 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 (r.ArtistId == artist.Id || t.ArtistId == artist.Id) - select new - { - t.Id, - size = t.FileSize, - time = t.Duration, - isMissing = t.Hash == null - }); + var artistTracks = from r in DbContext.Releases + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId + where r.ArtistId == artist.Id || t.ArtistId == artist.Id + select new + { + t.Id, + size = t.FileSize, + time = t.Duration, + isMissing = t.Hash == null + }; var validCartistTracks = artistTracks.Where(x => !x.isMissing); - long? trackTime = validCartistTracks.Sum(x => (long?)x.time); + var trackTime = validCartistTracks.Sum(x => (long?)x.time); result.Statistics = new CollectionStatistics { FileSize = artistTracks.Sum(x => (long?)x.size).ToFileSize(), - MissingTrackCount = artistTracks.Where(x => x.isMissing).Count(), + MissingTrackCount = artistTracks.Count(x => x.isMissing), ReleaseCount = artist.ReleaseCount, - ReleaseMediaCount = (from r in this.DbContext.Releases - join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + ReleaseMediaCount = (from r in DbContext.Releases + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId where r.ArtistId == artist.Id select rm.Id).Count(), - TrackTime = validCartistTracks.Any() ? new TimeInfo((decimal)trackTime).ToFullFormattedString() : "--:--", + TrackTime = validCartistTracks.Any() + ? new TimeInfo((decimal)trackTime).ToFullFormattedString() + : "--:--", TrackCount = validCartistTracks.Count(), TrackPlayedCount = artist.PlayedCount }; @@ -796,29 +739,31 @@ namespace Roadie.Api.Services } catch (Exception ex) { - this.Logger.LogError(ex, $"Error Getting Statistics for Artist `{ artist }`"); + Logger.LogError(ex, $"Error Getting Statistics for Artist `{artist}`"); } - } + if (includes.Contains("images")) { tsw.Restart(); - result.Images = this.DbContext.Images.Where(x => x.ArtistId == artist.Id).Select(x => MakeFullsizeImage(x.RoadieId, x.Caption)).ToArray(); + result.Images = DbContext.Images.Where(x => x.ArtistId == artist.Id) + .Select(x => MakeFullsizeImage(x.RoadieId, x.Caption)).ToArray(); - var artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); - var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly); + var artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); + var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), + ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly); if (artistImagesInFolder.Any()) - { - result.Images = result.Images.Concat(artistImagesInFolder.Select((x, i) => MakeFullsizeSecondaryImage(id, ImageType.ArtistSecondary, i))); - } + result.Images = result.Images.Concat(artistImagesInFolder.Select((x, i) => + MakeFullsizeSecondaryImage(id, ImageType.ArtistSecondary, i))); tsw.Stop(); timings.Add("images", tsw.ElapsedMilliseconds); } + if (includes.Contains("associatedartists")) { tsw.Restart(); - var associatedWithArtists = (from aa in this.DbContext.ArtistAssociations - join a in this.DbContext.Artists on aa.AssociatedArtistId equals a.Id + var associatedWithArtists = (from aa in DbContext.ArtistAssociations + join a in DbContext.Artists on aa.AssociatedArtistId equals a.Id where aa.ArtistId == artist.Id select new ArtistList { @@ -829,7 +774,7 @@ namespace Roadie.Api.Services Text = a.Name, Value = a.RoadieId.ToString() }, - Thumbnail = this.MakeArtistThumbnailImage(a.RoadieId), + Thumbnail = MakeArtistThumbnailImage(a.RoadieId), Rating = a.Rating, Rank = a.Rank, CreatedDate = a.CreatedDate, @@ -841,8 +786,8 @@ namespace Roadie.Api.Services SortName = a.SortName }).ToArray(); - var associatedArtists = (from aa in this.DbContext.ArtistAssociations - join a in this.DbContext.Artists on aa.ArtistId equals a.Id + var associatedArtists = (from aa in DbContext.ArtistAssociations + join a in DbContext.Artists on aa.ArtistId equals a.Id where aa.AssociatedArtistId == artist.Id select new ArtistList { @@ -853,7 +798,7 @@ namespace Roadie.Api.Services Text = a.Name, Value = a.RoadieId.ToString() }, - Thumbnail = this.MakeArtistThumbnailImage(a.RoadieId), + Thumbnail = MakeArtistThumbnailImage(a.RoadieId), Rating = a.Rating, Rank = a.Rank, CreatedDate = a.CreatedDate, @@ -864,11 +809,13 @@ namespace Roadie.Api.Services TrackCount = a.TrackCount, SortName = a.SortName }).ToArray(); - result.AssociatedArtists = associatedArtists.Union(associatedWithArtists, new ArtistListComparer()).OrderBy(x => x.SortName); + result.AssociatedArtists = associatedArtists.Union(associatedWithArtists, new ArtistListComparer()) + .OrderBy(x => x.SortName); result.AssociatedArtistsTokens = result.AssociatedArtists.Select(x => x.Artist).ToArray(); tsw.Stop(); timings.Add("associatedartists", tsw.ElapsedMilliseconds); } + if (includes.Contains("collections")) { tsw.Restart(); @@ -876,40 +823,45 @@ namespace Roadie.Api.Services { Limit = 100 }; - var r = await this.CollectionService.List(roadieUser: null, - request: collectionPagedRequest, artistId: artist.RoadieId); - if (r.IsSuccess) - { - result.CollectionsWithArtistReleases = r.Rows.ToArray(); - } + var r = await CollectionService.List(null, + collectionPagedRequest, artistId: artist.RoadieId); + if (r.IsSuccess) result.CollectionsWithArtistReleases = r.Rows.ToArray(); tsw.Stop(); timings.Add("collections", tsw.ElapsedMilliseconds); } - if(includes.Contains("comments")) + + if (includes.Contains("comments")) { tsw.Restart(); - var artistComments = this.DbContext.Comments.Include(x => x.User).Where(x => x.ArtistId == artist.Id).OrderByDescending(x => x.CreatedDate).ToArray(); - if(artistComments.Any()) + var artistComments = DbContext.Comments.Include(x => x.User).Where(x => x.ArtistId == artist.Id) + .OrderByDescending(x => x.CreatedDate).ToArray(); + if (artistComments.Any()) { var comments = new List(); var commentIds = artistComments.Select(x => x.Id).ToArray(); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + var userCommentReactions = (from cr in DbContext.CommentReactions where commentIds.Contains(cr.CommentId) select cr).ToArray(); foreach (var artistComment in artistComments) { var comment = artistComment.Adapt(); comment.DatabaseId = artistComment.Id; - comment.User = UserList.FromDataUser(artistComment.User, this.MakeUserThumbnailImage(artistComment.User.RoadieId)); - comment.DislikedCount = userCommentReactions.Count(x => x.CommentId == artistComment.Id && x.ReactionValue == CommentReaction.Dislike); - comment.LikedCount = userCommentReactions.Count(x => x.CommentId == artistComment.Id && x.ReactionValue == CommentReaction.Like); + comment.User = UserList.FromDataUser(artistComment.User, + MakeUserThumbnailImage(artistComment.User.RoadieId)); + comment.DislikedCount = userCommentReactions.Count(x => + x.CommentId == artistComment.Id && x.ReactionValue == CommentReaction.Dislike); + comment.LikedCount = userCommentReactions.Count(x => + x.CommentId == artistComment.Id && x.ReactionValue == CommentReaction.Like); comments.Add(comment); - } + } + result.Comments = comments; } + tsw.Stop(); timings.Add("comments", tsw.ElapsedMilliseconds); } + if (includes.Contains("playlists")) { tsw.Restart(); @@ -917,50 +869,56 @@ namespace Roadie.Api.Services { FilterToArtistId = artist.RoadieId }; - var r = await this.PlaylistService.List(pg); - if (r.IsSuccess) - { - result.PlaylistsWithArtistReleases = r.Rows.ToArray(); - } + var r = await PlaylistService.List(pg); + if (r.IsSuccess) result.PlaylistsWithArtistReleases = r.Rows.ToArray(); tsw.Stop(); timings.Add("playlists", tsw.ElapsedMilliseconds); } + if (includes.Contains("contributions")) { tsw.Restart(); - result.ArtistContributionReleases = (from t in this.DbContext.Tracks - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id - join r in this.DbContext.Releases.Include(x => x.Artist) on rm.ReleaseId equals r.Id + result.ArtistContributionReleases = (from t in DbContext.Tracks + join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + join r in DbContext.Releases.Include(x => x.Artist) on rm.ReleaseId equals r.Id where t.ArtistId == artist.Id - group r by r.Id into rr + group r by r.Id + into rr select rr) - .ToArray() - .Select(rr => rr.First()) - .Select(r => ReleaseList.FromDataRelease(r, r.Artist, this.HttpContext.BaseUrl, MakeArtistThumbnailImage(r.Artist.RoadieId), MakeReleaseThumbnailImage(r.RoadieId))) - .ToArray().OrderBy(x => x.Release.Text).ToArray(); - result.ArtistContributionReleases = result.ArtistContributionReleases.Any() ? result.ArtistContributionReleases : null; + .ToArray() + .Select(rr => rr.First()) + .Select(r => ReleaseList.FromDataRelease(r, r.Artist, HttpContext.BaseUrl, + MakeArtistThumbnailImage(r.Artist.RoadieId), MakeReleaseThumbnailImage(r.RoadieId))) + .ToArray().OrderBy(x => x.Release.Text).ToArray(); + result.ArtistContributionReleases = result.ArtistContributionReleases.Any() + ? result.ArtistContributionReleases + : null; tsw.Stop(); timings.Add("contributions", tsw.ElapsedMilliseconds); } + if (includes.Contains("labels")) { tsw.Restart(); - result.ArtistLabels = (from l in this.DbContext.Labels - join rl in this.DbContext.ReleaseLabels on l.Id equals rl.LabelId - join r in this.DbContext.Releases on rl.ReleaseId equals r.Id + result.ArtistLabels = (from l in DbContext.Labels + join rl in DbContext.ReleaseLabels on l.Id equals rl.LabelId + join r in DbContext.Releases on rl.ReleaseId equals r.Id where r.ArtistId == artist.Id orderby l.SortName - select LabelList.FromDataLabel(l, this.MakeLabelThumbnailImage(l.RoadieId))) - .ToArray() - .GroupBy(x => x.Label.Value).Select(x => x.First()).OrderBy(x => x.SortName).ThenBy(x => x.Label.Text).ToArray(); + select LabelList.FromDataLabel(l, MakeLabelThumbnailImage(l.RoadieId))) + .ToArray() + .GroupBy(x => x.Label.Value).Select(x => x.First()).OrderBy(x => x.SortName) + .ThenBy(x => x.Label.Text).ToArray(); result.ArtistLabels = result.ArtistLabels.Any() ? result.ArtistLabels : null; tsw.Stop(); timings.Add("labels", tsw.ElapsedMilliseconds); } } + sw.Stop(); timings.Add("operation", sw.ElapsedMilliseconds); - this.Logger.LogDebug("ArtistByIdAction Timings: id [{0}], includes [{1}], timings [{3}]", id, includes, JsonConvert.SerializeObject(timings)); + Logger.LogDebug("ArtistByIdAction Timings: id [{0}], includes [{1}], timings [{3}]", id, includes, + JsonConvert.SerializeObject(timings)); return new OperationResult { @@ -970,16 +928,13 @@ namespace Roadie.Api.Services }; } - private async Task> SaveImageBytes(User user, Guid id, byte[] imageBytes) + private async Task> SaveImageBytes(User user, Guid id, byte[] imageBytes) { var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == id); - if (artist == null) - { - return new OperationResult(true, string.Format("Artist Not Found [{0}]", id)); - } + var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == id); + if (artist == null) return new OperationResult(true, $"Artist Not Found [{id}]"); try { var now = DateTime.UtcNow; @@ -990,11 +945,11 @@ namespace Roadie.Api.Services artist.Thumbnail = ImageHelper.ConvertToJpegFormat(artist.Thumbnail); // Ensure artist folder exists - var artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); + var artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); if (!Directory.Exists(artistFolder)) { Directory.CreateDirectory(artistFolder); - this.Logger.LogInformation("Created Artist Folder [0] for `artist`", artistFolder, artist); + Logger.LogInformation("Created Artist Folder [0] for `artist`", artistFolder, artist); } // Save unaltered image to artist file @@ -1002,27 +957,124 @@ namespace Roadie.Api.Services File.WriteAllBytes(artistImage, artist.Thumbnail); // Resize to store in database as thumbnail - artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); + artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail, Configuration.MediumImageSize.Width, + Configuration.MediumImageSize.Height); } + artist.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(artist.CacheRegion); - this.Logger.LogInformation($"SaveImageBytes `{ artist }` By User `{ user }`"); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogInformation($"SaveImageBytes `{artist}` By User `{user}`"); } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); errors.Add(ex); } + sw.Stop(); - return new OperationResult + return new OperationResult { IsSuccess = !errors.Any(), - Data = base.MakeThumbnailImage(id, "artist", this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height, true), + Data = MakeThumbnailImage(id, "artist", Configuration.MediumImageSize.Width, + Configuration.MediumImageSize.Height, true), OperationTime = sw.ElapsedMilliseconds, Errors = errors }; } + + private async Task> ScanArtistReleasesFolders(Guid artistId, string destinationFolder, + bool doJustInfo) + { + SimpleContract.Requires(artistId != Guid.Empty, "Invalid ArtistId"); + + var result = true; + var resultErrors = new List(); + var sw = new Stopwatch(); + sw.Start(); + try + { + var artist = DbContext.Artists + .Include("Releases") + .Include("Releases.Labels") + .FirstOrDefault(x => x.RoadieId == artistId); + if (artist == null) + { + Logger.LogWarning("Unable To Find Artist [{0}]", artistId); + return new OperationResult(); + } + + var releaseScannedCount = 0; + var artistFolder = artist.ArtistFileFolder(Configuration, destinationFolder); + var scannedArtistFolders = new List(); + // Scan known releases for changes + if (artist.Releases != null) + foreach (var release in artist.Releases) + try + { + result = result && (await ReleaseFactory.ScanReleaseFolder(Guid.Empty, destinationFolder, + doJustInfo, release)).Data; + releaseScannedCount++; + scannedArtistFolders.Add(release.ReleaseFileFolder(artistFolder)); + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Serialize()); + } + + // Any folder found in Artist folder not already scanned scan + var folderProcessor = new FolderProcessor(Configuration, HttpEncoder, destinationFolder, DbContext, + CacheManager, Logger, ArtistLookupEngine, ArtistFactory, ReleaseFactory, ImageFactory, + ReleaseLookupEngine, AudioMetaDataHelper); + var nonReleaseFolders = from d in Directory.EnumerateDirectories(artistFolder) + where !(from r in scannedArtistFolders select r).Contains(d) + orderby d + select d; + foreach (var folder in nonReleaseFolders) + await folderProcessor.Process(new DirectoryInfo(folder), doJustInfo); + if (!doJustInfo) FolderProcessor.DeleteEmptyFolders(new DirectoryInfo(artistFolder), Logger); + + // Always update artist image if artist image is found on an artist rescan + var imageFiles = ImageHelper.ImageFilesInFolder(artistFolder, SearchOption.AllDirectories); + if (imageFiles != null && imageFiles.Any()) + { + var imageFile = imageFiles.First(); + var i = new FileInfo(imageFile); + var iName = i.Name.ToLower().Trim(); + var isArtistImage = iName.Contains("artist"); + if (isArtistImage) + { + // Read image and convert to jpeg + artist.Thumbnail = File.ReadAllBytes(i.FullName); + artist.Thumbnail = ImageHelper.ResizeImage(artist.Thumbnail, + Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height); + artist.Thumbnail = ImageHelper.ConvertToJpegFormat(artist.Thumbnail); + artist.LastUpdated = DateTime.UtcNow; + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogInformation("Update Thumbnail using Artist File [{0}]", iName); + } + } + + sw.Stop(); + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogInformation("Scanned Artist [{0}], Releases Scanned [{1}], OperationTime [{2}]", + artist.ToString(), releaseScannedCount, sw.ElapsedMilliseconds); + } + catch (Exception ex) + { + Logger.LogError(ex, ex.Serialize()); + resultErrors.Add(ex); + } + + return new OperationResult + { + Data = result, + IsSuccess = result, + Errors = resultErrors, + OperationTime = sw.ElapsedMilliseconds + }; + } } } \ No newline at end of file diff --git a/Roadie.Api.Services/BookmarkService.cs b/Roadie.Api.Services/BookmarkService.cs index eccda51..2c8678f 100644 --- a/Roadie.Api.Services/BookmarkService.cs +++ b/Roadie.Api.Services/BookmarkService.cs @@ -4,8 +4,10 @@ using Roadie.Library.Caching; using Roadie.Library.Configuration; using Roadie.Library.Encoding; using Roadie.Library.Enums; -using Roadie.Library.Models; +using Roadie.Library.Models.Collections; 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; @@ -15,7 +17,6 @@ using System.Linq; using System.Linq.Dynamic.Core; using System.Threading.Tasks; using data = Roadie.Library.Data; - using models = Roadie.Library.Models; namespace Roadie.Api.Services @@ -23,169 +24,163 @@ namespace Roadie.Api.Services public class BookmarkService : ServiceBase, IBookmarkService { public BookmarkService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { } - public Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null) + public Task> List(User roadieUser, + PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null) { var sw = new Stopwatch(); sw.Start(); - var result = (from b in this.DbContext.Bookmarks - join u in this.DbContext.Users on b.UserId equals u.Id - where b.UserId == roadieUser.Id - where (filterType == null || b.BookmarkType == filterType) - select new BookmarkList - { - Comment = b.Comment, - Position = b.Position, - User = new DataToken - { - Text = u.UserName, - Value = u.RoadieId.ToString() - }, - DatabaseId = b.Id, - Id = b.RoadieId, - CreatedDate = b.CreatedDate, - LastUpdated = b.LastUpdated, - Type = b.BookmarkType, - BookmarkTargetId = b.BookmarkTargetId - }); + var result = from b in DbContext.Bookmarks + join u in DbContext.Users on b.UserId equals u.Id + where b.UserId == roadieUser.Id + where filterType == null || b.BookmarkType == filterType + select new models.BookmarkList + { + Comment = b.Comment, + Position = b.Position, + User = new models.DataToken + { + Text = u.UserName, + Value = u.RoadieId.ToString() + }, + DatabaseId = b.Id, + Id = b.RoadieId, + CreatedDate = b.CreatedDate, + LastUpdated = b.LastUpdated, + Type = b.BookmarkType, + BookmarkTargetId = b.BookmarkTargetId + }; - var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "CreatedDate", "DESC" } }) : request.OrderValue(null); + var sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary { { "CreatedDate", "DESC" } }) + : request.OrderValue(); var rowCount = result.Count(); - BookmarkList[] rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); + var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); - var user = this.GetUser(roadieUser.UserId); + var user = GetUser(roadieUser.UserId); foreach (var row in rows) - { switch (row.Type) { case BookmarkType.Artist: - var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (artist == null) - { - continue; - } - row.Bookmark = new DataToken + var artist = DbContext.Artists.FirstOrDefault(x => x.Id == row.BookmarkTargetId); + if (artist == null) continue; + row.Bookmark = new models.DataToken { Text = artist.Name, Value = artist.RoadieId.ToString() }; - row.Artist = models.ArtistList.FromDataArtist(artist, this.MakeArtistThumbnailImage(artist.RoadieId)); - row.Thumbnail = this.MakeArtistThumbnailImage(artist.RoadieId); + row.Artist = + models.ArtistList.FromDataArtist(artist, MakeArtistThumbnailImage(artist.RoadieId)); + row.Thumbnail = MakeArtistThumbnailImage(artist.RoadieId); row.SortName = artist.SortName ?? artist.Name; break; case BookmarkType.Release: - var release = this.DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (release == null) - { - continue; - } - row.Bookmark = new DataToken + var release = DbContext.Releases.Include(x => x.Artist) + .FirstOrDefault(x => x.Id == row.BookmarkTargetId); + if (release == null) continue; + row.Bookmark = new models.DataToken { Text = release.Title, Value = release.RoadieId.ToString() }; - row.Release = models.Releases.ReleaseList.FromDataRelease(release, release.Artist, this.HttpContext.BaseUrl, this.MakeArtistThumbnailImage(release.Artist.RoadieId), this.MakeReleaseThumbnailImage(release.RoadieId)); - row.Thumbnail = this.MakeReleaseThumbnailImage(release.RoadieId); + row.Release = ReleaseList.FromDataRelease(release, release.Artist, HttpContext.BaseUrl, + MakeArtistThumbnailImage(release.Artist.RoadieId), + MakeReleaseThumbnailImage(release.RoadieId)); + row.Thumbnail = MakeReleaseThumbnailImage(release.RoadieId); row.SortName = release.Title; break; case BookmarkType.Track: - var track = this.DbContext.Tracks - .Include(x => x.ReleaseMedia) - .Include(x => x.ReleaseMedia.Release) - .Include(x => x.ReleaseMedia.Release.Artist) - .Include(x => x.TrackArtist) - .FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (track == null) - { - continue; - } - row.Bookmark = new DataToken + var track = DbContext.Tracks + .Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .Include(x => x.TrackArtist) + .FirstOrDefault(x => x.Id == row.BookmarkTargetId); + if (track == null) continue; + row.Bookmark = new models.DataToken { Text = track.Title, Value = track.RoadieId.ToString() }; - row.Track = TrackList.FromDataTrack(this.MakeTrackPlayUrl(user, track.Id, track.RoadieId), - track, - track.ReleaseMedia.MediaNumber, - track.ReleaseMedia.Release, - track.ReleaseMedia.Release.Artist, - track.TrackArtist, - this.HttpContext.BaseUrl, - this.MakeTrackThumbnailImage(track.RoadieId), - this.MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId), - this.MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId), - this.MakeArtistThumbnailImage(track.TrackArtist == null ? null : (Guid?)track.TrackArtist.RoadieId)); - row.Track.TrackPlayUrl = this.MakeTrackPlayUrl(user, track.Id, track.RoadieId); - row.Thumbnail = this.MakeTrackThumbnailImage(track.RoadieId); + row.Track = models.TrackList.FromDataTrack(MakeTrackPlayUrl(user, track.Id, track.RoadieId), + track, + track.ReleaseMedia.MediaNumber, + track.ReleaseMedia.Release, + track.ReleaseMedia.Release.Artist, + track.TrackArtist, + HttpContext.BaseUrl, + MakeTrackThumbnailImage(track.RoadieId), + MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId), + MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId), + MakeArtistThumbnailImage(track.TrackArtist == null + ? null + : (Guid?)track.TrackArtist.RoadieId)); + row.Track.TrackPlayUrl = MakeTrackPlayUrl(user, track.Id, track.RoadieId); + row.Thumbnail = MakeTrackThumbnailImage(track.RoadieId); row.SortName = track.Title; break; case BookmarkType.Playlist: - var playlist = this.DbContext.Playlists - .Include(x => x.User) - .FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (playlist == null) - { - continue; - } - row.Bookmark = new DataToken + var playlist = DbContext.Playlists + .Include(x => x.User) + .FirstOrDefault(x => x.Id == row.BookmarkTargetId); + if (playlist == null) continue; + row.Bookmark = new models.DataToken { Text = playlist.Name, Value = playlist.RoadieId.ToString() }; - row.Playlist = models.Playlists.PlaylistList.FromDataPlaylist(playlist, playlist.User, this.MakePlaylistThumbnailImage(playlist.RoadieId), this.MakeUserThumbnailImage(playlist.User.RoadieId)); - row.Thumbnail = this.MakePlaylistThumbnailImage(playlist.RoadieId); + row.Playlist = PlaylistList.FromDataPlaylist(playlist, playlist.User, + MakePlaylistThumbnailImage(playlist.RoadieId), + MakeUserThumbnailImage(playlist.User.RoadieId)); + row.Thumbnail = MakePlaylistThumbnailImage(playlist.RoadieId); row.SortName = playlist.Name; break; case BookmarkType.Collection: - var collection = this.DbContext.Collections.FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (collection == null) - { - continue; - } - row.Bookmark = new DataToken + var collection = DbContext.Collections.FirstOrDefault(x => x.Id == row.BookmarkTargetId); + if (collection == null) continue; + row.Bookmark = new models.DataToken { Text = collection.Name, Value = collection.RoadieId.ToString() }; - row.Collection = models.Collections.CollectionList.FromDataCollection(collection, (from crc in this.DbContext.CollectionReleases - where crc.CollectionId == collection.Id - select crc.Id).Count(), this.MakeCollectionThumbnailImage(collection.RoadieId)); - row.Thumbnail = this.MakeCollectionThumbnailImage(collection.RoadieId); + row.Collection = CollectionList.FromDataCollection(collection, + (from crc in DbContext.CollectionReleases + where crc.CollectionId == collection.Id + select crc.Id).Count(), MakeCollectionThumbnailImage(collection.RoadieId)); + row.Thumbnail = MakeCollectionThumbnailImage(collection.RoadieId); row.SortName = collection.SortName ?? collection.Name; break; case BookmarkType.Label: - var label = this.DbContext.Labels.FirstOrDefault(x => x.Id == row.BookmarkTargetId); - if (label == null) - { - continue; - } - row.Bookmark = new DataToken + var label = DbContext.Labels.FirstOrDefault(x => x.Id == row.BookmarkTargetId); + if (label == null) continue; + row.Bookmark = new models.DataToken { Text = label.Name, Value = label.RoadieId.ToString() }; - row.Label = models.LabelList.FromDataLabel(label, this.MakeLabelThumbnailImage(label.RoadieId)); - row.Thumbnail = this.MakeLabelThumbnailImage(label.RoadieId); + row.Label = models.LabelList.FromDataLabel(label, MakeLabelThumbnailImage(label.RoadieId)); + row.Thumbnail = MakeLabelThumbnailImage(label.RoadieId); row.SortName = label.SortName ?? label.Name; break; } - }; + + ; sw.Stop(); - return Task.FromResult(new Library.Models.Pagination.PagedResult + return Task.FromResult(new Library.Models.Pagination.PagedResult { TotalCount = rowCount, CurrentPage = request.PageValue, diff --git a/Roadie.Api.Services/CollectionService.cs b/Roadie.Api.Services/CollectionService.cs index b31c8a9..1d2cb07 100644 --- a/Roadie.Api.Services/CollectionService.cs +++ b/Roadie.Api.Services/CollectionService.cs @@ -8,8 +8,10 @@ using Roadie.Library.Encoding; using Roadie.Library.Enums; using Roadie.Library.Extensions; using Roadie.Library.Imaging; +using Roadie.Library.Models; using Roadie.Library.Models.Collections; using Roadie.Library.Models.Pagination; +using Roadie.Library.Models.Releases; using Roadie.Library.Models.Statistics; using Roadie.Library.Models.Users; using Roadie.Library.Utility; @@ -20,28 +22,27 @@ using System.Linq; using System.Linq.Dynamic.Core; using System.Threading.Tasks; using data = Roadie.Library.Data; -using Roadie.Library.Models; namespace Roadie.Api.Services { public class CollectionService : ServiceBase, ICollectionService { - private IBookmarkService BookmarkService { get; } = null; + private IBookmarkService BookmarkService { get; } public CollectionService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext dbContext, - ICacheManager cacheManager, - ILogger logger, - IBookmarkService bookmarkService) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext dbContext, + ICacheManager cacheManager, + ILogger logger, + IBookmarkService bookmarkService) : base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext) { - this.BookmarkService = bookmarkService; + BookmarkService = bookmarkService; } /// - /// Get blank Collection to add + /// Get blank Collection to add /// /// /// @@ -57,16 +58,16 @@ namespace Roadie.Api.Services }; var result = collection.Adapt(); result.Id = id; - result.Thumbnail = this.MakeNewImage("collection"); - result.MediumThumbnail = this.MakeNewImage("collection"); - result.Maintainer = new Library.Models.DataToken + result.Thumbnail = MakeNewImage("collection"); + result.MediumThumbnail = MakeNewImage("collection"); + result.Maintainer = new DataToken { Value = roadieUser.UserId.ToString(), Text = roadieUser.UserName }; sw.Stop(); - return new OperationResult() + return new OperationResult { Data = result, IsSuccess = true, @@ -74,39 +75,42 @@ namespace Roadie.Api.Services }; } - public async Task> ById(User roadieUser, Guid id, IEnumerable includes = null) + public async Task> ById(User roadieUser, Guid id, + IEnumerable includes = null) { var sw = Stopwatch.StartNew(); sw.Start(); - var cacheKey = string.Format("urn:collection_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes)); - var result = await this.CacheManager.GetAsync>(cacheKey, async () => - { - return await this.CollectionByIdAction(id, includes); - }, data.Artist.CacheRegionUrn(id)); + var cacheKey = string.Format("urn:collection_by_id_operation:{0}:{1}", id, + includes == null ? "0" : string.Join("|", includes)); + var result = await CacheManager.GetAsync(cacheKey, + async () => { return await CollectionByIdAction(id, includes); }, data.Artist.CacheRegionUrn(id)); sw.Stop(); if (result?.Data != null && roadieUser != null) { - var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection); + var userBookmarkResult = + await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection); if (userBookmarkResult.IsSuccess) - { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != null; - } + result.Data.UserBookmarked = + userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != + null; if (result.Data.Comments.Any()) { var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + var userCommentReactions = (from cr in DbContext.CommentReactions where commentIds.Contains(cr.CommentId) where cr.UserId == roadieUser.Id select cr).ToArray(); foreach (var comment in result.Data.Comments) { - var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); + var userCommentReaction = + userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; } } } + return new OperationResult(result.Messages) { Data = result?.Data, @@ -122,28 +126,27 @@ namespace Roadie.Api.Services var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == id); - if (collection == null) - { - return new OperationResult(true, $"Collection Not Found [{ id }]"); - } + var collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == id); + if (collection == null) return new OperationResult(true, $"Collection Not Found [{id}]"); if (!user.IsEditor) { - this.Logger.LogWarning($"DeleteCollection: Access Denied: `{ collection }`, By User `{user }`"); + Logger.LogWarning($"DeleteCollection: Access Denied: `{collection}`, By User `{user}`"); return new OperationResult("Access Denied"); } + try { - this.DbContext.Collections.Remove(collection); - await this.DbContext.SaveChangesAsync(); + DbContext.Collections.Remove(collection); + await DbContext.SaveChangesAsync(); } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); errors.Add(ex); } + sw.Stop(); - this.Logger.LogInformation($"DeleteCollection `{ collection }`, By User `{user }`"); + Logger.LogInformation($"DeleteCollection `{collection}`, By User `{user}`"); return new OperationResult { IsSuccess = !errors.Any(), @@ -153,7 +156,8 @@ namespace Roadie.Api.Services }; } - public Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null) + public Task> List(User roadieUser, PagedRequest request, + bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null) { var sw = new Stopwatch(); sw.Start(); @@ -167,7 +171,7 @@ namespace Roadie.Api.Services join `artist` a on r.artistId = a.id where a.roadieId = {0}"; - collections = this.DbContext.Collections.FromSql(sql, artistId); + collections = DbContext.Collections.FromSql(sql, artistId); } else if (releaseId.HasValue) { @@ -177,25 +181,27 @@ namespace Roadie.Api.Services join `release` r on r.id = cr.releaseId where r.roadieId = {0}"; - collections = this.DbContext.Collections.FromSql(sql, releaseId); + collections = DbContext.Collections.FromSql(sql, releaseId); } else { - collections = this.DbContext.Collections; + collections = DbContext.Collections; } - var result = (from c in collections - where (request.FilterValue.Length == 0 || (request.FilterValue.Length > 0 && c.Name.Contains(request.Filter))) - where (request.FilterToStatusValue == Statuses.Ok || (c.Status == request.FilterToStatusValue)) - select CollectionList.FromDataCollection(c, (from crc in this.DbContext.CollectionReleases - where crc.CollectionId == c.Id - select crc.Id).Count(), this.MakeCollectionThumbnailImage(c.RoadieId))); - var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "Collection.Text", "ASC" } }) : request.OrderValue(null); + + var result = from c in collections + where request.FilterValue.Length == 0 || + request.FilterValue.Length > 0 && c.Name.Contains(request.Filter) + where request.FilterToStatusValue == Statuses.Ok || c.Status == request.FilterToStatusValue + select CollectionList.FromDataCollection(c, (from crc in DbContext.CollectionReleases + where crc.CollectionId == c.Id + select crc.Id).Count(), MakeCollectionThumbnailImage(c.RoadieId)); + var sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary { { "Collection.Text", "ASC" } }) + : request.OrderValue(); var rowCount = result.Count(); var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); if (request.FilterToStatusValue == Statuses.Incomplete) - { rows = rows.OrderByDescending(x => x.PercentComplete).ThenBy(x => x.SortName).ToArray(); - } sw.Stop(); return Task.FromResult(new Library.Models.Pagination.PagedResult { @@ -208,7 +214,7 @@ namespace Roadie.Api.Services } /// - /// Updates (or Adds) Collection + /// Updates (or Adds) Collection /// public async Task> UpdateCollection(User user, Collection model) { @@ -219,16 +225,15 @@ namespace Roadie.Api.Services sw.Start(); var errors = new List(); - data.Collection collection = new data.Collection(); + var collection = new data.Collection(); if (!isNew) { - collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == model.Id); + collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == model.Id); if (collection == null) - { return new OperationResult(true, string.Format("Collection Not Found [{0}]", model.Id)); - } } + collection.IsLocked = model.IsLocked; collection.Name = model.Name; collection.SortName = model.SortName; @@ -251,26 +256,23 @@ namespace Roadie.Api.Services collection.Thumbnail = ImageHelper.ConvertToJpegFormat(collectionImage); // Resize to store in database as thumbnail - collection.Thumbnail = ImageHelper.ResizeImage(collection.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); + collection.Thumbnail = ImageHelper.ResizeImage(collection.Thumbnail, + Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height); } if (model.Maintainer?.Value != null) { - var maintainer = this.DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value)); - if (maintainer != null) - { - collection.MaintainerId = maintainer.Id; - } + var maintainer = + DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value)); + if (maintainer != null) collection.MaintainerId = maintainer.Id; } + collection.LastUpdated = now; - if (isNew) - { - await this.DbContext.Collections.AddAsync(collection); - } - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(collection.CacheRegion); - this.Logger.LogInformation($"UpdateArtist `{ collection }` By User `{ user }`"); + if (isNew) await DbContext.Collections.AddAsync(collection); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(collection.CacheRegion); + Logger.LogInformation($"UpdateArtist `{collection}` By User `{user}`"); return new OperationResult { IsSuccess = !errors.Any(), @@ -285,16 +287,15 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); sw.Start(); - var collection = this.GetCollection(id); + var collection = GetCollection(id); if (collection == null) - { - return Task.FromResult(new OperationResult(true, string.Format("Collection Not Found [{0}]", id))); - } + return Task.FromResult(new OperationResult(true, + string.Format("Collection Not Found [{0}]", id))); var result = collection.Adapt(); - var maintainer = this.DbContext.Users.FirstOrDefault(x => x.Id == collection.MaintainerId); - result.Maintainer = new Library.Models.DataToken + var maintainer = DbContext.Users.FirstOrDefault(x => x.Id == collection.MaintainerId); + result.Maintainer = new DataToken { Text = maintainer.UserName, Value = maintainer.RoadieId.ToString() @@ -302,9 +303,10 @@ namespace Roadie.Api.Services result.AlternateNames = collection.AlternateNames; result.Tags = collection.Tags; result.URLs = collection.URLs; - result.Thumbnail = this.MakeCollectionThumbnailImage(collection.RoadieId); - result.MediumThumbnail = base.MakeThumbnailImage(id, "collection", this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); - result.CollectionFoundCount = (from crc in this.DbContext.CollectionReleases + result.Thumbnail = MakeCollectionThumbnailImage(collection.RoadieId); + result.MediumThumbnail = MakeThumbnailImage(id, "collection", Configuration.MediumImageSize.Width, + Configuration.MediumImageSize.Height); + result.CollectionFoundCount = (from crc in DbContext.CollectionReleases where crc.CollectionId == collection.Id select crc.Id).Count(); if (includes != null && includes.Any()) @@ -321,31 +323,30 @@ namespace Roadie.Api.Services } if (includes.Contains("releases")) - { - result.Releases = (from crc in this.DbContext.CollectionReleases - join r in this.DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id + result.Releases = (from crc in DbContext.CollectionReleases + join r in DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id where crc.CollectionId == collection.Id orderby crc.ListNumber select new CollectionRelease { ListNumber = crc.ListNumber, - Release = Library.Models.Releases.ReleaseList.FromDataRelease(r, r.Artist, this.HttpContext.BaseUrl, this.MakeArtistThumbnailImage(r.Artist.RoadieId), this.MakeReleaseThumbnailImage(r.RoadieId)) + Release = ReleaseList.FromDataRelease(r, r.Artist, HttpContext.BaseUrl, + MakeArtistThumbnailImage(r.Artist.RoadieId), MakeReleaseThumbnailImage(r.RoadieId)) }).ToArray(); - } if (includes.Contains("stats")) { - var collectionReleases = (from crc in this.DbContext.CollectionReleases - join r in this.DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id - where crc.CollectionId == collection.Id - select r); + var collectionReleases = from crc in DbContext.CollectionReleases + join r in DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id + where crc.CollectionId == collection.Id + select r; - var collectionTracks = (from crc in this.DbContext.CollectionReleases - join r in this.DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id - 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 crc.CollectionId == collection.Id - select t); + var collectionTracks = from crc in DbContext.CollectionReleases + join r in DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId + where crc.CollectionId == collection.Id + select t; result.Statistics = new CollectionStatistics { @@ -359,25 +360,31 @@ namespace Roadie.Api.Services TrackPlayedCount = collectionReleases.Sum(x => x.PlayedCount) }; } + if (includes.Contains("comments")) { - var collectionComments = this.DbContext.Comments.Include(x => x.User).Where(x => x.CollectionId == collection.Id).OrderByDescending(x => x.CreatedDate).ToArray(); + var collectionComments = DbContext.Comments.Include(x => x.User) + .Where(x => x.CollectionId == collection.Id).OrderByDescending(x => x.CreatedDate).ToArray(); if (collectionComments.Any()) { var comments = new List(); var commentIds = collectionComments.Select(x => x.Id).ToArray(); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + var userCommentReactions = (from cr in DbContext.CommentReactions where commentIds.Contains(cr.CommentId) select cr).ToArray(); foreach (var collectionComment in collectionComments) { var comment = collectionComment.Adapt(); comment.DatabaseId = collectionComment.Id; - comment.User = UserList.FromDataUser(collectionComment.User, this.MakeUserThumbnailImage(collectionComment.User.RoadieId)); - comment.DislikedCount = userCommentReactions.Count(x => x.CommentId == collectionComment.Id && x.ReactionValue == CommentReaction.Dislike); - comment.LikedCount = userCommentReactions.Count(x => x.CommentId == collectionComment.Id && x.ReactionValue == CommentReaction.Like); + comment.User = UserList.FromDataUser(collectionComment.User, + MakeUserThumbnailImage(collectionComment.User.RoadieId)); + comment.DislikedCount = userCommentReactions.Count(x => + x.CommentId == collectionComment.Id && x.ReactionValue == CommentReaction.Dislike); + comment.LikedCount = userCommentReactions.Count(x => + x.CommentId == collectionComment.Id && x.ReactionValue == CommentReaction.Like); comments.Add(comment); } + result.Comments = comments; } } diff --git a/Roadie.Api.Services/CommentService.cs b/Roadie.Api.Services/CommentService.cs index 78f1604..6748da7 100644 --- a/Roadie.Api.Services/CommentService.cs +++ b/Roadie.Api.Services/CommentService.cs @@ -10,7 +10,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Linq.Dynamic.Core; using System.Threading.Tasks; using data = Roadie.Library.Data; @@ -19,11 +18,11 @@ namespace Roadie.Api.Services public class CommentService : ServiceBase, ICommentService { public CommentService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { } @@ -33,12 +32,10 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var artist = this.DbContext.Artists - .FirstOrDefault(x => x.RoadieId == artistId); + var artist = DbContext.Artists + .FirstOrDefault(x => x.RoadieId == artistId); if (artist == null) - { return new OperationResult(true, string.Format("Artist Not Found [{0}]", artistId)); - } var newComment = new data.Comment { @@ -46,9 +43,9 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - this.DbContext.Comments.Add(newComment); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(artist.CacheRegion); + DbContext.Comments.Add(newComment); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(artist.CacheRegion); sw.Stop(); result = true; return new OperationResult @@ -65,12 +62,10 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var collection = this.DbContext.Collections - .FirstOrDefault(x => x.RoadieId == collectionId); + var collection = DbContext.Collections + .FirstOrDefault(x => x.RoadieId == collectionId); if (collection == null) - { return new OperationResult(true, string.Format("Collection Not Found [{0}]", collectionId)); - } var newComment = new data.Comment { @@ -78,9 +73,9 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - this.DbContext.Comments.Add(newComment); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(collection.CacheRegion); + DbContext.Comments.Add(newComment); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(collection.CacheRegion); sw.Stop(); result = true; return new OperationResult @@ -97,12 +92,9 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var genre = this.DbContext.Genres - .FirstOrDefault(x => x.RoadieId == genreId); - if (genre == null) - { - return new OperationResult(true, string.Format("Genre Not Found [{0}]", genreId)); - } + var genre = DbContext.Genres + .FirstOrDefault(x => x.RoadieId == genreId); + if (genre == null) return new OperationResult(true, string.Format("Genre Not Found [{0}]", genreId)); var newComment = new data.Comment { @@ -110,9 +102,9 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - this.DbContext.Comments.Add(newComment); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(genre.CacheRegion); + DbContext.Comments.Add(newComment); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(genre.CacheRegion); sw.Stop(); result = true; return new OperationResult @@ -129,12 +121,9 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var label = this.DbContext.Labels - .FirstOrDefault(x => x.RoadieId == labelId); - if (label == null) - { - return new OperationResult(true, string.Format("Label Not Found [{0}]", labelId)); - } + var label = DbContext.Labels + .FirstOrDefault(x => x.RoadieId == labelId); + if (label == null) return new OperationResult(true, string.Format("Label Not Found [{0}]", labelId)); var newComment = new data.Comment { @@ -142,9 +131,9 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - this.DbContext.Comments.Add(newComment); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(label.CacheRegion); + DbContext.Comments.Add(newComment); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(label.CacheRegion); sw.Stop(); result = true; return new OperationResult @@ -161,12 +150,10 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var playlist = this.DbContext.Playlists - .FirstOrDefault(x => x.RoadieId == playlistId); + var playlist = DbContext.Playlists + .FirstOrDefault(x => x.RoadieId == playlistId); if (playlist == null) - { return new OperationResult(true, string.Format("Playlist Not Found [{0}]", playlistId)); - } var newComment = new data.Comment { @@ -174,9 +161,9 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - this.DbContext.Comments.Add(newComment); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(playlist.CacheRegion); + DbContext.Comments.Add(newComment); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(playlist.CacheRegion); sw.Stop(); result = true; return new OperationResult @@ -193,12 +180,10 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var release = this.DbContext.Releases - .FirstOrDefault(x => x.RoadieId == releaseId); + var release = DbContext.Releases + .FirstOrDefault(x => x.RoadieId == releaseId); if (release == null) - { return new OperationResult(true, string.Format("Release Not Found [{0}]", releaseId)); - } var newComment = new data.Comment { @@ -206,9 +191,9 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - this.DbContext.Comments.Add(newComment); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(release.CacheRegion); + DbContext.Comments.Add(newComment); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(release.CacheRegion); sw.Stop(); result = true; return new OperationResult @@ -225,12 +210,9 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var track = this.DbContext.Tracks - .FirstOrDefault(x => x.RoadieId == trackId); - if (track == null) - { - return new OperationResult(true, string.Format("Track Not Found [{0}]", trackId)); - } + var track = DbContext.Tracks + .FirstOrDefault(x => x.RoadieId == trackId); + if (track == null) return new OperationResult(true, string.Format("Track Not Found [{0}]", trackId)); var newComment = new data.Comment { @@ -238,9 +220,9 @@ namespace Roadie.Api.Services UserId = user.Id.Value, Cmt = cmt }; - this.DbContext.Comments.Add(newComment); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(track.CacheRegion); + DbContext.Comments.Add(newComment); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(track.CacheRegion); sw.Stop(); result = true; return new OperationResult @@ -257,14 +239,11 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var comment = this.DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); - if (comment == null) - { - return new OperationResult(true, string.Format("Comment Not Found [{0}]", id)); - } - this.DbContext.Remove(comment); - await this.DbContext.SaveChangesAsync(); - this.ClearCaches(comment); + var comment = DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); + if (comment == null) return new OperationResult(true, string.Format("Comment Not Found [{0}]", id)); + DbContext.Remove(comment); + await DbContext.SaveChangesAsync(); + ClearCaches(comment); sw.Stop(); result = true; return new OperationResult @@ -281,12 +260,10 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); var result = false; var errors = new List(); - var comment = this.DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); - if (comment == null) - { - return new OperationResult(true, string.Format("Comment Not Found [{0}]", id)); - } - var userCommentReaction = this.DbContext.CommentReactions.FirstOrDefault(x => x.CommentId == comment.Id && x.UserId == user.Id); + var comment = DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); + if (comment == null) return new OperationResult(true, string.Format("Comment Not Found [{0}]", id)); + var userCommentReaction = + DbContext.CommentReactions.FirstOrDefault(x => x.CommentId == comment.Id && x.UserId == user.Id); if (userCommentReaction == null) { userCommentReaction = new data.CommentReaction @@ -294,17 +271,20 @@ namespace Roadie.Api.Services CommentId = comment.Id, UserId = user.Id.Value }; - this.DbContext.CommentReactions.Add(userCommentReaction); + DbContext.CommentReactions.Add(userCommentReaction); } + userCommentReaction.Reaction = reaction == CommentReaction.Unknown ? null : reaction.ToString(); - await this.DbContext.SaveChangesAsync(); - this.ClearCaches(comment); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + await DbContext.SaveChangesAsync(); + ClearCaches(comment); + var userCommentReactions = (from cr in DbContext.CommentReactions where cr.CommentId == comment.Id select cr).ToArray(); var additionalData = new Dictionary(); - additionalData.Add("likedCount", userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Like).Count()); - additionalData.Add("dislikedCount", userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Dislike).Count()); + additionalData.Add("likedCount", + userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Like).Count()); + additionalData.Add("dislikedCount", + userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Dislike).Count()); sw.Stop(); result = true; return new OperationResult @@ -322,59 +302,38 @@ namespace Roadie.Api.Services switch (comment.CommentType) { case CommentType.Artist: - var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == comment.ArtistId); - if (artist != null) - { - this.CacheManager.ClearRegion(artist.CacheRegion); - } + var artist = DbContext.Artists.FirstOrDefault(x => x.Id == comment.ArtistId); + if (artist != null) CacheManager.ClearRegion(artist.CacheRegion); break; case CommentType.Collection: - var collection = this.DbContext.Collections.FirstOrDefault(x => x.Id == comment.CollectionId); - if (collection != null) - { - this.CacheManager.ClearRegion(collection.CacheRegion); - } + var collection = DbContext.Collections.FirstOrDefault(x => x.Id == comment.CollectionId); + if (collection != null) CacheManager.ClearRegion(collection.CacheRegion); break; case CommentType.Genre: - var genre = this.DbContext.Genres.FirstOrDefault(x => x.Id == comment.GenreId); - if (genre != null) - { - this.CacheManager.ClearRegion(genre.CacheRegion); - } + var genre = DbContext.Genres.FirstOrDefault(x => x.Id == comment.GenreId); + if (genre != null) CacheManager.ClearRegion(genre.CacheRegion); break; case CommentType.Label: - var label = this.DbContext.Labels.FirstOrDefault(x => x.Id == comment.LabelId); - if (label != null) - { - this.CacheManager.ClearRegion(label.CacheRegion); - } + var label = DbContext.Labels.FirstOrDefault(x => x.Id == comment.LabelId); + if (label != null) CacheManager.ClearRegion(label.CacheRegion); break; case CommentType.Playlist: - var playlist = this.DbContext.Playlists.FirstOrDefault(x => x.Id == comment.PlaylistId); - if (playlist != null) - { - this.CacheManager.ClearRegion(playlist.CacheRegion); - } + var playlist = DbContext.Playlists.FirstOrDefault(x => x.Id == comment.PlaylistId); + if (playlist != null) CacheManager.ClearRegion(playlist.CacheRegion); break; case CommentType.Release: - var release = this.DbContext.Releases.FirstOrDefault(x => x.Id == comment.ReleaseId); - if (release != null) - { - this.CacheManager.ClearRegion(release.CacheRegion); - } + var release = DbContext.Releases.FirstOrDefault(x => x.Id == comment.ReleaseId); + if (release != null) CacheManager.ClearRegion(release.CacheRegion); break; case CommentType.Track: - var track = this.DbContext.Tracks.FirstOrDefault(x => x.Id == comment.TrackId); - if (track != null) - { - this.CacheManager.ClearRegion(track.CacheRegion); - } + var track = DbContext.Tracks.FirstOrDefault(x => x.Id == comment.TrackId); + if (track != null) CacheManager.ClearRegion(track.CacheRegion); break; } } diff --git a/Roadie.Api.Services/EmailSenderService.cs b/Roadie.Api.Services/EmailSenderService.cs index d2ad377..c6afc59 100644 --- a/Roadie.Api.Services/EmailSenderService.cs +++ b/Roadie.Api.Services/EmailSenderService.cs @@ -2,8 +2,6 @@ using Roadie.Library.Configuration; using System.Net; using System.Net.Mail; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; namespace Roadie.Api.Services @@ -14,28 +12,25 @@ namespace Roadie.Api.Services public EmailSenderService(IRoadieSettings configuration) { - this.Configuration = configuration; + Configuration = configuration; } public async Task SendEmailAsync(string email, string subject, string htmlMessage) { - using (MailMessage mail = new MailMessage(this.Configuration.SmtpFromAddress, email)) + using (var mail = new MailMessage(Configuration.SmtpFromAddress, email)) { - using (SmtpClient client = new SmtpClient()) + using (var client = new SmtpClient()) { - client.Port = this.Configuration.SmtpPort; - client.EnableSsl = this.Configuration.SmtpUseSSl; + client.Port = Configuration.SmtpPort; + client.EnableSsl = Configuration.SmtpUseSSl; client.DeliveryMethod = SmtpDeliveryMethod.Network; client.UseDefaultCredentials = false; - client.Credentials = new NetworkCredential(this.Configuration.SmtpUsername, this.Configuration.SmtpPassword); - client.Host = this.Configuration.SmtpHost; + client.Credentials = new NetworkCredential(Configuration.SmtpUsername, Configuration.SmtpPassword); + client.Host = Configuration.SmtpHost; mail.Subject = subject; mail.IsBodyHtml = true; mail.Body = htmlMessage; - ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - return true; - }; + ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; await client.SendMailAsync(mail); } } diff --git a/Roadie.Api.Services/GenreService.cs b/Roadie.Api.Services/GenreService.cs index c55c977..785bc97 100644 --- a/Roadie.Api.Services/GenreService.cs +++ b/Roadie.Api.Services/GenreService.cs @@ -19,16 +19,17 @@ namespace Roadie.Api.Services public class GenreService : ServiceBase, IGenreService { public GenreService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext dbContext, - ICacheManager cacheManager, - ILogger logger) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext dbContext, + ICacheManager cacheManager, + ILogger logger) : base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext) { } - public Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false) + public Task> List(User roadieUser, PagedRequest request, + bool? doRandomize = false) { var sw = new Stopwatch(); sw.Start(); @@ -38,30 +39,31 @@ namespace Roadie.Api.Services request.Sort = request.Sort.Replace("createdDate", "createdDateTime"); request.Sort = request.Sort.Replace("lastUpdated", "lastUpdatedDateTime"); } - var result = (from g in this.DbContext.Genres - let releaseCount = (from rg in this.DbContext.ReleaseGenres - where rg.GenreId == g.Id - select rg.Id).Count() - let artistCount = (from rg in this.DbContext.ArtistGenres + + var result = from g in DbContext.Genres + let releaseCount = (from rg in DbContext.ReleaseGenres where rg.GenreId == g.Id select rg.Id).Count() - where (request.FilterValue.Length == 0 || (g.Name.Contains(request.FilterValue))) - select new GenreList - { - DatabaseId = g.Id, - Id = g.RoadieId, - Genre = new DataToken - { - Text = g.Name, - Value = g.RoadieId.ToString() - }, - ReleaseCount = releaseCount, - ArtistCount = artistCount, - CreatedDate = g.CreatedDate, - LastUpdated = g.LastUpdated, - }); + let artistCount = (from rg in DbContext.ArtistGenres + where rg.GenreId == g.Id + select rg.Id).Count() + where request.FilterValue.Length == 0 || g.Name.Contains(request.FilterValue) + select new GenreList + { + DatabaseId = g.Id, + Id = g.RoadieId, + Genre = new DataToken + { + Text = g.Name, + Value = g.RoadieId.ToString() + }, + ReleaseCount = releaseCount, + ArtistCount = artistCount, + CreatedDate = g.CreatedDate, + LastUpdated = g.LastUpdated + }; - GenreList[] rows = null; + GenreList[] rows; var rowCount = result.Count(); if (doRandomize ?? false) { @@ -71,9 +73,12 @@ namespace Roadie.Api.Services } else { - var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "Genre.Text", "ASC" } }) : request.OrderValue(null); + var sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary { { "Genre.Text", "ASC" } }) + : request.OrderValue(); rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); } + sw.Stop(); return Task.FromResult(new Library.Models.Pagination.PagedResult { diff --git a/Roadie.Api.Services/HttpContext.cs b/Roadie.Api.Services/HttpContext.cs index 0cc4132..5e57e77 100644 --- a/Roadie.Api.Services/HttpContext.cs +++ b/Roadie.Api.Services/HttpContext.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Roadie.Library.Configuration; using Roadie.Library.Utility; @@ -7,22 +8,18 @@ namespace Roadie.Api.Services public class HttpContext : IHttpContext { public string BaseUrl { get; set; } + public string ImageBaseUrl { get; set; } public HttpContext(IRoadieSettings configuration, IUrlHelper urlHelper) { var scheme = urlHelper.ActionContext.HttpContext.Request.Scheme; - if (configuration.UseSSLBehindProxy) - { - scheme = "https"; - } + if (configuration.UseSSLBehindProxy) scheme = "https"; var host = urlHelper.ActionContext.HttpContext.Request.Host; if (!string.IsNullOrEmpty(configuration.BehindProxyHost)) - { - host = new Microsoft.AspNetCore.Http.HostString(configuration.BehindProxyHost); - } - this.BaseUrl = $"{ scheme }://{ host }"; - this.ImageBaseUrl = $"{ this.BaseUrl}/images"; + host = new HostString(configuration.BehindProxyHost); + BaseUrl = $"{scheme}://{host}"; + ImageBaseUrl = $"{BaseUrl}/images"; } } } \ No newline at end of file diff --git a/Roadie.Api.Services/HttpEncoder.cs b/Roadie.Api.Services/HttpEncoder.cs index 6284fa9..5aae87d 100644 --- a/Roadie.Api.Services/HttpEncoder.cs +++ b/Roadie.Api.Services/HttpEncoder.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.WebUtilities; using Roadie.Library.Encoding; +using System.Text; using System.Web; namespace Roadie.Api.Services @@ -28,7 +29,7 @@ namespace Roadie.Api.Services public string UrlEncodeBase64(string input) { - return WebEncoders.Base64UrlEncode(System.Text.Encoding.ASCII.GetBytes(input)); + return WebEncoders.Base64UrlEncode(Encoding.ASCII.GetBytes(input)); } } } \ No newline at end of file diff --git a/Roadie.Api.Services/IAdminService.cs b/Roadie.Api.Services/IAdminService.cs index 81a45e0..98396a7 100644 --- a/Roadie.Api.Services/IAdminService.cs +++ b/Roadie.Api.Services/IAdminService.cs @@ -11,7 +11,8 @@ namespace Roadie.Api.Services { Task> DeleteArtist(ApplicationUser user, Guid artistId); - Task> DeleteArtistReleases(ApplicationUser user, Guid artistId, bool doDeleteFiles = false); + Task> DeleteArtistReleases(ApplicationUser user, Guid artistId, + bool doDeleteFiles = false); Task> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index); @@ -27,16 +28,19 @@ namespace Roadie.Api.Services Task>>> MissingCollectionReleases(ApplicationUser user); - Task> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = false); + Task> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, + bool doPurgeFirst = false); Task> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false); - Task> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true); + Task> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, + bool doPurgeFirst = false, bool doUpdateRanks = true); Task> ScanInboundFolder(ApplicationUser user, bool isReadOnly = false); Task> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false); - Task> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); + Task> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, + bool wasDoneForInvalidTrackPlay = false); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IArtistService.cs b/Roadie.Api.Services/IArtistService.cs index e4d7aff..2744a42 100644 --- a/Roadie.Api.Services/IArtistService.cs +++ b/Roadie.Api.Services/IArtistService.cs @@ -13,14 +13,15 @@ namespace Roadie.Api.Services { Task> ById(User roadieUser, Guid id, IEnumerable includes); - Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true); + Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, + bool? onlyIncludeWithReleases = true); Task> MergeArtists(User user, Guid artistToMergeId, Guid artistToMergeIntoId); - Task> SetReleaseImageByUrl(User user, Guid id, string imageUrl); + Task> SetReleaseImageByUrl(User user, Guid id, string imageUrl); Task> UpdateArtist(User user, Artist artist); - Task> UploadArtistImage(User user, Guid id, IFormFile file); + Task> UploadArtistImage(User user, Guid id, IFormFile file); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IBookmarkService.cs b/Roadie.Api.Services/IBookmarkService.cs index 914f89d..69e19df 100644 --- a/Roadie.Api.Services/IBookmarkService.cs +++ b/Roadie.Api.Services/IBookmarkService.cs @@ -8,6 +8,7 @@ namespace Roadie.Api.Services { public interface IBookmarkService { - Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null); + Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, + BookmarkType? filterType = null); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ICollectionService.cs b/Roadie.Api.Services/ICollectionService.cs index 2cc4039..0c53b7f 100644 --- a/Roadie.Api.Services/ICollectionService.cs +++ b/Roadie.Api.Services/ICollectionService.cs @@ -16,7 +16,8 @@ namespace Roadie.Api.Services Task> DeleteCollection(User user, Guid id); - Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null); + Task> List(User roadieUser, PagedRequest request, bool? doRandomize = false, + Guid? releaseId = null, Guid? artistId = null); Task> UpdateCollection(User roadieUser, Collection collection); } diff --git a/Roadie.Api.Services/IImageService.cs b/Roadie.Api.Services/IImageService.cs index 80a24da..68f553b 100644 --- a/Roadie.Api.Services/IImageService.cs +++ b/Roadie.Api.Services/IImageService.cs @@ -1,5 +1,6 @@ using Microsoft.Net.Http.Headers; using Roadie.Library; +using Roadie.Library.Models; using Roadie.Library.Models.Users; using Roadie.Library.SearchEngines.Imaging; using System; @@ -10,30 +11,36 @@ namespace Roadie.Api.Services { public interface IImageService { - Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> + ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, + EntityTagHeaderValue etag = null); - Task> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> CollectionImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null); Task> Delete(User user, Guid id); Task>> ImageProvidersSearch(string query); - Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> PlaylistImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null); - Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ReleaseImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null); - Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); + Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, + EntityTagHeaderValue etag = null); Task>> Search(string query, int resultsCount = 10); - Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); - Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); + Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IPlayActivityService.cs b/Roadie.Api.Services/IPlayActivityService.cs index c332287..970eb9c 100644 --- a/Roadie.Api.Services/IPlayActivityService.cs +++ b/Roadie.Api.Services/IPlayActivityService.cs @@ -10,8 +10,11 @@ namespace Roadie.Api.Services { public interface IPlayActivityService { + Task> List(PagedRequest request, User roadieUser = null, + DateTime? newerThan = null); + Task> NowPlaying(User roadieUser, ScrobbleInfo scrobble); + Task> Scrobble(User roadieUser, ScrobbleInfo scrobble); - Task> List(PagedRequest request, User roadieUser = null, DateTime? newerThan = null); } } \ No newline at end of file diff --git a/Roadie.Api.Services/IReleaseService.cs b/Roadie.Api.Services/IReleaseService.cs index a66cc4a..6e6dd01 100644 --- a/Roadie.Api.Services/IReleaseService.cs +++ b/Roadie.Api.Services/IReleaseService.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Roadie.Library; +using Roadie.Library.Models; using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Releases; using Roadie.Library.Models.Users; @@ -13,16 +14,18 @@ namespace Roadie.Api.Services { Task> ById(User roadieUser, Guid id, IEnumerable includes = null); - Task> List(User user, PagedRequest request, bool? doRandomize = false, IEnumerable includes = null); + Task> List(User user, PagedRequest request, bool? doRandomize = false, + IEnumerable includes = null); - Task> MergeReleases(User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia); + Task> MergeReleases(User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, + bool addAsMedia); Task> ReleaseZipped(User roadieUser, Guid id); - Task> SetReleaseImageByUrl(User user, Guid id, string imageUrl); + Task> SetReleaseImageByUrl(User user, Guid id, string imageUrl); Task> UpdateRelease(User user, Release release); - Task> UploadReleaseImage(User user, Guid id, IFormFile file); + Task> UploadReleaseImage(User user, Guid id, IFormFile file); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ISubsonicService.cs b/Roadie.Api.Services/ISubsonicService.cs index 67dca11..86d0e11 100644 --- a/Roadie.Api.Services/ISubsonicService.cs +++ b/Roadie.Api.Services/ISubsonicService.cs @@ -1,73 +1,82 @@ -using Roadie.Library.Models.ThirdPartyApi.Subsonic; +using Roadie.Library.Models; +using Roadie.Library.Models.ThirdPartyApi.Subsonic; using System.Threading.Tasks; +using User = Roadie.Library.Models.Users.User; namespace Roadie.Api.Services { public interface ISubsonicService { - Task> AddChatMessage(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> AddChatMessage(Request request, User roadieUser); Task> Authenticate(Request request); - Task> CreateBookmark(Request request, Roadie.Library.Models.Users.User roadieUser, int position, string comment); + Task> CreateBookmark(Request request, User roadieUser, int position, + string comment); - Task> CreatePlaylist(Request request, Roadie.Library.Models.Users.User roadieUser, string name, string[] songIds, string playlistId = null); + Task> CreatePlaylist(Request request, User roadieUser, string name, + string[] songIds, string playlistId = null); - Task> DeleteBookmark(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> DeleteBookmark(Request request, User roadieUser); - Task> DeletePlaylist(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> DeletePlaylist(Request request, User roadieUser); - Task> GetAlbum(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetAlbum(Request request, User roadieUser); - Task> GetAlbumInfo(Request request, Roadie.Library.Models.Users.User roadieUser, AlbumInfoVersion version); + Task> + GetAlbumInfo(Request request, User roadieUser, AlbumInfoVersion version); - Task> GetAlbumList(Request request, Roadie.Library.Models.Users.User roadieUser, AlbumListVersions version); + Task> GetAlbumList(Request request, User roadieUser, + AlbumListVersions version); - Task> GetArtist(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetArtist(Request request, User roadieUser); - Task> GetArtistInfo(Request request, int? count, bool includeNotPresent, ArtistInfoVersion version); + Task> GetArtistInfo(Request request, int? count, bool includeNotPresent, + ArtistInfoVersion version); - Task> GetArtists(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetArtists(Request request, User roadieUser); - Task> GetBookmarks(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetBookmarks(Request request, User roadieUser); - Task> GetChatMessages(Request request, Roadie.Library.Models.Users.User roadieUser, long? since); + Task> GetChatMessages(Request request, User roadieUser, long? since); - Task> GetCoverArt(Request request, int? size); + Task> GetCoverArt(Request request, int? size); Task> GetGenres(Request request); - Task> GetIndexes(Request request, Roadie.Library.Models.Users.User roadieUser, long? ifModifiedSince = null); + Task> GetIndexes(Request request, User roadieUser, + long? ifModifiedSince = null); SubsonicOperationResult GetLicense(Request request); SubsonicOperationResult GetLyrics(Request request, string artistId, string title); - Task> GetMusicDirectory(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetMusicDirectory(Request request, User roadieUser); Task> GetMusicFolders(Request request); - Task> GetNowPlaying(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetNowPlaying(Request request, User roadieUser); - Task> GetPlaylist(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetPlaylist(Request request, User roadieUser); - Task> GetPlaylists(Request request, Roadie.Library.Models.Users.User roadieUser, string filterToUserName); + Task> GetPlaylists(Request request, User roadieUser, string filterToUserName); - Task> GetPlayQueue(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetPlayQueue(Request request, User roadieUser); Task> GetPodcasts(Request request); - Task> GetRandomSongs(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetRandomSongs(Request request, User roadieUser); - Task> GetSimliarSongs(Request request, Roadie.Library.Models.Users.User roadieUser, SimilarSongsVersion version, int? count = 50); + Task> GetSimliarSongs(Request request, User roadieUser, + SimilarSongsVersion version, int? count = 50); - Task> GetSong(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetSong(Request request, User roadieUser); - Task> GetSongsByGenre(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetSongsByGenre(Request request, User roadieUser); - Task> GetStarred(Request request, Roadie.Library.Models.Users.User roadieUser, StarredVersion version); + Task> GetStarred(Request request, User roadieUser, StarredVersion version); - Task> GetTopSongs(Request request, Roadie.Library.Models.Users.User roadieUser, int? count = 50); + Task> GetTopSongs(Request request, User roadieUser, int? count = 50); Task> GetUser(Request request, string username); @@ -75,14 +84,18 @@ namespace Roadie.Api.Services SubsonicOperationResult Ping(Request request); - Task> SavePlayQueue(Request request, Roadie.Library.Models.Users.User roadieUser, string current, long? position); + Task> SavePlayQueue(Request request, User roadieUser, string current, + long? position); - Task> Search(Request request, Roadie.Library.Models.Users.User roadieUser, SearchVersion version); + Task> Search(Request request, User roadieUser, SearchVersion version); - Task> SetRating(Request request, Roadie.Library.Models.Users.User roadieUser, short rating); + Task> SetRating(Request request, User roadieUser, short rating); - Task> ToggleStar(Request request, Roadie.Library.Models.Users.User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null); + Task> ToggleStar(Request request, User roadieUser, bool star, + string[] albumIds = null, string[] artistIds = null); - Task> UpdatePlaylist(Request request, Roadie.Library.Models.Users.User roadieUser, string playlistId, string name = null, string comment = null, bool? isPublic = null, string[] songIdsToAdd = null, int[] songIndexesToRemove = null); + Task> UpdatePlaylist(Request request, User roadieUser, string playlistId, + string name = null, string comment = null, bool? isPublic = null, string[] songIdsToAdd = null, + int[] songIndexesToRemove = null); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ITrackService.cs b/Roadie.Api.Services/ITrackService.cs index 37d1d26..7696a1c 100644 --- a/Roadie.Api.Services/ITrackService.cs +++ b/Roadie.Api.Services/ITrackService.cs @@ -12,11 +12,13 @@ namespace Roadie.Api.Services { Task> ById(User roadieUser, Guid id, IEnumerable includes); - Task> List(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null); + Task> List(PagedRequest request, User roadieUser, bool? doRandomize = false, + Guid? releaseId = null); OperationResult StreamCheckAndInfo(User roadieUser, Guid id); - Task> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, User roadieUser); + Task> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, + User roadieUser); Task> UpdateTrack(User user, Track track); } diff --git a/Roadie.Api.Services/IUserService.cs b/Roadie.Api.Services/IUserService.cs index ac120a9..ec34609 100644 --- a/Roadie.Api.Services/IUserService.cs +++ b/Roadie.Api.Services/IUserService.cs @@ -43,8 +43,8 @@ namespace Roadie.Api.Services Task> SetTrackRating(Guid trackId, User roadieUser, short rating); - Task> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel); - Task> UpdateIntegrationGrant(Guid userId, string integrationName, string token); + + Task> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel); } } \ No newline at end of file diff --git a/Roadie.Api.Services/ImageService.cs b/Roadie.Api.Services/ImageService.cs index d1ee3f1..e7b7848 100644 --- a/Roadie.Api.Services/ImageService.cs +++ b/Roadie.Api.Services/ImageService.cs @@ -6,6 +6,7 @@ using Roadie.Library; using Roadie.Library.Caching; using Roadie.Library.Configuration; using Roadie.Library.Encoding; +using Roadie.Library.Enums; using Roadie.Library.Identity; using Roadie.Library.Imaging; using Roadie.Library.Models; @@ -25,105 +26,91 @@ namespace Roadie.Api.Services public class ImageService : ServiceBase, IImageService { private IImageSearchEngine BingSearchEngine { get; } + private IDefaultNotFoundImages DefaultNotFoundImages { get; } + private IImageSearchEngine ITunesSearchEngine { get; } private string Referrer { get; } + private string RequestIp { get; } public ImageService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger, - IDefaultNotFoundImages defaultNotFoundImages) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger, + IDefaultNotFoundImages defaultNotFoundImages) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { - this.DefaultNotFoundImages = defaultNotFoundImages; - this.BingSearchEngine = new BingImageSearchEngine(configuration, logger, this.RequestIp, this.Referrer); - this.ITunesSearchEngine = new ITunesSearchEngine(configuration, cacheManager, logger, this.RequestIp, this.Referrer); + DefaultNotFoundImages = defaultNotFoundImages; + BingSearchEngine = new BingImageSearchEngine(configuration, logger, RequestIp, Referrer); + ITunesSearchEngine = new ITunesSearchEngine(configuration, cacheManager, logger, RequestIp, Referrer); } - public async Task> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ArtistImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "ArtistImage", - regionUrn: data.Artist.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.ArtistImageAction(id, etag); - }, - etag: etag); + return await GetImageFileOperation("ArtistImage", + data.Artist.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ArtistImageAction(id, etag); }, + etag); } - public async Task> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ArtistSecondaryImage(Guid id, int imageId, int? width, + int? height, EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: $"ArtistSecondaryThumbnail-{imageId}", - regionUrn: data.Release.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.ArtistSecondaryImageAction(id, imageId, etag); - }, - etag: etag); + return await GetImageFileOperation($"ArtistSecondaryThumbnail-{imageId}", + data.Release.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ArtistSecondaryImageAction(id, imageId, etag); }, + etag); } - public async Task> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ById(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "ImageById", - regionUrn: data.Image.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.ImageByIdAction(id, etag); - }, - etag: etag); + return await GetImageFileOperation("ImageById", + data.Image.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ImageByIdAction(id, etag); }, + etag); } - public async Task> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> CollectionImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "CollectionThumbnail", - regionUrn: data.Collection.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.CollectionImageAction(id, etag); - }, - etag: etag); + return await GetImageFileOperation("CollectionThumbnail", + data.Collection.CacheRegionUrn(id), + id, + width, + height, + async () => { return await CollectionImageAction(id, etag); }, + etag); } public async Task> Delete(User user, Guid id) { var sw = Stopwatch.StartNew(); - var image = this.DbContext.Images - .Include("Release") - .Include("Artist") - .FirstOrDefault(x => x.RoadieId == id); - if (image == null) - { - return new OperationResult(true, string.Format("Image Not Found [{0}]", id)); - } - if (image.ArtistId.HasValue) - { - this.CacheManager.ClearRegion(data.Artist.CacheRegionUrn(image.Artist.RoadieId)); - } - if (image.ReleaseId.HasValue) - { - this.CacheManager.ClearRegion(data.Release.CacheRegionUrn(image.Release.RoadieId)); - } - this.DbContext.Images.Remove(image); - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(data.Image.CacheRegionUrn(id)); - this.Logger.LogInformation($"Deleted Image [{ id }], By User [{ user.ToString() }]"); + var image = DbContext.Images + .Include("Release") + .Include("Artist") + .FirstOrDefault(x => x.RoadieId == id); + if (image == null) return new OperationResult(true, string.Format("Image Not Found [{0}]", id)); + if (image.ArtistId.HasValue) CacheManager.ClearRegion(data.Artist.CacheRegionUrn(image.Artist.RoadieId)); + if (image.ReleaseId.HasValue) CacheManager.ClearRegion(data.Release.CacheRegionUrn(image.Release.RoadieId)); + DbContext.Images.Remove(image); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(data.Image.CacheRegionUrn(id)); + Logger.LogInformation($"Deleted Image [{id}], By User [{user}]"); sw.Stop(); return new OperationResult { @@ -141,14 +128,15 @@ namespace Roadie.Api.Services IEnumerable searchResults = null; try { - var manager = new ImageSearchManager(this.Configuration, this.CacheManager, this.Logger); + var manager = new ImageSearchManager(Configuration, CacheManager, Logger); searchResults = await manager.ImageSearch(query); } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); errors.Add(ex); } + sw.Stop(); return new OperationResult> { @@ -159,60 +147,52 @@ namespace Roadie.Api.Services }; } - public async Task> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> LabelImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "LabelThumbnail", - regionUrn: data.Label.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.LabelImageAction(id, etag); - }, - etag: etag); + return await GetImageFileOperation("LabelThumbnail", + data.Label.CacheRegionUrn(id), + id, + width, + height, + async () => { return await LabelImageAction(id, etag); }, + etag); } - public async Task> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> PlaylistImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "PlaylistThumbnail", - regionUrn: data.Playlist.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.PlaylistImageAction(id, etag); - }, - etag: etag); + return await GetImageFileOperation("PlaylistThumbnail", + data.Playlist.CacheRegionUrn(id), + id, + width, + height, + async () => { return await PlaylistImageAction(id, etag); }, + etag); } - public async Task> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ReleaseImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "ReleaseThumbnail", - regionUrn: data.Release.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.ReleaseImageAction(id, etag); - }, - etag: etag); + return await GetImageFileOperation("ReleaseThumbnail", + data.Release.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ReleaseImageAction(id, etag); }, + etag); } - public async Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> ReleaseSecondaryImage(Guid id, int imageId, int? width, + int? height, EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: $"ReleaseSecondaryThumbnail-{imageId}", - regionUrn: data.Release.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.ReleaseSecondaryImageAction(id, imageId, etag); - }, - etag: etag); + return await GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}", + data.Release.CacheRegionUrn(id), + id, + width, + height, + async () => { return await ReleaseSecondaryImageAction(id, imageId, etag); }, + etag); } public async Task>> Search(string query, int resultsCount = 10) @@ -224,21 +204,13 @@ namespace Roadie.Api.Services if (WebHelper.IsStringUrl(query)) { var s = ImageHelper.ImageSearchResultForImageUrl(query); - if (s != null) - { - result.Add(s); - } - } - var bingResults = await this.BingSearchEngine.PerformImageSearch(query, resultsCount); - if (bingResults != null) - { - result.AddRange(bingResults); - } - var iTunesResults = await this.ITunesSearchEngine.PerformImageSearch(query, resultsCount); - if (iTunesResults != null) - { - result.AddRange(iTunesResults); + if (s != null) result.Add(s); } + + var bingResults = await BingSearchEngine.PerformImageSearch(query, resultsCount); + if (bingResults != null) result.AddRange(bingResults); + var iTunesResults = await ITunesSearchEngine.PerformImageSearch(query, resultsCount); + if (iTunesResults != null) result.AddRange(iTunesResults); sw.Stop(); return new OperationResult> { @@ -248,66 +220,60 @@ namespace Roadie.Api.Services }; } - public async Task> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> TrackImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "TrackThumbnail", - regionUrn: data.Track.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.TrackImageAction(id, width, height, etag); - }, - etag: etag); + return await GetImageFileOperation("TrackThumbnail", + data.Track.CacheRegionUrn(id), + id, + width, + height, + async () => { return await TrackImageAction(id, width, height, etag); }, + etag); } - public async Task> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + public async Task> UserImage(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { - return await this.GetImageFileOperation(type: "UserById", - regionUrn: ApplicationUser.CacheRegionUrn(id), - id: id, - width: width, - height: height, - action: async () => - { - return await this.UserImageAction(id, etag); - }, - etag: etag); + return await GetImageFileOperation("UserById", + ApplicationUser.CacheRegionUrn(id), + id, + width, + height, + async () => { return await UserImageAction(id, etag); }, + etag); } private Task> ArtistImageAction(Guid id, EntityTagHeaderValue etag = null) { try { - var artist = this.GetArtist(id); + var artist = GetArtist(id); if (artist == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("Artist Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("Artist Not Found [{0}]", id))); byte[] imageBytes = null; string artistFolder = null; try { // See if artist images exists in artist folder - artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); + artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); if (!Directory.Exists(artistFolder)) { - this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ artist.ToString() }`"); + Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`"); } else { - var artistImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), Library.Enums.ImageType.Artist); - if (artistImages.Any()) - { - imageBytes = File.ReadAllBytes(artistImages.First().FullName); - } + var artistImages = + ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.Artist); + if (artistImages.Any()) imageBytes = File.ReadAllBytes(artistImages.First().FullName); } } catch (Exception ex) { - this.Logger.LogError(ex, $"Error Reading Folder [{ artistFolder }] For Artist [{ artist.Id }]"); + Logger.LogError(ex, $"Error Reading Folder [{artistFolder}] For Artist [{artist.Id}]"); } + imageBytes = imageBytes ?? artist.Thumbnail; var image = new data.Image { @@ -315,51 +281,50 @@ namespace Roadie.Api.Services CreatedDate = artist.CreatedDate, LastUpdated = artist.LastUpdated }; - if (imageBytes == null || !imageBytes.Any()) - { - image = this.DefaultNotFoundImages.Artist; - } + if (imageBytes == null || !imageBytes.Any()) image = DefaultNotFoundImages.Artist; return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } catch (Exception ex) { - this.Logger.LogError($"Error fetching Artist Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Artist Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private Task> ArtistSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null) + private Task> ArtistSecondaryImageAction(Guid id, int imageId, + EntityTagHeaderValue etag = null) { try { - var artist = this.GetArtist(id); + var artist = GetArtist(id); if (artist == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("Release Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("Release Not Found [{0}]", id))); byte[] imageBytes = null; string artistFolder = null; try { // See if cover art file exists in release folder - artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); + artistFolder = artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); if (!Directory.Exists(artistFolder)) { - this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ artist }`"); + Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{artist}`"); } else { - var artistSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), Library.Enums.ImageType.ArtistSecondary).ToArray(); + var artistSecondaryImages = ImageHelper + .FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary) + .ToArray(); if (artistSecondaryImages.Length >= imageId && artistSecondaryImages[imageId] != null) - { imageBytes = File.ReadAllBytes(artistSecondaryImages[imageId].FullName); - } } } catch (Exception ex) { - this.Logger.LogError(ex, $"Error Reading Artist Folder [{ artistFolder }] For Artist `{ artist }`"); + Logger.LogError(ex, $"Error Reading Artist Folder [{artistFolder}] For Artist `{artist}`"); } + var image = new data.Image { Bytes = imageBytes, @@ -370,8 +335,9 @@ namespace Roadie.Api.Services } catch (Exception ex) { - this.Logger.LogError($"Error fetching Release Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Release Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } @@ -379,11 +345,10 @@ namespace Roadie.Api.Services { try { - var collection = this.GetCollection(id); + var collection = GetCollection(id); if (collection == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("Collection Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("Collection Not Found [{0}]", id))); var image = new data.Image { Bytes = collection.Thumbnail, @@ -391,63 +356,57 @@ namespace Roadie.Api.Services LastUpdated = collection.LastUpdated }; if (collection.Thumbnail == null || !collection.Thumbnail.Any()) - { - image = this.DefaultNotFoundImages.Collection; - } + image = DefaultNotFoundImages.Collection; return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } catch (Exception ex) { - this.Logger.LogError($"Error fetching Collection Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Collection Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private FileOperationResult GenerateFileOperationResult(Guid id, data.Image image, EntityTagHeaderValue etag = null, string contentType = "image/jpeg") + private FileOperationResult GenerateFileOperationResult(Guid id, data.Image image, + EntityTagHeaderValue etag = null, string contentType = "image/jpeg") { - var imageEtag = EtagHelper.GenerateETag(this.HttpEncoder, image.Bytes); - if (EtagHelper.CompareETag(this.HttpEncoder, etag, imageEtag)) - { + var imageEtag = EtagHelper.GenerateETag(HttpEncoder, image.Bytes); + if (EtagHelper.CompareETag(HttpEncoder, etag, imageEtag)) return new FileOperationResult(OperationMessages.NotModified); - } if (!image?.Bytes?.Any() ?? false) - { return new FileOperationResult(string.Format("ImageById Not Set [{0}]", id)); - } - return new FileOperationResult(image?.Bytes?.Any() ?? false ? OperationMessages.OkMessage : OperationMessages.NoImageDataFound) + return new FileOperationResult(image?.Bytes?.Any() ?? false + ? OperationMessages.OkMessage + : OperationMessages.NoImageDataFound) { IsSuccess = true, Data = image.Adapt(), ContentType = contentType, - LastModified = (image.LastUpdated ?? image.CreatedDate), + LastModified = image.LastUpdated ?? image.CreatedDate, ETag = imageEtag }; } - private async Task> GetImageFileOperation(string type, string regionUrn, Guid id, int? width, int? height, Func>> action, EntityTagHeaderValue etag = null) + private async Task> GetImageFileOperation(string type, string regionUrn, Guid id, + int? width, int? height, Func>> action, EntityTagHeaderValue etag = null) { try { var sw = Stopwatch.StartNew(); - var result = (await this.CacheManager.GetAsync($"urn:{ type }_by_id_operation:{id}", action, regionUrn)).Adapt>(); - if (!result.IsSuccess) - { - return new FileOperationResult(result.IsNotFoundResult, result.Messages); - } - if (result.ETag == etag) - { - return new FileOperationResult(OperationMessages.NotModified); - } + var result = (await CacheManager.GetAsync($"urn:{type}_by_id_operation:{id}", action, regionUrn)) + .Adapt>(); + if (!result.IsSuccess) return new FileOperationResult(result.IsNotFoundResult, result.Messages); + if (result.ETag == etag) return new FileOperationResult(OperationMessages.NotModified); if ((width.HasValue || height.HasValue) && result?.Data?.Bytes != null) { result.Data.Bytes = ImageHelper.ResizeImage(result?.Data?.Bytes, width.Value, height.Value); - result.ETag = EtagHelper.GenerateETag(this.HttpEncoder, result.Data.Bytes); + result.ETag = EtagHelper.GenerateETag(HttpEncoder, result.Data.Bytes); result.LastModified = DateTime.UtcNow; - if (width.Value != this.Configuration.ThumbnailImageSize.Width || height.Value != this.Configuration.ThumbnailImageSize.Height) - { - this.Logger.LogTrace($"{ type }: Resized [{ id }], Width [{ width.Value }], Height [{ height.Value }]"); - } + if (width.Value != Configuration.ThumbnailImageSize.Width || + height.Value != Configuration.ThumbnailImageSize.Height) + Logger.LogTrace($"{type}: Resized [{id}], Width [{width.Value}], Height [{height.Value}]"); } + sw.Stop(); return new FileOperationResult(result.Messages) { @@ -462,8 +421,9 @@ namespace Roadie.Api.Services } catch (Exception ex) { - this.Logger.LogError(ex, $"GetImageFileOperation Error, Type [{ type }], id [{id}]"); + Logger.LogError(ex, $"GetImageFileOperation Error, Type [{type}], id [{id}]"); } + return new FileOperationResult("System Error"); } @@ -471,20 +431,20 @@ namespace Roadie.Api.Services { try { - var image = this.DbContext.Images - .Include("Release") - .Include("Artist") - .FirstOrDefault(x => x.RoadieId == id); + var image = DbContext.Images + .Include("Release") + .Include("Artist") + .FirstOrDefault(x => x.RoadieId == id); if (image == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("ImageById Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("ImageById Not Found [{0}]", id))); return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } catch (Exception ex) { - this.Logger.LogError($"Error fetching Image [{ id }]", ex); + Logger.LogError($"Error fetching Image [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } @@ -492,27 +452,24 @@ namespace Roadie.Api.Services { try { - var label = this.GetLabel(id); + var label = GetLabel(id); if (label == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("Label Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("Label Not Found [{0}]", id))); var image = new data.Image { Bytes = label.Thumbnail, CreatedDate = label.CreatedDate, LastUpdated = label.LastUpdated }; - if (label.Thumbnail == null || !label.Thumbnail.Any()) - { - image = this.DefaultNotFoundImages.Label; - } + if (label.Thumbnail == null || !label.Thumbnail.Any()) image = DefaultNotFoundImages.Label; return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } catch (Exception ex) { - this.Logger.LogError($"Error fetching Label Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Label Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } @@ -520,27 +477,24 @@ namespace Roadie.Api.Services { try { - var playlist = this.GetPlaylist(id); + var playlist = GetPlaylist(id); if (playlist == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("Playlist Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("Playlist Not Found [{0}]", id))); var image = new data.Image { Bytes = playlist.Thumbnail, CreatedDate = playlist.CreatedDate, LastUpdated = playlist.LastUpdated }; - if (playlist.Thumbnail == null || !playlist.Thumbnail.Any()) - { - image = this.DefaultNotFoundImages.Playlist; - } + if (playlist.Thumbnail == null || !playlist.Thumbnail.Any()) image = DefaultNotFoundImages.Playlist; return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } catch (Exception ex) { - this.Logger.LogError($"Error fetching Playlist Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Playlist Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } @@ -548,43 +502,44 @@ namespace Roadie.Api.Services { try { - var release = this.GetRelease(id); + var release = GetRelease(id); if (release == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("Release Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("Release Not Found [{0}]", id))); byte[] imageBytes = null; string artistFolder = null; string releaseFolder = null; try { // See if cover art file exists in release folder - artistFolder = release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); + artistFolder = release.Artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); if (!Directory.Exists(artistFolder)) { - this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ release.Artist.ToString() }`"); + Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`"); } else { releaseFolder = release.ReleaseFileFolder(artistFolder); if (!Directory.Exists(releaseFolder)) { - this.Logger.LogWarning($"Release Folder [{ releaseFolder }], Not Found For Release `{ release.ToString() }`"); + Logger.LogWarning($"Release Folder [{releaseFolder}], Not Found For Release `{release}`"); } else { - var releaseCoverFiles = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), Library.Enums.ImageType.Release); + var releaseCoverFiles = + ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), + ImageType.Release); if (releaseCoverFiles.Any()) - { imageBytes = File.ReadAllBytes(releaseCoverFiles.First().FullName); - } } } } catch (Exception ex) { - this.Logger.LogError(ex, $"Error Reading Release Folder [{ releaseFolder }] Artist Folder [{ artistFolder }] For Artist `{ release.Artist.Id }`"); + Logger.LogError(ex, + $"Error Reading Release Folder [{releaseFolder}] Artist Folder [{artistFolder}] For Artist `{release.Artist.Id}`"); } + imageBytes = imageBytes ?? release.Thumbnail; var image = new data.Image { @@ -592,60 +547,60 @@ namespace Roadie.Api.Services CreatedDate = release.CreatedDate, LastUpdated = release.LastUpdated }; - if (release.Thumbnail == null || !release.Thumbnail.Any()) - { - image = this.DefaultNotFoundImages.Release; - } + if (release.Thumbnail == null || !release.Thumbnail.Any()) image = DefaultNotFoundImages.Release; return Task.FromResult(GenerateFileOperationResult(id, image, etag)); } catch (Exception ex) { - this.Logger.LogError($"Error fetching Release Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Release Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private Task> ReleaseSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null) + private Task> ReleaseSecondaryImageAction(Guid id, int imageId, + EntityTagHeaderValue etag = null) { try { - var release = this.GetRelease(id); + var release = GetRelease(id); if (release == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("Release Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("Release Not Found [{0}]", id))); byte[] imageBytes = null; string artistFolder = null; string releaseFolder = null; try { // See if cover art file exists in release folder - artistFolder = release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder); + artistFolder = release.Artist.ArtistFileFolder(Configuration, Configuration.LibraryFolder); if (!Directory.Exists(artistFolder)) { - this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ release.Artist.ToString() }`"); + Logger.LogWarning($"Artist Folder [{artistFolder}], Not Found For Artist `{release.Artist}`"); } else { releaseFolder = release.ReleaseFileFolder(artistFolder); if (!Directory.Exists(releaseFolder)) { - this.Logger.LogWarning($"Release Folder [{ releaseFolder }], Not Found For Release `{ release.ToString() }`"); + Logger.LogWarning($"Release Folder [{releaseFolder}], Not Found For Release `{release}`"); } else { - var releaseSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), Library.Enums.ImageType.ReleaseSecondary).ToArray(); + var releaseSecondaryImages = ImageHelper + .FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary) + .ToArray(); if (releaseSecondaryImages.Length >= imageId && releaseSecondaryImages[imageId] != null) - { imageBytes = File.ReadAllBytes(releaseSecondaryImages[imageId].FullName); - } } } } catch (Exception ex) { - this.Logger.LogError(ex, $"Error Reading Release Folder [{ releaseFolder }] Artist Folder [{ artistFolder }] For Artist `{ release.Artist.Id }`"); + Logger.LogError(ex, + $"Error Reading Release Folder [{releaseFolder}] Artist Folder [{artistFolder}] For Artist `{release.Artist.Id}`"); } + var image = new data.Image { Bytes = imageBytes, @@ -656,26 +611,25 @@ namespace Roadie.Api.Services } catch (Exception ex) { - this.Logger.LogError($"Error fetching Release Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Release Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } - private async Task> TrackImageAction(Guid id, int? width, int? height, EntityTagHeaderValue etag = null) + private async Task> TrackImageAction(Guid id, int? width, int? height, + EntityTagHeaderValue etag = null) { try { - var track = this.GetTrack(id); + var track = GetTrack(id); if (track == null) - { return new FileOperationResult(true, string.Format("Track Not Found [{0}]", id)); - } var imageBytes = track.Thumbnail; - var trackThumbnailImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(track.PathToTrackThumbnail(this.Configuration, this.Configuration.LibraryFolder)), Library.Enums.ImageType.Track, SearchOption.TopDirectoryOnly); - if (trackThumbnailImages.Any()) - { - imageBytes = File.ReadAllBytes(trackThumbnailImages.First().FullName); - } + var trackThumbnailImages = ImageHelper.FindImageTypeInDirectory( + new DirectoryInfo(track.PathToTrackThumbnail(Configuration, Configuration.LibraryFolder)), + ImageType.Track, SearchOption.TopDirectoryOnly); + if (trackThumbnailImages.Any()) imageBytes = File.ReadAllBytes(trackThumbnailImages.First().FullName); var image = new data.Image { Bytes = track.Thumbnail, @@ -683,16 +637,15 @@ namespace Roadie.Api.Services LastUpdated = track.LastUpdated }; if (track.Thumbnail == null || !track.Thumbnail.Any()) - { // If no track image is found then return image for release - return await this.ReleaseImage(track.ReleaseMedia.Release.RoadieId, width, height, etag); - } + return await ReleaseImage(track.ReleaseMedia.Release.RoadieId, width, height, etag); return GenerateFileOperationResult(id, image, etag); } catch (Exception ex) { - this.Logger.LogError($"Error fetching Track Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching Track Thumbnail [{id}]", ex); } + return new FileOperationResult(OperationMessages.ErrorOccured); } @@ -700,27 +653,24 @@ namespace Roadie.Api.Services { try { - var user = this.GetUser(id); + var user = GetUser(id); if (user == null) - { - return Task.FromResult(new FileOperationResult(true, string.Format("User Not Found [{0}]", id))); - } + return Task.FromResult(new FileOperationResult(true, + string.Format("User Not Found [{0}]", id))); var image = new data.Image { Bytes = user.Avatar, CreatedDate = user.CreatedDate.Value, LastUpdated = user.LastUpdated }; - if (user.Avatar == null || !user.Avatar.Any()) - { - image = this.DefaultNotFoundImages.User; - } + if (user.Avatar == null || !user.Avatar.Any()) image = DefaultNotFoundImages.User; return Task.FromResult(GenerateFileOperationResult(id, image, etag, "image/png")); } catch (Exception ex) { - this.Logger.LogError($"Error fetching User Thumbnail [{ id }]", ex); + Logger.LogError($"Error fetching User Thumbnail [{id}]", ex); } + return Task.FromResult(new FileOperationResult(OperationMessages.ErrorOccured)); } } diff --git a/Roadie.Api.Services/LabelService.cs b/Roadie.Api.Services/LabelService.cs index b9ce8b4..8de4bd7 100644 --- a/Roadie.Api.Services/LabelService.cs +++ b/Roadie.Api.Services/LabelService.cs @@ -11,6 +11,7 @@ using Roadie.Library.Extensions; using Roadie.Library.Imaging; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; +using Roadie.Library.Models.Statistics; using Roadie.Library.Models.Users; using Roadie.Library.Utility; using System; @@ -26,54 +27,56 @@ namespace Roadie.Api.Services { public class LabelService : ServiceBase, ILabelService { - private IBookmarkService BookmarkService { get; } = null; + private IBookmarkService BookmarkService { get; } public LabelService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger, - ICollectionService collectionService, - IPlaylistService playlistService, - IBookmarkService bookmarkService) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger, + ICollectionService collectionService, + IPlaylistService playlistService, + IBookmarkService bookmarkService) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { - this.BookmarkService = bookmarkService; + BookmarkService = bookmarkService; } public async Task> ById(User roadieUser, Guid id, IEnumerable includes = null) { var sw = Stopwatch.StartNew(); sw.Start(); - var cacheKey = string.Format("urn:label_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes)); - var result = await this.CacheManager.GetAsync>(cacheKey, async () => - { - return await this.LabelByIdAction(id, includes); - }, data.Artist.CacheRegionUrn(id)); + var cacheKey = string.Format("urn:label_by_id_operation:{0}:{1}", id, + includes == null ? "0" : string.Join("|", includes)); + var result = await CacheManager.GetAsync(cacheKey, + async () => { return await LabelByIdAction(id, includes); }, data.Artist.CacheRegionUrn(id)); sw.Stop(); if (result?.Data != null && roadieUser != null) { - var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Label); + var userBookmarkResult = + await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Label); if (userBookmarkResult.IsSuccess) - { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != null; - } + result.Data.UserBookmarked = + userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != + null; if (result.Data.Comments.Any()) { var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + var userCommentReactions = (from cr in DbContext.CommentReactions where commentIds.Contains(cr.CommentId) where cr.UserId == roadieUser.Id select cr).ToArray(); foreach (var comment in result.Data.Comments) { - var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); + var userCommentReaction = + userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; } } } + return new OperationResult diff --git a/Roadie.Api.Services/ServiceBase.cs b/Roadie.Api.Services/ServiceBase.cs index 74358e6..f98de37 100644 --- a/Roadie.Api.Services/ServiceBase.cs +++ b/Roadie.Api.Services/ServiceBase.cs @@ -22,247 +22,188 @@ namespace Roadie.Api.Services { public static string TrackTokenSalt = "B0246908-FBD6-4E12-A96C-AF5B086115B3"; - protected readonly ICacheManager _cacheManager = null; - protected readonly IRoadieSettings _configuration = null; - protected readonly data.IRoadieDbContext _dbContext = null; - protected readonly IHttpContext _httpContext = null; - protected readonly IHttpEncoder _httpEncoder = null; - protected readonly ILogger _logger = null; + protected readonly ICacheManager _cacheManager; + protected readonly IRoadieSettings _configuration; + protected readonly data.IRoadieDbContext _dbContext; + protected readonly IHttpContext _httpContext; + protected readonly IHttpEncoder _httpEncoder; + protected readonly ILogger _logger; - protected ICacheManager CacheManager - { - get - { - return this._cacheManager; - } - } + protected ICacheManager CacheManager => _cacheManager; - protected IRoadieSettings Configuration - { - get - { - return this._configuration; - } - } + protected IRoadieSettings Configuration => _configuration; - protected data.IRoadieDbContext DbContext - { - get - { - return this._dbContext; - } - } + protected data.IRoadieDbContext DbContext => _dbContext; - protected IHttpContext HttpContext - { - get - { - return this._httpContext; - } - } + protected IHttpContext HttpContext => _httpContext; - protected IHttpEncoder HttpEncoder - { - get - { - return this._httpEncoder; - } - } + protected IHttpEncoder HttpEncoder => _httpEncoder; - protected ILogger Logger - { - get - { - return this._logger; - } - } + protected ILogger Logger => _logger; public ServiceBase(IRoadieSettings configuration, IHttpEncoder httpEncoder, data.IRoadieDbContext context, - ICacheManager cacheManager, ILogger logger, IHttpContext httpContext) + ICacheManager cacheManager, ILogger logger, IHttpContext httpContext) { - this._configuration = configuration; - this._httpEncoder = httpEncoder; - this._dbContext = context; - this._cacheManager = cacheManager; - this._logger = logger; - this._httpContext = httpContext; + _configuration = configuration; + _httpEncoder = httpEncoder; + _dbContext = context; + _cacheManager = cacheManager; + _logger = logger; + _httpContext = httpContext; } public static bool ConfirmTrackPlayToken(ApplicationUser user, Guid trackRoadieId, string token) { - if (string.IsNullOrEmpty(token)) - { - return false; - } - return ServiceBase.TrackPlayToken(user, trackRoadieId).Equals(token); + if (string.IsNullOrEmpty(token)) return false; + return TrackPlayToken(user, trackRoadieId).Equals(token); } public static string TrackPlayToken(ApplicationUser user, Guid trackId) { - var hashids = new Hashids(ServiceBase.TrackTokenSalt); + var hashids = new Hashids(TrackTokenSalt); var trackIdPart = BitConverter.ToInt32(trackId.ToByteArray(), 6); - if (trackIdPart < 0) - { - trackIdPart = trackIdPart * -1; - } - var token = hashids.Encode(user.Id, SafeParser.ToNumber(user.CreatedDate.Value.ToString("DDHHmmss")), trackIdPart); + if (trackIdPart < 0) trackIdPart = trackIdPart * -1; + var token = hashids.Encode(user.Id, SafeParser.ToNumber(user.CreatedDate.Value.ToString("DDHHmmss")), + trackIdPart); return token; } - public Image MakeThumbnailImage(Guid id, string type, int? width = null, int? height = null, bool includeCachebuster = false) + public Image MakeThumbnailImage(Guid id, string type, int? width = null, int? height = null, + bool includeCachebuster = false) { - return this.MakeImage(id, type, width ?? this.Configuration.ThumbnailImageSize.Width, height ?? this.Configuration.ThumbnailImageSize.Height, null, includeCachebuster); + return MakeImage(id, type, width ?? Configuration.ThumbnailImageSize.Width, + height ?? Configuration.ThumbnailImageSize.Height, null, includeCachebuster); } 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 + var trackArtistIds = (from r in DbContext.Releases + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join tr in 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); + trackArtistIds.Add(DbContext.Releases.FirstOrDefault(x => x.Id == releaseId).ArtistId); return trackArtistIds.Distinct().ToArray(); } protected data.Artist GetArtist(string artistName) { - if (string.IsNullOrEmpty(artistName)) + if (string.IsNullOrEmpty(artistName)) return null; + var artistByName = CacheManager.Get(data.Artist.CacheUrnByName(artistName), () => { - return null; - } - var artistByName = this.CacheManager.Get(data.Artist.CacheUrnByName(artistName), () => - { - return this.DbContext.Artists - .FirstOrDefault(x => x.Name == artistName); + return DbContext.Artists + .FirstOrDefault(x => x.Name == artistName); }, null); - if (artistByName == null) - { - return null; - } - return this.GetArtist(artistByName.RoadieId); + if (artistByName == null) return null; + return GetArtist(artistByName.RoadieId); } protected data.Artist GetArtist(Guid id) { - return this.CacheManager.Get(data.Artist.CacheUrn(id), () => + return CacheManager.Get(data.Artist.CacheUrn(id), () => { - return this.DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == id); + return DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == id); }, data.Artist.CacheRegionUrn(id)); } protected data.Collection GetCollection(Guid id) { - return this.CacheManager.Get(data.Collection.CacheUrn(id), () => + return CacheManager.Get(data.Collection.CacheUrn(id), () => { - return this.DbContext.Collections - .FirstOrDefault(x => x.RoadieId == id); + return DbContext.Collections + .FirstOrDefault(x => x.RoadieId == id); }, data.Collection.CacheRegionUrn(id)); } protected data.Label GetLabel(Guid id) { - return this.CacheManager.Get(data.Label.CacheUrn(id), () => + return CacheManager.Get(data.Label.CacheUrn(id), () => { - return this.DbContext.Labels - .FirstOrDefault(x => x.RoadieId == id); + return DbContext.Labels + .FirstOrDefault(x => x.RoadieId == id); }, data.Label.CacheRegionUrn(id)); } protected data.Playlist GetPlaylist(Guid id) { - return this.CacheManager.Get(data.Playlist.CacheUrn(id), () => + return CacheManager.Get(data.Playlist.CacheUrn(id), () => { - return this.DbContext.Playlists - .Include(x => x.User) - .FirstOrDefault(x => x.RoadieId == id); + return DbContext.Playlists + .Include(x => x.User) + .FirstOrDefault(x => x.RoadieId == id); }, data.Playlist.CacheRegionUrn(id)); } protected data.Release GetRelease(Guid id) { - return this.CacheManager.Get(data.Release.CacheUrn(id), () => + return CacheManager.Get(data.Release.CacheUrn(id), () => { - return this.DbContext.Releases - .Include(x => x.Artist) - .Include(x => x.Genres) - .Include("Genres.Genre") - .Include(x => x.Medias) - .Include("Medias.Tracks") - .Include("Medias.Tracks.TrackArtist") - .FirstOrDefault(x => x.RoadieId == id); + return DbContext.Releases + .Include(x => x.Artist) + .Include(x => x.Genres) + .Include("Genres.Genre") + .Include(x => x.Medias) + .Include("Medias.Tracks") + .Include("Medias.Tracks.TrackArtist") + .FirstOrDefault(x => x.RoadieId == id); }, data.Release.CacheRegionUrn(id)); } /// - /// Get Track by Subsonic Id ("T:guid") + /// Get Track by Subsonic Id ("T:guid") /// protected data.Track GetTrack(string id) { - Guid trackId = Guid.Empty; - if (Guid.TryParse(id, out trackId)) - { - return this.GetTrack(trackId); - } + var trackId = Guid.Empty; + if (Guid.TryParse(id, out trackId)) return GetTrack(trackId); return null; } // Only read operations protected data.Track GetTrack(Guid id) { - return this.CacheManager.Get(data.Track.CacheUrn(id), () => + return CacheManager.Get(data.Track.CacheUrn(id), () => { - return this.DbContext.Tracks - .Include(x => x.ReleaseMedia) - .Include(x => x.ReleaseMedia.Release) - .Include(x => x.ReleaseMedia.Release.Artist) - .Include(x => x.TrackArtist) - .FirstOrDefault(x => x.RoadieId == id); + return DbContext.Tracks + .Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .Include(x => x.TrackArtist) + .FirstOrDefault(x => x.RoadieId == id); }, data.Track.CacheRegionUrn(id)); } protected ApplicationUser GetUser(string username) { - if (string.IsNullOrEmpty(username)) - { - return null; - } - var userByUsername = this.CacheManager.Get(ApplicationUser.CacheUrnByUsername(username), () => - { - return this.DbContext.Users.FirstOrDefault(x => x.UserName == username); - }, null); - return this.GetUser(userByUsername?.RoadieId); + if (string.IsNullOrEmpty(username)) return null; + var userByUsername = CacheManager.Get(ApplicationUser.CacheUrnByUsername(username), + () => { return DbContext.Users.FirstOrDefault(x => x.UserName == username); }, null); + return GetUser(userByUsername?.RoadieId); } protected ApplicationUser GetUser(Guid? id) { - if (!id.HasValue) + if (!id.HasValue) return null; + return CacheManager.Get(ApplicationUser.CacheUrn(id.Value), () => { - return null; - } - return this.CacheManager.Get(ApplicationUser.CacheUrn(id.Value), () => - { - return this.DbContext.Users - .Include(x => x.UserRoles) - .Include("UserRoles.Role") - .Include("UserRoles.Role.RoleClaims") - .Include(x => x.Claims) - .Include(x => x.UserQues) - .Include("UserQues.Track") - .FirstOrDefault(x => x.RoadieId == id); + return DbContext.Users + .Include(x => x.UserRoles) + .Include("UserRoles.Role") + .Include("UserRoles.Role.RoleClaims") + .Include(x => x.Claims) + .Include(x => x.UserQues) + .Include("UserQues.Track") + .FirstOrDefault(x => x.RoadieId == id); }, ApplicationUser.CacheRegionUrn(id.Value)); } protected Image MakeArtistThumbnailImage(Guid? id) { - if (!id.HasValue) - { - return null; - } + if (!id.HasValue) return null; return MakeThumbnailImage(id.Value, "artist"); } @@ -273,26 +214,31 @@ namespace Roadie.Api.Services protected Image MakeFullsizeImage(Guid id, string caption = null) { - return new Image($"{this.HttpContext.ImageBaseUrl }/{id}", caption, $"{this.HttpContext.ImageBaseUrl }/{id}/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }"); + return new Image($"{HttpContext.ImageBaseUrl}/{id}", caption, + $"{HttpContext.ImageBaseUrl}/{id}/{Configuration.SmallImageSize.Width}/{Configuration.SmallImageSize.Height}"); } protected Image MakeFullsizeSecondaryImage(Guid id, ImageType type, int imageId, string caption = null) { if (type == ImageType.ArtistSecondary) - { - return new Image($"{this.HttpContext.ImageBaseUrl }/artist-secondary/{id}/{imageId}", caption, $"{this.HttpContext.ImageBaseUrl }/artist-secondary/{id}/{ imageId }/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }"); - } - return new Image($"{this.HttpContext.ImageBaseUrl }/release-secondary/{id}/{imageId}", caption, $"{this.HttpContext.ImageBaseUrl }/release-secondary/{id}/{ imageId }/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }"); + return new Image($"{HttpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}", caption, + $"{HttpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}/{Configuration.SmallImageSize.Width}/{Configuration.SmallImageSize.Height}"); + return new Image($"{HttpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}", caption, + $"{HttpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}/{Configuration.SmallImageSize.Width}/{Configuration.SmallImageSize.Height}"); } - protected Image MakeImage(Guid id, int width = 200, int height = 200, string caption = null, bool includeCachebuster = false) + protected Image MakeImage(Guid id, int width = 200, int height = 200, string caption = null, + bool includeCachebuster = false) { - return new Image($"{this.HttpContext.ImageBaseUrl }/{id}/{ width }/{ height }/{ (includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty) }", caption, $"{this.HttpContext.ImageBaseUrl }/{id}/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }"); + return new Image( + $"{HttpContext.ImageBaseUrl}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}", + caption, + $"{HttpContext.ImageBaseUrl}/{id}/{Configuration.SmallImageSize.Width}/{Configuration.SmallImageSize.Height}"); } protected Image MakeImage(Guid id, string type, IImageSize imageSize) { - return this.MakeImage(id, type, imageSize.Width, imageSize.Height); + return MakeImage(id, type, imageSize.Width, imageSize.Height); } protected Image MakeLabelThumbnailImage(Guid id) @@ -302,12 +248,12 @@ namespace Roadie.Api.Services protected string MakeLastFmUrl(string artistName, string releaseTitle) { - return "http://www.last.fm/music/" + this.HttpEncoder.UrlEncode($"{ artistName }/{ releaseTitle }"); + return "http://www.last.fm/music/" + HttpEncoder.UrlEncode($"{artistName}/{releaseTitle}"); } protected Image MakeNewImage(string type) { - return new Image($"{this.HttpContext.ImageBaseUrl }/{type}.jpg", null, null); + return new Image($"{HttpContext.ImageBaseUrl}/{type}.jpg", null, null); } protected Image MakePlaylistThumbnailImage(Guid id) @@ -322,7 +268,8 @@ namespace Roadie.Api.Services protected string MakeTrackPlayUrl(ApplicationUser user, int trackId, Guid trackRoadieId) { - return $"{ this.HttpContext.BaseUrl }/play/track/{user.Id}/{ ServiceBase.TrackPlayToken(user, trackRoadieId)}/{ trackRoadieId }.mp3"; + return + $"{HttpContext.BaseUrl}/play/track/{user.Id}/{TrackPlayToken(user, trackRoadieId)}/{trackRoadieId}.mp3"; } protected Image MakeTrackThumbnailImage(Guid id) @@ -337,16 +284,13 @@ namespace Roadie.Api.Services protected async Task> SetArtistRating(Guid artistId, ApplicationUser user, short rating) { - var artist = this.DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == artistId); - if (artist == null) - { - return new OperationResult(true, $"Invalid Artist Id [{ artistId }]"); - } + var artist = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == artistId); + if (artist == null) return new OperationResult(true, $"Invalid Artist Id [{artistId}]"); var now = DateTime.UtcNow; - var userArtist = this.DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); + var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); if (userArtist == null) { userArtist = new data.UserArtist @@ -355,31 +299,29 @@ namespace Roadie.Api.Services UserId = user.Id, ArtistId = artist.Id }; - this.DbContext.UserArtists.Add(userArtist); + DbContext.UserArtists.Add(userArtist); } else { userArtist.Rating = rating; userArtist.LastUpdated = now; } - await this.DbContext.SaveChangesAsync(); - var ratings = this.DbContext.UserArtists.Where(x => x.ArtistId == artist.Id && x.Rating > 0).Select(x => x.Rating); + await DbContext.SaveChangesAsync(); + + var ratings = DbContext.UserArtists.Where(x => x.ArtistId == artist.Id && x.Rating > 0) + .Select(x => x.Rating); if (ratings != null && ratings.Any()) - { artist.Rating = (short)ratings.Average(x => (decimal)x); - } else - { artist.Rating = 0; - } artist.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); - await this.UpdateArtistRank(artist.Id); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(artist.CacheRegion); + await DbContext.SaveChangesAsync(); + await UpdateArtistRank(artist.Id); + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(artist.CacheRegion); - artist = this.GetArtist(artistId); + artist = GetArtist(artistId); return new OperationResult { @@ -388,21 +330,20 @@ namespace Roadie.Api.Services }; } - protected async Task> SetReleaseRating(Guid releaseId, ApplicationUser user, short rating) + protected async Task> SetReleaseRating(Guid releaseId, ApplicationUser user, + short rating) { - var release = this.DbContext.Releases - .Include(x => x.Artist) - .Include(x => x.Genres) - .Include("Genres.Genre") - .Include(x => x.Medias) - .Include("Medias.Tracks") - .Include("Medias.Tracks.TrackArtist") - .FirstOrDefault(x => x.RoadieId == releaseId); - if (release == null) - { - return new OperationResult(true, $"Invalid Release Id [{ releaseId }]"); - } - var userRelease = this.DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); + var release = DbContext.Releases + .Include(x => x.Artist) + .Include(x => x.Genres) + .Include("Genres.Genre") + .Include(x => x.Medias) + .Include("Medias.Tracks") + .Include("Medias.Tracks.TrackArtist") + .FirstOrDefault(x => x.RoadieId == releaseId); + if (release == null) return new OperationResult(true, $"Invalid Release Id [{releaseId}]"); + var userRelease = + DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); var now = DateTime.UtcNow; if (userRelease == null) { @@ -412,32 +353,30 @@ namespace Roadie.Api.Services UserId = user.Id, ReleaseId = release.Id }; - this.DbContext.UserReleases.Add(userRelease); + DbContext.UserReleases.Add(userRelease); } else { userRelease.Rating = rating; userRelease.LastUpdated = now; } - await this.DbContext.SaveChangesAsync(); - var ratings = this.DbContext.UserReleases.Where(x => x.ReleaseId == release.Id && x.Rating > 0).Select(x => x.Rating); + await DbContext.SaveChangesAsync(); + + var ratings = DbContext.UserReleases.Where(x => x.ReleaseId == release.Id && x.Rating > 0) + .Select(x => x.Rating); if (ratings != null && ratings.Any()) - { release.Rating = (short)ratings.Average(x => (decimal)x); - } else - { release.Rating = 0; - } release.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); - await this.UpdateReleaseRank(release.Id); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(release.CacheRegion); - this.CacheManager.ClearRegion(release.Artist.CacheRegion); + await DbContext.SaveChangesAsync(); + await UpdateReleaseRank(release.Id); + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(release.CacheRegion); + CacheManager.ClearRegion(release.Artist.CacheRegion); - release = this.GetRelease(releaseId); + release = GetRelease(releaseId); return new OperationResult { @@ -450,18 +389,15 @@ namespace Roadie.Api.Services { var sw = Stopwatch.StartNew(); - var track = this.DbContext.Tracks - .Include(x => x.ReleaseMedia) - .Include(x => x.ReleaseMedia.Release) - .Include(x => x.ReleaseMedia.Release.Artist) - .Include(x => x.TrackArtist) - .FirstOrDefault(x => x.RoadieId == trackId); - if (track == null) - { - return new OperationResult(true, $"Invalid Track Id [{ trackId }]"); - } + var track = DbContext.Tracks + .Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .Include(x => x.TrackArtist) + .FirstOrDefault(x => x.RoadieId == trackId); + if (track == null) return new OperationResult(true, $"Invalid Track Id [{trackId}]"); var now = DateTime.UtcNow; - var userTrack = this.DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); + var userTrack = DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); if (userTrack == null) { userTrack = new data.UserTrack @@ -470,36 +406,30 @@ namespace Roadie.Api.Services UserId = user.Id, TrackId = track.Id }; - this.DbContext.UserTracks.Add(userTrack); + DbContext.UserTracks.Add(userTrack); } else { userTrack.Rating = rating; userTrack.LastUpdated = now; } - await this.DbContext.SaveChangesAsync(); - var ratings = this.DbContext.UserTracks.Where(x => x.TrackId == track.Id && x.Rating > 0).Select(x => x.Rating); + await DbContext.SaveChangesAsync(); + + var ratings = DbContext.UserTracks.Where(x => x.TrackId == track.Id && x.Rating > 0).Select(x => x.Rating); if (ratings != null && ratings.Any()) - { track.Rating = (short)ratings.Average(x => (decimal)x); - } else - { track.Rating = 0; - } track.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); - await this.UpdateReleaseRank(track.ReleaseMedia.Release.Id); + await DbContext.SaveChangesAsync(); + await UpdateReleaseRank(track.ReleaseMedia.Release.Id); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(track.CacheRegion); - this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); - this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); - if (track.TrackArtist != null) - { - this.CacheManager.ClearRegion(track.TrackArtist.CacheRegion); - } + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(track.CacheRegion); + CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); + CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); + if (track.TrackArtist != null) CacheManager.ClearRegion(track.TrackArtist.CacheRegion); sw.Stop(); return new OperationResult @@ -510,17 +440,15 @@ namespace Roadie.Api.Services }; } - protected async Task> ToggleArtistDisliked(Guid artistId, ApplicationUser user, bool isDisliked) + protected async Task> ToggleArtistDisliked(Guid artistId, ApplicationUser user, + bool isDisliked) { - var artist = this.DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == artistId); - if (artist == null) - { - return new OperationResult(true, $"Invalid Artist Id [{ artistId }]"); - } - var userArtist = this.DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); + var artist = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == artistId); + if (artist == null) return new OperationResult(true, $"Invalid Artist Id [{artistId}]"); + var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); if (userArtist == null) { userArtist = new data.UserArtist @@ -529,17 +457,18 @@ namespace Roadie.Api.Services UserId = user.Id, ArtistId = artist.Id }; - this.DbContext.UserArtists.Add(userArtist); + DbContext.UserArtists.Add(userArtist); } else { userArtist.IsDisliked = isDisliked; userArtist.LastUpdated = DateTime.UtcNow; } - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(artist.CacheRegion); + await DbContext.SaveChangesAsync(); + + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(artist.CacheRegion); return new OperationResult { @@ -548,17 +477,15 @@ namespace Roadie.Api.Services }; } - protected async Task> ToggleArtistFavorite(Guid artistId, ApplicationUser user, bool isFavorite) + protected async Task> ToggleArtistFavorite(Guid artistId, ApplicationUser user, + bool isFavorite) { - var artist = this.DbContext.Artists - .Include(x => x.Genres) - .Include("Genres.Genre") - .FirstOrDefault(x => x.RoadieId == artistId); - if (artist == null) - { - return new OperationResult(true, $"Invalid Artist Id [{ artistId }]"); - } - var userArtist = this.DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); + var artist = DbContext.Artists + .Include(x => x.Genres) + .Include("Genres.Genre") + .FirstOrDefault(x => x.RoadieId == artistId); + if (artist == null) return new OperationResult(true, $"Invalid Artist Id [{artistId}]"); + var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); if (userArtist == null) { userArtist = new data.UserArtist @@ -567,17 +494,18 @@ namespace Roadie.Api.Services UserId = user.Id, ArtistId = artist.Id }; - this.DbContext.UserArtists.Add(userArtist); + DbContext.UserArtists.Add(userArtist); } else { userArtist.IsFavorite = isFavorite; userArtist.LastUpdated = DateTime.UtcNow; } - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(artist.CacheRegion); + await DbContext.SaveChangesAsync(); + + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(artist.CacheRegion); return new OperationResult { @@ -586,21 +514,20 @@ namespace Roadie.Api.Services }; } - protected async Task> ToggleReleaseDisliked(Guid releaseId, ApplicationUser user, bool isDisliked) + protected async Task> ToggleReleaseDisliked(Guid releaseId, ApplicationUser user, + bool isDisliked) { - var release = this.DbContext.Releases - .Include(x => x.Artist) - .Include(x => x.Genres) - .Include("Genres.Genre") - .Include(x => x.Medias) - .Include("Medias.Tracks") - .Include("Medias.Tracks.TrackArtist") - .FirstOrDefault(x => x.RoadieId == releaseId); - if (release == null) - { - return new OperationResult(true, $"Invalid Release Id [{ releaseId }]"); - } - var userRelease = this.DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); + var release = DbContext.Releases + .Include(x => x.Artist) + .Include(x => x.Genres) + .Include("Genres.Genre") + .Include(x => x.Medias) + .Include("Medias.Tracks") + .Include("Medias.Tracks.TrackArtist") + .FirstOrDefault(x => x.RoadieId == releaseId); + if (release == null) return new OperationResult(true, $"Invalid Release Id [{releaseId}]"); + var userRelease = + DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); if (userRelease == null) { userRelease = new data.UserRelease @@ -609,18 +536,19 @@ namespace Roadie.Api.Services UserId = user.Id, ReleaseId = release.Id }; - this.DbContext.UserReleases.Add(userRelease); + DbContext.UserReleases.Add(userRelease); } else { userRelease.IsDisliked = isDisliked; userRelease.LastUpdated = DateTime.UtcNow; } - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(release.CacheRegion); - this.CacheManager.ClearRegion(release.Artist.CacheRegion); + await DbContext.SaveChangesAsync(); + + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(release.CacheRegion); + CacheManager.ClearRegion(release.Artist.CacheRegion); return new OperationResult { @@ -629,21 +557,20 @@ namespace Roadie.Api.Services }; } - protected async Task> ToggleReleaseFavorite(Guid releaseId, ApplicationUser user, bool isFavorite) + protected async Task> ToggleReleaseFavorite(Guid releaseId, ApplicationUser user, + bool isFavorite) { - var release = this.DbContext.Releases - .Include(x => x.Artist) - .Include(x => x.Genres) - .Include("Genres.Genre") - .Include(x => x.Medias) - .Include("Medias.Tracks") - .Include("Medias.Tracks.TrackArtist") - .FirstOrDefault(x => x.RoadieId == releaseId); - if (release == null) - { - return new OperationResult(true, $"Invalid Release Id [{ releaseId }]"); - } - var userRelease = this.DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); + var release = DbContext.Releases + .Include(x => x.Artist) + .Include(x => x.Genres) + .Include("Genres.Genre") + .Include(x => x.Medias) + .Include("Medias.Tracks") + .Include("Medias.Tracks.TrackArtist") + .FirstOrDefault(x => x.RoadieId == releaseId); + if (release == null) return new OperationResult(true, $"Invalid Release Id [{releaseId}]"); + var userRelease = + DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); if (userRelease == null) { userRelease = new data.UserRelease @@ -652,18 +579,19 @@ namespace Roadie.Api.Services UserId = user.Id, ReleaseId = release.Id }; - this.DbContext.UserReleases.Add(userRelease); + DbContext.UserReleases.Add(userRelease); } else { userRelease.IsFavorite = isFavorite; userRelease.LastUpdated = DateTime.UtcNow; } - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(release.CacheRegion); - this.CacheManager.ClearRegion(release.Artist.CacheRegion); + await DbContext.SaveChangesAsync(); + + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(release.CacheRegion); + CacheManager.ClearRegion(release.Artist.CacheRegion); return new OperationResult { @@ -672,19 +600,17 @@ namespace Roadie.Api.Services }; } - protected async Task> ToggleTrackDisliked(Guid trackId, ApplicationUser user, bool isDisliked) + protected async Task> ToggleTrackDisliked(Guid trackId, ApplicationUser user, + bool isDisliked) { - var track = this.DbContext.Tracks - .Include(x => x.ReleaseMedia) - .Include(x => x.ReleaseMedia.Release) - .Include(x => x.ReleaseMedia.Release.Artist) - .Include(x => x.TrackArtist) - .FirstOrDefault(x => x.RoadieId == trackId); - if (track == null) - { - return new OperationResult(true, $"Invalid Track Id [{ trackId }]"); - } - var userTrack = this.DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); + var track = DbContext.Tracks + .Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .Include(x => x.TrackArtist) + .FirstOrDefault(x => x.RoadieId == trackId); + if (track == null) return new OperationResult(true, $"Invalid Track Id [{trackId}]"); + var userTrack = DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); if (userTrack == null) { userTrack = new data.UserTrack @@ -693,19 +619,20 @@ namespace Roadie.Api.Services UserId = user.Id, TrackId = track.Id }; - this.DbContext.UserTracks.Add(userTrack); + DbContext.UserTracks.Add(userTrack); } else { userTrack.IsDisliked = isDisliked; userTrack.LastUpdated = DateTime.UtcNow; } - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(track.CacheRegion); - this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); - this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); + await DbContext.SaveChangesAsync(); + + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(track.CacheRegion); + CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); + CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); return new OperationResult { @@ -714,19 +641,17 @@ namespace Roadie.Api.Services }; } - protected async Task> ToggleTrackFavorite(Guid trackId, ApplicationUser user, bool isFavorite) + protected async Task> ToggleTrackFavorite(Guid trackId, ApplicationUser user, + bool isFavorite) { - var track = this.DbContext.Tracks - .Include(x => x.ReleaseMedia) - .Include(x => x.ReleaseMedia.Release) - .Include(x => x.ReleaseMedia.Release.Artist) - .Include(x => x.TrackArtist) - .FirstOrDefault(x => x.RoadieId == trackId); - if (track == null) - { - return new OperationResult(true, $"Invalid Track Id [{ trackId }]"); - } - var userTrack = this.DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); + var track = DbContext.Tracks + .Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .Include(x => x.TrackArtist) + .FirstOrDefault(x => x.RoadieId == trackId); + if (track == null) return new OperationResult(true, $"Invalid Track Id [{trackId}]"); + var userTrack = DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); if (userTrack == null) { userTrack = new data.UserTrack @@ -735,19 +660,20 @@ namespace Roadie.Api.Services UserId = user.Id, TrackId = track.Id }; - this.DbContext.UserTracks.Add(userTrack); + DbContext.UserTracks.Add(userTrack); } else { userTrack.IsFavorite = isFavorite; userTrack.LastUpdated = DateTime.UtcNow; } - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(user.CacheRegion); - this.CacheManager.ClearRegion(track.CacheRegion); - this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); - this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); + await DbContext.SaveChangesAsync(); + + CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(track.CacheRegion); + CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); + CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); return new OperationResult { @@ -758,83 +684,81 @@ namespace Roadie.Api.Services protected async Task UpdateArtistCounts(int artistId, DateTime now) { - var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == artistId); + var artist = 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) + artist.ReleaseCount = DbContext.Releases.Where(x => x.ArtistId == artistId).Count(); + artist.TrackCount = (from r in DbContext.Releases + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join tr in 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); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(artist.CacheRegion); } } /// - /// Update the counts for all artists on a release (both track and release artists) + /// 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); - } + foreach (var artistId in ArtistIdsForRelease(releaseId)) await UpdateArtistCounts(artistId, now); } /// - /// Update Artist Rank - /// Artist Rank is a sum of the artists release ranks + artist tracks rating + artist user rating + /// Update Artist Rank + /// Artist Rank is a sum of the artists release ranks + artist tracks rating + artist user rating /// protected async Task UpdateArtistRank(int artistId, bool updateReleaseRanks = false) { try { - var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == artistId); + var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId); if (artist != null) { if (updateReleaseRanks) { - var artistReleaseIds = this.DbContext.Releases.Where(x => x.ArtistId == artistId).Select(x => x.Id).ToArray(); + var artistReleaseIds = DbContext.Releases.Where(x => x.ArtistId == artistId).Select(x => x.Id) + .ToArray(); foreach (var artistReleaseId in artistReleaseIds) - { - await this.UpdateReleaseRank(artistReleaseId, false); - } + await UpdateReleaseRank(artistReleaseId, false); } - var artistTrackAverage = (from t in this.DbContext.Tracks - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id - join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId + var artistTrackAverage = (from t in DbContext.Tracks + join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + join ut in DbContext.UserTracks on t.Id equals ut.TrackId where t.ArtistId == artist.Id select ut.Rating).Select(x => (decimal?)x).Average(); - var artistReleaseRatingRating = (from r in this.DbContext.Releases - join ur in this.DbContext.UserReleases on r.Id equals ur.ReleaseId + var artistReleaseRatingRating = (from r in DbContext.Releases + join ur in DbContext.UserReleases on r.Id equals ur.ReleaseId where r.ArtistId == artist.Id select ur.Rating).Select(x => (decimal?)x).Average(); - var artistReleaseRankSum = (from r in this.DbContext.Releases + var artistReleaseRankSum = (from r in DbContext.Releases where r.ArtistId == artist.Id select r.Rank).ToArray().Sum(x => x) ?? 0; - artist.Rank = SafeParser.ToNumber(artistTrackAverage + artistReleaseRatingRating) + artistReleaseRankSum + artist.Rating; + artist.Rank = SafeParser.ToNumber(artistTrackAverage + artistReleaseRatingRating) + + artistReleaseRankSum + artist.Rating; - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(artist.CacheRegion); - this.Logger.LogInformation("UpdatedArtistRank For Artist `{0}`", artist); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(artist.CacheRegion); + Logger.LogInformation("UpdatedArtistRank For Artist `{0}`", artist); } } catch (Exception ex) { - this.Logger.LogError(ex, "Error in UpdateArtistRank ArtistId [{0}], UpdateReleaseRanks [{1}]", artistId, updateReleaseRanks); + Logger.LogError(ex, "Error in UpdateArtistRank ArtistId [{0}], UpdateReleaseRanks [{1}]", artistId, + updateReleaseRanks); } } /// - /// Find all artists involved with release and update their rank + /// Find all artists involved with release and update their rank /// protected async Task UpdateArtistsRankForRelease(data.Release release) { @@ -844,138 +768,142 @@ namespace Roadie.Api.Services { release.ArtistId }; - var trackArtistsForRelease = (from t in this.DbContext.Tracks - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + var trackArtistsForRelease = (from t in DbContext.Tracks + join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id where rm.ReleaseId == release.Id where t.ArtistId.HasValue select t.ArtistId.Value).ToArray(); artistsForRelease.AddRange(trackArtistsForRelease); - foreach (var artistId in artistsForRelease.Distinct()) - { - await this.UpdateArtistRank(artistId); - } + foreach (var artistId in artistsForRelease.Distinct()) await UpdateArtistRank(artistId); } } protected async Task UpdateLabelCounts(int labelId, DateTime now) { - var label = this.DbContext.Labels.FirstOrDefault(x => x.Id == labelId); + var label = 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 + label.ReleaseCount = DbContext.ReleaseLabels.Where(x => x.LabelId == label.Id).Count(); + label.ArtistCount = (from r in DbContext.Releases + join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId + join a in DbContext.Artists on r.ArtistId equals a.Id where rl.LabelId == label.Id - group a by a.Id into artists + 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 + label.TrackCount = (from r in DbContext.Releases + join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join t in 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); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(label.CacheRegion); } } protected async Task UpdatePlaylistCounts(int playlistId, DateTime now) { - var playlist = this.DbContext.Playlists.FirstOrDefault(x => x.Id == playlistId); + var playlist = 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(); + var playlistTracks = 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.ReleaseCount = + (short)playlistTracks.Select(x => x.Track.ReleaseMedia.ReleaseId).Distinct().Count(); playlist.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(playlist.CacheRegion); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(playlist.CacheRegion); } } protected async Task UpdateReleaseCounts(int releaseId, DateTime now) { - var release = this.DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); + var release = DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); if (release != null) { - release.PlayedCount = (from t in this.DbContext.Tracks - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + release.PlayedCount = (from t in DbContext.Tracks + join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id where rm.ReleaseId == releaseId where t.PlayedCount.HasValue select t).Sum(x => x.PlayedCount); - release.Duration = (from t in this.DbContext.Tracks - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + release.Duration = (from t in DbContext.Tracks + join rm in 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); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(release.CacheRegion); } } /// - /// Update Relase Rank - /// Release Rank Calculation = Average of Track User Ratings + (User Rating of Release / Release Track Count) + Collection Rank Value + /// Update Relase Rank + /// Release Rank Calculation = Average of Track User Ratings + (User Rating of Release / Release Track Count) + + /// Collection Rank Value /// protected async Task UpdateReleaseRank(int releaseId, bool updateArtistRank = true) { try { - var release = this.DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); + var release = DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); if (release != null) { - var releaseTrackAverage = (from ut in this.DbContext.UserTracks - join t in this.DbContext.Tracks on ut.TrackId equals t.Id - join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + var releaseTrackAverage = (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 where rm.ReleaseId == releaseId select ut.Rating).Select(x => (decimal?)x).Average(); - var releaseUserRatingRank = release.Rating > 0 ? (decimal?)release.Rating / (decimal?)release.TrackCount : 0; + var releaseUserRatingRank = release.Rating > 0 ? release.Rating / (decimal?)release.TrackCount : 0; - var collectionsWithRelease = (from c in this.DbContext.Collections - join cr in this.DbContext.CollectionReleases on c.Id equals cr.CollectionId - where c.CollectionType != Library.Enums.CollectionType.Chart - where cr.ReleaseId == release.Id - select new - { - c.CollectionCount, - cr.ListNumber - }); + var collectionsWithRelease = from c in DbContext.Collections + join cr in DbContext.CollectionReleases on c.Id equals cr.CollectionId + where c.CollectionType != CollectionType.Chart + where cr.ReleaseId == release.Id + select new + { + c.CollectionCount, + cr.ListNumber + }; decimal releaseCollectionRank = 0; foreach (var collectionWithRelease in collectionsWithRelease) { - var rank = (decimal)((collectionWithRelease.CollectionCount * .01) - ((collectionWithRelease.ListNumber - 1) * .01)); + var rank = (decimal)(collectionWithRelease.CollectionCount * .01 - + (collectionWithRelease.ListNumber - 1) * .01); releaseCollectionRank += rank; } - release.Rank = SafeParser.ToNumber(releaseTrackAverage) + releaseUserRatingRank + releaseCollectionRank; - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(release.CacheRegion); - this.Logger.LogInformation("UpdateReleaseRank For Release `{0}`", release); - if (updateArtistRank) - { - await this.UpdateArtistsRankForRelease(release); - } + release.Rank = SafeParser.ToNumber(releaseTrackAverage) + releaseUserRatingRank + + releaseCollectionRank; + + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(release.CacheRegion); + Logger.LogInformation("UpdateReleaseRank For Release `{0}`", release); + if (updateArtistRank) await UpdateArtistsRankForRelease(release); } } catch (Exception ex) { - this.Logger.LogError(ex, "Error UpdateReleaseRank RelaseId [{0}], UpdateArtistRank [{1}]", releaseId, updateArtistRank); + Logger.LogError(ex, "Error UpdateReleaseRank RelaseId [{0}], UpdateArtistRank [{1}]", releaseId, + updateArtistRank); } } - private Image MakeImage(Guid id, string type, int? width, int? height, string caption = null, bool includeCachebuster = false) + private Image MakeImage(Guid id, string type, int? width, int? height, string caption = null, + bool includeCachebuster = false) { - if (width.HasValue && height.HasValue && (width.Value != this.Configuration.ThumbnailImageSize.Width || height.Value != this.Configuration.ThumbnailImageSize.Height)) - { - return new Image($"{this.HttpContext.ImageBaseUrl }/{type}/{id}/{width}/{height}/{ (includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty) }", caption, $"{this.HttpContext.ImageBaseUrl }/{type}/{id}/{ this.Configuration.ThumbnailImageSize.Width }/{ this.Configuration.ThumbnailImageSize.Height }"); - } - return new Image($"{this.HttpContext.ImageBaseUrl }/{type}/{id}", caption, null); + if (width.HasValue && height.HasValue && (width.Value != Configuration.ThumbnailImageSize.Width || + height.Value != Configuration.ThumbnailImageSize.Height)) + return new Image( + $"{HttpContext.ImageBaseUrl}/{type}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}", + caption, + $"{HttpContext.ImageBaseUrl}/{type}/{id}/{Configuration.ThumbnailImageSize.Width}/{Configuration.ThumbnailImageSize.Height}"); + return new Image($"{HttpContext.ImageBaseUrl}/{type}/{id}", caption, null); } } } \ No newline at end of file diff --git a/Roadie.Api.Services/StatisticsService.cs b/Roadie.Api.Services/StatisticsService.cs index 848de4f..a0eb585 100644 --- a/Roadie.Api.Services/StatisticsService.cs +++ b/Roadie.Api.Services/StatisticsService.cs @@ -18,11 +18,11 @@ namespace Roadie.Api.Services public class StatisticsService : ServiceBase, IStatisticsService { public StatisticsService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { } @@ -34,7 +34,7 @@ namespace Roadie.Api.Services sw.Start(); try { - using (var conn = new MySqlConnection(this.Configuration.ConnectionString)) + using (var conn = new MySqlConnection(Configuration.ConnectionString)) { conn.Open(); var sql = @"SELECT rm.releaseMediaCount AS releaseMediaCount, COUNT(r.roadieId) AS releaseCount, @@ -75,9 +75,7 @@ namespace Roadie.Api.Services using (var rdr = await cmd.ExecuteReaderAsync()) { if (rdr.HasRows) - { while (rdr.Read()) - { result = new LibraryStats { UserCount = SafeParser.ToNumber(rdr["UserCount"]), @@ -92,13 +90,11 @@ namespace Roadie.Api.Services TotalTrackDuration = SafeParser.ToNumber(rdr["TotalTrackDuration"]), TotalTrackSize = SafeParser.ToNumber(rdr["TotalTrackSize"]) }; - } - } } } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); } finally { @@ -106,17 +102,16 @@ namespace Roadie.Api.Services } } } - var lastScan = this.DbContext.ScanHistories.OrderByDescending(x => x.CreatedDate).FirstOrDefault(); - if (lastScan != null) - { - result.LastScan = lastScan.CreatedDate; - } + + var lastScan = DbContext.ScanHistories.OrderByDescending(x => x.CreatedDate).FirstOrDefault(); + if (lastScan != null) result.LastScan = lastScan.CreatedDate; sw.Stop(); } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); } + return new OperationResult { IsSuccess = result != null, @@ -132,7 +127,7 @@ namespace Roadie.Api.Services var result = new List(); - using (var conn = new MySqlConnection(this.Configuration.ConnectionString)) + using (var conn = new MySqlConnection(Configuration.ConnectionString)) { conn.Open(); var sql = @"SELECT DATE_FORMAT(createdDate, '%Y-%m-%d') as date, count(1) as count @@ -146,21 +141,17 @@ namespace Roadie.Api.Services using (var rdr = cmd.ExecuteReader()) { if (rdr.HasRows) - { while (rdr.Read()) - { result.Add(new DateAndCount { Date = SafeParser.ToString(rdr["date"]), Count = SafeParser.ToNumber(rdr["count"]) }); - } - } } } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); } finally { diff --git a/Roadie.Api.Services/SubsonicService.cs b/Roadie.Api.Services/SubsonicService.cs index 40ea953..f0ce9e1 100644 --- a/Roadie.Api.Services/SubsonicService.cs +++ b/Roadie.Api.Services/SubsonicService.cs @@ -6,9 +6,12 @@ using Newtonsoft.Json; using Roadie.Library.Caching; using Roadie.Library.Configuration; using Roadie.Library.Encoding; +using Roadie.Library.Enums; using Roadie.Library.Extensions; using Roadie.Library.Identity; 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; @@ -16,7 +19,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Linq.Dynamic.Core; using System.Threading.Tasks; using data = Roadie.Library.Data; using subsonic = Roadie.Library.Models.ThirdPartyApi.Subsonic; @@ -24,143 +26,150 @@ using subsonic = Roadie.Library.Models.ThirdPartyApi.Subsonic; namespace Roadie.Api.Services { /// - /// Subsonic API emulator for Roadie. Enables Subsonic clients to work with Roadie. - /// - /// - /// - /// + /// Subsonic API emulator for Roadie. Enables Subsonic clients to work with Roadie. + /// + /// + /// + /// /// public class SubsonicService : ServiceBase, ISubsonicService { public const string SubsonicVersion = "1.16.1"; private IArtistService ArtistService { get; } + private IBookmarkService BookmarkService { get; } + private ICollectionService CollectionService { get; } + private IImageService ImageService { get; } + private IPlayActivityService PlayActivityService { get; } + private IPlaylistService PlaylistService { get; } + private IReleaseService ReleaseService { get; } + private ITrackService TrackService { get; } + private UserManager UserManger { get; } public SubsonicService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger, - IArtistService artistService, - ITrackService trackService, - ICollectionService collectionService, - IPlaylistService playlistService, - IReleaseService releaseService, - IImageService imageService, - IBookmarkService bookmarkService, - IPlayActivityService playActivityService, - UserManager userManager - ) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger, + IArtistService artistService, + ITrackService trackService, + ICollectionService collectionService, + IPlaylistService playlistService, + IReleaseService releaseService, + IImageService imageService, + IBookmarkService bookmarkService, + IPlayActivityService playActivityService, + UserManager userManager + ) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { - this.ArtistService = artistService; - this.BookmarkService = bookmarkService; - this.CollectionService = collectionService; - this.ImageService = imageService; - this.PlaylistService = playlistService; - this.PlayActivityService = playActivityService; - this.ReleaseService = releaseService; - this.TrackService = trackService; - this.UserManger = userManager; + ArtistService = artistService; + BookmarkService = bookmarkService; + CollectionService = collectionService; + ImageService = imageService; + PlaylistService = playlistService; + PlayActivityService = playActivityService; + ReleaseService = releaseService; + TrackService = trackService; + UserManger = userManager; } /// - /// Adds a message to the chat log. + /// Adds a message to the chat log. /// - public async Task> AddChatMessage(subsonic.Request request, User roadieUser) + public async Task> AddChatMessage(subsonic.Request request, + User roadieUser) { if (string.IsNullOrEmpty(request.Message)) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.RequiredParameterMissing, $"Message is required"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.RequiredParameterMissing, "Message is required"); var chatMessage = new data.ChatMessage { UserId = roadieUser.Id.Value, Message = request.Message }; - this.DbContext.ChatMessages.Add(chatMessage); - await this.DbContext.SaveChangesAsync(); + DbContext.ChatMessages.Add(chatMessage); + await DbContext.SaveChangesAsync(); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok } }; } /// - /// Authenticate the given credentials and return the corresponding ApplicationUser + /// Authenticate the given credentials and return the corresponding ApplicationUser /// - public async Task> Authenticate(subsonic.Request request) + public async Task> Authenticate( + subsonic.Request request) { if (request == null || string.IsNullOrEmpty(request?.u)) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.WrongUsernameOrPassword, $"Unknown Username"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.WrongUsernameOrPassword, "Unknown Username"); try { - var user = this.DbContext.Users - .FirstOrDefault(x => x.UserName == request.u); + var user = DbContext.Users + .FirstOrDefault(x => x.UserName == request.u); if (user == null) { - this.Logger.LogInformation($"Unknown User [{ request.u }]"); - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.WrongUsernameOrPassword, $"Unknown Username"); + Logger.LogInformation($"Unknown User [{request.u}]"); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.WrongUsernameOrPassword, "Unknown Username"); } + var password = request.Password; if (!string.IsNullOrEmpty(request.s)) - { try { var token = HashHelper.MD5Hash((user.ApiToken ?? user.Email) + request.s); - if (!token.Equals(request.t, StringComparison.OrdinalIgnoreCase)) - { - user = null; - } + if (!token.Equals(request.t, StringComparison.OrdinalIgnoreCase)) user = null; } catch { } - } + if (user != null && !string.IsNullOrEmpty(user.PasswordHash) && !string.IsNullOrEmpty(password)) - { try { - var hashCheck = this.UserManger.PasswordHasher.VerifyHashedPassword(user, user.PasswordHash, password); - if (hashCheck == PasswordVerificationResult.Failed) - { - user = null; - } + var hashCheck = + UserManger.PasswordHasher.VerifyHashedPassword(user, user.PasswordHash, password); + if (hashCheck == PasswordVerificationResult.Failed) user = null; } catch { } - } + if (user != null) { var now = DateTime.UtcNow; user.LastUpdated = now; user.LastApiAccess = now; - await this.DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync(); } + if (user == null) { - this.Logger.LogInformation($"Invalid Credentials given for User [{ request.u }]"); - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.WrongUsernameOrPassword, $"Unknown Username"); + Logger.LogInformation($"Invalid Credentials given for User [{request.u}]"); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.WrongUsernameOrPassword, "Unknown Username"); } - this.Logger.LogInformation($"Subsonic: Successfully Authenticated User [{ user.ToString() }] via Application [{ request.c }], Application Version [{ request.v }]"); + + Logger.LogInformation( + $"Subsonic: Successfully Authenticated User [{user}] via Application [{request.c}], Application Version [{request.v}]"); return new subsonic.SubsonicOperationResult { IsSuccess = true, @@ -172,38 +181,41 @@ namespace Roadie.Api.Services } catch (Exception ex) { - this.Logger.LogError(ex, "Subsonic.Authenticate, Error CheckPassword [" + JsonConvert.SerializeObject(request) + "]"); + Logger.LogError(ex, + "Subsonic.Authenticate, Error CheckPassword [" + JsonConvert.SerializeObject(request) + "]"); } + return null; } /// - /// Creates or updates a bookmark (a position within a media file). Bookmarks are personal and not visible to other users. + /// Creates or updates a bookmark (a position within a media file). Bookmarks are personal and not visible to other + /// users. /// - public async Task> CreateBookmark(subsonic.Request request, User roadieUser, int position, string comment) + public async Task> CreateBookmark(subsonic.Request request, + User roadieUser, int position, string comment) { if (!request.TrackId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.id }]"); - } - var track = this.GetTrack(request.TrackId.Value); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{request.id}]"); + var track = GetTrack(request.TrackId.Value); if (track == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.TrackId.Value }]"); - } - var userBookmark = this.DbContext.Bookmarks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == Library.Enums.BookmarkType.Track); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{request.TrackId.Value}]"); + var userBookmark = DbContext.Bookmarks.FirstOrDefault(x => + x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == BookmarkType.Track); var createdBookmark = false; if (userBookmark == null) { userBookmark = new data.Bookmark { BookmarkTargetId = track.Id, - BookmarkType = Library.Enums.BookmarkType.Track, + BookmarkType = BookmarkType.Track, UserId = roadieUser.Id.Value, Comment = comment, Position = position }; - this.DbContext.Bookmarks.Add(userBookmark); + DbContext.Bookmarks.Add(userBookmark); createdBookmark = true; } else @@ -212,25 +224,27 @@ namespace Roadie.Api.Services userBookmark.Position = position; userBookmark.Comment = comment; } - await this.DbContext.SaveChangesAsync(); - var user = this.GetUser(roadieUser.UserId); - this.CacheManager.ClearRegion(user.CacheRegion); + await DbContext.SaveChangesAsync(); - this.Logger.LogInformation($"{ (createdBookmark ? "Created" : "Updated") } Bookmark `{ userBookmark}` for User `{ roadieUser }`"); + var user = GetUser(roadieUser.UserId); + CacheManager.ClearRegion(user.CacheRegion); + + Logger.LogInformation( + $"{(createdBookmark ? "Created" : "Updated")} Bookmark `{userBookmark}` for User `{roadieUser}`"); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok } }; } /// - /// Creates (or updates) a playlist. + /// Creates (or updates) a playlist. /// /// Populated Subsonic Request /// Populated Roadie User @@ -238,49 +252,50 @@ namespace Roadie.Api.Services /// ID of a song in the playlist. Use one songId parameter for each song in the playlist. /// The playlist ID. (if updating else blank is adding) /// - public async Task> CreatePlaylist(subsonic.Request request, User roadieUser, string name, string[] songIds, string playlistId = null) + public async Task> CreatePlaylist(subsonic.Request request, + User roadieUser, string name, string[] songIds, string playlistId = null) { data.Playlist playlist = null; - Guid?[] songRoadieIds = new Guid?[0]; - IQueryable submittedTracks = new data.Track[0].AsQueryable(); + var songRoadieIds = new Guid?[0]; + var submittedTracks = new data.Track[0].AsQueryable(); if (songIds != null && songIds.Any()) { songRoadieIds = songIds.Select(x => SafeParser.ToGuid(x)).ToArray(); // Add (if not already) given tracks to Playlist - submittedTracks = (from t in this.DbContext.Tracks - where songRoadieIds.Contains(t.RoadieId) - select t); + submittedTracks = from t in DbContext.Tracks + where songRoadieIds.Contains(t.RoadieId) + select t; } + var didCreate = false; if (!string.IsNullOrEmpty(playlistId)) { request.id = playlistId; - playlist = this.DbContext.Playlists.Include(x => x.Tracks).FirstOrDefault(x => x.RoadieId == request.PlaylistId); + playlist = DbContext.Playlists.Include(x => x.Tracks) + .FirstOrDefault(x => x.RoadieId == request.PlaylistId); if (playlist == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid PlaylistId [{ playlistId }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid PlaylistId [{playlistId}]"); // When Create is called again on an existing delete all existing tracks and add given if (playlist.Tracks != null && playlist.Tracks.Any()) - { - this.DbContext.PlaylistTracks.RemoveRange(playlist.Tracks); - } - var listNumber = playlist.Tracks != null && playlist.Tracks.Any() ? playlist.Tracks?.Max(x => x.ListNumber) ?? 0 : 0; + DbContext.PlaylistTracks.RemoveRange(playlist.Tracks); + var listNumber = playlist.Tracks != null && playlist.Tracks.Any() + ? playlist.Tracks?.Max(x => x.ListNumber) ?? 0 + : 0; foreach (var submittedTrack in submittedTracks) - { if (playlist.Tracks == null || !playlist.Tracks.Any(x => x.TrackId == submittedTrack.Id)) { listNumber++; - this.DbContext.PlaylistTracks.Add(new data.PlaylistTrack + DbContext.PlaylistTracks.Add(new data.PlaylistTrack { PlayListId = playlist.Id, ListNumber = listNumber, TrackId = submittedTrack.Id }); } - } + playlist.Name = name ?? playlist.Name; playlist.LastUpdated = DateTime.UtcNow; } @@ -307,127 +322,131 @@ namespace Roadie.Api.Services Tracks = tracks }; didCreate = true; - this.DbContext.Playlists.Add(playlist); + DbContext.Playlists.Add(playlist); } - await this.DbContext.SaveChangesAsync(); - this.Logger.LogInformation($"Subsonic: User `{ roadieUser }` { (didCreate ? "created" : "modified") } Playlist `{ playlist }` added [{ songRoadieIds.Count() }] Tracks."); - request.id = subsonic.Request.PlaylistdIdentifier + playlist.RoadieId.ToString(); - return await this.GetPlaylist(request, roadieUser); + + await DbContext.SaveChangesAsync(); + Logger.LogInformation( + $"Subsonic: User `{roadieUser}` {(didCreate ? "created" : "modified")} Playlist `{playlist}` added [{songRoadieIds.Count()}] Tracks."); + request.id = subsonic.Request.PlaylistdIdentifier + playlist.RoadieId; + return await GetPlaylist(request, roadieUser); } /// - /// Deletes the bookmark for a given file. + /// Deletes the bookmark for a given file. /// - public async Task> DeleteBookmark(subsonic.Request request, User roadieUser) + public async Task> DeleteBookmark(subsonic.Request request, + User roadieUser) { if (!request.TrackId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.id }]"); - } - var track = this.GetTrack(request.TrackId.Value); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{request.id}]"); + var track = GetTrack(request.TrackId.Value); if (track == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.TrackId.Value }]"); - } - var userBookmark = this.DbContext.Bookmarks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == Library.Enums.BookmarkType.Track); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{request.TrackId.Value}]"); + var userBookmark = DbContext.Bookmarks.FirstOrDefault(x => + x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == BookmarkType.Track); if (userBookmark != null) { - this.DbContext.Bookmarks.Remove(userBookmark); - await this.DbContext.SaveChangesAsync(); + DbContext.Bookmarks.Remove(userBookmark); + await DbContext.SaveChangesAsync(); - var user = this.GetUser(roadieUser.UserId); - this.CacheManager.ClearRegion(user.CacheRegion); + var user = GetUser(roadieUser.UserId); + CacheManager.ClearRegion(user.CacheRegion); - this.Logger.LogInformation($"Subsonic: Deleted Bookmark `{ userBookmark}` for User `{ roadieUser }`"); + Logger.LogInformation($"Subsonic: Deleted Bookmark `{userBookmark}` for User `{roadieUser}`"); } + return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok } }; } /// - /// Deletes a saved playlist. + /// Deletes a saved playlist. /// - public async Task> DeletePlaylist(subsonic.Request request, User roadieUser) + public async Task> DeletePlaylist(subsonic.Request request, + User roadieUser) { if (!request.PlaylistId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.id }]"); - } - var playlist = this.GetPlaylist(request.PlaylistId.Value); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{request.id}]"); + var playlist = GetPlaylist(request.PlaylistId.Value); if (playlist == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.TrackId.Value }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{request.TrackId.Value}]"); if (playlist.UserId != roadieUser.Id && !roadieUser.IsAdmin) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, "User is not allowed to delete playlist."); - } - this.DbContext.Playlists.Remove(playlist); - await this.DbContext.SaveChangesAsync(); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, + "User is not allowed to delete playlist."); + DbContext.Playlists.Remove(playlist); + await DbContext.SaveChangesAsync(); - var user = this.GetUser(roadieUser.UserId); - this.CacheManager.ClearRegion(user.CacheRegion); + var user = GetUser(roadieUser.UserId); + CacheManager.ClearRegion(user.CacheRegion); - this.Logger.LogInformation($"Subsonic: Deleted Playlist `{ playlist}` for User `{ roadieUser }`"); + Logger.LogInformation($"Subsonic: Deleted Playlist `{playlist}` for User `{roadieUser}`"); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok } }; } /// - /// Returns details for an album, including a list of songs. This method organizes music according to ID3 tags. + /// Returns details for an album, including a list of songs. This method organizes music according to ID3 tags. /// - public async Task> GetAlbum(subsonic.Request request, User roadieUser) + public async Task> GetAlbum(subsonic.Request request, + User roadieUser) { try { var releaseId = SafeParser.ToGuid(request.id); if (!releaseId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{ request.ReleaseId}]"); - } - var release = this.GetRelease(releaseId.Value); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{request.ReleaseId}]"); + var release = GetRelease(releaseId.Value); if (release == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{ request.ReleaseId}]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{request.ReleaseId}]"); var trackPagedRequest = request.PagedRequest; trackPagedRequest.Sort = "TrackNumber"; trackPagedRequest.Order = "ASC"; - var releaseTracks = await this.TrackService.List(trackPagedRequest, roadieUser, false, releaseId); - var userRelease = roadieUser == null ? null : this.DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == roadieUser.Id); + var releaseTracks = await TrackService.List(trackPagedRequest, roadieUser, false, releaseId); + var userRelease = roadieUser == null + ? null + : DbContext.UserReleases.FirstOrDefault(x => + x.ReleaseId == release.Id && x.UserId == roadieUser.Id); var genre = release.Genres.FirstOrDefault(); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.album, Item = new subsonic.AlbumWithSongsID3 { artist = release.Artist.Name, - artistId = subsonic.Request.ArtistIdIdentifier + release.Artist.RoadieId.ToString(), - coverArt = subsonic.Request.ReleaseIdIdentifier + release.RoadieId.ToString(), + artistId = subsonic.Request.ArtistIdIdentifier + release.Artist.RoadieId, + coverArt = subsonic.Request.ReleaseIdIdentifier + release.RoadieId, created = release.CreatedDate, duration = release.Duration.ToSecondsFromMilliseconds(), genre = genre == null ? null : genre.Genre.Name, - id = subsonic.Request.ReleaseIdIdentifier + release.RoadieId.ToString(), + id = subsonic.Request.ReleaseIdIdentifier + release.RoadieId, name = release.Title, playCount = releaseTracks.Rows.Sum(x => x.PlayedCount) ?? 0, playCountSpecified = releaseTracks.Rows.Any(), @@ -436,33 +455,35 @@ namespace Roadie.Api.Services starredSpecified = userRelease?.IsFavorite ?? false, year = release.ReleaseDate != null ? release.ReleaseDate.Value.Year : 0, yearSpecified = release.ReleaseDate != null, - song = this.SubsonicChildrenForTracks(releaseTracks.Rows) + song = SubsonicChildrenForTracks(releaseTracks.Rows) } } }; } catch (Exception ex) { - this.Logger.LogError(ex, "GetAlbum Request [{0}], User [{1}]", JsonConvert.SerializeObject(request), roadieUser.ToString()); + Logger.LogError(ex, "GetAlbum Request [{0}], User [{1}]", JsonConvert.SerializeObject(request), + roadieUser.ToString()); } - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{ request.ReleaseId}]"); + + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{request.ReleaseId}]"); } /// - /// Returns album notes, image URLs etc, using data from last.fm. + /// Returns album notes, image URLs etc, using data from last.fm. /// - public Task> GetAlbumInfo(subsonic.Request request, User roadieUser, subsonic.AlbumInfoVersion version) + public Task> GetAlbumInfo(subsonic.Request request, + User roadieUser, subsonic.AlbumInfoVersion version) { var releaseId = SafeParser.ToGuid(request.id); if (!releaseId.HasValue) - { - return Task.FromResult(new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{ request.id }]")); - } - var release = this.GetRelease(releaseId.Value); + return Task.FromResult(new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{request.id}]")); + var release = GetRelease(releaseId.Value); if (release == null) - { - return Task.FromResult(new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{ request.id }]")); - } + return Task.FromResult(new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{request.id}]")); switch (version) { case subsonic.AlbumInfoVersion.One: @@ -472,15 +493,18 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.albumInfo, Item = new subsonic.AlbumInfo { - largeImageUrl = this.MakeImage(release.RoadieId, "release", this.Configuration.LargeImageSize).Url, - mediumImageUrl = this.MakeImage(release.RoadieId, "release", this.Configuration.MediumImageSize).Url, - smallImageUrl = this.MakeImage(release.RoadieId, "release", this.Configuration.SmallImageSize).Url, - lastFmUrl = this.MakeLastFmUrl(release.Artist.Name, release.Title), + largeImageUrl = + MakeImage(release.RoadieId, "release", Configuration.LargeImageSize).Url, + mediumImageUrl = MakeImage(release.RoadieId, "release", Configuration.MediumImageSize) + .Url, + smallImageUrl = + MakeImage(release.RoadieId, "release", Configuration.SmallImageSize).Url, + lastFmUrl = MakeLastFmUrl(release.Artist.Name, release.Title), musicBrainzId = release.MusicBrainzId, notes = release.Profile } @@ -488,21 +512,25 @@ namespace Roadie.Api.Services }); default: - return Task.FromResult(new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, $"Unknown Album Info Version [{ request.Type}]")); + return Task.FromResult(new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, + $"Unknown Album Info Version [{request.Type}]")); } } /// - /// Returns a list of random, newest, highest rated etc. albums. Similar to the album lists on the home page of the Subsonic web interface. + /// Returns a list of random, newest, highest rated etc. albums. Similar to the album lists on the home page of the + /// Subsonic web interface. /// - public async Task> GetAlbumList(subsonic.Request request, User roadieUser, subsonic.AlbumListVersions version) + public async Task> GetAlbumList(subsonic.Request request, + User roadieUser, subsonic.AlbumListVersions version) { - var releaseResult = new Library.Models.Pagination.PagedResult(); + var releaseResult = new PagedResult(); switch (request.Type) { case subsonic.ListType.Random: - releaseResult = await this.ReleaseService.List(roadieUser, request.PagedRequest, true); + releaseResult = await ReleaseService.List(roadieUser, request.PagedRequest, true); break; case subsonic.ListType.Highest: @@ -514,17 +542,17 @@ namespace Roadie.Api.Services case subsonic.ListType.Starred: case subsonic.ListType.ByGenre: case subsonic.ListType.ByYear: - releaseResult = await this.ReleaseService.List(roadieUser, request.PagedRequest); + releaseResult = await ReleaseService.List(roadieUser, request.PagedRequest); break; default: - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, $"Unknown Album List Type [{ request.Type}]"); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, + $"Unknown Album List Type [{request.Type}]"); } if (!releaseResult.IsSuccess) - { return new subsonic.SubsonicOperationResult(releaseResult.Message); - } switch (version) { @@ -534,12 +562,12 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.albumList, Item = new subsonic.AlbumList { - album = this.SubsonicChildrenForReleases(releaseResult.Rows, null) + album = SubsonicChildrenForReleases(releaseResult.Rows, null) } } }; @@ -550,69 +578,70 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.albumList2, Item = new subsonic.AlbumList2 { - album = this.SubsonicAlbumID3ForReleases(releaseResult.Rows) + album = SubsonicAlbumID3ForReleases(releaseResult.Rows) } } }; default: - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, $"Unknown AlbumListVersions [{ version }]"); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, + $"Unknown AlbumListVersions [{version}]"); } } /// - /// Returns details for an artist, including a list of albums. This method organizes music according to ID3 tags. + /// Returns details for an artist, including a list of albums. This method organizes music according to ID3 tags. /// - public async Task> GetArtist(subsonic.Request request, User roadieUser) + public async Task> GetArtist(subsonic.Request request, + User roadieUser) { var artistId = SafeParser.ToGuid(request.id); if (!artistId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{ request.id }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{request.id}]"); var pagedRequest = request.PagedRequest; pagedRequest.Sort = "Id"; pagedRequest.FilterToArtistId = artistId.Value; - var artistResult = await this.ArtistService.List(roadieUser, pagedRequest); + var artistResult = await ArtistService.List(roadieUser, pagedRequest); var artist = artistResult.Rows.Any() ? artistResult.Rows.First() : null; if (artist == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{ request.id }]"); - } - var artistReleaseResult = await this.ReleaseService.List(roadieUser, pagedRequest); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release [{request.id}]"); + var artistReleaseResult = await ReleaseService.List(roadieUser, pagedRequest); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.artist, - Item = this.SubsonicArtistWithAlbumsID3ForArtist(artist, this.SubsonicAlbumID3ForReleases(artistReleaseResult.Rows)) + Item = SubsonicArtistWithAlbumsID3ForArtist(artist, + SubsonicAlbumID3ForReleases(artistReleaseResult.Rows)) } }; } /// - /// Returns artist info with biography, image URLs and similar artists, using data from last.fm. + /// Returns artist info with biography, image URLs and similar artists, using data from last.fm. /// - public Task> GetArtistInfo(subsonic.Request request, int? count, bool includeNotPresent, subsonic.ArtistInfoVersion version) + public Task> GetArtistInfo(subsonic.Request request, + int? count, bool includeNotPresent, subsonic.ArtistInfoVersion version) { var artistId = SafeParser.ToGuid(request.id); if (!artistId.HasValue) - { - return Task.FromResult(new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ArtistId [{ request.id }]")); - } - var artist = this.GetArtist(artistId.Value); + return Task.FromResult(new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ArtistId [{request.id}]")); + var artist = GetArtist(artistId.Value); if (artist == null) - { - return Task.FromResult(new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ArtistId [{ request.id }]")); - } + return Task.FromResult(new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ArtistId [{request.id}]")); switch (version) { @@ -622,10 +651,10 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.artistInfo, - Item = this.SubsonicArtistInfoForArtist(artist) + Item = SubsonicArtistInfoForArtist(artist) } }); @@ -635,67 +664,72 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.artistInfo2, - Item = this.SubsonicArtistInfo2InfoForArtist(artist) + Item = SubsonicArtistInfo2InfoForArtist(artist) } }); default: - return Task.FromResult(new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, $"Unknown ArtistInfoVersion [{ version }]")); + return Task.FromResult(new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, + $"Unknown ArtistInfoVersion [{version}]")); } } /// - /// Similar to getIndexes, but organizes music according to ID3 tags. + /// Similar to getIndexes, but organizes music according to ID3 tags. /// - public async Task> GetArtists(subsonic.Request request, User roadieUser) + public async Task> GetArtists(subsonic.Request request, + User roadieUser) { - var cacheKey = $"urn:subsonic_artists:{ roadieUser.UserName }"; - return await this.CacheManager.GetAsync>(cacheKey, async () => - { - return await this.GetArtistsAction(request, roadieUser); - }, CacheManagerBase.SystemCacheRegionUrn); + var cacheKey = $"urn:subsonic_artists:{roadieUser.UserName}"; + return await CacheManager.GetAsync(cacheKey, + async () => { return await GetArtistsAction(request, roadieUser); }, + CacheManagerBase.SystemCacheRegionUrn); } /// - /// Returns all bookmarks for this user. A bookmark is a position within a certain media file. + /// Returns all bookmarks for this user. A bookmark is a position within a certain media file. /// - public async Task> GetBookmarks(subsonic.Request request, User roadieUser) + public async Task> GetBookmarks(subsonic.Request request, + User roadieUser) { var pagedRequest = request.PagedRequest; pagedRequest.Sort = "LastUpdated"; pagedRequest.Order = "DESC"; - var userBookmarkResult = await this.BookmarkService.List(roadieUser, pagedRequest, false, Library.Enums.BookmarkType.Track); - pagedRequest.FilterToTrackIds = userBookmarkResult.Rows.Select(x => SafeParser.ToGuid(x.Bookmark.Value)).ToArray(); - var trackListResult = await this.TrackService.List(pagedRequest, roadieUser); + var userBookmarkResult = await BookmarkService.List(roadieUser, pagedRequest, false, BookmarkType.Track); + pagedRequest.FilterToTrackIds = + userBookmarkResult.Rows.Select(x => SafeParser.ToGuid(x.Bookmark.Value)).ToArray(); + var trackListResult = await TrackService.List(pagedRequest, roadieUser); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.bookmarks, Item = new subsonic.Bookmarks { - bookmark = this.SubsonicBookmarksForBookmarks(userBookmarkResult.Rows, trackListResult.Rows) + bookmark = SubsonicBookmarksForBookmarks(userBookmarkResult.Rows, trackListResult.Rows) } } }; } /// - /// Returns the current visible (non-expired) chat messages. + /// Returns the current visible (non-expired) chat messages. /// - public Task> GetChatMessages(subsonic.Request request, User roadieUser, long? since) + public Task> GetChatMessages(subsonic.Request request, + User roadieUser, long? since) { - DateTime? messagesSince = since.HasValue ? (DateTime?)since.Value.FromUnixTime() : null; - var chatMessages = (from cm in this.DbContext.ChatMessages - join u in this.DbContext.Users on cm.UserId equals u.Id + var messagesSince = since.HasValue ? (DateTime?)since.Value.FromUnixTime() : null; + var chatMessages = (from cm in DbContext.ChatMessages + join u in DbContext.Users on cm.UserId equals u.Id where messagesSince == null || cm.CreatedDate >= messagesSince - where cm.Status != Library.Enums.Statuses.Deleted + where cm.Status != Statuses.Deleted orderby cm.CreatedDate descending select new subsonic.ChatMessage { @@ -708,7 +742,7 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.chatMessages, Item = new subsonic.ChatMessages @@ -720,78 +754,61 @@ namespace Roadie.Api.Services } /// - /// Returns a cover art image. + /// Returns a cover art image. /// - public async Task> GetCoverArt(subsonic.Request request, int? size) + public async Task> GetCoverArt(subsonic.Request request, int? size) { var sw = Stopwatch.StartNew(); - var result = new subsonic.SubsonicFileOperationResult + var result = new subsonic.SubsonicFileOperationResult { - Data = new Roadie.Library.Models.Image() + Data = new Image() }; if (request.ArtistId != null) { - var artistImage = await this.ImageService.ArtistImage(request.ArtistId.Value, size, size); - if (!artistImage.IsSuccess) - { - return artistImage.Adapt>(); - } + var artistImage = await ImageService.ArtistImage(request.ArtistId.Value, size, size); + if (!artistImage.IsSuccess) return artistImage.Adapt>(); result.Data.Bytes = artistImage.Data.Bytes; } else if (request.TrackId != null) { - var trackimage = await this.ImageService.TrackImage(request.TrackId.Value, size, size); - if (!trackimage.IsSuccess) - { - return trackimage.Adapt>(); - } + var trackimage = await ImageService.TrackImage(request.TrackId.Value, size, size); + if (!trackimage.IsSuccess) return trackimage.Adapt>(); result.Data.Bytes = trackimage.Data.Bytes; } else if (request.CollectionId != null) { - var collectionImage = await this.ImageService.CollectionImage(request.CollectionId.Value, size, size); + var collectionImage = await ImageService.CollectionImage(request.CollectionId.Value, size, size); if (!collectionImage.IsSuccess) - { return collectionImage.Adapt>(); - } result.Data.Bytes = collectionImage.Data.Bytes; } else if (request.ReleaseId != null) { - var releaseimage = await this.ImageService.ReleaseImage(request.ReleaseId.Value, size, size); - if (!releaseimage.IsSuccess) - { - return releaseimage.Adapt>(); - } + var releaseimage = await ImageService.ReleaseImage(request.ReleaseId.Value, size, size); + if (!releaseimage.IsSuccess) return releaseimage.Adapt>(); result.Data.Bytes = releaseimage.Data.Bytes; } else if (request.PlaylistId != null) { - var playlistImage = await this.ImageService.PlaylistImage(request.PlaylistId.Value, size, size); - if (!playlistImage.IsSuccess) - { - return playlistImage.Adapt>(); - } + var playlistImage = await ImageService.PlaylistImage(request.PlaylistId.Value, size, size); + if (!playlistImage.IsSuccess) return playlistImage.Adapt>(); result.Data.Bytes = playlistImage.Data.Bytes; } else if (!string.IsNullOrEmpty(request.u)) { - var user = this.GetUser(request.u); + var user = GetUser(request.u); if (user == null) - { - return new subsonic.SubsonicFileOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Username [{ request.u}]"); - } - var userImage = await this.ImageService.UserImage(user.RoadieId, size, size); - if (!userImage.IsSuccess) - { - return userImage.Adapt>(); - } + return new subsonic.SubsonicFileOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Username [{request.u}]"); + var userImage = await ImageService.UserImage(user.RoadieId, size, size); + if (!userImage.IsSuccess) return userImage.Adapt>(); result.Data.Bytes = userImage.Data.Bytes; } + result.IsSuccess = result.Data.Bytes != null; sw.Stop(); - return new subsonic.SubsonicFileOperationResult(result.Messages) + return new subsonic.SubsonicFileOperationResult(result.Messages) { Data = result.Data, ETag = result.ETag, @@ -804,17 +821,17 @@ namespace Roadie.Api.Services } /// - /// Returns all genres + /// Returns all genres /// public Task> GetGenres(subsonic.Request request) { - var genres = (from g in this.DbContext.Genres - let albumCount = (from rg in this.DbContext.ReleaseGenres + var genres = (from g in DbContext.Genres + let albumCount = (from rg in DbContext.ReleaseGenres where rg.GenreId == g.Id select rg.Id).Count() - let songCount = (from rg in this.DbContext.ReleaseGenres - join rm in this.DbContext.ReleaseMedias on rg.ReleaseId equals rm.ReleaseId - join t in this.DbContext.Tracks on rm.ReleaseId equals t.ReleaseMediaId + let songCount = (from rg in DbContext.ReleaseGenres + join rm in DbContext.ReleaseMedias on rg.ReleaseId equals rm.ReleaseId + join t in DbContext.Tracks on rm.ReleaseId equals t.ReleaseMediaId where rg.GenreId == g.Id select t.Id).Count() select new subsonic.Genre @@ -829,7 +846,7 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.genres, Item = new subsonic.Genres @@ -841,23 +858,28 @@ namespace Roadie.Api.Services } /// - /// Returns an indexed structure of all artists. + /// Returns an indexed structure of all artists. /// /// Query from application. /// If specified, only return artists in the music folder with the given ID. - /// If specified, only return a result if the artist collection has changed since the given time (in milliseconds since 1 Jan 1970). - public async Task> GetIndexes(subsonic.Request request, User roadieUser, long? ifModifiedSince = null) + /// + /// If specified, only return a result if the artist collection has changed since the given + /// time (in milliseconds since 1 Jan 1970). + /// + public async Task> GetIndexes(subsonic.Request request, + User roadieUser, long? ifModifiedSince = null) { - var cacheKey = string.Format("urn:subsonic_indexes"); - return await this.CacheManager.GetAsync>(cacheKey, async () => + var cacheKey = "urn:subsonic_indexes"; + return await CacheManager.GetAsync(cacheKey, async () => { // Dont send the user to get index list as user data (likes, dislikes, etc.) aren't used in this list and dont need performance hit - return await this.GetIndexesAction(request, null, ifModifiedSince); + return await GetIndexesAction(request, null, ifModifiedSince); }, CacheManagerBase.SystemCacheRegionUrn); } /// - /// Get details about the software license. Takes no extra parameters. Roadies gives everyone a premium 1 year license everytime they ask :) + /// Get details about the software license. Takes no extra parameters. Roadies gives everyone a premium 1 year license + /// everytime they ask :) /// public subsonic.SubsonicOperationResult GetLicense(subsonic.Request request) { @@ -866,12 +888,12 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.license, Item = new subsonic.License { - email = this.Configuration.SmtpFromAddress, + email = Configuration.SmtpFromAddress, valid = true, licenseExpires = DateTime.UtcNow.AddYears(1), licenseExpiresSpecified = true @@ -881,16 +903,17 @@ namespace Roadie.Api.Services } /// - /// Searches for and returns lyrics for a given song + /// Searches for and returns lyrics for a given song /// - public subsonic.SubsonicOperationResult GetLyrics(subsonic.Request request, string artistId, string title) + public subsonic.SubsonicOperationResult GetLyrics(subsonic.Request request, string artistId, + string title) { return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.lyrics, Item = new subsonic.Lyrics @@ -904,91 +927,104 @@ namespace Roadie.Api.Services } /// - /// Returns a listing of all files in a music directory. Typically used to get list of albums for an artist, or list of songs for an album. + /// Returns a listing of all files in a music directory. Typically used to get list of albums for an artist, or list of + /// songs for an album. /// /// Query from application. - /// A string which uniquely identifies the music folder. Obtained by calls to getIndexes or getMusicDirectory. + /// + /// A string which uniquely identifies the music folder. Obtained by calls to getIndexes or + /// getMusicDirectory. + /// /// - public async Task> GetMusicDirectory(subsonic.Request request, User roadieUser) + public async Task> GetMusicDirectory( + subsonic.Request request, User roadieUser) { var directory = new subsonic.Directory(); - var user = this.GetUser(roadieUser?.UserId); + var user = GetUser(roadieUser?.UserId); // Request to get albums for an Artist if (request.ArtistId != null) { var artistId = SafeParser.ToGuid(request.id); - var artist = this.GetArtist(artistId.Value); + var artist = GetArtist(artistId.Value); if (artist == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ArtistId [{ request.id}]"); - } - directory.id = subsonic.Request.ArtistIdIdentifier + artist.RoadieId.ToString(); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ArtistId [{request.id}]"); + directory.id = subsonic.Request.ArtistIdIdentifier + artist.RoadieId; directory.name = artist.Name; - var artistRating = user == null ? null : this.DbContext.UserArtists.FirstOrDefault(x => x.UserId == user.Id && x.ArtistId == artist.Id); + var artistRating = user == null + ? null + : DbContext.UserArtists.FirstOrDefault(x => x.UserId == user.Id && x.ArtistId == artist.Id); if (artistRating?.IsFavorite ?? false) { - directory.starred = (artistRating.LastUpdated ?? artistRating.CreatedDate); + directory.starred = artistRating.LastUpdated ?? artistRating.CreatedDate; directory.starredSpecified = true; } + var pagedRequest = request.PagedRequest; pagedRequest.FilterToArtistId = artist.RoadieId; pagedRequest.Sort = "Release.Text"; - var artistReleases = await this.ReleaseService.List(roadieUser, pagedRequest); - directory.child = this.SubsonicChildrenForReleases(artistReleases.Rows, subsonic.Request.ArtistIdIdentifier + artist.RoadieId.ToString()); + var artistReleases = await ReleaseService.List(roadieUser, pagedRequest); + directory.child = SubsonicChildrenForReleases(artistReleases.Rows, + subsonic.Request.ArtistIdIdentifier + artist.RoadieId); } // Request to get albums for in a Collection else if (request.CollectionId != null) { var collectionId = SafeParser.ToGuid(request.id); - var collection = this.GetCollection(collectionId.Value); + var collection = GetCollection(collectionId.Value); if (collection == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid CollectionId [{ request.id}]"); - } - directory.id = subsonic.Request.CollectionIdentifier + collection.RoadieId.ToString(); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid CollectionId [{request.id}]"); + directory.id = subsonic.Request.CollectionIdentifier + collection.RoadieId; directory.name = collection.Name; var pagedRequest = request.PagedRequest; pagedRequest.FilterToCollectionId = collection.RoadieId; - var collectionReleases = await this.ReleaseService.List(roadieUser, pagedRequest); - directory.child = this.SubsonicChildrenForReleases(collectionReleases.Rows, subsonic.Request.CollectionIdentifier + collection.RoadieId.ToString()); + var collectionReleases = await ReleaseService.List(roadieUser, pagedRequest); + directory.child = SubsonicChildrenForReleases(collectionReleases.Rows, + subsonic.Request.CollectionIdentifier + collection.RoadieId); } // Request to get Tracks for an Album else if (request.ReleaseId.HasValue) { var releaseId = SafeParser.ToGuid(request.id); - var release = this.GetRelease(releaseId.Value); + var release = GetRelease(releaseId.Value); if (release == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ReleaseId [{ request.id}]"); - } - directory.id = subsonic.Request.ReleaseIdIdentifier + release.RoadieId.ToString(); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid ReleaseId [{request.id}]"); + directory.id = subsonic.Request.ReleaseIdIdentifier + release.RoadieId; directory.name = release.Title; - var releaseRating = user == null ? null : this.DbContext.UserReleases.FirstOrDefault(x => x.UserId == user.Id && x.ReleaseId == release.Id); + var releaseRating = user == null + ? null + : DbContext.UserReleases.FirstOrDefault(x => x.UserId == user.Id && x.ReleaseId == release.Id); directory.averageRating = release.Rating ?? 0; - directory.parent = subsonic.Request.ArtistIdIdentifier + release.Artist.RoadieId.ToString(); + directory.parent = subsonic.Request.ArtistIdIdentifier + release.Artist.RoadieId; if (releaseRating?.IsFavorite ?? false) { directory.starred = releaseRating.LastUpdated ?? releaseRating.CreatedDate; directory.starredSpecified = true; } + var trackPagedRequest = request.PagedRequest; trackPagedRequest.Sort = "TrackNumber"; trackPagedRequest.Order = "ASC"; - var songTracks = await this.TrackService.List(trackPagedRequest, roadieUser, false, release.RoadieId); - directory.child = this.SubsonicChildrenForTracks(songTracks.Rows); + var songTracks = await TrackService.List(trackPagedRequest, roadieUser, false, release.RoadieId); + directory.child = SubsonicChildrenForTracks(songTracks.Rows); directory.playCount = directory.child.Select(x => x.playCount).Sum(); } else { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Unknown GetMusicDirectory Type [{ JsonConvert.SerializeObject(request) }], id [{ request.id }]"); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Unknown GetMusicDirectory Type [{JsonConvert.SerializeObject(request)}], id [{request.id}]"); } + return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.directory, Item = directory @@ -997,7 +1033,7 @@ namespace Roadie.Api.Services } /// - /// Returns all configured top-level music folders. Takes no extra parameters. + /// Returns all configured top-level music folders. Takes no extra parameters. /// public Task> GetMusicFolders(subsonic.Request request) { @@ -1006,38 +1042,42 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.musicFolders, Item = new subsonic.MusicFolders { - musicFolder = this.MusicFolders().ToArray() + musicFolder = MusicFolders().ToArray() } } }); } /// - /// Returns what is currently being played by all users. Takes no extra parameters. + /// Returns what is currently being played by all users. Takes no extra parameters. /// - public async Task> GetNowPlaying(subsonic.Request request, User roadieUser) + public async Task> GetNowPlaying(subsonic.Request request, + User roadieUser) { var pagedRequest = request.PagedRequest; pagedRequest.Sort = "PlayedDateDateTime"; pagedRequest.Order = "DESC"; - var playActivityResult = await this.PlayActivityService.List(pagedRequest, roadieUser, DateTime.UtcNow.AddDays(-1)); + var playActivityResult = + await PlayActivityService.List(pagedRequest, roadieUser, DateTime.UtcNow.AddDays(-1)); pagedRequest.Sort = null; pagedRequest.Order = null; - pagedRequest.FilterToTrackIds = playActivityResult.Rows.Select(x => SafeParser.ToGuid(x.Track.Track.Value)).Distinct().ToArray(); - var playActivityTracksResult = await this.TrackService.List(pagedRequest, roadieUser); + pagedRequest.FilterToTrackIds = playActivityResult.Rows.Select(x => SafeParser.ToGuid(x.Track.Track.Value)) + .Distinct().ToArray(); + var playActivityTracksResult = await TrackService.List(pagedRequest, roadieUser); var playEntries = new List(); var now = DateTime.UtcNow; foreach (var row in playActivityResult.Rows) { - var rowTrack = playActivityTracksResult.Rows.FirstOrDefault(x => x.Track.Value == row.Track.Track.Value); - var playEntryTrackChild = this.SubsonicChildForTrack(rowTrack); + var rowTrack = + playActivityTracksResult.Rows.FirstOrDefault(x => x.Track.Value == row.Track.Track.Value); + var playEntryTrackChild = SubsonicChildForTrack(rowTrack); var playEntry = playEntryTrackChild.Adapt(); playEntry.username = row.User.Text; playEntry.minutesAgo = (int)(now - row.PlayedDateDateTime.Value).TotalMinutes; @@ -1051,7 +1091,7 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.nowPlaying, Item = new subsonic.NowPlaying @@ -1063,72 +1103,75 @@ namespace Roadie.Api.Services } /// - /// Returns a listing of files in a saved playlist. + /// Returns a listing of files in a saved playlist. /// - public async Task> GetPlaylist(subsonic.Request request, User roadieUser) + public async Task> GetPlaylist(subsonic.Request request, + User roadieUser) { var playListId = SafeParser.ToGuid(request.id); if (!playListId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid PlaylistId [{ request.id }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid PlaylistId [{request.id}]"); var pagedRequest = request.PagedRequest; pagedRequest.Sort = "Id"; pagedRequest.FilterToPlaylistId = playListId.Value; - var playlistResult = await this.PlaylistService.List(pagedRequest, roadieUser); + var playlistResult = await PlaylistService.List(pagedRequest, roadieUser); var playlist = playlistResult.Rows.Any() ? playlistResult.Rows.First() : null; if (playlist == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid PlaylistId [{ request.id }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid PlaylistId [{request.id}]"); // For a playlist to show all the tracks in the playlist set the limit to the playlist size pagedRequest.Limit = playlist.PlaylistCount ?? pagedRequest.Limit; - var tracksForPlaylist = await this.TrackService.List(pagedRequest, roadieUser); + var tracksForPlaylist = await TrackService.List(pagedRequest, roadieUser); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.playlist, - Item = this.SubsonicPlaylistForPlaylist(playlist, tracksForPlaylist.Rows) + Item = SubsonicPlaylistForPlaylist(playlist, tracksForPlaylist.Rows) } }; } /// - /// Returns all playlists a user is allowed to play. + /// Returns all playlists a user is allowed to play. /// - public async Task> GetPlaylists(subsonic.Request request, User roadieUser, string filterToUserName) + public async Task> GetPlaylists(subsonic.Request request, + User roadieUser, string filterToUserName) { var pagedRequest = request.PagedRequest; pagedRequest.Sort = "Playlist.Text"; pagedRequest.Order = "ASC"; - var playlistResult = await this.PlaylistService.List(pagedRequest, roadieUser); + var playlistResult = await PlaylistService.List(pagedRequest, roadieUser); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.playlists, Item = new subsonic.Playlists { - playlist = this.SubsonicPlaylistsForPlaylists(playlistResult.Rows) + playlist = SubsonicPlaylistsForPlaylists(playlistResult.Rows) } } }; } /// - /// Returns the state of the play queue for this user (as set by savePlayQueue). This includes the tracks in the play queue, the currently playing track, and the position within this track. Typically used to allow a user to move between different clients/apps while retaining the same play queue (for instance when listening to an audio book). + /// Returns the state of the play queue for this user (as set by savePlayQueue). This includes the tracks in the play + /// queue, the currently playing track, and the position within this track. Typically used to allow a user to move + /// between different clients/apps while retaining the same play queue (for instance when listening to an audio book). /// - public async Task> GetPlayQueue(subsonic.Request request, User roadieUser) + public async Task> GetPlayQueue(subsonic.Request request, + User roadieUser) { - var user = this.GetUser(roadieUser.UserId); + var user = GetUser(roadieUser.UserId); subsonic.PlayQueue playQue = null; @@ -1137,7 +1180,7 @@ namespace Roadie.Api.Services var current = user.UserQues.FirstOrDefault(x => x.IsCurrent ?? false) ?? user.UserQues.First(); var pagedRequest = request.PagedRequest; pagedRequest.FilterToTrackIds = user.UserQues.Select(x => x.Track?.RoadieId).ToArray(); - var queTracksResult = await this.TrackService.List(pagedRequest, roadieUser); + var queTracksResult = await TrackService.List(pagedRequest, roadieUser); var queTrackRows = (from tt in queTracksResult.Rows join qt in user.UserQues on tt.DatabaseId equals qt.TrackId orderby qt.QueSortOrder @@ -1151,16 +1194,17 @@ namespace Roadie.Api.Services position = current.Position ?? 0, positionSpecified = current.Position.HasValue, username = user.UserName, - entry = this.SubsonicChildrenForTracks(queTrackRows) + entry = SubsonicChildrenForTracks(queTrackRows) }; } + return new subsonic.SubsonicOperationResult { IsSuccess = true, IsEmptyResponse = playQue == null, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.playQueue, Item = playQue @@ -1169,7 +1213,10 @@ namespace Roadie.Api.Services } /// - /// Returns all Podcast channels the server subscribes to, and (optionally) their episodes. This method can also be used to return details for only one channel - refer to the id parameter. A typical use case for this method would be to first retrieve all channels without episodes, and then retrieve all episodes for the single channel the user selects. + /// Returns all Podcast channels the server subscribes to, and (optionally) their episodes. This method can also be + /// used to return details for only one channel - refer to the id parameter. A typical use case for this method would + /// be to first retrieve all channels without episodes, and then retrieve all episodes for the single channel the user + /// selects. /// public Task> GetPodcasts(subsonic.Request request) { @@ -1178,7 +1225,7 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.podcasts, Item = new subsonic.Podcasts @@ -1190,34 +1237,37 @@ namespace Roadie.Api.Services } /// - /// Returns random songs matching the given criteria. + /// Returns random songs matching the given criteria. /// - public async Task> GetRandomSongs(subsonic.Request request, User roadieUser) + public async Task> GetRandomSongs(subsonic.Request request, + User roadieUser) { var songs = new List(); - var randomSongs = await this.TrackService.List(request.PagedRequest, roadieUser, true); + var randomSongs = await TrackService.List(request.PagedRequest, roadieUser, true); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.randomSongs, Item = new subsonic.Songs { - song = this.SubsonicChildrenForTracks(randomSongs.Rows) + song = SubsonicChildrenForTracks(randomSongs.Rows) } } }; } /// - /// Returns a random collection of songs from the given artist and similar artists, using data from last.fm. Typically used for artist radio features. + /// Returns a random collection of songs from the given artist and similar artists, using data from last.fm. Typically + /// used for artist radio features. /// - public Task> GetSimliarSongs(subsonic.Request request, User roadieUser, subsonic.SimilarSongsVersion version, int? count = 50) + public Task> GetSimliarSongs(subsonic.Request request, + User roadieUser, subsonic.SimilarSongsVersion version, int? count = 50) { // TODO How to determine similiar songs? Perhaps by genre? @@ -1229,7 +1279,7 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.similarSongs, Item = new subsonic.SimilarSongs @@ -1245,7 +1295,7 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.similarSongs2, Item = new subsonic.SimilarSongs2 @@ -1256,79 +1306,82 @@ namespace Roadie.Api.Services }); default: - return Task.FromResult(new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, $"Unknown SimilarSongsVersion [{ version }]")); + return Task.FromResult(new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, + $"Unknown SimilarSongsVersion [{version}]")); } } /// - /// Returns details for a song. + /// Returns details for a song. /// - public async Task> GetSong(subsonic.Request request, User roadieUser) + public async Task> GetSong(subsonic.Request request, + User roadieUser) { if (!request.TrackId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track [{ request.id }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track [{request.id}]"); var pagedRequest = request.PagedRequest; pagedRequest.FilterToTrackId = request.TrackId.Value; pagedRequest.Sort = "Id"; - var trackResult = await this.TrackService.List(pagedRequest, roadieUser); + var trackResult = await TrackService.List(pagedRequest, roadieUser); var track = trackResult.Rows.Any() ? trackResult.Rows.First() : null; if (track == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track [{ request.id }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track [{request.id}]"); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.song, - Item = this.SubsonicChildForTrack(track) + Item = SubsonicChildForTrack(track) } }; } /// - /// Returns songs in a given genre. + /// Returns songs in a given genre. /// - public async Task> GetSongsByGenre(subsonic.Request request, User roadieUser) + public async Task> GetSongsByGenre(subsonic.Request request, + User roadieUser) { var pagedRequest = request.PagedRequest; pagedRequest.FilterByGenre = request.Genre; pagedRequest.Sort = "Id"; - var trackResult = await this.TrackService.List(pagedRequest, roadieUser); + var trackResult = await TrackService.List(pagedRequest, roadieUser); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.songsByGenre, Item = new subsonic.Songs { - song = this.SubsonicChildrenForTracks(trackResult.Rows) + song = SubsonicChildrenForTracks(trackResult.Rows) } } }; } /// - /// Returns starred songs, albums and artists. + /// Returns starred songs, albums and artists. /// - public async Task> GetStarred(subsonic.Request request, User roadieUser, subsonic.StarredVersion version) + public async Task> GetStarred(subsonic.Request request, + User roadieUser, subsonic.StarredVersion version) { var pagedRequest = request.PagedRequest; pagedRequest.FilterFavoriteOnly = true; pagedRequest.Sort = "Id"; - var artistList = await this.ArtistService.List(roadieUser, pagedRequest); - var releaseList = await this.ReleaseService.List(roadieUser, pagedRequest); - var songList = await this.TrackService.List(pagedRequest, roadieUser); + var artistList = await ArtistService.List(roadieUser, pagedRequest); + var releaseList = await ReleaseService.List(roadieUser, pagedRequest); + var songList = await TrackService.List(pagedRequest, roadieUser); switch (version) { @@ -1338,14 +1391,14 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.starred, Item = new subsonic.Starred { - album = this.SubsonicChildrenForReleases(releaseList.Rows, null), - artist = this.SubsonicArtistsForArtists(artistList.Rows), - song = this.SubsonicChildrenForTracks(songList.Rows) + album = SubsonicChildrenForReleases(releaseList.Rows, null), + artist = SubsonicArtistsForArtists(artistList.Rows), + song = SubsonicChildrenForTracks(songList.Rows) } } }; @@ -1356,88 +1409,86 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.starred2, Item = new subsonic.Starred2 { - album = this.SubsonicAlbumID3ForReleases(releaseList.Rows), - artist = this.SubsonicArtistID3sForArtists(artistList.Rows), - song = this.SubsonicChildrenForTracks(songList.Rows) + album = SubsonicAlbumID3ForReleases(releaseList.Rows), + artist = SubsonicArtistID3sForArtists(artistList.Rows), + song = SubsonicChildrenForTracks(songList.Rows) } } }; default: - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, $"Unknown StarredVersion [{ version }]"); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, + $"Unknown StarredVersion [{version}]"); } } /// - /// Returns top songs for the given artist, using data from last.fm. + /// Returns top songs for the given artist, using data from last.fm. /// - public async Task> GetTopSongs(subsonic.Request request, User roadieUser, int? count = 50) + public async Task> GetTopSongs(subsonic.Request request, + User roadieUser, int? count = 50) { data.Artist artist = null; if (!string.IsNullOrEmpty(request.ArtistName)) - { artist = base.GetArtist(request.ArtistName); - } - else if (request.ArtistId.HasValue) - { - artist = this.GetArtist(request.ArtistId.Value); - } + else if (request.ArtistId.HasValue) artist = GetArtist(request.ArtistId.Value); if (artist == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Unknown Artist [{ request.ArtistName }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Unknown Artist [{request.ArtistName}]"); var pagedRequest = request.PagedRequest; pagedRequest.FilterToArtistId = artist.RoadieId; pagedRequest.FilterTopPlayedOnly = true; pagedRequest.Sort = "PlayedCount"; pagedRequest.Order = "DESC"; - var trackResult = await this.TrackService.List(pagedRequest, roadieUser); + var trackResult = await TrackService.List(pagedRequest, roadieUser); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.topSongs, Item = new subsonic.TopSongs { - song = this.SubsonicChildrenForTracks(trackResult.Rows) + song = SubsonicChildrenForTracks(trackResult.Rows) } } }; } /// - /// Get details about a given user, including which authorization roles and folder access it has. Can be used to enable/disable certain features in the client, such as jukebox control. + /// Get details about a given user, including which authorization roles and folder access it has. Can be used to + /// enable/disable certain features in the client, such as jukebox control. /// - public async Task> GetUser(subsonic.Request request, string username) + public async Task> GetUser(subsonic.Request request, + string username) { - var user = this.GetUser(username); + var user = GetUser(username); if (user == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Username [{ username }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Username [{username}]"); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.user, - Item = await this.SubsonicUserForUser(user) + Item = await SubsonicUserForUser(user) } }; } /// - /// Returns all video files. + /// Returns all video files. /// public subsonic.SubsonicOperationResult GetVideos(subsonic.Request request) { @@ -1446,7 +1497,7 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.videos, Item = new subsonic.Videos @@ -1458,7 +1509,7 @@ namespace Roadie.Api.Services } /// - /// Used to test connectivity with the server. Takes no extra parameters. + /// Used to test connectivity with the server. Takes no extra parameters. /// public subsonic.SubsonicOperationResult Ping(subsonic.Request request) { @@ -1467,36 +1518,37 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok } }; } /// - /// Saves the state of the play queue for this user. This includes the tracks in the play queue, the currently playing track, and the position within this track. Typically used to allow a user to move between different clients/apps while retaining the same play queue (for instance when listening to an audio book). + /// Saves the state of the play queue for this user. This includes the tracks in the play queue, the currently playing + /// track, and the position within this track. Typically used to allow a user to move between different clients/apps + /// while retaining the same play queue (for instance when listening to an audio book). /// - public async Task> SavePlayQueue(subsonic.Request request, User roadieUser, string current, long? position) + public async Task> SavePlayQueue(subsonic.Request request, + User roadieUser, string current, long? position) { // Remove any existing Que for User - var user = this.GetUser(roadieUser.UserId); - if (user.UserQues != null && user.UserQues.Any()) - { - this.DbContext.UserQues.RemoveRange(user.UserQues); - } + var user = GetUser(roadieUser.UserId); + if (user.UserQues != null && user.UserQues.Any()) DbContext.UserQues.RemoveRange(user.UserQues); // Create a new UserQue for each posted TrackId in ids if (request.ids != null && request.ids.Any()) { short queSortOrder = 0; var pagedRequest = request.PagedRequest; - pagedRequest.FilterToTrackIds = request.ids.Select(x => SafeParser.ToGuid(x)).Where(x => x.HasValue).ToArray(); - var trackListResult = await this.TrackService.List(pagedRequest, roadieUser); + pagedRequest.FilterToTrackIds = + request.ids.Select(x => SafeParser.ToGuid(x)).Where(x => x.HasValue).ToArray(); + var trackListResult = await TrackService.List(pagedRequest, roadieUser); var currentTrackId = SafeParser.ToGuid(current); foreach (var row in trackListResult.Rows) { queSortOrder++; - this.DbContext.UserQues.Add(new data.UserQue + DbContext.UserQues.Add(new data.UserQue { IsCurrent = row.Track.Value == currentTrackId?.ToString(), Position = row.Track.Value == currentTrackId?.ToString() ? position : null, @@ -1507,27 +1559,28 @@ namespace Roadie.Api.Services } } - await this.DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(user.CacheRegion); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok } }; } /// - /// Returns albums, artists and songs matching the given search criteria. Supports paging through the result. + /// Returns albums, artists and songs matching the given search criteria. Supports paging through the result. /// - public async Task> Search(subsonic.Request request, User roadieUser, subsonic.SearchVersion version) + public async Task> Search(subsonic.Request request, + User roadieUser, subsonic.SearchVersion version) { - var query = this.HttpEncoder.UrlDecode(request.Query).Replace("*", "").Replace("%", "").Replace(";", ""); + var query = HttpEncoder.UrlDecode(request.Query).Replace("*", "").Replace("%", "").Replace(";", ""); // Search artists with query returning ArtistCount skipping ArtistOffset var artistPagedRequest = request.PagedRequest; @@ -1535,7 +1588,7 @@ namespace Roadie.Api.Services artistPagedRequest.Limit = request.ArtistCount ?? artistPagedRequest.Limit; artistPagedRequest.SkipValue = request.ArtistOffset ?? artistPagedRequest.SkipValue; artistPagedRequest.Filter = query; - var artistResult = await this.ArtistService.List(roadieUser, artistPagedRequest); + var artistResult = await ArtistService.List(roadieUser, artistPagedRequest); // Search release with query returning RelaseCount skipping ReleaseOffset var releasePagedRequest = request.PagedRequest; @@ -1543,7 +1596,7 @@ namespace Roadie.Api.Services releasePagedRequest.Limit = request.AlbumCount ?? releasePagedRequest.Limit; releasePagedRequest.SkipValue = request.AlbumOffset ?? releasePagedRequest.SkipValue; releasePagedRequest.Filter = query; - var releaseResult = await this.ReleaseService.List(roadieUser, releasePagedRequest); + var releaseResult = await ReleaseService.List(roadieUser, releasePagedRequest); // Search tracks with query returning SongCount skipping SongOffset var trackPagedRequest = request.PagedRequest; @@ -1551,13 +1604,15 @@ namespace Roadie.Api.Services trackPagedRequest.Limit = request.SongCount ?? trackPagedRequest.Limit; trackPagedRequest.SkipValue = request.SongOffset ?? trackPagedRequest.SkipValue; trackPagedRequest.Filter = query; - var songResult = await this.TrackService.List(trackPagedRequest, roadieUser); - var songs = this.SubsonicChildrenForTracks(songResult.Rows); + var songResult = await TrackService.List(trackPagedRequest, roadieUser); + var songs = SubsonicChildrenForTracks(songResult.Rows); switch (version) { case subsonic.SearchVersion.One: - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleClientRestProtocolVersion, "Deprecated since 1.4.0, use search2 instead."); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleClientRestProtocolVersion, + "Deprecated since 1.4.0, use search2 instead."); case subsonic.SearchVersion.Two: return new subsonic.SubsonicOperationResult @@ -1565,13 +1620,13 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.searchResult2, Item = new subsonic.SearchResult2 { - artist = this.SubsonicArtistsForArtists(artistResult.Rows), - album = this.SubsonicChildrenForReleases(releaseResult.Rows, null), + artist = SubsonicArtistsForArtists(artistResult.Rows), + album = SubsonicChildrenForReleases(releaseResult.Rows, null), song = songs.ToArray() } } @@ -1583,121 +1638,114 @@ namespace Roadie.Api.Services IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.searchResult3, Item = new subsonic.SearchResult3 { - artist = this.SubsonicArtistID3sForArtists(artistResult.Rows), - album = this.SubsonicAlbumID3ForReleases(releaseResult.Rows), + artist = SubsonicArtistID3sForArtists(artistResult.Rows), + album = SubsonicAlbumID3ForReleases(releaseResult.Rows), song = songs.ToArray() } } }; default: - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, $"Unknown SearchVersion [{ version }]"); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.IncompatibleServerRestProtocolVersion, + $"Unknown SearchVersion [{version}]"); } } /// - /// Sets the rating for a music file. If rating is zero then remove rating. + /// Sets the rating for a music file. If rating is zero then remove rating. /// - public async Task> SetRating(subsonic.Request request, User roadieUser, short rating) + public async Task> SetRating(subsonic.Request request, + User roadieUser, short rating) { - var user = this.GetUser(roadieUser.UserId); + var user = GetUser(roadieUser.UserId); if (user == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, $"Invalid User [{ roadieUser }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, $"Invalid User [{roadieUser}]"); // Id can be a song, album or artist if (request.TrackId.HasValue) { - var starResult = await this.SetTrackRating(request.TrackId.Value, user, rating); + var starResult = await SetTrackRating(request.TrackId.Value, user, rating); if (starResult.IsSuccess) - { return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response() }; - } } else if (request.ReleaseId.HasValue) { - var starResult = await this.SetReleaseRating(request.ReleaseId.Value, user, rating); + var starResult = await SetReleaseRating(request.ReleaseId.Value, user, rating); if (starResult.IsSuccess) - { return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response() }; - } } else if (request.ArtistId.HasValue) { - var starResult = await this.SetArtistRating(request.ArtistId.Value, user, rating); + var starResult = await SetArtistRating(request.ArtistId.Value, user, rating); if (starResult.IsSuccess) - { return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response() }; - } } - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Unknown Star Id [{ JsonConvert.SerializeObject(request) }]"); + + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Unknown Star Id [{JsonConvert.SerializeObject(request)}]"); } /// - /// Attaches a star to a song, album or artist. + /// Attaches a star to a song, album or artist. /// - public async Task> ToggleStar(subsonic.Request request, User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null) + public async Task> ToggleStar(subsonic.Request request, + User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null) { - var user = this.GetUser(roadieUser.UserId); + var user = GetUser(roadieUser.UserId); if (user == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, $"Invalid User [{ roadieUser }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, $"Invalid User [{roadieUser}]"); // Id can be a song, album or artist if (request.TrackId.HasValue) { - var starResult = await this.ToggleTrackStar(request.TrackId.Value, user, star); + var starResult = await ToggleTrackStar(request.TrackId.Value, user, star); if (starResult.IsSuccess) - { return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response() }; - } } else if (request.ReleaseId.HasValue) { - var starResult = await this.ToggleReleaseStar(request.ReleaseId.Value, user, star); + var starResult = await ToggleReleaseStar(request.ReleaseId.Value, user, star); if (starResult.IsSuccess) - { return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response() }; - } } else if (request.ArtistId.HasValue) { - var starResult = await this.ToggleArtistStar(request.ArtistId.Value, user, star); + var starResult = await ToggleArtistStar(request.ArtistId.Value, user, star); if (starResult.IsSuccess) - { return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response() }; - } } else if (albumIds != null && albumIds.Any()) { @@ -1706,11 +1754,10 @@ namespace Roadie.Api.Services var releaseId = SafeParser.ToGuid(rId); if (releaseId.HasValue) { - var starResult = await this.ToggleReleaseStar(releaseId.Value, user, star); + var starResult = await ToggleReleaseStar(releaseId.Value, user, star); if (!starResult.IsSuccess) - { - return new subsonic.SubsonicOperationResult(starResult.ErrorCode.Value, starResult.Messages.FirstOrDefault()); - } + return new subsonic.SubsonicOperationResult(starResult.ErrorCode.Value, + starResult.Messages.FirstOrDefault()); } } } @@ -1721,19 +1768,21 @@ namespace Roadie.Api.Services var artistId = SafeParser.ToGuid(aId); if (artistId.HasValue) { - var starResult = await this.ToggleReleaseStar(artistId.Value, user, star); + var starResult = await ToggleReleaseStar(artistId.Value, user, star); if (!starResult.IsNotFoundResult) - { - return new subsonic.SubsonicOperationResult(starResult.ErrorCode.Value, starResult.Messages.FirstOrDefault()); - } + return new subsonic.SubsonicOperationResult(starResult.ErrorCode.Value, + starResult.Messages.FirstOrDefault()); } } } - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Unknown Star Id [{ JsonConvert.SerializeObject(request) }]"); + + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Unknown Star Id [{JsonConvert.SerializeObject(request)}]"); } /// - /// Updates a playlist. Only the owner of a playlist is allowed to update it. + /// Updates a playlist. Only the owner of a playlist is allowed to update it. /// /// Populated Subsonic Request /// Populated Roadie User @@ -1742,22 +1791,22 @@ namespace Roadie.Api.Services /// true if the playlist should be visible to all users, false otherwise. /// Add this song with this ID to the playlist. Multiple parameters allowed /// Remove the song at this position in the playlist. Multiple parameters allowed. - public async Task> UpdatePlaylist(subsonic.Request request, User roadieUser, string playListId, string name = null, string comment = null, bool? isPublic = null, string[] songIdsToAdd = null, int[] songIndexesToRemove = null) + public async Task> UpdatePlaylist(subsonic.Request request, + User roadieUser, string playListId, string name = null, string comment = null, bool? isPublic = null, + string[] songIdsToAdd = null, int[] songIndexesToRemove = null) { request.id = playListId ?? request.id; if (!request.PlaylistId.HasValue) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.id }]"); - } - var playlist = this.GetPlaylist(request.PlaylistId.Value); + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{request.id}]"); + var playlist = GetPlaylist(request.PlaylistId.Value); if (playlist == null) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.TrackId.Value }]"); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{request.TrackId.Value}]"); if (playlist.UserId != roadieUser.Id && !roadieUser.IsAdmin) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, "User is not allowed to update playlist."); - } + return new subsonic.SubsonicOperationResult( + subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, + "User is not allowed to update playlist."); playlist.Name = name ?? playlist.Name; playlist.IsPublic = isPublic ?? playlist.IsPublic; @@ -1767,43 +1816,41 @@ namespace Roadie.Api.Services { // Add new if not already on Playlist var songIdsToAddRoadieIds = songIdsToAdd.Select(x => SafeParser.ToGuid(x)).ToArray(); - var submittedTracks = (from t in this.DbContext.Tracks - where songIdsToAddRoadieIds.Contains(t.RoadieId) - select t); + var submittedTracks = from t in DbContext.Tracks + where songIdsToAddRoadieIds.Contains(t.RoadieId) + select t; var listNumber = playlist.Tracks?.Max(x => x.ListNumber) ?? 0; foreach (var submittedTrack in submittedTracks) - { - if (playlist.Tracks == null || playlist.Tracks == null || !playlist.Tracks.Any(x => x.TrackId == submittedTrack.Id)) + if (playlist.Tracks == null || playlist.Tracks == null || + !playlist.Tracks.Any(x => x.TrackId == submittedTrack.Id)) { listNumber++; - this.DbContext.PlaylistTracks.Add(new data.PlaylistTrack + DbContext.PlaylistTracks.Add(new data.PlaylistTrack { PlayListId = playlist.Id, ListNumber = listNumber, TrackId = submittedTrack.Id }); } - } } + if (songIndexesToRemove != null && songIndexesToRemove.Any()) - { // Remove tracks from playlist // Not clear from API documentation if this is zero based, wait until someone calls it to get values passed. - throw new NotImplementedException($"Request [{ JsonConvert.SerializeObject(request) }]"); - } + throw new NotImplementedException($"Request [{JsonConvert.SerializeObject(request)}]"); - await this.DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync(); - var user = this.GetUser(roadieUser.UserId); - this.CacheManager.ClearRegion(user.CacheRegion); + var user = GetUser(roadieUser.UserId); + CacheManager.ClearRegion(user.CacheRegion); return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok } }; @@ -1813,23 +1860,23 @@ namespace Roadie.Api.Services private string[] AllowedUsers() { - return this.CacheManager.Get(CacheManagerBase.SystemCacheRegionUrn + ":active_usernames", () => - { - return this.DbContext.Users.Where(x => x.IsActive ?? false).Select(x => x.UserName).ToArray(); - }, CacheManagerBase.SystemCacheRegionUrn); + return CacheManager.Get(CacheManagerBase.SystemCacheRegionUrn + ":active_usernames", + () => { return DbContext.Users.Where(x => x.IsActive ?? false).Select(x => x.UserName).ToArray(); }, + CacheManagerBase.SystemCacheRegionUrn); } private subsonic.MusicFolder CollectionMusicFolder() { - return this.MusicFolders().First(x => x.id == 1); + return MusicFolders().First(x => x.id == 1); } - private async Task> GetArtistsAction(subsonic.Request request, User roadieUser) + private async Task> GetArtistsAction( + subsonic.Request request, User roadieUser) { var indexes = new List(); - var musicFolder = this.MusicFolders().FirstOrDefault(x => x.id == (request.MusicFolderId ?? 2)); + var musicFolder = MusicFolders().FirstOrDefault(x => x.id == (request.MusicFolderId ?? 2)); var pagedRequest = request.PagedRequest; - if (musicFolder == this.CollectionMusicFolder()) + if (musicFolder == CollectionMusicFolder()) { // Indexes for "Collection" Artists alphabetically } @@ -1839,25 +1886,23 @@ namespace Roadie.Api.Services pagedRequest.SkipValue = 0; pagedRequest.Limit = short.MaxValue; pagedRequest.Sort = "Artist.Text"; - var artistList = await this.ArtistService.List(roadieUser: roadieUser, - request: pagedRequest, - doRandomize: false, - onlyIncludeWithReleases: true); + var artistList = await ArtistService.List(roadieUser, + pagedRequest); foreach (var artistGroup in artistList.Rows.GroupBy(x => x.Artist.Text.Substring(0, 1))) - { indexes.Add(new subsonic.IndexID3 { name = artistGroup.Key, - artist = this.SubsonicArtistID3sForArtists(artistGroup) + artist = SubsonicArtistID3sForArtists(artistGroup) }); - }; + ; } + return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.artists, Item = new subsonic.ArtistsID3 @@ -1868,37 +1913,39 @@ namespace Roadie.Api.Services }; } - private async Task> GetIndexesAction(subsonic.Request request, User roadieUser, long? ifModifiedSince = null) + private async Task> GetIndexesAction( + subsonic.Request request, User roadieUser, long? ifModifiedSince = null) { - var modifiedSinceFilter = ifModifiedSince.HasValue ? (DateTime?)ifModifiedSince.Value.FromUnixTime() : null; - subsonic.MusicFolder musicFolderFilter = !request.MusicFolderId.HasValue ? new subsonic.MusicFolder() : this.MusicFolders().FirstOrDefault(x => x.id == request.MusicFolderId.Value); + var modifiedSinceFilter = + ifModifiedSince.HasValue ? (DateTime?)ifModifiedSince.Value.FromUnixTime() : null; + var musicFolderFilter = !request.MusicFolderId.HasValue + ? new subsonic.MusicFolder() + : MusicFolders().FirstOrDefault(x => x.id == request.MusicFolderId.Value); var indexes = new List(); - if (musicFolderFilter.id == this.CollectionMusicFolder().id) + if (musicFolderFilter.id == CollectionMusicFolder().id) { // Collections for Music Folders by Alpha First - foreach (var collectionFirstLetter in (from c in this.DbContext.Collections + foreach (var collectionFirstLetter in (from c in DbContext.Collections let first = c.Name.Substring(0, 1) orderby first select first).Distinct().ToArray()) - { indexes.Add(new subsonic.Index { name = collectionFirstLetter, - artist = (from c in this.DbContext.Collections + artist = (from c in DbContext.Collections where c.Name.Substring(0, 1) == collectionFirstLetter where modifiedSinceFilter == null || c.LastUpdated >= modifiedSinceFilter orderby c.SortName, c.Name select new subsonic.Artist { - id = subsonic.Request.CollectionIdentifier + c.RoadieId.ToString(), + id = subsonic.Request.CollectionIdentifier + c.RoadieId, name = c.Name, - artistImageUrl = this.MakeCollectionThumbnailImage(c.RoadieId).Url, + artistImageUrl = MakeCollectionThumbnailImage(c.RoadieId).Url, averageRating = 0, userRating = 0 }).ToArray() }); - } } else { @@ -1907,25 +1954,23 @@ namespace Roadie.Api.Services pagedRequest.SkipValue = 0; pagedRequest.Limit = short.MaxValue; pagedRequest.Sort = "Artist.Text"; - var artistList = await this.ArtistService.List(roadieUser: roadieUser, - request: pagedRequest, - doRandomize: false, - onlyIncludeWithReleases: true); + var artistList = await ArtistService.List(roadieUser, + pagedRequest); foreach (var artistGroup in artistList.Rows.GroupBy(x => x.Artist.Text.Substring(0, 1))) - { indexes.Add(new subsonic.Index { name = artistGroup.Key, - artist = this.SubsonicArtistsForArtists(artistGroup) + artist = SubsonicArtistsForArtists(artistGroup) }); - }; + ; } + return new subsonic.SubsonicOperationResult { IsSuccess = true, Data = new subsonic.Response { - version = SubsonicService.SubsonicVersion, + version = SubsonicVersion, status = subsonic.ResponseStatus.ok, ItemElementName = subsonic.ItemChoiceType.indexes, Item = new subsonic.Indexes @@ -1941,23 +1986,23 @@ namespace Roadie.Api.Services { return new List { - new subsonic.MusicFolder { id = 1, name = "Collections"}, - new subsonic.MusicFolder { id = 2, name = "Music"} + new subsonic.MusicFolder {id = 1, name = "Collections"}, + new subsonic.MusicFolder {id = 2, name = "Music"} }; } private subsonic.MusicFolder MusicMusicFolder() { - return this.MusicFolders().First(x => x.id == 2); + return MusicFolders().First(x => x.id == 2); } - private new async Task> SetArtistRating(Guid artistId, ApplicationUser user, short rating) + private new async Task> SetArtistRating(Guid artistId, + ApplicationUser user, short rating) { var r = await base.SetArtistRating(artistId, user, rating); if (r.IsNotFoundResult) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Artist Id [{ artistId }]"); - } + return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Invalid Artist Id [{artistId}]"); return new subsonic.SubsonicOperationResult { IsSuccess = r.IsSuccess, @@ -1965,13 +2010,13 @@ namespace Roadie.Api.Services }; } - private new async Task> SetReleaseRating(Guid releaseId, ApplicationUser user, short rating) + private new async Task> SetReleaseRating(Guid releaseId, + ApplicationUser user, short rating) { var r = await base.SetReleaseRating(releaseId, user, rating); if (r.IsNotFoundResult) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release Id [{ releaseId }]"); - } + return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Invalid Release Id [{releaseId}]"); return new subsonic.SubsonicOperationResult { IsSuccess = r.IsSuccess, @@ -1979,13 +2024,13 @@ namespace Roadie.Api.Services }; } - private new async Task> SetTrackRating(Guid trackId, ApplicationUser user, short rating) + private new async Task> SetTrackRating(Guid trackId, + ApplicationUser user, short rating) { var r = await base.SetTrackRating(trackId, user, rating); if (r.IsNotFoundResult) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ trackId }]"); - } + return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Invalid Track Id [{trackId}]"); return new subsonic.SubsonicOperationResult { IsSuccess = r.IsSuccess, @@ -1997,13 +2042,13 @@ namespace Roadie.Api.Services { return new subsonic.AlbumID3 { - id = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + id = subsonic.Request.ReleaseIdIdentifier + r.Id, artistId = r.Artist.Value, name = r.Release.Text, songCount = r.TrackCount ?? 0, duration = r.Duration.ToSecondsFromMilliseconds(), artist = r.Artist.Text, - coverArt = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + coverArt = subsonic.Request.ReleaseIdIdentifier + r.Id, created = r.CreatedDate.Value, genre = r.Genre.Text, playCount = r.TrackPlayedCount ?? 0, @@ -2017,20 +2062,17 @@ namespace Roadie.Api.Services private subsonic.AlbumID3[] SubsonicAlbumID3ForReleases(IEnumerable r) { - if (r == null || !r.Any()) - { - return new subsonic.AlbumID3[0]; - } - return r.Select(x => this.SubsonicAlbumID3ForRelease(x)).ToArray(); + if (r == null || !r.Any()) return new subsonic.AlbumID3[0]; + return r.Select(x => SubsonicAlbumID3ForRelease(x)).ToArray(); } private subsonic.Artist SubsonicArtistForArtist(ArtistList artist) { return new subsonic.Artist { - id = subsonic.Request.ArtistIdIdentifier + artist.Artist.Value.ToString(), + id = subsonic.Request.ArtistIdIdentifier + artist.Artist.Value, name = artist.Artist.Text, - artistImageUrl = this.MakeArtistThumbnailImage(artist.Id).Url, + artistImageUrl = MakeArtistThumbnailImage(artist.Id).Url, averageRating = artist.Rating ?? 0, averageRatingSpecified = true, starred = artist.UserRating?.RatedDate ?? DateTime.UtcNow, @@ -2042,10 +2084,10 @@ namespace Roadie.Api.Services private subsonic.ArtistID3 SubsonicArtistID3ForArtist(ArtistList artist) { - var artistImageUrl = this.MakeArtistThumbnailImage(artist.Id).Url; + var artistImageUrl = MakeArtistThumbnailImage(artist.Id).Url; return new subsonic.ArtistID3 { - id = subsonic.Request.ArtistIdIdentifier + artist.Artist.Value.ToString(), + id = subsonic.Request.ArtistIdIdentifier + artist.Artist.Value, name = artist.Artist.Text, albumCount = artist.ReleaseCount ?? 0, coverArt = artistImageUrl, @@ -2057,11 +2099,8 @@ namespace Roadie.Api.Services private subsonic.ArtistID3[] SubsonicArtistID3sForArtists(IEnumerable artists) { - if (artists == null || !artists.Any()) - { - return new subsonic.ArtistID3[0]; - } - return artists.Select(x => this.SubsonicArtistID3ForArtist(x)).ToArray(); + if (artists == null || !artists.Any()) return new subsonic.ArtistID3[0]; + return artists.Select(x => SubsonicArtistID3ForArtist(x)).ToArray(); } private subsonic.ArtistInfo2 SubsonicArtistInfo2InfoForArtist(data.Artist artist) @@ -2069,11 +2108,11 @@ namespace Roadie.Api.Services return new subsonic.ArtistInfo2 { biography = artist.BioContext, - largeImageUrl = this.MakeImage(artist.RoadieId, "artist", this.Configuration.LargeImageSize).Url, - mediumImageUrl = this.MakeImage(artist.RoadieId, "artist", this.Configuration.MediumImageSize).Url, + largeImageUrl = MakeImage(artist.RoadieId, "artist", Configuration.LargeImageSize).Url, + mediumImageUrl = MakeImage(artist.RoadieId, "artist", Configuration.MediumImageSize).Url, musicBrainzId = artist.MusicBrainzId, similarArtist = new subsonic.ArtistID3[0], - smallImageUrl = this.MakeImage(artist.RoadieId, "artist", this.Configuration.SmallImageSize).Url + smallImageUrl = MakeImage(artist.RoadieId, "artist", Configuration.SmallImageSize).Url }; } @@ -2082,29 +2121,27 @@ namespace Roadie.Api.Services return new subsonic.ArtistInfo { biography = artist.BioContext, - largeImageUrl = this.MakeImage(artist.RoadieId, "artist", this.Configuration.LargeImageSize).Url, - mediumImageUrl = this.MakeImage(artist.RoadieId, "artist", this.Configuration.MediumImageSize).Url, + largeImageUrl = MakeImage(artist.RoadieId, "artist", Configuration.LargeImageSize).Url, + mediumImageUrl = MakeImage(artist.RoadieId, "artist", Configuration.MediumImageSize).Url, musicBrainzId = artist.MusicBrainzId, similarArtist = new subsonic.Artist[0], - smallImageUrl = this.MakeImage(artist.RoadieId, "artist", this.Configuration.SmallImageSize).Url + smallImageUrl = MakeImage(artist.RoadieId, "artist", Configuration.SmallImageSize).Url }; } private subsonic.Artist[] SubsonicArtistsForArtists(IEnumerable artists) { - if (artists == null || !artists.Any()) - { - return new subsonic.Artist[0]; - } - return artists.Select(x => this.SubsonicArtistForArtist(x)).ToArray(); + if (artists == null || !artists.Any()) return new subsonic.Artist[0]; + return artists.Select(x => SubsonicArtistForArtist(x)).ToArray(); } - private subsonic.ArtistWithAlbumsID3 SubsonicArtistWithAlbumsID3ForArtist(ArtistList artist, subsonic.AlbumID3[] releases) + private subsonic.ArtistWithAlbumsID3 SubsonicArtistWithAlbumsID3ForArtist(ArtistList artist, + subsonic.AlbumID3[] releases) { - var artistImageUrl = this.MakeArtistThumbnailImage(artist.Id).Url; + var artistImageUrl = MakeArtistThumbnailImage(artist.Id).Url; return new subsonic.ArtistWithAlbumsID3 { - id = subsonic.Request.ArtistIdIdentifier + artist.Artist.Value.ToString(), + id = subsonic.Request.ArtistIdIdentifier + artist.Artist.Value, album = releases, albumCount = releases.Count(), artistImageUrl = artistImageUrl, @@ -2128,27 +2165,28 @@ namespace Roadie.Api.Services }; } - private subsonic.Bookmark[] SubsonicBookmarksForBookmarks(IEnumerable bb, IEnumerable childTracks) + private subsonic.Bookmark[] SubsonicBookmarksForBookmarks(IEnumerable bb, + IEnumerable childTracks) { - if (bb == null || !bb.Any()) - { - return new subsonic.Bookmark[0]; - } + if (bb == null || !bb.Any()) return new subsonic.Bookmark[0]; var result = new List(); foreach (var bookmark in bb) { subsonic.Child child = null; switch (bookmark.Type.Value) { - case Library.Enums.BookmarkType.Track: - child = this.SubsonicChildForTrack(childTracks.FirstOrDefault(x => x.Id == SafeParser.ToGuid(bookmark.Bookmark.Value))); + case BookmarkType.Track: + child = SubsonicChildForTrack(childTracks.FirstOrDefault(x => + x.Id == SafeParser.ToGuid(bookmark.Bookmark.Value))); break; default: throw new NotImplementedException("Wrong Bookmark type to convert to Subsonic media Bookmark"); } - result.Add(this.SubsonicBookmarkForBookmark(bookmark, child)); + + result.Add(SubsonicBookmarkForBookmark(bookmark, child)); } + return result.ToArray(); } @@ -2156,13 +2194,13 @@ namespace Roadie.Api.Services { return new subsonic.Child { - id = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + id = subsonic.Request.ReleaseIdIdentifier + r.Id, album = r.Release.Text, - albumId = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + albumId = subsonic.Request.ReleaseIdIdentifier + r.Id, artist = r.Artist.Text, averageRating = r.Rating ?? 0, averageRatingSpecified = true, - coverArt = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + coverArt = subsonic.Request.ReleaseIdIdentifier + r.Id, created = r.CreatedDate.Value, createdSpecified = true, genre = r.Genre.Text, @@ -2185,7 +2223,7 @@ namespace Roadie.Api.Services { return new subsonic.Child { - id = subsonic.Request.TrackIdIdentifier + t.Id.ToString(), + id = subsonic.Request.TrackIdIdentifier + t.Id, album = t.Release.Release.Text, albumId = subsonic.Request.ReleaseIdIdentifier + t.Release.Release.Value, artist = t.Artist.Artist.Text, @@ -2195,7 +2233,7 @@ namespace Roadie.Api.Services bitRate = 320, bitRateSpecified = true, contentType = "audio/mpeg", - coverArt = subsonic.Request.TrackIdIdentifier + t.Id.ToString(), + coverArt = subsonic.Request.TrackIdIdentifier + t.Id, created = t.CreatedDate.Value, createdSpecified = true, discNumber = t.MediaNumber ?? 0, @@ -2204,7 +2242,7 @@ namespace Roadie.Api.Services durationSpecified = true, isDir = false, parent = subsonic.Request.ReleaseIdIdentifier + t.Release.Release.Value, - path = $"{ t.Artist.Artist.Text }/{ t.Release.Release.Text }/{ t.TrackNumber } - { t.Title }.mp3", + path = $"{t.Artist.Artist.Text}/{t.Release.Release.Text}/{t.TrackNumber} - {t.Title}.mp3", playCountSpecified = true, size = t.FileSize ?? 0, sizeSpecified = true, @@ -2230,54 +2268,46 @@ namespace Roadie.Api.Services private subsonic.Child[] SubsonicChildrenForReleases(IEnumerable r, string parent) { - if (r == null || !r.Any()) - { - return new subsonic.Child[0]; - } - return r.Select(x => this.SubsonicChildForRelease(x, parent, $"{ x.Artist.Text}/{ x.Release.Text}/")).ToArray(); + if (r == null || !r.Any()) return new subsonic.Child[0]; + return r.Select(x => SubsonicChildForRelease(x, parent, $"{x.Artist.Text}/{x.Release.Text}/")).ToArray(); } private subsonic.Child[] SubsonicChildrenForTracks(IEnumerable tracks) { - if (tracks == null || !tracks.Any()) - { - return new subsonic.Child[0]; - } - return tracks.Select(x => this.SubsonicChildForTrack(x)).ToArray(); + if (tracks == null || !tracks.Any()) return new subsonic.Child[0]; + return tracks.Select(x => SubsonicChildForTrack(x)).ToArray(); } - private subsonic.Playlist SubsonicPlaylistForPlaylist(Library.Models.Playlists.PlaylistList playlist, IEnumerable playlistTracks = null) + private subsonic.Playlist SubsonicPlaylistForPlaylist(PlaylistList playlist, + IEnumerable playlistTracks = null) { return new subsonic.PlaylistWithSongs { - coverArt = this.MakePlaylistThumbnailImage(playlist.Id).Url, - allowedUser = playlist.IsPublic ? this.AllowedUsers() : null, + coverArt = MakePlaylistThumbnailImage(playlist.Id).Url, + allowedUser = playlist.IsPublic ? AllowedUsers() : null, changed = playlist.LastUpdated ?? playlist.CreatedDate ?? DateTime.UtcNow, created = playlist.CreatedDate ?? DateTime.UtcNow, duration = playlist.Duration.ToSecondsFromMilliseconds(), - id = subsonic.Request.PlaylistdIdentifier + playlist.Id.ToString(), + id = subsonic.Request.PlaylistdIdentifier + playlist.Id, name = playlist.Playlist.Text, owner = playlist.User.Text, @public = playlist.IsPublic, publicSpecified = true, songCount = playlist.PlaylistCount ?? 0, - entry = this.SubsonicChildrenForTracks(playlistTracks) + entry = SubsonicChildrenForTracks(playlistTracks) }; } - private subsonic.Playlist[] SubsonicPlaylistsForPlaylists(IEnumerable playlists) + private subsonic.Playlist[] SubsonicPlaylistsForPlaylists(IEnumerable playlists) { - if (playlists == null || !playlists.Any()) - { - return new subsonic.Playlist[0]; - } - return playlists.Select(x => this.SubsonicPlaylistForPlaylist(x)).ToArray(); + if (playlists == null || !playlists.Any()) return new subsonic.Playlist[0]; + return playlists.Select(x => SubsonicPlaylistForPlaylist(x)).ToArray(); } - private async Task SubsonicUserForUser(Library.Identity.ApplicationUser user) + private async Task SubsonicUserForUser(ApplicationUser user) { - var isAdmin = await this.UserManger.IsInRoleAsync(user, "Admin"); - var isEditor = await this.UserManger.IsInRoleAsync(user, "Editor"); + var isAdmin = await UserManger.IsInRoleAsync(user, "Admin"); + var isEditor = await UserManger.IsInRoleAsync(user, "Editor"); return new subsonic.User { adminRole = false, // disabling this as we dont want Roadie user management done via Subsonic API @@ -2299,17 +2329,17 @@ namespace Roadie.Api.Services uploadRole = true, username = user.UserName, videoConversionRole = false, // Disable video nonsense - folder = this.MusicFolders().Select(x => x.id).ToArray() + folder = MusicFolders().Select(x => x.id).ToArray() }; } - private async Task> ToggleArtistStar(Guid artistId, ApplicationUser user, bool starred) + private async Task> ToggleArtistStar(Guid artistId, ApplicationUser user, + bool starred) { - var r = await base.ToggleArtistFavorite(artistId, user, starred); + var r = await ToggleArtistFavorite(artistId, user, starred); if (r.IsNotFoundResult) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Artist Id [{ artistId }]"); - } + return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Invalid Artist Id [{artistId}]"); return new subsonic.SubsonicOperationResult { IsSuccess = r.IsSuccess, @@ -2317,13 +2347,13 @@ namespace Roadie.Api.Services }; } - private async Task> ToggleReleaseStar(Guid releaseId, ApplicationUser user, bool starred) + private async Task> ToggleReleaseStar(Guid releaseId, + ApplicationUser user, bool starred) { - var r = await base.ToggleReleaseFavorite(releaseId, user, starred); + var r = await ToggleReleaseFavorite(releaseId, user, starred); if (r.IsNotFoundResult) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release Id [{ releaseId }]"); - } + return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Invalid Release Id [{releaseId}]"); return new subsonic.SubsonicOperationResult { IsSuccess = r.IsSuccess, @@ -2331,13 +2361,13 @@ namespace Roadie.Api.Services }; } - private async Task> ToggleTrackStar(Guid trackId, ApplicationUser user, bool starred) + private async Task> ToggleTrackStar(Guid trackId, ApplicationUser user, + bool starred) { - var r = await base.ToggleTrackFavorite(trackId, user, starred); + var r = await ToggleTrackFavorite(trackId, user, starred); if (r.IsNotFoundResult) - { - return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ trackId }]"); - } + return new subsonic.SubsonicOperationResult(subsonic.ErrorCodes.TheRequestedDataWasNotFound, + $"Invalid Track Id [{trackId}]"); return new subsonic.SubsonicOperationResult { IsSuccess = r.IsSuccess, diff --git a/Roadie.Api.Services/TokenService.cs b/Roadie.Api.Services/TokenService.cs index 954b5f8..e66ec3f 100644 --- a/Roadie.Api.Services/TokenService.cs +++ b/Roadie.Api.Services/TokenService.cs @@ -1,10 +1,12 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; using Roadie.Library.Identity; using System; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; +using System.Text; using System.Threading.Tasks; namespace Roadie.Api.Services @@ -15,7 +17,7 @@ namespace Roadie.Api.Services public TokenService(IConfiguration configuration) { - this._configuration = configuration; + _configuration = configuration; } public async Task GenerateToken(ApplicationUser user, UserManager userManager) @@ -27,7 +29,7 @@ namespace Roadie.Api.Services var tokenHandler = new JwtSecurityTokenHandler(); - var claims = new Claim[] + var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), new Claim("roadie_id", user.RoadieId.ToString()), @@ -38,17 +40,19 @@ namespace Roadie.Api.Services }.Union(userRoles); var now = DateTime.UtcNow; - var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(this._configuration.GetValue("Tokens:PrivateKey"))); - var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature); + var securityKey = + new SymmetricSecurityKey( + Encoding.Default.GetBytes(_configuration.GetValue("Tokens:PrivateKey"))); + var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature); var jwt = new JwtSecurityToken( signingCredentials: signingCredentials, claims: claims, notBefore: utcNow, - expires: utcNow.AddSeconds(this._configuration.GetValue("Tokens:Lifetime")), - audience: this._configuration.GetValue("Tokens:Audience"), - issuer: this._configuration.GetValue("Tokens:Issuer") - ); + expires: utcNow.AddSeconds(_configuration.GetValue("Tokens:Lifetime")), + audience: _configuration.GetValue("Tokens:Audience"), + issuer: _configuration.GetValue("Tokens:Issuer") + ); return new JwtSecurityTokenHandler().WriteToken(jwt); } diff --git a/Roadie.Api.Services/TrackService.cs b/Roadie.Api.Services/TrackService.cs index 5a07fb4..7dd10fb 100644 --- a/Roadie.Api.Services/TrackService.cs +++ b/Roadie.Api.Services/TrackService.cs @@ -9,10 +9,12 @@ using Roadie.Library.Configuration; using Roadie.Library.Encoding; using Roadie.Library.Enums; using Roadie.Library.Extensions; +using Roadie.Library.Identity; using Roadie.Library.Imaging; using Roadie.Library.Models; using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Releases; +using Roadie.Library.Models.Statistics; using Roadie.Library.Models.Users; using Roadie.Library.Utility; using System; @@ -29,20 +31,21 @@ namespace Roadie.Api.Services public class TrackService : ServiceBase, ITrackService { private IAdminService AdminService { get; } - private IBookmarkService BookmarkService { get; } = null; + + private IBookmarkService BookmarkService { get; } public TrackService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext dbContext, - ICacheManager cacheManager, - ILogger logger, - IBookmarkService bookmarkService, - IAdminService adminService) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext dbContext, + ICacheManager cacheManager, + ILogger logger, + IBookmarkService bookmarkService, + IAdminService adminService) : base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext) { - this.BookmarkService = bookmarkService; - this.AdminService = adminService; + BookmarkService = bookmarkService; + AdminService = adminService; } public static long DetermineByteEndFromHeaders(IHeaderDictionary headers, long fileLength) @@ -52,6 +55,7 @@ namespace Roadie.Api.Services { return defaultFileLength; } + long? result = null; var rangeHeader = headers["Range"]; string rangeEnd = null; @@ -66,11 +70,13 @@ namespace Roadie.Api.Services { rangeEnd = parts[1]; } + if (!string.IsNullOrEmpty(rangeEnd)) { - result = long.TryParse(rangeEnd, out long outValue) ? (int?)outValue : null; + result = long.TryParse(rangeEnd, out var outValue) ? (int?)outValue : null; } } + return result ?? defaultFileLength; } @@ -80,6 +86,7 @@ namespace Roadie.Api.Services { return 0; } + long result = 0; var rangeHeader = headers["Range"]; var rangeBegin = rangeHeader.FirstOrDefault(); @@ -94,6 +101,7 @@ namespace Roadie.Api.Services long.TryParse(rangeBegin, out result); } } + return result; } @@ -101,22 +109,26 @@ namespace Roadie.Api.Services { var sw = Stopwatch.StartNew(); sw.Start(); - var cacheKey = string.Format("urn:track_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes)); - var result = await this.CacheManager.GetAsync>(cacheKey, async () => - { - return await this.TrackByIdAction(id, includes); - }, data.Track.CacheRegionUrn(id)); + var cacheKey = string.Format("urn:track_by_id_operation:{0}:{1}", id, + includes == null ? "0" : string.Join("|", includes)); + var result = await CacheManager.GetAsync(cacheKey, + async () => { return await TrackByIdAction(id, includes); }, data.Track.CacheRegionUrn(id)); if (result?.Data != null && roadieUser != null) { - var user = this.GetUser(roadieUser.UserId); - var track = this.GetTrack(id); - result.Data.TrackPlayUrl = this.MakeTrackPlayUrl(user, track.Id, track.RoadieId); - var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Track); + var user = GetUser(roadieUser.UserId); + var track = GetTrack(id); + result.Data.TrackPlayUrl = MakeTrackPlayUrl(user, track.Id, track.RoadieId); + var userBookmarkResult = + await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Track); if (userBookmarkResult.IsSuccess) { - result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == track.RoadieId.ToString()) != null; + result.Data.UserBookmarked = + userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Value == track.RoadieId.ToString()) != + null; } - var userTrack = this.DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == roadieUser.Id); + + var userTrack = + DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == roadieUser.Id); if (userTrack != null) { result.Data.UserRating = new UserTrack @@ -128,21 +140,24 @@ namespace Roadie.Api.Services PlayedCount = userTrack.PlayedCount }; } + if (result.Data.Comments.Any()) { var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray(); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + var userCommentReactions = (from cr in DbContext.CommentReactions where commentIds.Contains(cr.CommentId) where cr.UserId == roadieUser.Id select cr).ToArray(); foreach (var comment in result.Data.Comments) { - var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); + var userCommentReaction = + userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId); comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike; comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like; } } } + sw.Stop(); return new OperationResult(result.Messages) { @@ -154,7 +169,8 @@ namespace Roadie.Api.Services }; } - public Task> List(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null) + public Task> List(PagedRequest request, User roadieUser, + bool? doRandomize = false, Guid? releaseId = null) { try { @@ -168,33 +184,34 @@ namespace Roadie.Api.Services request.Sort = request.Sort.Replace("Release.Text", "Release.Release.Text"); } - IQueryable favoriteTrackIds = (new int[0]).AsQueryable(); + var favoriteTrackIds = new int[0].AsQueryable(); if (request.FilterFavoriteOnly) { - favoriteTrackIds = (from t in this.DbContext.Tracks - join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId - where ut.UserId == roadieUser.Id - where ut.IsFavorite ?? false - select t.Id - ); + favoriteTrackIds = from t in DbContext.Tracks + join ut in DbContext.UserTracks on t.Id equals ut.TrackId + where ut.UserId == roadieUser.Id + where ut.IsFavorite ?? false + select t.Id; } - Dictionary playListTrackPositions = new Dictionary(); - int[] playlistTrackIds = new int[0]; + + var playListTrackPositions = new Dictionary(); + var playlistTrackIds = new int[0]; if (request.FilterToPlaylistId.HasValue) { - var playlistTrackInfos = (from plt in this.DbContext.PlaylistTracks - join p in this.DbContext.Playlists on plt.PlayListId equals p.Id - join t in this.DbContext.Tracks on plt.TrackId equals t.Id - where p.RoadieId == request.FilterToPlaylistId.Value - orderby plt.ListNumber - select new - { - plt.ListNumber, - t.Id - }); + var playlistTrackInfos = from plt in DbContext.PlaylistTracks + join p in DbContext.Playlists on plt.PlayListId equals p.Id + 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 + }; rowCount = playlistTrackInfos.Count(); - playListTrackPositions = playlistTrackInfos.Skip(request.SkipValue).Take(request.LimitValue).ToDictionary(x => x.Id, x => x.ListNumber); + playListTrackPositions = playlistTrackInfos.Skip(request.SkipValue).Take(request.LimitValue) + .ToDictionary(x => x.Id, x => x.ListNumber); playlistTrackIds = playListTrackPositions.Select(x => x.Key).ToArray(); request.Sort = "TrackNumber"; request.Order = "ASC"; @@ -202,16 +219,16 @@ namespace Roadie.Api.Services request.SkipValue = 0; } - int[] collectionTrackIds = new int[0]; + var collectionTrackIds = new int[0]; if (request.FilterToCollectionId.HasValue) { request.Limit = roadieUser?.PlayerTrackLimit ?? 50; - collectionTrackIds = (from cr in this.DbContext.CollectionReleases - join c in this.DbContext.Collections on cr.CollectionId equals c.Id - join r in this.DbContext.Releases on cr.ReleaseId equals r.Id - join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId - join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId + collectionTrackIds = (from cr in DbContext.CollectionReleases + join c in DbContext.Collections on cr.CollectionId equals c.Id + join r in DbContext.Releases on cr.ReleaseId equals r.Id + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId where c.RoadieId == request.FilterToCollectionId.Value orderby cr.ListNumber, rm.MediaNumber, t.TrackNumber select t.Id).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); @@ -221,16 +238,17 @@ namespace Roadie.Api.Services if (request.FilterTopPlayedOnly) { // Get request number of top played songs for artist - topTrackids = (from t in this.DbContext.Tracks - join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId - 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 + topTrackids = (from t in DbContext.Tracks + join ut in DbContext.UserTracks on t.Id equals ut.TrackId + join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + join r in DbContext.Releases on rm.ReleaseId equals r.Id + join a in DbContext.Artists on r.ArtistId equals a.Id where a.RoadieId == request.FilterToArtistId orderby ut.PlayedCount descending select t.Id - ).Skip(request.SkipValue).Take(request.LimitValue); + ).Skip(request.SkipValue).Take(request.LimitValue); } + int[] randomTrackIds = null; if (doRandomize ?? false) { @@ -263,8 +281,10 @@ namespace Roadie.Api.Services ORDER BY RAND()) ORDER BY RAND() LIMIT {1}"; - randomTrackIds = this.DbContext.Tracks.FromSql(sql, userId, request.Limit).Select(x => x.Id).ToArray(); + randomTrackIds = DbContext.Tracks.FromSql(sql, userId, request.Limit).Select(x => x.Id) + .ToArray(); } + if (request.FilterRatedOnly && !request.FilterFavoriteOnly) { var sql = @"SELECT t.id @@ -292,17 +312,20 @@ namespace Roadie.Api.Services ORDER BY RAND()) ORDER BY RAND() LIMIT {1}"; - randomTrackIds = this.DbContext.Tracks.FromSql(sql, userId, request.LimitValue).Select(x => x.Id).ToArray(); + randomTrackIds = DbContext.Tracks.FromSql(sql, userId, request.LimitValue).Select(x => x.Id) + .ToArray(); } + if (request.FilterFavoriteOnly) { rowCount = favoriteTrackIds.Count(); } else { - rowCount = this.DbContext.Tracks.Where(x => x.Hash != null).Count(); + rowCount = DbContext.Tracks.Where(x => x.Hash != null).Count(); } } + Guid?[] filterToTrackIds = null; if (request.FilterToTrackId.HasValue || request.FilterToTrackIds != null) { @@ -311,6 +334,7 @@ namespace Roadie.Api.Services { f.Add(request.FilterToTrackId); } + if (request.FilterToTrackIds != null) { foreach (var ft in request.FilterToTrackIds) @@ -321,9 +345,13 @@ namespace Roadie.Api.Services } } } + filterToTrackIds = f.ToArray(); } - var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) ? request.FilterValue.ToAlphanumericName() : null; + + var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue) + ? request.FilterValue.ToAlphanumericName() + : null; var isEqualFilter = false; if (!string.IsNullOrEmpty(request.FilterValue)) @@ -338,116 +366,120 @@ namespace Roadie.Api.Services } // Did this for performance against the Track table, with just * selcts the table scans are too much of a performance hit. - var resultQuery = (from t in this.DbContext.Tracks - 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 releaseArtist in this.DbContext.Artists on r.ArtistId equals releaseArtist.Id - join trackArtist in this.DbContext.Artists on t.ArtistId equals trackArtist.Id into tas - from trackArtist in tas.DefaultIfEmpty() - where (t.Hash != null) - where (releaseId == null || (releaseId != null && r.RoadieId == releaseId)) - where (filterToTrackIds == null || filterToTrackIds.Contains(t.RoadieId)) - where (request.FilterMinimumRating == null || t.Rating >= request.FilterMinimumRating.Value) - where (request.FilterValue == "" || (t.Title.Contains(request.FilterValue) || - t.AlternateNames.Contains(request.FilterValue) || - t.AlternateNames.Contains(normalizedFilterValue)) || - t.PartTitles.Contains(request.FilterValue)) - where (!isEqualFilter || (t.Title.Equals(request.FilterValue) || - t.AlternateNames.Equals(request.FilterValue) || - t.AlternateNames.Equals(normalizedFilterValue)) || - t.PartTitles.Equals(request.FilterValue)) - where (!request.FilterFavoriteOnly || favoriteTrackIds.Contains(t.Id)) - where (request.FilterToPlaylistId == null || playlistTrackIds.Contains(t.Id)) - where (!request.FilterTopPlayedOnly || topTrackids.Contains(t.Id)) - where (randomTrackIds == null || randomTrackIds.Contains(t.Id)) - where (request.FilterToArtistId == null || request.FilterToArtistId != null && ((t.TrackArtist != null && t.TrackArtist.RoadieId == request.FilterToArtistId) || r.Artist.RoadieId == request.FilterToArtistId)) - where (!request.IsHistoryRequest || t.PlayedCount > 0) - where (request.FilterToCollectionId == null || collectionTrackIds.Contains(t.Id)) - select new - { - ti = new - { - t.Id, - t.RoadieId, - t.CreatedDate, - t.LastUpdated, - t.LastPlayed, - t.Duration, - t.FileSize, - t.PlayedCount, - t.PartTitles, - t.Rating, - t.Tags, - t.TrackNumber, - t.Title - }, - rmi = new - { - rm.MediaNumber - }, - rl = new ReleaseList - { - DatabaseId = r.Id, - Id = r.RoadieId, - Artist = new DataToken - { - Value = releaseArtist.RoadieId.ToString(), - Text = releaseArtist.Name - }, - Release = new DataToken - { - Text = r.Title, - Value = r.RoadieId.ToString() - }, - ArtistThumbnail = this.MakeArtistThumbnailImage(releaseArtist.RoadieId), - CreatedDate = r.CreatedDate, - Duration = r.Duration, - LastPlayed = r.LastPlayed, - LastUpdated = r.LastUpdated, - LibraryStatus = r.LibraryStatus, - MediaCount = r.MediaCount, - Rating = r.Rating, - Rank = r.Rank, - ReleaseDateDateTime = r.ReleaseDate, - ReleasePlayUrl = $"{ this.HttpContext.BaseUrl }/play/release/{ r.RoadieId}", - Status = r.Status, - Thumbnail = this.MakeReleaseThumbnailImage(r.RoadieId), - TrackCount = r.TrackCount, - TrackPlayedCount = r.PlayedCount - }, - ta = trackArtist == null ? null : new ArtistList - { - DatabaseId = trackArtist.Id, - Id = trackArtist.RoadieId, - Artist = new DataToken { Text = trackArtist.Name, Value = trackArtist.RoadieId.ToString() }, - Rating = trackArtist.Rating, - Rank = trackArtist.Rank, - CreatedDate = trackArtist.CreatedDate, - LastUpdated = trackArtist.LastUpdated, - LastPlayed = trackArtist.LastPlayed, - PlayedCount = trackArtist.PlayedCount, - ReleaseCount = trackArtist.ReleaseCount, - TrackCount = trackArtist.TrackCount, - SortName = trackArtist.SortName, - Thumbnail = this.MakeArtistThumbnailImage(trackArtist.RoadieId) - }, - ra = new ArtistList - { - DatabaseId = releaseArtist.Id, - Id = releaseArtist.RoadieId, - Artist = new DataToken { Text = releaseArtist.Name, Value = releaseArtist.RoadieId.ToString() }, - Rating = releaseArtist.Rating, - Rank = releaseArtist.Rank, - CreatedDate = releaseArtist.CreatedDate, - LastUpdated = releaseArtist.LastUpdated, - LastPlayed = releaseArtist.LastPlayed, - PlayedCount = releaseArtist.PlayedCount, - ReleaseCount = releaseArtist.ReleaseCount, - TrackCount = releaseArtist.TrackCount, - SortName = releaseArtist.SortName, - Thumbnail = this.MakeArtistThumbnailImage(releaseArtist.RoadieId) - } - }); + var resultQuery = from t in DbContext.Tracks + 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 t.Hash != null + where releaseId == null || releaseId != null && r.RoadieId == releaseId + where filterToTrackIds == null || filterToTrackIds.Contains(t.RoadieId) + where request.FilterMinimumRating == null || t.Rating >= request.FilterMinimumRating.Value + where request.FilterValue == "" || t.Title.Contains(request.FilterValue) || + t.AlternateNames.Contains(request.FilterValue) || + t.AlternateNames.Contains(normalizedFilterValue) || t.PartTitles.Contains(request.FilterValue) + where !isEqualFilter || t.Title.Equals(request.FilterValue) || + t.AlternateNames.Equals(request.FilterValue) || + t.AlternateNames.Equals(normalizedFilterValue) || t.PartTitles.Equals(request.FilterValue) + where !request.FilterFavoriteOnly || favoriteTrackIds.Contains(t.Id) + where request.FilterToPlaylistId == null || playlistTrackIds.Contains(t.Id) + where !request.FilterTopPlayedOnly || topTrackids.Contains(t.Id) + where randomTrackIds == null || randomTrackIds.Contains(t.Id) + where request.FilterToArtistId == null || request.FilterToArtistId != null && + (t.TrackArtist != null && t.TrackArtist.RoadieId == request.FilterToArtistId || + r.Artist.RoadieId == request.FilterToArtistId) + where !request.IsHistoryRequest || t.PlayedCount > 0 + where request.FilterToCollectionId == null || collectionTrackIds.Contains(t.Id) + select new + { + ti = new + { + t.Id, + t.RoadieId, + t.CreatedDate, + t.LastUpdated, + t.LastPlayed, + t.Duration, + t.FileSize, + t.PlayedCount, + t.PartTitles, + t.Rating, + t.Tags, + t.TrackNumber, + t.Title + }, + rmi = new + { + rm.MediaNumber + }, + rl = new ReleaseList + { + DatabaseId = r.Id, + Id = r.RoadieId, + Artist = new DataToken + { + Value = releaseArtist.RoadieId.ToString(), + Text = releaseArtist.Name + }, + Release = new DataToken + { + Text = r.Title, + Value = r.RoadieId.ToString() + }, + ArtistThumbnail = MakeArtistThumbnailImage(releaseArtist.RoadieId), + CreatedDate = r.CreatedDate, + Duration = r.Duration, + LastPlayed = r.LastPlayed, + LastUpdated = r.LastUpdated, + LibraryStatus = r.LibraryStatus, + MediaCount = r.MediaCount, + Rating = r.Rating, + Rank = r.Rank, + ReleaseDateDateTime = r.ReleaseDate, + ReleasePlayUrl = $"{HttpContext.BaseUrl}/play/release/{r.RoadieId}", + Status = r.Status, + Thumbnail = MakeReleaseThumbnailImage(r.RoadieId), + TrackCount = r.TrackCount, + TrackPlayedCount = r.PlayedCount + }, + ta = trackArtist == null + ? null + : new ArtistList + { + DatabaseId = trackArtist.Id, + Id = trackArtist.RoadieId, + Artist = new DataToken + { Text = trackArtist.Name, Value = trackArtist.RoadieId.ToString() }, + Rating = trackArtist.Rating, + Rank = trackArtist.Rank, + CreatedDate = trackArtist.CreatedDate, + LastUpdated = trackArtist.LastUpdated, + LastPlayed = trackArtist.LastPlayed, + PlayedCount = trackArtist.PlayedCount, + ReleaseCount = trackArtist.ReleaseCount, + TrackCount = trackArtist.TrackCount, + SortName = trackArtist.SortName, + Thumbnail = MakeArtistThumbnailImage(trackArtist.RoadieId) + }, + ra = new ArtistList + { + DatabaseId = releaseArtist.Id, + Id = releaseArtist.RoadieId, + Artist = new DataToken + { Text = releaseArtist.Name, Value = releaseArtist.RoadieId.ToString() }, + Rating = releaseArtist.Rating, + Rank = releaseArtist.Rank, + CreatedDate = releaseArtist.CreatedDate, + LastUpdated = releaseArtist.LastUpdated, + LastPlayed = releaseArtist.LastPlayed, + PlayedCount = releaseArtist.PlayedCount, + ReleaseCount = releaseArtist.ReleaseCount, + TrackCount = releaseArtist.TrackCount, + SortName = releaseArtist.SortName, + Thumbnail = MakeArtistThumbnailImage(releaseArtist.RoadieId) + } + }; if (!string.IsNullOrEmpty(request.FilterValue)) { @@ -458,35 +490,38 @@ namespace Roadie.Api.Services resultQuery = resultQuery.Where(x => x.ti.Tags != null && x.ti.Tags.Contains(tagValue)); } } - var user = this.GetUser(roadieUser.UserId); + + var user = GetUser(roadieUser.UserId); var result = resultQuery.Select(x => - new TrackList - { - DatabaseId = x.ti.Id, - Id = x.ti.RoadieId, - Track = new DataToken - { - Text = x.ti.Title, - Value = x.ti.RoadieId.ToString() - }, - Artist = x.ra, - CreatedDate = x.ti.CreatedDate, - Duration = x.ti.Duration, - FileSize = x.ti.FileSize, - LastPlayed = x.ti.LastPlayed, - LastUpdated = x.ti.LastUpdated, - MediaNumber = x.rmi.MediaNumber, - PlayedCount = x.ti.PlayedCount, - PartTitles = x.ti.PartTitles, - Rating = x.ti.Rating, - Release = x.rl, - ReleaseDate = x.rl.ReleaseDateDateTime, - Thumbnail = this.MakeTrackThumbnailImage(x.ti.RoadieId), - Title = x.ti.Title, - TrackArtist = x.ta, - TrackNumber = playListTrackPositions.ContainsKey(x.ti.Id) ? playListTrackPositions[x.ti.Id] : x.ti.TrackNumber, - TrackPlayUrl = this.MakeTrackPlayUrl(user, x.ti.Id, x.ti.RoadieId) - }); + new TrackList + { + DatabaseId = x.ti.Id, + Id = x.ti.RoadieId, + Track = new DataToken + { + Text = x.ti.Title, + Value = x.ti.RoadieId.ToString() + }, + Artist = x.ra, + CreatedDate = x.ti.CreatedDate, + Duration = x.ti.Duration, + FileSize = x.ti.FileSize, + LastPlayed = x.ti.LastPlayed, + LastUpdated = x.ti.LastUpdated, + MediaNumber = x.rmi.MediaNumber, + PlayedCount = x.ti.PlayedCount, + PartTitles = x.ti.PartTitles, + Rating = x.ti.Rating, + Release = x.rl, + ReleaseDate = x.rl.ReleaseDateDateTime, + Thumbnail = MakeTrackThumbnailImage(x.ti.RoadieId), + Title = x.ti.Title, + TrackArtist = x.ta, + TrackNumber = playListTrackPositions.ContainsKey(x.ti.Id) + ? playListTrackPositions[x.ti.Id] + : x.ti.TrackNumber, + TrackPlayUrl = MakeTrackPlayUrl(user, x.ti.Id, x.ti.RoadieId) + }); string sortBy = null; rowCount = rowCount ?? result.Count(); @@ -494,12 +529,19 @@ namespace Roadie.Api.Services if (request.Action == User.ActionKeyUserRated) { - sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "UserTrack.Rating", "DESC" }, { "MediaNumber", "ASC" }, { "TrackNumber", "ASC" } }) : request.OrderValue(null); + sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary + {{"UserTrack.Rating", "DESC"}, {"MediaNumber", "ASC"}, {"TrackNumber", "ASC"}}) + : request.OrderValue(); } else { - sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "Release.Release.Text", "ASC" }, { "MediaNumber", "ASC" }, { "TrackNumber", "ASC" } }) : request.OrderValue(null); + sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary + {{"Release.Release.Text", "ASC"}, {"MediaNumber", "ASC"}, {"TrackNumber", "ASC"}}) + : request.OrderValue(); } + if (doRandomize ?? false) { rows = result.OrderBy(x => x.RandomSortId).Take(request.LimitValue).ToArray(); @@ -508,10 +550,11 @@ namespace Roadie.Api.Services { rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); } + if (rows.Any() && roadieUser != null) { var rowIds = rows.Select(x => x.DatabaseId).ToArray(); - var userTrackRatings = (from ut in this.DbContext.UserTracks + var userTrackRatings = (from ut in DbContext.UserTracks where ut.UserId == roadieUser.Id where rowIds.Contains(ut.TrackId) select ut).ToArray(); @@ -533,7 +576,7 @@ namespace Roadie.Api.Services } var releaseIds = rows.Select(x => x.Release.DatabaseId).Distinct().ToArray(); - var userReleaseRatings = (from ur in this.DbContext.UserReleases + var userReleaseRatings = (from ur in DbContext.UserReleases where releaseIds.Contains(ur.ReleaseId) select ur).ToArray(); @@ -548,23 +591,25 @@ namespace Roadie.Api.Services var artistIds = rows.Select(x => x.Artist.DatabaseId).ToArray(); if (artistIds != null && artistIds.Any()) { - var userArtistRatings = (from ua in this.DbContext.UserArtists + var userArtistRatings = (from ua in DbContext.UserArtists where ua.UserId == roadieUser.Id where artistIds.Contains(ua.ArtistId) select ua).ToArray(); foreach (var userArtistRating in userArtistRatings) { - foreach (var artistTrack in rows.Where(x => x.Artist.DatabaseId == userArtistRating.ArtistId)) + foreach (var artistTrack in rows.Where( + x => x.Artist.DatabaseId == userArtistRating.ArtistId)) { artistTrack.Artist.UserRating = userArtistRating.Adapt(); } } } - var trackArtistIds = rows.Where(x => x.TrackArtist != null).Select(x => x.TrackArtist.DatabaseId).ToArray(); + var trackArtistIds = rows.Where(x => x.TrackArtist != null).Select(x => x.TrackArtist.DatabaseId) + .ToArray(); if (trackArtistIds != null && trackArtistIds.Any()) { - var userTrackArtistRatings = (from ua in this.DbContext.UserArtists + var userTrackArtistRatings = (from ua in DbContext.UserArtists where ua.UserId == roadieUser.Id where trackArtistIds.Contains(ua.ArtistId) select ua).ToArray(); @@ -572,7 +617,9 @@ namespace Roadie.Api.Services { foreach (var userTrackArtistRating in userTrackArtistRatings) { - foreach (var artistTrack in rows.Where(x => x.TrackArtist != null && x.TrackArtist.DatabaseId == userTrackArtistRating.ArtistId)) + foreach (var artistTrack in rows.Where(x => + x.TrackArtist != null && + x.TrackArtist.DatabaseId == userTrackArtistRating.ArtistId)) { artistTrack.Artist.UserRating = userTrackArtistRating.Adapt(); } @@ -585,8 +632,8 @@ namespace Roadie.Api.Services { foreach (var row in rows) { - row.FavoriteCount = (from ut in this.DbContext.UserTracks - join tr in this.DbContext.Tracks on ut.TrackId equals tr.Id + row.FavoriteCount = (from ut in DbContext.UserTracks + join tr in DbContext.Tracks on ut.TrackId equals tr.Id where ut.TrackId == row.DatabaseId where ut.IsFavorite ?? false select ut.Id).Count(); @@ -605,7 +652,8 @@ namespace Roadie.Api.Services } catch (Exception ex) { - this.Logger.LogError(ex, "Error In List, Request [{0}], User [{1}]", JsonConvert.SerializeObject(request), roadieUser); + Logger.LogError(ex, "Error In List, Request [{0}], User [{1}]", JsonConvert.SerializeObject(request), + roadieUser); return Task.FromResult(new Library.Models.Pagination.PagedResult { Message = "An Error has occured" @@ -614,102 +662,115 @@ namespace Roadie.Api.Services } /// - /// Fast as possible check if exists and return minimum information on Track + /// Fast as possible check if exists and return minimum information on Track /// public OperationResult StreamCheckAndInfo(User roadieUser, Guid id) { - var track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == id); + var track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == id); if (track == null) { return new OperationResult(true, string.Format("Track Not Found [{0}]", id)); } - return new OperationResult() + + return new OperationResult { Data = track.Adapt(), IsSuccess = true }; } - public async Task> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, User roadieUser) + public async Task> TrackStreamInfo(Guid trackId, long beginBytes, + long endBytes, User roadieUser) { - var track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); + var track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); if (track == null) { // Not Found try recanning release - var release = (from r in this.DbContext.Releases - join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + var release = (from r in DbContext.Releases + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId where rm.Id == track.ReleaseMediaId select r).FirstOrDefault(); if (!release.IsLocked ?? false) { - await this.AdminService.ScanRelease(new Library.Identity.ApplicationUser + await AdminService.ScanRelease(new ApplicationUser { Id = roadieUser.Id.Value }, release.RoadieId, false, true); } - track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); + + track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); if (track == null) { - return new OperationResult($"TrackStreamInfo: Unable To Find Track [{ trackId }]"); + return new OperationResult($"TrackStreamInfo: Unable To Find Track [{trackId}]"); } } + if (!track.IsValid) { - return new OperationResult($"TrackStreamInfo: Invalid Track. Track Id [{trackId}], FilePath [{track.FilePath}], Filename [{track.FileName}]"); + return new OperationResult( + $"TrackStreamInfo: Invalid Track. Track Id [{trackId}], FilePath [{track.FilePath}], Filename [{track.FileName}]"); } + string trackPath = null; try { - trackPath = track.PathToTrack(this.Configuration, this.Configuration.LibraryFolder); + trackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder); } catch (Exception ex) { return new OperationResult(ex); } + var trackFileInfo = new FileInfo(trackPath); if (!trackFileInfo.Exists) { // Not Found try recanning release - var release = (from r in this.DbContext.Releases - join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + var release = (from r in DbContext.Releases + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId where rm.Id == track.ReleaseMediaId select r).FirstOrDefault(); if (!release.IsLocked ?? false) { - await this.AdminService.ScanRelease(new Library.Identity.ApplicationUser + await AdminService.ScanRelease(new ApplicationUser { Id = roadieUser.Id.Value }, release.RoadieId, false, true); } - track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); + + track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); if (track == null) { - return new OperationResult($"TrackStreamInfo: Unable To Find Track [{ trackId }]"); + return new OperationResult($"TrackStreamInfo: Unable To Find Track [{trackId}]"); } + try { - trackPath = track.PathToTrack(this.Configuration, this.Configuration.LibraryFolder); + trackPath = track.PathToTrack(Configuration, Configuration.LibraryFolder); } catch (Exception ex) { return new OperationResult(ex); } + if (!trackFileInfo.Exists) { track.UpdateTrackMissingFile(); - await this.DbContext.SaveChangesAsync(); - return new OperationResult($"TrackStreamInfo: TrackId [{trackId}] Unable to Find Track [{trackFileInfo.FullName}]"); + await DbContext.SaveChangesAsync(); + return new OperationResult( + $"TrackStreamInfo: TrackId [{trackId}] Unable to Find Track [{trackFileInfo.FullName}]"); } } - var contentDurationTimeSpan = TimeSpan.FromMilliseconds((double)(track.Duration ?? 0)); + + var contentDurationTimeSpan = TimeSpan.FromMilliseconds(track.Duration ?? 0); var info = new TrackStreamInfo { - FileName = this.HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly(), - ContentDisposition = $"attachment; filename=\"{ this.HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly() }\"", - ContentDuration = contentDurationTimeSpan.TotalSeconds.ToString(), + FileName = HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly(), + ContentDisposition = + $"attachment; filename=\"{HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly()}\"", + ContentDuration = contentDurationTimeSpan.TotalSeconds.ToString() }; var cacheTimeout = 86400; // 24 hours - var contentLength = (endBytes - beginBytes) + 1; + var contentLength = endBytes - beginBytes + 1; info.Track = new DataToken { Text = track.Title, @@ -719,14 +780,14 @@ namespace Roadie.Api.Services info.EndBytes = endBytes; info.ContentRange = $"bytes {beginBytes}-{endBytes}/{contentLength}"; info.ContentLength = contentLength.ToString(); - info.IsFullRequest = beginBytes == 0 && endBytes == (trackFileInfo.Length - 1); - info.IsEndRangeRequest = beginBytes > 0 && endBytes != (trackFileInfo.Length - 1); + info.IsFullRequest = beginBytes == 0 && endBytes == trackFileInfo.Length - 1; + info.IsEndRangeRequest = beginBytes > 0 && endBytes != trackFileInfo.Length - 1; info.LastModified = (track.LastUpdated ?? track.CreatedDate).ToString("R"); info.Etag = track.Etag; - info.CacheControl = $"public, max-age={ cacheTimeout.ToString() } "; + info.CacheControl = $"public, max-age={cacheTimeout.ToString()} "; info.Expires = DateTime.UtcNow.AddMinutes(cacheTimeout).ToString("R"); - int bytesToRead = (int)(endBytes - beginBytes) + 1; - byte[] trackBytes = new byte[bytesToRead]; + var bytesToRead = (int)(endBytes - beginBytes) + 1; + var trackBytes = new byte[bytesToRead]; using (var fs = trackFileInfo.OpenRead()) { try @@ -739,6 +800,7 @@ namespace Roadie.Api.Services return new OperationResult(ex); } } + info.Bytes = trackBytes; return new OperationResult { @@ -754,15 +816,16 @@ namespace Roadie.Api.Services var sw = new Stopwatch(); sw.Start(); var errors = new List(); - var track = this.DbContext.Tracks - .Include(x => x.ReleaseMedia) - .Include(x => x.ReleaseMedia.Release) - .Include(x => x.ReleaseMedia.Release.Artist) - .FirstOrDefault(x => x.RoadieId == model.Id); + var track = DbContext.Tracks + .Include(x => x.ReleaseMedia) + .Include(x => x.ReleaseMedia.Release) + .Include(x => x.ReleaseMedia.Release.Artist) + .FirstOrDefault(x => x.RoadieId == model.Id); if (track == null) { return new OperationResult(true, string.Format("Track Not Found [{0}]", model.Id)); } + try { var now = DateTime.UtcNow; @@ -776,14 +839,16 @@ namespace Roadie.Api.Services track.MusicBrainzId = model.MusicBrainzId; track.SpotifyId = model.SpotifyId; track.Tags = model.TagsList.ToDelimitedList(); - track.PartTitles = model.PartTitlesList == null || !model.PartTitlesList.Any() ? null : string.Join("\n", model.PartTitlesList); + track.PartTitles = model.PartTitlesList == null || !model.PartTitlesList.Any() + ? null + : string.Join("\n", model.PartTitlesList); if (model.TrackArtistToken != null) { var artistId = SafeParser.ToGuid(model.TrackArtistToken.Value); if (artistId.HasValue) { - var artist = this.GetArtist(artistId.Value); + var artist = GetArtist(artistId.Value); if (artist != null) { track.ArtistId = artist.Id; @@ -802,23 +867,27 @@ namespace Roadie.Api.Services track.Thumbnail = ImageHelper.ConvertToJpegFormat(trackImage); // Save unaltered image to cover file - var trackThumbnailName = track.PathToTrackThumbnail(this.Configuration, this.Configuration.LibraryFolder); + var trackThumbnailName = track.PathToTrackThumbnail(Configuration, Configuration.LibraryFolder); File.WriteAllBytes(trackThumbnailName, track.Thumbnail); // Resize to store in database as thumbnail - track.Thumbnail = ImageHelper.ResizeImage(track.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); + track.Thumbnail = ImageHelper.ResizeImage(track.Thumbnail, Configuration.MediumImageSize.Width, + Configuration.MediumImageSize.Height); didChangeThumbnail = true; } + track.LastUpdated = now; - await this.DbContext.SaveChangesAsync(); - this.CacheManager.ClearRegion(track.CacheRegion); - this.Logger.LogInformation($"UpdateTrack `{ track }` By User `{ user }`: Edited Track [{ didChangeTrack }], Uploaded new image [{ didChangeThumbnail }]"); + await DbContext.SaveChangesAsync(); + CacheManager.ClearRegion(track.CacheRegion); + Logger.LogInformation( + $"UpdateTrack `{track}` By User `{user}`: Edited Track [{didChangeTrack}], Uploaded new image [{didChangeThumbnail}]"); } catch (Exception ex) { - this.Logger.LogError(ex); + Logger.LogError(ex); errors.Add(ex); } + sw.Stop(); return new OperationResult @@ -835,50 +904,57 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); sw.Start(); - var track = this.GetTrack(id); + var track = GetTrack(id); if (track == null) { return Task.FromResult(new OperationResult(true, string.Format("Track Not Found [{0}]", id))); } + var result = track.Adapt(); result.IsLocked = (track.IsLocked ?? false) || (track.ReleaseMedia.IsLocked ?? false) || (track.ReleaseMedia.Release.IsLocked ?? false) || (track.ReleaseMedia.Release.Artist.IsLocked ?? false); - result.Thumbnail = base.MakeTrackThumbnailImage(id); - result.MediumThumbnail = base.MakeThumbnailImage(id, "track", this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height); + result.Thumbnail = MakeTrackThumbnailImage(id); + result.MediumThumbnail = MakeThumbnailImage(id, "track", Configuration.MediumImageSize.Width, + Configuration.MediumImageSize.Height); result.ReleaseMediaId = track.ReleaseMedia.RoadieId.ToString(); - result.Artist = ArtistList.FromDataArtist(track.ReleaseMedia.Release.Artist, this.MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId)); - result.ArtistThumbnail = this.MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId); - result.Release = ReleaseList.FromDataRelease(track.ReleaseMedia.Release, track.ReleaseMedia.Release.Artist, this.HttpContext.BaseUrl, this.MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId), this.MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId)); - result.ReleaseThumbnail = this.MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId); + result.Artist = ArtistList.FromDataArtist(track.ReleaseMedia.Release.Artist, + MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId)); + result.ArtistThumbnail = MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId); + result.Release = ReleaseList.FromDataRelease(track.ReleaseMedia.Release, track.ReleaseMedia.Release.Artist, + HttpContext.BaseUrl, MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId), + MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId)); + result.ReleaseThumbnail = MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId); if (track.ArtistId.HasValue) { - var trackArtist = this.DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId); + var trackArtist = DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId); if (trackArtist == null) { - this.Logger.LogWarning($"Unable to find Track Artist [{ track.ArtistId }"); + Logger.LogWarning($"Unable to find Track Artist [{track.ArtistId}"); } else { - result.TrackArtist = ArtistList.FromDataArtist(trackArtist, this.MakeArtistThumbnailImage(trackArtist.RoadieId)); + result.TrackArtist = + ArtistList.FromDataArtist(trackArtist, MakeArtistThumbnailImage(trackArtist.RoadieId)); result.TrackArtistToken = result.TrackArtist.Artist; - result.TrackArtistThumbnail = this.MakeArtistThumbnailImage(trackArtist.RoadieId); + result.TrackArtistThumbnail = MakeArtistThumbnailImage(trackArtist.RoadieId); } } + if (includes != null && includes.Any()) { if (includes.Contains("stats")) { - result.Statistics = new Library.Models.Statistics.TrackStatistics + result.Statistics = new TrackStatistics { FileSizeFormatted = ((long?)track.FileSize).ToFileSize(), Time = new TimeInfo((decimal)track.Duration).ToFullFormattedString(), PlayedCount = track.PlayedCount }; - var userTracks = (from t in this.DbContext.Tracks - join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId + var userTracks = (from t in DbContext.Tracks + join ut in DbContext.UserTracks on t.Id equals ut.TrackId where t.Id == track.Id select ut).ToArray(); if (userTracks != null && userTracks.Any()) @@ -887,25 +963,31 @@ namespace Roadie.Api.Services result.Statistics.FavoriteCount = userTracks.Count(x => x.IsFavorite ?? false); } } + if (includes.Contains("comments")) { - var trackComments = this.DbContext.Comments.Include(x => x.User).Where(x => x.TrackId == track.Id).OrderByDescending(x => x.CreatedDate).ToArray(); + var trackComments = DbContext.Comments.Include(x => x.User).Where(x => x.TrackId == track.Id) + .OrderByDescending(x => x.CreatedDate).ToArray(); if (trackComments.Any()) { var comments = new List(); var commentIds = trackComments.Select(x => x.Id).ToArray(); - var userCommentReactions = (from cr in this.DbContext.CommentReactions + var userCommentReactions = (from cr in DbContext.CommentReactions where commentIds.Contains(cr.CommentId) select cr).ToArray(); foreach (var trackComment in trackComments) { var comment = trackComment.Adapt(); comment.DatabaseId = trackComment.Id; - comment.User = UserList.FromDataUser(trackComment.User, this.MakeUserThumbnailImage(trackComment.User.RoadieId)); - comment.DislikedCount = userCommentReactions.Count(x => x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Dislike); - comment.LikedCount = userCommentReactions.Count(x => x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Like); + comment.User = UserList.FromDataUser(trackComment.User, + MakeUserThumbnailImage(trackComment.User.RoadieId)); + comment.DislikedCount = userCommentReactions.Count(x => + x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Dislike); + comment.LikedCount = userCommentReactions.Count(x => + x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Like); comments.Add(comment); } + result.Comments = comments; } } diff --git a/Roadie.Api.Services/UserService.cs b/Roadie.Api.Services/UserService.cs index b23f1af..ff1a4f8 100644 --- a/Roadie.Api.Services/UserService.cs +++ b/Roadie.Api.Services/UserService.cs @@ -7,11 +7,12 @@ using Roadie.Library; using Roadie.Library.Caching; using Roadie.Library.Configuration; using Roadie.Library.Encoding; +using Roadie.Library.Enums; using Roadie.Library.Identity; using Roadie.Library.Imaging; using Roadie.Library.MetaData.LastFm; -using Roadie.Library.Models; using Roadie.Library.Models.Pagination; +using Roadie.Library.Models.Releases; using Roadie.Library.Models.Statistics; using Roadie.Library.Models.Users; using Roadie.Library.Utility; @@ -33,17 +34,18 @@ namespace Roadie.Api.Services private UserManager UserManager { get; } public UserService(IRoadieSettings configuration, - IHttpEncoder httpEncoder, - IHttpContext httpContext, - data.IRoadieDbContext context, - ICacheManager cacheManager, - ILogger logger, - UserManager userManager - ) + IHttpEncoder httpEncoder, + IHttpContext httpContext, + data.IRoadieDbContext context, + ICacheManager cacheManager, + ILogger logger, + UserManager userManager + ) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { - this.UserManager = userManager; - this.LastFmHelper = new LastFmHelper(this.Configuration, this.CacheManager, this.Logger, context, httpEncoder); ; + UserManager = userManager; + LastFmHelper = new LastFmHelper(Configuration, CacheManager, Logger, context, httpEncoder); + ; } public async Task> ById(User user, Guid id, IEnumerable includes) @@ -54,21 +56,18 @@ namespace Roadie.Api.Services var sw = Stopwatch.StartNew(); sw.Start(); var cacheKey = string.Format("urn:user_by_id_operation:{0}", id); - var result = await this.CacheManager.GetAsync>(cacheKey, async () => + var result = await CacheManager.GetAsync(cacheKey, async () => { tsw.Restart(); - var rr = await this.UserByIdAction(id, includes); + var rr = await UserByIdAction(id, includes); tsw.Stop(); timings.Add("UserByIdAction", tsw.ElapsedMilliseconds); return rr; }, ApplicationUser.CacheRegionUrn(id)); sw.Stop(); - if (result?.Data != null) - { - result.Data.Avatar = this.MakeUserThumbnailImage(id); - } + if (result?.Data != null) result.Data.Avatar = MakeUserThumbnailImage(id); timings.Add("operation", sw.ElapsedMilliseconds); - this.Logger.LogDebug("ById Timings: id [{0}]", id); + Logger.LogDebug("ById Timings: id [{0}]", id); return new OperationResult(result.Messages) { Data = result?.Data, @@ -84,43 +83,48 @@ namespace Roadie.Api.Services var sw = new Stopwatch(); sw.Start(); - var result = (from u in this.DbContext.Users - let lastActivity = (from ut in this.DbContext.UserTracks - where ut.UserId == u.Id - select ut.LastPlayed).Max() - where (request.FilterValue.Length == 0 || (request.FilterValue.Length > 0 && (u.UserName.Contains(request.FilterValue)))) - select new UserList - { - DatabaseId = u.Id, - Id = u.RoadieId, - User = new DataToken - { - Text = u.UserName, - Value = u.RoadieId.ToString() - }, - IsEditor = u.UserRoles.Any(x => x.Role.Name == "Editor"), - IsPrivate = u.IsPrivate, - Thumbnail = this.MakeUserThumbnailImage(u.RoadieId), - CreatedDate = u.CreatedDate, - LastUpdated = u.LastUpdated, - RegisteredDate = u.RegisteredOn, - LastLoginDate = u.LastLogin, - LastApiAccessDate = u.LastApiAccess, - LastActivity = lastActivity - }); + var result = from u in DbContext.Users + let lastActivity = (from ut in DbContext.UserTracks + where ut.UserId == u.Id + select ut.LastPlayed).Max() + where request.FilterValue.Length == 0 || + request.FilterValue.Length > 0 && u.UserName.Contains(request.FilterValue) + select new UserList + { + DatabaseId = u.Id, + Id = u.RoadieId, + User = new models.DataToken + { + Text = u.UserName, + Value = u.RoadieId.ToString() + }, + IsEditor = u.UserRoles.Any(x => x.Role.Name == "Editor"), + IsPrivate = u.IsPrivate, + Thumbnail = MakeUserThumbnailImage(u.RoadieId), + CreatedDate = u.CreatedDate, + LastUpdated = u.LastUpdated, + RegisteredDate = u.RegisteredOn, + LastLoginDate = u.LastLogin, + LastApiAccessDate = u.LastApiAccess, + LastActivity = lastActivity + }; UserList[] rows = null; var rowCount = result.Count(); - var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "User.Text", "ASC" } }) : request.OrderValue(null); + var sortBy = string.IsNullOrEmpty(request.Sort) + ? request.OrderValue(new Dictionary { { "User.Text", "ASC" } }) + : request.OrderValue(); rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); if (rows.Any()) - { foreach (var row in rows) { - var userArtists = this.DbContext.UserArtists.Include(x => x.Artist).Where(x => x.UserId == row.DatabaseId).ToArray(); - var userReleases = this.DbContext.UserReleases.Include(x => x.Release).Where(x => x.UserId == row.DatabaseId).ToArray(); - var userTracks = this.DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == row.DatabaseId).ToArray(); + var userArtists = DbContext.UserArtists.Include(x => x.Artist) + .Where(x => x.UserId == row.DatabaseId).ToArray(); + var userReleases = DbContext.UserReleases.Include(x => x.Release) + .Where(x => x.UserId == row.DatabaseId).ToArray(); + var userTracks = DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == row.DatabaseId) + .ToArray(); row.Statistics = new UserStatistics { @@ -136,7 +140,7 @@ namespace Roadie.Api.Services DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count() }; } - } + sw.Stop(); return Task.FromResult(new Library.Models.Pagination.PagedResult { @@ -150,19 +154,13 @@ namespace Roadie.Api.Services public async Task> SetArtistBookmark(Guid artistId, User roadieUser, bool isBookmarked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - var artist = this.GetArtist(artistId); - if (artist == null) - { - return new OperationResult(true, $"Invalid Artist [{ artistId }]"); - } - var result = await this.SetBookmark(user, Library.Enums.BookmarkType.Artist, artist.Id, isBookmarked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + var artist = GetArtist(artistId); + if (artist == null) return new OperationResult(true, $"Invalid Artist [{artistId}]"); + var result = await SetBookmark(user, BookmarkType.Artist, artist.Id, isBookmarked); - this.CacheManager.ClearRegion(artist.CacheRegion); + CacheManager.ClearRegion(artist.CacheRegion); return new OperationResult { @@ -173,49 +171,35 @@ namespace Roadie.Api.Services public async Task> SetArtistDisliked(Guid artistId, User roadieUser, bool isDisliked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - return await base.ToggleArtistDisliked(artistId, user, isDisliked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + return await ToggleArtistDisliked(artistId, user, isDisliked); } public async Task> SetArtistFavorite(Guid artistId, User roadieUser, bool isFavorite) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - return await base.ToggleArtistFavorite(artistId, user, isFavorite); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + return await ToggleArtistFavorite(artistId, user, isFavorite); } public async Task> SetArtistRating(Guid artistId, User roadieUser, short rating) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); return await base.SetArtistRating(artistId, user, rating); } - public async Task> SetCollectionBookmark(Guid collectionId, User roadieUser, bool isBookmarked) + public async Task> SetCollectionBookmark(Guid collectionId, User roadieUser, + bool isBookmarked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - var collection = this.GetCollection(collectionId); - if (collection == null) - { - return new OperationResult(true, $"Invalid Collection [{ collectionId }]"); - } - var result = await this.SetBookmark(user, Library.Enums.BookmarkType.Collection, collection.Id, isBookmarked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + var collection = GetCollection(collectionId); + if (collection == null) return new OperationResult(true, $"Invalid Collection [{collectionId}]"); + var result = await SetBookmark(user, BookmarkType.Collection, collection.Id, isBookmarked); - this.CacheManager.ClearRegion(collection.CacheRegion); + CacheManager.ClearRegion(collection.CacheRegion); return new OperationResult { @@ -226,19 +210,13 @@ namespace Roadie.Api.Services public async Task> SetLabelBookmark(Guid labelId, User roadieUser, bool isBookmarked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - var label = this.GetLabel(labelId); - if (label == null) - { - return new OperationResult(true, $"Invalid Label [{ labelId }]"); - } - var result = await this.SetBookmark(user, Library.Enums.BookmarkType.Label, label.Id, isBookmarked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + var label = GetLabel(labelId); + if (label == null) return new OperationResult(true, $"Invalid Label [{labelId}]"); + var result = await SetBookmark(user, BookmarkType.Label, label.Id, isBookmarked); - this.CacheManager.ClearRegion(label.CacheRegion); + CacheManager.ClearRegion(label.CacheRegion); return new OperationResult { @@ -247,21 +225,16 @@ namespace Roadie.Api.Services }; } - public async Task> SetPlaylistBookmark(Guid playlistId, User roadieUser, bool isBookmarked) + public async Task> SetPlaylistBookmark(Guid playlistId, User roadieUser, + bool isBookmarked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - var playlist = this.GetPlaylist(playlistId); - if (playlist == null) - { - return new OperationResult(true, $"Invalid Playlist [{ playlistId }]"); - } - var result = await this.SetBookmark(user, Library.Enums.BookmarkType.Playlist, playlist.Id, isBookmarked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + var playlist = GetPlaylist(playlistId); + if (playlist == null) return new OperationResult(true, $"Invalid Playlist [{playlistId}]"); + var result = await SetBookmark(user, BookmarkType.Playlist, playlist.Id, isBookmarked); - this.CacheManager.ClearRegion(playlist.CacheRegion); + CacheManager.ClearRegion(playlist.CacheRegion); return new OperationResult { @@ -272,19 +245,13 @@ namespace Roadie.Api.Services public async Task> SetReleaseBookmark(Guid releaseid, User roadieUser, bool isBookmarked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - var release = this.GetRelease(releaseid); - if (release == null) - { - return new OperationResult(true, $"Invalid Release [{ releaseid }]"); - } - var result = await this.SetBookmark(user, Library.Enums.BookmarkType.Release, release.Id, isBookmarked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + var release = GetRelease(releaseid); + if (release == null) return new OperationResult(true, $"Invalid Release [{releaseid}]"); + var result = await SetBookmark(user, BookmarkType.Release, release.Id, isBookmarked); - this.CacheManager.ClearRegion(release.CacheRegion); + CacheManager.ClearRegion(release.CacheRegion); return new OperationResult { @@ -295,49 +262,34 @@ namespace Roadie.Api.Services public async Task> SetReleaseDisliked(Guid releaseId, User roadieUser, bool isDisliked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - return await base.ToggleReleaseDisliked(releaseId, user, isDisliked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + return await ToggleReleaseDisliked(releaseId, user, isDisliked); } public async Task> SetReleaseFavorite(Guid releaseId, User roadieUser, bool isFavorite) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - return await base.ToggleReleaseFavorite(releaseId, user, isFavorite); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + return await ToggleReleaseFavorite(releaseId, user, isFavorite); } public async Task> SetReleaseRating(Guid releaseId, User roadieUser, short rating) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); return await base.SetReleaseRating(releaseId, user, rating); } public async Task> SetTrackBookmark(Guid trackId, User roadieUser, bool isBookmarked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - var track = this.GetTrack(trackId); - if (track == null) - { - return new OperationResult(true, $"Invalid Track [{ trackId }]"); - } - var result = await this.SetBookmark(user, Library.Enums.BookmarkType.Track, track.Id, isBookmarked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + var track = GetTrack(trackId); + if (track == null) return new OperationResult(true, $"Invalid Track [{trackId}]"); + var result = await SetBookmark(user, BookmarkType.Track, track.Id, isBookmarked); - this.CacheManager.ClearRegion(track.CacheRegion); + CacheManager.ClearRegion(track.CacheRegion); return new OperationResult { @@ -348,137 +300,88 @@ namespace Roadie.Api.Services public async Task> SetTrackDisliked(Guid trackId, User roadieUser, bool isDisliked) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - return await base.ToggleTrackDisliked(trackId, user, isDisliked); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + return await ToggleTrackDisliked(trackId, user, isDisliked); } public async Task> SetTrackFavorite(Guid trackId, User roadieUser, bool isFavorite) { - var user = this.GetUser(roadieUser.UserId); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } - return await base.ToggleTrackFavorite(trackId, user, isFavorite); + var user = GetUser(roadieUser.UserId); + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); + return await ToggleTrackFavorite(trackId, user, isFavorite); } public async Task> SetTrackRating(Guid trackId, User roadieUser, short rating) { var timings = new Dictionary(); var sw = Stopwatch.StartNew(); - var user = this.GetUser(roadieUser.UserId); + var user = GetUser(roadieUser.UserId); sw.Stop(); timings.Add("GetUser", sw.ElapsedMilliseconds); - if (user == null) - { - return new OperationResult(true, $"Invalid User [{ roadieUser }]"); - } + if (user == null) return new OperationResult(true, $"Invalid User [{roadieUser}]"); sw.Start(); var result = await base.SetTrackRating(trackId, user, rating); sw.Stop(); timings.Add("SetTrackRating", sw.ElapsedMilliseconds); result.AdditionalData.Add("Timing", sw.ElapsedMilliseconds); - this.Logger.LogInformation($"User `{ roadieUser }` set rating [{ rating }] on TrackId [{ trackId }]. Result [{ JsonConvert.SerializeObject(result) }]"); + Logger.LogInformation( + $"User `{roadieUser}` set rating [{rating}] on TrackId [{trackId}]. Result [{JsonConvert.SerializeObject(result)}]"); return result; } - private async Task> UpdateLastFMSessionKey(ApplicationUser user, string token) + public async Task> UpdateIntegrationGrant(Guid userId, string integrationName, + string token) { - var lastFmSessionKeyResult = await this.LastFmHelper.GetSessionKeyForUserToken(token); - if(!lastFmSessionKeyResult.IsSuccess) - { - return new OperationResult(false, $"Unable to Get LastFM Session Key For Token [{ token }]"); - } - // Check concurrency stamp - if (user.ConcurrencyStamp != user.ConcurrencyStamp) - { - return new OperationResult - { - Errors = new List { new Exception("User data is stale.") } - }; - } - user.LastFMSessionKey = lastFmSessionKeyResult.Data; - user.LastUpdated = DateTime.UtcNow; - user.ConcurrencyStamp = Guid.NewGuid().ToString(); - await this.DbContext.SaveChangesAsync(); - - this.CacheManager.ClearRegion(ApplicationUser.CacheRegionUrn(user.RoadieId)); - - this.Logger.LogInformation($"User `{ user }` Updated LastFm SessionKey"); - - return new OperationResult - { - IsSuccess = true, - Data = true - }; - } - - public async Task> UpdateIntegrationGrant(Guid userId, string integrationName, string token) - { - var user = this.DbContext.Users.FirstOrDefault(x => x.RoadieId == userId); - if (user == null) - { - return new OperationResult(true, $"User Not Found [{ userId }]"); - } - if (integrationName == "lastfm") - { - return await this.UpdateLastFMSessionKey(user, token); - } + var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userId); + if (user == null) return new OperationResult(true, $"User Not Found [{userId}]"); + if (integrationName == "lastfm") return await UpdateLastFMSessionKey(user, token); throw new NotImplementedException(); } public async Task> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel) { - var user = this.DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId); + var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId); if (user == null) - { - return new OperationResult(true, string.Format("User Not Found [{0}]", userBeingUpdatedModel.UserId)); - } + return new OperationResult(true, + string.Format("User Not Found [{0}]", userBeingUpdatedModel.UserId)); if (user.Id != userPerformingUpdate.Id && !userPerformingUpdate.IsAdmin) - { return new OperationResult { Errors = new List { new Exception("Access Denied") } }; - } // Check concurrency stamp if (user.ConcurrencyStamp != userBeingUpdatedModel.ConcurrencyStamp) - { return new OperationResult { Errors = new List { new Exception("User data is stale.") } }; - } // Check that username (if changed) doesn't already exist if (user.UserName != userBeingUpdatedModel.UserName) { - var userByUsername = this.DbContext.Users.FirstOrDefault(x => x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper()); + var userByUsername = DbContext.Users.FirstOrDefault(x => + x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper()); if (userByUsername != null) - { return new OperationResult { Errors = new List { new Exception("Username already in use") } }; - } } + // Check that email (if changed) doesn't already exist if (user.Email != userBeingUpdatedModel.Email) { - var userByEmail = this.DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper()); + var userByEmail = + DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper()); if (userByEmail != null) - { return new OperationResult { Errors = new List { new Exception("Email already in use") } }; - } } + user.UserName = userBeingUpdatedModel.UserName; user.NormalizedUserName = userBeingUpdatedModel.UserName.ToUpper(); user.Email = userBeingUpdatedModel.Email; @@ -504,35 +407,36 @@ namespace Roadie.Api.Services { var imageData = ImageHelper.ImageDataFromUrl(userBeingUpdatedModel.AvatarData); if (imageData != null) - { - user.Avatar = ImageHelper.ResizeImage(imageData, this.Configuration.ThumbnailImageSize.Width, this.Configuration.ThumbnailImageSize.Height); - } + user.Avatar = ImageHelper.ResizeImage(imageData, Configuration.ThumbnailImageSize.Width, + Configuration.ThumbnailImageSize.Height); } - await this.DbContext.SaveChangesAsync(); - if (!string.IsNullOrEmpty(userBeingUpdatedModel.Password) && !string.IsNullOrEmpty(userBeingUpdatedModel.PasswordConfirmation)) + await DbContext.SaveChangesAsync(); + + if (!string.IsNullOrEmpty(userBeingUpdatedModel.Password) && + !string.IsNullOrEmpty(userBeingUpdatedModel.PasswordConfirmation)) { if (userBeingUpdatedModel.Password != userBeingUpdatedModel.PasswordConfirmation) - { return new OperationResult { Errors = new List { new Exception("Password does not match confirmation") } }; - } - string resetToken = await UserManager.GeneratePasswordResetTokenAsync(user); - var identityResult = await UserManager.ResetPasswordAsync(user, resetToken, userBeingUpdatedModel.Password); + var resetToken = await UserManager.GeneratePasswordResetTokenAsync(user); + var identityResult = + await UserManager.ResetPasswordAsync(user, resetToken, userBeingUpdatedModel.Password); if (!identityResult.Succeeded) - { return new OperationResult { - Errors = identityResult.Errors != null ? identityResult.Errors.Select(x => new Exception($"Code [{ x.Code }], Description [{ x.Description }]")) : new List { new Exception("Unable to reset password") } + Errors = identityResult.Errors != null + ? identityResult.Errors.Select(x => + new Exception($"Code [{x.Code}], Description [{x.Description}]")) + : new List { new Exception("Unable to reset password") } }; - } } - this.CacheManager.ClearRegion(ApplicationUser.CacheRegionUrn(user.RoadieId)); + CacheManager.ClearRegion(ApplicationUser.CacheRegionUrn(user.RoadieId)); - this.Logger.LogInformation($"User `{ userPerformingUpdate }` modifed user `{ userBeingUpdatedModel }`"); + Logger.LogInformation($"User `{userPerformingUpdate}` modifed user `{userBeingUpdatedModel}`"); return new OperationResult { @@ -541,18 +445,19 @@ namespace Roadie.Api.Services }; } - private async Task> SetBookmark(ApplicationUser user, Library.Enums.BookmarkType bookmarktype, int bookmarkTargetId, bool isBookmarked) + private async Task> SetBookmark(ApplicationUser user, BookmarkType bookmarktype, + int bookmarkTargetId, bool isBookmarked) { - var bookmark = this.DbContext.Bookmarks.FirstOrDefault(x => x.BookmarkTargetId == bookmarkTargetId && - x.BookmarkType == bookmarktype && - x.UserId == user.Id); + var bookmark = DbContext.Bookmarks.FirstOrDefault(x => x.BookmarkTargetId == bookmarkTargetId && + x.BookmarkType == bookmarktype && + x.UserId == user.Id); if (!isBookmarked) { // Remove bookmark if (bookmark != null) { - this.DbContext.Bookmarks.Remove(bookmark); - await this.DbContext.SaveChangesAsync(); + DbContext.Bookmarks.Remove(bookmark); + await DbContext.SaveChangesAsync(); } } else @@ -560,19 +465,46 @@ namespace Roadie.Api.Services // Add bookmark if (bookmark == null) { - this.DbContext.Bookmarks.Add(new data.Bookmark + DbContext.Bookmarks.Add(new data.Bookmark { UserId = user.Id, BookmarkTargetId = bookmarkTargetId, BookmarkType = bookmarktype, CreatedDate = DateTime.UtcNow, - Status = Library.Enums.Statuses.Ok + Status = Statuses.Ok }); - await this.DbContext.SaveChangesAsync(); + await DbContext.SaveChangesAsync(); } } - this.CacheManager.ClearRegion(user.CacheRegion); + CacheManager.ClearRegion(user.CacheRegion); + + return new OperationResult + { + IsSuccess = true, + Data = true + }; + } + + private async Task> UpdateLastFMSessionKey(ApplicationUser user, string token) + { + var lastFmSessionKeyResult = await LastFmHelper.GetSessionKeyForUserToken(token); + if (!lastFmSessionKeyResult.IsSuccess) + return new OperationResult(false, $"Unable to Get LastFM Session Key For Token [{token}]"); + // Check concurrency stamp + if (user.ConcurrencyStamp != user.ConcurrencyStamp) + return new OperationResult + { + Errors = new List { new Exception("User data is stale.") } + }; + user.LastFMSessionKey = lastFmSessionKeyResult.Data; + user.LastUpdated = DateTime.UtcNow; + user.ConcurrencyStamp = Guid.NewGuid().ToString(); + await DbContext.SaveChangesAsync(); + + CacheManager.ClearRegion(ApplicationUser.CacheRegionUrn(user.RoadieId)); + + Logger.LogInformation($"User `{user}` Updated LastFm SessionKey"); return new OperationResult { @@ -583,83 +515,98 @@ namespace Roadie.Api.Services private Task> UserByIdAction(Guid id, IEnumerable includes) { - var user = this.GetUser(id); + var user = GetUser(id); if (user == null) - { return Task.FromResult(new OperationResult(true, string.Format("User Not Found [{0}]", id))); - } var model = user.Adapt(); if (includes != null && includes.Any()) - { if (includes.Contains("stats")) { - var userArtists = this.DbContext.UserArtists.Include(x => x.Artist).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserArtist[0]; - var userReleases = this.DbContext.UserReleases.Include(x => x.Release).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserRelease[0]; - var userTracks = this.DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserTrack[0]; + var userArtists = + DbContext.UserArtists.Include(x => x.Artist).Where(x => x.UserId == user.Id).ToArray() ?? + new data.UserArtist[0]; + var userReleases = + DbContext.UserReleases.Include(x => x.Release).Where(x => x.UserId == user.Id).ToArray() ?? + new data.UserRelease[0]; + var userTracks = + DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == user.Id).ToArray() ?? + new data.UserTrack[0]; - var mostPlayedArtist = (from a in this.DbContext.Artists - join r in this.DbContext.Releases on a.Id equals r.ArtistId - join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId - join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId - join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId + var mostPlayedArtist = (from a in DbContext.Artists + join r in DbContext.Releases on a.Id equals r.ArtistId + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId + join ut in DbContext.UserTracks on t.Id equals ut.TrackId where ut.UserId == user.Id select new { a, ut.PlayedCount }) - .GroupBy(a => a.a) - .Select(x => new - { - Artist = x.Key, - Played = x.Sum(t => t.PlayedCount) - }) - .OrderByDescending(x => x.Played) - .FirstOrDefault(); + .GroupBy(a => a.a) + .Select(x => new + { + Artist = x.Key, + Played = x.Sum(t => t.PlayedCount) + }) + .OrderByDescending(x => x.Played) + .FirstOrDefault(); - var mostPlayedReleaseId = (from r in this.DbContext.Releases - join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId - join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId - join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId + var mostPlayedReleaseId = (from r in DbContext.Releases + join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId + join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId + join ut in DbContext.UserTracks on t.Id equals ut.TrackId where ut.UserId == user.Id select new { r, ut.PlayedCount }) - .GroupBy(r => r.r) - .Select(x => new - { - Release = x.Key, - Played = x.Sum(t => t.PlayedCount) - }) - .OrderByDescending(x => x.Played) - .Select(x => x.Release.RoadieId) - .FirstOrDefault(); + .GroupBy(r => r.r) + .Select(x => new + { + Release = x.Key, + Played = x.Sum(t => t.PlayedCount) + }) + .OrderByDescending(x => x.Played) + .Select(x => x.Release.RoadieId) + .FirstOrDefault(); - var mostPlayedRelease = this.GetRelease(mostPlayedReleaseId); + var mostPlayedRelease = GetRelease(mostPlayedReleaseId); var mostPlayedTrackUserTrack = userTracks - .OrderByDescending(x => x.PlayedCount) - .FirstOrDefault(); - var mostPlayedTrack = mostPlayedTrackUserTrack == null ? null : this.DbContext.Tracks - .Include(x => x.TrackArtist) - .Include(x => x.ReleaseMedia) - .Include("ReleaseMedia.Release") - .Include("ReleaseMedia.Release.Artist") - .FirstOrDefault(x => x.Id == mostPlayedTrackUserTrack.TrackId); + .OrderByDescending(x => x.PlayedCount) + .FirstOrDefault(); + var mostPlayedTrack = mostPlayedTrackUserTrack == null + ? null + : DbContext.Tracks + .Include(x => x.TrackArtist) + .Include(x => x.ReleaseMedia) + .Include("ReleaseMedia.Release") + .Include("ReleaseMedia.Release.Artist") + .FirstOrDefault(x => x.Id == mostPlayedTrackUserTrack.TrackId); model.Statistics = new UserStatistics { - MostPlayedArtist = mostPlayedArtist == null ? null : models.ArtistList.FromDataArtist(mostPlayedArtist.Artist, this.MakeArtistThumbnailImage(mostPlayedArtist.Artist.RoadieId)), - MostPlayedRelease = mostPlayedRelease == null ? null : models.Releases.ReleaseList.FromDataRelease(mostPlayedRelease, - mostPlayedRelease.Artist, - this.HttpContext.BaseUrl, - this.MakeArtistThumbnailImage(mostPlayedRelease.Artist.RoadieId), - this.MakeReleaseThumbnailImage(mostPlayedRelease.RoadieId)), - MostPlayedTrack = mostPlayedTrack == null ? null : TrackList.FromDataTrack(this.MakeTrackPlayUrl(user, mostPlayedTrack.Id, mostPlayedTrack.RoadieId), - mostPlayedTrack, - mostPlayedTrack.ReleaseMedia.MediaNumber, - mostPlayedTrack.ReleaseMedia.Release, - mostPlayedTrack.ReleaseMedia.Release.Artist, - mostPlayedTrack.TrackArtist, - this.HttpContext.BaseUrl, - this.MakeTrackThumbnailImage(mostPlayedTrack.RoadieId), - this.MakeReleaseThumbnailImage(mostPlayedTrack.ReleaseMedia.Release.RoadieId), - this.MakeArtistThumbnailImage(mostPlayedTrack.ReleaseMedia.Release.Artist.RoadieId), - this.MakeArtistThumbnailImage(mostPlayedTrack.TrackArtist == null ? null : (Guid?)mostPlayedTrack.TrackArtist.RoadieId)), + MostPlayedArtist = mostPlayedArtist == null + ? null + : models.ArtistList.FromDataArtist(mostPlayedArtist.Artist, + MakeArtistThumbnailImage(mostPlayedArtist.Artist.RoadieId)), + MostPlayedRelease = mostPlayedRelease == null + ? null + : ReleaseList.FromDataRelease(mostPlayedRelease, + mostPlayedRelease.Artist, + HttpContext.BaseUrl, + MakeArtistThumbnailImage(mostPlayedRelease.Artist.RoadieId), + MakeReleaseThumbnailImage(mostPlayedRelease.RoadieId)), + MostPlayedTrack = mostPlayedTrack == null + ? null + : models.TrackList.FromDataTrack( + MakeTrackPlayUrl(user, mostPlayedTrack.Id, mostPlayedTrack.RoadieId), + mostPlayedTrack, + mostPlayedTrack.ReleaseMedia.MediaNumber, + mostPlayedTrack.ReleaseMedia.Release, + mostPlayedTrack.ReleaseMedia.Release.Artist, + mostPlayedTrack.TrackArtist, + HttpContext.BaseUrl, + MakeTrackThumbnailImage(mostPlayedTrack.RoadieId), + MakeReleaseThumbnailImage(mostPlayedTrack.ReleaseMedia.Release.RoadieId), + MakeArtistThumbnailImage(mostPlayedTrack.ReleaseMedia.Release.Artist.RoadieId), + MakeArtistThumbnailImage(mostPlayedTrack.TrackArtist == null + ? null + : (Guid?)mostPlayedTrack.TrackArtist.RoadieId)), RatedArtists = userArtists.Where(x => x.Rating > 0).Count(), FavoritedArtists = userArtists.Where(x => x.IsFavorite ?? false).Count(), DislikedArtists = userArtists.Where(x => x.IsDisliked ?? false).Count(), @@ -672,7 +619,6 @@ namespace Roadie.Api.Services DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count() }; } - } return Task.FromResult(new OperationResult { diff --git a/Roadie.Api/Roadie.Api.csproj b/Roadie.Api/Roadie.Api.csproj index 78291c6..8ecf850 100644 --- a/Roadie.Api/Roadie.Api.csproj +++ b/Roadie.Api/Roadie.Api.csproj @@ -29,11 +29,11 @@ - + - + diff --git a/Roadie.sln b/Roadie.sln index 8f17a02..f0ebf86 100644 --- a/Roadie.sln +++ b/Roadie.sln @@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{1BA7 roadie.sql = roadie.sql Upgrade0001.sql = Upgrade0001.sql Upgrade0002.sql = Upgrade0002.sql + Upgrade0003.sql = Upgrade0003.sql EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roadie.Api.Services", "Roadie.Api.Services\Roadie.Api.Services.csproj", "{7B37031E-F2AE-4BE2-9F6F-005CA7A6FDF1}" diff --git a/Upgrade0003.sql b/Upgrade0003.sql index e69de29..084907b 100644 --- a/Upgrade0003.sql +++ b/Upgrade0003.sql @@ -0,0 +1,11 @@ +-- Create collection missing table for < 1.0.2.0 database +CREATE TABLE `collectionMissing` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `collectionId` int(11) NOT NULL, + `isArtistFound` tinyint(1) DEFAULT NULL, + `position` int(11) NOT NULL, + `artist` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL, + `release` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `ix_collection_collectionId` (`collectionId`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/roadie.sql b/roadie.sql index 58bd730..80a01c2 100644 --- a/roadie.sql +++ b/roadie.sql @@ -1,7 +1,7 @@ -- /// --- Roadie version 1.0.1.0 new database script, if upgrading skip this and run Upgrade*.sql scripts from your version to current. +-- Roadie version 1.0.2.0 new database script, if upgrading skip this and run Upgrade*.sql scripts from your version to current. -- /// --- + -- MySQL dump 10.17 Distrib 10.3.16-MariaDB, for Linux (x86_64) -- -- Host: localhost Database: roadie_dev @@ -186,6 +186,23 @@ CREATE TABLE `collection` ( KEY `maintainerId` (`maintainerId`), KEY `ix_collection_roadieId` (`roadieId`), CONSTRAINT `collection_ibfk_1` FOREIGN KEY (`maintainerId`) REFERENCES `user` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `collectionMissing` +-- + +DROP TABLE IF EXISTS `collectionMissing`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `collectionMissing` ( + `collectionId` int(11) NOT NULL, + `isArtistFound` tinyint(1) DEFAULT NULL, + `position` int(11) NOT NULL, + `artist` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL, + `release` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL, + KEY `ix_collection_collectionId` (`collectionId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -212,7 +229,7 @@ CREATE TABLE `collectionrelease` ( KEY `ix_collectionrelease_roadieId` (`roadieId`), CONSTRAINT `collectionrelease_ibfk_1` FOREIGN KEY (`releaseId`) REFERENCES `release` (`id`) ON DELETE CASCADE, CONSTRAINT `collectionrelease_ibfk_2` FOREIGN KEY (`collectionId`) REFERENCES `collection` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -256,7 +273,7 @@ CREATE TABLE `comment` ( CONSTRAINT `commentrelease_ibfk_1` FOREIGN KEY (`releaseId`) REFERENCES `release` (`id`) ON DELETE CASCADE, CONSTRAINT `commenttrack_ibfk_1` FOREIGN KEY (`trackId`) REFERENCES `track` (`id`) ON DELETE CASCADE, CONSTRAINT `commentuser_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -282,7 +299,7 @@ CREATE TABLE `commentReaction` ( KEY `commentReactioncomment_ibfk_1` (`commentId`), CONSTRAINT `commentReactioncomment_ibfk_1` FOREIGN KEY (`commentId`) REFERENCES `comment` (`id`) ON DELETE CASCADE, CONSTRAINT `commentReactionuser_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1014,4 +1031,4 @@ SET character_set_client = @saved_cs_client; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2019-06-25 15:15:52 +-- Dump completed on 2019-06-30 8:12:31