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

v20190317.1

This commit is contained in:
Steven Hildreth 2019-05-10 22:07:45 -05:00
parent ca98411c68
commit a876eec82b
15 changed files with 205 additions and 81 deletions

View file

@ -16,7 +16,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.3" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
</ItemGroup>

View file

@ -20,7 +20,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>

View file

@ -68,6 +68,15 @@ namespace Roadie.Library.Data
return FolderPathHelper.PathForTrack(configuration, this, libraryFolder);
}
/// <summary>
/// Returns a full file path to the current track thumbnail (if any)
/// </summary>
public string PathToTrackThumbnail(IRoadieSettings configuration, string libraryFolder)
{
return FolderPathHelper.PathForTrackThumbnail(configuration, this, libraryFolder);
}
public override string ToString()
{
return string.Format("Id [{0}], TrackNumber [{1}], Title [{2}]", this.Id, this.TrackNumber, this.Title);

View file

@ -11,6 +11,7 @@ namespace Roadie.Library.Enums
ArtistSecondary,
Release,
ReleaseSecondary,
Label
Label,
Track
}
}

View file

@ -49,6 +49,9 @@ namespace Roadie.Library.Models
[MaxLength(100)]
public string MusicBrainzId { get; set; }
// When populated a "data:image" base64 byte array of an image to use as new Thumbnail
public string NewThumbnailData { get; set; }
[MaxLength(65535)]
[JsonIgnore]
[IgnoreDataMember]
@ -65,7 +68,7 @@ namespace Roadie.Library.Models
{
return null;
}
return this.PartTitles.Split('|');
return this.PartTitles.Replace("|", "\n").Split("\n");
}
return this._partTitles;
}

View file

@ -9,20 +9,20 @@
<ItemGroup>
<PackageReference Include="CsvHelper" Version="12.1.2" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.3" />
<PackageReference Include="FluentFTP" Version="23.1.0" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.6" />
<PackageReference Include="FluentFTP" Version="24.0.0" />
<PackageReference Include="Hashids.net" Version="1.2.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.9.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.4" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" />
<PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" />
<PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" />
<PackageReference Include="Mapster" Version="3.3.2" />
<PackageReference Include="Mapster" Version="4.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="MimeMapping" Version="1.0.1.12" />
<PackageReference Include="NodaTime" Version="2.4.4" />
<PackageReference Include="NodaTime" Version="2.4.5" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
<PackageReference Include="RestSharp" Version="106.6.9" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0006" />
@ -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.6.0" />
<PackageReference Include="z440.atl.core" Version="2.9.0" />
<PackageReference Include="zlib.net-mutliplatform" Version="1.0.4" />
</ItemGroup>

View file

@ -82,6 +82,24 @@ namespace Roadie.Library.Utility
return directoryInfo.FullName;
}
/// <summary>
/// For a given Track database record determine the full path to the thumbnail using the given destination folder
/// </summary>
/// <param name="track">Populate track database record</param>
/// <param name="destinationFolder">Optional Root folder defaults to Library Folder from Settings</param>
public static string PathForTrackThumbnail(IRoadieSettings configuration, Data.Track track, string destinationFolder = null)
{
destinationFolder = destinationFolder ?? configuration.LibraryFolder;
if (string.IsNullOrEmpty(track.FilePath) || string.IsNullOrEmpty(track.FileName))
{
return null;
}
var fileName = Path.ChangeExtension(track.FileName, ".jpg");
var directoryInfo = new DirectoryInfo(Path.Combine(destinationFolder, track.FilePath, fileName));
return directoryInfo.FullName;
}
/// <summary>
/// Full path to Release folder using given full Artist folder
/// </summary>

View file

@ -191,6 +191,12 @@ namespace Roadie.Api.Services
File.Delete(trackPath);
this.Logger.LogWarning($"x For Track `{ track }`, Deleted File [{ trackPath }]");
}
var trackThumbnailName = track.PathToTrackThumbnail(this.Configuration, this.Configuration.LibraryFolder);
if (File.Exists(trackThumbnailName))
{
File.Delete(trackThumbnailName);
this.Logger.LogWarning($"x For Track `{ track }`, Deleted Thumbnail File [{ trackThumbnailName }]");
}
}
catch (Exception ex)
{

View file

@ -14,6 +14,8 @@ 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);

View file

