/// <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); } } }
//======================================================================================== // Methods //======================================================================================== /// <summary> /// Execute the scanner. /// </summary> public override void Execute() { if (!NetworkStatus.IsAvailable) { Logger.WriteLine(base.name, "Information scanner skipped, network not available"); return; } Logger.WriteLine(base.name, String.Format( "Information scanner beginning for {0} tracks on '{1}'/'{2}'", pids.Count, pids.Artist, pids.Album)); ScanTracks(pids); libraryPlaylist = null; pids.Clear(); pids = null; if (base.isActive) { Logger.WriteLine(base.name, "Information scanner completed"); } else { Logger.WriteLine(base.name, "Information scanner cancelled"); } }
/// <summary> /// Export the specified tracks to a given directory using the specified encoder. /// </summary> /// <param name="tracks"></param> /// <param name="encoder"></param> /// <param name="location"></param> /// <param name="createSubdir"></param> public void Export( PersistentIDCollection list, Encoder encoder, string playlistFormat, string location, bool createSubdirectories) { lock (sync) { disabled.Add(Resx.I_ScanDuplicates); disabled.Add(Resx.I_ScanFileWatch); disabled.Add(Resx.I_ScanMaintenance); } string pathFormat = null; if (createSubdirectories) { pathFormat = PathHelper.GetPathFormat(ExportScanner.DefaultPathFormat); } IScanner scanner = new ExportScanner( controller, catalog, list, encoder, playlistFormat, location, pathFormat, false); scanner.Completed = (Action) delegate { lock (sync) { disabled.Remove(Resx.I_ScanDuplicates); disabled.Remove(Resx.I_ScanFileWatch); disabled.Remove(Resx.I_ScanMaintenance); } }; AddScanner(scanner, ExportScannerPriority); }
/// <summary> /// Fix missing and incorrect information for all tracks in the current album. /// </summary> public void FixInformation(PersistentIDCollection list) { if (IsScannerAllowed(Resx.I_ScanInformation) && NetworkStatus.IsAvailable) { var scanner = new InformationScanner(controller, catalog, list); AddScanner(scanner, FixInformationScannerPriority); } }
/// <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> /// Export the specified tracks to a given USB MP3 player, synchronizing the /// contents of the specified location. /// </summary> /// <param name="list"></param> /// <param name="location"></param> /// <param name="pathFormat"></param> public void Export(PersistentIDCollection list, string location, string pathFormat) { if (disabled.Contains(Resx.I_ScanExport)) { return; } Encoder mp3Encoder = null; foreach (Encoder encoder in controller.Encoders) { // skip the Null encoder that's automatically added to controller.Encoders if (!encoder.IsEmpty) { if (encoder.Format.Equals("MP3")) { mp3Encoder = encoder; } } } if (mp3Encoder != null) { lock (sync) { disabled.Add(Resx.I_ScanDuplicates); disabled.Add(Resx.I_ScanFileWatch); disabled.Add(Resx.I_ScanMaintenance); } IScanner scanner = new ExportScanner( controller, catalog, list, mp3Encoder, PlaylistProviderFactory.NoFormat, location, pathFormat, true); scanner.Completed = (Action) delegate { Encoder mp3 = mp3Encoder; lock (sync) { disabled.Remove(Resx.I_ScanDuplicates); disabled.Remove(Resx.I_ScanFileWatch); disabled.Remove(Resx.I_ScanMaintenance); } mp3 = null; }; AddScanner(scanner, ExportScannerPriority); } // [foo] dispose this reference but do not release underlying instance passed to Librarian //mp3Encoder.Dispose(false); }
//======================================================================================== // Constructor //======================================================================================== /// <summary> /// Initialize a new instance of this scanner with the specified iTunes interface. /// </summary> /// <param name="itunes"></param> /// <param name="catalog"></param> public InformationScanner( Controller controller, ICatalog catalog, PersistentIDCollection pids) : base(Resx.I_ScanInformation, controller, catalog) { this.libraryPlaylist = controller.LibraryPlaylist; base.description = Resx.ScanInformation; base.tooltip = String.Format("{0}/{1}", (pids.Album ?? "?"), (pids.Artist ?? "?")); this.pids = pids; }
/// <summary> /// Scan all tracks in the given playlist. /// </summary> /// <param name="list"></param> private void ScanTracks(PersistentIDCollection pids) { // Artwork must be applied to every track in an album. // Although we may apply artwork to the CurrentTrack, it will not show up in the // iTunes artwork viewer until context is moved to another track and then the user // selects or plays that first track again. foreach (PersistentID persistentID in pids) { if (!base.isActive) { Logger.WriteLine(base.name, "Artwork scanner cancelled while scanning"); break; } using (Track track = libraryPlaylist.GetTrack(persistentID)) { if ((track != null) && (track.Kind == TrackKind.File)) { string album = track.Album; string artist = track.Artist; string key = track.MakeKey(); if (String.IsNullOrEmpty(track.Artwork)) { Logger.WriteLine(base.name, "Fetching album artwork for " + key); try { if (ScannerBase.isLive) { track.Artwork = GetArtworkPath(artist, album); } } catch (Exception exc) { Logger.WriteLine(base.name, String.Format("Error fetching artwork {0}, {1}, {2}", artist, track.Name, album), exc); } } else { Logger.WriteLine(base.name, "Album already has artwork " + key); } } } count++; base.ProgressPercentage = (int)((double)count / (double)total * 100.0); } }
/// <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> /// Scan all tracks in the given playlist. /// </summary> /// <param name="list"></param> private void ScanTracks(PersistentIDCollection pids) { var tagger = new Tagger(); int total = pids.Count; int count = 0; foreach (PersistentID persistentID in pids) { if (!base.isActive) { Logger.WriteLine(base.name, "Information scanner cancelled while scanning"); break; } using (Track track = libraryPlaylist.GetTrack(persistentID)) { if ((track != null) && (track.Kind == TrackKind.File)) { Logger.WriteLine(base.name, String.Format( "Fetching tag information for '{0}' ({1})", track.MakeKey(), track.UniqueID)); try { // store into a temporary TrackFile so we can decide which // properties to update... var buffer = new TrackFile(track); tagger.RetrieveTags(buffer); Reconcile(track, buffer); } catch (Exception exc) { Logger.WriteLine(base.name, String.Format("Error fetching information {0}, {1}, {2}", track.Artist, track.Name, track.Album), exc); } } } count++; base.ProgressPercentage = (int)((double)count / (double)total * 100.0); } tagger = null; }
/// <summary> /// Scan all tracks in the given playlist. /// </summary> /// <param name="list"></param> private void ScanTracks(PersistentIDCollection pids) { foreach (PersistentID persistentID in pids) { if (base.isActive) { using (Track track = libraryPlaylist.GetTrack(persistentID)) { if ((track != null) && (track.Kind == TrackKind.File)) { //Logger.WriteLine(base.name, "ScanTrying " + track.MakeKey()); if (String.IsNullOrEmpty(track.Location) || !File.Exists(track.Location)) { Logger.WriteLine(base.name, "Deleting phantom track " + track.MakeKey()); try { if (ScannerBase.isLive) { // deletes library entry but not physical media file track.Delete(); } } catch (Exception exc) { Logger.WriteLine(base.name, String.Format("Error deleting phantom {0}, {1}, {2}", track.Artist, track.Name, track.Album), exc); } } } } count++; base.ProgressPercentage = (int)((double)count / (double)total * 100.0); } else { Logger.WriteLine(base.name, "Phantom scanner cancelled while scanning"); break; } } }
/// <summary> /// Removes a playlist from the catalog. /// </summary> private void RemovePlaylist() { PersistentIDCollection playlistPIDs = new PersistentIDCollection(); foreach (Playlist playlist in controller.Playlists.Values) { if (playlist != null) { playlistPIDs.Add(playlist.PersistentID); playlist.Dispose(); } } if (playlistPIDs.Count > 0) { catalog.RefreshPlaylists(playlistPIDs); } }
/// <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); }
//======================================================================================== // Constructor //======================================================================================== /// <summary> /// Initialize a new instance of this scanner. /// </summary> /// <param name="controller">The iTunesAppClass to use.</param> /// <param name="catalog"></param> /// <param name="exportPIDs"> /// The list of persistent track IDs. When exporting multiple playlists, this list /// should include all tracks from all playlists where each track indicates its own /// source playlist. This allows comprehensive/overall feedback for UI progress bars. /// </param> /// <param name="encoder">The encoder to use to convert tracks.</param> /// <param name="playlistFormat"> /// The playlist output format or PlaylistProviderFactory.NoFormat for no playlist file. /// </param> /// <param name="location"> /// The target root directory path to which all entries will be copied or converted. /// </param> /// <param name="pathFormat"> /// The path format string as specified in FolderFormts.xml or null for title only. /// </param> /// <param name="isSynchronized"> /// True if target location should be synchronized, removing entries in the target /// location that are not included in the exportPIDs collection. /// </param> public ExportScanner( Controller controller, ICatalog catalog, PersistentIDCollection exportPIDs, Encoder encoder, string playlistFormat, string location, string pathFormat, bool isSynchronized) : base(Resx.I_ScanExport, controller, catalog) { base.description = isSynchronized ? Resx.ScanSynchronize : Resx.ScanExport; base.tooltip = String.Format(Resx.ExportingCount, exportPIDs.Count); this.exportPIDs = exportPIDs; this.encoder = encoder; this.expectedType = (encoder == null ? String.Empty : encoder.ExpectedType); this.playlistFormat = playlistFormat; this.playlistName = exportPIDs.Name; this.location = location; this.createSubdirectories = (pathFormat != null); this.pathFormat = pathFormat ?? PathHelper.GetPathFormat(FlatPathFormat); this.isSynchronized = isSynchronized; this.exports = null; this.writer = null; }
/// <summary> /// Verifies playlists in the catalog against a list of known existing playlists. /// </summary> /// <param name="playlistPIDs"> /// A collection of existing playlist persistent IDs; any playlist not referenced /// in the list will be removed from the catalog. /// </param> public abstract void RefreshPlaylists(PersistentIDCollection playlistPIDs);
/// <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> /// Verifies playlists in the catalog against a list of known existing playlists. /// </summary> /// <param name="playlistPIDs"> /// A collection of existing playlist persistent IDs; any playlist not referenced /// in the list will be removed from the catalog. /// </param> public override void RefreshPlaylists(PersistentIDCollection playlistPIDs) { }
/// <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) { }