/// <summary> /// Get a lits of iTunes Tracks given a list of track IDs. /// </summary> /// <param name="trackIDs"></param> /// <returns></returns> protected TrackCollection GetTracks(PersistentIDCollection persistentIDs) { TrackCollection tracks = new TrackCollection(); foreach (PersistentID persistentID in persistentIDs) { Track track = controller.LibraryPlaylist.GetTrack(persistentID); if (track != null) { tracks.Add(track); } } return(tracks); }
/// <summary> /// Returns a collection containing the tracks with the specified text. /// <para> /// If searchFields is ITPlaylistSearchFieldVisible , this is identical to the list of /// tracks displayed if the user enters the search text in the Search edit field in /// iTunes when this playlist is being displayed. /// </para> /// </summary> /// <param name="searchText"> /// The text to search for. This string cannot be longer than 255 characters. /// </param> /// <param name="searchFields"> /// Specifies which fields of each track should be searched for searchText. /// </param> /// <returns> /// Collection of Track objects. This will be empty if no tracks meet the search /// criteria. /// </returns> public TrackCollection Search(string searchText, ITPlaylistSearchField searchFields) { return(Invoke((Func <TrackCollection>) delegate { TrackCollection collection = new TrackCollection(); IITTrackCollection results = playlist.Search(searchText, searchFields); if (results != null) { foreach (IITTrack track in results) { if (track != null) { collection.Add(new Track(track)); } } } return collection; })); }
/// <summary> /// For unit testing; returns the first specified number of tracks from the playlist. /// </summary> public TrackCollection _GetFirstTracks(int count) { return(Invoke((Func <TrackCollection>) delegate { TrackCollection collection = new TrackCollection(); foreach (IITTrack track in playlist.Tracks) { if (track != null) { collection.Add(new Track(track)); } if (collection.Count >= count) { break; } } return collection; })); }
/// <summary> /// Archive all identified duplicate tracks from the main iTunes play list. /// </summary> /// <remarks> /// Duplicate files are moved from their current location into the pre-defined /// iTuner archive directory. The relative path of each file is preserved to /// maintain context. /// </remarks> private void ArchiveDuplicates(TrackCollection duplicates) { foreach (Track duplicate in duplicates.Values) { if (base.isActive && (duplicate != null)) { try { string location = duplicate.Location; bool fileIsSafe = false; // 1. archive the file if (String.IsNullOrEmpty(location)) { Logger.WriteLine(base.name, "Removing phantom duplicate " + duplicate.MakeKey()); // can safely remove library entry without affecting a physical file fileIsSafe = true; } else { string archive; if (location.StartsWith(base.catalog.MusicPath)) { // file is currently under rootPath archive = Path.Combine( base.ArchivePath, location.Substring(base.catalog.MusicPath.Length)); } else { // file is located external to rootPath string root = Path.GetPathRoot(location); archive = Path.Combine( base.ArchivePath, location.Substring(root.Length)); } if (!File.Exists(archive)) { string dir = Path.GetDirectoryName(archive); if (!Directory.Exists(dir)) { if (ScannerBase.isLive) { Directory.CreateDirectory(dir); } } // stop playback to avoid "in-use" IOException if (controller.CurrentTrack != null) { if (duplicate.TrackID == controller.CurrentTrack.TrackID) { controller.Stop(); } } if (ScannerBase.isLive) { //FileInfo file = new FileInfo(location); //if ((file.Attributes & FileAttributes.ReadOnly) > 0) //{ // file.Attributes ^= FileAttributes.ReadOnly; //} File.Move(location, archive); // can safely remove library entry without affecting a physical file fileIsSafe = true; } Logger.WriteLine(base.name, "Archived " + archive); } } // 2. delete library entry if (ScannerBase.isLive && fileIsSafe) { duplicate.Delete(); duplicate.Dispose(); } } catch (Exception exc) { Logger.WriteLine(base.name, String.Format("Error deleting {0}, {1}, {2}", duplicate.Artist, duplicate.Title, duplicate.Album), exc); } } else { Logger.WriteLine(base.name, "Duplicate scanner cancelled while cleaning"); break; } } }
//======================================================================================== // Methods //======================================================================================== /// <summary> /// Execute the scanner. /// </summary> /// <remarks> /// Duplicates are first identified by comparing Artist, Name, and Album. These are /// all stored as (ID3) tags within the media file. So these must match or the files /// are already different and any further analysis (checksum or MD5) would be useless. /// </remarks> public override void Execute() { try { Logger.WriteLine(base.name, "Duplicate scanner beginning"); int total; int count = 0; Track candidate; Candidates candidates = new Candidates(); TrackCollection duplicates = new TrackCollection(); Playlist libraryPlaylist = controller.LibraryPlaylist; PersistentIDCollection pids; if (!String.IsNullOrEmpty(albumFilter) && !String.IsNullOrEmpty(artistFilter)) { pids = catalog.FindTracksByAlbum(albumFilter, artistFilter); total = pids.Count; Logger.WriteLine(base.name, String.Format( "Analyzing album '{0}' by '{1}' with {2} tracks", albumFilter, artistFilter, total)); } else if (!playlistFilter.IsEmpty) { pids = catalog.FindTracksByPlaylist(playlistFilter); total = pids.Count; Logger.WriteLine(base.name, String.Format( "Analyzing playlist '{0}' with {1} tracks", catalog.FindPlaylistName(playlistFilter), total)); } else { pids = catalog.FindTracksByPlaylist(libraryPlaylist.PersistentID); total = pids.Count; Logger.WriteLine(base.name, String.Format("Analyzing {0} tracks", total)); } foreach (PersistentID persistentID in pids) { if (base.isActive) { Track track = libraryPlaylist.GetTrack(persistentID); if ((track != null) && (track.Kind == TrackKind.File)) { candidate = track; candidate.IsBuffered = true; // need to skip phantoms or Track.IsBetterThan will hang if (!String.IsNullOrEmpty(candidate.Location)) { Track demoted = candidates.Reconcile(candidate); if (demoted != null) { duplicates.Add(demoted); // DO NOT dispose demoted here because that would corrupt // the instance we just stored in the duplicates collection } } // DO NOT dispose candidate here because the instance is stored in // either the candidates or duplicates collection and disposing // would corrupt that reference } // DO NOT dispose track here because the instance is stored in // either the candidates or duplicates collection and disposing // would corrupt that reference count++; base.ProgressPercentage = (int)((double)count / (double)total * 100.0); } else { Logger.WriteLine(base.name, "Duplicate scanner cancelled while scanning"); break; } } if (base.isActive) { UpdateCandidates(candidates); } candidates.Clear(); candidates = null; if (base.isActive) { ArchiveDuplicates(duplicates); } duplicates.Dispose(); duplicates = null; pids.Clear(); pids = null; Logger.WriteLine(base.name, "Duplicate scanner completed"); } catch (Exception exc) { App.LogException(new SmartException(exc)); } }