mirror of
https://github.com/sphildreth/roadie
synced 2024-11-12 23:47:07 +00:00
Work for Roadie-VueJS #63
This commit is contained in:
parent
ed0aa051d6
commit
7710fef6b0
37 changed files with 984 additions and 446 deletions
|
@ -21,6 +21,7 @@ namespace Roadie.Library.Configuration
|
||||||
string ImageFolder { get; set; }
|
string ImageFolder { get; set; }
|
||||||
string LabelImageFolder { get; }
|
string LabelImageFolder { get; }
|
||||||
string CollectionImageFolder { get; }
|
string CollectionImageFolder { get; }
|
||||||
|
string GenreImageFolder { get; }
|
||||||
string PlaylistImageFolder { get; }
|
string PlaylistImageFolder { get; }
|
||||||
string UserImageFolder { get; }
|
string UserImageFolder { get; }
|
||||||
string ListenAddress { get; set; }
|
string ListenAddress { get; set; }
|
||||||
|
@ -47,5 +48,6 @@ namespace Roadie.Library.Configuration
|
||||||
bool IsRegistrationClosed { get; set; }
|
bool IsRegistrationClosed { get; set; }
|
||||||
bool UseRegistrationTokens { get; set; }
|
bool UseRegistrationTokens { get; set; }
|
||||||
string SearchEngineReposFolder { get; set; }
|
string SearchEngineReposFolder { get; set; }
|
||||||
|
short DefaultRowsPerPage { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -63,6 +63,14 @@ namespace Roadie.Library.Configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GenreImageFolder
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Path.Combine(ImageFolder ?? LibraryFolder, "__roadie_images", "genres");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string PlaylistImageFolder
|
public string PlaylistImageFolder
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -133,6 +141,8 @@ namespace Roadie.Library.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SearchEngineReposFolder { get; set; }
|
public string SearchEngineReposFolder { get; set; }
|
||||||
|
|
||||||
|
public short DefaultRowsPerPage { get; set; }
|
||||||
|
|
||||||
public RoadieSettings()
|
public RoadieSettings()
|
||||||
{
|
{
|
||||||
ArtistNameReplace = new Dictionary<string, IEnumerable<string>>
|
ArtistNameReplace = new Dictionary<string, IEnumerable<string>>
|
||||||
|
@ -152,6 +162,7 @@ namespace Roadie.Library.Configuration
|
||||||
SiteName = "Roadie";
|
SiteName = "Roadie";
|
||||||
SmallImageSize = new ImageSize { Width = 160, Height = 160 };
|
SmallImageSize = new ImageSize { Width = 160, Height = 160 };
|
||||||
ThumbnailImageSize = new ImageSize { Width = 80, Height = 80 };
|
ThumbnailImageSize = new ImageSize { Width = 80, Height = 80 };
|
||||||
|
DefaultRowsPerPage = 12;
|
||||||
|
|
||||||
SmtpFromAddress = "noreply@roadie.rocks";
|
SmtpFromAddress = "noreply@roadie.rocks";
|
||||||
SmtpPort = 587;
|
SmtpPort = 587;
|
||||||
|
|
|
@ -13,6 +13,22 @@ namespace Roadie.Library.Data
|
||||||
|
|
||||||
[Column("name")] [MaxLength(100)] public string Name { get; set; }
|
[Column("name")] [MaxLength(100)] public string Name { get; set; }
|
||||||
|
|
||||||
|
[Column("description")]
|
||||||
|
[MaxLength(4000)]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[Column("alternateNames", TypeName = "text")]
|
||||||
|
[MaxLength(65535)]
|
||||||
|
public string AlternateNames { get; set; }
|
||||||
|
|
||||||
|
[Column("tags", TypeName = "text")]
|
||||||
|
[MaxLength(65535)]
|
||||||
|
public string Tags { get; set; }
|
||||||
|
|
||||||
|
[Column("thumbnail", TypeName = "blob")]
|
||||||
|
[MaxLength(65535)]
|
||||||
|
public byte[] Thumbnail { get; set; }
|
||||||
|
|
||||||
[Column("normalizedName")] [MaxLength(100)] public string NormalizedName { get; set; }
|
[Column("normalizedName")] [MaxLength(100)] public string NormalizedName { get; set; }
|
||||||
|
|
||||||
public ICollection<ReleaseGenre> Releases { get; set; }
|
public ICollection<ReleaseGenre> Releases { get; set; }
|
||||||
|
@ -22,6 +38,7 @@ namespace Roadie.Library.Data
|
||||||
Releases = new HashSet<ReleaseGenre>();
|
Releases = new HashSet<ReleaseGenre>();
|
||||||
Artists = new HashSet<ArtistGenre>();
|
Artists = new HashSet<ArtistGenre>();
|
||||||
Comments = new HashSet<Comment>();
|
Comments = new HashSet<Comment>();
|
||||||
|
Status = Enums.Statuses.Ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
using System;
|
using Roadie.Library.Configuration;
|
||||||
|
using Roadie.Library.Extensions;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Roadie.Library.Data
|
namespace Roadie.Library.Data
|
||||||
{
|
{
|
||||||
|
@ -8,6 +11,14 @@ namespace Roadie.Library.Data
|
||||||
|
|
||||||
public string CacheRegion => CacheRegionUrn(RoadieId);
|
public string CacheRegion => CacheRegionUrn(RoadieId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a full file path to the Genre Image
|
||||||
|
/// </summary>
|
||||||
|
public string PathToImage(IRoadieSettings configuration)
|
||||||
|
{
|
||||||
|
return Path.Combine(configuration.GenreImageFolder, $"{ Name.ToFileNameFriendly() } [{ Id }].jpg");
|
||||||
|
}
|
||||||
|
|
||||||
public static string CacheRegionUrn(Guid Id)
|
public static string CacheRegionUrn(Guid Id)
|
||||||
{
|
{
|
||||||
return string.Format("urn:genre:{0}", Id);
|
return string.Format("urn:genre:{0}", Id);
|
||||||
|
|
|
@ -118,6 +118,9 @@ namespace Roadie.Library.Identity
|
||||||
[StringLength(50)]
|
[StringLength(50)]
|
||||||
public string Timezone { get; set; }
|
public string Timezone { get; set; }
|
||||||
|
|
||||||
|
[Column("defaultRowsPerPage")]
|
||||||
|
public short? DefaultRowsPerPage { get; set; }
|
||||||
|
|
||||||
public ICollection<UserTrack> TrackRatings { get; set; }
|
public ICollection<UserTrack> TrackRatings { get; set; }
|
||||||
public ICollection<UserQue> UserQues { get; set; }
|
public ICollection<UserQue> UserQues { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace Roadie.Library.Imaging
|
||||||
private Image _artist;
|
private Image _artist;
|
||||||
private Image _collection;
|
private Image _collection;
|
||||||
private Image _label;
|
private Image _label;
|
||||||
|
private Image _genre;
|
||||||
private Image _playlist;
|
private Image _playlist;
|
||||||
private Image _release;
|
private Image _release;
|
||||||
private Image _track;
|
private Image _track;
|
||||||
|
@ -23,6 +24,8 @@ namespace Roadie.Library.Imaging
|
||||||
|
|
||||||
public Image Label => _label ?? (_label = MakeImageFromFile(MakeImagePath(@"images/label.jpg")));
|
public Image Label => _label ?? (_label = MakeImageFromFile(MakeImagePath(@"images/label.jpg")));
|
||||||
|
|
||||||
|
public Image Genre => _genre ?? (_genre = MakeImageFromFile(MakeImagePath(@"images/genre.jpg")));
|
||||||
|
|
||||||
public Image Playlist => _playlist ?? (_playlist = MakeImageFromFile(MakeImagePath(@"images/playlist.jpg")));
|
public Image Playlist => _playlist ?? (_playlist = MakeImageFromFile(MakeImagePath(@"images/playlist.jpg")));
|
||||||
|
|
||||||
public Image Release => _release ?? (_release = MakeImageFromFile(MakeImagePath(@"images/release.jpg")));
|
public Image Release => _release ?? (_release = MakeImageFromFile(MakeImagePath(@"images/release.jpg")));
|
||||||
|
|
|
@ -7,6 +7,7 @@ namespace Roadie.Library.Imaging
|
||||||
Image Artist { get; }
|
Image Artist { get; }
|
||||||
Image Collection { get; }
|
Image Collection { get; }
|
||||||
Image Label { get; }
|
Image Label { get; }
|
||||||
|
Image Genre { get; }
|
||||||
Image Playlist { get; }
|
Image Playlist { get; }
|
||||||
Image Release { get; }
|
Image Release { get; }
|
||||||
Image Track { get; }
|
Image Track { get; }
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace Roadie.Library.Models
|
||||||
set => _urlsList = value;
|
set => _urlsList = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UserBookmarked { get; set; }
|
public bool? UserBookmarked { get; set; }
|
||||||
|
|
||||||
public EntityModelBase()
|
public EntityModelBase()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using Roadie.Library.Models.Statistics;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
@ -7,7 +8,26 @@ namespace Roadie.Library.Models
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class Genre : EntityModelBase
|
public class Genre : EntityModelBase
|
||||||
{
|
{
|
||||||
|
public const string DefaultIncludes = "stats";
|
||||||
|
|
||||||
|
public Image Thumbnail { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(4000)] public string Description { get; set; }
|
||||||
|
|
||||||
public IEnumerable<Comment> Comments { get; set; }
|
public IEnumerable<Comment> Comments { get; set; }
|
||||||
[MaxLength(100)] public string Name { get; set; }
|
[MaxLength(100)] public string Name { get; set; }
|
||||||
|
[MaxLength(100)] public string NormalizedName { get; set; }
|
||||||
|
public ReleaseGroupingStatistics Statistics { get; set; }
|
||||||
|
|
||||||
|
public static string CacheRegionUrn(Guid Id)
|
||||||
|
{
|
||||||
|
return string.Format("urn:genre:{0}", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CacheUrn(Guid Id)
|
||||||
|
{
|
||||||
|
return $"urn:genre_by_id:{Id}";
|
||||||
|
}
|
||||||
|
public Image MediumThumbnail { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,5 +8,6 @@ namespace Roadie.Library.Models
|
||||||
public int? ArtistCount { get; set; }
|
public int? ArtistCount { get; set; }
|
||||||
public DataToken Genre { get; set; }
|
public DataToken Genre { get; set; }
|
||||||
public int? ReleaseCount { get; set; }
|
public int? ReleaseCount { get; set; }
|
||||||
|
public Image Thumbnail { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,6 +34,7 @@ namespace Roadie.Library.Models.Pagination
|
||||||
public Statuses FilterToStatusValue => SafeParser.ToEnum<Statuses>(FilterToStatus);
|
public Statuses FilterToStatusValue => SafeParser.ToEnum<Statuses>(FilterToStatus);
|
||||||
public Guid? FilterToTrackId { get; set; }
|
public Guid? FilterToTrackId { get; set; }
|
||||||
public Guid?[] FilterToTrackIds { get; set; }
|
public Guid?[] FilterToTrackIds { get; set; }
|
||||||
|
public Guid? FilterToGenreId { get; set; }
|
||||||
public int? FilterToYear { get; set; }
|
public int? FilterToYear { get; set; }
|
||||||
public string FilterValue => Filter ?? string.Empty;
|
public string FilterValue => Filter ?? string.Empty;
|
||||||
public bool IsHistoryRequest { get; set; }
|
public bool IsHistoryRequest { get; set; }
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace Roadie.Library.Models.Statistics
|
||||||
public ArtistList MostPlayedArtist { get; set; }
|
public ArtistList MostPlayedArtist { get; set; }
|
||||||
public ReleaseList MostPlayedRelease { get; set; }
|
public ReleaseList MostPlayedRelease { get; set; }
|
||||||
public TrackList MostPlayedTrack { get; set; }
|
public TrackList MostPlayedTrack { get; set; }
|
||||||
|
public TrackList LastPlayedTrack { get; set; }
|
||||||
|
|
||||||
public int? PlayedTracks { get; set; }
|
public int? PlayedTracks { get; set; }
|
||||||
public int? RatedArtists { get; set; }
|
public int? RatedArtists { get; set; }
|
||||||
public int? RatedReleases { get; set; }
|
public int? RatedReleases { get; set; }
|
||||||
|
|
|
@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||||
namespace Roadie.Library.Models.Users
|
namespace Roadie.Library.Models.Users
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class User
|
public class User : EntityModelBase
|
||||||
{
|
{
|
||||||
public const string ActionKeyUserRated = "__userrated__";
|
public const string ActionKeyUserRated = "__userrated__";
|
||||||
public const string DefaultIncludes = "stats";
|
public const string DefaultIncludes = "stats";
|
||||||
|
@ -69,6 +69,10 @@ namespace Roadie.Library.Models.Users
|
||||||
[Required] [MaxLength(20)] public string UserName { get; set; }
|
[Required] [MaxLength(20)] public string UserName { get; set; }
|
||||||
public Image MediumThumbnail { get; set; }
|
public Image MediumThumbnail { get; set; }
|
||||||
|
|
||||||
|
public DateTime LastLogin { get; set; }
|
||||||
|
public DateTime LastApiAccess { get; set; }
|
||||||
|
public short? DefaultRowsPerPage { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"Id [{Id}], RoadieId [{UserId}], UserName [{UserName}]";
|
return $"Id [{Id}], RoadieId [{UserId}], UserName [{UserName}]";
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace Roadie.Library.Models.Users
|
||||||
public class UserList : EntityInfoModelBase
|
public class UserList : EntityInfoModelBase
|
||||||
{
|
{
|
||||||
public bool IsEditor { get; set; }
|
public bool IsEditor { get; set; }
|
||||||
|
public bool IsAdmin { get; set; }
|
||||||
public bool? IsPrivate { get; set; }
|
public bool? IsPrivate { get; set; }
|
||||||
public DateTime? LastActivity { get; set; }
|
public DateTime? LastActivity { get; set; }
|
||||||
public DateTime? LastApiAccessDate { get; set; }
|
public DateTime? LastApiAccessDate { get; set; }
|
||||||
|
@ -31,13 +32,15 @@ namespace Roadie.Library.Models.Users
|
||||||
Value = user.RoadieId.ToString()
|
Value = user.RoadieId.ToString()
|
||||||
},
|
},
|
||||||
IsEditor = user.UserRoles.Any(x => x.Role.Name == "Editor"),
|
IsEditor = user.UserRoles.Any(x => x.Role.Name == "Editor"),
|
||||||
|
IsAdmin = user.UserRoles.Any(x => x.Role.Name == "Admin"),
|
||||||
IsPrivate = user.IsPrivate,
|
IsPrivate = user.IsPrivate,
|
||||||
Thumbnail = thumbnail,
|
Thumbnail = thumbnail,
|
||||||
CreatedDate = user.CreatedDate,
|
CreatedDate = user.CreatedDate,
|
||||||
LastUpdated = user.LastUpdated,
|
LastUpdated = user.LastUpdated,
|
||||||
RegisteredDate = user.RegisteredOn,
|
RegisteredDate = user.RegisteredOn,
|
||||||
LastLoginDate = user.LastLogin,
|
LastLoginDate = user.LastLogin,
|
||||||
LastApiAccessDate = user.LastApiAccess
|
LastApiAccessDate = user.LastApiAccess,
|
||||||
|
Statistics = new UserStatistics()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,12 @@ namespace Roadie.Api.Services
|
||||||
private IReleaseService ReleaseService { get; }
|
private IReleaseService ReleaseService { get; }
|
||||||
|
|
||||||
private ILabelService LabelService { get; }
|
private ILabelService LabelService { get; }
|
||||||
|
private IGenreService GenreService { get; }
|
||||||
|
|
||||||
public AdminService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext,
|
public AdminService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext,
|
||||||
data.IRoadieDbContext context, ICacheManager cacheManager, ILogger<ArtistService> logger,
|
data.IRoadieDbContext context, ICacheManager cacheManager, ILogger<ArtistService> logger,
|
||||||
IHubContext<ScanActivityHub> scanActivityHub, IFileDirectoryProcessorService fileDirectoryProcessorService, IArtistService artistService,
|
IHubContext<ScanActivityHub> scanActivityHub, IFileDirectoryProcessorService fileDirectoryProcessorService, IArtistService artistService,
|
||||||
IReleaseService releaseService, IReleaseLookupEngine releaseLookupEngine, ILabelService labelService
|
IReleaseService releaseService, IReleaseLookupEngine releaseLookupEngine, ILabelService labelService, IGenreService genreService
|
||||||
)
|
)
|
||||||
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
||||||
{
|
{
|
||||||
|
@ -57,6 +58,7 @@ namespace Roadie.Api.Services
|
||||||
ArtistService = artistService;
|
ArtistService = artistService;
|
||||||
ReleaseService = releaseService;
|
ReleaseService = releaseService;
|
||||||
LabelService = labelService;
|
LabelService = labelService;
|
||||||
|
GenreService = genreService;
|
||||||
ReleaseLookupEngine = releaseLookupEngine;
|
ReleaseLookupEngine = releaseLookupEngine;
|
||||||
FileDirectoryProcessorService = fileDirectoryProcessorService;
|
FileDirectoryProcessorService = fileDirectoryProcessorService;
|
||||||
}
|
}
|
||||||
|
@ -158,6 +160,11 @@ namespace Roadie.Api.Services
|
||||||
Directory.CreateDirectory(Configuration.UserImageFolder);
|
Directory.CreateDirectory(Configuration.UserImageFolder);
|
||||||
Logger.LogInformation($"Created User Image Folder [{Configuration.UserImageFolder }]");
|
Logger.LogInformation($"Created User Image Folder [{Configuration.UserImageFolder }]");
|
||||||
}
|
}
|
||||||
|
if (!Directory.Exists(Configuration.GenreImageFolder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Configuration.GenreImageFolder);
|
||||||
|
Logger.LogInformation($"Created Genre Image Folder [{Configuration.GenreImageFolder }]");
|
||||||
|
}
|
||||||
if (!Directory.Exists(Configuration.PlaylistImageFolder))
|
if (!Directory.Exists(Configuration.PlaylistImageFolder))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Configuration.PlaylistImageFolder);
|
Directory.CreateDirectory(Configuration.PlaylistImageFolder);
|
||||||
|
@ -264,7 +271,7 @@ namespace Roadie.Api.Services
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError(ex);
|
Logger.LogError(ex);
|
||||||
await LogAndPublish("Error deleting artist secondary image.");
|
await LogAndPublish("Error deleting Label.");
|
||||||
errors.Add(ex);
|
errors.Add(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +286,42 @@ namespace Roadie.Api.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<OperationResult<bool>> DeleteGenre(ApplicationUser user, Guid genreId)
|
||||||
|
{
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
var errors = new List<Exception>();
|
||||||
|
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId);
|
||||||
|
if (genre == null)
|
||||||
|
{
|
||||||
|
await LogAndPublish($"DeleteLabel Unknown Genre [{genreId}]", LogLevel.Warning);
|
||||||
|
return new OperationResult<bool>(true, $"Genre Not Found [{genreId}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await GenreService.Delete(user, genreId);
|
||||||
|
CacheManager.ClearRegion(genre.CacheRegion);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex);
|
||||||
|
await LogAndPublish("Error deleting Genre.");
|
||||||
|
errors.Add(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
await LogAndPublish($"DeleteGenre `{genre}`, By User `{user}`", LogLevel.Information);
|
||||||
|
return new OperationResult<bool>
|
||||||
|
{
|
||||||
|
IsSuccess = !errors.Any(),
|
||||||
|
Data = true,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds,
|
||||||
|
Errors = errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index)
|
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index)
|
||||||
{
|
{
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
|
|
|
@ -21,7 +21,6 @@ using Roadie.Library.Models.Pagination;
|
||||||
using Roadie.Library.Models.Releases;
|
using Roadie.Library.Models.Releases;
|
||||||
using Roadie.Library.Models.Statistics;
|
using Roadie.Library.Models.Statistics;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
using Roadie.Library.Processors;
|
|
||||||
using Roadie.Library.Utility;
|
using Roadie.Library.Utility;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -45,6 +44,7 @@ namespace Roadie.Api.Services
|
||||||
|
|
||||||
private ICollectionService CollectionService { get; }
|
private ICollectionService CollectionService { get; }
|
||||||
|
|
||||||
|
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
|
||||||
private IFileNameHelper FileNameHelper { get; }
|
private IFileNameHelper FileNameHelper { get; }
|
||||||
|
|
||||||
private IID3TagsHelper ID3TagsHelper { get; }
|
private IID3TagsHelper ID3TagsHelper { get; }
|
||||||
|
@ -61,9 +61,7 @@ namespace Roadie.Api.Services
|
||||||
|
|
||||||
private IReleaseService ReleaseService { get; }
|
private IReleaseService ReleaseService { get; }
|
||||||
|
|
||||||
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
|
public ArtistService(IRoadieSettings configuration,
|
||||||
|
|
||||||
public ArtistService(IRoadieSettings configuration,
|
|
||||||
IHttpEncoder httpEncoder,
|
IHttpEncoder httpEncoder,
|
||||||
IHttpContext httpContext,
|
IHttpContext httpContext,
|
||||||
data.IRoadieDbContext dbContext,
|
data.IRoadieDbContext dbContext,
|
||||||
|
@ -214,65 +212,52 @@ namespace Roadie.Api.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private OperationResult<data.Artist> GetByExternalIds(string musicBrainzId = null, string iTunesId = null, string amgId = null, string spotifyId = null)
|
public async Task<Library.Models.Pagination.PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true)
|
||||||
{
|
|
||||||
var sw = new Stopwatch();
|
|
||||||
sw.Start();
|
|
||||||
var artist = (from a in DbContext.Artists
|
|
||||||
where a.MusicBrainzId != null && musicBrainzId != null && a.MusicBrainzId == musicBrainzId ||
|
|
||||||
a.ITunesId != null || iTunesId != null && a.ITunesId == iTunesId || a.AmgId != null ||
|
|
||||||
amgId != null && a.AmgId == amgId || a.SpotifyId != null ||
|
|
||||||
spotifyId != null && a.SpotifyId == spotifyId
|
|
||||||
select a).FirstOrDefault();
|
|
||||||
sw.Stop();
|
|
||||||
if (artist == null || !artist.IsValid)
|
|
||||||
Logger.LogTrace(
|
|
||||||
"ArtistFactory: Artist Not Found By External Ids: MusicbrainzId [{0}], iTunesIs [{1}], AmgId [{2}], SpotifyId [{3}]",
|
|
||||||
musicBrainzId, iTunesId, amgId, spotifyId);
|
|
||||||
return new OperationResult<data.Artist>
|
|
||||||
{
|
|
||||||
IsSuccess = artist != null,
|
|
||||||
OperationTime = sw.ElapsedMilliseconds,
|
|
||||||
Data = artist
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Library.Models.Pagination.PagedResult<ArtistList>> List(User roadieUser, PagedRequest request,
|
|
||||||
bool? doRandomize = false, bool? onlyIncludeWithReleases = true)
|
|
||||||
{
|
{
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
IQueryable<int> favoriteArtistIds = null;
|
IQueryable<int> favoriteArtistIds = null;
|
||||||
if (request.FilterFavoriteOnly)
|
if (request.FilterFavoriteOnly)
|
||||||
|
{
|
||||||
favoriteArtistIds = from a in DbContext.Artists
|
favoriteArtistIds = from a in DbContext.Artists
|
||||||
join ua in DbContext.UserArtists on a.Id equals ua.ArtistId
|
join ua in DbContext.UserArtists on a.Id equals ua.ArtistId
|
||||||
where ua.IsFavorite ?? false
|
where ua.IsFavorite ?? false
|
||||||
where roadieUser == null || ua.UserId == roadieUser.Id
|
where roadieUser == null || ua.UserId == roadieUser.Id
|
||||||
select a.Id;
|
select a.Id;
|
||||||
|
}
|
||||||
IQueryable<int> labelArtistIds = null;
|
IQueryable<int> labelArtistIds = null;
|
||||||
if (request.FilterToLabelId.HasValue)
|
if (request.FilterToLabelId.HasValue)
|
||||||
|
{
|
||||||
labelArtistIds = (from l in DbContext.Labels
|
labelArtistIds = (from l in DbContext.Labels
|
||||||
join rl in DbContext.ReleaseLabels on l.Id equals rl.LabelId
|
join rl in DbContext.ReleaseLabels on l.Id equals rl.LabelId
|
||||||
join r in DbContext.Releases on rl.ReleaseId equals r.Id
|
join r in DbContext.Releases on rl.ReleaseId equals r.Id
|
||||||
where l.RoadieId == request.FilterToLabelId
|
where l.RoadieId == request.FilterToLabelId
|
||||||
select r.ArtistId)
|
select r.ArtistId)
|
||||||
.Distinct();
|
.Distinct();
|
||||||
|
}
|
||||||
IQueryable<int> genreArtistIds = null;
|
IQueryable<int> genreArtistIds = null;
|
||||||
var isFilteredToGenre = false;
|
var isFilteredToGenre = false;
|
||||||
if (!string.IsNullOrEmpty(request.Filter) &&
|
if(request.FilterToGenreId.HasValue)
|
||||||
request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase))
|
{
|
||||||
|
genreArtistIds = (from ag in DbContext.ArtistGenres
|
||||||
|
join g in DbContext.Genres on ag.GenreId equals g.Id
|
||||||
|
where g.RoadieId == request.FilterToGenreId
|
||||||
|
select ag.ArtistId)
|
||||||
|
.Distinct();
|
||||||
|
isFilteredToGenre = true;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(request.Filter) && request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var genreFilter = request.Filter.Replace(":genre ", "");
|
var genreFilter = request.Filter.Replace(":genre ", "");
|
||||||
genreArtistIds = (from ag in DbContext.ArtistGenres
|
genreArtistIds = (from ag in DbContext.ArtistGenres
|
||||||
join g in DbContext.Genres on ag.GenreId equals g.Id
|
join g in DbContext.Genres on ag.GenreId equals g.Id
|
||||||
where g.Name.Contains(genreFilter)
|
where g.Name.Contains(genreFilter)
|
||||||
select ag.ArtistId)
|
select ag.ArtistId)
|
||||||
.Distinct();
|
.Distinct();
|
||||||
isFilteredToGenre = true;
|
isFilteredToGenre = true;
|
||||||
request.Filter = null;
|
request.Filter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var onlyWithReleases = onlyIncludeWithReleases ?? true;
|
var onlyWithReleases = onlyIncludeWithReleases ?? true;
|
||||||
var isEqualFilter = false;
|
var isEqualFilter = false;
|
||||||
if (!string.IsNullOrEmpty(request.FilterValue))
|
if (!string.IsNullOrEmpty(request.FilterValue))
|
||||||
|
@ -285,7 +270,6 @@ namespace Roadie.Api.Services
|
||||||
request.Filter = filter.Substring(1, filter.Length - 2);
|
request.Filter = filter.Substring(1, filter.Length - 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
|
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
|
||||||
? request.FilterValue.ToAlphanumericName()
|
? request.FilterValue.ToAlphanumericName()
|
||||||
: null;
|
: null;
|
||||||
|
@ -293,8 +277,18 @@ namespace Roadie.Api.Services
|
||||||
where !onlyWithReleases || a.ReleaseCount > 0
|
where !onlyWithReleases || a.ReleaseCount > 0
|
||||||
where request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId
|
where request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId
|
||||||
where request.FilterMinimumRating == null || a.Rating >= request.FilterMinimumRating.Value
|
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 request.FilterValue == "" ||
|
||||||
where !isEqualFilter || a.Name.Equals(request.FilterValue) || a.SortName.Equals(request.FilterValue) || a.AlternateNames.Equals(request.FilterValue) || a.AlternateNames.Equals(normalizedFilterValue)
|
a.Name.Contains(request.FilterValue) ||
|
||||||
|
a.SortName.Contains(request.FilterValue) ||
|
||||||
|
a.RealName.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.RealName.Equals(request.FilterValue) ||
|
||||||
|
a.AlternateNames.Equals(request.FilterValue) ||
|
||||||
|
a.AlternateNames.Equals(normalizedFilterValue)
|
||||||
where !request.FilterFavoriteOnly || favoriteArtistIds.Contains(a.Id)
|
where !request.FilterFavoriteOnly || favoriteArtistIds.Contains(a.Id)
|
||||||
where request.FilterToLabelId == null || labelArtistIds.Contains(a.Id)
|
where request.FilterToLabelId == null || labelArtistIds.Contains(a.Id)
|
||||||
where !isFilteredToGenre || genreArtistIds.Contains(a.Id)
|
where !isFilteredToGenre || genreArtistIds.Contains(a.Id)
|
||||||
|
@ -323,22 +317,24 @@ namespace Roadie.Api.Services
|
||||||
var rowCount = result.Count();
|
var rowCount = result.Count();
|
||||||
if (doRandomize ?? false)
|
if (doRandomize ?? false)
|
||||||
{
|
{
|
||||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? 100;
|
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||||
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
||||||
rows = result.OrderBy(x => x.RandomSortId).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
rows = result.OrderBy(x => x.RandomSortId)
|
||||||
|
.Take(request.LimitValue)
|
||||||
|
.ToArray();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string sortBy;
|
string sortBy;
|
||||||
if (request.ActionValue == User.ActionKeyUserRated)
|
if (request.ActionValue == User.ActionKeyUserRated)
|
||||||
{
|
{
|
||||||
sortBy = string.IsNullOrEmpty(request.Sort)
|
sortBy = string.IsNullOrEmpty(request.Sort)
|
||||||
? request.OrderValue(new Dictionary<string, string> { { "Rating", "DESC" }, { "Artist.Text", "ASC" } })
|
? request.OrderValue(new Dictionary<string, string> { { "Rating", "DESC" }, { "Artist.Text", "ASC" } })
|
||||||
: request.OrderValue();
|
: request.OrderValue();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sortBy = request.OrderValue(new Dictionary<string, string> {{"SortName", "ASC"}, {"Artist.Text", "ASC"}});
|
sortBy = request.OrderValue(new Dictionary<string, string> { { "SortName", "ASC" }, { "Artist.Text", "ASC" } });
|
||||||
}
|
}
|
||||||
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
||||||
}
|
}
|
||||||
|
@ -351,11 +347,11 @@ namespace Roadie.Api.Services
|
||||||
where rowIds.Contains(ua.ArtistId)
|
where rowIds.Contains(ua.ArtistId)
|
||||||
select ua).ToArray();
|
select ua).ToArray();
|
||||||
|
|
||||||
foreach (var userArtistRating in userArtistRatings.Where(x =>
|
foreach (var userArtistRating in userArtistRatings.Where(x => rows.Select(r => r.DatabaseId).Contains(x.ArtistId)))
|
||||||
rows.Select(r => r.DatabaseId).Contains(x.ArtistId)))
|
|
||||||
{
|
{
|
||||||
var row = rows.FirstOrDefault(x => x.DatabaseId == userArtistRating.ArtistId);
|
var row = rows.FirstOrDefault(x => x.DatabaseId == userArtistRating.ArtistId);
|
||||||
if (row != null)
|
if (row != null)
|
||||||
|
{
|
||||||
row.UserRating = new UserArtist
|
row.UserRating = new UserArtist
|
||||||
{
|
{
|
||||||
IsDisliked = userArtistRating.IsDisliked ?? false,
|
IsDisliked = userArtistRating.IsDisliked ?? false,
|
||||||
|
@ -363,11 +359,11 @@ namespace Roadie.Api.Services
|
||||||
Rating = userArtistRating.Rating,
|
Rating = userArtistRating.Rating,
|
||||||
RatedDate = userArtistRating.LastUpdated ?? userArtistRating.CreatedDate
|
RatedDate = userArtistRating.LastUpdated ?? userArtistRating.CreatedDate
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.Stop();
|
|
||||||
if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0)
|
if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0)
|
||||||
|
{
|
||||||
if (Configuration.RecordNoResultSearches)
|
if (Configuration.RecordNoResultSearches)
|
||||||
{
|
{
|
||||||
// Create request for no artist found
|
// Create request for no artist found
|
||||||
|
@ -379,7 +375,8 @@ namespace Roadie.Api.Services
|
||||||
DbContext.Requests.Add(req);
|
DbContext.Requests.Add(req);
|
||||||
await DbContext.SaveChangesAsync();
|
await DbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
return new Library.Models.Pagination.PagedResult<ArtistList>
|
return new Library.Models.Pagination.PagedResult<ArtistList>
|
||||||
{
|
{
|
||||||
TotalCount = rowCount,
|
TotalCount = rowCount,
|
||||||
|
@ -450,179 +447,6 @@ namespace Roadie.Api.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async Task<OperationResult<data.Artist>> MergeArtists(ApplicationUser user, data.Artist artistToMerge, data.Artist artistToMergeInto)
|
|
||||||
{
|
|
||||||
SimpleContract.Requires<ArgumentNullException>(artistToMerge != null, "Invalid Artist");
|
|
||||||
SimpleContract.Requires<ArgumentNullException>(artistToMergeInto != null, "Invalid Artist");
|
|
||||||
|
|
||||||
var result = false;
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
|
|
||||||
var sw = new Stopwatch();
|
|
||||||
sw.Start();
|
|
||||||
|
|
||||||
var artistToMergeFolder = artistToMerge.ArtistFileFolder(Configuration);
|
|
||||||
var artistToMergeIntoFolder = artistToMergeInto.ArtistFileFolder(Configuration);
|
|
||||||
|
|
||||||
artistToMergeInto.RealName = artistToMergeInto.RealName ?? artistToMerge.RealName;
|
|
||||||
artistToMergeInto.MusicBrainzId = artistToMergeInto.MusicBrainzId ?? artistToMerge.MusicBrainzId;
|
|
||||||
artistToMergeInto.ITunesId = artistToMergeInto.ITunesId ?? artistToMerge.ITunesId;
|
|
||||||
artistToMergeInto.AmgId = artistToMergeInto.AmgId ?? artistToMerge.AmgId;
|
|
||||||
artistToMergeInto.SpotifyId = artistToMergeInto.SpotifyId ?? artistToMerge.SpotifyId;
|
|
||||||
artistToMergeInto.Thumbnail = artistToMergeInto.Thumbnail ?? artistToMerge.Thumbnail;
|
|
||||||
artistToMergeInto.Profile = artistToMergeInto.Profile ?? artistToMerge.Profile;
|
|
||||||
artistToMergeInto.BirthDate = artistToMergeInto.BirthDate ?? artistToMerge.BirthDate;
|
|
||||||
artistToMergeInto.BeginDate = artistToMergeInto.BeginDate ?? artistToMerge.BeginDate;
|
|
||||||
artistToMergeInto.EndDate = artistToMergeInto.EndDate ?? artistToMerge.EndDate;
|
|
||||||
if (!string.IsNullOrEmpty(artistToMerge.ArtistType) && !artistToMerge.ArtistType.Equals("Other", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
artistToMergeInto.ArtistType = artistToMergeInto.ArtistType ?? artistToMerge.ArtistType;
|
|
||||||
}
|
|
||||||
artistToMergeInto.BioContext = artistToMergeInto.BioContext ?? artistToMerge.BioContext;
|
|
||||||
artistToMergeInto.DiscogsId = artistToMergeInto.DiscogsId ?? artistToMerge.DiscogsId;
|
|
||||||
artistToMergeInto.Tags = artistToMergeInto.Tags.AddToDelimitedList(artistToMerge.Tags.ToListFromDelimited());
|
|
||||||
var altNames = artistToMerge.AlternateNames.ToListFromDelimited().ToList();
|
|
||||||
altNames.Add(artistToMerge.Name);
|
|
||||||
altNames.Add(artistToMerge.SortName);
|
|
||||||
artistToMergeInto.AlternateNames = artistToMergeInto.AlternateNames.AddToDelimitedList(altNames);
|
|
||||||
artistToMergeInto.URLs = artistToMergeInto.URLs.AddToDelimitedList(artistToMerge.URLs.ToListFromDelimited());
|
|
||||||
artistToMergeInto.ISNI = artistToMergeInto.ISNI.AddToDelimitedList(artistToMerge.ISNI.ToListFromDelimited());
|
|
||||||
artistToMergeInto.LastUpdated = now;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var artistGenres = DbContext.ArtistGenres.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
|
||||||
if (artistGenres != null)
|
|
||||||
{
|
|
||||||
var existingArtistGenres = DbContext.ArtistGenres.Where(x => x.ArtistId == artistToMergeInto.Id).ToArray();
|
|
||||||
foreach (var artistGenre in artistGenres)
|
|
||||||
{
|
|
||||||
var existing = existingArtistGenres.FirstOrDefault(x => x.GenreId == artistGenre.GenreId);
|
|
||||||
// If not exist then add new for artist to merge into
|
|
||||||
if (existing == null)
|
|
||||||
{
|
|
||||||
DbContext.ArtistGenres.Add(new data.ArtistGenre
|
|
||||||
{
|
|
||||||
ArtistId = artistToMergeInto.Id,
|
|
||||||
GenreId = artistGenre.GenreId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var artistImages = DbContext.Images.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
|
||||||
if (artistImages != null)
|
|
||||||
{
|
|
||||||
foreach (var artistImage in artistImages)
|
|
||||||
{
|
|
||||||
artistImage.ArtistId = artistToMergeInto.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Move any Artist and Artist Secondary images from ArtistToMerge into ArtistToMergeInto folder
|
|
||||||
if (Directory.Exists(artistToMergeFolder))
|
|
||||||
{
|
|
||||||
var artistToMergeImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistToMergeFolder), ImageType.Artist);
|
|
||||||
var artistToMergeSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistToMergeFolder), ImageType.ArtistSecondary).ToList();
|
|
||||||
// Primary Artist image
|
|
||||||
if (artistToMergeImages.Any())
|
|
||||||
{
|
|
||||||
// If the ArtistToMergeInto already has a primary image then the ArtistToMerge primary image becomes a secondary image
|
|
||||||
var artistToMergeIntoPrimaryImage = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistToMergeIntoFolder), ImageType.Artist).FirstOrDefault();
|
|
||||||
if (artistToMergeIntoPrimaryImage != null)
|
|
||||||
{
|
|
||||||
artistToMergeSecondaryImages.Add(artistToMergeImages.First());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var artistImageFilename = Path.Combine(artistToMergeIntoFolder, ImageHelper.ArtistImageFilename);
|
|
||||||
artistToMergeImages.First().MoveTo(artistImageFilename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Secondary Artist images
|
|
||||||
if (artistToMergeSecondaryImages.Any())
|
|
||||||
{
|
|
||||||
var looper = 0;
|
|
||||||
foreach (var artistSecondaryImage in artistToMergeSecondaryImages)
|
|
||||||
{
|
|
||||||
var artistImageFilename = Path.Combine(artistToMergeIntoFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
|
||||||
while (File.Exists(artistImageFilename))
|
|
||||||
{
|
|
||||||
looper++;
|
|
||||||
artistImageFilename = Path.Combine(artistToMergeIntoFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
|
||||||
}
|
|
||||||
artistSecondaryImage.MoveTo(artistImageFilename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex, "MergeArtists: Error Moving Artist Primary and Secondary Images");
|
|
||||||
}
|
|
||||||
|
|
||||||
var userArtists = DbContext.UserArtists.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
|
||||||
if (artistImages != null)
|
|
||||||
{
|
|
||||||
foreach (var userArtist in userArtists)
|
|
||||||
{
|
|
||||||
userArtist.ArtistId = artistToMergeInto.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var artistTracks = DbContext.Tracks.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
|
||||||
if (artistTracks != null)
|
|
||||||
{
|
|
||||||
foreach (var artistTrack in artistTracks)
|
|
||||||
{
|
|
||||||
artistTrack.ArtistId = artistToMergeInto.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var artistReleases = DbContext.Releases.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
|
||||||
if (artistReleases != null)
|
|
||||||
{
|
|
||||||
foreach (var artistRelease in artistReleases)
|
|
||||||
{
|
|
||||||
// See if there is already a release by the same name for the artist to merge into, if so then merge releases
|
|
||||||
var artistToMergeHasRelease = DbContext.Releases.FirstOrDefault(x => x.ArtistId == artistToMerge.Id && x.Title == artistRelease.Title);
|
|
||||||
if (artistToMergeHasRelease != null)
|
|
||||||
{
|
|
||||||
await ReleaseService.MergeReleases(user, artistRelease, artistToMergeHasRelease, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
artistRelease.ArtistId = artistToMerge.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogWarning(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var release in DbContext.Releases.Include("Artist").Where(x => x.ArtistId == artistToMerge.Id).ToArray())
|
|
||||||
{
|
|
||||||
var originalReleaseFolder = release.ReleaseFileFolder(artistToMergeFolder);
|
|
||||||
await ReleaseService.UpdateRelease(user, release.Adapt<Release>(), originalReleaseFolder);
|
|
||||||
}
|
|
||||||
await DbContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
await Delete(user, artistToMerge);
|
|
||||||
|
|
||||||
|
|
||||||
result = true;
|
|
||||||
|
|
||||||
sw.Stop();
|
|
||||||
return new OperationResult<data.Artist>
|
|
||||||
{
|
|
||||||
Data = artistToMergeInto,
|
|
||||||
IsSuccess = result,
|
|
||||||
OperationTime = sw.ElapsedMilliseconds
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OperationResult<bool>> RefreshArtistMetadata(ApplicationUser user, Guid artistId)
|
public async Task<OperationResult<bool>> RefreshArtistMetadata(ApplicationUser user, Guid artistId)
|
||||||
{
|
{
|
||||||
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
|
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
|
||||||
|
@ -687,6 +511,97 @@ namespace Roadie.Api.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<OperationResult<bool>> ScanArtistReleasesFolders(ApplicationUser user, Guid artistId, string destinationFolder, bool doJustInfo)
|
||||||
|
{
|
||||||
|
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
|
||||||
|
|
||||||
|
var result = true;
|
||||||
|
var resultErrors = new List<Exception>();
|
||||||
|
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<bool>();
|
||||||
|
}
|
||||||
|
var releaseScannedCount = 0;
|
||||||
|
var artistFolder = artist.ArtistFileFolder(Configuration);
|
||||||
|
if (!Directory.Exists(artistFolder))
|
||||||
|
{
|
||||||
|
Logger.LogDebug($"ScanArtistReleasesFolders: ArtistFolder Not Found [{ artistFolder }] For Artist `{ artist }`");
|
||||||
|
return new OperationResult<bool>();
|
||||||
|
}
|
||||||
|
var scannedArtistFolders = new List<string>();
|
||||||
|
// Scan known releases for changes
|
||||||
|
if (artist.Releases != null)
|
||||||
|
{
|
||||||
|
foreach (var release in artist.Releases)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = result && (await ReleaseService.ScanReleaseFolder(user, Guid.Empty, 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 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 FileDirectoryProcessorService.Process(user, new DirectoryInfo(folder), doJustInfo);
|
||||||
|
}
|
||||||
|
if (!doJustInfo)
|
||||||
|
{
|
||||||
|
Services.FileDirectoryProcessorService.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 i = new FileInfo(imageFiles.First());
|
||||||
|
var iName = i.Name.ToLower().Trim();
|
||||||
|
if (ImageHelper.IsArtistImage(i))
|
||||||
|
{
|
||||||
|
// Read image and convert to jpeg
|
||||||
|
artist.Thumbnail = ImageHelper.ResizeToThumbnail(File.ReadAllBytes(i.FullName), Configuration);
|
||||||
|
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<bool>
|
||||||
|
{
|
||||||
|
Data = result,
|
||||||
|
IsSuccess = result,
|
||||||
|
Errors = resultErrors,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<OperationResult<Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl)
|
public async Task<OperationResult<Image>> SetReleaseImageByUrl(ApplicationUser user, Guid id, string imageUrl)
|
||||||
{
|
{
|
||||||
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
|
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
|
||||||
|
@ -773,11 +688,11 @@ namespace Roadie.Api.Services
|
||||||
// Ensure is jpeg first
|
// Ensure is jpeg first
|
||||||
artistSecondaryImage = ImageHelper.ConvertToJpegFormat(artistSecondaryImage);
|
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))
|
while (File.Exists(artistImageFilename))
|
||||||
{
|
{
|
||||||
looper++;
|
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);
|
File.WriteAllBytes(artistImageFilename, artistSecondaryImage);
|
||||||
|
@ -1317,6 +1232,200 @@ namespace Roadie.Api.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OperationResult<data.Artist> GetByExternalIds(string musicBrainzId = null, string iTunesId = null, string amgId = null, string spotifyId = null)
|
||||||
|
{
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
var artist = (from a in DbContext.Artists
|
||||||
|
where a.MusicBrainzId != null && musicBrainzId != null && a.MusicBrainzId == musicBrainzId ||
|
||||||
|
a.ITunesId != null || iTunesId != null && a.ITunesId == iTunesId || a.AmgId != null ||
|
||||||
|
amgId != null && a.AmgId == amgId || a.SpotifyId != null ||
|
||||||
|
spotifyId != null && a.SpotifyId == spotifyId
|
||||||
|
select a).FirstOrDefault();
|
||||||
|
sw.Stop();
|
||||||
|
if (artist == null || !artist.IsValid)
|
||||||
|
Logger.LogTrace(
|
||||||
|
"ArtistFactory: Artist Not Found By External Ids: MusicbrainzId [{0}], iTunesIs [{1}], AmgId [{2}], SpotifyId [{3}]",
|
||||||
|
musicBrainzId, iTunesId, amgId, spotifyId);
|
||||||
|
return new OperationResult<data.Artist>
|
||||||
|
{
|
||||||
|
IsSuccess = artist != null,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds,
|
||||||
|
Data = artist
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperationResult<data.Artist>> MergeArtists(ApplicationUser user, data.Artist artistToMerge, data.Artist artistToMergeInto)
|
||||||
|
{
|
||||||
|
SimpleContract.Requires<ArgumentNullException>(artistToMerge != null, "Invalid Artist");
|
||||||
|
SimpleContract.Requires<ArgumentNullException>(artistToMergeInto != null, "Invalid Artist");
|
||||||
|
|
||||||
|
var result = false;
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
|
var artistToMergeFolder = artistToMerge.ArtistFileFolder(Configuration);
|
||||||
|
var artistToMergeIntoFolder = artistToMergeInto.ArtistFileFolder(Configuration);
|
||||||
|
|
||||||
|
artistToMergeInto.RealName = artistToMergeInto.RealName ?? artistToMerge.RealName;
|
||||||
|
artistToMergeInto.MusicBrainzId = artistToMergeInto.MusicBrainzId ?? artistToMerge.MusicBrainzId;
|
||||||
|
artistToMergeInto.ITunesId = artistToMergeInto.ITunesId ?? artistToMerge.ITunesId;
|
||||||
|
artistToMergeInto.AmgId = artistToMergeInto.AmgId ?? artistToMerge.AmgId;
|
||||||
|
artistToMergeInto.SpotifyId = artistToMergeInto.SpotifyId ?? artistToMerge.SpotifyId;
|
||||||
|
artistToMergeInto.Thumbnail = artistToMergeInto.Thumbnail ?? artistToMerge.Thumbnail;
|
||||||
|
artistToMergeInto.Profile = artistToMergeInto.Profile ?? artistToMerge.Profile;
|
||||||
|
artistToMergeInto.BirthDate = artistToMergeInto.BirthDate ?? artistToMerge.BirthDate;
|
||||||
|
artistToMergeInto.BeginDate = artistToMergeInto.BeginDate ?? artistToMerge.BeginDate;
|
||||||
|
artistToMergeInto.EndDate = artistToMergeInto.EndDate ?? artistToMerge.EndDate;
|
||||||
|
if (!string.IsNullOrEmpty(artistToMerge.ArtistType) && !artistToMerge.ArtistType.Equals("Other", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
artistToMergeInto.ArtistType = artistToMergeInto.ArtistType ?? artistToMerge.ArtistType;
|
||||||
|
}
|
||||||
|
artistToMergeInto.BioContext = artistToMergeInto.BioContext ?? artistToMerge.BioContext;
|
||||||
|
artistToMergeInto.DiscogsId = artistToMergeInto.DiscogsId ?? artistToMerge.DiscogsId;
|
||||||
|
artistToMergeInto.Tags = artistToMergeInto.Tags.AddToDelimitedList(artistToMerge.Tags.ToListFromDelimited());
|
||||||
|
var altNames = artistToMerge.AlternateNames.ToListFromDelimited().ToList();
|
||||||
|
altNames.Add(artistToMerge.Name);
|
||||||
|
altNames.Add(artistToMerge.SortName);
|
||||||
|
artistToMergeInto.AlternateNames = artistToMergeInto.AlternateNames.AddToDelimitedList(altNames);
|
||||||
|
artistToMergeInto.URLs = artistToMergeInto.URLs.AddToDelimitedList(artistToMerge.URLs.ToListFromDelimited());
|
||||||
|
artistToMergeInto.ISNI = artistToMergeInto.ISNI.AddToDelimitedList(artistToMerge.ISNI.ToListFromDelimited());
|
||||||
|
artistToMergeInto.LastUpdated = now;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var artistGenres = DbContext.ArtistGenres.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||||
|
if (artistGenres != null)
|
||||||
|
{
|
||||||
|
var existingArtistGenres = DbContext.ArtistGenres.Where(x => x.ArtistId == artistToMergeInto.Id).ToArray();
|
||||||
|
foreach (var artistGenre in artistGenres)
|
||||||
|
{
|
||||||
|
var existing = existingArtistGenres.FirstOrDefault(x => x.GenreId == artistGenre.GenreId);
|
||||||
|
// If not exist then add new for artist to merge into
|
||||||
|
if (existing == null)
|
||||||
|
{
|
||||||
|
DbContext.ArtistGenres.Add(new data.ArtistGenre
|
||||||
|
{
|
||||||
|
ArtistId = artistToMergeInto.Id,
|
||||||
|
GenreId = artistGenre.GenreId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var artistImages = DbContext.Images.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||||
|
if (artistImages != null)
|
||||||
|
{
|
||||||
|
foreach (var artistImage in artistImages)
|
||||||
|
{
|
||||||
|
artistImage.ArtistId = artistToMergeInto.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Move any Artist and Artist Secondary images from ArtistToMerge into ArtistToMergeInto folder
|
||||||
|
if (Directory.Exists(artistToMergeFolder))
|
||||||
|
{
|
||||||
|
var artistToMergeImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistToMergeFolder), ImageType.Artist);
|
||||||
|
var artistToMergeSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistToMergeFolder), ImageType.ArtistSecondary).ToList();
|
||||||
|
// Primary Artist image
|
||||||
|
if (artistToMergeImages.Any())
|
||||||
|
{
|
||||||
|
// If the ArtistToMergeInto already has a primary image then the ArtistToMerge primary image becomes a secondary image
|
||||||
|
var artistToMergeIntoPrimaryImage = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistToMergeIntoFolder), ImageType.Artist).FirstOrDefault();
|
||||||
|
if (artistToMergeIntoPrimaryImage != null)
|
||||||
|
{
|
||||||
|
artistToMergeSecondaryImages.Add(artistToMergeImages.First());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var artistImageFilename = Path.Combine(artistToMergeIntoFolder, ImageHelper.ArtistImageFilename);
|
||||||
|
artistToMergeImages.First().MoveTo(artistImageFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Secondary Artist images
|
||||||
|
if (artistToMergeSecondaryImages.Any())
|
||||||
|
{
|
||||||
|
var looper = 0;
|
||||||
|
foreach (var artistSecondaryImage in artistToMergeSecondaryImages)
|
||||||
|
{
|
||||||
|
var artistImageFilename = Path.Combine(artistToMergeIntoFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
||||||
|
while (File.Exists(artistImageFilename))
|
||||||
|
{
|
||||||
|
looper++;
|
||||||
|
artistImageFilename = Path.Combine(artistToMergeIntoFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
||||||
|
}
|
||||||
|
artistSecondaryImage.MoveTo(artistImageFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "MergeArtists: Error Moving Artist Primary and Secondary Images");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userArtists = DbContext.UserArtists.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||||
|
if (artistImages != null)
|
||||||
|
{
|
||||||
|
foreach (var userArtist in userArtists)
|
||||||
|
{
|
||||||
|
userArtist.ArtistId = artistToMergeInto.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var artistTracks = DbContext.Tracks.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||||
|
if (artistTracks != null)
|
||||||
|
{
|
||||||
|
foreach (var artistTrack in artistTracks)
|
||||||
|
{
|
||||||
|
artistTrack.ArtistId = artistToMergeInto.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var artistReleases = DbContext.Releases.Where(x => x.ArtistId == artistToMerge.Id).ToArray();
|
||||||
|
if (artistReleases != null)
|
||||||
|
{
|
||||||
|
foreach (var artistRelease in artistReleases)
|
||||||
|
{
|
||||||
|
// See if there is already a release by the same name for the artist to merge into, if so then merge releases
|
||||||
|
var artistToMergeHasRelease = DbContext.Releases.FirstOrDefault(x => x.ArtistId == artistToMerge.Id && x.Title == artistRelease.Title);
|
||||||
|
if (artistToMergeHasRelease != null)
|
||||||
|
{
|
||||||
|
await ReleaseService.MergeReleases(user, artistRelease, artistToMergeHasRelease, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
artistRelease.ArtistId = artistToMerge.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var release in DbContext.Releases.Include("Artist").Where(x => x.ArtistId == artistToMerge.Id).ToArray())
|
||||||
|
{
|
||||||
|
var originalReleaseFolder = release.ReleaseFileFolder(artistToMergeFolder);
|
||||||
|
await ReleaseService.UpdateRelease(user, release.Adapt<Release>(), originalReleaseFolder);
|
||||||
|
}
|
||||||
|
await DbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
await Delete(user, artistToMerge);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
return new OperationResult<data.Artist>
|
||||||
|
{
|
||||||
|
Data = artistToMergeInto,
|
||||||
|
IsSuccess = result,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<OperationResult<Image>> SaveImageBytes(ApplicationUser user, Guid id, byte[] imageBytes)
|
private async Task<OperationResult<Image>> SaveImageBytes(ApplicationUser user, Guid id, byte[] imageBytes)
|
||||||
{
|
{
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
|
@ -1368,96 +1477,5 @@ namespace Roadie.Api.Services
|
||||||
Errors = errors
|
Errors = errors
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OperationResult<bool>> ScanArtistReleasesFolders(ApplicationUser user, Guid artistId, string destinationFolder, bool doJustInfo)
|
|
||||||
{
|
|
||||||
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
|
|
||||||
|
|
||||||
var result = true;
|
|
||||||
var resultErrors = new List<Exception>();
|
|
||||||
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<bool>();
|
|
||||||
}
|
|
||||||
var releaseScannedCount = 0;
|
|
||||||
var artistFolder = artist.ArtistFileFolder(Configuration);
|
|
||||||
if (!Directory.Exists(artistFolder))
|
|
||||||
{
|
|
||||||
Logger.LogDebug($"ScanArtistReleasesFolders: ArtistFolder Not Found [{ artistFolder }] For Artist `{ artist }`");
|
|
||||||
return new OperationResult<bool>();
|
|
||||||
}
|
|
||||||
var scannedArtistFolders = new List<string>();
|
|
||||||
// Scan known releases for changes
|
|
||||||
if (artist.Releases != null)
|
|
||||||
{
|
|
||||||
foreach (var release in artist.Releases)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = result && (await ReleaseService.ScanReleaseFolder(user, Guid.Empty, 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 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 FileDirectoryProcessorService.Process(user, new DirectoryInfo(folder), doJustInfo);
|
|
||||||
}
|
|
||||||
if (!doJustInfo)
|
|
||||||
{
|
|
||||||
Services.FileDirectoryProcessorService.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 i = new FileInfo(imageFiles.First());
|
|
||||||
var iName = i.Name.ToLower().Trim();
|
|
||||||
if (ImageHelper.IsArtistImage(i))
|
|
||||||
{
|
|
||||||
// Read image and convert to jpeg
|
|
||||||
artist.Thumbnail = ImageHelper.ResizeToThumbnail(File.ReadAllBytes(i.FullName), Configuration);
|
|
||||||
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<bool>
|
|
||||||
{
|
|
||||||
Data = result,
|
|
||||||
IsSuccess = result,
|
|
||||||
Errors = resultErrors,
|
|
||||||
OperationTime = sw.ElapsedMilliseconds
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,12 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using Mapster;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Roadie.Library;
|
||||||
using Roadie.Library.Caching;
|
using Roadie.Library.Caching;
|
||||||
using Roadie.Library.Configuration;
|
using Roadie.Library.Configuration;
|
||||||
using Roadie.Library.Encoding;
|
using Roadie.Library.Encoding;
|
||||||
|
using Roadie.Library.Identity;
|
||||||
|
using Roadie.Library.Imaging;
|
||||||
using Roadie.Library.Models;
|
using Roadie.Library.Models;
|
||||||
using Roadie.Library.Models.Pagination;
|
using Roadie.Library.Models.Pagination;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
|
@ -9,6 +14,7 @@ using Roadie.Library.Utility;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Dynamic.Core;
|
using System.Linq.Dynamic.Core;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -28,8 +34,155 @@ namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request,
|
public async Task<OperationResult<Genre>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
|
||||||
bool? doRandomize = false)
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
sw.Start();
|
||||||
|
var cacheKey = string.Format("urn:genre_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
|
||||||
|
var result = await CacheManager.GetAsync(cacheKey, async () =>
|
||||||
|
{
|
||||||
|
return await GenreByIdAction(id, includes);
|
||||||
|
}, data.Genre.CacheRegionUrn(id));
|
||||||
|
sw.Stop();
|
||||||
|
return new OperationResult<Genre>(result.Messages)
|
||||||
|
{
|
||||||
|
Data = result?.Data,
|
||||||
|
IsNotFoundResult = result?.IsNotFoundResult ?? false,
|
||||||
|
Errors = result?.Errors,
|
||||||
|
IsSuccess = result?.IsSuccess ?? false,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id)
|
||||||
|
{
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id);
|
||||||
|
if (genre == null) return new OperationResult<bool>(true, string.Format("Genre Not Found [{0}]", id));
|
||||||
|
DbContext.Genres.Remove(genre);
|
||||||
|
await DbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
var genreImageFilename = genre.PathToImage(Configuration);
|
||||||
|
if (File.Exists(genreImageFilename))
|
||||||
|
{
|
||||||
|
File.Delete(genreImageFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation("User `{0}` deleted Genre `{1}]`", user, genre);
|
||||||
|
CacheManager.ClearRegion(genre.CacheRegion);
|
||||||
|
sw.Stop();
|
||||||
|
return new OperationResult<bool>
|
||||||
|
{
|
||||||
|
IsSuccess = true,
|
||||||
|
Data = true,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Task<OperationResult<Genre>> GenreByIdAction(Guid id, IEnumerable<string> includes = null)
|
||||||
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
|
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id);
|
||||||
|
if (genre == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new OperationResult<Genre>(true, string.Format("Genre Not Found [{0}]", id)));
|
||||||
|
}
|
||||||
|
var result = genre.Adapt<Genre>();
|
||||||
|
result.AlternateNames = genre.AlternateNames;
|
||||||
|
result.Tags = genre.Tags;
|
||||||
|
result.Thumbnail = MakeLabelThumbnailImage(genre.RoadieId);
|
||||||
|
result.MediumThumbnail = MakeThumbnailImage(id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
|
||||||
|
if (includes != null && includes.Any())
|
||||||
|
{
|
||||||
|
if (includes.Contains("stats"))
|
||||||
|
{
|
||||||
|
var releaseCount = (from rg in DbContext.ReleaseGenres
|
||||||
|
where rg.GenreId == genre.Id
|
||||||
|
select rg.Id).Count();
|
||||||
|
var artistCount = (from rg in DbContext.ArtistGenres
|
||||||
|
where rg.GenreId == genre.Id
|
||||||
|
select rg.Id).Count();
|
||||||
|
result.Statistics = new Library.Models.Statistics.ReleaseGroupingStatistics
|
||||||
|
{
|
||||||
|
ArtistCount = artistCount,
|
||||||
|
ReleaseCount = releaseCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
|
return Task.FromResult(new OperationResult<Genre>
|
||||||
|
{
|
||||||
|
Data = result,
|
||||||
|
IsSuccess = result != null,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OperationResult<Image>> UploadGenreImage(User user, Guid id, IFormFile file)
|
||||||
|
{
|
||||||
|
var bytes = new byte[0];
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
file.CopyTo(ms);
|
||||||
|
bytes = ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await SaveImageBytes(user, id, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OperationResult<Image>> SetGenreImageByUrl(User user, Guid id, string imageUrl)
|
||||||
|
{
|
||||||
|
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task<OperationResult<Image>> SaveImageBytes(User user, Guid id, byte[] imageBytes)
|
||||||
|
{
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
var errors = new List<Exception>();
|
||||||
|
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id);
|
||||||
|
if (genre == null) return new OperationResult<Image>(true, string.Format("Genre Not Found [{0}]", id));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
genre.Thumbnail = imageBytes;
|
||||||
|
if (genre.Thumbnail != null)
|
||||||
|
{
|
||||||
|
// Save unaltered label image
|
||||||
|
File.WriteAllBytes(genre.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(imageBytes));
|
||||||
|
genre.Thumbnail = ImageHelper.ResizeToThumbnail(genre.Thumbnail, Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
genre.LastUpdated = now;
|
||||||
|
await DbContext.SaveChangesAsync();
|
||||||
|
CacheManager.ClearRegion(genre.CacheRegion);
|
||||||
|
Logger.LogInformation($"UploadGenreImage `{genre}` By User `{user}`");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex);
|
||||||
|
errors.Add(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
return new OperationResult<Image>
|
||||||
|
{
|
||||||
|
IsSuccess = !errors.Any(),
|
||||||
|
Data = MakeThumbnailImage(id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true),
|
||||||
|
OperationTime = sw.ElapsedMilliseconds,
|
||||||
|
Errors = errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false)
|
||||||
{
|
{
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
@ -60,7 +213,8 @@ namespace Roadie.Api.Services
|
||||||
ReleaseCount = releaseCount,
|
ReleaseCount = releaseCount,
|
||||||
ArtistCount = artistCount,
|
ArtistCount = artistCount,
|
||||||
CreatedDate = g.CreatedDate,
|
CreatedDate = g.CreatedDate,
|
||||||
LastUpdated = g.LastUpdated
|
LastUpdated = g.LastUpdated,
|
||||||
|
Thumbnail = MakeGenreThumbnailImage(g.RoadieId)
|
||||||
};
|
};
|
||||||
|
|
||||||
GenreList[] rows;
|
GenreList[] rows;
|
||||||
|
|
|
@ -19,6 +19,8 @@ namespace Roadie.Api.Services
|
||||||
|
|
||||||
Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId);
|
Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId);
|
||||||
|
|
||||||
|
Task<OperationResult<bool>> DeleteGenre(ApplicationUser user, Guid genreId);
|
||||||
|
|
||||||
Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles);
|
Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles);
|
||||||
|
|
||||||
Task<OperationResult<bool>> DeleteReleaseSecondaryImage(ApplicationUser user, Guid releaseId, int index);
|
Task<OperationResult<bool>> DeleteReleaseSecondaryImage(ApplicationUser user, Guid releaseId, int index);
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
using Roadie.Library.Models;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Roadie.Library;
|
||||||
|
using Roadie.Library.Identity;
|
||||||
|
using Roadie.Library.Models;
|
||||||
using Roadie.Library.Models.Pagination;
|
using Roadie.Library.Models.Pagination;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Roadie.Api.Services
|
namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
public interface IGenreService
|
public interface IGenreService
|
||||||
{
|
{
|
||||||
|
Task<OperationResult<Genre>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
|
||||||
|
Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id);
|
||||||
Task<PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false);
|
Task<PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false);
|
||||||
|
Task<OperationResult<Image>> SetGenreImageByUrl(User user, Guid id, string imageUrl);
|
||||||
|
Task<OperationResult<Image>> UploadGenreImage(User user, Guid id, IFormFile file);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,8 @@ namespace Roadie.Api.Services
|
||||||
|
|
||||||
Task<FileOperationResult<Image>> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
Task<FileOperationResult<Image>> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||||
|
|
||||||
|
Task<FileOperationResult<Image>> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||||
|
|
||||||
Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||||
|
|
||||||
Task<FileOperationResult<Image>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
Task<FileOperationResult<Image>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
public interface IUserService
|
public interface IUserService
|
||||||
{
|
{
|
||||||
Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes);
|
Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes, bool isAccountSettingsEdit = false);
|
||||||
|
|
||||||
Task<PagedResult<UserList>> List(PagedRequest request);
|
Task<PagedResult<UserList>> List(PagedRequest request);
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,17 @@ namespace Roadie.Api.Services
|
||||||
etag);
|
etag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<FileOperationResult<Image>> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||||
|
{
|
||||||
|
return await GetImageFileOperation("GenreThumbnail",
|
||||||
|
data.Label.CacheRegionUrn(id),
|
||||||
|
id,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
async () => { return await GenreImageAction(id, etag); },
|
||||||
|
etag);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
public async Task<FileOperationResult<Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
|
||||||
{
|
{
|
||||||
return await GetImageFileOperation("PlaylistThumbnail",
|
return await GetImageFileOperation("PlaylistThumbnail",
|
||||||
|
@ -480,6 +491,47 @@ namespace Roadie.Api.Services
|
||||||
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
|
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task<FileOperationResult<Image>> GenreImageAction(Guid id, EntityTagHeaderValue etag = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var genre = GetGenre(id);
|
||||||
|
if (genre == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new FileOperationResult<Image>(true, string.Format("Genre Not Found [{0}]", id)));
|
||||||
|
}
|
||||||
|
var image = new data.Image
|
||||||
|
{
|
||||||
|
Bytes = genre.Thumbnail,
|
||||||
|
CreatedDate = genre.CreatedDate,
|
||||||
|
LastUpdated = genre.LastUpdated
|
||||||
|
};
|
||||||
|
var genreImageFilename = genre.PathToImage(Configuration);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(genreImageFilename))
|
||||||
|
{
|
||||||
|
image.Bytes = File.ReadAllBytes(genreImageFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, $"Error Reading Image File [{genreImageFilename}]");
|
||||||
|
}
|
||||||
|
if (genre.Thumbnail == null || !genre.Thumbnail.Any())
|
||||||
|
{
|
||||||
|
image = DefaultNotFoundImages.Genre;
|
||||||
|
}
|
||||||
|
return Task.FromResult(GenerateFileOperationResult(id, image, etag));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError($"Error fetching Label Thumbnail [{id}]", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
|
||||||
|
}
|
||||||
|
|
||||||
private Task<FileOperationResult<Image>> PlaylistImageAction(Guid id, EntityTagHeaderValue etag = null)
|
private Task<FileOperationResult<Image>> PlaylistImageAction(Guid id, EntityTagHeaderValue etag = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -77,7 +77,7 @@ namespace Roadie.Api.Services
|
||||||
var cacheKey = string.Format("urn:label_by_id_operation:{0}:{1}", id,
|
var cacheKey = string.Format("urn:label_by_id_operation:{0}:{1}", id,
|
||||||
includes == null ? "0" : string.Join("|", includes));
|
includes == null ? "0" : string.Join("|", includes));
|
||||||
var result = await CacheManager.GetAsync(cacheKey,
|
var result = await CacheManager.GetAsync(cacheKey,
|
||||||
async () => { return await LabelByIdAction(id, includes); }, data.Artist.CacheRegionUrn(id));
|
async () => { return await LabelByIdAction(id, includes); }, data.Label.CacheRegionUrn(id));
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
if (result?.Data != null && roadieUser != null)
|
if (result?.Data != null && roadieUser != null)
|
||||||
{
|
{
|
||||||
|
@ -157,9 +157,11 @@ namespace Roadie.Api.Services
|
||||||
var rowCount = result.Count();
|
var rowCount = result.Count();
|
||||||
if (doRandomize ?? false)
|
if (doRandomize ?? false)
|
||||||
{
|
{
|
||||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? 100;
|
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||||
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
||||||
rows = result.OrderBy(x => x.RandomSortId).Take(request.LimitValue).ToArray();
|
rows = result.OrderBy(x => x.RandomSortId)
|
||||||
|
.Take(request.LimitValue)
|
||||||
|
.ToArray();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -341,8 +343,7 @@ namespace Roadie.Api.Services
|
||||||
result.Tags = label.Tags;
|
result.Tags = label.Tags;
|
||||||
result.URLs = label.URLs;
|
result.URLs = label.URLs;
|
||||||
result.Thumbnail = MakeLabelThumbnailImage(label.RoadieId);
|
result.Thumbnail = MakeLabelThumbnailImage(label.RoadieId);
|
||||||
result.MediumThumbnail = MakeThumbnailImage(id, "label", Configuration.MediumImageSize.Width,
|
result.MediumThumbnail = MakeThumbnailImage(id, "label", Configuration.MediumImageSize.Width,Configuration.MediumImageSize.Height);
|
||||||
Configuration.MediumImageSize.Height);
|
|
||||||
if (includes != null && includes.Any())
|
if (includes != null && includes.Any())
|
||||||
{
|
{
|
||||||
if (includes.Contains("stats"))
|
if (includes.Contains("stats"))
|
||||||
|
|
|
@ -113,8 +113,10 @@ namespace Roadie.Api.Services
|
||||||
sw.Start();
|
sw.Start();
|
||||||
var cacheKey = string.Format("urn:playlist_by_id_operation:{0}:{1}", id,
|
var cacheKey = string.Format("urn:playlist_by_id_operation:{0}:{1}", id,
|
||||||
includes == null ? "0" : string.Join("|", includes));
|
includes == null ? "0" : string.Join("|", includes));
|
||||||
var result = await CacheManager.GetAsync(cacheKey,
|
var result = await CacheManager.GetAsync(cacheKey, async () =>
|
||||||
async () => { return await PlaylistByIdAction(id, includes); }, data.Artist.CacheRegionUrn(id));
|
{
|
||||||
|
return await PlaylistByIdAction(id, includes);
|
||||||
|
}, data.Artist.CacheRegionUrn(id));
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
if (result?.Data != null && roadieUser != null)
|
if (result?.Data != null && roadieUser != null)
|
||||||
{
|
{
|
||||||
|
@ -122,17 +124,17 @@ namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
var user = GetUser(roadieUser.UserId);
|
var user = GetUser(roadieUser.UserId);
|
||||||
foreach (var track in result.Data.Tracks)
|
foreach (var track in result.Data.Tracks)
|
||||||
|
{
|
||||||
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, track.Track.DatabaseId, track.Track.Id);
|
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, track.Track.DatabaseId, track.Track.Id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin;
|
result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin;
|
||||||
var userBookmarkResult =
|
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist);
|
||||||
await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist);
|
|
||||||
if (userBookmarkResult.IsSuccess)
|
if (userBookmarkResult.IsSuccess)
|
||||||
result.Data.UserBookmarked =
|
{
|
||||||
userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) !=
|
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != null;
|
||||||
null;
|
}
|
||||||
|
|
||||||
if (result.Data.Comments.Any())
|
if (result.Data.Comments.Any())
|
||||||
{
|
{
|
||||||
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
|
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
|
||||||
|
@ -142,8 +144,7 @@ namespace Roadie.Api.Services
|
||||||
select cr).ToArray();
|
select cr).ToArray();
|
||||||
foreach (var comment in result.Data.Comments)
|
foreach (var comment in result.Data.Comments)
|
||||||
{
|
{
|
||||||
var userCommentReaction =
|
var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
|
||||||
userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
|
|
||||||
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
|
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
|
||||||
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
|
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
|
||||||
}
|
}
|
||||||
|
@ -402,15 +403,14 @@ namespace Roadie.Api.Services
|
||||||
var playlist = GetPlaylist(id);
|
var playlist = GetPlaylist(id);
|
||||||
|
|
||||||
if (playlist == null)
|
if (playlist == null)
|
||||||
return Task.FromResult(new OperationResult<Playlist>(true,
|
{
|
||||||
string.Format("Playlist Not Found [{0}]", id)));
|
return Task.FromResult(new OperationResult<Playlist>(true, string.Format("Playlist Not Found [{0}]", id)));
|
||||||
|
}
|
||||||
var result = playlist.Adapt<Playlist>();
|
var result = playlist.Adapt<Playlist>();
|
||||||
result.AlternateNames = playlist.AlternateNames;
|
result.AlternateNames = playlist.AlternateNames;
|
||||||
result.Tags = playlist.Tags;
|
result.Tags = playlist.Tags;
|
||||||
result.URLs = playlist.URLs;
|
result.URLs = playlist.URLs;
|
||||||
var maintainer = DbContext.Users.Include(x => x.UserRoles).Include("UserRoles.Role")
|
var maintainer = DbContext.Users.Include(x => x.UserRoles).Include("UserRoles.Role").FirstOrDefault(x => x.Id == playlist.UserId);
|
||||||
.FirstOrDefault(x => x.Id == playlist.UserId);
|
|
||||||
result.Maintainer = UserList.FromDataUser(maintainer, MakeUserThumbnailImage(maintainer.RoadieId));
|
result.Maintainer = UserList.FromDataUser(maintainer, MakeUserThumbnailImage(maintainer.RoadieId));
|
||||||
result.Thumbnail = MakePlaylistThumbnailImage(playlist.RoadieId);
|
result.Thumbnail = MakePlaylistThumbnailImage(playlist.RoadieId);
|
||||||
result.MediumThumbnail = MakeThumbnailImage(id, "playlist", Configuration.MediumImageSize.Width,
|
result.MediumThumbnail = MakeThumbnailImage(id, "playlist", Configuration.MediumImageSize.Width,
|
||||||
|
@ -424,6 +424,7 @@ namespace Roadie.Api.Services
|
||||||
select new { t, pltr }).ToArray();
|
select new { t, pltr }).ToArray();
|
||||||
|
|
||||||
if (includes.Contains("stats"))
|
if (includes.Contains("stats"))
|
||||||
|
{
|
||||||
result.Statistics = new ReleaseGroupingStatistics
|
result.Statistics = new ReleaseGroupingStatistics
|
||||||
{
|
{
|
||||||
ReleaseCount = result.ReleaseCount,
|
ReleaseCount = result.ReleaseCount,
|
||||||
|
@ -431,7 +432,9 @@ namespace Roadie.Api.Services
|
||||||
TrackSize = result.DurationTime,
|
TrackSize = result.DurationTime,
|
||||||
FileSize = playlistTracks.Sum(x => (long?)x.t.FileSize).ToFileSize()
|
FileSize = playlistTracks.Sum(x => (long?)x.t.FileSize).ToFileSize()
|
||||||
};
|
};
|
||||||
|
}
|
||||||
if (includes.Contains("tracks"))
|
if (includes.Contains("tracks"))
|
||||||
|
{
|
||||||
result.Tracks = (from plt in playlistTracks
|
result.Tracks = (from plt in playlistTracks
|
||||||
join rm in DbContext.ReleaseMedias on plt.t.ReleaseMediaId equals rm.Id
|
join rm in DbContext.ReleaseMedias on plt.t.ReleaseMediaId equals rm.Id
|
||||||
join r in DbContext.Releases on rm.ReleaseId equals r.Id
|
join r in DbContext.Releases on rm.ReleaseId equals r.Id
|
||||||
|
@ -453,10 +456,13 @@ namespace Roadie.Api.Services
|
||||||
MakeArtistThumbnailImage(releaseArtist.RoadieId),
|
MakeArtistThumbnailImage(releaseArtist.RoadieId),
|
||||||
MakeArtistThumbnailImage(trackArtist == null ? null : (Guid?)trackArtist.RoadieId))
|
MakeArtistThumbnailImage(trackArtist == null ? null : (Guid?)trackArtist.RoadieId))
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
}
|
||||||
if (includes.Contains("comments"))
|
if (includes.Contains("comments"))
|
||||||
{
|
{
|
||||||
var playlistComments = DbContext.Comments.Include(x => x.User)
|
var playlistComments = DbContext.Comments.Include(x => x.User)
|
||||||
.Where(x => x.PlaylistId == playlist.Id).OrderByDescending(x => x.CreatedDate).ToArray();
|
.Where(x => x.PlaylistId == playlist.Id)
|
||||||
|
.OrderByDescending(x => x.CreatedDate)
|
||||||
|
.ToArray();
|
||||||
if (playlistComments.Any())
|
if (playlistComments.Any())
|
||||||
{
|
{
|
||||||
var comments = new List<Comment>();
|
var comments = new List<Comment>();
|
||||||
|
@ -468,15 +474,11 @@ namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
var comment = playlistComment.Adapt<Comment>();
|
var comment = playlistComment.Adapt<Comment>();
|
||||||
comment.DatabaseId = playlistComment.Id;
|
comment.DatabaseId = playlistComment.Id;
|
||||||
comment.User = UserList.FromDataUser(playlistComment.User,
|
comment.User = UserList.FromDataUser(playlistComment.User, MakeUserThumbnailImage(playlistComment.User.RoadieId));
|
||||||
MakeUserThumbnailImage(playlistComment.User.RoadieId));
|
comment.DislikedCount = userCommentReactions.Count(x => x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Dislike);
|
||||||
comment.DislikedCount = userCommentReactions.Count(x =>
|
comment.LikedCount = userCommentReactions.Count(x => x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Like);
|
||||||
x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Dislike);
|
|
||||||
comment.LikedCount = userCommentReactions.Count(x =>
|
|
||||||
x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Like);
|
|
||||||
comments.Add(comment);
|
comments.Add(comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Comments = comments;
|
result.Comments = comments;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,8 +211,16 @@ namespace Roadie.Api.Services
|
||||||
select a.Id;
|
select a.Id;
|
||||||
IQueryable<int> genreReleaseIds = null;
|
IQueryable<int> genreReleaseIds = null;
|
||||||
var isFilteredToGenre = false;
|
var isFilteredToGenre = false;
|
||||||
if (!string.IsNullOrEmpty(request.FilterByGenre) || !string.IsNullOrEmpty(request.Filter) &&
|
if(request.FilterToGenreId.HasValue)
|
||||||
request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase))
|
{
|
||||||
|
genreReleaseIds = (from rg in DbContext.ReleaseGenres
|
||||||
|
join g in DbContext.Genres on rg.GenreId equals g.Id
|
||||||
|
where g.RoadieId == request.FilterToGenreId
|
||||||
|
select rg.ReleaseId)
|
||||||
|
.Distinct();
|
||||||
|
isFilteredToGenre = true;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(request.FilterByGenre) || !string.IsNullOrEmpty(request.Filter) && request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var genreFilter = request.FilterByGenre ??
|
var genreFilter = request.FilterByGenre ??
|
||||||
(request.Filter ?? string.Empty).Replace(":genre ", "",
|
(request.Filter ?? string.Empty).Replace(":genre ", "",
|
||||||
|
@ -221,7 +229,7 @@ namespace Roadie.Api.Services
|
||||||
join g in DbContext.Genres on rg.GenreId equals g.Id
|
join g in DbContext.Genres on rg.GenreId equals g.Id
|
||||||
where g.Name.Contains(genreFilter)
|
where g.Name.Contains(genreFilter)
|
||||||
select rg.ReleaseId)
|
select rg.ReleaseId)
|
||||||
.Distinct();
|
.Distinct();
|
||||||
request.Filter = null;
|
request.Filter = null;
|
||||||
isFilteredToGenre = true;
|
isFilteredToGenre = true;
|
||||||
}
|
}
|
||||||
|
@ -314,9 +322,11 @@ namespace Roadie.Api.Services
|
||||||
|
|
||||||
if (doRandomize ?? false)
|
if (doRandomize ?? false)
|
||||||
{
|
{
|
||||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? 100;
|
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||||
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
||||||
rows = result.OrderBy(x => x.RandomSortId).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
rows = result.OrderBy(x => x.RandomSortId)
|
||||||
|
.Take(request.LimitValue)
|
||||||
|
.ToArray();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -125,6 +125,15 @@ namespace Roadie.Api.Services
|
||||||
}, data.Label.CacheRegionUrn(id));
|
}, data.Label.CacheRegionUrn(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected data.Genre GetGenre(Guid id)
|
||||||
|
{
|
||||||
|
return CacheManager.Get(data.Genre.CacheUrn(id), () =>
|
||||||
|
{
|
||||||
|
return DbContext.Genres
|
||||||
|
.FirstOrDefault(x => x.RoadieId == id);
|
||||||
|
}, data.Genre.CacheRegionUrn(id));
|
||||||
|
}
|
||||||
|
|
||||||
protected data.Playlist GetPlaylist(Guid id)
|
protected data.Playlist GetPlaylist(Guid id)
|
||||||
{
|
{
|
||||||
return CacheManager.Get(data.Playlist.CacheUrn(id), () =>
|
return CacheManager.Get(data.Playlist.CacheUrn(id), () =>
|
||||||
|
@ -245,6 +254,11 @@ namespace Roadie.Api.Services
|
||||||
return MakeThumbnailImage(id, "label");
|
return MakeThumbnailImage(id, "label");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Image MakeGenreThumbnailImage(Guid id)
|
||||||
|
{
|
||||||
|
return MakeThumbnailImage(id, "genre");
|
||||||
|
}
|
||||||
|
|
||||||
protected string MakeLastFmUrl(string artistName, string releaseTitle)
|
protected string MakeLastFmUrl(string artistName, string releaseTitle)
|
||||||
{
|
{
|
||||||
return "http://www.last.fm/music/" + HttpEncoder.UrlEncode($"{artistName}/{releaseTitle}");
|
return "http://www.last.fm/music/" + HttpEncoder.UrlEncode($"{artistName}/{releaseTitle}");
|
||||||
|
|
|
@ -518,8 +518,12 @@ namespace Roadie.Api.Services
|
||||||
|
|
||||||
if (doRandomize ?? false)
|
if (doRandomize ?? false)
|
||||||
{
|
{
|
||||||
|
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||||
|
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
||||||
|
|
||||||
rows = result.OrderBy(x => x.Artist.RandomSortId)
|
rows = result.OrderBy(x => x.Artist.RandomSortId)
|
||||||
.ThenBy(x => x.RandomSortId)
|
.ThenBy(x => x.RandomSortId)
|
||||||
|
.Take(request.LimitValue)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -50,14 +50,20 @@ namespace Roadie.Api.Services
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes)
|
public async Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes, bool isAccountSettingsEdit = false)
|
||||||
{
|
{
|
||||||
var timings = new Dictionary<string, long>();
|
var timings = new Dictionary<string, long>();
|
||||||
var tsw = new Stopwatch();
|
var tsw = new Stopwatch();
|
||||||
|
if(isAccountSettingsEdit)
|
||||||
|
{
|
||||||
|
if(user.UserId != id && !user.IsAdmin)
|
||||||
|
{
|
||||||
|
return new OperationResult<User>(new Exception("Access Denied"));
|
||||||
|
}
|
||||||
|
}
|
||||||
var sw = Stopwatch.StartNew();
|
var sw = Stopwatch.StartNew();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
var cacheKey = string.Format("urn:user_by_id_operation:{0}", id);
|
var cacheKey = string.Format("urn:user_by_id_operation:{0}:{1}", id, isAccountSettingsEdit);
|
||||||
var result = await CacheManager.GetAsync(cacheKey, async () =>
|
var result = await CacheManager.GetAsync(cacheKey, async () =>
|
||||||
{
|
{
|
||||||
tsw.Restart();
|
tsw.Restart();
|
||||||
|
@ -67,7 +73,15 @@ namespace Roadie.Api.Services
|
||||||
return rr;
|
return rr;
|
||||||
}, ApplicationUser.CacheRegionUrn(id));
|
}, ApplicationUser.CacheRegionUrn(id));
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
if (result?.Data != null) result.Data.Avatar = MakeUserThumbnailImage(id);
|
if (result?.Data != null)
|
||||||
|
{
|
||||||
|
result.Data.Avatar = MakeUserThumbnailImage(id);
|
||||||
|
if(!isAccountSettingsEdit)
|
||||||
|
{
|
||||||
|
result.Data.ApiToken = null;
|
||||||
|
result.Data.ConcurrencyStamp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
timings.Add("operation", sw.ElapsedMilliseconds);
|
timings.Add("operation", sw.ElapsedMilliseconds);
|
||||||
Logger.LogDebug("ById Timings: id [{0}]", id);
|
Logger.LogDebug("ById Timings: id [{0}]", id);
|
||||||
return new OperationResult<User>(result.Messages)
|
return new OperationResult<User>(result.Messages)
|
||||||
|
@ -101,6 +115,7 @@ namespace Roadie.Api.Services
|
||||||
Value = u.RoadieId.ToString()
|
Value = u.RoadieId.ToString()
|
||||||
},
|
},
|
||||||
IsEditor = u.UserRoles.Any(x => x.Role.Name == "Editor"),
|
IsEditor = u.UserRoles.Any(x => x.Role.Name == "Editor"),
|
||||||
|
IsAdmin = u.UserRoles.Any(x => x.Role.Name == "Admin"),
|
||||||
IsPrivate = u.IsPrivate,
|
IsPrivate = u.IsPrivate,
|
||||||
Thumbnail = MakeUserThumbnailImage(u.RoadieId),
|
Thumbnail = MakeUserThumbnailImage(u.RoadieId),
|
||||||
CreatedDate = u.CreatedDate,
|
CreatedDate = u.CreatedDate,
|
||||||
|
@ -119,15 +134,18 @@ namespace Roadie.Api.Services
|
||||||
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
||||||
|
|
||||||
if (rows.Any())
|
if (rows.Any())
|
||||||
|
{
|
||||||
foreach (var row in rows)
|
foreach (var row in rows)
|
||||||
{
|
{
|
||||||
var userArtists = DbContext.UserArtists.Include(x => x.Artist)
|
var userArtists = DbContext.UserArtists.Include(x => x.Artist)
|
||||||
.Where(x => x.UserId == row.DatabaseId).ToArray();
|
.Where(x => x.UserId == row.DatabaseId)
|
||||||
|
.ToArray();
|
||||||
var userReleases = DbContext.UserReleases.Include(x => x.Release)
|
var userReleases = DbContext.UserReleases.Include(x => x.Release)
|
||||||
.Where(x => x.UserId == row.DatabaseId).ToArray();
|
.Where(x => x.UserId == row.DatabaseId)
|
||||||
var userTracks = DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == row.DatabaseId)
|
.ToArray();
|
||||||
.ToArray();
|
var userTracks = DbContext.UserTracks.Include(x => x.Track)
|
||||||
|
.Where(x => x.UserId == row.DatabaseId)
|
||||||
|
.ToArray();
|
||||||
row.Statistics = new UserStatistics
|
row.Statistics = new UserStatistics
|
||||||
{
|
{
|
||||||
RatedArtists = userArtists.Where(x => x.Rating > 0).Count(),
|
RatedArtists = userArtists.Where(x => x.Rating > 0).Count(),
|
||||||
|
@ -142,6 +160,7 @@ namespace Roadie.Api.Services
|
||||||
DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count()
|
DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
return Task.FromResult(new Library.Models.Pagination.PagedResult<UserList>
|
return Task.FromResult(new Library.Models.Pagination.PagedResult<UserList>
|
||||||
|
@ -347,41 +366,48 @@ namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId);
|
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
return new OperationResult<bool>(true,
|
{
|
||||||
string.Format("User Not Found [{0}]", userBeingUpdatedModel.UserId));
|
return new OperationResult<bool>(true, string.Format("User Not Found [{0}]", userBeingUpdatedModel.UserId));
|
||||||
|
}
|
||||||
if (user.Id != userPerformingUpdate.Id && !userPerformingUpdate.IsAdmin)
|
if (user.Id != userPerformingUpdate.Id && !userPerformingUpdate.IsAdmin)
|
||||||
|
{
|
||||||
return new OperationResult<bool>
|
return new OperationResult<bool>
|
||||||
{
|
{
|
||||||
Errors = new List<Exception> { new Exception("Access Denied") }
|
Errors = new List<Exception> { new Exception("Access Denied") }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
// Check concurrency stamp
|
// Check concurrency stamp
|
||||||
if (user.ConcurrencyStamp != userBeingUpdatedModel.ConcurrencyStamp)
|
if (user.ConcurrencyStamp != userBeingUpdatedModel.ConcurrencyStamp)
|
||||||
|
{
|
||||||
return new OperationResult<bool>
|
return new OperationResult<bool>
|
||||||
{
|
{
|
||||||
Errors = new List<Exception> { new Exception("User data is stale.") }
|
Errors = new List<Exception> { new Exception("User data is stale.") }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
// Check that username (if changed) doesn't already exist
|
// Check that username (if changed) doesn't already exist
|
||||||
if (user.UserName != userBeingUpdatedModel.UserName)
|
if (user.UserName != userBeingUpdatedModel.UserName)
|
||||||
{
|
{
|
||||||
var userByUsername = DbContext.Users.FirstOrDefault(x =>
|
var userByUsername = DbContext.Users.FirstOrDefault(x => x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper());
|
||||||
x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper());
|
|
||||||
if (userByUsername != null)
|
if (userByUsername != null)
|
||||||
|
{
|
||||||
return new OperationResult<bool>
|
return new OperationResult<bool>
|
||||||
{
|
{
|
||||||
Errors = new List<Exception> { new Exception("Username already in use") }
|
Errors = new List<Exception> { new Exception("Username already in use") }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that email (if changed) doesn't already exist
|
// Check that email (if changed) doesn't already exist
|
||||||
if (user.Email != userBeingUpdatedModel.Email)
|
if (user.Email != userBeingUpdatedModel.Email)
|
||||||
{
|
{
|
||||||
var userByEmail =
|
var userByEmail = DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper());
|
||||||
DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper());
|
|
||||||
if (userByEmail != null)
|
if (userByEmail != null)
|
||||||
|
{
|
||||||
return new OperationResult<bool>
|
return new OperationResult<bool>
|
||||||
{
|
{
|
||||||
Errors = new List<Exception> { new Exception("Email already in use") }
|
Errors = new List<Exception> { new Exception("Email already in use") }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var oldPathToImage = user.PathToImage(Configuration);
|
var oldPathToImage = user.PathToImage(Configuration);
|
||||||
var didChangeName = user.UserName != userBeingUpdatedModel.UserName;
|
var didChangeName = user.UserName != userBeingUpdatedModel.UserName;
|
||||||
|
@ -405,6 +431,7 @@ namespace Roadie.Api.Services
|
||||||
user.FtpUsername = userBeingUpdatedModel.FtpUsername;
|
user.FtpUsername = userBeingUpdatedModel.FtpUsername;
|
||||||
user.FtpPassword = EncryptionHelper.Encrypt(userBeingUpdatedModel.FtpPassword, user.RoadieId.ToString());
|
user.FtpPassword = EncryptionHelper.Encrypt(userBeingUpdatedModel.FtpPassword, user.RoadieId.ToString());
|
||||||
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||||
|
user.DefaultRowsPerPage = userBeingUpdatedModel.DefaultRowsPerPage;
|
||||||
|
|
||||||
if (didChangeName)
|
if (didChangeName)
|
||||||
{
|
{
|
||||||
|
@ -414,7 +441,6 @@ namespace Roadie.Api.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(userBeingUpdatedModel.AvatarData))
|
if (!string.IsNullOrEmpty(userBeingUpdatedModel.AvatarData))
|
||||||
{
|
{
|
||||||
var imageData = ImageHelper.ImageDataFromUrl(userBeingUpdatedModel.AvatarData);
|
var imageData = ImageHelper.ImageDataFromUrl(userBeingUpdatedModel.AvatarData);
|
||||||
|
@ -560,33 +586,43 @@ namespace Roadie.Api.Services
|
||||||
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
|
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
|
||||||
where ut.UserId == user.Id
|
where ut.UserId == user.Id
|
||||||
select new { a, ut.PlayedCount })
|
select new { a, ut.PlayedCount })
|
||||||
.GroupBy(a => a.a)
|
.GroupBy(a => a.a)
|
||||||
.Select(x => new
|
.Select(x => new
|
||||||
{
|
{
|
||||||
Artist = x.Key,
|
Artist = x.Key,
|
||||||
Played = x.Sum(t => t.PlayedCount)
|
Played = x.Sum(t => t.PlayedCount)
|
||||||
})
|
})
|
||||||
.OrderByDescending(x => x.Played)
|
.OrderByDescending(x => x.Played)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
var mostPlayedReleaseId = (from r in DbContext.Releases
|
var mostPlayedReleaseId = (from r in DbContext.Releases
|
||||||
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||||
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
||||||
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
|
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
|
||||||
where ut.UserId == user.Id
|
where ut.UserId == user.Id
|
||||||
select new { r, ut.PlayedCount })
|
select new { r, ut.PlayedCount })
|
||||||
.GroupBy(r => r.r)
|
.GroupBy(r => r.r)
|
||||||
.Select(x => new
|
.Select(x => new
|
||||||
{
|
{
|
||||||
Release = x.Key,
|
Release = x.Key,
|
||||||
Played = x.Sum(t => t.PlayedCount)
|
Played = x.Sum(t => t.PlayedCount)
|
||||||
})
|
})
|
||||||
.OrderByDescending(x => x.Played)
|
.OrderByDescending(x => x.Played)
|
||||||
.Select(x => x.Release.RoadieId)
|
.Select(x => x.Release.RoadieId)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
var mostPlayedRelease = GetRelease(mostPlayedReleaseId);
|
var mostPlayedRelease = GetRelease(mostPlayedReleaseId);
|
||||||
var mostPlayedTrackUserTrack = userTracks
|
var mostPlayedTrackUserTrack = userTracks.OrderByDescending(x => x.PlayedCount)
|
||||||
.OrderByDescending(x => x.PlayedCount)
|
.FirstOrDefault();
|
||||||
.FirstOrDefault();
|
var lastPlayedTrackUserTrack = userTracks.OrderByDescending(x => x.LastPlayed)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
var lastPlayedTrack = lastPlayedTrackUserTrack == null
|
||||||
|
? null
|
||||||
|
: DbContext.Tracks
|
||||||
|
.Include(x => x.TrackArtist)
|
||||||
|
.Include(x => x.ReleaseMedia)
|
||||||
|
.Include("ReleaseMedia.Release")
|
||||||
|
.Include("ReleaseMedia.Release.Artist")
|
||||||
|
.FirstOrDefault(x => x.Id == lastPlayedTrackUserTrack.TrackId);
|
||||||
var mostPlayedTrack = mostPlayedTrackUserTrack == null
|
var mostPlayedTrack = mostPlayedTrackUserTrack == null
|
||||||
? null
|
? null
|
||||||
: DbContext.Tracks
|
: DbContext.Tracks
|
||||||
|
@ -598,6 +634,22 @@ namespace Roadie.Api.Services
|
||||||
|
|
||||||
model.Statistics = new UserStatistics
|
model.Statistics = new UserStatistics
|
||||||
{
|
{
|
||||||
|
LastPlayedTrack = lastPlayedTrack == null
|
||||||
|
? null
|
||||||
|
: models.TrackList.FromDataTrack(
|
||||||
|
MakeTrackPlayUrl(user, lastPlayedTrack.Id, lastPlayedTrack.RoadieId),
|
||||||
|
lastPlayedTrack,
|
||||||
|
lastPlayedTrack.ReleaseMedia.MediaNumber,
|
||||||
|
lastPlayedTrack.ReleaseMedia.Release,
|
||||||
|
lastPlayedTrack.ReleaseMedia.Release.Artist,
|
||||||
|
lastPlayedTrack.TrackArtist,
|
||||||
|
HttpContext.BaseUrl,
|
||||||
|
MakeTrackThumbnailImage(lastPlayedTrack.RoadieId),
|
||||||
|
MakeReleaseThumbnailImage(lastPlayedTrack.ReleaseMedia.Release.RoadieId),
|
||||||
|
MakeArtistThumbnailImage(lastPlayedTrack.ReleaseMedia.Release.Artist.RoadieId),
|
||||||
|
MakeArtistThumbnailImage(lastPlayedTrack.TrackArtist == null
|
||||||
|
? null
|
||||||
|
: (Guid?)lastPlayedTrack.TrackArtist.RoadieId)),
|
||||||
MostPlayedArtist = mostPlayedArtist == null
|
MostPlayedArtist = mostPlayedArtist == null
|
||||||
? null
|
? null
|
||||||
: models.ArtistList.FromDataArtist(mostPlayedArtist.Artist,
|
: models.ArtistList.FromDataArtist(mostPlayedArtist.Artist,
|
||||||
|
@ -634,7 +686,7 @@ namespace Roadie.Api.Services
|
||||||
RatedTracks = userTracks.Where(x => x.Rating > 0).Count(),
|
RatedTracks = userTracks.Where(x => x.Rating > 0).Count(),
|
||||||
PlayedTracks = userTracks.Where(x => x.PlayedCount.HasValue).Select(x => x.PlayedCount).Sum(),
|
PlayedTracks = userTracks.Where(x => x.PlayedCount.HasValue).Select(x => x.PlayedCount).Sum(),
|
||||||
FavoritedTracks = userTracks.Where(x => x.IsFavorite ?? false).Count(),
|
FavoritedTracks = userTracks.Where(x => x.IsFavorite ?? false).Count(),
|
||||||
DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count()
|
DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,9 +102,11 @@ namespace Roadie.Api.Controllers
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Login user
|
// Login user
|
||||||
var loginResult =
|
var loginResult = await SignInManager.PasswordSignInAsync(model.Username, model.Password, false, false);
|
||||||
await SignInManager.PasswordSignInAsync(model.Username, model.Password, false, false);
|
if (!loginResult.Succeeded)
|
||||||
if (!loginResult.Succeeded) return BadRequest();
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
var user = await UserManager.FindByNameAsync(model.Username);
|
var user = await UserManager.FindByNameAsync(model.Username);
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
user.LastLogin = now;
|
user.LastLogin = now;
|
||||||
|
@ -127,8 +129,7 @@ namespace Roadie.Api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
|
CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
|
||||||
var avatarUrl =
|
var avatarUrl = $"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}";
|
||||||
$"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}";
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
Username = user.UserName,
|
Username = user.UserName,
|
||||||
|
@ -139,7 +140,8 @@ namespace Roadie.Api.Controllers
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
Token = t,
|
Token = t,
|
||||||
user.Timeformat,
|
user.Timeformat,
|
||||||
user.Timezone
|
user.Timezone,
|
||||||
|
DefaultRowsPerPage = user.DefaultRowsPerPage ?? RoadieSettings.DefaultRowsPerPage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
@ -82,6 +82,15 @@ namespace Roadie.Api.Controllers
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("delete/genre/{id}")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
public async Task<IActionResult> DeleteGenre(Guid id)
|
||||||
|
{
|
||||||
|
var result = await AdminService.DeleteGenre(await UserManager.GetUserAsync(User), id);
|
||||||
|
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("delete/releasesecondaryimage/{id}/{index}")]
|
[HttpPost("delete/releasesecondaryimage/{id}/{index}")]
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
public async Task<IActionResult> DeleteReleaseSecondaryImage(Guid id, int index)
|
public async Task<IActionResult> DeleteReleaseSecondaryImage(Guid id, int index)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -10,6 +11,8 @@ using Roadie.Library.Models.Pagination;
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using models = Roadie.Library.Models;
|
||||||
|
|
||||||
namespace Roadie.Api.Controllers
|
namespace Roadie.Api.Controllers
|
||||||
{
|
{
|
||||||
|
@ -29,33 +32,16 @@ namespace Roadie.Api.Controllers
|
||||||
GenreService = genreService;
|
GenreService = genreService;
|
||||||
}
|
}
|
||||||
|
|
||||||
//[EnableQuery]
|
[HttpGet("{id}")]
|
||||||
//public IActionResult Get()
|
[ProducesResponseType(200)]
|
||||||
//{
|
[ProducesResponseType(404)]
|
||||||
// return Ok(this._RoadieDbContext.Labels.ProjectToType<models.Label>());
|
public async Task<IActionResult> Get(Guid id, string inc = null)
|
||||||
//}
|
{
|
||||||
|
var result = await GenreService.ById(await CurrentUserModel(), id,(inc ?? models.Genre.DefaultIncludes).ToLower().Split(","));
|
||||||
//[HttpGet("{id}")]
|
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||||
//[ProducesResponseType(200)]
|
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
//[ProducesResponseType(404)]
|
return Ok(result);
|
||||||
//public IActionResult Get(Guid id)
|
}
|
||||||
//{
|
|
||||||
// var key = id.ToString();
|
|
||||||
// var result = this._cacheManager.Get<models.Label>(key, () =>
|
|
||||||
// {
|
|
||||||
// var d = this._RoadieDbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
|
|
||||||
// if (d != null)
|
|
||||||
// {
|
|
||||||
// return d.Adapt<models.Label>();
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
// }, key);
|
|
||||||
// if (result == null)
|
|
||||||
// {
|
|
||||||
// return NotFound();
|
|
||||||
// }
|
|
||||||
// return Ok(result);
|
|
||||||
//}
|
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
|
@ -80,5 +66,35 @@ namespace Roadie.Api.Controllers
|
||||||
|
|
||||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("setImageByUrl/{id}/{imageUrl}")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(404)]
|
||||||
|
[Authorize(Policy = "Editor")]
|
||||||
|
public async Task<IActionResult> SetGenreImageByUrl(Guid id, string imageUrl)
|
||||||
|
{
|
||||||
|
var result = await GenreService.SetGenreImageByUrl(await CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
|
||||||
|
if (result == null || result.IsNotFoundResult)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
if (!result.IsSuccess)
|
||||||
|
{
|
||||||
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
|
}
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("uploadImage/{id}")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(404)]
|
||||||
|
[Authorize(Policy = "Editor")]
|
||||||
|
public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
|
||||||
|
{
|
||||||
|
var result = await GenreService.UploadGenreImage(await CurrentUserModel(), id, file);
|
||||||
|
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||||
|
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -117,6 +117,21 @@ namespace Roadie.Api.Controllers
|
||||||
result.ETag);
|
result.ETag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("genre/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(404)]
|
||||||
|
public async Task<IActionResult> GenreImage(Guid id, int? width, int? height)
|
||||||
|
{
|
||||||
|
var result = await ImageService.GenreImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height);
|
||||||
|
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||||
|
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
|
return File(result.Data.Bytes,
|
||||||
|
result.ContentType,
|
||||||
|
$"{result.Data.Caption ?? id.ToString()}.jpg",
|
||||||
|
result.LastModified,
|
||||||
|
result.ETag);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("playlist/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
|
[HttpGet("playlist/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
[ProducesResponseType(404)]
|
[ProducesResponseType(404)]
|
||||||
|
@ -186,6 +201,17 @@ namespace Roadie.Api.Controllers
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("search/genre/{query}/{resultsCount:int?}")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(404)]
|
||||||
|
public async Task<IActionResult> SearchForGenreImage(string query, int? resultsCount)
|
||||||
|
{
|
||||||
|
var result = await ImageService.Search(query, resultsCount ?? 10);
|
||||||
|
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||||
|
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("search/release/{query}/{resultsCount:int?}")]
|
[HttpPost("search/release/{query}/{resultsCount:int?}")]
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
[ProducesResponseType(404)]
|
[ProducesResponseType(404)]
|
||||||
|
|
|
@ -40,6 +40,36 @@ namespace Roadie.Api.Controllers
|
||||||
RoadieHttpContext = httpContext;
|
RoadieHttpContext = httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("accountsettings/{id}")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(204)]
|
||||||
|
[ProducesResponseType(404)]
|
||||||
|
public async Task<IActionResult> GetAccountSettings(Guid id, string inc = null)
|
||||||
|
{
|
||||||
|
var user = await CurrentUserModel();
|
||||||
|
var result = await CacheManager.GetAsync($"urn:user_edit_model_by_id:{id}", async () =>
|
||||||
|
{
|
||||||
|
return await UserService.ById(user, id, (inc ?? Library.Models.Users.User.DefaultIncludes).ToLower().Split(","), true);
|
||||||
|
}, ControllerCacheRegionUrn);
|
||||||
|
if (result == null || result.IsNotFoundResult)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
if (!result.IsSuccess)
|
||||||
|
{
|
||||||
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
|
}
|
||||||
|
result.AdditionalClientData = new Dictionary<string, object>();
|
||||||
|
if (RoadieSettings.Integrations.LastFmProviderEnabled)
|
||||||
|
{
|
||||||
|
var lastFmCallBackUrl = $"{RoadieHttpContext.BaseUrl}/users/integration/grant?userId={user.UserId}&iname=lastfm";
|
||||||
|
result.AdditionalClientData.Add("lastFMIntegrationUrl", $"http://www.last.fm/api/auth/?api_key={RoadieSettings.Integrations.LastFMApiKey}&cb={WebUtility.UrlEncode(lastFmCallBackUrl)}");
|
||||||
|
}
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
[ProducesResponseType(204)]
|
[ProducesResponseType(204)]
|
||||||
|
@ -52,17 +82,14 @@ namespace Roadie.Api.Controllers
|
||||||
{
|
{
|
||||||
return await UserService.ById(user, id, (inc ?? Library.Models.Users.User.DefaultIncludes).ToLower().Split(","));
|
return await UserService.ById(user, id, (inc ?? Library.Models.Users.User.DefaultIncludes).ToLower().Split(","));
|
||||||
}, ControllerCacheRegionUrn);
|
}, ControllerCacheRegionUrn);
|
||||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
if (result == null || result.IsNotFoundResult)
|
||||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
||||||
result.AdditionalClientData = new Dictionary<string, object>();
|
|
||||||
if (RoadieSettings.Integrations.LastFmProviderEnabled)
|
|
||||||
{
|
{
|
||||||
var lastFmCallBackUrl =
|
return NotFound();
|
||||||
$"{RoadieHttpContext.BaseUrl}/users/integration/grant?userId={user.UserId}&iname=lastfm";
|
}
|
||||||
result.AdditionalClientData.Add("lastFMIntegrationUrl",
|
if (!result.IsSuccess)
|
||||||
$"http://www.last.fm/api/auth/?api_key={RoadieSettings.Integrations.LastFMApiKey}&cb={WebUtility.UrlEncode(lastFmCallBackUrl)}");
|
{
|
||||||
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +266,10 @@ namespace Roadie.Api.Controllers
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
public async Task<IActionResult> UpdateProfile(User model)
|
public async Task<IActionResult> UpdateProfile(User model)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
var user = await CurrentUserModel();
|
var user = await CurrentUserModel();
|
||||||
var result = await UserService.UpdateProfile(user, model);
|
var result = await UserService.UpdateProfile(user, model);
|
||||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||||
|
@ -260,7 +290,8 @@ namespace Roadie.Api.Controllers
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
Token = t,
|
Token = t,
|
||||||
modelUser.Timeformat,
|
modelUser.Timeformat,
|
||||||
modelUser.Timezone
|
modelUser.Timezone,
|
||||||
|
DefaultRowsPerPage = modelUser.DefaultRowsPerPage ?? RoadieSettings.DefaultRowsPerPage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
Roadie.Api/wwwroot/Images/genre.jpg
Normal file
BIN
Roadie.Api/wwwroot/Images/genre.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 187 KiB |
|
@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{1BA7
|
||||||
Upgrade0003.sql = Upgrade0003.sql
|
Upgrade0003.sql = Upgrade0003.sql
|
||||||
Upgrade0004.sql = Upgrade0004.sql
|
Upgrade0004.sql = Upgrade0004.sql
|
||||||
Upgrade0005.sql = Upgrade0005.sql
|
Upgrade0005.sql = Upgrade0005.sql
|
||||||
|
Upgrade0006.sql = Upgrade0006.sql
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roadie.Api.Services", "Roadie.Api.Services\Roadie.Api.Services.csproj", "{7B37031E-F2AE-4BE2-9F6F-005CA7A6FDF1}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roadie.Api.Services", "Roadie.Api.Services\Roadie.Api.Services.csproj", "{7B37031E-F2AE-4BE2-9F6F-005CA7A6FDF1}"
|
||||||
|
|
6
Upgrade0006.sql
Normal file
6
Upgrade0006.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
-- v1.0.3.1 -- Genre tables modifications for https://github.com/sphildreth/roadie-vuejs/issues/63
|
||||||
|
ALTER TABLE `genre` ADD `thumbnail` BLOB NULL;
|
||||||
|
ALTER TABLE `genre` ADD `alternateNames` MEDIUMTEXT NULL;
|
||||||
|
ALTER TABLE `genre` ADD `description` varchar(4000) NULL;
|
||||||
|
ALTER TABLE `genre` ADD `tags` MEDIUMTEXT NULL;
|
||||||
|
ALTER TABLE `user` ADD `defaultRowsPerPage` SMALLINT NULL;
|
18
roadie.sql
18
roadie.sql
|
@ -1,5 +1,5 @@
|
||||||
-- ///
|
-- ///
|
||||||
-- Roadie version 1.0.2.3 new database script, if upgrading skip this and run Upgrade*.sql scripts from your version to current.
|
-- Roadie version 1.0.3.1 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)
|
-- MySQL dump 10.17 Distrib 10.3.16-MariaDB, for Linux (x86_64)
|
||||||
--
|
--
|
||||||
|
@ -338,6 +338,10 @@ CREATE TABLE `genre` (
|
||||||
`lastUpdated` datetime DEFAULT NULL,
|
`lastUpdated` datetime DEFAULT NULL,
|
||||||
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
`normalizedName` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
`normalizedName` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||||
|
`thumbnail` blob DEFAULT NULL,
|
||||||
|
`alternateNames` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||||
|
`description` varchar(4000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||||
|
`tags` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `ix_genre_name` (`name`),
|
UNIQUE KEY `ix_genre_name` (`name`),
|
||||||
KEY `ix_genre_roadieId` (`roadieId`),
|
KEY `ix_genre_roadieId` (`roadieId`),
|
||||||
|
@ -629,7 +633,7 @@ CREATE TABLE `request` (
|
||||||
KEY `ix_request_roadieId` (`roadieId`),
|
KEY `ix_request_roadieId` (`roadieId`),
|
||||||
KEY `requestartist_ibfk_1` (`userId`),
|
KEY `requestartist_ibfk_1` (`userId`),
|
||||||
CONSTRAINT `requestartist_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE
|
CONSTRAINT `requestartist_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB AUTO_INCREMENT=148 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -655,7 +659,7 @@ CREATE TABLE `scanHistory` (
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `ix_scanHistory_roadieId` (`roadieId`),
|
KEY `ix_scanHistory_roadieId` (`roadieId`),
|
||||||
KEY `rscanHistoryt_ibfk_1` (`userId`)
|
KEY `rscanHistoryt_ibfk_1` (`userId`)
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=97 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -723,7 +727,7 @@ CREATE TABLE `track` (
|
||||||
KEY `track_artistId_IDX` (`artistId`) USING BTREE,
|
KEY `track_artistId_IDX` (`artistId`) USING BTREE,
|
||||||
KEY `track_releaseMediaId_IDX` (`releaseMediaId`) USING BTREE,
|
KEY `track_releaseMediaId_IDX` (`releaseMediaId`) USING BTREE,
|
||||||
CONSTRAINT `track_ibfk_1` FOREIGN KEY (`releaseMediaId`) REFERENCES `releasemedia` (`id`) ON DELETE CASCADE
|
CONSTRAINT `track_ibfk_1` FOREIGN KEY (`releaseMediaId`) REFERENCES `releasemedia` (`id`) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=615 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB AUTO_INCREMENT=623 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -959,7 +963,7 @@ CREATE TABLE `usersInRoles` (
|
||||||
KEY `ix_usersInRoles_userId` (`userId`),
|
KEY `ix_usersInRoles_userId` (`userId`),
|
||||||
CONSTRAINT `usersInRoles_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE,
|
CONSTRAINT `usersInRoles_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE,
|
||||||
CONSTRAINT `usersInRoles_ibfk_2` FOREIGN KEY (`userRoleId`) REFERENCES `userrole` (`id`) ON DELETE CASCADE
|
CONSTRAINT `usersInRoles_ibfk_2` FOREIGN KEY (`userRoleId`) REFERENCES `userrole` (`id`) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -989,7 +993,7 @@ CREATE TABLE `usertrack` (
|
||||||
KEY `ix_usertrack_roadieId` (`roadieId`),
|
KEY `ix_usertrack_roadieId` (`roadieId`),
|
||||||
CONSTRAINT `usertrack_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE,
|
CONSTRAINT `usertrack_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE,
|
||||||
CONSTRAINT `usertrack_ibfk_2` FOREIGN KEY (`trackId`) REFERENCES `track` (`id`) ON DELETE CASCADE
|
CONSTRAINT `usertrack_ibfk_2` FOREIGN KEY (`trackId`) REFERENCES `track` (`id`) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=237 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB AUTO_INCREMENT=238 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -1075,4 +1079,4 @@ SET character_set_client = @saved_cs_client;
|
||||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
-- Dump completed on 2019-07-18 17:31:08
|
-- Dump completed on 2019-08-02 11:14:18
|
||||||
|
|
Loading…
Reference in a new issue