using Shadow.Data; using Shadow.Entities; using System; using System.IO; namespace Shadow.Tools; public class LibraryWatcher(string watchPath, string[] excludedPaths, ApplicationDbContext dbContext) { private readonly string libraryPath = watchPath; private readonly string[] excludedPaths = excludedPaths; private readonly ApplicationDbContext db = dbContext; private readonly GeneralUseHelpers guhf = new(); /// /// Returns a sorted list of paths to all files in a directory, recursively. /// /// Path to directory /// Sorted list of filepaths public async Task> GetFilesRecursivelyAsync(string directory) { string[] allowedExtensions = [".flac", ".m4a", ".mp3", ".ogg", ".wav"]; try { List files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories) .Where(file => allowedExtensions.Any(file.ToLower().EndsWith)) .ToList(); files.Sort(); return files; } catch (DirectoryNotFoundException) { Console.WriteLine($"[Error] Directory \"{directory}\" does not exist!\n" + " Please create it manually, or use `Shadow setupWizard`."); throw new DirectoryNotFoundException(); } } /// /// Return all multimedia content inside of library /// /// List of multimedia filepaths public async Task> GetAllMultimediaAsync() { // List files in cache // Note: currently, the only excluded path from scanning is the thumbnail cache. // This might change in the future. List cacheFiles = await GetFilesRecursivelyAsync(excludedPaths[0]); // List files in library excluding cache List libraryContent = await GetFilesRecursivelyAsync(libraryPath); List libraryMultimedia = libraryContent.Except(cacheFiles).ToList(); return libraryMultimedia; } /// /// Scan the library in its entirety /// /// public async Task> PerformFullScanAsync() { Console.WriteLine("Performing full library scan..."); List multimedia = await GetAllMultimediaAsync(); foreach (string filepath in multimedia) { Console.WriteLine(filepath); Dictionary fileInfo = await MetadataExtractor.ExtractAsync(filepath); // Pretend we are doing parsing here... Console.WriteLine(GeneralUseHelpers.DictAsJson(fileInfo)); MediaParser.CreateSong(db, fileInfo); } Console.WriteLine($"Full scan complete! Processed {multimedia.Count} files."); // Update state inside of DB string currentLibraryState = MetadataExtractor.GetStringMD5(string.Join("\n", multimedia)); Global lastLibraryState = db.Globals.FirstOrDefault(g => g.Key == "libraryState") ?? new() { Key = "libraryState"}; lastLibraryState.Value = currentLibraryState; db.Update(lastLibraryState); await db.SaveChangesAsync(); return multimedia; } }