From e8f61da6f8c54ecb601d8d03caa1a2a0e7706ad0 Mon Sep 17 00:00:00 2001 From: Brian Bicknell Date: Thu, 27 Feb 2025 21:58:27 -0500 Subject: [PATCH] Added initial database audio library class logic. --- Harmonia.Core/Library/IAudioLibrary.cs | 156 +++++++++++++++++++-- Harmonia.Core/Scanner/AudioFileScanner.cs | 34 ++++- Harmonia.Core/Scanner/IAudioFileScanner.cs | 1 + 3 files changed, 178 insertions(+), 13 deletions(-) diff --git a/Harmonia.Core/Library/IAudioLibrary.cs b/Harmonia.Core/Library/IAudioLibrary.cs index f351cf0..6aced9d 100644 --- a/Harmonia.Core/Library/IAudioLibrary.cs +++ b/Harmonia.Core/Library/IAudioLibrary.cs @@ -1,4 +1,8 @@ -namespace Harmonia.Core.Library; +using Harmonia.Core.Scanner; +using Microsoft.EntityFrameworkCore; +using SongModel = Harmonia.Core.Models.Song; + +namespace Harmonia.Core.Library; public interface IAudioLibrary { @@ -14,24 +18,21 @@ public interface IAudioLibrary event EventHandler? ScanFinished; } -public class AudioLibrary : IAudioLibrary +public abstract class AudioLibrary : IAudioLibrary { - private readonly List _locations = []; - public event EventHandler? LocationAdded; public event EventHandler? LocationRemoved; public event EventHandler? ScanStarted; public event EventHandler? ScanFinished; + protected abstract bool IncludeLocation(string path); + protected abstract void ScanLocation(string path); + public void AddLocation(string path) { - if (_locations.Contains(path)) + if (IncludeLocation(path) == false) return; - // TODO: CHeck for subpaths - - _locations.Add(path); - LocationAdded?.Invoke(this, new EventArgs()); } @@ -44,8 +45,8 @@ public class AudioLibrary : IAudioLibrary { foreach (string path in paths) { - if (_locations.Contains(path) == false) - continue; + //if (_locations.Contains(path) == false) + // continue; } @@ -64,4 +65,137 @@ public class AudioLibrary : IAudioLibrary ScanFinished?.Invoke(this, new EventArgs()); } +} + +public class AudioDatabaseLibrary(IAudioFileScanner audioFileScanner, AudioLibraryContext context) : AudioLibrary +{ + protected override bool IncludeLocation(string path) + { + if (CanAddLocation(path) == false) + return false; + + Location location = GetLocation(path) ?? CreateLocation(path); + ScanLocation(location); + + return true; + } + + private bool CanAddLocation(string path) + { + return context.Folders.Any(folder => folder.Name == path) == false; + } + + private Location CreateLocation(string path) + { + Location location = new() + { + Name = path, + Type = 0 + }; + + context.Locations.Add(location); + context.SaveChanges(); + + return location; + } + + private Location? GetLocation(string path) + { + return context.Locations.FirstOrDefault(location => location.Name == path); + } + + protected override void ScanLocation(string path) + { + Location? location = GetLocation(path); + + if (location == null) + return; + + ScanLocation(location); + } + + private void ScanLocation(Location location) + { + SongModel[] songModels = audioFileScanner.GetSongsFromPath(location.Name); + + AddFolders(location, songModels); + AddSongs(songModels); + + // Remove old songs, and clear out folders that no longer hold songs + } + + private void AddFolders(Location location, SongModel[] songModels) + { + string[] folderNames = [.. songModels.Select(songModel => Directory.GetParent(songModel.FileName)?.FullName ?? string.Empty) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Distinct()]; + + Folder[] folders = [.. context.Folders.Where(folder => folder.Location == location)]; + } + + private void AddSongs(SongModel[] songModels) + { + foreach (SongModel songModel in songModels) + { + Folder? folder = context.Folders.FirstOrDefault(x => x.Name == songModel.FileDirectory); + + if (folder == null) + continue; + + Song song = AddOrUpdateSong(songModel, folder); + AddOrUpdateSongTag(songModel, song); + } + } + + private Song AddOrUpdateSong(SongModel songModel, Folder folder) + { + string shortFileName = Path.GetFileName(songModel.FileName); + + var song = context.Songs.FirstOrDefault(x => x.Folder == folder && string.Equals(x.FileName, shortFileName, StringComparison.OrdinalIgnoreCase)); + + if (song == null) + { + song = new() + { + FileName = shortFileName, + Folder = folder + }; + + context.Songs.Add(song); + } + + song.Size = (long)songModel.Size; + song.Duration = songModel.Length.Ticks; + song.Modified = songModel.LastModified; + song.BitRate = (int)songModel.BitRate; + song.SampleRate = (int)songModel.SampleRate; + + return song; + } + + private SongTag AddOrUpdateSongTag(SongModel songModel, Song song) + { + var songTag = context.SongTags.FirstOrDefault(x => x.Song == song); + + if (songTag == null) + { + songTag = new SongTag() + { + Song = song + }; + + context.SongTags.Add(songTag); + } + + songTag.Title = songModel.Title; + songTag.Album = songModel.Album; + songTag.Artist = string.Join(";", songModel.Artists); + songTag.AlbumArtist = string.Join(";", songModel.AlbumArtists); + songTag.TrackNumber = songModel.TrackNumber; + songTag.DiscNumber = songModel.DiscNumber; + songTag.Genre = songModel.Genre; + songTag.ReleaseDate = songModel.Year != null ? new DateTime(songModel.Year.Value, 1, 1) : null; + + return songTag; + } } \ No newline at end of file diff --git a/Harmonia.Core/Scanner/AudioFileScanner.cs b/Harmonia.Core/Scanner/AudioFileScanner.cs index 55f918c..23521e7 100644 --- a/Harmonia.Core/Scanner/AudioFileScanner.cs +++ b/Harmonia.Core/Scanner/AudioFileScanner.cs @@ -1,10 +1,11 @@ -using Harmonia.Core.Imaging; +using Harmonia.Core.Engine; +using Harmonia.Core.Imaging; using Harmonia.Core.Models; using Harmonia.Core.Tags; namespace Harmonia.Core.Scanner; -public class AudioFileScanner(ITagResolver tagResolver, IAudioImageExtractor audioImageExtractor) : IAudioFileScanner +public class AudioFileScanner(IAudioEngine audioEngine, ITagResolver tagResolver, IAudioImageExtractor audioImageExtractor) : IAudioFileScanner { public Song[] GetSongs(string[] fileNames) { @@ -54,4 +55,33 @@ public class AudioFileScanner(ITagResolver tagResolver, IAudioImageExtractor aud return song; } + + public Song[] GetSongsFromPath(string path) + { + FileInfo[] fileInfoList = GetAllFilesFromDirectory(path); + string[] fileNames = [.. fileInfoList.Select(x => x.FullName)]; + + return GetSongs(fileNames); + } + + private FileInfo[] GetAllFilesFromDirectory(string directoryName) + { + DirectoryInfo directoryInfo = new(directoryName); + + if (directoryInfo.Exists == false) + return []; + + List fileInfoList = []; + + var directories = directoryInfo.GetDirectories().Where(x => x.Attributes.HasFlag(FileAttributes.Hidden) == false); + + var extensions = audioEngine.SupportedFormats.Select(x => x.Replace("*.", ".").ToLower()).ToList(); + var files = directoryInfo.EnumerateFiles("*.*", SearchOption.AllDirectories).Where(x => x.Attributes.HasFlag(FileAttributes.Hidden) == false); + var songFiles = files.Where(x => extensions.Contains(x.Extension.ToLower())).OrderBy(x => x.Name); + + foreach (FileInfo fileInfo in songFiles) + fileInfoList.Add(fileInfo); + + return [.. fileInfoList]; + } } \ No newline at end of file diff --git a/Harmonia.Core/Scanner/IAudioFileScanner.cs b/Harmonia.Core/Scanner/IAudioFileScanner.cs index 0d21678..305a092 100644 --- a/Harmonia.Core/Scanner/IAudioFileScanner.cs +++ b/Harmonia.Core/Scanner/IAudioFileScanner.cs @@ -5,4 +5,5 @@ namespace Harmonia.Core.Scanner; public interface IAudioFileScanner { Song[] GetSongs(string[] fileNames); + Song[] GetSongsFromPath(string path); } \ No newline at end of file