@ -379,37 +379,45 @@ namespace Roadie.Api.Services
private async Task<FileOperationResult<Image>> GetImageFileOperation(string type, string regionUrn, Guid id, int? width, int? height, Func<Task<FileOperationResult<Image>>> action, EntityTagHeaderValue etag = null)
{
var sw = Stopwatch.StartNew();
var result = (await this.CacheManager.GetAsync($"urn:{ type }_by_id_operation:{id}", action, regionUrn)).Adapt<FileOperationResult<Image>>();
if (!result.IsSuccess)
try
{
return new FileOperationResult<Image>(result.IsNotFoundResult, result.Messages);
}
if (result.ETag == etag)
{
return new FileOperationResult<Image>(OperationMessages.NotModified);
}
if ((width.HasValue || height.HasValue) && result?.Data?.Bytes != null)
{
result.Data.Bytes = ImageHelper.ResizeImage(result?.Data?.Bytes, width.Value, height.Value);
result.ETag = EtagHelper.GenerateETag(this.HttpEncoder, result.Data.Bytes);
result.LastModified = DateTime.UtcNow;
if (width.Value != this.Configuration.ThumbnailImageSize.Width || height.Value != this.Configuration.ThumbnailImageSize.Height)
var sw = Stopwatch.StartNew();
var result = (await this.CacheManager.GetAsync($"urn:{ type }_by_id_operation:{id}", action, regionUrn)).Adapt<FileOperationResult<Image>>();
if (!result.IsSuccess)
{
this.Logger.LogTrace($"{ type }: Resized [{ id }], Width [{ width.Value }], Height [{ height.Value }]");
return new FileOperationResult<Image>(result.IsNotFoundResult, result.Messages);
}
if (result.ETag == etag)
{
return new FileOperationResult<Image>(OperationMessages.NotModified);
}
if ((width.HasValue || height.HasValue) && result?.Data?.Bytes != null)
{
result.Data.Bytes = ImageHelper.ResizeImage(result?.Data?.Bytes, width.Value, height.Value);
result.ETag = EtagHelper.GenerateETag(this.HttpEncoder, result.Data.Bytes);
result.LastModified = DateTime.UtcNow;
if (width.Value != this.Configuration.ThumbnailImageSize.Width || height.Value != this.Configuration.ThumbnailImageSize.Height)
{
this.Logger.LogTrace($"{ type }: Resized [{ id }], Width [{ width.Value }], Height [{ height.Value }]");
}
}
sw.Stop();
return new FileOperationResult<Image>(result.Messages)
{
Data = result.Data,
ETag = result.ETag,
LastModified = result.LastModified,
ContentType = result.ContentType,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
sw.Stop();
return new FileOperationResult<Image>(result.Messages)
catch (Exception ex)
{
Data = result.Data,
ETag = result.ETag,
LastModified = result.LastModified,
ContentType = result.ContentType,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
this.Logger.LogError(ex, $"GetImageFileOperation Error, Type [{ type }], id [{id}]");
}
return new FileOperationResult<Image>("System Error");
}
private Task<FileOperationResult<Image>> ImageByIdAction(Guid id, EntityTagHeaderValue etag = null)
@ -663,6 +671,12 @@ namespace Roadie.Api.Services
{
return new FileOperationResult<Image>(true, string.Format("Track Not Found [{0}]", id));
}
var imageBytes = track.Thumbnail;
var trackThumbnailImages = ImageHelper.FindImageTypeInDirectory(new DirectoryInfo(track.PathToTrackThumbnail(this.Configuration, this.Configuration.LibraryFolder)), Library.Enums.ImageType.Track, SearchOption.TopDirectoryOnly);
if (trackThumbnailImages.Any())
{
imageBytes = File.ReadAllBytes(trackThumbnailImages.First().FullName);
}
var image = new data.Image
{
Bytes = track.Thumbnail,

View file

@ -425,53 +425,34 @@ namespace Roadie.Api.Services
{
if (includes.Contains("tracks"))
{
var releaseIds = rows.Select(x => x.Id).ToArray();
var artistTracks = (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 a in this.DbContext.Artists on r.ArtistId equals a.Id
where (releaseIds.Contains(r.RoadieId))
orderby r.Id, rm.MediaNumber, t.TrackNumber
select new
{
t,
releaseMedia = rm,
release = r
});
var releaseTrackIds = artistTracks.Select(x => x.t.Id).ToList();
var artistUserTracks = (from ut in this.DbContext.UserTracks
where ut.UserId == roadieUser.Id
where (from x in releaseTrackIds select x).Contains(ut.TrackId)
select ut).ToArray();
var user = this.GetUser(roadieUser.UserId);
foreach (var release in rows)
{
var releaseMedias = new List<ReleaseMediaList>();
foreach (var releaseMedia in artistTracks.Where(x => x.release.RoadieId == release.Id).Select(x => x.releaseMedia).Distinct().ToArray())
release.Media = this.DbContext.ReleaseMedias
.Include(x => x.Tracks)
.Where(x => x.ReleaseId == release.DatabaseId)
.ProjectToType<ReleaseMediaList>()
.OrderBy(x => x.MediaNumber)
.ToArray();
var userRatingsForRelease = (from ut in this.DbContext.UserTracks
join t in this.DbContext.Tracks on ut.TrackId equals t.Id
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == release.DatabaseId
where ut.UserId == roadieUser.Id
select new { trackId = t.RoadieId, ut }).ToArray();
foreach (var userRatingForRelease in userRatingsForRelease)
{
var rm = releaseMedia.Adapt<ReleaseMediaList>();
var rmTracks = new List<TrackList>();
foreach (var track in artistTracks.Where(x => x.t.ReleaseMediaId == releaseMedia.Id).OrderBy(x => x.t.TrackNumber).ToArray())
var mediaTrack = release.Media.SelectMany(x => x.Tracks).FirstOrDefault(x => x.Id == userRatingForRelease.trackId);
if (mediaTrack != null)
{
var t = track.t.Adapt<TrackList>();
t.CssClass = string.IsNullOrEmpty(track.t.Hash) ? "Missing" : "Ok";
t.TrackPlayUrl = this.MakeTrackPlayUrl(user, track.t.Id, track.t.RoadieId);
var userRating = artistUserTracks.FirstOrDefault(x => x.TrackId == track.t.Id);
if (userRating != null)
mediaTrack.UserRating = new UserTrack
{
t.UserRating = new UserTrack
{
Rating = userRating.Rating,
IsFavorite = userRating.IsFavorite ?? false,
IsDisliked = userRating.IsDisliked ?? false
};
}
rmTracks.Add(t);
Rating = userRatingForRelease.ut.Rating,
IsFavorite = userRatingForRelease.ut.IsFavorite ?? false,
IsDisliked = userRatingForRelease.ut.IsDisliked ?? false
};
}
rm.Tracks = rmTracks;
releaseMedias.Add(rm);
}
release.Media = releaseMedias.OrderBy(x => x.MediaNumber).ToArray();
}
}
}

View file

@ -10,7 +10,7 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.2.0" />
<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.11" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.13" />
</ItemGroup>
<ItemGroup>

View file

@ -9,6 +9,7 @@ using Roadie.Library.Configuration;
using Roadie.Library.Encoding;
using Roadie.Library.Enums;
using Roadie.Library.Extensions;
using Roadie.Library.Imaging;
using Roadie.Library.Models;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Releases;
@ -598,6 +599,73 @@ namespace Roadie.Api.Services
}
}
public async Task<OperationResult<bool>> UpdateTrack(User user, Track model)
{
var didChangeTrack = false;
var didChangeThumbnail = false;
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var track = this.DbContext.Tracks
.Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist)
.FirstOrDefault(x => x.RoadieId == model.Id);
if (track == null)
{
return new OperationResult<bool>(true, string.Format("Track Not Found [{0}]", model.Id));
}
try
{
var now = DateTime.UtcNow;
track.IsLocked = model.IsLocked;
track.Status = SafeParser.ToEnum<Statuses>(model.Status);
track.Title = model.Title;
track.AlternateNames = model.AlternateNamesList.ToDelimitedList();
track.Rating = model.Rating;
track.AmgId = model.AmgId;
track.LastFMId = model.LastFMId;
track.MusicBrainzId = model.MusicBrainzId;
track.SpotifyId = model.SpotifyId;
track.Tags = model.TagsList.ToDelimitedList();
track.PartTitles = model.PartTitlesList == null || !model.PartTitlesList.Any() ? null : string.Join("\n", model.PartTitlesList);
var trackImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (trackImage != null)
{
// Ensure is jpeg first
track.Thumbnail = ImageHelper.ConvertToJpegFormat(trackImage);
// Save unaltered image to cover file
var trackThumbnailName = track.PathToTrackThumbnail(this.Configuration, this.Configuration.LibraryFolder);
File.WriteAllBytes(trackThumbnailName, track.Thumbnail);
// Resize to store in database as thumbnail
track.Thumbnail = ImageHelper.ResizeImage(track.Thumbnail, this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height);
didChangeThumbnail = true;
}
track.LastUpdated = now;
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(track.CacheRegion);
this.Logger.LogInformation($"UpdateTrack `{ track }` By User `{ user }`: Edited Track [{ didChangeTrack }], Uploaded new image [{ didChangeThumbnail }]");
}
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
};
}
/// <summary>
/// Fast as possible check if exists and return minimum information on Track
/// </summary>

View file

@ -47,6 +47,28 @@ 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)

View file

@ -23,8 +23,8 @@
<ItemGroup>
<PackageReference Include="BCrypt-Core" Version="2.0.0" />
<PackageReference Include="Mapster" Version="3.3.2" />
<PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.0" />
<PackageReference Include="Mapster" Version="4.0.0" />
<PackageReference Include="Microsoft.AspNet.SignalR" Version="2.4.1" />
<PackageReference Include="Microsoft.AspNetCore.All" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
@ -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.11" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.13" />
</ItemGroup>
<ItemGroup>