2
0
Fork 0
mirror of https://github.com/sphildreth/roadie synced 2025-02-17 21:48:27 +00:00

Delete Users Code Complete.

This commit is contained in:
Steven Hildreth 2019-05-29 17:25:40 -05:00
parent 7606a604f8
commit af71e4c9f2
40 changed files with 1198 additions and 1173 deletions

View file

@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="CsvHelper" Version="12.1.2" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.6" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.7" />
<PackageReference Include="FluentFTP" Version="24.0.0" />
<PackageReference Include="Hashids.net" Version="1.2.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.4" />
@ -32,7 +32,7 @@
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.5.0" />
<PackageReference Include="System.Runtime.Caching" Version="4.5.0" />
<PackageReference Include="z440.atl.core" Version="2.9.0" />
<PackageReference Include="z440.atl.core" Version="2.10.0" />
<PackageReference Include="zlib.net-mutliplatform" Version="1.0.4" />
</ItemGroup>

View file

@ -208,7 +208,7 @@ namespace Roadie.Library.MetaData.ID3Tags
result.Release = id3v2.Album;
result.Artist = id3v2.AlbumArtist ?? id3v2.Artist;
result.ArtistRaw = id3v2.AlbumArtist ?? id3v2.Artist;
result.Genres = id3v2.Genre?.Split(new char[] { ',', '\\' });
result.Genres = id3v2.Genre?.Split(new char[] { ',', '\\', ';', '|' });
result.TrackArtist = id3v2.OriginalArtist ?? id3v2.Artist ?? id3v2.AlbumArtist;
result.TrackArtistRaw = id3v2.OriginalArtist;
result.AudioBitrate = (int?)audioFile.Bitrate;

View file

