Beta v20180203.1

This commit is contained in:
Steven Hildreth 2019-02-03 11:50:17 -06:00
parent 5e21e63c65
commit 0183be8bfd
12 changed files with 367 additions and 40 deletions

View file

@ -0,0 +1,72 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Data;
using Roadie.Library.Engines;
using Roadie.Library.Factories;
using Roadie.Library.MetaData.ID3Tags;
using Roadie.Library.Processors;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Xunit;
namespace Roadie.Library.Tests
{
public class ArtistLookupEngineTests
{
private IEventMessageLogger MessageLogger { get; }
private ILogger Logger
{
get
{
return this.MessageLogger as ILogger;
}
}
private IRoadieSettings Configuration { get; }
public DictionaryCacheManager CacheManager { get; }
private Encoding.IHttpEncoder HttpEncoder { get; }
public ArtistLookupEngineTests()
{
this.MessageLogger = new EventMessageLogger();
this.MessageLogger.Messages += MessageLogger_Messages;
var settings = new RoadieSettings();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("appsettings.test.json");
IConfiguration configuration = configurationBuilder.Build();
configuration.GetSection("RoadieSettings").Bind(settings);
this.Configuration = settings;
this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4)));
this.HttpEncoder = new Encoding.DummyHttpEncoder();
}
private void MessageLogger_Messages(object sender, EventMessage e)
{
Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
}
[Fact]
public void Get_Artist_By_Name()
{
var optionsBuilder = new DbContextOptionsBuilder<RoadieDbContext>();
optionsBuilder.UseMySql("server=voyager;userid=roadie;password=MenAtW0rk668;persistsecurityinfo=True;database=roadie;ConvertZeroDateTime=true");
using (var context = new RoadieDbContext(optionsBuilder.Options))
{
IArtistLookupEngine artistLookupEngine = new ArtistLookupEngine(this.Configuration, this.HttpEncoder, context, this.CacheManager, this.Logger);
var a = artistLookupEngine.DatabaseQueryForArtistName("Nas");
}
}
}
}

View file

@ -47,7 +47,7 @@ namespace Roadie.Library.Tests
[Fact]
public void Load_Root_Level_Configuration()
{
var inboundFolder = @"Z:/incoming/";
var inboundFolder = @"C:\roadie_dev_root\inbound";
var configInboundFolder = this.Settings.InboundFolder;
Assert.Equal(inboundFolder, configInboundFolder);
}

View file

@ -1,3 +1,4 @@
using Roadie.Library.Enums;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@ -33,5 +34,10 @@ namespace Roadie.Library.Data
public int? TrackCount { get; set; }
public List<ReleaseLabel> ReleaseLabels { get; set; }
public Label()
{
this.Status = Statuses.Ok;
}
}
}

View file

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Roadie.Library.Encoding
{
public class DummyHttpEncoder : IHttpEncoder
{
public string HtmlEncode(string s)
{
return s;
}
public string UrlDecode(string s)
{
return s;
}
public string UrlEncode(string s)
{
return s;
}
public string UrlEncodeBase64(byte[] input)
{
throw new NotImplementedException();
}
public string UrlEncodeBase64(string input)
{
throw new NotImplementedException();
}
}
}

View file

