diff --git a/Roadie.Api.Library.Tests/HashHelperTests.cs b/Roadie.Api.Library.Tests/HashHelperTests.cs new file mode 100644 index 0000000..56169a7 --- /dev/null +++ b/Roadie.Api.Library.Tests/HashHelperTests.cs @@ -0,0 +1,82 @@ +using Roadie.Library.Utility; +using System; +using Xunit; + +namespace Roadie.Library.Tests +{ + public class HashHelperTests + { + [Fact] + public void MD5HandleNullString() + { + string s = null; + Assert.Null(HashHelper.CreateMD5(s)); + } + + [Fact] + public void MD5HandleNullArray() + { + byte[] b = null; + Assert.Null(HashHelper.CreateMD5(b)); + } + + [Fact] + public void CreateAndCompareMd5BlankString() + { + var s = ""; + var md51 = HashHelper.CreateMD5(s); + var md52 = HashHelper.CreateMD5(s); + Assert.Equal(md51, md52); + } + + [Fact] + public void CreateAndCompareMd5String() + { + var s = "This is a test"; + var md51 = HashHelper.CreateMD5(s); + var md52 = HashHelper.CreateMD5(s); + Assert.Equal(md51, md52); + } + + [Fact] + public void CreateAndCompareMd5Bytes() + { + var sb = System.Text.Encoding.UTF8.GetBytes("This is a test"); + var md51 = HashHelper.CreateMD5(sb); + var md52 = HashHelper.CreateMD5(sb); + Assert.Equal(md51, md52); + } + + [Fact] + public void CreateAndCompareMd5BytesToString() + { + var s = "This is a test"; + var sb = System.Text.Encoding.UTF8.GetBytes("This is a test"); + var md51 = HashHelper.CreateMD5(sb); + var md52 = HashHelper.CreateMD5(s); + Assert.Equal(md51, md52); + } + + [Fact] + public void CreateAndCompareMd5LongString() + { + var s = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; + var md51 = HashHelper.CreateMD5(s); + var md52 = HashHelper.CreateMD5(s); + Assert.Equal(md51, md52); + Assert.Equal("01aad0e51fcd5582b307613842e4ffe5", md51.ToLower()); + } + + + [Fact] + public void CreateAndEnsureStandardMd5() + { + var s = "This is a test"; + var md5 = HashHelper.CreateMD5(s); + // From https://www.md5hashgenerator.com/ + // From http://onlinemd5.com/ + // From https://md5.online/ + Assert.Equal("ce114e4501d2f4e2dcea3e17b546f339", md5.ToLower()); + } + } +} diff --git a/Roadie.Api.Library/Data/ArtistPartial.cs b/Roadie.Api.Library/Data/ArtistPartial.cs index dc5295d..d039fa7 100644 --- a/Roadie.Api.Library/Data/ArtistPartial.cs +++ b/Roadie.Api.Library/Data/ArtistPartial.cs @@ -16,13 +16,7 @@ namespace Roadie.Library.Data { get { - using (var md5 = MD5.Create()) - { - return string.Concat(md5 - .ComputeHash( - System.Text.Encoding.Default.GetBytes(string.Format("{0}{1}", RoadieId, LastUpdated))) - .Select(x => x.ToString("D2"))); - } + return HashHelper.CreateMD5($"{ RoadieId }{ LastUpdated }"); } } diff --git a/Roadie.Api.Library/Data/LabelPartial.cs b/Roadie.Api.Library/Data/LabelPartial.cs index 1bdf4a9..c26fca0 100644 --- a/Roadie.Api.Library/Data/LabelPartial.cs +++ b/Roadie.Api.Library/Data/LabelPartial.cs @@ -1,5 +1,6 @@ using Roadie.Library.Configuration; using Roadie.Library.Extensions; +using Roadie.Library.Utility; using System; using System.Diagnostics; using System.IO; @@ -18,13 +19,7 @@ namespace Roadie.Library.Data { get { - using (var md5 = MD5.Create()) - { - return string.Concat(md5 - .ComputeHash( - System.Text.Encoding.Default.GetBytes(string.Format("{0}{1}", RoadieId, LastUpdated))) - .Select(x => x.ToString("D2"))); - } + return HashHelper.CreateMD5($"{ RoadieId }{ LastUpdated }"); } } diff --git a/Roadie.Api.Library/Data/ReleasePartial.cs b/Roadie.Api.Library/Data/ReleasePartial.cs index df3032d..9232f49 100644 --- a/Roadie.Api.Library/Data/ReleasePartial.cs +++ b/Roadie.Api.Library/Data/ReleasePartial.cs @@ -17,13 +17,7 @@ namespace Roadie.Library.Data { get { - using (var md5 = MD5.Create()) - { - return string.Concat(md5 - .ComputeHash( - System.Text.Encoding.Default.GetBytes(string.Format("{0}{1}", RoadieId, LastUpdated))) - .Select(x => x.ToString("D2"))); - } + return HashHelper.CreateMD5($"{ RoadieId }{ LastUpdated }"); } } diff --git a/Roadie.Api.Library/Data/TrackPartial.cs b/Roadie.Api.Library/Data/TrackPartial.cs index f912282..70d391f 100644 --- a/Roadie.Api.Library/Data/TrackPartial.cs +++ b/Roadie.Api.Library/Data/TrackPartial.cs @@ -19,17 +19,23 @@ namespace Roadie.Library.Data { get { - using (var md5 = MD5.Create()) - { - return string.Concat(md5 - .ComputeHash( - System.Text.Encoding.Default.GetBytes(string.Format("{0}{1}", RoadieId, LastUpdated))) - .Select(x => x.ToString("D2"))); - } + return HashHelper.CreateMD5($"{ RoadieId}{ LastUpdated}"); } } - public bool IsValid => !string.IsNullOrEmpty(Hash); + /// + /// Are the track details valid so the track can play. + /// + public bool IsValid + { + get + { + return !string.IsNullOrEmpty(Hash) && + !string.IsNullOrEmpty(FileName) && + FileSize.HasValue && + !string.IsNullOrEmpty(FilePath); + } + } public Artist TrackArtist { get; set; } diff --git a/Roadie.Api.Library/Extensions/ByteExt.cs b/Roadie.Api.Library/Extensions/ByteExt.cs deleted file mode 100644 index 1d24d49..0000000 --- a/Roadie.Api.Library/Extensions/ByteExt.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Roadie.Library.Extensions -{ - public static class ByteExt - { - //public static int ComputeHash(this byte[] data) - //{ - // if (data == null || data.Length == 0) return 0; - // unchecked - // { - // const int p = 16777619; - // var hash = (int)2166136261; - - // for (var i = 0; i < data.Length; i++) hash = (hash ^ data[i]) * p; - // hash += hash << 13; - // hash ^= hash >> 7; - // hash += hash << 3; - // hash ^= hash >> 17; - // hash += hash << 5; - // return hash; - // } - //} - } -} \ No newline at end of file diff --git a/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs b/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs index 07c99fa..5719156 100644 --- a/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs +++ b/Roadie.Api.Library/SearchEngines/MetaData/LastFm/LastFmHelper.cs @@ -68,21 +68,6 @@ namespace Roadie.Library.MetaData.LastFm SelectSingleNode(navigator, LastFmErrorXPath)), webException); } - // http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx - // Hash an input string and return the hash as - // a 32 character hexadecimal string. - public static string Hash(string input) - { - // Create a new instance of the MD5CryptoServiceProvider object. - using (var md5Hasher = MD5.Create()) - { - var data = md5Hasher.ComputeHash(System.Text.Encoding.ASCII.GetBytes(input)); - var sb = new StringBuilder(); - foreach (var b in data) sb.Append(b.ToString("X2")); - return sb.ToString(); - } - } - public static XPathNavigator SelectSingleNode(XPathNavigator navigator, string xpath) { var node = navigator.SelectSingleNode(xpath); @@ -474,8 +459,7 @@ namespace Roadie.Library.MetaData.LastFm return builder.ToString(); } - private string GenerateMethodSignature(string method, IDictionary parameters = null, - string sk = null) + private string GenerateMethodSignature(string method, IDictionary parameters = null, string sk = null) { if (parameters == null) parameters = new Dictionary(); if (!parameters.ContainsKey("method")) parameters.Add("method", method); @@ -483,9 +467,11 @@ namespace Roadie.Library.MetaData.LastFm if (!string.IsNullOrEmpty(sk) && !parameters.ContainsKey("sk")) parameters.Add("sk", sk); var builder = new StringBuilder(); foreach (var kv in parameters.OrderBy(kv => kv.Key, StringComparer.Ordinal)) + { builder.Append($"{kv.Key}{kv.Value}"); + } builder.Append(_apiKey.KeySecret); - return Hash(builder.ToString()); + return HashHelper.CreateMD5(builder.ToString()); } } } \ No newline at end of file diff --git a/Roadie.Api.Library/Utility/FolderPathHelper.cs b/Roadie.Api.Library/Utility/FolderPathHelper.cs index c4c07f6..08acac7 100644 --- a/Roadie.Api.Library/Utility/FolderPathHelper.cs +++ b/Roadie.Api.Library/Utility/FolderPathHelper.cs @@ -118,7 +118,10 @@ namespace Roadie.Library.Utility /// Optional Root folder defaults to Library Folder from Settings public static string PathForTrack(IRoadieSettings configuration, Track track) { - if (string.IsNullOrEmpty(track.FilePath) || string.IsNullOrEmpty(track.FileName)) return null; + if (string.IsNullOrEmpty(track.FilePath) || string.IsNullOrEmpty(track.FileName)) + { + return null; + } var directoryInfo = new DirectoryInfo(Path.Combine(configuration.LibraryFolder, track.FilePath, track.FileName)); return directoryInfo.FullName; } diff --git a/Roadie.Api.Library/Utility/HashHelper.cs b/Roadie.Api.Library/Utility/HashHelper.cs index 3c547f7..37af0af 100644 --- a/Roadie.Api.Library/Utility/HashHelper.cs +++ b/Roadie.Api.Library/Utility/HashHelper.cs @@ -8,27 +8,34 @@ namespace Roadie.Library.Utility { public static string CreateMD5(string input) { - if (string.IsNullOrEmpty(input)) return null; - return CreateMD5(System.Text.Encoding.ASCII.GetBytes(input)); + if (string.IsNullOrEmpty(input)) + { + return null; + } + return CreateMD5(System.Text.Encoding.UTF8.GetBytes(input)); } public static string CreateMD5(byte[] bytes) { - if (bytes == null || !bytes.Any()) return null; + if (bytes == null || !bytes.Any()) + { + return null; + } using (var md5 = MD5.Create()) { - return System.Text.Encoding.ASCII.GetString(md5.ComputeHash(bytes)); + byte[] data = md5.ComputeHash(bytes); + + // Create a new Stringbuilder to collect the bytes and create a string. + StringBuilder sBuilder = new StringBuilder(); + + // Loop through each byte of the hashed data and format each one as a hexadecimal string. + for (int i = 0; i < data.Length; i++) + { + sBuilder.Append(data[i].ToString("x2")); + } + // Return the hexadecimal string. + return sBuilder.ToString(); } } - - public static string MD5Hash(string input) - { - var hash = new StringBuilder(); - var md5provider = new MD5CryptoServiceProvider(); - var bytes = md5provider.ComputeHash(new UTF8Encoding().GetBytes(input)); - - for (var i = 0; i < bytes.Length; i++) hash.Append(bytes[i].ToString("x2")); - return hash.ToString(); - } } } \ No newline at end of file diff --git a/Roadie.Api.Services/AdminService.cs b/Roadie.Api.Services/AdminService.cs index af4aec3..1628532 100644 --- a/Roadie.Api.Services/AdminService.cs +++ b/Roadie.Api.Services/AdminService.cs @@ -934,8 +934,7 @@ namespace Roadie.Api.Services return await ScanFolder(user, d, dest, isReadOnly); } - public async Task> ScanRelease(ApplicationUser user, Guid releaseId, - bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) + public async Task> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false) { var sw = new Stopwatch(); sw.Start(); @@ -954,8 +953,6 @@ namespace Roadie.Api.Services try { var result = await ReleaseService.ScanReleaseFolder(user, release.RoadieId, isReadOnly, release); - await UpdateReleaseRank(release.Id); - CacheManager.ClearRegion(release.CacheRegion); } catch (Exception ex) { diff --git a/Roadie.Api.Services/IAdminService.cs b/Roadie.Api.Services/IAdminService.cs index 099d536..d5c279b 100644 --- a/Roadie.Api.Services/IAdminService.cs +++ b/Roadie.Api.Services/IAdminService.cs @@ -13,8 +13,7 @@ namespace Roadie.Api.Services Task> DeleteArtist(ApplicationUser user, Guid artistId); - Task> DeleteArtistReleases(ApplicationUser user, Guid artistId, - bool doDeleteFiles = false); + Task> DeleteArtistReleases(ApplicationUser user, Guid artistId, bool doDeleteFiles = false); Task> DeleteArtistSecondaryImage(ApplicationUser user, Guid artistId, int index); @@ -32,22 +31,19 @@ namespace Roadie.Api.Services Task>>> MissingCollectionReleases(ApplicationUser user); - Task> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, - bool doPurgeFirst = false); + Task> ScanAllCollections(ApplicationUser user, bool isReadOnly = false, bool doPurgeFirst = false); Task> ScanArtist(ApplicationUser user, Guid artistId, bool isReadOnly = false); Task> ScanArtists(ApplicationUser user, IEnumerable artistIds, bool isReadOnly = false); - Task> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, - bool doPurgeFirst = false, bool doUpdateRanks = true); + Task> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true); Task> ScanInboundFolder(ApplicationUser user, bool isReadOnly = false); Task> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false); - Task> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, - bool wasDoneForInvalidTrackPlay = false); + Task> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false, bool wasDoneForInvalidTrackPlay = false); Task> ValidateInviteToken(Guid? tokenId); diff --git a/Roadie.Api.Services/ReleaseService.cs b/Roadie.Api.Services/ReleaseService.cs index bdadeae..eef8ee4 100644 --- a/Roadie.Api.Services/ReleaseService.cs +++ b/Roadie.Api.Services/ReleaseService.cs @@ -983,8 +983,7 @@ namespace Roadie.Api.Services /// public async Task> ScanReleaseFolder(ApplicationUser user, Guid releaseId, bool doJustInfo, data.Release releaseToScan = null) { - SimpleContract.Requires( - releaseId != Guid.Empty && releaseToScan == null || releaseToScan != null, "Invalid ReleaseId"); + SimpleContract.Requires(releaseId != Guid.Empty && releaseToScan == null || releaseToScan != null, "Invalid ReleaseId"); _addedTrackIds.Clear(); @@ -1012,8 +1011,9 @@ namespace Roadie.Api.Services releasePath = release.ReleaseFileFolder(release.Artist.ArtistFileFolder(Configuration)); var releaseDirectory = new DirectoryInfo(releasePath); if (!Directory.Exists(releasePath)) - Logger.LogWarning("Unable To Find Release Folder [{0}] For Release `{1}`", releasePath, - release.ToString()); + { + Logger.LogWarning("Unable To Find Release Folder [{0}] For Release `{1}`", releasePath, release.ToString()); + } var now = DateTime.UtcNow; #region Get Tracks for Release from DB and set as missing any not found in Folder @@ -1021,15 +1021,13 @@ namespace Roadie.Api.Services foreach (var releaseMedia in DbContext.ReleaseMedias.Where(x => x.ReleaseId == release.Id).ToArray()) { var foundMissingTracks = false; - foreach (var existingTrack in DbContext.Tracks.Where(x => x.ReleaseMediaId == releaseMedia.Id) - .ToArray()) + foreach (var existingTrack in DbContext.Tracks.Where(x => x.ReleaseMediaId == releaseMedia.Id).ToArray()) { var trackPath = existingTrack.PathToTrack(Configuration); if (!File.Exists(trackPath)) { - Logger.LogWarning("Track `{0}`, File [{1}] Not Found.", existingTrack.ToString(), - trackPath); + Logger.LogWarning("Track `{0}`, File [{1}] Not Found.", existingTrack.ToString(), trackPath); if (!doJustInfo) { existingTrack.UpdateTrackMissingFile(now); @@ -1040,21 +1038,24 @@ namespace Roadie.Api.Services } } - if (foundMissingTracks) await DbContext.SaveChangesAsync(); + if (foundMissingTracks) + { + await DbContext.SaveChangesAsync(); + } } #endregion Get Tracks for Release from DB and set as missing any not found in Folder #region Scan Folder and Add or Update Existing Tracks from Files - var existingReleaseMedia = DbContext.ReleaseMedias.Include(x => x.Tracks) - .Where(x => x.ReleaseId == release.Id).ToList(); + var existingReleaseMedia = DbContext.ReleaseMedias.Include(x => x.Tracks).Where(x => x.ReleaseId == release.Id).ToList(); var foundInFolderTracks = new List(); short totalNumberOfTracksFound = 0; // This is the number of tracks metadata says the release should have (releaseMediaNumber, TotalNumberOfTracks) var releaseMediaTotalNumberOfTracks = new Dictionary(); var releaseMediaTracksFound = new Dictionary(); if (Directory.Exists(releasePath)) + { foreach (var file in releaseDirectory.GetFiles("*.mp3", SearchOption.AllDirectories)) { int? trackArtistId = null; @@ -1065,21 +1066,19 @@ namespace Roadie.Api.Services if (audioMetaData.IsValid) { - var trackHash = HashHelper.CreateMD5( - release.ArtistId + file.LastWriteTimeUtc.GetHashCode().ToString() + - audioMetaData.GetHashCode()); + var trackHash = HashHelper.CreateMD5(release.ArtistId + file.LastWriteTimeUtc.GetHashCode().ToString() + audioMetaData.GetHashCode()); totalNumberOfTracksFound++; totalTrackCount = totalTrackCount ?? (short)(audioMetaData.TotalTrackNumbers ?? 0); var releaseMediaNumber = (short)(audioMetaData.Disc ?? 1); if (!releaseMediaTotalNumberOfTracks.ContainsKey(releaseMediaNumber)) - releaseMediaTotalNumberOfTracks.Add(releaseMediaNumber, - (short)(audioMetaData.TotalTrackNumbers ?? 0)); + { + releaseMediaTotalNumberOfTracks.Add(releaseMediaNumber, (short)(audioMetaData.TotalTrackNumbers ?? 0)); + } else - releaseMediaTotalNumberOfTracks[releaseMediaNumber] = - releaseMediaTotalNumberOfTracks[releaseMediaNumber] - .TakeLarger((short)(audioMetaData.TotalTrackNumbers ?? 0)); - var releaseMedia = - existingReleaseMedia.FirstOrDefault(x => x.MediaNumber == releaseMediaNumber); + { + releaseMediaTotalNumberOfTracks[releaseMediaNumber] = releaseMediaTotalNumberOfTracks[releaseMediaNumber].TakeLarger((short)(audioMetaData.TotalTrackNumbers ?? 0)); + } + var releaseMedia = existingReleaseMedia.FirstOrDefault(x => x.MediaNumber == releaseMediaNumber); if (releaseMedia == null) { // New ReleaseMedia - Not Found In Database @@ -1100,8 +1099,7 @@ namespace Roadie.Api.Services releaseMedia.LastUpdated = now; } - var track = releaseMedia.Tracks.FirstOrDefault(x => - x.TrackNumber == audioMetaData.TrackNumber); + var track = releaseMedia.Tracks.FirstOrDefault(x => x.TrackNumber == audioMetaData.TrackNumber); if (track == null) { // New Track - Not Found In Database @@ -1129,16 +1127,15 @@ namespace Roadie.Api.Services { if (audioMetaData.TrackArtists.Count() == 1) { - var trackArtistData = - await ArtistLookupEngine.GetByName( - new AudioMetaData { Artist = audioMetaData.TrackArtist }, true); + var trackArtistData = await ArtistLookupEngine.GetByName(new AudioMetaData { Artist = audioMetaData.TrackArtist }, true); if (trackArtistData.IsSuccess && release.ArtistId != trackArtistData.Data.Id) + { trackArtistId = trackArtistData.Data.Id; + } } else if (audioMetaData.TrackArtists.Any()) { - partTitles = string.Join(AudioMetaData.ArtistSplitCharacter.ToString(), - audioMetaData.TrackArtists); + partTitles = string.Join(AudioMetaData.ArtistSplitCharacter.ToString(), audioMetaData.TrackArtists); } else { @@ -1147,8 +1144,7 @@ namespace Roadie.Api.Services } var alt = track.Title.ToAlphanumericName(); - track.AlternateNames = - !alt.Equals(audioMetaData.Title, StringComparison.OrdinalIgnoreCase) + track.AlternateNames = !alt.Equals(audioMetaData.Title, StringComparison.OrdinalIgnoreCase) ? track.AlternateNames.AddToDelimitedList(new[] { alt }) : null; track.ArtistId = trackArtistId; @@ -1164,16 +1160,15 @@ namespace Roadie.Api.Services { if (audioMetaData.TrackArtists.Count() == 1) { - var trackArtistData = - await ArtistLookupEngine.GetByName( - new AudioMetaData { Artist = audioMetaData.TrackArtist }, true); + var trackArtistData = await ArtistLookupEngine.GetByName( new AudioMetaData { Artist = audioMetaData.TrackArtist }, true); if (trackArtistData.IsSuccess && release.ArtistId != trackArtistData.Data.Id) + { trackArtistId = trackArtistData.Data.Id; + } } else if (audioMetaData.TrackArtists.Any()) { - partTitles = string.Join(AudioMetaData.ArtistSplitCharacter.ToString(), - audioMetaData.TrackArtists); + partTitles = string.Join(AudioMetaData.ArtistSplitCharacter.ToString(), audioMetaData.TrackArtists); } else { @@ -1196,7 +1191,9 @@ namespace Roadie.Api.Services track.LastUpdated = now; var alt = track.Title.ToAlphanumericName(); if (!alt.Equals(track.Title, StringComparison.OrdinalIgnoreCase)) + { track.AlternateNames = track.AlternateNames.AddToDelimitedList(new[] { alt }); + } track.TrackNumber = audioMetaData.TrackNumber ?? -1; track.LastUpdated = now; modifiedRelease = true; @@ -1210,20 +1207,24 @@ namespace Roadie.Api.Services foundInFolderTracks.Add(track); if (releaseMediaTracksFound.ContainsKey(releaseMedia.Id)) + { releaseMediaTracksFound[releaseMedia.Id]++; + } else + { releaseMediaTracksFound[releaseMedia.Id] = 1; + } } else { - Logger.LogWarning("Release Track File Has Invalid MetaData `{0}`", - audioMetaData.ToString()); + Logger.LogWarning("Release Track File Has Invalid MetaData `{0}`", audioMetaData.ToString()); } } + } else - Logger.LogWarning("Unable To Find Releaes Path [{0}] For Release `{1}`", releasePath, - release.ToString()); - + { + Logger.LogWarning("Unable To Find Releaes Path [{0}] For Release `{1}`", releasePath, release.ToString()); + } var releaseMediaNumbersFound = new List(); foreach (var kp in releaseMediaTracksFound) { @@ -1231,38 +1232,38 @@ namespace Roadie.Api.Services if (releaseMedia != null) { if (!releaseMediaNumbersFound.Any(x => x == releaseMedia.MediaNumber)) + { releaseMediaNumbersFound.Add(releaseMedia.MediaNumber); + } var releaseMediaFoundInFolderTrackNumbers = foundInFolderTracks .Where(x => x.ReleaseMediaId == releaseMedia.Id).Select(x => x.TrackNumber).OrderBy(x => x) .ToArray(); - var areTracksForRelaseMediaSequential = releaseMediaFoundInFolderTrackNumbers - .Zip(releaseMediaFoundInFolderTrackNumbers.Skip(1), (a, b) => a + 1 == b).All(x => x); + var areTracksForRelaseMediaSequential = releaseMediaFoundInFolderTrackNumbers.Zip(releaseMediaFoundInFolderTrackNumbers.Skip(1), (a, b) => a + 1 == b).All(x => x); if (!areTracksForRelaseMediaSequential) + { Logger.LogDebug("ReleaseMedia [{0}] Track Numbers Are Not Sequential", releaseMedia.Id); + } releaseMedia.TrackCount = kp.Value; releaseMedia.LastUpdated = now; releaseMedia.Status = areTracksForRelaseMediaSequential ? Statuses.Ok : Statuses.Incomplete; await DbContext.SaveChangesAsync(); modifiedRelease = true; } - - ; } - var foundInFolderTrackNumbers = - foundInFolderTracks.Select(x => x.TrackNumber).OrderBy(x => x).ToArray(); + var foundInFolderTrackNumbers = foundInFolderTracks.Select(x => x.TrackNumber).OrderBy(x => x).ToArray(); if (modifiedRelease || !foundInFolderTrackNumbers.Count().Equals(release.TrackCount) || releaseMediaNumbersFound.Count() != (release.MediaCount ?? 0)) { - var areTracksForRelaseSequential = foundInFolderTrackNumbers - .Zip(foundInFolderTrackNumbers.Skip(1), (a, b) => a + 1 == b).All(x => x); - var maxFoundInFolderTrackNumbers = - foundInFolderTrackNumbers.Any() ? foundInFolderTrackNumbers.Max() : (short)0; + var areTracksForRelaseSequential = foundInFolderTrackNumbers.Zip(foundInFolderTrackNumbers.Skip(1), (a, b) => a + 1 == b).All(x => x); + var maxFoundInFolderTrackNumbers = foundInFolderTrackNumbers.Any() ? foundInFolderTrackNumbers.Max() : (short)0; release.Status = areTracksForRelaseSequential ? Statuses.Ok : Statuses.Incomplete; release.TrackCount = (short)foundInFolderTrackNumbers.Count(); release.MediaCount = (short)releaseMediaNumbersFound.Count(); if (release.TrackCount < maxFoundInFolderTrackNumbers) + { release.TrackCount = maxFoundInFolderTrackNumbers; + } release.LibraryStatus = release.TrackCount > 0 && release.TrackCount == totalNumberOfTracksFound ? LibraryStatus.Complete : LibraryStatus.Incomplete; @@ -1299,12 +1300,16 @@ namespace Roadie.Api.Services await UpdateReleaseCounts(release.Id, now); await UpdateArtistCountsForRelease(release.Id, now); - if (release.Labels != null && release.Labels.Any()) - foreach (var label in release.Labels) - await UpdateLabelCounts(label.Id, now); + await UpdateReleaseRank(release.Id); - Logger.LogInformation("Scanned Release `{0}` Folder [{1}], Modified Release [{2}], OperationTime [{3}]", - release.ToString(), releasePath, modifiedRelease, sw.ElapsedMilliseconds); + if (release.Labels != null && release.Labels.Any()) + { + foreach (var label in release.Labels) + { + await UpdateLabelCounts(label.Id, now); + } + } + Logger.LogInformation("Scanned Release `{0}` Folder [{1}], Modified Release [{2}], OperationTime [{3}]", release.ToString(), releasePath, modifiedRelease, sw.ElapsedMilliseconds); result = true; } catch (Exception ex) diff --git a/Roadie.Api.Services/SubsonicService.cs b/Roadie.Api.Services/SubsonicService.cs index dcf0915..54c2071 100644 --- a/Roadie.Api.Services/SubsonicService.cs +++ b/Roadie.Api.Services/SubsonicService.cs @@ -135,7 +135,7 @@ namespace Roadie.Api.Services if (!string.IsNullOrEmpty(request.s)) try { - var token = HashHelper.MD5Hash((user.ApiToken ?? user.Email) + request.s); + var token = HashHelper.CreateMD5((user.ApiToken ?? user.Email) + request.s); if (!token.Equals(request.t, StringComparison.OrdinalIgnoreCase)) user = null; } catch diff --git a/Roadie.Api.Services/TrackService.cs b/Roadie.Api.Services/TrackService.cs index f1f3cbb..86bac0f 100644 --- a/Roadie.Api.Services/TrackService.cs +++ b/Roadie.Api.Services/TrackService.cs @@ -657,7 +657,7 @@ namespace Roadie.Api.Services long endBytes, User roadieUser) { var track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); - if (track == null) + if (!(track?.IsValid ?? true)) { // Not Found try recanning release var release = (from r in DbContext.Releases @@ -670,21 +670,21 @@ namespace Roadie.Api.Services { Id = roadieUser.Id.Value }, release.RoadieId, false, true); + track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); + } + else + { + Logger.LogWarning($"TrackStreamInfo: Track [{ trackId }] was invalid but release [{ release.RoadieId }] is locked, did not rescan."); } - - track = DbContext.Tracks.FirstOrDefault(x => x.RoadieId == trackId); if (track == null) { return new OperationResult($"TrackStreamInfo: Unable To Find Track [{trackId}]"); } + if (!track.IsValid) + { + return new OperationResult($"TrackStreamInfo: Invalid Track. Track Id [{trackId}], FilePath [{track.FilePath}], Filename [{track.FileName}]"); + } } - - if (!track.IsValid) - { - return new OperationResult( - $"TrackStreamInfo: Invalid Track. Track Id [{trackId}], FilePath [{track.FilePath}], Filename [{track.FileName}]"); - } - string trackPath = null; try {