@ -19,7 +19,6 @@ 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.Processors;
using Roadie.Library.Utility;
using System;
@ -89,45 +88,6 @@ namespace Roadie.Api.Services
this.ArtistFactory = new ArtistFactory(configuration, httpEncoder, context, cacheManager, MessageLogger, this.ArtistLookupEngine, this.ReleaseFactory, this.ImageFactory, this.ReleaseLookupEngine, this.AudioMetaDataHelper);
}
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null)
{
await this.LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{ artistId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Artist Not Found [{ artistId }]");
}
try
{
var artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly);
var artistImageFilename = artistImagesInFolder.Skip(index).FirstOrDefault();
if (artistImageFilename.Exists)
{
artistImageFilename.Delete();
}
this.CacheManager.ClearRegion(artist.CacheRegion);
}
catch (Exception ex)
{
this.Logger.LogError(ex);
await this.LogAndPublish("Error deleting artist secondary image.");
errors.Add(ex);
}
sw.Stop();
await this.LogAndPublish($"DeleteArtistSecondaryImage `{ artist }` Index [{ index }], By User `{user }`", LogLevel.Information);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> DeleteArtist(ApplicationUser user, Guid artistId)
{
var sw = new Stopwatch();
@ -200,6 +160,80 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null)
{
await this.LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{ artistId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Artist Not Found [{ artistId }]");
}
try
{
var artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
var artistImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly);
var artistImageFilename = artistImagesInFolder.Skip(index).FirstOrDefault();
if (artistImageFilename.Exists)
{
artistImageFilename.Delete();
}
this.CacheManager.ClearRegion(artist.CacheRegion);
}
catch (Exception ex)
{
this.Logger.LogError(ex);
await this.LogAndPublish("Error deleting artist secondary image.");
errors.Add(ex);
}
sw.Stop();
await this.LogAndPublish($"DeleteArtistSecondaryImage `{ artist }` Index [{ index }], By User `{user }`", LogLevel.Information);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var release = this.DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId);
try
{
if (release == null)
{
await this.LogAndPublish($"DeleteRelease Unknown Release [{ releaseId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Release Not Found [{ releaseId }]");
}
await this.ReleaseFactory.Delete(release, doDeleteFiles ?? false);
}
catch (Exception ex)
{
this.Logger.LogError(ex);
await this.LogAndPublish("Error deleting release.");
errors.Add(ex);
}
sw.Stop();
await this.LogAndPublish($"DeleteRelease `{ release }`, By User `{ user}`", LogLevel.Information);
this.CacheManager.Clear();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> DeleteTrack(ApplicationUser user, Guid trackId, bool? doDeleteFile)
{
var sw = new Stopwatch();
@ -263,31 +297,39 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles)
public async Task<OperationResult<bool>> DeleteUser(ApplicationUser applicationUser, Guid userId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var release = this.DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId);
var user = this.DbContext.Users.FirstOrDefault(x => x.RoadieId == userId);
if (user.Id == applicationUser.Id)
{
var ex = new Exception("User cannot self.");
this.Logger.LogError(ex);
await this.LogAndPublish("Error deleting user.");
errors.Add(ex);
}
try
{
if (release == null)
if (user == null)
{
await this.LogAndPublish($"DeleteRelease Unknown Release [{ releaseId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Release Not Found [{ releaseId }]");
await this.LogAndPublish($"DeleteUser Unknown User [{ userId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"User Not Found [{ userId }]");
}
await this.ReleaseFactory.Delete(release, doDeleteFiles ?? false);
this.DbContext.Users.Remove(user);
await this.DbContext.SaveChangesAsync();
}
catch (Exception ex)
{
this.Logger.LogError(ex);
await this.LogAndPublish("Error deleting release.");
await this.LogAndPublish("Error deleting user.");
errors.Add(ex);
}
sw.Stop();
await this.LogAndPublish($"DeleteRelease `{ release }`, By User `{ user}`", LogLevel.Information);
await this.LogAndPublish($"DeleteUser `{ user }`, By User `{ user}`", LogLevel.Information);
this.CacheManager.Clear();
return new OperationResult<bool>
{
@ -357,6 +399,86 @@ namespace Roadie.Api.Services
};
}
public Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user)
{
var sw = Stopwatch.StartNew();
sw.Start();
var missingData = new Dictionary<string, List<string>>();
foreach (var collection in this.DbContext.Collections.Where(x => x.Status != Statuses.Complete))
{
var collectionReleases = (from cr in this.DbContext.CollectionReleases
where cr.CollectionId == collection.Id
select cr);
PositionAristRelease[] pars = null;
try
{
pars = collection.PositionArtistReleases().ToArray();
}
catch (Exception ex)
{
missingData.Add($"CSV Error [{ collection.Name }]", new List<string> { ex.Message });
continue;
}
foreach (var par in pars)
{
var cr = collectionReleases.FirstOrDefault(x => x.ListNumber == par.Position);
if (cr == null)
{
// If artist is already in result then add missing album to artist, if not then add artist then add missing album
if (!missingData.ContainsKey(par.Artist))
{
missingData.Add(par.Artist, new List<string>());
}
var r = $"[{ collection.Name }]:[{ par.Release }]";
missingData[par.Artist].Add(r);
}
}
}
sw.Stop();
return Task.FromResult(new OperationResult<Dictionary<string, List<string>>>
{
Data = missingData.OrderBy(x => x.Value.Count()).ToDictionary(x => x.Key, x => x.Value),
IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds
});
}
public async Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = true)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collections = this.DbContext.Collections.Where(x => x.Status != Statuses.Complete).ToArray();
foreach (var collection in collections)
{
try
{
var result = await this.ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst);
if (!result.IsSuccess)
{
errors.AddRange(result.Errors);
}
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
}
sw.Stop();
this.Logger.LogInformation(string.Format("ScanAllCollections, By User `{0}`", user));
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false)
{
var sw = new Stopwatch();
@ -400,87 +522,6 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = true)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collections = this.DbContext.Collections.Where(x => x.Status != Statuses.Complete).ToArray();
foreach(var collection in collections)
{
try
{
var result = await this.ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst);
if (!result.IsSuccess)
{
errors.AddRange(result.Errors);
}
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
}
sw.Stop();
this.Logger.LogInformation(string.Format("ScanAllCollections, By User `{0}`", user));
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user)
{
var sw = Stopwatch.StartNew();
sw.Start();
var missingData = new Dictionary<string, List<string>>();
foreach (var collection in this.DbContext.Collections.Where(x => x.Status != Statuses.Complete))
{
var collectionReleases = (from cr in this.DbContext.CollectionReleases
where cr.CollectionId == collection.Id
select cr);
PositionAristRelease[] pars = null;
try
{
pars = collection.PositionArtistReleases().ToArray();
}
catch (Exception ex)
{
missingData.Add($"CSV Error [{ collection.Name }]", new List<string>{ ex.Message });
continue;
}
foreach (var par in pars)
{
var cr = collectionReleases.FirstOrDefault(x => x.ListNumber == par.Position);
if (cr == null)
{
// If artist is already in result then add missing album to artist, if not then add artist then add missing album
if(!missingData.ContainsKey(par.Artist))
{
missingData.Add(par.Artist, new List<string>());
}
var r = $"[{ collection.Name }]:[{ par.Release }]";
missingData[par.Artist].Add(r);
}
}
}
sw.Stop();
return Task.FromResult(new OperationResult<Dictionary<string, List<string>>>
{
Data = missingData.OrderBy(x => x.Value.Count()).ToDictionary(x => x.Key, x => x.Value),
IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds
});
}
public async Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = true)
{
var sw = new Stopwatch();
@ -508,8 +549,7 @@ namespace Roadie.Api.Services
{
var now = DateTime.UtcNow;
foreach (var csvRelease in par)
{
{
data.Artist artist = null;
data.Release release = null;
@ -517,11 +557,11 @@ namespace Roadie.Api.Services
var specialSearchName = csvRelease.Artist.ToAlphanumericName();
var artistResults = (from a in this.DbContext.Artists
where (a.Name.Contains(searchName) ||
a.SortName.Contains(searchName) ||
a.AlternateNames.Contains(searchName) ||
a.AlternateNames.Contains(specialSearchName))
select a).ToArray();
where (a.Name.Contains(searchName) ||
a.SortName.Contains(searchName) ||
a.AlternateNames.Contains(searchName) ||
a.AlternateNames.Contains(specialSearchName))
select a).ToArray();
if (!artistResults.Any())
{
this.Logger.LogWarning("Unable To Find Artist [{0}], SearchName [{1}]", csvRelease.Artist, searchName);
@ -534,13 +574,13 @@ namespace Roadie.Api.Services
searchName = csvRelease.Release.NormalizeName().ToLower();
specialSearchName = csvRelease.Release.ToAlphanumericName();
release = (from r in this.DbContext.Releases
where (r.ArtistId == artist.Id)
where (r.Title.Contains(searchName) ||
r.AlternateNames.Contains(searchName) ||
r.AlternateNames.Contains(specialSearchName))
select r
where (r.ArtistId == artist.Id)
where (r.Title.Contains(searchName) ||
r.AlternateNames.Contains(searchName) ||
r.AlternateNames.Contains(specialSearchName))
select r
).FirstOrDefault();
if(release != null)
if (release != null)
{
break;
}
@ -550,9 +590,9 @@ namespace Roadie.Api.Services
this.Logger.LogWarning("Unable To Find Release [{0}], SearchName [{1}]", csvRelease.Release, searchName);
csvRelease.Status = Library.Enums.Statuses.Missing;
continue;
}
var isInCollection = this.DbContext.CollectionReleases.FirstOrDefault(x => x.CollectionId == collection.Id &&
x.ListNumber == csvRelease.Position &&
}
var isInCollection = this.DbContext.CollectionReleases.FirstOrDefault(x => x.CollectionId == collection.Id &&
x.ListNumber == csvRelease.Position &&
x.ReleaseId == release.Id);
// Found in Database but not in collection add to Collection
if (isInCollection == null)
@ -656,7 +696,7 @@ namespace Roadie.Api.Services
errors.Add(ex);
}
sw.Stop();
this.DbContext.ScanHistories.Add(new data.ScanHistory
{
UserId = user.Id,

View file

@ -184,7 +184,7 @@ namespace Roadie.Api.Services
}
var onlyWithReleases = onlyIncludeWithReleases ?? true;
var isEqualFilter = false;
if(!string.IsNullOrEmpty(request.FilterValue))
if (!string.IsNullOrEmpty(request.FilterValue))
{
var filter = request.FilterValue;
// if filter string is wrapped in quotes then is an exact not like search, e.g. "Diana Ross" should not return "Diana Ross & The Supremes"
@ -199,9 +199,9 @@ 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) ||
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) ||
@ -276,7 +276,7 @@ namespace Roadie.Api.Services
}
}
sw.Stop();
if(!string.IsNullOrEmpty(request.Filter) && rowCount == 0)
if (!string.IsNullOrEmpty(request.Filter) && rowCount == 0)
{
// Create request for no artist found
var req = new data.Request
@ -297,6 +297,57 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> MergeArtists(User user, Guid artistToMergeId, Guid artistToMergeIntoId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var artistToMerge = this.DbContext.Artists
.Include(x => x.Genres)
.Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistToMergeId);
if (artistToMerge == null)
{
this.Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeId);
return new OperationResult<bool>(true, string.Format("Artist Not Found [{0}]", artistToMergeId));
}
var mergeIntoArtist = this.DbContext.Artists
.Include(x => x.Genres)
.Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistToMergeIntoId);
if (mergeIntoArtist == null)
{
this.Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeIntoId);
return new OperationResult<bool>(true, string.Format("Artist Not Found [{0}]", artistToMergeIntoId));
}
try
{
var result = await this.ArtistFactory.MergeArtists(artistToMerge, mergeIntoArtist, true);
if (!result.IsSuccess)
{
this.CacheManager.ClearRegion(artistToMerge.CacheRegion);
this.CacheManager.ClearRegion(mergeIntoArtist.CacheRegion);
this.Logger.LogInformation("MergeArtists `{0}` => `{1}`, By User `{2}`", artistToMerge, mergeIntoArtist, user);
}
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> ScanArtistReleasesFolders(Guid artistId, string destinationFolder, bool doJustInfo)
{
SimpleContract.Requires<ArgumentOutOfRangeException>(artistId != Guid.Empty, "Invalid ArtistId");
@ -460,7 +511,7 @@ namespace Roadie.Api.Services
didChangeThumbnail = true;
}
if(model.NewSecondaryImagesData != null && model.NewSecondaryImagesData.Any())
if (model.NewSecondaryImagesData != null && model.NewSecondaryImagesData.Any())
{
// Additional images to add to artist
var looper = 0;
@ -473,7 +524,7 @@ namespace Roadie.Api.Services
artistSecondaryImage = ImageHelper.ConvertToJpegFormat(artistSecondaryImage);
var aristImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
while(File.Exists(aristImageFilename))
while (File.Exists(aristImageFilename))
{
looper++;
aristImageFilename = Path.Combine(newArtistFolder, string.Format(ImageHelper.ArtistSecondaryImageFilename, looper.ToString("00")));
@ -600,59 +651,6 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> MergeArtists(User user, Guid artistToMergeId, Guid artistToMergeIntoId)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var artistToMerge = this.DbContext.Artists
.Include(x => x.Genres)
.Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistToMergeId);
if (artistToMerge == null)
{
this.Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeId);
return new OperationResult<bool>(true, string.Format("Artist Not Found [{0}]", artistToMergeId));
}
var mergeIntoArtist = this.DbContext.Artists
.Include(x => x.Genres)
.Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistToMergeIntoId);
if (mergeIntoArtist == null)
{
this.Logger.LogWarning("MergeArtists Unknown Artist [{0}]", artistToMergeIntoId);
return new OperationResult<bool>(true, string.Format("Artist Not Found [{0}]", artistToMergeIntoId));
}
try
{
var result = await this.ArtistFactory.MergeArtists(artistToMerge, mergeIntoArtist, true);
if (!result.IsSuccess)
{
this.CacheManager.ClearRegion(artistToMerge.CacheRegion);
this.CacheManager.ClearRegion(mergeIntoArtist.CacheRegion);
this.Logger.LogInformation("MergeArtists `{0}` => `{1}`, By User `{2}`", artistToMerge, mergeIntoArtist, user);
}
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<Library.Models.Image>> UploadArtistImage(User user, Guid id, IFormFile file)
{
var bytes = new byte[0];
@ -769,7 +767,6 @@ namespace Roadie.Api.Services
};
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);
}
catch (Exception ex)
{

View file

@ -69,7 +69,7 @@ namespace Roadie.Api.Services
{
case BookmarkType.Artist:
var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == row.BookmarkTargetId);
if(artist == null)
if (artist == null)
{
continue;
}

View file

@ -16,7 +16,6 @@ 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;
@ -74,6 +73,120 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<Collection>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = string.Format("urn:collection_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
var result = await this.CacheManager.GetAsync<OperationResult<Collection>>(cacheKey, async () =>
{
return await this.CollectionByIdAction(id, includes);
}, data.Artist.CacheRegionUrn(id));
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != null;
}
}
return new OperationResult<Collection>(result.Messages)
{
Data = result?.Data,
IsNotFoundResult = result?.IsNotFoundResult ?? false,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<bool>> DeleteCollection(User user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == id);
if (collection == null)
{
return new OperationResult<bool>(true, $"Collection Not Found [{ id }]");
}
if (!user.IsEditor)
{
this.Logger.LogWarning($"DeleteCollection: Access Denied: `{ collection }`, By User `{user }`");
return new OperationResult<bool>("Access Denied");
}
try
{
this.DbContext.Collections.Remove(collection);
await this.DbContext.SaveChangesAsync();
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
this.Logger.LogInformation($"DeleteCollection `{ collection }`, By User `{user }`");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public Task<Library.Models.Pagination.PagedResult<CollectionList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null)
{
var sw = new Stopwatch();
sw.Start();
IQueryable<data.Collection> collections = null;
if (artistId.HasValue)
{
var sql = @"select DISTINCT c.*
from `collectionrelease` cr
join `collection` c on c.id = cr.collectionId
join `release` r on r.id = cr.releaseId
join `artist` a on r.artistId = a.id
where a.roadieId = {0}";
collections = this.DbContext.Collections.FromSql(sql, artistId);
}
else if (releaseId.HasValue)
{
var sql = @"select DISTINCT c.*
from `collectionrelease` cr
join `collection` c on c.id = cr.collectionId
join `release` r on r.id = cr.releaseId
where r.roadieId = {0}";
collections = this.DbContext.Collections.FromSql(sql, releaseId);
}
else
{
collections = this.DbContext.Collections;
}
var result = (from c in collections
where (request.FilterValue.Length == 0 || (request.FilterValue.Length > 0 && c.Name.Contains(request.Filter)))
select CollectionList.FromDataCollection(c, (from crc in this.DbContext.CollectionReleases
where crc.CollectionId == c.Id
select crc.Id).Count(), this.MakeCollectionThumbnailImage(c.RoadieId)));
var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary<string, string> { { "Collection.Text", "ASC" } }) : request.OrderValue(null);
var rowCount = result.Count();
var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<CollectionList>
{
TotalCount = rowCount,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
}
/// <summary>
/// Updates (or Adds) Collection
/// </summary>
@ -88,7 +201,7 @@ namespace Roadie.Api.Services
data.Collection collection = new data.Collection();
if(!isNew)
if (!isNew)
{
collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == model.Id);
if (collection == null)
@ -147,121 +260,6 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> DeleteCollection(User user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == id);
if (collection == null)
{
return new OperationResult<bool>(true, $"Collection Not Found [{ id }]");
}
if(!user.IsEditor)
{
this.Logger.LogWarning($"DeleteCollection: Access Denied: `{ collection }`, By User `{user }`");
return new OperationResult<bool>("Access Denied");
}
try
{
this.DbContext.Collections.Remove(collection);
await this.DbContext.SaveChangesAsync();
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
this.Logger.LogInformation($"DeleteCollection `{ collection }`, By User `{user }`");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<Collection>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = string.Format("urn:collection_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
var result = await this.CacheManager.GetAsync<OperationResult<Collection>>(cacheKey, async () =>
{
return await this.CollectionByIdAction(id, includes);
}, data.Artist.CacheRegionUrn(id));
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != null;
}
}
return new OperationResult<Collection>(result.Messages)
{
Data = result?.Data,
IsNotFoundResult = result?.IsNotFoundResult ?? false,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
public Task<Library.Models.Pagination.PagedResult<CollectionList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null)
{
var sw = new Stopwatch();
sw.Start();
IQueryable<data.Collection> collections = null;
if (artistId.HasValue)
{
var sql = @"select DISTINCT c.*
from `collectionrelease` cr
join `collection` c on c.id = cr.collectionId
join `release` r on r.id = cr.releaseId
join `artist` a on r.artistId = a.id
where a.roadieId = {0}";
collections = this.DbContext.Collections.FromSql(sql, artistId);
}
else if (releaseId.HasValue)
{
var sql = @"select DISTINCT c.*
from `collectionrelease` cr
join `collection` c on c.id = cr.collectionId
join `release` r on r.id = cr.releaseId
where r.roadieId = {0}";
collections = this.DbContext.Collections.FromSql(sql, releaseId);
}
else
{
collections = this.DbContext.Collections;
}
var result = (from c in collections
where (request.FilterValue.Length == 0 || (request.FilterValue.Length > 0 && c.Name.Contains(request.Filter)))
select CollectionList.FromDataCollection(c, (from crc in this.DbContext.CollectionReleases
where crc.CollectionId == c.Id
select crc.Id).Count(), this.MakeCollectionThumbnailImage(c.RoadieId)));
var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary<string, string> { { "Collection.Text", "ASC" } }) : request.OrderValue(null);
var rowCount = result.Count();
var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<CollectionList>
{
TotalCount = rowCount,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
}
private Task<OperationResult<Collection>> CollectionByIdAction(Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();

View file

@ -17,7 +17,7 @@ namespace Roadie.Api.Services
scheme = "https";
}
var host = urlHelper.ActionContext.HttpContext.Request.Host;
if(!string.IsNullOrEmpty(configuration.BehindProxyHost))
if (!string.IsNullOrEmpty(configuration.BehindProxyHost))
{
host = new Microsoft.AspNetCore.Http.HostString(configuration.BehindProxyHost);
}

View file

@ -11,16 +11,20 @@ namespace Roadie.Api.Services
{
Task<OperationResult<bool>> DeleteArtist(ApplicationUser user, Guid artistId);
Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index);
Task<OperationResult<bool>> DeleteArtistReleases(ApplicationUser user, Guid artistId, bool doDeleteFiles = false);
Task<OperationResult<bool>> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index);
Task<OperationResult<bool>> DeleteRelease(ApplicationUser user, Guid releaseId, bool? doDeleteFiles);
Task<OperationResult<bool>> DeleteTrack(ApplicationUser user, Guid trackId, bool? doDeleteFile);
Task<OperationResult<bool>> DeleteUser(ApplicationUser applicationUser, Guid id);
Task<OperationResult<bool>> DoInitialSetup(ApplicationUser user, UserManager<ApplicationUser> userManager);
Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user);
Task<OperationResult<bool>> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = true);
Task<OperationResult<bool>> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false);
@ -32,7 +36,5 @@ namespace Roadie.Api.Services
Task<OperationResult<bool>> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false);
Task<OperationResult<bool>> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false);
Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(ApplicationUser user);
}
}

