/// <summary>Reads the album and artist data from specified media file or folder, and adds it to the <see cref="mAlbums"/> collection.</summary> /// <param name="filePathPattern">Use null to use the ID3 tags instead</param> /// <returns>The <see cref="Album"/> that was added to <see cref="mAlbums"/>, or <c>null</c> if none could be read.</returns> private Album ReadMediaFile(FileSystemInfo file, Regex filePathPattern) { if (file is DirectoryInfo && filePathPattern == null) //If a DirectoryInfo is used, then the filePathPattern must have ended in \. { throw new ArgumentException("Directories are only supported for pattern matching, not ID3 tags", "file"); } Dispatcher.BeginInvoke(DispatcherPriority.DataBind, new ThreadStart(delegate { ProgressText = "Searching... " + file.Name; })); string artistName = null; string albumName = null; int? embeddedArtIndex = null; if (filePathPattern == null) { //Read ID3 Tags TagLib.File fileTags = null; try { fileTags = TagLib.File.Create(file.FullName, TagLib.ReadStyle.None); if (fileTags.Tag.AlbumArtists.Length == 0) { artistName = String.Join(" / ", fileTags.Tag.Performers); } else { artistName = String.Join(" / ", fileTags.Tag.AlbumArtists); } albumName = fileTags.Tag.Album; var embeddedPictures = fileTags.Tag.Pictures; if (embeddedPictures.Length > 0) { //There's an embedded picture //Check to see if there's a picture described as the front cover, to use in preference for (int i = 0; i < embeddedPictures.Length; i++) { if (embeddedPictures[i].Type == TagLib.PictureType.FrontCover) { embeddedArtIndex = i; break; } } if (!embeddedArtIndex.HasValue) { //None of the embedded pictures were tagged as "FrontCover", so just use the first picture embeddedArtIndex = 0; } } } catch (Exception e) { System.Diagnostics.Trace.WriteLine("TagLib# could not get artist and album information for file: " + file.FullName); System.Diagnostics.Trace.Indent(); System.Diagnostics.Trace.WriteLine(e.Message); System.Diagnostics.Trace.Unindent(); return(null); //If this media file couldn't be read, just go on to the next one. } finally { if (fileTags != null) { fileTags.Mode = TagLib.File.AccessMode.Closed; } } } else { //Read from file path Match match = filePathPattern.Match(file.FullName); if (match.Success) { artistName = match.Groups["artist"].Value; albumName = match.Groups["album"].Value; } } if (!(String.IsNullOrEmpty(artistName) && String.IsNullOrEmpty(albumName))) //No point adding it if no artist or album could be found. { string basePath; if (file is FileInfo) { basePath = ((FileInfo)file).DirectoryName; } else { System.Diagnostics.Debug.Assert(file is DirectoryInfo, "Expecting file to be one of FileInfo or DirectoryInfo"); basePath = ((DirectoryInfo)file).FullName; } Album album = new Album(basePath, artistName, albumName); if (embeddedArtIndex.HasValue) { //Read the picture from the data album.SetArtFile(EmbeddedArtHelpers.GetEmbeddedFilePath(file.FullName, embeddedArtIndex.Value)); } Dispatcher.Invoke(DispatcherPriority.DataBind, new ThreadStart(delegate { mAlbums.Add(album); })); return(album); } return(null); }
/// <summary> /// Checks through a set of albums for those that should be concatenated into a single Various Artists album. /// </summary> /// <param name="addedAlbums"></param> private void DetectVariousArtistsAlbums(IList <Album> albums) { //Get the albums in each folder Dictionary <String, List <Album> > folders = new Dictionary <string, List <Album> >(albums.Count); foreach (Album album in albums) { if (album != null) { List <Album> folderAlbums; if (!folders.TryGetValue(album.BasePath, out folderAlbums)) { folderAlbums = new List <Album>(albums.Count); folders.Add(album.BasePath, folderAlbums); } folderAlbums.Add(album); } } foreach (List <Album> folder in folders.Values) { //If the folder contains all the same album name, but different artists, then it is VA string albumName = folder[0].Name; string artist = folder[0].Artist; string basePath = folder[0].BasePath; bool variousArtists = false; foreach (Album album in folder) { if (album.Name != albumName) { //This is not all the same album variousArtists = false; break; } if (!variousArtists && album.Artist != artist) { variousArtists = true; } } if (variousArtists) { //This is a various artists album, so replace them with a single Various Artists album Dispatcher.Invoke(DispatcherPriority.DataBind, new ThreadStart(delegate { Album variousArtistsAlbum = new Album(basePath, sVariousArtistsName, albumName); if (!mAlbums.Contains(variousArtistsAlbum)) //If this album already exists, no need to add it again. { //Add replacement VA album mAlbums.Insert(mAlbums.IndexOf(folder[0]), variousArtistsAlbum); } //Remove individual albums, but copy art from at least one of them (if present) foreach (Album album in folder) { if (album.Artist != sVariousArtistsName) //If, by chance, one of the albums was already called "Various Artists", then it will have merged with the newly added one. { if (variousArtistsAlbum.ArtFileStatus != ArtFileStatus.Present && album.ArtFile != null) { variousArtistsAlbum.SetArtFile(album.ArtFile); } mAlbums.Remove(album); } } })); } } }
private void SearchWorker() { Album album = null; Dispatcher.Invoke(DispatcherPriority.Send, new ThreadStart(delegate { album = mQueueList.GetNextAlbum(); IsSearching = true; })); while (album != null) { album.ArtFileStatus = ArtFileStatus.Searching; Dispatcher.BeginInvoke(DispatcherPriority.DataBind, new ThreadStart(delegate { if (mQueueList.ShouldAutoscroll) { mQueueList.ScrollIntoView(album); } })); try { List <Source> currentSourcesToSearch; lock (mSourcesToSearch) { //Take a copy of the source list so that it doesn't get affected by changes) currentSourcesToSearch = new List <Source>(mSourcesToSearch); } foreach (var source in currentSourcesToSearch) //Try each source in turn { Dispatcher.BeginInvoke(DispatcherPriority.DataBind, new ThreadStart(delegate { ProgressText = String.Format("Searching {0} for {1} / {2}", source.Name, album.Artist, album.Name); })); source.Results.CollectionChanged += OnSourceFoundResult; source.QueryContinueSearch += OnSourceQueryContinue; source.SearchCompleted += OnSourceSearchComplete; mWaitForSourceCompleted.Reset(); Dispatcher.BeginInvoke(DispatcherPriority.Normal, new ThreadStart(delegate { //source.Search must be done in the main dispatcher thread, as that's the thread which the observable collection will be updated on. source.Search(album.Artist, album.Name); })); //Wait for the source to have finished searching mWaitForSourceCompleted.WaitOne(); System.Diagnostics.Debug.Assert(source.IsSearching == false, "Source is supposed to have completed by now"); //Done with this source - stop listening to its events. source.Results.CollectionChanged -= OnSourceFoundResult; source.QueryContinueSearch -= OnSourceQueryContinue; source.SearchCompleted -= OnSourceSearchComplete; //Was a valid result found in this source? if (mSearchResult != null) { var observableCollectionOfDisposables = source.Results as ObservableCollectionOfDisposables <AlbumArt>; if (observableCollectionOfDisposables != null) { //Detach the result from the collection so that it isn't disposed of when the source is re-used. observableCollectionOfDisposables.Detach(mSearchResult); //Responsibility for disposing of mSearchResult is now taken here, and will be passed on to mResults when it is added to that collection. } //No need to search other sources. break; } } //Was a valid result found in any source? if (mSearchResult != null) { //Yes, so save it and add it to the results list. Dispatcher.Invoke(DispatcherPriority.DataBind, new ThreadStart(delegate { mSearchResult.DefaultFilePathPattern = album.ArtFile; //If the file already exists, overwrite it if (System.IO.File.Exists(mSearchResult.FilePath)) { try { System.IO.File.Delete(mSearchResult.FilePath); } catch (Exception ex) { System.Diagnostics.Trace.TraceError("Could not delete file \"{0}\": {1}", mSearchResult.FilePath, ex.Message); } } mSearchResult.Save(); mResults.Add(mSearchResult); album.SetArtFile(mSearchResult.FilePath); Progress++; })); mResultsLookup.Add(album, mSearchResult); mReverseLookup.Add(mSearchResult, album); mSearchResult = null; } else { mAlbumsMissingResults.Add(album); album.ArtFileStatus = ArtFileStatus.Missing; } } catch (ThreadAbortException) { album.ArtFileStatus = ArtFileStatus.Queued; return; } //Get the next album Dispatcher.Invoke(DispatcherPriority.Send, new ThreadStart(delegate { album = mQueueList.GetNextAlbum(); })); } Dispatcher.BeginInvoke(DispatcherPriority.DataBind, new ThreadStart(delegate { IsSearching = false; System.Diagnostics.Debug.Assert(mQueueList.Items.Count - mResults.Count == mAlbumsMissingResults.Count, "Unexpected albums missing results count"); ProgressText = String.Format("Done. {0} albums found, {1} not found.", mResults.Count, mAlbumsMissingResults.Count); mSearchThread = null; ReEnableStartButton(); })); }