Updated NuGet packages and CodeMaid services.

This commit is contained in:
Steven Hildreth 2019-08-22 15:33:07 -05:00
parent 14a7119993
commit 004ca33d48
21 changed files with 497 additions and 577 deletions

View file

@ -10,8 +10,8 @@
<ItemGroup>
<PackageReference Include="AutoCompare.Core" Version="1.0.0" />
<PackageReference Include="CsvHelper" Version="12.1.2" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.5.2" />
<PackageReference Include="FluentFTP" Version="27.0.1" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.6.0" />
<PackageReference Include="FluentFTP" Version="27.0.2" />
<PackageReference Include="Hashids.net" Version="1.2.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.12" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" />
@ -24,7 +24,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="6.2.2" />
<PackageReference Include="MimeMapping" Version="1.0.1.12" />
<PackageReference Include="MimeMapping" Version="1.0.1.14" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NodaTime" Version="2.4.6" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />

View file

@ -35,18 +35,17 @@ namespace Roadie.Api.Services
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
private IGenreService GenreService { get; }
private ILabelService LabelService { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; }
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, IGenreService genreService
)
)
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
{
ScanActivityHub = scanActivityHub;
@ -137,143 +136,38 @@ namespace Roadie.Api.Services
};
}
/// <summary>
/// Perform checks/setup on start of application
/// </summary>
public void PerformStartUpTasks()
{
var sw = Stopwatch.StartNew();
#region Setup Configured storage folders
try
{
if (!Directory.Exists(Configuration.LibraryFolder))
{
Directory.CreateDirectory(Configuration.LibraryFolder);
Logger.LogInformation($"Created Library Folder [{Configuration.LibraryFolder }]");
}
if (!Directory.Exists(Configuration.UserImageFolder))
{
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);
Logger.LogInformation($"Created Playlist Image Folder [{Configuration.PlaylistImageFolder }]");
}
if (!Directory.Exists(Configuration.CollectionImageFolder))
{
Directory.CreateDirectory(Configuration.CollectionImageFolder);
Logger.LogInformation($"Created Collection Image Folder [{Configuration.CollectionImageFolder }]");
}
if (!Directory.Exists(Configuration.LabelImageFolder))
{
Directory.CreateDirectory(Configuration.LabelImageFolder);
Logger.LogInformation($"Created Label Image Folder [{Configuration.LabelImageFolder}]");
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error setting up storage folders. Ensure application has create folder permissions.");
throw;
}
#endregion
sw.Stop();
Logger.LogInformation($"Administration startup tasks completed, elapsed time [{ sw.ElapsedMilliseconds }]");
}
public async Task<OperationResult<bool>> ValidateInviteToken(Guid? tokenId)
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
if(tokenId == null)
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null)
{
return new OperationResult<bool>(true, $"Invalid Invite TokenId [{tokenId}]");
}
var token = DbContext.InviteTokens.FirstOrDefault(x => x.RoadieId == tokenId);
if(token == null)
{
return new OperationResult<bool>(true, $"Invite Token Not Found [{tokenId}]");
}
if(token.ExpiresDate < DateTime.UtcNow || token.Status == Statuses.Expired)
{
token.Status = Statuses.Expired;
token.LastUpdated = DateTime.UtcNow;
await DbContext.SaveChangesAsync();
return new OperationResult<bool>(true, $"Invite Token [{tokenId}] Expired [{ token.ExpiresDate }]");
}
if(token.Status == Statuses.Complete)
{
return new OperationResult<bool>(true, $"Invite Token [{tokenId}] Already Used");
}
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> UpdateInviteTokenUsed(Guid? tokenId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
if (tokenId == null)
{
return new OperationResult<bool>(true, $"Invalid Invite TokenId [{tokenId}]");
}
var token = DbContext.InviteTokens.FirstOrDefault(x => x.RoadieId == tokenId);
if (token == null)
{
return new OperationResult<bool>(true, $"Invite Token Not Found [{tokenId}]");
}
token.Status = Statuses.Complete;
token.LastUpdated = DateTime.UtcNow;
await DbContext.SaveChangesAsync();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == labelId);
if (label == null)
{
await LogAndPublish($"DeleteLabel Unknown Label [{labelId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Label Not Found [{labelId}]");
await LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{artistId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]");
}
try
{
await LabelService.Delete(user, labelId);
CacheManager.ClearRegion(label.CacheRegion);
var artistFolder = artist.ArtistFileFolder(Configuration);
var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly);
var artistImageFilename = artistImagesInFolder.Skip(index).FirstOrDefault();
if (artistImageFilename.Exists)
{
artistImageFilename.Delete();
}
CacheManager.ClearRegion(artist.CacheRegion);
}
catch (Exception ex)
{
Logger.LogError(ex);
await LogAndPublish("Error deleting Label.");
await LogAndPublish("Error deleting artist secondary image.");
errors.Add(ex);
}
sw.Stop();
await LogAndPublish($"DeleteLabel `{label}`, By User `{user}`", LogLevel.Information);
await LogAndPublish($"DeleteArtistSecondaryImage `{artist}` Index [{index}], By User `{user}`", LogLevel.Information);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
@ -318,39 +212,32 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index)
public async Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null)
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == labelId);
if (label == null)
{
await LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{artistId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]");
await LogAndPublish($"DeleteLabel Unknown Label [{labelId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Label Not Found [{labelId}]");
}
try
{
var artistFolder = artist.ArtistFileFolder(Configuration);
var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly);
var artistImageFilename = artistImagesInFolder.Skip(index).FirstOrDefault();
if (artistImageFilename.Exists)
{
artistImageFilename.Delete();
}
CacheManager.ClearRegion(artist.CacheRegion);
await LabelService.Delete(user, labelId);
CacheManager.ClearRegion(label.CacheRegion);
}
catch (Exception ex)
{
Logger.LogError(ex);
await LogAndPublish("Error deleting artist secondary image.");
await LogAndPublish("Error deleting Label.");
errors.Add(ex);
}
sw.Stop();
await LogAndPublish($"DeleteArtistSecondaryImage `{artist}` Index [{index}], By User `{user}`", LogLevel.Information);
await LogAndPublish($"DeleteLabel `{label}`, By User `{user}`", LogLevel.Information);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
@ -506,7 +393,6 @@ namespace Roadie.Api.Services
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> DeleteUser(ApplicationUser applicationUser, Guid userId)
@ -670,6 +556,60 @@ namespace Roadie.Api.Services
});
}
/// <summary>
/// Perform checks/setup on start of application
/// </summary>
public void PerformStartUpTasks()
{
var sw = Stopwatch.StartNew();
#region Setup Configured storage folders
try
{
if (!Directory.Exists(Configuration.LibraryFolder))
{
Directory.CreateDirectory(Configuration.LibraryFolder);
Logger.LogInformation($"Created Library Folder [{Configuration.LibraryFolder }]");
}
if (!Directory.Exists(Configuration.UserImageFolder))
{
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);
Logger.LogInformation($"Created Playlist Image Folder [{Configuration.PlaylistImageFolder }]");
}
if (!Directory.Exists(Configuration.CollectionImageFolder))
{
Directory.CreateDirectory(Configuration.CollectionImageFolder);
Logger.LogInformation($"Created Collection Image Folder [{Configuration.CollectionImageFolder }]");
}
if (!Directory.Exists(Configuration.LabelImageFolder))
{
Directory.CreateDirectory(Configuration.LabelImageFolder);
Logger.LogInformation($"Created Label Image Folder [{Configuration.LabelImageFolder}]");
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error setting up storage folders. Ensure application has create folder permissions.");
throw;
}
#endregion Setup Configured storage folders
sw.Stop();
Logger.LogInformation($"Administration startup tasks completed, elapsed time [{ sw.ElapsedMilliseconds }]");
}
public async Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false,
bool doPurgeFirst = false)
{
@ -710,32 +650,6 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> ScanArtists(ApplicationUser user, IEnumerable<Guid> artistIds, bool isReadOnly = false)
{
var sw = Stopwatch.StartNew();
var errors = new List<Exception>();
foreach (var artistId in artistIds)
{
var result = await ScanArtist(user, artistId, isReadOnly);
if (!result.IsSuccess)
{
if (result.Errors?.Any() ?? false)
{
errors.AddRange(result.Errors);
}
}
}
sw.Stop();
await LogAndPublish($"** Completed! ScanArtists: Artist Count [{ artistIds.Count() }], Elapsed Time [{sw.Elapsed}]");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false)
{
var sw = Stopwatch.StartNew();
@ -779,6 +693,32 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> ScanArtists(ApplicationUser user, IEnumerable<Guid> artistIds, bool isReadOnly = false)
{
var sw = Stopwatch.StartNew();
var errors = new List<Exception>();
foreach (var artistId in artistIds)
{
var result = await ScanArtist(user, artistId, isReadOnly);
if (!result.IsSuccess)
{
if (result.Errors?.Any() ?? false)
{
errors.AddRange(result.Errors);
}
}
}
sw.Stop();
await LogAndPublish($"** Completed! ScanArtists: Artist Count [{ artistIds.Count() }], Elapsed Time [{sw.Elapsed}]");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId,
bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true)
{
@ -975,32 +915,6 @@ namespace Roadie.Api.Services
return await ScanFolder(user, d, isReadOnly);
}
public async Task<OperationResult<bool>> ScanReleases(ApplicationUser user, IEnumerable<Guid> releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false)
{
var sw = Stopwatch.StartNew();
var errors = new List<Exception>();
foreach (var releaseId in releaseIds)
{
var result = await ScanRelease(user, releaseId, isReadOnly, wasDoneForInvalidTrackPlay);
if (!result.IsSuccess)
{
if (result.Errors?.Any() ?? false)
{
errors.AddRange(result.Errors);
}
}
}
sw.Stop();
await LogAndPublish($"** Completed! ScanReleases: Release Count [{ releaseIds.Count() }], Elapsed Time [{sw.Elapsed}]");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false)
{
var sw = new Stopwatch();
@ -1046,6 +960,92 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> ScanReleases(ApplicationUser user, IEnumerable<Guid> releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false)
{
var sw = Stopwatch.StartNew();
var errors = new List<Exception>();
foreach (var releaseId in releaseIds)
{
var result = await ScanRelease(user, releaseId, isReadOnly, wasDoneForInvalidTrackPlay);
if (!result.IsSuccess)
{
if (result.Errors?.Any() ?? false)
{
errors.AddRange(result.Errors);
}
}
}
sw.Stop();
await LogAndPublish($"** Completed! ScanReleases: Release Count [{ releaseIds.Count() }], Elapsed Time [{sw.Elapsed}]");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> UpdateInviteTokenUsed(Guid? tokenId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
if (tokenId == null)
{
return new OperationResult<bool>(true, $"Invalid Invite TokenId [{tokenId}]");
}
var token = DbContext.InviteTokens.FirstOrDefault(x => x.RoadieId == tokenId);
if (token == null)
{
return new OperationResult<bool>(true, $"Invite Token Not Found [{tokenId}]");
}
token.Status = Statuses.Complete;
token.LastUpdated = DateTime.UtcNow;
await DbContext.SaveChangesAsync();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> ValidateInviteToken(Guid? tokenId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
if (tokenId == null)
{
return new OperationResult<bool>(true, $"Invalid Invite TokenId [{tokenId}]");
}
var token = DbContext.InviteTokens.FirstOrDefault(x => x.RoadieId == tokenId);
if (token == null)
{
return new OperationResult<bool>(true, $"Invite Token Not Found [{tokenId}]");
}
if (token.ExpiresDate < DateTime.UtcNow || token.Status == Statuses.Expired)
{
token.Status = Statuses.Expired;
token.LastUpdated = DateTime.UtcNow;
await DbContext.SaveChangesAsync();
return new OperationResult<bool>(true, $"Invite Token [{tokenId}] Expired [{ token.ExpiresDate }]");
}
if (token.Status == Statuses.Complete)
{
return new OperationResult<bool>(true, $"Invite Token [{tokenId}] Already Used");
}
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
private void EventMessageLogger_Messages(object sender, EventMessage e) => Task.WaitAll(LogAndPublish(e.Message, e.Level));
private async Task LogAndPublish(string message, LogLevel level = LogLevel.Trace)

View file

@ -2,7 +2,6 @@
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
@ -13,9 +12,6 @@ using Roadie.Library.Extensions;
using Roadie.Library.Identity;
using Roadie.Library.Imaging;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.FileName;
using Roadie.Library.MetaData.ID3Tags;
using Roadie.Library.MetaData.LastFm;
using Roadie.Library.Models;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Releases;
@ -30,7 +26,6 @@ using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using data = Roadie.Library.Data;
using mb = Roadie.Library.MetaData.MusicBrainz;
namespace Roadie.Api.Services
{
@ -45,20 +40,9 @@ namespace Roadie.Api.Services
private ICollectionService CollectionService { get; }
private IFileDirectoryProcessorService FileDirectoryProcessorService { get; }
private IFileNameHelper FileNameHelper { get; }
private IID3TagsHelper ID3TagsHelper { get; }
private ILabelLookupEngine LabelLookupEngine { get; }
private ILastFmHelper LastFmHelper { get; }
private mb.IMusicBrainzProvider MusicBrainzProvider { get; }
private IPlaylistService PlaylistService { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; }
private IReleaseService ReleaseService { get; }
public ArtistService(IRoadieSettings configuration,
@ -72,13 +56,7 @@ namespace Roadie.Api.Services
IBookmarkService bookmarkService,
IReleaseService releaseService,
IArtistLookupEngine artistLookupEngine,
mb.IMusicBrainzProvider musicBrainzProvider,
ILastFmHelper lastFmHelper,
IFileNameHelper fileNameHelper,
IID3TagsHelper id3tagsHelper,
IAudioMetaDataHelper audioMetaDataHelper,
IReleaseLookupEngine releaseLookupEngine,
ILabelLookupEngine labelLookupEngine,
IFileDirectoryProcessorService fileDirectoryProcessorService
)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
@ -86,18 +64,8 @@ namespace Roadie.Api.Services
CollectionService = collectionService;
PlaylistService = playlistService;
BookmarkService = bookmarkService;
MusicBrainzProvider = musicBrainzProvider;
LastFmHelper = lastFmHelper;
FileNameHelper = fileNameHelper;
ID3TagsHelper = id3tagsHelper;
ArtistLookupEngine = artistLookupEngine;
LabelLookupEngine = labelLookupEngine;
ReleaseLookupEngine = releaseLookupEngine;
AudioMetaDataHelper = audioMetaDataHelper;
ReleaseService = releaseService;
FileDirectoryProcessorService = fileDirectoryProcessorService;
}
@ -238,7 +206,7 @@ namespace Roadie.Api.Services
}
IQueryable<int> genreArtistIds = null;
var isFilteredToGenre = false;
if(request.FilterToGenreId.HasValue)
if (request.FilterToGenreId.HasValue)
{
genreArtistIds = (from ag in DbContext.ArtistGenres
join g in DbContext.Genres on ag.GenreId equals g.Id
@ -275,7 +243,7 @@ namespace Roadie.Api.Services
: null;
int[] randomArtistIds = null;
if (doRandomize ??false)
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
var userId = roadieUser?.Id ?? -1;
@ -288,27 +256,26 @@ namespace Roadie.Api.Services
AND {2} = 0)
order BY RIGHT(HEX((1 << 24) * (1 + RAND())), 6)
LIMIT 0, {0}";
randomArtistIds = (from a in DbContext.Artists.FromSql(sql, randomLimit, userId, request.FilterFavoriteOnly ? "1" : "0")
randomArtistIds = (from a in DbContext.Artists.FromSql(sql, randomLimit, userId, request.FilterFavoriteOnly ? "1" : "0")
select a.Id).ToArray();
rowCount = DbContext.Artists.Count();
}
var result = (from a in DbContext.Artists
where !onlyWithReleases || a.ReleaseCount > 0
where randomArtistIds == null || randomArtistIds.Contains(a.Id)
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) ||
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.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(request.FilterValue) ||
a.AlternateNames.Equals(normalizedFilterValue)
where !request.FilterFavoriteOnly || favoriteArtistIds.Contains(a.Id)
where request.FilterToLabelId == null || labelArtistIds.Contains(a.Id)
@ -642,10 +609,10 @@ namespace Roadie.Api.Services
return new OperationResult<bool>(true, $"Artist Not Found [{model.Id}]");
}
// If artist is being renamed, see if artist already exists with new model supplied name
if(artist.Name.ToAlphanumericName() != model.Name.ToAlphanumericName())
if (artist.Name.ToAlphanumericName() != model.Name.ToAlphanumericName())
{
var existingArtist = DbContext.Artists.FirstOrDefault(x => x.Name == model.Name);
if(existingArtist != null)
if (existingArtist != null)
{
return new OperationResult<bool>($"Artist already exists with name [{ model.Name }].");
}
@ -1240,7 +1207,7 @@ namespace Roadie.Api.Services
if (r.IsSuccess)
{
result.PlaylistsWithArtistReleases = r.Rows.ToArray();
}
}
tsw.Stop();
timings.Add("playlists", tsw.ElapsedMilliseconds);
}
@ -1265,7 +1232,6 @@ namespace Roadie.Api.Services
.ToArray()
.Select(r => ReleaseList.FromDataRelease(r, r.Artist, HttpContext.BaseUrl, MakeArtistThumbnailImage(r.Artist.RoadieId), MakeReleaseThumbnailImage(r.RoadieId)));
result.ArtistContributionReleases = result.ArtistContributionReleases.Any()
? result.ArtistContributionReleases
: null;

View file

@ -1,7 +1,6 @@
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
@ -84,7 +83,7 @@ namespace Roadie.Api.Services
sw.Start();
var cacheKey = string.Format("urn:collection_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
var result = await CacheManager.GetAsync(cacheKey, async () =>
var result = await CacheManager.GetAsync(cacheKey, async () =>
{
return await CollectionByIdAction(id, includes);
}, data.Artist.CacheRegionUrn(id));
@ -179,7 +178,6 @@ namespace Roadie.Api.Services
join a in DbContext.Artists on r.ArtistId equals a.Id
where a.RoadieId == artistId
select c).Distinct();
}
else if (releaseId.HasValue)
{
@ -302,7 +300,7 @@ namespace Roadie.Api.Services
var collectionImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (collectionImage != null)
{
// Save unaltered collection image
// Save unaltered collection image
File.WriteAllBytes(collection.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(collectionImage));
// Update Thumbnail
collection.Thumbnail = ImageHelper.ResizeToThumbnail(collectionImage, Configuration);

View file

@ -37,11 +37,10 @@ namespace Roadie.Api.Services
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
private IFileProcessor FileProcessor { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; }
private IReleaseService ReleaseService { get; }
private IFileProcessor FileProcessor { get; }
public FileDirectoryProcessorService(IRoadieSettings configuration,
IHttpEncoder httpEncoder,
IHttpContext httpContext,

View file

@ -2,12 +2,11 @@
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Extensions;
using Roadie.Library.Encoding;
using Roadie.Library.Extensions;
using Roadie.Library.Identity;
using Roadie.Library.Imaging;
using Roadie.Library.Models;
@ -42,7 +41,7 @@ namespace Roadie.Api.Services
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 () =>
var result = await CacheManager.GetAsync(cacheKey, async () =>
{
return await GenreByIdAction(id, includes);
}, data.Genre.CacheRegionUrn(id));
@ -83,7 +82,99 @@ namespace Roadie.Api.Services
};
}
public Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false)
{
var sw = new Stopwatch();
sw.Start();
int? rowCount = null;
if (!string.IsNullOrEmpty(request.Sort))
{
request.Sort = request.Sort.Replace("createdDate", "createdDateTime");
request.Sort = request.Sort.Replace("lastUpdated", "lastUpdatedDateTime");
}
int[] randomGenreIds = null;
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
// This is MySQL specific but I can't figure out how else to get random without throwing EF local evaluate warnings.
var sql = @"select g.id
FROM `genre` g
order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}";
randomGenreIds = (from l in DbContext.Genres.FromSql(sql, randomLimit)
select l.Id).ToArray();
rowCount = DbContext.Genres.Count();
}
var result = from g in DbContext.Genres
where randomGenreIds == null || randomGenreIds.Contains(g.Id)
let releaseCount = (from rg in DbContext.ReleaseGenres
where rg.GenreId == g.Id
select rg.Id).Count()
let artistCount = (from rg in DbContext.ArtistGenres
where rg.GenreId == g.Id
select rg.Id).Count()
where request.FilterValue.Length == 0 || g.Name.Contains(request.FilterValue)
select new GenreList
{
DatabaseId = g.Id,
Id = g.RoadieId,
Genre = new DataToken
{
Text = g.Name,
Value = g.RoadieId.ToString()
},
ReleaseCount = releaseCount,
ArtistCount = artistCount,
CreatedDate = g.CreatedDate,
LastUpdated = g.LastUpdated,
Thumbnail = MakeGenreThumbnailImage(g.RoadieId)
};
GenreList[] rows;
rowCount = rowCount ?? result.Count();
if (doRandomize ?? false)
{
rows = result.ToArray();
}
else
{
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "Genre.Text", "ASC" } })
: request.OrderValue();
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
}
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<GenreList>
{
TotalCount = rowCount.Value,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
}
public async Task<OperationResult<Image>> SetGenreImageByUrl(User user, Guid id, string imageUrl)
{
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
}
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);
}
private Task<OperationResult<Genre>> GenreByIdAction(Guid id, IEnumerable<string> includes = null)
{
@ -139,24 +230,6 @@ namespace Roadie.Api.Services
});
}
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();
@ -170,7 +243,7 @@ namespace Roadie.Api.Services
genre.Thumbnail = imageBytes;
if (genre.Thumbnail != null)
{
// Save unaltered label image
// Save unaltered label image
File.WriteAllBytes(genre.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(imageBytes));
genre.Thumbnail = ImageHelper.ResizeToThumbnail(genre.Thumbnail, Configuration);
}
@ -196,84 +269,5 @@ namespace Roadie.Api.Services
Errors = errors
};
}
public Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false)
{
var sw = new Stopwatch();
sw.Start();
int? rowCount = null;
if (!string.IsNullOrEmpty(request.Sort))
{
request.Sort = request.Sort.Replace("createdDate", "createdDateTime");
request.Sort = request.Sort.Replace("lastUpdated", "lastUpdatedDateTime");
}
int[] randomGenreIds = null;
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
// This is MySQL specific but I can't figure out how else to get random without throwing EF local evaluate warnings.
var sql = @"select g.id
FROM `genre` g
order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}";
randomGenreIds = (from l in DbContext.Genres.FromSql(sql, randomLimit)
select l.Id).ToArray();
rowCount = DbContext.Genres.Count();
}
var result = from g in DbContext.Genres
where randomGenreIds == null || randomGenreIds.Contains(g.Id)
let releaseCount = (from rg in DbContext.ReleaseGenres
where rg.GenreId == g.Id
select rg.Id).Count()
let artistCount = (from rg in DbContext.ArtistGenres
where rg.GenreId == g.Id
select rg.Id).Count()
where request.FilterValue.Length == 0 || g.Name.Contains(request.FilterValue)
select new GenreList
{
DatabaseId = g.Id,
Id = g.RoadieId,
Genre = new DataToken
{
Text = g.Name,
Value = g.RoadieId.ToString()
},
ReleaseCount = releaseCount,
ArtistCount = artistCount,
CreatedDate = g.CreatedDate,
LastUpdated = g.LastUpdated,
Thumbnail = MakeGenreThumbnailImage(g.RoadieId)
};
GenreList[] rows;
rowCount = rowCount ?? result.Count();
if (doRandomize ?? false)
{
rows = result.ToArray();
}
else
{
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "Genre.Text", "ASC" } })
: request.OrderValue();
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
}
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<GenreList>
{
TotalCount = rowCount.Value,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
}
}
}