View file

@ -15,12 +15,12 @@ namespace Roadie.Api.Services
Task<PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true);
Task<OperationResult<bool>> MergeArtists(User user, Guid artistToMergeId, Guid artistToMergeIntoId);
Task<OperationResult<Library.Models.Image>> SetReleaseImageByUrl(User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateArtist(User user, Artist artist);
Task<OperationResult<Library.Models.Image>> UploadArtistImage(User user, Guid id, IFormFile file);
Task<OperationResult<bool>> MergeArtists(User user, Guid artistToMergeId, Guid artistToMergeIntoId);
}
}

View file

@ -12,12 +12,12 @@ namespace Roadie.Api.Services
{
OperationResult<Collection> Add(User roadieUser);
Task<OperationResult<bool>> UpdateCollection(User roadieUser, Collection collection);
Task<OperationResult<Collection>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> DeleteCollection(User user, Guid id);
Task<OperationResult<Collection>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<PagedResult<CollectionList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null);
Task<OperationResult<bool>> UpdateCollection(User roadieUser, Collection collection);
}
}

View file

@ -11,6 +11,7 @@ namespace Roadie.Api.Services
public interface IImageService
{
Task<FileOperationResult<Library.Models.Image>> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ById(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
@ -26,6 +27,7 @@ namespace Roadie.Api.Services
Task<FileOperationResult<Library.Models.Image>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<Library.Models.Image>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10);

View file

@ -15,6 +15,8 @@ namespace Roadie.Api.Services
Task<PagedResult<ReleaseList>> List(User user, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null);
Task<OperationResult<bool>> MergeReleases(User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia);
Task<FileOperationResult<byte[]>> ReleaseZipped(User roadieUser, Guid id);
Task<OperationResult<Library.Models.Image>> SetReleaseImageByUrl(User user, Guid id, string imageUrl);
@ -22,7 +24,5 @@ namespace Roadie.Api.Services
Task<OperationResult<bool>> UpdateRelease(User user, Release release);
Task<OperationResult<Library.Models.Image>> UploadReleaseImage(User user, Guid id, IFormFile file);
Task<OperationResult<bool>> MergeReleases(User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia);
}
}

View file

@ -14,10 +14,10 @@ namespace Roadie.Api.Services
Task<Library.Models.Pagination.PagedResult<TrackList>> List(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null);
Task<OperationResult<bool>> UpdateTrack(User user, Track track);
OperationResult<Track> StreamCheckAndInfo(User roadieUser, Guid id);
Task<OperationResult<TrackStreamInfo>> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, User roadieUser);
Task<OperationResult<bool>> UpdateTrack(User user, Track track);
}
}

View file

@ -201,7 +201,7 @@ namespace Roadie.Api.Services
etag: etag);
}
public async Task<FileOperationResult<Image>> ReleaseSecondaryImage(Guid id,int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
public async Task<FileOperationResult<Image>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await this.GetImageFileOperation(type: $"ReleaseSecondaryThumbnail-{imageId}",
regionUrn: data.Release.CacheRegionUrn(id),
@ -298,7 +298,7 @@ namespace Roadie.Api.Services
else
{
var artistImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), Library.Enums.ImageType.Artist);
if(artistImages.Any())
if (artistImages.Any())
{
imageBytes = File.ReadAllBytes(artistImages.First().FullName);
}
@ -328,6 +328,53 @@ namespace Roadie.Api.Services
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
}
private Task<FileOperationResult<Image>> ArtistSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null)
{
try
{
var artist = this.GetArtist(id);
if (artist == null)
{
return Task.FromResult(new FileOperationResult<Image>(true, string.Format("Release Not Found [{0}]", id)));
}
byte[] imageBytes = null;
string artistFolder = null;
try
{
// See if cover art file exists in release folder
artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
if (!Directory.Exists(artistFolder))
{
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ artist }`");
}
else
{
var artistSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), Library.Enums.ImageType.ArtistSecondary).ToArray();
if (artistSecondaryImages.Length >= imageId && artistSecondaryImages[imageId] != null)
{
imageBytes = File.ReadAllBytes(artistSecondaryImages[imageId].FullName);
}
}
}
catch (Exception ex)
{
this.Logger.LogError(ex, $"Error Reading Artist Folder [{ artistFolder }] For Artist `{ artist }`");
}
var image = new data.Image
{
Bytes = imageBytes,
CreatedDate = artist.CreatedDate,
LastUpdated = artist.LastUpdated
};
return Task.FromResult(GenerateFileOperationResult(id, image, etag));
}
catch (Exception ex)
{
this.Logger.LogError($"Error fetching Release Thumbnail [{ id }]", ex);
}
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
}
private Task<FileOperationResult<Image>> CollectionImageAction(Guid id, EntityTagHeaderValue etag = null)
{
try
@ -527,7 +574,7 @@ namespace Roadie.Api.Services
else
{
var releaseCoverFiles = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), Library.Enums.ImageType.Release);
if(releaseCoverFiles.Any())
if (releaseCoverFiles.Any())
{
imageBytes = File.ReadAllBytes(releaseCoverFiles.First().FullName);
}
@ -588,10 +635,10 @@ namespace Roadie.Api.Services
else
{
var releaseSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), Library.Enums.ImageType.ReleaseSecondary).ToArray();
if(releaseSecondaryImages.Length >= imageId && releaseSecondaryImages[imageId] != null)
if (releaseSecondaryImages.Length >= imageId && releaseSecondaryImages[imageId] != null)
{
imageBytes = File.ReadAllBytes(releaseSecondaryImages[imageId].FullName);
}
}
}
}
}
@ -614,54 +661,6 @@ namespace Roadie.Api.Services
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
}
private Task<FileOperationResult<Image>> ArtistSecondaryImageAction(Guid id, int imageId, EntityTagHeaderValue etag = null)
{
try
{
var artist = this.GetArtist(id);
if (artist == null)
{
return Task.FromResult(new FileOperationResult<Image>(true, string.Format("Release Not Found [{0}]", id)));
}
byte[] imageBytes = null;
string artistFolder = null;
try
{
// See if cover art file exists in release folder
artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
if (!Directory.Exists(artistFolder))
{
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ artist }`");
}
else
{
var artistSecondaryImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), Library.Enums.ImageType.ArtistSecondary).ToArray();
if (artistSecondaryImages.Length >= imageId && artistSecondaryImages[imageId] != null)
{
imageBytes = File.ReadAllBytes(artistSecondaryImages[imageId].FullName);
}
}
}
catch (Exception ex)
{
this.Logger.LogError(ex, $"Error Reading Artist Folder [{ artistFolder }] For Artist `{ artist }`");
}
var image = new data.Image
{
Bytes = imageBytes,
CreatedDate = artist.CreatedDate,
LastUpdated = artist.LastUpdated
};
return Task.FromResult(GenerateFileOperationResult(id, image, etag));
}
catch (Exception ex)
{
this.Logger.LogError($"Error fetching Release Thumbnail [{ id }]", ex);
}
return Task.FromResult(new FileOperationResult<Image>(OperationMessages.ErrorOccured));
}
private async Task<FileOperationResult<Image>> TrackImageAction(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
try

View file

@ -138,49 +138,6 @@ namespace Roadie.Api.Services
return await this.SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
}
private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(User user, Guid id, byte[] imageBytes)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var label = this.DbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
if (label == null)
{
return new OperationResult<Library.Models.Image>(true, string.Format("Label Not Found [{0}]", id));
}
try
{
var now = DateTime.UtcNow;
label.Thumbnail = imageBytes;
if (label.Thumbnail != null)
{
// Ensure is jpeg first
label.Thumbnail = ImageHelper.ConvertToJpegFormat(label.Thumbnail);
// Resize to store in database as thumbnail
label.Thumbnail = ImageHelper.ResizeImage(label.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height);
}
label.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(label.CacheRegion);
this.Logger.LogInformation($"UploadLabelImage `{ label }` By User `{ user }`");
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<Library.Models.Image>
{
IsSuccess = !errors.Any(),
Data = base.MakeThumbnailImage(id, "label", this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height, true),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> UpdateLabel(User user, Label model)
{
var sw = new Stopwatch();
@ -304,5 +261,48 @@ namespace Roadie.Api.Services
OperationTime = sw.ElapsedMilliseconds
});
}
private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(User user, Guid id, byte[] imageBytes)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var label = this.DbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
if (label == null)
{
return new OperationResult<Library.Models.Image>(true, string.Format("Label Not Found [{0}]", id));
}
try
{
var now = DateTime.UtcNow;
label.Thumbnail = imageBytes;
if (label.Thumbnail != null)
{
// Ensure is jpeg first
label.Thumbnail = ImageHelper.ConvertToJpegFormat(label.Thumbnail);
// Resize to store in database as thumbnail
label.Thumbnail = ImageHelper.ResizeImage(label.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height);
}
label.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(label.CacheRegion);
this.Logger.LogInformation($"UploadLabelImage `{ label }` By User `{ user }`");
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<Library.Models.Image>
{
IsSuccess = !errors.Any(),
Data = base.MakeThumbnailImage(id, "label", this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height, true),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
}
}

