mirror of
https://github.com/sphildreth/roadie
synced 2024-11-21 19:53:11 +00:00
parent
3cfd12a330
commit
f556c28c29
83 changed files with 2451 additions and 1310 deletions
|
@ -22,7 +22,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace Roadie.Library.Tests
|
|||
IConfiguration configuration = configurationBuilder.Build();
|
||||
configuration.GetSection("RoadieSettings").Bind(settings);
|
||||
Configuration = settings;
|
||||
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
CacheManager = new DictionaryCacheManager(Logger, new NewtonsoftCacheSerializer(Logger), new CachePolicy(TimeSpan.FromHours(4)));
|
||||
HttpEncoder = new Encoding.DummyHttpEncoder();
|
||||
}
|
||||
|
||||
|
|
|
@ -290,5 +290,22 @@ namespace Roadie.Library.Tests
|
|||
var d = input.ToSecondsFromMilliseconds();
|
||||
Assert.Equal(shouldBe, d);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Song (ft. Joe)")]
|
||||
[InlineData("Song (featuring Joe)")]
|
||||
[InlineData("Song (feat. Joe)")]
|
||||
public void StringHaveFeatureFragments(string input)
|
||||
{
|
||||
Assert.True(input.HasFeaturingFragments());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Future Feature")]
|
||||
[InlineData("Feature Song")]
|
||||
public void StringShouldNotHaveFeatureFragments(string input)
|
||||
{
|
||||
Assert.False(input.HasFeaturingFragments());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace Roadie.Library.Tests
|
|||
configuration.GetSection("RoadieSettings").Bind(settings);
|
||||
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
|
||||
Configuration = settings;
|
||||
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
CacheManager = new DictionaryCacheManager(Logger, new NewtonsoftCacheSerializer(Logger), new CachePolicy(TimeSpan.FromHours(4)));
|
||||
var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>();
|
||||
tagHelperLooper.Messages += MessageLoggerMessages;
|
||||
TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper);
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace Roadie.Library.Tests
|
|||
configuration.GetSection("RoadieSettings").Bind(settings);
|
||||
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
|
||||
Configuration = settings;
|
||||
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
CacheManager = new DictionaryCacheManager(Logger, new NewtonsoftCacheSerializer(Logger), new CachePolicy(TimeSpan.FromHours(4)));
|
||||
var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>();
|
||||
tagHelperLooper.Messages += MessageLoggerMessages;
|
||||
TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper);
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Roadie.Library.Tests
|
|||
IConfiguration configuration = configurationBuilder.Build();
|
||||
configuration.GetSection("RoadieSettings").Bind(settings);
|
||||
Configuration = settings;
|
||||
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
CacheManager = new DictionaryCacheManager(Logger, new NewtonsoftCacheSerializer(Logger), new CachePolicy(TimeSpan.FromHours(4)));
|
||||
HttpEncoder = new Encoding.DummyHttpEncoder();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -10,19 +9,15 @@ namespace Roadie.Library.Caching
|
|||
public const string SystemCacheRegionUrn = "urn:system";
|
||||
|
||||
protected readonly CachePolicy _defaultPolicy;
|
||||
protected readonly JsonSerializerSettings _serializerSettings;
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
protected ICacheSerializer CacheSerializer { get; }
|
||||
|
||||
public CacheManagerBase(ILogger logger, CachePolicy defaultPolicy)
|
||||
public CacheManagerBase(ILogger logger, ICacheSerializer cacheSerializer, CachePolicy defaultPolicy)
|
||||
{
|
||||
Logger = logger;
|
||||
CacheSerializer = cacheSerializer;
|
||||
_defaultPolicy = defaultPolicy;
|
||||
_serializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new IgnoreJsonAttributesResolver(),
|
||||
Formatting = Formatting.Indented
|
||||
};
|
||||
}
|
||||
|
||||
public abstract bool Add<TCacheValue>(string key, TCacheValue value);
|
||||
|
@ -55,24 +50,5 @@ namespace Roadie.Library.Caching
|
|||
|
||||
public abstract bool Remove(string key, string region);
|
||||
|
||||
protected TOut Deserialize<TOut>(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return default(TOut);
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<TOut>(s, _serializerSettings);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
|
||||
return default(TOut);
|
||||
}
|
||||
|
||||
protected string Serialize(object o)
|
||||
{
|
||||
return JsonConvert.SerializeObject(o, _serializerSettings);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,8 +11,8 @@ namespace Roadie.Library.Caching
|
|||
|
||||
private Dictionary<string, object> Cache { get; }
|
||||
|
||||
public DictionaryCacheManager(ILogger logger, CachePolicy defaultPolicy)
|
||||
: base(logger, defaultPolicy)
|
||||
public DictionaryCacheManager(ILogger logger, ICacheSerializer cacheSerializer, CachePolicy defaultPolicy)
|
||||
: base(logger, cacheSerializer, defaultPolicy)
|
||||
{
|
||||
Cache = new Dictionary<string, object>();
|
||||
}
|
||||
|
|
12
Roadie.Api.Library/Caching/ICacheSerializer.cs
Normal file
12
Roadie.Api.Library/Caching/ICacheSerializer.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Roadie.Library.Caching
|
||||
{
|
||||
public interface ICacheSerializer
|
||||
{
|
||||
string Serialize(object o);
|
||||
TOut Deserialize<TOut>(string s);
|
||||
}
|
||||
}
|
|
@ -9,8 +9,8 @@ namespace Roadie.Library.Caching
|
|||
{
|
||||
private MemoryCache _cache;
|
||||
|
||||
public MemoryCacheManager(ILogger logger, CachePolicy defaultPolicy)
|
||||
: base(logger, defaultPolicy)
|
||||
public MemoryCacheManager(ILogger logger, ICacheSerializer cacheSerializer, CachePolicy defaultPolicy)
|
||||
: base(logger, cacheSerializer, defaultPolicy)
|
||||
{
|
||||
_cache = new MemoryCache(new MemoryCacheOptions());
|
||||
}
|
||||
|
|
43
Roadie.Api.Library/Caching/NewtonsoftCacheSerializer.cs
Normal file
43
Roadie.Api.Library/Caching/NewtonsoftCacheSerializer.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace Roadie.Library.Caching
|
||||
{
|
||||
public class NewtonsoftCacheSerializer : ICacheSerializer
|
||||
{
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected readonly JsonSerializerSettings _serializerSettings;
|
||||
|
||||
public NewtonsoftCacheSerializer(ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
_serializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new IgnoreJsonAttributesResolver(),
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
Formatting = Formatting.Indented
|
||||
};
|
||||
}
|
||||
|
||||
public TOut Deserialize<TOut>(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return default(TOut);
|
||||
}
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<TOut>(s, _serializerSettings);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
return default(TOut);
|
||||
}
|
||||
|
||||
public string Serialize(object o) => JsonConvert.SerializeObject(o, _serializerSettings);
|
||||
}
|
||||
}
|
|
@ -26,8 +26,8 @@ namespace Roadie.Library.Caching
|
|||
|
||||
private IDatabase Redis => _redis ?? (_redis = Connection.GetDatabase());
|
||||
|
||||
public RedisCacheManager(ILogger logger, CachePolicy defaultPolicy)
|
||||
: base(logger, defaultPolicy)
|
||||
public RedisCacheManager(ILogger logger, ICacheSerializer cacheSerializer, CachePolicy defaultPolicy)
|
||||
: base(logger, cacheSerializer, defaultPolicy)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ namespace Roadie.Library.Caching
|
|||
public override bool Add<TCacheValue>(string key, TCacheValue value, string region, CachePolicy policy)
|
||||
{
|
||||
if (_doTraceLogging) Logger.LogTrace("Added [{0}], Region [{1}]", key, region);
|
||||
return Redis.StringSet(key, Serialize(value));
|
||||
return Redis.StringSet(key, CacheSerializer.Serialize(value));
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
|
@ -135,7 +135,7 @@ namespace Roadie.Library.Caching
|
|||
|
||||
private TOut Get<TOut>(string key, string region, CachePolicy policy)
|
||||
{
|
||||
var result = Deserialize<TOut>(Redis.StringGet(key));
|
||||
var result = CacheSerializer.Deserialize<TOut>(Redis.StringGet(key));
|
||||
if (result == null)
|
||||
{
|
||||
if (_doTraceLogging) Logger.LogTrace("Get Cache Miss Key [{0}], Region [{1}]", key, region);
|
||||
|
|
52
Roadie.Api.Library/Caching/Utf8JsonCacheSerializer.cs
Normal file
52
Roadie.Api.Library/Caching/Utf8JsonCacheSerializer.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Utf8Json;
|
||||
|
||||
namespace Roadie.Library.Caching
|
||||
{
|
||||
public class Utf8JsonCacheSerializer : ICacheSerializer
|
||||
{
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
public Utf8JsonCacheSerializer(ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public string Serialize(object o)
|
||||
{
|
||||
if(o == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return System.Text.Encoding.UTF8.GetString(JsonSerializer.Serialize(o));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public TOut Deserialize<TOut>(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return default(TOut);
|
||||
}
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<TOut>(s);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
return default(TOut);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,6 +46,8 @@ namespace Roadie.Library.Configuration
|
|||
|
||||
public string UnknownFolder { get; set; }
|
||||
|
||||
public bool DoDetectFeatureFragments { get; set; }
|
||||
|
||||
public Processing()
|
||||
{
|
||||
DoAudioCleanup = true;
|
||||
|
@ -55,6 +57,7 @@ namespace Roadie.Library.Configuration
|
|||
DoParseFromLastFM = true;
|
||||
DoParseFromMusicBrainz = true;
|
||||
DoSaveEditsToTags = true;
|
||||
DoDetectFeatureFragments = true;
|
||||
|
||||
MaximumArtistImagesToAdd = 12;
|
||||
MaximumReleaseImagesToAdd = 12;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using CsvHelper;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
|
@ -14,6 +12,10 @@ namespace Roadie.Library.Data
|
|||
{
|
||||
public partial class Collection
|
||||
{
|
||||
public const string ArtistPosition = "artist";
|
||||
public const string PositionPosition = "position";
|
||||
public const string ReleasePosition = "release";
|
||||
|
||||
/// <summary>
|
||||
/// If the given value in either Artist or Release starts with this then the next value is the database Id, example "1,~4,~19"
|
||||
/// </summary>
|
||||
|
@ -35,7 +37,10 @@ namespace Roadie.Library.Data
|
|||
foreach (var pos in ListInCSVFormat.Split(','))
|
||||
{
|
||||
looper++;
|
||||
if (pos.ToLower().Equals("artist")) _artistColumn = looper;
|
||||
if (String.Equals(pos, ArtistPosition, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_artistColumn = looper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +75,10 @@ namespace Roadie.Library.Data
|
|||
foreach (var pos in ListInCSVFormat.Split(','))
|
||||
{
|
||||
looper++;
|
||||
if (pos.ToLower().Equals("position")) _positionColumn = looper;
|
||||
if (String.Equals(pos, PositionPosition, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_positionColumn = looper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +96,10 @@ namespace Roadie.Library.Data
|
|||
foreach (var pos in ListInCSVFormat.Split(','))
|
||||
{
|
||||
looper++;
|
||||
if (pos.ToLower().Equals("release")) _releaseColumn = looper;
|
||||
if (String.Equals(pos, ReleasePosition, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_releaseColumn = looper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,10 +140,7 @@ namespace Roadie.Library.Data
|
|||
MissingFieldFound = null,
|
||||
HasHeaderRecord = false
|
||||
};
|
||||
configuration.BadDataFound = context =>
|
||||
{
|
||||
Trace.WriteLine($"PositionArtistReleases: Bad data found on row '{context.RawRow}'", "Warning");
|
||||
};
|
||||
configuration.BadDataFound = context => Trace.WriteLine($"PositionArtistReleases: Bad data found on row '{context.RawRow}'", "Warning");
|
||||
using (var csv = new CsvReader(sr, configuration))
|
||||
{
|
||||
while (csv.Read())
|
||||
|
@ -160,6 +168,4 @@ namespace Roadie.Library.Data
|
|||
return $"Id [{Id}], Name [{Name}], RoadieId [{RoadieId}]";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Data;
|
||||
|
@ -19,6 +18,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using discogs = Roadie.Library.SearchEngines.MetaData.Discogs;
|
||||
using lastfm = Roadie.Library.MetaData.LastFm;
|
||||
|
@ -251,7 +251,7 @@ namespace Roadie.Library.Engines
|
|||
}
|
||||
if (!string.IsNullOrEmpty(releaseRoadieDataFilename) && File.Exists(releaseRoadieDataFilename))
|
||||
{
|
||||
artist = JsonConvert.DeserializeObject<Artist>(File.ReadAllText(releaseRoadieDataFilename));
|
||||
artist = JsonSerializer.Deserialize<Artist>(File.ReadAllText(releaseRoadieDataFilename));
|
||||
var addResult = await Add(artist).ConfigureAwait(false);
|
||||
if (!addResult.IsSuccess)
|
||||
{
|
||||
|
|
|
@ -30,7 +30,11 @@ namespace Roadie.Library.Engines
|
|||
{
|
||||
public class ReleaseLookupEngine : LookupEngineBase, IReleaseLookupEngine
|
||||
{
|
||||
private IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
private ILabelLookupEngine LabelLookupEngine { get; }
|
||||
|
||||
public List<int> _addedReleaseIds = new List<int>();
|
||||
|
||||
public List<int> _addedTrackIds = new List<int>();
|
||||
|
||||
public IEnumerable<int> AddedReleaseIds => _addedReleaseIds;
|
||||
|
@ -49,10 +53,6 @@ namespace Roadie.Library.Engines
|
|||
|
||||
public IReleaseSearchEngine WikipediaReleaseSearchEngine { get; }
|
||||
|
||||
private IArtistLookupEngine ArtistLookupEngine { get; }
|
||||
|
||||
private ILabelLookupEngine LabelLookupEngine { get; }
|
||||
|
||||
public ReleaseLookupEngine(IRoadieSettings configuration, IHttpEncoder httpEncoder, IRoadieDbContext context,
|
||||
ICacheManager cacheManager, ILogger<ReleaseLookupEngine> logger, IArtistLookupEngine artistLookupEngine,
|
||||
ILabelLookupEngine labelLookupEngine, musicbrainz.IMusicBrainzProvider musicBrainzProvider, lastfm.ILastFmHelper lastFmHelper,
|
||||
|
@ -117,7 +117,11 @@ namespace Roadie.Library.Engines
|
|||
{
|
||||
var genreName = releaseGenreTable.ToAlphanumericName().ToTitleCase();
|
||||
var normalizedName = genreName.ToUpper();
|
||||
if (string.IsNullOrEmpty(genreName)) continue;
|
||||
if (string.IsNullOrEmpty(genreName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (genreName.Length > 100)
|
||||
{
|
||||
var originalName = genreName;
|
||||
|
@ -204,76 +208,73 @@ namespace Roadie.Library.Engines
|
|||
}
|
||||
}
|
||||
|
||||
if (doAddTracksInDatabase)
|
||||
if (doAddTracksInDatabase && releaseMedias?.Any(x => x.Status == Statuses.New) == true)
|
||||
{
|
||||
if (releaseMedias?.Any(x => x.Status == Statuses.New) == true)
|
||||
foreach (var newReleaseMedia in releaseMedias.Where(x => x.Status == Statuses.New))
|
||||
{
|
||||
foreach (var newReleaseMedia in releaseMedias.Where(x => x.Status == Statuses.New))
|
||||
var releasemedia = new ReleaseMedia
|
||||
{
|
||||
var releasemedia = new ReleaseMedia
|
||||
Status = Statuses.Incomplete,
|
||||
MediaNumber = newReleaseMedia.MediaNumber,
|
||||
SubTitle = newReleaseMedia.SubTitle,
|
||||
TrackCount = newReleaseMedia.TrackCount,
|
||||
ReleaseId = release.Id
|
||||
};
|
||||
var releasemediatracks = new List<Track>();
|
||||
foreach (var newTrack in newReleaseMedia.Tracks)
|
||||
{
|
||||
int? trackArtistId = null;
|
||||
string partTitles = null;
|
||||
if (newTrack.TrackArtist != null)
|
||||
{
|
||||
Status = Statuses.Incomplete,
|
||||
MediaNumber = newReleaseMedia.MediaNumber,
|
||||
SubTitle = newReleaseMedia.SubTitle,
|
||||
TrackCount = newReleaseMedia.TrackCount,
|
||||
ReleaseId = release.Id
|
||||
};
|
||||
var releasemediatracks = new List<Track>();
|
||||
foreach (var newTrack in newReleaseMedia.Tracks)
|
||||
{
|
||||
int? trackArtistId = null;
|
||||
string partTitles = null;
|
||||
if (newTrack.TrackArtist != null)
|
||||
if (!release.IsCastRecording)
|
||||
{
|
||||
if (!release.IsCastRecording)
|
||||
var trackArtistData = await ArtistLookupEngine.GetByName(new AudioMetaData { Artist = newTrack.TrackArtist.Name }, true).ConfigureAwait(false);
|
||||
if (trackArtistData.IsSuccess)
|
||||
{
|
||||
var trackArtistData = await ArtistLookupEngine.GetByName(new AudioMetaData { Artist = newTrack.TrackArtist.Name }, true).ConfigureAwait(false);
|
||||
if (trackArtistData.IsSuccess)
|
||||
{
|
||||
trackArtistId = trackArtistData.Data.Id;
|
||||
}
|
||||
}
|
||||
else if (newTrack.TrackArtists?.Any() == true)
|
||||
{
|
||||
partTitles = string.Join("/", newTrack.TrackArtists);
|
||||
}
|
||||
else
|
||||
{
|
||||
partTitles = newTrack.TrackArtist.Name;
|
||||
trackArtistId = trackArtistData.Data.Id;
|
||||
}
|
||||
}
|
||||
|
||||
releasemediatracks.Add(new Track
|
||||
else if (newTrack.TrackArtists?.Any() == true)
|
||||
{
|
||||
ArtistId = trackArtistId,
|
||||
PartTitles = partTitles,
|
||||
Status = Statuses.Incomplete,
|
||||
TrackNumber = newTrack.TrackNumber,
|
||||
MusicBrainzId = newTrack.MusicBrainzId,
|
||||
SpotifyId = newTrack.SpotifyId,
|
||||
AmgId = newTrack.AmgId,
|
||||
Title = newTrack.Title,
|
||||
AlternateNames = newTrack.AlternateNames,
|
||||
Duration = newTrack.Duration,
|
||||
Tags = newTrack.Tags,
|
||||
ISRC = newTrack.ISRC,
|
||||
LastFMId = newTrack.LastFMId
|
||||
});
|
||||
partTitles = string.Join("/", newTrack.TrackArtists);
|
||||
}
|
||||
else
|
||||
{
|
||||
partTitles = newTrack.TrackArtist.Name;
|
||||
}
|
||||
}
|
||||
|
||||
releasemedia.Tracks = releasemediatracks;
|
||||
DbContext.ReleaseMedias.Add(releasemedia);
|
||||
_addedTrackIds.AddRange(releasemedia.Tracks.Select(x => x.Id));
|
||||
releasemediatracks.Add(new Track
|
||||
{
|
||||
ArtistId = trackArtistId,
|
||||
PartTitles = partTitles,
|
||||
Status = Statuses.Incomplete,
|
||||
TrackNumber = newTrack.TrackNumber,
|
||||
MusicBrainzId = newTrack.MusicBrainzId,
|
||||
SpotifyId = newTrack.SpotifyId,
|
||||
AmgId = newTrack.AmgId,
|
||||
Title = newTrack.Title,
|
||||
AlternateNames = newTrack.AlternateNames,
|
||||
Duration = newTrack.Duration,
|
||||
Tags = newTrack.Tags,
|
||||
ISRC = newTrack.ISRC,
|
||||
LastFMId = newTrack.LastFMId
|
||||
});
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
releasemedia.Tracks = releasemediatracks;
|
||||
DbContext.ReleaseMedias.Add(releasemedia);
|
||||
_addedTrackIds.AddRange(releasemedia.Tracks.Select(x => x.Id));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
sw.Stop();
|
||||
|
@ -314,21 +315,21 @@ namespace Roadie.Library.Engines
|
|||
var specialSearchNameEnd = $"|{specialSearchName}";
|
||||
|
||||
return await (from a in DbContext.Releases
|
||||
where a.ArtistId == artist.Id
|
||||
where a.Title.ToLower() == searchName ||
|
||||
a.Title.ToLower() == specialSearchName ||
|
||||
a.SortTitle.ToLower() == searchName ||
|
||||
a.SortTitle.ToLower() == searchSortName ||
|
||||
a.SortTitle.ToLower() == specialSearchName ||
|
||||
a.AlternateNames.ToLower().Equals(searchName) ||
|
||||
a.AlternateNames.ToLower().StartsWith(searchNameStart) ||
|
||||
a.AlternateNames.ToLower().Contains(searchNameIn) ||
|
||||
a.AlternateNames.ToLower().EndsWith(searchNameEnd) ||
|
||||
a.AlternateNames.ToLower().Equals(specialSearchName) ||
|
||||
a.AlternateNames.ToLower().StartsWith(specialSearchNameStart) ||
|
||||
a.AlternateNames.ToLower().Contains(specialSearchNameIn) ||
|
||||
a.AlternateNames.ToLower().EndsWith(specialSearchNameEnd)
|
||||
select a
|
||||
where a.ArtistId == artist.Id
|
||||
where a.Title.ToLower() == searchName ||
|
||||
a.Title.ToLower() == specialSearchName ||
|
||||
a.SortTitle.ToLower() == searchName ||
|
||||
a.SortTitle.ToLower() == searchSortName ||
|
||||
a.SortTitle.ToLower() == specialSearchName ||
|
||||
a.AlternateNames.ToLower().Equals(searchName) ||
|
||||
a.AlternateNames.ToLower().StartsWith(searchNameStart) ||
|
||||
a.AlternateNames.ToLower().Contains(searchNameIn) ||
|
||||
a.AlternateNames.ToLower().EndsWith(searchNameEnd) ||
|
||||
a.AlternateNames.ToLower().Equals(specialSearchName) ||
|
||||
a.AlternateNames.ToLower().StartsWith(specialSearchNameStart) ||
|
||||
a.AlternateNames.ToLower().Contains(specialSearchNameIn) ||
|
||||
a.AlternateNames.ToLower().EndsWith(specialSearchNameEnd)
|
||||
select a
|
||||
).FirstOrDefaultAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -348,7 +349,7 @@ namespace Roadie.Library.Engines
|
|||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var cacheRegion = new Release { Artist = artist, Title = metaData.Release }.CacheRegion;
|
||||
var cacheKey = string.Format("urn:release_by_artist_id_and_name:{0}:{1}", artist.Id, metaData.Release);
|
||||
var cacheKey = $"urn:release_by_artist_id_and_name:{artist.Id}:{metaData.Release}";
|
||||
var resultInCache = CacheManager.Get<Release>(cacheKey, cacheRegion);
|
||||
if (resultInCache != null)
|
||||
{
|
||||
|
@ -403,7 +404,11 @@ namespace Roadie.Library.Engines
|
|||
}
|
||||
}
|
||||
|
||||
if (release != null) CacheManager.Add(cacheKey, release);
|
||||
if (release != null)
|
||||
{
|
||||
CacheManager.Add(cacheKey, release);
|
||||
}
|
||||
|
||||
return new OperationResult<Release>
|
||||
{
|
||||
IsSuccess = release != null,
|
||||
|
@ -437,7 +442,11 @@ namespace Roadie.Library.Engines
|
|||
var resultsExceptions = new List<Exception>();
|
||||
var releaseGenres = new List<string>();
|
||||
// Add any Genre found in the given MetaData
|
||||
if (metaData.Genres != null) releaseGenres.AddRange(metaData.Genres);
|
||||
if (metaData.Genres != null)
|
||||
{
|
||||
releaseGenres.AddRange(metaData.Genres);
|
||||
}
|
||||
|
||||
var releaseLabels = new List<ReleaseLabelSearchResult>();
|
||||
var releaseMedias = new List<ReleaseMediaSearchResult>();
|
||||
var releaseImages = new List<IImage>();
|
||||
|
@ -464,10 +473,26 @@ namespace Roadie.Library.Engines
|
|||
result.AlternateNames = result.AlternateNames.AddToDelimitedList(i.AlternateNames);
|
||||
}
|
||||
|
||||
if (i.Tags != null) result.Tags = result.Tags.AddToDelimitedList(i.Tags);
|
||||
if (i.Urls != null) result.URLs = result.URLs.AddToDelimitedList(i.Urls);
|
||||
if (i.ImageUrls != null) releaseImageUrls.AddRange(i.ImageUrls);
|
||||
if (i.ReleaseGenres != null) releaseGenres.AddRange(i.ReleaseGenres);
|
||||
if (i.Tags != null)
|
||||
{
|
||||
result.Tags = result.Tags.AddToDelimitedList(i.Tags);
|
||||
}
|
||||
|
||||
if (i.Urls != null)
|
||||
{
|
||||
result.URLs = result.URLs.AddToDelimitedList(i.Urls);
|
||||
}
|
||||
|
||||
if (i.ImageUrls != null)
|
||||
{
|
||||
releaseImageUrls.AddRange(i.ImageUrls);
|
||||
}
|
||||
|
||||
if (i.ReleaseGenres != null)
|
||||
{
|
||||
releaseGenres.AddRange(i.ReleaseGenres);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(i.ReleaseThumbnailUrl))
|
||||
{
|
||||
releaseImages.Add(new Imaging.Image()
|
||||
|
@ -486,11 +511,22 @@ namespace Roadie.Library.Engines
|
|||
? SafeParser.ToEnum<ReleaseType>(i.ReleaseType)
|
||||
: result.ReleaseType
|
||||
});
|
||||
if (i.ReleaseLabel != null) releaseLabels.AddRange(i.ReleaseLabel);
|
||||
if (i.ReleaseMedia != null) releaseMedias.AddRange(i.ReleaseMedia);
|
||||
if (i.ReleaseLabel != null)
|
||||
{
|
||||
releaseLabels.AddRange(i.ReleaseLabel);
|
||||
}
|
||||
|
||||
if (i.ReleaseMedia != null)
|
||||
{
|
||||
releaseMedias.AddRange(i.ReleaseMedia);
|
||||
}
|
||||
}
|
||||
|
||||
if (iTunesResult.Errors != null)
|
||||
{
|
||||
resultsExceptions.AddRange(iTunesResult.Errors);
|
||||
}
|
||||
|
||||
if (iTunesResult.Errors != null) resultsExceptions.AddRange(iTunesResult.Errors);
|
||||
sw2.Stop();
|
||||
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: ITunesArtistSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
|
||||
}
|
||||
|
@ -508,13 +544,32 @@ namespace Roadie.Library.Engines
|
|||
{
|
||||
var mb = mbResult.Data.First();
|
||||
if (mb.AlternateNames != null)
|
||||
{
|
||||
result.AlternateNames = result.AlternateNames.AddToDelimitedList(mb.AlternateNames);
|
||||
if (mb.Tags != null) result.Tags = result.Tags.AddToDelimitedList(mb.Tags);
|
||||
if (mb.Urls != null) result.URLs = result.URLs.AddToDelimitedList(mb.Urls);
|
||||
if (mb.ImageUrls != null) releaseImageUrls.AddRange(mb.ImageUrls);
|
||||
if (mb.ReleaseGenres != null) releaseGenres.AddRange(mb.ReleaseGenres);
|
||||
}
|
||||
|
||||
if (mb.Tags != null)
|
||||
{
|
||||
result.Tags = result.Tags.AddToDelimitedList(mb.Tags);
|
||||
}
|
||||
|
||||
if (mb.Urls != null)
|
||||
{
|
||||
result.URLs = result.URLs.AddToDelimitedList(mb.Urls);
|
||||
}
|
||||
|
||||
if (mb.ImageUrls != null)
|
||||
{
|
||||
releaseImageUrls.AddRange(mb.ImageUrls);
|
||||
}
|
||||
|
||||
if (mb.ReleaseGenres != null)
|
||||
{
|
||||
releaseGenres.AddRange(mb.ReleaseGenres);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(mb.ReleaseTitle) &&
|
||||
!mb.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
!mb.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.AlternateNames.AddToDelimitedList(new[] { mb.ReleaseTitle });
|
||||
}
|
||||
|
@ -541,11 +596,22 @@ namespace Roadie.Library.Engines
|
|||
? SafeParser.ToEnum<ReleaseType>(mb.ReleaseType)
|
||||
: result.ReleaseType
|
||||
});
|
||||
if (mb.ReleaseLabel != null) releaseLabels.AddRange(mb.ReleaseLabel);
|
||||
if (mb.ReleaseMedia != null) releaseMedias.AddRange(mb.ReleaseMedia);
|
||||
if (mb.ReleaseLabel != null)
|
||||
{
|
||||
releaseLabels.AddRange(mb.ReleaseLabel);
|
||||
}
|
||||
|
||||
if (mb.ReleaseMedia != null)
|
||||
{
|
||||
releaseMedias.AddRange(mb.ReleaseMedia);
|
||||
}
|
||||
}
|
||||
|
||||
if (mbResult.Errors != null)
|
||||
{
|
||||
resultsExceptions.AddRange(mbResult.Errors);
|
||||
}
|
||||
|
||||
if (mbResult.Errors != null) resultsExceptions.AddRange(mbResult.Errors);
|
||||
sw2.Stop();
|
||||
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: MusicBrainzReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
|
||||
}
|
||||
|
@ -564,13 +630,32 @@ namespace Roadie.Library.Engines
|
|||
{
|
||||
var l = lastFmResult.Data.First();
|
||||
if (l.AlternateNames != null)
|
||||
{
|
||||
result.AlternateNames = result.AlternateNames.AddToDelimitedList(l.AlternateNames);
|
||||
if (l.Tags != null) result.Tags = result.Tags.AddToDelimitedList(l.Tags);
|
||||
if (l.Urls != null) result.URLs = result.URLs.AddToDelimitedList(l.Urls);
|
||||
if (l.ImageUrls != null) releaseImageUrls.AddRange(l.ImageUrls);
|
||||
if (l.ReleaseGenres != null) releaseGenres.AddRange(l.ReleaseGenres);
|
||||
}
|
||||
|
||||
if (l.Tags != null)
|
||||
{
|
||||
result.Tags = result.Tags.AddToDelimitedList(l.Tags);
|
||||
}
|
||||
|
||||
if (l.Urls != null)
|
||||
{
|
||||
result.URLs = result.URLs.AddToDelimitedList(l.Urls);
|
||||
}
|
||||
|
||||
if (l.ImageUrls != null)
|
||||
{
|
||||
releaseImageUrls.AddRange(l.ImageUrls);
|
||||
}
|
||||
|
||||
if (l.ReleaseGenres != null)
|
||||
{
|
||||
releaseGenres.AddRange(l.ReleaseGenres);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(l.ReleaseTitle) &&
|
||||
!l.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
!l.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.AlternateNames.AddToDelimitedList(new[] { l.ReleaseTitle });
|
||||
}
|
||||
|
@ -596,11 +681,22 @@ namespace Roadie.Library.Engines
|
|||
? SafeParser.ToEnum<ReleaseType>(l.ReleaseType)
|
||||
: result.ReleaseType
|
||||
});
|
||||
if (l.ReleaseLabel != null) releaseLabels.AddRange(l.ReleaseLabel);
|
||||
if (l.ReleaseMedia != null) releaseMedias.AddRange(l.ReleaseMedia);
|
||||
if (l.ReleaseLabel != null)
|
||||
{
|
||||
releaseLabels.AddRange(l.ReleaseLabel);
|
||||
}
|
||||
|
||||
if (l.ReleaseMedia != null)
|
||||
{
|
||||
releaseMedias.AddRange(l.ReleaseMedia);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastFmResult.Errors != null)
|
||||
{
|
||||
resultsExceptions.AddRange(lastFmResult.Errors);
|
||||
}
|
||||
|
||||
if (lastFmResult.Errors != null) resultsExceptions.AddRange(lastFmResult.Errors);
|
||||
sw2.Stop();
|
||||
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: LastFmReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
|
||||
}
|
||||
|
@ -617,12 +713,28 @@ namespace Roadie.Library.Engines
|
|||
if (spotifyResult.IsSuccess)
|
||||
{
|
||||
var s = spotifyResult.Data.First();
|
||||
if (s.Tags != null) result.Tags = result.Tags.AddToDelimitedList(s.Tags);
|
||||
if (s.Urls != null) result.URLs = result.URLs.AddToDelimitedList(s.Urls);
|
||||
if (s.ImageUrls != null) releaseImageUrls.AddRange(s.ImageUrls);
|
||||
if (s.ReleaseGenres != null) releaseGenres.AddRange(s.ReleaseGenres);
|
||||
if (s.Tags != null)
|
||||
{
|
||||
result.Tags = result.Tags.AddToDelimitedList(s.Tags);
|
||||
}
|
||||
|
||||
if (s.Urls != null)
|
||||
{
|
||||
result.URLs = result.URLs.AddToDelimitedList(s.Urls);
|
||||
}
|
||||
|
||||
if (s.ImageUrls != null)
|
||||
{
|
||||
releaseImageUrls.AddRange(s.ImageUrls);
|
||||
}
|
||||
|
||||
if (s.ReleaseGenres != null)
|
||||
{
|
||||
releaseGenres.AddRange(s.ReleaseGenres);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(s.ReleaseTitle) &&
|
||||
!s.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
!s.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.AlternateNames.AddToDelimitedList(new[] { s.ReleaseTitle });
|
||||
}
|
||||
|
@ -647,11 +759,22 @@ namespace Roadie.Library.Engines
|
|||
? SafeParser.ToEnum<ReleaseType>(s.ReleaseType)
|
||||
: result.ReleaseType
|
||||
});
|
||||
if (s.ReleaseLabel != null) releaseLabels.AddRange(s.ReleaseLabel);
|
||||
if (s.ReleaseMedia != null) releaseMedias.AddRange(s.ReleaseMedia);
|
||||
if (s.ReleaseLabel != null)
|
||||
{
|
||||
releaseLabels.AddRange(s.ReleaseLabel);
|
||||
}
|
||||
|
||||
if (s.ReleaseMedia != null)
|
||||
{
|
||||
releaseMedias.AddRange(s.ReleaseMedia);
|
||||
}
|
||||
}
|
||||
|
||||
if (spotifyResult.Errors != null)
|
||||
{
|
||||
resultsExceptions.AddRange(spotifyResult.Errors);
|
||||
}
|
||||
|
||||
if (spotifyResult.Errors != null) resultsExceptions.AddRange(spotifyResult.Errors);
|
||||
sw2.Stop();
|
||||
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: SpotifyReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
|
||||
}
|
||||
|
@ -668,12 +791,23 @@ namespace Roadie.Library.Engines
|
|||
if (discogsResult.IsSuccess)
|
||||
{
|
||||
var d = discogsResult.Data.First();
|
||||
if (d.Urls != null) result.URLs = result.URLs.AddToDelimitedList(d.Urls);
|
||||
if (d.ImageUrls != null) releaseImageUrls.AddRange(d.ImageUrls);
|
||||
if (d.Urls != null)
|
||||
{
|
||||
result.URLs = result.URLs.AddToDelimitedList(d.Urls);
|
||||
}
|
||||
|
||||
if (d.ImageUrls != null)
|
||||
{
|
||||
releaseImageUrls.AddRange(d.ImageUrls);
|
||||
}
|
||||
|
||||
if (d.AlternateNames != null)
|
||||
{
|
||||
result.AlternateNames = result.AlternateNames.AddToDelimitedList(d.AlternateNames);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(d.ReleaseTitle) &&
|
||||
!d.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
!d.ReleaseTitle.Equals(result.Title, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.AlternateNames.AddToDelimitedList(new[] { d.ReleaseTitle });
|
||||
}
|
||||
|
@ -694,11 +828,22 @@ namespace Roadie.Library.Engines
|
|||
? SafeParser.ToEnum<ReleaseType>(d.ReleaseType)
|
||||
: result.ReleaseType
|
||||
});
|
||||
if (d.ReleaseLabel != null) releaseLabels.AddRange(d.ReleaseLabel);
|
||||
if (d.ReleaseMedia != null) releaseMedias.AddRange(d.ReleaseMedia);
|
||||
if (d.ReleaseLabel != null)
|
||||
{
|
||||
releaseLabels.AddRange(d.ReleaseLabel);
|
||||
}
|
||||
|
||||
if (d.ReleaseMedia != null)
|
||||
{
|
||||
releaseMedias.AddRange(d.ReleaseMedia);
|
||||
}
|
||||
}
|
||||
|
||||
if (discogsResult.Errors != null)
|
||||
{
|
||||
resultsExceptions.AddRange(discogsResult.Errors);
|
||||
}
|
||||
|
||||
if (discogsResult.Errors != null) resultsExceptions.AddRange(discogsResult.Errors);
|
||||
sw2.Stop();
|
||||
Logger.LogTrace($"PerformMetaDataProvidersReleaseSearch: DiscogsReleaseSearchEngine Complete [{ sw2.ElapsedMilliseconds }]");
|
||||
}
|
||||
|
@ -885,7 +1030,10 @@ namespace Roadie.Library.Engines
|
|||
: rmTrack.AlternateNames.AddToDelimitedList(releaseTrack.AlternateNames);
|
||||
rmTrack.ISRC ??= releaseTrack.ISRC;
|
||||
rmTrack.LastFMId ??= releaseTrack.LastFMId;
|
||||
if (!foundTrack) rmTracks.Add(rmTrack);
|
||||
if (!foundTrack)
|
||||
{
|
||||
rmTracks.Add(rmTrack);
|
||||
}
|
||||
}
|
||||
|
||||
rm.Tracks = rmTracks;
|
||||
|
@ -916,7 +1064,10 @@ namespace Roadie.Library.Engines
|
|||
{
|
||||
var imageCoverByReleaseName = Array.Find(imageFilesInFolder, x =>
|
||||
x == result.Title || x == result.Title.ToFileNameFriendly());
|
||||
if (imageCoverByReleaseName != null) coverFileName = imageCoverByReleaseName;
|
||||
if (imageCoverByReleaseName != null)
|
||||
{
|
||||
coverFileName = imageCoverByReleaseName;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cover.Any())
|
||||
|
|
|
@ -15,6 +15,5 @@ namespace Roadie.Library.Extensions
|
|||
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
return Convert.ToInt64((date - epoch).TotalSeconds);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Roadie.Library.Extensions
|
||||
{
|
||||
|
@ -17,7 +16,11 @@ namespace Roadie.Library.Extensions
|
|||
|
||||
public static TimeSpan? ToTimeSpan(this decimal? value)
|
||||
{
|
||||
if (!value.HasValue) return null;
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds((double)value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Roadie.Library.Extensions
|
||||
{
|
||||
|
@ -10,7 +8,7 @@ namespace Roadie.Library.Extensions
|
|||
{
|
||||
public static string ToTimings(this IDictionary<string, long> values)
|
||||
{
|
||||
if(values == null || !values.Any())
|
||||
if (values == null || !values.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -24,7 +22,5 @@ namespace Roadie.Library.Extensions
|
|||
}
|
||||
return string.Join(", ", timings);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Roadie.Library.Extensions
|
||||
{
|
||||
|
@ -7,12 +7,15 @@ namespace Roadie.Library.Extensions
|
|||
{
|
||||
public static string Serialize(this Exception input)
|
||||
{
|
||||
if (input == null) return null;
|
||||
var settings = new JsonSerializerSettings
|
||||
if (input == null)
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
return JsonConvert.SerializeObject(input, Formatting.Indented, settings);
|
||||
return null;
|
||||
}
|
||||
return JsonSerializer.Serialize(input, new JsonSerializerOptions
|
||||
{
|
||||
IgnoreNullValues = true,
|
||||
WriteIndented = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,10 +18,16 @@ namespace Roadie.Library.Extensions
|
|||
/// <returns>The copied object.</returns>
|
||||
public static T Clone<T>(this T source)
|
||||
{
|
||||
if (!typeof(T).IsSerializable) throw new ArgumentException("The type must be serializable.", "source");
|
||||
if (!typeof(T).IsSerializable)
|
||||
{
|
||||
throw new ArgumentException("The type must be serializable.", nameof(source));
|
||||
}
|
||||
|
||||
// Don't serialize a null object, simply return the default for that object
|
||||
if (ReferenceEquals(source, null)) return default(T);
|
||||
if (ReferenceEquals(source, null))
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
using (Stream stream = new MemoryStream())
|
||||
|
@ -37,8 +43,12 @@ namespace Roadie.Library.Extensions
|
|||
var oProperties = OriginalEntity.GetType().GetProperties();
|
||||
|
||||
foreach (var CurrentProperty in oProperties.Where(p => p.CanWrite))
|
||||
{
|
||||
if (CurrentProperty.GetValue(NewEntity, null) != null)
|
||||
{
|
||||
CurrentProperty.SetValue(OriginalEntity, CurrentProperty.GetValue(NewEntity, null), null);
|
||||
}
|
||||
}
|
||||
|
||||
return OriginalEntity;
|
||||
}
|
||||
|
@ -47,7 +57,11 @@ namespace Roadie.Library.Extensions
|
|||
{
|
||||
var fi = source.GetType().GetField(source.ToString());
|
||||
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
if (attributes != null && attributes.Length > 0) return attributes[0].Description;
|
||||
if (attributes != null && attributes.Length > 0)
|
||||
{
|
||||
return attributes[0].Description;
|
||||
}
|
||||
|
||||
return source.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ namespace Roadie.Library.Extensions
|
|||
{
|
||||
public static int? Or(this int? value, int? alternative)
|
||||
{
|
||||
if (!value.HasValue && !alternative.HasValue) return null;
|
||||
if (!value.HasValue && !alternative.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.HasValue ? value : alternative;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ namespace Roadie.Library.Extensions
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static string ToDelimitedList<T>(this IList<T> list, char delimiter = '|')
|
||||
{
|
||||
return ((ICollection<T>)list).ToDelimitedList(delimiter);
|
||||
|
@ -39,7 +38,11 @@ namespace Roadie.Library.Extensions
|
|||
|
||||
public static string ToDelimitedList<T>(this IEnumerable<T> list, char delimiter = '|')
|
||||
{
|
||||
if (list == null || !list.Any()) return null;
|
||||
if (list == null || !list.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return string.Join(delimiter.ToString(), list);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ namespace Roadie.Library.Extensions
|
|||
{
|
||||
public static string ToFileSize(this long? l)
|
||||
{
|
||||
if (!l.HasValue) return "0";
|
||||
if (!l.HasValue)
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
|
||||
return string.Format(new FileSizeFormatProvider(), "{0:fs}", l);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,26 @@
|
|||
{
|
||||
public static short? Or(this short? value, short? alternative)
|
||||
{
|
||||
if (!value.HasValue && !alternative.HasValue) return null;
|
||||
if (!value.HasValue && !alternative.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.HasValue ? value : alternative;
|
||||
}
|
||||
|
||||
public static short? TakeLarger(this short? value, short? alternative)
|
||||
{
|
||||
if (!value.HasValue && !alternative.HasValue) return null;
|
||||
if (!value.HasValue && alternative.HasValue) return alternative.Value;
|
||||
if (!value.HasValue && !alternative.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!value.HasValue && alternative.HasValue)
|
||||
{
|
||||
return alternative.Value;
|
||||
}
|
||||
|
||||
return value.Value > alternative.Value ? value.Value : alternative.Value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Roadie.Library.Extensions
|
||||
{
|
||||
|
@ -88,7 +87,7 @@ namespace Roadie.Library.Extensions
|
|||
var rs = removeStringsRegex ?? settings.Processing.RemoveStringsRegex;
|
||||
if (!string.IsNullOrEmpty(rs))
|
||||
{
|
||||
result = Regex.Replace(result, rs, "", RegexOptions.IgnoreCase);
|
||||
result = Regex.Replace(result, rs, string.Empty, RegexOptions.IgnoreCase);
|
||||
}
|
||||
if (result.Length > 5)
|
||||
{
|
||||
|
@ -106,9 +105,25 @@ namespace Roadie.Library.Extensions
|
|||
return Regex.Replace(result, @"\s+", " ").Trim();
|
||||
}
|
||||
|
||||
public static bool HasFeaturingFragments(this string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Regex.IsMatch(input,
|
||||
@"\((ft.|feat.|featuring|feature)+",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static bool DoesStartWithNumber(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return false;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var firstPart = input.Split(' ').First().SafeReplace("[").SafeReplace("]");
|
||||
return SafeParser.ToNumber<long>(firstPart) > 0;
|
||||
}
|
||||
|
@ -116,32 +131,59 @@ namespace Roadie.Library.Extensions
|
|||
public static string FromHexString(this string hexString)
|
||||
{
|
||||
var bytes = new byte[hexString.Length / 2];
|
||||
for (var i = 0; i < bytes.Length; i++) bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
|
||||
}
|
||||
|
||||
return System.Text.Encoding.UTF8.GetString(bytes); // returns: "Hello world" for "48656C6C6F20776F726C64"
|
||||
}
|
||||
|
||||
public static bool IsValidFilename(this string input)
|
||||
{
|
||||
var containsABadCharacter = new Regex("[" + Regex.Escape(new string(Path.GetInvalidPathChars())) + "]");
|
||||
if (containsABadCharacter.IsMatch(input)) return false;
|
||||
;
|
||||
var containsABadCharacter = new Regex($"[{Regex.Escape(new string(Path.GetInvalidPathChars()))}]");
|
||||
if (containsABadCharacter.IsMatch(input))
|
||||
{
|
||||
return false;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsValueInDelimitedList(this string input, string value, char delimiter = '|')
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return false;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = input.Split(delimiter);
|
||||
return !p.Any() ? false : p.Any(x => x.Trim().Equals(value, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static string LastSegmentInUrl(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var uri = new Uri(input);
|
||||
return uri.Segments.Last();
|
||||
}
|
||||
|
||||
public static string NormalizeName(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
input = input.ToLower();
|
||||
var removeParts = new List<string> { " ft. ", " ft ", " feat ", " feat. " };
|
||||
foreach (var removePart in removeParts) input = input.Replace(removePart, "");
|
||||
foreach (var removePart in removeParts)
|
||||
{
|
||||
input = input.Replace(removePart, string.Empty);
|
||||
}
|
||||
|
||||
var cultInfo = new CultureInfo("en-US", false).TextInfo;
|
||||
return cultInfo.ToTitleCase(input).Trim();
|
||||
}
|
||||
|
@ -158,7 +200,10 @@ namespace Roadie.Library.Extensions
|
|||
for (var i = 0; i < normalizedString.Length; i++)
|
||||
{
|
||||
var c = normalizedString[i];
|
||||
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark) stringBuilder.Append(c);
|
||||
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
|
||||
{
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
|
@ -166,7 +211,11 @@ namespace Roadie.Library.Extensions
|
|||
|
||||
public static string RemoveFirst(this string input, string remove = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
var index = input.IndexOf(remove);
|
||||
return index < 0
|
||||
? input
|
||||
|
@ -175,7 +224,11 @@ namespace Roadie.Library.Extensions
|
|||
|
||||
public static string RemoveStartsWith(this string input, string remove = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return input;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
var index = input.IndexOf(remove);
|
||||
var result = input;
|
||||
while (index == 0)
|
||||
|
@ -194,7 +247,11 @@ namespace Roadie.Library.Extensions
|
|||
(sb, c) =>
|
||||
{
|
||||
string r;
|
||||
if (UnicodeAccents.TryGetValue(c, out r)) return sb.Append(r);
|
||||
if (UnicodeAccents.TryGetValue(c, out r))
|
||||
{
|
||||
return sb.Append(r);
|
||||
}
|
||||
|
||||
return sb.Append(c);
|
||||
}).ToString();
|
||||
}
|
||||
|
@ -211,21 +268,33 @@ namespace Roadie.Library.Extensions
|
|||
|
||||
public static string SafeReplace(this string input, string replace, string replaceWith = " ")
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return null;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return input.Replace(replace, replaceWith);
|
||||
}
|
||||
|
||||
public static string ScrubHtml(this string value)
|
||||
{
|
||||
var step1 = Regex.Replace(value, @"<[^>]+>| ", "").Trim();
|
||||
var step1 = Regex.Replace(value, @"<[^>]+>| ", string.Empty).Trim();
|
||||
var step2 = Regex.Replace(step1, @"\s{2,}", " ");
|
||||
return step2;
|
||||
}
|
||||
|
||||
public static string StripStartingNumber(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return null;
|
||||
if (input.DoesStartWithNumber()) return string.Join(" ", input.Split(' ').Skip(1));
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.DoesStartWithNumber())
|
||||
{
|
||||
return string.Join(" ", input.Split(' ').Skip(1));
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
|
@ -240,23 +309,40 @@ namespace Roadie.Library.Extensions
|
|||
.Replace("%", "per");
|
||||
input = WebUtility.HtmlDecode(input);
|
||||
input = input.ScrubHtml().ToLower()
|
||||
.Replace("&", "and");
|
||||
.Replace("&", "and");
|
||||
var arr = input.ToCharArray();
|
||||
arr = Array.FindAll(arr, c => (c == ',' && !stripCommas) || (char.IsWhiteSpace(c) && !stripSpaces) || char.IsLetterOrDigit(c));
|
||||
input = new string(arr).RemoveDiacritics().RemoveUnicodeAccents().Translit();
|
||||
input = Regex.Replace(input, $"[^A-Za-z0-9{ ( !stripSpaces ? @"\s" : "") }{ (!stripCommas ? "," : "")}]+", "");
|
||||
input = Regex.Replace(input, $"[^A-Za-z0-9{ (!stripSpaces ? @"\s" : string.Empty) }{ (!stripCommas ? "," : string.Empty)}]+", string.Empty);
|
||||
return input;
|
||||
}
|
||||
|
||||
public static string ToContentDispositionFriendly(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return null;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return input.Replace(',', ' ');
|
||||
}
|
||||
|
||||
public static string ToCSV(this IEnumerable<string> input)
|
||||
{
|
||||
if (input == null || !input.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return string.Join(",", input);
|
||||
}
|
||||
|
||||
public static string ToFileNameFriendly(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return null;
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Regex.Replace(PathSanitizer.SanitizeFilename(input, ' '), @"\s+", " ").Trim();
|
||||
}
|
||||
|
||||
|
@ -275,14 +361,21 @@ namespace Roadie.Library.Extensions
|
|||
var sb = new StringBuilder();
|
||||
|
||||
var bytes = System.Text.Encoding.UTF8.GetBytes(str);
|
||||
foreach (var t in bytes) sb.Append(t.ToString("X2"));
|
||||
foreach (var t in bytes)
|
||||
{
|
||||
sb.Append(t.ToString("X2"));
|
||||
}
|
||||
|
||||
return sb.ToString(); // returns: "48656C6C6F20776F726C64" for "Hello world"
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ToListFromDelimited(this string input, char delimiter = '|')
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return new string[0];
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
return input.Split(delimiter);
|
||||
}
|
||||
|
||||
|
@ -300,7 +393,7 @@ namespace Roadie.Library.Extensions
|
|||
{
|
||||
if (r.StartsWith("The "))
|
||||
{
|
||||
r = r.Replace("The ", "") + ", The";
|
||||
r = $"{(r.Replace("The ", string.Empty))}, The";
|
||||
}
|
||||
}
|
||||
return r.NameCase();
|
||||
|
@ -309,21 +402,36 @@ namespace Roadie.Library.Extensions
|
|||
|
||||
public static int? ToTrackDuration(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(input.Replace(":", ""))) return null;
|
||||
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(input.Replace(":", string.Empty)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var parts = input.Contains(":") ? input.Split(':').ToList() : new List<string> { input };
|
||||
while (parts.Count() < 3) parts.Insert(0, "00:");
|
||||
while (parts.Count() < 3)
|
||||
{
|
||||
parts.Insert(0, "00:");
|
||||
}
|
||||
|
||||
var tsRaw = string.Empty;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (tsRaw.Length > 0) tsRaw += ":";
|
||||
if (tsRaw.Length > 0)
|
||||
{
|
||||
tsRaw += ":";
|
||||
}
|
||||
|
||||
tsRaw += part.PadLeft(2, '0').Substring(0, 2);
|
||||
}
|
||||
|
||||
var ts = TimeSpan.MinValue;
|
||||
var success = TimeSpan.TryParse(tsRaw, out ts);
|
||||
if (success) return (int?)ts.TotalMilliseconds;
|
||||
if (success)
|
||||
{
|
||||
return (int?)ts.TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -366,27 +474,11 @@ namespace Roadie.Library.Extensions
|
|||
public static string TrimEnd(this string input, string suffixToRemove)
|
||||
{
|
||||
if (input != null && suffixToRemove != null && input.EndsWith(suffixToRemove))
|
||||
{
|
||||
return input.Substring(0, input.Length - suffixToRemove.Length);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public static string LastSegmentInUrl(this string input)
|
||||
{
|
||||
if(string.IsNullOrEmpty(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var uri = new Uri(input);
|
||||
return uri.Segments.Last();
|
||||
}
|
||||
|
||||
public static string ToCSV(this IEnumerable<string> input)
|
||||
{
|
||||
if(input == null || !input.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return string.Join(",", input);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,11 @@ namespace Roadie.Library.Extensions
|
|||
{
|
||||
public static string ToDuration(this TimeSpan input)
|
||||
{
|
||||
if (input == null || input.TotalMilliseconds == 0) return "--/--/--";
|
||||
if (input == null || input.TotalMilliseconds == 0)
|
||||
{
|
||||
return "--/--/--";
|
||||
}
|
||||
|
||||
return input.ToString(@"ddd\.hh\:mm\:ss");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Encoding;
|
||||
|
@ -14,6 +13,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.FilePlugins
|
||||
|
@ -98,9 +98,16 @@ namespace Roadie.Library.FilePlugins
|
|||
return result;
|
||||
}
|
||||
|
||||
if (CheckMakeFolder(artistFolder)) Logger.LogTrace("Created ArtistFolder [{0}]", artistFolder);
|
||||
if (CheckMakeFolder(releaseFolder)) Logger.LogTrace("Created ReleaseFolder [{0}]", releaseFolder);
|
||||
if (CheckMakeFolder(artistFolder))
|
||||
{
|
||||
Logger.LogTrace("Created ArtistFolder [{0}]", artistFolder);
|
||||
}
|
||||
if (CheckMakeFolder(releaseFolder))
|
||||
{
|
||||
Logger.LogTrace("Created ReleaseFolder [{0}]", releaseFolder);
|
||||
}
|
||||
|
||||
string imageFilename = null;
|
||||
try
|
||||
{
|
||||
// See if file folder parent folder (likely file is in release folder) has primary artist image if so then move to artist folder
|
||||
|
@ -110,8 +117,8 @@ namespace Roadie.Library.FilePlugins
|
|||
if (artistImages.Count > 0)
|
||||
{
|
||||
var artistImage = artistImages[0];
|
||||
var artistImageFilename = Path.Combine(artistFolder, ImageHelper.ArtistImageFilename);
|
||||
if (artistImageFilename != artistImage.FullName)
|
||||
imageFilename = Path.Combine(artistFolder, ImageHelper.ArtistImageFilename);
|
||||
if (imageFilename != artistImage.FullName)
|
||||
{
|
||||
// Read image and convert to jpeg
|
||||
var imageBytes = File.ReadAllBytes(artistImage.FullName);
|
||||
|
@ -120,7 +127,7 @@ namespace Roadie.Library.FilePlugins
|
|||
// Move artist image to artist folder
|
||||
if (!doJustInfo)
|
||||
{
|
||||
File.WriteAllBytes(artistImageFilename, imageBytes);
|
||||
File.WriteAllBytes(imageFilename, imageBytes);
|
||||
artistImage.Delete();
|
||||
}
|
||||
|
||||
|
@ -179,7 +186,10 @@ namespace Roadie.Library.FilePlugins
|
|||
// Read image and convert to jpeg
|
||||
var imageBytes = File.ReadAllBytes(releaseImage.FullName);
|
||||
imageBytes = ImageHelper.ConvertToJpegFormat(imageBytes);
|
||||
|
||||
if(imageBytes == null)
|
||||
{
|
||||
Logger.LogWarning($"Unable to read image [{ releaseImage.FullName }]");
|
||||
}
|
||||
// Move cover to release folder
|
||||
if (!doJustInfo)
|
||||
{
|
||||
|
@ -199,8 +209,7 @@ namespace Roadie.Library.FilePlugins
|
|||
foreach (var releaseImage in releaseImages)
|
||||
{
|
||||
looper++;
|
||||
var releaseImageFilename = Path.Combine(releaseFolder,
|
||||
string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00")));
|
||||
var releaseImageFilename = Path.Combine(releaseFolder, string.Format(ImageHelper.ReleaseSecondaryImageFilename, looper.ToString("00")));
|
||||
if (releaseImageFilename != releaseImage.FullName)
|
||||
{
|
||||
// Read image and convert to jpeg
|
||||
|
@ -221,7 +230,7 @@ namespace Roadie.Library.FilePlugins
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error with Managing Images For [{0}]", fileInfo.FullName);
|
||||
Logger.LogError(ex, $"Error with Managing Images For [{fileInfo.FullName}], ImageFilename [{imageFilename }]");
|
||||
}
|
||||
|
||||
var doesFileExistsForTrack = File.Exists(destinationName);
|
||||
|
@ -291,8 +300,7 @@ namespace Roadie.Library.FilePlugins
|
|||
}
|
||||
|
||||
sw.Stop();
|
||||
Logger.LogTrace("<< Audio: Process Complete. Result `{0}`, ElapsedTime [{1}]",
|
||||
JsonConvert.SerializeObject(result), sw.ElapsedMilliseconds);
|
||||
Logger.LogTrace("<< Audio: Process Complete. Result `{0}`, ElapsedTime [{1}]", JsonSerializer.Serialize(result), sw.ElapsedMilliseconds);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Roadie.Library.Configuration;
|
||||
using ImageMagick;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.MetaData.Audio;
|
||||
|
@ -20,121 +21,123 @@ namespace Roadie.Library.Imaging
|
|||
public static class ImageHelper
|
||||
{
|
||||
public static string ArtistImageFilename = "artist.jpg";
|
||||
|
||||
public static string ArtistSecondaryImageFilename = "artist {0}.jpg"; // Replace with counter of image
|
||||
|
||||
public static string ReleaseCoverFilename = "cover.jpg";
|
||||
|
||||
public static string ReleaseSecondaryImageFilename = "release {0}.jpg"; // Replace with counter of image
|
||||
|
||||
public static byte[] ConvertToJpegFormat(byte[] imageBytes)
|
||||
{
|
||||
if(imageBytes?.Any() != true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using(var outStream = new MemoryStream())
|
||||
{
|
||||
IImageFormat imageFormat = null;
|
||||
using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
|
||||
{
|
||||
image.SaveAsJpeg(outStream);
|
||||
}
|
||||
return outStream.ToArray();
|
||||
}
|
||||
} catch(Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error Converting Image to Jpg [{ ex.Message }]", "Warning");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Only user avatars are GIF to allow for animation.
|
||||
/// </summary>
|
||||
public static byte[] ConvertToGifFormat(byte[] imageBytes)
|
||||
{
|
||||
if(imageBytes?.Any() != true)
|
||||
if (imageBytes?.Any() != true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
using(var outStream = new MemoryStream())
|
||||
using (var outStream = new MemoryStream())
|
||||
{
|
||||
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);
|
||||
}
|
||||
return outStream.ToArray();
|
||||
}
|
||||
} catch(Exception ex)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error Converting Image to Gif [{ ex.Message }]", "Warning");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] ConvertToJpegFormat(byte[] imageBytes)
|
||||
{
|
||||
if (imageBytes?.Any() != true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return ConvertToJpegFormatViaSixLabors(imageBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error Converting Image to Jpg via SixLabors [{ ex }]", "Warning");
|
||||
}
|
||||
try
|
||||
{
|
||||
return ConvertToJpegFormatViaMagick(imageBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error Converting Image to Jpg via Magick [{ ex }]", "Warning");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] ConvertToJpegFormatViaSixLabors(byte[] imageBytes)
|
||||
{
|
||||
using (var outStream = new MemoryStream())
|
||||
{
|
||||
IImageFormat imageFormat = null;
|
||||
using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
|
||||
{
|
||||
image.SaveAsJpeg(outStream);
|
||||
}
|
||||
return outStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ConvertToJpegFormatViaMagick(byte[] imageBytes)
|
||||
{
|
||||
using (var image = new MagickImage(imageBytes))
|
||||
{
|
||||
image.Format = MagickFormat.Jpeg;
|
||||
return image.ToByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<FileInfo> FindImagesByName(DirectoryInfo directory,
|
||||
string name,
|
||||
SearchOption folderSearchOptions = SearchOption.AllDirectories)
|
||||
{
|
||||
var result = new List<FileInfo>();
|
||||
if(directory?.Exists != true || string.IsNullOrEmpty(name))
|
||||
if (directory?.Exists != true || string.IsNullOrEmpty(name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var imageFilesInFolder = ImageFilesInFolder(directory.FullName, folderSearchOptions);
|
||||
if(imageFilesInFolder?.Any() != true)
|
||||
if (imageFilesInFolder?.Any() != true)
|
||||
{
|
||||
return result;
|
||||
if(imageFilesInFolder.Length > 0)
|
||||
}
|
||||
|
||||
if (imageFilesInFolder.Length > 0)
|
||||
{
|
||||
name = name.ToAlphanumericName();
|
||||
foreach(var imageFileInFolder in imageFilesInFolder)
|
||||
foreach (var imageFileInFolder in imageFilesInFolder)
|
||||
{
|
||||
var image = new FileInfo(imageFileInFolder);
|
||||
var filenameWithoutExtension = Path.GetFileName(imageFileInFolder).ToAlphanumericName();
|
||||
var imageName = image.Name.ToAlphanumericName();
|
||||
if(imageName.Equals(name) || filenameWithoutExtension.Equals(name))
|
||||
if (imageName.Equals(name) || filenameWithoutExtension.Equals(name))
|
||||
{
|
||||
result.Add(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool IsImageBetterQuality(string image1, string compareToImage)
|
||||
{
|
||||
if(string.IsNullOrEmpty(compareToImage))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
try
|
||||
{
|
||||
if(string.IsNullOrEmpty(image1) || !File.Exists(image1))
|
||||
{
|
||||
return File.Exists(compareToImage);
|
||||
}
|
||||
using(var imageComparing = SixLabors.ImageSharp.Image.Load(image1))
|
||||
{
|
||||
using(var imageToCompare = SixLabors.ImageSharp.Image.Load(compareToImage))
|
||||
{
|
||||
// Generally a larger image is the preferred image
|
||||
var isBigger = imageToCompare.Height > imageComparing.Height &&
|
||||
imageToCompare.Width > imageComparing.Width;
|
||||
if(isBigger)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error IsImageBetterQuality Image Comparing [{ image1 }] to [{ compareToImage }], Error [{ ex }]",
|
||||
"Warning");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IEnumerable<FileInfo> FindImageTypeInDirectory(DirectoryInfo directory,
|
||||
ImageType type,
|
||||
SearchOption folderSearchOptions = SearchOption.AllDirectories)
|
||||
|
@ -149,10 +152,10 @@ namespace Roadie.Library.Imaging
|
|||
{
|
||||
return result;
|
||||
}
|
||||
foreach(var imageFile in imageFilesInFolder)
|
||||
foreach (var imageFile in imageFilesInFolder)
|
||||
{
|
||||
var image = new FileInfo(imageFile);
|
||||
switch(type)
|
||||
switch (type)
|
||||
{
|
||||
case ImageType.Artist:
|
||||
if (IsArtistImage(image))
|
||||
|
@ -197,174 +200,21 @@ namespace Roadie.Library.Imaging
|
|||
string[] patterns = null,
|
||||
SearchOption options = SearchOption.TopDirectoryOnly)
|
||||
{
|
||||
if(!Directory.Exists(path))
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
if(patterns == null || patterns.Length == 0)
|
||||
if (patterns == null || patterns.Length == 0)
|
||||
{
|
||||
return Directory.GetFiles(path, "*", options);
|
||||
}
|
||||
if(patterns.Length == 1)
|
||||
if (patterns.Length == 1)
|
||||
{
|
||||
return Directory.GetFiles(path, patterns[0], options);
|
||||
}
|
||||
return patterns.SelectMany(pattern => Directory.GetFiles(path, pattern, options)).Distinct().ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ImageDataFromUrl(string imageUrl)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(imageUrl) && !imageUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var dataString = imageUrl.Trim()
|
||||
.Replace('-', '+')
|
||||
.Replace("data:image/jpeg;base64,", "")
|
||||
.Replace("data:image/bmp;base64,", "")
|
||||
.Replace("data:image/gif;base64,", "")
|
||||
.Replace("data:image/png;base64,", "");
|
||||
return Convert.FromBase64String(dataString);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string[] ImageExtensions()
|
||||
{ return new string[6] { "*.bmp", "*.jpeg", "*.jpe", "*.jpg", "*.png", "*.gif" }; }
|
||||
|
||||
public static string[] ImageFilesInFolder(string folder, SearchOption searchOption)
|
||||
{ return GetFiles(folder, ImageExtensions(), searchOption); }
|
||||
|
||||
public static string[] ImageMimeTypes()
|
||||
{ return new string[4] { "image/bmp", "image/jpeg", "image/png", "image/gif" }; }
|
||||
|
||||
public static ImageSearchResult ImageSearchResultForImageUrl(string imageUrl)
|
||||
{
|
||||
if(!WebHelper.IsStringUrl(imageUrl))
|
||||
return null;
|
||||
var result = new ImageSearchResult();
|
||||
var imageBytes = WebHelper.BytesForImageUrl(imageUrl);
|
||||
IImageFormat imageFormat = null;
|
||||
using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
|
||||
{
|
||||
result.Height = image.Height.ToString();
|
||||
result.Width = image.Width.ToString();
|
||||
result.MediaUrl = imageUrl;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool IsArtistImage(FileInfo fileinfo)
|
||||
{
|
||||
if(fileinfo == null)
|
||||
return false;
|
||||
return Regex.IsMatch(fileinfo.Name,
|
||||
@"(band|artist|group|photo)\.(jpg|jpeg|png|bmp|gif)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsArtistSecondaryImage(FileInfo fileinfo)
|
||||
{
|
||||
if(fileinfo == null)
|
||||
return false;
|
||||
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)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsLabelImage(FileInfo fileinfo)
|
||||
{
|
||||
if(fileinfo == null)
|
||||
return false;
|
||||
return Regex.IsMatch(fileinfo.Name,
|
||||
@"(label|recordlabel|record_label)\.(jpg|jpeg|png|bmp|gif)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsReleaseImage(FileInfo fileinfo, string releaseName = null)
|
||||
{
|
||||
if(fileinfo == null)
|
||||
return false;
|
||||
return Regex.IsMatch(fileinfo.Name,
|
||||
@"((f[-_\s]*[0-9]*)|00|art|big[art]*|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsReleaseSecondaryImage(FileInfo fileinfo)
|
||||
{
|
||||
if(fileinfo == null)
|
||||
return false;
|
||||
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)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static byte[] ResizeImage(byte[] imageBytes, int width, int height) => ImageHelper.ResizeImage(imageBytes,
|
||||
width,
|
||||
height,
|
||||
false)
|
||||
.Item2;
|
||||
|
||||
/// <summary>
|
||||
/// Resize a given image to given dimensions if needed
|
||||
/// </summary>
|
||||
/// <param name="imageBytes">Image bytes to resize</param>
|
||||
/// <param name="width">Resize to width</param>
|
||||
/// <param name="height">Resize to height</param>
|
||||
/// <param name="forceResize">Force resize</param>
|
||||
/// <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)
|
||||
{
|
||||
if(imageBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
using(var outStream = new MemoryStream())
|
||||
{
|
||||
var resized = false;
|
||||
IImageFormat imageFormat = null;
|
||||
using(var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
|
||||
{
|
||||
var doForce = forceResize ?? false;
|
||||
if(doForce || image.Width > width || image.Height > height)
|
||||
{
|
||||
int newWidth, newHeight;
|
||||
if(doForce)
|
||||
{
|
||||
newWidth = width;
|
||||
newHeight = height;
|
||||
} else
|
||||
{
|
||||
float aspect = image.Width / (float)image.Height;
|
||||
if(aspect < 1)
|
||||
{
|
||||
newWidth = (int)(width * aspect);
|
||||
newHeight = (int)(newWidth / aspect);
|
||||
} else
|
||||
{
|
||||
newHeight = (int)(height / aspect);
|
||||
newWidth = (int)(newHeight * aspect);
|
||||
}
|
||||
}
|
||||
image.Mutate(ctx => ctx.Resize(newWidth, newHeight));
|
||||
resized = true;
|
||||
}
|
||||
image.Save(outStream, imageFormat);
|
||||
}
|
||||
return new Tuple<bool, byte[]>(resized, outStream.ToArray());
|
||||
}
|
||||
} catch(Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error Resizing Image [{ex}]", "Warning");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get image data from all sources for either fileanme or MetaData
|
||||
/// </summary>
|
||||
|
@ -380,6 +230,29 @@ namespace Roadie.Library.Imaging
|
|||
return ImageForFilename(configuration, filename);
|
||||
}
|
||||
|
||||
public static byte[] ImageDataFromUrl(string imageUrl)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(imageUrl) && !imageUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var dataString = imageUrl.Trim()
|
||||
.Replace('-', '+')
|
||||
.Replace("data:image/jpeg;base64,", string.Empty)
|
||||
.Replace("data:image/bmp;base64,", string.Empty)
|
||||
.Replace("data:image/gif;base64,", string.Empty)
|
||||
.Replace("data:image/png;base64,", string.Empty)
|
||||
.Replace("data:image/webp;base64,", string.Empty);
|
||||
return Convert.FromBase64String(dataString);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string[] ImageExtensions()
|
||||
{ return new string[7] { "*.bmp", "*.jpeg", "*.jpe", "*.jpg", "*.png", "*.gif", "*.webp" }; }
|
||||
|
||||
public static string[] ImageFilesInFolder(string folder, SearchOption searchOption)
|
||||
{ return GetFiles(folder, ImageExtensions(), searchOption); }
|
||||
|
||||
/// <summary>
|
||||
/// Does image exist with the same filename
|
||||
/// </summary>
|
||||
|
@ -389,7 +262,7 @@ namespace Roadie.Library.Imaging
|
|||
{
|
||||
AudioMetaDataImage imageMetaData = null;
|
||||
|
||||
if(string.IsNullOrEmpty(filename))
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
return imageMetaData;
|
||||
}
|
||||
|
@ -397,9 +270,9 @@ namespace Roadie.Library.Imaging
|
|||
{
|
||||
var fileInfo = new FileInfo(filename);
|
||||
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
|
||||
{
|
||||
|
@ -408,23 +281,31 @@ namespace Roadie.Library.Imaging
|
|||
MimeType = Library.Processors.FileProcessor.DetermineFileType(fileInfo)
|
||||
};
|
||||
}
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
// Is there a picture in filename folder (for the Release)
|
||||
var pictures = fileInfo.Directory.GetFiles("*.jpg");
|
||||
var tagImages = new List<AudioMetaDataImage>();
|
||||
if(pictures?.Any() == true)
|
||||
if (pictures?.Any() == true)
|
||||
{
|
||||
// See if there is a "cover" or "front" jpg file if so use it
|
||||
FileInfo picture = Array.Find(pictures,
|
||||
x => x.Name.Equals("cover", StringComparison.OrdinalIgnoreCase));
|
||||
if(picture == null)
|
||||
if (picture == null)
|
||||
{
|
||||
picture = Array.Find(pictures,
|
||||
x => x.Name.Equals("front", StringComparison.OrdinalIgnoreCase));
|
||||
if(picture == null)
|
||||
x => x.Name.Equals("front", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (picture == null)
|
||||
{
|
||||
picture = pictures[0];
|
||||
if(picture != null)
|
||||
using(var processor = new ImageProcessor(configuration))
|
||||
}
|
||||
|
||||
if (picture != null)
|
||||
{
|
||||
using (var processor = new ImageProcessor(configuration))
|
||||
{
|
||||
imageMetaData = new AudioMetaDataImage
|
||||
{
|
||||
|
@ -433,11 +314,14 @@ namespace Roadie.Library.Imaging
|
|||
MimeType = Library.Processors.FileProcessor.DetermineFileType(picture)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(FileNotFoundException)
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
} catch(Exception ex)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine(ex, ex.Serialize());
|
||||
}
|
||||
|
@ -445,58 +329,132 @@ namespace Roadie.Library.Imaging
|
|||
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 string[] ImageMimeTypes()
|
||||
{ return new string[4] { "image/bmp", "image/jpeg", "image/png", "image/gif" }; }
|
||||
|
||||
public static ImageSearchResult ImageSearchResultForImageUrl(string imageUrl)
|
||||
{
|
||||
return MakeImage(configuration,
|
||||
httpContext,
|
||||
id,
|
||||
type,
|
||||
width ?? configuration.ThumbnailImageSize.Width,
|
||||
height ?? configuration.ThumbnailImageSize.Height,
|
||||
null,
|
||||
includeCachebuster);
|
||||
if (!WebHelper.IsStringUrl(imageUrl))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new ImageSearchResult();
|
||||
var imageBytes = WebHelper.BytesForImageUrl(imageUrl);
|
||||
IImageFormat imageFormat = null;
|
||||
using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
|
||||
{
|
||||
result.Height = image.Height.ToString();
|
||||
result.Width = image.Width.ToString();
|
||||
result.MediaUrl = imageUrl;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static models.Image MakeNewImage(IHttpContext httpContext, string type)
|
||||
{ return new models.Image($"{httpContext.ImageBaseUrl}/{type}.jpg", null, null); }
|
||||
public static bool IsArtistImage(FileInfo fileinfo)
|
||||
{
|
||||
if (fileinfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static models.Image MakePlaylistThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "playlist"); }
|
||||
return Regex.IsMatch(fileinfo.Name,
|
||||
@"(band|artist|group|photo)\.(jpg|jpeg|png|bmp|gif)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static models.Image MakeReleaseThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "release"); }
|
||||
public static bool IsArtistSecondaryImage(FileInfo fileinfo)
|
||||
{
|
||||
if (fileinfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static models.Image MakeTrackThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "track"); }
|
||||
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)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static models.Image MakeUserThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "user"); }
|
||||
public static bool IsImageBetterQuality(string image1, string compareToImage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(compareToImage))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(image1) || !File.Exists(image1))
|
||||
{
|
||||
return File.Exists(compareToImage);
|
||||
}
|
||||
using (var imageComparing = SixLabors.ImageSharp.Image.Load(image1))
|
||||
{
|
||||
using (var imageToCompare = SixLabors.ImageSharp.Image.Load(compareToImage))
|
||||
{
|
||||
// Generally a larger image is the preferred image
|
||||
var isBigger = imageToCompare.Height > imageComparing.Height &&
|
||||
imageToCompare.Width > imageComparing.Width;
|
||||
if (isBigger)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error IsImageBetterQuality Image Comparing [{ image1 }] to [{ compareToImage }], Error [{ ex }]",
|
||||
"Warning");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static models.Image MakeLabelThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "label"); }
|
||||
public static bool IsLabelImage(FileInfo fileinfo)
|
||||
{
|
||||
if (fileinfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Regex.IsMatch(fileinfo.Name,
|
||||
@"(label|recordlabel|record_label)\.(jpg|jpeg|png|bmp|gif)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsReleaseImage(FileInfo fileinfo, string releaseName = null)
|
||||
{
|
||||
if (fileinfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Regex.IsMatch(fileinfo.Name,
|
||||
@"((f[-_\s]*[0-9]*)|00|art|big[art]*|cover|cvr|folder|release|front[-_\s]*)\.(jpg|jpeg|png|bmp|gif)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsReleaseSecondaryImage(FileInfo fileinfo)
|
||||
{
|
||||
if (fileinfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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)",
|
||||
RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
public static models.Image MakeArtistThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid? id)
|
||||
{
|
||||
if(!id.HasValue)
|
||||
if (!id.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return MakeThumbnailImage(configuration, httpContext, id.Value, "artist");
|
||||
}
|
||||
|
||||
|
@ -522,7 +480,7 @@ namespace Roadie.Library.Imaging
|
|||
int imageId,
|
||||
string caption = null)
|
||||
{
|
||||
if(type == ImageType.ArtistSecondary)
|
||||
if (type == ImageType.ArtistSecondary)
|
||||
{
|
||||
return new models.Image($"{httpContext.ImageBaseUrl}/artist-secondary/{id}/{imageId}",
|
||||
caption,
|
||||
|
@ -538,8 +496,12 @@ namespace Roadie.Library.Imaging
|
|||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "genre"); }
|
||||
|
||||
public static models.Image MakeUnknownImage(IHttpContext httpContext, string unknownType = "unknown")
|
||||
{ return new models.Image($"{httpContext.ImageBaseUrl}/{ unknownType }.jpg"); }
|
||||
public static models.Image MakeImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
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,
|
||||
|
@ -554,13 +516,6 @@ namespace Roadie.Library.Imaging
|
|||
$"{httpContext.ImageBaseUrl}/{id}/{configuration.SmallImageSize.Width}/{configuration.SmallImageSize.Height}");
|
||||
}
|
||||
|
||||
public static models.Image MakeImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
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,
|
||||
|
@ -570,14 +525,135 @@ namespace Roadie.Library.Imaging
|
|||
string caption = null,
|
||||
bool includeCachebuster = false)
|
||||
{
|
||||
if(width.HasValue &&
|
||||
if (width.HasValue &&
|
||||
height.HasValue &&
|
||||
(width.Value != configuration.ThumbnailImageSize.Width ||
|
||||
height.Value != configuration.ThumbnailImageSize.Height))
|
||||
{
|
||||
return new models.Image($"{httpContext.ImageBaseUrl}/{type}/{id}/{width}/{height}/{(includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty)}",
|
||||
caption,
|
||||
$"{httpContext.ImageBaseUrl}/{type}/{id}/{configuration.ThumbnailImageSize.Width}/{configuration.ThumbnailImageSize.Height}");
|
||||
caption,
|
||||
$"{httpContext.ImageBaseUrl}/{type}/{id}/{configuration.ThumbnailImageSize.Width}/{configuration.ThumbnailImageSize.Height}");
|
||||
}
|
||||
|
||||
return new models.Image($"{httpContext.ImageBaseUrl}/{type}/{id}", caption, null);
|
||||
}
|
||||
|
||||
public static models.Image MakeLabelThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "label"); }
|
||||
|
||||
public static models.Image MakeNewImage(IHttpContext httpContext, string type)
|
||||
{ return new models.Image($"{httpContext.ImageBaseUrl}/{type}.jpg", null, null); }
|
||||
|
||||
public static models.Image MakePlaylistThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "playlist"); }
|
||||
|
||||
public static models.Image MakeReleaseThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "release"); }
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static models.Image MakeTrackThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "track"); }
|
||||
|
||||
public static models.Image MakeUnknownImage(IHttpContext httpContext, string unknownType = "unknown")
|
||||
{ return new models.Image($"{httpContext.ImageBaseUrl}/{ unknownType }.jpg"); }
|
||||
|
||||
public static models.Image MakeUserThumbnailImage(IRoadieSettings configuration,
|
||||
IHttpContext httpContext,
|
||||
Guid id)
|
||||
{ return MakeThumbnailImage(configuration, httpContext, id, "user"); }
|
||||
|
||||
public static byte[] ResizeImage(byte[] imageBytes, int width, int height) => ImageHelper.ResizeImage(imageBytes,
|
||||
width,
|
||||
height,
|
||||
false)
|
||||
.Item2;
|
||||
|
||||
/// <summary>
|
||||
/// Resize a given image to given dimensions if needed
|
||||
/// </summary>
|
||||
/// <param name="imageBytes">Image bytes to resize</param>
|
||||
/// <param name="width">Resize to width</param>
|
||||
/// <param name="height">Resize to height</param>
|
||||
/// <param name="forceResize">Force resize</param>
|
||||
/// <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)
|
||||
{
|
||||
if (imageBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
using (var outStream = new MemoryStream())
|
||||
{
|
||||
var resized = false;
|
||||
IImageFormat imageFormat = null;
|
||||
using (var image = SixLabors.ImageSharp.Image.Load(imageBytes, out imageFormat))
|
||||
{
|
||||
var doForce = forceResize ?? false;
|
||||
if (doForce || image.Width > width || image.Height > height)
|
||||
{
|
||||
int newWidth, newHeight;
|
||||
if (doForce)
|
||||
{
|
||||
newWidth = width;
|
||||
newHeight = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
float aspect = image.Width / (float)image.Height;
|
||||
if (aspect < 1)
|
||||
{
|
||||
newWidth = (int)(width * aspect);
|
||||
newHeight = (int)(newWidth / aspect);
|
||||
}
|
||||
else
|
||||
{
|
||||
newHeight = (int)(height / aspect);
|
||||
newWidth = (int)(newHeight * aspect);
|
||||
}
|
||||
}
|
||||
image.Mutate(ctx => ctx.Resize(newWidth, newHeight));
|
||||
resized = true;
|
||||
}
|
||||
image.Save(outStream, imageFormat);
|
||||
}
|
||||
return new Tuple<bool, byte[]>(resized, outStream.ToArray());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Error Resizing Image [{ex}]", "Warning");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ using HashidsNet;
|
|||
using Mapster;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Enums;
|
||||
|
@ -21,6 +20,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Roadie.Library.Inspect
|
||||
{
|
||||
|
@ -28,7 +28,13 @@ namespace Roadie.Library.Inspect
|
|||
{
|
||||
private const string Salt = "6856F2EE-5965-4345-884B-2CCA457AAF59";
|
||||
|
||||
private IRoadieSettings Configuration { get; }
|
||||
private ILogger Logger => MessageLogger as ILogger;
|
||||
private IEventMessageLogger MessageLogger { get; }
|
||||
private ID3TagsHelper TagsHelper { get; }
|
||||
|
||||
private IEnumerable<IInspectorDirectoryPlugin> _directoryPlugins;
|
||||
|
||||
private IEnumerable<IInspectorFilePlugin> _filePlugins;
|
||||
|
||||
public DictionaryCacheManager CacheManager { get; }
|
||||
|
@ -47,6 +53,7 @@ namespace Roadie.Library.Inspect
|
|||
.SelectMany(s => s.GetTypes())
|
||||
.Where(p => type.IsAssignableFrom(p));
|
||||
foreach (var t in types)
|
||||
{
|
||||
if (t.GetInterface("IInspectorDirectoryPlugin") != null && !t.IsAbstract && !t.IsInterface)
|
||||
{
|
||||
var plugin = Activator.CreateInstance(t, Configuration, CacheManager, Logger, TagsHelper) as IInspectorDirectoryPlugin;
|
||||
|
@ -59,6 +66,7 @@ namespace Roadie.Library.Inspect
|
|||
Console.WriteLine($"╠╣ Not Loading Disabled Plugin [{plugin.Description}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -113,18 +121,8 @@ namespace Roadie.Library.Inspect
|
|||
}
|
||||
}
|
||||
|
||||
private IRoadieSettings Configuration { get; }
|
||||
|
||||
private ILogger Logger => MessageLogger as ILogger;
|
||||
|
||||
private IEventMessageLogger MessageLogger { get; }
|
||||
|
||||
private ID3TagsHelper TagsHelper { get; }
|
||||
|
||||
public Inspector()
|
||||
{
|
||||
Console.WriteLine("Roadie Media Inspector");
|
||||
|
||||
MessageLogger = new EventMessageLogger<Inspector>();
|
||||
MessageLogger.Messages += MessageLogger_Messages;
|
||||
|
||||
|
@ -135,31 +133,141 @@ namespace Roadie.Library.Inspect
|
|||
configuration.GetSection("RoadieSettings").Bind(settings);
|
||||
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
|
||||
Configuration = settings;
|
||||
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
CacheManager = new DictionaryCacheManager(Logger, new NewtonsoftCacheSerializer(Logger), new CachePolicy(TimeSpan.FromHours(4)));
|
||||
|
||||
var tagHelperLooper = new EventMessageLogger<ID3TagsHelper>();
|
||||
tagHelperLooper.Messages += MessageLogger_Messages;
|
||||
TagsHelper = new ID3TagsHelper(Configuration, CacheManager, tagHelperLooper);
|
||||
}
|
||||
|
||||
public static string ArtistInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist);
|
||||
|
||||
public static string ReleaseInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist + metaData.Release);
|
||||
|
||||
public static string ToToken(string input)
|
||||
private void InspectImage(bool isReadOnly, bool doCopy, string dest, string subdirectory, FileInfo image)
|
||||
{
|
||||
var hashids = new Hashids(Salt);
|
||||
var numbers = 0;
|
||||
var bytes = System.Text.Encoding.ASCII.GetBytes(input);
|
||||
var looper = bytes.Length / 4;
|
||||
for (var i = 0; i < looper; i++)
|
||||
if (!image.Exists)
|
||||
{
|
||||
numbers += BitConverter.ToInt32(bytes, i * 4);
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"╟ ■ InspectImage: Image Not Found [{image.FullName}]");
|
||||
Console.ResetColor();
|
||||
return;
|
||||
}
|
||||
if (numbers < 0) numbers *= -1;
|
||||
return hashids.Encode(numbers);
|
||||
|
||||
Console.WriteLine($"╟─ Inspecting Image [{image.FullName}]");
|
||||
var newImageFolder = new DirectoryInfo(Path.Combine(dest, subdirectory));
|
||||
if (!newImageFolder.Exists)
|
||||
{
|
||||
newImageFolder.Create();
|
||||
}
|
||||
|
||||
var newImagePath = Path.Combine(dest, subdirectory, image.Name);
|
||||
|
||||
if (image.FullName != newImagePath)
|
||||
{
|
||||
var looper = 0;
|
||||
while (File.Exists(newImagePath))
|
||||
{
|
||||
looper++;
|
||||
newImagePath = Path.Combine(dest, subdirectory, looper.ToString("00"), image.Name);
|
||||
}
|
||||
if (isReadOnly)
|
||||
{
|
||||
Console.WriteLine($"╟ 🔒 Read Only Mode: Would be [{(doCopy ? "Copied" : "Moved")}] to [{newImagePath}]");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!doCopy)
|
||||
{
|
||||
image.MoveTo(newImagePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
image.CopyTo(newImagePath, true);
|
||||
}
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine($"╠═ 🚛 {(doCopy ? "Copied" : "Moved")} Image File to [{newImagePath}]");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"📛 Error file [{image.FullName}], newImagePath [{newImagePath}], Exception: [{ex}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
private void MessageLogger_Messages(object sender, EventMessage e)
|
||||
{
|
||||
Console.WriteLine($"Log Level [{e.Level}] Log Message [{e.Message}] ");
|
||||
var message = e.Message;
|
||||
switch (e.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;
|
||||
}
|
||||
}
|
||||
|
||||
private string RunScript(string scriptFilename, bool doCopy, bool isReadOnly, string directoryToInspect, string dest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(scriptFilename))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(scriptFilename))
|
||||
{
|
||||
Console.WriteLine($"Script Not Found: [{ scriptFilename }]");
|
||||
return null;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Running Script: [{ scriptFilename }]");
|
||||
var script = File.ReadAllText(scriptFilename);
|
||||
using (var ps = PowerShell.Create())
|
||||
{
|
||||
var r = string.Empty;
|
||||
var results = ps.AddScript(script)
|
||||
.AddParameter("DoCopy", doCopy)
|
||||
.AddParameter("IsReadOnly", isReadOnly)
|
||||
.AddParameter("DirectoryToInspect", directoryToInspect)
|
||||
.AddParameter("Dest", dest)
|
||||
.Invoke();
|
||||
foreach (var result in results)
|
||||
{
|
||||
r += result + Environment.NewLine;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"📛 Error with Script File [{scriptFilename}], Error [{ex}] ");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string ArtistInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist);
|
||||
|
||||
public void Inspect(bool doCopy, bool isReadOnly, string directoryToInspect, string destination, bool dontAppendSubFolder, bool dontDeleteEmptyFolders, bool dontRunPreScripts)
|
||||
{
|
||||
Configuration.Inspector.IsInReadOnlyMode = isReadOnly;
|
||||
|
@ -171,13 +279,32 @@ namespace Roadie.Library.Inspect
|
|||
|
||||
Trace.Listeners.Add(new LoggingTraceListener());
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine(" ▄▄▄ ▄▄▄· ·▄▄▄▄ ▪ ▄▄▄ . • ▌ ▄ ·. ▄▄▄ .·▄▄▄▄ ▪ ▄▄▄· ▪ ▐ ▄ .▄▄ · ▄▄▄·▄▄▄ . ▄▄· ▄▄▄▄▄ ▄▄▄ ");
|
||||
Console.WriteLine(" ▀▄ █·▪ ▐█ ▀█ ██▪ ██ ██ ▀▄.▀· ·██ ▐███▪▀▄.▀·██▪ ██ ██ ▐█ ▀█ ██ •█▌▐█▐█ ▀. ▐█ ▄█▀▄.▀·▐█ ▌▪•██ ▪ ▀▄ █·");
|
||||
Console.WriteLine(" ▐▀▀▄ ▄█▀▄ ▄█▀▀█ ▐█· ▐█▌▐█·▐▀▀▪▄ ▐█ ▌▐▌▐█·▐▀▀▪▄▐█· ▐█▌▐█·▄█▀▀█ ▐█·▐█▐▐▌▄▀▀▀█▄ ██▀·▐▀▀▪▄██ ▄▄ ▐█.▪ ▄█▀▄ ▐▀▀▄ ");
|
||||
Console.WriteLine(" ▐█•█▌▐█▌.▐▌▐█ ▪▐▌██. ██ ▐█▌▐█▄▄▌ ██ ██▌▐█▌▐█▄▄▌██. ██ ▐█▌▐█ ▪▐▌ ▐█▌██▐█▌▐█▄▪▐█▐█▪·•▐█▄▄▌▐███▌ ▐█▌·▐█▌.▐▌▐█•█▌");
|
||||
Console.WriteLine(" .▀ ▀ ▀█▄▀▪ ▀ ▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀ ▀▀ █▪▀▀▀ ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀ ▀ ▀▀▀▀▀ █▪ ▀▀▀▀ .▀ ▀▀▀ ·▀▀▀ ▀▀▀ ▀█▄▀▪.▀ ▀");
|
||||
Console.WriteLine("");
|
||||
Console.ResetColor();
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.White;
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
Console.WriteLine($"✨ Inspector Start, UTC [{DateTime.UtcNow.ToString("s")}]");
|
||||
Console.ResetColor();
|
||||
|
||||
if (!Directory.Exists(directoryToInspect))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"📛 Folder To Inspect [{ directoryToInspect }] is not found.");
|
||||
Console.ResetColor();
|
||||
return;
|
||||
}
|
||||
|
||||
string scriptResult = null;
|
||||
// Run PreInspect script
|
||||
dontRunPreScripts = File.Exists(Configuration.Processing.PreInspectScript) && dontRunPreScripts;
|
||||
if (dontRunPreScripts)
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.Blue;
|
||||
|
@ -198,7 +325,10 @@ namespace Roadie.Library.Inspect
|
|||
}
|
||||
// Create a new destination subfolder for each Inspector run by Current timestamp
|
||||
var dest = Path.Combine(destination, DateTime.UtcNow.ToString("yyyyMMddHHmm"));
|
||||
if (isReadOnly || dontAppendSubFolder) dest = destination;
|
||||
if (isReadOnly || dontAppendSubFolder)
|
||||
{
|
||||
dest = destination;
|
||||
}
|
||||
// Get all the directorys in the directory
|
||||
var directoryDirectories = Directory.GetDirectories(directoryToInspect, "*.*", SearchOption.AllDirectories);
|
||||
var directories = new List<string>
|
||||
|
@ -239,7 +369,7 @@ namespace Roadie.Library.Inspect
|
|||
var pluginResult = plugin.Process(directoryInfo);
|
||||
if (!pluginResult.IsSuccess)
|
||||
{
|
||||
Console.WriteLine($"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]");
|
||||
Console.WriteLine($"📛 Plugin Failed: Error [{JsonSerializer.Serialize(pluginResult)}]");
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(pluginResult.Data))
|
||||
|
@ -261,7 +391,11 @@ namespace Roadie.Library.Inspect
|
|||
Console.WriteLine($"╟─ 🎵 Inspecting [{fileInfo.FullName}]");
|
||||
var tagLib = TagsHelper.MetaDataForFile(fileInfo.FullName, true);
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
if (!tagLib?.IsSuccess ?? false) Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
if (!tagLib?.IsSuccess ?? false)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
}
|
||||
|
||||
Console.WriteLine($"╟ (Pre ) : {tagLib.Data}");
|
||||
Console.ResetColor();
|
||||
tagLib.Data.Filename = fileInfo.FullName;
|
||||
|
@ -270,7 +404,7 @@ namespace Roadie.Library.Inspect
|
|||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine($"╟ ❗ INVALID: Missing: {ID3TagsHelper.DetermineMissingRequiredMetaData(originalMetaData)}");
|
||||
Console.WriteLine($"╟ [{JsonConvert.SerializeObject(tagLib, Newtonsoft.Json.Formatting.Indented)}]");
|
||||
Console.WriteLine($"╟ [{JsonSerializer.Serialize(tagLib, new JsonSerializerOptions { WriteIndented = true })}]");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
|
@ -283,7 +417,7 @@ namespace Roadie.Library.Inspect
|
|||
if (!pluginResult.IsSuccess)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]");
|
||||
Console.WriteLine($"📛 Plugin Failed: Error [{JsonSerializer.Serialize(pluginResult)}]");
|
||||
Console.ResetColor();
|
||||
return;
|
||||
}
|
||||
|
@ -421,7 +555,11 @@ namespace Roadie.Library.Inspect
|
|||
{
|
||||
if (fileInfo.FullName != newPath)
|
||||
{
|
||||
if (File.Exists(newPath)) File.Delete(newPath);
|
||||
if (File.Exists(newPath))
|
||||
{
|
||||
File.Delete(newPath);
|
||||
}
|
||||
|
||||
fileInfo.MoveTo(newPath);
|
||||
}
|
||||
}
|
||||
|
@ -453,12 +591,14 @@ namespace Roadie.Library.Inspect
|
|||
var pluginResult = plugin.Process(directoryInfo);
|
||||
if (!pluginResult.IsSuccess)
|
||||
{
|
||||
Console.WriteLine($"📛 Plugin Failed: Error [{JsonConvert.SerializeObject(pluginResult)}]");
|
||||
Console.WriteLine($"📛 Plugin Failed: Error [{JsonSerializer.Serialize(pluginResult)}]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(pluginResult.Data))
|
||||
{
|
||||
Console.WriteLine($"╠╣ Directory Plugin Message: {pluginResult.Data}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,7 +609,7 @@ namespace Roadie.Library.Inspect
|
|||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
Console.WriteLine("📛 Exception: " + ex);
|
||||
Console.WriteLine($"📛 Exception: {ex}");
|
||||
}
|
||||
|
||||
if (!dontDeleteEmptyFolders)
|
||||
|
@ -496,120 +636,24 @@ namespace Roadie.Library.Inspect
|
|||
}
|
||||
}
|
||||
|
||||
private void InspectImage(bool isReadOnly, bool doCopy, string dest, string subdirectory, FileInfo image)
|
||||
public static string ReleaseInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist + metaData.Release);
|
||||
|
||||
public static string ToToken(string input)
|
||||
{
|
||||
if (!image.Exists)
|
||||
var hashids = new Hashids(Salt);
|
||||
var numbers = 0;
|
||||
var bytes = System.Text.Encoding.ASCII.GetBytes(input);
|
||||
var looper = bytes.Length / 4;
|
||||
for (var i = 0; i < looper; i++)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"╟ ■ InspectImage: Image Not Found [{image.FullName}]");
|
||||
Console.ResetColor();
|
||||
return;
|
||||
numbers += BitConverter.ToInt32(bytes, i * 4);
|
||||
}
|
||||
if (numbers < 0)
|
||||
{
|
||||
numbers *= -1;
|
||||
}
|
||||
|
||||
Console.WriteLine($"╟─ Inspecting Image [{image.FullName}]");
|
||||
var newImageFolder = new DirectoryInfo(Path.Combine(dest, subdirectory));
|
||||
if (!newImageFolder.Exists) newImageFolder.Create();
|
||||
var newImagePath = Path.Combine(dest, subdirectory, image.Name);
|
||||
|
||||
if (image.FullName != newImagePath)
|
||||
{
|
||||
var looper = 0;
|
||||
while (File.Exists(newImagePath))
|
||||
{
|
||||
looper++;
|
||||
newImagePath = Path.Combine(dest, subdirectory, looper.ToString("00"), image.Name);
|
||||
}
|
||||
if (isReadOnly)
|
||||
{
|
||||
Console.WriteLine($"╟ 🔒 Read Only Mode: Would be [{(doCopy ? "Copied" : "Moved")}] to [{newImagePath}]");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!doCopy)
|
||||
{
|
||||
image.MoveTo(newImagePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
image.CopyTo(newImagePath, true);
|
||||
}
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine($"╠═ 🚛 {(doCopy ? "Copied" : "Moved")} Image File to [{newImagePath}]");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex);
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"📛 Error file [{image.FullName}], newImagePath [{newImagePath}], Exception: [{ex}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
private void MessageLogger_Messages(object sender, EventMessage e)
|
||||
{
|
||||
Console.WriteLine($"Log Level [{e.Level}] Log Message [{e.Message}] ");
|
||||
var message = e.Message;
|
||||
switch (e.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;
|
||||
}
|
||||
}
|
||||
|
||||
private string RunScript(string scriptFilename, bool doCopy, bool isReadOnly, string directoryToInspect, string dest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(scriptFilename))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"Running Script: [{ scriptFilename }]");
|
||||
var script = File.ReadAllText(scriptFilename);
|
||||
using (var ps = PowerShell.Create())
|
||||
{
|
||||
var r = string.Empty;
|
||||
var results = ps.AddScript(script)
|
||||
.AddParameter("DoCopy", doCopy)
|
||||
.AddParameter("IsReadOnly", isReadOnly)
|
||||
.AddParameter("DirectoryToInspect", directoryToInspect)
|
||||
.AddParameter("Dest", dest)
|
||||
.Invoke();
|
||||
foreach (var result in results)
|
||||
{
|
||||
r += result + Environment.NewLine;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"📛 Error with Script File [{scriptFilename}], Error [{ex}] ");
|
||||
}
|
||||
return null;
|
||||
return hashids.Encode(numbers);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,11 @@ namespace Roadie.Library.Inspect.Plugins.Directory
|
|||
{
|
||||
if (fileExtensionsToDelete.Any(x => x.Equals(file.Extension, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode) file.Delete();
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode)
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
|
||||
deletedFiles.Add(file.Name);
|
||||
Console.WriteLine($" X Deleted File [{file}], Was found in in FileExtensionsToDelete");
|
||||
}
|
||||
|
|
|
@ -36,10 +36,13 @@ namespace Roadie.Library.Inspect.Plugins.Directory
|
|||
var firstMetaData = metaDatasForFilesInFolder.OrderBy(x => x.Filename ?? string.Empty)
|
||||
.ThenBy(x => SafeParser.ToNumber<short>(x.TrackNumber)).FirstOrDefault();
|
||||
if (firstMetaData == null)
|
||||
{
|
||||
return new OperationResult<string>("Error Getting First MetaData")
|
||||
{
|
||||
Data = $"Unable to read Metadatas for Directory [{directory.FullName}]"
|
||||
};
|
||||
}
|
||||
|
||||
var artist = firstMetaData.Artist;
|
||||
foreach (var metaData in metaDatasForFilesInFolder.Where(x => x.Artist != artist))
|
||||
{
|
||||
|
@ -47,7 +50,10 @@ namespace Roadie.Library.Inspect.Plugins.Directory
|
|||
Console.WriteLine(
|
||||
$"╟ Setting Artist to [{artist}], was [{metaData.Artist}] on file [{metaData.FileInfo.Name}");
|
||||
metaData.Artist = artist;
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode) TagsHelper.WriteTags(metaData, metaData.Filename);
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode)
|
||||
{
|
||||
TagsHelper.WriteTags(metaData, metaData.Filename);
|
||||
}
|
||||
}
|
||||
|
||||
data = $"Found [{found}] files, Modified [{modified}] files";
|
||||
|
|
|
@ -39,7 +39,10 @@ namespace Roadie.Library.Inspect.Plugins.Directory
|
|||
Console.WriteLine(
|
||||
$"╟ Setting Release to [{release}], was [{metaData.Release}] on file [{metaData.FileInfo.Name}");
|
||||
metaData.Release = release;
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode) TagsHelper.WriteTags(metaData, metaData.Filename);
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode)
|
||||
{
|
||||
TagsHelper.WriteTags(metaData, metaData.Filename);
|
||||
}
|
||||
}
|
||||
|
||||
data = $"Found [{found}] files, Modified [{modified}] files";
|
||||
|
|
|
@ -38,7 +38,10 @@ namespace Roadie.Library.Inspect.Plugins.Directory
|
|||
Console.WriteLine(
|
||||
$"╟ Setting Year to [{year}], was [{metaData.Year}] on file [{metaData.FileInfo.Name}");
|
||||
metaData.Year = year;
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode) TagsHelper.WriteTags(metaData, metaData.Filename);
|
||||
if (!Configuration.Inspector.IsInReadOnlyMode)
|
||||
{
|
||||
TagsHelper.WriteTags(metaData, metaData.Filename);
|
||||
}
|
||||
}
|
||||
|
||||
data = $"Found [{found}] files, Modified [{modified}] files";
|
||||
|
|
|
@ -5,8 +5,11 @@ namespace Roadie.Library.Inspect.Plugins.Directory
|
|||
public interface IInspectorDirectoryPlugin
|
||||
{
|
||||
string Description { get; }
|
||||
|
||||
bool IsEnabled { get; }
|
||||
|
||||
bool IsPostProcessingPlugin { get; }
|
||||
|
||||
int Order { get; }
|
||||
|
||||
OperationResult<string> Process(DirectoryInfo directory);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using MetadataExtractor;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Extensions;
|
||||
|
@ -14,16 +15,15 @@ namespace Roadie.Library.Inspect.Plugins.File
|
|||
|
||||
public override int Order => 5;
|
||||
|
||||
public CleanUpArtists(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger,
|
||||
IID3TagsHelper tagsHelper)
|
||||
public CleanUpArtists(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
|
||||
: base(configuration, cacheManager, logger, tagsHelper)
|
||||
{
|
||||
}
|
||||
|
||||
public string CleanArtist(string artist, string trackArtist = null)
|
||||
{
|
||||
artist = artist ?? string.Empty;
|
||||
trackArtist = trackArtist ?? string.Empty;
|
||||
artist ??= string.Empty;
|
||||
trackArtist ??= string.Empty;
|
||||
var splitCharacter = AudioMetaData.ArtistSplitCharacter.ToString();
|
||||
|
||||
// Replace seperators with proper split character
|
||||
|
@ -39,12 +39,21 @@ namespace Roadie.Library.Inspect.Plugins.File
|
|||
}
|
||||
if (!string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(trackArtist))
|
||||
{
|
||||
result = result.Replace(splitCharacter + trackArtist + splitCharacter, "", StringComparison.OrdinalIgnoreCase);
|
||||
result = result.Replace(trackArtist + splitCharacter, "", StringComparison.OrdinalIgnoreCase);
|
||||
result = result.Replace(splitCharacter + trackArtist, "", StringComparison.OrdinalIgnoreCase);
|
||||
result = result.Replace(trackArtist, "", StringComparison.OrdinalIgnoreCase);
|
||||
result = result.Replace(splitCharacter + trackArtist + splitCharacter, string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
result = result.Replace(trackArtist + splitCharacter, string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
result = result.Replace(splitCharacter + trackArtist, string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
result = result.Replace(trackArtist, string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
if(Configuration.Processing.DoDetectFeatureFragments)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
if(result.HasFeaturingFragments())
|
||||
{
|
||||
throw new RoadieProcessingException($"Artist name [{ result }] has Feature fragments.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.IsNullOrEmpty(result) ? null : result;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,13 @@ namespace Roadie.Library.Inspect.Plugins.File
|
|||
{
|
||||
var result = new OperationResult<AudioMetaData>();
|
||||
if (Configuration.Processing.DoAudioCleanup)
|
||||
{
|
||||
if (Configuration.Processing.DoClearComments)
|
||||
{
|
||||
metaData.Comments = null;
|
||||
}
|
||||
}
|
||||
|
||||
result.Data = metaData;
|
||||
result.IsSuccess = true;
|
||||
return result;
|
||||
|
|
|
@ -27,9 +27,21 @@ namespace Roadie.Library.Inspect.Plugins.File
|
|||
var originalRelease = metaData.Release;
|
||||
metaData.Release = metaData.Release
|
||||
?.CleanString(Configuration, Configuration.Processing.ReleaseRemoveStringsRegex).ToTitleCase(false);
|
||||
if (string.IsNullOrEmpty(metaData.Release)) metaData.Release = originalRelease;
|
||||
if (string.IsNullOrEmpty(metaData.Release))
|
||||
{
|
||||
metaData.Release = originalRelease;
|
||||
}
|
||||
}
|
||||
if (Configuration.Processing.DoDetectFeatureFragments)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(metaData?.Release))
|
||||
{
|
||||
if (metaData.Release.HasFeaturingFragments())
|
||||
{
|
||||
throw new RoadieProcessingException($"Release title [{ metaData?.Release }] has Feature fragments.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Data = metaData;
|
||||
result.IsSuccess = true;
|
||||
return result;
|
||||
|
|
|
@ -27,9 +27,21 @@ namespace Roadie.Library.Inspect.Plugins.File
|
|||
var originalTitle = metaData.Title;
|
||||
metaData.Title = metaData.Title
|
||||
?.CleanString(Configuration, Configuration.Processing.TrackRemoveStringsRegex).ToTitleCase(false);
|
||||
if (string.IsNullOrEmpty(metaData.Title)) metaData.Title = originalTitle;
|
||||
if (string.IsNullOrEmpty(metaData.Title))
|
||||
{
|
||||
metaData.Title = originalTitle;
|
||||
}
|
||||
}
|
||||
if (Configuration.Processing.DoDetectFeatureFragments)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(metaData?.Title))
|
||||
{
|
||||
if (metaData.Release.HasFeaturingFragments())
|
||||
{
|
||||
throw new RoadieProcessingException($"Track title [{ metaData?.Title }] has Feature fragments.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Data = metaData;
|
||||
result.IsSuccess = true;
|
||||
return result;
|
||||
|
|
|
@ -20,25 +20,27 @@ namespace Roadie.Library.Inspect.Plugins.File
|
|||
{
|
||||
}
|
||||
|
||||
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
|
||||
{
|
||||
return attributes & ~attributesToRemove;
|
||||
}
|
||||
|
||||
public override OperationResult<AudioMetaData> Process(AudioMetaData metaData)
|
||||
{
|
||||
var result = new OperationResult<AudioMetaData>();
|
||||
if (Configuration.Processing.DoAudioCleanup)
|
||||
{
|
||||
if (metaData.FileInfo.IsReadOnly)
|
||||
{
|
||||
metaData.FileInfo.Attributes =
|
||||
RemoveAttribute(metaData.FileInfo.Attributes, FileAttributes.ReadOnly);
|
||||
Console.WriteLine($"╟ Removed read only attribute on file file [{metaData.FileInfo.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
result.Data = metaData;
|
||||
result.IsSuccess = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
|
||||
{
|
||||
return attributes & ~attributesToRemove;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,9 @@ namespace Roadie.Library.Inspect.Plugins.File
|
|||
public interface IInspectorFilePlugin
|
||||
{
|
||||
string Description { get; }
|
||||
|
||||
bool IsEnabled { get; }
|
||||
|
||||
int Order { get; }
|
||||
|
||||
OperationResult<AudioMetaData> Process(AudioMetaData metaData);
|
||||
|
|
|
@ -12,22 +12,21 @@ namespace Roadie.Library.Inspect.Plugins
|
|||
{
|
||||
public abstract class PluginBase
|
||||
{
|
||||
public abstract string Description { get; }
|
||||
|
||||
public abstract int Order { get; }
|
||||
private Dictionary<string, IEnumerable<AudioMetaData>> CachedAudioDatas { get; }
|
||||
|
||||
protected ICacheManager CacheManager { get; }
|
||||
|
||||
protected IRoadieSettings Configuration { get; }
|
||||
|
||||
protected IEnumerable<string> ListReplacements { get; } = new List<string>
|
||||
{" ; ", " ;", "; ", ";", ";", "\\"};
|
||||
protected IEnumerable<string> ListReplacements { get; } = new List<string> { " ; ", " ;", "; ", ";", ";", "\\" };
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected IID3TagsHelper TagsHelper { get; }
|
||||
|
||||
private Dictionary<string, IEnumerable<AudioMetaData>> CachedAudioDatas { get; }
|
||||
public abstract string Description { get; }
|
||||
|
||||
public abstract int Order { get; }
|
||||
|
||||
public PluginBase(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger,
|
||||
IID3TagsHelper tagsHelper)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Models.Collections;
|
||||
using Roadie.Library.Models.Collections;
|
||||
using Roadie.Library.Models.Playlists;
|
||||
using Roadie.Library.Models.Releases;
|
||||
using Roadie.Library.Models.Statistics;
|
||||
|
@ -8,6 +7,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ namespace Roadie.Library.Models
|
|||
[IgnoreDataMember]
|
||||
public string ISNI { get; set; }
|
||||
|
||||
[JsonProperty("isniList")]
|
||||
[JsonPropertyName("isniList")]
|
||||
public IEnumerable<string> ISNIList
|
||||
{
|
||||
get
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Models.Collections;
|
||||
using Roadie.Library.Models.Playlists;
|
||||
using Roadie.Library.Models.Releases;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models.Collections
|
||||
{
|
||||
|
@ -7,7 +7,9 @@ namespace Roadie.Library.Models.Collections
|
|||
internal class CollectionReleaseList : EntityInfoModelBase
|
||||
{
|
||||
private string _listNumber;
|
||||
|
||||
public DataToken Artist { get; set; }
|
||||
|
||||
public string ArtistThumbnailUrl { get; set; }
|
||||
|
||||
public string ListNumber
|
||||
|
@ -16,10 +18,16 @@ namespace Roadie.Library.Models.Collections
|
|||
set => _listNumber = value;
|
||||
}
|
||||
|
||||
[JsonIgnore] public int ListNumberValue { get; set; }
|
||||
[JsonIgnore]
|
||||
public int ListNumberValue { get; set; }
|
||||
|
||||
public DataToken Release { get; set; }
|
||||
[JsonIgnore] public DateTime? ReleaseDateDateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTime? ReleaseDateDateTime { get; set; }
|
||||
|
||||
public short? ReleaseRating { get; set; }
|
||||
|
||||
public string ReleaseThumbnailUrl { get; set; }
|
||||
|
||||
public string ReleaseYear => ReleaseDateDateTime.HasValue
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Mapster;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using Mapster;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
@ -26,9 +26,6 @@ namespace Roadie.Library.Models
|
|||
|
||||
public DateTime? LastUpdated { get; set; }
|
||||
|
||||
[MaxLength(250)]
|
||||
public virtual string SortName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Random int to sort when Random Request
|
||||
/// </summary>
|
||||
|
@ -36,6 +33,9 @@ namespace Roadie.Library.Models
|
|||
[JsonIgnore]
|
||||
public int RandomSortId { get; set; }
|
||||
|
||||
[MaxLength(250)]
|
||||
public virtual string SortName { get; set; }
|
||||
|
||||
public EntityInfoModelBase()
|
||||
{
|
||||
RandomSortId = StaticRandom.Instance.Next();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Mapster;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
|
@ -7,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
@ -7,9 +6,13 @@ namespace Roadie.Library.Models
|
|||
public sealed class LabelList : EntityInfoModelBase
|
||||
{
|
||||
public int? ArtistCount { get; set; }
|
||||
|
||||
public DataToken Label { get; set; }
|
||||
|
||||
public int? ReleaseCount { get; set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public int? TrackCount { get; set; }
|
||||
|
||||
public static LabelList FromDataLabel(Data.Label label, Image labelThumbnail)
|
||||
|
|
|
@ -1,35 +1,52 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Models.Users;
|
||||
using Roadie.Library.Models.Users;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class PlayActivityList
|
||||
{
|
||||
|
||||
public DataToken Artist { get; set; }
|
||||
|
||||
public Image ArtistThumbnail { get; set; }
|
||||
|
||||
public bool IsNowPlaying { get; set; }
|
||||
|
||||
public string PlayedDate => PlayedDateDateTime.HasValue ? PlayedDateDateTime.Value.ToString("s") : null;
|
||||
|
||||
[JsonIgnore] public DateTime? PlayedDateDateTime { get; set; }
|
||||
[JsonIgnore]
|
||||
public DateTime? PlayedDateDateTime { get; set; }
|
||||
|
||||
public string PlayedDay => PlayedDateDateTime.HasValue ? PlayedDateDateTime.Value.ToString("MM/dd/yyyy") : null;
|
||||
|
||||
public int? Rating { get; set; }
|
||||
|
||||
public DataToken Release { get; set; }
|
||||
|
||||
public string ReleasePlayUrl { get; set; }
|
||||
|
||||
public Image ReleaseThumbnail { get; set; }
|
||||
|
||||
public TrackList Track { get; set; }
|
||||
|
||||
public DataToken TrackArtist { get; set; }
|
||||
|
||||
public string TrackPlayUrl { get; set; }
|
||||
|
||||
public DataToken User { get; set; }
|
||||
|
||||
public int? UserRating { get; set; }
|
||||
|
||||
public Image UserThumbnail { get; set; }
|
||||
|
||||
public UserTrack UserTrack { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"User [{User}], Artist [{Artist}], Release [{Release}], Track [{Track}]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Roadie.Library.Models.Player
|
||||
{
|
||||
|
@ -11,14 +11,11 @@ namespace Roadie.Library.Models.Player
|
|||
{
|
||||
private string _rawTracks;
|
||||
|
||||
public string RawTracks => _rawTracks ?? (_rawTracks = JsonConvert.SerializeObject(Tracks));
|
||||
public string RawTracks => _rawTracks ?? (_rawTracks = JsonSerializer.Serialize(Tracks));
|
||||
|
||||
public string SiteName { get; set; }
|
||||
|
||||
public string TotalTrackTime
|
||||
{
|
||||
get { return TimeSpan.FromMilliseconds((double)Tracks.Sum(x => x.Duration)).ToString(@"hh\:mm\:ss"); }
|
||||
}
|
||||
public string TotalTrackTime => TimeSpan.FromMilliseconds((double)Tracks.Sum(x => x.Duration)).ToString(@"hh\:mm\:ss");
|
||||
|
||||
public string TrackCount => Tracks.Count().ToString("D3");
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models.Releases
|
||||
{
|
||||
|
@ -7,10 +7,17 @@ namespace Roadie.Library.Models.Releases
|
|||
public sealed class ReleaseLabelList : EntityInfoModelBase
|
||||
{
|
||||
public string BeginDate => BeginDatedDateTime.HasValue ? BeginDatedDateTime.Value.ToString("s") : null;
|
||||
[JsonIgnore] public DateTime? BeginDatedDateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTime? BeginDatedDateTime { get; set; }
|
||||
|
||||
public string CatalogNumber { get; set; }
|
||||
|
||||
public string EndDate => EndDatedDateTime.HasValue ? EndDatedDateTime.Value.ToString("s") : null;
|
||||
[JsonIgnore] public DateTime? EndDatedDateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTime? EndDatedDateTime { get; set; }
|
||||
|
||||
public DataToken Label { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
using Mapster;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Models.Users;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models.Releases
|
||||
{
|
||||
|
@ -14,14 +14,20 @@ namespace Roadie.Library.Models.Releases
|
|||
public sealed class ReleaseList : EntityInfoModelBase
|
||||
{
|
||||
public DataToken Artist { get; set; }
|
||||
|
||||
public Image ArtistThumbnail { get; set; }
|
||||
|
||||
public decimal? Duration { get; set; }
|
||||
|
||||
public string DurationTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Duration.HasValue) return "--:--";
|
||||
if (!Duration.HasValue)
|
||||
{
|
||||
return "--:--";
|
||||
}
|
||||
|
||||
return new TimeInfo(Duration.Value).ToFullFormattedString();
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +47,7 @@ namespace Roadie.Library.Models.Releases
|
|||
}
|
||||
|
||||
public DateTime? LastPlayed { get; set; }
|
||||
|
||||
public LibraryStatus? LibraryStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -49,17 +56,26 @@ namespace Roadie.Library.Models.Releases
|
|||
public int? ListNumber { get; set; }
|
||||
|
||||
public IEnumerable<ReleaseMediaList> Media { get; set; }
|
||||
|
||||
public int? MediaCount { get; set; }
|
||||
|
||||
public double? Rank { get; set; }
|
||||
|
||||
public short? Rating { get; set; }
|
||||
|
||||
public DataToken Release { get; set; }
|
||||
|
||||
public string ReleaseDate => ReleaseDateDateTime.HasValue
|
||||
? ReleaseDateDateTime.Value.ToUniversalTime().ToString("yyyy-MM-dd")
|
||||
: null;
|
||||
|
||||
[JsonIgnore] public DateTime? ReleaseDateDateTime { get; set; }
|
||||
[JsonIgnore] [AdaptIgnore] public string ReleaseName => Release?.Text;
|
||||
[JsonIgnore]
|
||||
public DateTime? ReleaseDateDateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[AdaptIgnore]
|
||||
public string ReleaseName => Release?.Text;
|
||||
|
||||
public string ReleasePlayUrl { get; set; }
|
||||
|
||||
public string ReleaseYear => ReleaseDateDateTime.HasValue
|
||||
|
@ -67,10 +83,15 @@ namespace Roadie.Library.Models.Releases
|
|||
: null;
|
||||
|
||||
public Statuses? Status { get; set; }
|
||||
|
||||
public string StatusVerbose => (Status ?? Statuses.Missing).ToString();
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public int? TrackCount { get; set; }
|
||||
|
||||
public int? TrackPlayedCount { get; set; }
|
||||
|
||||
public UserRelease UserRating { get; set; }
|
||||
|
||||
public static ReleaseList FromDataRelease(Data.Release release, Data.Artist artist, string baseUrl,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Models.Pagination;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
||||
{
|
||||
|
@ -20,8 +20,16 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
if (id.StartsWith(ArtistIdIdentifier)) return SafeParser.ToGuid(id);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.StartsWith(ArtistIdIdentifier))
|
||||
{
|
||||
return SafeParser.ToGuid(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +48,16 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
if (id.StartsWith(CollectionIdentifier)) return SafeParser.ToGuid(id);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.StartsWith(CollectionIdentifier))
|
||||
{
|
||||
return SafeParser.ToGuid(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -73,8 +89,12 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
// Default should be false (XML)
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(f)) return false;
|
||||
return f.ToLower().StartsWith("j");
|
||||
if (string.IsNullOrEmpty(f))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return f.StartsWith("j", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,8 +110,16 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(p)) return null;
|
||||
if (p.StartsWith("enc:")) return p.ToLower().Replace("enc:", "").FromHexString();
|
||||
if (string.IsNullOrEmpty(p))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (p.StartsWith("enc:"))
|
||||
{
|
||||
return p.ToLower().Replace("enc:", string.Empty).FromHexString();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +128,16 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
if (id.StartsWith(PlaylistdIdentifier)) return SafeParser.ToGuid(id);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.StartsWith(PlaylistdIdentifier))
|
||||
{
|
||||
return SafeParser.ToGuid(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -115,8 +151,16 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
if (id.StartsWith(ReleaseIdIdentifier)) return SafeParser.ToGuid(id);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.StartsWith(ReleaseIdIdentifier))
|
||||
{
|
||||
return SafeParser.ToGuid(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -145,8 +189,16 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
if (id.StartsWith(TrackIdIdentifier)) return SafeParser.ToGuid(id);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.StartsWith(TrackIdIdentifier))
|
||||
{
|
||||
return SafeParser.ToGuid(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Models.Releases;
|
||||
using Roadie.Library.Models.Releases;
|
||||
using Roadie.Library.Models.Statistics;
|
||||
using Roadie.Library.Models.Users;
|
||||
using Roadie.Library.Utility;
|
||||
|
@ -7,6 +6,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
@ -17,33 +17,47 @@ namespace Roadie.Library.Models
|
|||
|
||||
private IEnumerable<string> _partTitles;
|
||||
|
||||
[MaxLength(50)] public string AmgId { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string AmgId { get; set; }
|
||||
|
||||
public ArtistList Artist { get; set; }
|
||||
public IEnumerable<CreditList> Credits { get; set; }
|
||||
|
||||
public Image ArtistThumbnail { get; set; }
|
||||
|
||||
public IEnumerable<Comment> Comments { get; set; }
|
||||
|
||||
public IEnumerable<CreditList> Credits { get; set; }
|
||||
|
||||
public int Duration { get; set; }
|
||||
|
||||
public string DurationTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Duration < 1) return "--:--";
|
||||
if (Duration < 1)
|
||||
{
|
||||
return "--:--";
|
||||
}
|
||||
|
||||
return new TimeInfo(Duration).ToFullFormattedString();
|
||||
}
|
||||
}
|
||||
|
||||
public long FileSize { get; set; }
|
||||
[MaxLength(32)] public string Hash { get; set; }
|
||||
|
||||
[MaxLength(15)] public string ISRC { get; set; }
|
||||
[MaxLength(32)]
|
||||
public string Hash { get; set; }
|
||||
|
||||
[MaxLength(50)] public string LastFMId { get; set; }
|
||||
[MaxLength(15)]
|
||||
public string ISRC { get; set; }
|
||||
|
||||
[MaxLength(50)]
|
||||
public string LastFMId { get; set; }
|
||||
|
||||
public DateTime? LastPlayed { get; set; }
|
||||
|
||||
public Image MediumThumbnail { get; set; }
|
||||
|
||||
[MaxLength(100)] public string MusicBrainzId { get; set; }
|
||||
|
||||
// When populated a "data:image" base64 byte array of an image to use as new Thumbnail
|
||||
|
@ -60,7 +74,11 @@ namespace Roadie.Library.Models
|
|||
{
|
||||
if (_partTitles == null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(PartTitles)) return null;
|
||||
if (string.IsNullOrEmpty(PartTitles))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return PartTitles.Replace("|", "\n").Split("\n");
|
||||
}
|
||||
|
||||
|
@ -70,7 +88,9 @@ namespace Roadie.Library.Models
|
|||
}
|
||||
|
||||
public int PlayedCount { get; set; }
|
||||
|
||||
public short Rating { get; set; }
|
||||
|
||||
public ReleaseList Release { get; set; }
|
||||
|
||||
public string ReleaseMediaId { get; set; }
|
||||
|
@ -82,7 +102,10 @@ namespace Roadie.Library.Models
|
|||
public TrackStatistics Statistics { get; set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
[MaxLength(250)] [Required] public string Title { get; set; }
|
||||
|
||||
[MaxLength(250)]
|
||||
[Required]
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Track Artist, not release artist. If this is present then the track has an artist different than the release.
|
||||
|
@ -90,15 +113,15 @@ namespace Roadie.Library.Models
|
|||
public ArtistList TrackArtist { get; set; }
|
||||
|
||||
public Image TrackArtistThumbnail { get; set; }
|
||||
|
||||
public DataToken TrackArtistToken { get; set; }
|
||||
|
||||
[Required] public short TrackNumber { get; set; }
|
||||
|
||||
public string TrackPlayUrl { get; set; }
|
||||
|
||||
public UserTrack UserRating { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Id [{ Id }], Title [{ Title }]";
|
||||
}
|
||||
public override string ToString() => $"Id [{ Id }], Title [{ Title }]";
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Models.Releases;
|
||||
using Roadie.Library.Models.Users;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
using Release = Roadie.Library.Data.Release;
|
||||
using Roadie.Library.Enums;
|
||||
|
||||
namespace Roadie.Library.Models
|
||||
{
|
||||
|
@ -18,6 +18,7 @@ namespace Roadie.Library.Models
|
|||
public sealed class TrackList : EntityInfoModelBase
|
||||
{
|
||||
public ArtistList Artist { get; set; }
|
||||
|
||||
public int? Duration { get; set; }
|
||||
|
||||
public string DurationTime => Duration.HasValue ? new TimeInfo(Duration.Value).ToFullFormattedString() : "--:--";
|
||||
|
@ -25,8 +26,11 @@ namespace Roadie.Library.Models
|
|||
public string DurationTimeShort => Duration.HasValue ? new TimeInfo(Duration.Value).ToShortFormattedString() : "--:--";
|
||||
|
||||
public int? FavoriteCount { get; set; }
|
||||
|
||||
public int? FileSize { get; set; }
|
||||
|
||||
public DateTime? LastPlayed { get; set; }
|
||||
|
||||
public int? MediaNumber { get; set; }
|
||||
|
||||
[MaxLength(65535)]
|
||||
|
@ -38,100 +42,84 @@ namespace Roadie.Library.Models
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(PartTitles)) return null;
|
||||
if (string.IsNullOrEmpty(PartTitles))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return PartTitles.Split('\n');
|
||||
}
|
||||
}
|
||||
|
||||
public int? PlayedCount { get; set; }
|
||||
public short? Rating { get; set; }
|
||||
public ReleaseList Release { get; set; }
|
||||
[JsonIgnore] public DateTime? ReleaseDate { get; set; }
|
||||
public short? ReleaseRating { get; set; }
|
||||
public Image Thumbnail { get; set; }
|
||||
public string Title { get; set; }
|
||||
public DataToken Track { get; set; }
|
||||
public ArtistList TrackArtist { get; set; }
|
||||
[JsonIgnore] public string TrackId => Track?.Value;
|
||||
|
||||
[JsonIgnore] public string TrackName => Track?.Text;
|
||||
[JsonIgnore] public string ReleaseName => Release?.Release?.Text;
|
||||
public short? Rating { get; set; }
|
||||
|
||||
public ReleaseList Release { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ReleaseName => Release?.Release?.Text;
|
||||
|
||||
public short? ReleaseRating { get; set; }
|
||||
|
||||
public Statuses? Status { get; set; }
|
||||
|
||||
public string StatusVerbose => (Status ?? Statuses.Missing).ToString();
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public DataToken Track { get; set; }
|
||||
|
||||
public ArtistList TrackArtist { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string TrackId => Track?.Value;
|
||||
|
||||
[JsonIgnore]
|
||||
public string TrackName => Track?.Text;
|
||||
|
||||
public int? TrackNumber { get; set; }
|
||||
|
||||
public string TrackPlayUrl { get; set; }
|
||||
|
||||
public UserTrack UserRating { get; set; }
|
||||
|
||||
public int? Year
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ReleaseDate.HasValue) return ReleaseDate.Value.Year;
|
||||
if (ReleaseDate.HasValue)
|
||||
{
|
||||
return ReleaseDate.Value.Year;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetArtistReleaseHashCode() => this.Artist?.Artist?.Value.GetHashCode() + this.Release?.Release?.Value.GetHashCode() ?? 0;
|
||||
|
||||
public override int GetHashCode() => this.Artist?.Artist?.Value.GetHashCode() + this.Release?.Release?.Value.GetHashCode() + this.Track?.Value.GetHashCode() ?? 0;
|
||||
|
||||
public static IEnumerable<int> AllIndexesOfArtist(IEnumerable<TrackList> tracks, string artistId)
|
||||
{
|
||||
if(tracks == null || !tracks.Any())
|
||||
if (tracks?.Any() != true)
|
||||
{
|
||||
return new int[0];
|
||||
}
|
||||
return tracks.Select((b, i) => b.Artist?.Artist?.Value == artistId ? i : -1).Where(i => i != -1).ToArray();
|
||||
return tracks.Select((b, i) => b.Artist?.Artist?.Value == artistId ? i : -1).Where(i => i != -1).ToArray();
|
||||
}
|
||||
|
||||
public static IEnumerable<int> AllIndexesOfRelease(IEnumerable<TrackList> tracks, string releaseId)
|
||||
{
|
||||
if (tracks == null || !tracks.Any())
|
||||
if (tracks?.Any() != true)
|
||||
{
|
||||
return new int[0];
|
||||
}
|
||||
return tracks.Select((b, i) => b.Release?.Release?.Value == releaseId ? i : -1).Where(i => i != -1).ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the given list is sorted so that Artist and Release don't repeat in sequence.
|
||||
/// </summary>
|
||||
public static IEnumerable<TrackList> Shuffle(IEnumerable<TrackList> tracks)
|
||||
{
|
||||
|
||||
var shuffledTracks = new List<TrackList>();
|
||||
var skippedTracks = new List<TrackList>();
|
||||
foreach(var track in tracks)
|
||||
{
|
||||
var trackArtist = track.Artist?.Artist?.Value ?? track.Release?.Artist?.Value;
|
||||
var trackRelease = track.Release?.Release?.Value;
|
||||
if (!shuffledTracks.Any(x => x.Artist?.Artist?.Value == trackArtist &&
|
||||
x.Release?.Release?.Value != trackRelease))
|
||||
{
|
||||
shuffledTracks.Add(track);
|
||||
}
|
||||
else
|
||||
{
|
||||
skippedTracks.Add(track);
|
||||
}
|
||||
}
|
||||
var result = new List<TrackList>(shuffledTracks);
|
||||
while (skippedTracks.ToList().Any())
|
||||
{
|
||||
var st = skippedTracks.First();
|
||||
var trackArtist = st.Artist?.Artist?.Value ?? st.Release?.Artist?.Value;
|
||||
var insertAt = AllIndexesOfArtist(result, trackArtist).Last() + 2;
|
||||
if(insertAt < result.Count() - 1)
|
||||
{
|
||||
result.Insert(insertAt, st);
|
||||
skippedTracks.Remove(st);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static TrackList FromDataTrack(string trackPlayUrl,
|
||||
Data.Track track,
|
||||
int releaseMediaNumber,
|
||||
|
@ -173,6 +161,44 @@ namespace Roadie.Library.Models
|
|||
};
|
||||
}
|
||||
|
||||
public int GetArtistReleaseHashCode() => this.Artist?.Artist?.Value.GetHashCode() + this.Release?.Release?.Value.GetHashCode() ?? 0;
|
||||
|
||||
public override int GetHashCode() => this.Artist?.Artist?.Value.GetHashCode() + this.Release?.Release?.Value.GetHashCode() + this.Track?.Value.GetHashCode() ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the given list is sorted so that Artist and Release don't repeat in sequence.
|
||||
/// </summary>
|
||||
public static IEnumerable<TrackList> Shuffle(IEnumerable<TrackList> tracks)
|
||||
{
|
||||
var shuffledTracks = new List<TrackList>();
|
||||
var skippedTracks = new List<TrackList>();
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
var trackArtist = track.Artist?.Artist?.Value ?? track.Release?.Artist?.Value;
|
||||
var trackRelease = track.Release?.Release?.Value;
|
||||
if (!shuffledTracks.Any(x => x.Artist?.Artist?.Value == trackArtist &&
|
||||
x.Release?.Release?.Value != trackRelease))
|
||||
{
|
||||
shuffledTracks.Add(track);
|
||||
}
|
||||
else
|
||||
{
|
||||
skippedTracks.Add(track);
|
||||
}
|
||||
}
|
||||
var result = new List<TrackList>(shuffledTracks);
|
||||
while (skippedTracks.ToList().Count > 0)
|
||||
{
|
||||
var st = skippedTracks[0];
|
||||
var trackArtist = st.Artist?.Artist?.Value ?? st.Release?.Artist?.Value;
|
||||
var insertAt = AllIndexesOfArtist(result, trackArtist).Last() + 2;
|
||||
if (insertAt < result.Count() - 1)
|
||||
{
|
||||
result.Insert(insertAt, st);
|
||||
skippedTracks.Remove(st);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using Roadie.Library.Models.Statistics;
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.Models.Users
|
||||
{
|
||||
|
@ -11,7 +12,9 @@ namespace Roadie.Library.Models.Users
|
|||
{
|
||||
public const string ActionKeyUserRated = "__userrated__";
|
||||
public const string DefaultIncludes = "stats";
|
||||
[MaxLength(100)] public string ApiToken { get; set; }
|
||||
|
||||
[MaxLength(100)]
|
||||
public string ApiToken { get; set; }
|
||||
|
||||
public Image Avatar { get; set; }
|
||||
|
||||
|
@ -20,7 +23,12 @@ namespace Roadie.Library.Models.Users
|
|||
/// </summary>
|
||||
public string AvatarData { get; set; }
|
||||
|
||||
[Required] [MaxLength(100)] public string ConcurrencyStamp { get; set; }
|
||||
[Required]
|
||||
[MaxLength(100)]
|
||||
public string ConcurrencyStamp { get; set; }
|
||||
|
||||
public short? DefaultRowsPerPage { get; set; }
|
||||
|
||||
public bool DoUseHtmlPlayer { get; set; }
|
||||
|
||||
[Required]
|
||||
|
@ -28,18 +36,34 @@ namespace Roadie.Library.Models.Users
|
|||
[DataType(DataType.EmailAddress)]
|
||||
public string Email { get; set; }
|
||||
|
||||
[MaxLength(500)] public string FtpDirectory { get; set; }
|
||||
[MaxLength(500)]
|
||||
public string FtpDirectory { get; set; }
|
||||
|
||||
[MaxLength(500)] public string FtpPassword { get; set; }
|
||||
[MaxLength(500)]
|
||||
public string FtpPassword { get; set; }
|
||||
|
||||
[MaxLength(250)] public string FtpUrl { get; set; }
|
||||
[MaxLength(250)]
|
||||
[DataType(DataType.Url)]
|
||||
public string FtpUrl { get; set; }
|
||||
|
||||
[MaxLength(50)] public string FtpUsername { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string FtpUsername { get; set; }
|
||||
|
||||
[JsonPropertyName("UserDbId")]
|
||||
public new int? Id { get; set; }
|
||||
|
||||
public bool IsAdmin { get; set; }
|
||||
|
||||
public bool IsEditor { get; set; }
|
||||
|
||||
public bool IsPrivate { get; set; }
|
||||
|
||||
public DateTime LastApiAccess { get; set; }
|
||||
|
||||
public DateTime LastLogin { get; set; }
|
||||
|
||||
public Image MediumThumbnail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Posted password only used when changing password from profile edits
|
||||
/// </summary>
|
||||
|
@ -55,23 +79,34 @@ namespace Roadie.Library.Models.Users
|
|||
public string PasswordConfirmation { get; set; }
|
||||
|
||||
public short? PlayerTrackLimit { get; set; }
|
||||
[MaxLength(65535)] public string Profile { get; set; }
|
||||
|
||||
[MaxLength(65535)]
|
||||
public string Profile { get; set; }
|
||||
|
||||
public short? RandomReleaseLimit { get; set; }
|
||||
|
||||
public short? RecentlyPlayedLimit { get; set; }
|
||||
|
||||
public bool RemoveTrackFromQueAfterPlayed { get; set; }
|
||||
[NotMapped] [AdaptIgnore] public UserStatistics Statistics { get; set; }
|
||||
[StringLength(50)] [Required] public string Timeformat { get; set; }
|
||||
|
||||
[MaxLength(50)] [Required] public string Timezone { get; set; }
|
||||
[NotMapped]
|
||||
[AdaptIgnore]
|
||||
public UserStatistics Statistics { get; set; }
|
||||
|
||||
[AdaptMember("RoadieId")] public Guid UserId { get; set; }
|
||||
[StringLength(50)]
|
||||
[Required]
|
||||
public string Timeformat { get; set; }
|
||||
|
||||
[Required] [MaxLength(20)] public string UserName { get; set; }
|
||||
public Image MediumThumbnail { get; set; }
|
||||
[MaxLength(50)]
|
||||
[Required]
|
||||
public string Timezone { get; set; }
|
||||
|
||||
public DateTime LastLogin { get; set; }
|
||||
public DateTime LastApiAccess { get; set; }
|
||||
public short? DefaultRowsPerPage { get; set; }
|
||||
[AdaptMember("RoadieId")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(20)]
|
||||
public string UserName { get; set; }
|
||||
|
||||
public override string ToString() => $"Id [{Id}], RoadieId [{UserId}], UserName [{UserName}]";
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Roadie.Library
|
||||
|
@ -10,6 +10,7 @@ namespace Roadie.Library
|
|||
public class OperationResult<T>
|
||||
{
|
||||
private List<Exception> _errors;
|
||||
|
||||
private List<string> _messages;
|
||||
|
||||
[XmlIgnore]
|
||||
|
@ -22,12 +23,15 @@ namespace Roadie.Library
|
|||
/// <summary>
|
||||
/// Client friendly exceptions
|
||||
/// </summary>
|
||||
[JsonProperty("errors")]
|
||||
[JsonPropertyName("errors")]
|
||||
public IEnumerable<AppException> AppExceptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Errors == null || !Errors.Any()) return null;
|
||||
if (Errors?.Any() != true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Errors.Select(x => new AppException(x.Message));
|
||||
}
|
||||
|
@ -41,7 +45,8 @@ namespace Roadie.Library
|
|||
[JsonIgnore]
|
||||
public IEnumerable<Exception> Errors { get; set; }
|
||||
|
||||
[JsonIgnore] public bool IsAccessDeniedResult { get; set; }
|
||||
[JsonIgnore]
|
||||
public bool IsAccessDeniedResult { get; set; }
|
||||
|
||||
[JsonIgnore] public bool IsNotFoundResult { get; set; }
|
||||
|
||||
|
@ -57,29 +62,13 @@ namespace Roadie.Library
|
|||
|
||||
public OperationResult(IEnumerable<string> messages = null)
|
||||
{
|
||||
if (messages != null && messages.Any())
|
||||
if (messages?.Any() == true)
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
messages.ToList().ForEach(AddMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public OperationResult(bool isNotFoundResult, IEnumerable<string> messages = null)
|
||||
{
|
||||
IsNotFoundResult = isNotFoundResult;
|
||||
if (messages != null && messages.Any())
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
messages.ToList().ForEach(AddMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public OperationResult(bool isNotFoundResult, string message)
|
||||
{
|
||||
IsNotFoundResult = isNotFoundResult;
|
||||
AddMessage(message);
|
||||
}
|
||||
|
||||
public OperationResult(string message = null)
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
|
@ -91,6 +80,22 @@ namespace Roadie.Library
|
|||
AddError(error);
|
||||
}
|
||||
|
||||
public OperationResult(bool isNotFoundResult, IEnumerable<string> messages = null)
|
||||
{
|
||||
IsNotFoundResult = isNotFoundResult;
|
||||
if (messages?.Any() == true)
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
messages.ToList().ForEach(AddMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public OperationResult(bool isNotFoundResult, string message)
|
||||
{
|
||||
IsNotFoundResult = isNotFoundResult;
|
||||
AddMessage(message);
|
||||
}
|
||||
|
||||
public OperationResult(string message = null, Exception error = null)
|
||||
{
|
||||
AddMessage(message);
|
||||
|
@ -101,9 +106,7 @@ namespace Roadie.Library
|
|||
{
|
||||
if (exception != null)
|
||||
{
|
||||
if (_errors == null) _errors = new List<Exception>();
|
||||
|
||||
_errors.Add(exception);
|
||||
(_errors ?? (_errors = new List<Exception>())).Add(exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,9 +114,7 @@ namespace Roadie.Library
|
|||
{
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
if (_messages == null) _messages = new List<string>();
|
||||
|
||||
_messages.Add(message);
|
||||
(_messages ?? (_messages = new List<string>())).Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,17 +18,19 @@
|
|||
<PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" />
|
||||
<PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" />
|
||||
<PackageReference Include="LiteDB" Version="5.0.8" />
|
||||
<PackageReference Include="Magick.NET-Q16-x64" Version="7.20.0" />
|
||||
<PackageReference Include="Mapster" Version="5.3.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.4" />
|
||||
<PackageReference Include="MetadataExtractor" Version="2.4.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="1.3.4" />
|
||||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.1" />
|
||||
<PackageReference Include="MimeMapping" Version="1.0.1.26" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.2" />
|
||||
<PackageReference Include="MimeMapping" Version="1.0.1.30" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.1" />
|
||||
<PackageReference Include="RestSharp" Version="106.11.4" />
|
||||
|
@ -39,6 +41,7 @@
|
|||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.7.0" />
|
||||
<PackageReference Include="System.Runtime.Caching" Version="4.7.0" />
|
||||
<PackageReference Include="Utf8Json" Version="1.3.7" />
|
||||
<PackageReference Include="z440.atl.core" Version="3.5.0" />
|
||||
<PackageReference Include="zlib.net-mutliplatform" Version="1.0.4" />
|
||||
</ItemGroup>
|
||||
|
|
23
Roadie.Api.Library/RoadieProcessingException.cs
Normal file
23
Roadie.Api.Library/RoadieProcessingException.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Roadie.Library
|
||||
{
|
||||
public sealed class RoadieProcessingException : Exception
|
||||
{
|
||||
public RoadieProcessingException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public RoadieProcessingException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public RoadieProcessingException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.MetaData.Audio
|
||||
{
|
||||
|
@ -14,10 +14,11 @@ namespace Roadie.Library.MetaData.Audio
|
|||
public sealed class AudioMetaData : IAudioMetaData
|
||||
{
|
||||
public const char ArtistSplitCharacter = '/';
|
||||
|
||||
public const int MinimumYearValue = 1900;
|
||||
public const string SoundTrackArtist = "Sound Tracks";
|
||||
|
||||
private string _artist;
|
||||
|
||||
private bool _doModifyArtistNameOnGet = true;
|
||||
|
||||
private FileInfo _fileInfo;
|
||||
|
@ -43,15 +44,20 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_doModifyArtistNameOnGet)
|
||||
if (!string.IsNullOrEmpty(_artist) && _artist.Contains(ArtistSplitCharacter.ToString()))
|
||||
return _artist.Split(ArtistSplitCharacter).First();
|
||||
if (_doModifyArtistNameOnGet && !string.IsNullOrEmpty(_artist) && _artist.Contains(ArtistSplitCharacter.ToString()))
|
||||
{
|
||||
return _artist.Split(ArtistSplitCharacter)[0];
|
||||
}
|
||||
|
||||
return _artist;
|
||||
}
|
||||
set
|
||||
{
|
||||
_artist = value;
|
||||
if (!string.IsNullOrEmpty(_artist)) _artist = _artist.Replace(';', ArtistSplitCharacter);
|
||||
if (!string.IsNullOrEmpty(_artist))
|
||||
{
|
||||
_artist = _artist.Replace(';', ArtistSplitCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,12 +72,36 @@ namespace Roadie.Library.MetaData.Audio
|
|||
get
|
||||
{
|
||||
var result = AudioMetaDataWeights.None;
|
||||
if (!string.IsNullOrEmpty(Artist)) result |= AudioMetaDataWeights.Artist;
|
||||
if (!string.IsNullOrEmpty(Title)) result |= AudioMetaDataWeights.Time;
|
||||
if ((Year ?? 0) > 1) result |= AudioMetaDataWeights.Year;
|
||||
if ((TrackNumber ?? 0) > 1) result |= AudioMetaDataWeights.TrackNumber;
|
||||
if ((TotalTrackNumbers ?? 0) > 1) result |= AudioMetaDataWeights.TrackTotalNumber;
|
||||
if (TotalSeconds > 1) result |= AudioMetaDataWeights.Time;
|
||||
if (!string.IsNullOrEmpty(Artist))
|
||||
{
|
||||
result |= AudioMetaDataWeights.Artist;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
result |= AudioMetaDataWeights.Time;
|
||||
}
|
||||
|
||||
if ((Year ?? 0) > 1)
|
||||
{
|
||||
result |= AudioMetaDataWeights.Year;
|
||||
}
|
||||
|
||||
if ((TrackNumber ?? 0) > 1)
|
||||
{
|
||||
result |= AudioMetaDataWeights.TrackNumber;
|
||||
}
|
||||
|
||||
if ((TotalTrackNumbers ?? 0) > 1)
|
||||
{
|
||||
result |= AudioMetaDataWeights.TrackTotalNumber;
|
||||
}
|
||||
|
||||
if (TotalSeconds > 1)
|
||||
{
|
||||
result |= AudioMetaDataWeights.Time;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +120,11 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Filename)) return null;
|
||||
if (string.IsNullOrEmpty(Filename))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Path.GetDirectoryName(Filename);
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +139,8 @@ namespace Roadie.Library.MetaData.Audio
|
|||
/// </summary>
|
||||
public string DiscSubTitle { get; set; }
|
||||
|
||||
[JsonIgnore] public FileInfo FileInfo => _fileInfo ?? (_fileInfo = new FileInfo(Filename));
|
||||
[JsonIgnore]
|
||||
public FileInfo FileInfo => _fileInfo ?? (_fileInfo = new FileInfo(Filename));
|
||||
|
||||
/// <summary>
|
||||
/// Full filename to the file used to get this AudioMetaData
|
||||
|
@ -114,7 +149,8 @@ namespace Roadie.Library.MetaData.Audio
|
|||
|
||||
public ICollection<string> Genres { get; set; }
|
||||
|
||||
[JsonIgnore] public IEnumerable<AudioMetaDataImage> Images { get; set; }
|
||||
[JsonIgnore]
|
||||
public IEnumerable<AudioMetaDataImage> Images { get; set; }
|
||||
|
||||
public string ISRC { get; internal set; }
|
||||
|
||||
|
@ -122,10 +158,13 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
get
|
||||
{
|
||||
if (Genres != null && Genres.Any())
|
||||
if (Genres?.Any() == true)
|
||||
{
|
||||
var soundtrackGenres = new List<string> { "24", "soundtrack" };
|
||||
if (Genres.Intersect(soundtrackGenres, StringComparer.OrdinalIgnoreCase).Any()) return true;
|
||||
if (Genres.Intersect(soundtrackGenres, StringComparer.OrdinalIgnoreCase).Any())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -142,7 +181,7 @@ namespace Roadie.Library.MetaData.Audio
|
|||
Release = Release == null ? null : Release.Equals("Unknown Release") ? null : Release;
|
||||
if (!string.IsNullOrEmpty(Title))
|
||||
{
|
||||
var trackNumberTitle = string.Format("Track {0}", TrackNumber);
|
||||
var trackNumberTitle = $"Track {TrackNumber}";
|
||||
Title = Title == trackNumberTitle ? null : Title;
|
||||
}
|
||||
|
||||
|
@ -193,7 +232,10 @@ namespace Roadie.Library.MetaData.Audio
|
|||
set
|
||||
{
|
||||
_title = value;
|
||||
if (IsSoundTrack) Artist = SoundTrackArtist;
|
||||
if (IsSoundTrack)
|
||||
{
|
||||
Artist = SoundTrackArtist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,7 +248,11 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
get
|
||||
{
|
||||
if (Time == null) return 0;
|
||||
if (Time == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Time.Value.TotalSeconds;
|
||||
}
|
||||
}
|
||||
|
@ -225,9 +271,11 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
string result = null;
|
||||
if (!string.IsNullOrEmpty(_trackArtist))
|
||||
result = _trackArtist.Split(ArtistSplitCharacter).First().ToTitleCase();
|
||||
result = !_artist?.Equals(result, StringComparison.OrdinalIgnoreCase) ?? false ? result : null;
|
||||
return result;
|
||||
{
|
||||
result = _trackArtist.Split(ArtistSplitCharacter)[0].ToTitleCase();
|
||||
}
|
||||
|
||||
return !String.Equals(_artist, result, StringComparison.OrdinalIgnoreCase) ? result : null;
|
||||
}
|
||||
set => _trackArtist = value;
|
||||
}
|
||||
|
@ -242,17 +290,33 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_trackArtist)) return new string[0];
|
||||
if (string.IsNullOrEmpty(_trackArtist))
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
if (!_trackArtist.Contains(ArtistSplitCharacter.ToString()))
|
||||
{
|
||||
if (string.IsNullOrEmpty(TrackArtist)) return new string[0];
|
||||
if (string.IsNullOrEmpty(TrackArtist))
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
return new string[1] { TrackArtist };
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_artist) || !string.IsNullOrEmpty(_trackArtist))
|
||||
if (!_artist.Equals(_trackArtist, StringComparison.OrdinalIgnoreCase))
|
||||
return _trackArtist.Split(ArtistSplitCharacter).Where(x => !string.IsNullOrEmpty(x))
|
||||
.Select(x => x.ToTitleCase()).OrderBy(x => x).ToArray();
|
||||
{
|
||||
if (!String.Equals(_artist, _trackArtist, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return _trackArtist.Split(ArtistSplitCharacter)
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.Select(x => x.ToTitleCase())
|
||||
.OrderBy(x => x)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
@ -280,8 +344,11 @@ namespace Roadie.Library.MetaData.Audio
|
|||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var item = obj as AudioMetaData;
|
||||
if (item == null) return false;
|
||||
if (!(obj is AudioMetaData item))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return item.GetHashCode() == GetHashCode();
|
||||
}
|
||||
|
||||
|
@ -290,12 +357,12 @@ namespace Roadie.Library.MetaData.Audio
|
|||
unchecked
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 23 + Artist.GetHashCode();
|
||||
hash = hash * 23 + Release.GetHashCode();
|
||||
hash = hash * 23 + Title.GetHashCode();
|
||||
hash = hash * 23 + TrackNumber.GetHashCode();
|
||||
hash = hash * 23 + AudioBitrate.GetHashCode();
|
||||
hash = hash * 23 + AudioSampleRate.GetHashCode();
|
||||
hash = (hash * 23) + Artist.GetHashCode();
|
||||
hash = (hash * 23) + Release.GetHashCode();
|
||||
hash = (hash * 23) + Title.GetHashCode();
|
||||
hash = (hash * 23) + TrackNumber.GetHashCode();
|
||||
hash = (hash * 23) + AudioBitrate.GetHashCode();
|
||||
hash = (hash * 23) + AudioSampleRate.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
@ -314,9 +381,17 @@ namespace Roadie.Library.MetaData.Audio
|
|||
{
|
||||
var result =
|
||||
$"IsValid: {IsValid}{(IsSoundTrack ? " [SoundTrack ]" : string.Empty)}, ValidWeight {ValidWeight}, Artist: {Artist}";
|
||||
if (!string.IsNullOrEmpty(TrackArtist)) result += $", TrackArtist: {TrackArtist}";
|
||||
if (!string.IsNullOrEmpty(TrackArtist))
|
||||
{
|
||||
result += $", TrackArtist: {TrackArtist}";
|
||||
}
|
||||
|
||||
result += $", Release: {Release}, TrackNumber: {TrackNumber}, TrackTotal: {TotalTrackNumbers}";
|
||||
if (TotalDiscCount > 1) result += $", Disc: {Disc}/{TotalDiscCount}";
|
||||
if (TotalDiscCount > 1)
|
||||
{
|
||||
result += $", Disc: {Disc}/{TotalDiscCount}";
|
||||
}
|
||||
|
||||
result += $", Title: {Title}, Year: {Year}, Duration: {(Time == null ? "-" : Time.Value.ToString())}";
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
public string comment { get; set; }
|
||||
public int edit { get; set; }
|
||||
public bool front { get; set; }
|
||||
public string id { get; set; }
|
||||
public string image { get; set; }
|
||||
public Thumbnails thumbnails { get; set; }
|
||||
public string[] types { get; set; }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.MetaData.MusicBrainz
|
||||
{
|
||||
|
@ -13,51 +13,59 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
public class CoverArtArchive
|
||||
{
|
||||
public bool artwork { get; set; }
|
||||
|
||||
public bool back { get; set; }
|
||||
|
||||
public int? count { get; set; }
|
||||
|
||||
public bool darkened { get; set; }
|
||||
|
||||
public bool front { get; set; }
|
||||
}
|
||||
|
||||
public class Label
|
||||
{
|
||||
public List<Alias> aliases { get; set; }
|
||||
|
||||
public string disambiguation { get; set; }
|
||||
|
||||
public NameAndCount[] genres { get; set; }
|
||||
|
||||
public string id { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "label-code")]
|
||||
[JsonPropertyName("label-code")]
|
||||
public int? labelcode { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "sort-name")]
|
||||
[JsonPropertyName("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
|
||||
public NameAndCount[] tags { get; set; }
|
||||
public NameAndCount[] genres { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LabelInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "catalog-number")]
|
||||
[JsonPropertyName("catalog-number")]
|
||||
public string catalognumber { get; set; }
|
||||
|
||||
public Label label { get; set; }
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class Medium
|
||||
{
|
||||
public object format { get; set; }
|
||||
|
||||
public int? position { get; set; }
|
||||
|
||||
public string title { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "track-count")]
|
||||
[JsonPropertyName("track-count")]
|
||||
public short? trackcount { get; set; }
|
||||
[JsonProperty(PropertyName = "track-offset")]
|
||||
|
||||
[JsonPropertyName("track-offset")]
|
||||
public int? trackoffset { get; set; }
|
||||
|
||||
public List<Track> tracks { get; set; }
|
||||
|
@ -67,37 +75,57 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
public class Recording
|
||||
{
|
||||
public List<Alias> aliases { get; set; }
|
||||
|
||||
public string disambiguation { get; set; }
|
||||
public string id { get; set; }
|
||||
public int? length { get; set; }
|
||||
public string title { get; set; }
|
||||
public bool video { get; set; }
|
||||
public NameAndCount[] tags { get; set; }
|
||||
|
||||
public NameAndCount[] genres { get; set; }
|
||||
|
||||
public string id { get; set; }
|
||||
|
||||
public int? length { get; set; }
|
||||
|
||||
public NameAndCount[] tags { get; set; }
|
||||
|
||||
public string title { get; set; }
|
||||
|
||||
public bool video { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Relation
|
||||
{
|
||||
public List<object> attributes { get; set; }
|
||||
|
||||
public AttributeValues attributevalues { get; set; }
|
||||
|
||||
public object begin { get; set; }
|
||||
|
||||
public string direction { get; set; }
|
||||
|
||||
public object end { get; set; }
|
||||
public bool ended { get; set; }
|
||||
|
||||
public bool? ended { get; set; }
|
||||
|
||||
public string sourcecredit { get; set; }
|
||||
|
||||
public string targetcredit { get; set; }
|
||||
|
||||
public string targettype { get; set; }
|
||||
|
||||
public string type { get; set; }
|
||||
|
||||
public string typeid { get; set; }
|
||||
|
||||
public MbUrl url { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RepositoryRelease
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string ArtistMbId { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public Release Release { get; set; }
|
||||
}
|
||||
|
||||
|
@ -106,46 +134,61 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
public class Release
|
||||
{
|
||||
public List<object> aliases { get; set; }
|
||||
|
||||
[JsonPropertyName("artist-credits")]
|
||||
public Artist[] artistcredits { get; set; }
|
||||
|
||||
public string asin { get; set; }
|
||||
|
||||
public string barcode { get; set; }
|
||||
|
||||
public string country { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "cover-art-archive")]
|
||||
[JsonPropertyName("cover-art-archive")]
|
||||
public CoverArtArchive coverartarchive { get; set; }
|
||||
|
||||
public string coverThumbnailUrl { get; set; }
|
||||
|
||||
public string date { get; set; }
|
||||
|
||||
public string disambiguation { get; set; }
|
||||
|
||||
public string id { get; set; }
|
||||
|
||||
public List<string> imageUrls { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "label-info")]
|
||||
[JsonPropertyName("label-info")]
|
||||
public List<LabelInfo> labelinfo { get; set; }
|
||||
|
||||
public List<Medium> media { get; set; }
|
||||
|
||||
public string packaging { get; set; }
|
||||
|
||||
public string quality { get; set; }
|
||||
|
||||
public List<Relation> relations { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "release-events")]
|
||||
[JsonPropertyName("release-events")]
|
||||
public List<ReleaseEvents> releaseevents { get; set; }
|
||||
[JsonProperty(PropertyName = "release-group")]
|
||||
|
||||
[JsonPropertyName("release-group")]
|
||||
public ReleaseGroup releasegroup { get; set; }
|
||||
|
||||
public string status { get; set; }
|
||||
[JsonProperty(PropertyName = "text-representation")]
|
||||
|
||||
[JsonPropertyName("text-representation")]
|
||||
public TextRepresentation textrepresentation { get; set; }
|
||||
|
||||
public string title { get; set; }
|
||||
[JsonProperty("artist-credits")]
|
||||
public Artist[] artistcredits { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ReleaseBrowseResult
|
||||
{
|
||||
[JsonProperty(PropertyName = "release-count")]
|
||||
[JsonPropertyName("release-count")]
|
||||
public int? releasecount { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "release-offset")]
|
||||
[JsonPropertyName("release-offset")]
|
||||
public int? releaseoffset { get; set; }
|
||||
|
||||
public List<Release> releases { get; set; }
|
||||
|
@ -163,22 +206,31 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
public class ReleaseGroup
|
||||
{
|
||||
public List<object> aliases { get; set; }
|
||||
|
||||
public string disambiguation { get; set; }
|
||||
[JsonProperty("first-release-date")]
|
||||
|
||||
[JsonPropertyName("first-release-date")]
|
||||
public string firstreleasedate { get; set; }
|
||||
public string id { get; set; }
|
||||
[JsonProperty("primary-type")]
|
||||
public string primarytype { get; set; }
|
||||
public List<object> secondarytypes { get; set; }
|
||||
public string title { get; set; }
|
||||
public NameAndCount[] tags { get; set; }
|
||||
|
||||
public NameAndCount[] genres { get; set; }
|
||||
|
||||
public string id { get; set; }
|
||||
|
||||
[JsonPropertyName("primary-type")]
|
||||
public string primarytype { get; set; }
|
||||
|
||||
public List<object> secondarytypes { get; set; }
|
||||
|
||||
public NameAndCount[] tags { get; set; }
|
||||
|
||||
public string title { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TextRepresentation
|
||||
{
|
||||
public string language { get; set; }
|
||||
|
||||
public string script { get; set; }
|
||||
}
|
||||
|
||||
|
@ -186,20 +238,23 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
public class Track
|
||||
{
|
||||
public string id { get; set; }
|
||||
|
||||
public int? length { get; set; }
|
||||
|
||||
public string number { get; set; }
|
||||
public string position { get; set; }
|
||||
|
||||
public int? position { get; set; }
|
||||
|
||||
public Recording recording { get; set; }
|
||||
|
||||
public string title { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MbUrl
|
||||
{
|
||||
public string id { get; set; }
|
||||
|
||||
public string resource { get; set; }
|
||||
}
|
||||
}
|
|
@ -88,7 +88,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
Release = release.title,
|
||||
Title = track.title,
|
||||
Time = track.length.HasValue ? (TimeSpan?)TimeSpan.FromMilliseconds(track.length.Value) : null,
|
||||
TrackNumber = SafeParser.ToNumber<short?>(track.position ?? track.number) ?? 0,
|
||||
TrackNumber = SafeParser.ToNumber<short?>(track.position) ?? SafeParser.ToNumber<short?>(track.number) ?? 0,
|
||||
Disc = media.position,
|
||||
Year = date > 0 ? (int?)date : null,
|
||||
TotalTrackNumbers = media.trackcount
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Utility;
|
||||
using Roadie.Library.Utility;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -48,6 +48,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
{
|
||||
var tryCount = 0;
|
||||
var result = default(T);
|
||||
string downloadedString = null;
|
||||
while (result == null && tryCount < MaxRetries)
|
||||
{
|
||||
try
|
||||
|
@ -55,7 +56,11 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
using (var webClient = new WebClient())
|
||||
{
|
||||
webClient.Headers.Add("user-agent", WebHelper.UserAgent);
|
||||
result = JsonConvert.DeserializeObject<T>(await webClient.DownloadStringTaskAsync(new Uri(url)).ConfigureAwait(false));
|
||||
downloadedString = await webClient.DownloadStringTaskAsync(new Uri(url)).ConfigureAwait(false);
|
||||
if (!string.IsNullOrWhiteSpace(downloadedString))
|
||||
{
|
||||
result = JsonSerializer.Deserialize<T>(downloadedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
|
@ -69,7 +74,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"GetAsync: [{ ex.ToString() }]", "Warning");
|
||||
Trace.WriteLine($"GetAsync: DownloadedString [{ downloadedString }], Exception: [{ ex }]", "Warning");
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -1,140 +1,194 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roadie.Library.MetaData.MusicBrainz
|
||||
{
|
||||
[Serializable]
|
||||
public class ArtistResult
|
||||
{
|
||||
public DateTime created { get; set; }
|
||||
public int count { get; set; }
|
||||
public int offset { get; set; }
|
||||
public Artist[] artists { get; set; }
|
||||
|
||||
public int count { get; set; }
|
||||
|
||||
public DateTime created { get; set; }
|
||||
|
||||
public int offset { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RepositoryArtist
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string ArtistName { get; set; }
|
||||
public string ArtistMbId { get; set; }
|
||||
public Artist Artist { get; set; }
|
||||
|
||||
public string ArtistMbId { get; set; }
|
||||
|
||||
public string ArtistName { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
[DebuggerDisplay("name: {name}")]
|
||||
[Serializable]
|
||||
public class Artist
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
[JsonProperty("type-id")]
|
||||
public string typeid { get; set; }
|
||||
public int score { get; set; }
|
||||
public string name { get; set; }
|
||||
[JsonProperty("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
public string gender { get; set; }
|
||||
public string country { get; set; }
|
||||
public Area area { get; set; }
|
||||
[JsonProperty("begin_area")]
|
||||
public BeginAndEndArea beginarea { get; set; }
|
||||
[JsonProperty("end_area")]
|
||||
public BeginAndEndArea endarea { get; set; }
|
||||
public string[] ipis { get; set; }
|
||||
[JsonProperty("life-span")]
|
||||
public LifeSpan lifespan { get; set; }
|
||||
public Alias[] aliases { get; set; }
|
||||
public NameAndCount[] tags { get; set; }
|
||||
public IsniList[] isnilist { get; set; }
|
||||
|
||||
public Area area { get; set; }
|
||||
|
||||
[JsonPropertyName("begin_area")]
|
||||
public BeginAndEndArea beginarea { get; set; }
|
||||
|
||||
public string country { get; set; }
|
||||
|
||||
[JsonPropertyName("end_area")]
|
||||
public BeginAndEndArea endarea { get; set; }
|
||||
|
||||
public string gender { get; set; }
|
||||
|
||||
public NameAndCount[] genres { get; set; }
|
||||
|
||||
public string id { get; set; }
|
||||
|
||||
public string[] ipis { get; set; }
|
||||
|
||||
public IsniList[] isnilist { get; set; }
|
||||
|
||||
[JsonPropertyName("life-span")]
|
||||
public LifeSpan lifespan { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public Relation[] relations { get; set; }
|
||||
|
||||
public int score { get; set; }
|
||||
|
||||
[JsonPropertyName("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
|
||||
public NameAndCount[] tags { get; set; }
|
||||
|
||||
public string type { get; set; }
|
||||
|
||||
[JsonPropertyName("type-id")]
|
||||
public string typeid { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Relations
|
||||
{
|
||||
[JsonProperty("attribute-ids")]
|
||||
[JsonPropertyName("attribute-ids")]
|
||||
public string[] attributeids { get; set; }
|
||||
|
||||
public string[] attributes { get; set; }
|
||||
public string direction { get; set; }
|
||||
[JsonProperty("target-credit")]
|
||||
public string targetcredit { get; set; }
|
||||
[JsonProperty("type-id")]
|
||||
public string typeid { get; set; }
|
||||
[JsonProperty("target-type")]
|
||||
public string targettype { get; set; }
|
||||
public string type { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public Url url { get; set; }
|
||||
|
||||
public string[] attributevalues { get; set; }
|
||||
[JsonProperty("source-credit")]
|
||||
public string sourcecredit { get; set; }
|
||||
public object end { get; set; }
|
||||
|
||||
public object begin { get; set; }
|
||||
|
||||
public string direction { get; set; }
|
||||
|
||||
public object end { get; set; }
|
||||
|
||||
public bool? ended { get; set; }
|
||||
|
||||
[JsonPropertyName("source-credit")]
|
||||
public string sourcecredit { get; set; }
|
||||
|
||||
[JsonPropertyName("target-credit")]
|
||||
public string targetcredit { get; set; }
|
||||
|
||||
[JsonPropertyName("target-type")]
|
||||
public string targettype { get; set; }
|
||||
|
||||
public string type { get; set; }
|
||||
|
||||
[JsonPropertyName("type-id")]
|
||||
public string typeid { get; set; }
|
||||
|
||||
public Url url { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Url
|
||||
{
|
||||
public string resource { get; set; }
|
||||
public string id { get; set; }
|
||||
}
|
||||
|
||||
public string resource { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Area
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
[JsonProperty("type-id")]
|
||||
public string typeid { get; set; }
|
||||
public string name { get; set; }
|
||||
[JsonProperty("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
[JsonProperty("life-span")]
|
||||
public LifeSpan lifespan { get; set; }
|
||||
[JsonProperty("iso-3166-1-codes")]
|
||||
|
||||
[JsonPropertyName("iso-3166-1-codes")]
|
||||
public string[] iso31661codes { get; set; }
|
||||
|
||||
[JsonPropertyName("life-span")]
|
||||
public LifeSpan lifespan { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
[JsonPropertyName("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
|
||||
public string type { get; set; }
|
||||
|
||||
[JsonPropertyName("type-id")]
|
||||
public string typeid { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LifeSpan
|
||||
{
|
||||
public string end { get; set; }
|
||||
public string ended { get; set; }
|
||||
public string begin { get; set; }
|
||||
|
||||
public string end { get; set; }
|
||||
|
||||
public bool? ended { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class BeginAndEndArea
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
public string typeid { get; set; }
|
||||
public string name { get; set; }
|
||||
[JsonProperty("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
[JsonProperty("life-span")]
|
||||
|
||||
[JsonPropertyName("life-span")]
|
||||
public LifeSpan lifespan { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
[JsonPropertyName("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
|
||||
public string type { get; set; }
|
||||
|
||||
public string typeid { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Alias
|
||||
{
|
||||
[JsonProperty("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
[JsonProperty("type-id")]
|
||||
public string typeid { get; set; }
|
||||
public string name { get; set; }
|
||||
public string locale { get; set; }
|
||||
public string type { get; set; }
|
||||
public string primary { get; set; }
|
||||
public string begin { get; set; }
|
||||
|
||||
public string end { get; set; }
|
||||
public bool ended { get; set; }
|
||||
|
||||
public bool? ended { get; set; }
|
||||
|
||||
public string locale { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public bool? primary { get; set; }
|
||||
|
||||
[JsonPropertyName("sort-name")]
|
||||
public string sortname { get; set; }
|
||||
|
||||
public string type { get; set; }
|
||||
|
||||
[JsonPropertyName("type-id")]
|
||||
public string typeid { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
@ -147,7 +201,7 @@ namespace Roadie.Library.MetaData.MusicBrainz
|
|||
public class NameAndCount
|
||||
{
|
||||
public int? count { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Hashids.net" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.6.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.1.2" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -102,78 +102,56 @@ namespace Roadie.Api.Services
|
|||
return await GetArtist(artistByName.RoadieId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected async Task<data.Artist> GetArtist(Guid id)
|
||||
protected Task<data.Artist> GetArtist(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(data.Artist.CacheUrn(id), async () =>
|
||||
return CacheManager.GetAsync(data.Artist.CacheUrn(id), async () =>
|
||||
{
|
||||
return await DbContext.Artists
|
||||
.Include(x => x.Genres)
|
||||
.Include("Genres.Genre")
|
||||
.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
|
||||
.FirstOrDefaultAsync(x => x.RoadieId == id)
|
||||
.ConfigureAwait(false);
|
||||
}, data.Artist.CacheRegionUrn(id));
|
||||
}
|
||||
|
||||
protected async Task<data.Collection> GetCollection(Guid id)
|
||||
protected Task<data.Collection> GetCollection(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(data.Collection.CacheUrn(id), async () =>
|
||||
return CacheManager.GetAsync(data.Collection.CacheUrn(id), async () =>
|
||||
{
|
||||
return await DbContext.Collections.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, data.Collection.CacheRegionUrn(id)).ConfigureAwait(false);
|
||||
}, data.Collection.CacheRegionUrn(id));
|
||||
}
|
||||
|
||||
protected async Task<data.Genre> GetGenre(Guid id)
|
||||
protected Task<data.Genre> GetGenre(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(data.Genre.CacheUrn(id), async () =>
|
||||
return CacheManager.GetAsync(data.Genre.CacheUrn(id), async () =>
|
||||
{
|
||||
return await DbContext.Genres.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, data.Genre.CacheRegionUrn(id)).ConfigureAwait(false);
|
||||
}, data.Genre.CacheRegionUrn(id));
|
||||
}
|
||||
|
||||
protected async Task<data.Label> GetLabel(Guid id)
|
||||
protected Task<data.Label> GetLabel(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(data.Label.CacheUrn(id), async () =>
|
||||
return CacheManager.GetAsync(data.Label.CacheUrn(id), async () =>
|
||||
{
|
||||
return await DbContext.Labels.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, data.Label.CacheRegionUrn(id)).ConfigureAwait(false);
|
||||
}, data.Label.CacheRegionUrn(id));
|
||||
}
|
||||
|
||||
protected async Task<data.Playlist> GetPlaylist(Guid id)
|
||||
protected Task<data.Playlist> GetPlaylist(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(data.Playlist.CacheUrn(id), async () =>
|
||||
return CacheManager.GetAsync(data.Playlist.CacheUrn(id), async () =>
|
||||
{
|
||||
return await DbContext.Playlists
|
||||
.Include(x => x.User)
|
||||
.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, data.Playlist.CacheRegionUrn(id)).ConfigureAwait(false);
|
||||
.FirstOrDefaultAsync(x => x.RoadieId == id)
|
||||
.ConfigureAwait(false);
|
||||
}, data.Playlist.CacheRegionUrn(id));
|
||||
}
|
||||
|
||||
protected async Task<data.Release> GetRelease(Guid id)
|
||||
protected Task<data.Release> GetRelease(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(data.Release.CacheUrn(id), async () =>
|
||||
return CacheManager.GetAsync(data.Release.CacheUrn(id), async () =>
|
||||
{
|
||||
return await DbContext.Releases
|
||||
.Include(x => x.Artist)
|
||||
|
@ -183,7 +161,7 @@ namespace Roadie.Api.Services
|
|||
.Include("Medias.Tracks")
|
||||
.Include("Medias.Tracks.TrackArtist")
|
||||
.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, data.Release.CacheRegionUrn(id)).ConfigureAwait(false);
|
||||
}, data.Release.CacheRegionUrn(id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -199,13 +177,9 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
|
||||
// Only read operations
|
||||
protected async Task<data.Track> GetTrack(Guid id)
|
||||
protected Task<data.Track> GetTrack(Guid id)
|
||||
{
|
||||
if(id == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(data.Track.CacheUrn(id), async () =>
|
||||
return CacheManager.GetAsync(data.Track.CacheUrn(id), async () =>
|
||||
{
|
||||
return await DbContext.Tracks
|
||||
.Include(x => x.ReleaseMedia)
|
||||
|
@ -213,7 +187,7 @@ namespace Roadie.Api.Services
|
|||
.Include(x => x.ReleaseMedia.Release.Artist)
|
||||
.Include(x => x.TrackArtist)
|
||||
.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, data.Track.CacheRegionUrn(id)).ConfigureAwait(false);
|
||||
}, data.Track.CacheRegionUrn(id));
|
||||
}
|
||||
|
||||
protected async Task<User> GetUser(string username)
|
||||
|
@ -229,13 +203,9 @@ namespace Roadie.Api.Services
|
|||
return await GetUser(userByUsername?.RoadieId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected async Task<User> GetUser(Guid? id)
|
||||
protected Task<User> GetUser(Guid? id)
|
||||
{
|
||||
if (!id.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await CacheManager.GetAsync(User.CacheUrn(id.Value), async () =>
|
||||
return CacheManager.GetAsync(User.CacheUrn(id.Value), async () =>
|
||||
{
|
||||
return await DbContext.Users
|
||||
.Include(x => x.UserRoles)
|
||||
|
@ -245,7 +215,7 @@ namespace Roadie.Api.Services
|
|||
.Include(x => x.UserQues)
|
||||
.Include("UserQues.Track")
|
||||
.FirstOrDefaultAsync(x => x.RoadieId == id).ConfigureAwait(false);
|
||||
}, User.CacheRegionUrn(id.Value)).ConfigureAwait(false);
|
||||
}, User.CacheRegionUrn(id.Value));
|
||||
}
|
||||
|
||||
protected string MakeLastFmUrl(string artistName, string releaseTitle) => "http://www.last.fm/music/" + HttpEncoder.UrlEncode($"{artistName}/{releaseTitle}");
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,6 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
|
@ -24,6 +23,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using data = Roadie.Library.Data;
|
||||
|
||||
|
@ -791,7 +791,7 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error In List, Request [{0}], User [{1}]", JsonConvert.SerializeObject(request), roadieUser);
|
||||
Logger.LogError(ex, "Error In List, Request [{0}], User [{1}]", JsonSerializer.Serialize(request), roadieUser);
|
||||
return new Library.Models.Pagination.PagedResult<TrackList>
|
||||
{
|
||||
Message = "An Error has occured"
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
|
@ -23,6 +22,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using data = Roadie.Library.Data;
|
||||
using models = Roadie.Library.Models;
|
||||
|
@ -593,7 +593,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
result.AdditionalData.Add("Timing", sw.ElapsedMilliseconds);
|
||||
Logger.LogTrace(
|
||||
$"User `{roadieUser}` set rating [{rating}] on TrackId [{trackId}]. Result [{JsonConvert.SerializeObject(result)}]");
|
||||
$"User `{roadieUser}` set rating [{rating}] on TrackId [{trackId}]. Result [{JsonSerializer.Serialize(result)}]");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Api.Services;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
|
@ -13,6 +12,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using models = Roadie.Library.Models.Users;
|
||||
|
||||
|
@ -146,7 +146,7 @@ namespace Roadie.Api.Controllers
|
|||
};
|
||||
await playActivityService.NowPlayingAsync(user, scrobble).ConfigureAwait(false);
|
||||
sw.Stop();
|
||||
Logger.LogTrace($"StreamTrack ElapsedTime [{sw.ElapsedMilliseconds}], Timings [{JsonConvert.SerializeObject(timings)}], StreamInfo `{info?.Data}`");
|
||||
Logger.LogTrace($"StreamTrack ElapsedTime [{sw.ElapsedMilliseconds}], Timings [{JsonSerializer.Serialize(timings)}], StreamInfo `{info?.Data}`");
|
||||
return new EmptyResult();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Api.ModelBinding;
|
||||
using Roadie.Api.Services;
|
||||
using Roadie.Library.Caching;
|
||||
|
@ -15,6 +14,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using User = Roadie.Library.Models.Users.User;
|
||||
|
||||
|
@ -364,7 +364,7 @@ namespace Roadie.Api.Controllers
|
|||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
Logger.LogWarning($"GetCoverArt Failed For [{JsonConvert.SerializeObject(request)}]");
|
||||
Logger.LogWarning($"GetCoverArt Failed For [{ JsonSerializer.Serialize(request)}]");
|
||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
}
|
||||
|
||||
|
@ -901,7 +901,7 @@ namespace Roadie.Api.Controllers
|
|||
}
|
||||
else
|
||||
{
|
||||
jsonResult = $"{{ \"subsonic-response\": {{ \"status\":\"{status}\", \"version\": \"{version}\", \"{responseType}\":{((response?.Data != null ? JsonConvert.SerializeObject(response.Data.Item) : string.Empty))}}}}}";
|
||||
jsonResult = $"{{ \"subsonic-response\": {{ \"status\":\"{status}\", \"version\": \"{version}\", \"{responseType}\":{((response?.Data != null ? JsonSerializer.Serialize(response.Data.Item) : string.Empty))}}}}}";
|
||||
}
|
||||
|
||||
if ((request?.f ?? string.Empty).Equals("jsonp", StringComparison.OrdinalIgnoreCase))
|
||||
|
|
|
@ -21,9 +21,7 @@ namespace Roadie.Api.Controllers
|
|||
[Authorize]
|
||||
public class UserController : EntityControllerBase
|
||||
{
|
||||
|
||||
private IHttpContext RoadieHttpContext { get; }
|
||||
|
||||
private IUserService UserService { get; }
|
||||
|
||||
private readonly ITokenService TokenService;
|
||||
|
@ -65,8 +63,14 @@ namespace Roadie.Api.Controllers
|
|||
public async Task<IActionResult> Get(Guid id, string inc = null)
|
||||
{
|
||||
var user = await CurrentUserModel().ConfigureAwait(false);
|
||||
var result = await CacheManager.GetAsync($"urn:user_model_by_id:{id}",
|
||||
async () => await UserService.ByIdAsync(user, id, (inc ?? Library.Models.Users.User.DefaultIncludes).ToLower().Split(",")).ConfigureAwait(false), ControllerCacheRegionUrn).ConfigureAwait(false);
|
||||
var result = await CacheManager.GetAsync($"urn:user_model_by_id:{id}", async () =>
|
||||
{
|
||||
return await UserService.ByIdAsync(user, id, (inc ?? Library.Models.Users.User.DefaultIncludes)
|
||||
.ToLower()
|
||||
.Split(","))
|
||||
.ConfigureAwait(false);
|
||||
},
|
||||
ControllerCacheRegionUrn).ConfigureAwait(false);
|
||||
if (result?.IsNotFoundResult != false)
|
||||
{
|
||||
return NotFound();
|
||||
|
@ -386,11 +390,5 @@ namespace Roadie.Api.Controllers
|
|||
DefaultRowsPerPage = modelUser.DefaultRowsPerPage ?? RoadieSettings.DefaultRowsPerPage
|
||||
});
|
||||
}
|
||||
|
||||
public class PagingParams
|
||||
{
|
||||
public int Limit { get; set; } = 5;
|
||||
public int Page { get; set; } = 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Pastel;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
@ -31,19 +32,19 @@ namespace Roadie.Api
|
|||
|
||||
#if DEBUG
|
||||
// Logging Output tests
|
||||
Log.Verbose(":: Log Test: Verbose (Trace,None)"); // Microsoft.Extensions.Logging.LogLevel.Trace and Microsoft.Extensions.Logging.LogLevel.None
|
||||
Log.Debug(":: Log Test: Debug"); // Microsoft.Extensions.Logging.LogLevel.Debug
|
||||
Log.Information(":: Log Test: Information"); // Microsoft.Extensions.Logging.LogLevel.Information
|
||||
Log.Warning(":: Log Test: Warning"); // Microsoft.Extensions.Logging.LogLevel.Warning
|
||||
Log.Error(new Exception("Log Test Exception"), "Log Test Error Message"); // Microsoft.Extensions.Logging.LogLevel.Error
|
||||
Log.Fatal(":: Log Test: Fatal (Critial)"); // Microsoft.Extensions.Logging.LogLevel.Critical
|
||||
Trace.WriteLine(":: Log Test: Trace WriteLine()");
|
||||
Log.Verbose(":: Log Test: Verbose (Trace,None), Test 'value', Test: Value"); // Microsoft.Extensions.Logging.LogLevel.Trace and Microsoft.Extensions.Logging.LogLevel.None
|
||||
Log.Debug(":: Log Test: Debug, Test 'value', Test: Value"); // Microsoft.Extensions.Logging.LogLevel.Debug
|
||||
Log.Information(":: Log Test: Information, Test 'value', Test: Value"); // Microsoft.Extensions.Logging.LogLevel.Information
|
||||
Log.Warning(":: Log Test: Warning, Test 'value', Test: Value"); // Microsoft.Extensions.Logging.LogLevel.Warning
|
||||
Log.Error(new Exception("Log Test Exception"), "Log Test Error Message, Test 'value', Test: Value"); // Microsoft.Extensions.Logging.LogLevel.Error
|
||||
Log.Fatal(":: Log Test: Fatal (Critial), Test 'value', Test: Value"); // Microsoft.Extensions.Logging.LogLevel.Critical
|
||||
Trace.WriteLine(":: Log Test: Trace WriteLine(), Test 'value', Test: Value");
|
||||
#endif
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine(@" ____ __ __ ____ __ ____ __ ____ __ ");
|
||||
Console.WriteLine(@"( _ \ / \ / _\ ( \( )( __) / _\ ( _ \( ) ");
|
||||
Console.WriteLine(@" ) /( O )/ \ ) D ( )( ) _) / \ ) __/ )( ");
|
||||
Console.WriteLine(@"(__\_) \__/ \_/\_/(____/(__)(____) \_/\_/(__) (__) ");
|
||||
Console.WriteLine(@" ____ __ __ ____ __ ____ __ ____ __ ".Pastel("#FEFF0E"));
|
||||
Console.WriteLine(@"( _ \ / \ / _\ ( \( )( __) / _\ ( _ \( ) ".Pastel("#F49014"));
|
||||
Console.WriteLine(@" ) /( O )/ \ ) D ( )( ) _) / \ ) __/ )( ".Pastel("#E30014"));
|
||||
Console.WriteLine(@"(__\_) \__/ \_/\_/(____/(__)(____) \_/\_/(__) (__) ".Pastel("#DB0083"));
|
||||
Console.WriteLine("");
|
||||
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.1.2</VersionPrefix>
|
||||
<VersionPrefix>1.1.3</VersionPrefix>
|
||||
<VersionSuffix></VersionSuffix>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -31,15 +31,16 @@
|
|||
<PackageReference Include="BCrypt-Core" Version="2.0.0" />
|
||||
<PackageReference Include="Mapster" Version="5.3.2" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.14.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.3" />
|
||||
<PackageReference Include="Pastel" Version="1.3.2" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||
|
@ -49,7 +50,7 @@
|
|||
<PackageReference Include="Serilog.Sinks.LiteDB.NetStandard" Version="1.0.14" />
|
||||
<PackageReference Include="Serilog.Sinks.RollingFileAlternate" Version="2.0.9" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.6.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.1.2" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
33
Roadie.Api/RoadieSerilogThemes.cs
Normal file
33
Roadie.Api/RoadieSerilogThemes.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Serilog.Sinks.SystemConsole.Themes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Api
|
||||
{
|
||||
public static class RoadieSerilogThemes
|
||||
{
|
||||
public static AnsiConsoleTheme RoadieRainbow { get; } = new AnsiConsoleTheme(
|
||||
new Dictionary<ConsoleThemeStyle, string>
|
||||
{
|
||||
[ConsoleThemeStyle.Text] = "\x1b[38;5;0034m",
|
||||
[ConsoleThemeStyle.SecondaryText] = "\x1b[38;5;0025m",
|
||||
[ConsoleThemeStyle.TertiaryText] = "\x1b[38;5;0089m",
|
||||
[ConsoleThemeStyle.Invalid] = "\x1b[38;5;0126m",
|
||||
[ConsoleThemeStyle.Null] = "\x1b[38;5;0100m",
|
||||
[ConsoleThemeStyle.Name] = "\x1b[38;5;0007m",
|
||||
[ConsoleThemeStyle.String] = "\x1b[38;5;0117m",
|
||||
[ConsoleThemeStyle.Number] = "\x1b[38;5;0200m",
|
||||
[ConsoleThemeStyle.Boolean] = "\x1b[38;5;0027m",
|
||||
[ConsoleThemeStyle.Scalar] = "\x1b[38;5;0085m",
|
||||
[ConsoleThemeStyle.LevelVerbose] = "\x1b[38;5;0007m",
|
||||
[ConsoleThemeStyle.LevelDebug] = "\x1b[38;5;0015m",
|
||||
[ConsoleThemeStyle.LevelInformation] = "\x1b[38;5;0015m",
|
||||
[ConsoleThemeStyle.LevelWarning] = "\x1b[38;5;0011m",
|
||||
[ConsoleThemeStyle.LevelError] = "\x1b[38;5;0015m\x1b[48;5;0196m",
|
||||
[ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0011m\x1b[48;5;0009m",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Api.Hubs;
|
||||
using Roadie.Api.ModelBinding;
|
||||
using Roadie.Api.Services;
|
||||
|
@ -113,10 +112,17 @@ namespace Roadie.Api
|
|||
services.AddSingleton<IHttpEncoder, HttpEncoder>();
|
||||
services.AddSingleton<IEmailSender, EmailSenderService>();
|
||||
|
||||
services.AddSingleton<ICacheSerializer>(options =>
|
||||
{
|
||||
var logger = options.GetService<ILogger<Utf8JsonCacheSerializer>>();
|
||||
return new Utf8JsonCacheSerializer(logger);
|
||||
});
|
||||
|
||||
services.AddSingleton<ICacheManager>(options =>
|
||||
{
|
||||
var logger = options.GetService<ILogger<MemoryCacheManager>>();
|
||||
return new MemoryCacheManager(logger, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
var serializer = options.GetService<ICacheSerializer>();
|
||||
return new MemoryCacheManager(logger, serializer, new CachePolicy(TimeSpan.FromHours(4)));
|
||||
});
|
||||
|
||||
var dbFolder = new DirectoryInfo(settings.FileDatabaseOptions.DatabaseFolder);
|
||||
|
@ -326,11 +332,7 @@ namespace Roadie.Api
|
|||
options.RespectBrowserAcceptHeader = true; // false by default
|
||||
options.ModelBinderProviders.Insert(0, new SubsonicRequestBinderProvider());
|
||||
})
|
||||
.AddNewtonsoftJson(options =>
|
||||
{
|
||||
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
})
|
||||
.AddJsonOptions(options => options.JsonSerializerOptions.IgnoreNullValues = true)
|
||||
.AddXmlSerializerFormatters();
|
||||
|
||||
services.Configure<IdentityOptions>(options =>
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
{
|
||||
"Name": "Console",
|
||||
"Args": {
|
||||
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
|
||||
"restrictedToMinimumLevel": "Information"
|
||||
"theme": "Roadie.Api.RoadieSerilogThemes::RoadieRainbow, Roadie.Api",
|
||||
"restrictedToMinimumLevel": "Verbose"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Dlna.Server.Metadata;
|
||||
using Roadie.Dlna.Utility;
|
||||
using System;
|
||||
|
@ -13,23 +12,16 @@ namespace Roadie.Dlna.Server
|
|||
internal partial class MediaMount
|
||||
{
|
||||
private const string NS_DC = "http://purl.org/dc/elements/1.1/";
|
||||
|
||||
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
|
||||
|
||||
private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
|
||||
|
||||
private const string NS_SEC = "http://www.sec.co.kr/";
|
||||
|
||||
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
|
||||
|
||||
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
|
||||
|
||||
private static readonly IDictionary<string, AttributeCollection> soapCache = new LeastRecentlyUsedDictionary<string, AttributeCollection>(200);
|
||||
|
||||
private static readonly XmlNamespaceManager namespaceMgr = CreateNamespaceManager();
|
||||
|
||||
private static readonly string featureList = Encoding.UTF8.GetString(ResourceHelper.GetResourceData("x_featurelist.xml") ?? new byte[0]);
|
||||
|
||||
private static readonly IDictionary<string, AttributeCollection> soapCache = new LeastRecentlyUsedDictionary<string, AttributeCollection>(200);
|
||||
|
||||
private static void AddBookmarkInfo(IMediaResource resource, XmlElement item)
|
||||
{
|
||||
var bookmarkable = resource as IBookmarkable;
|
||||
|
@ -79,10 +71,7 @@ namespace Roadie.Dlna.Server
|
|||
var res = result.CreateElement(string.Empty, "res", NS_DIDL);
|
||||
res.InnerText = curl;
|
||||
|
||||
res.SetAttribute("protocolInfo", string.Format(
|
||||
"http-get:*:{1}:DLNA.ORG_PN={0};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
|
||||
c.PN, DlnaMaps.Mime[c.Type], DlnaMaps.DefaultStreaming
|
||||
));
|
||||
res.SetAttribute("protocolInfo", $"http-get:*:{DlnaMaps.Mime[c.Type]}:DLNA.ORG_PN={c.PN};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={DlnaMaps.DefaultStreaming}");
|
||||
var width = c.MetaWidth;
|
||||
var height = c.MetaHeight;
|
||||
if (width.HasValue && height.HasValue)
|
||||
|
@ -270,10 +259,7 @@ namespace Roadie.Dlna.Server
|
|||
res.SetAttribute("duration", prop);
|
||||
}
|
||||
|
||||
res.SetAttribute("protocolInfo", string.Format(
|
||||
"http-get:*:{1}:DLNA.ORG_PN={0};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
|
||||
resource.PN, DlnaMaps.Mime[resource.Type], DlnaMaps.DefaultStreaming
|
||||
));
|
||||
res.SetAttribute("protocolInfo", $"http-get:*:{DlnaMaps.Mime[resource.Type]}:DLNA.ORG_PN={resource.PN};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={DlnaMaps.DefaultStreaming}");
|
||||
item.AppendChild(res);
|
||||
|
||||
AddCover(request, resource, item);
|
||||
|
@ -509,7 +495,7 @@ namespace Roadie.Dlna.Server
|
|||
{
|
||||
Logger.LogError(ex, $"ProcessSoapRequest. Error Loading Request From [{ request.RemoteEndpoint.Address }], Body [{ request.Body }]");
|
||||
}
|
||||
var sparams = new RawHeaders();
|
||||
var sparams = new RawHeaders();
|
||||
var body = soap.SelectSingleNode("//soap:Body", namespaceMgr);
|
||||
if (body == null)
|
||||
{
|
||||
|
@ -625,5 +611,7 @@ namespace Roadie.Dlna.Server
|
|||
rv.Headers.Add("EXT", string.Empty);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static readonly XmlNamespaceManager namespaceMgr = CreateNamespaceManager();
|
||||
}
|
||||
}
|
BIN
libraries/libwebp_x64.dll
Normal file
BIN
libraries/libwebp_x64.dll
Normal file
Binary file not shown.
Loading…
Reference in a new issue