private void GetAllFiles(MountedDevice device, string subPath, AudioFile[] allAudioFiles, List <string> newFiles, List <string> existingFiles) { // Probe all subdirectories string path = device.MountPath + subPath; string[] directories = null; try { directories = Directory.GetDirectories(path); foreach (string directory in directories) { try { GetAllFiles(device, subPath + (subPath.EndsWith("/") ? null : "/") + Path.GetFileName(directory), allAudioFiles, newFiles, existingFiles); } catch { } } string[] mp3files = Directory.GetFiles(path, "*.mp3"); if (mp3files.Length < 1) { return; } foreach (string file in mp3files) { string relativePath = Path.Combine(subPath, Path.GetFileName(file)); if (allAudioFiles.Any(audioFile => audioFile.RelativePath == relativePath)) { existingFiles.Add(relativePath); } else { newFiles.Add(relativePath); } } } catch { } }
/// <summary> /// Creates a new instance fo this class. /// </summary> /// <param name='device'> /// The device that these event arguments represent. /// </param> public MountedDeviceEventArgs(MountedDevice device) { Device = device; }
private void ProcessFileList(MountedDevice device, List <string> mp3Files, AudioFile[] allAudioFiles) { // Create a new notification object if required AudioLibraryUpdateNotification notification = new AudioLibraryUpdateNotification(); // Now probe each file within this directory try { foreach (string relativePath in mp3Files) { string file = Path.Combine(device.MountPath + relativePath); if (!File.Exists(file)) { Logger.Error("File no longer exists when probing mounted file " + file + " for audio information, aborting loop"); break; } try { AudioFile audioFile = new AudioFile(); using (TagLib.File tagFile = TagLib.File.Create(file)) { if (!string.IsNullOrEmpty(tagFile.Tag.Album)) { audioFile.Album = tagFile.Tag.Album; } if (!string.IsNullOrEmpty(tagFile.Tag.JoinedAlbumArtists)) { audioFile.Artist = tagFile.Tag.JoinedAlbumArtists; } if (tagFile.Properties.Duration != TimeSpan.Zero) { audioFile.Duration = tagFile.Properties.Duration; } if (!string.IsNullOrEmpty(tagFile.Tag.Title)) { audioFile.Title = tagFile.Tag.Title; } if (tagFile.Tag.Track != 0) { audioFile.TrackNumber = (int)tagFile.Tag.Track; } } if (string.IsNullOrEmpty(audioFile.Artist)) { string[] parts = relativePath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); int depth = parts.Length - 1; string partToParse = parts[depth < 2 ? 0 : depth - 2]; if (partToParse.Contains("-")) { partToParse = partToParse.Split(new char[] { '-' })[0].Trim(); } audioFile.Artist = partToParse; if (audioFile.Artist == string.Empty) { audioFile.Artist = null; } } if (string.IsNullOrEmpty(audioFile.Album)) { string[] parts = relativePath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); int depth = parts.Length - 1; string partToParse = parts[depth < 1 ? 0 : depth - 1]; if (partToParse.Contains("-")) { partToParse = partToParse.Split(new char[] { '-' })[0].Trim(); } audioFile.Album = partToParse; if (audioFile.Album == string.Empty) { audioFile.Album = null; } } if (string.IsNullOrEmpty(audioFile.Title)) { audioFile.Title = Path.GetFileNameWithoutExtension(file).Trim(); if (audioFile.Title.Contains("-")) { audioFile.Title = audioFile.Title.Split(new char[] { '-' }, 2)[1].Trim(); } while (!string.IsNullOrEmpty(audioFile.Title) && char.IsDigit(audioFile.Title[0])) { if (audioFile.Title.Length == 1) { audioFile.Title = null; break; } audioFile.Title = audioFile.Title.Substring(1, audioFile.Title.Length - 1).Trim(); } if (audioFile.Title == string.Empty) { audioFile.Title = null; } } audioFile.DeviceUuid = device.Uuid; audioFile.LastSeen = DateTime.UtcNow; audioFile.RelativePath = relativePath; Logger.Debug(audioFile.ToString()); // See if we have an existing file for this one AudioFile existing = allAudioFiles.Where(existingFile => existingFile.RelativePath == audioFile.RelativePath).FirstOrDefault(); bool existingIsEqual = false; if (existing != null) { existingIsEqual = audioFile.Equals(existing); if (!existingIsEqual) { if ((existing.Album != audioFile.Album) || (existing.Artist != audioFile.Artist)) { existing.ArtworkSearchDate = null; } existing.Album = audioFile.Album; existing.Artist = audioFile.Artist; existing.Duration = audioFile.Duration; existing.LastSeen = audioFile.LastSeen; existing.Title = audioFile.Title; existing.TrackNumber = audioFile.TrackNumber; audioFile = existing; } } // The "AC/DC Fix" if ((audioFile.Artist == "AC;DC") || (audioFile.Artist == "AC; DC") || (audioFile.Artist == "AC_DC")) { audioFile.Artist = "AC/DC"; } // Add to update notification if new or if it is updated if (audioFile.AudioFileId == 0) { notification.NewFiles.Add(audioFile); } else if (!existingIsEqual) { notification.UpdatedFiles.Add(audioFile); } // Deal database updates and changes and firing of notifications if we have reached enough // files in this object and create a new object if (notification.ReadyToSend) { ProcessFileChanges(notification); notification = new AudioLibraryUpdateNotification(); } } catch (Exception ex) { Logger.Error("Failed to probe file " + file, ex); } } } catch { } // Deal database updates and changes and firing of notifications if we have reached enough // files in this object and create a new object ProcessFileChanges(notification); }
/// <summary> /// This thread is used to probe all files on a particular device. /// </summary> /// <param name='args'> /// The arguments to pass to the thread. /// </param> private void AudioDiscoveryThread(object args) { try { // Cast object from thread arguments MountedDevice device = (MountedDevice)args; // Get a list from database of all existing tracks AudioFile[] allAudioFiles = AudioFileFactory.ApplicationInstance.ReadAll().Where(file => file.DeviceUuid == device.Uuid).ToArray(); // Recursively gain two lists - new and existing files. We always process new files // first, then online, then we check for changed to online and fire updated // and finally deleted to give quickest response to the client List <string> newFiles = new List <string>(); List <string> existingFiles = new List <string>(); GetAllFiles(device, "/", allAudioFiles, newFiles, existingFiles); // Now process the file sets DateTime startTime = DateTime.UtcNow; Thread.CurrentThread.Priority = ThreadPriority.AboveNormal; ProcessFileList(device, newFiles, allAudioFiles); AudioLibraryUpdateNotification notification = new AudioLibraryUpdateNotification(); if (existingFiles.Count > 0) { foreach (string relativePath in existingFiles) { AudioFile file = allAudioFiles.FirstOrDefault(f => f.RelativePath == relativePath); if (file == null) { continue; } notification.OnlineFiles.Add(file); } ProcessFileChanges(notification); notification = new AudioLibraryUpdateNotification(); } Thread.CurrentThread.Priority = ThreadPriority.BelowNormal; ProcessFileList(device, existingFiles, allAudioFiles); Thread.CurrentThread.Priority = ThreadPriority.Normal; foreach (AudioFile file in allAudioFiles) { if (file.LastSeen < startTime) { notification.DeletedFiles.Add(file.AudioFileId); } } if (notification.DeletedFiles.Count > 0) { Controller.NotificationNetworkServer.SendNotification(notification); } AudioFileFactory.ApplicationInstance.RemoveForUuid(device.Uuid, startTime); } catch (ThreadAbortException) { } catch (Exception ex) { Logger.Error("Fatal error during audio file discovery", ex); } finally { lock (_discoveryThreads) { MountedDevice key = null; foreach (KeyValuePair <MountedDevice, Thread> kvp in _discoveryThreads) { if (kvp.Value == Thread.CurrentThread) { key = kvp.Key; break; } } if ((key != null) && (_discoveryThreads.ContainsKey(key))) { _discoveryThreads.Remove(key); } } } }