/// <summary> /// Adds one or more tracks to an existing playlist in the catalog. /// </summary> private void AddTracksToPlaylist() { PersistentID playlistPID = controller.GetPersistentID(action.PlaylistOID); if (!playlistPID.IsEmpty) { PersistentIDCollection trackPIDs = new PersistentIDCollection(); ObjectIDCollection trackOIDs = action.TrackOIDs; foreach (ObjectID trackOID in trackOIDs) { PersistentID trackPID = controller.GetPersistentID(trackOID); if (!trackPID.IsEmpty) { trackPID.TransientID = trackOID.TrackID; trackPIDs.Add(trackPID); } } if (trackPIDs.Count > 0) { catalog.AddTracksToPlaylist(trackPIDs, playlistPID); } } }
/// <summary> /// Delete the specified track from all playlists and the main library. /// </summary> /// <param name="persistentID"></param> private void DeleteTrack(PersistentID persistentID) { Track track = controller.LibraryPlaylist.GetTrack(persistentID); // when a track is deleted from a source's primary playlist, it will be deleted // from all playlist's in that source if (track != null) { Logger.WriteLine(base.name, "Deleting track " + track.MakeKey()); try { if (ScannerBase.isLive) { track.Delete(); } } catch (Exception exc) { Logger.WriteLine(base.name, "Error deleting track " + track.MakeKey(), exc); } finally { track.Dispose(); track = null; } } }
/// <summary> /// Initialize a new instance for secondary wrappers such as Playlists, Tracks, etc. /// </summary> public Interaction() { this.isPrimaryController = false; this.objectID = null; this.persistentID = PersistentID.Empty; this.isDisposed = false; }
/// <summary> /// Gets the track specified by its persistent ID. /// </summary> public Track GetTrack(PersistentID persistentID) { return(Invoke((Func <Track>) delegate { return new Track(playlist.Tracks .get_ItemByPersistentID(persistentID.HighBits, persistentID.LowBits)); })); }
//======================================================================================== // Constructor //======================================================================================== /// <summary> /// Initialize a new instance of this scaner with the specified iTunes interface. /// </summary> /// <param name="itunes"></param> /// <param name="catalog"></param> public DuplicateScanner(Controller controller, ICatalog catalog) : base(Resx.I_ScanDuplicates, controller, catalog) { base.description = Resx.ScanDuplicates; this.albumFilter = null; this.artistFilter = null; this.playlistFilter = PersistentID.Empty; }
/// <summary> /// Removes one or more tracks from an existing playlist in the catalog. /// </summary> private void RemoveTracksFromPlaylist() { PersistentID playlistPID = controller.GetPersistentID(action.PlaylistOID); if (!playlistPID.IsEmpty) { catalog.RefreshPlaylist(playlistPID); } }
//======================================================================================== // Constructor //======================================================================================== /// <summary> /// Initialize a new instance of this scanner with the specified iTunes interface. /// </summary> /// <param name="itunes"></param> /// <param name="catalog"></param> public PhantomScanner(Controller controller, ICatalog catalog) : base(Resx.I_ScanPhantoms, controller, catalog) { base.description = Resx.ScanPhantoms; this.albumFilter = null; this.artistFilter = null; this.playlistFilter = PersistentID.Empty; }
/// <summary> /// Retrieves a list of all tracks found in the given playlist. /// </summary> /// <param name="playlistID">The unique PersistentID of the playlist to examine.</param> /// <returns></returns> /// <remarks> /// iTunes allows users to create multiple playlists with the same name. So we /// must use the PersistentID of the playlist instead of its canonical name. /// </remarks> public override PersistentIDCollection FindTracksByPlaylist(PersistentID playlistID) { if (!isReady) { return(new PersistentIDCollection()); } // find the <plist><dict><key>Playlists</key><array> root node var playlistRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Playlists" select node.NextNode; // find the parent <array><dict> node of the named playlist // <array><dict><key>Name</key><string>Library</string> var playlistNodes = from node in ((XElement)playlistRoot.Single()) .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Playlist Persistent ID" && ((XElement)node.NextNode).Value == (string)playlistID select node.Parent; // collect all Track ID values from this playlist var trackIDs = from node in ((XElement)playlistNodes.Single()) .Elements(ns + "array") .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Track ID" select node.NextNode; // find the <plist><dict><key>Tracks</key><dict> root node var trackRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Tracks" select node.NextNode; // join tracks on trackID to extract the persistent IDs var tracks = from node in (from node in ((XElement)trackRoot.Single()).Elements(ns + "key") join id in trackIDs on((XElement)node).Value equals((XElement)id).Value select((XElement)node.NextNode) ).Elements(ns + "key") where ((XElement)node).Value == "Persistent ID" select PersistentID.Parse(((XElement)node.NextNode).Value); PersistentIDCollection list = new PersistentIDCollection(tracks.ToList <PersistentID>()); return(list); }
//======================================================================================== // Constructor //======================================================================================== /// <summary> /// Initialize a new instance of this scanner with the specified iTunes interface. /// </summary> /// <param name="itunes"></param> /// <param name="catalog"></param> public PhantomScanner(Controller controller, ICatalog catalog) : base(Resx.I_ScanPhantoms, controller, catalog) { base.description = Resx.ScanPhantoms; base.tooltip = String.Empty; this.albumFilter = null; this.artistFilter = null; this.playlistFilter = PersistentID.Empty; }
private string pendingLyrics; // pending lyrics to apply to track //======================================================================================== // Constructor //======================================================================================== /// <summary> /// Initialize a new instance that wraps the given iTunes playlist COM object. /// </summary> /// <param name="track">An IITTrack instance.</param> public Track(IITTrack track) : base() { this.track = track; this.persistentID = PersistentID.Empty; this.artist = null; this.location = null; this.uniqueID = null; this.isBuffered = false; this.buffer = null; }
/// <summary> /// Update the specified track with the given location fullpath. /// </summary> /// <param name="persistentID"></param> /// <param name="path"></param> /// <returns> /// The iTunes imposed full path of the track. /// </returns> private string RenameTrackLocation(PersistentID persistentID, string path) { Track track = controller.LibraryPlaylist.GetTrack(persistentID); string realPath = path; if (track != null) { Logger.WriteLine(base.name, String.Format( "Updating track location {0} to {1}", track.MakeKey(), path)); try { if (ScannerBase.isLive) { // Note that this WILL set the path correctly, however, if iTunes is // set to automatically manage the library, it will IMMEDIATELY // rename the file to its prescribed correct name via ID3 tags... // In other words, even if you manually renamed the file // "Aerosmith\Dream On.mp3" to 'Aerosmith\Dream_On_1.mp3", iTunes will // immediately rename it back to "Aerosmith\Dream On.mp3" track.Location = path; // so now get the track again, otherwise it's not immediately refreshed, // and grab the real path applied by iTunes track = controller.LibraryPlaylist.GetTrack(persistentID); realPath = track.Location; } } catch (Exception exc) { Logger.WriteLine(base.name, "Error changing track location for " + track.MakeKey(), exc); } finally { track.Dispose(); track = null; } } return(realPath); }
/// <summary> /// Retrieves a list of all tracks by the specivied artist on the named album. /// </summary> /// <param name="album">The name of the album.</param> /// <param name="artist">The name of the artist.</param> /// <returns></returns> public override PersistentIDCollection FindTracksByAlbum(string album, string artist) { if (!isReady) { return(new PersistentIDCollection()); } album = album.Trim().ToLower(); artist = artist.Trim().ToLower(); // find the <plist><dict><key>Tracks</key><dict> root node var trackRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Tracks" select node.NextNode; // Tracks/dict is the container for all tracks where each is a key/dict pair // collect all dict elements related to specified album var albumTracks = from node in (from node in ((XElement)trackRoot.Single()) .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Album" && ((XElement)node.NextNode).Value.Trim().ToLower() == album select node.Parent ).Elements(ns + "key") where node.Value == "Artist" && ((XElement)node.NextNode).Value.Trim().ToLower() == artist select node.Parent; // collect all persistent IDs from these dict elements var tracks = from node in albumTracks.Elements(ns + "key") where node.Value == "Persistent ID" select PersistentID.Parse(((XElement)node.NextNode).Value); PersistentIDCollection list = new PersistentIDCollection(tracks.ToList <PersistentID>()); return(list); }
/// <summary> /// Find the name of the playlist specified by its persistent ID. /// </summary> /// <param name="playlistID">The unique persisent ID of the playlist to find.</param> /// <returns></returns> public override string FindPlaylistName(PersistentID playlistID) { if (!isReady) { return(null); } // find the <plist><dict><key>Playlists</key><array> root node var playlistRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Playlists" select node.NextNode; // find the parent <array><dict> node of the named playlist // <array><dict><key>Name</key><string>Library</string> var playlistNodes = from node in ((XElement)playlistRoot.Single()) .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Playlist Persistent ID" && ((XElement)node.NextNode).Value == (string)playlistID select node.Parent; if (playlistNodes == null) { return(null); } string name = (from node in ((XElement)playlistNodes.Single()) .Elements(ns + "key") where node.Value == "Name" select((XElement)node.NextNode).Value).FirstOrDefault() as String; return(name); }
/// <summary> /// Retrieves a list of all tracks found in the given playlist. /// </summary> /// <param name="playlistID">The unique PersistentID of the playlist to examine.</param> /// <returns></returns> /// <remarks> /// iTunes allows users to create multiple playlists with the same name. So we /// must use the PersistentID of the playlist instead of its canonical name. /// </remarks> public override PersistentIDCollection FindTracksByPlaylist(PersistentID playlistID) { if (!isReady) { return new PersistentIDCollection(); } // find the <plist><dict><key>Playlists</key><array> root node var playlistRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Playlists" select node.NextNode; // find the parent <array><dict> node of the named playlist // <array><dict><key>Name</key><string>Library</string> var playlistNodes = from node in ((XElement)playlistRoot.Single()) .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Playlist Persistent ID" && ((XElement)node.NextNode).Value == (string)playlistID select node.Parent; // collect all Track ID values from this playlist var trackIDs = from node in ((XElement)playlistNodes.Single()) .Elements(ns + "array") .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Track ID" select node.NextNode; // find the <plist><dict><key>Tracks</key><dict> root node var trackRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Tracks" select node.NextNode; // join tracks on trackID to extract the persistent IDs var tracks = from node in (from node in ((XElement)trackRoot.Single()).Elements(ns + "key") join id in trackIDs on ((XElement)node).Value equals ((XElement)id).Value select ((XElement)node.NextNode) ).Elements(ns + "key") where ((XElement)node).Value == "Persistent ID" select PersistentID.Parse(((XElement)node.NextNode).Value); PersistentIDCollection list = new PersistentIDCollection(tracks.ToList<PersistentID>()); return list; }
/// <summary> /// Update the specified track with the given location fullpath. /// </summary> /// <param name="persistentID"></param> /// <param name="path"></param> /// <returns> /// The iTunes imposed full path of the track. /// </returns> private string RenameTrackLocation(PersistentID persistentID, string path) { Track track = controller.LibraryPlaylist.GetTrack(persistentID); string realPath = path; if (track != null) { Logger.WriteLine(base.name, String.Format( "Updating track location {0} to {1}", track.MakeKey(), path)); try { if (ScannerBase.isLive) { // Note that this WILL set the path correctly, however, if iTunes is // set to automatically manage the library, it will IMMEDIATELY // rename the file to its prescribed correct name via ID3 tags... // In other words, even if you manually renamed the file // "Aerosmith\Dream On.mp3" to 'Aerosmith\Dream_On_1.mp3", iTunes will // immediately rename it back to "Aerosmith\Dream On.mp3" track.Location = path; // so now get the track again, otherwise it's not immediately refreshed, // and grab the real path applied by iTunes track = controller.LibraryPlaylist.GetTrack(persistentID); realPath = track.Location; } } catch (Exception exc) { Logger.WriteLine(base.name, "Error changing track location for " + track.MakeKey(), exc); } finally { track.Dispose(); track = null; } } return realPath; }
/// <summary> /// Reloads the specified playlist from the Library XML file. /// </summary> /// <param name="playlistPID">The persistent ID of the playlist to refresh.</param> public abstract void RefreshPlaylist(PersistentID playlistPID);
/// <summary> /// Retrieves a list of all tracks found in the given playlist. /// </summary> /// <param name="playlistID">The unique PersistentID of the playlist to examine.</param> /// <returns></returns> /// <remarks> /// iTunes allows users to create multiple playlists with the same name. So we /// must use the PersistentID of the playlist instead of its canonical name. /// </remarks> public abstract PersistentIDCollection FindTracksByPlaylist(PersistentID playlistID);
/// <summary> /// Determines if this value is equal to the given value. /// </summary> /// <param name="other"></param> /// <returns></returns> public bool Equals(PersistentID other) { return(this.persistentID.Equals(other.persistentID)); }
/// <summary> /// Adds one or more tracks to the specified playlist. /// </summary> /// <param name="playlistPID"></param> /// <param name="trackPIDs"></param> public abstract void AddTracksToPlaylist( PersistentIDCollection trackPIDs, PersistentID playlistPID);
/// <summary> /// Reloads the specified playlist from the Library XML file. /// </summary> /// <param name="playlistPID">The persistent ID of the playlist to refresh.</param> public override void RefreshPlaylist(PersistentID playlistPID) { }
/// <summary> /// Adds a track entry to the specified playlist. /// </summary> /// <param name="playlistPID"></param> /// <param name="trackPIDs"></param> public override void AddTracksToPlaylist( PersistentIDCollection trackPIDs, PersistentID playlistPID) { }
/// <summary> /// Find the name of the playlist specified by its persistent ID. /// </summary> /// <param name="playlistID">The unique persisent ID of the playlist to find.</param> /// <returns></returns> public abstract string FindPlaylistName(PersistentID playlistID);
//======================================================================================== // Methods //======================================================================================== #region Queries /// <summary> /// Retrieves a list of all musical file extensions found in the given playlist. /// </summary> /// <param name="playlistID">The unique PersistentID of the playlist to examine.</param> /// <returns></returns> /// <remarks> /// iTunes allows users to create multiple playlists with the same name. So we /// must use the PersistentID of the playlist instead of its canonical name. /// </remarks> public override StringCollection FindExtensionsByPlaylist(PersistentID playlistID) { if (!isReady) { return(new StringCollection()); } // find the <plist><dict><key>Playlists</key><array> root node var playlistRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Playlists" select node.NextNode; // find the parent <array><dict> node of the named playlist // <array><dict><key>Name</key><string>Library</string> var playlistNodes = from node in ((XElement)playlistRoot.Single()) .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Playlist Persistent ID" && ((XElement)node.NextNode).Value == (string)playlistID select node.Parent; // collect all Track ID values from this playlist var trackIDs = from node in ((XElement)playlistNodes.Single()) .Elements(ns + "array") .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Track ID" select node.NextNode; // find the <plist><dict><key>Tracks</key><dict> root node var trackRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Tracks" select node.NextNode; // join tracks on trackID to extract the distinct extensions var extensions = from node in (from node in ((XElement)trackRoot.Single()).Elements(ns + "key") join id in trackIDs on((XElement)node).Value equals((XElement)id).Value select((XElement)node.NextNode) ).Elements(ns + "key") where ((XElement)node).Value == "Location" select Path.GetExtension(((XElement)node.NextNode).Value).ToLower(); StringCollection list = null; if (extensions != null) { list = FilterMusicalExtensions(extensions.Distinct()); } return(list); }
/// <summary> /// Find the name of the playlist specified by its persistent ID. /// </summary> /// <param name="playlistID">The unique persisent ID of the playlist to find.</param> /// <returns></returns> public override string FindPlaylistName(PersistentID playlistID) { if (!isReady) { return null; } // find the <plist><dict><key>Playlists</key><array> root node var playlistRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Playlists" select node.NextNode; // find the parent <array><dict> node of the named playlist // <array><dict><key>Name</key><string>Library</string> var playlistNodes = from node in ((XElement)playlistRoot.Single()) .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Playlist Persistent ID" && ((XElement)node.NextNode).Value == (string)playlistID select node.Parent; if (playlistNodes == null) { return null; } string name = (from node in ((XElement)playlistNodes.Single()) .Elements(ns + "key") where node.Value == "Name" select ((XElement)node.NextNode).Value).FirstOrDefault() as String; return name; }
/// <summary> /// Retrieves a list of all musical file extensions found in the given playlist. /// </summary> /// <param name="playlistID">The unique PersistentID of the playlist to examine.</param> /// <returns></returns> /// <remarks> /// iTunes allows users to create multiple playlists with the same name. So we /// must use the PersistentID of the playlist instead of its canonical name. /// </remarks> public abstract StringCollection FindExtensionsByPlaylist(PersistentID playlistID);
/// <summary> /// Retrieves a list of all musical file extensions found in the given playlist. /// </summary> /// <param name="playlistID">The unique PersistentID of the playlist to examine.</param> /// <returns></returns> /// <remarks> /// iTunes allows users to create multiple playlists with the same name. So we /// must use the PersistentID of the playlist instead of its canonical name. /// </remarks> public override StringCollection FindExtensionsByPlaylist(PersistentID playlistID) { if (!isReady) { return new StringCollection(); } // find the <plist><dict><key>Playlists</key><array> root node var playlistRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Playlists" select node.NextNode; // find the parent <array><dict> node of the named playlist // <array><dict><key>Name</key><string>Library</string> var playlistNodes = from node in ((XElement)playlistRoot.Single()) .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Playlist Persistent ID" && ((XElement)node.NextNode).Value == (string)playlistID select node.Parent; // collect all Track ID values from this playlist var trackIDs = from node in ((XElement)playlistNodes.Single()) .Elements(ns + "array") .Elements(ns + "dict") .Elements(ns + "key") where node.Value == "Track ID" select node.NextNode; // find the <plist><dict><key>Tracks</key><dict> root node var trackRoot = from node in root .Element(ns + "dict") .Elements(ns + "key") where node.Value == "Tracks" select node.NextNode; // join tracks on trackID to extract the distinct extensions var extensions = from node in (from node in ((XElement)trackRoot.Single()).Elements(ns + "key") join id in trackIDs on ((XElement)node).Value equals ((XElement)id).Value select ((XElement)node.NextNode) ).Elements(ns + "key") where ((XElement)node).Value == "Location" select Path.GetExtension(((XElement)node.NextNode).Value).ToLower(); StringCollection list = FilterMusicalExtensions(extensions.Distinct()); return list; }
/// <summary> /// Gets the track specified by its persistent ID. /// </summary> public Track GetTrack(PersistentID persistentID) { return Invoke((Func<Track>)delegate { return new Track(playlist.Tracks .get_ItemByPersistentID(persistentID.HighBits, persistentID.LowBits)); }); }
/// <summary> /// /// </summary> private void ExecuteInternal() { var playlist = controller.CreatePlaylist( Path.GetFileNameWithoutExtension(playlistPath)); if (playlist == null) { Logger.WriteLine(Logger.Level.Error, base.name, "Error creating playlist"); return; } int count = 0; // tracks successfully imported int failed = 0; // tracks failed import int numFound = 0; // tracks found in library int numRetrieved = 0; // tracks retrieved from GenPUID/MusicBrainz int numSimilar = 0; // tracks identified using similar artists int numFailed = 0; // tracks unidentified string path; string description = String.Empty; Tagger tagger = null; while ((path = reader.GetNext()) != null) { if (!base.isActive) { // scanner cancelled by user break; } if (!File.Exists(path)) { Logger.WriteLine(base.name, "Could not find " + path); continue; } base.UpdateProgress(Path.GetFileNameWithoutExtension(path)); var trackFile = new TrackFile(path); Tagger.ReadFileTags(trackFile); if (String.IsNullOrEmpty(trackFile.Title) || (String.IsNullOrEmpty(trackFile.Album) && String.IsNullOrEmpty(trackFile.Artist))) { Logger.WriteLine(Logger.Level.Debug, base.name, "retrieving tag for " + trackFile.Location); if (tagger == null) { tagger = new Tagger(); } tagger.RetrieveTags(trackFile); numRetrieved++; } PersistentID pid = catalog.FindTrack( trackFile.Album, trackFile.Artist, trackFile.Title); if (pid.IsEmpty) { numFailed++; Logger.WriteLine(base.name, String.Format("Could not find track '{0}', '{1}', '{2}'", trackFile.Artist, trackFile.Album, trackFile.Title)); continue; } Track track = controller.LibraryPlaylist.GetTrack(pid); numFound++; if (track.Artist.Equals(trackFile.Artist)) { Logger.WriteLine(Logger.Level.Debug, base.name, String.Format("Found track ({0} | {1} | {2})", track.Artist, track.Album, track.Title)); } else { Logger.WriteLine(Logger.Level.Debug, base.name, String.Format("Found similar track ({0} | {1} | {2})", track.Artist, track.Album, track.Title)); numSimilar++; } try { track = playlist.AddTrack(track); if (track == null) { failed++; Logger.WriteLine(Logger.Level.Error, base.name, String.Format("Error importing {0}", path)); } else { description = String.Format("{0}, {1}", track.Name, track.Artist); count++; Logger.WriteLine(base.name, String.Format("Imported {0}", description)); } } catch (Exception) { // TODO: ? } finally { track.Dispose(); } } base.UpdateProgress(Resx.Completed); playlist.Dispose(); Logger.WriteLine(base.name, String.Format("- found:{0}, failed:{1}, similar:{2}, retrieved:{3}", numFound, numFailed, numSimilar, numRetrieved)); }
/// <summary> /// /// </summary> /// <param name="persistentID"></param> private void WaitUntilAvailable(PersistentID persistentID) { Track track = null; try { // if we're currently in a protection fault then this will block until // the user dismisses the dialog... track = controller.LibraryPlaylist.GetTrack(persistentID); } catch (Exception exc) { Logger.WriteLine( base.name, "Error while waiting for Protected fault to recover", exc); } track.Dispose(); track = null; }
/// <summary> /// Determines if this value is equal to the given value. /// </summary> /// <param name="other"></param> /// <returns></returns> public bool Equals(PersistentID other) { return this.persistentID.Equals(other.persistentID); }