View file

@ -168,7 +168,7 @@ namespace Roadie.Api.Services
}
catch (Exception ex)
{
this.Logger.LogError(ex, $"CreatePlayActivity RoadieUser `{ roadieUser }` StreamInfo `{ streamInfo }`");
this.Logger.LogError(ex, $"CreatePlayActivity RoadieUser `{ roadieUser }` StreamInfo `{ streamInfo }`");
}
return new OperationResult<PlayActivityList>();
}

View file

@ -63,113 +63,6 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> UpdatePlaylistTracks(User user, PlaylistTrackModifyRequest request)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = this.DbContext.Playlists.Include(x => x.Tracks).FirstOrDefault(x => x.RoadieId == request.Id);
if (playlist == null)
{
return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", request.Id));
}
try
{
var now = DateTime.UtcNow;
playlist.Tracks.Clear();
var tracks = (from t in this.DbContext.Tracks
join plt in request.Tracks on t.RoadieId equals plt.Track.Id
select t).ToArray();
foreach(var newPlaylistTrack in request.Tracks.OrderBy(x => x.ListNumber))
{
var track = tracks.FirstOrDefault(x => x.RoadieId == newPlaylistTrack.Track.Id);
playlist.Tracks.Add(new data.PlaylistTrack
{
ListNumber = newPlaylistTrack.ListNumber,
PlayListId = playlist.Id,
CreatedDate = now,
TrackId = track.Id
});
}
playlist.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
// await base.UpdatePlaylistCounts(playlist.Id, now);
this.Logger.LogInformation($"UpdatePlaylistTracks `{ playlist }` By User `{ user }`");
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> UpdatePlaylist(User user, Playlist model)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = this.DbContext.Playlists.FirstOrDefault(x => x.RoadieId == model.Id);
if (playlist == null)
{
return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", model.Id));
}
try
{
var now = DateTime.UtcNow;
playlist.AlternateNames = model.AlternateNamesList.ToDelimitedList();
playlist.Description = model.Description;
playlist.IsLocked = model.IsLocked;
playlist.IsPublic = model.IsPublic;
playlist.Name = model.Name;
playlist.Status = SafeParser.ToEnum<Statuses>(model.Status);
playlist.Tags = model.TagsList.ToDelimitedList();
playlist.URLs = model.URLsList.ToDelimitedList();
var playlistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (playlistImage != null)
{
// Ensure is jpeg first
playlist.Thumbnail = ImageHelper.ConvertToJpegFormat(playlistImage);
// Resize to store in database as thumbnail
playlist.Thumbnail = ImageHelper.ResizeImage(playlist.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height);
}
playlist.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(playlist.CacheRegion);
this.Logger.LogInformation($"UpdatePlaylist `{ playlist }` By User `{ user }`");
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> AddTracksToPlaylist(data.Playlist playlist, IEnumerable<Guid> trackIds)
{
var sw = new Stopwatch();
@ -362,6 +255,112 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> UpdatePlaylist(User user, Playlist model)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = this.DbContext.Playlists.FirstOrDefault(x => x.RoadieId == model.Id);
if (playlist == null)
{
return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", model.Id));
}
try
{
var now = DateTime.UtcNow;
playlist.AlternateNames = model.AlternateNamesList.ToDelimitedList();
playlist.Description = model.Description;
playlist.IsLocked = model.IsLocked;
playlist.IsPublic = model.IsPublic;
playlist.Name = model.Name;
playlist.Status = SafeParser.ToEnum<Statuses>(model.Status);
playlist.Tags = model.TagsList.ToDelimitedList();
playlist.URLs = model.URLsList.ToDelimitedList();
var playlistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (playlistImage != null)
{
// Ensure is jpeg first
playlist.Thumbnail = ImageHelper.ConvertToJpegFormat(playlistImage);
// Resize to store in database as thumbnail
playlist.Thumbnail = ImageHelper.ResizeImage(playlist.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height);
}
playlist.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(playlist.CacheRegion);
this.Logger.LogInformation($"UpdatePlaylist `{ playlist }` By User `{ user }`");
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> UpdatePlaylistTracks(User user, PlaylistTrackModifyRequest request)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = this.DbContext.Playlists.Include(x => x.Tracks).FirstOrDefault(x => x.RoadieId == request.Id);
if (playlist == null)
{
return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", request.Id));
}
try
{
var now = DateTime.UtcNow;
playlist.Tracks.Clear();
var tracks = (from t in this.DbContext.Tracks
join plt in request.Tracks on t.RoadieId equals plt.Track.Id
select t).ToArray();
foreach (var newPlaylistTrack in request.Tracks.OrderBy(x => x.ListNumber))
{
var track = tracks.FirstOrDefault(x => x.RoadieId == newPlaylistTrack.Track.Id);
playlist.Tracks.Add(new data.PlaylistTrack
{
ListNumber = newPlaylistTrack.ListNumber,
PlayListId = playlist.Id,
CreatedDate = now,
TrackId = track.Id
});
}
playlist.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
// await base.UpdatePlaylistCounts(playlist.Id, now);
this.Logger.LogInformation($"UpdatePlaylistTracks `{ playlist }` By User `{ user }`");
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
private Task<OperationResult<Playlist>> PlaylistByIdAction(Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();

View file

@ -245,8 +245,8 @@ namespace Roadie.Api.Services
where (!isFilteredToGenre || genreReleaseIds.Contains(r.Id))
where (request.FilterFromYear == null || r.ReleaseDate != null && r.ReleaseDate.Value.Year <= request.FilterFromYear)
where (request.FilterToYear == null || r.ReleaseDate != null && r.ReleaseDate.Value.Year >= request.FilterToYear)
where (request.FilterValue == "" || (r.Title.Contains(request.FilterValue) ||
r.AlternateNames.Contains(request.FilterValue) ||
where (request.FilterValue == "" || (r.Title.Contains(request.FilterValue) ||
r.AlternateNames.Contains(request.FilterValue) ||
r.AlternateNames.Contains(normalizedFilterValue)))
where (!isEqualFilter || (r.Title.Equals(request.FilterValue) ||
r.AlternateNames.Equals(request.FilterValue) ||
@ -838,7 +838,7 @@ namespace Roadie.Api.Services
var artistFolder = release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
var releaseFolder = release.ReleaseFileFolder(artistFolder);
var releaseImagesInFolder = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary, SearchOption.TopDirectoryOnly);
if(releaseImagesInFolder.Any())
if (releaseImagesInFolder.Any())
{
result.Images = result.Images.Concat(releaseImagesInFolder.Select((x, i) => MakeFullsizeSecondaryImage(id, ImageType.ReleaseSecondary, i)));
}

View file

@ -7,10 +7,10 @@
<ItemGroup>
<PackageReference Include="Hashids.net" Version="1.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.2.5" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.13" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.15" />
</ItemGroup>
<ItemGroup>

View file

@ -278,7 +278,7 @@ namespace Roadie.Api.Services
protected Image MakeFullsizeSecondaryImage(Guid id, ImageType type, int imageId, string caption = null)
{
if(type == ImageType.ArtistSecondary)
if (type == ImageType.ArtistSecondary)
{
return new Image($"{this.HttpContext.ImageBaseUrl }/artist-secondary/{id}/{imageId}", caption, $"{this.HttpContext.ImageBaseUrl }/artist-secondary/{id}/{ imageId }/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }");
}
@ -305,6 +305,11 @@ namespace Roadie.Api.Services
return "http://www.last.fm/music/" + this.HttpEncoder.UrlEncode($"{ artistName }/{ releaseTitle }");
}
protected Image MakeNewImage(string type)
{
return new Image($"{this.HttpContext.ImageBaseUrl }/{type}.jpg", null, null);
}
protected Image MakePlaylistThumbnailImage(Guid id)
{
return MakeThumbnailImage(id, "playlist");
@ -424,7 +429,7 @@ namespace Roadie.Api.Services
else
{
release.Rating = 0;
}
}
release.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
await this.UpdateReleaseRank(release.Id);
@ -444,7 +449,7 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<short>> SetTrackRating(Guid trackId, ApplicationUser user, short rating)
{
var sw = Stopwatch.StartNew();
var track = this.DbContext.Tracks
.Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release)
@ -478,7 +483,8 @@ namespace Roadie.Api.Services
if (ratings != null && ratings.Any())
{
track.Rating = (short)ratings.Average(x => (decimal)x);
} else
}
else
{
track.Rating = 0;
}
@ -490,7 +496,7 @@ namespace Roadie.Api.Services
this.CacheManager.ClearRegion(track.CacheRegion);
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion);
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion);
if(track.TrackArtist != null)
if (track.TrackArtist != null)
{
this.CacheManager.ClearRegion(track.TrackArtist.CacheRegion);
}
@ -779,6 +785,78 @@ namespace Roadie.Api.Services
}
}
/// <summary>
/// Update Artist Rank
/// Artist Rank is a sum of the artists release ranks + artist tracks rating + artist user rating
/// </summary>
protected async Task UpdateArtistRank(int artistId, bool updateReleaseRanks = false)
{
try
{
var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == artistId);
if (artist != null)
{
if (updateReleaseRanks)
{
var artistReleaseIds = this.DbContext.Releases.Where(x => x.ArtistId == artistId).Select(x => x.Id).ToArray();
foreach (var artistReleaseId in artistReleaseIds)
{
await this.UpdateReleaseRank(artistReleaseId, false);
}
}
var artistTrackAverage = (from t in this.DbContext.Tracks
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId
where t.ArtistId == artist.Id
select ut.Rating).Select(x => (decimal?)x).Average();
var artistReleaseRatingRating = (from r in this.DbContext.Releases
join ur in this.DbContext.UserReleases on r.Id equals ur.ReleaseId
where r.ArtistId == artist.Id
select ur.Rating).Select(x => (decimal?)x).Average();
var artistReleaseRankSum = (from r in this.DbContext.Releases
where r.ArtistId == artist.Id
select r.Rank).ToArray().Sum(x => x) ?? 0;
artist.Rank = SafeParser.ToNumber<decimal>(artistTrackAverage + artistReleaseRatingRating) + artistReleaseRankSum + artist.Rating;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(artist.CacheRegion);
this.Logger.LogInformation("UpdatedArtistRank For Artist `{0}`", artist);
}
}
catch (Exception ex)
{
this.Logger.LogError(ex, "Error in UpdateArtistRank ArtistId [{0}], UpdateReleaseRanks [{1}]", artistId, updateReleaseRanks);
}
}
/// <summary>
/// Find all artists involved with release and update their rank
/// </summary>
protected async Task UpdateArtistsRankForRelease(data.Release release)
{
if (release != null)
{
var artistsForRelease = new List<int>
{
release.ArtistId
};
var trackArtistsForRelease = (from t in this.DbContext.Tracks
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == release.Id
where t.ArtistId.HasValue
select t.ArtistId.Value).ToArray();
artistsForRelease.AddRange(trackArtistsForRelease);
foreach (var artistId in artistsForRelease.Distinct())
{
await this.UpdateArtistRank(artistId);
}
}
}
protected async Task UpdateLabelCounts(int labelId, DateTime now)
{
var label = this.DbContext.Labels.FirstOrDefault(x => x.Id == labelId);
@ -826,10 +904,10 @@ namespace Roadie.Api.Services
if (release != null)
{
release.PlayedCount = (from t in this.DbContext.Tracks
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == releaseId
where t.PlayedCount.HasValue
select t).Sum(x => x.PlayedCount);
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == releaseId
where t.PlayedCount.HasValue
select t).Sum(x => x.PlayedCount);
release.Duration = (from t in this.DbContext.Tracks
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == releaseId
@ -839,78 +917,6 @@ namespace Roadie.Api.Services
}
}
/// <summary>
/// Find all artists involved with release and update their rank
/// </summary>
protected async Task UpdateArtistsRankForRelease(data.Release release)
{
if(release != null)
{
var artistsForRelease = new List<int>
{
release.ArtistId
};
var trackArtistsForRelease = (from t in this.DbContext.Tracks
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == release.Id
where t.ArtistId.HasValue
select t.ArtistId.Value).ToArray();
artistsForRelease.AddRange(trackArtistsForRelease);
foreach(var artistId in artistsForRelease.Distinct())
{
await this.UpdateArtistRank(artistId);
}
}
}
/// <summary>
/// Update Artist Rank
/// Artist Rank is a sum of the artists release ranks + artist tracks rating + artist user rating
/// </summary>
protected async Task UpdateArtistRank(int artistId, bool updateReleaseRanks = false)
{
try
{
var artist = this.DbContext.Artists.FirstOrDefault(x => x.Id == artistId);
if (artist != null)
{
if (updateReleaseRanks)
{
var artistReleaseIds = this.DbContext.Releases.Where(x => x.ArtistId == artistId).Select(x => x.Id).ToArray();
foreach (var artistReleaseId in artistReleaseIds)
{
await this.UpdateReleaseRank(artistReleaseId, false);
}
}
var artistTrackAverage = (from t in this.DbContext.Tracks
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId
where t.ArtistId == artist.Id
select ut.Rating).Select(x => (decimal?)x).Average();
var artistReleaseRatingRating = (from r in this.DbContext.Releases
join ur in this.DbContext.UserReleases on r.Id equals ur.ReleaseId
where r.ArtistId == artist.Id
select ur.Rating).Select(x => (decimal?)x).Average();
var artistReleaseRankSum = (from r in this.DbContext.Releases
where r.ArtistId == artist.Id
select r.Rank).ToArray().Sum(x => x) ?? 0;
artist.Rank = SafeParser.ToNumber<decimal>(artistTrackAverage + artistReleaseRatingRating) + artistReleaseRankSum + artist.Rating;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(artist.CacheRegion);
this.Logger.LogInformation("UpdatedArtistRank For Artist `{0}`", artist);
}
}
catch (Exception ex)
{
this.Logger.LogError(ex, "Error in UpdateArtistRank ArtistId [{0}], UpdateReleaseRanks [{1}]", artistId, updateReleaseRanks);
}
}
/// <summary>
/// Update Relase Rank
/// Release Rank Calculation = Average of Track User Ratings + (User Rating of Release / Release Track Count) + Collection Rank Value
@ -963,12 +969,6 @@ namespace Roadie.Api.Services
}
}
protected Image MakeNewImage(string type)
{
return new Image($"{this.HttpContext.ImageBaseUrl }/{type}.jpg", null, null);
}
private Image MakeImage(Guid id, string type, int? width, int? height, string caption = null, bool includeCachebuster = false)
{
if (width.HasValue && height.HasValue && (width.Value != this.Configuration.ThumbnailImageSize.Width || height.Value != this.Configuration.ThumbnailImageSize.Height))

View file

@ -112,7 +112,6 @@ namespace Roadie.Api.Services
result.LastScan = lastScan.CreatedDate;
}
sw.Stop();
}
catch (Exception ex)
{

View file

@ -964,7 +964,7 @@ namespace Roadie.Api.Services
}
directory.id = subsonic.Request.ReleaseIdIdentifier + release.RoadieId.ToString();
directory.name = release.Title;
var releaseRating = user == null ? null : this.DbContext.UserReleases.FirstOrDefault(x =>x.UserId == user.Id && x.ReleaseId == release.Id);
var releaseRating = user == null ? null : this.DbContext.UserReleases.FirstOrDefault(x => x.UserId == user.Id && x.ReleaseId == release.Id);
directory.averageRating = release.Rating ?? 0;
directory.parent = subsonic.Request.ArtistIdIdentifier + release.Artist.RoadieId.ToString();
if (releaseRating?.IsFavorite ?? false)

