private bool ValidateMusicFileSet(MusicDb db, List <MusicFile> files) { // 0. make sure there are some files if (!(files.Count > 0)) { log.Warning($"ValidateMusicFileSet(): file set is empty"); return(false); } // 1. make sure that all files are in the same original folder and in the same style if (!files.All(f => f.Style == files[0].Style && f.DiskRoot == files[0].DiskRoot && f.StylePath == files[0].StylePath && f.OpusPath == files[0].OpusPath)) { log.Warning($"ValidateMusicFileSet(): all files are not from the same original folder"); return(false); } // 2. make sure all files are of the same opustype, i.e. either in a collection or not in a collection, or singles if (!files.All(f => f.OpusType == files[0].OpusType)) { log.Warning($"ValidateMusicFileSet(): all files are not of the same opus type"); return(false); } foreach (var file in files) { if (!db.ValidateTags(file)) { return(false); } } return(true); }
private async Task UpdateTagsAsync(MusicDb db, List <MusicFile> files /*, bool force*/) { foreach (var musicFile in files) { await db.UpdateTagsAsync(musicFile /*, force*/); } }
private void TryAddTaskAgain(long itemId) { using (var db = new MusicDb(connectionString)) { var taskItem = db.TaskItems.Find(itemId); var maxRetries = options.MaxTaskRetries; bool requeue = false; if (taskItem.RetryCount < maxRetries) { taskItem.Status = Music.Core.TaskStatus.Pending; taskItem.ScheduledAt = DateTimeOffset.Now; taskItem.RetryCount++; requeue = true; log.Information($"{taskItem} requeued, retry {taskItem.RetryCount} of {maxRetries}"); } else { taskItem.Status = Music.Core.TaskStatus.Failed; taskItem.FinishedAt = DateTimeOffset.Now; log.Information($"{taskItem} failed - retries exhausted"); } db.SaveChanges(); if (requeue) { QueueTask(taskItem); } } }
public static bool ValidateArtists(this MusicDb db) { var result = true; foreach (var artist in db.Artists) { var styleCount = artist.ArtistStyles.Count(); var r = styleCount > 0; if (!r) { log.Warning($"Artist {artist.Name} [A-{artist.Id}] has no artiststyle entries"); if (result == true) { result = false; } } r = artist.Works.Count() > 0 || artist.Compositions.Count() > 0; if (!r) { log.Warning($"Artist {artist.Name} [A-{artist.Id}] has neither works nor compositions"); if (result == true) { result = false; } } } log.Information("ValidateArtists() completed"); return(result); }
private async Task UpdateAsync(MusicDb db /*, IEnumerable<string> paths*/) { var ti = await db.TaskItems.FindAsync(taskId); var artists = await db.ArtistStyles.Where(x => x.StyleId == musicStyle && x.Artist.Type != ArtistType.Various) .Select(x => x.Artist) .ToArrayAsync(); foreach (var artist in artists) { var portraitFile = artist.GetPortraitFile(musicOptions); if (portraitFile == null) { if (artist.Portrait != null) { artist.Portrait = null; log.Information($"{ti} artist {artist.Name} portrait removed"); } } else if (artist.Portrait.HasChanged(portraitFile)) { artist.Portrait = await portraitFile.GetImage(); log.Information($"{ti} artist {artist.Name} portrait updated"); } } ti.Status = Music.Core.TaskStatus.Finished; ti.FinishedAt = DateTimeOffset.Now; await db.SaveChangesAsync(); }
/// <summary> /// returns a list of directories found in all sources that match either the artist, or the work /// Note that a directory will match if the name matches ignoring accents or case /// </summary> /// <param name="musicStyle"></param> /// <param name="musicOptions"></param> /// <param name="artistName"></param> /// <param name="workName"></param> /// <returns></returns> /// <summary> /// Reads all the tags in the audio file and adds them to the database /// (Not all the tags are used) /// </summary> /// <param name="mf"></param> /// <param name="db"></param> /// <returns></returns> public static async Task UpdateTagsAsync(this MusicDb db, MusicFile mf /*, bool force = false*/) { if (/*force == true*/ /*||*/ mf.ParsingStage == MusicFileParsingStage.Unknown || mf.ParsingStage == MusicFileParsingStage.Initial || (mf.FileLastWriteTimeUtc != new IO.FileInfo(mf.File).LastWriteTimeUtc)) { // for western classical the important tags are: // ArtistName, WorkName, Year, Composition, Orchestra, Conductor, Performers, TrackNumber, Title // what about cover art?? can I assume that there will always be an image file on disk somewhere?? var oldTags = mf.IdTags.ToArray(); mf.IdTags.Clear(); db.IdTags.RemoveRange(oldTags); switch (mf.Encoding) { case EncodingType.flac: db.UpdateFlacTagsAsync(mf); break; default: await db.UpdateMp3TagsAsync(mf); break; } mf.ParsingStage = MusicFileParsingStage.IdTagsComplete; } }
public static void Delete(this MusicDb musicDb, MusicFile mf, DeleteContext context) { void clearRelatedEntities(MusicFile file) { musicDb.RemovePlaylistItems(file); var tags = file.IdTags.ToArray(); musicDb.IdTags.RemoveRange(tags); } clearRelatedEntities(mf); var track = mf.Track; track?.MusicFiles.Remove(mf); if (track?.MusicFiles.All(x => x.IsGenerated) ?? false) { foreach (var f in track.MusicFiles.ToArray()) { clearRelatedEntities(f); f.Track = null; track.MusicFiles.Remove(f); musicDb.MusicFiles.Remove(f); log.Information($"{context}: Musicfile [MF-{f.Id}] deleted: {f.File}"); } } if (track?.MusicFiles.Count() == 0) { musicDb.Delete(track, context); } musicDb.MusicFiles.Remove(mf); log.Information($"{context}: Musicfile [MF-{mf.Id}] deleted: {mf.File}"); }
private async Task <TaskItem> AddTask(MusicDb db, MusicStyles style, TaskType type, string taskString, /* bool queueTask = true,*/ bool force = false) { var existing = db.TaskItems .Where(t => t.Type == type && t.MusicStyle == style && t.TaskString.ToLower() == taskString.ToLower() && (t.Status == Music.Core.TaskStatus.Pending || t.Status == Music.Core.TaskStatus.InProgress) && t.Force == force); if (existing.Count() == 0) { var now = DateTimeOffset.Now; var ti = new TaskItem { Type = type, CreatedAt = now, ScheduledAt = now, Status = Music.Core.TaskStatus.Pending, MusicStyle = style, TaskString = taskString, Force = force }; await db.TaskItems.AddAsync(ti); await db.SaveChangesAsync(); taskRunner.QueueTask(ti); return(ti); } else { log.Debug($"Task type {type} for target {taskString} skipped as alrerady present"); } return(null); }
private TaskItem CreateTask(MusicDb db, MusicStyles style, TaskType type, string taskString) { var existing = db.TaskItems .Where(t => t.Type == type && t.MusicStyle == style && t.TaskString.ToLower() == taskString.ToLower() && (t.Status == Music.Core.TaskStatus.Pending || t.Status == Music.Core.TaskStatus.InProgress) ); if (existing.Count() == 0) { var now = DateTimeOffset.Now; var ti = new TaskItem { Type = type, CreatedAt = now, ScheduledAt = now, Status = Music.Core.TaskStatus.Pending, MusicStyle = style, TaskString = taskString }; return(ti); } else { log.Information($"Task type {type} for target {taskString} skipped as already present"); } return(null); }
public WesternClassicalAlbumSet(MusicDb db, MusicOptions musicOptions, IEnumerable <MusicFile> musicFiles, TaskItem taskItem) : base(db, musicOptions, MusicStyles.WesternClassical, musicFiles, taskItem) { this.ArtistName = OpusType == OpusType.Collection ? "Various Composers" : MusicOptions.ReplaceAlias(FirstFile.Musician); this.AlbumName = FirstFile.OpusName; this.YearNumber = FirstFile.GetYear() ?? 0; }
private bool SetTaskItemStatus(long itemId, Music.Core.TaskStatus status, out TaskType type) { bool result = false; type = TaskType.DiskPath; using (var db = new MusicDb(connectionString)) { var taskItem = db.TaskItems.Find(itemId); try { taskItem.Status = status;// Music.Core.TaskStatus.InProgress; type = taskItem.Type; db.SaveChanges(); result = true; } catch (Exception xe) { if (xe.InnerException != null) { log.Information($"{taskItem} error: {xe.GetType().Name}, inner exception {xe.InnerException.GetType().Name} {xe.InnerException.Message}"); } else { log.Information($"{taskItem} error: {xe.GetType().Name}, {xe.Message}"); } } } return(result); }
public static bool ValidatePerformances(this MusicDb db) { var result = true; foreach (var performance in db.Performances) { var movementCount = performance.Movements.Count(); var r = movementCount > 0; if (!r) { log.Warning($"{performance.Composition.Artist.Name} [A-{performance.Composition.Artist.Id}], \"{performance.Composition.Name}\" [C-{performance.Composition.Id}] performed by \"{performance.Performers}\" [P-{performance.Id}] has no movements"); if (result == true) { result = false; } } if (movementCount > 0) { var workCount = performance.Movements.Select(x => x.Work).Distinct().Count(); r = workCount == 1; if (!r) { log.Warning($"{performance.Composition.Artist.Name} [A-{performance.Composition.Artist.Id}], \"{performance.Composition.Name} [C-{performance.Composition.Id}] movements\" in performance by {performance.Performers} [P-{performance.Id}] have a work count of {workCount}"); if (result == true) { result = false; } } } } log.Information("ValidatePerformances() completed"); return(result); }
public static bool ValidateWorks(this MusicDb db) { var result = true; foreach (var work in db.Works) { var styles = work.Artist.ArtistStyles.Select(x => x.StyleId); var r = styles.Any(x => x == work.StyleId); if (!r) { log.Warning($"Work {work.Name} [{work.Id}] is in style {work.StyleId} but artist {work.Artist} [W-{work.Artist.Id}] is not"); if (result == true) { result = false; } } r = work.Tracks.Count() > 0; if (!r) { log.Warning($"Work {work.Name} [W-{work.Id}] has no tracks"); if (result == true) { result = false; } } } log.Information("ValidateWorks() completed"); return(result); }
private async Task Start() { log.Information($"started"); while (!cancellationToken.IsCancellationRequested) { await Task.Delay(2000); try { TaskItem taskItem = null; using (var db = new MusicDb(connectionString)) { taskItem = db.TaskItems .Where(x => x.Type == TaskType.ResampleWork && x.Status == Music.Core.TaskStatus.Pending) .OrderBy(x => x.ScheduledAt) .FirstOrDefault(); if (taskItem != null) { taskItem.Status = Music.Core.TaskStatus.InProgress; await db.SaveChangesAsync(); } } if (taskItem != null) { await ResampleAsync(taskItem.Id); } } catch (Exception xe) { log.Error(xe); } } log.Error($"finished!!!!!!!!!!!!!!!!!!!!!"); }
public static async Task FillPlaylistForRuntime(this MusicDb db, Playlist pl) { foreach (var pli in pl.Items) { await db.FillPlaylistItemForRuntime(pli); } }
public static async Task FillPlaylistItemForRuntime(this MusicDb db, PlaylistItem pli) { switch (pli.Type) { case PlaylistItemType.MusicFile: pli.MusicFile = await db.MusicFiles.FindAsync(pli.MusicFileId); break; case PlaylistItemType.Track: pli.Track = await db.Tracks.FindAsync(pli.ItemId); break; case PlaylistItemType.Work: pli.Work = await db.Works.FindAsync(pli.ItemId); break; case PlaylistItemType.Performance: pli.Performance = await db.Performances.FindAsync(pli.ItemId); break; } }
private static void Delete(this MusicDb musicDb, Track track, DeleteContext context) { long artistId = track.Work.ArtistId; foreach (var musicFile in track.MusicFiles.ToArray()) { musicFile.Track = null; musicDb.Delete(musicFile, context); } var performance = track.Performance; performance?.Movements.Remove(track); if (performance?.Movements.Count() == 0) { musicDb.Delete(performance, context); } var work = track.Work; work?.Tracks.Remove(track); if (work?.Tracks.Count() == 0) { musicDb.Delete(work, context); } musicDb.Tracks.Remove(track); context.SetModifiedArtistId(artistId); log.Information($"{context}: Track [T-{track.Id}] deleted: {track.Title}"); }
//private readonly IHubContext<PlayHub, IHubMessage> playHub; public PlayerController(/*SchedulerService schedulerService,*/ /*Microsoft.Extensions.Hosting.IHostedService hs,*/ MusicDb mdb, IOptions <MusicServerOptions> serverOptions, PlayManager pm, ILoggerFactory loggerFactory, ILogger <PlayerController> logger, /*IHostingEnvironment env,*/ IWebHostEnvironment env) : base(logger, env) { this.musicServerOptions = serverOptions.Value; playManager = pm;// schedulerService.GetRealtimeTask<PlayManager>();// (hs as SchedulerService).GetRealtimeTask<PlayManager>(); this.musicDb = mdb; this.loggerFactory = loggerFactory; }
private async Task <(MusicStyles ms, string dataString, bool forSingles, bool force)> GetTaskAsync() { using (var db = new MusicDb(connectionString)) { var taskItem = await db.TaskItems.FindAsync(taskId); return(taskItem.MusicStyle, taskItem.TaskString, taskItem.ForSingles, taskItem.Force); } }
public async Task <List <MusicFile> > UpdateAudioFilesToDbAsync(MusicDb db) { var musicFiles = new List <MusicFile>(); foreach (var audioFile in new AudioFileCollection(this)) { var ap = audioFile.GetAudioProperties(); var mf = await db.MusicFiles.SingleOrDefaultAsync(x => x.File == audioFile.File.FullName); if (mf == null) { var opusType = IsCollection ? OpusType.Collection : OpusType.Normal; if (ForSinglesOnly) { opusType = OpusType.Singles; } mf = new MusicFile { DiskRoot = currentPathData.DiskRoot, IsGenerated = currentPathData.IsGenerated, Encoding = Path.GetExtension(audioFile.File.FullName).Substring(1).To <EncodingType>(), Musician = currentPathData.ArtistPath, MusicianType = IsCollection ? ArtistType.Various : ArtistType.Artist, OpusName = opusType == OpusType.Singles ? $"{currentPathData.ArtistPath} Singles" : currentPathData.OpusPath, OpusType = opusType, IsMultiPart = HasParts, PartName = HasParts ? audioFile.Part.Name : string.Empty, PartNumber = HasParts ? audioFile.Part.Number : 0, Style = mfi.MusicStyle, StylePath = currentPathData.StylePath, OpusPath = IsCollection ? currentPathData.OpusPath : ForSinglesOnly ? currentPathData.ArtistPath : Path.Combine(currentPathData.ArtistPath, currentPathData.OpusPath), Mode = ap.Mode, File = audioFile.File.FullName, Uid = Guid.NewGuid().ToString(), Mood = string.Empty }; await db.MusicFiles.AddAsync(mf); log.Debug($"{mf.File} added to db"); } mf.Duration = ap.Duration; mf.BitsPerSample = ap.BitsPerSample; mf.SampleRate = ap.SampleRate; mf.MinimumBitRate = ap.MinimumBitRate; mf.MaximumBitRate = ap.MaximumBitRate; mf.AverageBitRate = ap.AverageBitRate; mf.FileLastWriteTimeUtc = audioFile.File.LastWriteTimeUtc; mf.FileLength = audioFile.File.Length; mf.ParsingStage = MusicFileParsingStage.Initial; musicFiles.Add(mf); } return(musicFiles); }
public WesternClassicalCompositionSet(MusicDb db, MusicOptions musicOptions, string composerName, string compositionName, IEnumerable <MusicFile> musicFiles, TaskItem taskItem) : base(db, musicOptions, MusicStyles.WesternClassical, musicFiles, taskItem) { var fileNameList = musicFiles.Select(x => Path.GetFileName(x.File)); this.ComposerName = musicOptions.ReplaceAlias(composerName); this.CompositionName = musicOptions.ReplaceAlias(compositionName); //**NB** following calls must remain in this order!! conductors = GetConductors(); orchestras = GetOrchestras(); otherPerformers = GetOtherPerformers(); }
public int RemoveCurrentMusicFiles(MusicDb db) { int count = 0; var filesInDb = GetMusicFilesFromDb(db); foreach (var mf in filesInDb.ToArray()) { ++count; var dc = new DeleteContext(this); db.Delete(mf, dc); } return(count); }
/// <summary> /// files is a set of music files fom the same opus (ie. originalyl from the same disk folder) /// </summary> /// <param name="files"></param> internal MusicSetCollection(MusicOptions musicOptions, MusicDb musicDb, OpusFolder musicFolder, List <MusicFile> files, TaskItem taskItem) { this.musicOptions = musicOptions; this.musicDb = musicDb; this.musicFolder = musicFolder; this.files = files; this.taskItem = taskItem; //Debug.Assert(ValidateMusicFileSet()); var firstFile = files.First(); isCollection = firstFile.OpusType == OpusType.Collection; musicStyle = firstFile.Style; }
public IEnumerable <MusicFile> GetMusicFilesFromDb(MusicDb db) { var opusPath = currentPathData.OpusPath != null?Path.Combine(currentPathData.ArtistPath, currentPathData.OpusPath) : currentPathData.ArtistPath; var result = db.MusicFiles.Where(mf => mf.DiskRoot.ToLower() == currentPathData.DiskRoot.ToLower() && mf.StylePath.ToLower() == currentPathData.StylePath.ToLower()) .ToArray() .Where(mf => (mf.OpusType == OpusType.Collection ? Path.Combine("collections", mf.OpusPath.ToLower()) : mf.OpusPath.ToLower()) == opusPath.ToLower()) ; return(result.ToArray().OrderBy(x => x.File, StringComparer.CurrentCultureIgnoreCase)); }
public static PlaylistRuntime ToRuntime(this Playlist list, MusicDb db, DeviceRuntime dr) { return(new PlaylistRuntime { Id = list.Id, Name = list.Name, Type = list.Type, Items = list.Items .Select(x => x.ToRuntime(db, dr)) .Where(x => x != null) .OrderBy(x => x.Sequence).ToList() }); }
internal PopularMusicAlbumSet(MusicDb db, MusicOptions musicOptions, string artistName, string albumName, IEnumerable <MusicFile> musicFiles, TaskItem taskItem) : base(db, musicOptions, MusicStyles.Popular, musicFiles, taskItem) { this.ArtistName = FirstFile.GetArtistName() ?? artistName; this.AlbumName = FirstFile.GetWorkName() ?? albumName; this.YearNumber = FirstFile.GetYear() ?? 0; if (string.Compare(this.AlbumName, "Greatest Hits", true) == 0) { // prefix with artist name as there are so many called "Greatest Hits" this.AlbumName = $"{ArtistName} Greatest Hits"; } }
/// <summary> /// create a a music set for the given music files in the given music style /// </summary> /// <param name="musicOptions"></param> /// <param name="musicStyle"></param> /// <param name="musicFiles"></param> public MusicSet(MusicDb db, MusicOptions musicOptions, MusicStyles musicStyle, IEnumerable <MusicFile> musicFiles, TaskItem taskItem) { Debug.Assert(musicFiles.Count() > 0); this.log = ApplicationLoggerFactory.CreateLogger(this.GetType()); this.MusicDb = db; this.MusicOptions = musicOptions; this.MusicStyle = musicStyle; this.MusicFiles = musicFiles; this.taskItem = taskItem; this.FirstFile = musicFiles.First(); this.OpusType = FirstFile.OpusType; this.generated = FirstFile.IsGenerated; }
public static bool Validate(this MusicDb musicDb) { var list = new List <bool>() { musicDb.ValidateArtists(), musicDb.ValidateWorks(), musicDb.ValidateTracks(), musicDb.ValidateCompositions(), musicDb.ValidatePerformances() }; return(list.All(x => x == true)); }
private async Task <List <CatalogueResult> > CatalogueAsync(MusicDb db, PathData pd) { db.ChangeTracker.AutoDetectChangesEnabled = false; taskItem = await db.TaskItems.FindAsync(taskId); try { bool changesPresent(OpusFolder folder) { var(result, changes) = folder.CheckForChanges(db); if (result) { log.Information($"{folder}, change {changes}"); } return(result); }; var results = new List <CatalogueResult>(); var folder = new OpusFolder(musicOptions, pd); //if (forceChanges == true || folder.CheckForChanges(db)) if (forceChanges == true || changesPresent(folder)) { var delay = GetRandomDelay(); log.Debug($"{taskItem} starting {folder.ToString()} after delay of {delay}ms"); await Task.Delay(TimeSpan.FromMilliseconds(delay)); results = await ProcessFolderAsync(db, folder); var success = results.All(x => x.Status == CatalogueStatus.Success || x.Status == CatalogueStatus.GeneratedFilesOutOfDate); taskItem.Status = success ? Music.Core.TaskStatus.Finished : Music.Core.TaskStatus.Failed; } else { taskItem.Status = Music.Core.TaskStatus.Finished; results.Add(new CatalogueResult { Status = CatalogueStatus.Success }); log.Information($"{taskItem} starting {folder.ToString()} no update required"); } taskItem.FinishedAt = DateTimeOffset.Now; await db.SaveChangesAsync(); return(results); } catch (Exception xe) { log.Error(xe, $"task {taskItem}"); throw new CatalogueFailed { TaskId = taskId }; } }
private IEnumerable <IMusicSet> GetMusicSets(MusicDb db, OpusFolder musicFolder, List <MusicFile> files) { Debug.Assert(ValidateMusicFileSet(db, files) == true); var style = files.First().Style; switch (style) { default: case MusicStyles.Popular: return(new PopularMusicSetCollection(musicOptions, db, musicFolder, files, taskItem)); case MusicStyles.WesternClassical: return(new WesternClassicalMusicSetCollection(musicOptions, db, musicFolder, files, taskItem)); } }