@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
@ -31,6 +30,7 @@ namespace Roadie.Library.Engines
public class ArtistLookupEngine : LookupEngineBase, IArtistLookupEngine
{
private List<int> _addedArtistIds = new List<int>();
public IEnumerable<int> AddedArtistIds
{
get
@ -38,18 +38,17 @@ namespace Roadie.Library.Engines
return this._addedArtistIds;
}
}
public IArtistSearchEngine ITunesArtistSearchEngine { get; }
public IArtistSearchEngine DiscogsArtistSearchEngine { get; }
public IArtistSearchEngine ITunesArtistSearchEngine { get; }
public IArtistSearchEngine LastFmArtistSearchEngine { get; }
public IArtistSearchEngine MusicBrainzArtistSearchEngine { get; }
public IArtistSearchEngine SpotifyArtistSearchEngine { get; }
public IArtistSearchEngine WikipediaArtistSearchEngine { get; }
public ArtistLookupEngine(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context, ICacheManager cacheManager, ILogger logger)
: base(configuration, httpEncoder, context, cacheManager, logger)
{
this.ITunesArtistSearchEngine = new ITunesSearchEngine(this.Configuration, this.CacheManager, this.Logger);
this.MusicBrainzArtistSearchEngine = new musicbrainz.MusicBrainzProvider(this.Configuration, this.CacheManager, this.Logger);
this.LastFmArtistSearchEngine = new lastfm.LastFmHelper(this.Configuration, this.CacheManager, this.Logger);
@ -157,6 +156,46 @@ namespace Roadie.Library.Engines
};
}
public Artist DatabaseQueryForArtistName(string name, string sortName = null)
{
if (string.IsNullOrEmpty(name))
{
return null;
}
try
{
var searchName = name.NormalizeName();
var searchSortName = !string.IsNullOrEmpty(sortName) ? sortName.NormalizeName().ToLower() : searchName;
var specialSearchName = name.ToAlphanumericName();
var searchNameStart = $"{ searchName }|";
var searchNameIn = $"|{ searchName }|";
var searchNameEnd = $"|{ searchName }";
var specialSearchNameStart = $"{ specialSearchName }|";
var specialSearchNameIn = $"|{ specialSearchName }|";
var specialSearchNameEnd = $"|{ specialSearchName }";
return (from a in this.DbContext.Artists
where (a.Name == searchName ||
a.SortName == searchName ||
a.SortName == searchSortName ||
a.AlternateNames.StartsWith(searchNameStart) ||
a.AlternateNames.Contains(searchNameIn) ||
a.AlternateNames.EndsWith(searchNameEnd) ||
a.AlternateNames.StartsWith(specialSearchNameStart) ||
a.AlternateNames.Contains(specialSearchNameIn) ||
a.AlternateNames.EndsWith(specialSearchNameEnd))
select a
).FirstOrDefault();
}
catch (Exception ex)
{
this.Logger.LogError(ex, ex.Serialize());
}
return null;
}
public async Task<OperationResult<Artist>> GetByName(AudioMetaData metaData, bool doFindIfNotInDatabase = false)
{
try
@ -188,7 +227,7 @@ namespace Roadie.Library.Engines
// See if roadie.json file exists in the metadata files folder, if so then use artist data from that
var releaseRoadieDataFilename = Path.Combine(Path.GetDirectoryName(metaData.Filename), "roadie.artist.json");
if(File.Exists(releaseRoadieDataFilename))
if (File.Exists(releaseRoadieDataFilename))
{
artist = JsonConvert.DeserializeObject<Artist>(File.ReadAllText(releaseRoadieDataFilename));
var addResult = await this.Add(artist);
@ -240,7 +279,6 @@ namespace Roadie.Library.Engines
this.Logger.LogError(ex, ex.Serialize());
}
}
}
}
if (artist != null && artist.IsValid)
@ -694,31 +732,5 @@ namespace Roadie.Library.Engines
OperationTime = sw.ElapsedMilliseconds
};
}
private Artist DatabaseQueryForArtistName(string name, string sortName = null)
{
if (string.IsNullOrEmpty(name))
{
return null;
}
try
{
var searchName = name.NormalizeName();
var specialSearchName = name.ToAlphanumericName();
return (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
).FirstOrDefault();
}
catch (Exception ex)
{
this.Logger.LogError(ex, ex.Serialize());
}
return null;
}
}
}

View file

@ -20,5 +20,6 @@ namespace Roadie.Library.Engines
Task<OperationResult<Artist>> Add(Artist artist);
Task<OperationResult<Artist>> GetByName(AudioMetaData metaData, bool doFindIfNotInDatabase = false);
Task<OperationResult<Artist>> PerformMetaDataProvidersArtistSearch(AudioMetaData metaData);
Artist DatabaseQueryForArtistName(string name, string sortName = null);
}
}

