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

Inspector work.

This commit is contained in:
Steven Hildreth 2019-06-01 23:27:17 -05:00
parent af71e4c9f2
commit f36d54a193
54 changed files with 1146 additions and 380 deletions

View file

@ -1,5 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.IO;
using McMaster.Extensions.CommandLineUtils;
namespace Inspector
@ -19,10 +20,20 @@ namespace Inspector
[Option("-c", "Copy Dont Move Originals", CommandOptionType.NoValue)]
public bool DoCopy { get; }
[Option("-r", "Only show what would be done, don't modify any files", CommandOptionType.NoValue)]
public bool IsReadOnly { get; }
[Option("-s", "Don't append a subfolder to the Destination folder", CommandOptionType.NoValue)]
public bool DontAppendSubFolder { get; }
[Option("-x", "Don't delete empty folders after inspection, if moving", CommandOptionType.NoValue)]
public bool DontDeleteEmptyFolders { get; }
private void OnExecute()
{
var inspector = new Roadie.Library.Inspect.Inspector();
inspector.Inspect(this.DoCopy, this.Folder, this.Destination ?? this.Folder);
inspector.Inspect(DoCopy, IsReadOnly, Folder, Destination ?? Folder, DontAppendSubFolder, IsReadOnly ? true : DontDeleteEmptyFolders);
}
}

View file

@ -2,7 +2,7 @@
"profiles": {
"Inspector": {
"commandName": "Project",
"commandLineArgs": "-f \"C:\\roadie_dev_root\\inbound\""
"commandLineArgs": "-f \"C:\\roadie_dev_root\\inbound\" -r"
}
}
}

View file

