mirror of
https://github.com/sphildreth/roadie
synced 2024-11-10 06:44:12 +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 LabelImageFolder { get; }
|
||||
string CollectionImageFolder { get; }
|
||||
string GenreImageFolder { get; }
|
||||
string PlaylistImageFolder { get; }
|
||||
string UserImageFolder { get; }
|
||||
string ListenAddress { get; set; }
|
||||
|
@ -47,5 +48,6 @@ namespace Roadie.Library.Configuration
|
|||
bool IsRegistrationClosed { get; set; }
|
||||
bool UseRegistrationTokens { 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
|
||||
{
|
||||
get
|
||||
|
@ -133,6 +141,8 @@ namespace Roadie.Library.Configuration
|
|||
/// </summary>
|
||||
public string SearchEngineReposFolder { get; set; }
|
||||
|
||||
public short DefaultRowsPerPage { get; set; }
|
||||
|
||||
public RoadieSettings()
|
||||
{
|
||||
ArtistNameReplace = new Dictionary<string, IEnumerable<string>>
|
||||
|
@ -152,6 +162,7 @@ namespace Roadie.Library.Configuration
|
|||
SiteName = "Roadie";
|
||||
SmallImageSize = new ImageSize { Width = 160, Height = 160 };
|
||||
ThumbnailImageSize = new ImageSize { Width = 80, Height = 80 };
|
||||
DefaultRowsPerPage = 12;
|
||||
|
||||
SmtpFromAddress = "noreply@roadie.rocks";
|
||||
SmtpPort = 587;
|
||||
|
|
|
@ -13,6 +13,22 @@ namespace Roadie.Library.Data
|
|||
|
||||
[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; }
|
||||
|
||||
public ICollection<ReleaseGenre> Releases { get; set; }
|
||||
|
@ -22,6 +38,7 @@ namespace Roadie.Library.Data
|
|||
Releases = new HashSet<ReleaseGenre>();
|
||||
Artists = new HashSet<ArtistGenre>();
|
||||
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
|
||||
{
|
||||
|
@ -8,6 +11,14 @@ namespace Roadie.Library.Data
|
|||
|
||||
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)
|
||||
{
|
||||
return string.Format("urn:genre:{0}", Id);
|
||||
|
|
|
@ -118,6 +118,9 @@ namespace Roadie.Library.Identity
|
|||
[StringLength(50)]
|
||||
public string Timezone { get; set; }
|
||||
|
||||
[Column("defaultRowsPerPage")]
|
||||
public short? DefaultRowsPerPage { get; set; }
|
||||
|
||||
public ICollection<UserTrack> TrackRatings { get; set; }
|
||||
public ICollection<UserQue> UserQues { get; set; }
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Roadie.Library.Imaging
|
|||
private Image _artist;
|
||||
private Image _collection;
|
||||
private Image _label;
|
||||
private Image _genre;
|
||||
private Image _playlist;
|
||||
private Image _release;
|
||||
private Image _track;
|
||||
|
@ -23,6 +24,8 @@ namespace Roadie.Library.Imaging
|
|||
|
||||
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 Release => _release ?? (_release = MakeImageFromFile(MakeImagePath(@"images/release.jpg")));
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Roadie.Library.Imaging
|
|||
Image Artist { get; }
|
||||
Image Collection { get; }
|
||||
Image Label { get; }
|
||||
Image Genre { get; }
|
||||
Image Playlist { get; }
|
||||
Image Release { get; }
|
||||
Image Track { get; }
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace Roadie.Library.Models
|
|||
set => _urlsList = value;
|
||||
}
|
||||
|
||||
public bool UserBookmarked { get; set; }
|
||||
public bool? UserBookmarked { get; set; }
|
||||
|
||||
public EntityModelBase()
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using Roadie.Library.Models.Statistics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
|
@ -7,7 +8,26 @@ namespace Roadie.Library.Models
|
|||
[Serializable]
|
||||
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; }
|
||||
[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 DataToken Genre { 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 Guid? FilterToTrackId { get; set; }
|
||||
public Guid?[] FilterToTrackIds { get; set; }
|
||||
public Guid? FilterToGenreId { get; set; }
|
||||
public int? FilterToYear { get; set; }
|
||||
public string FilterValue => Filter ?? string.Empty;
|
||||
public bool IsHistoryRequest { get; set; }
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace Roadie.Library.Models.Statistics
|
|||
public ArtistList MostPlayedArtist { get; set; }
|
||||
public ReleaseList MostPlayedRelease { get; set; }
|
||||
public TrackList MostPlayedTrack { get; set; }
|
||||
public TrackList LastPlayedTrack { get; set; }
|
||||
|
||||
public int? PlayedTracks { get; set; }
|
||||
public int? RatedArtists { get; set; }
|
||||
public int? RatedReleases { get; set; }
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||
namespace Roadie.Library.Models.Users
|
||||
{
|
||||
[Serializable]
|
||||
public class User
|
||||
public class User : EntityModelBase
|
||||
{
|
||||
public const string ActionKeyUserRated = "__userrated__";
|
||||
public const string DefaultIncludes = "stats";
|
||||
|
@ -69,6 +69,10 @@ namespace Roadie.Library.Models.Users
|
|||
[Required] [MaxLength(20)] public string UserName { 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()
|
||||
{
|
||||
return $"Id [{Id}], RoadieId [{UserId}], UserName [{UserName}]";
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Roadie.Library.Models.Users
|
|||
public class UserList : EntityInfoModelBase
|
||||
{
|
||||
public bool IsEditor { get; set; }
|
||||
public bool IsAdmin { get; set; }
|
||||
public bool? IsPrivate { get; set; }
|
||||
public DateTime? LastActivity { get; set; }
|
||||
public DateTime? LastApiAccessDate { get; set; }
|
||||
|
@ -31,13 +32,15 @@ namespace Roadie.Library.Models.Users
|
|||
Value = user.RoadieId.ToString()
|
||||
},
|
||||
IsEditor = user.UserRoles.Any(x => x.Role.Name == "Editor"),
|
||||
IsAdmin = user.UserRoles.Any(x => x.Role.Name == "Admin"),
|
||||
IsPrivate = user.IsPrivate,
|
||||
Thumbnail = thumbnail,
|
||||
CreatedDate = user.CreatedDate,
|
||||
LastUpdated = user.LastUpdated,
|
||||
RegisteredDate = user.RegisteredOn,
|
||||
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 ILabelService LabelService { get; }
|
||||
private IGenreService GenreService { get; }
|
||||
|
||||
public AdminService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext,
|
||||
data.IRoadieDbContext context, ICacheManager cacheManager, ILogger<ArtistService> logger,
|
||||
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)
|
||||
{
|
||||
|
@ -57,6 +58,7 @@ namespace Roadie.Api.Services
|
|||
ArtistService = artistService;
|
||||
ReleaseService = releaseService;
|
||||
LabelService = labelService;
|
||||
GenreService = genreService;
|
||||
ReleaseLookupEngine = releaseLookupEngine;
|
||||
FileDirectoryProcessorService = fileDirectoryProcessorService;
|
||||
}
|
||||
|
@ -158,6 +160,11 @@ namespace Roadie.Api.Services
|
|||
Directory.CreateDirectory(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))
|
||||
{
|
||||
Directory.CreateDirectory(Configuration.PlaylistImageFolder);
|
||||
|
@ -264,7 +271,7 @@ namespace Roadie.Api.Services
|
|||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
await LogAndPublish("Error deleting artist secondary image.");
|
||||
await LogAndPublish("Error deleting Label.");
|
||||
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)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
|
|
|
@ -21,7 +21,6 @@ using Roadie.Library.Models.Pagination;
|
|||
using Roadie.Library.Models.Releases;
|
||||
using Roadie.Library.Models.Statistics;
|
||||
using Roadie.Library.Models.Users;
|
||||
using Roadie.Library.Processors;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -45,6 +44,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
private ICollectionService CollectionService { get; }
|
||||
|
||||
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
|
||||
private IFileNameHelper FileNameHelper { get; }
|
||||
|
||||
private IID3TagsHelper ID3TagsHelper { get; }
|
||||
|
@ -61,9 +61,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
private IReleaseService ReleaseService { get; }
|
||||
|
||||
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
|
||||
|
||||
public ArtistService(IRoadieSettings configuration,
|
||||
public ArtistService(IRoadieSettings configuration,
|
||||
IHttpEncoder httpEncoder,
|
||||
IHttpContext httpContext,
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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();
|
||||
|
||||
IQueryable<int> favoriteArtistIds = null;
|
||||
if (request.FilterFavoriteOnly)
|
||||
{
|
||||
favoriteArtistIds = from a in DbContext.Artists
|
||||
join ua in DbContext.UserArtists on a.Id equals ua.ArtistId
|
||||
where ua.IsFavorite ?? false
|
||||
where roadieUser == null || ua.UserId == roadieUser.Id
|
||||
select a.Id;
|
||||
}
|
||||
IQueryable<int> labelArtistIds = null;
|
||||
if (request.FilterToLabelId.HasValue)
|
||||
{
|
||||
labelArtistIds = (from l in DbContext.Labels
|
||||
join rl in DbContext.ReleaseLabels on l.Id equals rl.LabelId
|
||||
join r in DbContext.Releases on rl.ReleaseId equals r.Id
|
||||
where l.RoadieId == request.FilterToLabelId
|
||||
select r.ArtistId)
|
||||
.Distinct();
|
||||
.Distinct();
|
||||
}
|
||||
IQueryable<int> genreArtistIds = null;
|
||||
var isFilteredToGenre = false;
|
||||
if (!string.IsNullOrEmpty(request.Filter) &&
|
||||
request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase))
|
||||
if(request.FilterToGenreId.HasValue)
|
||||
{
|
||||
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 ", "");
|
||||
genreArtistIds = (from ag in DbContext.ArtistGenres
|
||||
join g in DbContext.Genres on ag.GenreId equals g.Id
|
||||
where g.Name.Contains(genreFilter)
|
||||
select ag.ArtistId)
|
||||
.Distinct();
|
||||
.Distinct();
|
||||
isFilteredToGenre = true;
|
||||
request.Filter = null;
|
||||
}
|
||||
|
||||
var onlyWithReleases = onlyIncludeWithReleases ?? true;
|
||||
var isEqualFilter = false;
|
||||
if (!string.IsNullOrEmpty(request.FilterValue))
|
||||
|
@ -285,7 +270,6 @@ namespace Roadie.Api.Services
|
|||
request.Filter = filter.Substring(1, filter.Length - 2);
|
||||
}
|
||||
}
|
||||
|
||||
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
|
||||
? request.FilterValue.ToAlphanumericName()
|
||||
: null;
|
||||
|
@ -293,8 +277,18 @@ namespace Roadie.Api.Services
|
|||
where !onlyWithReleases || a.ReleaseCount > 0
|
||||
where request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId
|
||||
where request.FilterMinimumRating == null || a.Rating >= request.FilterMinimumRating.Value
|
||||
where request.FilterValue == "" || a.Name.Contains(request.FilterValue) || a.SortName.Contains(request.FilterValue) || a.AlternateNames.Contains(request.FilterValue) || a.AlternateNames.Contains(normalizedFilterValue)
|
||||
where !isEqualFilter || a.Name.Equals(request.FilterValue) || a.SortName.Equals(request.FilterValue) || a.AlternateNames.Equals(request.FilterValue) || a.AlternateNames.Equals(normalizedFilterValue)
|
||||
where request.FilterValue == "" ||
|
||||
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.FilterToLabelId == null || labelArtistIds.Contains(a.Id)
|
||||
where !isFilteredToGenre || genreArtistIds.Contains(a.Id)
|
||||
|
@ -323,22 +317,24 @@ namespace Roadie.Api.Services
|
|||
var rowCount = result.Count();
|
||||
if (doRandomize ?? false)
|
||||
{
|
||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? 100;
|
||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||
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
|
||||
{
|
||||
string sortBy;
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
@ -351,11 +347,11 @@ namespace Roadie.Api.Services
|
|||
where rowIds.Contains(ua.ArtistId)
|
||||
select ua).ToArray();
|
||||
|
||||
foreach (var userArtistRating in userArtistRatings.Where(x =>
|
||||
rows.Select(r => r.DatabaseId).Contains(x.ArtistId)))
|
||||
foreach (var userArtistRating in userArtistRatings.Where(x => rows.Select(r => r.DatabaseId).Contains(x.ArtistId)))
|
||||
{
|
||||
var row = rows.FirstOrDefault(x => x.DatabaseId == userArtistRating.ArtistId);
|
||||
if (row != null)
|
||||
{
|
||||
row.UserRating = new UserArtist
|
||||
{
|
||||
IsDisliked = userArtistRating.IsDisliked ?? false,
|
||||
|
@ -363,11 +359,11 @@ namespace Roadie.Api.Services
|
|||
Rating = userArtistRating.Rating,
|
||||
RatedDate = userArtistRating.LastUpdated ?? userArtistRating.CreatedDate
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0)
|
||||
{
|
||||
if (Configuration.RecordNoResultSearches)
|
||||
{
|
||||
// Create request for no artist found
|
||||
|
@ -379,7 +375,8 @@ namespace Roadie.Api.Services
|
|||
DbContext.Requests.Add(req);
|
||||
await DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
}
|
||||
sw.Stop();
|
||||
return new Library.Models.Pagination.PagedResult<ArtistList>
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
|
||||
|
@ -773,11 +688,11 @@ namespace Roadie.Api.Services
|
|||
// Ensure is jpeg first
|
||||
artistSecondaryImage = ImageHelper.ConvertToJpegFormat(artistSecondaryImage);
|
||||
|
||||
var artistImageFilename = Path.Combine(newArtistFolder,string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
||||
var artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
||||
while (File.Exists(artistImageFilename))
|
||||
{
|
||||
looper++;
|
||||
artistImageFilename = Path.Combine(newArtistFolder,string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
||||
artistImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
|
||||
}
|
||||
|
||||
File.WriteAllBytes(artistImageFilename, artistSecondaryImage);
|
||||
|
@ -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)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
|
@ -1368,96 +1477,5 @@ namespace Roadie.Api.Services
|
|||
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.Configuration;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Imaging;
|
||||
using Roadie.Library.Models;
|
||||
using Roadie.Library.Models.Pagination;
|
||||
using Roadie.Library.Models.Users;
|
||||
|
@ -9,6 +14,7 @@ using Roadie.Library.Utility;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -28,8 +34,155 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
}
|
||||
|
||||
public Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request,
|
||||
bool? doRandomize = false)
|
||||
public async Task<OperationResult<Genre>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
|
||||
{
|
||||
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();
|
||||
sw.Start();
|
||||
|
@ -60,7 +213,8 @@ namespace Roadie.Api.Services
|
|||
ReleaseCount = releaseCount,
|
||||
ArtistCount = artistCount,
|
||||
CreatedDate = g.CreatedDate,
|
||||
LastUpdated = g.LastUpdated
|
||||
LastUpdated = g.LastUpdated,
|
||||
Thumbnail = MakeGenreThumbnailImage(g.RoadieId)
|
||||
};
|
||||
|
||||
GenreList[] rows;
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace Roadie.Api.Services
|
|||
|
||||
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>> 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.Users;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Api.Services
|
||||
{
|
||||
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<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>> 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>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
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);
|
||||
|
||||
|
|
|
@ -128,6 +128,17 @@ namespace Roadie.Api.Services
|
|||
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)
|
||||
{
|
||||
return await GetImageFileOperation("PlaylistThumbnail",
|
||||
|
@ -480,6 +491,47 @@ namespace Roadie.Api.Services
|
|||
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)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -77,7 +77,7 @@ namespace Roadie.Api.Services
|
|||
var cacheKey = string.Format("urn:label_by_id_operation:{0}:{1}", id,
|
||||
includes == null ? "0" : string.Join("|", includes));
|
||||
var result = await CacheManager.GetAsync(cacheKey,
|
||||
async () => { return await LabelByIdAction(id, includes); }, data.Artist.CacheRegionUrn(id));
|
||||
async () => { return await LabelByIdAction(id, includes); }, data.Label.CacheRegionUrn(id));
|
||||
sw.Stop();
|
||||
if (result?.Data != null && roadieUser != null)
|
||||
{
|
||||
|
@ -157,9 +157,11 @@ namespace Roadie.Api.Services
|
|||
var rowCount = result.Count();
|
||||
if (doRandomize ?? false)
|
||||
{
|
||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? 100;
|
||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||
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
|
||||
{
|
||||
|
@ -341,8 +343,7 @@ namespace Roadie.Api.Services
|
|||
result.Tags = label.Tags;
|
||||
result.URLs = label.URLs;
|
||||
result.Thumbnail = MakeLabelThumbnailImage(label.RoadieId);
|
||||
result.MediumThumbnail = MakeThumbnailImage(id, "label", Configuration.MediumImageSize.Width,
|
||||
Configuration.MediumImageSize.Height);
|
||||
result.MediumThumbnail = MakeThumbnailImage(id, "label", Configuration.MediumImageSize.Width,Configuration.MediumImageSize.Height);
|
||||
if (includes != null && includes.Any())
|
||||
{
|
||||
if (includes.Contains("stats"))
|
||||
|
|
|
@ -113,8 +113,10 @@ namespace Roadie.Api.Services
|
|||
sw.Start();
|
||||
var cacheKey = string.Format("urn:playlist_by_id_operation:{0}:{1}", id,
|
||||
includes == null ? "0" : string.Join("|", includes));
|
||||
var result = await CacheManager.GetAsync(cacheKey,
|
||||
async () => { return await PlaylistByIdAction(id, includes); }, data.Artist.CacheRegionUrn(id));
|
||||
var result = await CacheManager.GetAsync(cacheKey, async () =>
|
||||
{
|
||||
return await PlaylistByIdAction(id, includes);
|
||||
}, data.Artist.CacheRegionUrn(id));
|
||||
sw.Stop();
|
||||
if (result?.Data != null && roadieUser != null)
|
||||
{
|
||||
|
@ -122,17 +124,17 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
var user = GetUser(roadieUser.UserId);
|
||||
foreach (var track in result.Data.Tracks)
|
||||
{
|
||||
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, track.Track.DatabaseId, track.Track.Id);
|
||||
}
|
||||
}
|
||||
|
||||
result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin;
|
||||
var userBookmarkResult =
|
||||
await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist);
|
||||
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist);
|
||||
if (userBookmarkResult.IsSuccess)
|
||||
result.Data.UserBookmarked =
|
||||
userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) !=
|
||||
null;
|
||||
|
||||
{
|
||||
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != null;
|
||||
}
|
||||
if (result.Data.Comments.Any())
|
||||
{
|
||||
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
|
||||
|
@ -142,8 +144,7 @@ namespace Roadie.Api.Services
|
|||
select cr).ToArray();
|
||||
foreach (var comment in result.Data.Comments)
|
||||
{
|
||||
var userCommentReaction =
|
||||
userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
|
||||
var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
|
||||
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
|
||||
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
|
||||
}
|
||||
|
@ -402,15 +403,14 @@ namespace Roadie.Api.Services
|
|||
var playlist = GetPlaylist(id);
|
||||
|
||||
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>();
|
||||
result.AlternateNames = playlist.AlternateNames;
|
||||
result.Tags = playlist.Tags;
|
||||
result.URLs = playlist.URLs;
|
||||
var maintainer = DbContext.Users.Include(x => x.UserRoles).Include("UserRoles.Role")
|
||||
.FirstOrDefault(x => x.Id == playlist.UserId);
|
||||
var maintainer = DbContext.Users.Include(x => x.UserRoles).Include("UserRoles.Role").FirstOrDefault(x => x.Id == playlist.UserId);
|
||||
result.Maintainer = UserList.FromDataUser(maintainer, MakeUserThumbnailImage(maintainer.RoadieId));
|
||||
result.Thumbnail = MakePlaylistThumbnailImage(playlist.RoadieId);
|
||||
result.MediumThumbnail = MakeThumbnailImage(id, "playlist", Configuration.MediumImageSize.Width,
|
||||
|
@ -424,6 +424,7 @@ namespace Roadie.Api.Services
|
|||
select new { t, pltr }).ToArray();
|
||||
|
||||
if (includes.Contains("stats"))
|
||||
{
|
||||
result.Statistics = new ReleaseGroupingStatistics
|
||||
{
|
||||
ReleaseCount = result.ReleaseCount,
|
||||
|
@ -431,7 +432,9 @@ namespace Roadie.Api.Services
|
|||
TrackSize = result.DurationTime,
|
||||
FileSize = playlistTracks.Sum(x => (long?)x.t.FileSize).ToFileSize()
|
||||
};
|
||||
}
|
||||
if (includes.Contains("tracks"))
|
||||
{
|
||||
result.Tracks = (from plt in playlistTracks
|
||||
join rm in DbContext.ReleaseMedias on plt.t.ReleaseMediaId equals rm.Id
|
||||
join r in DbContext.Releases on rm.ReleaseId equals r.Id
|
||||
|
@ -453,10 +456,13 @@ namespace Roadie.Api.Services
|
|||
MakeArtistThumbnailImage(releaseArtist.RoadieId),
|
||||
MakeArtistThumbnailImage(trackArtist == null ? null : (Guid?)trackArtist.RoadieId))
|
||||
}).ToArray();
|
||||
}
|
||||
if (includes.Contains("comments"))
|
||||
{
|
||||
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())
|
||||
{
|
||||
var comments = new List<Comment>();
|
||||
|
@ -468,15 +474,11 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
var comment = playlistComment.Adapt<Comment>();
|
||||
comment.DatabaseId = playlistComment.Id;
|
||||
comment.User = UserList.FromDataUser(playlistComment.User,
|
||||
MakeUserThumbnailImage(playlistComment.User.RoadieId));
|
||||
comment.DislikedCount = userCommentReactions.Count(x =>
|
||||
x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Dislike);
|
||||
comment.LikedCount = userCommentReactions.Count(x =>
|
||||
x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Like);
|
||||
comment.User = UserList.FromDataUser(playlistComment.User, MakeUserThumbnailImage(playlistComment.User.RoadieId));
|
||||
comment.DislikedCount = userCommentReactions.Count(x => x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Dislike);
|
||||
comment.LikedCount = userCommentReactions.Count(x => x.CommentId == playlistComment.Id && x.ReactionValue == CommentReaction.Like);
|
||||
comments.Add(comment);
|
||||
}
|
||||
|
||||
result.Comments = comments;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,8 +211,16 @@ namespace Roadie.Api.Services
|
|||
select a.Id;
|
||||
IQueryable<int> genreReleaseIds = null;
|
||||
var isFilteredToGenre = false;
|
||||
if (!string.IsNullOrEmpty(request.FilterByGenre) || !string.IsNullOrEmpty(request.Filter) &&
|
||||
request.Filter.StartsWith(":genre", StringComparison.OrdinalIgnoreCase))
|
||||
if(request.FilterToGenreId.HasValue)
|
||||
{
|
||||
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 ??
|
||||
(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
|
||||
where g.Name.Contains(genreFilter)
|
||||
select rg.ReleaseId)
|
||||
.Distinct();
|
||||
.Distinct();
|
||||
request.Filter = null;
|
||||
isFilteredToGenre = true;
|
||||
}
|
||||
|
@ -314,9 +322,11 @@ namespace Roadie.Api.Services
|
|||
|
||||
if (doRandomize ?? false)
|
||||
{
|
||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? 100;
|
||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||
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
|
||||
{
|
||||
|
|
|
@ -125,6 +125,15 @@ namespace Roadie.Api.Services
|
|||
}, 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)
|
||||
{
|
||||
return CacheManager.Get(data.Playlist.CacheUrn(id), () =>
|
||||
|
@ -245,6 +254,11 @@ namespace Roadie.Api.Services
|
|||
return MakeThumbnailImage(id, "label");
|
||||
}
|
||||
|
||||
protected Image MakeGenreThumbnailImage(Guid id)
|
||||
{
|
||||
return MakeThumbnailImage(id, "genre");
|
||||
}
|
||||
|
||||
protected string MakeLastFmUrl(string artistName, string releaseTitle)
|
||||
{
|
||||
return "http://www.last.fm/music/" + HttpEncoder.UrlEncode($"{artistName}/{releaseTitle}");
|
||||
|
|
|
@ -518,8 +518,12 @@ namespace Roadie.Api.Services
|
|||
|
||||
if (doRandomize ?? false)
|
||||
{
|
||||
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.Limit;
|
||||
request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue;
|
||||
|
||||
rows = result.OrderBy(x => x.Artist.RandomSortId)
|
||||
.ThenBy(x => x.RandomSortId)
|
||||
.Take(request.LimitValue)
|
||||
.ToArray();
|
||||
}
|
||||
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 tsw = new Stopwatch();
|
||||
|
||||
if(isAccountSettingsEdit)
|
||||
{
|
||||
if(user.UserId != id && !user.IsAdmin)
|
||||
{
|
||||
return new OperationResult<User>(new Exception("Access Denied"));
|
||||
}
|
||||
}
|
||||
var sw = Stopwatch.StartNew();
|
||||
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 () =>
|
||||
{
|
||||
tsw.Restart();
|
||||
|
@ -67,7 +73,15 @@ namespace Roadie.Api.Services
|
|||
return rr;
|
||||
}, ApplicationUser.CacheRegionUrn(id));
|
||||
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);
|
||||
Logger.LogDebug("ById Timings: id [{0}]", id);
|
||||
return new OperationResult<User>(result.Messages)
|
||||
|
@ -101,6 +115,7 @@ namespace Roadie.Api.Services
|
|||
Value = u.RoadieId.ToString()
|
||||
},
|
||||
IsEditor = u.UserRoles.Any(x => x.Role.Name == "Editor"),
|
||||
IsAdmin = u.UserRoles.Any(x => x.Role.Name == "Admin"),
|
||||
IsPrivate = u.IsPrivate,
|
||||
Thumbnail = MakeUserThumbnailImage(u.RoadieId),
|
||||
CreatedDate = u.CreatedDate,
|
||||
|
@ -119,15 +134,18 @@ namespace Roadie.Api.Services
|
|||
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
||||
|
||||
if (rows.Any())
|
||||
{
|
||||
foreach (var row in rows)
|
||||
{
|
||||
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)
|
||||
.Where(x => x.UserId == row.DatabaseId).ToArray();
|
||||
var userTracks = DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == row.DatabaseId)
|
||||
.ToArray();
|
||||
|
||||
.Where(x => x.UserId == row.DatabaseId)
|
||||
.ToArray();
|
||||
var userTracks = DbContext.UserTracks.Include(x => x.Track)
|
||||
.Where(x => x.UserId == row.DatabaseId)
|
||||
.ToArray();
|
||||
row.Statistics = new UserStatistics
|
||||
{
|
||||
RatedArtists = userArtists.Where(x => x.Rating > 0).Count(),
|
||||
|
@ -142,6 +160,7 @@ namespace Roadie.Api.Services
|
|||
DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
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);
|
||||
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)
|
||||
{
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Errors = new List<Exception> { new Exception("Access Denied") }
|
||||
};
|
||||
}
|
||||
// Check concurrency stamp
|
||||
if (user.ConcurrencyStamp != userBeingUpdatedModel.ConcurrencyStamp)
|
||||
{
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Errors = new List<Exception> { new Exception("User data is stale.") }
|
||||
};
|
||||
}
|
||||
// Check that username (if changed) doesn't already exist
|
||||
if (user.UserName != userBeingUpdatedModel.UserName)
|
||||
{
|
||||
var userByUsername = DbContext.Users.FirstOrDefault(x =>
|
||||
x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper());
|
||||
var userByUsername = DbContext.Users.FirstOrDefault(x => x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper());
|
||||
if (userByUsername != null)
|
||||
{
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Errors = new List<Exception> { new Exception("Username already in use") }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check that email (if changed) doesn't already exist
|
||||
if (user.Email != userBeingUpdatedModel.Email)
|
||||
{
|
||||
var userByEmail =
|
||||
DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper());
|
||||
var userByEmail = DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper());
|
||||
if (userByEmail != null)
|
||||
{
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
Errors = new List<Exception> { new Exception("Email already in use") }
|
||||
};
|
||||
}
|
||||
}
|
||||
var oldPathToImage = user.PathToImage(Configuration);
|
||||
var didChangeName = user.UserName != userBeingUpdatedModel.UserName;
|
||||
|
@ -405,6 +431,7 @@ namespace Roadie.Api.Services
|
|||
user.FtpUsername = userBeingUpdatedModel.FtpUsername;
|
||||
user.FtpPassword = EncryptionHelper.Encrypt(userBeingUpdatedModel.FtpPassword, user.RoadieId.ToString());
|
||||
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||
user.DefaultRowsPerPage = userBeingUpdatedModel.DefaultRowsPerPage;
|
||||
|
||||
if (didChangeName)
|
||||
{
|
||||
|
@ -414,7 +441,6 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(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
|
||||
where ut.UserId == user.Id
|
||||
select new { a, ut.PlayedCount })
|
||||
.GroupBy(a => a.a)
|
||||
.Select(x => new
|
||||
{
|
||||
Artist = x.Key,
|
||||
Played = x.Sum(t => t.PlayedCount)
|
||||
})
|
||||
.OrderByDescending(x => x.Played)
|
||||
.FirstOrDefault();
|
||||
.GroupBy(a => a.a)
|
||||
.Select(x => new
|
||||
{
|
||||
Artist = x.Key,
|
||||
Played = x.Sum(t => t.PlayedCount)
|
||||
})
|
||||
.OrderByDescending(x => x.Played)
|
||||
.FirstOrDefault();
|
||||
var mostPlayedReleaseId = (from r in DbContext.Releases
|
||||
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
||||
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
|
||||
where ut.UserId == user.Id
|
||||
select new { r, ut.PlayedCount })
|
||||
.GroupBy(r => r.r)
|
||||
.Select(x => new
|
||||
{
|
||||
Release = x.Key,
|
||||
Played = x.Sum(t => t.PlayedCount)
|
||||
})
|
||||
.OrderByDescending(x => x.Played)
|
||||
.Select(x => x.Release.RoadieId)
|
||||
.FirstOrDefault();
|
||||
.GroupBy(r => r.r)
|
||||
.Select(x => new
|
||||
{
|
||||
Release = x.Key,
|
||||
Played = x.Sum(t => t.PlayedCount)
|
||||
})
|
||||
.OrderByDescending(x => x.Played)
|
||||
.Select(x => x.Release.RoadieId)
|
||||
.FirstOrDefault();
|
||||
var mostPlayedRelease = GetRelease(mostPlayedReleaseId);
|
||||
var mostPlayedTrackUserTrack = userTracks
|
||||
.OrderByDescending(x => x.PlayedCount)
|
||||
.FirstOrDefault();
|
||||
var mostPlayedTrackUserTrack = userTracks.OrderByDescending(x => x.PlayedCount)
|
||||
.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
|
||||
? null
|
||||
: DbContext.Tracks
|
||||
|
@ -598,6 +634,22 @@ namespace Roadie.Api.Services
|
|||
|
||||
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
|
||||
? null
|
||||
: models.ArtistList.FromDataArtist(mostPlayedArtist.Artist,
|
||||
|
@ -634,7 +686,7 @@ namespace Roadie.Api.Services
|
|||
RatedTracks = userTracks.Where(x => x.Rating > 0).Count(),
|
||||
PlayedTracks = userTracks.Where(x => x.PlayedCount.HasValue).Select(x => x.PlayedCount).Sum(),
|
||||
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
|
||||
{
|
||||
// Login user
|
||||
var loginResult =
|
||||
await SignInManager.PasswordSignInAsync(model.Username, model.Password, false, false);
|
||||
if (!loginResult.Succeeded) return BadRequest();
|
||||
var loginResult = await SignInManager.PasswordSignInAsync(model.Username, model.Password, false, false);
|
||||
if (!loginResult.Succeeded)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
var user = await UserManager.FindByNameAsync(model.Username);
|
||||
var now = DateTime.UtcNow;
|
||||
user.LastLogin = now;
|
||||
|
@ -127,8 +129,7 @@ namespace Roadie.Api.Controllers
|
|||
}
|
||||
|
||||
CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
|
||||
var avatarUrl =
|
||||
$"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}";
|
||||
var avatarUrl = $"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}";
|
||||
return Ok(new
|
||||
{
|
||||
Username = user.UserName,
|
||||
|
@ -139,7 +140,8 @@ namespace Roadie.Api.Controllers
|
|||
avatarUrl,
|
||||
Token = t,
|
||||
user.Timeformat,
|
||||
user.Timezone
|
||||
user.Timezone,
|
||||
DefaultRowsPerPage = user.DefaultRowsPerPage ?? RoadieSettings.DefaultRowsPerPage
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -82,6 +82,15 @@ namespace Roadie.Api.Controllers
|
|||
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}")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> DeleteReleaseSecondaryImage(Guid id, int index)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -10,6 +11,8 @@ using Roadie.Library.Models.Pagination;
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using models = Roadie.Library.Models;
|
||||
|
||||
namespace Roadie.Api.Controllers
|
||||
{
|
||||
|
@ -29,33 +32,16 @@ namespace Roadie.Api.Controllers
|
|||
GenreService = genreService;
|
||||
}
|
||||
|
||||
//[EnableQuery]
|
||||
//public IActionResult Get()
|
||||
//{
|
||||
// return Ok(this._RoadieDbContext.Labels.ProjectToType<models.Label>());
|
||||
//}
|
||||
|
||||
//[HttpGet("{id}")]
|
||||
//[ProducesResponseType(200)]
|
||||
//[ProducesResponseType(404)]
|
||||
//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("{id}")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(404)]
|
||||
public async Task<IActionResult> Get(Guid id, string inc = null)
|
||||
{
|
||||
var result = await GenreService.ById(await CurrentUserModel(), id,(inc ?? models.Genre.DefaultIncludes).ToLower().Split(","));
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(200)]
|
||||
|
@ -80,5 +66,35 @@ namespace Roadie.Api.Controllers
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
[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?}")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(404)]
|
||||
|
@ -186,6 +201,17 @@ namespace Roadie.Api.Controllers
|
|||
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?}")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(404)]
|
||||
|
|
|
@ -40,6 +40,36 @@ namespace Roadie.Api.Controllers
|
|||
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}")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(204)]
|
||||
|
@ -52,17 +82,14 @@ namespace Roadie.Api.Controllers
|
|||
{
|
||||
return await UserService.ById(user, id, (inc ?? Library.Models.Users.User.DefaultIncludes).ToLower().Split(","));
|
||||
}, 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)
|
||||
if (result == null || result.IsNotFoundResult)
|
||||
{
|
||||
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 NotFound();
|
||||
}
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
|
@ -239,7 +266,10 @@ namespace Roadie.Api.Controllers
|
|||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> UpdateProfile(User model)
|
||||
{
|
||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
var user = await CurrentUserModel();
|
||||
var result = await UserService.UpdateProfile(user, model);
|
||||
if (result == null || result.IsNotFoundResult) return NotFound();
|
||||
|
@ -260,7 +290,8 @@ namespace Roadie.Api.Controllers
|
|||
avatarUrl,
|
||||
Token = t,
|
||||
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
|
||||
Upgrade0004.sql = Upgrade0004.sql
|
||||
Upgrade0005.sql = Upgrade0005.sql
|
||||
Upgrade0006.sql = Upgrade0006.sql
|
||||
EndProjectSection
|
||||
EndProject
|
||||
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)
|
||||
--
|
||||
|
@ -338,6 +338,10 @@ CREATE TABLE `genre` (
|
|||
`lastUpdated` datetime DEFAULT NULL,
|
||||
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT 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`),
|
||||
UNIQUE KEY `ix_genre_name` (`name`),
|
||||
KEY `ix_genre_roadieId` (`roadieId`),
|
||||
|
@ -629,7 +633,7 @@ CREATE TABLE `request` (
|
|||
KEY `ix_request_roadieId` (`roadieId`),
|
||||
KEY `requestartist_ibfk_1` (`userId`),
|
||||
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 */;
|
||||
|
||||
--
|
||||
|
@ -655,7 +659,7 @@ CREATE TABLE `scanHistory` (
|
|||
PRIMARY KEY (`id`),
|
||||
KEY `ix_scanHistory_roadieId` (`roadieId`),
|
||||
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 */;
|
||||
|
||||
--
|
||||
|
@ -723,7 +727,7 @@ CREATE TABLE `track` (
|
|||
KEY `track_artistId_IDX` (`artistId`) USING BTREE,
|
||||
KEY `track_releaseMediaId_IDX` (`releaseMediaId`) USING BTREE,
|
||||
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 */;
|
||||
|
||||
--
|
||||
|
@ -959,7 +963,7 @@ CREATE TABLE `usersInRoles` (
|
|||
KEY `ix_usersInRoles_userId` (`userId`),
|
||||
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
|
||||
) 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 */;
|
||||
|
||||
--
|
||||
|
@ -989,7 +993,7 @@ CREATE TABLE `usertrack` (
|
|||
KEY `ix_usertrack_roadieId` (`roadieId`),
|
||||
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
|
||||
) 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 */;
|
||||
|
||||
--
|
||||
|
@ -1075,4 +1079,4 @@ SET character_set_client = @saved_cs_client;
|
|||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2019-07-18 17:31:08
|
||||
-- Dump completed on 2019-08-02 11:14:18
|
||||
|
|
Loading…
Reference in a new issue