View file

@ -31,6 +31,9 @@ namespace Roadie.Library.Models
public Image MediumThumbnail { get; set; }
// When populated a "data:image" base64 byte array of an image to use as new Thumbnail
public string NewThumbnailData { get; set; }
public ReleaseGroupingStatistics Statistics { get; set; }
public decimal? Duration { get; set; }

View file

@ -1,4 +1,5 @@
using Roadie.Library;
using Microsoft.AspNetCore.Http;
using Roadie.Library;
using Roadie.Library.Models;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users;
@ -13,5 +14,11 @@ namespace Roadie.Api.Services
Task<OperationResult<Label>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<PagedResult<LabelList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false);
Task<OperationResult<Image>> SetLabelImageByUrl(User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateLabel(User user, Label label);
Task<OperationResult<Image>> UploadLabelImage(User user, Guid id, IFormFile file);
}
}

View file

@ -1,4 +1,5 @@
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Roadie.Library;
@ -7,6 +8,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.Users;
@ -14,6 +16,7 @@ using Roadie.Library.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
@ -130,6 +133,122 @@ namespace Roadie.Api.Services
});
}
public async Task<OperationResult<Image>> SetLabelImageByUrl(User user, Guid id, string imageUrl)
{
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();
sw.Start();
var errors = new List<Exception>();
var label = this.DbContext.Labels.FirstOrDefault(x => x.RoadieId == model.Id);
if (label == null)
{
return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", model.Id));
}
try
{
var now = DateTime.UtcNow;
label.AlternateNames = model.AlternateNamesList.ToDelimitedList();
label.BeginDate = model.BeginDate;
label.DiscogsId = model.DiscogsId;
label.EndDate = model.EndDate;
label.IsLocked = model.IsLocked;
label.MusicBrainzId = model.MusicBrainzId;
label.Name = model.Name;
label.Profile = model.Profile;
label.SortName = model.SortName;
label.Status = SafeParser.ToEnum<Statuses>(model.Status);
label.Tags = model.TagsList.ToDelimitedList();
label.URLs = model.URLsList.ToDelimitedList();
var labelImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (labelImage != null)
{
// Ensure is jpeg first
label.Thumbnail = ImageHelper.ConvertToJpegFormat(labelImage);
// 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($"UpdateLabel `{ label }` 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<Image>> UploadLabelImage(User user, Guid id, IFormFile file)
{
var bytes = new byte[0];
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
bytes = ms.ToArray();
}
return await this.SaveImageBytes(user, id, bytes);
}
private Task<OperationResult<Label>> LabelByIdAction(Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();

View file

@ -256,5 +256,22 @@ namespace Roadie.Api.Controllers
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

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -10,6 +11,7 @@ using Roadie.Library.Models.Pagination;
using System;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using models = Roadie.Library.Models;
namespace Roadie.Api.Controllers
@ -59,5 +61,63 @@ 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);
}
[HttpPost("setImageByUrl/{id}/{imageUrl}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> SetLabelImageByUrl(Guid id, string imageUrl)
{
var result = await this.LabelService.SetLabelImageByUrl(await this.CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl));
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("edit")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[Authorize(Policy = "Editor")]
public async Task<IActionResult> Update(models.Label label)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await this.LabelService.UpdateLabel(await this.CurrentUserModel(), label);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
}
}

View file

@ -140,14 +140,10 @@ namespace Roadie.Api
settings.ContentPath = hostingEnvironment.WebRootPath;
settings.ConnectionString = this._configuration.GetConnectionString("RoadieDatabaseConnection");
// This is so 'User Secrets' can be used in Debugging
var integrationKeys = this._configuration.GetSection("IntegrationKeys")
.Get<IntegrationKey>();
if(integrationKeys == null)
{
Console.WriteLine("Unable to find IntegrationKeys, Integrations will not have proper API keys setup.");
}
else if (integrationKeys != null)
if (integrationKeys != null)
{
settings.Integrations.ApiKeys = new System.Collections.Generic.List<ApiKey>
{