@ -93,6 +93,11 @@ namespace Roadie.Library.Tests
[InlineData("1983/02/24")]
[InlineData("1983//1983")]
[InlineData("1983\\1983")]
[InlineData("1983,1983")]
[InlineData("1983;1983")]
[InlineData("1983 1983")]
[InlineData("1983;;1983")]
[InlineData("1983; 1983")]
[InlineData("83")]
public void ParseYearShouldBeNinteenEightyThree(string year)
{
@ -179,6 +184,21 @@ namespace Roadie.Library.Tests
Assert.Null(dn);
}
[Theory]
[InlineData("01. Up.mp3")]
[InlineData("01 - Relentless Ascension.mp3")]
[InlineData("01.Say Goodbye.mp3")]
[InlineData("01 Up.mp3")]
[InlineData("1 - Tithe II.mp3")]
[InlineData("1 Up.mp3")]
[InlineData("1. Up.mp3")]
[InlineData("1- Up.mp3")]
public void DetermineTrackNumber(string filename)
{
var tn = ID3TagsHelper.DetermineTrackNumber(filename);
Assert.True(tn > 0);
}
[Fact]
public void ReadTotalTrackNumbersFromCue_Should_Be_Five()
{
@ -386,10 +406,10 @@ namespace Roadie.Library.Tests
}
[Theory]
[InlineData(@"N:\Rita Ora - Phoenix (Deluxe) (2018) Mp3 (320kbps) [Hunter]\Rita Ora - Phoenix (Deluxe) (2018)")]
[InlineData(@"N:\Travis Scott - ASTROWORLD (2018) Mp3 (320kbps) [Hunter]")]
[InlineData(@"N:\Lil Wayne - Tha Carter V (2018) Mp3 (320kbps) [Hunter]")]
[InlineData(@"N:\Beyonce & JAY-Z - EVERYTHING IS LOVE (2018) Mp3 (320kbps) [Hunter]")]
[InlineData(@"C:\roadie_dev_root\Rita Ora - Phoenix (Deluxe) (2018) Mp3 (320kbps) [Hunter]\Rita Ora - Phoenix (Deluxe) (2018)")]
[InlineData(@"C:\roadie_dev_root\Travis Scott - ASTROWORLD (2018) Mp3 (320kbps) [Hunter]")]
[InlineData(@"C:\roadie_dev_root\Lil Wayne - Tha Carter V (2018) Mp3 (320kbps) [Hunter]")]
[InlineData(@"C:\roadie_dev_root\Beyonce & JAY-Z - EVERYTHING IS LOVE (2018) Mp3 (320kbps) [Hunter]")]
public void ReadFolderTestAllFiles(string folderName)
{
if (!Directory.Exists(folderName))
@ -470,7 +490,7 @@ namespace Roadie.Library.Tests
[Fact]
public void ReadID3TagsFromFileWithAlbumNoTrackSet()
{
var file = new FileInfo(@"Z:\inbound\MEGAPACK ---METAL-DEATH-BLACK---\ebony_tears-evil_as_hell-2001-ss\01-deviation-ss.mp3");
var file = new FileInfo(@"M:\inbound\MEGAPACK ---METAL-DEATH-BLACK---\ebony_tears-evil_as_hell-2001-ss\01-deviation-ss.mp3");
if (file.Exists)
{
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName);
@ -496,7 +516,7 @@ namespace Roadie.Library.Tests
[Fact]
public void Read_File_Test_Is_valid()
{
var file = new FileInfo(@"Z:\unknown\2eec19bd-3575-4b7f-84dd-db2a0ec3e2f3~[2009] Dolly - Disc 1 Of 4~06 Nobody But You (Previously Unissued).mp3");
var file = new FileInfo(@"M:\unknown\2eec19bd-3575-4b7f-84dd-db2a0ec3e2f3~[2009] Dolly - Disc 1 Of 4~06 Nobody But You (Previously Unissued).mp3");
if (file.Exists)
{
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName);
@ -523,7 +543,7 @@ namespace Roadie.Library.Tests
[Fact]
public void Read_File_Test_Is_valid2()
{
var file = new FileInfo(@"Z:\library_old\Perverse\[2014] Champion Dub\01 Champion Dub (Original Mix).mp3");
var file = new FileInfo(@"M:\library_old\Perverse\[2014] Champion Dub\01 Champion Dub (Original Mix).mp3");
if (file.Exists)
{
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName);
@ -578,7 +598,7 @@ namespace Roadie.Library.Tests
[Fact]
public void ReadID3TagsFromFileWithTrackAndArtistTheSame()
{
var file = new FileInfo(@"Z:\library\Blind Melon\[1992] Blind Melon\01. Blind Melon - Soak The Sin.mp3");
var file = new FileInfo(@"M:\library\Blind Melon\[1992] Blind Melon\01. Blind Melon - Soak The Sin.mp3");
if (file.Exists)
{
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName);
@ -670,7 +690,7 @@ namespace Roadie.Library.Tests
[Fact]
public void ReadID3TagsFromFile2()
{
var file = new FileInfo(@"Z:\library\Denver, John\[1972] Aerie\10 Readjustment Blues.mp3");
var file = new FileInfo(@"M:\library\Denver, John\[1972] Aerie\10 Readjustment Blues.mp3");
if (file.Exists)
{
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName);
@ -724,7 +744,7 @@ namespace Roadie.Library.Tests
[Fact]
public void ReadID3TagsFromFile4()
{
var file = new FileInfo(@"Z:\library\Ac Dc\[1975] T.N.T\01 It'S A Long Way To The Top (If You Wanna Rock 'N' Roll).mp3");
var file = new FileInfo(@"M:\library\Ac Dc\[1975] T.N.T\01 It'S A Long Way To The Top (If You Wanna Rock 'N' Roll).mp3");
if (file.Exists)
{
var tagLib = this.TagsHelper.MetaDataForFile(file.FullName);

View file

@ -46,6 +46,7 @@ namespace Roadie.Library.Tests
[InlineData("cover.png")]
[InlineData("Cover.Jpg")]
[InlineData("Release.JPG")]
[InlineData("folder.JPG")]
[InlineData("front.jpg")]
[InlineData("FrOnt.jpg")]
[InlineData("Art - front.jpg")]
@ -106,6 +107,7 @@ namespace Roadie.Library.Tests
[InlineData("cover 1.jpg")]
[InlineData("cover_01.jpg")]
[InlineData("cover 03.jpg")]
[InlineData("Dixieland-Front1.jpg")]
public void Test_Should_Not_Be_Release_Images(string input)
{
Assert.False(ImageHelper.IsReleaseImage(new FileInfo(input)));
@ -153,7 +155,8 @@ namespace Roadie.Library.Tests
[InlineData("Booklet-1.jpg")]
[InlineData("Booklet-10.jpg")]
[InlineData("Booklet_1.jpg")]
[InlineData("Booklet_1.jpg")]
[InlineData("Booklet 3.jpg")]
[InlineData("Booklet.jpg")]
[InlineData("Book.jpg")]
[InlineData("Book_3.jpg")]
@ -181,6 +184,9 @@ namespace Roadie.Library.Tests
[InlineData("release_1.jpg")]
[InlineData("release 3.jpg")]
[InlineData("release 10.jpg")]
[InlineData("Dixieland-Label-Side 1.JPG")]
[InlineData("Dixieland-Label-Side 2.JPG")]
[InlineData("Hearing Is Believing-Inside 1.jpg")]
public void Test_Should_Be_Release_Secondary_Images(string input)
{
Assert.True(ImageHelper.IsReleaseSecondaryImage(new FileInfo(input)));

View file

@ -48,11 +48,11 @@ namespace Roadie.Library.Tests
{
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\02 - Not A Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\02 - Not A Depth Charge.mp3"
}
};
var n = ID3TagsHelper.DetermineTotalDiscNumbers(three);
@ -62,11 +62,11 @@ namespace Roadie.Library.Tests
{
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\02 - Not A Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD1\02 - Not A Depth Charge.mp3"
}
};
n = ID3TagsHelper.DetermineTotalDiscNumbers(three);
@ -80,23 +80,23 @@ namespace Roadie.Library.Tests
{
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Not A Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Not A Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 04\01 - First.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 04\01 - First.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 06\02 - Second.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 06\02 - Second.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 10\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 10\01 - Depth Charge.mp3"
}
};
var n = ID3TagsHelper.DetermineTotalDiscNumbers(three);
@ -110,35 +110,35 @@ namespace Roadie.Library.Tests
{
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\01 - First.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\01 - First.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\02 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\02 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\03 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\03 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\04 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 03\04 - Depth Charge.mp3"
}
};
var n = ID3TagsHelper.DetermineTotalDiscNumbers(three);
@ -152,23 +152,23 @@ namespace Roadie.Library.Tests
{
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\02 - Not A Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - First.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - First.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 02\02 - Second.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 01\01 - Depth Charge.mp3"
}
};
var n = ID3TagsHelper.DetermineTotalDiscNumbers(three);
@ -178,11 +178,11 @@ namespace Roadie.Library.Tests
{
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD0\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD0\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD2\02 - Not A Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD2\02 - Not A Depth Charge.mp3"
}
};
n = ID3TagsHelper.DetermineTotalDiscNumbers(three);
@ -192,11 +192,11 @@ namespace Roadie.Library.Tests
{
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 1\01 - Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 1\01 - Depth Charge.mp3"
},
new AudioMetaData
{
Filename = @"N:\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 2\02 - Not A Depth Charge.mp3"
Filename = @"C:\roadie_dev_root\Devin Townsend (1996 - 2016)\The Devin Townsend Band (2003 - 2006)\2003 - Accelerated Evolution\CD 2\02 - Not A Depth Charge.mp3"
}
};
n = ID3TagsHelper.DetermineTotalDiscNumbers(three);

View file

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

View file

@ -6,7 +6,7 @@ namespace Roadie.Library.Configuration
/// This is a Api Key used by Roadie to interact with an API (ie KeyName is "BingImageSearch" and its key is the BingImageSearch Key)
/// </summary>
[Serializable]
public class ApiKey
public class ApiKey : IApiKey
{
public string ApiName { get; set; }
public string Key { get; set; }

View file

@ -3,7 +3,7 @@
namespace Roadie.Library.Configuration
{
[Serializable]
public class Converting
public class Converting : IConverting
{
public string APEConvertCommand { get; set; }
public bool DoDeleteAfter { get; set; }

View file

@ -3,7 +3,7 @@
namespace Roadie.Library.Configuration
{
[Serializable]
public class FilePlugins
public class FilePlugins : IFilePlugins
{
public int MinWeightToDelete { get; set; }
}

View file

@ -0,0 +1,9 @@
namespace Roadie.Library.Configuration
{
public interface IApiKey
{
string ApiName { get; set; }
string Key { get; set; }
string KeySecret { get; set; }
}
}

View file

@ -0,0 +1,10 @@
namespace Roadie.Library.Configuration
{
public interface IConverting
{
string APEConvertCommand { get; set; }
bool DoDeleteAfter { get; set; }
string M4AConvertCommand { get; set; }
string OGGConvertCommand { get; set; }
}
}

View file

@ -0,0 +1,7 @@
namespace Roadie.Library.Configuration
{
public interface IFilePlugins
{
int MinWeightToDelete { get; set; }
}
}

View file

@ -0,0 +1,8 @@
namespace Roadie.Library.Configuration
{
public interface IImageSize
{
short Height { get; set; }
short Width { get; set; }
}
}

View file

@ -0,0 +1,8 @@
namespace Roadie.Library.Configuration
{
public interface IInspector
{
bool DoCopyFiles { get; set; }
bool IsInReadOnlyMode { get; set; }
}
}

View file

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace Roadie.Library.Configuration
{
public interface IIntegrations
{
List<ApiKey> ApiKeys { get; set; }
string DiscogsConsumerKey { get; }
string DiscogsConsumerSecret { get; }
bool DiscogsProviderEnabled { get; set; }
short? DiscogsReadWriteTimeout { get; set; }
short? DiscogsTimeout { get; set; }
bool ITunesProviderEnabled { get; set; }
string LastFMApiKey { get; }
string LastFmApiSecret { get; }
bool LastFmProviderEnabled { get; set; }
bool MusicBrainzProviderEnabled { get; set; }
bool SpotifyProviderEnabled { get; set; }
}
}

View file

@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace Roadie.Library.Configuration
{
public interface IProcessing
{
string ArtistRemoveStringsRegex { get; set; }
bool DoAudioCleanup { get; set; }
bool DoClearComments { get; set; }
bool DoDeleteUnknowns { get; set; }
bool DoFolderArtistNameSet { get; set; }
bool DoMoveUnknowns { get; set; }
bool DoParseFromDiscogsDB { get; }
bool DoParseFromDiscogsDBFindingTrackForArtist { get; }
bool DoParseFromFileName { get; set; }
bool DoParseFromLastFM { get; }
bool DoParseFromMusicBrainz { get; }
bool DoSaveEditsToTags { get; set; }
int MaxImageWidth { get; set; }
int MaximumArtistImagesToAdd { get; set; }
int MaximumReleaseImagesToAdd { get; set; }
string ReleaseRemoveStringsRegex { get; set; }
string RemoveStringsRegex { get; set; }
List<ReplacementString> ReplaceStrings { get; set; }
string TrackRemoveStringsRegex { get; set; }
string UnknownFolder { get; set; }
}
}

View file

@ -0,0 +1,7 @@
namespace Roadie.Library.Configuration
{
public interface IRedisCache
{
string ConnectionString { get; set; }
}
}

View file

@ -0,0 +1,9 @@
namespace Roadie.Library.Configuration
{
public interface IReplacementString
{
string Key { get; set; }
int Order { get; set; }
string ReplaceWith { get; set; }
}
}

View file

@ -7,36 +7,37 @@ namespace Roadie.Library.Configuration
Dictionary<string, List<string>> ArtistNameReplace { get; set; }
string ConnectionString { get; set; }
string ContentPath { get; set; }
Converting Converting { get; set; }
IConverting Converting { get; set; }
string DefaultTimeZone { get; set; }
string DiagnosticsPassword { get; set; }
IEnumerable<string> DontDoMetaDataProvidersSearchArtists { get; set; }
IEnumerable<string> FileExtensionsToDelete { get; set; }
FilePlugins FilePlugins { get; set; }
IFilePlugins FilePlugins { get; set; }
string InboundFolder { get; set; }
Integrations Integrations { get; set; }
ImageSize LargeImageSize { get; set; }
ImageSize MaximumImageSize { get; set; }
IIntegrations Integrations { get; set; }
IImageSize LargeImageSize { get; set; }
IImageSize MaximumImageSize { get; set; }
string LibraryFolder { get; set; }
string ListenAddress { get; set; }
ImageSize MediumImageSize { get; set; }
Processing Processing { get; set; }
IImageSize MediumImageSize { get; set; }
IProcessing Processing { get; set; }
bool RecordNoResultSearches { get; set; }
RedisCache Redis { get; set; }
IRedisCache Redis { get; set; }
string SecretKey { get; set; }
string SingleArtistHoldingFolder { get; set; }
string SiteName { get; set; }
ImageSize SmallImageSize { get; set; }
IImageSize SmallImageSize { get; set; }
string SmtpFromAddress { get; set; }
string SmtpHost { get; set; }
string SmtpPassword { get; set; }
int SmtpPort { get; set; }
string SmtpUsername { get; set; }
bool SmtpUseSSl { get; set; }
ImageSize ThumbnailImageSize { get; set; }
IImageSize ThumbnailImageSize { get; set; }
Dictionary<string, string> TrackPathReplace { get; set; }
bool UseSSLBehindProxy { get; set; }
string BehindProxyHost { get; set; }
string WebsocketAddress { get; set; }
IInspector Inspector { get; set; }
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Roadie.Library.Configuration
{
public class Inspector : IInspector
{
/// <summary>
/// When true then make a copy of files to new destination versus moving files to destination.
/// </summary>
public bool DoCopyFiles { get; set; }
/// <summary>
/// When true then don't modify any files only report what would be done.
/// </summary>
public bool IsInReadOnlyMode { get; set; }
}
}

View file

@ -4,7 +4,7 @@ using System.Linq;
namespace Roadie.Library.Configuration
{
public class Integrations
public class Integrations : IIntegrations
{
private string _discogsConsumerKey = null;
private string _discogsConsumerSecret = null;
@ -14,6 +14,7 @@ namespace Roadie.Library.Configuration
private string _lastFMSecret = null;
public List<ApiKey> ApiKeys { get; set; }
public string DiscogsConsumerKey
{
get

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Roadie.Library.Configuration
{
[Serializable]
public class Processing
public class Processing : IProcessing
{
public bool DoAudioCleanup { get; set; }
public bool DoClearComments { get; set; }

View file

@ -3,7 +3,7 @@
namespace Roadie.Library.Configuration
{
[Serializable]
public class RedisCache
public class RedisCache : IRedisCache
{
public string ConnectionString { get; set; }
}

View file

@ -5,7 +5,7 @@ using System.Text;
namespace Roadie.Library.Configuration
{
[Serializable]
public class ReplacementString
public class ReplacementString : IReplacementString
{
public int Order { get; set; }
public string Key { get; set; }

View file

@ -22,23 +22,23 @@ namespace Roadie.Library.Configuration
/// </summary>
public string ContentPath { get; set; }
public Converting Converting { get; set; }
public IConverting Converting { get; set; }
public string DefaultTimeZone { get; set; }
public string DiagnosticsPassword { get; set; }
public IEnumerable<string> DontDoMetaDataProvidersSearchArtists { get; set; }
public IEnumerable<string> FileExtensionsToDelete { get; set; }
public FilePlugins FilePlugins { get; set; }
public IFilePlugins FilePlugins { get; set; }
public string InboundFolder { get; set; }
public Integrations Integrations { get; set; }
public ImageSize LargeImageSize { get; set; }
public ImageSize MaximumImageSize { get; set; }
public IIntegrations Integrations { get; set; }
public IImageSize LargeImageSize { get; set; }
public IImageSize MaximumImageSize { get; set; }
public string LibraryFolder { get; set; }
public string ListenAddress { get; set; }
public ImageSize MediumImageSize { get; set; }
public Processing Processing { get; set; }
public IImageSize MediumImageSize { get; set; }
public IProcessing Processing { get; set; }
public bool RecordNoResultSearches { get; set; }
public RedisCache Redis { get; set; }
public ImageSize SmallImageSize { get; set; }
public IRedisCache Redis { get; set; }
public IImageSize SmallImageSize { get; set; }
public string SecretKey { get; set; }
public string SingleArtistHoldingFolder { get; set; }
public string SiteName { get; set; }
@ -48,19 +48,24 @@ namespace Roadie.Library.Configuration
public int SmtpPort { get; set; }
public string SmtpUsername { get; set; }
public bool SmtpUseSSl { get; set; }
public ImageSize ThumbnailImageSize { get; set; }
public IImageSize ThumbnailImageSize { get; set; }
public Dictionary<string, string> TrackPathReplace { get; set; }
public bool UseSSLBehindProxy { get; set; }
public string BehindProxyHost { get; set; }
public string WebsocketAddress { get; set; }
public IInspector Inspector { get; set; }
public RoadieSettings()
{
this.ThumbnailImageSize = new ImageSize { Width = 80, Height = 80 };
this.SmallImageSize = new ImageSize { Width = 160, Height = 160 };
this.MediumImageSize = new ImageSize { Width = 320, Height = 320 };
this.LargeImageSize = new ImageSize { Width = 500, Height = 500 };
this.MaximumImageSize = new ImageSize { Width = 1024, Height = 1024 };
ThumbnailImageSize = new ImageSize { Width = 80, Height = 80 };
SmallImageSize = new ImageSize { Width = 160, Height = 160 };
MediumImageSize = new ImageSize { Width = 320, Height = 320 };
LargeImageSize = new ImageSize { Width = 500, Height = 500 };
MaximumImageSize = new ImageSize { Width = 1024, Height = 1024 };
Inspector = new Inspector();
Converting = new Converting();
Integrations = new Integrations();
Processing = new Processing();
}
}
}

View file

@ -3,7 +3,7 @@
namespace Roadie.Library.Configuration
{
[Serializable]
public class ImageSize
public class ImageSize : IImageSize
{
public short Height { get; set; }
public short Width { get; set; }

View file

@ -10,6 +10,7 @@ using Roadie.Library.Imaging;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -115,11 +116,9 @@ namespace Roadie.Library.FilePlugins
try
{
// See if file folder parent folder (likely file is in release folder) has primary artist image if so then move to artist folder
var artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.Artist);
if (!artistImages.Any())
{
artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory.Parent, Enums.ImageType.Artist);
}
var artistImages = new List<FileInfo>();
artistImages.AddRange(ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.Artist));
artistImages.AddRange(ImageHelper.FindImageTypeInDirectory(fileInfo.Directory.Parent, Enums.ImageType.Artist));
if (artistImages.Any())
{
var artistImage = artistImages.First();
@ -141,11 +140,9 @@ namespace Roadie.Library.FilePlugins
}
// See if any secondary artist images if so then move to artist folder
artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.ArtistSecondary);
if (!artistImages.Any())
{
artistImages = ImageHelper.FindImageTypeInDirectory(fileInfo.Directory.Parent, Enums.ImageType.Artist);
}
artistImages.Clear();
artistImages.AddRange(ImageHelper.FindImageTypeInDirectory(fileInfo.Directory, Enums.ImageType.ArtistSecondary));
artistImages.AddRange(ImageHelper.FindImageTypeInDirectory(fileInfo.Directory.Parent, Enums.ImageType.Artist));
if (artistImages.Any())
{
var looper = 0;

View file

@ -154,7 +154,7 @@ namespace Roadie.Library.Imaging
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"((f[-_\s]*[0-9]*)|cover|release|front)\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
return Regex.IsMatch(fileinfo.Name, @"((f[-_\s]*[0-9]*)|cover|folder|release|front)\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsReleaseSecondaryImage(FileInfo fileinfo)
@ -163,7 +163,7 @@ namespace Roadie.Library.Imaging
{
return false;
}
return Regex.IsMatch(fileinfo.Name, @"((book[let]*[-_]*[0-9]*)|(encartes[-_\s]*[(]*[0-9]*[)]*)|(cover[\s_-]+[0-9]+)|back|disc|inside|inlet|inlay|cd[0-9]*|inside|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
return Regex.IsMatch(fileinfo.Name, @"((book[let]*[-_\s]*[0-9]*)|(encartes[-_\s]*[(]*[0-9]*[)]*)|(cover[\s_-]+[0-9]+)|back|disc|(.*)[in]*side(.*)|inlet|inlay|cd[0-9]*|(release[\s_-]+[0-9]+))\.(jpg|jpeg|png|bmp|gif)", RegexOptions.IgnoreCase);
}
public static bool IsLabelImage(FileInfo fileinfo)

View file

@ -1,4 +1,5 @@
using HashidsNet;
using Mapster;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
@ -6,14 +7,18 @@ using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Extensions;
using Roadie.Library.Inspect.Plugins;
using Roadie.Library.Inspect.Plugins.File;
using Roadie.Library.Inspect.Plugins.Directory;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
using Roadie.Library.Processors;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Roadie.Library.Imaging;
using Roadie.Library.Utility;
namespace Roadie.Library.Inspect
{
@ -21,63 +26,94 @@ namespace Roadie.Library.Inspect
{
private static readonly string Salt = "6856F2EE-5965-4345-884B-2CCA457AAF59";
private IEnumerable<IInspectorPlugin> _plugins = null;
private IEnumerable<IInspectorFilePlugin> _filePlugins = null;
private IEnumerable<IInspectorDirectoryPlugin> _directoryPlugins = null;
public IEnumerable<IInspectorPlugin> Plugins
public DictionaryCacheManager CacheManager { get; }
public IEnumerable<IInspectorFilePlugin> FilePlugins
{
get
{
if (this._plugins == null)
if (_filePlugins == null)
{
var plugins = new List<IInspectorPlugin>();
var plugins = new List<IInspectorFilePlugin>();
try
{
var type = typeof(IInspectorPlugin);
var type = typeof(IInspectorFilePlugin);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
foreach (Type t in types)
{
if (t.GetInterface("IInspectorPlugin") != null && !t.IsAbstract && !t.IsInterface)
if (t.GetInterface("IInspectorFilePlugin") != null && !t.IsAbstract && !t.IsInterface)
{
IInspectorPlugin plugin = Activator.CreateInstance(t, new object[] { this.Configuration, this.CacheManager, this.Logger }) as IInspectorPlugin;
IInspectorFilePlugin plugin = Activator.CreateInstance(t, new object[] { Configuration, CacheManager, Logger, TagsHelper }) as IInspectorFilePlugin;
plugins.Add(plugin);
}
}
}
catch (Exception ex)
{
this.Logger.LogError(ex);
Logger.LogError(ex);
}
this._plugins = plugins.ToArray();
_filePlugins = plugins.ToArray();
}
return this._plugins;
return _filePlugins;
}
}
private IEventMessageLogger MessageLogger { get; }
public IEnumerable<IInspectorDirectoryPlugin> DirectoryPlugins
{
get
{
if (_filePlugins == null)
{
var plugins = new List<IInspectorDirectoryPlugin>();
try
{
var type = typeof(IInspectorDirectoryPlugin);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
foreach (Type t in types)
{
if (t.GetInterface("IInspectorDirectoryPlugin") != null && !t.IsAbstract && !t.IsInterface)
{
IInspectorDirectoryPlugin plugin = Activator.CreateInstance(t, new object[] { Configuration, CacheManager, Logger, TagsHelper }) as IInspectorDirectoryPlugin;
plugins.Add(plugin);
}
}
}
catch (Exception ex)
{
Logger.LogError(ex);
}
_directoryPlugins = plugins.ToArray();
}
return _directoryPlugins;
}
}
private IRoadieSettings Configuration { get; }
private ILogger Logger
{
get
{
return this.MessageLogger as ILogger;
return MessageLogger as ILogger;
}
}
private IEventMessageLogger MessageLogger { get; }
private ID3TagsHelper TagsHelper { get; }
private IRoadieSettings Configuration { get; }
public DictionaryCacheManager CacheManager { get; }
public Inspector()
{
Console.WriteLine("Roadie Media Inspector");
this.MessageLogger = new EventMessageLogger();
this.MessageLogger.Messages += MessageLogger_Messages;
MessageLogger = new EventMessageLogger();
MessageLogger.Messages += MessageLogger_Messages;
var settings = new RoadieSettings();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
@ -85,154 +121,283 @@ namespace Roadie.Library.Inspect
IConfiguration configuration = configurationBuilder.Build();
configuration.GetSection("RoadieSettings").Bind(settings);
settings.ConnectionString = configuration.GetConnectionString("RoadieDatabaseConnection");
this.Configuration = settings;
this.CacheManager = new DictionaryCacheManager(this.Logger, new CachePolicy(TimeSpan.FromHours(4)));
this.TagsHelper = new ID3TagsHelper(this.Configuration, this.CacheManager, this.Logger);
Configuration = settings;
CacheManager = new DictionaryCacheManager(Logger, new CachePolicy(TimeSpan.FromHours(4)));
TagsHelper = new ID3TagsHelper(Configuration, CacheManager, Logger);
}
private void MessageLogger_Messages(object sender, EventMessage e)
public static string ToToken(string input)
{
Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
}
public static string ArtistInspectorToken(AudioMetaData metaData)
{
var hashids = new Hashids(Inspector.Salt);
var artistId = 0;
var bytes = System.Text.Encoding.ASCII.GetBytes(metaData.Artist);
var looper = bytes.Length / 4;
for(var i = 0; i < looper; i++)
{
artistId += BitConverter.ToInt32(bytes, i * 4);
}
if (artistId < 0)
{
artistId = artistId * -1;
}
var token = hashids.Encode(artistId);
return token;
}
public static string ReleaseInspectorToken(AudioMetaData metaData)
{
var hashids = new Hashids(Inspector.Salt);
var releaseId = 0;
var bytes = System.Text.Encoding.ASCII.GetBytes(metaData.Artist + metaData.Release);
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++)
{
releaseId += BitConverter.ToInt32(bytes, i * 4);
numbers += BitConverter.ToInt32(bytes, i * 4);
}
if (releaseId < 0)
if (numbers < 0)
{
releaseId = releaseId * -1;
numbers *= -1;
}
var token = hashids.Encode(releaseId);
var token = hashids.Encode(numbers);
return token;
}
public void Inspect(bool doCopy, string folder, string destination)
public static string ArtistInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist);
public static string ReleaseInspectorToken(AudioMetaData metaData) => ToToken(metaData.Artist + metaData.Release);
public void Inspect(bool doCopy, bool isReadOnly, string directoryToInspect, string destination, bool dontAppendSubFolder, bool dontDeleteEmptyFolders)
{
Configuration.Inspector.IsInReadOnlyMode = isReadOnly;
Configuration.Inspector.DoCopyFiles = doCopy;
var artistsFound = new List<string>();
var releasesFound = new List<string>();
var mp3FilesFoundCount = 0;
// 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 && !Directory.Exists(dest))
{
Directory.CreateDirectory(dest);
}
// Get all the directorys in the directory
var folderDirectories = Directory.GetDirectories(folder, "*.*", SearchOption.AllDirectories);
var directoryDirectories = Directory.GetDirectories(directoryToInspect, "*.*", SearchOption.AllDirectories);
var directories = new List<string>
{
folder
directoryToInspect
};
directories.AddRange(folderDirectories);
directories.AddRange(directoryDirectories);
directories.Remove(dest);
var inspectedImagesInDirectories = new List<string>();
foreach (var directory in directories)
{
var directoryInfo = new DirectoryInfo(directory);
var sw = Stopwatch.StartNew();
Console.WriteLine($"╔ ░▒▓ Inspecting [{ directory }] ▓▒░");
Console.WriteLine("╠═╗");
// Get all the MP3 files in the folder
Console.WriteLine("╠╦════════════════════════╣");
// Get all the MP3 files in 'directory'
var files = Directory.GetFiles(directory, "*.mp3", SearchOption.TopDirectoryOnly);
if (files == null || !files.Any())
{
continue;
}
Console.WriteLine($"Found [{ files.Length }] mp3 Files");
Console.WriteLine("╠═╣");
// Get audiometadata and output details including weight/validity
foreach (var file in files)
// Run directory plugins against current directory
foreach (var plugin in DirectoryPlugins.OrderBy(x => x.Order))
{
var tagLib = this.TagsHelper.MetaDataForFile(file);
Console.WriteLine(tagLib.Data);
}
Console.WriteLine("╠═╣");
List<AudioMetaData> fileMetaDatas = new List<AudioMetaData>();
List<FileInfo> fileInfos = new List<FileInfo>();
foreach (var file in files)
{
var fileInfo = new FileInfo(file);
var tagLib = this.TagsHelper.MetaDataForFile(fileInfo.FullName);
var artistToken = ArtistInspectorToken(tagLib.Data);
var releaseToken = ReleaseInspectorToken(tagLib.Data);
var newFileName = $"{artistToken}_{releaseToken}_CD{ (tagLib.Data.Disk ?? ID3TagsHelper.DetermineDiscNumber(tagLib.Data)).ToString("000") }_{ tagLib.Data.TrackNumber.Value.ToString("0000") }.mp3";
var subFolder = folder == destination ? fileInfo.DirectoryName : destination;
var newPath = Path.Combine(destination, subFolder, newFileName.ToFileNameFriendly());
if (!doCopy)
{
if (fileInfo.FullName != newPath)
{
if (File.Exists(newPath))
{
File.Delete(newPath);
}
fileInfo.MoveTo(newPath);
}
}
else
{
fileInfo.CopyTo(newPath, true);
}
tagLib.Data.Filename = fileInfo.FullName;
fileMetaDatas.Add(tagLib.Data);
fileInfos.Add(fileInfo);
}
// Perform InspectorPlugins
IEnumerable<AudioMetaData> pluginMetaData = fileMetaDatas.OrderBy(x => x.Filename);
foreach (var plugin in this.Plugins.OrderBy(x => x.Order))
{
Console.WriteLine($"╟ Running Plugin { plugin.Description }");
OperationResult<IEnumerable<AudioMetaData>> pluginResult = null;
Console.WriteLine($"╠╬═ Running Directory Plugin { plugin.Description }");
try
{
pluginResult = plugin.Process(pluginMetaData);
var pluginResult = plugin.Process(directoryInfo);
if (!pluginResult.IsSuccess)
{
Console.WriteLine($"Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
return;
}
else
{
Console.WriteLine($"╠╣ Directory Plugin Message: { pluginResult.Data }");
}
}
catch (Exception ex)
{
Console.WriteLine($"Plugin Error: [{ ex.ToString() }]");
return;
}
if (!pluginResult.IsSuccess)
}
Console.WriteLine($"╠╝");
Console.WriteLine($"╟─ Found [{ files.Length }] mp3 Files");
List<AudioMetaData> fileMetaDatas = new List<AudioMetaData>();
List<FileInfo> fileInfos = new List<FileInfo>();
// Inspect the found MP3 files in 'directory'
foreach (var file in files)
{
mp3FilesFoundCount++;
var fileInfo = new FileInfo(file);
Console.WriteLine($"╟─ Inspecting [{ fileInfo.FullName }]");
var tagLib = TagsHelper.MetaDataForFile(fileInfo.FullName);
Console.WriteLine($"╟ (Pre ) : { tagLib.Data }");
tagLib.Data.Filename = fileInfo.FullName;
var originalMetaData = tagLib.Data.Adapt<AudioMetaData>();
var pluginMetaData = tagLib.Data;
// Run all file plugins against the MP3 file modifying the MetaData
foreach (var plugin in FilePlugins.OrderBy(x => x.Order))
{
Console.WriteLine($"Plugin Failed. Error [{ JsonConvert.SerializeObject(pluginResult)}]");
Console.WriteLine($"╟┤ Running File Plugin { plugin.Description }");
OperationResult<AudioMetaData> pluginResult = null;
try
{
pluginResult = plugin.Process(pluginMetaData);
if (!pluginResult.IsSuccess)
{
Console.WriteLine($"Plugin Failed: Error [{ JsonConvert.SerializeObject(pluginResult)}]");
return;
}
}
catch (Exception ex)
{
Console.WriteLine($"Plugin Error: [{ ex.ToString() }]");
return;
}
pluginMetaData = pluginResult.Data;
}
pluginMetaData = pluginResult.Data;
}
// Save all plugin modifications to the MetaData
foreach (var metadata in pluginMetaData)
{
this.TagsHelper.WriteTags(metadata, metadata.Filename);
}
Console.WriteLine("╠═╣");
// Get audiometadata and output details including weight/validity
foreach (var fileInfo in fileInfos)
{
var tagLib = this.TagsHelper.MetaDataForFile(fileInfo.FullName);
if (!tagLib.IsSuccess || !tagLib.Data.IsValid)
// See if the MetaData from the Plugins is different from the original
var differences = AutoCompare.Comparer.Compare(originalMetaData, pluginMetaData);
if (differences.Any())
{
Console.WriteLine($"■■ INVALID: {tagLib.Data }");
var skipDifferences = new List<string> { "AudioMetaDataWeights", "FileInfo", "Images", "TrackArtists", "ValidWeight" };
var differencesDescription = $"{ System.Environment.NewLine }";
foreach (var difference in differences)
{
if (skipDifferences.Contains(difference.Name))
{
continue;
}
differencesDescription += $"╟ || { difference.Name } : Was [{ difference.OldValue}] Now [{ difference.NewValue}]{ System.Environment.NewLine }";
}
Console.Write($"╟ ≡ != ID3 Tag Modified: { differencesDescription }");
if (!isReadOnly)
{
TagsHelper.WriteTags(pluginMetaData, pluginMetaData.Filename);
}
else
{
Console.WriteLine("╟ ■ Read Only Mode: Not Modifying File ID3 Tags.");
}
}
else
{
Console.WriteLine(tagLib.Data);
Console.WriteLine($"╟ ≡ == ID3 Tag NOT Modified");
}
if(!pluginMetaData.IsValid)
{
Console.WriteLine($"╟ ■■ INVALID: { pluginMetaData }");
}
else
{
Console.WriteLine($"╟ (Post) : { pluginMetaData }");
}
var artistToken = ArtistInspectorToken(tagLib.Data);
if(!artistsFound.Contains(artistToken))
{
artistsFound.Add(artistToken);
}
var releaseToken = ReleaseInspectorToken(tagLib.Data);
if (!releasesFound.Contains(releaseToken))
{
releasesFound.Add(releaseToken);
}
var newFileName = $"CD{ (tagLib.Data.Disk ?? ID3TagsHelper.DetermineDiscNumber(tagLib.Data)).ToString("000") }_{ tagLib.Data.TrackNumber.Value.ToString("0000") }.mp3";
// Artist sub folder is created to hold Releases for Artist and Artist Images
var artistSubDirectory = directory == dest ? fileInfo.DirectoryName : Path.Combine(dest, artistToken);
// Each release is put into a subfolder into the current run Inspector folder to hold MP3 Files and Release Images
var subDirectory = directory == dest ? fileInfo.DirectoryName : Path.Combine(dest, artistToken, releaseToken);
if(!isReadOnly && !Directory.Exists(subDirectory))
{
Directory.CreateDirectory(subDirectory);
}
// If enabled move MP3 to new folder
var newPath = Path.Combine(dest, subDirectory, newFileName.ToFileNameFriendly());
if (isReadOnly)
{
Console.WriteLine($"╟ ■ Read Only Mode: File would be [{ (doCopy ? "Copied" : "Moved") }] to [{ newPath }]");
}
else
{
if (!doCopy)
{
if (fileInfo.FullName != newPath)
{
if (File.Exists(newPath))
{
File.Delete(newPath);
}
fileInfo.MoveTo(newPath);
}
}
else
{
fileInfo.CopyTo(newPath, true);
}
}
if (!inspectedImagesInDirectories.Contains(fileInfo.DirectoryName))
{
// Get all artist images and move to artist folder
var foundArtistImages = new List<FileInfo>();
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, Enums.ImageType.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo.Parent, Enums.ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.Artist, SearchOption.TopDirectoryOnly));
foundArtistImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.ArtistSecondary, SearchOption.TopDirectoryOnly));
foreach (var artistImage in foundArtistImages)
{
MoveImage(isReadOnly, doCopy, dest, artistSubDirectory, artistImage);
}
// Get all release images and move to release folder
var foundReleaseImages = new List<FileInfo>();
foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.Release, SearchOption.AllDirectories));
foundReleaseImages.AddRange(ImageHelper.FindImageTypeInDirectory(directoryInfo, Enums.ImageType.ReleaseSecondary, SearchOption.AllDirectories));
foreach (var foundReleaseImage in foundReleaseImages)
{
MoveImage(isReadOnly, doCopy, dest, subDirectory, foundReleaseImage);
}
inspectedImagesInDirectories.Add(fileInfo.DirectoryName);
}
Console.WriteLine("╠════════════════════════╣");
}
Console.WriteLine("╚═╝");
sw.Stop();
Console.WriteLine($"╚═ Elapsed Time { sw.ElapsedMilliseconds.ToString("0000000") }, Artists { artistsFound.Count() }, Releases { releasesFound.Count() }, MP3s { mp3FilesFoundCount } ═╝");
}
if (!dontDeleteEmptyFolders)
{
var delEmptyFolderIn = new DirectoryInfo(directoryToInspect);
Console.WriteLine($"╟ X Deleting Empty folders in [{ delEmptyFolderIn.FullName }]");
FolderPathHelper.DeleteEmptyDirs(directoryToInspect);
}
else
{
Console.WriteLine("╟ ■ Read Only Mode: Not deleting empty folders.");
}
}
private void MoveImage(bool isReadOnly, bool doCopy, string dest, string subdirectory, FileInfo image)
{
Console.WriteLine($"╟─ Inspecting Image [{ image.FullName }]");
var newImagePath = Path.Combine(dest, subdirectory, image.Name);
if (isReadOnly)
{
Console.WriteLine($"╟ ■ Read Only Mode: Would be [{ (doCopy ? "Copied" : "Moved") }] to [{ newImagePath }]");
}
else
{
if (!doCopy)
{
if (image.FullName != newImagePath)
{
if (File.Exists(newImagePath))
{
File.Delete(newImagePath);
}
image.MoveTo(newImagePath);
}
}
else
{
image.CopyTo(newImagePath, true);
}
}
}
private void MessageLogger_Messages(object sender, EventMessage e) => Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
}
}
}

View file

@ -1,42 +0,0 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Extensions;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
using System.Collections.Generic;
using System.Linq;
namespace Roadie.Library.Inspect.Plugins
{
public class CleanMetaData : PluginBase
{
public override int Order => 2;
public override string Description => "Clean: Clean the primary elements of MetaData";
public CleanMetaData(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
: base(configuration, cacheManager, logger)
{
}
public override OperationResult<IEnumerable<AudioMetaData>> Process(IEnumerable<AudioMetaData> metaDatas)
{
var result = new OperationResult<IEnumerable<AudioMetaData>>();
if (this.Configuration.Processing.DoAudioCleanup)
{
foreach (var metaData in metaDatas)
{
metaData.Artist = metaData.Artist.CleanString(this.Configuration, this.Configuration.Processing.ArtistRemoveStringsRegex).ToTitleCase(doPutTheAtEnd: false);
metaData.Release = metaData.Release.CleanString(this.Configuration, this.Configuration.Processing.ReleaseRemoveStringsRegex).ToTitleCase(doPutTheAtEnd: false);
metaData.TrackArtist = metaData.TrackArtist.CleanString(this.Configuration, this.Configuration.Processing.ReleaseRemoveStringsRegex).ToTitleCase(doPutTheAtEnd: false);
metaData.Title = metaData.Title.CleanString(this.Configuration, this.Configuration.Processing.TrackRemoveStringsRegex).ToTitleCase(doPutTheAtEnd: false);
}
}
result.Data = metaDatas;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,64 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.ID3Tags;
using System;
using System.IO;
using System.Linq;
namespace Roadie.Library.Inspect.Plugins.Directory
{
public class EnsureArtistConsistent : FolderPluginBase
{
public override string Description
{
get
{
return "Consistent: Ensure all MP3 files in folder have same Artist (TPE1)";
}
}
public override int Order { get; } = 1;
public EnsureArtistConsistent(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public override OperationResult<string> Process(DirectoryInfo directory)
{
var result = new OperationResult<string>();
var data = string.Empty;
var found = 0;
var modified = 0;
var metaDatasForFilesInFolder = GetAudioMetaDatasForDirectory(directory);
if (metaDatasForFilesInFolder.Any())
{
found = metaDatasForFilesInFolder.Count();
var firstMetaData = metaDatasForFilesInFolder.OrderBy(x => 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))
{
modified++;
Console.WriteLine($"╟ Setting Arist to [{ artist }], was [{ metaData.Artist }] on file [{ metaData.FileInfo.Name}");
metaData.Artist = artist;
if (!Configuration.Inspector.IsInReadOnlyMode)
{
TagsHelper.WriteTags(metaData, metaData.Filename);
}
}
data = $"Found [{ found }] files, Modified [{ modified }] files";
}
result.Data = data;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,57 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.ID3Tags;
using System;
using System.IO;
using System.Linq;
namespace Roadie.Library.Inspect.Plugins.Directory
{
public class EnsureReleaseConsistent : FolderPluginBase
{
public override string Description
{
get
{
return "Consistent: Ensure all MP3 files in folder have same Release Title (TALB)";
}
}
public override int Order { get; } = 1;
public EnsureReleaseConsistent(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public override OperationResult<string> Process(DirectoryInfo directory)
{
var result = new OperationResult<string>();
var data = string.Empty;
var found = 0;
var modified = 0;
var metaDatasForFilesInFolder = GetAudioMetaDatasForDirectory(directory);
if (metaDatasForFilesInFolder.Any())
{
found = metaDatasForFilesInFolder.Count();
var firstMetaData = metaDatasForFilesInFolder.OrderBy(x => x.TrackNumber).First();
var release = firstMetaData.Release;
foreach (var metaData in metaDatasForFilesInFolder.Where(x => x.Release != release))
{
modified++;
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);
}
}
data = $"Found [{ found }] files, Modified [{ modified }] files";
}
result.Data = data;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,57 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.ID3Tags;
using System;
using System.IO;
using System.Linq;
namespace Roadie.Library.Inspect.Plugins.Directory
{
public class EnsureYearConsistent : FolderPluginBase
{
public override string Description
{
get
{
return "Consistent: Ensure all MP3 files in folder have same Year (TYER)";
}
}
public override int Order { get; } = 1;
public EnsureYearConsistent(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public override OperationResult<string> Process(DirectoryInfo directory)
{
var result = new OperationResult<string>();
var data = string.Empty;
var found = 0;
var modified = 0;
var metaDatasForFilesInFolder = GetAudioMetaDatasForDirectory(directory);
if (metaDatasForFilesInFolder.Any())
{
found = metaDatasForFilesInFolder.Count();
var firstMetaData = metaDatasForFilesInFolder.OrderBy(x => x.TrackNumber).First();
var year = firstMetaData.Year;
foreach (var metaData in metaDatasForFilesInFolder.Where(x => x.Year != year))
{
modified++;
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);
}
}
data = $"Found [{ found }] files, Modified [{ modified }] files";
}
result.Data = data;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,18 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.ID3Tags;
using System.IO;
namespace Roadie.Library.Inspect.Plugins.Directory
{
public abstract class FolderPluginBase : PluginBase, IInspectorDirectoryPlugin
{
public FolderPluginBase(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public abstract OperationResult<string> Process(DirectoryInfo directory);
}
}

View file

@ -0,0 +1,12 @@
using System.IO;
namespace Roadie.Library.Inspect.Plugins.Directory
{
public interface IInspectorDirectoryPlugin
{
string Description { get; }
int Order { get; }
OperationResult<string> Process(DirectoryInfo directory);
}
}

View file

@ -0,0 +1,58 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Extensions;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
using System;
namespace Roadie.Library.Inspect.Plugins.File
{
public class CleanUpArtists : FilePluginBase
{
public override string Description => "Clean: Artist (TPE1) and TrackArtist (TOPE)";
public override int Order => 2;
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;
var splitCharacter = AudioMetaData.ArtistSplitCharacter.ToString();
// Replace seperators with proper split character
foreach (var replace in ListReplacements)
{
artist = artist.Replace(replace, splitCharacter, StringComparison.OrdinalIgnoreCase);
}
var result = artist.CleanString(this.Configuration, this.Configuration.Processing.ArtistRemoveStringsRegex).ToTitleCase(doPutTheAtEnd: false);
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);
}
return string.IsNullOrEmpty(result) ? null : result;
}
public override OperationResult<AudioMetaData> Process(AudioMetaData metaData)
{
var result = new OperationResult<AudioMetaData>();
if (this.Configuration.Processing.DoAudioCleanup)
{
metaData.Artist = CleanArtist(metaData.ArtistRaw);
metaData.TrackArtist = CleanArtist(metaData.TrackArtistRaw, metaData.ArtistRaw);
}
result.Data = metaData;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,34 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
namespace Roadie.Library.Inspect.Plugins.File
{
public class CleanUpComments : FilePluginBase
{
public override string Description => "Clean: Clear Comments (COMM)";
public override int Order => 2;
public CleanUpComments(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public override OperationResult<AudioMetaData> Process(AudioMetaData metaData)
{
var result = new OperationResult<AudioMetaData>();
if (this.Configuration.Processing.DoAudioCleanup)
{
if (this.Configuration.Processing.DoClearComments)
{
metaData.Comments = null;
}
}
result.Data = metaData;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,32 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Extensions;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
namespace Roadie.Library.Inspect.Plugins.File
{
public class CleanUpReleaseTitle : FilePluginBase
{
public override string Description => "Clean: Release Title (TALB)";
public override int Order => 2;
public CleanUpReleaseTitle(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public override OperationResult<AudioMetaData> Process(AudioMetaData metaData)
{
var result = new OperationResult<AudioMetaData>();
if (this.Configuration.Processing.DoAudioCleanup)
{
metaData.Release = metaData.Release.CleanString(this.Configuration, this.Configuration.Processing.ReleaseRemoveStringsRegex).ToTitleCase(doPutTheAtEnd: false);
}
result.Data = metaData;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,32 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Extensions;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
namespace Roadie.Library.Inspect.Plugins.File
{
public class CleanUpTrackTitle : FilePluginBase
{
public override string Description => "Clean: Clean Track Title (TIT2) ";
public override int Order => 2;
public CleanUpTrackTitle(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public override OperationResult<AudioMetaData> Process(AudioMetaData metaData)
{
var result = new OperationResult<AudioMetaData>();
if (this.Configuration.Processing.DoAudioCleanup)
{
metaData.Title = metaData.Title.CleanString(this.Configuration, this.Configuration.Processing.TrackRemoveStringsRegex).ToTitleCase(doPutTheAtEnd: false);
}
result.Data = metaData;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -0,0 +1,18 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
namespace Roadie.Library.Inspect.Plugins.File
{
public abstract class FilePluginBase : PluginBase, IInspectorFilePlugin
{
public FilePluginBase(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public abstract OperationResult<AudioMetaData> Process(AudioMetaData metaData);
}
}

View file

@ -0,0 +1,12 @@
using Roadie.Library.MetaData.Audio;
namespace Roadie.Library.Inspect.Plugins.File
{
public interface IInspectorFilePlugin
{
string Description { get; }
int Order { get; }
OperationResult<AudioMetaData> Process(AudioMetaData metaData);
}
}

View file

@ -0,0 +1,40 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
using System.Linq;
namespace Roadie.Library.Inspect.Plugins.File
{
public class Renumber : FilePluginBase
{
public override string Description
{
get
{
return "Renumber: Renumber all given tracks sequentially and set maximum number of tracks";
}
}
public override int Order { get; } = 1;
public Renumber(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
: base(configuration, cacheManager, logger, tagsHelper)
{
}
public override OperationResult<AudioMetaData> Process(AudioMetaData metaData)
{
var result = new OperationResult<AudioMetaData>();
var metaDatasForFilesInFolder = GetAudioMetaDatasForDirectory(metaData.FileInfo.Directory);
metaData.TrackNumber = metaData.TrackNumber ?? ID3TagsHelper.DetermineTrackNumber(metaData.Filename);
metaData.TotalTrackNumbers = ID3TagsHelper.DetermineTotalTrackNumbers(metaData.Filename) ?? metaDatasForFilesInFolder.Count();
metaData.Disk = ID3TagsHelper.DetermineDiscNumber(metaData);
metaData.TotalDiscCount = ID3TagsHelper.DetermineTotalDiscNumbers(metaDatasForFilesInFolder);
result.Data = metaData;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -1,14 +0,0 @@
using Roadie.Library.MetaData.Audio;
using System.Collections.Generic;
using System.IO;
namespace Roadie.Library.Inspect.Plugins
{
public interface IInspectorPlugin
{
string Description { get; }
int Order { get; }
OperationResult<IEnumerable<AudioMetaData>> Process(IEnumerable<AudioMetaData> metaDatas);
}
}

View file

@ -2,30 +2,54 @@
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.Audio;
using System;
using Roadie.Library.MetaData.ID3Tags;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
namespace Roadie.Library.Inspect.Plugins
{
public abstract class PluginBase : IInspectorPlugin
public abstract class PluginBase
{
protected IRoadieSettings Configuration { get; }
protected ICacheManager CacheManager { get; }
protected ILogger Logger { get; }
public abstract int Order { get; }
public abstract string Description { get; }
public abstract int Order { get; }
protected ICacheManager CacheManager { get; }
protected IRoadieSettings Configuration { get; }
protected IEnumerable<string> ListReplacements { get; } = new List<string> { " ; ", " ;", "; ", ";", " & ", " &", "& ", ";", "&" };
protected ILogger Logger { get; }
protected IID3TagsHelper TagsHelper { get; }
private Dictionary<string, IEnumerable<AudioMetaData>> CachedAudioDatas { get; set; }
public PluginBase(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
public PluginBase(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger, IID3TagsHelper tagsHelper)
{
this.Configuration = configuration;
this.CacheManager = cacheManager;
this.Logger = logger;
this.TagsHelper = tagsHelper;
CachedAudioDatas = new Dictionary<string, IEnumerable<AudioMetaData>>();
}
public abstract OperationResult<IEnumerable<AudioMetaData>> Process(IEnumerable<AudioMetaData> metaDatas);
protected IEnumerable<AudioMetaData> GetAudioMetaDatasForDirectory(DirectoryInfo directory)
{
try
{
if (!CachedAudioDatas.ContainsKey(directory.FullName))
{
var filesInMetaDataFolder = directory.GetFiles("*.mp3", SearchOption.TopDirectoryOnly);
var metaDatasForFilesInFolder = new List<AudioMetaData>();
foreach (var fileInMetaDataFolder in filesInMetaDataFolder)
{
metaDatasForFilesInFolder.Add(TagsHelper.MetaDataForFile(fileInMetaDataFolder.FullName).Data);
}
CachedAudioDatas.Add(directory.FullName, metaDatasForFilesInFolder);
}
return CachedAudioDatas[directory.FullName];
}
catch (System.Exception ex)
{
Logger.LogError(ex);
}
return Enumerable.Empty<AudioMetaData>();
}
}
}
}

View file

@ -1,55 +0,0 @@
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.MetaData.Audio;
using Roadie.Library.MetaData.ID3Tags;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Roadie.Library.Inspect.Plugins
{
public class Renumber : PluginBase
{
public override string Description
{
get
{
return "Renumber: Renumber all given tracks sequentially and set maximum number of tracks";
}
}
public override int Order { get; } = 1;
public Renumber(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
: base(configuration, cacheManager, logger)
{
}
public override OperationResult<IEnumerable<AudioMetaData>> Process(IEnumerable<AudioMetaData> metaDatas)
{
var result = new OperationResult<IEnumerable<AudioMetaData>>();
var totalNumberOfMedia = ID3TagsHelper.DetermineTotalDiscNumbers(metaDatas);
var folders = metaDatas.GroupBy(x => x.FileInfo.DirectoryName);
foreach(var folder in folders)
{
short looper = 0;
foreach(var metaData in folder)
{
looper++;
metaData.TrackNumber = looper;
metaData.TotalTrackNumbers = ID3TagsHelper.DetermineTotalTrackNumbers(metaData.Filename) ?? folder.Count();
metaData.Disk = ID3TagsHelper.DetermineDiscNumber(metaData);
metaData.TotalDiscCount = totalNumberOfMedia;
}
}
result.Data = metaDatas;
result.IsSuccess = true;
return result;
}
}
}

View file

@ -8,11 +8,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoCompare.Core" Version="1.0.0" />
<PackageReference Include="CsvHelper" Version="12.1.2" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.4.7" />
<PackageReference Include="FluentFTP" Version="24.0.0" />
<PackageReference Include="Hashids.net" Version="1.2.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.4" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.6" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" />
<PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" />
<PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" />

View file

@ -14,10 +14,10 @@ namespace Roadie.Library.SearchEngines.Imaging
protected readonly IRoadieSettings _configuratio = null;
protected readonly string _referrer = null;
protected readonly string _requestIp = null;
protected ApiKey _apiKey = null;
protected IApiKey _apiKey = null;
protected ILogger _logger = null;
protected ApiKey ApiKey
protected IApiKey ApiKey
{
get
{

View file

@ -1,4 +1,5 @@
using Roadie.Library.Extensions;
using Roadie.Library.Inspect.Plugins;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -9,7 +10,7 @@ namespace Roadie.Library.MetaData.Audio
{
[Serializable]
[DebuggerDisplay("Artist: {Artist}, TrackArtist: {TrackArtist}, Release: {Release}, TrackNumber: {TrackNumber}, Title: {Title}, Year: {Year}")]
public sealed class AudioMetaData
public sealed class AudioMetaData : IAudioMetaData
{
public const char ArtistSplitCharacter = '/';
@ -60,27 +61,6 @@ namespace Roadie.Library.MetaData.Audio
public string ArtistRaw { get; set; }
/// <summary>
/// TPE1 All Lead Artists
/// <seealso cref="http://id3.org/id3v2.3.0"/>
/// </summary>
/// <remarks>Per ID3.Org Spec: The 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group' is used for the main artist(s). They are seperated with the "/" character.</remarks>
public IEnumerable<string> Artists
{
get
{
if (string.IsNullOrEmpty(this._artist))
{
return new string[0];
}
if (!this._artist.Contains(AudioMetaData.ArtistSplitCharacter.ToString()))
{
return new string[0];
}
return this._artist.Split(AudioMetaData.ArtistSplitCharacter).Select(x => x.ToTitleCase()).ToArray();
}
}
public int? AudioBitrate { get; set; }
public int? AudioChannels { get; set; }
@ -215,6 +195,11 @@ namespace Roadie.Library.MetaData.Audio
/// </summary>
public string Release { get; set; }
/// <summary>
/// COMM
/// </summary>
public string Comments { get; set; }
public string ReleaseLastFmId { get; set; }
public string ReleaseMusicBrainzId { get; set; }
@ -230,6 +215,9 @@ namespace Roadie.Library.MetaData.Audio
/// </summary>
public TimeSpan? Time { get; set; }
/// <summary>
/// TIT2
/// </summary>
public string Title
{
get
@ -269,29 +257,26 @@ namespace Roadie.Library.MetaData.Audio
public int? TotalTrackNumbers { get; set; }
/// <summary>
/// TOPE First Contributing Artist
/// TOPE First Contributing Artist, null if same as Artist
/// </summary>
public string TrackArtist
{
get
{
if (!string.IsNullOrEmpty(this._trackArtist) && this._trackArtist.Contains(AudioMetaData.ArtistSplitCharacter.ToString()))
string result = null;
if (!string.IsNullOrEmpty(this._trackArtist))
{
return this._trackArtist.Split(AudioMetaData.ArtistSplitCharacter).First().ToTitleCase();
result = this._trackArtist.Split(AudioMetaData.ArtistSplitCharacter).First().ToTitleCase();
}
if (!string.IsNullOrEmpty(this._artist) || !string.IsNullOrEmpty(this._trackArtist))
if (!string.IsNullOrEmpty(this._artist) || !string.IsNullOrEmpty(result))
{
return !this._artist.Equals(this._trackArtist, StringComparison.OrdinalIgnoreCase) ? this._trackArtist : null;
result = !this._artist.Equals(result, StringComparison.OrdinalIgnoreCase) ? result : null;
}
return null;
return result;
}
set
{
this._trackArtist = value;
if (!string.IsNullOrEmpty(this._trackArtist))
{
this._trackArtist = this._trackArtist.Replace(';', AudioMetaData.ArtistSplitCharacter).ToTitleCase();
}
}
}
@ -321,7 +306,7 @@ namespace Roadie.Library.MetaData.Audio
{
if (!this._artist.Equals(this._trackArtist, StringComparison.OrdinalIgnoreCase))
{
return this._trackArtist.Split(AudioMetaData.ArtistSplitCharacter).Where(x => !string.IsNullOrEmpty(x)).Select(x => x.ToTitleCase()).ToArray();
return this._trackArtist.Split(AudioMetaData.ArtistSplitCharacter).Where(x => !string.IsNullOrEmpty(x)).Select(x => x.ToTitleCase()).OrderBy(x => x).ToArray();
}
}
return new string[0];
@ -398,7 +383,13 @@ namespace Roadie.Library.MetaData.Audio
public override string ToString()
{
return string.Format($"IsValid: {this.IsValid}{ (this.IsSoundTrack ? " [SoundTrack ]" : string.Empty)}, ValidWeight {this.ValidWeight}, Artist: {this.Artist}, Release: {this.Release}, TrackNumber: {this.TrackNumber}, TrackTotal: {this.TotalTrackNumbers}, Title: {this.Title}, Year: {this.Year}, Duration: {(this.Time == null ? "-" : this.Time.Value.ToString())}");
var result = $"IsValid: {this.IsValid}{ (this.IsSoundTrack ? " [SoundTrack ]" : string.Empty)}, ValidWeight {this.ValidWeight}, Artist: {this.Artist}";
if(!string.IsNullOrEmpty(this.TrackArtist))
{
result += $", TrackArtist: { this.TrackArtist}";
}
result += $", Release: {this.Release}, TrackNumber: {this.TrackNumber}, TrackTotal: {this.TotalTrackNumbers}, Title: {this.Title}, Year: {this.Year}, Duration: {(this.Time == null ? "-" : this.Time.Value.ToString())}";
return result;
}
}
}

View file

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Roadie.Library.MetaData.Audio
{
public interface IAudioMetaData
{
string AmgId { get; set; }
string Artist { get; set; }
string ArtistRaw { get; set; }
int? AudioBitrate { get; set; }
int? AudioChannels { get; set; }
AudioMetaDataWeights AudioMetaDataWeights { get; }
int? AudioSampleRate { get; set; }
string Comments { get; set; }
string Directory { get; }
int? Disk { get; set; }
string DiskSubTitle { get; set; }
FileInfo FileInfo { get; }
string Filename { get; set; }
ICollection<string> Genres { get; set; }
IEnumerable<AudioMetaDataImage> Images { get; set; }
string ISRC { get; }
bool IsSoundTrack { get; }
bool IsValid { get; }
string LastFmId { get; set; }
string MusicBrainzId { get; set; }
string Release { get; set; }
string ReleaseLastFmId { get; set; }
string ReleaseMusicBrainzId { get; set; }
ulong? SampleLength { get; set; }
string SpecialTitle { get; set; }
string SpotifyId { get; set; }
TimeSpan? Time { get; set; }
string Title { get; set; }
int? TotalDiscCount { get; set; }
double TotalSeconds { get; }
int? TotalTrackNumbers { get; set; }
string TrackArtist { get; set; }
string TrackArtistRaw { get; set; }
IEnumerable<string> TrackArtists { get; }
short? TrackNumber { get; set; }
int ValidWeight { get; }
int? Year { get; set; }
bool Equals(object obj);
int GetHashCode();
void SetArtistName(string name);
string ToString();
}
}

View file

@ -91,6 +91,7 @@ namespace Roadie.Library.MetaData.ID3Tags
Album = metaData.Release,
Title = metaData.Title,
Year = metaData.Year.Value.ToString(),
Genre = metaData.Genres == null || !metaData.Genres.Any() ? null : string.Join("/", metaData.Genres),
TrackNumber = totalTrackNumber < 99 ? $"{trackNumber.ToString("00")}/{totalTrackNumber.ToString("00")}" : $"{trackNumber.ToString()}/{totalTrackNumber.ToString()}",
DiscNumber = discCount < 99 ? $"{disc.ToString("00")}/{discCount.ToString("00")}" : $"{disc.ToString()}/{discCount.ToString()}"
};
@ -155,7 +156,7 @@ namespace Roadie.Library.MetaData.ID3Tags
result.ArtistRaw = theTrack.AlbumArtist ?? theTrack.Artist;
result.Genres = theTrack.Genre?.Split(new char[] { ',', '\\' });
result.TrackArtist = theTrack.OriginalArtist ?? theTrack.Artist ?? theTrack.AlbumArtist;
result.TrackArtistRaw = theTrack.OriginalArtist;
result.TrackArtistRaw = theTrack.OriginalArtist ?? theTrack.Artist ?? theTrack.AlbumArtist;
result.AudioBitrate = (int?)theTrack.Bitrate;
result.AudioSampleRate = (int)theTrack.Bitrate;
result.Disk = theTrack.DiscNumber;
@ -204,18 +205,19 @@ namespace Roadie.Library.MetaData.ID3Tags
IAudioFile audioFile = AudioFile.Create(fileName, true);
if (ID3v2Tag.DoesTagExist(fileName))
{
IID3v2Tag id3v2 = new ID3v2Tag(fileName);
result.Release = id3v2.Album;
IID3v2Tag id3v2 = new ID3v2Tag(fileName);
result.Artist = id3v2.AlbumArtist ?? id3v2.Artist;
result.ArtistRaw = id3v2.AlbumArtist ?? id3v2.Artist;
result.Genres = id3v2.Genre?.Split(new char[] { ',', '\\', ';', '|' });
result.TrackArtist = id3v2.OriginalArtist ?? id3v2.Artist ?? id3v2.AlbumArtist;
result.TrackArtistRaw = id3v2.OriginalArtist;
result.AudioBitrate = (int?)audioFile.Bitrate;
result.AudioChannels = audioFile.Channels;
result.AudioSampleRate = (int)audioFile.Bitrate;
result.Comments = id3v2.CommentsList != null ? string.Join("|", id3v2.CommentsList?.Select(x => x.Value)) : null;
result.Disk = ID3TagsHelper.ParseDiscNumber(id3v2.DiscNumber);
result.DiskSubTitle = id3v2.SetSubtitle;
result.Genres = id3v2.Genre?.Split(new char[] { ',', '\\', ';', '|' });
result.Release = id3v2.Album;
result.TrackArtist = id3v2.OriginalArtist ?? id3v2.Artist ?? id3v2.AlbumArtist;
result.TrackArtistRaw = id3v2.OriginalArtist ?? id3v2.Artist ?? id3v2.AlbumArtist;
result.Images = id3v2.PictureList?.Select(x => new AudioMetaDataImage
{
Data = x.PictureData,
@ -266,6 +268,16 @@ namespace Roadie.Library.MetaData.ID3Tags
};
}
public static short? DetermineTrackNumber(string filename)
{
var part = filename.Substring(0, 2);
part = part.Replace(".", "");
part = part.Replace("-", "");
part = part.Replace(" ", "");
return SafeParser.ToNumber<short?>(part);
}
public static short? DetermineTotalTrackNumbers(string filename, string trackNumber = null)
{
short? result = null;

View file

@ -10,7 +10,7 @@ namespace Roadie.Library.MetaData
protected readonly IRoadieSettings _configuration = null;
protected readonly ILogger _logger = null;
protected ApiKey _apiKey = null;
protected IApiKey _apiKey = null;
public virtual bool IsEnabled
{
@ -20,7 +20,7 @@ namespace Roadie.Library.MetaData
}
}
protected ApiKey ApiKey
protected IApiKey ApiKey
{
get
{

View file

@ -28,6 +28,33 @@ namespace Roadie.Library.Utility
return directoryInfo.FullName;
}
public static void DeleteEmptyDirs(string dir)
{
if (String.IsNullOrEmpty(dir))
{
throw new ArgumentException("Starting directory is a null reference or an empty string", "dir");
}
try
{
foreach (var d in Directory.EnumerateDirectories(dir))
{
DeleteEmptyDirs(d);
}
var entries = Directory.EnumerateFileSystemEntries(dir);
if (!entries.Any())
{
try
{
Directory.Delete(dir);
}
catch (UnauthorizedAccessException) { }
catch (DirectoryNotFoundException) { }
}
}
catch (UnauthorizedAccessException) { }
}
/// <summary>
/// Delete any empty folders in the given folder
/// </summary>

View file

@ -91,7 +91,12 @@ namespace Roadie.Library.Utility
var i = input as string ?? input.ToString();
if (!string.IsNullOrEmpty(i))
{
if (Regex.IsMatch(i, @"([0-9]{4}).+([0-9]{4})"))
{
i = i.Substring(0, 4);
}
i = Regex.Replace(i, @"(\\)", "/");
i = Regex.Replace(i, @"(;)", "/");
i = Regex.Replace(i, @"(\/+)", "/");
i = Regex.Replace(i, @"(-+)", "/");
var parts = i.Contains("/") ? i.Split('/').ToList() : new List<string> { i };

View file

@ -290,7 +290,7 @@ namespace Roadie.Api.Services
return new Image($"{this.HttpContext.ImageBaseUrl }/{id}/{ width }/{ height }/{ (includeCachebuster ? DateTime.UtcNow.Ticks.ToString() : string.Empty) }", caption, $"{this.HttpContext.ImageBaseUrl }/{id}/{ this.Configuration.SmallImageSize.Width }/{ this.Configuration.SmallImageSize.Height }");
}
protected Image MakeImage(Guid id, string type, ImageSize imageSize)
protected Image MakeImage(Guid id, string type, IImageSize imageSize)
{
return this.MakeImage(id, type, imageSize.Width, imageSize.Height);
}