Performance improvements, async work, and formatting with CodeRush.

This commit is contained in:
Steven Hildreth 2020-06-07 17:46:24 -05:00
parent 625fdf7266
commit 3cfd12a330
111 changed files with 6249 additions and 5370 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
[*.cs]
# Default severity for analyzer diagnostics with category 'Style'
dotnet_analyzer_diagnostic.category-Style.severity = none

View file

@ -29,7 +29,13 @@ namespace Inspector
public static int Main(string[] args) => CommandLineApplication.Execute<Program>(args); public static int Main(string[] args) => CommandLineApplication.Execute<Program>(args);
#pragma warning disable RCS1213 // Remove unused member declaration.
#pragma warning disable IDE0051 // Remove unused private members
#pragma warning disable CRR0026
private void OnExecute() private void OnExecute()
#pragma warning restore IDE0051 // Remove unused private members
#pragma warning restore RCS1213 // Remove unused member declaration.
#pragma warning restore CRR0026
{ {
var inspector = new Roadie.Library.Inspect.Inspector(); var inspector = new Roadie.Library.Inspect.Inspector();
inspector.Inspect(DoCopy, IsReadOnly, Folder, Destination ?? Folder, DontAppendSubFolder, IsReadOnly ? true : DontDeleteEmptyFolders, DontRunPreScript); inspector.Inspect(DoCopy, IsReadOnly, Folder, Destination ?? Folder, DontAppendSubFolder, IsReadOnly ? true : DontDeleteEmptyFolders, DontRunPreScript);

View file

@ -6,9 +6,9 @@ namespace Roadie.Api.Hubs
{ {
public class PlayActivityHub : Hub public class PlayActivityHub : Hub
{ {
public async Task SendActivity(PlayActivityList playActivity) public Task SendActivityAsync(PlayActivityList playActivity, System.Threading.CancellationToken cancellationToken)
{ {
await Clients.All.SendAsync("PlayActivity", playActivity); return Clients.All.SendAsync("PlayActivity", playActivity, cancellationToken);
} }
} }
} }

View file

@ -5,9 +5,9 @@ namespace Roadie.Api.Hubs
{ {
public class ScanActivityHub : Hub public class ScanActivityHub : Hub
{ {
public async Task SendSystemActivity(string scanActivity) public Task SendSystemActivityAsync(string scanActivity, System.Threading.CancellationToken cancellationToken)
{ {
await Clients.All.SendAsync("SendSystemActivity", scanActivity); return Clients.All.SendAsync("SendSystemActivity", scanActivity, cancellationToken);
} }
} }
} }

View file

@ -26,7 +26,7 @@ namespace Roadie.Library.Tests
{ {
get get
{ {
return this.MessageLogger as ILogger; return MessageLogger as ILogger;
} }
} }
@ -38,17 +38,17 @@ namespace Roadie.Library.Tests
public ArtistLookupEngineTests() public ArtistLookupEngineTests()
{ {
this.MessageLogger = new EventMessageLogger<ArtistLookupEngineTests>(); MessageLogger = new EventMessageLogger<ArtistLookupEngineTests>();
this.MessageLogger.Messages += MessageLogger_Messages; MessageLogger.Messages += MessageLogger_Messages;
var settings = new RoadieSettings(); var settings = new RoadieSettings();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("appsettings.test.json"); configurationBuilder.AddJsonFile("appsettings.test.json");
IConfiguration configuration = configurationBuilder.Build(); IConfiguration configuration = configurationBuilder.Build();
configuration.GetSection("RoadieSettings").Bind(settings); configuration.GetSection("RoadieSettings").Bind(settings);
this.Configuration = settings; Configuration = settings;
this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
this.HttpEncoder = new Encoding.DummyHttpEncoder(); HttpEncoder = new Encoding.DummyHttpEncoder();
} }
private void MessageLogger_Messages(object sender, EventMessage e) private void MessageLogger_Messages(object sender, EventMessage e)

View file

@ -18,37 +18,30 @@ namespace Roadie.Library.Tests
return config; return config;
} }
private readonly IRoadieSettings _settings = null; private readonly IRoadieSettings _settings;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private IConfiguration Configuration
{
get
{
return this._configuration;
}
}
private IRoadieSettings Settings private IRoadieSettings Settings
{ {
get get
{ {
return this._settings; return _settings;
} }
} }
public ConfigurationTests() public ConfigurationTests()
{ {
this._configuration = InitConfiguration(); _configuration = InitConfiguration();
this._settings = new RoadieSettings(); _settings = new RoadieSettings();
this._configuration.GetSection("RoadieSettings").Bind(this._settings); _configuration.GetSection("RoadieSettings").Bind(_settings);
} }
[Fact] [Fact]
public void LoadRootLevelConfiguration() public void LoadRootLevelConfiguration()
{ {
var inboundFolder = @"C:\roadie_dev_root\inbound"; var inboundFolder = @"C:\roadie_dev_root\inbound";
var configInboundFolder = this.Settings.InboundFolder; var configInboundFolder = Settings.InboundFolder;
Assert.Equal(inboundFolder, configInboundFolder); Assert.Equal(inboundFolder, configInboundFolder);
} }

View file

@ -220,8 +220,8 @@ namespace Roadie.Library.Tests
var lastTrack = shuffledTracks.First(); var lastTrack = shuffledTracks.First();
foreach(var track in shuffledTracks.Skip(1)) foreach(var track in shuffledTracks.Skip(1))
{ {
Assert.False(track.Artist.Artist.Text == lastTrack.Artist.Artist.Text && Assert.False(string.Equals(track.Artist.Artist.Text, lastTrack.Artist.Artist.Text, StringComparison.Ordinal) &&
track.Release.Release.Text == lastTrack.Release.Release.Text); string.Equals(track.Release.Release.Text, lastTrack.Release.Release.Text, StringComparison.Ordinal));
lastTrack = track; lastTrack = track;
} }

View file

@ -17,7 +17,7 @@ namespace Roadie.Library.Tests
configurationBuilder.AddJsonFile("appsettings.test.json"); configurationBuilder.AddJsonFile("appsettings.test.json");
IConfiguration configuration = configurationBuilder.Build(); IConfiguration configuration = configurationBuilder.Build();
configuration.GetSection("RoadieSettings").Bind(settings); configuration.GetSection("RoadieSettings").Bind(settings);
this.Configuration = settings; Configuration = settings;
} }
[Theory] [Theory]

View file

@ -21,7 +21,7 @@ namespace Roadie.Library.Tests
{ {
get get
{ {
return this.MessageLogger as ILogger; return MessageLogger as ILogger;
} }
} }
@ -32,8 +32,8 @@ namespace Roadie.Library.Tests
public ID3TagsHelperTests() public ID3TagsHelperTests()
{ {
this.MessageLogger = new EventMessageLogger<ID3TagsHelperTests>(); MessageLogger = new EventMessageLogger<ID3TagsHelperTests>();
this.MessageLogger.Messages += MessageLoggerMessages; MessageLogger.Messages += MessageLoggerMessages;
var settings = new RoadieSettings(); var settings = new RoadieSettings();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
@ -41,11 +41,11 @@ namespace Roadie.Library.Tests
IConfiguration configuration = configurationBuilder.Build(); IConfiguration configuration = configurationBuilder.Build();
configuration.GetSection("RoadieSettings").Bind(settings); configuration.GetSection("RoadieSettings").Bind(settings);
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection"); settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
this.Configuration = settings; Configuration = settings;
this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>(); var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>();
tagHelperLooper.Messages += MessageLoggerMessages; tagHelperLooper.Messages += MessageLoggerMessages;
this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, tagHelperLooper); TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper);
} }
private void MessageLoggerMessages(object sender, EventMessage e) private void MessageLoggerMessages(object sender, EventMessage e)
@ -384,7 +384,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"C:\roadie_dev_root\mp3_tests\01 O.P.D. (Obsessive Personality Disorder).mp3"); var file = new FileInfo(@"C:\roadie_dev_root\mp3_tests\01 O.P.D. (Obsessive Personality Disorder).mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName, true); var tagLib = TagsHelper.MetaDataForFile(file.FullName, true);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -410,7 +410,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"C:\roadie_dev_root\library\Dream Theater\[2016] The Astonishing\01 2285 Entr acte.mp3"); var file = new FileInfo(@"C:\roadie_dev_root\library\Dream Theater\[2016] The Astonishing\01 2285 Entr acte.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -436,7 +436,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"E:\Roadie_Test_Files\13-anna_kendrick-true_colors-a57e270d\01-justin_timberlake-hair_up-ef53c026.mp3"); var file = new FileInfo(@"E:\Roadie_Test_Files\13-anna_kendrick-true_colors-a57e270d\01-justin_timberlake-hair_up-ef53c026.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -473,7 +473,7 @@ namespace Roadie.Library.Tests
var folderFiles = Directory.GetFiles(folderName, "*.mp3", SearchOption.AllDirectories); var folderFiles = Directory.GetFiles(folderName, "*.mp3", SearchOption.AllDirectories);
foreach(var file in folderFiles) foreach(var file in folderFiles)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file); var tagLib = TagsHelper.MetaDataForFile(file);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -493,7 +493,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"E:\Roadie_Test_Files\01 01 Angel Of Death.mp3"); var file = new FileInfo(@"E:\Roadie_Test_Files\01 01 Angel Of Death.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -519,7 +519,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"E:\Roadie_Test_Files\06 You'Re Sensational.mp3"); var file = new FileInfo(@"E:\Roadie_Test_Files\06 You'Re Sensational.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -545,7 +545,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"M:\inbound\MEGAPACK ---METAL-DEATH-BLACK---\ebony_tears-evil_as_hell-2001-ss\01-deviation-ss.mp3"); var file = new FileInfo(@"M:\inbound\MEGAPACK ---METAL-DEATH-BLACK---\ebony_tears-evil_as_hell-2001-ss\01-deviation-ss.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -571,7 +571,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"M:\unknown\2eec19bd-3575-4b7f-84dd-db2a0ec3e2f3~[2009] Dolly - Disc 1 Of 4~06 Nobody But You (Previously Unissued).mp3"); var file = new FileInfo(@"M:\unknown\2eec19bd-3575-4b7f-84dd-db2a0ec3e2f3~[2009] Dolly - Disc 1 Of 4~06 Nobody But You (Previously Unissued).mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -598,7 +598,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"M:\library_old\Perverse\[2014] Champion Dub\01 Champion Dub (Original Mix).mp3"); var file = new FileInfo(@"M:\library_old\Perverse\[2014] Champion Dub\01 Champion Dub (Original Mix).mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -625,7 +625,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"C:\roadie_dev_root\inbound\Dreadful Fate - Vengeance (2018)\01-dreadful_fate-vengeance.mp3"); var file = new FileInfo(@"C:\roadie_dev_root\inbound\Dreadful Fate - Vengeance (2018)\01-dreadful_fate-vengeance.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -653,7 +653,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"M:\library\Blind Melon\[1992] Blind Melon\01. Blind Melon - Soak The Sin.mp3"); var file = new FileInfo(@"M:\library\Blind Melon\[1992] Blind Melon\01. Blind Melon - Soak The Sin.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -680,7 +680,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"E:\Roadie_Test_Files\Test.mp3"); var file = new FileInfo(@"E:\Roadie_Test_Files\Test.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -714,14 +714,14 @@ namespace Roadie.Library.Tests
short trackNumber = 15; short trackNumber = 15;
var numberOfTracks = 25; var numberOfTracks = 25;
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
metaData.TrackNumber = trackNumber; metaData.TrackNumber = trackNumber;
metaData.TotalTrackNumbers = numberOfTracks; metaData.TotalTrackNumbers = numberOfTracks;
this.TagsHelper.WriteTags(metaData, file.FullName); TagsHelper.WriteTags(metaData, file.FullName);
var tagLibAfterWrite = this.TagsHelper.MetaDataForFile(file.FullName); var tagLibAfterWrite = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
Assert.Equal(metaData.Artist, tagLibAfterWrite.Data.Artist); Assert.Equal(metaData.Artist, tagLibAfterWrite.Data.Artist);
Assert.Equal(metaData.Release, tagLibAfterWrite.Data.Release); Assert.Equal(metaData.Release, tagLibAfterWrite.Data.Release);
@ -745,7 +745,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"M:\library\Denver, John\[1972] Aerie\10 Readjustment Blues.mp3"); var file = new FileInfo(@"M:\library\Denver, John\[1972] Aerie\10 Readjustment Blues.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.True(metaData.IsValid); Assert.True(metaData.IsValid);
@ -772,7 +772,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"E:\Roadie_Test_Files\01. What's Yesterday.mp3"); var file = new FileInfo(@"E:\Roadie_Test_Files\01. What's Yesterday.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -799,7 +799,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"M:\library\Ac Dc\[1975] T.N.T\01 It'S A Long Way To The Top (If You Wanna Rock 'N' Roll).mp3"); var file = new FileInfo(@"M:\library\Ac Dc\[1975] T.N.T\01 It'S A Long Way To The Top (If You Wanna Rock 'N' Roll).mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -825,7 +825,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"E:\Roadie_Test_Files\01 Martian.mp3"); var file = new FileInfo(@"E:\Roadie_Test_Files\01 Martian.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -851,7 +851,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"C:\roadie_dev_root\inbound\[2016] Invention Of Knowledge\01 Invention.mp3"); var file = new FileInfo(@"C:\roadie_dev_root\inbound\[2016] Invention Of Knowledge\01 Invention.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -877,7 +877,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"C:\roadie_dev_root\1985 - Sacred Heart\Dio - Sacred Heart (1).mp3"); var file = new FileInfo(@"C:\roadie_dev_root\1985 - Sacred Heart\Dio - Sacred Heart (1).mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -903,7 +903,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"C:\roadie_dev_root\Grift\2017 Arvet\01 - Flyktfast.mp3"); var file = new FileInfo(@"C:\roadie_dev_root\Grift\2017 Arvet\01 - Flyktfast.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);
@ -929,7 +929,7 @@ namespace Roadie.Library.Tests
var file = new FileInfo(@"C:\roadie_dev_root\Distorted Harmony - A Way Out - 2018\kWlZr0N_o72dwo0_CD001_0001.mp3"); var file = new FileInfo(@"C:\roadie_dev_root\Distorted Harmony - A Way Out - 2018\kWlZr0N_o72dwo0_CD001_0001.mp3");
if (file.Exists) if (file.Exists)
{ {
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName); var tagLib = TagsHelper.MetaDataForFile(file.FullName);
Assert.True(tagLib.IsSuccess); Assert.True(tagLib.IsSuccess);
var metaData = tagLib.Data; var metaData = tagLib.Data;
Assert.NotNull(metaData.Artist); Assert.NotNull(metaData.Artist);

View file

@ -1,12 +1,5 @@
using Microsoft.EntityFrameworkCore; using Roadie.Library.Imaging;
using Microsoft.Extensions.Configuration;
using Roadie.Library.Configuration;
using Roadie.Library.Data;
using Roadie.Library.Data.Context;
using Roadie.Library.FilePlugins;
using Roadie.Library.Imaging;
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Xunit; using Xunit;
@ -38,6 +31,9 @@ namespace Roadie.Library.Tests
[InlineData("logo.png")] [InlineData("logo.png")]
[InlineData("Logo.Jpg")] [InlineData("Logo.Jpg")]
[InlineData("logo.gif")] [InlineData("logo.gif")]
[InlineData("band_logo.gif")]
[InlineData("134logo.gif")]
[InlineData("Dream_Theater_Logo.gif")]
[InlineData("artist_logo.jpg")] [InlineData("artist_logo.jpg")]
[InlineData("Artist_logo.jpg")] [InlineData("Artist_logo.jpg")]
[InlineData("ARTIST_LOGO.JPG")] [InlineData("ARTIST_LOGO.JPG")]
@ -292,7 +288,7 @@ namespace Roadie.Library.Tests
public void GetReleaseImageInFolder() public void GetReleaseImageInFolder()
{ {
var folder = new DirectoryInfo(@"C:\roadie_dev_root\image_tests"); var folder = new DirectoryInfo(@"C:\roadie_dev_root\image_tests");
if(!folder.Exists) if (!folder.Exists)
{ {
Assert.True(true); Assert.True(true);
return; return;

View file

@ -23,7 +23,7 @@ namespace Roadie.Library.Tests
{ {
get get
{ {
return this.MessageLogger as ILogger; return MessageLogger as ILogger;
} }
} }
@ -35,8 +35,8 @@ namespace Roadie.Library.Tests
public InspectorTests() public InspectorTests()
{ {
this.MessageLogger = new EventMessageLogger<ID3TagsHelperTests>(); MessageLogger = new EventMessageLogger<ID3TagsHelperTests>();
this.MessageLogger.Messages += MessageLoggerMessages; MessageLogger.Messages += MessageLoggerMessages;
var settings = new configuration.RoadieSettings(); var settings = new configuration.RoadieSettings();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
@ -44,11 +44,11 @@ namespace Roadie.Library.Tests
IConfiguration configuration = configurationBuilder.Build(); IConfiguration configuration = configurationBuilder.Build();
configuration.GetSection("RoadieSettings").Bind(settings); configuration.GetSection("RoadieSettings").Bind(settings);
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection"); settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
this.Configuration = settings; Configuration = settings;
this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4))); CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>(); var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>();
tagHelperLooper.Messages += MessageLoggerMessages; tagHelperLooper.Messages += MessageLoggerMessages;
this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, tagHelperLooper); TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper);
} }
private void MessageLoggerMessages(object sender, EventMessage e) private void MessageLoggerMessages(object sender, EventMessage e)

View file

@ -6,10 +6,8 @@ using Roadie.Library.MetaData.MusicBrainz;
using Roadie.Library.Processors; using Roadie.Library.Processors;
using Roadie.Library.SearchEngines.MetaData.Discogs; using Roadie.Library.SearchEngines.MetaData.Discogs;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
@ -50,7 +48,7 @@ namespace Roadie.Library.Tests
[Fact] [Fact]
public async Task DiscogsHelperReleaseSearch() public async Task DiscogsHelperReleaseSearch()
{ {
if(!Configuration.Integrations.DiscogsProviderEnabled) if (!Configuration.Integrations.DiscogsProviderEnabled)
{ {
return; return;
} }
@ -62,7 +60,7 @@ namespace Roadie.Library.Tests
var artistName = "With The Dead"; var artistName = "With The Dead";
var title = "Love From With The Dead"; var title = "Love From With The Dead";
var result = await engine.PerformReleaseSearch(artistName, title, 1); var result = await engine.PerformReleaseSearch(artistName, title, 1).ConfigureAwait(false);
Assert.NotNull(result); Assert.NotNull(result);
Assert.NotEmpty(result.Data); Assert.NotEmpty(result.Data);
@ -84,10 +82,9 @@ namespace Roadie.Library.Tests
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = await mb.PerformArtistSearch(artistName, 1); var result = await mb.PerformArtistSearchAsync(artistName, 1).ConfigureAwait(false);
sw.Stop(); sw.Stop();
var elapsedTime = sw.ElapsedMilliseconds;
Assert.NotNull(result); Assert.NotNull(result);
Assert.NotNull(result.Data); Assert.NotNull(result.Data);
@ -113,10 +110,9 @@ namespace Roadie.Library.Tests
var title = "Piano Man"; var title = "Piano Man";
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = await mb.PerformReleaseSearch(artistName, title, 1); var result = await mb.PerformReleaseSearch(artistName, title, 1).ConfigureAwait(false);
sw.Stop(); sw.Stop();
var elapsedTime = sw.ElapsedMilliseconds;
Assert.NotNull(result); Assert.NotNull(result);
Assert.NotNull(result.Data); Assert.NotNull(result.Data);

View file

@ -12,21 +12,21 @@ namespace Roadie.Library.Tests
{ {
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IRoadieSettings _settings = null; private readonly IRoadieSettings _settings;
private IRoadieSettings Configuration private IRoadieSettings Configuration
{ {
get get
{ {
return this._settings; return _settings;
} }
} }
public StringExtensionTests() public StringExtensionTests()
{ {
this._configuration = InitConfiguration(); _configuration = InitConfiguration();
this._settings = new RoadieSettings(); _settings = new RoadieSettings();
this._configuration.GetSection("RoadieSettings").Bind(this._settings); _configuration.GetSection("RoadieSettings").Bind(_settings);
} }
public static IConfiguration InitConfiguration() public static IConfiguration InitConfiguration()
@ -102,7 +102,7 @@ namespace Roadie.Library.Tests
public void CleanStringReleaseShouldBeAngie(string input) public void CleanStringReleaseShouldBeAngie(string input)
{ {
var r = @"(\s*(-\s)*((CD[_\-#\s]*[0-9]*)))|((\(|\[)+([0-9]|,|self|bonus|re(leas|master|(e|d)*)*|th|anniversary|cd|disc|deluxe|dig(ipack)*|vinyl|japan(ese)*|asian|remastered|limited|ltd|expanded|edition|\s)+(]|\)*))"; var r = @"(\s*(-\s)*((CD[_\-#\s]*[0-9]*)))|((\(|\[)+([0-9]|,|self|bonus|re(leas|master|(e|d)*)*|th|anniversary|cd|disc|deluxe|dig(ipack)*|vinyl|japan(ese)*|asian|remastered|limited|ltd|expanded|edition|\s)+(]|\)*))";
var cleaned = input.CleanString(this.Configuration, r); var cleaned = input.CleanString(Configuration, r);
Assert.Equal("Angie", cleaned); Assert.Equal("Angie", cleaned);
} }

View file

@ -2,6 +2,7 @@
"RoadieSettings": { "RoadieSettings": {
"InboundFolder": "C:\\roadie_dev_root\\inbound", "InboundFolder": "C:\\roadie_dev_root\\inbound",
"LibraryFolder": "C:\\\\roadie_dev_root\\\\library", "LibraryFolder": "C:\\\\roadie_dev_root\\\\library",
"SearchEngineReposFolder": "C:\\\\roadie_dev_root\\\\repos",
"Processing": { "Processing": {
"RemoveStringsRegex": "\\b[0-9]+\\s#\\s\\b", "RemoveStringsRegex": "\\b[0-9]+\\s#\\s\\b",
"ReleaseRemoveStringsRegex": "(\\\\s*(-\\\\s)*((CD[_\\-#\\s]*[0-9]*)))|((\\\\(|\\\\[)+([0-9]|,|self|bonus|re(leas|master|(e|d)*)*|th|anniversary|cd|disc|deluxe|dig(ipack)*|vinyl|japan(ese)*|asian|remastered|limited|ltd|expanded|edition|\\\\s)+(]|\\\\)*))", "ReleaseRemoveStringsRegex": "(\\\\s*(-\\\\s)*((CD[_\\-#\\s]*[0-9]*)))|((\\\\(|\\\\[)+([0-9]|,|self|bonus|re(leas|master|(e|d)*)*|th|anniversary|cd|disc|deluxe|dig(ipack)*|vinyl|japan(ese)*|asian|remastered|limited|ltd|expanded|edition|\\\\s)+(]|\\\\)*))",

View file

@ -113,7 +113,7 @@ namespace Roadie.Library.Caching
var r = Get<TOut>(key, region); var r = Get<TOut>(key, region);
if (r == null) if (r == null)
{ {
r = await getItem(); r = await getItem().ConfigureAwait(false);
Add(key, r, region); Add(key, r, region);
Logger.LogTrace($"-+> Cache Miss for Key [{key}], Region [{region}]"); Logger.LogTrace($"-+> Cache Miss for Key [{key}], Region [{region}]");
} }

View file

@ -95,7 +95,7 @@ namespace Roadie.Library.Caching
var r = Get<TOut>(key, region); var r = Get<TOut>(key, region);
if (r == null) if (r == null)
{ {
r = await getItem(); r = await getItem().ConfigureAwait(false);
Add(key, r, region); Add(key, r, region);
Logger.LogTrace($"-+> Cache Miss for Key [{key}], Region [{region}]"); Logger.LogTrace($"-+> Cache Miss for Key [{key}], Region [{region}]");
} }

View file

@ -5,14 +5,14 @@ namespace Roadie.Library.Data.Context
{ {
public interface IRoadieDbRandomizer public interface IRoadieDbRandomizer
{ {
Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); Task<SortedDictionary<int, int>> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); Task<SortedDictionary<int, int>> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); Task<SortedDictionary<int, int>> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); Task<SortedDictionary<int, int>> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); Task<SortedDictionary<int, int>> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
} }
} }

View file

@ -16,9 +16,9 @@ namespace Roadie.Library.Data.Context.Implementation
{ {
} }
public override async Task<Artist> LastPlayedArtist(int userId) public override Task<Artist> LastPlayedArtist(int userId)
{ {
return await (from ut in UserTracks return (from ut in UserTracks
join t in Tracks on ut.TrackId equals t.Id join t in Tracks on ut.TrackId equals t.Id
join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in Releases on rm.ReleaseId equals r.Id join r in Releases on rm.ReleaseId equals r.Id
@ -28,9 +28,9 @@ namespace Roadie.Library.Data.Context.Implementation
select a).FirstOrDefaultAsync(); select a).FirstOrDefaultAsync();
} }
public override async Task<Release> LastPlayedRelease(int userId) public override Task<Release> LastPlayedRelease(int userId)
{ {
return await (from ut in UserTracks return (from ut in UserTracks
join t in Tracks on ut.TrackId equals t.Id join t in Tracks on ut.TrackId equals t.Id
join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in Releases on rm.ReleaseId equals r.Id join r in Releases on rm.ReleaseId equals r.Id
@ -39,9 +39,9 @@ namespace Roadie.Library.Data.Context.Implementation
select r).FirstOrDefaultAsync(); select r).FirstOrDefaultAsync();
} }
public override async Task<Track> LastPlayedTrack(int userId) public override Task<Track> LastPlayedTrack(int userId)
{ {
return await (from ut in UserTracks return (from ut in UserTracks
join t in Tracks on ut.TrackId equals t.Id join t in Tracks on ut.TrackId equals t.Id
where ut.UserId == userId where ut.UserId == userId
orderby ut.LastPlayed descending orderby ut.LastPlayed descending
@ -50,7 +50,7 @@ namespace Roadie.Library.Data.Context.Implementation
public override async Task<Artist> MostPlayedArtist(int userId) public override async Task<Artist> MostPlayedArtist(int userId)
{ {
var mostPlayedTrack = await MostPlayedTrack(userId); var mostPlayedTrack = await MostPlayedTrack(userId).ConfigureAwait(false);
if (mostPlayedTrack != null) if (mostPlayedTrack != null)
{ {
return await (from t in Tracks return await (from t in Tracks
@ -58,35 +58,35 @@ namespace Roadie.Library.Data.Context.Implementation
join r in Releases on rm.ReleaseId equals r.Id join r in Releases on rm.ReleaseId equals r.Id
join a in Artists on r.ArtistId equals a.Id join a in Artists on r.ArtistId equals a.Id
where t.Id == mostPlayedTrack.Id where t.Id == mostPlayedTrack.Id
select a).FirstOrDefaultAsync(); select a).FirstOrDefaultAsync().ConfigureAwait(false);
} }
return null; return null;
} }
public override async Task<Release> MostPlayedRelease(int userId) public override async Task<Release> MostPlayedRelease(int userId)
{ {
var mostPlayedTrack = await MostPlayedTrack(userId); var mostPlayedTrack = await MostPlayedTrack(userId).ConfigureAwait(false);
if (mostPlayedTrack != null) if (mostPlayedTrack != null)
{ {
return await (from t in Tracks return await (from t in Tracks
join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id join rm in ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in Releases on rm.ReleaseId equals r.Id join r in Releases on rm.ReleaseId equals r.Id
where t.Id == mostPlayedTrack.Id where t.Id == mostPlayedTrack.Id
select r).FirstOrDefaultAsync(); select r).FirstOrDefaultAsync().ConfigureAwait(false);
} }
return null; return null;
} }
public override async Task<Track> MostPlayedTrack(int userId) public override Task<Track> MostPlayedTrack(int userId)
{ {
return await (from ut in UserTracks return (from ut in UserTracks
join t in Tracks on ut.TrackId equals t.Id join t in Tracks on ut.TrackId equals t.Id
where ut.UserId == userId where ut.UserId == userId
orderby ut.PlayedCount descending orderby ut.PlayedCount descending
select t).FirstOrDefaultAsync(); select t).FirstOrDefaultAsync();
} }
public override async Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
List<Artist> randomArtists = null; List<Artist> randomArtists = null;
if (doOnlyFavorites) if (doOnlyFavorites)
@ -98,7 +98,7 @@ namespace Roadie.Library.Data.Context.Implementation
select a select a
).OrderBy(x => Guid.NewGuid()) ).OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
else if (doOnlyRated) else if (doOnlyRated)
{ {
@ -109,7 +109,7 @@ namespace Roadie.Library.Data.Context.Implementation
select a select a
).OrderBy(x => Guid.NewGuid()) ).OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
else else
{ {
@ -120,29 +120,29 @@ namespace Roadie.Library.Data.Context.Implementation
select a) select a)
.OrderBy(x => Guid.NewGuid()) .OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
var dict = randomArtists.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); var dict = randomArtists.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var randomGenres = await Genres.OrderBy(x => Guid.NewGuid()) var randomGenres = await Genres.OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
var dict = randomGenres.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); var dict = randomGenres.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var randomLabels = await Labels.OrderBy(x => Guid.NewGuid()).Take(randomLimit).ToListAsync(); var randomLabels = await Labels.OrderBy(x => Guid.NewGuid()).Take(randomLimit).ToListAsync().ConfigureAwait(false);
var dict = randomLabels.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); var dict = randomLabels.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
List<Release> randomReleases = null; List<Release> randomReleases = null;
if (doOnlyFavorites) if (doOnlyFavorites)
@ -154,7 +154,7 @@ namespace Roadie.Library.Data.Context.Implementation
select r select r
).OrderBy(x => Guid.NewGuid()) ).OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
else if (doOnlyRated) else if (doOnlyRated)
{ {
@ -165,7 +165,7 @@ namespace Roadie.Library.Data.Context.Implementation
select r select r
).OrderBy(x => Guid.NewGuid()) ).OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
else else
{ {
@ -176,13 +176,13 @@ namespace Roadie.Library.Data.Context.Implementation
select r) select r)
.OrderBy(x => Guid.NewGuid()) .OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
var dict = randomReleases.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); var dict = randomReleases.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
List<Track> randomTracks = null; List<Track> randomTracks = null;
if (doOnlyFavorites) if (doOnlyFavorites)
@ -194,7 +194,7 @@ namespace Roadie.Library.Data.Context.Implementation
select t select t
).OrderBy(x => Guid.NewGuid()) ).OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
else if (doOnlyRated) else if (doOnlyRated)
{ {
@ -205,7 +205,7 @@ namespace Roadie.Library.Data.Context.Implementation
select t select t
).OrderBy(x => Guid.NewGuid()) ).OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
else else
{ {
@ -216,7 +216,7 @@ namespace Roadie.Library.Data.Context.Implementation
select t) select t)
.OrderBy(x => Guid.NewGuid()) .OrderBy(x => Guid.NewGuid())
.Take(randomLimit) .Take(randomLimit)
.ToListAsync(); .ToListAsync().ConfigureAwait(false);
} }
var dict = randomTracks.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value); var dict = randomTracks.Select((x, i) => new { key = i, value = x.Id }).Take(randomLimit).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);

View file

@ -26,7 +26,7 @@ namespace Roadie.Library.Data.Context.Implementation
where ut.userId = {0} where ut.userId = {0}
ORDER by ut.lastPlayed desc ORDER by ut.lastPlayed desc
LIMIT 1"; LIMIT 1";
return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync(); return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync().ConfigureAwait(false);
} }
public override async Task<Release> LastPlayedRelease(int userId) public override async Task<Release> LastPlayedRelease(int userId)
@ -41,7 +41,8 @@ namespace Roadie.Library.Data.Context.Implementation
LIMIT 1"; LIMIT 1";
return await Releases.FromSqlRaw(sql, userId) return await Releases.FromSqlRaw(sql, userId)
.Include(x => x.Artist) .Include(x => x.Artist)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync()
.ConfigureAwait(false);
} }
public override async Task<Track> LastPlayedTrack(int userId) public override async Task<Track> LastPlayedTrack(int userId)
@ -57,7 +58,8 @@ namespace Roadie.Library.Data.Context.Implementation
.Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia)
.Include("ReleaseMedia.Release") .Include("ReleaseMedia.Release")
.Include("ReleaseMedia.Release.Artist") .Include("ReleaseMedia.Release.Artist")
.FirstOrDefaultAsync(); .FirstOrDefaultAsync()
.ConfigureAwait(false);
} }
public override async Task<Artist> MostPlayedArtist(int userId) public override async Task<Artist> MostPlayedArtist(int userId)
@ -72,7 +74,7 @@ namespace Roadie.Library.Data.Context.Implementation
group by r.id group by r.id
order by SUM(ut.playedCount) desc order by SUM(ut.playedCount) desc
LIMIT 1"; LIMIT 1";
return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync(); return await Artists.FromSqlRaw(sql, userId).FirstOrDefaultAsync().ConfigureAwait(false);
} }
public override async Task<Release> MostPlayedRelease(int userId) public override async Task<Release> MostPlayedRelease(int userId)
@ -88,7 +90,8 @@ namespace Roadie.Library.Data.Context.Implementation
LIMIT 1"; LIMIT 1";
return await Releases.FromSqlRaw(sql, userId) return await Releases.FromSqlRaw(sql, userId)
.Include(x => x.Artist) .Include(x => x.Artist)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync()
.ConfigureAwait(false);
} }
public override async Task<Track> MostPlayedTrack(int userId) public override async Task<Track> MostPlayedTrack(int userId)
@ -105,10 +108,11 @@ namespace Roadie.Library.Data.Context.Implementation
.Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia)
.Include("ReleaseMedia.Release") .Include("ReleaseMedia.Release")
.Include("ReleaseMedia.Release.Artist") .Include("ReleaseMedia.Release.Artist")
.FirstOrDefaultAsync(); .FirstOrDefaultAsync()
.ConfigureAwait(false);
} }
public override async Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var sql = @"SELECT a.id var sql = @"SELECT a.id
FROM `artist` a FROM `artist` a
@ -117,34 +121,34 @@ namespace Roadie.Library.Data.Context.Implementation
AND {2} = 1) AND {2} = 1)
order BY RIGHT(HEX((1 << 24) * (1 + RAND())), 6) order BY RIGHT(HEX((1 << 24) * (1 + RAND())), 6)
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var sql = @"SELECT g.id var sql = @"SELECT g.id
FROM `genre` g FROM `genre` g
ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var sql = @"SELECT l.id var sql = @"SELECT l.id
FROM `label` l FROM `label` l
ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var sql = @"SELECT r.id var sql = @"SELECT r.id
FROM `release` r FROM `release` r
@ -153,12 +157,12 @@ namespace Roadie.Library.Data.Context.Implementation
AND {2} = 1) AND {2} = 1)
ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var sql = @"SELECT t.id var sql = @"SELECT t.id
FROM `track` t FROM `track` t
@ -194,7 +198,7 @@ namespace Roadie.Library.Data.Context.Implementation
WHERE ut.userId = {1} AND ut.isFavorite = 1) AND {2} = 1) OR {2} = 0) WHERE ut.userId = {1} AND ut.isFavorite = 1) AND {2} = 1) OR {2} = 0)
order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6) order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Tracks.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToListAsync(); var ids = await Tracks.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }

View file

@ -22,7 +22,7 @@ namespace Roadie.Library.Data.Context.Implementation
} }
} }
public override async Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
// TODO Rating? // TODO Rating?
var sql = @"SELECT a.id var sql = @"SELECT a.id
@ -32,34 +32,34 @@ namespace Roadie.Library.Data.Context.Implementation
AND {2} = 1) AND {2} = 1)
order by random() order by random()
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var sql = @"SELECT g.id var sql = @"SELECT g.id
FROM genre g FROM genre g
order by random() order by random()
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
var sql = @"SELECT l.id var sql = @"SELECT l.id
FROM label l FROM label l
order by random() order by random()
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
// TODO Rating? // TODO Rating?
var sql = @"SELECT r.id var sql = @"SELECT r.id
@ -69,12 +69,12 @@ namespace Roadie.Library.Data.Context.Implementation
AND {2} = 1) AND {2} = 1)
order by random() order by random()
LIMIT 0, {0}"; LIMIT 0, {0}";
var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync(); var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }
public override async Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false) public override async Task<SortedDictionary<int, int>> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{ {
// When using the regular 'FromSqlRaw' with parameters SQLite returns no records. // When using the regular 'FromSqlRaw' with parameters SQLite returns no records.
@ -103,7 +103,7 @@ namespace Roadie.Library.Data.Context.Implementation
WHERE ut.userId = {userId} AND ut.isFavorite = 1) AND {df} = 1) OR {df} = 0) WHERE ut.userId = {userId} AND ut.isFavorite = 1) AND {df} = 1) OR {df} = 0)
order by random() order by random()
LIMIT 0, {randomLimit}"; LIMIT 0, {randomLimit}";
var ids = await Tracks.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync(); var ids = await Tracks.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync().ConfigureAwait(false);
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value); var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict); return new SortedDictionary<int, int>(dict);
} }

View file

@ -6,15 +6,15 @@ namespace Roadie.Library.Data.Context
{ {
public abstract partial class RoadieDbContext : DbContext, IRoadieDbContext public abstract partial class RoadieDbContext : DbContext, IRoadieDbContext
{ {
public abstract Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); public abstract Task<SortedDictionary<int, int>> RandomArtistIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); public abstract Task<SortedDictionary<int, int>> RandomGenreIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); public abstract Task<SortedDictionary<int, int>> RandomLabelIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); public abstract Task<SortedDictionary<int, int>> RandomReleaseIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false); public abstract Task<SortedDictionary<int, int>> RandomTrackIdsAsync(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<Artist> MostPlayedArtist(int userId); public abstract Task<Artist> MostPlayedArtist(int userId);

View file

@ -349,7 +349,7 @@ namespace Roadie.Library.Engines
if (ITunesArtistSearchEngine.IsEnabled) if (ITunesArtistSearchEngine.IsEnabled)
{ {
var sw2 = Stopwatch.StartNew(); var sw2 = Stopwatch.StartNew();
var iTunesResult = await ITunesArtistSearchEngine.PerformArtistSearch(artistName, 1).ConfigureAwait(false); var iTunesResult = await ITunesArtistSearchEngine.PerformArtistSearchAsync(artistName, 1).ConfigureAwait(false);
if (iTunesResult.IsSuccess) if (iTunesResult.IsSuccess)
{ {
var i = iTunesResult.Data.First(); var i = iTunesResult.Data.First();
@ -406,7 +406,7 @@ namespace Roadie.Library.Engines
if (MusicBrainzArtistSearchEngine.IsEnabled) if (MusicBrainzArtistSearchEngine.IsEnabled)
{ {
var sw2 = Stopwatch.StartNew(); var sw2 = Stopwatch.StartNew();
var mbResult = await MusicBrainzArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); var mbResult = await MusicBrainzArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false);
if (mbResult.IsSuccess) if (mbResult.IsSuccess)
{ {
var mb = mbResult.Data.First(); var mb = mbResult.Data.First();
@ -471,7 +471,7 @@ namespace Roadie.Library.Engines
if (LastFmArtistSearchEngine.IsEnabled) if (LastFmArtistSearchEngine.IsEnabled)
{ {
var sw2 = Stopwatch.StartNew(); var sw2 = Stopwatch.StartNew();
var lastFmResult = await LastFmArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); var lastFmResult = await LastFmArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false);
if (lastFmResult.IsSuccess) if (lastFmResult.IsSuccess)
{ {
var l = lastFmResult.Data.First(); var l = lastFmResult.Data.First();
@ -515,7 +515,7 @@ namespace Roadie.Library.Engines
if (SpotifyArtistSearchEngine.IsEnabled) if (SpotifyArtistSearchEngine.IsEnabled)
{ {
var sw2 = Stopwatch.StartNew(); var sw2 = Stopwatch.StartNew();
var spotifyResult = await SpotifyArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); var spotifyResult = await SpotifyArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false);
if (spotifyResult.IsSuccess) if (spotifyResult.IsSuccess)
{ {
var s = spotifyResult.Data.First(); var s = spotifyResult.Data.First();
@ -557,7 +557,7 @@ namespace Roadie.Library.Engines
if (DiscogsArtistSearchEngine.IsEnabled) if (DiscogsArtistSearchEngine.IsEnabled)
{ {
var sw2 = Stopwatch.StartNew(); var sw2 = Stopwatch.StartNew();
var discogsResult = await DiscogsArtistSearchEngine.PerformArtistSearch(result.Name, 1).ConfigureAwait(false); var discogsResult = await DiscogsArtistSearchEngine.PerformArtistSearchAsync(result.Name, 1).ConfigureAwait(false);
if (discogsResult.IsSuccess) if (discogsResult.IsSuccess)
{ {
var d = discogsResult?.Data?.FirstOrDefault(); var d = discogsResult?.Data?.FirstOrDefault();
@ -614,7 +614,7 @@ namespace Roadie.Library.Engines
{ {
wikiName += " band"; wikiName += " band";
} }
var wikipediaResult = await WikipediaArtistSearchEngine.PerformArtistSearch(wikiName, 1).ConfigureAwait(false); var wikipediaResult = await WikipediaArtistSearchEngine.PerformArtistSearchAsync(wikiName, 1).ConfigureAwait(false);
if (wikipediaResult?.Data != null) if (wikipediaResult?.Data != null)
{ {
if (wikipediaResult.IsSuccess) if (wikipediaResult.IsSuccess)

View file

@ -455,7 +455,7 @@ namespace Roadie.Library.Engines
{ {
var sw2 = Stopwatch.StartNew(); var sw2 = Stopwatch.StartNew();
Logger.LogTrace("ITunesReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title); Logger.LogTrace("ITunesReleaseSearchEngine Release Search for ArtistName [{0}], ReleaseTitle [{1}]", metaData.Artist, result.Title);
var iTunesResult = await ITunesReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1); var iTunesResult = await ITunesReleaseSearchEngine.PerformReleaseSearch(metaData.Artist, result.Title, 1).ConfigureAwait(false);
if (iTunesResult.IsSuccess) if (iTunesResult.IsSuccess)
{ {
var i = iTunesResult.Data.First(); var i = iTunesResult.Data.First();

View file

@ -26,24 +26,23 @@ namespace Roadie.Library.Imaging
public static byte[] ConvertToJpegFormat(byte[] imageBytes) public static byte[] ConvertToJpegFormat(byte[] imageBytes)
{ {
if (imageBytes == null || !imageBytes.Any()) if(imageBytes?.Any() != true)
{ {
return null; return null;
} }
try try
{ {
using (var outStream = new MemoryStream()) using(var outStream = new MemoryStream())
{ {
IImageFormat imageFormat = null; IImageFormat imageFormat = null;
using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
{ {
image.SaveAsJpeg(outStream); image.SaveAsJpeg(outStream);
} }
return outStream.ToArray(); return outStream.ToArray();
} }
} } catch(Exception ex)
catch (Exception ex)
{ {
Trace.WriteLine($"Error Converting Image to Jpg [{ ex.Message }]", "Warning"); Trace.WriteLine($"Error Converting Image to Jpg [{ ex.Message }]", "Warning");
} }
@ -55,44 +54,48 @@ namespace Roadie.Library.Imaging
/// </summary> /// </summary>
public static byte[] ConvertToGifFormat(byte[] imageBytes) public static byte[] ConvertToGifFormat(byte[] imageBytes)
{ {
if (imageBytes == null || !imageBytes.Any()) if(imageBytes?.Any() != true)
{ {
return null; return null;
} }
try try
{ {
using (var outStream = new MemoryStream()) using(var outStream = new MemoryStream())
{ {
IImageFormat imageFormat = null; IImageFormat imageFormat = null;
using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
{ {
image.SaveAsGif(outStream); image.SaveAsGif(outStream);
} }
return outStream.ToArray(); return outStream.ToArray();
} }
} } catch(Exception ex)
catch (Exception ex)
{ {
Trace.WriteLine($"Error Converting Image to Gif [{ ex.Message }]", "Warning"); Trace.WriteLine($"Error Converting Image to Gif [{ ex.Message }]", "Warning");
} }
return null; return null;
} }
public static IEnumerable<FileInfo> FindImagesByName(DirectoryInfo directory, string name, SearchOption folderSearchOptions = SearchOption.AllDirectories) public static IEnumerable<FileInfo> FindImagesByName(DirectoryInfo directory,
string name,
SearchOption folderSearchOptions = SearchOption.AllDirectories)
{ {
var result = new List<FileInfo>(); var result = new List<FileInfo>();
if (directory == null || !directory.Exists || string.IsNullOrEmpty(name)) return result; if(directory?.Exists != true || string.IsNullOrEmpty(name))
return result;
var imageFilesInFolder = ImageFilesInFolder(directory.FullName, folderSearchOptions); var imageFilesInFolder = ImageFilesInFolder(directory.FullName, folderSearchOptions);
if (imageFilesInFolder == null || !imageFilesInFolder.Any()) return result; if(imageFilesInFolder?.Any() != true)
if (imageFilesInFolder.Any()) return result;
if(imageFilesInFolder.Length > 0)
{ {
name = name.ToAlphanumericName(); name = name.ToAlphanumericName();
foreach (var imageFileInFolder in imageFilesInFolder) foreach(var imageFileInFolder in imageFilesInFolder)
{ {
var image = new FileInfo(imageFileInFolder); var image = new FileInfo(imageFileInFolder);
var filenameWithoutExtension = Path.GetFileName(imageFileInFolder).ToAlphanumericName(); var filenameWithoutExtension = Path.GetFileName(imageFileInFolder).ToAlphanumericName();
var imageName = image.Name.ToAlphanumericName(); var imageName = image.Name.ToAlphanumericName();
if (imageName.Equals(name) || filenameWithoutExtension.Equals(name)) result.Add(image); if(imageName.Equals(name) || filenameWithoutExtension.Equals(name))
result.Add(image);
} }
} }
@ -107,77 +110,102 @@ namespace Roadie.Library.Imaging
} }
try try
{ {
if (string.IsNullOrEmpty(image1) || !File.Exists(image1)) if(string.IsNullOrEmpty(image1) || !File.Exists(image1))
{ {
return File.Exists(compareToImage); return File.Exists(compareToImage);
} }
using (var imageComparing = SixLabors.ImageSharp.Image.Load(image1)) using(var imageComparing = SixLabors.ImageSharp.Image.Load(image1))
{ {
using (var imageToCompare = SixLabors.ImageSharp.Image.Load(compareToImage)) using(var imageToCompare = SixLabors.ImageSharp.Image.Load(compareToImage))
{ {
// Generally a larger image is the preferred image // Generally a larger image is the preferred image
var isBigger = imageToCompare.Height > imageComparing.Height && imageToCompare.Width > imageComparing.Width; var isBigger = imageToCompare.Height > imageComparing.Height &&
if (isBigger) imageToCompare.Width > imageComparing.Width;
if(isBigger)
{ {
return true; return true;
} }
} }
} }
} } catch(Exception ex)
catch (Exception ex)
{ {
Trace.WriteLine($"Error IsImageBetterQuality Image Comparing [{ image1 }] to [{ compareToImage }], Error [{ ex }]", "Warning"); Trace.WriteLine($"Error IsImageBetterQuality Image Comparing [{ image1 }] to [{ compareToImage }], Error [{ ex }]",
"Warning");
} }
return false; return false;
} }
public static IEnumerable<FileInfo> FindImageTypeInDirectory(DirectoryInfo directory, ImageType type, SearchOption folderSearchOptions = SearchOption.AllDirectories) public static IEnumerable<FileInfo> FindImageTypeInDirectory(DirectoryInfo directory,
ImageType type,
SearchOption folderSearchOptions = SearchOption.AllDirectories)
{ {
var result = new List<FileInfo>(); var result = new List<FileInfo>();
if (directory == null || !directory.Exists) return result; if (directory?.Exists != true)
{
return result;
}
var imageFilesInFolder = ImageFilesInFolder(directory.FullName, folderSearchOptions); var imageFilesInFolder = ImageFilesInFolder(directory.FullName, folderSearchOptions);
if (imageFilesInFolder == null || !imageFilesInFolder.Any()) return result; if (imageFilesInFolder?.Any() != true)
foreach (var imageFile in imageFilesInFolder) {
return result;
}
foreach(var imageFile in imageFilesInFolder)
{ {
var image = new FileInfo(imageFile); var image = new FileInfo(imageFile);
switch (type) switch(type)
{ {
case ImageType.Artist: case ImageType.Artist:
if (IsArtistImage(image)) result.Add(image); if (IsArtistImage(image))
{
result.Add(image);
}
break; break;
case ImageType.ArtistSecondary: case ImageType.ArtistSecondary:
if (IsArtistSecondaryImage(image)) result.Add(image); if (IsArtistSecondaryImage(image))
{
result.Add(image);
}
break; break;
case ImageType.Release: case ImageType.Release:
if (IsReleaseImage(image)) result.Add(image); if (IsReleaseImage(image))
{
result.Add(image);
}
break; break;
case ImageType.ReleaseSecondary: case ImageType.ReleaseSecondary:
if (IsReleaseSecondaryImage(image)) result.Add(image); if (IsReleaseSecondaryImage(image))
{
result.Add(image);
}
break; break;
case ImageType.Label: case ImageType.Label:
if (IsLabelImage(image)) result.Add(image); if (IsLabelImage(image))
{
result.Add(image);
}
break; break;
} }
} }
return result.OrderBy(x => x.Name); return result.OrderBy(x => x.Name);
} }
public static string[] GetFiles(string path, string[] patterns = null, SearchOption options = SearchOption.TopDirectoryOnly) public static string[] GetFiles(string path,
string[] patterns = null,
SearchOption options = SearchOption.TopDirectoryOnly)
{ {
if (!Directory.Exists(path)) if(!Directory.Exists(path))
{ {
return new string[0]; return new string[0];
} }
if (patterns == null || patterns.Length == 0) if(patterns == null || patterns.Length == 0)
{ {
return Directory.GetFiles(path, "*", options); return Directory.GetFiles(path, "*", options);
} }
if (patterns.Length == 1) if(patterns.Length == 1)
{ {
return Directory.GetFiles(path, patterns[0], options); return Directory.GetFiles(path, patterns[0], options);
} }
@ -186,9 +214,10 @@ namespace Roadie.Library.Imaging
public static byte[] ImageDataFromUrl(string imageUrl) public static byte[] ImageDataFromUrl(string imageUrl)
{ {
if (!string.IsNullOrEmpty(imageUrl) && !imageUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) if(!string.IsNullOrEmpty(imageUrl) && !imageUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{ {
var dataString = imageUrl.Trim().Replace('-', '+') var dataString = imageUrl.Trim()
.Replace('-', '+')
.Replace("data:image/jpeg;base64,", "") .Replace("data:image/jpeg;base64,", "")
.Replace("data:image/bmp;base64,", "") .Replace("data:image/bmp;base64,", "")
.Replace("data:image/gif;base64,", "") .Replace("data:image/gif;base64,", "")
@ -200,27 +229,22 @@ namespace Roadie.Library.Imaging
} }
public static string[] ImageExtensions() public static string[] ImageExtensions()
{ { return new string[6] { "*.bmp", "*.jpeg", "*.jpe", "*.jpg", "*.png", "*.gif" }; }
return new string[6] { "*.bmp", "*.jpeg", "*.jpe", "*.jpg", "*.png", "*.gif" };
}
public static string[] ImageFilesInFolder(string folder, SearchOption searchOption) public static string[] ImageFilesInFolder(string folder, SearchOption searchOption)
{ { return GetFiles(folder, ImageExtensions(), searchOption); }
return GetFiles(folder, ImageExtensions(), searchOption);
}
public static string[] ImageMimeTypes() public static string[] ImageMimeTypes()
{ { return new string[4] { "image/bmp", "image/jpeg", "image/png", "image/gif" }; }
return new string[4] { "image/bmp", "image/jpeg", "image/png", "image/gif" };
}
public static ImageSearchResult ImageSearchResultForImageUrl(string imageUrl) public static ImageSearchResult ImageSearchResultForImageUrl(string imageUrl)
{ {
if (!WebHelper.IsStringUrl(imageUrl)) return null; if(!WebHelper.IsStringUrl(imageUrl))
return null;
var result = new ImageSearchResult(); var result = new ImageSearchResult();
var imageBytes = WebHelper.BytesForImageUrl(imageUrl); var imageBytes = WebHelper.BytesForImageUrl(imageUrl);
IImageFormat imageFormat = null; IImageFormat imageFormat = null;
using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
{ {
result.Height = image.Height.ToString(); result.Height = image.Height.ToString();
result.Width = image.Width.ToString(); result.Width = image.Width.ToString();
@ -232,14 +256,17 @@ namespace Roadie.Library.Imaging
public static bool IsArtistImage(FileInfo fileinfo) public static bool IsArtistImage(FileInfo fileinfo)
{ {
if (fileinfo == null) return false; if(fileinfo == null)
return Regex.IsMatch(fileinfo.Name, @"(band|artist|group|photo)\.(jpg|jpeg|png|bmp|gif)", return false;
return Regex.IsMatch(fileinfo.Name,
@"(band|artist|group|photo)\.(jpg|jpeg|png|bmp|gif)",
RegexOptions.IgnoreCase); RegexOptions.IgnoreCase);
} }
public static bool IsArtistSecondaryImage(FileInfo fileinfo) public static bool IsArtistSecondaryImage(FileInfo fileinfo)
{ {
if (fileinfo == null) return false; if(fileinfo == null)
return false;
return Regex.IsMatch(fileinfo.Name, return Regex.IsMatch(fileinfo.Name,
@"(artist_logo|logo|photo[-_\s]*[0-9]+|(artist[\s_-]+[0-9]+)|(band[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", @"(artist_logo|logo|photo[-_\s]*[0-9]+|(artist[\s_-]+[0-9]+)|(band[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)",
RegexOptions.IgnoreCase); RegexOptions.IgnoreCase);
@ -247,14 +274,17 @@ namespace Roadie.Library.Imaging
public static bool IsLabelImage(FileInfo fileinfo) public static bool IsLabelImage(FileInfo fileinfo)
{ {
if (fileinfo == null) return false; if(fileinfo == null)
return Regex.IsMatch(fileinfo.Name, @"(label|recordlabel|record_label)\.(jpg|jpeg|png|bmp|gif)", return false;
return Regex.IsMatch(fileinfo.Name,
@"(label|recordlabel|record_label)\.(jpg|jpeg|png|bmp|gif)",
RegexOptions.IgnoreCase); RegexOptions.IgnoreCase);
} }
public static bool IsReleaseImage(FileInfo fileinfo, string releaseName = null) public static bool IsReleaseImage(FileInfo fileinfo, string releaseName = null)
{ {
if (fileinfo == null) return false; if(fileinfo == null)
return false;
return Regex.IsMatch(fileinfo.Name, return Regex.IsMatch(fileinfo.Name,
@"((f[-_\s]*[0-9]*)|00|art|big[art]*|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)", @"((f[-_\s]*[0-9]*)|00|art|big[art]*|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)",
RegexOptions.IgnoreCase); RegexOptions.IgnoreCase);
@ -262,13 +292,18 @@ namespace Roadie.Library.Imaging
public static bool IsReleaseSecondaryImage(FileInfo fileinfo) public static bool IsReleaseSecondaryImage(FileInfo fileinfo)
{ {
if (fileinfo == null) return false; if(fileinfo == null)
return false;
return Regex.IsMatch(fileinfo.Name, return Regex.IsMatch(fileinfo.Name,
@"((img[\s-_]*[0-9]*[\s-_]*[0-9]*)|(book[let]*[#-_\s(]*[0-9]*-*[0-9]*(\))*)|(encartes[-_\s]*[(]*[0-9]*[)]*)|sc[an]*(.)?[0-9]*|matrix(.)?[0-9]*|(cover[\s_-]*[0-9]+)|back|traycard|jewel case|disc|(.*)[in]*side(.*)|in([side|lay|let|site])*[0-9]*|digipack.?\[?\(?([0-9]*)\]?\)?|cd.?\[?\(?([0-9]*)\]?\)?|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", @"((img[\s-_]*[0-9]*[\s-_]*[0-9]*)|(book[let]*[#-_\s(]*[0-9]*-*[0-9]*(\))*)|(encartes[-_\s]*[(]*[0-9]*[)]*)|sc[an]*(.)?[0-9]*|matrix(.)?[0-9]*|(cover[\s_-]*[0-9]+)|back|traycard|jewel case|disc|(.*)[in]*side(.*)|in([side|lay|let|site])*[0-9]*|digipack.?\[?\(?([0-9]*)\]?\)?|cd.?\[?\(?([0-9]*)\]?\)?|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)",
RegexOptions.IgnoreCase); RegexOptions.IgnoreCase);
} }
public static byte[] ResizeImage(byte[] imageBytes, int width, int height) => ImageHelper.ResizeImage(imageBytes, width, height, false).Item2; public static byte[] ResizeImage(byte[] imageBytes, int width, int height) => ImageHelper.ResizeImage(imageBytes,
width,
height,
false)
.Item2;
/// <summary> /// <summary>
/// Resize a given image to given dimensions if needed /// Resize a given image to given dimensions if needed
@ -278,38 +313,39 @@ namespace Roadie.Library.Imaging
/// <param name="height">Resize to height</param> /// <param name="height">Resize to height</param>
/// <param name="forceResize">Force resize</param> /// <param name="forceResize">Force resize</param>
/// <returns>Tuple with bool for did resize and byte array of image</returns> /// <returns>Tuple with bool for did resize and byte array of image</returns>
public static Tuple<bool, byte[]> ResizeImage(byte[] imageBytes, int width, int height, bool? forceResize = false) public static Tuple<bool, byte[]> ResizeImage(byte[] imageBytes,
int width,
int height,
bool? forceResize = false)
{ {
if (imageBytes == null) if(imageBytes == null)
{ {
return null; return null;
} }
try try
{ {
using (var outStream = new MemoryStream()) using(var outStream = new MemoryStream())
{ {
var resized = false; var resized = false;
IImageFormat imageFormat = null; IImageFormat imageFormat = null;
using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat)) using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
{ {
var doForce = forceResize ?? false; var doForce = forceResize ?? false;
if (doForce || image.Width > width || image.Height > height) if(doForce || image.Width > width || image.Height > height)
{ {
int newWidth, newHeight; int newWidth, newHeight;
if (doForce) if(doForce)
{ {
newWidth = width; newWidth = width;
newHeight = height; newHeight = height;
} } else
else
{ {
float aspect = image.Width / (float)image.Height; float aspect = image.Width / (float)image.Height;
if (aspect < 1) if(aspect < 1)
{ {
newWidth = (int)(width * aspect); newWidth = (int)(width * aspect);
newHeight = (int)(newWidth / aspect); newHeight = (int)(newWidth / aspect);
} } else
else
{ {
newHeight = (int)(height / aspect); newHeight = (int)(height / aspect);
newWidth = (int)(newHeight * aspect); newWidth = (int)(newHeight * aspect);
@ -322,8 +358,7 @@ namespace Roadie.Library.Imaging
} }
return new Tuple<bool, byte[]>(resized, outStream.ToArray()); return new Tuple<bool, byte[]>(resized, outStream.ToArray());
} }
} } catch(Exception ex)
catch (Exception ex)
{ {
Trace.WriteLine($"Error Resizing Image [{ex}]", "Warning"); Trace.WriteLine($"Error Resizing Image [{ex}]", "Warning");
} }
@ -335,8 +370,9 @@ namespace Roadie.Library.Imaging
/// </summary> /// </summary>
/// <param name="filename">Name of the File (ie a CUE file)</param> /// <param name="filename">Name of the File (ie a CUE file)</param>
/// <param name="metaData">Populated MetaData</param> /// <param name="metaData">Populated MetaData</param>
/// <returns></returns> public static AudioMetaDataImage GetPictureForMetaData(IRoadieSettings configuration,
public static AudioMetaDataImage GetPictureForMetaData(IRoadieSettings configuration, string filename, AudioMetaData metaData) string filename,
AudioMetaData metaData)
{ {
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(filename), "Invalid Filename"); SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(filename), "Invalid Filename");
SimpleContract.Requires<ArgumentException>(metaData != null, "Invalid MetaData"); SimpleContract.Requires<ArgumentException>(metaData != null, "Invalid MetaData");
@ -353,7 +389,7 @@ namespace Roadie.Library.Imaging
{ {
AudioMetaDataImage imageMetaData = null; AudioMetaDataImage imageMetaData = null;
if (string.IsNullOrEmpty(filename)) if(string.IsNullOrEmpty(filename))
{ {
return imageMetaData; return imageMetaData;
} }
@ -361,9 +397,9 @@ namespace Roadie.Library.Imaging
{ {
var fileInfo = new FileInfo(filename); var fileInfo = new FileInfo(filename);
var ReleaseCover = Path.ChangeExtension(filename, "jpg"); var ReleaseCover = Path.ChangeExtension(filename, "jpg");
if (File.Exists(ReleaseCover)) if(File.Exists(ReleaseCover))
{ {
using (var processor = new ImageProcessor(configuration)) using(var processor = new ImageProcessor(configuration))
{ {
imageMetaData = new AudioMetaDataImage imageMetaData = new AudioMetaDataImage
{ {
@ -372,24 +408,23 @@ namespace Roadie.Library.Imaging
MimeType = Library.Processors.FileProcessor.DetermineFileType(fileInfo) MimeType = Library.Processors.FileProcessor.DetermineFileType(fileInfo)
}; };
} }
} } else
else
{ {
// Is there a picture in filename folder (for the Release) // Is there a picture in filename folder (for the Release)
var pictures = fileInfo.Directory.GetFiles("*.jpg"); var pictures = fileInfo.Directory.GetFiles("*.jpg");
var tagImages = new List<AudioMetaDataImage>(); var tagImages = new List<AudioMetaDataImage>();
if (pictures != null && pictures.Any()) if(pictures?.Any() == true)
{ {
FileInfo picture = null;
// See if there is a "cover" or "front" jpg file if so use it // See if there is a "cover" or "front" jpg file if so use it
picture = pictures.FirstOrDefault(x => FileInfo picture = Array.Find(pictures,
x.Name.Equals("cover", StringComparison.OrdinalIgnoreCase)); x => x.Name.Equals("cover", StringComparison.OrdinalIgnoreCase));
if (picture == null) if(picture == null)
picture = pictures.FirstOrDefault(x => picture = Array.Find(pictures,
x.Name.Equals("front", StringComparison.OrdinalIgnoreCase)); x => x.Name.Equals("front", StringComparison.OrdinalIgnoreCase));
if (picture == null) picture = pictures.First(); if(picture == null)
if (picture != null) picture = pictures[0];
using (var processor = new ImageProcessor(configuration)) if(picture != null)
using(var processor = new ImageProcessor(configuration))
{ {
imageMetaData = new AudioMetaDataImage imageMetaData = new AudioMetaDataImage
{ {
@ -400,11 +435,9 @@ namespace Roadie.Library.Imaging
} }
} }
} }
} } catch(FileNotFoundException)
catch (FileNotFoundException)
{ {
} } catch(Exception ex)
catch (Exception ex)
{ {
Trace.WriteLine(ex, ex.Serialize()); Trace.WriteLine(ex, ex.Serialize());
} }
@ -412,95 +445,136 @@ namespace Roadie.Library.Imaging
return imageMetaData; return imageMetaData;
} }
public static models.Image MakeThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string type, int? width = null, int? height = null, bool includeCachebuster = false) public static models.Image MakeThumbnailImage(IRoadieSettings configuration,
IHttpContext httpContext,
Guid id,
string type,
int? width = null,
int? height = null,
bool includeCachebuster = false)
{ {
return MakeImage(configuration, httpContext, id, type, width ?? configuration.ThumbnailImageSize.Width, height ?? configuration.ThumbnailImageSize.Height, null, includeCachebuster); return MakeImage(configuration,
httpContext,
id,
type,
width ?? configuration.ThumbnailImageSize.Width,
height ?? configuration.ThumbnailImageSize.Height,
null,
includeCachebuster);
} }
public static models.Image MakeNewImage(IHttpContext httpContext, string type) public static models.Image MakeNewImage(IHttpContext httpContext, string type)
{ { return new models.Image($"{httpContext.ImageBaseUrl}/{type}.jpg", null, null); }
return new models.Image($"{httpContext.ImageBaseUrl}/{type}.jpg", null, null);
}
public static models.Image MakePlaylistThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) public static models.Image MakePlaylistThumbnailImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeThumbnailImage(configuration, httpContext, id, "playlist"); Guid id)
} { return MakeThumbnailImage(configuration, httpContext, id, "playlist"); }
public static models.Image MakeReleaseThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) public static models.Image MakeReleaseThumbnailImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeThumbnailImage(configuration, httpContext, id, "release"); Guid id)
} { return MakeThumbnailImage(configuration, httpContext, id, "release"); }
public static models.Image MakeTrackThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) public static models.Image MakeTrackThumbnailImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeThumbnailImage(configuration, httpContext, id, "track"); Guid id)
} { return MakeThumbnailImage(configuration, httpContext, id, "track"); }
public static models.Image MakeUserThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) public static models.Image MakeUserThumbnailImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeThumbnailImage(configuration, httpContext, id, "user"); Guid id)
} { return MakeThumbnailImage(configuration, httpContext, id, "user"); }
public static models.Image MakeLabelThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) public static models.Image MakeLabelThumbnailImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeThumbnailImage(configuration, httpContext, id, "label"); Guid id)
} { return MakeThumbnailImage(configuration, httpContext, id, "label"); }
public static models.Image MakeArtistThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid? id) public static models.Image MakeArtistThumbnailImage(IRoadieSettings configuration,
IHttpContext httpContext,
Guid? id)
{ {
if (!id.HasValue) return null; if(!id.HasValue)
return null;
return MakeThumbnailImage(configuration, httpContext, id.Value, "artist"); return MakeThumbnailImage(configuration, httpContext, id.Value, "artist");
} }
public static models.Image MakeCollectionThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) public static models.Image MakeCollectionThumbnailImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeThumbnailImage(configuration, httpContext, id, "collection"); Guid id)
} { return MakeThumbnailImage(configuration, httpContext, id, "collection"); }
public static models.Image MakeFullsizeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string caption = null) public static models.Image MakeFullsizeImage(IRoadieSettings configuration,
IHttpContext httpContext,
Guid id,
string caption = null)
{ {
return new models.Image($"{httpContext.ImageBaseUrl}/{id}", caption, return new models.Image($"{httpContext.ImageBaseUrl}/{id}",
caption,
$"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); $"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}");
} }
public static models.Image MakeFullsizeSecondaryImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, ImageType type, int imageId, string caption = null) public static models.Image MakeFullsizeSecondaryImage(IRoadieSettings configuration,
IHttpContext httpContext,
Guid id,
ImageType type,
int imageId,
string caption = null)
{ {
if (type == ImageType.ArtistSecondary) if(type == ImageType.ArtistSecondary)
{ {
return new models.Image($"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}", caption, $"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); return new models.Image($"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}",
caption,
$"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}");
} }
return new models.Image($"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}", caption, $"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); return new models.Image($"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}",
caption,
$"{httpContext.ImageBaseUrl}/release-secondary/{id}/{imageId}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}");
} }
public static models.Image MakeGenreThumbnailImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id) public static models.Image MakeGenreThumbnailImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeThumbnailImage(configuration, httpContext, id, "genre"); Guid id)
} { return MakeThumbnailImage(configuration, httpContext, id, "genre"); }
public static models.Image MakeUnknownImage(IHttpContext httpContext, string unknownType = "unknown") public static models.Image MakeUnknownImage(IHttpContext httpContext, string unknownType = "unknown")
{ { return new models.Image($"{httpContext.ImageBaseUrl}/{ unknownType }.jpg"); }
return new models.Image($"{httpContext.ImageBaseUrl}/{ unknownType }.jpg");
}
public static models.Image MakeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, int width = 200, int height = 200, string caption = null, bool includeCachebuster = false) public static models.Image MakeImage(IRoadieSettings configuration,
IHttpContext httpContext,
Guid id,
int width = 200,
int height = 200,
string caption = null,
bool includeCachebuster = false)
{ {
return new models.Image($"{httpContext.ImageBaseUrl}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}", return new models.Image($"{httpContext.ImageBaseUrl}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}",
caption, caption,
$"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}"); $"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}");
} }
public static models.Image MakeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string type, IImageSize imageSize) public static models.Image MakeImage(IRoadieSettings configuration,
{ IHttpContext httpContext,
return MakeImage(configuration, httpContext, id, type, imageSize.Width, imageSize.Height); Guid id,
} string type,
IImageSize imageSize)
{ return MakeImage(configuration, httpContext, id, type, imageSize.Width, imageSize.Height); }
public static models.Image MakeImage(IRoadieSettings configuration, IHttpContext httpContext, Guid id, string type, int? width, int? height, string caption = null, bool includeCachebuster = false) public static models.Image MakeImage(IRoadieSettings configuration,
IHttpContext httpContext,
Guid id,
string type,
int? width,
int? height,
string caption = null,
bool includeCachebuster = false)
{ {
if (width.HasValue && height.HasValue && (width.Value != configuration.ThumbnailImageSize.Width || if(width.HasValue &&
height.HasValue &&
(width.Value != configuration.ThumbnailImageSize.Width ||
height.Value != configuration.ThumbnailImageSize.Height)) height.Value != configuration.ThumbnailImageSize.Height))
return new models.Image( return new models.Image($"{httpContext.ImageBaseUrl}/{type}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}",
$"{httpContext.ImageBaseUrl}/{type}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}",
caption, caption,
$"{httpContext.ImageBaseUrl}/{type}/{id}/{configuration.ThumbnailImageSize.Width}/{configuration.ThumbnailImageSize.Height}"); $"{httpContext.ImageBaseUrl}/{type}/{id}/{configuration.ThumbnailImageSize.Width}/{configuration.ThumbnailImageSize.Height}");
return new models.Image($"{httpContext.ImageBaseUrl}/{type}/{id}", caption, null); return new models.Image($"{httpContext.ImageBaseUrl}/{type}/{id}", caption, null);

View file

@ -26,7 +26,7 @@ namespace Roadie.Library.Inspect
{ {
public class Inspector public class Inspector
{ {
private static readonly string Salt = "6856F2EE-5965-4345-884B-2CCA457AAF59"; private const string Salt = "6856F2EE-5965-4345-884B-2CCA457AAF59";
private IEnumerable<IInspectorDirectoryPlugin> _directoryPlugins; private IEnumerable<IInspectorDirectoryPlugin> _directoryPlugins;
private IEnumerable<IInspectorFilePlugin> _filePlugins; private IEnumerable<IInspectorFilePlugin> _filePlugins;
@ -49,15 +49,17 @@ namespace Roadie.Library.Inspect
foreach (var t in types) foreach (var t in types)
if (t.GetInterface("IInspectorDirectoryPlugin") != null && !t.IsAbstract && !t.IsInterface) if (t.GetInterface("IInspectorDirectoryPlugin") != null && !t.IsAbstract && !t.IsInterface)
{ {
var plugin = var plugin = Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as IInspectorDirectoryPlugin;
Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as
IInspectorDirectoryPlugin;
if (plugin.IsEnabled) if (plugin.IsEnabled)
{
plugins.Add(plugin); plugins.Add(plugin);
}
else else
{
Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{plugin.Description}]"); Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{plugin.Description}]");
} }
} }
}
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
@ -84,18 +86,21 @@ namespace Roadie.Library.Inspect
.SelectMany(s => s.GetTypes()) .SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p)); .Where(p => type.IsAssignableFrom(p));
foreach (var t in types) foreach (var t in types)
{
if (t.GetInterface("IInspectorFilePlugin") != null && !t.IsAbstract && !t.IsInterface) if (t.GetInterface("IInspectorFilePlugin") != null && !t.IsAbstract && !t.IsInterface)
{ {
var plugin = var plugin = Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as IInspectorFilePlugin;
Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as
IInspectorFilePlugin;
if (plugin.IsEnabled) if (plugin.IsEnabled)
{
plugins.Add(plugin); plugins.Add(plugin);
}
else else
{
Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{plugin.Description}]"); Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{plugin.Description}]");
} }
} }
}
}
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
@ -147,10 +152,12 @@ namespace Roadie.Library.Inspect
var numbers = 0; var numbers = 0;
var bytes = System.Text.Encoding.ASCII.GetBytes(input); var bytes = System.Text.Encoding.ASCII.GetBytes(input);
var looper = bytes.Length / 4; var looper = bytes.Length / 4;
for (var i = 0; i < looper; i++) numbers += BitConverter.ToInt32(bytes, i * 4); for (var i = 0; i < looper; i++)
{
numbers += BitConverter.ToInt32(bytes, i * 4);
}
if (numbers < 0) numbers *= -1; if (numbers < 0) numbers *= -1;
var token = hashids.Encode(numbers); return hashids.Encode(numbers);
return token;
} }
public void Inspect(bool doCopy, bool isReadOnly, string directoryToInspect, string destination, bool dontAppendSubFolder, bool dontDeleteEmptyFolders, bool dontRunPreScripts) public void Inspect(bool doCopy, bool isReadOnly, string directoryToInspect, string destination, bool dontAppendSubFolder, bool dontDeleteEmptyFolders, bool dontRunPreScripts)
@ -171,11 +178,11 @@ namespace Roadie.Library.Inspect
string scriptResult = null; string scriptResult = null;
// Run PreInspect script // Run PreInspect script
if(dontRunPreScripts) if (dontRunPreScripts)
{ {
Console.BackgroundColor = ConsoleColor.Blue; Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"Skipping PreInspectScript."); Console.WriteLine("Skipping PreInspectScript.");
Console.ResetColor(); Console.ResetColor();
} }
else else
@ -217,7 +224,7 @@ namespace Roadie.Library.Inspect
// Get all the MP3 files in 'directory' // Get all the MP3 files in 'directory'
var files = Directory.GetFiles(directory, "*.mp3", SearchOption.TopDirectoryOnly); var files = Directory.GetFiles(directory, "*.mp3", SearchOption.TopDirectoryOnly);
if (files != null && files.Any()) if (files?.Any() == true)
{ {
if (!isReadOnly && !createdDestinationFolder && !Directory.Exists(dest)) if (!isReadOnly && !createdDestinationFolder && !Directory.Exists(dest))
{ {
@ -226,21 +233,20 @@ namespace Roadie.Library.Inspect
} }
// Run directory plugins against current directory // Run directory plugins against current directory
foreach (var plugin in DirectoryPlugins.Where(x => !x.IsPostProcessingPlugin) foreach (var plugin in DirectoryPlugins.Where(x => !x.IsPostProcessingPlugin).OrderBy(x => x.Order))
.OrderBy(x => x.Order))
{ {
Console.WriteLine($"╠╬═ Running Directory Plugin {plugin.Description}"); Console.WriteLine($"╠╬═ Running Directory Plugin {plugin.Description}");
var pluginResult = plugin.Process(directoryInfo); var pluginResult = plugin.Process(directoryInfo);
if (!pluginResult.IsSuccess) if (!pluginResult.IsSuccess)
{ {
Console.WriteLine( Console.WriteLine($"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]");
$"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]");
return; return;
} }
if (!string.IsNullOrEmpty(pluginResult.Data)) if (!string.IsNullOrEmpty(pluginResult.Data))
{
Console.WriteLine($"╠╣ Directory Plugin Message: {pluginResult.Data}"); Console.WriteLine($"╠╣ Directory Plugin Message: {pluginResult.Data}");
} }
}
Console.WriteLine("╠╝"); Console.WriteLine("╠╝");
Console.WriteLine($"╟─ Found [{files.Length}] mp3 Files"); Console.WriteLine($"╟─ Found [{files.Length}] mp3 Files");
@ -263,8 +269,7 @@ namespace Roadie.Library.Inspect
if (!originalMetaData.IsValid) if (!originalMetaData.IsValid)
{ {
Console.ForegroundColor = ConsoleColor.DarkYellow; Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine( Console.WriteLine($"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(originalMetaData)}");
$"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(originalMetaData)}");
Console.WriteLine($"╟ [{JsonConvert.SerializeObject(tagLib, Newtonsoft.Json.Formatting.Indented)}]"); Console.WriteLine($"╟ [{JsonConvert.SerializeObject(tagLib, Newtonsoft.Json.Formatting.Indented)}]");
Console.ResetColor(); Console.ResetColor();
} }
@ -274,13 +279,11 @@ namespace Roadie.Library.Inspect
foreach (var plugin in FilePlugins.OrderBy(x => x.Order)) foreach (var plugin in FilePlugins.OrderBy(x => x.Order))
{ {
Console.WriteLine($"╟┤ Running File Plugin {plugin.Description}"); Console.WriteLine($"╟┤ Running File Plugin {plugin.Description}");
OperationResult<AudioMetaData> pluginResult = null; OperationResult<AudioMetaData> pluginResult = plugin.Process(pluginMetaData);
pluginResult = plugin.Process(pluginMetaData);
if (!pluginResult.IsSuccess) if (!pluginResult.IsSuccess)
{ {
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine( Console.WriteLine($"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]");
$"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]");
Console.ResetColor(); Console.ResetColor();
return; return;
} }
@ -291,8 +294,7 @@ namespace Roadie.Library.Inspect
if (!pluginMetaData.IsValid) if (!pluginMetaData.IsValid)
{ {
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine( Console.WriteLine($"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}");
$"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}");
Console.ResetColor(); Console.ResetColor();
return; return;
} }
@ -301,16 +303,17 @@ namespace Roadie.Library.Inspect
if (originalMetaData != null && pluginMetaData != null) if (originalMetaData != null && pluginMetaData != null)
{ {
var differences = Comparer.Compare(originalMetaData, pluginMetaData); var differences = Comparer.Compare(originalMetaData, pluginMetaData);
if (differences.Any()) if (differences.Count > 0)
{ {
var skipDifferences = new List<string> var skipDifferences = new List<string> { "AudioMetaDataWeights", "FileInfo", "Images", "TrackArtists" };
{"AudioMetaDataWeights", "FileInfo", "Images", "TrackArtists"};
var differencesDescription = $"{Environment.NewLine}"; var differencesDescription = $"{Environment.NewLine}";
foreach (var difference in differences) foreach (var difference in differences)
{ {
if (skipDifferences.Contains(difference.Name)) continue; if (skipDifferences.Contains(difference.Name))
differencesDescription += {
$"╟ || {difference.Name} : Was [{difference.OldValue}] Now [{difference.NewValue}]{Environment.NewLine}"; continue;
}
differencesDescription += $"╟ || {difference.Name} : Was [{difference.OldValue}] Now [{difference.NewValue}]{Environment.NewLine}";
} }
Console.Write($"╟ ≡ != ID3 Tag Modified: {differencesDescription}"); Console.Write($"╟ ≡ != ID3 Tag Modified: {differencesDescription}");
@ -339,15 +342,13 @@ namespace Roadie.Library.Inspect
{ {
var oBad = originalMetaData == null; var oBad = originalMetaData == null;
var pBad = pluginMetaData == null; var pBad = pluginMetaData == null;
Console.WriteLine( Console.WriteLine($"╟ !! MetaData comparison skipped. {(oBad ? "Pre MetaData is Invalid" : string.Empty)} {(pBad ? "Post MetaData is Invalid" : string.Empty)}");
$"╟ !! MetaData comparison skipped. {(oBad ? "Pre MetaData is Invalid" : "")} {(pBad ? "Post MetaData is Invalid" : "")}");
} }
if (!pluginMetaData.IsValid) if (!pluginMetaData.IsValid)
{ {
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine( Console.WriteLine($"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}");
$"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(pluginMetaData)}");
Console.ResetColor(); Console.ResetColor();
} }
else else
@ -357,11 +358,16 @@ namespace Roadie.Library.Inspect
Console.ResetColor(); Console.ResetColor();
var artistToken = ArtistInspectorToken(tagLib.Data); var artistToken = ArtistInspectorToken(tagLib.Data);
if (!artistsFound.Contains(artistToken)) artistsFound.Add(artistToken); if (!artistsFound.Contains(artistToken))
{
artistsFound.Add(artistToken);
}
var releaseToken = ReleaseInspectorToken(tagLib.Data); var releaseToken = ReleaseInspectorToken(tagLib.Data);
if (!releasesFound.Contains(releaseToken)) releasesFound.Add(releaseToken); if (!releasesFound.Contains(releaseToken))
var newFileName = {
$"CD{(tagLib.Data.Disc ?? ID3TagsHelper.DetermineDiscNumber(tagLib.Data)).ToString("000")}_{tagLib.Data.TrackNumber.Value.ToString("0000")}.mp3"; releasesFound.Add(releaseToken);
}
var newFileName = $"CD{(tagLib.Data.Disc ?? ID3TagsHelper.DetermineDiscNumber(tagLib.Data)).ToString("000")}_{tagLib.Data.TrackNumber.Value.ToString("0000")}.mp3";
// Artist sub folder is created to hold Releases for Artist and Artist Images // Artist sub folder is created to hold Releases for Artist and Artist Images
var artistSubDirectory = directory == dest var artistSubDirectory = directory == dest
? fileInfo.DirectoryName ? fileInfo.DirectoryName
@ -371,40 +377,35 @@ namespace Roadie.Library.Inspect
? fileInfo.DirectoryName ? fileInfo.DirectoryName
: Path.Combine(dest, artistToken, releaseToken); : Path.Combine(dest, artistToken, releaseToken);
if (!isReadOnly && !Directory.Exists(subDirectory)) if (!isReadOnly && !Directory.Exists(subDirectory))
{
Directory.CreateDirectory(subDirectory); Directory.CreateDirectory(subDirectory);
}
// Inspect images // Inspect images
if (!inspectedImagesInDirectories.Contains(directoryInfo.FullName)) if (!inspectedImagesInDirectories.Contains(directoryInfo.FullName))
{ {
// Get all artist images and move to artist folder // Get all artist images and move to artist folder
var foundArtistImages = new List<FileInfo>(); var foundArtistImages = new List<FileInfo>();
foundArtistImages.AddRange(ImageHelper.FindImagesByName(directoryInfo, foundArtistImages.AddRange(ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Artist, SearchOption.TopDirectoryOnly));
tagLib.Data.Artist, SearchOption.TopDirectoryOnly)); foundArtistImages.AddRange(ImageHelper.FindImagesByName(directoryInfo.Parent, tagLib.Data.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImagesByName(directoryInfo.Parent, foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, ImageType.Artist, SearchOption.TopDirectoryOnly));
tagLib.Data.Artist, SearchOption.TopDirectoryOnly)); foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory( foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.Artist, SearchOption.TopDirectoryOnly));
directoryInfo.Parent, ImageType.Artist, SearchOption.TopDirectoryOnly)); foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(
directoryInfo.Parent, ImageType.ArtistSecondary,
SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo,
ImageType.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo,
ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foreach (var artistImage in foundArtistImages) foreach (var artistImage in foundArtistImages)
{
InspectImage(isReadOnly, doCopy, dest, artistSubDirectory, artistImage); InspectImage(isReadOnly, doCopy, dest, artistSubDirectory, artistImage);
}
// Get all release images and move to release folder // Get all release images and move to release folder
var foundReleaseImages = new List<FileInfo>(); var foundReleaseImages = new List<FileInfo>();
foundReleaseImages.AddRange( foundReleaseImages.AddRange(ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Release));
ImageHelper.FindImagesByName(directoryInfo, tagLib.Data.Release)); foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.Release));
foundReleaseImages.AddRange( foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.ReleaseSecondary));
ImageHelper.FindImageTypeInDirectory(directoryInfo, ImageType.Release));
foundReleaseImages.AddRange(
ImageHelper.FindImageTypeInDirectory(directoryInfo,
ImageType.ReleaseSecondary));
foreach (var foundReleaseImage in foundReleaseImages) foreach (var foundReleaseImage in foundReleaseImages)
{
InspectImage(isReadOnly, doCopy, dest, subDirectory, foundReleaseImage); InspectImage(isReadOnly, doCopy, dest, subDirectory, foundReleaseImage);
}
inspectedImagesInDirectories.Add(directoryInfo.FullName); inspectedImagesInDirectories.Add(directoryInfo.FullName);
} }
@ -412,8 +413,7 @@ namespace Roadie.Library.Inspect
var newPath = Path.Combine(dest, subDirectory, newFileName.ToFileNameFriendly()); var newPath = Path.Combine(dest, subDirectory, newFileName.ToFileNameFriendly());
if (isReadOnly) if (isReadOnly)
{ {
Console.WriteLine( Console.WriteLine($"╟ 🔒 Read Only Mode: File would be [{(doCopy ? "Copied" : "Moved")}] to [{newPath}]");
$"╟ 🔒 Read Only Mode: File would be [{(doCopy ? "Copied" : "Moved")}] to [{newPath}]");
} }
else else
{ {
@ -464,8 +464,7 @@ namespace Roadie.Library.Inspect
Console.WriteLine("╠╝"); Console.WriteLine("╠╝");
sw.Stop(); sw.Stop();
Console.WriteLine( Console.WriteLine($"╚═ Elapsed Time {sw.ElapsedMilliseconds.ToString("0000000")}, Artists {artistsFound.Count}, Releases {releasesFound.Count}, MP3s {mp3FilesFoundCount} ═╝");
$"╚═ Elapsed Time {sw.ElapsedMilliseconds.ToString("0000000")}, Artists {artistsFound.Count()}, Releases {releasesFound.Count()}, MP3s {mp3FilesFoundCount} ═╝");
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -492,8 +491,7 @@ namespace Roadie.Library.Inspect
{ {
Console.BackgroundColor = ConsoleColor.Blue; Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine( Console.WriteLine($"PostInspectScript Results: {Environment.NewLine + scriptResult + Environment.NewLine}");
$"PostInspectScript Results: {Environment.NewLine + scriptResult + Environment.NewLine}");
Console.ResetColor(); Console.ResetColor();
} }
} }
@ -521,17 +519,22 @@ namespace Roadie.Library.Inspect
looper++; looper++;
newImagePath = Path.Combine(dest, subdirectory, looper.ToString("00"), image.Name); newImagePath = Path.Combine(dest, subdirectory, looper.ToString("00"), image.Name);
} }
if (isReadOnly) if (isReadOnly)
Console.WriteLine( {
$"╟ 🔒 Read Only Mode: Would be [{(doCopy ? "Copied" : "Moved")}] to [{newImagePath}]"); Console.WriteLine($"╟ 🔒 Read Only Mode: Would be [{(doCopy ? "Copied" : "Moved")}] to [{newImagePath}]");
}
else else
{
try try
{ {
if (!doCopy) if (!doCopy)
{
image.MoveTo(newImagePath); image.MoveTo(newImagePath);
}
else else
{
image.CopyTo(newImagePath, true); image.CopyTo(newImagePath, true);
}
Console.ForegroundColor = ConsoleColor.DarkYellow; Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"╠═ 🚛 {(doCopy ? "Copied" : "Moved")} Image File to [{newImagePath}]"); Console.WriteLine($"╠═ 🚛 {(doCopy ? "Copied" : "Moved")} Image File to [{newImagePath}]");
} }
@ -539,8 +542,8 @@ namespace Roadie.Library.Inspect
{ {
Logger.LogError(ex); Logger.LogError(ex);
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine( Console.WriteLine($"📛 Error file [{image.FullName}], newImagePath [{newImagePath}], Exception: [{ex}]");
$"📛 Error file [{image.FullName}], newImagePath [{newImagePath}], Exception: [{ex}]"); }
} }
} }
@ -588,9 +591,17 @@ namespace Roadie.Library.Inspect
var script = File.ReadAllText(scriptFilename); var script = File.ReadAllText(scriptFilename);
using (var ps = PowerShell.Create()) using (var ps = PowerShell.Create())
{ {
var r = ""; var r = string.Empty;
var results = ps.AddScript(script).Invoke(); var results = ps.AddScript(script)
foreach (var result in results) r += result + Environment.NewLine; .AddParameter("DoCopy", doCopy)
.AddParameter("IsReadOnly", isReadOnly)
.AddParameter("DirectoryToInspect", directoryToInspect)
.AddParameter("Dest", dest)
.Invoke();
foreach (var result in results)
{
r += result + Environment.NewLine;
}
return r; return r;
} }
} }
@ -604,7 +615,6 @@ namespace Roadie.Library.Inspect
public class LoggingTraceListener : TraceListener public class LoggingTraceListener : TraceListener
{ {
public override void Write(string message) public override void Write(string message)
{ {
Console.WriteLine($"╠╬═ { message }"); Console.WriteLine($"╠╬═ { message }");
@ -614,7 +624,5 @@ namespace Roadie.Library.Inspect
{ {
Console.WriteLine($"╠╬═ { message }"); Console.WriteLine($"╠╬═ { message }");
} }
} }
} }

View file

@ -33,7 +33,7 @@ namespace Roadie.Library.Scrobble
/// indication of what music player they're using. /// indication of what music player they're using.
/// </remark> /// </remark>
/// </summary> /// </summary>
public override async Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.NowPlaying(roadieUser, scrobble); public override async Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.NowPlaying(roadieUser, scrobble).ConfigureAwait(false);
/// <summary> /// <summary>
/// Send a Scrobble Request /// Send a Scrobble Request
@ -42,7 +42,7 @@ namespace Roadie.Library.Scrobble
/// listening history and generate personalised charts and recommendations (and more). /// listening history and generate personalised charts and recommendations (and more).
/// </remark> /// </remark>
/// </summary> /// </summary>
public override async Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.Scrobble(roadieUser, scrobble); public override async Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble) => await LastFmHelper.Scrobble(roadieUser, scrobble).ConfigureAwait(false);
} }
} }

View file

@ -37,7 +37,7 @@ namespace Roadie.Library.Scrobble
{ {
Data = true, Data = true,
IsSuccess = true IsSuccess = true
}); }).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@ -59,12 +59,13 @@ namespace Roadie.Library.Scrobble
}; };
} }
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var track = DbContext.Tracks var track = await DbContext.Tracks
.Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist) .Include(x => x.TrackArtist)
.FirstOrDefault(x => x.RoadieId == scrobble.TrackId); .FirstOrDefaultAsync(x => x.RoadieId == scrobble.TrackId)
.ConfigureAwait(false);
if (track == null) if (track == null)
{ {
return new OperationResult<bool>($"Scrobble: Unable To Find Track [{scrobble.TrackId}]"); return new OperationResult<bool>($"Scrobble: Unable To Find Track [{scrobble.TrackId}]");
@ -80,8 +81,8 @@ namespace Roadie.Library.Scrobble
{ {
if (roadieUser != null) if (roadieUser != null)
{ {
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId); var user = await DbContext.Users.FirstOrDefaultAsync(x => x.RoadieId == roadieUser.UserId).ConfigureAwait(false);
userTrack = DbContext.UserTracks.FirstOrDefault(x => x.UserId == user.Id && x.TrackId == track.Id); userTrack = await DbContext.UserTracks.FirstOrDefaultAsync(x => x.UserId == user.Id && x.TrackId == track.Id).ConfigureAwait(false);
if (userTrack == null) if (userTrack == null)
{ {
userTrack = new data.UserTrack(now) userTrack = new data.UserTrack(now)
@ -89,7 +90,7 @@ namespace Roadie.Library.Scrobble
UserId = user.Id, UserId = user.Id,
TrackId = track.Id TrackId = track.Id
}; };
DbContext.UserTracks.Add(userTrack); await DbContext.UserTracks.AddAsync(userTrack).ConfigureAwait(false);
} }
userTrack.LastPlayed = now; userTrack.LastPlayed = now;
@ -101,20 +102,21 @@ namespace Roadie.Library.Scrobble
track.PlayedCount = (track.PlayedCount ?? 0) + 1; track.PlayedCount = (track.PlayedCount ?? 0) + 1;
track.LastPlayed = now; track.LastPlayed = now;
var release = DbContext.Releases var release = await DbContext.Releases
.Include(x => x.Artist) .Include(x => x.Artist)
.FirstOrDefault(x => x.RoadieId == track.ReleaseMedia.Release.RoadieId); .FirstOrDefaultAsync(x => x.RoadieId == track.ReleaseMedia.Release.RoadieId)
.ConfigureAwait(false);
release.LastPlayed = now; release.LastPlayed = now;
release.PlayedCount = (release.PlayedCount ?? 0) + 1; release.PlayedCount = (release.PlayedCount ?? 0) + 1;
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == release.Artist.RoadieId); var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.RoadieId == release.Artist.RoadieId).ConfigureAwait(false);
artist.LastPlayed = now; artist.LastPlayed = now;
artist.PlayedCount = (artist.PlayedCount ?? 0) + 1; artist.PlayedCount = (artist.PlayedCount ?? 0) + 1;
data.Artist trackArtist = null; data.Artist trackArtist = null;
if (track.ArtistId.HasValue) if (track.ArtistId.HasValue)
{ {
trackArtist = DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId); trackArtist = await DbContext.Artists.FirstOrDefaultAsync(x => x.Id == track.ArtistId).ConfigureAwait(false);
if (trackArtist != null) if (trackArtist != null)
{ {
trackArtist.LastPlayed = now; trackArtist.LastPlayed = now;
@ -127,7 +129,7 @@ namespace Roadie.Library.Scrobble
} }
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(track.CacheRegion); CacheManager.ClearRegion(track.CacheRegion);
CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion);

View file

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching; using Roadie.Library.Caching;
using Roadie.Library.Configuration; using Roadie.Library.Configuration;
using Roadie.Library.Data.Context; using Roadie.Library.Data.Context;
@ -10,7 +11,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using data = Roadie.Library.Data;
namespace Roadie.Library.Scrobble namespace Roadie.Library.Scrobble
{ {
@ -31,6 +31,18 @@ namespace Roadie.Library.Scrobble
private IEnumerable<IScrobblerIntegration> Scrobblers { get; } private IEnumerable<IScrobblerIntegration> Scrobblers { get; }
public ScrobbleHandler(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext, ICacheManager cacheManager, RoadieScrobbler roadieScrobbler)
{
Logger = logger;
Configuration = configuration;
DbContext = dbContext;
var scrobblers = new List<IScrobblerIntegration>
{
roadieScrobbler
};
Scrobblers = scrobblers;
}
public ScrobbleHandler(IRoadieSettings configuration, ILogger<ScrobbleHandler> logger, IRoadieDbContext dbContext, public ScrobbleHandler(IRoadieSettings configuration, ILogger<ScrobbleHandler> logger, IRoadieDbContext dbContext,
ICacheManager cacheManager, IHttpEncoder httpEncoder, IHttpContext httpContext, ICacheManager cacheManager, IHttpEncoder httpEncoder, IHttpContext httpContext,
ILastFmHelper lastFmHelper, IRoadieScrobbler roadieScrobbler, ILastFMScrobbler lastFMScrobbler) ILastFmHelper lastFmHelper, IRoadieScrobbler roadieScrobbler, ILastFMScrobbler lastFMScrobbler)
@ -51,16 +63,28 @@ namespace Roadie.Library.Scrobble
Scrobblers = scrobblers; Scrobblers = scrobblers;
} }
public ScrobbleHandler(IRoadieSettings configuration, ILogger logger, IRoadieDbContext dbContext, ICacheManager cacheManager, RoadieScrobbler roadieScrobbler) private async Task<ScrobbleInfo> GetScrobbleInfoDetailsAsync(ScrobbleInfo scrobble)
{ {
Logger = logger; var scrobbleInfo = await (from t in DbContext.Tracks
Configuration = configuration; join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
DbContext = dbContext; join r in DbContext.Releases on rm.ReleaseId equals r.Id
var scrobblers = new List<IScrobblerIntegration> join a in DbContext.Artists on r.ArtistId equals a.Id
where t.RoadieId == scrobble.TrackId
select new
{ {
roadieScrobbler ArtistName = a.Name,
}; ReleaseTitle = r.Title,
Scrobblers = scrobblers; TrackTitle = t.Title,
t.TrackNumber,
t.Duration
}).FirstOrDefaultAsync().ConfigureAwait(false);
scrobble.ArtistName = scrobbleInfo.ArtistName;
scrobble.ReleaseTitle = scrobbleInfo.ReleaseTitle;
scrobble.TrackTitle = scrobbleInfo.TrackTitle;
scrobble.TrackNumber = scrobbleInfo.TrackNumber.ToString();
scrobble.TrackDuration = TimeSpan.FromMilliseconds(scrobbleInfo.Duration ?? 0);
return scrobble;
} }
/// <summary> /// <summary>
@ -68,8 +92,11 @@ namespace Roadie.Library.Scrobble
/// </summary> /// </summary>
public async Task<OperationResult<bool>> NowPlaying(User user, ScrobbleInfo scrobble) public async Task<OperationResult<bool>> NowPlaying(User user, ScrobbleInfo scrobble)
{ {
var s = GetScrobbleInfoDetails(scrobble); var s = await GetScrobbleInfoDetailsAsync(scrobble).ConfigureAwait(false);
foreach (var scrobbler in Scrobblers) await Task.Run(async () => await scrobbler.NowPlaying(user, s)); foreach (var scrobbler in Scrobblers)
{
await scrobbler.NowPlaying(user, s).ConfigureAwait(false);
}
return new OperationResult<bool> return new OperationResult<bool>
{ {
Data = true, Data = true,
@ -82,10 +109,10 @@ namespace Roadie.Library.Scrobble
/// </summary> /// </summary>
public async Task<OperationResult<bool>> Scrobble(User user, ScrobbleInfo scrobble) public async Task<OperationResult<bool>> Scrobble(User user, ScrobbleInfo scrobble)
{ {
var s = GetScrobbleInfoDetails(scrobble); var s = await GetScrobbleInfoDetailsAsync(scrobble).ConfigureAwait(false);
foreach (var scrobbler in Scrobblers) foreach (var scrobbler in Scrobblers)
{ {
await Task.Run(async () => await scrobbler.Scrobble(user, s)); await scrobbler.Scrobble(user, s).ConfigureAwait(false);
} }
return new OperationResult<bool> return new OperationResult<bool>
{ {
@ -93,29 +120,5 @@ namespace Roadie.Library.Scrobble
IsSuccess = true IsSuccess = true
}; };
} }
private ScrobbleInfo GetScrobbleInfoDetails(ScrobbleInfo scrobble)
{
var scrobbleInfo = (from t in DbContext.Tracks
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
join a in DbContext.Artists on r.ArtistId equals a.Id
where t.RoadieId == scrobble.TrackId
select new
{
ArtistName = a.Name,
ReleaseTitle = r.Title,
TrackTitle = t.Title,
t.TrackNumber,
t.Duration
}).FirstOrDefault();
scrobble.ArtistName = scrobbleInfo.ArtistName;
scrobble.ReleaseTitle = scrobbleInfo.ReleaseTitle;
scrobble.TrackTitle = scrobbleInfo.TrackTitle;
scrobble.TrackNumber = scrobbleInfo.TrackNumber.ToString();
scrobble.TrackDuration = TimeSpan.FromMilliseconds(scrobbleInfo.Duration ?? 0);
return scrobble;
}
} }
} }

View file

@ -44,11 +44,11 @@ namespace Roadie.Library.SearchEngines.Imaging
return request; return request;
} }
public override async Task<IEnumerable<ImageSearchResult>> PerformImageSearch(string query, int resultsCount) public override async Task<IEnumerable<ImageSearchResult>> PerformImageSearchAsync(string query, int resultsCount)
{ {
var request = BuildRequest(query, resultsCount); var request = BuildRequest(query, resultsCount);
var response = await _client.ExecuteAsync<BingImageResult>(request); var response = await _client.ExecuteAsync<BingImageResult>(request).ConfigureAwait(false);
if (response.StatusCode == HttpStatusCode.Unauthorized) if (response.StatusCode == HttpStatusCode.Unauthorized)
throw new AuthenticationException("Api Key is not correct"); throw new AuthenticationException("Api Key is not correct");

View file

@ -10,6 +10,6 @@ namespace Roadie.Library.SearchEngines.Imaging
RestRequest BuildRequest(string query, int resultsCount); RestRequest BuildRequest(string query, int resultsCount);
Task<IEnumerable<ImageSearchResult>> PerformImageSearch(string query, int resultsCount); Task<IEnumerable<ImageSearchResult>> PerformImageSearchAsync(string query, int resultsCount);
} }
} }

View file

@ -25,14 +25,14 @@ namespace Roadie.Library.SearchEngines.Imaging
public override bool IsEnabled => Configuration.Integrations.ITunesProviderEnabled; public override bool IsEnabled => Configuration.Integrations.ITunesProviderEnabled;
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount) public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearchAsync(string query, int resultsCount)
{ {
ArtistSearchResult data = null; ArtistSearchResult data = null;
try try
{ {
var request = BuildRequest(query, 1, "musicArtist"); var request = BuildRequest(query, 1, "musicArtist");
var response = await _client.ExecuteAsync<ITunesSearchResult>(request); var response = await _client.ExecuteAsync<ITunesSearchResult>(request).ConfigureAwait(false);
if (response.ResponseStatus == ResponseStatus.Error) if (response.ResponseStatus == ResponseStatus.Error)
{ {
if (response.StatusCode == HttpStatusCode.Unauthorized) if (response.StatusCode == HttpStatusCode.Unauthorized)
@ -79,7 +79,7 @@ namespace Roadie.Library.SearchEngines.Imaging
#pragma warning disable CS1998 #pragma warning disable CS1998
public override async Task<IEnumerable<ImageSearchResult>> PerformImageSearch(string query, int resultsCount) public override async Task<IEnumerable<ImageSearchResult>> PerformImageSearchAsync(string query, int resultsCount)
{ {
var request = BuildRequest(query, resultsCount); var request = BuildRequest(query, resultsCount);
ImageSearchResult[] result = null; ImageSearchResult[] result = null;
@ -116,7 +116,7 @@ namespace Roadie.Library.SearchEngines.Imaging
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query, int resultsCount) public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query, int resultsCount)
{ {
var request = BuildRequest(query, 1, "album"); var request = BuildRequest(query, 1, "album");
var response = await _client.ExecuteAsync<ITunesSearchResult>(request); var response = await _client.ExecuteAsync<ITunesSearchResult>(request).ConfigureAwait(false);
if (response.ResponseStatus == ResponseStatus.Error) if (response.ResponseStatus == ResponseStatus.Error)
{ {
if (response.StatusCode == HttpStatusCode.Unauthorized) if (response.StatusCode == HttpStatusCode.Unauthorized)

View file

@ -50,7 +50,7 @@ namespace Roadie.Library.SearchEngines.Imaging
public abstract RestRequest BuildRequest(string query, int resultsCount); public abstract RestRequest BuildRequest(string query, int resultsCount);
public virtual Task<IEnumerable<ImageSearchResult>> PerformImageSearch(string query, int resultsCount) public virtual Task<IEnumerable<ImageSearchResult>> PerformImageSearchAsync(string query, int resultsCount)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View file

@ -37,7 +37,7 @@ namespace Roadie.Library.SearchEngines.Imaging
} }
if (Configuration.Integrations.BingImageSearchEngineEnabled) if (Configuration.Integrations.BingImageSearchEngineEnabled)
{ {
var bingResults = await _bingSearchEngine.PerformImageSearch(query, count); var bingResults = await _bingSearchEngine.PerformImageSearchAsync(query, count).ConfigureAwait(false);
if (bingResults != null) if (bingResults != null)
{ {
result.AddRange(bingResults); result.AddRange(bingResults);
@ -45,7 +45,7 @@ namespace Roadie.Library.SearchEngines.Imaging
} }
if (Configuration.Integrations.ITunesProviderEnabled) if (Configuration.Integrations.ITunesProviderEnabled)
{ {
var iTunesResults = await _itunesSearchEngine.PerformImageSearch(query, count); var iTunesResults = await _itunesSearchEngine.PerformImageSearchAsync(query, count).ConfigureAwait(false);
if (iTunesResults != null) if (iTunesResults != null)
{ {
result.AddRange(iTunesResults); result.AddRange(iTunesResults);

View file

@ -103,11 +103,11 @@ namespace Roadie.Library.MetaData.Audio
if (!result.IsValid) if (!result.IsValid)
{ {
tagSources.Add("MusicBrainz"); tagSources.Add("MusicBrainz");
result = await ParseFromMusicBrainz(result); result = await ParseFromMusicBrainzAsync(result).ConfigureAwait(false);
if (!result.IsValid) if (!result.IsValid)
{ {
tagSources.Add("LastFm"); tagSources.Add("LastFm");
result = await GetFromLastFmIntegration(result); result = await GetFromLastFmIntegrationAsync(result).ConfigureAwait(false);
} }
} }
@ -183,15 +183,18 @@ namespace Roadie.Library.MetaData.Audio
return result; return result;
} }
private async Task<AudioMetaData> GetFromLastFmIntegration(AudioMetaData metaData) private async Task<AudioMetaData> GetFromLastFmIntegrationAsync(AudioMetaData metaData)
{ {
var artistName = metaData.Artist; var artistName = metaData.Artist;
var ReleaseName = metaData.Release; var ReleaseName = metaData.Release;
if (DoParseFromLastFM) if (DoParseFromLastFM)
{ {
if (string.IsNullOrEmpty(artistName) && string.IsNullOrEmpty(ReleaseName)) return metaData; if (string.IsNullOrEmpty(artistName) && string.IsNullOrEmpty(ReleaseName))
var lastFmReleaseTracks = await LastFmHelper.TracksForRelease(artistName, ReleaseName); {
return metaData;
}
var lastFmReleaseTracks = await LastFmHelper.TracksForReleaseAsync(artistName, ReleaseName).ConfigureAwait(false);
if (lastFmReleaseTracks != null) if (lastFmReleaseTracks != null)
{ {
var lastFmReleaseTrack = lastFmReleaseTracks.FirstOrDefault(x => var lastFmReleaseTrack = lastFmReleaseTracks.FirstOrDefault(x =>
@ -224,12 +227,11 @@ namespace Roadie.Library.MetaData.Audio
return metaData; return metaData;
} }
private async Task<AudioMetaData> ParseFromMusicBrainz(AudioMetaData metaData) private async Task<AudioMetaData> ParseFromMusicBrainzAsync(AudioMetaData metaData)
{ {
if (DoParseFromMusicBrainz) if (DoParseFromMusicBrainz)
{ {
var musicBrainzReleaseTracks = var musicBrainzReleaseTracks = await MusicBrainzProvider.MusicBrainzReleaseTracksAsync(metaData.Artist, metaData.Release).ConfigureAwait(false);
await MusicBrainzProvider.MusicBrainzReleaseTracks(metaData.Artist, metaData.Release);
if (musicBrainzReleaseTracks != null) if (musicBrainzReleaseTracks != null)
{ {
var musicBrainzReleaseTrack = musicBrainzReleaseTracks.FirstOrDefault(x => var musicBrainzReleaseTrack = musicBrainzReleaseTracks.FirstOrDefault(x =>

View file

@ -24,7 +24,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
_apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey") ?? new ApiKey(); _apiKey = configuration.Integrations.ApiKeys.FirstOrDefault(x => x.ApiName == "DiscogsConsumerKey") ?? new ApiKey();
} }
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount) public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearchAsync(string query, int resultsCount)
{ {
ArtistSearchResult data = null; ArtistSearchResult data = null;
try try
@ -40,7 +40,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
UserAgent = WebHelper.UserAgent UserAgent = WebHelper.UserAgent
}; };
var response = await client.ExecuteAsync<DiscogsResult>(request); var response = await client.ExecuteAsync<DiscogsResult>(request).ConfigureAwait(false);
if (response.ResponseStatus == ResponseStatus.Error) if (response.ResponseStatus == ResponseStatus.Error)
{ {
@ -61,7 +61,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
{ {
UserAgent = WebHelper.UserAgent UserAgent = WebHelper.UserAgent
}; };
var artistResponse = await c2.ExecuteTaskAsync<DiscogArtistResponse>(request); var artistResponse = await c2.ExecuteTaskAsync<DiscogArtistResponse>(request).ConfigureAwait(false);
var artist = artistResponse.Data; var artist = artistResponse.Data;
if (artist != null) if (artist != null)
{ {
@ -99,7 +99,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
} }
} }
return null; return null;
}, "uri:metadata"); }, "uri:metadata").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -126,7 +126,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
var client = new RestClient("https://api.discogs.com/database"); var client = new RestClient("https://api.discogs.com/database");
client.UserAgent = WebHelper.UserAgent; client.UserAgent = WebHelper.UserAgent;
var response = await client.ExecuteAsync<DiscogsResult>(request); var response = await client.ExecuteAsync<DiscogsResult>(request).ConfigureAwait(false);
if (response.ResponseStatus == ResponseStatus.Error) if (response.ResponseStatus == ResponseStatus.Error)
{ {
@ -144,7 +144,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
request = BuildLabelRequest(responseData.id); request = BuildLabelRequest(responseData.id);
var c2 = new RestClient("https://api.discogs.com/"); var c2 = new RestClient("https://api.discogs.com/");
c2.UserAgent = WebHelper.UserAgent; c2.UserAgent = WebHelper.UserAgent;
var labelResponse = await c2.ExecuteTaskAsync<DiscogsLabelResult>(request); var labelResponse = await c2.ExecuteTaskAsync<DiscogsLabelResult>(request).ConfigureAwait(false);
var label = labelResponse.Data; var label = labelResponse.Data;
if (label != null) if (label != null)
{ {
@ -157,7 +157,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
if (label.images != null) if (label.images != null)
{ {
images.AddRange(label.images.Where(x => x.type != "primary").Select(x => x.uri)); images.AddRange(label.images.Where(x => x.type != "primary").Select(x => x.uri));
var primaryImage = label.images.FirstOrDefault(x => x.type == "primary"); var primaryImage = label.images.Find(x => x.type == "primary");
if (primaryImage != null) labelThumbnailUrl = primaryImage.uri; if (primaryImage != null) labelThumbnailUrl = primaryImage.uri;
if (string.IsNullOrEmpty(labelThumbnailUrl)) if (string.IsNullOrEmpty(labelThumbnailUrl))
labelThumbnailUrl = label.images.First(x => !string.IsNullOrEmpty(x.uri)).uri; labelThumbnailUrl = label.images.First(x => !string.IsNullOrEmpty(x.uri)).uri;
@ -176,7 +176,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
} }
} }
return null; return null;
}, "uri:metadata"); }, "uri:metadata").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -207,7 +207,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
Timeout = SafeParser.ToNumber<int>(Configuration.Integrations.DiscogsTimeout) Timeout = SafeParser.ToNumber<int>(Configuration.Integrations.DiscogsTimeout)
}; };
var response = await client.ExecuteAsync<DiscogsReleaseSearchResult>(request); var response = await client.ExecuteAsync<DiscogsReleaseSearchResult>(request).ConfigureAwait(false);
if (response?.ResponseStatus == null || response.ResponseStatus == ResponseStatus.Error) if (response?.ResponseStatus == null || response.ResponseStatus == ResponseStatus.Error)
{ {
if (response.StatusCode == HttpStatusCode.Unauthorized) if (response.StatusCode == HttpStatusCode.Unauthorized)
@ -225,7 +225,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
{ {
UserAgent = WebHelper.UserAgent UserAgent = WebHelper.UserAgent
}; };
var releaseResult = await c2.ExecuteTaskAsync<DiscogReleaseDetail>(request); var releaseResult = await c2.ExecuteTaskAsync<DiscogReleaseDetail>(request).ConfigureAwait(false);
var release = releaseResult?.Data; var release = releaseResult?.Data;
if (release != null) if (release != null)
{ {
@ -296,7 +296,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
if (release.identifiers != null) if (release.identifiers != null)
{ {
var barcode = release.identifiers.FirstOrDefault(x => (x.type ?? string.Empty) == "Barcode"); var barcode = release.identifiers.Find(x => (x.type ?? string.Empty) == "Barcode");
if (barcode?.value != null) if (barcode?.value != null)
{ {
data.Tags = new[] { "barcode:" + barcode.value }; data.Tags = new[] { "barcode:" + barcode.value };
@ -305,7 +305,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Discogs
} }
} }
return null; return null;
}, "uri:metadata"); }, "uri:metadata").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {

View file

@ -7,6 +7,6 @@ namespace Roadie.Library.SearchEngines.MetaData
{ {
bool IsEnabled { get; } bool IsEnabled { get; }
Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount); Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearchAsync(string query, int resultsCount);
} }
} }

View file

@ -10,6 +10,6 @@ namespace Roadie.Library.MetaData.LastFm
{ {
Task<OperationResult<string>> GetSessionKeyForUserToken(string token); Task<OperationResult<string>> GetSessionKeyForUserToken(string token);
Task<IEnumerable<AudioMetaData>> TracksForRelease(string artist, string Release); Task<IEnumerable<AudioMetaData>> TracksForReleaseAsync(string artist, string Release);
} }
} }

View file

@ -86,7 +86,7 @@ namespace Roadie.Library.MetaData.LastFm
}; };
var request = new RestRequest(Method.GET); var request = new RestRequest(Method.GET);
var client = new RestClient(BuildUrl("auth.getSession", parameters)); var client = new RestClient(BuildUrl("auth.getSession", parameters));
var responseXML = await client.ExecuteAsync<string>(request); var responseXML = await client.ExecuteAsync<string>(request).ConfigureAwait(false);
var doc = new XmlDocument(); var doc = new XmlDocument();
doc.LoadXml(responseXML.Content); doc.LoadXml(responseXML.Content);
var sessionKey = doc.GetElementsByTagName("key")[0].InnerText; var sessionKey = doc.GetElementsByTagName("key")[0].InnerText;
@ -139,7 +139,7 @@ namespace Roadie.Library.MetaData.LastFm
var xp = GetResponseAsXml(request); var xp = GetResponseAsXml(request);
Logger.LogTrace($"LastFmHelper: RoadieUser `{roadieUser}` NowPlaying `{scrobble}` LastFmResult [{xp.InnerXml}]"); Logger.LogTrace($"LastFmHelper: RoadieUser `{roadieUser}` NowPlaying `{scrobble}` LastFmResult [{xp.InnerXml}]");
result = true; result = true;
}); }).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -153,7 +153,7 @@ namespace Roadie.Library.MetaData.LastFm
}; };
} }
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount) public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearchAsync(string query, int resultsCount)
{ {
try try
{ {
@ -163,7 +163,7 @@ namespace Roadie.Library.MetaData.LastFm
Logger.LogTrace("LastFmHelper:PerformArtistSearch:{0}", query); Logger.LogTrace("LastFmHelper:PerformArtistSearch:{0}", query);
var auth = new LastAuth(ApiKey.Key, ApiKey.KeySecret); var auth = new LastAuth(ApiKey.Key, ApiKey.KeySecret);
var albumApi = new ArtistApi(auth); var albumApi = new ArtistApi(auth);
var response = await albumApi.GetInfoAsync(query); var response = await albumApi.GetInfoAsync(query).ConfigureAwait(false);
if (!response.Success) if (!response.Success)
{ {
return null; return null;
@ -183,7 +183,7 @@ namespace Roadie.Library.MetaData.LastFm
result.Urls = new[] { lastFmArtist.Url.ToString() }; result.Urls = new[] { lastFmArtist.Url.ToString() };
} }
return result; return result;
}, "uri:metadata"); }, "uri:metadata").ConfigureAwait(false);
return new OperationResult<IEnumerable<ArtistSearchResult>> return new OperationResult<IEnumerable<ArtistSearchResult>>
{ {
IsSuccess = data != null, IsSuccess = data != null,
@ -205,7 +205,7 @@ namespace Roadie.Library.MetaData.LastFm
{ {
var request = new RestRequest(Method.GET); var request = new RestRequest(Method.GET);
var client = new RestClient(string.Format("http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={0}&artist={1}&album={2}&format=xml", ApiKey.Key, artistName, query)); var client = new RestClient(string.Format("http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={0}&artist={1}&album={2}&format=xml", ApiKey.Key, artistName, query));
var responseData = await client.ExecuteAsync<lfm>(request); var responseData = await client.ExecuteAsync<lfm>(request).ConfigureAwait(false);
ReleaseSearchResult result = null; ReleaseSearchResult result = null;
@ -246,7 +246,7 @@ namespace Roadie.Library.MetaData.LastFm
} }
} }
return result; return result;
}, "uri:metadata"); }, "uri:metadata").ConfigureAwait(false);
return new OperationResult<IEnumerable<ReleaseSearchResult>> return new OperationResult<IEnumerable<ReleaseSearchResult>>
{ {
IsSuccess = data != null, IsSuccess = data != null,
@ -269,7 +269,7 @@ namespace Roadie.Library.MetaData.LastFm
// If less than half of duration then create a NowPlaying // If less than half of duration then create a NowPlaying
if (scrobble.ElapsedTimeOfTrackPlayed.TotalMinutes < 4 || if (scrobble.ElapsedTimeOfTrackPlayed.TotalMinutes < 4 ||
scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds < scrobble.TrackDuration.TotalSeconds / 2) scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds < scrobble.TrackDuration.TotalSeconds / 2)
return await NowPlaying(roadieUser, scrobble); return await NowPlaying(roadieUser, scrobble).ConfigureAwait(false);
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId); var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId);
@ -319,7 +319,7 @@ namespace Roadie.Library.MetaData.LastFm
}; };
} }
public async Task<IEnumerable<AudioMetaData>> TracksForRelease(string artist, string Release) public async Task<IEnumerable<AudioMetaData>> TracksForReleaseAsync(string artist, string Release)
{ {
if (string.IsNullOrEmpty(artist) || string.IsNullOrEmpty(Release)) return null; if (string.IsNullOrEmpty(artist) || string.IsNullOrEmpty(Release)) return null;
var result = new List<AudioMetaData>(); var result = new List<AudioMetaData>();
@ -333,7 +333,7 @@ namespace Roadie.Library.MetaData.LastFm
{ {
var auth = new LastAuth(ApiKey.Key, ApiKey.KeySecret); var auth = new LastAuth(ApiKey.Key, ApiKey.KeySecret);
var albumApi = new AlbumApi(auth); // this is an unauthenticated call to the API var albumApi = new AlbumApi(auth); // this is an unauthenticated call to the API
var response = await albumApi.GetInfoAsync(artist, Release); var response = await albumApi.GetInfoAsync(artist, Release).ConfigureAwait(false);
releaseInfo = response.Content; releaseInfo = response.Content;
if (releaseInfo != null) CacheManager.Add(responseCacheKey, releaseInfo); if (releaseInfo != null) CacheManager.Add(responseCacheKey, releaseInfo);
} }

View file

@ -7,6 +7,6 @@ namespace Roadie.Library.MetaData.MusicBrainz
{ {
public interface IMusicBrainzProvider : IArtistSearchEngine, IReleaseSearchEngine public interface IMusicBrainzProvider : IArtistSearchEngine, IReleaseSearchEngine
{ {
Task<IEnumerable<AudioMetaData>> MusicBrainzReleaseTracks(string artistName, string releaseTitle); Task<IEnumerable<AudioMetaData>> MusicBrainzReleaseTracksAsync(string artistName, string releaseTitle);
} }
} }

View file

@ -23,14 +23,14 @@ namespace Roadie.Library.MetaData.MusicBrainz
Repository = new MusicBrainzRepository(configuration, logger); Repository = new MusicBrainzRepository(configuration, logger);
} }
public async Task<IEnumerable<AudioMetaData>> MusicBrainzReleaseTracks(string artistName, string releaseTitle) public async Task<IEnumerable<AudioMetaData>> MusicBrainzReleaseTracksAsync(string artistName, string releaseTitle)
{ {
try try
{ {
if (string.IsNullOrEmpty(artistName) && string.IsNullOrEmpty(releaseTitle)) return null; if (string.IsNullOrEmpty(artistName) && string.IsNullOrEmpty(releaseTitle)) return null;
// Find the Artist // Find the Artist
var artistCacheKey = string.Format("uri:musicbrainz:artist:{0}", artistName); var artistCacheKey = string.Format("uri:musicbrainz:artist:{0}", artistName);
var artistSearch = await PerformArtistSearch(artistName, 1); var artistSearch = await PerformArtistSearchAsync(artistName, 1).ConfigureAwait(false);
if (!artistSearch.IsSuccess) return null; if (!artistSearch.IsSuccess) return null;
var artist = artistSearch.Data.First(); var artist = artistSearch.Data.First();
@ -56,8 +56,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
} }
// Now get The Release Details // Now get The Release Details
release = await MusicBrainzRequestHelper.GetAsync<Release>( release = await MusicBrainzRequestHelper.GetAsync<Release>(MusicBrainzRequestHelper.CreateLookupUrl("release", ReleaseResult.MusicBrainzId, "recordings")).ConfigureAwait(false);
MusicBrainzRequestHelper.CreateLookupUrl("release", ReleaseResult.MusicBrainzId, "recordings"));
if (release == null) return null; if (release == null) return null;
CacheManager.Add(ReleaseCacheKey, release); CacheManager.Add(ReleaseCacheKey, release);
} }
@ -113,12 +112,12 @@ namespace Roadie.Library.MetaData.MusicBrainz
return null; return null;
} }
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount) public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearchAsync(string query, int resultsCount)
{ {
ArtistSearchResult result = null; ArtistSearchResult result = null;
Logger.LogTrace("MusicBrainzProvider:PerformArtistSearch:{0}", query); Logger.LogTrace("MusicBrainzProvider:PerformArtistSearch:{0}", query);
var a = await Repository.ArtistByName(query, resultsCount); var a = await Repository.ArtistByNameAsync(query, resultsCount).ConfigureAwait(false);
if (a != null) if (a != null)
{ {
var imageUrls = a.relations?.Where(x => x.type.Equals("image", StringComparison.OrdinalIgnoreCase)).Select(x => x.url.resource).Distinct().ToArray(); var imageUrls = a.relations?.Where(x => x.type.Equals("image", StringComparison.OrdinalIgnoreCase)).Select(x => x.url.resource).Distinct().ToArray();
@ -174,7 +173,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
ReleaseSearchResult result = null; ReleaseSearchResult result = null;
try try
{ {
var releaseInfosForArtist = await ReleasesForArtist(artistName); var releaseInfosForArtist = await ReleasesForArtistAsync(artistName).ConfigureAwait(false);
if (releaseInfosForArtist != null) if (releaseInfosForArtist != null)
{ {
var r = releaseInfosForArtist.FirstOrDefault(x => x.title.Equals(query, StringComparison.OrdinalIgnoreCase)); var r = releaseInfosForArtist.FirstOrDefault(x => x.title.Equals(query, StringComparison.OrdinalIgnoreCase));
@ -208,7 +207,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
ReleaseGenres = r.releasegroup?.genres?.Select(x => x.name).Distinct().ToArray(), ReleaseGenres = r.releasegroup?.genres?.Select(x => x.name).Distinct().ToArray(),
ReleaseType = r.releasegroup?.primarytype ReleaseType = r.releasegroup?.primarytype
}; };
var coverUrls = await CoverArtForMusicBrainzReleaseById(r.id); var coverUrls = await CoverArtForMusicBrainzReleaseByIdAsync(r.id).ConfigureAwait(false);
if (coverUrls != null) if (coverUrls != null)
{ {
var frontCover = coverUrls.images.FirstOrDefault(i => i.front); var frontCover = coverUrls.images.FirstOrDefault(i => i.front);
@ -296,16 +295,13 @@ namespace Roadie.Library.MetaData.MusicBrainz
}; };
} }
private async Task<CoverArtArchivesResult> CoverArtForMusicBrainzReleaseById(string musicBrainzId) private Task<CoverArtArchivesResult> CoverArtForMusicBrainzReleaseByIdAsync(string musicBrainzId) => MusicBrainzRequestHelper.GetAsync<CoverArtArchivesResult>(MusicBrainzRequestHelper.CreateCoverArtReleaseUrl(musicBrainzId));
{
return await MusicBrainzRequestHelper.GetAsync<CoverArtArchivesResult>(MusicBrainzRequestHelper.CreateCoverArtReleaseUrl(musicBrainzId));
}
private async Task<IEnumerable<Release>> ReleasesForArtist(string artist, string artistMusicBrainzId = null) private async Task<IEnumerable<Release>> ReleasesForArtistAsync(string artist, string artistMusicBrainzId = null)
{ {
if (string.IsNullOrEmpty(artistMusicBrainzId)) if (string.IsNullOrEmpty(artistMusicBrainzId))
{ {
var artistSearch = await PerformArtistSearch(artist, 1); var artistSearch = await PerformArtistSearchAsync(artist, 1).ConfigureAwait(false);
if (artistSearch == null || !artistSearch.IsSuccess) if (artistSearch == null || !artistSearch.IsSuccess)
{ {
return null; return null;
@ -317,7 +313,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
} }
artistMusicBrainzId = mbArtist.MusicBrainzId; artistMusicBrainzId = mbArtist.MusicBrainzId;
} }
return await Repository.ReleasesForArtist(artistMusicBrainzId); return await Repository.ReleasesForArtist(artistMusicBrainzId).ConfigureAwait(false);
} }
} }
} }

View file

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Roadie.Library.MetaData.MusicBrainz namespace Roadie.Library.MetaData.MusicBrainz
@ -21,7 +20,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
Logger = logger; Logger = logger;
var location = System.Reflection.Assembly.GetEntryAssembly().Location; var location = System.Reflection.Assembly.GetEntryAssembly().Location;
var directory = configuration.SearchEngineReposFolder ?? Path.Combine(System.IO.Path.GetDirectoryName(location), "SearchEngineRepos"); var directory = configuration.SearchEngineReposFolder ?? Path.Combine(System.IO.Path.GetDirectoryName(location), "SearchEngineRepos");
if(!Directory.Exists(directory)) if (!Directory.Exists(directory))
{ {
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
} }
@ -33,7 +32,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
/// </summary> /// </summary>
/// <param name="name">Query name of Artist</param> /// <param name="name">Query name of Artist</param>
/// <param name="resultsCount">Maximum Number of Results</param> /// <param name="resultsCount">Maximum Number of Results</param>
public async Task<Artist> ArtistByName(string name, int? resultsCount = null) public async Task<Artist> ArtistByNameAsync(string name, int? resultsCount = null)
{ {
Artist result = null; Artist result = null;
@ -48,14 +47,14 @@ namespace Roadie.Library.MetaData.MusicBrainz
if (artist == null) if (artist == null)
{ {
// Perform a query to get the MbId for the Name // Perform a query to get the MbId for the Name
var artistResult = await MusicBrainzRequestHelper.GetAsync<ArtistResult>(MusicBrainzRequestHelper.CreateSearchTemplate("artist", name, resultsCount ?? 1, 0)); var artistResult = await MusicBrainzRequestHelper.GetAsync<ArtistResult>(MusicBrainzRequestHelper.CreateSearchTemplate("artist", name, resultsCount ?? 1, 0)).ConfigureAwait(false);
if (artistResult == null || artistResult.artists == null || !artistResult.artists.Any() || artistResult.count < 1) if (artistResult == null || artistResult.artists == null || !artistResult.artists.Any() || artistResult.count < 1)
{ {
return null; return null;
} }
var mbId = artistResult.artists.First().id; var mbId = artistResult.artists.First().id;
// Now perform a detail request to get the details by the MbId // Now perform a detail request to get the details by the MbId
result = await MusicBrainzRequestHelper.GetAsync<Artist>(MusicBrainzRequestHelper.CreateLookupUrl("artist", mbId, "aliases+tags+genres+url-rels")); result = await MusicBrainzRequestHelper.GetAsync<Artist>(MusicBrainzRequestHelper.CreateLookupUrl("artist", mbId, "aliases+tags+genres+url-rels")).ConfigureAwait(false);
if (result != null) if (result != null)
{ {
col.Insert(new RepositoryArtist col.Insert(new RepositoryArtist
@ -99,13 +98,13 @@ namespace Roadie.Library.MetaData.MusicBrainz
col.EnsureIndex(x => x.ArtistMbId); col.EnsureIndex(x => x.ArtistMbId);
col.EnsureIndex(x => x.Release.id); col.EnsureIndex(x => x.Release.id);
var releases = col.Find(x => x.ArtistMbId == artistMbId); var releases = col.Find(x => x.ArtistMbId == artistMbId);
if(releases == null || !releases.Any()) if (releases == null || !releases.Any())
{ {
// Query to get collection of Releases for Artist // Query to get collection of Releases for Artist
var pageSize = 50; var pageSize = 50;
var page = 0; var page = 0;
var url = MusicBrainzRequestHelper.CreateArtistBrowseTemplate(artistMbId, pageSize, 0); var url = MusicBrainzRequestHelper.CreateArtistBrowseTemplate(artistMbId, pageSize, 0);
var mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync<ReleaseBrowseResult>(url); var mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync<ReleaseBrowseResult>(url).ConfigureAwait(false);
var totalReleases = mbReleaseBrowseResult != null ? mbReleaseBrowseResult.releasecount : 0; var totalReleases = mbReleaseBrowseResult != null ? mbReleaseBrowseResult.releasecount : 0;
var totalPages = Math.Ceiling((decimal)totalReleases / pageSize); var totalPages = Math.Ceiling((decimal)totalReleases / pageSize);
var fetchResult = new List<Release>(); var fetchResult = new List<Release>();
@ -116,7 +115,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
fetchResult.AddRange(mbReleaseBrowseResult.releases.Where(x => !string.IsNullOrEmpty(x.date))); fetchResult.AddRange(mbReleaseBrowseResult.releases.Where(x => !string.IsNullOrEmpty(x.date)));
} }
page++; page++;
mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync<ReleaseBrowseResult>(MusicBrainzRequestHelper.CreateArtistBrowseTemplate(artistMbId, pageSize, pageSize * page)); mbReleaseBrowseResult = await MusicBrainzRequestHelper.GetAsync<ReleaseBrowseResult>(MusicBrainzRequestHelper.CreateArtistBrowseTemplate(artistMbId, pageSize, pageSize * page)).ConfigureAwait(false);
} while (page < totalPages); } while (page < totalPages);
col.InsertBulk(fetchResult.Select(x => new RepositoryRelease col.InsertBulk(fetchResult.Select(x => new RepositoryRelease
{ {

View file

@ -55,7 +55,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
using (var webClient = new WebClient()) using (var webClient = new WebClient())
{ {
webClient.Headers.Add("user-agent", WebHelper.UserAgent); webClient.Headers.Add("user-agent", WebHelper.UserAgent);
result = JsonConvert.DeserializeObject<T>(await webClient.DownloadStringTaskAsync(new Uri(url))); result = JsonConvert.DeserializeObject<T>(await webClient.DownloadStringTaskAsync(new Uri(url)).ConfigureAwait(false));
} }
} }
catch (WebException ex) catch (WebException ex)

View file

@ -23,7 +23,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify
{ {
} }
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearchAsync(string query,
int resultsCount) int resultsCount)
{ {
ArtistSearchResult data = null; ArtistSearchResult data = null;
@ -37,7 +37,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify
var client = new RestClient("http://api.spotify.com/v1"); var client = new RestClient("http://api.spotify.com/v1");
client.UserAgent = WebHelper.UserAgent; client.UserAgent = WebHelper.UserAgent;
var response = await client.ExecuteAsync<SpotifyResult>(request); var response = await client.ExecuteAsync<SpotifyResult>(request).ConfigureAwait(false);
if (response.ResponseStatus == ResponseStatus.Error) if (response.ResponseStatus == ResponseStatus.Error)
{ {
@ -79,7 +79,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName,
string query, int resultsCount) string query, int resultsCount)
{ {
var artistResult = await PerformArtistSearch(artistName, resultsCount); var artistResult = await PerformArtistSearchAsync(artistName, resultsCount).ConfigureAwait(false);
if (!artistResult.IsSuccess) return new OperationResult<IEnumerable<ReleaseSearchResult>>(); if (!artistResult.IsSuccess) return new OperationResult<IEnumerable<ReleaseSearchResult>>();
try try
{ {
@ -88,7 +88,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify
ReleaseSearchResult result = null; ReleaseSearchResult result = null;
var response = await AlbumsForArtist(artistResult.Data.First().SpotifyId); var response = await AlbumsForArtistAsync(artistResult.Data.First().SpotifyId).ConfigureAwait(false);
if (response != null && response.items != null) if (response != null && response.items != null)
{ {
string foundByAlternateName = null; string foundByAlternateName = null;
@ -104,7 +104,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify
} }
var client = new RestClient(string.Format("http://api.spotify.com/v1/albums/{0}", spotifyAlbum.id)); var client = new RestClient(string.Format("http://api.spotify.com/v1/albums/{0}", spotifyAlbum.id));
var albumResult = await client.ExecuteAsync<AlbumResult>(request); var albumResult = await client.ExecuteAsync<AlbumResult>(request).ConfigureAwait(false);
if (albumResult != null && albumResult.Data != null) if (albumResult != null && albumResult.Data != null)
{ {
var sa = albumResult.Data; var sa = albumResult.Data;
@ -203,7 +203,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify
return new OperationResult<IEnumerable<ReleaseSearchResult>>(); return new OperationResult<IEnumerable<ReleaseSearchResult>>();
} }
private async Task<Albums> AlbumsForArtist(string spotifyId) private async Task<Albums> AlbumsForArtistAsync(string spotifyId)
{ {
var cacheKey = string.Format("uri:spotify:AlbumsForArtist:{0}", spotifyId); var cacheKey = string.Format("uri:spotify:AlbumsForArtist:{0}", spotifyId);
var result = CacheManager.Get<Albums>(cacheKey); var result = CacheManager.Get<Albums>(cacheKey);
@ -213,7 +213,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Spotify
var client = new RestClient(string.Format( var client = new RestClient(string.Format(
"http://api.spotify.com/v1/artists/{0}/albums?offset=0&limit=25&album_type=album&market=US", "http://api.spotify.com/v1/artists/{0}/albums?offset=0&limit=25&album_type=album&market=US",
spotifyId)); spotifyId));
var artistAlbumsResponse = await client.ExecuteAsync<Albums>(request); var artistAlbumsResponse = await client.ExecuteAsync<Albums>(request).ConfigureAwait(false);
result = artistAlbumsResponse != null && artistAlbumsResponse.Data != null result = artistAlbumsResponse != null && artistAlbumsResponse.Data != null
? artistAlbumsResponse.Data ? artistAlbumsResponse.Data
: null; : null;

View file

@ -21,7 +21,7 @@ namespace Roadie.Library.SearchEngines.MetaData.Wikipedia
HttpEncoder = httpEncoder; HttpEncoder = httpEncoder;
} }
public Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount) public Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearchAsync(string query, int resultsCount)
{ {
if(string.IsNullOrEmpty(query) || resultsCount == 0) if(string.IsNullOrEmpty(query) || resultsCount == 0)
{ {

View file

@ -66,7 +66,7 @@ namespace Roadie.Library.Utility
using (var webClient = new WebClient()) using (var webClient = new WebClient())
{ {
webClient.Headers.Add("user-agent", UserAgent); webClient.Headers.Add("user-agent", UserAgent);
imageBytes = await webClient.DownloadDataTaskAsync(new Uri(url)); imageBytes = await webClient.DownloadDataTaskAsync(new Uri(url)).ConfigureAwait(false);
} }
} }
catch catch

View file

@ -1,5 +1,4 @@
using Mapster; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -29,9 +28,10 @@ namespace Roadie.Api.Services
{ {
public class AdminService : ServiceBase, IAdminService public class AdminService : ServiceBase, IAdminService
{ {
protected IHubContext<ScanActivityHub> ScanActivityHub { get; } private IArtistLookupEngine ArtistLookupEngine { get; }
private IArtistService ArtistService { get; } private IArtistService ArtistService { get; }
private IBookmarkService BookmarkService { get; }
private IEventMessageLogger EventMessageLogger { get; } private IEventMessageLogger EventMessageLogger { get; }
@ -39,12 +39,12 @@ namespace Roadie.Api.Services
private IGenreService GenreService { get; } private IGenreService GenreService { get; }
private ILabelService LabelService { get; } private ILabelService LabelService { get; }
private IArtistLookupEngine ArtistLookupEngine { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; } private IReleaseLookupEngine ReleaseLookupEngine { get; }
private IBookmarkService BookmarkService { get; }
private IReleaseService ReleaseService { get; } private IReleaseService ReleaseService { get; }
protected IHubContext<ScanActivityHub> ScanActivityHub { get; }
public AdminService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext, public AdminService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext,
IRoadieDbContext context, ICacheManager cacheManager, ILogger<ArtistService> logger, IRoadieDbContext context, ICacheManager cacheManager, ILogger<ArtistService> logger,
IHubContext<ScanActivityHub> scanActivityHub, IFileDirectoryProcessorService fileDirectoryProcessorService, IArtistService artistService, IHubContext<ScanActivityHub> scanActivityHub, IFileDirectoryProcessorService fileDirectoryProcessorService, IArtistService artistService,
@ -67,21 +67,94 @@ namespace Roadie.Api.Services
BookmarkService = bookmarkService; BookmarkService = bookmarkService;
} }
public async Task<OperationResult<bool>> DeleteArtist(User user, Guid artistId, bool deleteFolder) private void EventMessageLogger_Messages(object sender, EventMessage e) => Task.WaitAll(LogAndPublishAsync(e.Message, e.Level));
private async Task LogAndPublishAsync(string message, LogLevel level = LogLevel.Trace)
{
switch (level)
{
case LogLevel.Trace:
Logger.LogTrace(message);
break;
case LogLevel.Debug:
Logger.LogDebug(message);
break;
case LogLevel.Information:
Logger.LogInformation(message);
break;
case LogLevel.Warning:
Logger.LogWarning(message);
break;
case LogLevel.Critical:
Logger.LogCritical(message);
break;
}
await ScanActivityHub.Clients.All.SendAsync("SendSystemActivityAsync", message).ConfigureAwait(false);
}
private async Task<OperationResult<bool>> ScanFolderAsync(User user, DirectoryInfo d, bool isReadOnly, bool doDeleteFiles = true)
{
var sw = new Stopwatch();
sw.Start();
long processedFiles = 0;
await LogAndPublishAsync($"** Processing Folder: [{d.FullName}]").ConfigureAwait(false);
long processedFolders = 0;
foreach (var folder in Directory.EnumerateDirectories(d.FullName).ToArray())
{
var directoryProcessResult = await FileDirectoryProcessorService.ProcessAsync(user: user,
folder: new DirectoryInfo(folder),
doJustInfo: isReadOnly,
doDeleteFiles: doDeleteFiles).ConfigureAwait(false);
processedFolders++;
processedFiles += SafeParser.ToNumber<int>(directoryProcessResult.AdditionalData["ProcessedFiles"]);
}
CacheManager.Clear();
if (!isReadOnly)
{
Services.FileDirectoryProcessorService.DeleteEmptyFolders(d, Logger);
}
sw.Stop();
var newScanHistory = new data.ScanHistory
{
UserId = user.Id,
NewArtists = FileDirectoryProcessorService.AddedArtistIds.Count(),
NewReleases = FileDirectoryProcessorService.AddedReleaseIds.Count(),
NewTracks = FileDirectoryProcessorService.AddedTrackIds.Count(),
TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds
};
DbContext.ScanHistories.Add(newScanHistory);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
await LogAndPublishAsync($"** Completed! Processed Folders [{processedFolders}], Processed Files [{processedFiles}], New Artists [{ newScanHistory.NewArtists }], New Releases [{ newScanHistory.NewReleases }], New Tracks [{ newScanHistory.NewTracks }] : Elapsed Time [{sw.Elapsed}]").ConfigureAwait(false);
return new OperationResult<bool>
{
Data = true,
IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<bool>> DeleteArtistAsync(User user, Guid artistId, bool deleteFolder)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var errors = new List<Exception>(); var errors = new List<Exception>();
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.RoadieId == artistId).ConfigureAwait(false);
if (artist == null) if (artist == null)
{ {
await LogAndPublish($"DeleteArtist Unknown Artist [{artistId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteArtist Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]"); return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]");
} }
try try
{ {
var result = await ArtistService.Delete(user, artist, deleteFolder); var result = await ArtistService.DeleteAsync(user, artist, deleteFolder).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
return new OperationResult<bool> return new OperationResult<bool>
@ -93,22 +166,22 @@ namespace Roadie.Api.Services
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting artist."); await LogAndPublishAsync("Error deleting artist.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteArtist `{artist}`, By User `{user}`", LogLevel.Information); await LogAndPublishAsync($"DeleteArtist `{artist}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteArtistReleases(User user, Guid artistId, bool doDeleteFiles = false) public async Task<OperationResult<bool>> DeleteArtistReleasesAsync(User user, Guid artistId, bool doDeleteFiles = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -116,34 +189,34 @@ namespace Roadie.Api.Services
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null) if (artist == null)
{ {
await LogAndPublish($"DeleteArtistReleases Unknown Artist [{artistId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteArtistReleases Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]"); return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]");
} }
try try
{ {
await ReleaseService.DeleteReleases(user, DbContext.Releases.Where(x => x.ArtistId == artist.Id).Select(x => x.RoadieId).ToArray(), doDeleteFiles); await ReleaseService.DeleteReleasesAsync(user, DbContext.Releases.Where(x => x.ArtistId == artist.Id).Select(x => x.RoadieId).ToArray(), doDeleteFiles).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting artist."); await LogAndPublishAsync("Error deleting artist.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteArtistReleases `{artist}`, By User `{user}`", LogLevel.Information); await LogAndPublishAsync($"DeleteArtistReleases `{artist}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteArtistSecondaryImage(User user, Guid artistId, int index) public async Task<OperationResult<bool>> DeleteArtistSecondaryImageAsync(User user, Guid artistId, int index)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -151,7 +224,7 @@ namespace Roadie.Api.Services
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null) if (artist == null)
{ {
await LogAndPublish($"DeleteArtistSecondaryImage Unknown Artist [{artistId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteArtistSecondaryImage Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]"); return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]");
} }
@ -169,22 +242,22 @@ namespace Roadie.Api.Services
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting artist secondary image."); await LogAndPublishAsync("Error deleting artist secondary image.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteArtistSecondaryImage `{artist}` Index [{index}], By User `{user}`", LogLevel.Information); await LogAndPublishAsync($"DeleteArtistSecondaryImage `{artist}` Index [{index}], By User `{user}`", LogLevel.Information).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteGenre(User user, Guid genreId) public async Task<OperationResult<bool>> DeleteGenreAsync(User user, Guid genreId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -192,34 +265,34 @@ namespace Roadie.Api.Services
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId); var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == genreId);
if (genre == null) if (genre == null)
{ {
await LogAndPublish($"DeleteLabel Unknown Genre [{genreId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteLabel Unknown Genre [{genreId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Genre Not Found [{genreId}]"); return new OperationResult<bool>(true, $"Genre Not Found [{genreId}]");
} }
try try
{ {
await GenreService.Delete(user, genreId); await GenreService.DeleteAsync(user, genreId).ConfigureAwait(false);
CacheManager.ClearRegion(genre.CacheRegion); CacheManager.ClearRegion(genre.CacheRegion);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting Genre."); await LogAndPublishAsync("Error deleting Genre.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteGenre `{genre}`, By User `{user}`", LogLevel.Information); await LogAndPublishAsync($"DeleteGenre `{genre}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteLabel(User user, Guid labelId) public async Task<OperationResult<bool>> DeleteLabelAsync(User user, Guid labelId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -227,34 +300,34 @@ namespace Roadie.Api.Services
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == labelId); var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == labelId);
if (label == null) if (label == null)
{ {
await LogAndPublish($"DeleteLabel Unknown Label [{labelId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteLabel Unknown Label [{labelId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Label Not Found [{labelId}]"); return new OperationResult<bool>(true, $"Label Not Found [{labelId}]");
} }
try try
{ {
await LabelService.Delete(user, labelId); await LabelService.DeleteAsync(user, labelId).ConfigureAwait(false);
CacheManager.ClearRegion(label.CacheRegion); CacheManager.ClearRegion(label.CacheRegion);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting Label."); await LogAndPublishAsync("Error deleting Label.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteLabel `{label}`, By User `{user}`", LogLevel.Information); await LogAndPublishAsync($"DeleteLabel `{label}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteRelease(User user, Guid releaseId, bool? doDeleteFiles) public async Task<OperationResult<bool>> DeleteReleaseAsync(User user, Guid releaseId, bool? doDeleteFiles)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -266,31 +339,31 @@ namespace Roadie.Api.Services
{ {
if (release == null) if (release == null)
{ {
await LogAndPublish($"DeleteRelease Unknown Release [{releaseId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteRelease Unknown Release [{releaseId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]"); return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]");
} }
await ReleaseService.Delete(user, release, doDeleteFiles ?? false); await ReleaseService.DeleteAsync(user, release, doDeleteFiles ?? false).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting release."); await LogAndPublishAsync("Error deleting release.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteRelease `{release}`, By User `{user}`", LogLevel.Information); await LogAndPublishAsync($"DeleteRelease `{release}`, By User `{user}`", LogLevel.Information).ConfigureAwait(false);
CacheManager.Clear(); CacheManager.Clear();
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteReleaseSecondaryImage(User user, Guid releaseId, int index) public async Task<OperationResult<bool>> DeleteReleaseSecondaryImageAsync(User user, Guid releaseId, int index)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -298,7 +371,7 @@ namespace Roadie.Api.Services
var release = DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId); var release = DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == releaseId);
if (release == null) if (release == null)
{ {
await LogAndPublish($"DeleteReleaseSecondaryImage Unknown Release [{releaseId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteReleaseSecondaryImage Unknown Release [{releaseId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]"); return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]");
} }
@ -316,22 +389,22 @@ namespace Roadie.Api.Services
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting release secondary image."); await LogAndPublishAsync("Error deleting release secondary image.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteReleaseSecondaryImage `{release}` Index [{index}], By User `{user}`", LogLevel.Information); await LogAndPublishAsync($"DeleteReleaseSecondaryImage `{release}` Index [{index}], By User `{user}`", LogLevel.Information).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteTracks(User user, IEnumerable<Guid> trackIds, bool? doDeleteFile) public async Task<OperationResult<bool>> DeleteTracksAsync(User user, IEnumerable<Guid> trackIds, bool? doDeleteFile)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -348,12 +421,12 @@ namespace Roadie.Api.Services
{ {
if (track == null) if (track == null)
{ {
await LogAndPublish($"DeleteTracks Unknown Track [{trackId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteTracks Unknown Track [{trackId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Track Not Found [{trackId}]"); return new OperationResult<bool>(true, $"Track Not Found [{trackId}]");
} }
DbContext.Tracks.Remove(track); DbContext.Tracks.Remove(track);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
if (doDeleteFile ?? false) if (doDeleteFile ?? false)
{ {
string trackPath = null; string trackPath = null;
@ -375,33 +448,33 @@ namespace Roadie.Api.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, string.Format("Error Deleting File [{0}] For Track [{1}] Exception [{2}]", trackPath, track.Id, ex.Serialize())); Logger.LogError(ex, $"Error Deleting File [{trackPath}] For Track [{track.Id}] Exception [{ex.Serialize()}]");
} }
} }
await ReleaseService.ScanReleaseFolder(user, track.ReleaseMedia.Release.RoadieId, false, track.ReleaseMedia.Release); await ReleaseService.ScanReleaseFolderAsync(user, track.ReleaseMedia.Release.RoadieId, false, track.ReleaseMedia.Release).ConfigureAwait(false);
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Track, track.Id); await BookmarkService.RemoveAllBookmarksForItemAsync(BookmarkType.Track, track.Id).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting track."); await LogAndPublishAsync("Error deleting track.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteTracks `{track}`, By User `{user}`", LogLevel.Warning); await LogAndPublishAsync($"DeleteTracks `{track}`, By User `{user}`", LogLevel.Warning).ConfigureAwait(false);
} }
CacheManager.Clear(); CacheManager.Clear();
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> DeleteUser(User applicationUser, Guid userId) public async Task<OperationResult<bool>> DeleteUserAsync(User applicationUser, Guid userId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -412,7 +485,7 @@ namespace Roadie.Api.Services
{ {
var ex = new Exception("User cannot self."); var ex = new Exception("User cannot self.");
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting user."); await LogAndPublishAsync("Error deleting user.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
@ -420,12 +493,12 @@ namespace Roadie.Api.Services
{ {
if (user == null) if (user == null)
{ {
await LogAndPublish($"DeleteUser Unknown User [{userId}]", LogLevel.Warning); await LogAndPublishAsync($"DeleteUser Unknown User [{userId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"User Not Found [{userId}]"); return new OperationResult<bool>(true, $"User Not Found [{userId}]");
} }
DbContext.Users.Remove(user); DbContext.Users.Remove(user);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
var userImageFilename = user.PathToImage(Configuration); var userImageFilename = user.PathToImage(Configuration);
if (File.Exists(userImageFilename)) if (File.Exists(userImageFilename))
{ {
@ -435,16 +508,16 @@ namespace Roadie.Api.Services
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
await LogAndPublish("Error deleting user."); await LogAndPublishAsync("Error deleting user.").ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"DeleteUser `{user}`, By User `{user}`", LogLevel.Warning); await LogAndPublishAsync($"DeleteUser `{user}`, By User `{user}`", LogLevel.Warning).ConfigureAwait(false);
CacheManager.Clear(); CacheManager.Clear();
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
@ -454,7 +527,7 @@ namespace Roadie.Api.Services
/// <summary> /// <summary>
/// This is a very simple way to seed the database or setup configuration when the first (who becomes "Admin") user registers /// This is a very simple way to seed the database or setup configuration when the first (who becomes "Admin") user registers
/// </summary> /// </summary>
public async Task<OperationResult<bool>> DoInitialSetup(User user, UserManager<User> userManager) public async Task<OperationResult<bool>> DoInitialSetupAsync(User user, UserManager<User> userManager)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -483,10 +556,10 @@ namespace Roadie.Api.Services
Description = "Users who have Edit Permissions", Description = "Users who have Edit Permissions",
NormalizedName = "EDITOR" NormalizedName = "EDITOR"
}); });
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
// Add given user to Admin role // Add given user to Admin role
await userManager.AddToRoleAsync(user, "Admin"); await userManager.AddToRoleAsync(user, "Admin").ConfigureAwait(false);
// Create special system artists of 'Sound Tracks' and 'Various Artists' // Create special system artists of 'Sound Tracks' and 'Various Artists'
DbContext.Artists.Add(new data.Artist DbContext.Artists.Add(new data.Artist
@ -515,7 +588,7 @@ namespace Roadie.Api.Services
Tags = "compilations|various", Tags = "compilations|various",
URLs = "https://en.wikipedia.org/wiki/Compilation_album" URLs = "https://en.wikipedia.org/wiki/Compilation_album"
}); });
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
@ -525,7 +598,7 @@ namespace Roadie.Api.Services
}; };
} }
public Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(User user) public Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleasesAsync(User user)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
@ -567,7 +640,7 @@ namespace Roadie.Api.Services
sw.Stop(); sw.Stop();
return Task.FromResult(new OperationResult<Dictionary<string, List<string>>> return Task.FromResult(new OperationResult<Dictionary<string, List<string>>>
{ {
Data = missingData.OrderBy(x => x.Value.Count()).ToDictionary(x => x.Key, x => x.Value), Data = missingData.OrderBy(x => x.Value.Count).ToDictionary(x => x.Key, x => x.Value),
IsSuccess = true, IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds OperationTime = sw.ElapsedMilliseconds
}); });
@ -619,15 +692,12 @@ namespace Roadie.Api.Services
Directory.CreateDirectory(Configuration.LabelImageFolder); Directory.CreateDirectory(Configuration.LabelImageFolder);
Logger.LogInformation($"Created Label Image Folder [{Configuration.LabelImageFolder}]"); Logger.LogInformation($"Created Label Image Folder [{Configuration.LabelImageFolder}]");
} }
if (Configuration.DbContextToUse != DbContexts.MySQL) if (Configuration.DbContextToUse != DbContexts.MySQL && !Directory.Exists(Configuration.FileDatabaseOptions.DatabaseFolder))
{
if (!Directory.Exists(Configuration.FileDatabaseOptions.DatabaseFolder))
{ {
Directory.CreateDirectory(Configuration.FileDatabaseOptions.DatabaseFolder); Directory.CreateDirectory(Configuration.FileDatabaseOptions.DatabaseFolder);
Logger.LogInformation($"Created File Database Folder [{Configuration.FileDatabaseOptions.DatabaseFolder}]"); Logger.LogInformation($"Created File Database Folder [{Configuration.FileDatabaseOptions.DatabaseFolder}]");
} }
} }
}
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error setting up storage folders. Ensure application has create folder permissions."); Logger.LogError(ex, "Error setting up storage folders. Ensure application has create folder permissions.");
@ -640,7 +710,7 @@ namespace Roadie.Api.Services
Logger.LogInformation($"Administration startup tasks completed, elapsed time [{ sw.ElapsedMilliseconds }]"); Logger.LogInformation($"Administration startup tasks completed, elapsed time [{ sw.ElapsedMilliseconds }]");
} }
public async Task<OperationResult<bool>> ScanAllCollections(User user, bool isReadOnly = false, bool doPurgeFirst = false) public async Task<OperationResult<bool>> ScanAllCollectionsAsync(User user, bool isReadOnly = false, bool doPurgeFirst = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -652,7 +722,7 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var result = await ScanCollection(user, collection.RoadieId, isReadOnly, doPurgeFirst, false).ConfigureAwait(false); var result = await ScanCollectionAsync(user, collection.RoadieId, isReadOnly, doPurgeFirst, false).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
errors.AddRange(result.Errors); errors.AddRange(result.Errors);
@ -661,7 +731,7 @@ namespace Roadie.Api.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
await LogAndPublish(ex.ToString(), LogLevel.Error).ConfigureAwait(false); await LogAndPublishAsync(ex.ToString(), LogLevel.Error).ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
} }
@ -671,7 +741,7 @@ namespace Roadie.Api.Services
await UpdateReleaseRank(updatedReleaseId).ConfigureAwait(false); await UpdateReleaseRank(updatedReleaseId).ConfigureAwait(false);
} }
sw.Stop(); sw.Stop();
await LogAndPublish($"ScanAllCollections, By User `{user}`, Updated Release Count [{updatedReleaseIds.Distinct().Count()}], ElapsedTime [{sw.ElapsedMilliseconds}]", LogLevel.Warning).ConfigureAwait(false); await LogAndPublishAsync($"ScanAllCollections, By User `{user}`, Updated Release Count [{updatedReleaseIds.Distinct().Count()}], ElapsedTime [{sw.ElapsedMilliseconds}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = errors.Count == 0, IsSuccess = errors.Count == 0,
@ -681,7 +751,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> ScanArtist(User user, Guid artistId, bool isReadOnly = false) public async Task<OperationResult<bool>> ScanArtistAsync(User user, Guid artistId, bool isReadOnly = false)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
@ -689,18 +759,18 @@ namespace Roadie.Api.Services
var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId); var artist = DbContext.Artists.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null) if (artist == null)
{ {
await LogAndPublish($"ScanArtist Unknown Artist [{artistId}]", LogLevel.Warning); await LogAndPublishAsync($"ScanArtist Unknown Artist [{artistId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]"); return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]");
} }
try try
{ {
var result = await ArtistService.ScanArtistReleasesFolders(user, artist.RoadieId, Configuration.LibraryFolder, isReadOnly); await ArtistService.ScanArtistReleasesFoldersAsync(user, artist.RoadieId, Configuration.LibraryFolder, isReadOnly).ConfigureAwait(false);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
} }
catch (Exception ex) catch (Exception ex)
{ {
await LogAndPublish(ex.ToString(), LogLevel.Error); await LogAndPublishAsync(ex.ToString(), LogLevel.Error).ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
@ -713,56 +783,52 @@ namespace Roadie.Api.Services
NewTracks = ReleaseService.AddedTrackIds.Count(), NewTracks = ReleaseService.AddedTrackIds.Count(),
TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds
}); });
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
await UpdateArtistRank(artist.Id, true); await UpdateArtistRank(artist.Id, true).ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
AdditionalData = new Dictionary<string, object> { { "artistAverage", artist.Rating } }, AdditionalData = new Dictionary<string, object> { { "artistAverage", artist.Rating } },
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> ScanArtists(User user, IEnumerable<Guid> artistIds, bool isReadOnly = false) public async Task<OperationResult<bool>> ScanArtistsAsync(User user, IEnumerable<Guid> artistIds, bool isReadOnly = false)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var errors = new List<Exception>(); var errors = new List<Exception>();
foreach (var artistId in artistIds) foreach (var artistId in artistIds)
{ {
var result = await ScanArtist(user, artistId, isReadOnly); var result = await ScanArtistAsync(user, artistId, isReadOnly).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess && (result.Errors?.Any() ?? false))
{
if (result.Errors?.Any() ?? false)
{ {
errors.AddRange(result.Errors); errors.AddRange(result.Errors);
} }
} }
}
sw.Stop(); sw.Stop();
await LogAndPublish($"** Completed! ScanArtists: Artist Count [{ artistIds.Count() }], Elapsed Time [{sw.Elapsed}]"); await LogAndPublishAsync($"** Completed! ScanArtists: Artist Count [{ artistIds.Count() }], Elapsed Time [{sw.Elapsed}]").ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> ScanCollection(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true) public async Task<OperationResult<bool>> ScanCollectionAsync(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var releaseIdsInCollection = new List<int>(); var releaseIdsInCollection = new List<int>();
var updatedReleaseIds = new List<int>(); var updatedReleaseIds = new List<int>();
var result = new List<data.PositionArtistRelease>();
var errors = new List<Exception>(); var errors = new List<Exception>();
var collection = await DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == collectionId).ConfigureAwait(false); var collection = await DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == collectionId).ConfigureAwait(false);
if (collection == null) if (collection == null)
{ {
await LogAndPublish($"ScanCollection Unknown Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false); await LogAndPublishAsync($"ScanCollection Unknown Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Collection Not Found [{collectionId}]"); return new OperationResult<bool>(true, $"Collection Not Found [{collectionId}]");
} }
@ -770,7 +836,7 @@ namespace Roadie.Api.Services
{ {
if (doPurgeFirst) if (doPurgeFirst)
{ {
await LogAndPublish($"ScanCollection Purging Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false); await LogAndPublishAsync($"ScanCollection Purging Collection [{collectionId}]", LogLevel.Warning).ConfigureAwait(false);
var crs = await DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArrayAsync().ConfigureAwait(false); var crs = await DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArrayAsync().ConfigureAwait(false);
DbContext.CollectionReleases.RemoveRange(crs); DbContext.CollectionReleases.RemoveRange(crs);
await DbContext.SaveChangesAsync().ConfigureAwait(false); await DbContext.SaveChangesAsync().ConfigureAwait(false);
@ -790,8 +856,8 @@ namespace Roadie.Api.Services
data.Release release = null; data.Release release = null;
var isArtistNameDbKey = csvRelease.Artist.StartsWith(Roadie.Library.Data.Collection.DatabaseIdKey); var isArtistNameDbKey = csvRelease.Artist.StartsWith(Roadie.Library.Data.Collection.DatabaseIdKey);
int? artistId = isArtistNameDbKey ? SafeParser.ToNumber<int?>(csvRelease.Artist.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, "")) : null; int? artistId = isArtistNameDbKey ? SafeParser.ToNumber<int?>(csvRelease.Artist.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, string.Empty)) : null;
if(artistId.HasValue) if (artistId.HasValue)
{ {
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId.Value); var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId.Value);
if (artist != null) if (artist != null)
@ -802,9 +868,9 @@ namespace Roadie.Api.Services
else else
{ {
artistsMatchingName = await ArtistLookupEngine.DatabaseQueryForArtistName(csvRelease.Artist).ConfigureAwait(false); artistsMatchingName = await ArtistLookupEngine.DatabaseQueryForArtistName(csvRelease.Artist).ConfigureAwait(false);
if (artistsMatchingName == null || !artistsMatchingName.Any()) if (artistsMatchingName?.Any() != true)
{ {
await LogAndPublish($"CSV Position [{ csvRelease.Position }] Unable To Find Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false); await LogAndPublishAsync($"CSV Position [{ csvRelease.Position }] Unable To Find Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false);
csvRelease.Status = Statuses.Missing; csvRelease.Status = Statuses.Missing;
await DbContext.CollectionMissings.AddAsync(new data.CollectionMissing await DbContext.CollectionMissings.AddAsync(new data.CollectionMissing
{ {
@ -817,13 +883,13 @@ namespace Roadie.Api.Services
} }
else if (artistsMatchingName.Count() > 1) else if (artistsMatchingName.Count() > 1)
{ {
await LogAndPublish($"CSV Position [{ csvRelease.Position }] Found [{ artistsMatchingName.Count() }] Artists by [{csvRelease.Artist}]", LogLevel.Information).ConfigureAwait(false); await LogAndPublishAsync($"CSV Position [{ csvRelease.Position }] Found [{ artistsMatchingName.Count() }] Artists by [{csvRelease.Artist}]", LogLevel.Information).ConfigureAwait(false);
} }
} }
foreach (var artist in artistsMatchingName) foreach (var artist in artistsMatchingName)
{ {
var isReleaseNameDbKey = csvRelease.Release.StartsWith(Roadie.Library.Data.Collection.DatabaseIdKey); var isReleaseNameDbKey = csvRelease.Release.StartsWith(Roadie.Library.Data.Collection.DatabaseIdKey);
int? releaseId = isReleaseNameDbKey ? SafeParser.ToNumber<int?>(csvRelease.Release.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, "")) : null; int? releaseId = isReleaseNameDbKey ? SafeParser.ToNumber<int?>(csvRelease.Release.Replace(Roadie.Library.Data.Collection.DatabaseIdKey, string.Empty)) : null;
if (releaseId.HasValue) if (releaseId.HasValue)
{ {
release = await DbContext.Releases.FirstOrDefaultAsync(x => x.Id == releaseId.Value).ConfigureAwait(false); release = await DbContext.Releases.FirstOrDefaultAsync(x => x.Id == releaseId.Value).ConfigureAwait(false);
@ -840,7 +906,7 @@ namespace Roadie.Api.Services
if (release == null) if (release == null)
{ {
await LogAndPublish($"CSV Position [{ csvRelease.Position }] Unable To Find Release [{csvRelease.Release}], for Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false); await LogAndPublishAsync($"CSV Position [{ csvRelease.Position }] Unable To Find Release [{csvRelease.Release}], for Artist [{csvRelease.Artist}]", LogLevel.Warning).ConfigureAwait(false);
csvRelease.Status = Statuses.Missing; csvRelease.Status = Statuses.Missing;
await DbContext.CollectionMissings.AddAsync(new data.CollectionMissing await DbContext.CollectionMissings.AddAsync(new data.CollectionMissing
{ {
@ -867,7 +933,7 @@ namespace Roadie.Api.Services
CollectionId = collection.Id, CollectionId = collection.Id,
ReleaseId = release.Id, ReleaseId = release.Id,
ListNumber = csvRelease.Position ListNumber = csvRelease.Position
}); }).ConfigureAwait(false);
updated = true; updated = true;
} }
// If Item in Collection is at different List number update CollectionRelease // If Item in Collection is at different List number update CollectionRelease
@ -909,13 +975,13 @@ namespace Roadie.Api.Services
where cr.CollectionId == collection.Id where cr.CollectionId == collection.Id
where !releaseIdsInCollection.Contains(cr.ReleaseId) where !releaseIdsInCollection.Contains(cr.ReleaseId)
select cr).ToArrayAsync().ConfigureAwait(false); select cr).ToArrayAsync().ConfigureAwait(false);
if (collectionReleasesToRemove.Any()) if (collectionReleasesToRemove.Length > 0)
{ {
await LogAndPublish($"Removing [{collectionReleasesToRemove.Count()}] Stale Release Records from Collection.", LogLevel.Information); await LogAndPublishAsync($"Removing [{collectionReleasesToRemove.Length}] Stale Release Records from Collection.", LogLevel.Information).ConfigureAwait(false);
DbContext.CollectionReleases.RemoveRange(collectionReleasesToRemove); DbContext.CollectionReleases.RemoveRange(collectionReleasesToRemove);
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
if (doUpdateRanks) if (doUpdateRanks)
{ {
foreach (var updatedReleaseId in updatedReleaseIds) foreach (var updatedReleaseId in updatedReleaseIds)
@ -933,31 +999,23 @@ namespace Roadie.Api.Services
} }
sw.Stop(); sw.Stop();
Logger.LogWarning(string.Format("RescanCollection `{0}`, By User `{1}`, ElapsedTime [{2}]", collection, user, sw.ElapsedMilliseconds)); Logger.LogWarning($"RescanCollection `{collection}`, By User `{user}`, ElapsedTime [{sw.ElapsedMilliseconds}]");
return new OperationResult<bool> return new OperationResult<bool>
{ {
AdditionalData = new Dictionary<string, object> { { "updatedReleaseIds", updatedReleaseIds.ToArray() } }, AdditionalData = new Dictionary<string, object> { { "updatedReleaseIds", updatedReleaseIds.ToArray() } },
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> ScanInboundFolder(User user, bool isReadOnly = false) public Task<OperationResult<bool>> ScanInboundFolderAsync(User user, bool isReadOnly = false) => ScanFolderAsync(user, new DirectoryInfo(Configuration.InboundFolder), isReadOnly);
{
var d = new DirectoryInfo(Configuration.InboundFolder);
return await ScanFolder(user, d, isReadOnly);
}
public async Task<OperationResult<bool>> ScanLibraryFolder(User user, bool isReadOnly = false) public Task<OperationResult<bool>> ScanLibraryFolderAsync(User user, bool isReadOnly = false) => ScanFolderAsync(user, new DirectoryInfo(Configuration.LibraryFolder), isReadOnly, false);
{
var d = new DirectoryInfo(Configuration.LibraryFolder);
return await ScanFolder(user, d, isReadOnly, false);
}
public async Task<OperationResult<bool>> ScanRelease(User user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) public async Task<OperationResult<bool>> ScanReleaseAsync(User user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -969,17 +1027,17 @@ namespace Roadie.Api.Services
.FirstOrDefault(x => x.RoadieId == releaseId); .FirstOrDefault(x => x.RoadieId == releaseId);
if (release == null) if (release == null)
{ {
await LogAndPublish($"ScanRelease Unknown Release [{releaseId}]", LogLevel.Warning); await LogAndPublishAsync($"ScanRelease Unknown Release [{releaseId}]", LogLevel.Warning).ConfigureAwait(false);
return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]"); return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]");
} }
try try
{ {
var result = await ReleaseService.ScanReleaseFolder(user, release.RoadieId, isReadOnly, release); await ReleaseService.ScanReleaseFolderAsync(user, release.RoadieId, isReadOnly, release).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await LogAndPublish(ex.ToString(), LogLevel.Error); await LogAndPublishAsync(ex.ToString(), LogLevel.Error).ConfigureAwait(false);
errors.Add(ex); errors.Add(ex);
} }
@ -992,43 +1050,40 @@ namespace Roadie.Api.Services
NewTracks = ReleaseService.AddedTrackIds.Count(), NewTracks = ReleaseService.AddedTrackIds.Count(),
TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds
}); });
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> ScanReleases(User user, IEnumerable<Guid> releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) public async Task<OperationResult<bool>> ScanReleasesAsync(User user, IEnumerable<Guid> releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var errors = new List<Exception>(); var errors = new List<Exception>();
foreach (var releaseId in releaseIds) foreach (var releaseId in releaseIds)
{ {
var result = await ScanRelease(user, releaseId, isReadOnly, wasDoneForInvalidTrackPlay); var result = await ScanReleaseAsync(user, releaseId, isReadOnly, wasDoneForInvalidTrackPlay).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess && (result.Errors?.Any() ?? false))
{
if (result.Errors?.Any() ?? false)
{ {
errors.AddRange(result.Errors); errors.AddRange(result.Errors);
} }
} }
}
sw.Stop(); sw.Stop();
await LogAndPublish($"** Completed! ScanReleases: Release Count [{ releaseIds.Count() }], Elapsed Time [{sw.Elapsed}]"); await LogAndPublishAsync($"** Completed! ScanReleases: Release Count [{ releaseIds.Count() }], Elapsed Time [{sw.Elapsed}]").ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> UpdateInviteTokenUsed(Guid? tokenId) public async Task<OperationResult<bool>> UpdateInviteTokenUsedAsync(Guid? tokenId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -1044,17 +1099,17 @@ namespace Roadie.Api.Services
} }
token.Status = Statuses.Complete; token.Status = Statuses.Complete;
token.LastUpdated = DateTime.UtcNow; token.LastUpdated = DateTime.UtcNow;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<bool>> ValidateInviteToken(Guid? tokenId) public async Task<OperationResult<bool>> ValidateInviteTokenAsync(Guid? tokenId)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -1072,7 +1127,7 @@ namespace Roadie.Api.Services
{ {
token.Status = Statuses.Expired; token.Status = Statuses.Expired;
token.LastUpdated = DateTime.UtcNow; token.LastUpdated = DateTime.UtcNow;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
return new OperationResult<bool>(true, $"Invite Token [{tokenId}] Expired [{ token.ExpiresDate }]"); return new OperationResult<bool>(true, $"Invite Token [{tokenId}] Expired [{ token.ExpiresDate }]");
} }
if (token.Status == Statuses.Complete) if (token.Status == Statuses.Complete)
@ -1081,84 +1136,11 @@ namespace Roadie.Api.Services
} }
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = true, Data = true,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
private void EventMessageLogger_Messages(object sender, EventMessage e) => Task.WaitAll(LogAndPublish(e.Message, e.Level));
private async Task LogAndPublish(string message, LogLevel level = LogLevel.Trace)
{
switch (level)
{
case LogLevel.Trace:
Logger.LogTrace(message);
break;
case LogLevel.Debug:
Logger.LogDebug(message);
break;
case LogLevel.Information:
Logger.LogInformation(message);
break;
case LogLevel.Warning:
Logger.LogWarning(message);
break;
case LogLevel.Critical:
Logger.LogCritical(message);
break;
}
await ScanActivityHub.Clients.All.SendAsync("SendSystemActivity", message).ConfigureAwait(false);
}
private async Task<OperationResult<bool>> ScanFolder(User user, DirectoryInfo d, bool isReadOnly, bool doDeleteFiles = true)
{
var sw = new Stopwatch();
sw.Start();
long processedFiles = 0;
await LogAndPublish($"** Processing Folder: [{d.FullName}]");
long processedFolders = 0;
foreach (var folder in Directory.EnumerateDirectories(d.FullName).ToArray())
{
var directoryProcessResult = await FileDirectoryProcessorService.Process(user:user,
folder: new DirectoryInfo(folder),
doJustInfo: isReadOnly,
doDeleteFiles: doDeleteFiles);
processedFolders++;
processedFiles += SafeParser.ToNumber<int>(directoryProcessResult.AdditionalData["ProcessedFiles"]);
}
CacheManager.Clear();
if (!isReadOnly)
{
Services.FileDirectoryProcessorService.DeleteEmptyFolders(d, Logger);
}
sw.Stop();
var newScanHistory = new data.ScanHistory
{
UserId = user.Id,
NewArtists = FileDirectoryProcessorService.AddedArtistIds.Count(),
NewReleases = FileDirectoryProcessorService.AddedReleaseIds.Count(),
NewTracks = FileDirectoryProcessorService.AddedTrackIds.Count(),
TimeSpanInSeconds = (int)sw.Elapsed.TotalSeconds
};
DbContext.ScanHistories.Add(newScanHistory);
await DbContext.SaveChangesAsync();
await LogAndPublish($"** Completed! Processed Folders [{processedFolders}], Processed Files [{processedFiles}], New Artists [{ newScanHistory.NewArtists }], New Releases [{ newScanHistory.NewReleases }], New Tracks [{ newScanHistory.NewTracks }] : Elapsed Time [{sw.Elapsed}]");
return new OperationResult<bool>
{
Data = true,
IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds
};
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,4 @@
using Mapster; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Roadie.Library; using Roadie.Library;
using Roadie.Library.Caching; using Roadie.Library.Caching;
@ -20,7 +19,6 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core;
using System.Threading.Tasks; using System.Threading.Tasks;
using data = Roadie.Library.Data;
using models = Roadie.Library.Models; using models = Roadie.Library.Models;
namespace Roadie.Api.Services namespace Roadie.Api.Services
@ -41,30 +39,7 @@ namespace Roadie.Api.Services
UserService = userService; UserService = userService;
} }
/// <summary> public async Task<Library.Models.Pagination.PagedResult<models.BookmarkList>> ListAsync(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null)
/// When a bookmarkable item gets deleted then delete any bookmarks to that item, since its a generic column there is not FK setup.
/// </summary>
public async Task<OperationResult<bool>> RemoveAllBookmarksForItem(BookmarkType type, int id)
{
var bookmarks = await DbContext.Bookmarks.Include(x => x.User)
.Where(x => x.BookmarkType == type && x.BookmarkTargetId == id)
.ToListAsync();
var users = bookmarks.Select(x => x.User).ToList().Distinct();
DbContext.Bookmarks.RemoveRange(bookmarks);
await DbContext.SaveChangesAsync();
foreach(var user in users)
{
CacheManager.ClearRegion(user.CacheRegion);
}
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
public async Task<Library.Models.Pagination.PagedResult<models.BookmarkList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -95,14 +70,19 @@ namespace Roadie.Api.Services
var rowCount = result.Count(); var rowCount = result.Count();
var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
var user = await GetUser(roadieUser.UserId); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
foreach (var row in rows) foreach (var row in rows)
{
switch (row.Type) switch (row.Type)
{ {
case BookmarkType.Artist: case BookmarkType.Artist:
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == row.BookmarkTargetId); var artist = DbContext.Artists.FirstOrDefault(x => x.Id == row.BookmarkTargetId);
if (artist == null) continue; if (artist == null)
{
continue;
}
row.Bookmark = new models.DataToken row.Bookmark = new models.DataToken
{ {
Text = artist.Name, Text = artist.Name,
@ -117,7 +97,11 @@ namespace Roadie.Api.Services
case BookmarkType.Release: case BookmarkType.Release:
var release = DbContext.Releases.Include(x => x.Artist) var release = DbContext.Releases.Include(x => x.Artist)
.FirstOrDefault(x => x.Id == row.BookmarkTargetId); .FirstOrDefault(x => x.Id == row.BookmarkTargetId);
if (release == null) continue; if (release == null)
{
continue;
}
row.Bookmark = new models.DataToken row.Bookmark = new models.DataToken
{ {
Text = release.Title, Text = release.Title,
@ -137,7 +121,11 @@ namespace Roadie.Api.Services
.Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist) .Include(x => x.TrackArtist)
.FirstOrDefault(x => x.Id == row.BookmarkTargetId); .FirstOrDefault(x => x.Id == row.BookmarkTargetId);
if (track == null) continue; if (track == null)
{
continue;
}
row.Bookmark = new models.DataToken row.Bookmark = new models.DataToken
{ {
Text = track.Title, Text = track.Title,
@ -165,7 +153,11 @@ namespace Roadie.Api.Services
var playlist = DbContext.Playlists var playlist = DbContext.Playlists
.Include(x => x.User) .Include(x => x.User)
.FirstOrDefault(x => x.Id == row.BookmarkTargetId); .FirstOrDefault(x => x.Id == row.BookmarkTargetId);
if (playlist == null) continue; if (playlist == null)
{
continue;
}
row.Bookmark = new models.DataToken row.Bookmark = new models.DataToken
{ {
Text = playlist.Name, Text = playlist.Name,
@ -180,7 +172,11 @@ namespace Roadie.Api.Services
case BookmarkType.Collection: case BookmarkType.Collection:
var collection = DbContext.Collections.FirstOrDefault(x => x.Id == row.BookmarkTargetId); var collection = DbContext.Collections.FirstOrDefault(x => x.Id == row.BookmarkTargetId);
if (collection == null) continue; if (collection == null)
{
continue;
}
row.Bookmark = new models.DataToken row.Bookmark = new models.DataToken
{ {
Text = collection.Name, Text = collection.Name,
@ -196,7 +192,11 @@ namespace Roadie.Api.Services
case BookmarkType.Label: case BookmarkType.Label:
var label = DbContext.Labels.FirstOrDefault(x => x.Id == row.BookmarkTargetId); var label = DbContext.Labels.FirstOrDefault(x => x.Id == row.BookmarkTargetId);
if (label == null) continue; if (label == null)
{
continue;
}
row.Bookmark = new models.DataToken row.Bookmark = new models.DataToken
{ {
Text = label.Name, Text = label.Name,
@ -207,8 +207,7 @@ namespace Roadie.Api.Services
row.SortName = label.SortName ?? label.Name; row.SortName = label.SortName ?? label.Name;
break; break;
} }
};
;
sw.Stop(); sw.Stop();
return new Library.Models.Pagination.PagedResult<models.BookmarkList> return new Library.Models.Pagination.PagedResult<models.BookmarkList>
{ {
@ -219,5 +218,29 @@ namespace Roadie.Api.Services
Rows = rows Rows = rows
}; };
} }
/// <summary>
/// When a bookmarkable item gets deleted then delete any bookmarks to that item, since its a generic column there is not FK setup.
/// </summary>
public async Task<OperationResult<bool>> RemoveAllBookmarksForItemAsync(BookmarkType type, int id)
{
var bookmarks = await DbContext.Bookmarks.Include(x => x.User)
.Where(x => x.BookmarkType == type && x.BookmarkTargetId == id)
.ToListAsync()
.ConfigureAwait(false);
var users = bookmarks.Select(x => x.User).ToList().Distinct();
DbContext.Bookmarks.RemoveRange(bookmarks);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
foreach (var user in users)
{
CacheManager.ClearRegion(user.CacheRegion);
}
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
} }
} }

View file

@ -43,307 +43,7 @@ namespace Roadie.Api.Services
BookmarkService = bookmarkService; BookmarkService = bookmarkService;
} }
/// <summary> private async Task<OperationResult<Collection>> CollectionByIdActionAsync(Guid id, IEnumerable<string> includes = null)
/// Get blank Collection to add
/// </summary>
/// <param name="roadieUser"></param>
/// <returns></returns>
public OperationResult<Collection> Add(User roadieUser)
{
var sw = Stopwatch.StartNew();
sw.Start();
var id = Guid.Empty;
var collection = new data.Collection
{
Status = Statuses.New
};
var result = collection.Adapt<Collection>();
result.Id = id;
result.Thumbnail = ImageHelper.MakeNewImage(HttpContext,"collection");
result.MediumThumbnail = ImageHelper.MakeNewImage(HttpContext,"collection");
result.Maintainer = new DataToken
{
Value = roadieUser.UserId.ToString(),
Text = roadieUser.UserName
};
sw.Stop();
return new OperationResult<Collection>
{
Data = result,
IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds
};
}
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 CacheManager.GetAsync(cacheKey, async () =>
{
return await CollectionByIdAction(id, includes);
}, data.Artist.CacheRegionUrn(id));
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments.Any())
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr).ToArray();
foreach (var comment in result.Data.Comments)
{
var userCommentReaction =
userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
}
}
}
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 = DbContext.Collections.FirstOrDefault(x => x.RoadieId == id);
if (collection == null) return new OperationResult<bool>(true, $"Collection Not Found [{id}]");
if (!user.IsEditor)
{
Logger.LogWarning($"DeleteCollection: Access Denied: `{collection}`, By User `{user}`");
var r = new OperationResult<bool>("Access Denied");
r.IsAccessDeniedResult = true;
return r;
}
try
{
DbContext.Collections.Remove(collection);
await DbContext.SaveChangesAsync();
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Collection, collection.Id);
var collectionImageFilename = collection.PathToImage(Configuration);
if (File.Exists(collectionImageFilename))
{
File.Delete(collectionImageFilename);
}
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
Logger.LogWarning($"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)
{
collections = (from cr in DbContext.CollectionReleases
join c in DbContext.Collections on cr.CollectionId equals c.Id
join r in DbContext.Releases on cr.ReleaseId equals r.Id
join a in DbContext.Artists on r.ArtistId equals a.Id
where a.RoadieId == artistId
select c).Distinct();
}
else if (releaseId.HasValue)
{
collections = (from cr in DbContext.CollectionReleases
join c in DbContext.Collections on cr.CollectionId equals c.Id
join r in DbContext.Releases on cr.ReleaseId equals r.Id
where r.RoadieId == releaseId
select c).Distinct();
}
else
{
collections = DbContext.Collections;
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
? request.FilterValue.ToAlphanumericName()
: null;
var result = from c in collections
let collectionCount = (from crc in DbContext.CollectionReleases
where crc.CollectionId == c.Id
select crc.Id).Count()
where string.IsNullOrEmpty(normalizedFilterValue) || (
c.Name.ToLower().Contains(normalizedFilterValue) ||
c.SortName.ToLower().Contains(normalizedFilterValue) ||
c.AlternateNames.ToLower().Contains(normalizedFilterValue)
)
where request.FilterToStatusValue == Statuses.Ok || c.Status == request.FilterToStatusValue
select new CollectionList
{
DatabaseId = c.Id,
Collection = new DataToken
{
Text = c.Name,
Value = c.RoadieId.ToString()
},
Id = c.RoadieId,
CollectionCount = c.CollectionCount,
CollectionType = (c.CollectionType ?? CollectionType.Unknown).ToString(),
CollectionFoundCount = collectionCount,
CreatedDate = c.CreatedDate,
IsLocked = c.IsLocked,
LastUpdated = c.LastUpdated,
Thumbnail = ImageHelper.MakeCollectionThumbnailImage(Configuration, HttpContext, c.RoadieId)
};
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "Collection.Text", "ASC" } })
: request.OrderValue();
var rowCount = result.Count();
var rows = result
.OrderBy(sortBy)
.Skip(request.SkipValue)
.Take(request.LimitValue)
.ToArray();
if (request.FilterToStatusValue == Statuses.Incomplete)
{
rows = rows.OrderByDescending(x => x.PercentComplete).ThenBy(x => x.SortName).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>
public async Task<OperationResult<bool>> UpdateCollection(User user, Collection model)
{
var isNew = model.Id == Guid.Empty;
var now = DateTime.UtcNow;
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collection = new data.Collection();
if (!isNew)
{
collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == model.Id);
if (collection == null)
{
return new OperationResult<bool>(true, string.Format("Collection Not Found [{0}]", model.Id));
}
// If collection is being renamed, see if collection already exists with new model supplied name
var collectionName = collection.SortNameValue;
var collectionModelName = model.SortNameValue;
if (collectionName.ToAlphanumericName() != collectionModelName.ToAlphanumericName())
{
var existingCollection = DbContext.Collections.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName);
if (existingCollection != null)
{
return new OperationResult<bool>($"Collection already exists `{ collection }` with name [{ collectionModelName }].");
}
}
}
collection.IsLocked = model.IsLocked;
var oldPathToImage = collection.PathToImage(Configuration);
var didChangeName = collection.Name != model.Name && !isNew;
collection.Name = model.Name;
collection.SortName = model.SortName;
collection.Edition = model.Edition;
collection.ListInCSVFormat = model.ListInCSVFormat;
collection.ListInCSV = model.ListInCSV;
collection.Description = model.Description;
collection.URLs = model.URLs;
collection.Status = SafeParser.ToEnum<Statuses>(model.Status);
collection.CollectionType = SafeParser.ToEnum<CollectionType>(model.CollectionType);
collection.Tags = model.TagsList.ToDelimitedList();
collection.URLs = model.URLsList.ToDelimitedList();
collection.AlternateNames = model.AlternateNamesList.ToDelimitedList();
collection.CollectionCount = model.CollectionCount;
if (model.Maintainer?.Value != null)
{
var maintainer = DbContext.Users.FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value));
if (maintainer != null)
{
collection.MaintainerId = maintainer.Id;
}
}
if (isNew)
{
await DbContext.Collections.AddAsync(collection);
await DbContext.SaveChangesAsync();
}
if (didChangeName)
{
if (File.Exists(oldPathToImage))
{
File.Move(oldPathToImage, collection.PathToImage(Configuration));
}
}
var collectionImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (collectionImage != null)
{
// Save unaltered collection image
File.WriteAllBytes(collection.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(collectionImage));
}
collection.LastUpdated = now;
await DbContext.SaveChangesAsync();
CacheManager.ClearRegion(collection.CacheRegion);
Logger.LogInformation($"UpdateCollection `{collection}` By User `{user}`");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
private async Task<OperationResult<Collection>> CollectionByIdAction(Guid id, IEnumerable<string> includes = null)
{ {
var timings = new Dictionary<string, long>(); var timings = new Dictionary<string, long>();
var tsw = new Stopwatch(); var tsw = new Stopwatch();
@ -352,13 +52,13 @@ namespace Roadie.Api.Services
sw.Start(); sw.Start();
tsw.Restart(); tsw.Restart();
var collection = await GetCollection(id); var collection = await GetCollection(id).ConfigureAwait(false);
tsw.Stop(); tsw.Stop();
timings.Add("getCollection", tsw.ElapsedMilliseconds); timings.Add("getCollection", tsw.ElapsedMilliseconds);
if (collection == null) if (collection == null)
{ {
return new OperationResult<Collection>(true, string.Format("Collection Not Found [{0}]", id)); return new OperationResult<Collection>(true, $"Collection Not Found [{id}]");
} }
var result = collection.Adapt<Collection>(); var result = collection.Adapt<Collection>();
var maintainer = DbContext.Users.FirstOrDefault(x => x.Id == collection.MaintainerId); var maintainer = DbContext.Users.FirstOrDefault(x => x.Id == collection.MaintainerId);
@ -474,5 +174,309 @@ namespace Roadie.Api.Services
OperationTime = sw.ElapsedMilliseconds OperationTime = sw.ElapsedMilliseconds
}; };
} }
/// <summary>
/// Get blank Collection to add
/// </summary>
/// <param name="roadieUser"></param>
/// <returns></returns>
public OperationResult<Collection> Add(User roadieUser)
{
var sw = Stopwatch.StartNew();
sw.Start();
var id = Guid.Empty;
var collection = new data.Collection
{
Status = Statuses.New
};
var result = collection.Adapt<Collection>();
result.Id = id;
result.Thumbnail = ImageHelper.MakeNewImage(HttpContext, "collection");
result.MediumThumbnail = ImageHelper.MakeNewImage(HttpContext, "collection");
result.Maintainer = new DataToken
{
Value = roadieUser.UserId.ToString(),
Text = roadieUser.UserName
};
sw.Stop();
return new OperationResult<Collection>
{
Data = result,
IsSuccess = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<Collection>> ByIdAsync(User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = $"urn:collection_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
var result = await CacheManager.GetAsync(cacheKey, async () =>
{
return await CollectionByIdActionAsync(id, includes).ConfigureAwait(false);
}, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Collection).ConfigureAwait(false);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments.Any())
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr).ToArray();
foreach (var comment in result.Data.Comments)
{
var userCommentReaction =
userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
}
}
}
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>> DeleteCollectionAsync(User user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == id);
if (collection == null)
{
return new OperationResult<bool>(true, $"Collection Not Found [{id}]");
}
if (!user.IsEditor)
{
Logger.LogWarning($"DeleteCollection: Access Denied: `{collection}`, By User `{user}`");
var r = new OperationResult<bool>("Access Denied");
r.IsAccessDeniedResult = true;
return r;
}
try
{
DbContext.Collections.Remove(collection);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
await BookmarkService.RemoveAllBookmarksForItemAsync(BookmarkType.Collection, collection.Id).ConfigureAwait(false);
var collectionImageFilename = collection.PathToImage(Configuration);
if (File.Exists(collectionImageFilename))
{
File.Delete(collectionImageFilename);
}
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
Logger.LogWarning($"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>> ListAsync(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)
{
collections = (from cr in DbContext.CollectionReleases
join c in DbContext.Collections on cr.CollectionId equals c.Id
join r in DbContext.Releases on cr.ReleaseId equals r.Id
join a in DbContext.Artists on r.ArtistId equals a.Id
where a.RoadieId == artistId
select c).Distinct();
}
else if (releaseId.HasValue)
{
collections = (from cr in DbContext.CollectionReleases
join c in DbContext.Collections on cr.CollectionId equals c.Id
join r in DbContext.Releases on cr.ReleaseId equals r.Id
where r.RoadieId == releaseId
select c).Distinct();
}
else
{
collections = DbContext.Collections;
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
? request.FilterValue.ToAlphanumericName()
: null;
var result = from c in collections
let collectionCount = (from crc in DbContext.CollectionReleases
where crc.CollectionId == c.Id
select crc.Id).Count()
where string.IsNullOrEmpty(normalizedFilterValue) || (
c.Name.ToLower().Contains(normalizedFilterValue) ||
c.SortName.ToLower().Contains(normalizedFilterValue) ||
c.AlternateNames.ToLower().Contains(normalizedFilterValue)
)
where request.FilterToStatusValue == Statuses.Ok || c.Status == request.FilterToStatusValue
select new CollectionList
{
DatabaseId = c.Id,
Collection = new DataToken
{
Text = c.Name,
Value = c.RoadieId.ToString()
},
Id = c.RoadieId,
CollectionCount = c.CollectionCount,
CollectionType = (c.CollectionType ?? CollectionType.Unknown).ToString(),
CollectionFoundCount = collectionCount,
CreatedDate = c.CreatedDate,
IsLocked = c.IsLocked,
LastUpdated = c.LastUpdated,
Thumbnail = ImageHelper.MakeCollectionThumbnailImage(Configuration, HttpContext, c.RoadieId)
};
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "Collection.Text", "ASC" } })
: request.OrderValue();
var rowCount = result.Count();
var rows = result
.OrderBy(sortBy)
.Skip(request.SkipValue)
.Take(request.LimitValue)
.ToArray();
if (request.FilterToStatusValue == Statuses.Incomplete)
{
rows = rows.OrderByDescending(x => x.PercentComplete).ThenBy(x => x.SortName).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>
public async Task<OperationResult<bool>> UpdateCollectionAsync(User user, Collection model)
{
var isNew = model.Id == Guid.Empty;
var now = DateTime.UtcNow;
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var collection = new data.Collection();
if (!isNew)
{
collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == model.Id);
if (collection == null)
{
return new OperationResult<bool>(true, $"Collection Not Found [{model.Id}]");
}
// If collection is being renamed, see if collection already exists with new model supplied name
var collectionName = collection.SortNameValue;
var collectionModelName = model.SortNameValue;
if (collectionName.ToAlphanumericName() != collectionModelName.ToAlphanumericName())
{
var existingCollection = DbContext.Collections.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName);
if (existingCollection != null)
{
return new OperationResult<bool>($"Collection already exists `{ collection }` with name [{ collectionModelName }].");
}
}
}
collection.IsLocked = model.IsLocked;
var oldPathToImage = collection.PathToImage(Configuration);
var didChangeName = collection.Name != model.Name && !isNew;
collection.Name = model.Name;
collection.SortName = model.SortName;
collection.Edition = model.Edition;
collection.ListInCSVFormat = model.ListInCSVFormat;
collection.ListInCSV = model.ListInCSV;
collection.Description = model.Description;
collection.URLs = model.URLs;
collection.Status = SafeParser.ToEnum<Statuses>(model.Status);
collection.CollectionType = SafeParser.ToEnum<CollectionType>(model.CollectionType);
collection.Tags = model.TagsList.ToDelimitedList();
collection.URLs = model.URLsList.ToDelimitedList();
collection.AlternateNames = model.AlternateNamesList.ToDelimitedList();
collection.CollectionCount = model.CollectionCount;
if (model.Maintainer?.Value != null)
{
var maintainer = await DbContext.Users.FirstOrDefaultAsync(x => x.RoadieId == SafeParser.ToGuid(model.Maintainer.Value)).ConfigureAwait(false);
if (maintainer != null)
{
collection.MaintainerId = maintainer.Id;
}
}
if (isNew)
{
await DbContext.Collections.AddAsync(collection).ConfigureAwait(false);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
if (didChangeName)
{
if (File.Exists(oldPathToImage))
{
File.Move(oldPathToImage, collection.PathToImage(Configuration));
}
}
var collectionImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (collectionImage != null)
{
// Save unaltered collection image
File.WriteAllBytes(collection.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(collectionImage));
}
collection.LastUpdated = now;
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(collection.CacheRegion);
Logger.LogInformation($"UpdateCollection `{collection}` By User `{user}`");
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
} }
} }

View file

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Roadie.Library; using Roadie.Library;
using Roadie.Library.Caching; using Roadie.Library.Caching;
using Roadie.Library.Configuration; using Roadie.Library.Configuration;
@ -28,15 +29,85 @@ namespace Roadie.Api.Services
{ {
} }
public async Task<OperationResult<bool>> AddNewArtistComment(User user, Guid artistId, string cmt) private void ClearCaches(data.Comment comment)
{
switch (comment.CommentType)
{
case CommentType.Artist:
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == comment.ArtistId);
if (artist != null)
{
CacheManager.ClearRegion(artist.CacheRegion);
}
break;
case CommentType.Collection:
var collection = DbContext.Collections.FirstOrDefault(x => x.Id == comment.CollectionId);
if (collection != null)
{
CacheManager.ClearRegion(collection.CacheRegion);
}
break;
case CommentType.Genre:
var genre = DbContext.Genres.FirstOrDefault(x => x.Id == comment.GenreId);
if (genre != null)
{
CacheManager.ClearRegion(genre.CacheRegion);
}
break;
case CommentType.Label:
var label = DbContext.Labels.FirstOrDefault(x => x.Id == comment.LabelId);
if (label != null)
{
CacheManager.ClearRegion(label.CacheRegion);
}
break;
case CommentType.Playlist:
var playlist = DbContext.Playlists.FirstOrDefault(x => x.Id == comment.PlaylistId);
if (playlist != null)
{
CacheManager.ClearRegion(playlist.CacheRegion);
}
break;
case CommentType.Release:
var release = DbContext.Releases.FirstOrDefault(x => x.Id == comment.ReleaseId);
if (release != null)
{
CacheManager.ClearRegion(release.CacheRegion);
}
break;
case CommentType.Track:
var track = DbContext.Tracks.FirstOrDefault(x => x.Id == comment.TrackId);
if (track != null)
{
CacheManager.ClearRegion(track.CacheRegion);
}
break;
}
}
public async Task<OperationResult<bool>> AddNewArtistCommentAsync(User user, Guid artistId, string cmt)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var artist = DbContext.Artists var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.RoadieId == artistId).ConfigureAwait(false);
.FirstOrDefault(x => x.RoadieId == artistId);
if (artist == null) if (artist == null)
return new OperationResult<bool>(true, string.Format("Artist Not Found [{0}]", artistId)); {
return new OperationResult<bool>(true, $"Artist Not Found [{artistId}]");
}
var newComment = new data.Comment var newComment = new data.Comment
{ {
@ -44,8 +115,8 @@ namespace Roadie.Api.Services
UserId = user.Id.Value, UserId = user.Id.Value,
Cmt = cmt Cmt = cmt
}; };
DbContext.Comments.Add(newComment); await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -58,15 +129,16 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> AddNewCollectionComment(User user, Guid collectionId, string cmt) public async Task<OperationResult<bool>> AddNewCollectionCommentAsync(User user, Guid collectionId, string cmt)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var collection = DbContext.Collections var collection = await DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == collectionId).ConfigureAwait(false);
.FirstOrDefault(x => x.RoadieId == collectionId);
if (collection == null) if (collection == null)
return new OperationResult<bool>(true, string.Format("Collection Not Found [{0}]", collectionId)); {
return new OperationResult<bool>(true, $"Collection Not Found [{collectionId}]");
}
var newComment = new data.Comment var newComment = new data.Comment
{ {
@ -74,8 +146,8 @@ namespace Roadie.Api.Services
UserId = user.Id.Value, UserId = user.Id.Value,
Cmt = cmt Cmt = cmt
}; };
DbContext.Comments.Add(newComment); await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(collection.CacheRegion); CacheManager.ClearRegion(collection.CacheRegion);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -88,14 +160,16 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> AddNewGenreComment(User user, Guid genreId, string cmt) public async Task<OperationResult<bool>> AddNewGenreCommentAsync(User user, Guid genreId, string cmt)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var genre = DbContext.Genres var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == genreId).ConfigureAwait(false);
.FirstOrDefault(x => x.RoadieId == genreId); if (genre == null)
if (genre == null) return new OperationResult<bool>(true, string.Format("Genre Not Found [{0}]", genreId)); {
return new OperationResult<bool>(true, $"Genre Not Found [{genreId}]");
}
var newComment = new data.Comment var newComment = new data.Comment
{ {
@ -103,8 +177,8 @@ namespace Roadie.Api.Services
UserId = user.Id.Value, UserId = user.Id.Value,
Cmt = cmt Cmt = cmt
}; };
DbContext.Comments.Add(newComment); await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(genre.CacheRegion); CacheManager.ClearRegion(genre.CacheRegion);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -117,14 +191,16 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> AddNewLabelComment(User user, Guid labelId, string cmt) public async Task<OperationResult<bool>> AddNewLabelCommentAsync(User user, Guid labelId, string cmt)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var label = DbContext.Labels var label = await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == labelId).ConfigureAwait(false);
.FirstOrDefault(x => x.RoadieId == labelId); if (label == null)
if (label == null) return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", labelId)); {
return new OperationResult<bool>(true, $"Label Not Found [{labelId}]");
}
var newComment = new data.Comment var newComment = new data.Comment
{ {
@ -132,8 +208,8 @@ namespace Roadie.Api.Services
UserId = user.Id.Value, UserId = user.Id.Value,
Cmt = cmt Cmt = cmt
}; };
DbContext.Comments.Add(newComment); await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(label.CacheRegion); CacheManager.ClearRegion(label.CacheRegion);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -146,15 +222,16 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> AddNewPlaylistComment(User user, Guid playlistId, string cmt) public async Task<OperationResult<bool>> AddNewPlaylistCommentAsync(User user, Guid playlistId, string cmt)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var playlist = DbContext.Playlists var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == playlistId).ConfigureAwait(false);
.FirstOrDefault(x => x.RoadieId == playlistId);
if (playlist == null) if (playlist == null)
return new OperationResult<bool>(true, string.Format("Playlist Not Found [{0}]", playlistId)); {
return new OperationResult<bool>(true, $"Playlist Not Found [{playlistId}]");
}
var newComment = new data.Comment var newComment = new data.Comment
{ {
@ -162,8 +239,8 @@ namespace Roadie.Api.Services
UserId = user.Id.Value, UserId = user.Id.Value,
Cmt = cmt Cmt = cmt
}; };
DbContext.Comments.Add(newComment); await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(playlist.CacheRegion); CacheManager.ClearRegion(playlist.CacheRegion);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -176,15 +253,16 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> AddNewReleaseComment(User user, Guid releaseId, string cmt) public async Task<OperationResult<bool>> AddNewReleaseCommentAsync(User user, Guid releaseId, string cmt)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var release = DbContext.Releases var release = await DbContext.Releases.FirstOrDefaultAsync(x => x.RoadieId == releaseId).ConfigureAwait(false);
.FirstOrDefault(x => x.RoadieId == releaseId);
if (release == null) if (release == null)
return new OperationResult<bool>(true, string.Format("Release Not Found [{0}]", releaseId)); {
return new OperationResult<bool>(true, $"Release Not Found [{releaseId}]");
}
var newComment = new data.Comment var newComment = new data.Comment
{ {
@ -192,8 +270,8 @@ namespace Roadie.Api.Services
UserId = user.Id.Value, UserId = user.Id.Value,
Cmt = cmt Cmt = cmt
}; };
DbContext.Comments.Add(newComment); await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(release.CacheRegion); CacheManager.ClearRegion(release.CacheRegion);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -206,14 +284,16 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> AddNewTrackComment(User user, Guid trackId, string cmt) public async Task<OperationResult<bool>> AddNewTrackCommentAsync(User user, Guid trackId, string cmt)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var track = DbContext.Tracks var track = await DbContext.Tracks.FirstOrDefaultAsync(x => x.RoadieId == trackId).ConfigureAwait(false);
.FirstOrDefault(x => x.RoadieId == trackId); if (track == null)
if (track == null) return new OperationResult<bool>(true, string.Format("Track Not Found [{0}]", trackId)); {
return new OperationResult<bool>(true, $"Track Not Found [{trackId}]");
}
var newComment = new data.Comment var newComment = new data.Comment
{ {
@ -221,8 +301,8 @@ namespace Roadie.Api.Services
UserId = user.Id.Value, UserId = user.Id.Value,
Cmt = cmt Cmt = cmt
}; };
DbContext.Comments.Add(newComment); await DbContext.Comments.AddAsync(newComment).ConfigureAwait(false);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(track.CacheRegion); CacheManager.ClearRegion(track.CacheRegion);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -235,15 +315,19 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> DeleteComment(User user, Guid id) public async Task<OperationResult<bool>> DeleteCommentAsync(User user, Guid id)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var comment = DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); var comment = await DbContext.Comments.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
if (comment == null) return new OperationResult<bool>(true, string.Format("Comment Not Found [{0}]", id)); if (comment == null)
{
return new OperationResult<bool>(true, $"Comment Not Found [{id}]");
}
DbContext.Remove(comment); DbContext.Remove(comment);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
ClearCaches(comment); ClearCaches(comment);
sw.Stop(); sw.Stop();
result = true; result = true;
@ -256,15 +340,18 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> SetCommentReaction(User user, Guid id, CommentReaction reaction) public async Task<OperationResult<bool>> SetCommentReactionAsync(User user, Guid id, CommentReaction reaction)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var result = false; var result = false;
var errors = new List<Exception>(); var errors = new List<Exception>();
var comment = DbContext.Comments.FirstOrDefault(x => x.RoadieId == id); var comment = await DbContext.Comments.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
if (comment == null) return new OperationResult<bool>(true, string.Format("Comment Not Found [{0}]", id)); if (comment == null)
var userCommentReaction = {
DbContext.CommentReactions.FirstOrDefault(x => x.CommentId == comment.Id && x.UserId == user.Id); return new OperationResult<bool>(true, $"Comment Not Found [{id}]");
}
var userCommentReaction = await DbContext.CommentReactions.FirstOrDefaultAsync(x => x.CommentId == comment.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userCommentReaction == null) if (userCommentReaction == null)
{ {
userCommentReaction = new data.CommentReaction userCommentReaction = new data.CommentReaction
@ -272,20 +359,20 @@ namespace Roadie.Api.Services
CommentId = comment.Id, CommentId = comment.Id,
UserId = user.Id.Value UserId = user.Id.Value
}; };
DbContext.CommentReactions.Add(userCommentReaction); await DbContext.CommentReactions.AddAsync(userCommentReaction).ConfigureAwait(false);
} }
userCommentReaction.Reaction = reaction == CommentReaction.Unknown ? null : reaction.ToString(); userCommentReaction.Reaction = reaction == CommentReaction.Unknown ? null : reaction.ToString();
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
ClearCaches(comment); ClearCaches(comment);
var userCommentReactions = (from cr in DbContext.CommentReactions var userCommentReactions = await (from cr in DbContext.CommentReactions
where cr.CommentId == comment.Id where cr.CommentId == comment.Id
select cr).ToArray(); select cr)
.ToArrayAsync()
.ConfigureAwait(false);
var additionalData = new Dictionary<string, object>(); var additionalData = new Dictionary<string, object>();
additionalData.Add("likedCount", additionalData.Add("likedCount", userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Like).Count());
userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Like).Count()); additionalData.Add("dislikedCount", userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Dislike).Count());
additionalData.Add("dislikedCount",
userCommentReactions.Where(x => x.ReactionValue == CommentReaction.Dislike).Count());
sw.Stop(); sw.Stop();
result = true; result = true;
return new OperationResult<bool> return new OperationResult<bool>
@ -297,46 +384,5 @@ namespace Roadie.Api.Services
OperationTime = sw.ElapsedMilliseconds OperationTime = sw.ElapsedMilliseconds
}; };
} }
private void ClearCaches(data.Comment comment)
{
switch (comment.CommentType)
{
case CommentType.Artist:
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == comment.ArtistId);
if (artist != null) CacheManager.ClearRegion(artist.CacheRegion);
break;
case CommentType.Collection:
var collection = DbContext.Collections.FirstOrDefault(x => x.Id == comment.CollectionId);
if (collection != null) CacheManager.ClearRegion(collection.CacheRegion);
break;
case CommentType.Genre:
var genre = DbContext.Genres.FirstOrDefault(x => x.Id == comment.GenreId);
if (genre != null) CacheManager.ClearRegion(genre.CacheRegion);
break;
case CommentType.Label:
var label = DbContext.Labels.FirstOrDefault(x => x.Id == comment.LabelId);
if (label != null) CacheManager.ClearRegion(label.CacheRegion);
break;
case CommentType.Playlist:
var playlist = DbContext.Playlists.FirstOrDefault(x => x.Id == comment.PlaylistId);
if (playlist != null) CacheManager.ClearRegion(playlist.CacheRegion);
break;
case CommentType.Release:
var release = DbContext.Releases.FirstOrDefault(x => x.Id == comment.ReleaseId);
if (release != null) CacheManager.ClearRegion(release.CacheRegion);
break;
case CommentType.Track:
var track = DbContext.Tracks.FirstOrDefault(x => x.Id == comment.TrackId);
if (track != null) CacheManager.ClearRegion(track.CacheRegion);
break;
}
}
} }
} }

View file

@ -18,7 +18,7 @@ namespace Roadie.Api.Services
public async Task SendEmailAsync(string email, string subject, string htmlMessage) public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{ {
if(string.IsNullOrEmpty(Configuration.SmtpHost)) if (string.IsNullOrEmpty(Configuration.SmtpHost))
{ {
Trace.WriteLine("Email Server (Configuration.SmtpHost) Not Configured", "Warning"); Trace.WriteLine("Email Server (Configuration.SmtpHost) Not Configured", "Warning");
return; return;
@ -37,7 +37,7 @@ namespace Roadie.Api.Services
mail.IsBodyHtml = true; mail.IsBodyHtml = true;
mail.Body = htmlMessage; mail.Body = htmlMessage;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
await client.SendMailAsync(mail); await client.SendMailAsync(mail).ConfigureAwait(false);
} }
} }
} }

View file

@ -18,12 +18,20 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core;
using System.Threading.Tasks; using System.Threading.Tasks;
using data = Roadie.Library.Data;
namespace Roadie.Api.Services namespace Roadie.Api.Services
{ {
public class FileDirectoryProcessorService : ServiceBase, IFileDirectoryProcessorService public class FileDirectoryProcessorService : ServiceBase, IFileDirectoryProcessorService
{ {
private IArtistLookupEngine ArtistLookupEngine { get; }
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
private IFileProcessor FileProcessor { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; }
private IReleaseService ReleaseService { get; }
private List<int> _addedArtistIds = new List<int>(); private List<int> _addedArtistIds = new List<int>();
private List<int> _addedReleaseIds = new List<int>(); private List<int> _addedReleaseIds = new List<int>();
private List<int> _addedTrackIds = new List<int>(); private List<int> _addedTrackIds = new List<int>();
@ -34,14 +42,6 @@ namespace Roadie.Api.Services
public int? ProcessLimit { get; set; } public int? ProcessLimit { get; set; }
private IArtistLookupEngine ArtistLookupEngine { get; }
private IAudioMetaDataHelper AudioMetaDataHelper { get; }
private IFileProcessor FileProcessor { get; }
private IReleaseLookupEngine ReleaseLookupEngine { get; }
private IReleaseService ReleaseService { get; }
public FileDirectoryProcessorService(IRoadieSettings configuration, public FileDirectoryProcessorService(IRoadieSettings configuration,
IHttpEncoder httpEncoder, IHttpEncoder httpEncoder,
IHttpContext httpContext, IHttpContext httpContext,
@ -62,73 +62,17 @@ namespace Roadie.Api.Services
FileProcessor = fileProcessor; FileProcessor = fileProcessor;
} }
public static OperationResult<bool> DeleteEmptyFolders(DirectoryInfo processingFolder, ILogger logger)
{
var result = new OperationResult<bool>();
try
{
result.IsSuccess = FolderPathHelper.DeleteEmptyFolders(processingFolder);
}
catch (Exception ex)
{
logger.LogError(ex, string.Format("Error Deleting Empty Folder [{0}] Error [{1}]", processingFolder.FullName, ex.Serialize()));
}
return result;
}
public async Task<OperationResult<bool>> Process(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true)
{
var sw = new Stopwatch();
sw.Start();
await PreProcessFolder(folder, doJustInfo);
var processedFiles = 0;
var pluginResultInfos = new List<PluginResultInfo>();
var errors = new List<string>();
_addedArtistIds.Clear();
_addedReleaseIds.Clear();
_addedTrackIds.Clear();
FileProcessor.SubmissionId = submissionId;
foreach (var file in Directory.EnumerateFiles(folder.FullName, "*.*", SearchOption.AllDirectories)
.ToArray())
{
var operation = await FileProcessor.Process(file, doJustInfo);
if (operation != null && operation.AdditionalData != null &&
operation.AdditionalData.ContainsKey(PluginResultInfo.AdditionalDataKeyPluginResultInfo))
{
pluginResultInfos.Add(operation.AdditionalData[PluginResultInfo.AdditionalDataKeyPluginResultInfo] as PluginResultInfo);
processedFiles++;
}
if (ProcessLimit.HasValue && processedFiles > ProcessLimit.Value) break;
}
await PostProcessFolder(user, folder, pluginResultInfos, doJustInfo, doDeleteFiles);
sw.Stop();
_addedArtistIds.AddRange(ArtistLookupEngine.AddedArtistIds);
_addedReleaseIds.AddRange(ReleaseLookupEngine.AddedReleaseIds);
_addedTrackIds.AddRange(ReleaseLookupEngine.AddedTrackIds);
Logger.LogInformation("Completed! Processed Folder [{0}]: Processed Files [{1}] : Elapsed Time [{2}]", folder.FullName, processedFiles, sw.Elapsed);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
AdditionalData = new Dictionary<string, object> { { "ProcessedFiles", processedFiles } },
OperationTime = sw.ElapsedMilliseconds
};
}
/// <summary> /// <summary>
/// Perform any operations to the given folder and the plugin results after processing /// Perform any operations to the given folder and the plugin results after processing
/// </summary> /// </summary>
private async Task<bool> PostProcessFolder(User user, DirectoryInfo inboundFolder, IEnumerable<PluginResultInfo> pluginResults, bool doJustInfo, bool doDeleteFiles = true) private async Task<bool> PostProcessFolderAsync(User user, DirectoryInfo inboundFolder, IEnumerable<PluginResultInfo> pluginResults, bool doJustInfo, bool doDeleteFiles = true)
{ {
SimpleContract.Requires<ArgumentNullException>(inboundFolder != null, "Invalid InboundFolder"); SimpleContract.Requires<ArgumentNullException>(inboundFolder != null, "Invalid InboundFolder");
if (pluginResults != null) if (pluginResults != null)
{ {
foreach (var releasesInfo in pluginResults.GroupBy(x => x.ReleaseId).Select(x => x.First())) foreach (var releasesInfo in pluginResults.GroupBy(x => x.ReleaseId).Select(x => x.First()))
{ {
await ReleaseService.ScanReleaseFolder(user, releasesInfo.ReleaseId, doJustInfo); await ReleaseService.ScanReleaseFolderAsync(user, releasesInfo.ReleaseId, doJustInfo).ConfigureAwait(false);
_addedTrackIds.AddRange(ReleaseService.AddedTrackIds); _addedTrackIds.AddRange(ReleaseService.AddedTrackIds);
} }
} }
@ -136,6 +80,7 @@ namespace Roadie.Api.Services
{ {
var fileExtensionsToDelete = Configuration.FileExtensionsToDelete ?? new string[0]; var fileExtensionsToDelete = Configuration.FileExtensionsToDelete ?? new string[0];
if (fileExtensionsToDelete.Any()) if (fileExtensionsToDelete.Any())
{
foreach (var fileInFolder in inboundFolder.GetFiles("*.*", SearchOption.AllDirectories)) foreach (var fileInFolder in inboundFolder.GetFiles("*.*", SearchOption.AllDirectories))
{ {
if (fileExtensionsToDelete.Any(x => x.Equals(fileInFolder.Extension, StringComparison.OrdinalIgnoreCase))) if (fileExtensionsToDelete.Any(x => x.Equals(fileInFolder.Extension, StringComparison.OrdinalIgnoreCase)))
@ -148,6 +93,7 @@ namespace Roadie.Api.Services
} }
} }
} }
}
DeleteEmptyFolders(inboundFolder, Logger); DeleteEmptyFolders(inboundFolder, Logger);
} }
@ -158,9 +104,67 @@ namespace Roadie.Api.Services
/// <summary> /// <summary>
/// Perform any operations to the given folder before processing /// Perform any operations to the given folder before processing
/// </summary> /// </summary>
private Task<bool> PreProcessFolder(DirectoryInfo inboundFolder, bool doJustInfo = false) private Task<bool> PreProcessFolderAsync(DirectoryInfo inboundFolder, bool doJustInfo = false)
{ {
return Task.FromResult(true); return Task.FromResult(true);
} }
public static OperationResult<bool> DeleteEmptyFolders(DirectoryInfo processingFolder, ILogger logger)
{
var result = new OperationResult<bool>();
try
{
result.IsSuccess = FolderPathHelper.DeleteEmptyFolders(processingFolder);
}
catch (Exception ex)
{
logger.LogError(ex, $"Error Deleting Empty Folder [{processingFolder.FullName}] Error [{ex.Serialize()}]");
}
return result;
}
public async Task<OperationResult<bool>> ProcessAsync(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true)
{
var sw = new Stopwatch();
sw.Start();
await PreProcessFolderAsync(folder, doJustInfo).ConfigureAwait(false);
var processedFiles = 0;
var pluginResultInfos = new List<PluginResultInfo>();
var errors = new List<string>();
_addedArtistIds.Clear();
_addedReleaseIds.Clear();
_addedTrackIds.Clear();
FileProcessor.SubmissionId = submissionId;
foreach (var file in Directory.EnumerateFiles(folder.FullName, "*.*", SearchOption.AllDirectories) .ToArray())
{
var operation = await FileProcessor.Process(file, doJustInfo).ConfigureAwait(false);
if (operation != null && operation.AdditionalData != null &&
operation.AdditionalData.ContainsKey(PluginResultInfo.AdditionalDataKeyPluginResultInfo))
{
pluginResultInfos.Add(operation.AdditionalData[PluginResultInfo.AdditionalDataKeyPluginResultInfo] as PluginResultInfo);
processedFiles++;
}
if (ProcessLimit.HasValue && processedFiles > ProcessLimit.Value)
{
break;
}
}
await PostProcessFolderAsync(user, folder, pluginResultInfos, doJustInfo, doDeleteFiles).ConfigureAwait(false);
sw.Stop();
_addedArtistIds.AddRange(ArtistLookupEngine.AddedArtistIds);
_addedReleaseIds.AddRange(ReleaseLookupEngine.AddedReleaseIds);
_addedTrackIds.AddRange(ReleaseLookupEngine.AddedTrackIds);
Logger.LogInformation("Completed! Processed Folder [{0}]: Processed Files [{1}] : Elapsed Time [{2}]", folder.FullName, processedFiles, sw.Elapsed);
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
AdditionalData = new Dictionary<string, object> { { "ProcessedFiles", processedFiles } },
OperationTime = sw.ElapsedMilliseconds
};
}
} }
} }

View file

@ -9,11 +9,9 @@ using Roadie.Library.Data.Context;
using Roadie.Library.Encoding; using Roadie.Library.Encoding;
using Roadie.Library.Enums; using Roadie.Library.Enums;
using Roadie.Library.Extensions; using Roadie.Library.Extensions;
using Roadie.Library.Identity;
using Roadie.Library.Imaging; using Roadie.Library.Imaging;
using Roadie.Library.Models; using Roadie.Library.Models;
using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users;
using Roadie.Library.Utility; using Roadie.Library.Utility;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -38,15 +36,109 @@ namespace Roadie.Api.Services
{ {
} }
public async Task<OperationResult<Genre>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null) private Task<OperationResult<Genre>> GenreByIdAction(Guid id, IEnumerable<string> includes = null)
{
var timings = new Dictionary<string, long>();
var tsw = new Stopwatch();
var sw = Stopwatch.StartNew();
sw.Start();
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id);
if (genre == null)
{
return Task.FromResult(new OperationResult<Genre>(true, $"Genre Not Found [{id}]"));
}
tsw.Stop();
timings.Add("getGenre", tsw.ElapsedMilliseconds);
tsw.Restart();
var result = genre.Adapt<Genre>();
result.AlternateNames = genre.AlternateNames;
result.Tags = genre.Tags;
result.Thumbnail = ImageHelper.MakeGenreThumbnailImage(Configuration, HttpContext, genre.RoadieId);
result.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds);
if (includes != null && includes.Any())
{
if (includes.Contains("stats"))
{
tsw.Restart();
var releaseCount = (from rg in DbContext.ReleaseGenres
where rg.GenreId == genre.Id
select rg.Id).Count();
var artistCount = (from rg in DbContext.ArtistGenres
where rg.GenreId == genre.Id
select rg.Id).Count();
result.Statistics = new Library.Models.Statistics.ReleaseGroupingStatistics
{
ArtistCount = artistCount,
ReleaseCount = releaseCount
};
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);
}
}
sw.Stop();
Logger.LogInformation($"ByIdAction: Genre `{ genre }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]");
return Task.FromResult(new OperationResult<Genre>
{
Data = result,
IsSuccess = result != null,
OperationTime = sw.ElapsedMilliseconds
});
}
private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(Library.Models.Users.User user, Guid id, byte[] imageBytes)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
if (genre == null)
{
return new OperationResult<Library.Models.Image>(true, $"Genre Not Found [{id}]");
}
try
{
var now = DateTime.UtcNow;
if (imageBytes != null)
{
// Save unaltered genre image
File.WriteAllBytes(genre.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(imageBytes));
}
genre.LastUpdated = now;
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(genre.CacheRegion);
Logger.LogInformation($"UploadGenreImage `{genre}` By User `{user}`");
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<Library.Models.Image>
{
IsSuccess = !errors.Any(),
Data = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<Genre>> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null)
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
var cacheKey = string.Format("urn:genre_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes)); var cacheKey = $"urn:genre_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
var result = await CacheManager.GetAsync(cacheKey, async () => var result = await CacheManager.GetAsync(cacheKey, async () =>
{ {
return await GenreByIdAction(id, includes); return await GenreByIdAction(id, includes).ConfigureAwait(false);
}, data.Genre.CacheRegionUrn(id)); }, data.Genre.CacheRegionUrn(id)).ConfigureAwait(false);
sw.Stop(); sw.Stop();
return new OperationResult<Genre>(result.Messages) return new OperationResult<Genre>(result.Messages)
{ {
@ -58,14 +150,18 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id) public async Task<OperationResult<bool>> DeleteAsync(Library.Identity.User user, Guid id)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id); var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
if (genre == null) return new OperationResult<bool>(true, string.Format("Genre Not Found [{0}]", id)); if (genre == null)
{
return new OperationResult<bool>(true, $"Genre Not Found [{id}]");
}
DbContext.Genres.Remove(genre); DbContext.Genres.Remove(genre);
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
var genreImageFilename = genre.PathToImage(Configuration); var genreImageFilename = genre.PathToImage(Configuration);
if (File.Exists(genreImageFilename)) if (File.Exists(genreImageFilename))
@ -84,7 +180,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<Library.Models.Pagination.PagedResult<GenreList>> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false) public async Task<Library.Models.Pagination.PagedResult<GenreList>> ListAsync(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -102,7 +198,7 @@ namespace Roadie.Api.Services
if (doRandomize ?? false) if (doRandomize ?? false)
{ {
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue; var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomGenreData = await DbContext.RandomGenreIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly); randomGenreData = await DbContext.RandomGenreIdsAsync(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly).ConfigureAwait(false);
randomGenreIds = randomGenreData.Select(x => x.Value).ToArray(); randomGenreIds = randomGenreData.Select(x => x.Value).ToArray();
rowCount = DbContext.Genres.Count(); rowCount = DbContext.Genres.Count();
} }
@ -169,20 +265,17 @@ namespace Roadie.Api.Services
}); });
} }
public async Task<OperationResult<Library.Models.Image>> SetGenreImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl) public Task<OperationResult<Library.Models.Image>> SetGenreImageByUrlAsync(Library.Models.Users.User user, Guid id, string imageUrl) => SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
{
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
}
public async Task<OperationResult<bool>> UpdateGenre(Library.Models.Users.User user, Genre model) public async Task<OperationResult<bool>> UpdateGenreAsync(Library.Models.Users.User user, Genre model)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var errors = new List<Exception>(); var errors = new List<Exception>();
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == model.Id); var genre = await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false);
if (genre == null) if (genre == null)
{ {
return new OperationResult<bool>(true, string.Format("Genre Not Found [{0}]", model.Id)); return new OperationResult<bool>(true, $"Genre Not Found [{model.Id}]");
} }
// If genre is being renamed, see if genre already exists with new model supplied name // If genre is being renamed, see if genre already exists with new model supplied name
var genreName = genre.SortNameValue; var genreName = genre.SortNameValue;
@ -229,7 +322,7 @@ namespace Roadie.Api.Services
File.WriteAllBytes(genre.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(genreImage)); File.WriteAllBytes(genre.PathToImage(Configuration), ImageHelper.ConvertToJpegFormat(genreImage));
} }
genre.LastUpdated = now; genre.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(genre.CacheRegion); CacheManager.ClearRegion(genre.CacheRegion);
Logger.LogInformation($"UpdateGenre `{genre}` By User `{user}`"); Logger.LogInformation($"UpdateGenre `{genre}` By User `{user}`");
@ -244,14 +337,14 @@ namespace Roadie.Api.Services
return new OperationResult<bool> return new OperationResult<bool>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = !errors.Any(), Data = errors.Count == 0,
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<Library.Models.Image>> UploadGenreImage(Library.Models.Users.User user, Guid id, IFormFile file) public async Task<OperationResult<Library.Models.Image>> UploadGenreImageAsync(Library.Models.Users.User user, Guid id, IFormFile file)
{ {
var bytes = new byte[0]; var bytes = new byte[0];
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
@ -259,102 +352,7 @@ namespace Roadie.Api.Services
file.CopyTo(ms); file.CopyTo(ms);
bytes = ms.ToArray(); bytes = ms.ToArray();
} }
return await SaveImageBytes(user, id, bytes).ConfigureAwait(false);
return await SaveImageBytes(user, id, bytes);
}
private Task<OperationResult<Genre>> GenreByIdAction(Guid id, IEnumerable<string> includes = null)
{
var timings = new Dictionary<string, long>();
var tsw = new Stopwatch();
var sw = Stopwatch.StartNew();
sw.Start();
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id);
if (genre == null)
{
return Task.FromResult(new OperationResult<Genre>(true, string.Format("Genre Not Found [{0}]", id)));
}
tsw.Stop();
timings.Add("getGenre", tsw.ElapsedMilliseconds);
tsw.Restart();
var result = genre.Adapt<Genre>();
result.AlternateNames = genre.AlternateNames;
result.Tags = genre.Tags;
result.Thumbnail = ImageHelper.MakeGenreThumbnailImage(Configuration, HttpContext, genre.RoadieId);
result.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds);
if (includes != null && includes.Any())
{
if (includes.Contains("stats"))
{
tsw.Restart();
var releaseCount = (from rg in DbContext.ReleaseGenres
where rg.GenreId == genre.Id
select rg.Id).Count();
var artistCount = (from rg in DbContext.ArtistGenres
where rg.GenreId == genre.Id
select rg.Id).Count();
result.Statistics = new Library.Models.Statistics.ReleaseGroupingStatistics
{
ArtistCount = artistCount,
ReleaseCount = releaseCount
};
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);
}
}
sw.Stop();
Logger.LogInformation($"ByIdAction: Genre `{ genre }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]");
return Task.FromResult(new OperationResult<Genre>
{
Data = result,
IsSuccess = result != null,
OperationTime = sw.ElapsedMilliseconds
});
}
private async Task<OperationResult<Library.Models.Image>> SaveImageBytes(Library.Models.Users.User user, Guid id, byte[] imageBytes)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var genre = DbContext.Genres.FirstOrDefault(x => x.RoadieId == id);
if (genre == null)
{
return new OperationResult<Library.Models.Image>(true, string.Format("Genre Not Found [{0}]", id));
}
try
{
var now = DateTime.UtcNow;
if (imageBytes != null)
{
// Save unaltered genre image
File.WriteAllBytes(genre.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(imageBytes));
}
genre.LastUpdated = now;
await DbContext.SaveChangesAsync();
CacheManager.ClearRegion(genre.CacheRegion);
Logger.LogInformation($"UploadGenreImage `{genre}` By User `{user}`");
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<Library.Models.Image>
{
IsSuccess = !errors.Any(),
Data = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "genre", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
} }
} }
} }

View file

@ -14,10 +14,17 @@ namespace Roadie.Api.Services
public HttpContext(IRoadieSettings configuration, IUrlHelper urlHelper) public HttpContext(IRoadieSettings configuration, IUrlHelper urlHelper)
{ {
var scheme = urlHelper.ActionContext.HttpContext.Request.Scheme; var scheme = urlHelper.ActionContext.HttpContext.Request.Scheme;
if (configuration.UseSSLBehindProxy) scheme = "https"; if (configuration.UseSSLBehindProxy)
{
scheme = "https";
}
var host = urlHelper.ActionContext.HttpContext.Request.Host; var host = urlHelper.ActionContext.HttpContext.Request.Host;
if (!string.IsNullOrEmpty(configuration.BehindProxyHost)) if (!string.IsNullOrEmpty(configuration.BehindProxyHost))
{
host = new HostString(configuration.BehindProxyHost); host = new HostString(configuration.BehindProxyHost);
}
BaseUrl = $"{scheme}://{host}"; BaseUrl = $"{scheme}://{host}";
ImageBaseUrl = $"{BaseUrl}/images"; ImageBaseUrl = $"{BaseUrl}/images";
} }

View file

@ -9,48 +9,48 @@ namespace Roadie.Api.Services
{ {
public interface IAdminService public interface IAdminService
{ {
Task<OperationResult<bool>> DeleteArtist(User user, Guid artistId, bool deleteFolder); Task<OperationResult<bool>> DeleteArtistAsync(User user, Guid artistId, bool deleteFolder);
Task<OperationResult<bool>> DeleteArtistReleases(User user, Guid artistId, bool doDeleteFiles = false); Task<OperationResult<bool>> DeleteArtistReleasesAsync(User user, Guid artistId, bool doDeleteFiles = false);
Task<OperationResult<bool>> DeleteArtistSecondaryImage(User user, Guid artistId, int index); Task<OperationResult<bool>> DeleteArtistSecondaryImageAsync(User user, Guid artistId, int index);
Task<OperationResult<bool>> DeleteGenre(User user, Guid genreId); Task<OperationResult<bool>> DeleteGenreAsync(User user, Guid genreId);
Task<OperationResult<bool>> DeleteLabel(User user, Guid labelId); Task<OperationResult<bool>> DeleteLabelAsync(User user, Guid labelId);
Task<OperationResult<bool>> DeleteRelease(User user, Guid releaseId, bool? doDeleteFiles); Task<OperationResult<bool>> DeleteReleaseAsync(User user, Guid releaseId, bool? doDeleteFiles);
Task<OperationResult<bool>> DeleteReleaseSecondaryImage(User user, Guid releaseId, int index); Task<OperationResult<bool>> DeleteReleaseSecondaryImageAsync(User user, Guid releaseId, int index);
Task<OperationResult<bool>> DeleteTracks(User user, IEnumerable<Guid> trackIds, bool? doDeleteFile); Task<OperationResult<bool>> DeleteTracksAsync(User user, IEnumerable<Guid> trackIds, bool? doDeleteFile);
Task<OperationResult<bool>> DeleteUser(User applicationUser, Guid id); Task<OperationResult<bool>> DeleteUserAsync(User applicationUser, Guid id);
Task<OperationResult<bool>> DoInitialSetup(User user, UserManager<User> userManager); Task<OperationResult<bool>> DoInitialSetupAsync(User user, UserManager<User> userManager);
Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleases(User user); Task<OperationResult<Dictionary<string, List<string>>>> MissingCollectionReleasesAsync(User user);
void PerformStartUpTasks(); void PerformStartUpTasks();
Task<OperationResult<bool>> ScanAllCollections(User user, bool isReadOnly = false, bool doPurgeFirst = false); Task<OperationResult<bool>> ScanAllCollectionsAsync(User user, bool isReadOnly = false, bool doPurgeFirst = false);
Task<OperationResult<bool>> ScanArtist(User user, Guid artistId, bool isReadOnly = false); Task<OperationResult<bool>> ScanArtistAsync(User user, Guid artistId, bool isReadOnly = false);
Task<OperationResult<bool>> ScanArtists(User user, IEnumerable<Guid> artistIds, bool isReadOnly = false); Task<OperationResult<bool>> ScanArtistsAsync(User user, IEnumerable<Guid> artistIds, bool isReadOnly = false);
Task<OperationResult<bool>> ScanCollection(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true); Task<OperationResult<bool>> ScanCollectionAsync(User user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true);
Task<OperationResult<bool>> ScanInboundFolder(User user, bool isReadOnly = false); Task<OperationResult<bool>> ScanInboundFolderAsync(User user, bool isReadOnly = false);
Task<OperationResult<bool>> ScanLibraryFolder(User user, bool isReadOnly = false); Task<OperationResult<bool>> ScanLibraryFolderAsync(User user, bool isReadOnly = false);
Task<OperationResult<bool>> ScanRelease(User user, Guid releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); Task<OperationResult<bool>> ScanReleaseAsync(User user, Guid releaseIds, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false);
Task<OperationResult<bool>> ScanReleases(User user, IEnumerable<Guid> releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); Task<OperationResult<bool>> ScanReleasesAsync(User user, IEnumerable<Guid> releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false);
Task<OperationResult<bool>> UpdateInviteTokenUsed(Guid? tokenId); Task<OperationResult<bool>> UpdateInviteTokenUsedAsync(Guid? tokenId);
Task<OperationResult<bool>> ValidateInviteToken(Guid? tokenId); Task<OperationResult<bool>> ValidateInviteTokenAsync(Guid? tokenId);
} }
} }

View file

@ -1,9 +1,7 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Roadie.Library; using Roadie.Library;
using Roadie.Library.Identity;
using Roadie.Library.Models; using Roadie.Library.Models;
using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,22 +10,22 @@ namespace Roadie.Api.Services
{ {
public interface IArtistService public interface IArtistService
{ {
Task<OperationResult<Artist>> ById(Library.Models.Users.User user, Guid id, IEnumerable<string> includes); Task<OperationResult<Artist>> ByIdAsync(Library.Models.Users.User user, Guid id, IEnumerable<string> includes);
Task<OperationResult<bool>> Delete(Library.Identity.User user, Library.Data.Artist Artist, bool deleteFolder); Task<OperationResult<bool>> DeleteAsync(Library.Identity.User user, Library.Data.Artist Artist, bool deleteFolder);
Task<PagedResult<ArtistList>> List(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true); Task<PagedResult<ArtistList>> ListAsync(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true);
Task<OperationResult<bool>> MergeArtists(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId); Task<OperationResult<bool>> MergeArtistsAsync(Library.Identity.User user, Guid artistToMergeId, Guid artistToMergeIntoId);
Task<OperationResult<bool>> RefreshArtistMetadata(Library.Identity.User user, Guid ArtistId); Task<OperationResult<bool>> RefreshArtistMetadataAsync(Library.Identity.User user, Guid ArtistId);
Task<OperationResult<bool>> ScanArtistReleasesFolders(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo); Task<OperationResult<bool>> ScanArtistReleasesFoldersAsync(Library.Identity.User user, Guid artistId, string destinationFolder, bool doJustInfo);
Task<OperationResult<Image>> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl); Task<OperationResult<Image>> SetReleaseImageByUrlAsync(Library.Identity.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateArtist(Library.Identity.User user, Artist artist); Task<OperationResult<bool>> UpdateArtistAsync(Library.Identity.User user, Artist artist);
Task<OperationResult<Image>> UploadArtistImage(Library.Identity.User user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadArtistImageAsync(Library.Identity.User user, Guid id, IFormFile file);
} }
} }

View file

@ -9,7 +9,7 @@ namespace Roadie.Api.Services
{ {
public interface IBookmarkService public interface IBookmarkService
{ {
Task<OperationResult<bool>> RemoveAllBookmarksForItem(BookmarkType type, int id); Task<PagedResult<BookmarkList>> ListAsync(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null);
Task<PagedResult<BookmarkList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null); Task<OperationResult<bool>> RemoveAllBookmarksForItemAsync(BookmarkType type, int id);
} }
} }

View file

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

View file

@ -8,22 +8,22 @@ namespace Roadie.Api.Services
{ {
public interface ICommentService public interface ICommentService
{ {
Task<OperationResult<bool>> AddNewArtistComment(User user, Guid artistId, string cmt); Task<OperationResult<bool>> AddNewArtistCommentAsync(User user, Guid artistId, string cmt);
Task<OperationResult<bool>> AddNewCollectionComment(User user, Guid collectionId, string cmt); Task<OperationResult<bool>> AddNewCollectionCommentAsync(User user, Guid collectionId, string cmt);
Task<OperationResult<bool>> AddNewGenreComment(User user, Guid genreId, string cmt); Task<OperationResult<bool>> AddNewGenreCommentAsync(User user, Guid genreId, string cmt);
Task<OperationResult<bool>> AddNewLabelComment(User user, Guid labelId, string cmt); Task<OperationResult<bool>> AddNewLabelCommentAsync(User user, Guid labelId, string cmt);
Task<OperationResult<bool>> AddNewPlaylistComment(User user, Guid playlistId, string cmt); Task<OperationResult<bool>> AddNewPlaylistCommentAsync(User user, Guid playlistId, string cmt);
Task<OperationResult<bool>> AddNewReleaseComment(User user, Guid releaseId, string cmt); Task<OperationResult<bool>> AddNewReleaseCommentAsync(User user, Guid releaseId, string cmt);
Task<OperationResult<bool>> AddNewTrackComment(User user, Guid trackId, string cmt); Task<OperationResult<bool>> AddNewTrackCommentAsync(User user, Guid trackId, string cmt);
Task<OperationResult<bool>> DeleteComment(User user, Guid id); Task<OperationResult<bool>> DeleteCommentAsync(User user, Guid id);
Task<OperationResult<bool>> SetCommentReaction(User user, Guid id, CommentReaction reaction); Task<OperationResult<bool>> SetCommentReactionAsync(User user, Guid id, CommentReaction reaction);
} }
} }

View file

@ -14,6 +14,6 @@ namespace Roadie.Api.Services
int? ProcessLimit { get; set; } int? ProcessLimit { get; set; }
Task<OperationResult<bool>> Process(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true); Task<OperationResult<bool>> ProcessAsync(User user, DirectoryInfo folder, bool doJustInfo, int? submissionId = null, bool doDeleteFiles = true);
} }
} }

View file

@ -1,9 +1,7 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Roadie.Library; using Roadie.Library;
using Roadie.Library.Identity;
using Roadie.Library.Models; using Roadie.Library.Models;
using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,16 +10,16 @@ namespace Roadie.Api.Services
{ {
public interface IGenreService public interface IGenreService
{ {
Task<OperationResult<Genre>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null); Task<OperationResult<Genre>> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id); Task<OperationResult<bool>> DeleteAsync(Library.Identity.User user, Guid id);
Task<PagedResult<GenreList>> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false); Task<PagedResult<GenreList>> ListAsync(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false);
Task<OperationResult<Image>> SetGenreImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl); Task<OperationResult<Image>> SetGenreImageByUrlAsync(Library.Models.Users.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateGenre(Library.Models.Users.User user, Genre model); Task<OperationResult<bool>> UpdateGenreAsync(Library.Models.Users.User user, Genre model);
Task<OperationResult<Image>> UploadGenreImage(Library.Models.Users.User user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadGenreImageAsync(Library.Models.Users.User user, Guid id, IFormFile file);
} }
} }

View file

@ -1,8 +1,6 @@
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Roadie.Library; using Roadie.Library;
using Roadie.Library.Identity;
using Roadie.Library.Imaging; using Roadie.Library.Imaging;
using Roadie.Library.Models;
using Roadie.Library.SearchEngines.Imaging; using Roadie.Library.SearchEngines.Imaging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -16,26 +14,26 @@ namespace Roadie.Api.Services
string RequestIp { get; set; } string RequestIp { get; set; }
Task<FileOperationResult<IImage>> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> ArtistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> ArtistSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> CollectionImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> GenreImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> LabelImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> PlaylistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> ReleaseImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> ReleaseSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null);
Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10); Task<OperationResult<IEnumerable<ImageSearchResult>>> SearchAsync(string query, int resultsCount = 10);
Task<FileOperationResult<IImage>> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> TrackImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
Task<FileOperationResult<IImage>> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null); Task<FileOperationResult<IImage>> UserImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null);
} }
} }

View file

@ -1,9 +1,7 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Roadie.Library; using Roadie.Library;
using Roadie.Library.Identity;
using Roadie.Library.Models; using Roadie.Library.Models;
using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,18 +10,18 @@ namespace Roadie.Api.Services
{ {
public interface ILabelService public interface ILabelService
{ {
Task<OperationResult<Label>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null); Task<OperationResult<Label>> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id); Task<OperationResult<bool>> DeleteAsync(Library.Identity.User user, Guid id);
Task<PagedResult<LabelList>> List(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false); Task<PagedResult<LabelList>> ListAsync(Library.Models.Users.User roadieUser, PagedRequest request, bool? doRandomize = false);
Task<OperationResult<bool>> MergeLabelsIntoLabel(Library.Identity.User user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge); Task<OperationResult<bool>> MergeLabelsIntoLabelAsync(Library.Identity.User user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge);
Task<OperationResult<Image>> SetLabelImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl); Task<OperationResult<Image>> SetLabelImageByUrlAsync(Library.Models.Users.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateLabel(Library.Models.Users.User user, Label label); Task<OperationResult<bool>> UpdateLabelAsync(Library.Models.Users.User user, Label label);
Task<OperationResult<Image>> UploadLabelImage(Library.Models.Users.User user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadLabelImageAsync(Library.Models.Users.User user, Guid id, IFormFile file);
} }
} }

View file

@ -7,24 +7,24 @@ namespace Roadie.Api.Services
{ {
public interface ILookupService public interface ILookupService
{ {
Task<OperationResult<IEnumerable<DataToken>>> ArtistTypes(); Task<OperationResult<IEnumerable<DataToken>>> ArtistTypesAsync();
Task<OperationResult<IEnumerable<DataToken>>> BandStatus(); Task<OperationResult<IEnumerable<DataToken>>> BandStatusAsync();
Task<OperationResult<IEnumerable<DataToken>>> BookmarkTypes(); Task<OperationResult<IEnumerable<DataToken>>> BookmarkTypesAsync();
Task<OperationResult<IEnumerable<DataToken>>> CollectionTypes(); Task<OperationResult<IEnumerable<DataToken>>> CollectionTypesAsync();
Task<OperationResult<IEnumerable<DataToken>>> LibraryStatus(); Task<OperationResult<IEnumerable<DataToken>>> CreditCategoriesAsync();
Task<OperationResult<IEnumerable<DataToken>>> QueMessageTypes(); Task<OperationResult<IEnumerable<DataToken>>> LibraryStatusAsync();
Task<OperationResult<IEnumerable<DataToken>>> ReleaseTypes(); Task<OperationResult<IEnumerable<DataToken>>> QueMessageTypesAsync();
Task<OperationResult<IEnumerable<DataToken>>> RequestStatus(); Task<OperationResult<IEnumerable<DataToken>>> ReleaseTypesAsync();
Task<OperationResult<IEnumerable<DataToken>>> Status(); Task<OperationResult<IEnumerable<DataToken>>> RequestStatusAsync();
Task<OperationResult<IEnumerable<DataToken>>> CreditCategories(); Task<OperationResult<IEnumerable<DataToken>>> StatusAsync();
} }
} }

View file

@ -10,10 +10,10 @@ namespace Roadie.Api.Services
{ {
public interface IPlayActivityService public interface IPlayActivityService
{ {
Task<PagedResult<PlayActivityList>> List(PagedRequest request, User roadieUser = null, DateTime? newerThan = null); Task<PagedResult<PlayActivityList>> ListAsync(PagedRequest request, User roadieUser = null, DateTime? newerThan = null);
Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble); Task<OperationResult<bool>> NowPlayingAsync(User roadieUser, ScrobbleInfo scrobble);
Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble); Task<OperationResult<bool>> ScrobbleAsync(User roadieUser, ScrobbleInfo scrobble);
} }
} }

View file

@ -11,20 +11,20 @@ namespace Roadie.Api.Services
{ {
public interface IPlaylistService public interface IPlaylistService
{ {
Task<OperationResult<PlaylistList>> AddNewPlaylist(User user, Playlist model); Task<OperationResult<PlaylistList>> AddNewPlaylistAsync(User user, Playlist model);
Task<OperationResult<bool>> AddTracksToPlaylist(data.Playlist playlist, IEnumerable<Guid> trackIds); Task<OperationResult<bool>> AddTracksToPlaylistAsync(data.Playlist playlist, IEnumerable<Guid> trackIds);
Task<OperationResult<Playlist>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null); Task<OperationResult<Playlist>> ByIdAsync(User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> DeletePlaylist(User user, Guid id); Task<OperationResult<bool>> DeletePlaylistAsync(User user, Guid id);
Task<PagedResult<PlaylistList>> List(PagedRequest request, User roadieUser = null); Task<PagedResult<PlaylistList>> ListAsync(PagedRequest request, User roadieUser = null);
Task<OperationResult<bool>> ReorderPlaylist(data.Playlist playlist); Task<OperationResult<bool>> ReorderPlaylistAsync(data.Playlist playlist);
Task<OperationResult<bool>> UpdatePlaylist(User user, Playlist label); Task<OperationResult<bool>> UpdatePlaylistAsync(User user, Playlist label);
Task<OperationResult<bool>> UpdatePlaylistTracks(User user, PlaylistTrackModifyRequest request); Task<OperationResult<bool>> UpdatePlaylistTracksAsync(User user, PlaylistTrackModifyRequest request);
} }
} }

View file

@ -1,10 +1,8 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Roadie.Library; using Roadie.Library;
using Roadie.Library.Identity;
using Roadie.Library.Models; using Roadie.Library.Models;
using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Releases; using Roadie.Library.Models.Releases;
using Roadie.Library.Models.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -15,26 +13,26 @@ namespace Roadie.Api.Services
{ {
IEnumerable<int> AddedTrackIds { get; } IEnumerable<int> AddedTrackIds { get; }
Task<OperationResult<Release>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null); Task<OperationResult<Release>> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null);
Task<OperationResult<bool>> Delete(Library.Identity.User user, Library.Data.Release release, bool doDeleteFiles = false, bool doUpdateArtistCounts = true); Task<OperationResult<bool>> DeleteAsync(Library.Identity.User user, Library.Data.Release release, bool doDeleteFiles = false, bool doUpdateArtistCounts = true);
Task<OperationResult<bool>> DeleteReleases(Library.Identity.User user, IEnumerable<Guid> releaseIds, bool doDeleteFiles = false); Task<OperationResult<bool>> DeleteReleasesAsync(Library.Identity.User user, IEnumerable<Guid> releaseIds, bool doDeleteFiles = false);
Task<PagedResult<ReleaseList>> List(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null); Task<PagedResult<ReleaseList>> ListAsync(Library.Models.Users.User user, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null);
Task<OperationResult<bool>> MergeReleases(Library.Identity.User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia); Task<OperationResult<bool>> MergeReleasesAsync(Library.Identity.User user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia);
Task<OperationResult<bool>> MergeReleases(Library.Identity.User user, Library.Data.Release releaseToMerge, Library.Data.Release releaseToMergeInto, bool addAsMedia); Task<OperationResult<bool>> MergeReleasesAsync(Library.Identity.User user, Library.Data.Release releaseToMerge, Library.Data.Release releaseToMergeInto, bool addAsMedia);
Task<FileOperationResult<byte[]>> ReleaseZipped(Library.Models.Users.User roadieUser, Guid id); Task<FileOperationResult<byte[]>> ReleaseZippedAsync(Library.Models.Users.User roadieUser, Guid id);
Task<OperationResult<bool>> ScanReleaseFolder(Library.Identity.User user, Guid releaseId, bool doJustInfo, Library.Data.Release releaseToScan = null); Task<OperationResult<bool>> ScanReleaseFolderAsync(Library.Identity.User user, Guid releaseId, bool doJustInfo, Library.Data.Release releaseToScan = null);
Task<OperationResult<Image>> SetReleaseImageByUrl(Library.Identity.User user, Guid id, string imageUrl); Task<OperationResult<Image>> SetReleaseImageByUrlAsync(Library.Identity.User user, Guid id, string imageUrl);
Task<OperationResult<bool>> UpdateRelease(Library.Identity.User user, Release release, string originalReleaseFolder = null); Task<OperationResult<bool>> UpdateReleaseAsync(Library.Identity.User user, Release release, string originalReleaseFolder = null);
Task<OperationResult<Image>> UploadReleaseImage(Library.Identity.User user, Guid id, IFormFile file); Task<OperationResult<Image>> UploadReleaseImageAsync(Library.Identity.User user, Guid id, IFormFile file);
} }
} }

View file

@ -7,17 +7,17 @@ namespace Roadie.Api.Services
{ {
public interface IStatisticsService public interface IStatisticsService
{ {
Task<OperationResult<LibraryStats>> LibraryStatistics();
Task<OperationResult<IEnumerable<DateAndCount>>> ArtistsByDate(); Task<OperationResult<IEnumerable<DateAndCount>>> ArtistsByDateAsync();
Task<OperationResult<LibraryStats>> LibraryStatisticsAsync();
Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDate(); Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDateAsync();
Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDecade(); Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDecadeAsync();
Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByDate(); Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByDateAsync();
Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByUser(); Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByUserAsync();
} }
} }

View file

@ -1,5 +1,4 @@
using Roadie.Library.Models; using Roadie.Library.Models.ThirdPartyApi.Subsonic;
using Roadie.Library.Models.ThirdPartyApi.Subsonic;
using System.Threading.Tasks; using System.Threading.Tasks;
using User = Roadie.Library.Models.Users.User; using User = Roadie.Library.Models.Users.User;
@ -7,85 +6,85 @@ namespace Roadie.Api.Services
{ {
public interface ISubsonicService public interface ISubsonicService
{ {
Task<SubsonicOperationResult<Response>> AddChatMessage(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> AddChatMessageAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<SubsonicAuthenticateResponse>> Authenticate(Request request); Task<SubsonicOperationResult<SubsonicAuthenticateResponse>> AuthenticateAsync(Request request);
Task<SubsonicOperationResult<Response>> CreateBookmark(Request request, User roadieUser, int position, string comment); Task<SubsonicOperationResult<Response>> CreateBookmarkAsync(Request request, User roadieUser, int position, string comment);
Task<SubsonicOperationResult<Response>> CreatePlaylist(Request request, User roadieUser, string name, string[] songIds, string playlistId = null); Task<SubsonicOperationResult<Response>> CreatePlaylistAsync(Request request, User roadieUser, string name, string[] songIds, string playlistId = null);
Task<SubsonicOperationResult<Response>> DeleteBookmark(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> DeleteBookmarkAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> DeletePlaylist(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> DeletePlaylistAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetAlbum(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetAlbumAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetAlbumInfo(Request request, User roadieUser, AlbumInfoVersion version); Task<SubsonicOperationResult<Response>> GetAlbumInfoAsync(Request request, User roadieUser, AlbumInfoVersion version);
Task<SubsonicOperationResult<Response>> GetAlbumList(Request request, User roadieUser, AlbumListVersions version); Task<SubsonicOperationResult<Response>> GetAlbumListAsync(Request request, User roadieUser, AlbumListVersions version);
Task<SubsonicOperationResult<Response>> GetArtist(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetArtistAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetArtistInfo(Request request, int? count, bool includeNotPresent, ArtistInfoVersion version); Task<SubsonicOperationResult<Response>> GetArtistInfoAsync(Request request, int? count, bool includeNotPresent, ArtistInfoVersion version);
Task<SubsonicOperationResult<Response>> GetArtists(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetArtistsAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetBookmarks(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetBookmarksAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetChatMessages(Request request, User roadieUser, long? since); Task<SubsonicOperationResult<Response>> GetChatMessagesAsync(Request request, User roadieUser, long? since);
Task<SubsonicFileOperationResult<Library.Models.Image>> GetCoverArt(Request request, int? size); Task<SubsonicFileOperationResult<Library.Models.Image>> GetCoverArtAsync(Request request, int? size);
Task<SubsonicOperationResult<Response>> GetGenres(Request request); Task<SubsonicOperationResult<Response>> GetGenresAsync(Request request);
Task<SubsonicOperationResult<Response>> GetIndexes(Request request, User roadieUser, long? ifModifiedSince = null); Task<SubsonicOperationResult<Response>> GetIndexesAsync(Request request, User roadieUser, long? ifModifiedSince = null);
SubsonicOperationResult<Response> GetLicense(Request request); SubsonicOperationResult<Response> GetLicense(Request request);
SubsonicOperationResult<Response> GetLyrics(Request request, string artistId, string title); SubsonicOperationResult<Response> GetLyrics(Request request, string artistId, string title);
Task<SubsonicOperationResult<Response>> GetMusicDirectory(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetMusicDirectoryAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetMusicFolders(Request request); Task<SubsonicOperationResult<Response>> GetMusicFoldersAsync(Request request);
Task<SubsonicOperationResult<Response>> GetNowPlaying(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetNowPlayingAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetPlaylist(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetPlaylistAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetPlaylists(Request request, User roadieUser, string filterToUserName); Task<SubsonicOperationResult<Response>> GetPlaylistsAsync(Request request, User roadieUser, string filterToUserName);
Task<SubsonicOperationResult<Response>> GetPlayQueue(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetPlayQueueAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetPodcasts(Request request); Task<SubsonicOperationResult<Response>> GetPodcastsAsync(Request request);
Task<SubsonicOperationResult<Response>> GetRandomSongs(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetRandomSongsAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetSimliarSongs(Request request, User roadieUser, SimilarSongsVersion version, int? count = 50); Task<SubsonicOperationResult<Response>> GetSimliarSongsAsync(Request request, User roadieUser, SimilarSongsVersion version, int? count = 50);
Task<SubsonicOperationResult<Response>> GetSong(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetSongAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetSongsByGenre(Request request, User roadieUser); Task<SubsonicOperationResult<Response>> GetSongsByGenreAsync(Request request, User roadieUser);
Task<SubsonicOperationResult<Response>> GetStarred(Request request, User roadieUser, StarredVersion version); Task<SubsonicOperationResult<Response>> GetStarredAsync(Request request, User roadieUser, StarredVersion version);
Task<SubsonicOperationResult<Response>> GetTopSongs(Request request, User roadieUser, int? count = 50); Task<SubsonicOperationResult<Response>> GetTopSongsAsync(Request request, User roadieUser, int? count = 50);
Task<SubsonicOperationResult<Response>> GetUser(Request request, string username); Task<SubsonicOperationResult<Response>> GetUserAsync(Request request, string username);
SubsonicOperationResult<Response> GetVideos(Request request); SubsonicOperationResult<Response> GetVideos(Request request);
SubsonicOperationResult<Response> Ping(Request request); SubsonicOperationResult<Response> Ping(Request request);
Task<SubsonicOperationResult<Response>> SavePlayQueue(Request request, User roadieUser, string current, long? position); Task<SubsonicOperationResult<Response>> SavePlayQueueAsync(Request request, User roadieUser, string current, long? position);
Task<SubsonicOperationResult<Response>> Search(Request request, User roadieUser, SearchVersion version); Task<SubsonicOperationResult<Response>> SearchAsync(Request request, User roadieUser, SearchVersion version);
Task<SubsonicOperationResult<Response>> SetRating(Request request, User roadieUser, short rating); Task<SubsonicOperationResult<Response>> SetRatingAsync(Request request, User roadieUser, short rating);
Task<SubsonicOperationResult<Response>> ToggleStar(Request request, User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null); Task<SubsonicOperationResult<Response>> ToggleStarAsync(Request request, User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null);
Task<SubsonicOperationResult<Response>> UpdatePlaylist(Request request, User roadieUser, string playlistId, Task<SubsonicOperationResult<Response>> UpdatePlaylistAsync(Request request, User roadieUser, string playlistId,
string name = null, string comment = null, bool? isPublic = null, string[] songIdsToAdd = null, string name = null, string comment = null, bool? isPublic = null, string[] songIdsToAdd = null,
int[] songIndexesToRemove = null); int[] songIndexesToRemove = null);
} }

View file

@ -6,6 +6,6 @@ namespace Roadie.Api.Services
{ {
public interface ITokenService public interface ITokenService
{ {
Task<string> GenerateToken(User user, UserManager<User> userManager); Task<string> GenerateTokenAsync(User user, UserManager<User> userManager);
} }
} }

View file

@ -10,16 +10,14 @@ namespace Roadie.Api.Services
{ {
public interface ITrackService public interface ITrackService
{ {
Task<OperationResult<Track>> ById(User roadieUser, Guid id, IEnumerable<string> includes); Task<OperationResult<Track>> ByIdAsyncAsync(User roadieUser, Guid id, IEnumerable<string> includes);
Task<PagedResult<TrackList>> List(PagedRequest request, User roadieUser, bool? doRandomize = false, Task<PagedResult<TrackList>> ListAsync(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null);
Guid? releaseId = null);
OperationResult<Track> StreamCheckAndInfo(User roadieUser, Guid id); OperationResult<Track> StreamCheckAndInfo(User roadieUser, Guid id);
Task<OperationResult<TrackStreamInfo>> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, Task<OperationResult<TrackStreamInfo>> TrackStreamInfoAsync(Guid trackId, long beginBytes, long endBytes, User roadieUser);
User roadieUser);
Task<OperationResult<bool>> UpdateTrack(User user, Track track); Task<OperationResult<bool>> UpdateTrackAsync(User user, Track track);
} }
} }

View file

@ -9,44 +9,44 @@ namespace Roadie.Api.Services
{ {
public interface IUserService public interface IUserService
{ {
Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes, bool isAccountSettingsEdit = false); Task<OperationResult<User>> ByIdAsync(User user, Guid id, IEnumerable<string> includes, bool isAccountSettingsEdit = false);
Task<PagedResult<UserList>> List(PagedRequest request); Task<OperationResult<bool>> DeleteAllBookmarksAsync(User roadieUser);
Task<OperationResult<bool>> DeleteAllBookmarks(User roadieUser); Task<PagedResult<UserList>> ListAsync(PagedRequest request);
Task<OperationResult<bool>> SetArtistBookmark(Guid artistId, User roadieUser, bool isBookmarked); Task<OperationResult<bool>> SetArtistBookmarkAsync(Guid artistId, User roadieUser, bool isBookmarked);
Task<OperationResult<bool>> SetArtistDisliked(Guid artistId, User roadieUser, bool isDisliked); Task<OperationResult<bool>> SetArtistDislikedAsync(Guid artistId, User roadieUser, bool isDisliked);
Task<OperationResult<bool>> SetArtistFavorite(Guid artistId, User roadieUser, bool isFavorite); Task<OperationResult<bool>> SetArtistFavoriteAsync(Guid artistId, User roadieUser, bool isFavorite);
Task<OperationResult<short>> SetArtistRating(Guid artistId, User roadieUser, short rating); Task<OperationResult<short>> SetArtistRatingAsync(Guid artistId, User roadieUser, short rating);
Task<OperationResult<bool>> SetCollectionBookmark(Guid collectionId, User roadieUser, bool isBookmarked); Task<OperationResult<bool>> SetCollectionBookmarkAsync(Guid collectionId, User roadieUser, bool isBookmarked);
Task<OperationResult<bool>> SetLabelBookmark(Guid labelId, User roadieUser, bool isBookmarked); Task<OperationResult<bool>> SetLabelBookmarkAsync(Guid labelId, User roadieUser, bool isBookmarked);
Task<OperationResult<bool>> SetPlaylistBookmark(Guid playlistId, User roadieUser, bool isBookmarked); Task<OperationResult<bool>> SetPlaylistBookmarkAsync(Guid playlistId, User roadieUser, bool isBookmarked);
Task<OperationResult<bool>> SetReleaseBookmark(Guid releaseid, User roadieUser, bool isBookmarked); Task<OperationResult<bool>> SetReleaseBookmarkAsync(Guid releaseid, User roadieUser, bool isBookmarked);
Task<OperationResult<bool>> SetReleaseDisliked(Guid releaseId, User roadieUser, bool isDisliked); Task<OperationResult<bool>> SetReleaseDislikedAsync(Guid releaseId, User roadieUser, bool isDisliked);
Task<OperationResult<bool>> SetReleaseFavorite(Guid releaseId, User roadieUser, bool isFavorite); Task<OperationResult<bool>> SetReleaseFavoriteAsync(Guid releaseId, User roadieUser, bool isFavorite);
Task<OperationResult<short>> SetReleaseRating(Guid releaseId, User roadieUser, short rating); Task<OperationResult<short>> SetReleaseRatingAsync(Guid releaseId, User roadieUser, short rating);
Task<OperationResult<bool>> SetTrackBookmark(Guid trackId, User roadieUser, bool isBookmarked); Task<OperationResult<bool>> SetTrackBookmarkAsync(Guid trackId, User roadieUser, bool isBookmarked);
Task<OperationResult<bool>> SetTrackDisliked(Guid trackId, User roadieUser, bool isDisliked); Task<OperationResult<bool>> SetTrackDislikedAsync(Guid trackId, User roadieUser, bool isDisliked);
Task<OperationResult<bool>> SetTrackFavorite(Guid releaseId, User roadieUser, bool isFavorite); Task<OperationResult<bool>> SetTrackFavoriteAsync(Guid releaseId, User roadieUser, bool isFavorite);
Task<OperationResult<short>> SetTrackRating(Guid trackId, User roadieUser, short rating); Task<OperationResult<short>> SetTrackRatingAsync(Guid trackId, User roadieUser, short rating);
Task<OperationResult<bool>> UpdateIntegrationGrant(Guid userId, string integrationName, string token); Task<OperationResult<bool>> UpdateIntegrationGrantAsync(Guid userId, string integrationName, string token);
Task<OperationResult<bool>> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel); Task<OperationResult<bool>> UpdateProfileAsync(User userPerformingUpdate, User userBeingUpdatedModel);
} }
} }

View file

@ -23,12 +23,20 @@ namespace Roadie.Api.Services
{ {
public class ImageService : ServiceBase, IImageService public class ImageService : ServiceBase, IImageService
{ {
public string Referrer { get; set; }
public string RequestIp { get; set; }
private IDefaultNotFoundImages DefaultNotFoundImages { get; } private IDefaultNotFoundImages DefaultNotFoundImages { get; }
private IImageSearchManager ImageSearchManager { get; } private IImageSearchManager ImageSearchManager { get; }
public string Referrer { get; set; }
public string RequestIp { get; set; }
public ImageService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager,
ILogger logger, DefaultNotFoundImages defaultNotFoundImages)
: base(configuration, null, dbContext, cacheManager, logger, null)
{
DefaultNotFoundImages = defaultNotFoundImages;
}
public ImageService(IRoadieSettings configuration, public ImageService(IRoadieSettings configuration,
IHttpEncoder httpEncoder, IHttpEncoder httpEncoder,
IHttpContext httpContext, IHttpContext httpContext,
@ -43,149 +51,6 @@ namespace Roadie.Api.Services
ImageSearchManager = imageSearchManager; ImageSearchManager = imageSearchManager;
} }
public ImageService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager,
ILogger logger, DefaultNotFoundImages defaultNotFoundImages)
: base(configuration, null, dbContext, cacheManager, logger, null)
{
DefaultNotFoundImages = defaultNotFoundImages;
}
public async Task<FileOperationResult<IImage>> ArtistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("ArtistImage",
data.Artist.CacheRegionUrn(id),
id,
width,
height,
async () => { return await ArtistImageAction(id, etag); },
etag);
}
public async Task<FileOperationResult<IImage>> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation($"ArtistSecondaryThumbnail.{imageId}",
data.Artist.CacheRegionUrn(id),
id,
width,
height,
async () => { return await ArtistSecondaryImageAction(id, imageId, etag); },
etag);
}
public async Task<FileOperationResult<IImage>> CollectionImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("CollectionThumbnail",
data.Collection.CacheRegionUrn(id),
id,
width,
height,
async () => { return await CollectionImageAction(id, etag); },
etag);
}
public async Task<FileOperationResult<IImage>> GenreImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("GenreThumbnail",
data.Genre.CacheRegionUrn(id),
id,
width,
height,
async () => await GenreImageAction(id, etag),
etag);
}
public async Task<FileOperationResult<IImage>> LabelImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("LabelThumbnail",
data.Label.CacheRegionUrn(id),
id,
width,
height,
async () => await LabelImageAction(id, etag),
etag);
}
public async Task<FileOperationResult<IImage>> PlaylistImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("PlaylistThumbnail",
data.Playlist.CacheRegionUrn(id),
id,
width,
height,
async () => await PlaylistImageAction(id, etag),
etag);
}
public async Task<FileOperationResult<IImage>> ReleaseImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("ReleaseThumbnail",
data.Release.CacheRegionUrn(id),
id,
width,
height,
async () => await ReleaseImageAction(id, etag),
etag);
}
public async Task<FileOperationResult<IImage>> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}",
data.Release.CacheRegionUrn(id),
id,
width,
height,
async () => { return await ReleaseSecondaryImageAction(id, imageId, etag); },
etag);
}
public async Task<OperationResult<IEnumerable<ImageSearchResult>>> Search(string query, int resultsCount = 10)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
IEnumerable<ImageSearchResult> searchResults = null;
try
{
searchResults = await ImageSearchManager.ImageSearch(query);
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<IEnumerable<ImageSearchResult>>
{
Data = searchResults,
IsSuccess = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<FileOperationResult<IImage>> TrackImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("TrackThumbnail",
data.Track.CacheRegionUrn(id),
id,
width,
height,
async () => await TrackImageAction(id, width, height, etag),
etag);
}
public async Task<FileOperationResult<IImage>> UserImage(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return await GetImageFileOperation("UserById",
User.CacheRegionUrn(id),
id,
width,
height,
async () => await UserImageAction(id, etag),
etag);
}
/// <summary> /// <summary>
/// Get image for an artist, see if the artist has an image in their folder and use that else use Artist.Thumbnail, is also not found use Artist DefaultNotFound image. /// Get image for an artist, see if the artist has an image in their folder and use that else use Artist.Thumbnail, is also not found use Artist DefaultNotFound image.
/// </summary> /// </summary>
@ -193,10 +58,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var artist = await GetArtist(id); var artist = await GetArtist(id).ConfigureAwait(false);
if (artist == null) if (artist == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Artist Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Artist Not Found [{id}]");
} }
byte[] imageBytes = null; byte[] imageBytes = null;
string artistFolder = null; string artistFolder = null;
@ -226,7 +91,7 @@ namespace Roadie.Api.Services
{ {
Bytes = imageBytes, Bytes = imageBytes,
}; };
if (imageBytes == null || !imageBytes.Any()) if (imageBytes?.Any() != true)
{ {
image = DefaultNotFoundImages.Artist; image = DefaultNotFoundImages.Artist;
} }
@ -244,10 +109,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var artist = await GetArtist(id); var artist = await GetArtist(id).ConfigureAwait(false);
if (artist == null) if (artist == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Release Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Release Not Found [{id}]");
} }
byte[] imageBytes = null; byte[] imageBytes = null;
string artistFolder = null; string artistFolder = null;
@ -265,9 +130,11 @@ namespace Roadie.Api.Services
.FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary) .FindImageTypeInDirectory(new DirectoryInfo(artistFolder), ImageType.ArtistSecondary)
.ToArray(); .ToArray();
if (artistSecondaryImages.Length >= imageId && artistSecondaryImages[imageId] != null) if (artistSecondaryImages.Length >= imageId && artistSecondaryImages[imageId] != null)
{
imageBytes = File.ReadAllBytes(artistSecondaryImages[imageId].FullName); imageBytes = File.ReadAllBytes(artistSecondaryImages[imageId].FullName);
} }
} }
}
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, $"Error Reading Artist Folder [{artistFolder}] For Artist `{artist}`"); Logger.LogError(ex, $"Error Reading Artist Folder [{artistFolder}] For Artist `{artist}`");
@ -292,10 +159,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var collection = await GetCollection(id); var collection = await GetCollection(id).ConfigureAwait(false);
if (collection == null) if (collection == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Collection Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Collection Not Found [{id}]");
} }
IImage image = new Library.Imaging.Image(id) IImage image = new Library.Imaging.Image(id)
{ {
@ -313,7 +180,7 @@ namespace Roadie.Api.Services
{ {
Logger.LogError(ex, $"Error Reading Image File [{collectionImageFilename}]"); Logger.LogError(ex, $"Error Reading Image File [{collectionImageFilename}]");
} }
if (image.Bytes == null || !image.Bytes.Any()) if (image.Bytes?.Any() != true)
{ {
image = DefaultNotFoundImages.Collection; image = DefaultNotFoundImages.Collection;
} }
@ -336,7 +203,7 @@ namespace Roadie.Api.Services
} }
if (image?.Bytes?.Any() != true) if (image?.Bytes?.Any() != true)
{ {
return new FileOperationResult<IImage>(string.Format("ImageById Not Set [{0}]", id)); return new FileOperationResult<IImage>($"ImageById Not Set [{id}]");
} }
return new FileOperationResult<IImage>(image?.Bytes?.Any() ?? false return new FileOperationResult<IImage>(image?.Bytes?.Any() ?? false
? OperationMessages.OkMessage ? OperationMessages.OkMessage
@ -354,10 +221,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var genre = await GetGenre(id); var genre = await GetGenre(id).ConfigureAwait(false);
if (genre == null) if (genre == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Genre Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Genre Not Found [{id}]");
} }
IImage image = new Library.Imaging.Image(id) IImage image = new Library.Imaging.Image(id)
{ {
@ -375,7 +242,7 @@ namespace Roadie.Api.Services
{ {
Logger.LogError(ex, $"Error Reading Image File [{genreImageFilename}]"); Logger.LogError(ex, $"Error Reading Image File [{genreImageFilename}]");
} }
if (image.Bytes == null || !image.Bytes.Any()) if (image.Bytes?.Any() != true)
{ {
image = DefaultNotFoundImages.Genre; image = DefaultNotFoundImages.Genre;
} }
@ -395,7 +262,7 @@ namespace Roadie.Api.Services
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var sizeHash = (width ?? 0) + (height ?? 0); var sizeHash = (width ?? 0) + (height ?? 0);
var result = await CacheManager.GetAsync($"urn:{type}_by_id_operation:{id}:{sizeHash}", action, regionUrn); var result = await CacheManager.GetAsync($"urn:{type}_by_id_operation:{id}:{sizeHash}", action, regionUrn).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
return new FileOperationResult<IImage>(result.IsNotFoundResult, result.Messages); return new FileOperationResult<IImage>(result.IsNotFoundResult, result.Messages);
@ -450,10 +317,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var label = await GetLabel(id); var label = await GetLabel(id).ConfigureAwait(false);
if (label == null) if (label == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Label Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Label Not Found [{id}]");
} }
IImage image = new Library.Imaging.Image(id) IImage image = new Library.Imaging.Image(id)
{ {
@ -471,7 +338,7 @@ namespace Roadie.Api.Services
{ {
Logger.LogError(ex, $"Error Reading Image File [{labelImageFilename}]"); Logger.LogError(ex, $"Error Reading Image File [{labelImageFilename}]");
} }
if (image.Bytes == null || !image.Bytes.Any()) if (image.Bytes?.Any() != true)
{ {
image = DefaultNotFoundImages.Label; image = DefaultNotFoundImages.Label;
} }
@ -489,10 +356,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var playlist = await GetPlaylist(id); var playlist = await GetPlaylist(id).ConfigureAwait(false);
if (playlist == null) if (playlist == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Playlist Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Playlist Not Found [{id}]");
} }
IImage image = new Library.Imaging.Image(id) IImage image = new Library.Imaging.Image(id)
{ {
@ -510,7 +377,7 @@ namespace Roadie.Api.Services
{ {
Logger.LogError(ex, $"Error Reading Image File [{playlistImageFilename}]"); Logger.LogError(ex, $"Error Reading Image File [{playlistImageFilename}]");
} }
if (image.Bytes == null || !image.Bytes.Any()) if (image.Bytes?.Any() != true)
{ {
image = DefaultNotFoundImages.Playlist; image = DefaultNotFoundImages.Playlist;
} }
@ -531,10 +398,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var release = await GetRelease(id); var release = await GetRelease(id).ConfigureAwait(false);
if (release == null) if (release == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Release Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Release Not Found [{id}]");
} }
byte[] imageBytes = null; byte[] imageBytes = null;
string artistFolder = null; string artistFolder = null;
@ -591,10 +458,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var release = await GetRelease(id); var release = await GetRelease(id).ConfigureAwait(false);
if (release == null) if (release == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Release Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Release Not Found [{id}]");
} }
byte[] imageBytes = null; byte[] imageBytes = null;
string artistFolder = null; string artistFolder = null;
@ -620,10 +487,12 @@ namespace Roadie.Api.Services
.FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary) .FindImageTypeInDirectory(new DirectoryInfo(releaseFolder), ImageType.ReleaseSecondary)
.ToArray(); .ToArray();
if (releaseSecondaryImages.Length >= imageId && releaseSecondaryImages[imageId] != null) if (releaseSecondaryImages.Length >= imageId && releaseSecondaryImages[imageId] != null)
{
imageBytes = File.ReadAllBytes(releaseSecondaryImages[imageId].FullName); imageBytes = File.ReadAllBytes(releaseSecondaryImages[imageId].FullName);
} }
} }
} }
}
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, Logger.LogError(ex,
@ -649,10 +518,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var track = await GetTrack(id); var track = await GetTrack(id).ConfigureAwait(false);
if (track == null) if (track == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("Track Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"Track Not Found [{id}]");
} }
IImage image = new Library.Imaging.Image(id) IImage image = new Library.Imaging.Image(id)
{ {
@ -663,10 +532,10 @@ namespace Roadie.Api.Services
{ {
image.Bytes = File.ReadAllBytes(trackImageFileName); image.Bytes = File.ReadAllBytes(trackImageFileName);
} }
if (image.Bytes == null || !image.Bytes.Any()) if (image.Bytes?.Any() != true)
{ {
// If no track image is found then return image for release // If no track image is found then return image for release
return await ReleaseImage(track.ReleaseMedia.Release.RoadieId, width, height, etag); return await ReleaseImageAsync(track.ReleaseMedia.Release.RoadieId, width, height, etag).ConfigureAwait(false);
} }
return GenerateFileOperationResult(id, image, etag); return GenerateFileOperationResult(id, image, etag);
} }
@ -682,10 +551,10 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var user = await GetUser(id); var user = await GetUser(id).ConfigureAwait(false);
if (user == null) if (user == null)
{ {
return new FileOperationResult<IImage>(true, string.Format("User Not Found [{0}]", id)); return new FileOperationResult<IImage>(true, $"User Not Found [{id}]");
} }
IImage image = new Library.Imaging.Image(id) IImage image = new Library.Imaging.Image(id)
{ {
@ -703,7 +572,7 @@ namespace Roadie.Api.Services
{ {
Logger.LogError(ex, $"Error Reading Image File [{userImageFilename}]"); Logger.LogError(ex, $"Error Reading Image File [{userImageFilename}]");
} }
if (image.Bytes == null || !image.Bytes.Any()) if (image.Bytes?.Any() != true)
{ {
image = DefaultNotFoundImages.User; image = DefaultNotFoundImages.User;
} }
@ -716,5 +585,141 @@ namespace Roadie.Api.Services
return new FileOperationResult<IImage>(OperationMessages.ErrorOccured); return new FileOperationResult<IImage>(OperationMessages.ErrorOccured);
} }
public Task<FileOperationResult<IImage>> ArtistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation(nameof(ArtistImageAsync),
data.Artist.CacheRegionUrn(id),
id,
width,
height,
async () => { return await ArtistImageAction(id, etag).ConfigureAwait(false); },
etag);
}
public Task<FileOperationResult<IImage>> ArtistSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation($"ArtistSecondaryThumbnail.{imageId}",
data.Artist.CacheRegionUrn(id),
id,
width,
height,
async () => { return await ArtistSecondaryImageAction(id, imageId, etag).ConfigureAwait(false); },
etag);
}
public Task<FileOperationResult<IImage>> CollectionImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation("CollectionThumbnail",
data.Collection.CacheRegionUrn(id),
id,
width,
height,
async () => { return await CollectionImageAction(id, etag).ConfigureAwait(false); },
etag);
}
public Task<FileOperationResult<IImage>> GenreImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation("GenreThumbnail",
data.Genre.CacheRegionUrn(id),
id,
width,
height,
async () => await GenreImageAction(id, etag).ConfigureAwait(false),
etag);
}
public Task<FileOperationResult<IImage>> LabelImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation("LabelThumbnail",
data.Label.CacheRegionUrn(id),
id,
width,
height,
async () => await LabelImageAction(id, etag).ConfigureAwait(false),
etag);
}
public Task<FileOperationResult<IImage>> PlaylistImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation("PlaylistThumbnail",
data.Playlist.CacheRegionUrn(id),
id,
width,
height,
async () => await PlaylistImageAction(id, etag).ConfigureAwait(false),
etag);
}
public Task<FileOperationResult<IImage>> ReleaseImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation("ReleaseThumbnail",
data.Release.CacheRegionUrn(id),
id,
width,
height,
async () => await ReleaseImageAction(id, etag).ConfigureAwait(false),
etag);
}
public Task<FileOperationResult<IImage>> ReleaseSecondaryImageAsync(Guid id, int imageId, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation($"ReleaseSecondaryThumbnail-{imageId}",
data.Release.CacheRegionUrn(id),
id,
width,
height,
async () => { return await ReleaseSecondaryImageAction(id, imageId, etag).ConfigureAwait(false); },
etag);
}
public async Task<OperationResult<IEnumerable<ImageSearchResult>>> SearchAsync(string query, int resultsCount = 10)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
IEnumerable<ImageSearchResult> searchResults = null;
try
{
searchResults = await ImageSearchManager.ImageSearch(query).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<IEnumerable<ImageSearchResult>>
{
Data = searchResults,
IsSuccess = errors.Count == 0,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public Task<FileOperationResult<IImage>> TrackImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation("TrackThumbnail",
data.Track.CacheRegionUrn(id),
id,
width,
height,
async () => await TrackImageAction(id, width, height, etag).ConfigureAwait(false),
etag);
}
public Task<FileOperationResult<IImage>> UserImageAsync(Guid id, int? width, int? height, EntityTagHeaderValue etag = null)
{
return GetImageFileOperation("UserById",
User.CacheRegionUrn(id),
id,
width,
height,
async () => await UserImageAction(id, etag).ConfigureAwait(false),
etag);
}
} }
} }

View file

@ -9,7 +9,6 @@ using Roadie.Library.Data.Context;
using Roadie.Library.Encoding; using Roadie.Library.Encoding;
using Roadie.Library.Enums; using Roadie.Library.Enums;
using Roadie.Library.Extensions; using Roadie.Library.Extensions;
using Roadie.Library.Identity;
using Roadie.Library.Imaging; using Roadie.Library.Imaging;
using Roadie.Library.Models; using Roadie.Library.Models;
using Roadie.Library.Models.Pagination; using Roadie.Library.Models.Pagination;
@ -43,318 +42,6 @@ namespace Roadie.Api.Services
BookmarkService = bookmarkService; BookmarkService = bookmarkService;
} }
public async Task<OperationResult<Label>> ById(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = string.Format("urn:label_by_id_operation:{0}:{1}", id,
includes == null ? "0" : string.Join("|", includes));
var result = await CacheManager.GetAsync(cacheKey,
async () => { return await LabelByIdAction(id, includes); }, data.Label.CacheRegionUrn(id));
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Label);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments.Any())
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr).ToArray();
foreach (var comment in result.Data.Comments)
{
var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
}
}
}
return new OperationResult<Label>(result.Messages)
{
Data = result?.Data,
IsNotFoundResult = result?.IsNotFoundResult ?? false,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<bool>> Delete(Library.Identity.User user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
if (label == null) return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", id));
DbContext.Labels.Remove(label);
await DbContext.SaveChangesAsync();
var labelImageFilename = label.PathToImage(Configuration);
if (File.Exists(labelImageFilename))
{
File.Delete(labelImageFilename);
}
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Label, label.Id);
Logger.LogWarning("User `{0}` deleted Label `{1}]`", user, label);
CacheManager.ClearRegion(label.CacheRegion);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = true,
Data = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<Library.Models.Pagination.PagedResult<LabelList>> List(Library.Models.Users.User roadieUser, PagedRequest request,
bool? doRandomize = false)
{
var sw = new Stopwatch();
sw.Start();
int? rowCount = null;
if (!string.IsNullOrEmpty(request.Sort))
{
request.Sort = request.Sort.Replace("createdDate", "createdDateTime");
request.Sort = request.Sort.Replace("lastUpdated", "lastUpdatedDateTime");
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
? request.FilterValue.ToAlphanumericName()
: null;
int[] randomLabelIds = null;
SortedDictionary<int, int> randomLabelData = null;
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomLabelData = await DbContext.RandomLabelIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomLabelIds = randomLabelData.Select(x => x.Value).ToArray();
rowCount = DbContext.Labels.Count();
}
var result = from l in DbContext.Labels
where randomLabelIds == null || randomLabelIds.Contains(l.Id)
where string.IsNullOrEmpty(normalizedFilterValue) || (
l.Name.ToLower().Contains(normalizedFilterValue) ||
l.SortName.ToLower().Contains(normalizedFilterValue) ||
l.AlternateNames.ToLower().Contains(normalizedFilterValue)
)
select new LabelList
{
DatabaseId = l.Id,
Id = l.RoadieId,
Label = new DataToken
{
Text = l.Name,
Value = l.RoadieId.ToString()
},
SortName = l.SortName,
CreatedDate = l.CreatedDate,
LastUpdated = l.LastUpdated,
ArtistCount = l.ArtistCount,
ReleaseCount = l.ReleaseCount,
TrackCount = l.TrackCount,
Thumbnail = ImageHelper.MakeLabelThumbnailImage(Configuration, HttpContext, l.RoadieId)
};
LabelList[] rows = null;
rowCount = rowCount ?? result.Count();
if (doRandomize ?? false)
{
var resultData = result.ToArray();
rows = (from r in resultData
join ra in randomLabelData on r.DatabaseId equals ra.Value
orderby ra.Key
select r
).ToArray();
}
else
{
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "SortName", "ASC" }, { "Label.Text", "ASC" } })
: request.OrderValue();
rows = result
.OrderBy(sortBy)
.Skip(request.SkipValue)
.Take(request.LimitValue)
.ToArray();
}
sw.Stop();
return new Library.Models.Pagination.PagedResult<LabelList>
{
TotalCount = rowCount.Value,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
};
}
public async Task<OperationResult<bool>> MergeLabelsIntoLabel(Library.Identity.User user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == intoLabelId);
if (label == null)
{
return new OperationResult<bool>(true, string.Format("Merge Into Label Not Found [{0}]", intoLabelId));
}
var now = DateTime.UtcNow;
var labelsToMerge = (from l in DbContext.Labels
join ltm in labelIdsToMerge on l.RoadieId equals ltm
select l);
foreach (var labelToMerge in labelsToMerge)
{
label.MusicBrainzId = label.MusicBrainzId ?? labelToMerge.MusicBrainzId;
label.SortName = label.SortName ?? labelToMerge.SortName;
label.Profile = label.Profile ?? labelToMerge.Profile;
label.BeginDate = label.BeginDate ?? labelToMerge.BeginDate;
label.EndDate = label.EndDate ?? labelToMerge.EndDate;
label.Profile = label.Profile ?? labelToMerge.Profile;
label.DiscogsId = label.DiscogsId ?? labelToMerge.DiscogsId;
label.ImageUrl = label.ImageUrl ?? labelToMerge.ImageUrl;
label.Tags = label.Tags.AddToDelimitedList(labelToMerge.Tags.ToListFromDelimited());
var altNames = labelToMerge.AlternateNames.ToListFromDelimited().ToList();
altNames.Add(labelToMerge.Name);
altNames.Add(labelToMerge.SortName);
altNames.Add(labelToMerge.Name.ToAlphanumericName());
label.AlternateNames = label.AlternateNames.AddToDelimitedList(altNames);
label.URLs = label.URLs.AddToDelimitedList(labelToMerge.URLs.ToListFromDelimited());
var labelToMergeReleases = (from rl in DbContext.ReleaseLabels
where rl.LabelId == labelToMerge.Id
select rl);
foreach (var labelToMergeRelease in labelToMergeReleases)
{
labelToMergeRelease.LabelId = label.Id;
labelToMergeRelease.LastUpdated = now;
}
label.LastUpdated = now;
await DbContext.SaveChangesAsync();
}
await UpdateLabelCounts(label.Id, now);
CacheManager.ClearRegion(label.CacheRegion);
Logger.LogInformation($"MergeLabelsIntoLabel `{label}`, Merged Label Ids [{ string.Join(",", labelIdsToMerge) }] By User `{user}`");
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<Library.Models.Image>> SetLabelImageByUrl(Library.Models.Users.User user, Guid id, string imageUrl)
{
return await SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
}
public async Task<OperationResult<bool>> UpdateLabel(Library.Models.Users.User user, Label model)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == model.Id);
if (label == null)
{
return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", model.Id));
}
// If label is being renamed, see if label already exists with new model supplied name
var labelName = label.SortNameValue;
var labelModelName = model.SortNameValue;
var oldPathToImage = label.PathToImage(Configuration);
var didChangeName = !labelName.ToAlphanumericName().Equals(labelModelName.ToAlphanumericName(), StringComparison.OrdinalIgnoreCase);
if (didChangeName)
{
var existingLabel = DbContext.Labels.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName );
if (existingLabel != null)
{
return new OperationResult<bool>($"Label already exists `{ existingLabel }` with name [{ labelModelName }].");
}
}
try
{
var now = DateTime.UtcNow;
var specialLabelName = model.Name.ToAlphanumericName();
var alt = new List<string>(model.AlternateNamesList);
if (!model.AlternateNamesList.Contains(specialLabelName, StringComparer.OrdinalIgnoreCase))
{
alt.Add(specialLabelName);
}
label.AlternateNames = alt.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();
if (didChangeName)
{
if (File.Exists(oldPathToImage))
{
File.Move(oldPathToImage, label.PathToImage(Configuration));
}
}
var labelImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (labelImage != null)
{
// Save unaltered label image
File.WriteAllBytes(label.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(labelImage));
}
label.LastUpdated = now;
await DbContext.SaveChangesAsync();
CacheManager.ClearRegion(label.CacheRegion);
Logger.LogInformation($"UpdateLabel `{label}` By User `{user}`");
}
catch (Exception ex)
{
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>> UploadLabelImage(Library.Models.Users.User user, Guid id, IFormFile file)
{
var bytes = new byte[0];
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
bytes = ms.ToArray();
}
return await SaveImageBytes(user, id, bytes);
}
private async Task<OperationResult<Label>> LabelByIdAction(Guid id, IEnumerable<string> includes = null) private async Task<OperationResult<Label>> LabelByIdAction(Guid id, IEnumerable<string> includes = null)
{ {
var timings = new Dictionary<string, long>(); var timings = new Dictionary<string, long>();
@ -364,12 +51,12 @@ namespace Roadie.Api.Services
sw.Start(); sw.Start();
tsw.Restart(); tsw.Restart();
var label = await GetLabel(id); var label = await GetLabel(id).ConfigureAwait(false);
tsw.Stop(); tsw.Stop();
timings.Add("GetLabel", tsw.ElapsedMilliseconds); timings.Add(nameof(GetLabel), tsw.ElapsedMilliseconds);
if (label == null) if (label == null)
{ {
return new OperationResult<Label>(true, string.Format("Label Not Found [{0}]", id)); return new OperationResult<Label>(true, $"Label Not Found [{id}]");
} }
tsw.Restart(); tsw.Restart();
var result = label.Adapt<Label>(); var result = label.Adapt<Label>();
@ -456,8 +143,12 @@ namespace Roadie.Api.Services
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var errors = new List<Exception>(); var errors = new List<Exception>();
var label = DbContext.Labels.FirstOrDefault(x => x.RoadieId == id); var label = await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
if (label == null) return new OperationResult<Library.Models.Image>(true, string.Format("Label Not Found [{0}]", id)); if (label == null)
{
return new OperationResult<Library.Models.Image>(true, $"Label Not Found [{id}]");
}
try try
{ {
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
@ -467,7 +158,7 @@ namespace Roadie.Api.Services
File.WriteAllBytes(label.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(imageBytes)); File.WriteAllBytes(label.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(imageBytes));
} }
label.LastUpdated = now; label.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(label.CacheRegion); CacheManager.ClearRegion(label.CacheRegion);
Logger.LogInformation($"UploadLabelImage `{label}` By User `{user}`"); Logger.LogInformation($"UploadLabelImage `{label}` By User `{user}`");
} }
@ -481,11 +172,324 @@ namespace Roadie.Api.Services
return new OperationResult<Library.Models.Image> return new OperationResult<Library.Models.Image>
{ {
IsSuccess = !errors.Any(), IsSuccess = errors.Count == 0,
Data = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "label", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true), Data = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "label", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height, true),
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Errors = errors Errors = errors
}; };
} }
public async Task<OperationResult<Label>> ByIdAsync(Library.Models.Users.User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = $"urn:label_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
var result = await CacheManager.GetAsync(cacheKey,
async () => await LabelByIdAction(id, includes).ConfigureAwait(false), data.Label.CacheRegionUrn(id)
).ConfigureAwait(false);
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Label).ConfigureAwait(false);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments.Any())
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr).ToArray();
foreach (var comment in result.Data.Comments)
{
var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
}
}
}
return new OperationResult<Label>(result.Messages)
{
Data = result?.Data,
IsNotFoundResult = result?.IsNotFoundResult ?? false,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<bool>> DeleteAsync(Library.Identity.User user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var label = await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
if (label == null)
{
return new OperationResult<bool>(true, $"Label Not Found [{id}]");
}
DbContext.Labels.Remove(label);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
var labelImageFilename = label.PathToImage(Configuration);
if (File.Exists(labelImageFilename))
{
File.Delete(labelImageFilename);
}
await BookmarkService.RemoveAllBookmarksForItemAsync(BookmarkType.Label, label.Id).ConfigureAwait(false);
Logger.LogWarning("User `{0}` deleted Label `{1}]`", user, label);
CacheManager.ClearRegion(label.CacheRegion);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = true,
Data = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<Library.Models.Pagination.PagedResult<LabelList>> ListAsync(Library.Models.Users.User roadieUser, PagedRequest request,
bool? doRandomize = false)
{
var sw = new Stopwatch();
sw.Start();
int? rowCount = null;
if (!string.IsNullOrEmpty(request.Sort))
{
request.Sort = request.Sort.Replace("createdDate", "createdDateTime");
request.Sort = request.Sort.Replace("lastUpdated", "lastUpdatedDateTime");
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
? request.FilterValue.ToAlphanumericName()
: null;
int[] randomLabelIds = null;
SortedDictionary<int, int> randomLabelData = null;
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomLabelData = await DbContext.RandomLabelIdsAsync(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly).ConfigureAwait(false);
randomLabelIds = randomLabelData.Select(x => x.Value).ToArray();
rowCount = DbContext.Labels.Count();
}
var result = from l in DbContext.Labels
where randomLabelIds == null || randomLabelIds.Contains(l.Id)
where string.IsNullOrEmpty(normalizedFilterValue) || (
l.Name.ToLower().Contains(normalizedFilterValue) ||
l.SortName.ToLower().Contains(normalizedFilterValue) ||
l.AlternateNames.ToLower().Contains(normalizedFilterValue)
)
select new LabelList
{
DatabaseId = l.Id,
Id = l.RoadieId,
Label = new DataToken
{
Text = l.Name,
Value = l.RoadieId.ToString()
},
SortName = l.SortName,
CreatedDate = l.CreatedDate,
LastUpdated = l.LastUpdated,
ArtistCount = l.ArtistCount,
ReleaseCount = l.ReleaseCount,
TrackCount = l.TrackCount,
Thumbnail = ImageHelper.MakeLabelThumbnailImage(Configuration, HttpContext, l.RoadieId)
};
LabelList[] rows = null;
rowCount = rowCount ?? result.Count();
if (doRandomize ?? false)
{
var resultData = await result.ToArrayAsync().ConfigureAwait(false);
rows = (from r in resultData
join ra in randomLabelData on r.DatabaseId equals ra.Value
orderby ra.Key
select r
)
.ToArray();
}
else
{
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "SortName", "ASC" }, { "Label.Text", "ASC" } })
: request.OrderValue();
rows = await result
.OrderBy(sortBy)
.Skip(request.SkipValue)
.Take(request.LimitValue)
.ToArrayAsync()
.ConfigureAwait(false);
}
sw.Stop();
return new Library.Models.Pagination.PagedResult<LabelList>
{
TotalCount = rowCount.Value,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
};
}
public async Task<OperationResult<bool>> MergeLabelsIntoLabelAsync(Library.Identity.User user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var label = await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == intoLabelId).ConfigureAwait(false);
if (label == null)
{
return new OperationResult<bool>(true, $"Merge Into Label Not Found [{intoLabelId}]");
}
var now = DateTime.UtcNow;
var labelsToMerge = (from l in DbContext.Labels
join ltm in labelIdsToMerge on l.RoadieId equals ltm
select l);
foreach (var labelToMerge in labelsToMerge)
{
label.MusicBrainzId = label.MusicBrainzId ?? labelToMerge.MusicBrainzId;
label.SortName = label.SortName ?? labelToMerge.SortName;
label.Profile = label.Profile ?? labelToMerge.Profile;
label.BeginDate = label.BeginDate ?? labelToMerge.BeginDate;
label.EndDate = label.EndDate ?? labelToMerge.EndDate;
label.Profile = label.Profile ?? labelToMerge.Profile;
label.DiscogsId = label.DiscogsId ?? labelToMerge.DiscogsId;
label.ImageUrl = label.ImageUrl ?? labelToMerge.ImageUrl;
label.Tags = label.Tags.AddToDelimitedList(labelToMerge.Tags.ToListFromDelimited());
var altNames = labelToMerge.AlternateNames.ToListFromDelimited().ToList();
altNames.Add(labelToMerge.Name);
altNames.Add(labelToMerge.SortName);
altNames.Add(labelToMerge.Name.ToAlphanumericName());
label.AlternateNames = label.AlternateNames.AddToDelimitedList(altNames);
label.URLs = label.URLs.AddToDelimitedList(labelToMerge.URLs.ToListFromDelimited());
var labelToMergeReleases = (from rl in DbContext.ReleaseLabels
where rl.LabelId == labelToMerge.Id
select rl);
foreach (var labelToMergeRelease in labelToMergeReleases)
{
labelToMergeRelease.LabelId = label.Id;
labelToMergeRelease.LastUpdated = now;
}
label.LastUpdated = now;
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
await UpdateLabelCounts(label.Id, now).ConfigureAwait(false);
CacheManager.ClearRegion(label.CacheRegion);
Logger.LogInformation($"MergeLabelsIntoLabel `{label}`, Merged Label Ids [{ string.Join(",", labelIdsToMerge) }] By User `{user}`");
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public Task<OperationResult<Library.Models.Image>> SetLabelImageByUrlAsync(Library.Models.Users.User user, Guid id, string imageUrl) => SaveImageBytes(user, id, WebHelper.BytesForImageUrl(imageUrl));
public async Task<OperationResult<bool>> UpdateLabelAsync(Library.Models.Users.User user, Label model)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var label = await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false);
if (label == null)
{
return new OperationResult<bool>(true, $"Label Not Found [{model.Id}]");
}
// If label is being renamed, see if label already exists with new model supplied name
var labelName = label.SortNameValue;
var labelModelName = model.SortNameValue;
var oldPathToImage = label.PathToImage(Configuration);
var didChangeName = !labelName.ToAlphanumericName().Equals(labelModelName.ToAlphanumericName(), StringComparison.OrdinalIgnoreCase);
if (didChangeName)
{
var existingLabel = DbContext.Labels.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName);
if (existingLabel != null)
{
return new OperationResult<bool>($"Label already exists `{ existingLabel }` with name [{ labelModelName }].");
}
}
try
{
var now = DateTime.UtcNow;
var specialLabelName = model.Name.ToAlphanumericName();
var alt = new List<string>(model.AlternateNamesList);
if (!model.AlternateNamesList.Contains(specialLabelName, StringComparer.OrdinalIgnoreCase))
{
alt.Add(specialLabelName);
}
label.AlternateNames = alt.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();
if (didChangeName)
{
if (File.Exists(oldPathToImage))
{
File.Move(oldPathToImage, label.PathToImage(Configuration));
}
}
var labelImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (labelImage != null)
{
// Save unaltered label image
File.WriteAllBytes(label.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(labelImage));
}
label.LastUpdated = now;
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(label.CacheRegion);
Logger.LogInformation($"UpdateLabel `{label}` By User `{user}`");
}
catch (Exception ex)
{
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>> UploadLabelImageAsync(Library.Models.Users.User user, Guid id, IFormFile file)
{
var bytes = new byte[0];
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
bytes = ms.ToArray();
}
return await SaveImageBytes(user, id, bytes).ConfigureAwait(false);
}
} }
} }

View file

@ -34,7 +34,7 @@ namespace Roadie.Api.Services
{ {
} }
public Task<OperationResult<IEnumerable<DataToken>>> ArtistTypes() public Task<OperationResult<IEnumerable<DataToken>>> ArtistTypesAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -45,7 +45,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DataToken>>> BandStatus() public Task<OperationResult<IEnumerable<DataToken>>> BandStatusAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -56,7 +56,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DataToken>>> BookmarkTypes() public Task<OperationResult<IEnumerable<DataToken>>> BookmarkTypesAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -67,7 +67,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DataToken>>> CollectionTypes() public Task<OperationResult<IEnumerable<DataToken>>> CollectionTypesAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -78,7 +78,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DataToken>>> LibraryStatus() public Task<OperationResult<IEnumerable<DataToken>>> LibraryStatusAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -89,7 +89,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DataToken>>> QueMessageTypes() public Task<OperationResult<IEnumerable<DataToken>>> QueMessageTypesAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -100,7 +100,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DataToken>>> ReleaseTypes() public Task<OperationResult<IEnumerable<DataToken>>> ReleaseTypesAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -111,7 +111,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DataToken>>> RequestStatus() public Task<OperationResult<IEnumerable<DataToken>>> RequestStatusAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>
@ -122,17 +122,17 @@ namespace Roadie.Api.Services
}); });
} }
public async Task<OperationResult<IEnumerable<DataToken>>> CreditCategories() public async Task<OperationResult<IEnumerable<DataToken>>> CreditCategoriesAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var data = await CacheManager.GetAsync(CreditCategoriesCacheKey, async () => var data = await CacheManager.GetAsync(CreditCategoriesCacheKey, async () =>
{ {
return (await DbContext.CreditCategory.ToListAsync()).Select(x => new DataToken return (await DbContext.CreditCategory.ToListAsync().ConfigureAwait(false)).Select(x => new DataToken
{ {
Value = x.RoadieId.ToString(), Value = x.RoadieId.ToString(),
Text = x.Name Text = x.Name
}).ToArray(); }).ToArray();
}, CacheManagerBase.SystemCacheRegionUrn); }, CacheManagerBase.SystemCacheRegionUrn).ConfigureAwait(false);
return new OperationResult<IEnumerable<DataToken>> return new OperationResult<IEnumerable<DataToken>>
{ {
Data = data, Data = data,
@ -141,7 +141,7 @@ namespace Roadie.Api.Services
}; };
} }
public Task<OperationResult<IEnumerable<DataToken>>> Status() public Task<OperationResult<IEnumerable<DataToken>>> StatusAsync()
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
return Task.FromResult(new OperationResult<IEnumerable<DataToken>> return Task.FromResult(new OperationResult<IEnumerable<DataToken>>

View file

@ -19,7 +19,6 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core;
using System.Threading.Tasks; using System.Threading.Tasks;
using data = Roadie.Library.Data;
namespace Roadie.Api.Services namespace Roadie.Api.Services
{ {
@ -29,15 +28,6 @@ namespace Roadie.Api.Services
protected IScrobbleHandler ScrobblerHandler { get; } protected IScrobbleHandler ScrobblerHandler { get; }
public PlayActivityService(IRoadieSettings configuration, IHttpEncoder httpEncoder,IHttpContext httpContext,
IRoadieDbContext dbContext, ICacheManager cacheManager,ILogger<PlayActivityService> logger,
IScrobbleHandler scrobbleHandler, IHubContext<PlayActivityHub> playActivityHub)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
{
PlayActivityHub = playActivityHub;
ScrobblerHandler = scrobbleHandler;
}
public PlayActivityService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager, public PlayActivityService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager,
ILogger logger, ScrobbleHandler scrobbleHandler) ILogger logger, ScrobbleHandler scrobbleHandler)
: base(configuration, null, dbContext, cacheManager, logger, null) : base(configuration, null, dbContext, cacheManager, logger, null)
@ -45,7 +35,93 @@ namespace Roadie.Api.Services
ScrobblerHandler = scrobbleHandler; ScrobblerHandler = scrobbleHandler;
} }
public Task<Library.Models.Pagination.PagedResult<PlayActivityList>> List(PagedRequest request,User roadieUser = null, DateTime? newerThan = null) public PlayActivityService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext,
IRoadieDbContext dbContext, ICacheManager cacheManager, ILogger<PlayActivityService> logger,
IScrobbleHandler scrobbleHandler, IHubContext<PlayActivityHub> playActivityHub)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
{
PlayActivityHub = playActivityHub;
ScrobblerHandler = scrobbleHandler;
}
private async Task PublishPlayActivity(User roadieUser, ScrobbleInfo scrobble, bool isNowPlaying)
{
// Only broadcast if the user is not public and played duration is more than half of duration
if (roadieUser?.IsPrivate != true &&
scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds > scrobble.TrackDuration.TotalSeconds / 2)
{
var sw = Stopwatch.StartNew();
var track = await DbContext.Tracks
.Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist)
.FirstOrDefaultAsync(x => x.RoadieId == scrobble.TrackId)
.ConfigureAwait(false);
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId);
var userTrack =
DbContext.UserTracks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.TrackId == track.Id);
var pl = new PlayActivityList
{
Artist = new DataToken
{
Text = track.ReleaseMedia.Release.Artist.Name,
Value = track.ReleaseMedia.Release.Artist.RoadieId.ToString()
},
TrackArtist = track.TrackArtist == null
? null
: new DataToken
{
Text = track.TrackArtist.Name,
Value = track.TrackArtist.RoadieId.ToString()
},
Release = new DataToken
{
Text = track.ReleaseMedia.Release.Title,
Value = track.ReleaseMedia.Release.RoadieId.ToString()
},
Track = TrackList.FromDataTrack(null,
track,
track.ReleaseMedia.MediaNumber,
track.ReleaseMedia.Release,
track.ReleaseMedia.Release.Artist,
track.TrackArtist,
HttpContext.BaseUrl,
ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, track.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.TrackArtist == null
? null
: (Guid?)track.TrackArtist.RoadieId)),
User = new DataToken
{
Text = roadieUser.UserName,
Value = roadieUser.UserId.ToString()
},
ArtistThumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.TrackArtist != null
? track.TrackArtist.RoadieId
: track.ReleaseMedia.Release.Artist.RoadieId),
PlayedDateDateTime = scrobble.TimePlayed,
IsNowPlaying = isNowPlaying,
Rating = track.Rating,
ReleasePlayUrl = $"{HttpContext.BaseUrl}/play/release/{track.ReleaseMedia.Release.RoadieId}",
ReleaseThumbnail = ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId),
TrackPlayUrl = $"{HttpContext.BaseUrl}/play/track/{track.RoadieId}.mp3",
UserRating = userTrack?.Rating,
UserThumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, roadieUser.UserId)
};
try
{
await PlayActivityHub.Clients.All.SendAsync("SendActivityAsync", pl).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.LogError(ex);
}
}
}
public async Task<Library.Models.Pagination.PagedResult<PlayActivityList>> ListAsync(PagedRequest request, User roadieUser = null, DateTime? newerThan = null)
{ {
try try
{ {
@ -119,118 +195,46 @@ namespace Roadie.Api.Services
? request.OrderValue(new Dictionary<string, string> { { "PlayedDateDateTime", "DESC" } }) ? request.OrderValue(new Dictionary<string, string> { { "PlayedDateDateTime", "DESC" } })
: request.OrderValue(); : request.OrderValue();
var rowCount = result.Count(); var rowCount = result.Count();
var rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); var rows = await result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArrayAsync().ConfigureAwait(false);
sw.Stop(); sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<PlayActivityList> return new Library.Models.Pagination.PagedResult<PlayActivityList>
{ {
TotalCount = rowCount, TotalCount = rowCount,
CurrentPage = request.PageValue, CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue), TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds, OperationTime = sw.ElapsedMilliseconds,
Rows = rows Rows = rows
}); };
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex); Logger.LogError(ex);
} }
return Task.FromResult(new Library.Models.Pagination.PagedResult<PlayActivityList>()); return new Library.Models.Pagination.PagedResult<PlayActivityList>();
} }
public async Task<OperationResult<bool>> NowPlaying(User roadieUser, ScrobbleInfo scrobble) public async Task<OperationResult<bool>> NowPlayingAsync(User roadieUser, ScrobbleInfo scrobble)
{ {
var scrobbleResult = await ScrobblerHandler.NowPlaying(roadieUser, scrobble); var scrobbleResult = await ScrobblerHandler.NowPlaying(roadieUser, scrobble).ConfigureAwait(false);
if (!scrobbleResult.IsSuccess) return scrobbleResult;
await PublishPlayActivity(roadieUser, scrobble, true);
return scrobbleResult;
}
public async Task<OperationResult<bool>> Scrobble(User roadieUser, ScrobbleInfo scrobble)
{
var scrobbleResult = await ScrobblerHandler.Scrobble(roadieUser, scrobble);
if (!scrobbleResult.IsSuccess) if (!scrobbleResult.IsSuccess)
{ {
return scrobbleResult; return scrobbleResult;
} }
await PublishPlayActivity(roadieUser, scrobble, false);
await PublishPlayActivity(roadieUser, scrobble, true).ConfigureAwait(false);
return scrobbleResult; return scrobbleResult;
} }
private async Task PublishPlayActivity(User roadieUser, ScrobbleInfo scrobble, bool isNowPlaying) public async Task<OperationResult<bool>> ScrobbleAsync(User roadieUser, ScrobbleInfo scrobble)
{ {
// Only broadcast if the user is not public and played duration is more than half of duration var scrobbleResult = await ScrobblerHandler.Scrobble(roadieUser, scrobble).ConfigureAwait(false);
if (roadieUser?.IsPrivate != true && if (!scrobbleResult.IsSuccess)
scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds > scrobble.TrackDuration.TotalSeconds / 2)
{ {
var sw = Stopwatch.StartNew(); return scrobbleResult;
var track = 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 == scrobble.TrackId);
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId);
var userTrack =
DbContext.UserTracks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.TrackId == track.Id);
var pl = new PlayActivityList
{
Artist = new DataToken
{
Text = track.ReleaseMedia.Release.Artist.Name,
Value = track.ReleaseMedia.Release.Artist.RoadieId.ToString()
},
TrackArtist = track.TrackArtist == null
? null
: new DataToken
{
Text = track.TrackArtist.Name,
Value = track.TrackArtist.RoadieId.ToString()
},
Release = new DataToken
{
Text = track.ReleaseMedia.Release.Title,
Value = track.ReleaseMedia.Release.RoadieId.ToString()
},
Track = TrackList.FromDataTrack(null,
track,
track.ReleaseMedia.MediaNumber,
track.ReleaseMedia.Release,
track.ReleaseMedia.Release.Artist,
track.TrackArtist,
HttpContext.BaseUrl,
ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, track.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.TrackArtist == null
? null
: (Guid?)track.TrackArtist.RoadieId)),
User = new DataToken
{
Text = roadieUser.UserName,
Value = roadieUser.UserId.ToString()
},
ArtistThumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.TrackArtist != null
? track.TrackArtist.RoadieId
: track.ReleaseMedia.Release.Artist.RoadieId),
PlayedDateDateTime = scrobble.TimePlayed,
IsNowPlaying = isNowPlaying,
Rating = track.Rating,
ReleasePlayUrl = $"{HttpContext.BaseUrl}/play/release/{track.ReleaseMedia.Release.RoadieId}",
ReleaseThumbnail = ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId),
TrackPlayUrl = $"{HttpContext.BaseUrl}/play/track/{track.RoadieId}.mp3",
UserRating = userTrack?.Rating,
UserThumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, roadieUser.UserId)
};
try
{
await PlayActivityHub.Clients.All.SendAsync("SendActivity", pl);
}
catch (Exception ex)
{
Logger.LogError(ex);
}
} }
await PublishPlayActivity(roadieUser, scrobble, false).ConfigureAwait(false);
return scrobbleResult;
} }
} }
} }

View file

@ -42,412 +42,6 @@ namespace Roadie.Api.Services
BookmarkService = bookmarkService; BookmarkService = bookmarkService;
} }
public async Task<OperationResult<PlaylistList>> AddNewPlaylist(User user, Playlist model)
{
var playlist = new data.Playlist
{
IsPublic = model.IsPublic,
Description = model.Description,
Name = model.Name,
UserId = user.Id
};
DbContext.Playlists.Add(playlist);
await DbContext.SaveChangesAsync();
var r = await AddTracksToPlaylist(playlist,
model.Tracks.OrderBy(x => x.ListNumber).Select(x => x.Track.Id));
var request = new PagedRequest
{
FilterToPlaylistId = playlist.RoadieId
};
var result = await List(request, user);
return new OperationResult<PlaylistList>
{
Data = result.Rows.First(),
IsSuccess = true
};
}
public async Task<OperationResult<bool>> AddTracksToPlaylist(data.Playlist playlist, IEnumerable<Guid> trackIds)
{
var sw = new Stopwatch();
sw.Start();
var result = false;
var now = DateTime.UtcNow;
var existingTracksForPlaylist = from plt in DbContext.PlaylistTracks
join t in DbContext.Tracks on plt.TrackId equals t.Id
where plt.PlayListId == playlist.Id
select t;
var newTracksForPlaylist = (from t in DbContext.Tracks
where (from x in trackIds select x).Contains(t.RoadieId)
where !(from x in existingTracksForPlaylist select x.RoadieId).Contains(t.RoadieId)
select t).ToArray();
foreach (var newTrackForPlaylist in newTracksForPlaylist)
DbContext.PlaylistTracks.Add(new data.PlaylistTrack
{
TrackId = newTrackForPlaylist.Id,
PlayListId = playlist.Id
});
playlist.LastUpdated = now;
await DbContext.SaveChangesAsync();
result = true;
var r = await ReorderPlaylist(playlist);
result = result && r.IsSuccess;
await UpdatePlaylistCounts(playlist.Id, now);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = result,
Data = result,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<Playlist>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = string.Format("urn:playlist_by_id_operation:{0}:{1}", id,
includes == null ? "0" : string.Join("|", includes));
var result = await CacheManager.GetAsync(cacheKey, async () =>
{
return await PlaylistByIdAction(id, includes);
}, data.Artist.CacheRegionUrn(id));
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
if (result?.Data?.Tracks != null)
{
var user = await GetUser(roadieUser.UserId);
foreach (var track in result.Data.Tracks)
{
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, HttpContext.BaseUrl, track.Track.Id);
}
}
result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin;
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments.Any())
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr).ToArray();
foreach (var comment in result.Data.Comments)
{
var userCommentReaction = userCommentReactions.FirstOrDefault(x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
}
}
}
return new OperationResult<Playlist>(result.Messages)
{
Data = result?.Data,
IsNotFoundResult = result?.IsNotFoundResult ?? false,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<bool>> DeletePlaylist(User user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var playlist = DbContext.Playlists.FirstOrDefault(x => x.RoadieId == id);
if (playlist == null)
{
return new OperationResult<bool>(true, string.Format("Playlist Not Found [{0}]", id));
}
if (!user.IsAdmin && user.Id != playlist.UserId)
{
Logger.LogWarning("User `{0}` attempted to delete Playlist `{1}`", user, playlist);
return new OperationResult<bool>("Access Denied")
{
IsAccessDeniedResult = true
};
}
DbContext.Playlists.Remove(playlist);
await DbContext.SaveChangesAsync();
await BookmarkService.RemoveAllBookmarksForItem(BookmarkType.Playlist, playlist.Id);
var playlistImageFilename = playlist.PathToImage(Configuration);
if (File.Exists(playlistImageFilename))
{
File.Delete(playlistImageFilename);
}
Logger.LogWarning("User `{0}` deleted Playlist `{1}]`", user, playlist);
CacheManager.ClearRegion(playlist.CacheRegion);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = true,
Data = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public Task<Library.Models.Pagination.PagedResult<PlaylistList>> List(PagedRequest request, User roadieUser = null)
{
var sw = new Stopwatch();
sw.Start();
var playlistWithArtistTrackIds = new int[0];
if (request.FilterToArtistId.HasValue)
{
playlistWithArtistTrackIds = (from pl in DbContext.Playlists
join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
join t in DbContext.Tracks on pltr.TrackId equals t.Id
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
join a in DbContext.Artists on r.ArtistId equals a.Id
where a.RoadieId == request.FilterToArtistId
select pl.Id
).ToArray();
}
var playlistReleaseTrackIds = new int[0];
if (request.FilterToReleaseId.HasValue)
{
playlistReleaseTrackIds = (from pl in DbContext.Playlists
join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
join t in DbContext.Tracks on pltr.TrackId equals t.Id
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
where r.RoadieId == request.FilterToReleaseId
select pl.Id
).ToArray();
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
? request.FilterValue.ToAlphanumericName()
: null;
var result = from pl in DbContext.Playlists
join u in DbContext.Users on pl.UserId equals u.Id
where request.FilterToPlaylistId == null || pl.RoadieId == request.FilterToPlaylistId
where request.FilterToArtistId == null || playlistWithArtistTrackIds.Contains(pl.Id)
where request.FilterToReleaseId == null || playlistReleaseTrackIds.Contains(pl.Id)
where roadieUser == null && pl.IsPublic || roadieUser != null && u.RoadieId == roadieUser.UserId || pl.IsPublic
where string.IsNullOrEmpty(normalizedFilterValue) || (
pl.Name.ToLower().Contains(normalizedFilterValue) ||
pl.AlternateNames.ToLower().Contains(normalizedFilterValue)
)
select new PlaylistList
{
Playlist = new DataToken
{
Text = pl.Name,
Value = pl.RoadieId.ToString()
},
User = new DataToken
{
Text = u.UserName,
Value = u.RoadieId.ToString()
},
PlaylistCount = pl.TrackCount,
IsPublic = pl.IsPublic,
Duration = pl.Duration,
TrackCount = pl.TrackCount,
CreatedDate = pl.CreatedDate,
LastUpdated = pl.LastUpdated,
UserThumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, u.RoadieId),
Id = pl.RoadieId,
Thumbnail = ImageHelper.MakePlaylistThumbnailImage(Configuration, HttpContext, pl.RoadieId)
};
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "Playlist.Text", "ASC" } })
: request.OrderValue();
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<PlaylistList>
{
TotalCount = rowCount,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
}
public async Task<OperationResult<bool>> ReorderPlaylist(data.Playlist playlist)
{
var sw = new Stopwatch();
sw.Start();
var result = false;
var now = DateTime.UtcNow;
if (playlist != null)
{
var looper = 0;
foreach (var playlistTrack in DbContext.PlaylistTracks.Where(x => x.PlayListId == playlist.Id)
.OrderBy(x => x.CreatedDate))
{
looper++;
playlistTrack.ListNumber = looper;
playlistTrack.LastUpdated = now;
}
await DbContext.SaveChangesAsync();
result = true;
}
return new OperationResult<bool>
{
IsSuccess = result,
Data = result
};
}
public async Task<OperationResult<bool>> UpdatePlaylist(User user, Playlist model)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = DbContext.Playlists.FirstOrDefault(x => x.RoadieId == model.Id);
if (playlist == null)
{
return new OperationResult<bool>(true, string.Format("Playlist Not Found [{0}]", model.Id));
}
if (!user.IsAdmin && user.Id != playlist.UserId)
{
Logger.LogWarning("User `{0}` attempted to update Playlist `{1}`", user, playlist);
return new OperationResult<bool>("Access Denied")
{
IsAccessDeniedResult = true
};
}
try
{
var now = DateTime.UtcNow;
playlist.AlternateNames = model.AlternateNamesList.ToDelimitedList();
playlist.Description = model.Description;
playlist.IsLocked = model.IsLocked;
playlist.IsPublic = model.IsPublic;
var oldPathToImage = playlist.PathToImage(Configuration);
var didChangeName = playlist.Name != model.Name;
playlist.Name = model.Name;
playlist.Status = SafeParser.ToEnum<Statuses>(model.Status);
playlist.Tags = model.TagsList.ToDelimitedList();
playlist.URLs = model.URLsList.ToDelimitedList();
if (didChangeName)
{
if (File.Exists(oldPathToImage))
{
File.Move(oldPathToImage, playlist.PathToImage(Configuration));
}
}
var playlistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (playlistImage != null)
{
// Save unaltered playlist image
File.WriteAllBytes(playlist.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(playlistImage));
}
playlist.LastUpdated = now;
await DbContext.SaveChangesAsync();
CacheManager.ClearRegion(playlist.CacheRegion);
Logger.LogInformation($"UpdatePlaylist `{playlist}` By User `{user}`");
}
catch (Exception ex)
{
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 = 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));
}
if (!user.IsAdmin && user.Id != playlist.UserId)
{
Logger.LogWarning("User `{0}` attempted to update Playlist Tracks `{1}`", user, playlist);
return new OperationResult<bool>("Access Denied")
{
IsAccessDeniedResult = true
};
}
try
{
var now = DateTime.UtcNow;
playlist.Tracks.Clear();
var tracks = (from t in 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 DbContext.SaveChangesAsync();
// await base.UpdatePlaylistCounts(playlist.Id, now);
Logger.LogInformation($"UpdatePlaylistTracks `{playlist}` By User `{user}`");
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = !errors.Any(),
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
private async Task<OperationResult<Playlist>> PlaylistByIdAction(Guid id, IEnumerable<string> includes = null) private async Task<OperationResult<Playlist>> PlaylistByIdAction(Guid id, IEnumerable<string> includes = null)
{ {
var timings = new Dictionary<string, long>(); var timings = new Dictionary<string, long>();
@ -457,13 +51,13 @@ namespace Roadie.Api.Services
sw.Start(); sw.Start();
tsw.Restart(); tsw.Restart();
var playlist = await GetPlaylist(id); var playlist = await GetPlaylist(id).ConfigureAwait(false);
tsw.Stop(); tsw.Stop();
timings.Add("getPlaylist", tsw.ElapsedMilliseconds); timings.Add("getPlaylist", tsw.ElapsedMilliseconds);
if (playlist == null) if (playlist == null)
{ {
return new OperationResult<Playlist>(true, string.Format("Playlist Not Found [{0}]", id)); return new OperationResult<Playlist>(true, $"Playlist Not Found [{id}]");
} }
tsw.Restart(); tsw.Restart();
var result = playlist.Adapt<Playlist>(); var result = playlist.Adapt<Playlist>();
@ -563,5 +157,416 @@ namespace Roadie.Api.Services
OperationTime = sw.ElapsedMilliseconds OperationTime = sw.ElapsedMilliseconds
}; };
} }
public async Task<OperationResult<PlaylistList>> AddNewPlaylistAsync(User user, Playlist model)
{
var playlist = new data.Playlist
{
IsPublic = model.IsPublic,
Description = model.Description,
Name = model.Name,
UserId = user.Id
};
DbContext.Playlists.Add(playlist);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
var r = await AddTracksToPlaylistAsync(playlist, model.Tracks.OrderBy(x => x.ListNumber).Select(x => x.Track.Id)).ConfigureAwait(false);
var request = new PagedRequest
{
FilterToPlaylistId = playlist.RoadieId
};
var result = await ListAsync(request, user).ConfigureAwait(false);
return new OperationResult<PlaylistList>
{
Data = result.Rows.First(),
IsSuccess = true
};
}
public async Task<OperationResult<bool>> AddTracksToPlaylistAsync(data.Playlist playlist, IEnumerable<Guid> trackIds)
{
var sw = new Stopwatch();
sw.Start();
var result = false;
var now = DateTime.UtcNow;
var existingTracksForPlaylist = from plt in DbContext.PlaylistTracks
join t in DbContext.Tracks on plt.TrackId equals t.Id
where plt.PlayListId == playlist.Id
select t;
var newTracksForPlaylist = await (from t in DbContext.Tracks
where (from x in trackIds select x).Contains(t.RoadieId)
where !(from x in existingTracksForPlaylist select x.RoadieId).Contains(t.RoadieId)
select t)
.ToArrayAsync()
.ConfigureAwait(false);
foreach (var newTrackForPlaylist in newTracksForPlaylist)
{
DbContext.PlaylistTracks.Add(new data.PlaylistTrack
{
TrackId = newTrackForPlaylist.Id,
PlayListId = playlist.Id
});
}
playlist.LastUpdated = now;
await DbContext.SaveChangesAsync().ConfigureAwait(false);
result = true;
var r = await ReorderPlaylistAsync(playlist).ConfigureAwait(false);
result = result && r.IsSuccess;
await UpdatePlaylistCounts(playlist.Id, now).ConfigureAwait(false);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = result,
Data = result,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<Playlist>> ByIdAsync(User roadieUser, Guid id, IEnumerable<string> includes = null)
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = $"urn:playlist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
var result = await CacheManager.GetAsync(cacheKey, async () =>
{
return await PlaylistByIdAction(id, includes).ConfigureAwait(false);
}, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
if (result?.Data?.Tracks != null)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
foreach (var track in result.Data.Tracks)
{
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, HttpContext.BaseUrl, track.Track.Id);
}
}
result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin;
var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Playlist).ConfigureAwait(false);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments.Any())
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = await (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr)
.ToArrayAsync()
.ConfigureAwait(false);
foreach (var comment in result.Data.Comments)
{
var userCommentReaction = Array.Find(userCommentReactions, x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
}
}
}
return new OperationResult<Playlist>(result.Messages)
{
Data = result?.Data,
IsNotFoundResult = result?.IsNotFoundResult ?? false,
Errors = result?.Errors,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<bool>> DeletePlaylistAsync(User user, Guid id)
{
var sw = new Stopwatch();
sw.Start();
var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
if (playlist == null)
{
return new OperationResult<bool>(true, $"Playlist Not Found [{id}]");
}
if (!user.IsAdmin && user.Id != playlist.UserId)
{
Logger.LogWarning("User `{0}` attempted to delete Playlist `{1}`", user, playlist);
return new OperationResult<bool>("Access Denied")
{
IsAccessDeniedResult = true
};
}
DbContext.Playlists.Remove(playlist);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
await BookmarkService.RemoveAllBookmarksForItemAsync(BookmarkType.Playlist, playlist.Id).ConfigureAwait(false);
var playlistImageFilename = playlist.PathToImage(Configuration);
if (File.Exists(playlistImageFilename))
{
File.Delete(playlistImageFilename);
}
Logger.LogWarning("User `{0}` deleted Playlist `{1}]`", user, playlist);
CacheManager.ClearRegion(playlist.CacheRegion);
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = true,
Data = true,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<Library.Models.Pagination.PagedResult<PlaylistList>> ListAsync(PagedRequest request, User roadieUser = null)
{
var sw = new Stopwatch();
sw.Start();
var playlistWithArtistTrackIds = new int[0];
if (request.FilterToArtistId.HasValue)
{
playlistWithArtistTrackIds = await (from pl in DbContext.Playlists
join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
join t in DbContext.Tracks on pltr.TrackId equals t.Id
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
join a in DbContext.Artists on r.ArtistId equals a.Id
where a.RoadieId == request.FilterToArtistId
select pl.Id
).ToArrayAsync().ConfigureAwait(false);
}
var playlistReleaseTrackIds = new int[0];
if (request.FilterToReleaseId.HasValue)
{
playlistReleaseTrackIds = await (from pl in DbContext.Playlists
join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
join t in DbContext.Tracks on pltr.TrackId equals t.Id
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
where r.RoadieId == request.FilterToReleaseId
select pl.Id
).ToArrayAsync().ConfigureAwait(false);
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
? request.FilterValue.ToAlphanumericName()
: null;
var result = from pl in DbContext.Playlists
join u in DbContext.Users on pl.UserId equals u.Id
where request.FilterToPlaylistId == null || pl.RoadieId == request.FilterToPlaylistId
where request.FilterToArtistId == null || playlistWithArtistTrackIds.Contains(pl.Id)
where request.FilterToReleaseId == null || playlistReleaseTrackIds.Contains(pl.Id)
where roadieUser == null && pl.IsPublic || roadieUser != null && u.RoadieId == roadieUser.UserId || pl.IsPublic
where string.IsNullOrEmpty(normalizedFilterValue) || (
pl.Name.ToLower().Contains(normalizedFilterValue) ||
pl.AlternateNames.ToLower().Contains(normalizedFilterValue)
)
select new PlaylistList
{
Playlist = new DataToken
{
Text = pl.Name,
Value = pl.RoadieId.ToString()
},
User = new DataToken
{
Text = u.UserName,
Value = u.RoadieId.ToString()
},
PlaylistCount = pl.TrackCount,
IsPublic = pl.IsPublic,
Duration = pl.Duration,
TrackCount = pl.TrackCount,
CreatedDate = pl.CreatedDate,
LastUpdated = pl.LastUpdated,
UserThumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, u.RoadieId),
Id = pl.RoadieId,
Thumbnail = ImageHelper.MakePlaylistThumbnailImage(Configuration, HttpContext, pl.RoadieId)
};
var sortBy = string.IsNullOrEmpty(request.Sort)
? request.OrderValue(new Dictionary<string, string> { { "Playlist.Text", "ASC" } })
: request.OrderValue();
var rowCount = await result.CountAsync().ConfigureAwait(false);
var rows = await result
.OrderBy(sortBy)
.Skip(request.SkipValue)
.Take(request.LimitValue)
.ToArrayAsync()
.ConfigureAwait(false);
sw.Stop();
return new Library.Models.Pagination.PagedResult<PlaylistList>
{
TotalCount = rowCount,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
};
}
public async Task<OperationResult<bool>> ReorderPlaylistAsync(data.Playlist playlist)
{
var sw = new Stopwatch();
sw.Start();
var result = false;
var now = DateTime.UtcNow;
if (playlist != null)
{
var looper = 0;
foreach (var playlistTrack in DbContext.PlaylistTracks.Where(x => x.PlayListId == playlist.Id).OrderBy(x => x.CreatedDate))
{
looper++;
playlistTrack.ListNumber = looper;
playlistTrack.LastUpdated = now;
}
await DbContext.SaveChangesAsync().ConfigureAwait(false);
result = true;
}
return new OperationResult<bool>
{
IsSuccess = result,
Data = result
};
}
public async Task<OperationResult<bool>> UpdatePlaylistAsync(User user, Playlist model)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false);
if (playlist == null)
{
return new OperationResult<bool>(true, $"Playlist Not Found [{model.Id}]");
}
if (!user.IsAdmin && user.Id != playlist.UserId)
{
Logger.LogWarning("User `{0}` attempted to update Playlist `{1}`", user, playlist);
return new OperationResult<bool>("Access Denied")
{
IsAccessDeniedResult = true
};
}
try
{
var now = DateTime.UtcNow;
playlist.AlternateNames = model.AlternateNamesList.ToDelimitedList();
playlist.Description = model.Description;
playlist.IsLocked = model.IsLocked;
playlist.IsPublic = model.IsPublic;
var oldPathToImage = playlist.PathToImage(Configuration);
var didChangeName = playlist.Name != model.Name;
playlist.Name = model.Name;
playlist.Status = SafeParser.ToEnum<Statuses>(model.Status);
playlist.Tags = model.TagsList.ToDelimitedList();
playlist.URLs = model.URLsList.ToDelimitedList();
if (didChangeName)
{
if (File.Exists(oldPathToImage))
{
File.Move(oldPathToImage, playlist.PathToImage(Configuration));
}
}
var playlistImage = ImageHelper.ImageDataFromUrl(model.NewThumbnailData);
if (playlistImage != null)
{
// Save unaltered playlist image
await File.WriteAllBytesAsync(playlist.PathToImage(Configuration, true), ImageHelper.ConvertToJpegFormat(playlistImage)).ConfigureAwait(false);
}
playlist.LastUpdated = now;
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(playlist.CacheRegion);
Logger.LogInformation($"UpdatePlaylist `{playlist}` By User `{user}`");
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = errors.Count == 0,
Data = errors.Count == 0,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> UpdatePlaylistTracksAsync(User user, PlaylistTrackModifyRequest request)
{
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = await DbContext.Playlists.Include(x => x.Tracks).FirstOrDefaultAsync(x => x.RoadieId == request.Id).ConfigureAwait(false);
if (playlist == null)
{
return new OperationResult<bool>(true, $"Label Not Found [{request.Id}]");
}
if (!user.IsAdmin && user.Id != playlist.UserId)
{
Logger.LogWarning("User `{0}` attempted to update Playlist Tracks `{1}`", user, playlist);
return new OperationResult<bool>("Access Denied")
{
IsAccessDeniedResult = true
};
}
try
{
var now = DateTime.UtcNow;
playlist.Tracks.Clear();
var tracks = await (from t in DbContext.Tracks
join plt in request.Tracks on t.RoadieId equals plt.Track.Id
select t).ToArrayAsync().ConfigureAwait(false);
foreach (var newPlaylistTrack in request.Tracks.OrderBy(x => x.ListNumber))
{
var track = Array.Find(tracks, 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 DbContext.SaveChangesAsync().ConfigureAwait(false);
// await base.UpdatePlaylistCounts(playlist.Id, now);
Logger.LogInformation($"UpdatePlaylistTracks `{playlist}` By User `{user}`");
}
catch (Exception ex)
{
Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
return new OperationResult<bool>
{
IsSuccess = errors.Count == 0,
Data = errors.Count == 0,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -99,91 +99,91 @@ namespace Roadie.Api.Services
{ {
return null; return null;
} }
return await GetArtist(artistByName.RoadieId); return await GetArtist(artistByName.RoadieId).ConfigureAwait(false);
} }
protected Task<data.Artist> GetArtist(Guid id) protected async Task<data.Artist> GetArtist(Guid id)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
{ {
return Task.FromResult<data.Artist>(null); return null;
} }
return CacheManager.GetAsync(data.Artist.CacheUrn(id), () => return await CacheManager.GetAsync(data.Artist.CacheUrn(id), async () =>
{ {
return DbContext.Artists return await DbContext.Artists
.Include(x => x.Genres) .Include(x => x.Genres)
.Include("Genres.Genre") .Include("Genres.Genre")
.FirstOrDefaultAsync(x => x.RoadieId == id); .FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, data.Artist.CacheRegionUrn(id)); }, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
} }
protected Task<data.Collection> GetCollection(Guid id) protected async Task<data.Collection> GetCollection(Guid id)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
{ {
return Task.FromResult<data.Collection>(null); return null;
} }
return CacheManager.GetAsync(data.Collection.CacheUrn(id), () => return await CacheManager.GetAsync(data.Collection.CacheUrn(id), async () =>
{ {
return DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == id); return await DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, data.Collection.CacheRegionUrn(id)); }, data.Collection.CacheRegionUrn(id)).ConfigureAwait(false);
} }
protected Task<data.Genre> GetGenre(Guid id) protected async Task<data.Genre> GetGenre(Guid id)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
{ {
return Task.FromResult<data.Genre>(null); return null;
} }
return CacheManager.GetAsync(data.Genre.CacheUrn(id), () => return await CacheManager.GetAsync(data.Genre.CacheUrn(id), async () =>
{ {
return DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == id); return await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, data.Genre.CacheRegionUrn(id)); }, data.Genre.CacheRegionUrn(id)).ConfigureAwait(false);
} }
protected Task<data.Label> GetLabel(Guid id) protected async Task<data.Label> GetLabel(Guid id)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
{ {
return Task.FromResult<data.Label>(null); return null;
} }
return CacheManager.GetAsync(data.Label.CacheUrn(id), () => return await CacheManager.GetAsync(data.Label.CacheUrn(id), async () =>
{ {
return DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == id); return await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, data.Label.CacheRegionUrn(id)); }, data.Label.CacheRegionUrn(id)).ConfigureAwait(false);
} }
protected Task<data.Playlist> GetPlaylist(Guid id) protected async Task<data.Playlist> GetPlaylist(Guid id)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
{ {
return Task.FromResult<data.Playlist>(null); return null;
} }
return CacheManager.GetAsync(data.Playlist.CacheUrn(id), () => return await CacheManager.GetAsync(data.Playlist.CacheUrn(id), async () =>
{ {
return DbContext.Playlists return await DbContext.Playlists
.Include(x => x.User) .Include(x => x.User)
.FirstOrDefaultAsync(x => x.RoadieId == id); .FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, data.Playlist.CacheRegionUrn(id)); }, data.Playlist.CacheRegionUrn(id)).ConfigureAwait(false);
} }
protected Task<data.Release> GetRelease(Guid id) protected async Task<data.Release> GetRelease(Guid id)
{ {
if (id == Guid.Empty) if (id == Guid.Empty)
{ {
return Task.FromResult<data.Release>(null); return null;
} }
return CacheManager.GetAsync(data.Release.CacheUrn(id), () => return await CacheManager.GetAsync(data.Release.CacheUrn(id), async () =>
{ {
return DbContext.Releases return await DbContext.Releases
.Include(x => x.Artist) .Include(x => x.Artist)
.Include(x => x.Genres) .Include(x => x.Genres)
.Include("Genres.Genre") .Include("Genres.Genre")
.Include(x => x.Medias) .Include(x => x.Medias)
.Include("Medias.Tracks") .Include("Medias.Tracks")
.Include("Medias.Tracks.TrackArtist") .Include("Medias.Tracks.TrackArtist")
.FirstOrDefaultAsync(x => x.RoadieId == id); .FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, data.Release.CacheRegionUrn(id)); }, data.Release.CacheRegionUrn(id)).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@ -193,27 +193,27 @@ namespace Roadie.Api.Services
{ {
if (Guid.TryParse(id, out Guid trackId)) if (Guid.TryParse(id, out Guid trackId))
{ {
return await GetTrack(trackId); return await GetTrack(trackId).ConfigureAwait(false);
} }
return null; return null;
} }
// Only read operations // Only read operations
protected Task<data.Track> GetTrack(Guid id) protected async Task<data.Track> GetTrack(Guid id)
{ {
if(id == Guid.Empty) if(id == Guid.Empty)
{ {
return Task.FromResult<data.Track>(null); return null;
} }
return CacheManager.GetAsync(data.Track.CacheUrn(id), () => return await CacheManager.GetAsync(data.Track.CacheUrn(id), async () =>
{ {
return DbContext.Tracks return await DbContext.Tracks
.Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist) .Include(x => x.TrackArtist)
.FirstOrDefaultAsync(x => x.RoadieId == id); .FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, data.Track.CacheRegionUrn(id)); }, data.Track.CacheRegionUrn(id)).ConfigureAwait(false);
} }
protected async Task<User> GetUser(string username) protected async Task<User> GetUser(string username)
@ -225,40 +225,41 @@ namespace Roadie.Api.Services
var userByUsername = await CacheManager.GetAsync(User.CacheUrnByUsername(username), () => var userByUsername = await CacheManager.GetAsync(User.CacheUrnByUsername(username), () =>
{ {
return DbContext.Users.FirstOrDefaultAsync(x => x.UserName == username); return DbContext.Users.FirstOrDefaultAsync(x => x.UserName == username);
}, null); }, null).ConfigureAwait(false);
return await GetUser(userByUsername?.RoadieId).ConfigureAwait(false); return await GetUser(userByUsername?.RoadieId).ConfigureAwait(false);
} }
protected Task<User> GetUser(Guid? id) protected async Task<User> GetUser(Guid? id)
{ {
if (!id.HasValue) if (!id.HasValue)
{ {
return Task.FromResult<User>(null); return null;
} }
return CacheManager.GetAsync(User.CacheUrn(id.Value), () => return await CacheManager.GetAsync(User.CacheUrn(id.Value), async () =>
{ {
return DbContext.Users return await DbContext.Users
.Include(x => x.UserRoles) .Include(x => x.UserRoles)
.Include("UserRoles.Role") .Include("UserRoles.Role")
.Include("UserRoles.Role.RoleClaims") .Include("UserRoles.Role.RoleClaims")
.Include(x => x.UserClaims) .Include(x => x.UserClaims)
.Include(x => x.UserQues) .Include(x => x.UserQues)
.Include("UserQues.Track") .Include("UserQues.Track")
.FirstOrDefaultAsync(x => x.RoadieId == id); .FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
}, User.CacheRegionUrn(id.Value)); }, User.CacheRegionUrn(id.Value)).ConfigureAwait(false);
} }
protected string MakeLastFmUrl(string artistName, string releaseTitle) => "http://www.last.fm/music/" + HttpEncoder.UrlEncode($"{artistName}/{releaseTitle}"); protected string MakeLastFmUrl(string artistName, string releaseTitle) => "http://www.last.fm/music/" + HttpEncoder.UrlEncode($"{artistName}/{releaseTitle}");
protected async Task<OperationResult<short>> SetArtistRating(Guid artistId, User user, short rating) protected async Task<OperationResult<short>> SetArtistRating(Guid artistId, User user, short rating)
{ {
var artist = DbContext.Artists var artist = await DbContext.Artists
.Include(x => x.Genres) .Include(x => x.Genres)
.Include("Genres.Genre") .Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistId); .FirstOrDefaultAsync(x => x.RoadieId == artistId)
.ConfigureAwait(false);
if (artist == null) return new OperationResult<short>(true, $"Invalid Artist Id [{artistId}]"); if (artist == null) return new OperationResult<short>(true, $"Invalid Artist Id [{artistId}]");
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); var userArtist = await DbContext.UserArtists.FirstOrDefaultAsync(x => x.ArtistId == artist.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userArtist == null) if (userArtist == null)
{ {
userArtist = new data.UserArtist userArtist = new data.UserArtist
@ -278,7 +279,7 @@ namespace Roadie.Api.Services
await DbContext.SaveChangesAsync().ConfigureAwait(false); await DbContext.SaveChangesAsync().ConfigureAwait(false);
var ratings = await DbContext.UserArtists.Where(x => x.ArtistId == artist.Id && x.Rating > 0).Select(x => x.Rating).ToListAsync().ConfigureAwait(false); var ratings = await DbContext.UserArtists.Where(x => x.ArtistId == artist.Id && x.Rating > 0).Select(x => x.Rating).ToListAsync().ConfigureAwait(false);
if (ratings != null && ratings.Any()) if (ratings != null && ratings.Count > 0)
{ {
artist.Rating = (short)ratings.Average(x => (decimal)x); artist.Rating = (short)ratings.Average(x => (decimal)x);
} }
@ -288,7 +289,7 @@ namespace Roadie.Api.Services
} }
artist.LastUpdated = now; artist.LastUpdated = now;
await DbContext.SaveChangesAsync().ConfigureAwait(false); await DbContext.SaveChangesAsync().ConfigureAwait(false);
await UpdateArtistRank(artist.Id); await UpdateArtistRank(artist.Id).ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
@ -303,19 +304,19 @@ namespace Roadie.Api.Services
protected async Task<OperationResult<short>> SetReleaseRating(Guid releaseId, User user, short rating) protected async Task<OperationResult<short>> SetReleaseRating(Guid releaseId, User user, short rating)
{ {
var release = DbContext.Releases var release = await DbContext.Releases
.Include(x => x.Artist) .Include(x => x.Artist)
.Include(x => x.Genres) .Include(x => x.Genres)
.Include("Genres.Genre") .Include("Genres.Genre")
.Include(x => x.Medias) .Include(x => x.Medias)
.Include("Medias.Tracks") .Include("Medias.Tracks")
.Include("Medias.Tracks.TrackArtist") .Include("Medias.Tracks.TrackArtist")
.FirstOrDefault(x => x.RoadieId == releaseId); .FirstOrDefaultAsync(x => x.RoadieId == releaseId).ConfigureAwait(false);
if (release == null) if (release == null)
{ {
return new OperationResult<short>(true, $"Invalid Release Id [{releaseId}]"); return new OperationResult<short>(true, $"Invalid Release Id [{releaseId}]");
} }
var userRelease = DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); var userRelease = await DbContext.UserReleases.FirstOrDefaultAsync(x => x.ReleaseId == release.Id && x.UserId == user.Id).ConfigureAwait(false);
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
if (userRelease == null) if (userRelease == null)
{ {
@ -325,7 +326,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
ReleaseId = release.Id ReleaseId = release.Id
}; };
DbContext.UserReleases.Add(userRelease); await DbContext.UserReleases.AddAsync(userRelease).ConfigureAwait(false);
} }
else else
{ {
@ -333,10 +334,10 @@ namespace Roadie.Api.Services
userRelease.LastUpdated = now; userRelease.LastUpdated = now;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
var ratings = await DbContext.UserReleases.Where(x => x.ReleaseId == release.Id && x.Rating > 0).Select(x => x.Rating).ToListAsync(); var ratings = await DbContext.UserReleases.Where(x => x.ReleaseId == release.Id && x.Rating > 0).Select(x => x.Rating).ToListAsync().ConfigureAwait(false);
if (ratings != null && ratings.Any()) if (ratings != null && ratings.Count > 0)
{ {
release.Rating = (short)ratings.Average(x => (decimal)x); release.Rating = (short)ratings.Average(x => (decimal)x);
} }
@ -345,13 +346,13 @@ namespace Roadie.Api.Services
release.Rating = 0; release.Rating = 0;
} }
release.LastUpdated = now; release.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
await UpdateReleaseRank(release.Id); await UpdateReleaseRank(release.Id).ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(release.CacheRegion); CacheManager.ClearRegion(release.CacheRegion);
CacheManager.ClearRegion(release.Artist.CacheRegion); CacheManager.ClearRegion(release.Artist.CacheRegion);
release = await GetRelease(releaseId); release = await GetRelease(releaseId).ConfigureAwait(false);
return new OperationResult<short> return new OperationResult<short>
{ {
@ -364,18 +365,19 @@ namespace Roadie.Api.Services
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var track = DbContext.Tracks var track = await DbContext.Tracks
.Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist) .Include(x => x.TrackArtist)
.FirstOrDefault(x => x.RoadieId == trackId); .FirstOrDefaultAsync(x => x.RoadieId == trackId)
.ConfigureAwait(false);
if (track == null) if (track == null)
{ {
return new OperationResult<short>(true, $"Invalid Track Id [{trackId}]"); return new OperationResult<short>(true, $"Invalid Track Id [{trackId}]");
} }
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var userTrack = DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); var userTrack = await DbContext.UserTracks.FirstOrDefaultAsync(x => x.TrackId == track.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userTrack == null) if (userTrack == null)
{ {
userTrack = new data.UserTrack userTrack = new data.UserTrack
@ -384,7 +386,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
TrackId = track.Id TrackId = track.Id
}; };
DbContext.UserTracks.Add(userTrack); await DbContext.UserTracks.AddAsync(userTrack).ConfigureAwait(false);
} }
else else
{ {
@ -392,10 +394,10 @@ namespace Roadie.Api.Services
userTrack.LastUpdated = now; userTrack.LastUpdated = now;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
var ratings = await DbContext.UserTracks.Where(x => x.TrackId == track.Id && x.Rating > 0).Select(x => x.Rating).ToListAsync(); var ratings = await DbContext.UserTracks.Where(x => x.TrackId == track.Id && x.Rating > 0).Select(x => x.Rating).ToListAsync().ConfigureAwait(false);
if (ratings != null && ratings.Any()) if (ratings != null && ratings.Count > 0)
{ {
track.Rating = (short)ratings.Average(x => (double)x); track.Rating = (short)ratings.Average(x => (double)x);
} }
@ -404,8 +406,8 @@ namespace Roadie.Api.Services
track.Rating = 0; track.Rating = 0;
} }
track.LastUpdated = now; track.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
await UpdateReleaseRank(track.ReleaseMedia.Release.Id); await UpdateReleaseRank(track.ReleaseMedia.Release.Id).ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(track.CacheRegion); CacheManager.ClearRegion(track.CacheRegion);
@ -430,12 +432,13 @@ namespace Roadie.Api.Services
var result = false; var result = false;
try try
{ {
var artist = DbContext.Artists var artist = await DbContext.Artists
.Include(x => x.Genres) .Include(x => x.Genres)
.Include("Genres.Genre") .Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistId); .FirstOrDefaultAsync(x => x.RoadieId == artistId)
.ConfigureAwait(false);
if (artist == null) return new OperationResult<bool>(true, $"Invalid Artist Id [{artistId}]"); if (artist == null) return new OperationResult<bool>(true, $"Invalid Artist Id [{artistId}]");
var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); var userArtist = await DbContext.UserArtists.FirstOrDefaultAsync(x => x.ArtistId == artist.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userArtist == null) if (userArtist == null)
{ {
userArtist = new data.UserArtist userArtist = new data.UserArtist
@ -444,7 +447,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
ArtistId = artist.Id ArtistId = artist.Id
}; };
DbContext.UserArtists.Add(userArtist); await DbContext.UserArtists.AddAsync(userArtist).ConfigureAwait(false);
} }
else else
{ {
@ -452,7 +455,7 @@ namespace Roadie.Api.Services
userArtist.LastUpdated = DateTime.UtcNow; userArtist.LastUpdated = DateTime.UtcNow;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
@ -474,12 +477,13 @@ namespace Roadie.Api.Services
var result = false; var result = false;
try try
{ {
var artist = DbContext.Artists var artist = await DbContext.Artists
.Include(x => x.Genres) .Include(x => x.Genres)
.Include("Genres.Genre") .Include("Genres.Genre")
.FirstOrDefault(x => x.RoadieId == artistId); .FirstOrDefaultAsync(x => x.RoadieId == artistId)
.ConfigureAwait(false);
if (artist == null) return new OperationResult<bool>(true, $"Invalid Artist Id [{artistId}]"); if (artist == null) return new OperationResult<bool>(true, $"Invalid Artist Id [{artistId}]");
var userArtist = DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == user.Id); var userArtist = await DbContext.UserArtists.FirstOrDefaultAsync(x => x.ArtistId == artist.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userArtist == null) if (userArtist == null)
{ {
userArtist = new data.UserArtist userArtist = new data.UserArtist
@ -488,7 +492,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
ArtistId = artist.Id ArtistId = artist.Id
}; };
DbContext.UserArtists.Add(userArtist); await DbContext.UserArtists.AddAsync(userArtist).ConfigureAwait(false);
} }
else else
{ {
@ -496,7 +500,7 @@ namespace Roadie.Api.Services
userArtist.LastUpdated = DateTime.UtcNow; userArtist.LastUpdated = DateTime.UtcNow;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
@ -518,19 +522,20 @@ namespace Roadie.Api.Services
var result = false; var result = false;
try try
{ {
var release = DbContext.Releases var release = await DbContext.Releases
.Include(x => x.Artist) .Include(x => x.Artist)
.Include(x => x.Genres) .Include(x => x.Genres)
.Include("Genres.Genre") .Include("Genres.Genre")
.Include(x => x.Medias) .Include(x => x.Medias)
.Include("Medias.Tracks") .Include("Medias.Tracks")
.Include("Medias.Tracks.TrackArtist") .Include("Medias.Tracks.TrackArtist")
.FirstOrDefault(x => x.RoadieId == releaseId); .FirstOrDefaultAsync(x => x.RoadieId == releaseId)
.ConfigureAwait(false);
if (release == null) if (release == null)
{ {
return new OperationResult<bool>(true, $"Invalid Release Id [{releaseId}]"); return new OperationResult<bool>(true, $"Invalid Release Id [{releaseId}]");
} }
var userRelease = DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); var userRelease = await DbContext.UserReleases.FirstOrDefaultAsync(x => x.ReleaseId == release.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userRelease == null) if (userRelease == null)
{ {
userRelease = new data.UserRelease userRelease = new data.UserRelease
@ -539,7 +544,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
ReleaseId = release.Id ReleaseId = release.Id
}; };
DbContext.UserReleases.Add(userRelease); await DbContext.UserReleases.AddAsync(userRelease).ConfigureAwait(false);
} }
else else
{ {
@ -547,7 +552,7 @@ namespace Roadie.Api.Services
userRelease.LastUpdated = DateTime.UtcNow; userRelease.LastUpdated = DateTime.UtcNow;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(release.CacheRegion); CacheManager.ClearRegion(release.CacheRegion);
@ -582,7 +587,7 @@ namespace Roadie.Api.Services
{ {
return new OperationResult<bool>(true, $"Invalid Release Id [{releaseId}]"); return new OperationResult<bool>(true, $"Invalid Release Id [{releaseId}]");
} }
var userRelease = DbContext.UserReleases.FirstOrDefault(x => x.ReleaseId == release.Id && x.UserId == user.Id); var userRelease = await DbContext.UserReleases.FirstOrDefaultAsync(x => x.ReleaseId == release.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userRelease == null) if (userRelease == null)
{ {
userRelease = new data.UserRelease userRelease = new data.UserRelease
@ -591,7 +596,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
ReleaseId = release.Id ReleaseId = release.Id
}; };
DbContext.UserReleases.Add(userRelease); await DbContext.UserReleases.AddAsync(userRelease).ConfigureAwait(false);
} }
else else
{ {
@ -599,7 +604,7 @@ namespace Roadie.Api.Services
userRelease.LastUpdated = DateTime.UtcNow; userRelease.LastUpdated = DateTime.UtcNow;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(release.CacheRegion); CacheManager.ClearRegion(release.CacheRegion);
@ -622,17 +627,18 @@ namespace Roadie.Api.Services
var result = false; var result = false;
try try
{ {
var track = DbContext.Tracks var track = await DbContext.Tracks
.Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist) .Include(x => x.TrackArtist)
.FirstOrDefault(x => x.RoadieId == trackId); .FirstOrDefaultAsync(x => x.RoadieId == trackId)
.ConfigureAwait(false);
if (track == null) if (track == null)
{ {
return new OperationResult<bool>(true, $"Invalid Track Id [{trackId}]"); return new OperationResult<bool>(true, $"Invalid Track Id [{trackId}]");
} }
var userTrack = DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); var userTrack = await DbContext.UserTracks.FirstOrDefaultAsync(x => x.TrackId == track.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userTrack == null) if (userTrack == null)
{ {
userTrack = new data.UserTrack userTrack = new data.UserTrack
@ -641,7 +647,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
TrackId = track.Id TrackId = track.Id
}; };
DbContext.UserTracks.Add(userTrack); await DbContext.UserTracks.AddAsync(userTrack).ConfigureAwait(false);
} }
else else
{ {
@ -649,7 +655,7 @@ namespace Roadie.Api.Services
userTrack.LastUpdated = DateTime.UtcNow; userTrack.LastUpdated = DateTime.UtcNow;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(track.CacheRegion); CacheManager.ClearRegion(track.CacheRegion);
@ -673,17 +679,18 @@ namespace Roadie.Api.Services
var result = false; var result = false;
try try
{ {
var track = DbContext.Tracks var track = await DbContext.Tracks
.Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia)
.Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release)
.Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.ReleaseMedia.Release.Artist)
.Include(x => x.TrackArtist) .Include(x => x.TrackArtist)
.FirstOrDefault(x => x.RoadieId == trackId); .FirstOrDefaultAsync(x => x.RoadieId == trackId)
.ConfigureAwait(false);
if (track == null) if (track == null)
{ {
return new OperationResult<bool>(true, $"Invalid Track Id [{trackId}]"); return new OperationResult<bool>(true, $"Invalid Track Id [{trackId}]");
} }
var userTrack = DbContext.UserTracks.FirstOrDefault(x => x.TrackId == track.Id && x.UserId == user.Id); var userTrack = await DbContext.UserTracks.FirstOrDefaultAsync(x => x.TrackId == track.Id && x.UserId == user.Id).ConfigureAwait(false);
if (userTrack == null) if (userTrack == null)
{ {
userTrack = new data.UserTrack userTrack = new data.UserTrack
@ -692,7 +699,7 @@ namespace Roadie.Api.Services
UserId = user.Id, UserId = user.Id,
TrackId = track.Id TrackId = track.Id
}; };
DbContext.UserTracks.Add(userTrack); await DbContext.UserTracks.AddAsync(userTrack).ConfigureAwait(false);
} }
else else
{ {
@ -700,7 +707,7 @@ namespace Roadie.Api.Services
userTrack.LastUpdated = DateTime.UtcNow; userTrack.LastUpdated = DateTime.UtcNow;
} }
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion); CacheManager.ClearRegion(user.CacheRegion);
CacheManager.ClearRegion(track.CacheRegion); CacheManager.ClearRegion(track.CacheRegion);
@ -724,18 +731,18 @@ namespace Roadie.Api.Services
protected async Task UpdateArtistCounts(int artistId, DateTime now) protected async Task UpdateArtistCounts(int artistId, DateTime now)
{ {
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId); var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.Id == artistId).ConfigureAwait(false);
if (artist != null) if (artist != null)
{ {
artist.ReleaseCount = await DbContext.Releases.Where(x => x.ArtistId == artistId).CountAsync(); artist.ReleaseCount = await DbContext.Releases.Where(x => x.ArtistId == artistId).CountAsync().ConfigureAwait(false);
artist.TrackCount = await (from r in DbContext.Releases artist.TrackCount = await (from r in DbContext.Releases
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId
where tr.ArtistId == artistId || r.ArtistId == artistId where tr.ArtistId == artistId || r.ArtistId == artistId
select tr).CountAsync(); select tr).CountAsync().ConfigureAwait(false);
artist.LastUpdated = now; artist.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
} }
} }
@ -750,10 +757,10 @@ namespace Roadie.Api.Services
join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId
join plt in DbContext.PlaylistTracks on tr.Id equals plt.TrackId join plt in DbContext.PlaylistTracks on tr.Id equals plt.TrackId
where r.ArtistId == artistId where r.ArtistId == artistId
select plt.PlayListId).ToListAsync(); select plt.PlayListId).ToListAsync().ConfigureAwait(false);
foreach (var playlistId in playlistIds.Distinct()) foreach (var playlistId in playlistIds.Distinct())
{ {
await UpdatePlaylistCounts(playlistId, now); await UpdatePlaylistCounts(playlistId, now).ConfigureAwait(false);
} }
} }
@ -768,10 +775,10 @@ namespace Roadie.Api.Services
join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId
join plt in DbContext.PlaylistTracks on tr.Id equals plt.TrackId join plt in DbContext.PlaylistTracks on tr.Id equals plt.TrackId
where r.Id == releaseId where r.Id == releaseId
select plt.PlayListId).ToListAsync(); select plt.PlayListId).ToListAsync().ConfigureAwait(false);
foreach(var playlistId in playlistIds.Distinct()) foreach (var playlistId in playlistIds.Distinct())
{ {
await UpdatePlaylistCounts(playlistId, now); await UpdatePlaylistCounts(playlistId, now).ConfigureAwait(false);
} }
} }
@ -786,11 +793,11 @@ namespace Roadie.Api.Services
join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId join tr in DbContext.Tracks on rm.Id equals tr.ReleaseMediaId
where r.Id == releaseId where r.Id == releaseId
where tr.ArtistId != null where tr.ArtistId != null
select tr.ArtistId.Value).ToListAsync(); select tr.ArtistId.Value).ToListAsync().ConfigureAwait(false);
trackArtistIds.Add(DbContext.Releases.FirstOrDefault(x => x.Id == releaseId).ArtistId); trackArtistIds.Add(DbContext.Releases.FirstOrDefault(x => x.Id == releaseId).ArtistId);
foreach (var artistId in trackArtistIds.Distinct()) foreach (var artistId in trackArtistIds.Distinct())
{ {
await UpdateArtistCounts(artistId, now); await UpdateArtistCounts(artistId, now).ConfigureAwait(false);
} }
} }
@ -802,15 +809,15 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var artist = DbContext.Artists.FirstOrDefault(x => x.Id == artistId); var artist = await DbContext.Artists.FirstOrDefaultAsync(x => x.Id == artistId).ConfigureAwait(false);
if (artist != null) if (artist != null)
{ {
if (updateReleaseRanks) if (updateReleaseRanks)
{ {
var artistReleaseIds = await DbContext.Releases.Where(x => x.ArtistId == artistId).Select(x => x.Id).ToArrayAsync(); var artistReleaseIds = await DbContext.Releases.Where(x => x.ArtistId == artistId).Select(x => x.Id).ToArrayAsync().ConfigureAwait(false);
foreach (var artistReleaseId in artistReleaseIds) foreach (var artistReleaseId in artistReleaseIds)
{ {
await UpdateReleaseRank(artistReleaseId, false); await UpdateReleaseRank(artistReleaseId, false).ConfigureAwait(false);
} }
} }
@ -818,20 +825,20 @@ namespace Roadie.Api.Services
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join ut in DbContext.UserTracks on t.Id equals ut.TrackId join ut in DbContext.UserTracks on t.Id equals ut.TrackId
where t.ArtistId == artist.Id where t.ArtistId == artist.Id
select ut.Rating).Select(x => (decimal?)x).ToArrayAsync()).Average(); select ut.Rating).Select(x => (decimal?)x).ToArrayAsync().ConfigureAwait(false)).Average();
var artistReleaseRatingRating = (await (from r in DbContext.Releases var artistReleaseRatingRating = (await (from r in DbContext.Releases
join ur in DbContext.UserReleases on r.Id equals ur.ReleaseId join ur in DbContext.UserReleases on r.Id equals ur.ReleaseId
where r.ArtistId == artist.Id where r.ArtistId == artist.Id
select ur.Rating).Select(x => (decimal?)x).ToArrayAsync()).Average(); select ur.Rating).Select(x => (decimal?)x).ToArrayAsync().ConfigureAwait(false)).Average();
var artistReleaseRankSum = (await (from r in DbContext.Releases var artistReleaseRankSum = (await (from r in DbContext.Releases
where r.ArtistId == artist.Id where r.ArtistId == artist.Id
select r.Rank).ToArrayAsync()).Sum(x => x) ?? 0; select r.Rank).ToArrayAsync().ConfigureAwait(false)).Sum(x => x) ?? 0;
artist.Rank = SafeParser.ToNumber<decimal>(artistTrackAverage + artistReleaseRatingRating) + artistReleaseRankSum + artist.Rating; artist.Rank = SafeParser.ToNumber<decimal>(artistTrackAverage + artistReleaseRatingRating) + artistReleaseRankSum + artist.Rating;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(artist.CacheRegion); CacheManager.ClearRegion(artist.CacheRegion);
Logger.LogTrace("UpdatedArtistRank For Artist `{0}`", artist); Logger.LogTrace("UpdatedArtistRank For Artist `{0}`", artist);
} }
@ -858,74 +865,74 @@ namespace Roadie.Api.Services
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == release.Id where rm.ReleaseId == release.Id
where t.ArtistId.HasValue where t.ArtistId.HasValue
select t.ArtistId.Value).ToArrayAsync(); select t.ArtistId.Value).ToArrayAsync().ConfigureAwait(false);
artistsForRelease.AddRange(trackArtistsForRelease); artistsForRelease.AddRange(trackArtistsForRelease);
foreach (var artistId in artistsForRelease.Distinct()) foreach (var artistId in artistsForRelease.Distinct())
{ {
await UpdateArtistRank(artistId); await UpdateArtistRank(artistId).ConfigureAwait(false);
} }
} }
} }
protected async Task UpdateLabelCounts(int labelId, DateTime now) protected async Task UpdateLabelCounts(int labelId, DateTime now)
{ {
var label = DbContext.Labels.FirstOrDefault(x => x.Id == labelId); var label = await DbContext.Labels.FirstOrDefaultAsync(x => x.Id == labelId).ConfigureAwait(false);
if (label != null) if (label != null)
{ {
label.ReleaseCount = await DbContext.ReleaseLabels.Where(x => x.LabelId == label.Id).CountAsync(); label.ReleaseCount = await DbContext.ReleaseLabels.Where(x => x.LabelId == label.Id).CountAsync().ConfigureAwait(false);
label.ArtistCount = await (from r in DbContext.Releases label.ArtistCount = await (from r in DbContext.Releases
join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId
join a in DbContext.Artists on r.ArtistId equals a.Id join a in DbContext.Artists on r.ArtistId equals a.Id
where rl.LabelId == label.Id where rl.LabelId == label.Id
group a by a.Id group a by a.Id
into artists into artists
select artists).Select(x => x.Key).CountAsync(); select artists).Select(x => x.Key).CountAsync().ConfigureAwait(false);
label.TrackCount = await (from r in DbContext.Releases label.TrackCount = await (from r in DbContext.Releases
join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId join rl in DbContext.ReleaseLabels on r.Id equals rl.ReleaseId
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
where rl.LabelId == label.Id where rl.LabelId == label.Id
select t).CountAsync(); select t).CountAsync().ConfigureAwait(false);
label.LastUpdated = now; label.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(label.CacheRegion); CacheManager.ClearRegion(label.CacheRegion);
} }
} }
protected async Task UpdatePlaylistCounts(int playlistId, DateTime now) protected async Task UpdatePlaylistCounts(int playlistId, DateTime now)
{ {
var playlist = DbContext.Playlists.FirstOrDefault(x => x.Id == playlistId); var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.Id == playlistId).ConfigureAwait(false);
if (playlist != null) if (playlist != null)
{ {
var playlistTracks = await DbContext.PlaylistTracks var playlistTracks = await DbContext.PlaylistTracks
.Include(x => x.Track) .Include(x => x.Track)
.Include("Track.ReleaseMedia") .Include("Track.ReleaseMedia")
.Where(x => x.PlayListId == playlist.Id).ToArrayAsync(); .Where(x => x.PlayListId == playlist.Id).ToArrayAsync().ConfigureAwait(false);
playlist.TrackCount = (short)playlistTracks.Count(); playlist.TrackCount = (short)playlistTracks.Length;
playlist.Duration = playlistTracks.Sum(x => x.Track.Duration); playlist.Duration = playlistTracks.Sum(x => x.Track.Duration);
playlist.ReleaseCount = (short)playlistTracks.Select(x => x.Track.ReleaseMedia.ReleaseId).Distinct().Count(); playlist.ReleaseCount = (short)playlistTracks.Select(x => x.Track.ReleaseMedia.ReleaseId).Distinct().Count();
playlist.LastUpdated = now; playlist.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(playlist.CacheRegion); CacheManager.ClearRegion(playlist.CacheRegion);
} }
} }
protected async Task UpdateReleaseCounts(int releaseId, DateTime now) protected async Task UpdateReleaseCounts(int releaseId, DateTime now)
{ {
var release = DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); var release = await DbContext.Releases.FirstOrDefaultAsync(x => x.Id == releaseId).ConfigureAwait(false);
if (release != null) if (release != null)
{ {
release.PlayedCount = await (from t in DbContext.Tracks release.PlayedCount = await (from t in DbContext.Tracks
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == releaseId where rm.ReleaseId == releaseId
where t.PlayedCount.HasValue where t.PlayedCount.HasValue
select t).SumAsync(x => x.PlayedCount); select t).SumAsync(x => x.PlayedCount).ConfigureAwait(false);
release.Duration = await (from t in DbContext.Tracks release.Duration = await (from t in DbContext.Tracks
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
where rm.ReleaseId == releaseId where rm.ReleaseId == releaseId
select t).SumAsync(x => x.Duration); select t).SumAsync(x => x.Duration).ConfigureAwait(false);
release.LastUpdated = now; release.LastUpdated = now;
await DbContext.SaveChangesAsync(); await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(release.CacheRegion); CacheManager.ClearRegion(release.CacheRegion);
} }
} }
@ -939,7 +946,7 @@ namespace Roadie.Api.Services
{ {
try try
{ {
var release = DbContext.Releases.FirstOrDefault(x => x.Id == releaseId); var release = await DbContext.Releases.FirstOrDefaultAsync(x => x.Id == releaseId).ConfigureAwait(false);
if (release != null) if (release != null)
{ {
var releaseTrackAverage = (await (from ut in DbContext.UserTracks var releaseTrackAverage = (await (from ut in DbContext.UserTracks

View file

@ -26,7 +26,39 @@ namespace Roadie.Api.Services
{ {
} }
public Task<OperationResult<LibraryStats>> LibraryStatistics() public Task<OperationResult<IEnumerable<DateAndCount>>> ArtistsByDateAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.Artists
orderby r.CreatedDate
select r.CreatedDate)
.ToArray()
.GroupBy(x => x.ToString("yyyy-MM-dd"))
.Select(x => new
{
date = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.date,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<LibraryStats>> LibraryStatisticsAsync()
{ {
LibraryStats result = null; LibraryStats result = null;
var sw = new Stopwatch(); var sw = new Stopwatch();
@ -65,39 +97,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DateAndCount>>> ArtistsByDate() public Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDateAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.Artists
orderby r.CreatedDate
select r.CreatedDate)
.ToArray()
.GroupBy(x => x.ToString("yyyy-MM-dd"))
.Select(x => new
{
date = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.date,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDate()
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -129,71 +129,7 @@ namespace Roadie.Api.Services
}); });
} }
public Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByUser() public Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDecadeAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.UserTracks
join u in DbContext.Users on r.UserId equals u.Id
select new { u.UserName, r.PlayedCount })
.ToArray()
.GroupBy(x => x.UserName)
.Select(x => new
{
username = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.username,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByDate()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.UserTracks
orderby r.LastPlayed
select r.LastPlayed ?? r.CreatedDate)
.ToArray()
.GroupBy(x => x.ToString("yyyy-MM-dd"))
.Select(x => new
{
date = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.date,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDecade()
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -235,5 +171,69 @@ namespace Roadie.Api.Services
Data = result Data = result
}); });
} }
public Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByDateAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.UserTracks
orderby r.LastPlayed
select r.LastPlayed ?? r.CreatedDate)
.ToArray()
.GroupBy(x => x.ToString("yyyy-MM-dd"))
.Select(x => new
{
date = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.date,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByUserAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.UserTracks
join u in DbContext.Users on r.UserId equals u.Id
select new { u.UserName, r.PlayedCount })
.ToArray()
.GroupBy(x => x.UserName)
.Select(x => new
{
username = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.username,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -20,11 +20,11 @@ namespace Roadie.Api.Services
_configuration = configuration; _configuration = configuration;
} }
public async Task<string> GenerateToken(User user, UserManager<User> userManager) public async Task<string> GenerateTokenAsync(User user, UserManager<User> userManager)
{ {
var utcNow = DateTime.UtcNow; var utcNow = DateTime.UtcNow;
var roles = await userManager.GetRolesAsync(user); var roles = await userManager.GetRolesAsync(user).ConfigureAwait(false);
var userRoles = roles.Select(r => new Claim(ClaimTypes.Role, r)).ToArray(); var userRoles = roles.Select(r => new Claim(ClaimTypes.Role, r)).ToArray();
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();

View file

@ -37,9 +37,25 @@ namespace Roadie.Api.Services
private IBookmarkService BookmarkService { get; } private IBookmarkService BookmarkService { get; }
public TrackService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext, public TrackService(
IRoadieDbContext dbContext, ICacheManager cacheManager, ILogger<TrackService> logger, IRoadieSettings configuration,
IBookmarkService bookmarkService, IAdminService adminService, IAudioMetaDataHelper audioMetaDataHelper) IRoadieDbContext dbContext,
ICacheManager cacheManager,
ILogger logger)
: base(configuration, null, dbContext, cacheManager, logger, null)
{
}
public TrackService(
IRoadieSettings configuration,
IHttpEncoder httpEncoder,
IHttpContext httpContext,
IRoadieDbContext dbContext,
ICacheManager cacheManager,
ILogger<TrackService> logger,
IBookmarkService bookmarkService,
IAdminService adminService,
IAudioMetaDataHelper audioMetaDataHelper)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext) : base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
{ {
BookmarkService = bookmarkService; BookmarkService = bookmarkService;
@ -47,82 +63,167 @@ namespace Roadie.Api.Services
AdminService = adminService; AdminService = adminService;
} }
public TrackService(IRoadieSettings configuration, IRoadieDbContext dbContext, ICacheManager cacheManager, ILogger logger) private async Task<OperationResult<Track>> TrackByIdActionAsync(Guid id, IEnumerable<string> includes)
: base(configuration, null, dbContext, cacheManager, logger, null)
{
}
public static long DetermineByteEndFromHeaders(IHeaderDictionary headers, long fileLength)
{
var defaultFileLength = fileLength - 1;
if (headers?.Any(x => x.Key == "Range") != true)
{
return defaultFileLength;
}
long? result = null;
var rangeHeader = headers["Range"];
string rangeEnd = null;
var rangeBegin = rangeHeader.FirstOrDefault();
if (!string.IsNullOrEmpty(rangeBegin))
{
//bytes=0-
rangeBegin = rangeBegin.Replace("bytes=", "");
var parts = rangeBegin.Split('-');
rangeBegin = parts[0];
if (parts.Length > 1)
{
rangeEnd = parts[1];
}
if (!string.IsNullOrEmpty(rangeEnd))
{
result = long.TryParse(rangeEnd, out var outValue) ? (int?)outValue : null;
}
}
return result ?? defaultFileLength;
}
public static long DetermineByteStartFromHeaders(IHeaderDictionary headers)
{
if (headers?.Any(x => x.Key == "Range") != true)
{
return 0;
}
long result = 0;
var rangeHeader = headers["Range"];
var rangeBegin = rangeHeader.FirstOrDefault();
if (!string.IsNullOrEmpty(rangeBegin))
{
//bytes=0-
rangeBegin = rangeBegin.Replace("bytes=", "");
var parts = rangeBegin.Split('-');
rangeBegin = parts[0];
if (!string.IsNullOrEmpty(rangeBegin))
{
long.TryParse(rangeBegin, out result);
}
}
return result;
}
public async Task<OperationResult<Track>> ById(User roadieUser, Guid id, IEnumerable<string> includes)
{ {
var timings = new Dictionary<string, long>(); var timings = new Dictionary<string, long>();
var tsw = new Stopwatch(); var tsw = new Stopwatch();
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
var cacheKey = string.Format("urn:track_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
tsw.Restart();
var track = await GetTrack(id).ConfigureAwait(false);
tsw.Stop();
timings.Add("getTrack", tsw.ElapsedMilliseconds);
if (track == null)
{
return new OperationResult<Track>(true, $"Track Not Found [{id}]");
}
tsw.Restart();
var result = track.Adapt<Track>();
result.IsLocked = (track.IsLocked ?? false) ||
(track.ReleaseMedia.IsLocked ?? false) ||
(track.ReleaseMedia.Release.IsLocked ?? false) ||
(track.ReleaseMedia.Release.Artist.IsLocked ?? false);
result.Thumbnail = ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, id);
result.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "track", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
result.ReleaseMediaId = track.ReleaseMedia.RoadieId.ToString();
result.Artist = ArtistList.FromDataArtist(track.ReleaseMedia.Release.Artist,
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId));
result.ArtistThumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId);
result.Release = ReleaseList.FromDataRelease(track.ReleaseMedia.Release, track.ReleaseMedia.Release.Artist,
HttpContext.BaseUrl, ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId));
result.ReleaseThumbnail = ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId);
tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds);
if (track.ArtistId.HasValue)
{
tsw.Restart();
var trackArtist = DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId);
if (trackArtist == null)
{
Logger.LogWarning($"Unable to find Track Artist [{track.ArtistId}");
}
else
{
result.TrackArtist =
ArtistList.FromDataArtist(trackArtist, ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, trackArtist.RoadieId));
result.TrackArtistToken = result.TrackArtist.Artist;
result.TrackArtistThumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, trackArtist.RoadieId);
}
tsw.Stop();
timings.Add("trackArtist", tsw.ElapsedMilliseconds);
}
if (includes?.Any() == true)
{
if (includes.Contains("credits"))
{
tsw.Restart();
result.Credits = (await (from c in DbContext.Credits
join cc in DbContext.CreditCategory on c.CreditCategoryId equals cc.Id
join a in DbContext.Artists on c.ArtistId equals a.Id into agg
from a in agg.DefaultIfEmpty()
where c.TrackId == track.Id
select new { c, cc, a })
.ToListAsync().ConfigureAwait(false))
.Select(x => new CreditList
{
Id = x.c.RoadieId,
Artist = x.a == null ? null : ArtistList.FromDataArtist(x.a, ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, x.a.RoadieId)),
Category = new DataToken
{
Text = x.cc.Name,
Value = x.cc.RoadieId.ToString()
},
CreditName = x.a?.Name ?? x.c.CreditToName,
Description = x.c.Description
}).ToArray();
tsw.Stop();
timings.Add("credits", tsw.ElapsedMilliseconds);
}
if (includes.Contains("stats"))
{
tsw.Restart();
result.Statistics = new TrackStatistics
{
FileSizeFormatted = ((long?)track.FileSize).ToFileSize(),
Time = new TimeInfo((decimal)track.Duration).ToFullFormattedString(),
PlayedCount = track.PlayedCount
};
var userTracks = (from t in DbContext.Tracks
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
where t.Id == track.Id
select ut).ToArray();
if (userTracks?.Any() == true)
{
result.Statistics.DislikedCount = userTracks.Count(x => x.IsDisliked ?? false);
result.Statistics.FavoriteCount = userTracks.Count(x => x.IsFavorite ?? false);
}
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);
}
if (includes.Contains("comments"))
{
tsw.Restart();
var trackComments = DbContext.Comments.Include(x => x.User).Where(x => x.TrackId == track.Id)
.OrderByDescending(x => x.CreatedDate).ToArray();
if (trackComments.Length > 0)
{
var comments = new List<Comment>();
var commentIds = trackComments.Select(x => x.Id).ToArray();
var userCommentReactions = (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
select cr).ToArray();
foreach (var trackComment in trackComments)
{
var comment = trackComment.Adapt<Comment>();
comment.DatabaseId = trackComment.Id;
comment.User = UserList.FromDataUser(trackComment.User,
ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, trackComment.User.RoadieId));
comment.DislikedCount = userCommentReactions.Count(x =>
x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Dislike);
comment.LikedCount = userCommentReactions.Count(x =>
x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Like);
comments.Add(comment);
}
result.Comments = comments;
}
tsw.Stop();
timings.Add("comments", tsw.ElapsedMilliseconds);
}
}
sw.Stop();
Logger.LogInformation($"ByIdAction: Track `{ track }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]");
return new OperationResult<Track>
{
Data = result,
IsSuccess = result != null,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<Track>> ByIdAsyncAsync(User roadieUser, Guid id, IEnumerable<string> includes)
{
var timings = new Dictionary<string, long>();
var tsw = new Stopwatch();
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = $"urn:track_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
var result = await CacheManager.GetAsync(cacheKey, async () => var result = await CacheManager.GetAsync(cacheKey, async () =>
{ {
tsw.Restart(); tsw.Restart();
var rr = await TrackByIdAction(id, includes).ConfigureAwait(false); var rr = await TrackByIdActionAsync(id, includes).ConfigureAwait(false);
tsw.Stop(); tsw.Stop();
timings.Add("TrackByIdAction", tsw.ElapsedMilliseconds); timings.Add(nameof(TrackByIdActionAsync), tsw.ElapsedMilliseconds);
return rr; return rr;
}, data.Track.CacheRegionUrn(id)).ConfigureAwait(false); }, data.Track.CacheRegionUrn(id)).ConfigureAwait(false);
if (result?.Data != null && roadieUser != null) if (result?.Data != null && roadieUser != null)
@ -140,7 +241,7 @@ namespace Roadie.Api.Services
result.Data.TrackPlayUrl = MakeTrackPlayUrl(user, HttpContext.BaseUrl, track.RoadieId); result.Data.TrackPlayUrl = MakeTrackPlayUrl(user, HttpContext.BaseUrl, track.RoadieId);
tsw.Restart(); tsw.Restart();
var userBookmarkResult = await BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Track).ConfigureAwait(false); var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Track).ConfigureAwait(false);
if (userBookmarkResult.IsSuccess) if (userBookmarkResult.IsSuccess)
{ {
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == track?.RoadieId.ToString()) != null; result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == track?.RoadieId.ToString()) != null;
@ -195,7 +296,64 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<Library.Models.Pagination.PagedResult<TrackList>> List(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null) public static long DetermineByteEndFromHeaders(IHeaderDictionary headers, long fileLength)
{
var defaultFileLength = fileLength - 1;
if (headers?.Any(x => x.Key == "Range") != true)
{
return defaultFileLength;
}
long? result = null;
var rangeHeader = headers["Range"];
string rangeEnd = null;
var rangeBegin = rangeHeader.FirstOrDefault();
if (!string.IsNullOrEmpty(rangeBegin))
{
//bytes=0-
rangeBegin = rangeBegin.Replace("bytes=", string.Empty);
var parts = rangeBegin.Split('-');
rangeBegin = parts[0];
if (parts.Length > 1)
{
rangeEnd = parts[1];
}
if (!string.IsNullOrEmpty(rangeEnd))
{
result = long.TryParse(rangeEnd, out var outValue) ? (int?)outValue : null;
}
}
return result ?? defaultFileLength;
}
public static long DetermineByteStartFromHeaders(IHeaderDictionary headers)
{
if (headers?.Any(x => x.Key == "Range") != true)
{
return 0;
}
long result = 0;
var rangeHeader = headers["Range"];
var rangeBegin = rangeHeader.FirstOrDefault();
if (!string.IsNullOrEmpty(rangeBegin))
{
//bytes=0-
rangeBegin = rangeBegin.Replace("bytes=", string.Empty);
var parts = rangeBegin.Split('-');
rangeBegin = parts[0];
if (!string.IsNullOrEmpty(rangeBegin))
{
long.TryParse(rangeBegin, out result);
}
}
return result;
}
public async Task<Library.Models.Pagination.PagedResult<TrackList>> ListAsync(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null)
{ {
try try
{ {
@ -285,7 +443,7 @@ namespace Roadie.Api.Services
if (doRandomize ?? false) if (doRandomize ?? false)
{ {
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.LimitValue; var randomLimit = roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomTrackData = await DbContext.RandomTrackIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly).ConfigureAwait(false); randomTrackData = await DbContext.RandomTrackIdsAsync(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly).ConfigureAwait(false);
randomTrackIds = randomTrackData.Select(x => x.Value).ToArray(); randomTrackIds = randomTrackData.Select(x => x.Value).ToArray();
rowCount = DbContext.Releases.Count(); rowCount = DbContext.Releases.Count();
} }
@ -450,7 +608,7 @@ namespace Roadie.Api.Services
if (!string.IsNullOrEmpty(request.FilterValue) && request.FilterValue.StartsWith("#")) if (!string.IsNullOrEmpty(request.FilterValue) && request.FilterValue.StartsWith("#"))
{ {
// Find any releases by tags // Find any releases by tags
var tagValue = request.FilterValue.Replace("#", ""); var tagValue = request.FilterValue.Replace("#", string.Empty);
resultQuery = resultQuery.Where(x => x.ti.Tags != null && x.ti.Tags.Contains(tagValue)); resultQuery = resultQuery.Where(x => x.ti.Tags != null && x.ti.Tags.Contains(tagValue));
} }
@ -649,7 +807,7 @@ namespace Roadie.Api.Services
var track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == id); var track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == id);
if (track == null) if (track == null)
{ {
return new OperationResult<Track>(true, string.Format("Track Not Found [{0}]", id)); return new OperationResult<Track>(true, $"Track Not Found [{id}]");
} }
return new OperationResult<Track> return new OperationResult<Track>
@ -659,7 +817,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<TrackStreamInfo>> TrackStreamInfo(Guid trackId, long beginBytes, long endBytes, User roadieUser) public async Task<OperationResult<TrackStreamInfo>> TrackStreamInfoAsync(Guid trackId, long beginBytes, long endBytes, User roadieUser)
{ {
var track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); var track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId);
if (!(track?.IsValid ?? true)) if (!(track?.IsValid ?? true))
@ -671,7 +829,7 @@ namespace Roadie.Api.Services
select r).FirstOrDefault(); select r).FirstOrDefault();
if (!release.IsLocked ?? false && roadieUser != null) if (!release.IsLocked ?? false && roadieUser != null)
{ {
await AdminService.ScanRelease(new Library.Identity.User await AdminService.ScanReleaseAsync(new Library.Identity.User
{ {
Id = roadieUser.Id.Value Id = roadieUser.Id.Value
}, release.RoadieId, false, true).ConfigureAwait(false); }, release.RoadieId, false, true).ConfigureAwait(false);
@ -710,7 +868,7 @@ namespace Roadie.Api.Services
select r).FirstOrDefault(); select r).FirstOrDefault();
if (!release.IsLocked ?? false && roadieUser != null) if (!release.IsLocked ?? false && roadieUser != null)
{ {
await AdminService.ScanRelease(new Library.Identity.User await AdminService.ScanReleaseAsync(new Library.Identity.User
{ {
Id = roadieUser.Id.Value Id = roadieUser.Id.Value
}, release.RoadieId, false, true).ConfigureAwait(false); }, release.RoadieId, false, true).ConfigureAwait(false);
@ -788,7 +946,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> UpdateTrack(User user, Track model) public async Task<OperationResult<bool>> UpdateTrackAsync(User user, Track model)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -800,7 +958,7 @@ namespace Roadie.Api.Services
.FirstOrDefault(x => x.RoadieId == model.Id); .FirstOrDefault(x => x.RoadieId == model.Id);
if (track == null) if (track == null)
{ {
return new OperationResult<bool>(true, string.Format("Track Not Found [{0}]", model.Id)); return new OperationResult<bool>(true, $"Track Not Found [{model.Id}]");
} }
try try
@ -938,152 +1096,5 @@ namespace Roadie.Api.Services
Errors = errors Errors = errors
}; };
} }
private async Task<OperationResult<Track>> TrackByIdAction(Guid id, IEnumerable<string> includes)
{
var timings = new Dictionary<string, long>();
var tsw = new Stopwatch();
var sw = Stopwatch.StartNew();
sw.Start();
tsw.Restart();
var track = await GetTrack(id).ConfigureAwait(false);
tsw.Stop();
timings.Add("getTrack", tsw.ElapsedMilliseconds);
if (track == null)
{
return new OperationResult<Track>(true, string.Format("Track Not Found [{0}]", id));
}
tsw.Restart();
var result = track.Adapt<Track>();
result.IsLocked = (track.IsLocked ?? false) ||
(track.ReleaseMedia.IsLocked ?? false) ||
(track.ReleaseMedia.Release.IsLocked ?? false) ||
(track.ReleaseMedia.Release.Artist.IsLocked ?? false);
result.Thumbnail = ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, id);
result.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "track", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
result.ReleaseMediaId = track.ReleaseMedia.RoadieId.ToString();
result.Artist = ArtistList.FromDataArtist(track.ReleaseMedia.Release.Artist,
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId));
result.ArtistThumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId);
result.Release = ReleaseList.FromDataRelease(track.ReleaseMedia.Release, track.ReleaseMedia.Release.Artist,
HttpContext.BaseUrl, ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId));
result.ReleaseThumbnail = ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, track.ReleaseMedia.Release.RoadieId);
tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds);
if (track.ArtistId.HasValue)
{
tsw.Restart();
var trackArtist = DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId);
if (trackArtist == null)
{
Logger.LogWarning($"Unable to find Track Artist [{track.ArtistId}");
}
else
{
result.TrackArtist =
ArtistList.FromDataArtist(trackArtist, ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, trackArtist.RoadieId));
result.TrackArtistToken = result.TrackArtist.Artist;
result.TrackArtistThumbnail = ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, trackArtist.RoadieId);
}
tsw.Stop();
timings.Add("trackArtist", tsw.ElapsedMilliseconds);
}
if (includes?.Any() == true)
{
if (includes.Contains("credits"))
{
tsw.Restart();
result.Credits = (await (from c in DbContext.Credits
join cc in DbContext.CreditCategory on c.CreditCategoryId equals cc.Id
join a in DbContext.Artists on c.ArtistId equals a.Id into agg
from a in agg.DefaultIfEmpty()
where c.TrackId == track.Id
select new { c, cc, a })
.ToListAsync().ConfigureAwait(false))
.Select(x => new CreditList
{
Id = x.c.RoadieId,
Artist = x.a == null ? null : ArtistList.FromDataArtist(x.a, ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, x.a.RoadieId)),
Category = new DataToken
{
Text = x.cc.Name,
Value = x.cc.RoadieId.ToString()
},
CreditName = x.a?.Name ?? x.c.CreditToName,
Description = x.c.Description
}).ToArray();
tsw.Stop();
timings.Add("credits", tsw.ElapsedMilliseconds);
}
if (includes.Contains("stats"))
{
tsw.Restart();
result.Statistics = new TrackStatistics
{
FileSizeFormatted = ((long?)track.FileSize).ToFileSize(),
Time = new TimeInfo((decimal)track.Duration).ToFullFormattedString(),
PlayedCount = track.PlayedCount
};
var userTracks = (from t in DbContext.Tracks
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
where t.Id == track.Id
select ut).ToArray();
if (userTracks?.Any() == true)
{
result.Statistics.DislikedCount = userTracks.Count(x => x.IsDisliked ?? false);
result.Statistics.FavoriteCount = userTracks.Count(x => x.IsFavorite ?? false);
}
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);
}
if (includes.Contains("comments"))
{
tsw.Restart();
var trackComments = DbContext.Comments.Include(x => x.User).Where(x => x.TrackId == track.Id)
.OrderByDescending(x => x.CreatedDate).ToArray();
if (trackComments.Length > 0)
{
var comments = new List<Comment>();
var commentIds = trackComments.Select(x => x.Id).ToArray();
var userCommentReactions = (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
select cr).ToArray();
foreach (var trackComment in trackComments)
{
var comment = trackComment.Adapt<Comment>();
comment.DatabaseId = trackComment.Id;
comment.User = UserList.FromDataUser(trackComment.User,
ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, trackComment.User.RoadieId));
comment.DislikedCount = userCommentReactions.Count(x =>
x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Dislike);
comment.LikedCount = userCommentReactions.Count(x =>
x.CommentId == trackComment.Id && x.ReactionValue == CommentReaction.Like);
comments.Add(comment);
}
result.Comments = comments;
}
tsw.Stop();
timings.Add("comments", tsw.ElapsedMilliseconds);
}
}
sw.Stop();
Logger.LogInformation($"ByIdAction: Track `{ track }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]");
return new OperationResult<Track>
{
Data = result,
IsSuccess = result != null,
OperationTime = sw.ElapsedMilliseconds
};
}
} }
} }

View file

@ -50,7 +50,179 @@ namespace Roadie.Api.Services
LastFmHelper = lastFmHelper; LastFmHelper = lastFmHelper;
} }
public async Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes, bool isAccountSettingsEdit = false) private async Task<OperationResult<bool>> SetBookmark(Library.Identity.User user, BookmarkType bookmarktype, int bookmarkTargetId, bool isBookmarked)
{
var bookmark = DbContext.Bookmarks.FirstOrDefault(x => x.BookmarkTargetId == bookmarkTargetId &&
x.BookmarkType == bookmarktype &&
x.UserId == user.Id);
if (!isBookmarked)
{
// Remove bookmark
if (bookmark != null)
{
DbContext.Bookmarks.Remove(bookmark);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
else
{
// Add bookmark
if (bookmark == null)
{
await DbContext.Bookmarks.AddAsync(new data.Bookmark
{
UserId = user.Id,
BookmarkTargetId = bookmarkTargetId,
BookmarkType = bookmarktype,
CreatedDate = DateTime.UtcNow,
Status = Statuses.Ok
}).ConfigureAwait(false);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
CacheManager.ClearRegion(user.CacheRegion);
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
private async Task<OperationResult<bool>> UpdateLastFMSessionKey(Library.Identity.User user, string token)
{
var lastFmSessionKeyResult = await LastFmHelper.GetSessionKeyForUserToken(token).ConfigureAwait(false);
if (!lastFmSessionKeyResult.IsSuccess)
{
return new OperationResult<bool>(false, $"Unable to Get LastFM Session Key For Token [{token}]");
}
// Check concurrency stamp
if (user.ConcurrencyStamp != user.ConcurrencyStamp)
{
return new OperationResult<bool>
{
Errors = new List<Exception> { new Exception("User data is stale.") }
};
}
user.LastFMSessionKey = lastFmSessionKeyResult.Data;
user.LastUpdated = DateTime.UtcNow;
user.ConcurrencyStamp = Guid.NewGuid().ToString();
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(Library.Identity.User.CacheRegionUrn(user.RoadieId));
Logger.LogTrace($"User `{user}` Updated LastFm SessionKey");
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
private async Task<OperationResult<User>> UserByIdAction(Guid id, IEnumerable<string> includes)
{
var timings = new Dictionary<string, long>();
var tsw = new Stopwatch();
tsw.Restart();
var user = await GetUser(id).ConfigureAwait(false);
tsw.Stop();
timings.Add("getUser", tsw.ElapsedMilliseconds);
if (user == null)
{
return new OperationResult<User>(true, $"User Not Found [{id}]");
}
tsw.Restart();
var model = user.Adapt<User>();
model.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "user", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
model.IsAdmin = user.UserRoles?.Any(x => x.Role?.NormalizedName == "ADMIN") ?? false;
model.IsEditor = model.IsAdmin || (user.UserRoles?.Any(x => x.Role?.NormalizedName == "EDITOR") ?? false);
tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds);
if (includes?.Any() == true && includes.Contains("stats"))
{
tsw.Restart();
var userArtists = DbContext.UserArtists.Include(x => x.Artist).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserArtist[0];
var userReleases = DbContext.UserReleases.Include(x => x.Release).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserRelease[0];
var userTracks = DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserTrack[0];
var mostPlayedArtist = await DbContext.MostPlayedArtist(user.Id).ConfigureAwait(false);
var mostPlayedRelease = await DbContext.MostPlayedRelease(user.Id).ConfigureAwait(false);
var lastPlayedTrack = await GetTrack((await DbContext.MostPlayedTrack(user.Id).ConfigureAwait(false))?.RoadieId ?? Guid.Empty).ConfigureAwait(false);
var mostPlayedTrack = await GetTrack((await DbContext.LastPlayedTrack(user.Id).ConfigureAwait(false))?.RoadieId ?? Guid.Empty).ConfigureAwait(false);
model.Statistics = new UserStatistics
{
LastPlayedTrack = lastPlayedTrack == null
? null
: models.TrackList.FromDataTrack(
MakeTrackPlayUrl(user, HttpContext.BaseUrl, lastPlayedTrack.RoadieId),
lastPlayedTrack,
lastPlayedTrack.ReleaseMedia.MediaNumber,
lastPlayedTrack.ReleaseMedia.Release,
lastPlayedTrack.ReleaseMedia.Release.Artist,
lastPlayedTrack.TrackArtist,
HttpContext.BaseUrl,
ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, lastPlayedTrack.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, lastPlayedTrack.ReleaseMedia.Release.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, lastPlayedTrack.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, lastPlayedTrack.TrackArtist == null
? null
: (Guid?)lastPlayedTrack.TrackArtist.RoadieId)),
MostPlayedArtist = mostPlayedArtist == null
? null
: models.ArtistList.FromDataArtist(mostPlayedArtist,
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedArtist.RoadieId)),
MostPlayedRelease = mostPlayedRelease == null
? null
: ReleaseList.FromDataRelease(mostPlayedRelease,
mostPlayedRelease.Artist,
HttpContext.BaseUrl,
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedRelease.Artist.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, mostPlayedRelease.RoadieId)),
MostPlayedTrack = mostPlayedTrack == null
? null
: models.TrackList.FromDataTrack(
MakeTrackPlayUrl(user, HttpContext.BaseUrl, mostPlayedTrack.RoadieId),
mostPlayedTrack,
mostPlayedTrack.ReleaseMedia.MediaNumber,
mostPlayedTrack.ReleaseMedia.Release,
mostPlayedTrack.ReleaseMedia.Release.Artist,
mostPlayedTrack.TrackArtist,
HttpContext.BaseUrl,
ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, mostPlayedTrack.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, mostPlayedTrack.ReleaseMedia.Release.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedTrack.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedTrack.TrackArtist == null
? null
: (Guid?)mostPlayedTrack.TrackArtist.RoadieId)),
RatedArtists = userArtists.Count(x => x.Rating > 0),
FavoritedArtists = userArtists.Count(x => x.IsFavorite ?? false),
DislikedArtists = userArtists.Count(x => x.IsDisliked ?? false),
RatedReleases = userReleases.Count(x => x.Rating > 0),
FavoritedReleases = userReleases.Count(x => x.IsFavorite ?? false),
DislikedReleases = userReleases.Count(x => x.IsDisliked ?? false),
RatedTracks = userTracks.Count(x => x.Rating > 0),
PlayedTracks = userTracks.Where(x => x.PlayedCount.HasValue).Select(x => x.PlayedCount).Sum(),
FavoritedTracks = userTracks.Count(x => x.IsFavorite ?? false),
DislikedTracks = userTracks.Count(x => x.IsDisliked ?? false)
};
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);
}
Logger.LogInformation($"ByIdAction: User `{ user }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]");
return new OperationResult<User>
{
IsSuccess = true,
Data = model
};
}
public async Task<OperationResult<User>> ByIdAsync(User user, Guid id, IEnumerable<string> includes, bool isAccountSettingsEdit = false)
{ {
if (isAccountSettingsEdit && user.UserId != id && !user.IsAdmin) if (isAccountSettingsEdit && user.UserId != id && !user.IsAdmin)
{ {
@ -61,7 +233,7 @@ namespace Roadie.Api.Services
} }
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
sw.Start(); sw.Start();
var cacheKey = string.Format("urn:user_by_id_operation:{0}:{1}", id, isAccountSettingsEdit); var cacheKey = $"urn:user_by_id_operation:{id}:{isAccountSettingsEdit}";
var result = await CacheManager.GetAsync(cacheKey, async () => await UserByIdAction(id, includes).ConfigureAwait(false), Library.Identity.User.CacheRegionUrn(id)).ConfigureAwait(false); var result = await CacheManager.GetAsync(cacheKey, async () => await UserByIdAction(id, includes).ConfigureAwait(false), Library.Identity.User.CacheRegionUrn(id)).ConfigureAwait(false);
sw.Stop(); sw.Stop();
if (result?.Data != null) if (result?.Data != null)
@ -83,7 +255,27 @@ namespace Roadie.Api.Services
}; };
} }
public Task<models.Pagination.PagedResult<UserList>> List(PagedRequest request) public async Task<OperationResult<bool>> DeleteAllBookmarksAsync(User roadieUser)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
}
DbContext.Bookmarks.RemoveRange(DbContext.Bookmarks.Where(x => x.UserId == user.Id));
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion);
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
public Task<models.Pagination.PagedResult<UserList>> ListAsync(PagedRequest request)
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
@ -162,27 +354,7 @@ namespace Roadie.Api.Services
}); });
} }
public async Task<OperationResult<bool>> DeleteAllBookmarks(User roadieUser) public async Task<OperationResult<bool>> SetArtistBookmarkAsync(Guid artistId, User roadieUser, bool isBookmarked)
{
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null)
{
return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
}
DbContext.Bookmarks.RemoveRange(DbContext.Bookmarks.Where(x => x.UserId == user.Id));
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(user.CacheRegion);
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
public async Task<OperationResult<bool>> SetArtistBookmark(Guid artistId, User roadieUser, bool isBookmarked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -206,7 +378,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> SetArtistDisliked(Guid artistId, User roadieUser, bool isDisliked) public async Task<OperationResult<bool>> SetArtistDislikedAsync(Guid artistId, User roadieUser, bool isDisliked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -216,7 +388,7 @@ namespace Roadie.Api.Services
return await ToggleArtistDisliked(artistId, user, isDisliked).ConfigureAwait(false); return await ToggleArtistDisliked(artistId, user, isDisliked).ConfigureAwait(false);
} }
public async Task<OperationResult<bool>> SetArtistFavorite(Guid artistId, User roadieUser, bool isFavorite) public async Task<OperationResult<bool>> SetArtistFavoriteAsync(Guid artistId, User roadieUser, bool isFavorite)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -226,7 +398,7 @@ namespace Roadie.Api.Services
return await ToggleArtistFavorite(artistId, user, isFavorite).ConfigureAwait(false); return await ToggleArtistFavorite(artistId, user, isFavorite).ConfigureAwait(false);
} }
public async Task<OperationResult<short>> SetArtistRating(Guid artistId, User roadieUser, short rating) public async Task<OperationResult<short>> SetArtistRatingAsync(Guid artistId, User roadieUser, short rating)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -236,7 +408,7 @@ namespace Roadie.Api.Services
return await SetArtistRating(artistId, user, rating).ConfigureAwait(false); return await SetArtistRating(artistId, user, rating).ConfigureAwait(false);
} }
public async Task<OperationResult<bool>> SetCollectionBookmark(Guid collectionId, User roadieUser, bool isBookmarked) public async Task<OperationResult<bool>> SetCollectionBookmarkAsync(Guid collectionId, User roadieUser, bool isBookmarked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -259,7 +431,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> SetLabelBookmark(Guid labelId, User roadieUser, bool isBookmarked) public async Task<OperationResult<bool>> SetLabelBookmarkAsync(Guid labelId, User roadieUser, bool isBookmarked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -282,7 +454,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> SetPlaylistBookmark(Guid playlistId, User roadieUser, public async Task<OperationResult<bool>> SetPlaylistBookmarkAsync(Guid playlistId, User roadieUser,
bool isBookmarked) bool isBookmarked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
@ -306,7 +478,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> SetReleaseBookmark(Guid releaseid, User roadieUser, bool isBookmarked) public async Task<OperationResult<bool>> SetReleaseBookmarkAsync(Guid releaseid, User roadieUser, bool isBookmarked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -329,7 +501,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> SetReleaseDisliked(Guid releaseId, User roadieUser, bool isDisliked) public async Task<OperationResult<bool>> SetReleaseDislikedAsync(Guid releaseId, User roadieUser, bool isDisliked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -339,7 +511,7 @@ namespace Roadie.Api.Services
return await ToggleReleaseDisliked(releaseId, user, isDisliked).ConfigureAwait(false); return await ToggleReleaseDisliked(releaseId, user, isDisliked).ConfigureAwait(false);
} }
public async Task<OperationResult<bool>> SetReleaseFavorite(Guid releaseId, User roadieUser, bool isFavorite) public async Task<OperationResult<bool>> SetReleaseFavoriteAsync(Guid releaseId, User roadieUser, bool isFavorite)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -349,7 +521,7 @@ namespace Roadie.Api.Services
return await ToggleReleaseFavorite(releaseId, user, isFavorite).ConfigureAwait(false); return await ToggleReleaseFavorite(releaseId, user, isFavorite).ConfigureAwait(false);
} }
public async Task<OperationResult<short>> SetReleaseRating(Guid releaseId, User roadieUser, short rating) public async Task<OperationResult<short>> SetReleaseRatingAsync(Guid releaseId, User roadieUser, short rating)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -359,7 +531,7 @@ namespace Roadie.Api.Services
return await SetReleaseRating(releaseId, user, rating).ConfigureAwait(false); return await SetReleaseRating(releaseId, user, rating).ConfigureAwait(false);
} }
public async Task<OperationResult<bool>> SetTrackBookmark(Guid trackId, User roadieUser, bool isBookmarked) public async Task<OperationResult<bool>> SetTrackBookmarkAsync(Guid trackId, User roadieUser, bool isBookmarked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -382,7 +554,7 @@ namespace Roadie.Api.Services
}; };
} }
public async Task<OperationResult<bool>> SetTrackDisliked(Guid trackId, User roadieUser, bool isDisliked) public async Task<OperationResult<bool>> SetTrackDislikedAsync(Guid trackId, User roadieUser, bool isDisliked)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -392,7 +564,7 @@ namespace Roadie.Api.Services
return await ToggleTrackDisliked(trackId, user, isDisliked).ConfigureAwait(false); return await ToggleTrackDisliked(trackId, user, isDisliked).ConfigureAwait(false);
} }
public async Task<OperationResult<bool>> SetTrackFavorite(Guid trackId, User roadieUser, bool isFavorite) public async Task<OperationResult<bool>> SetTrackFavoriteAsync(Guid trackId, User roadieUser, bool isFavorite)
{ {
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
if (user == null) if (user == null)
@ -402,13 +574,13 @@ namespace Roadie.Api.Services
return await ToggleTrackFavorite(trackId, user, isFavorite).ConfigureAwait(false); return await ToggleTrackFavorite(trackId, user, isFavorite).ConfigureAwait(false);
} }
public async Task<OperationResult<short>> SetTrackRating(Guid trackId, User roadieUser, short rating) public async Task<OperationResult<short>> SetTrackRatingAsync(Guid trackId, User roadieUser, short rating)
{ {
var timings = new Dictionary<string, long>(); var timings = new Dictionary<string, long>();
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
var user = await GetUser(roadieUser.UserId).ConfigureAwait(false); var user = await GetUser(roadieUser.UserId).ConfigureAwait(false);
sw.Stop(); sw.Stop();
timings.Add("GetUser", sw.ElapsedMilliseconds); timings.Add(nameof(GetUser), sw.ElapsedMilliseconds);
if (user == null) if (user == null)
{ {
@ -417,7 +589,7 @@ namespace Roadie.Api.Services
sw.Start(); sw.Start();
var result = await SetTrackRating(trackId, user, rating).ConfigureAwait(false); var result = await SetTrackRating(trackId, user, rating).ConfigureAwait(false);
sw.Stop(); sw.Stop();
timings.Add("SetTrackRating", sw.ElapsedMilliseconds); timings.Add(nameof(SetTrackRatingAsync), sw.ElapsedMilliseconds);
result.AdditionalData.Add("Timing", sw.ElapsedMilliseconds); result.AdditionalData.Add("Timing", sw.ElapsedMilliseconds);
Logger.LogTrace( Logger.LogTrace(
@ -425,7 +597,7 @@ namespace Roadie.Api.Services
return result; return result;
} }
public async Task<OperationResult<bool>> UpdateIntegrationGrant(Guid userId, string integrationName, string token) public async Task<OperationResult<bool>> UpdateIntegrationGrantAsync(Guid userId, string integrationName, string token)
{ {
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userId); var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userId);
if (user == null) if (user == null)
@ -439,12 +611,12 @@ namespace Roadie.Api.Services
throw new NotImplementedException(); throw new NotImplementedException();
} }
public async Task<OperationResult<bool>> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel) public async Task<OperationResult<bool>> UpdateProfileAsync(User userPerformingUpdate, User userBeingUpdatedModel)
{ {
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId); var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId);
if (user == null) if (user == null)
{ {
return new OperationResult<bool>(true, string.Format("User Not Found [{0}]", userBeingUpdatedModel.UserId)); return new OperationResult<bool>(true, $"User Not Found [{userBeingUpdatedModel.UserId}]");
} }
if (user.Id != userPerformingUpdate.Id && !userPerformingUpdate.IsAdmin) if (user.Id != userPerformingUpdate.Id && !userPerformingUpdate.IsAdmin)
{ {
@ -556,175 +728,5 @@ namespace Roadie.Api.Services
Data = true Data = true
}; };
} }
private async Task<OperationResult<bool>> SetBookmark(Library.Identity.User user, BookmarkType bookmarktype, int bookmarkTargetId, bool isBookmarked)
{
var bookmark = DbContext.Bookmarks.FirstOrDefault(x => x.BookmarkTargetId == bookmarkTargetId &&
x.BookmarkType == bookmarktype &&
x.UserId == user.Id);
if (!isBookmarked)
{
// Remove bookmark
if (bookmark != null)
{
DbContext.Bookmarks.Remove(bookmark);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
else
{
// Add bookmark
if (bookmark == null)
{
await DbContext.Bookmarks.AddAsync(new data.Bookmark
{
UserId = user.Id,
BookmarkTargetId = bookmarkTargetId,
BookmarkType = bookmarktype,
CreatedDate = DateTime.UtcNow,
Status = Statuses.Ok
}).ConfigureAwait(false);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
CacheManager.ClearRegion(user.CacheRegion);
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
private async Task<OperationResult<bool>> UpdateLastFMSessionKey(Library.Identity.User user, string token)
{
var lastFmSessionKeyResult = await LastFmHelper.GetSessionKeyForUserToken(token).ConfigureAwait(false);
if (!lastFmSessionKeyResult.IsSuccess)
return new OperationResult<bool>(false, $"Unable to Get LastFM Session Key For Token [{token}]");
// Check concurrency stamp
if (user.ConcurrencyStamp != user.ConcurrencyStamp)
{
return new OperationResult<bool>
{
Errors = new List<Exception> { new Exception("User data is stale.") }
};
}
user.LastFMSessionKey = lastFmSessionKeyResult.Data;
user.LastUpdated = DateTime.UtcNow;
user.ConcurrencyStamp = Guid.NewGuid().ToString();
await DbContext.SaveChangesAsync().ConfigureAwait(false);
CacheManager.ClearRegion(Library.Identity.User.CacheRegionUrn(user.RoadieId));
Logger.LogTrace($"User `{user}` Updated LastFm SessionKey");
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
private async Task<OperationResult<User>> UserByIdAction(Guid id, IEnumerable<string> includes)
{
var timings = new Dictionary<string, long>();
var tsw = new Stopwatch();
tsw.Restart();
var user = await GetUser(id).ConfigureAwait(false);
tsw.Stop();
timings.Add("getUser", tsw.ElapsedMilliseconds);
if (user == null)
{
return new OperationResult<User>(true, string.Format("User Not Found [{0}]", id));
}
tsw.Restart();
var model = user.Adapt<User>();
model.MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "user", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height);
model.IsAdmin = user.UserRoles?.Any(x => x.Role?.NormalizedName == "ADMIN") ?? false;
model.IsEditor = model.IsAdmin || (user.UserRoles?.Any(x => x.Role?.NormalizedName == "EDITOR") ?? false);
tsw.Stop();
timings.Add("adapt", tsw.ElapsedMilliseconds);
if (includes?.Any() == true && includes.Contains("stats"))
{
tsw.Restart();
var userArtists = DbContext.UserArtists.Include(x => x.Artist).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserArtist[0];
var userReleases = DbContext.UserReleases.Include(x => x.Release).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserRelease[0];
var userTracks = DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == user.Id).ToArray() ?? new data.UserTrack[0];
var mostPlayedArtist = await DbContext.MostPlayedArtist(user.Id).ConfigureAwait(false);
var mostPlayedRelease = await DbContext.MostPlayedRelease(user.Id).ConfigureAwait(false);
var lastPlayedTrack = await GetTrack((await DbContext.MostPlayedTrack(user.Id).ConfigureAwait(false))?.RoadieId ?? Guid.Empty).ConfigureAwait(false);
var mostPlayedTrack = await GetTrack((await DbContext.LastPlayedTrack(user.Id).ConfigureAwait(false))?.RoadieId ?? Guid.Empty).ConfigureAwait(false);
model.Statistics = new UserStatistics
{
LastPlayedTrack = lastPlayedTrack == null
? null
: models.TrackList.FromDataTrack(
MakeTrackPlayUrl(user, HttpContext.BaseUrl, lastPlayedTrack.RoadieId),
lastPlayedTrack,
lastPlayedTrack.ReleaseMedia.MediaNumber,
lastPlayedTrack.ReleaseMedia.Release,
lastPlayedTrack.ReleaseMedia.Release.Artist,
lastPlayedTrack.TrackArtist,
HttpContext.BaseUrl,
ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, lastPlayedTrack.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, lastPlayedTrack.ReleaseMedia.Release.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, lastPlayedTrack.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, lastPlayedTrack.TrackArtist == null
? null
: (Guid?)lastPlayedTrack.TrackArtist.RoadieId)),
MostPlayedArtist = mostPlayedArtist == null
? null
: models.ArtistList.FromDataArtist(mostPlayedArtist,
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedArtist.RoadieId)),
MostPlayedRelease = mostPlayedRelease == null
? null
: ReleaseList.FromDataRelease(mostPlayedRelease,
mostPlayedRelease.Artist,
HttpContext.BaseUrl,
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedRelease.Artist.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, mostPlayedRelease.RoadieId)),
MostPlayedTrack = mostPlayedTrack == null
? null
: models.TrackList.FromDataTrack(
MakeTrackPlayUrl(user, HttpContext.BaseUrl, mostPlayedTrack.RoadieId),
mostPlayedTrack,
mostPlayedTrack.ReleaseMedia.MediaNumber,
mostPlayedTrack.ReleaseMedia.Release,
mostPlayedTrack.ReleaseMedia.Release.Artist,
mostPlayedTrack.TrackArtist,
HttpContext.BaseUrl,
ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, mostPlayedTrack.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, mostPlayedTrack.ReleaseMedia.Release.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedTrack.ReleaseMedia.Release.Artist.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, mostPlayedTrack.TrackArtist == null
? null
: (Guid?)mostPlayedTrack.TrackArtist.RoadieId)),
RatedArtists = userArtists.Count(x => x.Rating > 0),
FavoritedArtists = userArtists.Count(x => x.IsFavorite ?? false),
DislikedArtists = userArtists.Count(x => x.IsDisliked ?? false),
RatedReleases = userReleases.Count(x => x.Rating > 0),
FavoritedReleases = userReleases.Count(x => x.IsFavorite ?? false),
DislikedReleases = userReleases.Count(x => x.IsDisliked ?? false),
RatedTracks = userTracks.Count(x => x.Rating > 0),
PlayedTracks = userTracks.Where(x => x.PlayedCount.HasValue).Select(x => x.PlayedCount).Sum(),
FavoritedTracks = userTracks.Count(x => x.IsFavorite ?? false),
DislikedTracks = userTracks.Count(x => x.IsDisliked ?? false)
};
tsw.Stop();
timings.Add("stats", tsw.ElapsedMilliseconds);
}
Logger.LogInformation($"ByIdAction: User `{ user }`: includes [{includes.ToCSV()}], timings: [{ timings.ToTimings() }]");
return new OperationResult<User>
{
IsSuccess = true,
Data = model
};
}
} }
} }

View file

@ -28,11 +28,6 @@ namespace Roadie.Api.Controllers
[AllowAnonymous] [AllowAnonymous]
public class AccountController : ControllerBase public class AccountController : ControllerBase
{ {
private readonly ILogger<AccountController> Logger;
private readonly SignInManager<User> SignInManager;
private readonly ITokenService TokenService;
private readonly UserManager<User> UserManager;
private string _baseUrl;
private IAdminService AdminService { get; } private IAdminService AdminService { get; }
@ -43,10 +38,17 @@ namespace Roadie.Api.Controllers
if (_baseUrl == null) if (_baseUrl == null)
{ {
var scheme = Request.Scheme; var scheme = Request.Scheme;
if (RoadieSettings.UseSSLBehindProxy) scheme = "https"; if (RoadieSettings.UseSSLBehindProxy)
{
scheme = "https";
}
var host = Request.Host; var host = Request.Host;
if (!string.IsNullOrEmpty(RoadieSettings.BehindProxyHost)) if (!string.IsNullOrEmpty(RoadieSettings.BehindProxyHost))
{
host = new HostString(RoadieSettings.BehindProxyHost); host = new HostString(RoadieSettings.BehindProxyHost);
}
_baseUrl = $"{scheme}://{host}"; _baseUrl = $"{scheme}://{host}";
} }
@ -62,9 +64,22 @@ namespace Roadie.Api.Controllers
private IRoadieSettings RoadieSettings { get; } private IRoadieSettings RoadieSettings { get; }
public AccountController(IAdminService adminService, UserManager<User> userManager, SignInManager<User> signInManager, private string _baseUrl;
IConfiguration configuration, ILogger<AccountController> logger, ITokenService tokenService, private readonly ILogger<AccountController> Logger;
ICacheManager cacheManager, IEmailSender emailSender, IHttpContext httpContext) private readonly SignInManager<User> SignInManager;
private readonly ITokenService TokenService;
private readonly UserManager<User> UserManager;
public AccountController(
IAdminService adminService,
UserManager<User> userManager,
SignInManager<User> signInManager,
IConfiguration configuration,
ILogger<AccountController> logger,
ITokenService tokenService,
ICacheManager cacheManager,
IEmailSender emailSender,
IHttpContext httpContext)
{ {
UserManager = userManager; UserManager = userManager;
SignInManager = signInManager; SignInManager = signInManager;
@ -73,7 +88,7 @@ namespace Roadie.Api.Controllers
CacheManager = cacheManager; CacheManager = cacheManager;
RoadieSettings = new RoadieSettings(); RoadieSettings = new RoadieSettings();
configuration.GetSection("RoadieSettings").Bind(RoadieSettings); configuration.GetSection(nameof(RoadieSettings)).Bind(RoadieSettings);
AdminService = adminService; AdminService = adminService;
EmailSender = emailSender; EmailSender = emailSender;
RoadieHttpContext = httpContext; RoadieHttpContext = httpContext;
@ -101,38 +116,41 @@ namespace Roadie.Api.Controllers
[HttpPost] [HttpPost]
[Route("token")] [Route("token")]
public async Task<IActionResult> CreateToken([FromBody] LoginModel model) public async Task<IActionResult> CreateTokenAsync([FromBody] LoginModel model)
{ {
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
try try
{ {
// Login user // Login user
var loginResult = await SignInManager.PasswordSignInAsync(model.Username, model.Password, false, false); var loginResult = await SignInManager.PasswordSignInAsync(model.Username, model.Password, false, false).ConfigureAwait(false);
if (!loginResult.Succeeded) if (!loginResult.Succeeded)
{ {
return BadRequest(); return BadRequest();
} }
var user = await UserManager.FindByNameAsync(model.Username); var user = await UserManager.FindByNameAsync(model.Username).ConfigureAwait(false);
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
user.LastLogin = now; user.LastLogin = now;
user.LastUpdated = now; user.LastUpdated = now;
await UserManager.UpdateAsync(user); await UserManager.UpdateAsync(user).ConfigureAwait(false);
var t = await TokenService.GenerateToken(user, UserManager); var t = await TokenService.GenerateTokenAsync(user, UserManager).ConfigureAwait(false);
Logger.LogTrace($"Successfully authenticated User [{model.Username}]"); Logger.LogTrace($"Successfully authenticated User [{model.Username}]");
if (!user.EmailConfirmed) if (!user.EmailConfirmed)
{
try try
{ {
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); var code = await UserManager.GenerateEmailConfirmationTokenAsync(user).ConfigureAwait(false);
var callbackUrl = $"{BaseUrl}/auth/confirmemail?userId={user.Id}&code={code}"; var callbackUrl = $"{BaseUrl}/auth/confirmemail?userId={user.Id}&code={code}";
await EmailSender.SendEmailAsync(user.Email, await EmailSender.SendEmailAsync(user.Email,
$"Confirm your {RoadieSettings.SiteName} email", $"Confirm your {RoadieSettings.SiteName} email",
$"Please confirm your {RoadieSettings.SiteName} account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); $"Please confirm your {RoadieSettings.SiteName} account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.")
.ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error sending confirmation Email"); Logger.LogError(ex, "Error sending confirmation Email");
} }
}
CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn); CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
var avatarUrl = $"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}"; var avatarUrl = $"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}";
@ -163,7 +181,7 @@ namespace Roadie.Api.Controllers
[Authorize] [Authorize]
[HttpPost] [HttpPost]
[Route("refreshtoken")] [Route("refreshtoken")]
public async Task<IActionResult> RefreshToken() public async Task<IActionResult> RefreshTokenAsync()
{ {
try try
{ {
@ -172,8 +190,8 @@ namespace Roadie.Api.Controllers
.FirstOrDefault(); .FirstOrDefault();
if (!string.IsNullOrWhiteSpace(username)) if (!string.IsNullOrWhiteSpace(username))
{ {
var user = await UserManager.FindByNameAsync(username); var user = await UserManager.FindByNameAsync(username).ConfigureAwait(false);
return Ok(await TokenService.GenerateToken(user, UserManager)); return Ok(await TokenService.GenerateTokenAsync(user, UserManager).ConfigureAwait(false));
} }
ModelState.AddModelError("Authentication", "Authentication failed!"); ModelState.AddModelError("Authentication", "Authentication failed!");
} }
@ -187,7 +205,7 @@ namespace Roadie.Api.Controllers
[HttpPost] [HttpPost]
[Route("register")] [Route("register")]
[AllowAnonymous] [AllowAnonymous]
public async Task<IActionResult> Register([FromBody] RegisterModel registerModel) public async Task<IActionResult> RegisterAsync([FromBody] RegisterModel registerModel)
{ {
try try
{ {
@ -205,7 +223,7 @@ namespace Roadie.Api.Controllers
if (RoadieSettings.UseRegistrationTokens) if (RoadieSettings.UseRegistrationTokens)
{ {
var tokenValidation = await AdminService.ValidateInviteToken(registerModel.InviteToken); var tokenValidation = await AdminService.ValidateInviteTokenAsync(registerModel.InviteToken).ConfigureAwait(false);
if (!tokenValidation.IsSuccess) if (!tokenValidation.IsSuccess)
{ {
Logger.LogTrace("Invalid Invite Token"); Logger.LogTrace("Invalid Invite Token");
@ -213,42 +231,46 @@ namespace Roadie.Api.Controllers
} }
} }
var existinUserByUsername = await UserManager.FindByNameAsync(registerModel.Username); var existinUserByUsername = await UserManager.FindByNameAsync(registerModel.Username).ConfigureAwait(false);
if (existinUserByUsername != null) if (existinUserByUsername != null)
{ {
return StatusCode((int)HttpStatusCode.BadRequest, new { Title = "User With Username Already Exists!" }); return StatusCode((int)HttpStatusCode.BadRequest, new { Title = "User With Username Already Exists!" });
} }
var existingUserByEmail = await UserManager.FindByEmailAsync(registerModel.Email); var existingUserByEmail = await UserManager.FindByEmailAsync(registerModel.Email).ConfigureAwait(false);
if (existingUserByEmail != null) if (existingUserByEmail != null)
{ {
return StatusCode((int)HttpStatusCode.BadRequest, new { Title = "User With Email Already Exists!" }); return StatusCode((int)HttpStatusCode.BadRequest, new { Title = "User With Email Already Exists!" });
} }
var identityResult = await UserManager.CreateAsync(user, registerModel.Password); var identityResult = await UserManager.CreateAsync(user, registerModel.Password).ConfigureAwait(false);
if (identityResult.Succeeded) if (identityResult.Succeeded)
{ {
if (user.Id == 1) await AdminService.DoInitialSetup(user, UserManager); if (user.Id == 1)
{
await AdminService.DoInitialSetupAsync(user, UserManager).ConfigureAwait(false);
}
try try
{ {
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); var code = await UserManager.GenerateEmailConfirmationTokenAsync(user).ConfigureAwait(false);
var callbackUrl = $"{BaseUrl}/auth/confirmemail?userId={user.Id}&code={code}"; var callbackUrl = $"{BaseUrl}/auth/confirmemail?userId={user.Id}&code={code}";
await EmailSender.SendEmailAsync(user.Email, $"Confirm your {RoadieSettings.SiteName} email", await EmailSender.SendEmailAsync(user.Email, $"Confirm your {RoadieSettings.SiteName} email",
$"Please confirm your {RoadieSettings.SiteName} account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); $"Please confirm your {RoadieSettings.SiteName} account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, $"Error Sending Register Email to [{registerModel.Email}]"); Logger.LogError(ex, $"Error Sending Register Email to [{registerModel.Email}]");
} }
await SignInManager.SignInAsync(user, false); await SignInManager.SignInAsync(user, false).ConfigureAwait(false);
var t = await TokenService.GenerateToken(user, UserManager); var t = await TokenService.GenerateTokenAsync(user, UserManager).ConfigureAwait(false);
Logger.LogTrace($"Successfully created and authenticated User [{registerModel.Username}]"); Logger.LogTrace($"Successfully created and authenticated User [{registerModel.Username}]");
CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn); CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
var avatarUrl = $"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}"; var avatarUrl = $"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}";
if (registerModel.InviteToken.HasValue) if (registerModel.InviteToken.HasValue)
{ {
await AdminService.UpdateInviteTokenUsed(registerModel.InviteToken); await AdminService.UpdateInviteTokenUsedAsync(registerModel.InviteToken).ConfigureAwait(false);
} }
return Ok(new return Ok(new
{ {
@ -286,22 +308,22 @@ namespace Roadie.Api.Controllers
} }
[HttpPost("resetpassword")] [HttpPost("resetpassword")]
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordModel resetPasswordModel) public async Task<IActionResult> ResetPasswordAsync([FromBody] ResetPasswordModel resetPasswordModel)
{ {
try try
{ {
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
var user = await UserManager.FindByNameAsync(resetPasswordModel.Username); var user = await UserManager.FindByNameAsync(resetPasswordModel.Username).ConfigureAwait(false);
var token = Encoding.ASCII.GetString(WebEncoders.Base64UrlDecode(resetPasswordModel.Token)); var token = Encoding.ASCII.GetString(WebEncoders.Base64UrlDecode(resetPasswordModel.Token));
var identityResult = await UserManager.ResetPasswordAsync(user, token, resetPasswordModel.Password); var identityResult = await UserManager.ResetPasswordAsync(user, token, resetPasswordModel.Password).ConfigureAwait(false);
if (identityResult.Succeeded) if (identityResult.Succeeded)
{ {
CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn); CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
await SignInManager.SignInAsync(user, false); await SignInManager.SignInAsync(user, false).ConfigureAwait(false);
var avatarUrl = var avatarUrl =
$"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}"; $"{RoadieHttpContext.ImageBaseUrl}/user/{user.RoadieId}/{RoadieSettings.ThumbnailImageSize.Width}/{RoadieSettings.ThumbnailImageSize.Height}";
var t = await TokenService.GenerateToken(user, UserManager); var t = await TokenService.GenerateTokenAsync(user, UserManager).ConfigureAwait(false);
return Ok(new return Ok(new
{ {
Username = user.UserName, Username = user.UserName,
@ -329,22 +351,22 @@ namespace Roadie.Api.Controllers
} }
[HttpGet("sendpasswordresetemail")] [HttpGet("sendpasswordresetemail")]
public async Task<IActionResult> SendPasswordResetEmail(string username, string callbackUrl) public async Task<IActionResult> SendPasswordResetEmailAsync(string username, string callbackUrl)
{ {
try try
{ {
var user = await UserManager.FindByNameAsync(username); var user = await UserManager.FindByNameAsync(username).ConfigureAwait(false);
if (user == null) if (user == null)
{ {
Logger.LogError($"Unable to find user by username [{username}]"); Logger.LogError($"Unable to find user by username [{username}]");
return StatusCode(500); return StatusCode(500);
} }
var token = await UserManager.GeneratePasswordResetTokenAsync(user); var token = await UserManager.GeneratePasswordResetTokenAsync(user).ConfigureAwait(false);
callbackUrl = callbackUrl + "?username=" + username + "&token=" + callbackUrl = $"{callbackUrl}?username={username}&token={WebEncoders.Base64UrlEncode(Encoding.ASCII.GetBytes(token))}";
WebEncoders.Base64UrlEncode(Encoding.ASCII.GetBytes(token));
await EmailSender.SendEmailAsync(user.Email, $"Reset your {RoadieSettings.SiteName} password", await EmailSender.SendEmailAsync(user.Email, $"Reset your {RoadieSettings.SiteName} password",
$"A request has been made to reset your password for your {RoadieSettings.SiteName} account. To proceed <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>click here</a>."); $"A request has been made to reset your password for your {RoadieSettings.SiteName} account. To proceed <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>click here</a>.")
.ConfigureAwait(false);
Logger.LogTrace("User [{0}] Email [{1}] Requested Password Reset Callback [{2}]", username, Logger.LogTrace("User [{0}] Email [{1}] Requested Password Reset Callback [{2}]", username,
user.Email, callbackUrl); user.Email, callbackUrl);
return Ok(); return Ok();

View file

@ -21,8 +21,12 @@ namespace Roadie.Api.Controllers
{ {
private IAdminService AdminService { get; } private IAdminService AdminService { get; }
public AdminController(IAdminService adminService, ILogger<AdminController> logger, ICacheManager cacheManager, public AdminController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IAdminService adminService,
ILogger<AdminController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -42,7 +46,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> DeleteArtist(Guid id, bool? doDeleteFile) public async Task<IActionResult> DeleteArtist(Guid id, bool? doDeleteFile)
{ {
var result = await AdminService.DeleteArtist(await UserManager.GetUserAsync(User), id, doDeleteFile ?? true); var result = await AdminService.DeleteArtistAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, doDeleteFile ?? true).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -58,7 +62,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> DeleteArtistReleases(Guid id) public async Task<IActionResult> DeleteArtistReleases(Guid id)
{ {
var result = await AdminService.DeleteArtistReleases(await UserManager.GetUserAsync(User), id); var result = await AdminService.DeleteArtistReleasesAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -74,39 +78,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> DeleteArtistSecondaryImage(Guid id, int index) public async Task<IActionResult> DeleteArtistSecondaryImage(Guid id, int index)
{ {
var result = await AdminService.DeleteArtistSecondaryImage(await UserManager.GetUserAsync(User), id, index); var result = await AdminService.DeleteArtistSecondaryImageAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, index).ConfigureAwait(false);
if (!result.IsSuccess)
{
if (result.Messages?.Any() ?? false)
{
return StatusCode((int)HttpStatusCode.BadRequest, result.Messages);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("delete/release/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteRelease(Guid id, bool? doDeleteFiles)
{
var result = await AdminService.DeleteRelease(await UserManager.GetUserAsync(User), id, doDeleteFiles);
if (!result.IsSuccess)
{
if (result.Messages?.Any() ?? false)
{
return StatusCode((int)HttpStatusCode.BadRequest, result.Messages);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("delete/label/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteLabel(Guid id)
{
var result = await AdminService.DeleteLabel(await UserManager.GetUserAsync(User), id);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -122,7 +94,39 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> DeleteGenre(Guid id) public async Task<IActionResult> DeleteGenre(Guid id)
{ {
var result = await AdminService.DeleteGenre(await UserManager.GetUserAsync(User), id); var result = await AdminService.DeleteGenreAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id).ConfigureAwait(false);
if (!result.IsSuccess)
{
if (result.Messages?.Any() ?? false)
{
return StatusCode((int)HttpStatusCode.BadRequest, result.Messages);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("delete/label/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteLabel(Guid id)
{
var result = await AdminService.DeleteLabelAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id).ConfigureAwait(false);
if (!result.IsSuccess)
{
if (result.Messages?.Any() ?? false)
{
return StatusCode((int)HttpStatusCode.BadRequest, result.Messages);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("delete/release/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteRelease(Guid id, bool? doDeleteFiles)
{
var result = await AdminService.DeleteReleaseAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, doDeleteFiles).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -139,16 +143,20 @@ namespace Roadie.Api.Controllers
public async Task<IActionResult> DeleteReleaseSecondaryImage(Guid id, int index) public async Task<IActionResult> DeleteReleaseSecondaryImage(Guid id, int index)
{ {
var result = var result =
await AdminService.DeleteReleaseSecondaryImage(await UserManager.GetUserAsync(User), id, index); await AdminService.DeleteReleaseSecondaryImageAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, index).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
[HttpPost("delete/tracks")] [HttpPost("delete/track/{id}")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> DeleteTracks([FromBody]IEnumerable<Guid> ids, bool? doDeleteFile) public async Task<IActionResult> DeleteTrack(Guid id, bool? doDeleteFile)
{ {
var result = await AdminService.DeleteTracks(await UserManager.GetUserAsync(User), ids, doDeleteFile); var result = await AdminService.DeleteTracksAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), new Guid[1] { id }, doDeleteFile).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -160,11 +168,11 @@ namespace Roadie.Api.Controllers
return Ok(result); return Ok(result);
} }
[HttpPost("delete/track/{id}")] [HttpPost("delete/tracks")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> DeleteTrack(Guid id, bool? doDeleteFile) public async Task<IActionResult> DeleteTracks([FromBody] IEnumerable<Guid> ids, bool? doDeleteFile)
{ {
var result = await AdminService.DeleteTracks(await UserManager.GetUserAsync(User), new Guid[1] { id }, doDeleteFile); var result = await AdminService.DeleteTracksAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), ids, doDeleteFile).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -180,7 +188,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> DeleteUser(Guid id) public async Task<IActionResult> DeleteUser(Guid id)
{ {
var result = await AdminService.DeleteUser(await UserManager.GetUserAsync(User), id); var result = await AdminService.DeleteUserAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -196,7 +204,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> MissingCollectionReleases() public async Task<IActionResult> MissingCollectionReleases()
{ {
var result = await AdminService.MissingCollectionReleases(await UserManager.GetUserAsync(User)); var result = await AdminService.MissingCollectionReleasesAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false)).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -212,7 +220,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanAllCollections() public async Task<IActionResult> ScanAllCollections()
{ {
var result = await AdminService.ScanAllCollections(await UserManager.GetUserAsync(User).ConfigureAwait(false)).ConfigureAwait(false); var result = await AdminService.ScanAllCollectionsAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false)).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -228,7 +236,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanArtist(Guid id) public async Task<IActionResult> ScanArtist(Guid id)
{ {
var result = await AdminService.ScanArtist(await UserManager.GetUserAsync(User), id); var result = await AdminService.ScanArtistAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -244,7 +252,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanArtists(IEnumerable<Guid> ids) public async Task<IActionResult> ScanArtists(IEnumerable<Guid> ids)
{ {
var result = await AdminService.ScanArtists(await UserManager.GetUserAsync(User), ids); var result = await AdminService.ScanArtistsAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), ids).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -260,7 +268,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanCollection(Guid id, bool doPurgeFirst = false) public async Task<IActionResult> ScanCollection(Guid id, bool doPurgeFirst = false)
{ {
var result = await AdminService.ScanCollection(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, doPurgeFirst: doPurgeFirst).ConfigureAwait(false); var result = await AdminService.ScanCollectionAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, doPurgeFirst: doPurgeFirst).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -276,7 +284,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanInbound() public async Task<IActionResult> ScanInbound()
{ {
var result = await AdminService.ScanInboundFolder(await UserManager.GetUserAsync(User)); var result = await AdminService.ScanInboundFolderAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false)).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -292,7 +300,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanLibrary() public async Task<IActionResult> ScanLibrary()
{ {
var result = await AdminService.ScanLibraryFolder(await UserManager.GetUserAsync(User)); var result = await AdminService.ScanLibraryFolderAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false)).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -308,7 +316,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanRelease(Guid id) public async Task<IActionResult> ScanRelease(Guid id)
{ {
var result = await AdminService.ScanRelease(await UserManager.GetUserAsync(User), id); var result = await AdminService.ScanReleaseAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)
@ -324,7 +332,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ScanReleases(IEnumerable<Guid> ids) public async Task<IActionResult> ScanReleases(IEnumerable<Guid> ids)
{ {
var result = await AdminService.ScanReleases(await UserManager.GetUserAsync(User), ids); var result = await AdminService.ScanReleasesAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), ids).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.Messages?.Any() ?? false) if (result.Messages?.Any() ?? false)

View file

@ -25,8 +25,12 @@ namespace Roadie.Api.Controllers
{ {
private IArtistService ArtistService { get; } private IArtistService ArtistService { get; }
public ArtistController(IArtistService artistService, ILogger<ArtistController> logger, ICacheManager cacheManager, public ArtistController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IArtistService artistService,
ILogger<ArtistController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -41,13 +45,17 @@ namespace Roadie.Api.Controllers
{ {
var user = await CurrentUserModel().ConfigureAwait(false); var user = await CurrentUserModel().ConfigureAwait(false);
var result = var result =
await ArtistService.ById(user, id, (inc ?? models.Artist.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false); await ArtistService.ByIdAsync(user, id, (inc ?? models.Artist.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
} }
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -57,11 +65,15 @@ namespace Roadie.Api.Controllers
{ {
try try
{ {
PagedResult<models.ArtistList> result = await ArtistService.List(await CurrentUserModel().ConfigureAwait(false), PagedResult<models.ArtistList> result = await ArtistService.ListAsync(await CurrentUserModel().ConfigureAwait(false),
request, request,
doRandomize ?? false, doRandomize ?? false,
false).ConfigureAwait(false); false).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
@ -82,7 +94,7 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> MergeArtists(Guid artistToMergeId, Guid artistToMergeIntoId) public async Task<IActionResult> MergeArtists(Guid artistToMergeId, Guid artistToMergeIntoId)
{ {
var result = await ArtistService.MergeArtists(await UserManager.GetUserAsync(User).ConfigureAwait(false), artistToMergeId, artistToMergeIntoId).ConfigureAwait(false); var result = await ArtistService.MergeArtistsAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), artistToMergeId, artistToMergeIntoId).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -110,8 +122,12 @@ namespace Roadie.Api.Controllers
public async Task<IActionResult> SetArtistImageByUrl(Guid id, string imageUrl) public async Task<IActionResult> SetArtistImageByUrl(Guid id, string imageUrl)
{ {
var result = var result =
await ArtistService.SetReleaseImageByUrl(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, HttpUtility.UrlDecode(imageUrl)).ConfigureAwait(false); await ArtistService.SetReleaseImageByUrlAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, HttpUtility.UrlDecode(imageUrl)).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) return NotFound(); if (result?.IsNotFoundResult != false)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -137,7 +153,7 @@ namespace Roadie.Api.Controllers
{ {
return BadRequest(ModelState); return BadRequest(ModelState);
} }
var result = await ArtistService.UpdateArtist(await UserManager.GetUserAsync(User).ConfigureAwait(false), artist).ConfigureAwait(false); var result = await ArtistService.UpdateArtistAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), artist).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -163,7 +179,7 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> UploadImage(Guid id, IFormFile file) public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
{ {
var result = await ArtistService.UploadArtistImage(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, file).ConfigureAwait(false); var result = await ArtistService.UploadArtistImageAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, file).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();

View file

@ -21,8 +21,12 @@ namespace Roadie.Api.Controllers
{ {
private IBookmarkService BookmarkService { get; } private IBookmarkService BookmarkService { get; }
public BookmarkController(IBookmarkService bookmarkService, ILogger<BookmarkController> logger, ICacheManager cacheManager, public BookmarkController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IBookmarkService bookmarkService,
ILogger<BookmarkController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -35,9 +39,12 @@ namespace Roadie.Api.Controllers
{ {
try try
{ {
var result = await BookmarkService.List(await CurrentUserModel(), var result = await BookmarkService.ListAsync(await CurrentUserModel().ConfigureAwait(false), request).ConfigureAwait(false);
request); if (!result.IsSuccess)
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); {
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)

View file

@ -23,9 +23,12 @@ namespace Roadie.Api.Controllers
{ {
private ICollectionService CollectionService { get; } private ICollectionService CollectionService { get; }
public CollectionController(ICollectionService collectionService, ILogger<CollectionController> logger, public CollectionController(
ICollectionService collectionService,
ILogger<CollectionController> logger,
ICacheManager cacheManager, ICacheManager cacheManager,
UserManager<User> userManager, IRoadieSettings roadieSettings) UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -37,7 +40,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Add() public async Task<IActionResult> Add()
{ {
var result = CollectionService.Add(await CurrentUserModel()); var result = CollectionService.Add(await CurrentUserModel().ConfigureAwait(false));
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -59,7 +62,7 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> DeleteCollection(Guid id) public async Task<IActionResult> DeleteCollection(Guid id)
{ {
var result = await CollectionService.DeleteCollection(await CurrentUserModel(), id); var result = await CollectionService.DeleteCollectionAsync(await CurrentUserModel().ConfigureAwait(false), id).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -84,9 +87,13 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null) public async Task<IActionResult> Get(Guid id, string inc = null)
{ {
var result = await CollectionService.ById(await CurrentUserModel(), id, var result = await CollectionService.ByIdAsync(await CurrentUserModel().ConfigureAwait(false), id,
(inc ?? Collection.DefaultIncludes).ToLower().Split(",")); (inc ?? Collection.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) return NotFound(); if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -108,8 +115,12 @@ namespace Roadie.Api.Controllers
{ {
try try
{ {
var result = await CollectionService.List(await CurrentUserModel(), request); var result = await CollectionService.ListAsync(await CurrentUserModel().ConfigureAwait(false), request).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
@ -134,7 +145,7 @@ namespace Roadie.Api.Controllers
{ {
return BadRequest(ModelState); return BadRequest(ModelState);
} }
var result = await CollectionService.UpdateCollection(await CurrentUserModel(), collection); var result = await CollectionService.UpdateCollectionAsync(await CurrentUserModel().ConfigureAwait(false), collection).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();

View file

@ -23,9 +23,12 @@ namespace Roadie.Api.Controllers
{ {
private ICommentService CommentService { get; } private ICommentService CommentService { get; }
public CommentController(ILogger<CommentController> logger, ICacheManager cacheManager, public CommentController(
ILogger<CommentController> logger,
ICacheManager cacheManager,
UserManager<User> userManager, UserManager<User> userManager,
IRoadieSettings roadieSettings, ICommentService commentService) IRoadieSettings roadieSettings,
ICommentService commentService)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -37,9 +40,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewArtistComment(Guid id, models.Comment model) public async Task<IActionResult> AddNewArtistComment(Guid id, models.Comment model)
{ {
if (string.IsNullOrEmpty(model.Cmt)) return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment"); if (string.IsNullOrEmpty(model.Cmt))
var result = await CommentService.AddNewArtistComment(await CurrentUserModel(), id, model.Cmt); {
if (result == null || result.IsNotFoundResult) return NotFound(); return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment");
}
var result = await CommentService.AddNewArtistCommentAsync(await CurrentUserModel().ConfigureAwait(false), id, model.Cmt).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -60,9 +71,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewCollectionComment(Guid id, models.Comment model) public async Task<IActionResult> AddNewCollectionComment(Guid id, models.Comment model)
{ {
if (string.IsNullOrEmpty(model.Cmt)) return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment"); if (string.IsNullOrEmpty(model.Cmt))
var result = await CommentService.AddNewCollectionComment(await CurrentUserModel(), id, model.Cmt); {
if (result == null || result.IsNotFoundResult) return NotFound(); return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment");
}
var result = await CommentService.AddNewCollectionCommentAsync(await CurrentUserModel().ConfigureAwait(false), id, model.Cmt).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -83,9 +102,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewGenreComment(Guid id, models.Comment model) public async Task<IActionResult> AddNewGenreComment(Guid id, models.Comment model)
{ {
if (string.IsNullOrEmpty(model.Cmt)) return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment"); if (string.IsNullOrEmpty(model.Cmt))
var result = await CommentService.AddNewGenreComment(await CurrentUserModel(), id, model.Cmt); {
if (result == null || result.IsNotFoundResult) return NotFound(); return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment");
}
var result = await CommentService.AddNewGenreCommentAsync(await CurrentUserModel().ConfigureAwait(false), id, model.Cmt).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -106,9 +133,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewLabelComment(Guid id, models.Comment model) public async Task<IActionResult> AddNewLabelComment(Guid id, models.Comment model)
{ {
if (string.IsNullOrEmpty(model.Cmt)) return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment"); if (string.IsNullOrEmpty(model.Cmt))
var result = await CommentService.AddNewLabelComment(await CurrentUserModel(), id, model.Cmt); {
if (result == null || result.IsNotFoundResult) return NotFound(); return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment");
}
var result = await CommentService.AddNewLabelCommentAsync(await CurrentUserModel().ConfigureAwait(false), id, model.Cmt).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -129,9 +164,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewPlaylistComment(Guid id, models.Comment model) public async Task<IActionResult> AddNewPlaylistComment(Guid id, models.Comment model)
{ {
if (string.IsNullOrEmpty(model.Cmt)) return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment"); if (string.IsNullOrEmpty(model.Cmt))
var result = await CommentService.AddNewPlaylistComment(await CurrentUserModel(), id, model.Cmt); {
if (result == null || result.IsNotFoundResult) return NotFound(); return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment");
}
var result = await CommentService.AddNewPlaylistCommentAsync(await CurrentUserModel().ConfigureAwait(false), id, model.Cmt).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -152,9 +195,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewReleaseComment(Guid id, models.Comment model) public async Task<IActionResult> AddNewReleaseComment(Guid id, models.Comment model)
{ {
if (string.IsNullOrEmpty(model.Cmt)) return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment"); if (string.IsNullOrEmpty(model.Cmt))
var result = await CommentService.AddNewReleaseComment(await CurrentUserModel(), id, model.Cmt); {
if (result == null || result.IsNotFoundResult) return NotFound(); return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment");
}
var result = await CommentService.AddNewReleaseCommentAsync(await CurrentUserModel().ConfigureAwait(false), id, model.Cmt).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -175,9 +226,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewTrackComment(Guid id, models.Comment model) public async Task<IActionResult> AddNewTrackComment(Guid id, models.Comment model)
{ {
if (string.IsNullOrEmpty(model.Cmt)) return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment"); if (string.IsNullOrEmpty(model.Cmt))
var result = await CommentService.AddNewTrackComment(await CurrentUserModel(), id, model.Cmt); {
if (result == null || result.IsNotFoundResult) return NotFound(); return StatusCode((int)HttpStatusCode.BadRequest, "Invalid Comment");
}
var result = await CommentService.AddNewTrackCommentAsync(await CurrentUserModel().ConfigureAwait(false), id, model.Cmt).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -198,8 +257,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> DeleteComment(Guid id) public async Task<IActionResult> DeleteComment(Guid id)
{ {
var result = await CommentService.DeleteComment(await CurrentUserModel(), id); var result = await CommentService.DeleteCommentAsync(await CurrentUserModel().ConfigureAwait(false), id).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) return NotFound(); if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -220,8 +283,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> SetCommentReaction(Guid id, CommentReaction reaction) public async Task<IActionResult> SetCommentReaction(Guid id, CommentReaction reaction)
{ {
var result = await CommentService.SetCommentReaction(await CurrentUserModel(), id, reaction); var result = await CommentService.SetCommentReactionAsync(await CurrentUserModel().ConfigureAwait(false), id, reaction).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) return NotFound(); if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)

View file

@ -22,8 +22,6 @@ namespace Roadie.Api.Controllers
{ {
public const string ControllerCacheRegionUrn = "urn:controller_cache"; public const string ControllerCacheRegionUrn = "urn:controller_cache";
private models.User _currentUser;
protected ICacheManager CacheManager { get; } protected ICacheManager CacheManager { get; }
protected ILogger Logger { get; set; } protected ILogger Logger { get; set; }
@ -32,7 +30,11 @@ namespace Roadie.Api.Controllers
protected UserManager<User> UserManager { get; } protected UserManager<User> UserManager { get; }
public EntityControllerBase(ICacheManager cacheManager, IRoadieSettings roadieSettings, private models.User _currentUser;
protected EntityControllerBase(
ICacheManager cacheManager,
IRoadieSettings roadieSettings,
UserManager<User> userManager) UserManager<User> userManager)
{ {
CacheManager = cacheManager; CacheManager = cacheManager;
@ -48,8 +50,8 @@ namespace Roadie.Api.Controllers
{ {
_currentUser = await CacheManager.GetAsync($"urn:controller_user:{User.Identity.Name}", async () => _currentUser = await CacheManager.GetAsync($"urn:controller_user:{User.Identity.Name}", async () =>
{ {
return UserModelForUser(await UserManager.GetUserAsync(User)); return UserModelForUser(await UserManager.GetUserAsync(User).ConfigureAwait(false));
}, ControllerCacheRegionUrn); }, ControllerCacheRegionUrn).ConfigureAwait(false);
} }
} }
if (_currentUser == null) if (_currentUser == null)
@ -67,7 +69,7 @@ namespace Roadie.Api.Controllers
var tsw = new Stopwatch(); var tsw = new Stopwatch();
tsw.Restart(); tsw.Restart();
var user = currentUser ?? await CurrentUserModel(); var user = currentUser ?? await CurrentUserModel().ConfigureAwait(false); ;
var track = trackService.StreamCheckAndInfo(user, id); var track = trackService.StreamCheckAndInfo(user, id);
if (track == null || (track?.IsNotFoundResult ?? false)) if (track == null || (track?.IsNotFoundResult ?? false))
{ {
@ -86,18 +88,23 @@ namespace Roadie.Api.Controllers
timings.Add("TrackService.StreamCheckAndInfo", tsw.ElapsedMilliseconds); timings.Add("TrackService.StreamCheckAndInfo", tsw.ElapsedMilliseconds);
tsw.Restart(); tsw.Restart();
var info = await trackService.TrackStreamInfo(id, var info = await trackService.TrackStreamInfoAsync(id,
TrackService.DetermineByteStartFromHeaders(Request.Headers), TrackService.DetermineByteStartFromHeaders(Request.Headers),
TrackService.DetermineByteEndFromHeaders(Request.Headers, track.Data.FileSize), TrackService.DetermineByteEndFromHeaders(Request.Headers, track.Data.FileSize),
user); user).ConfigureAwait(false);
if (!info?.IsSuccess ?? false || info?.Data == null) if (!info?.IsSuccess ?? false || info?.Data == null)
{ {
if (info?.Errors != null && (info?.Errors.Any() ?? false)) if (info?.Errors != null && (info?.Errors.Any() ?? false))
{
Logger.LogCritical( Logger.LogCritical(
$"StreamTrack: TrackStreamInfo Invalid For TrackId [{id}] OperationResult Errors [{string.Join('|', info?.Errors ?? new Exception[0])}], For User [{currentUser}]"); $"StreamTrack: TrackStreamInfo Invalid For TrackId [{id}] OperationResult Errors [{string.Join('|', info?.Errors ?? new Exception[0])}], For User [{currentUser}]");
}
else else
{
Logger.LogCritical( Logger.LogCritical(
$"StreamTrack: TrackStreamInfo Invalid For TrackId [{id}] OperationResult Messages [{string.Join('|', info?.Messages ?? new string[0])}], For User [{currentUser}]"); $"StreamTrack: TrackStreamInfo Invalid For TrackId [{id}] OperationResult Messages [{string.Join('|', info?.Messages ?? new string[0])}], For User [{currentUser}]");
}
return NotFound("Unknown TrackId"); return NotFound("Unknown TrackId");
} }
@ -129,7 +136,7 @@ namespace Roadie.Api.Controllers
} }
Response.Headers.Add("Expires", info.Data.Expires); Response.Headers.Add("Expires", info.Data.Expires);
await Response.Body.WriteAsync(info.Data.Bytes, 0, info.Data.Bytes.Length); await Response.Body.WriteAsync(info.Data.Bytes, 0, info.Data.Bytes.Length).ConfigureAwait(false);
var scrobble = new ScrobbleInfo var scrobble = new ScrobbleInfo
{ {
@ -137,7 +144,7 @@ namespace Roadie.Api.Controllers
TimePlayed = DateTime.UtcNow, TimePlayed = DateTime.UtcNow,
TrackId = id TrackId = id
}; };
await playActivityService.NowPlaying(user, scrobble); await playActivityService.NowPlayingAsync(user, scrobble).ConfigureAwait(false);
sw.Stop(); sw.Stop();
Logger.LogTrace($"StreamTrack ElapsedTime [{sw.ElapsedMilliseconds}], Timings [{JsonConvert.SerializeObject(timings)}], StreamInfo `{info?.Data}`"); Logger.LogTrace($"StreamTrack ElapsedTime [{sw.ElapsedMilliseconds}], Timings [{JsonConvert.SerializeObject(timings)}], StreamInfo `{info?.Data}`");
return new EmptyResult(); return new EmptyResult();
@ -145,7 +152,11 @@ namespace Roadie.Api.Controllers
protected models.User UserModelForUser(User user) protected models.User UserModelForUser(User user)
{ {
if (user == null) return null; if (user == null)
{
return null;
}
var result = user.Adapt<models.User>(); var result = user.Adapt<models.User>();
result.IsAdmin = User.IsInRole("Admin"); result.IsAdmin = User.IsInRole("Admin");
result.IsEditor = User.IsInRole("Editor") || result.IsAdmin; result.IsEditor = User.IsInRole("Editor") || result.IsAdmin;

View file

@ -25,8 +25,12 @@ namespace Roadie.Api.Controllers
{ {
private IGenreService GenreService { get; } private IGenreService GenreService { get; }
public GenreController(IGenreService genreService, ILogger<GenreController> logger, ICacheManager cacheManager, public GenreController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IGenreService genreService,
ILogger<GenreController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -38,9 +42,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null) public async Task<IActionResult> Get(Guid id, string inc = null)
{ {
var result = await GenreService.ById(await CurrentUserModel(), id, (inc ?? models.Genre.DefaultIncludes).ToLower().Split(",")); var result = await GenreService.ByIdAsync(await CurrentUserModel().ConfigureAwait(false), id, (inc ?? models.Genre.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) return NotFound(); if (result == null || result.IsNotFoundResult)
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); {
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -50,10 +62,14 @@ namespace Roadie.Api.Controllers
{ {
try try
{ {
var result = await GenreService.List(await CurrentUserModel(), var result = await GenreService.ListAsync(await CurrentUserModel().ConfigureAwait(false),
request, request,
doRandomize); doRandomize).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
@ -74,7 +90,7 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> SetGenreImageByUrl(Guid id, string imageUrl) public async Task<IActionResult> SetGenreImageByUrl(Guid id, string imageUrl)
{ {
var result = await GenreService.SetGenreImageByUrl(await CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl)); var result = await GenreService.SetGenreImageByUrlAsync(await CurrentUserModel().ConfigureAwait(false), id, HttpUtility.UrlDecode(imageUrl)).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -94,29 +110,6 @@ namespace Roadie.Api.Controllers
return Ok(result); 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 GenreService.UploadGenreImage(await CurrentUserModel(), id, file);
if (result == null || result.IsNotFoundResult) return NotFound();
if (!result.IsSuccess)
{
if (result.IsAccessDeniedResult)
{
return StatusCode((int)HttpStatusCode.Forbidden);
}
if (result.Messages?.Any() ?? false)
{
return StatusCode((int)HttpStatusCode.BadRequest, result.Messages);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("edit")] [HttpPost("edit")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
@ -127,7 +120,7 @@ namespace Roadie.Api.Controllers
{ {
return BadRequest(ModelState); return BadRequest(ModelState);
} }
var result = await GenreService.UpdateGenre(await CurrentUserModel(), genre); var result = await GenreService.UpdateGenreAsync(await CurrentUserModel().ConfigureAwait(false), genre).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -142,5 +135,32 @@ namespace Roadie.Api.Controllers
} }
return Ok(result); 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 GenreService.UploadGenreImageAsync(await CurrentUserModel().ConfigureAwait(false), id, file).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
if (result.IsAccessDeniedResult)
{
return StatusCode((int)HttpStatusCode.Forbidden);
}
if (result.Messages?.Any() ?? false)
{
return StatusCode((int)HttpStatusCode.BadRequest, result.Messages);
}
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
} }
} }

View file

@ -20,20 +20,26 @@ namespace Roadie.Api.Controllers
{ {
private IImageService ImageService { get; } private IImageService ImageService { get; }
public ImageController(IImageService imageService, ILogger<ImageController> logger, ICacheManager cacheManager, public ImageController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IImageService imageService,
ILogger<ImageController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
ImageService = imageService; ImageService = imageService;
} }
private IActionResult MakeFileResult(byte[] bytes, string fileName, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue eTag) => File(bytes, contentType, fileName, lastModified, eTag);
[HttpGet("artist/{id}/{width:int?}/{height:int?}/{cacheBuster?}")] [HttpGet("artist/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> ArtistImage(Guid id, int? width, int? height) public async Task<IActionResult> ArtistImage(Guid id, int? width, int? height)
{ {
var result = await ImageService.ArtistImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false); var result = await ImageService.ArtistImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -50,7 +56,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height) public async Task<IActionResult> ArtistSecondaryImage(Guid id, int imageId, int? width, int? height)
{ {
var result = await ImageService.ArtistSecondaryImage(id, imageId, width, height).ConfigureAwait(false); var result = await ImageService.ArtistSecondaryImageAsync(id, imageId, width, height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -67,24 +73,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> CollectionImage(Guid id, int? width, int? height) public async Task<IActionResult> CollectionImage(Guid id, int? width, int? height)
{ {
var result = await ImageService.CollectionImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false); var result = await ImageService.CollectionImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return MakeFileResult(result.Data.Bytes, $"{id}.jpg", result.ContentType, result.LastModified, result.ETag);
}
[HttpGet("label/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> LabelImage(Guid id, int? width, int? height)
{
var result = await ImageService.LabelImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -101,7 +90,24 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> GenreImage(Guid id, int? width, int? height) public async Task<IActionResult> GenreImage(Guid id, int? width, int? height)
{ {
var result = await ImageService.GenreImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false); var result = await ImageService.GenreImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return MakeFileResult(result.Data.Bytes, $"{id}.jpg", result.ContentType, result.LastModified, result.ETag);
}
[HttpGet("label/{id}/{width:int?}/{height:int?}/{cacheBuster?}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> LabelImage(Guid id, int? width, int? height)
{
var result = await ImageService.LabelImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -118,7 +124,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> PlaylistImage(Guid id, int? width, int? height) public async Task<IActionResult> PlaylistImage(Guid id, int? width, int? height)
{ {
var result = await ImageService.PlaylistImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false); var result = await ImageService.PlaylistImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -135,7 +141,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> ReleaseImage(Guid id, int? width, int? height) public async Task<IActionResult> ReleaseImage(Guid id, int? width, int? height)
{ {
var result = await ImageService.ReleaseImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false); var result = await ImageService.ReleaseImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -152,7 +158,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height) public async Task<IActionResult> ReleaseSecondaryImage(Guid id, int imageId, int? width, int? height)
{ {
var result = await ImageService.ReleaseSecondaryImage(id, imageId, width ?? RoadieSettings.MaximumImageSize.Width, height ?? RoadieSettings.MaximumImageSize.Height).ConfigureAwait(false); var result = await ImageService.ReleaseSecondaryImageAsync(id, imageId, width ?? RoadieSettings.MaximumImageSize.Width, height ?? RoadieSettings.MaximumImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -169,24 +175,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> SearchForArtistImage(string query, int? resultsCount) public async Task<IActionResult> SearchForArtistImage(string query, int? resultsCount)
{ {
var result = await ImageService.Search(query, resultsCount ?? 10).ConfigureAwait(false); var result = await ImageService.SearchAsync(query, resultsCount ?? 10).ConfigureAwait(false);
if (result?.IsNotFoundResult != false)
{
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 ImageService.Search(query, resultsCount ?? 10).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -203,9 +192,34 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> SearchForGenreImage(string query, int? resultsCount) public async Task<IActionResult> SearchForGenreImage(string query, int? resultsCount)
{ {
var result = await ImageService.Search(query, resultsCount ?? 10).ConfigureAwait(false); var result = await ImageService.SearchAsync(query, resultsCount ?? 10).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) return NotFound(); if (result?.IsNotFoundResult != false)
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); {
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 ImageService.SearchAsync(query, resultsCount ?? 10).ConfigureAwait(false);
if (result?.IsNotFoundResult != false)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -214,9 +228,17 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> SearchForReleaseCover(string query, int? resultsCount) public async Task<IActionResult> SearchForReleaseCover(string query, int? resultsCount)
{ {
var result = await ImageService.Search(query, resultsCount ?? 10).ConfigureAwait(false); var result = await ImageService.SearchAsync(query, resultsCount ?? 10).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) return NotFound(); if (result?.IsNotFoundResult != false)
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); {
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -225,7 +247,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> TrackImage(Guid id, int? width, int? height) public async Task<IActionResult> TrackImage(Guid id, int? width, int? height)
{ {
var result = await ImageService.TrackImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false); var result = await ImageService.TrackImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -237,8 +259,6 @@ namespace Roadie.Api.Controllers
return MakeFileResult(result.Data.Bytes, $"{id}.jpg", result.ContentType, result.LastModified, result.ETag); return MakeFileResult(result.Data.Bytes, $"{id}.jpg", result.ContentType, result.LastModified, result.ETag);
} }
private IActionResult MakeFileResult(byte[] bytes, string fileName, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue eTag) => File(bytes, contentType, fileName, lastModified, eTag);
/// <summary> /// <summary>
/// NOTE that user images/avatars are GIF not JPG this is so it looks better in the menus/applications and allows for animated avatars. /// NOTE that user images/avatars are GIF not JPG this is so it looks better in the menus/applications and allows for animated avatars.
/// </summary> /// </summary>
@ -247,7 +267,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> UserImage(Guid id, int? width, int? height) public async Task<IActionResult> UserImage(Guid id, int? width, int? height)
{ {
var result = await ImageService.UserImage(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false); var result = await ImageService.UserImageAsync(id, width ?? RoadieSettings.ThumbnailImageSize.Width, height ?? RoadieSettings.ThumbnailImageSize.Height).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();

View file

@ -25,8 +25,12 @@ namespace Roadie.Api.Controllers
{ {
private ILabelService LabelService { get; } private ILabelService LabelService { get; }
public LabelController(ILabelService labelService, ILogger<LabelController> logger, ICacheManager cacheManager, public LabelController(
UserManager<User> userManager, IRoadieSettings roadieSettings) ILabelService labelService,
ILogger<LabelController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -38,7 +42,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null) public async Task<IActionResult> Get(Guid id, string inc = null)
{ {
var result = await LabelService.ById(await CurrentUserModel(), id, (inc ?? models.Label.DefaultIncludes).ToLower().Split(",")); var result = await LabelService.ByIdAsync(await CurrentUserModel().ConfigureAwait(false), id, (inc ?? models.Label.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -56,10 +60,14 @@ namespace Roadie.Api.Controllers
{ {
try try
{ {
var result = await LabelService.List(await CurrentUserModel(), var result = await LabelService.ListAsync(await CurrentUserModel().ConfigureAwait(false),
request, request,
doRandomize); doRandomize).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
@ -80,7 +88,7 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> MergeLabels(Guid labelToMergeId, Guid labelToMergeIntoId) public async Task<IActionResult> MergeLabels(Guid labelToMergeId, Guid labelToMergeIntoId)
{ {
var result = await LabelService.MergeLabelsIntoLabel(await UserManager.GetUserAsync(User), labelToMergeIntoId, new Guid[1] { labelToMergeId }); var result = await LabelService.MergeLabelsIntoLabelAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), labelToMergeIntoId, new Guid[1] { labelToMergeId }).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -98,7 +106,7 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> SetLabelImageByUrl(Guid id, string imageUrl) public async Task<IActionResult> SetLabelImageByUrl(Guid id, string imageUrl)
{ {
var result = await LabelService.SetLabelImageByUrl(await CurrentUserModel(), id, HttpUtility.UrlDecode(imageUrl)); var result = await LabelService.SetLabelImageByUrlAsync(await CurrentUserModel().ConfigureAwait(false), id, HttpUtility.UrlDecode(imageUrl)).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -120,7 +128,7 @@ namespace Roadie.Api.Controllers
{ {
return BadRequest(ModelState); return BadRequest(ModelState);
} }
var result = await LabelService.UpdateLabel(await CurrentUserModel(), label); var result = await LabelService.UpdateLabelAsync(await CurrentUserModel().ConfigureAwait(false), label).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -142,9 +150,17 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> UploadImage(Guid id, IFormFile file) public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
{ {
var result = await LabelService.UploadLabelImage(await CurrentUserModel(), id, file); var result = await LabelService.UploadLabelImageAsync(await CurrentUserModel().ConfigureAwait(false), id, file).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) return NotFound(); if (result == null || result.IsNotFoundResult)
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); {
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
} }

View file

@ -20,8 +20,12 @@ namespace Roadie.Api.Controllers
{ {
private ILookupService LookupService { get; } private ILookupService LookupService { get; }
public LookupController(ILabelService labelService, ILogger<LookupController> logger, ICacheManager cacheManager, public LookupController(
UserManager<User> userManager, ILookupService lookupService, IRoadieSettings roadieSettings) ILogger<LookupController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
ILookupService lookupService,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -33,8 +37,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> ArtistTypes(Guid id, string inc = null) public async Task<IActionResult> ArtistTypes(Guid id, string inc = null)
{ {
var result = await LookupService.ArtistTypes(); var result = await LookupService.ArtistTypesAsync().ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -43,8 +51,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> BandStatus(Guid id, string inc = null) public async Task<IActionResult> BandStatus(Guid id, string inc = null)
{ {
var result = await LookupService.BandStatus(); var result = await LookupService.BandStatusAsync().ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -53,8 +65,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> BookmarkTypes(Guid id, string inc = null) public async Task<IActionResult> BookmarkTypes(Guid id, string inc = null)
{ {
var result = await LookupService.BookmarkTypes(); var result = await LookupService.BookmarkTypesAsync().ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -63,58 +79,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> CollectionTypes(Guid id, string inc = null) public async Task<IActionResult> CollectionTypes(Guid id, string inc = null)
{ {
var result = await LookupService.CollectionTypes(); var result = await LookupService.CollectionTypesAsync().ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
return Ok(result); {
return StatusCode((int)HttpStatusCode.InternalServerError);
} }
[HttpGet("libraryStatus")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> LibraryStatus(Guid id, string inc = null)
{
var result = await LookupService.LibraryStatus();
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
return Ok(result);
}
[HttpGet("queMessageTypes")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> QueMessageTypes(Guid id, string inc = null)
{
var result = await LookupService.QueMessageTypes();
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
return Ok(result);
}
[HttpGet("releaseTypes")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> ReleaseTypes(Guid id, string inc = null)
{
var result = await LookupService.ReleaseTypes();
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
return Ok(result);
}
[HttpGet("requestStatus")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> RequestStatus(Guid id, string inc = null)
{
var result = await LookupService.RequestStatus();
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
return Ok(result);
}
[HttpGet("status")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> Status(Guid id, string inc = null)
{
var result = await LookupService.Status();
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError);
return Ok(result); return Ok(result);
} }
@ -123,8 +93,82 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> CreditCategories(Guid id, string inc = null) public async Task<IActionResult> CreditCategories(Guid id, string inc = null)
{ {
var result = await LookupService.Status(); var result = await LookupService.StatusAsync().ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("libraryStatus")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> LibraryStatus(Guid id, string inc = null)
{
var result = await LookupService.LibraryStatusAsync().ConfigureAwait(false);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("queMessageTypes")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> QueMessageTypes(Guid id, string inc = null)
{
var result = await LookupService.QueMessageTypesAsync().ConfigureAwait(false);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("releaseTypes")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> ReleaseTypes(Guid id, string inc = null)
{
var result = await LookupService.ReleaseTypesAsync().ConfigureAwait(false);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("requestStatus")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> RequestStatus(Guid id, string inc = null)
{
var result = await LookupService.RequestStatusAsync().ConfigureAwait(false);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpGet("status")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<IActionResult> Status(Guid id, string inc = null)
{
var result = await LookupService.StatusAsync().ConfigureAwait(false);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
} }

View file

@ -22,9 +22,12 @@ namespace Roadie.Api.Controllers
{ {
private IPlayActivityService PlayActivityService { get; } private IPlayActivityService PlayActivityService { get; }
public PlayActivityController(IPlayActivityService playActivityService, ILogger<PlayActivityController> logger, public PlayActivityController(
IPlayActivityService playActivityService,
ILogger<PlayActivityController> logger,
ICacheManager cacheManager, ICacheManager cacheManager,
UserManager<User> userManager, IRoadieSettings roadieSettings) UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -35,8 +38,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> PlayActivity([FromQuery] PagedRequest request) public async Task<IActionResult> PlayActivity([FromQuery] PagedRequest request)
{ {
var result = await PlayActivityService.List(request).ConfigureAwait(false); var result = await PlayActivityService.ListAsync(request).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -45,11 +52,19 @@ namespace Roadie.Api.Controllers
public async Task<IActionResult> PlayActivity([FromQuery] PagedRequest request, Guid userId) public async Task<IActionResult> PlayActivity([FromQuery] PagedRequest request, Guid userId)
{ {
var user = UserManager.Users.FirstOrDefault(x => x.RoadieId == userId); var user = UserManager.Users.FirstOrDefault(x => x.RoadieId == userId);
if (user == null) return NotFound(); if (user == null)
var result = await PlayActivityService.List(request, {
return NotFound();
}
var result = await PlayActivityService.ListAsync(request,
UserModelForUser(user)).ConfigureAwait(false); UserModelForUser(user)).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
} }

View file

@ -28,8 +28,13 @@ namespace Roadie.Api.Controllers
private ITrackService TrackService { get; } private ITrackService TrackService { get; }
public PlayController(ITrackService trackService, IReleaseService releaseService, IPlayActivityService playActivityService, public PlayController(
ILogger<PlayController> logger, ICacheManager cacheManager, UserManager<User> userManager, ITrackService trackService,
IReleaseService releaseService,
IPlayActivityService playActivityService,
ILogger<PlayController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings) IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
@ -43,8 +48,12 @@ namespace Roadie.Api.Controllers
public async Task<FileResult> M3uForRelease(Guid id) public async Task<FileResult> M3uForRelease(Guid id)
{ {
var user = await CurrentUserModel().ConfigureAwait(false); var user = await CurrentUserModel().ConfigureAwait(false);
var release = await ReleaseService.ById(user, id, new string[1] { "tracks" }).ConfigureAwait(false); var release = await ReleaseService.ByIdAsync(user, id, new string[1] { "tracks" }).ConfigureAwait(false);
if (release?.IsNotFoundResult != false) Response.StatusCode = (int)HttpStatusCode.NotFound; if (release?.IsNotFoundResult != false)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
var m3u = M3uHelper.M3uContentForTracks(release.Data.Medias.SelectMany(x => x.Tracks)); var m3u = M3uHelper.M3uContentForTracks(release.Data.Medias.SelectMany(x => x.Tracks));
return File(Encoding.Default.GetBytes(m3u), "audio/mpeg-url"); return File(Encoding.Default.GetBytes(m3u), "audio/mpeg-url");
} }
@ -53,10 +62,18 @@ namespace Roadie.Api.Controllers
public async Task<FileResult> M3uForTrack(Guid id) public async Task<FileResult> M3uForTrack(Guid id)
{ {
var user = await CurrentUserModel().ConfigureAwait(false); var user = await CurrentUserModel().ConfigureAwait(false);
var track = await TrackService.ById(user, id, null).ConfigureAwait(false); var track = await TrackService.ByIdAsyncAsync(user, id, null).ConfigureAwait(false);
if (track?.IsNotFoundResult != false) Response.StatusCode = (int)HttpStatusCode.NotFound; if (track?.IsNotFoundResult != false)
var release = await ReleaseService.ById(user, track.Data.Release.Id, new string[1] { "tracks" }).ConfigureAwait(false); {
if (release?.IsNotFoundResult != false) Response.StatusCode = (int)HttpStatusCode.NotFound; Response.StatusCode = (int)HttpStatusCode.NotFound;
}
var release = await ReleaseService.ByIdAsync(user, track.Data.Release.Id, new string[1] { "tracks" }).ConfigureAwait(false);
if (release?.IsNotFoundResult != false)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
var m3u = M3uHelper.M3uContentForTracks( var m3u = M3uHelper.M3uContentForTracks(
release.Data.Medias.SelectMany(x => x.Tracks).Where(x => x.Id == id)); release.Data.Medias.SelectMany(x => x.Tracks).Where(x => x.Id == id));
return File(Encoding.Default.GetBytes(m3u), "audio/mpeg-url"); return File(Encoding.Default.GetBytes(m3u), "audio/mpeg-url");
@ -71,14 +88,22 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Scrobble(Guid id, string startedPlaying, bool isRandom) public async Task<IActionResult> Scrobble(Guid id, string startedPlaying, bool isRandom)
{ {
var result = await PlayActivityService.Scrobble(await CurrentUserModel().ConfigureAwait(false), new ScrobbleInfo var result = await PlayActivityService.ScrobbleAsync(await CurrentUserModel().ConfigureAwait(false), new ScrobbleInfo
{ {
TrackId = id, TrackId = id,
TimePlayed = SafeParser.ToDateTime(startedPlaying) ?? DateTime.UtcNow, TimePlayed = SafeParser.ToDateTime(startedPlaying) ?? DateTime.UtcNow,
IsRandomizedScrobble = isRandom IsRandomizedScrobble = isRandom
}).ConfigureAwait(false); }).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) return NotFound(); if (result?.IsNotFoundResult != false)
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); {
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -90,7 +115,11 @@ namespace Roadie.Api.Controllers
public async Task<IActionResult> StreamTrack(int userId, string trackPlayToken, Guid id) public async Task<IActionResult> StreamTrack(int userId, string trackPlayToken, Guid id)
{ {
var user = UserManager.Users.FirstOrDefault(x => x.Id == userId); var user = UserManager.Users.FirstOrDefault(x => x.Id == userId);
if (user == null) return StatusCode((int)HttpStatusCode.Unauthorized); 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); return StatusCode((int)HttpStatusCode.Unauthorized);

View file

@ -23,8 +23,12 @@ namespace Roadie.Api.Controllers
{ {
private IPlaylistService PlaylistService { get; } private IPlaylistService PlaylistService { get; }
public PlaylistController(IPlaylistService playlistService, ILogger<PlaylistController> logger, ICacheManager cacheManager, public PlaylistController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IPlaylistService playlistService,
ILogger<PlaylistController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -36,7 +40,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> AddNewPlaylist([FromBody] Playlist model) public async Task<IActionResult> AddNewPlaylist([FromBody] Playlist model)
{ {
var result = await PlaylistService.AddNewPlaylist(await CurrentUserModel(), model); var result = await PlaylistService.AddNewPlaylistAsync(await CurrentUserModel().ConfigureAwait(false), model).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -57,7 +61,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> DeletePlaylist(Guid id) public async Task<IActionResult> DeletePlaylist(Guid id)
{ {
var result = await PlaylistService.DeletePlaylist(await CurrentUserModel(), id); var result = await PlaylistService.DeletePlaylistAsync(await CurrentUserModel().ConfigureAwait(false), id).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -82,7 +86,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null) public async Task<IActionResult> Get(Guid id, string inc = null)
{ {
var result = await PlaylistService.ById(await CurrentUserModel(), id, (inc ?? Playlist.DefaultIncludes).ToLower().Split(",")); var result = await PlaylistService.ByIdAsync(await CurrentUserModel().ConfigureAwait(false), id, (inc ?? Playlist.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -106,9 +110,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> List([FromQuery] PagedRequest request, string inc) public async Task<IActionResult> List([FromQuery] PagedRequest request, string inc)
{ {
var result = await PlaylistService.List(roadieUser: await CurrentUserModel(), var result = await PlaylistService.ListAsync(roadieUser: await CurrentUserModel().ConfigureAwait(false), request: request).ConfigureAwait(false);
request: request); if (!result.IsSuccess)
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); {
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
@ -117,8 +124,12 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Update(Playlist playlist) public async Task<IActionResult> Update(Playlist playlist)
{ {
if (!ModelState.IsValid) return BadRequest(ModelState); if (!ModelState.IsValid)
var result = await PlaylistService.UpdatePlaylist(await CurrentUserModel(), playlist); {
return BadRequest(ModelState);
}
var result = await PlaylistService.UpdatePlaylistAsync(await CurrentUserModel().ConfigureAwait(false), playlist).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();
@ -143,7 +154,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> UpdateTracks(PlaylistTrackModifyRequest request) public async Task<IActionResult> UpdateTracks(PlaylistTrackModifyRequest request)
{ {
var result = await PlaylistService.UpdatePlaylistTracks(await CurrentUserModel(), request); var result = await PlaylistService.UpdatePlaylistTracksAsync(await CurrentUserModel().ConfigureAwait(false), request).ConfigureAwait(false);
if (result == null || result.IsNotFoundResult) if (result == null || result.IsNotFoundResult)
{ {
return NotFound(); return NotFound();

View file

@ -25,8 +25,12 @@ namespace Roadie.Api.Controllers
{ {
private IReleaseService ReleaseService { get; } private IReleaseService ReleaseService { get; }
public ReleaseController(IReleaseService releaseService, ILogger<ReleaseController> logger, ICacheManager cacheManager, public ReleaseController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IReleaseService releaseService,
ILogger<ReleaseController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
@ -38,7 +42,7 @@ namespace Roadie.Api.Controllers
[ProducesResponseType(404)] [ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id, string inc = null) public async Task<IActionResult> Get(Guid id, string inc = null)
{ {
var result = await ReleaseService.ById(await CurrentUserModel().ConfigureAwait(false), id, (inc ?? Release.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false); var result = await ReleaseService.ByIdAsync(await CurrentUserModel().ConfigureAwait(false), id, (inc ?? Release.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -56,11 +60,15 @@ namespace Roadie.Api.Controllers
{ {
try try
{ {
var result = await ReleaseService.List(await CurrentUserModel().ConfigureAwait(false), var result = await ReleaseService.ListAsync(await CurrentUserModel().ConfigureAwait(false),
request, request,
doRandomize ?? false, doRandomize ?? false,
(inc ?? Release.DefaultListIncludes).ToLower().Split(",")).ConfigureAwait(false); (inc ?? Release.DefaultListIncludes).ToLower().Split(",")).ConfigureAwait(false);
if (!result.IsSuccess) return StatusCode((int)HttpStatusCode.InternalServerError); if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result); return Ok(result);
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
@ -81,8 +89,12 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> MergeReleases(Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia) public async Task<IActionResult> MergeReleases(Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia)
{ {
var result = await ReleaseService.MergeReleases(await UserManager.GetUserAsync(User).ConfigureAwait(false), releaseToMergeId, releaseToMergeIntoId, addAsMedia).ConfigureAwait(false); var result = await ReleaseService.MergeReleasesAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), releaseToMergeId, releaseToMergeIntoId, addAsMedia).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) return NotFound(); if (result?.IsNotFoundResult != false)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -104,8 +116,12 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> SetReleaseImageByUrl(Guid id, string imageUrl) public async Task<IActionResult> SetReleaseImageByUrl(Guid id, string imageUrl)
{ {
var result = await ReleaseService.SetReleaseImageByUrl(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, HttpUtility.UrlDecode(imageUrl)).ConfigureAwait(false); var result = await ReleaseService.SetReleaseImageByUrlAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, HttpUtility.UrlDecode(imageUrl)).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) return NotFound(); if (result?.IsNotFoundResult != false)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)
@ -131,7 +147,7 @@ namespace Roadie.Api.Controllers
{ {
return BadRequest(ModelState); return BadRequest(ModelState);
} }
var result = await ReleaseService.UpdateRelease(await UserManager.GetUserAsync(User).ConfigureAwait(false), release).ConfigureAwait(false); var result = await ReleaseService.UpdateReleaseAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), release).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) if (result?.IsNotFoundResult != false)
{ {
return NotFound(); return NotFound();
@ -153,8 +169,12 @@ namespace Roadie.Api.Controllers
[Authorize(Policy = "Editor")] [Authorize(Policy = "Editor")]
public async Task<IActionResult> UploadImage(Guid id, IFormFile file) public async Task<IActionResult> UploadImage(Guid id, IFormFile file)
{ {
var result = await ReleaseService.UploadReleaseImage(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, file).ConfigureAwait(false); var result = await ReleaseService.UploadReleaseImageAsync(await UserManager.GetUserAsync(User).ConfigureAwait(false), id, file).ConfigureAwait(false);
if (result?.IsNotFoundResult != false) return NotFound(); if (result?.IsNotFoundResult != false)
{
return NotFound();
}
if (!result.IsSuccess) if (!result.IsSuccess)
{ {
if (result.IsAccessDeniedResult) if (result.IsAccessDeniedResult)

View file

@ -20,14 +20,22 @@ namespace Roadie.Api.Controllers
{ {
private IStatisticsService StatisticsService { get; } private IStatisticsService StatisticsService { get; }
public StatsController(IStatisticsService statisticsService, ILogger<StatsController> logger, ICacheManager cacheManager, public StatsController(
UserManager<User> userManager, IRoadieSettings roadieSettings) IStatisticsService statisticsService,
ILogger<StatsController> logger,
ICacheManager cacheManager,
UserManager<User> userManager,
IRoadieSettings roadieSettings)
: base(cacheManager, roadieSettings, userManager) : base(cacheManager, roadieSettings, userManager)
{ {
Logger = logger; Logger = logger;
StatisticsService = statisticsService; StatisticsService = statisticsService;
} }
[HttpGet("artistsByDate")]
[ProducesResponseType(200)]
public async Task<IActionResult> ArtistsByDate() => Ok(await StatisticsService.ArtistsByDateAsync().ConfigureAwait(false));
[HttpGet("info")] [HttpGet("info")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
[AllowAnonymous] [AllowAnonymous]
@ -39,12 +47,14 @@ namespace Roadie.Api.Controllers
var messages = new List<string> var messages = new List<string>
{ {
"▜ Memory Information: ", "▜ Memory Information: ",
string.Format("My process used working set {0:n3} K of working set and CPU {1:n} msec", $"My process used working set {mem / 1024.0:n3} K of working set and CPU {cpu.TotalMilliseconds:n} msec"
mem / 1024.0, cpu.TotalMilliseconds)
}; };
foreach (var aProc in Process.GetProcesses()) foreach (var aProc in Process.GetProcesses())
{
messages.Add(string.Format("Proc {0,30} CPU {1,-20:n} msec", aProc.ProcessName, messages.Add(string.Format("Proc {0,30} CPU {1,-20:n} msec", aProc.ProcessName,
cpu.TotalMilliseconds)); cpu.TotalMilliseconds));
}
messages.Add("▟ Memory Information: "); messages.Add("▟ Memory Information: ");
return Ok(messages); return Ok(messages);
@ -52,31 +62,27 @@ namespace Roadie.Api.Controllers
[HttpGet("library")] [HttpGet("library")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> Library() => Ok(await StatisticsService.LibraryStatistics()); public async Task<IActionResult> Library() => Ok(await StatisticsService.LibraryStatisticsAsync().ConfigureAwait(false));
[HttpGet("ping")] [HttpGet("ping")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
[AllowAnonymous] [AllowAnonymous]
public IActionResult Ping() => Ok("pong"); public IActionResult Ping() => Ok("pong");
[HttpGet("artistsByDate")]
[ProducesResponseType(200)]
public async Task<IActionResult> ArtistsByDate() => Ok(await StatisticsService.ArtistsByDate());
[HttpGet("releasesByDate")] [HttpGet("releasesByDate")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ReleasesByDate() => Ok(await StatisticsService.ReleasesByDate()); public async Task<IActionResult> ReleasesByDate() => Ok(await StatisticsService.ReleasesByDateAsync().ConfigureAwait(false));
[HttpGet("releasesByDecade")] [HttpGet("releasesByDecade")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> ReleasesByDecade() => Ok(await StatisticsService.ReleasesByDecade()); public async Task<IActionResult> ReleasesByDecade() => Ok(await StatisticsService.ReleasesByDecadeAsync().ConfigureAwait(false));
[HttpGet("songsPlayedByDate")] [HttpGet("songsPlayedByDate")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> SongsPlayedByDate() => Ok(await StatisticsService.SongsPlayedByDate()); public async Task<IActionResult> SongsPlayedByDate() => Ok(await StatisticsService.SongsPlayedByDateAsync().ConfigureAwait(false));
[HttpGet("songsPlayedByUser")] [HttpGet("songsPlayedByUser")]
[ProducesResponseType(200)] [ProducesResponseType(200)]
public async Task<IActionResult> SongsPlayedByUser() => Ok(await StatisticsService.SongsPlayedByUser()); public async Task<IActionResult> SongsPlayedByUser() => Ok(await StatisticsService.SongsPlayedByUserAsync().ConfigureAwait(false));
} }
} }

Some files were not shown because too many files have changed in this diff Show more