View file

@ -28,8 +28,8 @@ namespace Roadie.Api.Services
{
public class TrackService : ServiceBase, ITrackService
{
private IBookmarkService BookmarkService { get; } = null;
private IAdminService AdminService { get; }
private IBookmarkService BookmarkService { get; } = null;
public TrackService(IRoadieSettings configuration,
IHttpEncoder httpEncoder,
@ -226,13 +226,13 @@ namespace Roadie.Api.Services
if (!request.FilterRatedOnly && !request.FilterFavoriteOnly)
{
var sql = @"SELECT t.id
FROM `track` t
FROM `track` t
JOIN `releasemedia` rm on (t.releaseMediaId = rm.id)
WHERE t.Hash IS NOT NULL
WHERE t.Hash IS NOT NULL
AND t.id NOT IN (SELECT ut.trackId
FROM `usertrack` ut
WHERE ut.userId = {0}
AND ut.isDisliked = 1)
AND ut.isDisliked = 1)
AND rm.releaseId in (select distinct r.id
FROM `release` r
WHERE r.id NOT IN (SELECT ur.releaseId
@ -240,28 +240,28 @@ namespace Roadie.Api.Services
WHERE ur.userId = {0}
AND ur.isDisliked = 1)
AND r.artistId IN (select DISTINCT a.id
FROM `artist` a
FROM `artist` a
WHERE a.id NOT IN (select ua.artistId
FROM `userartist` ua
FROM `userartist` ua
where ua.userId = {0}
AND ua.isDisliked = 1)
ORDER BY RAND())
ORDER BY RAND())
ORDER BY RAND()
ORDER BY RAND()
LIMIT {1}";
randomTrackIds = this.DbContext.Tracks.FromSql(sql, userId, request.Limit).Select(x => x.Id).ToArray();
}
if (request.FilterRatedOnly && !request.FilterFavoriteOnly)
{
var sql = @"SELECT t.id
FROM `track` t
FROM `track` t
JOIN `releasemedia` rm on (t.releaseMediaId = rm.id)
WHERE t.Hash IS NOT NULL
WHERE t.Hash IS NOT NULL
AND t.rating > 0
AND t.id NOT IN (SELECT ut.trackId
FROM `usertrack` ut
WHERE ut.userId = {0}
AND ut.isDisliked = 1)
AND ut.isDisliked = 1)
AND rm.releaseId in (select distinct r.id
FROM `release` r
WHERE r.id NOT IN (SELECT ur.releaseId
@ -269,14 +269,14 @@ namespace Roadie.Api.Services
WHERE ur.userId = {0}
AND ur.isDisliked = 1)
AND r.artistId IN (select DISTINCT a.id
FROM `artist` a
FROM `artist` a
WHERE a.id NOT IN (select ua.artistId
FROM `userartist` ua
FROM `userartist` ua
where ua.userId = {0}
AND ua.isDisliked = 1)
ORDER BY RAND())
ORDER BY RAND())
ORDER BY RAND()
ORDER BY RAND()
LIMIT {1}";
randomTrackIds = this.DbContext.Tracks.FromSql(sql, userId, request.LimitValue).Select(x => x.Id).ToArray();
}
@ -334,8 +334,8 @@ namespace Roadie.Api.Services
where (releaseId == null || (releaseId != null && r.RoadieId == releaseId))
where (filterToTrackIds == null || filterToTrackIds.Contains(t.RoadieId))
where (request.FilterMinimumRating == null || t.Rating >= request.FilterMinimumRating.Value)
where (request.FilterValue == "" || (t.Title.Contains(request.FilterValue) ||
t.AlternateNames.Contains(request.FilterValue) ||
where (request.FilterValue == "" || (t.Title.Contains(request.FilterValue) ||
t.AlternateNames.Contains(request.FilterValue) ||
t.AlternateNames.Contains(normalizedFilterValue)) ||
t.PartTitles.Contains(request.FilterValue))
where (!isEqualFilter || (t.Title.Equals(request.FilterValue) ||
@ -486,14 +486,14 @@ namespace Roadie.Api.Services
{
sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary<string, string> { { "Release.Release.Text", "ASC" }, { "MediaNumber", "ASC" }, { "TrackNumber", "ASC" } }) : request.OrderValue(null);
}
if(doRandomize ?? false)
if (doRandomize ?? false)
{
rows = result.OrderBy(x => x.RandomSortId).Take(request.LimitValue).ToArray();
}
else
{
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
}
}
if (rows.Any() && roadieUser != null)
{
var rowIds = rows.Select(x => x.DatabaseId).ToArray();
@ -599,6 +599,140 @@ namespace Roadie.Api.Services
}
}
/// <summary>
/// Fast as possible check if exists and return minimum information on Track
/// </summary>
public OperationResult<Track> StreamCheckAndInfo(User roadieUser, Guid id)
{
var track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == id);
if (track == null)
{
return new OperationResult<Track>(true, string.Format("Track Not Found [{0}]", id));
}
return new OperationResult<Track>()
{
Data = track.Adapt<Track>(),
IsSuccess = true
};
}
public async Task<OperationResult<TrackStreamInfo>> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, User roadieUser)
{
var track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
// Not Found try recanning release
var release = (from r in this.DbContext.Releases
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
where rm.Id == track.ReleaseMediaId
select r).FirstOrDefault();
if (!release.IsLocked ?? false)
{
await this.AdminService.ScanRelease(new Library.Identity.ApplicationUser
{
Id = roadieUser.Id.Value
}, release.RoadieId, false, true);
}
track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: Unable To Find Track [{ trackId }]");
}
}
if (!track.IsValid)
{
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: Invalid Track. Track Id [{trackId}], FilePath [{track.FilePath}], Filename [{track.FileName}]");
}
string trackPath = null;
try
{
trackPath = track.PathToTrack(this.Configuration, this.Configuration.LibraryFolder);
}
catch (Exception ex)
{
return new OperationResult<TrackStreamInfo>(ex);
}
var trackFileInfo = new FileInfo(trackPath);
if (!trackFileInfo.Exists)
{
// Not Found try recanning release
var release = (from r in this.DbContext.Releases
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
where rm.Id == track.ReleaseMediaId
select r).FirstOrDefault();
if (!release.IsLocked ?? false)
{
await this.AdminService.ScanRelease(new Library.Identity.ApplicationUser
{
Id = roadieUser.Id.Value
}, release.RoadieId, false, true);
}
track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: Unable To Find Track [{ trackId }]");
}
try
{
trackPath = track.PathToTrack(this.Configuration, this.Configuration.LibraryFolder);
}
catch (Exception ex)
{
return new OperationResult<TrackStreamInfo>(ex);
}
if (!trackFileInfo.Exists)
{
track.UpdateTrackMissingFile();
await this.DbContext.SaveChangesAsync();
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: TrackId [{trackId}] Unable to Find Track [{trackFileInfo.FullName}]");
}
}
var contentDurationTimeSpan = TimeSpan.FromMilliseconds((double)(track.Duration ?? 0));
var info = new TrackStreamInfo
{
FileName = this.HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly(),
ContentDisposition = $"attachment; filename=\"{ this.HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly() }\"",
ContentDuration = contentDurationTimeSpan.TotalSeconds.ToString(),
};
var cacheTimeout = 86400; // 24 hours
var contentLength = (endBytes - beginBytes) + 1;
info.Track = new DataToken
{
Text = track.Title,
Value = track.RoadieId.ToString()
};
info.BeginBytes = beginBytes;
info.EndBytes = endBytes;
info.ContentRange = $"bytes {beginBytes}-{endBytes}/{contentLength}";
info.ContentLength = contentLength.ToString();
info.IsFullRequest = beginBytes == 0 && endBytes == (trackFileInfo.Length - 1);
info.IsEndRangeRequest = beginBytes > 0 && endBytes != (trackFileInfo.Length - 1);
info.LastModified = (track.LastUpdated ?? track.CreatedDate).ToString("R");
info.Etag = track.Etag;
info.CacheControl = $"public, max-age={ cacheTimeout.ToString() } ";
info.Expires = DateTime.UtcNow.AddMinutes(cacheTimeout).ToString("R");
int bytesToRead = (int)(endBytes - beginBytes) + 1;
byte[] trackBytes = new byte[bytesToRead];
using (var fs = trackFileInfo.OpenRead())
{
try
{
fs.Seek(beginBytes, SeekOrigin.Begin);
var r = fs.Read(trackBytes, 0, bytesToRead);
}
catch (Exception ex)
{
return new OperationResult<TrackStreamInfo>(ex);
}
}
info.Bytes = trackBytes;
return new OperationResult<TrackStreamInfo>
{
IsSuccess = true,
Data = info
};
}
public async Task<OperationResult<bool>> UpdateTrack(User user, Track model)
{
var didChangeTrack = false;
@ -682,141 +816,6 @@ namespace Roadie.Api.Services
};
}
/// <summary>
/// Fast as possible check if exists and return minimum information on Track
/// </summary>
public OperationResult<Track> StreamCheckAndInfo(User roadieUser, Guid id)
{
var track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == id);
if (track == null)
{
return new OperationResult<Track>(true, string.Format("Track Not Found [{0}]", id));
}
return new OperationResult<Track>()
{
Data = track.Adapt<Track>(),
IsSuccess = true
};
}
public async Task<OperationResult<TrackStreamInfo>> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, User roadieUser)
{
var track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
// Not Found try recanning release
var release = (from r in this.DbContext.Releases
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
where rm.Id == track.ReleaseMediaId
select r).FirstOrDefault();
if (!release.IsLocked ?? false)
{
await this.AdminService.ScanRelease(new Library.Identity.ApplicationUser
{
Id = roadieUser.Id.Value
}, release.RoadieId, false, true);
}
track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId);
if(track == null)
{
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: Unable To Find Track [{ trackId }]");
}
}
if (!track.IsValid)
{
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: Invalid Track. Track Id [{trackId}], FilePath [{track.FilePath}], Filename [{track.FileName}]");
}
string trackPath = null;
try
{
trackPath = track.PathToTrack(this.Configuration, this.Configuration.LibraryFolder);
}
catch (Exception ex)
{
return new OperationResult<TrackStreamInfo>(ex);
}
var trackFileInfo = new FileInfo(trackPath);
if (!trackFileInfo.Exists)
{
// Not Found try recanning release
var release = (from r in this.DbContext.Releases
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
where rm.Id == track.ReleaseMediaId
select r).FirstOrDefault();
if (!release.IsLocked ?? false)
{
await this.AdminService.ScanRelease(new Library.Identity.ApplicationUser
{
Id = roadieUser.Id.Value
}, release.RoadieId, false, true);
}
track = this.DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: Unable To Find Track [{ trackId }]");
}
try
{
trackPath = track.PathToTrack(this.Configuration, this.Configuration.LibraryFolder);
}
catch (Exception ex)
{
return new OperationResult<TrackStreamInfo>(ex);
}
if (!trackFileInfo.Exists)
{
track.UpdateTrackMissingFile();
await this.DbContext.SaveChangesAsync();
return new OperationResult<TrackStreamInfo>($"TrackStreamInfo: TrackId [{trackId}] Unable to Find Track [{trackFileInfo.FullName}]");
}
}
var contentDurationTimeSpan = TimeSpan.FromMilliseconds((double)(track.Duration ?? 0));
var info = new TrackStreamInfo
{
FileName = this.HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly(),
ContentDisposition = $"attachment; filename=\"{ this.HttpEncoder.UrlEncode(track.FileName).ToContentDispositionFriendly() }\"",
ContentDuration = contentDurationTimeSpan.TotalSeconds.ToString(),
};
var cacheTimeout = 86400; // 24 hours
var contentLength = (endBytes - beginBytes) + 1;
info.Track = new DataToken
{
Text = track.Title,
Value = track.RoadieId.ToString()
};
info.BeginBytes = beginBytes;
info.EndBytes = endBytes;
info.ContentRange = $"bytes {beginBytes}-{endBytes}/{contentLength}";
info.ContentLength = contentLength.ToString();
info.IsFullRequest = beginBytes == 0 && endBytes == (trackFileInfo.Length - 1);
info.IsEndRangeRequest = beginBytes > 0 && endBytes != (trackFileInfo.Length - 1);
info.LastModified = (track.LastUpdated ?? track.CreatedDate).ToString("R");
info.Etag = track.Etag;
info.CacheControl = $"public, max-age={ cacheTimeout.ToString() } ";
info.Expires = DateTime.UtcNow.AddMinutes(cacheTimeout).ToString("R");
int bytesToRead = (int)(endBytes - beginBytes) + 1;
byte[] trackBytes = new byte[bytesToRead];
using (var fs = trackFileInfo.OpenRead())
{
try
{
fs.Seek(beginBytes, SeekOrigin.Begin);
var r = fs.Read(trackBytes, 0, bytesToRead);
}
catch (Exception ex)
{
return new OperationResult<TrackStreamInfo>(ex);
}
}
info.Bytes = trackBytes;
return new OperationResult<TrackStreamInfo>
{
IsSuccess = true,
Data = info
};
}
private Task<OperationResult<Track>> TrackByIdAction(Guid id, IEnumerable<string> includes)
{
var sw = Stopwatch.StartNew();

View file

@ -365,7 +365,7 @@ namespace Roadie.Api.Services
public async Task<OperationResult<short>> SetTrackRating(Guid trackId, User roadieUser, short rating)
{
var timings = new Dictionary<string, long>();
var sw = Stopwatch.StartNew();
var sw = Stopwatch.StartNew();
var user = this.GetUser(roadieUser.UserId);
sw.Stop();
timings.Add("GetUser", sw.ElapsedMilliseconds);

View file

@ -24,18 +24,13 @@ namespace Roadie.Api.Controllers
[AllowAnonymous]
public class AccountController : ControllerBase
{
private string _baseUrl = null;
private readonly IConfiguration Configuration;
private readonly ILogger<AccountController> Logger;
private readonly SignInManager<ApplicationUser> SignInManager;
private readonly ITokenService TokenService;
private readonly UserManager<ApplicationUser> UserManager;
private string _baseUrl = null;
private IAdminService AdminService { get; }
private ICacheManager CacheManager { get; }
private IEmailSender EmailSender { get; }
private IHttpContext RoadieHttpContext { get; }
private IRoadieSettings RoadieSettings { get; }
private string BaseUrl
{
@ -59,6 +54,11 @@ namespace Roadie.Api.Controllers
}
}
private ICacheManager CacheManager { get; }
private IEmailSender EmailSender { get; }
private IHttpContext RoadieHttpContext { get; }
private IRoadieSettings RoadieSettings { get; }
public AccountController(
IAdminService adminService,
UserManager<ApplicationUser> userManager,
@ -277,7 +277,7 @@ namespace Roadie.Api.Controllers
public async Task<IActionResult> SendPasswordResetEmail(string username, string callbackUrl)
{
var user = await UserManager.FindByNameAsync(username);
if(user == null)
if (user == null)
{
this.Logger.LogError($"Unable to find user by username [{ username }]");
return StatusCode(500);

View file

@ -35,12 +35,11 @@ namespace Roadie.Api.Controllers
return Ok();
}
[HttpGet("scan/inbound")]
[HttpPost("delete/artist/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanInbound()
public async Task<IActionResult> DeleteArtist(Guid id)
{
var result = await this.AdminService.ScanInboundFolder(await this.UserManager.GetUserAsync(User));
var result = await this.AdminService.DeleteArtist(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
@ -48,11 +47,11 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpGet("scan/library")]
[HttpPost("delete/artist/releases/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanLibrary()
public async Task<IActionResult> DeleteArtistReleases(Guid id)
{
var result = await this.AdminService.ScanLibraryFolder(await this.UserManager.GetUserAsync(User));
var result = await this.AdminService.DeleteArtistReleases(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
@ -60,11 +59,11 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("scan/artist/{id}")]
[HttpPost("delete/artistsecondaryimage/{id}/{index}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanArtist(Guid id)
public async Task<IActionResult> DeleteArtistSecondaryImage(Guid id, int index)
{
var result = await this.AdminService.ScanArtist(await this.UserManager.GetUserAsync(User), id);
var result = await this.AdminService.DeleteArtistSecondaryImage(await this.UserManager.GetUserAsync(User), id, index);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
@ -72,55 +71,6 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("scan/release/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanRelease(Guid id)
{
var result = await this.AdminService.ScanRelease(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("scan/collection/rescanall")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanAllCollections()
{
var result = await this.AdminService.ScanAllCollections(await this.UserManager.GetUserAsync(User));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("scan/collection/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanCollection(Guid id)
{
var result = await this.AdminService.ScanCollection(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("missingcollectionreleases")]
[ProducesResponseType(200)]
public async Task<IActionResult> MissingCollectionReleases()
{
var result = await this.AdminService.MissingCollectionReleases(await this.UserManager.GetUserAsync(User));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("delete/release/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteRelease(Guid id, bool? doDeleteFiles)
@ -145,11 +95,11 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("delete/artist/{id}")]
[HttpPost("delete/user/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteArtist(Guid id)
public async Task<IActionResult> DeleteUser(Guid id)
{
var result = await this.AdminService.DeleteArtist(await this.UserManager.GetUserAsync(User), id);
var result = await this.AdminService.DeleteUser(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
@ -157,11 +107,11 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("delete/artistsecondaryimage/{id}/{index}")]
[HttpPost("missingcollectionreleases")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteArtistSecondaryImage(Guid id, int index)
public async Task<IActionResult> MissingCollectionReleases()
{
var result = await this.AdminService.DeleteArtistSecondaryImage(await this.UserManager.GetUserAsync(User), id, index);
var result = await this.AdminService.MissingCollectionReleases(await this.UserManager.GetUserAsync(User));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
@ -169,12 +119,71 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("delete/artist/releases/{id}")]
[HttpPost("scan/collection/rescanall")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteArtistReleases(Guid id)
public async Task<IActionResult> ScanAllCollections()
{
var result = await this.AdminService.DeleteArtistReleases(await this.UserManager.GetUserAsync(User), id);
var result = await this.AdminService.ScanAllCollections(await this.UserManager.GetUserAsync(User));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("scan/artist/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanArtist(Guid id)
{
var result = await this.AdminService.ScanArtist(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("scan/collection/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanCollection(Guid id)
{
var result = await this.AdminService.ScanCollection(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("scan/inbound")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanInbound()
{
var result = await this.AdminService.ScanInboundFolder(await this.UserManager.GetUserAsync(User));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("scan/library")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanLibrary()
{
var result = await this.AdminService.ScanLibraryFolder(await this.UserManager.GetUserAsync(User));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("scan/release/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanRelease(Guid id)
{
var result = await this.AdminService.ScanRelease(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);

View file

@ -98,7 +98,6 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("setImageByUrl/{id}/{imageUrl}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -117,24 +116,6 @@ namespace Roadie.Api.Controllers
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 this.ArtistService.UploadArtistImage(await this.CurrentUserModel(), id, file);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -157,6 +138,22 @@ namespace Roadie.Api.Controllers
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 this.ArtistService.UploadArtistImage(await this.CurrentUserModel(), id, file);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
}
}

View file

@ -29,23 +29,6 @@ namespace Roadie.Api.Controllers
this.CollectionService = collectionService;
}
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null)
{
var result = await this.CollectionService.ById(await this.CurrentUserModel(), id, (inc ?? models.Collections.Collection.DefaultIncludes).ToLower().Split(","));
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("add")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -59,28 +42,6 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> Update(models.Collections.Collection collection)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await this.CollectionService.UpdateCollection(await this.CurrentUserModel(), collection);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("delete/{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -95,6 +56,22 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null)
{
var result = await this.CollectionService.ById(await this.CurrentUserModel(), id, (inc ?? models.Collections.Collection.DefaultIncludes).ToLower().Split(","));
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet]
[ProducesResponseType(200)]
@ -120,5 +97,27 @@ namespace Roadie.Api.Controllers
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> Update(models.Collections.Collection collection)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await this.CollectionService.UpdateCollection(await this.CurrentUserModel(), collection);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
}
}

View file

@ -25,9 +25,9 @@ namespace Roadie.Api.Controllers
public const string ControllerCacheRegionUrn = "urn:controller_cache";
private models.User _currentUser = null;
protected ILogger Logger { get; set; }
protected ICacheManager CacheManager { get; }
protected IConfiguration Configuration { get; }
protected ILogger Logger { get; set; }
protected IRoadieSettings RoadieSettings { get; }
protected UserManager<ApplicationUser> UserManager { get; }
@ -50,28 +50,16 @@ namespace Roadie.Api.Controllers
this._currentUser = await this.CacheManager.GetAsync($"urn:controller_user:{ this.User.Identity.Name }", async () =>
{
return this.UserModelForUser(await this.UserManager.GetUserAsync(User));
}, ControllerCacheRegionUrn);
}, ControllerCacheRegionUrn);
}
}
if(this._currentUser == null)
if (this._currentUser == null)
{
throw new UnauthorizedAccessException("Access Denied");
}
return this._currentUser;
}
protected models.User UserModelForUser(ApplicationUser user)
{
if(user == null)
{
return null;
}
var result = user.Adapt<models.User>();
result.IsAdmin = User.IsInRole("Admin");
result.IsEditor = User.IsInRole("Editor") || result.IsAdmin;
return result;
}
protected async Task<IActionResult> StreamTrack(Guid id, ITrackService trackService, IPlayActivityService playActivityService, models.User currentUser = null)
{
var sw = Stopwatch.StartNew();
@ -81,7 +69,7 @@ namespace Roadie.Api.Controllers
tsw.Restart();
var user = currentUser ?? await this.CurrentUserModel();
var track = trackService.StreamCheckAndInfo(user, id);
if (track == null || ( track?.IsNotFoundResult ?? false))
if (track == null || (track?.IsNotFoundResult ?? false))
{
if (track?.Errors != null && (track?.Errors.Any() ?? false))
{
@ -140,8 +128,18 @@ namespace Roadie.Api.Controllers
sw.Stop();
this.Logger.LogInformation($"StreamTrack ElapsedTime [{ sw.ElapsedMilliseconds }], Timings [{ JsonConvert.SerializeObject(timings) }] PlayActivity `{ playListUser?.Data.ToString() }`, StreamInfo `{ info?.Data.ToString() }`");
return new EmptyResult();
}
protected models.User UserModelForUser(ApplicationUser user)
{
if (user == null)
{
return null;
}
var result = user.Adapt<models.User>();
result.IsAdmin = User.IsInRole("Admin");
result.IsEditor = User.IsInRole("Editor") || result.IsAdmin;
return result;
}
}
}

View file

@ -75,7 +75,6 @@ namespace Roadie.Api.Controllers
entityTag: result.ETag);
}
[HttpGet("collection/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -219,6 +218,56 @@ namespace Roadie.Api.Controllers
entityTag: result.ETag);
}
[HttpPost("search/artist/{query}/{resultsCount:int?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> SearchForArtistImage(string query, int? resultsCount)
{
var result = await this.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/label/{query}/{resultsCount:int?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> SearchForLabelImage(string query, int? resultsCount)
{
var result = await this.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)]
public async Task<IActionResult> SearchForReleaseCover(string query, int? resultsCount)
{
var result = await this.ImageService.Search(query, resultsCount ?? 10);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("track/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
@ -264,57 +313,5 @@ namespace Roadie.Api.Controllers
lastModified: result.LastModified,
entityTag: result.ETag);
}
[HttpPost("search/release/{query}/{resultsCount:int?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> SearchForReleaseCover(string query, int? resultsCount)
{
var result = await this.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/artist/{query}/{resultsCount:int?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> SearchForArtistImage(string query, int? resultsCount)
{
var result = await this.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/label/{query}/{resultsCount:int?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> SearchForLabelImage(string query, int? resultsCount)
{
var result = await this.ImageService.Search(query, resultsCount ?? 10);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
}
}

View file

@ -74,24 +74,6 @@ namespace Roadie.Api.Controllers
return StatusCode((int)HttpStatusCode.InternalServerError);
}
[HttpPost("uploadImage/{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
{
var result = await this.LabelService.UploadLabelImage(await this.CurrentUserModel(), id, file);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("setImageByUrl/{id}/{imageUrl}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -131,5 +113,23 @@ namespace Roadie.Api.Controllers
}
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 this.LabelService.UploadLabelImage(await this.CurrentUserModel(), id, file);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
}
}

View file

@ -3,11 +3,9 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Roadie.Api.Services;
using Roadie.Library.Caching;
using Roadie.Library.Identity;
using Roadie.Library.Utility;
using System;
using System.Net;
using System.Threading.Tasks;

View file

@ -8,7 +8,6 @@ using Roadie.Library.Caching;
using Roadie.Library.Identity;
using Roadie.Library.Utility;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
@ -73,11 +72,11 @@ namespace Roadie.Api.Controllers
public async Task<IActionResult> StreamTrack(int userId, string trackPlayToken, Guid id)
{
var user = this.UserManager.Users.FirstOrDefault(x => x.Id == userId);
if(user == null)
if (user == null)
{
return StatusCode((int)HttpStatusCode.Unauthorized);
}
if(!ServiceBase.ConfirmTrackPlayToken(user, id, trackPlayToken))
if (!ServiceBase.ConfirmTrackPlayToken(user, id, trackPlayToken))
{
return StatusCode((int)HttpStatusCode.Unauthorized);
}

View file

@ -6,11 +6,9 @@ using Microsoft.Extensions.Logging;
using Roadie.Api.Services;
using Roadie.Library.Caching;
using Roadie.Library.Identity;
using Roadie.Library.Models;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Playlists;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using models = Roadie.Library.Models;
@ -32,36 +30,6 @@ namespace Roadie.Api.Controllers
this.PlaylistService = playlistService;
}
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null)
{
var result = await this.PlaylistService.ById(await this.CurrentUserModel(), id, (inc ?? models.Playlists.Playlist.DefaultIncludes).ToLower().Split(","));
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet]
[ProducesResponseType(200)]
public async Task<IActionResult> List([FromQuery]PagedRequest request, string inc)
{
var result = await this.PlaylistService.List(roadieUser: await this.CurrentUserModel(),
request: request);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("add")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -85,7 +53,7 @@ namespace Roadie.Api.Controllers
{
return NotFound();
}
if(result != null && result.IsAccessDeniedResult)
if (result != null && result.IsAccessDeniedResult)
{
return StatusCode((int)HttpStatusCode.Forbidden);
}
@ -96,6 +64,36 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null)
{
var result = await this.PlaylistService.ById(await this.CurrentUserModel(), id, (inc ?? models.Playlists.Playlist.DefaultIncludes).ToLower().Split(","));
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet]
[ProducesResponseType(200)]
public async Task<IActionResult> List([FromQuery]PagedRequest request, string inc)
{
var result = await this.PlaylistService.List(roadieUser: await this.CurrentUserModel(),
request: request);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -133,8 +131,5 @@ namespace Roadie.Api.Controllers
}
return Ok(result);
}
}
}

View file

@ -4,14 +4,11 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Roadie.Api.Services;
using Roadie.Library.Caching;
using Roadie.Library.Data;
using Roadie.Library.Identity;
using Roadie.Library.Models.Pagination;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using System.Web;
@ -51,6 +48,33 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpGet]
[ProducesResponseType(200)]
public async Task<IActionResult> List([FromQuery]PagedRequest request, string inc, bool? doRandomize = false)
{
try
{
var result = await this.ReleaseService.List(user: await this.CurrentUserModel(),
request: request,
doRandomize: doRandomize ?? false,
includes: (inc ?? models.Releases.Release.DefaultListIncludes).ToLower().Split(","));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
catch (UnauthorizedAccessException)
{
return StatusCode((int)HttpStatusCode.Unauthorized);
}
catch (Exception ex)
{
this.Logger.LogError(ex);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
[HttpPost("mergeReleases/{releaseToMergeId}/{releaseToMergeIntoId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -69,17 +93,13 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("edit")]
[HttpPost("setImageByUrl/{id}/{imageUrl}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy="Editor")]
public async Task<IActionResult> Update(models.Releases.Release release)
[Authorize(Policy = "Editor")]
public async Task<IActionResult> SetReleaseImageByUrl(Guid id, string imageUrl)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await this.ReleaseService.UpdateRelease(await this.CurrentUserModel(), release);
var result = await this.ReleaseService.SetReleaseImageByUrl(await this.CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
if (result == null || result.IsNotFoundResult)
{
return NotFound();
@ -91,40 +111,17 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpGet]
[ProducesResponseType(200)]
public async Task<IActionResult> List([FromQuery]PagedRequest request, string inc, bool? doRandomize = false)
{
try
{
var result = await this.ReleaseService.List(user: await this.CurrentUserModel(),
request: request,
doRandomize: doRandomize ?? false,
includes: (inc ?? models.Releases.Release.DefaultListIncludes).ToLower().Split(","));
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
catch(UnauthorizedAccessException)
{
return StatusCode((int)HttpStatusCode.Unauthorized);
}
catch (Exception ex)
{
this.Logger.LogError(ex);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
[HttpPost("setImageByUrl/{id}/{imageUrl}")]
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> SetReleaseImageByUrl(Guid id, string imageUrl)
public async Task<IActionResult> Update(models.Releases.Release release)
{
var result = await this.ReleaseService.SetReleaseImageByUrl(await this.CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await this.ReleaseService.UpdateRelease(await this.CurrentUserModel(), release);
if (result == null || result.IsNotFoundResult)
{
return NotFound();

View file

@ -27,14 +27,6 @@ namespace Roadie.Api.Controllers
this.StatisticsService = statisticsService;
}
[HttpGet("ping")]
[ProducesResponseType(200)]
[AllowAnonymous]
public IActionResult Ping()
{
return Ok("pong");
}
[HttpGet("info")]
[ProducesResponseType(200)]
[AllowAnonymous]
@ -62,6 +54,14 @@ namespace Roadie.Api.Controllers
return Ok(await this.StatisticsService.LibraryStatistics());
}
[HttpGet("ping")]
[ProducesResponseType(200)]
[AllowAnonymous]
public IActionResult Ping()
{
return Ok("pong");
}
[HttpGet("releasesByDate")]
[ProducesResponseType(200)]
public async Task<IActionResult> ReleasesByDate()

View file

@ -43,6 +43,20 @@ namespace Roadie.Api.Controllers
this.PlayActivityService = playActivityService;
}
[HttpGet("addChatMessage.view")]
[HttpPost("addChatMessage.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> AddChatMessage(SubsonicRequest request)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.AddChatMessage(request, this.SubsonicUser);
return this.BuildResponse(request, result);
}
[HttpGet("createBookmark.view")]
[HttpPost("createBookmark.view")]
[ProducesResponseType(200)]
@ -276,6 +290,20 @@ namespace Roadie.Api.Controllers
return this.BuildResponse(request, result, "bookmarks");
}
[HttpGet("getChatMessages.view")]
[HttpPost("getChatMessages.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetChatMessages(SubsonicRequest request, long? since)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.GetChatMessages(request, this.SubsonicUser, since);
return this.BuildResponse(request, result, "chatMessages");
}
[HttpGet("getCoverArt.view")]
[HttpPost("getCoverArt.view")]
[ProducesResponseType(200)]
@ -404,20 +432,6 @@ namespace Roadie.Api.Controllers
return this.BuildResponse(request, result, "playlists");
}
[HttpGet("savePlayQueue.view")]
[HttpPost("savePlayQueue.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> SavePlayQueue(SubsonicRequest request, string username, string current, long? position)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.SavePlayQueue(request, this.SubsonicUser, current, position);
return this.BuildResponse(request, result);
}
[HttpGet("getPlayQueue.view")]
[HttpPost("getPlayQueue.view")]
[ProducesResponseType(200)]
@ -562,34 +576,6 @@ namespace Roadie.Api.Controllers
return this.BuildResponse(request, result, "topSongs");
}
[HttpGet("getChatMessages.view")]
[HttpPost("getChatMessages.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetChatMessages(SubsonicRequest request, long? since)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.GetChatMessages(request, this.SubsonicUser, since);
return this.BuildResponse(request, result, "chatMessages");
}
[HttpGet("addChatMessage.view")]
[HttpPost("addChatMessage.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> AddChatMessage(SubsonicRequest request)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.AddChatMessage(request, this.SubsonicUser);
return this.BuildResponse(request, result);
}
[HttpGet("getUser.view")]
[HttpPost("getUser.view")]
[ProducesResponseType(200)]
@ -631,6 +617,20 @@ namespace Roadie.Api.Controllers
return Content("<subsonic-response xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://subsonic.org/restapi\" status=\"ok\" version=\"1.16.0\" />", "application/xml");
}
[HttpGet("savePlayQueue.view")]
[HttpPost("savePlayQueue.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> SavePlayQueue(SubsonicRequest request, string username, string current, long? position)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.SavePlayQueue(request, this.SubsonicUser, current, position);
return this.BuildResponse(request, result);
}
/// <summary>
/// Returns albums, artists and songs matching the given search criteria. Supports paging through the result.
/// </summary>

View file

@ -11,6 +11,7 @@ using Roadie.Library.Models.Pagination;
using System;
using System.Net;
using System.Threading.Tasks;
using models = Roadie.Library.Models;
namespace Roadie.Api.Controllers
@ -47,28 +48,6 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> Update(Track track)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await this.TrackService.UpdateTrack(await this.CurrentUserModel(), track);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet]
[ProducesResponseType(200)]
public async Task<IActionResult> List([FromQuery]PagedRequest request, string inc, bool? doRandomize = false)
@ -94,5 +73,27 @@ namespace Roadie.Api.Controllers
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> Update(Track track)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await this.TrackService.UpdateTrack(await this.CurrentUserModel(), track);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
}
}

View file

@ -23,8 +23,8 @@ namespace Roadie.Api.Controllers
public class UserController : EntityControllerBase
{
private readonly ITokenService TokenService;
private IUserService UserService { get; }
private IHttpContext RoadieHttpContext { get; }
private IUserService UserService { get; }
public UserController(IUserService userService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, ITokenService tokenService, UserManager<ApplicationUser> userManager, IHttpContext httpContext)
: base(cacheManager, configuration, userManager)

View file

@ -34,7 +34,7 @@
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.RollingFileAlternate" Version="2.0.9" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.13" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.15" />
</ItemGroup>
<ItemGroup>