Merge work

This commit is contained in:
Steven Hildreth 2019-01-12 15:10:00 -06:00
parent d4485d08a7
commit 5acad5d4b6
9 changed files with 221 additions and 45 deletions

View file

@ -396,17 +396,18 @@ namespace Roadie.Api.Services
var newArtists = 0;
var newReleases = 0;
var newTracks = 0;
OperationResult<bool> result = null;
foreach (var folder in Directory.EnumerateDirectories(d.FullName).ToArray())
{
var result = await folderProcessor.Process(new DirectoryInfo(folder), isReadOnly);
if (result.AdditionalData != null)
{
newArtists += SafeParser.ToNumber<int>(result.AdditionalData["newArtists"]);
newReleases += SafeParser.ToNumber<int>(result.AdditionalData["newReleases"]);
newTracks += SafeParser.ToNumber<int>(result.AdditionalData["newTracks"]);
}
result = await folderProcessor.Process(new DirectoryInfo(folder), isReadOnly);
processedFolders++;
}
if (result.AdditionalData != null)
{
newArtists = SafeParser.ToNumber<int>(result.AdditionalData["newArtists"]);
newReleases = SafeParser.ToNumber<int>(result.AdditionalData["newReleases"]);
newTracks = SafeParser.ToNumber<int>(result.AdditionalData["newTracks"]);
}
if (!isReadOnly)
{
FolderProcessor.DeleteEmptyFolders(d, this.Logger);

View file

@ -544,6 +544,53 @@ 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.GetArtist(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.GetArtist(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];

View file

@ -20,5 +20,7 @@ namespace Roadie.Api.Services
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

@ -22,5 +22,7 @@ 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

@ -678,6 +678,45 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> MergeReleases(User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var releaseToMerge = this.GetRelease(releaseToMergeId);
if (releaseToMerge == null)
{
this.Logger.LogWarning("MergeReleases Unknown Release [{0}]", releaseToMergeId);
return new OperationResult<bool>(true, string.Format("Release Not Found [{0}]", releaseToMergeId));
}
var releaseToMergeInfo = this.GetRelease(releaseToMergeIntoId);
if (releaseToMergeInfo == null)
{
this.Logger.LogWarning("MergeReleases Unknown Release [{0}]", releaseToMergeIntoId);
return new OperationResult<bool>(true, string.Format("Release Not Found [{0}]", releaseToMergeIntoId));
}
try
{
await this.ReleaseFactory.MergeReleases(releaseToMerge, releaseToMergeInfo, addAsMedia);
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
this.Logger.LogInformation("MergeReleases Release `{0}` Merged Into Release `{1}`, By User `{2}`", releaseToMerge, releaseToMergeInfo, user);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<Library.Models.Image>> UploadReleaseImage(User user, Guid id, IFormFile file)
{
var bytes = new byte[0];

View file

@ -209,6 +209,7 @@ namespace Roadie.Api.Services
return null;
}
// Only read operations
protected data.Track GetTrack(Guid id)
{
return this.CacheManager.Get(data.Track.CacheUrn(id), () =>
@ -324,11 +325,15 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<short>> SetArtistRating(Guid artistId, ApplicationUser user, short rating)
{
var artist = this.GetArtist(artistId);
var artist = this.DbContext.Artists
.Include(x => x.Genres)
.Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null)
{
return new OperationResult<short>(true, $"Invalid Artist Id [{ artistId }]");
}
var now = DateTime.UtcNow;
var userArtist = this.DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id);
if (userArtist == null)
{
@ -343,15 +348,13 @@ namespace Roadie.Api.Services
else
{
userArtist.Rating = rating;
userArtist.LastUpdated = DateTime.UtcNow;
userArtist.LastUpdated = now;
}
await this.DbContext.SaveChangesAsync();
var sql = "UPDATE `artist` set lastUpdated = UTC_DATE(), rating = (SELECT cast(avg(ur.rating) as signed) " +
"FROM `userartist` ur " +
"where artistId = {0}) " +
"WHERE id = {0};";
await this.DbContext.Database.ExecuteSqlCommandAsync(sql, artist.Id);
artist.Rating = (short)this.DbContext.UserArtists.Where(x => x.ArtistId == artist.Id && x.Rating > 0).Average(x => (decimal)x.Rating);
artist.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(user.CacheRegion);
this.CacheManager.ClearRegion(artist.CacheRegion);
@ -367,7 +370,14 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<short>> SetReleaseRating(Guid releaseId, ApplicationUser user, short rating)
{
var release = this.GetRelease(releaseId);
var release = this.DbContext.Releases
.Include(x => x.Artist)
.Include(x => x.Genres)
.Include("Genres.Genre")
.Include(x => x.Medias)
.Include("Medias.Tracks")
.Include("Medias.Tracks.TrackArtist")
.FirstOrDefault(x => x.RoadieId == releaseId);
if (release == null)
{
return new OperationResult<short>(true, $"Invalid Release Id [{ releaseId }]");
@ -391,11 +401,9 @@ namespace Roadie.Api.Services
}
await this.DbContext.SaveChangesAsync();
var sql = "UPDATE `release` set lastUpdated = UTC_DATE(), rating = (SELECT cast(avg(ur.rating) as signed) " +
"FROM `userrelease` ur " +
"where releaseId = {0}) " +
"WHERE id = {0};";
await this.DbContext.Database.ExecuteSqlCommandAsync(sql, release.Id);
release.Rating = (short)this.DbContext.UserReleases.Where(x => x.ReleaseId == release.Id && x.Rating > 0).Average(x => (decimal)x.Rating);
release.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(user.CacheRegion);
this.CacheManager.ClearRegion(release.CacheRegion);
@ -412,11 +420,17 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<short>> SetTrackRating(Guid trackId, ApplicationUser user, short rating)
{
var track = this.GetTrack(trackId);
var track = this.DbContext.Tracks
.Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist)
.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
return new OperationResult<short>(true, $"Invalid Track Id [{ trackId }]");
}
var now = DateTime.UtcNow;
var userTrack = this.DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id);
if (userTrack == null)
{
@ -431,15 +445,13 @@ namespace Roadie.Api.Services
else
{
userTrack.Rating = rating;
userTrack.LastUpdated = DateTime.UtcNow;
userTrack.LastUpdated = now;
}
await this.DbContext.SaveChangesAsync();
var sql = "UPDATE `track` set lastUpdated = UTC_DATE(), rating = (SELECT cast(avg(ur.rating) as signed) " +
"FROM `usertrack` ur " +
"where trackId = {0}) " +
"WHERE id = {0};";
await this.DbContext.Database.ExecuteSqlCommandAsync(sql, track.Id);
track.Rating = (short)this.DbContext.UserTracks.Where(x => x.TrackId == track.Id && x.Rating > 0).Average(x => (decimal)x.Rating);
track.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(user.CacheRegion);
this.CacheManager.ClearRegion(track.CacheRegion);
@ -457,7 +469,10 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<bool>> ToggleArtistDisliked(Guid artistId, ApplicationUser user, bool isDisliked)
{
var artist = this.GetArtist(artistId);
var artist = this.DbContext.Artists
.Include(x => x.Genres)
.Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null)
{
return new OperationResult<bool>(true, $"Invalid Artist Id [{ artistId }]");
@ -492,7 +507,10 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<bool>> ToggleArtistFavorite(Guid artistId, ApplicationUser user, bool isFavorite)
{
var artist = this.GetArtist(artistId);
var artist = this.DbContext.Artists
.Include(x => x.Genres)
.Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null)
{
return new OperationResult<bool>(true, $"Invalid Artist Id [{ artistId }]");
@ -527,7 +545,14 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<bool>> ToggleReleaseDisliked(Guid releaseId, ApplicationUser user, bool isDisliked)
{
var release = this.GetRelease(releaseId);
var release = this.DbContext.Releases
.Include(x => x.Artist)
.Include(x => x.Genres)
.Include("Genres.Genre")
.Include(x => x.Medias)
.Include("Medias.Tracks")
.Include("Medias.Tracks.TrackArtist")
.FirstOrDefault(x => x.RoadieId == releaseId);
if (release == null)
{
return new OperationResult<bool>(true, $"Invalid Release Id [{ releaseId }]");
@ -563,7 +588,14 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<bool>> ToggleReleaseFavorite(Guid releaseId, ApplicationUser user, bool isFavorite)
{
var release = this.GetRelease(releaseId);
var release = this.DbContext.Releases
.Include(x => x.Artist)
.Include(x => x.Genres)
.Include("Genres.Genre")
.Include(x => x.Medias)
.Include("Medias.Tracks")
.Include("Medias.Tracks.TrackArtist")
.FirstOrDefault(x => x.RoadieId == releaseId);
if (release == null)
{
return new OperationResult<bool>(true, $"Invalid Release Id [{ releaseId }]");
@ -599,7 +631,12 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<bool>> ToggleTrackDisliked(Guid trackId, ApplicationUser user, bool isDisliked)
{
var track = this.GetTrack(trackId);
var track = this.DbContext.Tracks
.Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist)
.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
return new OperationResult<bool>(true, $"Invalid Track Id [{ trackId }]");
@ -636,7 +673,12 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<bool>> ToggleTrackFavorite(Guid trackId, ApplicationUser user, bool isFavorite)
{
var track = this.GetTrack(trackId);
var track = this.DbContext.Tracks
.Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist)
.FirstOrDefault(x => x.RoadieId == trackId);
if (track == null)
{
return new OperationResult<bool>(true, $"Invalid Track Id [{ trackId }]");

View file

@ -20,6 +20,7 @@ using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using data = Roadie.Library.Data;
using models = Roadie.Library.Models;
namespace Roadie.Api.Services
@ -533,7 +534,6 @@ namespace Roadie.Api.Services
{
if (includes.Contains("stats"))
{
var userArtists = this.DbContext.UserArtists.Include(x => x.Artist).Where(x => x.UserId == user.Id).ToArray();
var userReleases = this.DbContext.UserReleases.Include(x => x.Release).Where(x => x.UserId == user.Id).ToArray();
var userTracks = this.DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == user.Id).ToArray();
@ -546,7 +546,8 @@ namespace Roadie.Api.Services
where ut.UserId == user.Id
select new { a, ut.PlayedCount })
.GroupBy(a => a.a)
.Select(x => new {
.Select(x => new
{
Artist = x.Key,
Played = x.Sum(t => t.PlayedCount)
})
@ -554,13 +555,14 @@ namespace Roadie.Api.Services
.FirstOrDefault();
var mostPlayedReleaseId = (from r in this.DbContext.Releases
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId
join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId
where ut.UserId == user.Id
select new { r, ut.PlayedCount })
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId
join ut in this.DbContext.UserTracks on t.Id equals ut.TrackId
where ut.UserId == user.Id
select new { r, ut.PlayedCount })
.GroupBy(r => r.r)
.Select(x => new {
.Select(x => new
{
Release = x.Key,
Played = x.Sum(t => t.PlayedCount)
})
@ -583,10 +585,10 @@ namespace Roadie.Api.Services
model.Statistics = new UserStatistics
{
MostPlayedArtist = mostPlayedArtist == null ? null : models.ArtistList.FromDataArtist(mostPlayedArtist.Artist, this.MakeArtistThumbnailImage(mostPlayedArtist.Artist.RoadieId)),
MostPlayedRelease = mostPlayedRelease == null ? null : models.Releases.ReleaseList.FromDataRelease(mostPlayedRelease,
mostPlayedRelease.Artist,
this.HttpContext.BaseUrl,
this.MakeArtistThumbnailImage(mostPlayedRelease.Artist.RoadieId),
MostPlayedRelease = mostPlayedRelease == null ? null : models.Releases.ReleaseList.FromDataRelease(mostPlayedRelease,
mostPlayedRelease.Artist,
this.HttpContext.BaseUrl,
this.MakeArtistThumbnailImage(mostPlayedRelease.Artist.RoadieId),
this.MakeReleaseThumbnailImage(mostPlayedRelease.RoadieId)),
MostPlayedTrack = mostPlayedTrack == null ? null : TrackList.FromDataTrack(this.MakeTrackPlayUrl(user, mostPlayedTrack.Id, mostPlayedTrack.RoadieId),
mostPlayedTrack,

View file

@ -68,9 +68,29 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("mergeArtists/{artistToMergeId}/{artistToMergeIntoId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> MergeArtists(Guid artistToMergeId, Guid artistToMergeIntoId)
{
var result = await this.ArtistService.MergeArtists(await this.CurrentUserModel(), artistToMergeId, artistToMergeIntoId);
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)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> SetArtistImageByUrl(Guid id, string imageUrl)
{
var result = await this.ArtistService.SetReleaseImageByUrl(await this.CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
@ -88,6 +108,7 @@ namespace Roadie.Api.Controllers
[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);

View file

@ -50,6 +50,24 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("mergeReleases/{releaseToMergeId}/{releaseToMergeIntoId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> MergeReleases(Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia)
{
var result = await this.ReleaseService.MergeReleases(await this.CurrentUserModel(), releaseToMergeId, releaseToMergeIntoId, addAsMedia);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
@ -98,6 +116,7 @@ namespace Roadie.Api.Controllers
[HttpPost("setImageByUrl/{id}/{imageUrl}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> SetReleaseImageByUrl(Guid id, string imageUrl)
{
var result = await this.ReleaseService.SetReleaseImageByUrl(await this.CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
@ -115,6 +134,7 @@ namespace Roadie.Api.Controllers
[HttpPost("uploadImage/{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
{
var result = await this.ReleaseService.UploadReleaseImage(await this.CurrentUserModel(), id, file);