View file

@ -9,18 +9,16 @@ namespace Roadie.Api.Services
{
public interface IAdminService
{
void PerformStartUpTasks();
Task<OperationResult<bool>> DeleteArtist(ApplicationUser user, Guid artistId);
Task<OperationResult<bool>> DeleteArtistReleases(ApplicationUser user, Guid artistId, bool doDeleteFiles = false);
Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index);
Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId);
Task<OperationResult<bool>> DeleteGenre(ApplicationUser user, Guid genreId);
Task<OperationResult<bool>> DeleteLabel(ApplicationUser user, Guid labelId);
Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles);
Task<OperationResult<bool>> DeleteReleaseSecondaryImage(ApplicationUser user, Guid releaseId, int index);
@ -33,6 +31,8 @@ namespace Roadie.Api.Services
Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user);
void PerformStartUpTasks();
Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = false);
Task<OperationResult<bool>> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false);
@ -49,8 +49,8 @@ namespace Roadie.Api.Services
Task<OperationResult<bool>> ScanReleases(ApplicationUser user, IEnumerable<Guid> releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false);
Task<OperationResult<bool>> ValidateInviteToken(Guid? tokenId);
Task<OperationResult<bool>> UpdateInviteTokenUsed(Guid? tokenId);
Task<OperationResult<bool>> ValidateInviteToken(Guid? tokenId);
}
}

View file

@ -1,8 +1,8 @@
using System.Collections.Generic;
using Roadie.Library;
using Roadie.Library.Identity;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Roadie.Library;
using Roadie.Library.Identity;
namespace Roadie.Api.Services
{

View file

@ -13,10 +13,13 @@ 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);
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);
}
}

View file

@ -2,7 +2,6 @@
using Roadie.Library;
using Roadie.Library.Identity;
using Roadie.Library.Models;
using Roadie.Library.Models.Users;
using Roadie.Library.SearchEngines.Imaging;
using System;
using System.Collections.Generic;
@ -26,10 +25,10 @@ namespace Roadie.Api.Services
Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id);
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>> LabelImage(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);

View file

@ -14,10 +14,10 @@ namespace Roadie.Api.Services
{
Task<OperationResult<Label>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<PagedResult<LabelList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false);
Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id);
Task<PagedResult<LabelList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false);
Task<OperationResult<bool>> MergeLabelsIntoLabel(ApplicationUser user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge);
Task<OperationResult<Image>> SetLabelImageByUrl(User user, Guid id, string imageUrl);

View file

@ -7,12 +7,9 @@ using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Encoding;
using Roadie.Library.Enums;
using Roadie.Library.Extensions;
using Roadie.Library.Identity;
using Roadie.Library.Imaging;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.Models;
using Roadie.Library.Models.Users;
using Roadie.Library.SearchEngines.Imaging;
using Roadie.Library.Utility;
using System;
@ -27,14 +24,12 @@ namespace Roadie.Api.Services
{
public class ImageService : ServiceBase, IImageService
{
public string Referrer { get; set; }
public string RequestIp { get; set; }
private IDefaultNotFoundImages DefaultNotFoundImages { get; }
private IImageSearchManager ImageSearchManager { get; }
public string Referrer { get; set; }
public string RequestIp { get; set; }
public ImageService(IRoadieSettings configuration,
IHttpEncoder httpEncoder,
IHttpContext httpContext,
@ -116,6 +111,16 @@ namespace Roadie.Api.Services
};
}
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>> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
@ -128,17 +133,6 @@ 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",
@ -196,7 +190,6 @@ namespace Roadie.Api.Services
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<FileOperationResult<Image>> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
@ -387,6 +380,47 @@ namespace Roadie.Api.Services
};
}
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 async Task<FileOperationResult<Image>> GetImageFileOperation(string type, string regionUrn, Guid id, int? width, int? height, Func<Task<FileOperationResult<Image>>> action, EntityTagHeaderValue etag = null)
{
try
@ -475,7 +509,7 @@ namespace Roadie.Api.Services
var labelImageFilename = label.PathToImage(Configuration);
try
{
if(File.Exists(labelImageFilename))
if (File.Exists(labelImageFilename))
{
image.Bytes = File.ReadAllBytes(labelImageFilename);
}
@ -498,47 +532,6 @@ 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

View file

@ -44,32 +44,6 @@ namespace Roadie.Api.Services
BookmarkService = bookmarkService;
}
public async Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
if (label == null) return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", id));
DbContext.Labels.Remove(label);
await DbContext.SaveChangesAsync();
var labelImageFilename = label.PathToImage(Configuration);
if(File.Exists(labelImageFilename))
{
File.Delete(labelImageFilename);
}
Logger.LogWarning("User `{0}` deleted Label `{1}]`", user, label);
CacheManager.ClearRegion(label.CacheRegion);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = true,
Data = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<Label>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
@ -112,6 +86,32 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> Delete(ApplicationUser user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
if (label == null) return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", id));
DbContext.Labels.Remove(label);
await DbContext.SaveChangesAsync();
var labelImageFilename = label.PathToImage(Configuration);
if (File.Exists(labelImageFilename))
{
File.Delete(labelImageFilename);
}
Logger.LogWarning("User `{0}` deleted Label `{1}]`", user, label);
CacheManager.ClearRegion(label.CacheRegion);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = true,
Data = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public Task<Library.Models.Pagination.PagedResult<LabelList>> List(User roadieUser, PagedRequest request,
bool? doRandomize = false)
{
@ -135,19 +135,18 @@ namespace Roadie.Api.Services
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
// This is MySQL specific but I can't figure out how else to get random without throwing EF local evaluate warnings.
var sql = @"select l.id
var sql = @"select l.id
FROM `label` l
order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}";
randomLabelIds = (from l in DbContext.Labels.FromSql(sql, randomLimit)
select l.Id).ToArray();
rowCount = DbContext.Labels.Count();
}
var result = from l in DbContext.Labels
where randomLabelIds == null || randomLabelIds.Contains(l.Id)
where request.FilterValue == "" || (
where request.FilterValue == "" || (
l.Name.Contains(request.FilterValue) ||
l.SortName.Contains(request.FilterValue) ||
l.AlternateNames.Contains(request.FilterValue) ||
@ -317,7 +316,7 @@ namespace Roadie.Api.Services
var labelImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (labelImage != null)
{
// Save unaltered label image
// Save unaltered label image
File.WriteAllBytes(label.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(labelImage));
label.Thumbnail = ImageHelper.ResizeToThumbnail(labelImage, Configuration);
}
@ -379,7 +378,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);
tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds);
if (includes != null && includes.Any())
@ -466,7 +465,7 @@ namespace Roadie.Api.Services
label.Thumbnail = imageBytes;
if (label.Thumbnail != null)
{
// Save unaltered label image
// Save unaltered label image
File.WriteAllBytes(label.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(imageBytes));
label.Thumbnail = ImageHelper.ResizeToThumbnail(label.Thumbnail, Configuration);
}

View file

@ -113,7 +113,7 @@ 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 () =>
var result = await CacheManager.GetAsync(cacheKey, async () =>
{
return await PlaylistByIdAction(id, includes);
}, data.Artist.CacheRegionUrn(id));
@ -351,7 +351,7 @@ namespace Roadie.Api.Services
var playlistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (playlistImage != null)
{
// Save unaltered playlist image
// Save unaltered playlist image
File.WriteAllBytes(playlist.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(playlistImage));
// Update thumbnail
playlist.Thumbnail = ImageHelper.ResizeToThumbnail(playlistImage, Configuration);

View file

@ -12,10 +12,6 @@ using Roadie.Library.Extensions;
using Roadie.Library.Identity;
using Roadie.Library.Imaging;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.FileName;
using Roadie.Library.MetaData.ID3Tags;
using Roadie.Library.MetaData.LastFm;
using Roadie.Library.MetaData.MusicBrainz;
using Roadie.Library.Models;
using Roadie.Library.Models.Collections;
using Roadie.Library.Models.Pagination;
@ -48,53 +44,24 @@ namespace Roadie.Api.Services
private IBookmarkService BookmarkService { get; }
private ICollectionService CollectionService { get; }
private IFileNameHelper FileNameHelper { get; }
private IID3TagsHelper ID3TagsHelper { get; }
private ILabelLookupEngine LabelLookupEngine { get; }
private ILastFmHelper LastFmHelper { get; }
private IMusicBrainzProvider MusicBrainzProvider { get; }
private IPlaylistService PlaylistService { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; }
public ReleaseService(IRoadieSettings configuration,
IHttpEncoder httpEncoder,
IHttpContext httpContext,
data.IRoadieDbContext dbContext,
ICacheManager cacheManager,
ICollectionService collectionService,
IPlaylistService playlistService,
ILogger<ReleaseService> logger,
IBookmarkService bookmarkService,
IArtistLookupEngine artistLookupEngine,
IReleaseLookupEngine releaseLookupEngine,
IMusicBrainzProvider musicBrainzProvider,
ILastFmHelper lastFmHelper,
IFileNameHelper fileNameHelper,
IID3TagsHelper id3tagsHelper,
IAudioMetaDataHelper audioMetaDataHelper,
ILabelLookupEngine labelLookupEngine)
IAudioMetaDataHelper audioMetaDataHelper
)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
{
CollectionService = collectionService;
PlaylistService = playlistService;
BookmarkService = bookmarkService;
MusicBrainzProvider = musicBrainzProvider;
LastFmHelper = lastFmHelper;
FileNameHelper = fileNameHelper;
ID3TagsHelper = id3tagsHelper;
ArtistLookupEngine = artistLookupEngine;
LabelLookupEngine = labelLookupEngine;
ReleaseLookupEngine = releaseLookupEngine;
AudioMetaDataHelper = audioMetaDataHelper;
}
@ -1688,7 +1655,7 @@ namespace Roadie.Api.Services
release.LastUpdated = now;
await DbContext.SaveChangesAsync();
await CheckAndChangeReleaseTitle(release, originalReleaseFolder);
// Update release ID3 Tags
// Update release ID3 Tags
foreach (var mp3 in Directory.GetFiles(releaseFolder, "*.mp3", SearchOption.AllDirectories))
{
var trackFileInfo = new FileInfo(mp3);
@ -1697,7 +1664,7 @@ namespace Roadie.Api.Services
{
audioMetaData.Year = release.ReleaseYear;
audioMetaData.Release = release.Title;
if(artist != null)
if (artist != null)
{
audioMetaData.Artist = artist.Name;
}

View file

@ -22,18 +22,23 @@ namespace Roadie.Api.Services
{
public static string TrackTokenSalt = "B0246908-FBD6-4E12-A96C-AF5B086115B3";
private static readonly Lazy<Hashids> hashIds = new Lazy<Hashids>(() =>
{
return new Hashids(TrackTokenSalt);
});
protected readonly ICacheManager _cacheManager;
protected readonly IRoadieSettings _configuration;
protected readonly data.IRoadieDbContext _dbContext;
protected readonly IHttpContext _httpContext;
protected readonly IHttpEncoder _httpEncoder;
protected readonly ILogger _logger;
private static readonly Lazy<Hashids> hashIds = new Lazy<Hashids>(() =>
{
return new Hashids(TrackTokenSalt);
});
protected ICacheManager CacheManager => _cacheManager;
protected IRoadieSettings Configuration => _configuration;
@ -66,6 +71,11 @@ namespace Roadie.Api.Services
return TrackPlayToken(user, trackRoadieId).Equals(token);
}
public static string MakeTrackPlayUrl(ApplicationUser user, string baseUrl, int trackId, Guid trackRoadieId)
{
return $"{baseUrl}/play/track/{user.Id}/{TrackPlayToken(user, trackRoadieId)}/{trackRoadieId}.mp3";
}
public static string TrackPlayToken(ApplicationUser user, Guid trackId)
{
var trackIdPart = BitConverter.ToInt32(trackId.ToByteArray(), 6);
@ -126,15 +136,6 @@ namespace Roadie.Api.Services
}, data.Collection.CacheRegionUrn(id));
}
protected data.Label GetLabel(Guid id)
{
return CacheManager.Get(data.Label.CacheUrn(id), () =>
{
return DbContext.Labels
.FirstOrDefault(x => x.RoadieId == id);
}, data.Label.CacheRegionUrn(id));
}
protected data.Genre GetGenre(Guid id)
{
return CacheManager.Get(data.Genre.CacheUrn(id), () =>
@ -144,6 +145,15 @@ namespace Roadie.Api.Services
}, data.Genre.CacheRegionUrn(id));
}
protected data.Label GetLabel(Guid id)
{
return CacheManager.Get(data.Label.CacheUrn(id), () =>
{
return DbContext.Labels
.FirstOrDefault(x => x.RoadieId == id);
}, data.Label.CacheRegionUrn(id));
}
protected data.Playlist GetPlaylist(Guid id)
{
return CacheManager.Get(data.Playlist.CacheUrn(id), () =>
@ -245,8 +255,13 @@ namespace Roadie.Api.Services
$"{HttpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}/{Configuration.SmallImageSize.Width}/{Configuration.SmallImageSize.Height}");
}
protected Image MakeGenreThumbnailImage(Guid id)
{
return MakeThumbnailImage(id, "genre");
}
protected Image MakeImage(Guid id, int width = 200, int height = 200, string caption = null,
bool includeCachebuster = false)
bool includeCachebuster = false)
{
return new Image(
$"{HttpContext.ImageBaseUrl}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}",
@ -264,11 +279,6 @@ 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}");
@ -289,11 +299,6 @@ namespace Roadie.Api.Services
return MakeThumbnailImage(id, "release");
}
public static string MakeTrackPlayUrl(ApplicationUser user, string baseUrl, int trackId, Guid trackRoadieId)
{
return $"{baseUrl}/play/track/{user.Id}/{TrackPlayToken(user, trackRoadieId)}/{trackRoadieId}.mp3";
}
protected Image MakeTrackThumbnailImage(Guid id)
{
return MakeThumbnailImage(id, "track");
@ -535,7 +540,7 @@ namespace Roadie.Api.Services
};
}
protected async Task<OperationResult<bool>> ToggleReleaseDisliked(Guid releaseId, ApplicationUser user, bool isDisliked)
protected async Task<OperationResult<bool>> ToggleReleaseDisliked(Guid releaseId, ApplicationUser user, bool isDisliked)
{
var release = DbContext.Releases
.Include(x => x.Artist)
@ -549,7 +554,7 @@ namespace Roadie.Api.Services
{
return new OperationResult<bool>(true, $"Invalid Release Id [{releaseId}]");
}
var userRelease = DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id);
var userRelease = DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id);
if (userRelease == null)
{
userRelease = new data.UserRelease
@ -717,13 +722,13 @@ namespace Roadie.Api.Services
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId);
if (artist != null)
{
artist.ReleaseCount = DbContext.Releases.Where(x => x.ArtistId == artistId).Count();
artist.ReleaseCount = DbContext.Releases.Where(x => x.ArtistId == artistId).Count();
artist.TrackCount = (from r in DbContext.Releases
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId
where tr.ArtistId == artistId || r.ArtistId == artistId
select tr).Count();
artist.LastUpdated = now;
await DbContext.SaveChangesAsync();
CacheManager.ClearRegion(artist.CacheRegion);

View file

@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging;
using MySql.Data.MySqlClient;
using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;

View file

@ -372,9 +372,9 @@ namespace Roadie.Api.Services
/// <summary>
/// Deletes a saved playlist.
/// </summary>
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> DeletePlaylist(subsonic.Request request,User roadieUser)
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> DeletePlaylist(subsonic.Request request, User roadieUser)
{
//request.PlaylistId.Value
//request.PlaylistId.Value
var deleteResult = await PlaylistService.DeletePlaylist(roadieUser, request.PlaylistId.Value);
if (deleteResult == null || deleteResult.IsNotFoundResult)
@ -2221,7 +2221,7 @@ namespace Roadie.Api.Services
private subsonic.Child SubsonicChildForTrack(TrackList t)
{
var userRating = t.UserRating?.Rating ?? 0;
if(userRating > 0)
if (userRating > 0)
{
// This is done as many subsonic apps think rating "1" is don't play song, versus a minimum indication of like as intended for Roadie.
// To disable this set the configuration SubsonicRatingBoost to 0

View file

@ -37,8 +37,8 @@ namespace Roadie.Api.Services
private IBookmarkService BookmarkService { get; }
public TrackService(IRoadieSettings configuration, IHttpEncoder httpEncoder,IHttpContext httpContext,
data.IRoadieDbContext dbContext, ICacheManager cacheManager,ILogger<TrackService> logger,
public TrackService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext,
data.IRoadieDbContext dbContext, ICacheManager cacheManager, ILogger<TrackService> logger,
IBookmarkService bookmarkService, IAdminService adminService, IAudioMetaDataHelper audioMetaDataHelper)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
{
@ -112,14 +112,13 @@ namespace Roadie.Api.Services
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = string.Format("urn:track_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
var result = await CacheManager.GetAsync(cacheKey, async () =>
var result = await CacheManager.GetAsync(cacheKey, async () =>
{
tsw.Restart();
var rr = await TrackByIdAction(id, includes);
tsw.Stop();
timings.Add("TrackByIdAction", tsw.ElapsedMilliseconds);
return rr;
}, data.Track.CacheRegionUrn(id));
if (result?.Data != null && roadieUser != null)
{
@ -162,7 +161,6 @@ namespace Roadie.Api.Services
tsw.Stop();
timings.Add("userTracks", tsw.ElapsedMilliseconds);
if (result.Data.Comments.Any())
{
tsw.Restart();
@ -311,28 +309,28 @@ namespace Roadie.Api.Services
request.FilterFavoriteOnly = false;
}
randomTrackIds = (from t in DbContext.Tracks
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
join a in DbContext.Artists on r.ArtistId equals a.Id
where !request.FilterRatedOnly || request.FilterRatedOnly && t.Rating > 0
where !dislikedArtistIds.Contains(r.ArtistId)
where !dislikedArtistIds.Contains(t.ArtistId ?? 0)
where !dislikedReleaseIds.Contains(r.Id)
where !dislikedTrackIds.Contains(t.Id)
where favoritedTrackIds == null || favoritedTrackIds.Contains(t.Id)
where t.Hash != null
select new TrackList
{
DatabaseId = t.Id,
Artist = new ArtistList
{
Artist = new DataToken { Value = a.RoadieId.ToString(), Text = a.Name }
},
Release = new ReleaseList
{
Release = new DataToken { Value = r.RoadieId.ToString(), Text = r.Title }
}
})
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
join a in DbContext.Artists on r.ArtistId equals a.Id
where !request.FilterRatedOnly || request.FilterRatedOnly && t.Rating > 0
where !dislikedArtistIds.Contains(r.ArtistId)
where !dislikedArtistIds.Contains(t.ArtistId ?? 0)
where !dislikedReleaseIds.Contains(r.Id)
where !dislikedTrackIds.Contains(t.Id)
where favoritedTrackIds == null || favoritedTrackIds.Contains(t.Id)
where t.Hash != null
select new TrackList
{
DatabaseId = t.Id,
Artist = new ArtistList
{
Artist = new DataToken { Value = a.RoadieId.ToString(), Text = a.Name }
},
Release = new ReleaseList
{
Release = new DataToken { Value = r.RoadieId.ToString(), Text = r.Title }
}
})
.OrderBy(x => x.Artist.RandomSortId)
.ThenBy(x => x.RandomSortId)
.ThenBy(x => x.RandomSortId)
@ -539,7 +537,7 @@ namespace Roadie.Api.Services
if (request.Action == User.ActionKeyUserRated)
{
sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string>{{"UserTrack.Rating", "DESC"}, {"MediaNumber", "ASC"}, {"TrackNumber", "ASC"}})
? request.OrderValue(new Dictionary<string, string> { { "UserTrack.Rating", "DESC" }, { "MediaNumber", "ASC" }, { "TrackNumber", "ASC" } })
: request.OrderValue();
}
else
@ -557,7 +555,7 @@ namespace Roadie.Api.Services
}
}
if(doRandomize ?? false)
if (doRandomize ?? false)
{
rows = TrackList.Shuffle(result).ToArray();
}
@ -670,7 +668,7 @@ namespace Roadie.Api.Services
}
catch (Exception ex)
{
Logger.LogError(ex, "Error In List, Request [{0}], User [{1}]", JsonConvert.SerializeObject(request),roadieUser);
Logger.LogError(ex, "Error In List, Request [{0}], User [{1}]", JsonConvert.SerializeObject(request), roadieUser);
return Task.FromResult(new Library.Models.Pagination.PagedResult<TrackList>
{
Message = "An Error has occured"
@ -813,7 +811,7 @@ namespace Roadie.Api.Services
info.CacheControl = "no-store, must-revalidate, no-cache, max-age=0";
info.Pragma = "no-cache";
info.Expires = "Mon, 01 Jan 1990 00:00:00 GMT";
}
}
var bytesToRead = (int)(endBytes - beginBytes) + 1;
var trackBytes = new byte[bytesToRead];
using (var fs = trackFileInfo.OpenRead())
@ -857,7 +855,7 @@ namespace Roadie.Api.Services
try
{
var originalTitle = track.Title;
var originalTrackNumber = track.TrackNumber;
var originalTrackNumber = track.TrackNumber;
var originalFilename = track.PathToTrack(Configuration);
var now = DateTime.UtcNow;
track.IsLocked = model.IsLocked;
@ -906,13 +904,13 @@ namespace Roadie.Api.Services
// See if Title was changed if so then modify DB Filename and rename track
var shouldFileNameBeUpdated = originalTitle != track.Title || originalTrackNumber != track.TrackNumber;
if(shouldFileNameBeUpdated)
if (shouldFileNameBeUpdated)
{
track.FileName = FolderPathHelper.TrackFileName(Configuration, track.Title, track.TrackNumber, track.ReleaseMedia.MediaNumber, track.ReleaseMedia.TrackCount);
File.Move(originalFilename, track.PathToTrack(Configuration));
}
track.LastUpdated = now;
await DbContext.SaveChangesAsync();
await DbContext.SaveChangesAsync();
var trackFileInfo = new FileInfo(track.PathToTrack(Configuration));
var audioMetaData = await AudioMetaDataHelper.GetInfo(trackFileInfo);

View file

@ -53,9 +53,9 @@ namespace Roadie.Api.Services
public async Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes, bool isAccountSettingsEdit = false)
{
if(isAccountSettingsEdit)
if (isAccountSettingsEdit)
{
if(user.UserId != id && !user.IsAdmin)
if (user.UserId != id && !user.IsAdmin)
{
var r = new OperationResult<User>("Access Denied");
r.IsAccessDeniedResult = true;
@ -74,9 +74,9 @@ namespace Roadie.Api.Services
if (result?.Data != null)
{
result.Data.Avatar = MakeUserThumbnailImage(id);
if(!isAccountSettingsEdit)
if (!isAccountSettingsEdit)
{
result.Data.ApiToken = null;
result.Data.ApiToken = null;
result.Data.ConcurrencyStamp = null;
}
}
@ -502,11 +502,11 @@ namespace Roadie.Api.Services
{
imageData = ImageHelper.ConvertToGifFormat(imageData);
// Save unaltered user image
// Save unaltered user image
File.WriteAllBytes(user.PathToImage(Configuration), imageData);
// Update thumbnail
user.Avatar = ImageHelper.ResizeImage(imageData, Configuration.ThumbnailImageSize.Width, Configuration.ThumbnailImageSize.Height);
if(user.Avatar.Length >= ImageHelper.MaximumThumbnailByteSize)
if (user.Avatar.Length >= ImageHelper.MaximumThumbnailByteSize)
{
user.Avatar = null;
}
@ -752,7 +752,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()
};
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);

View file

@ -27,9 +27,9 @@
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.10.0" />
<PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.1" />
<PackageReference Include="Microsoft.AspNetCore.All" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.2.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.2.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.2.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />