/// <summary> /// Method for dispatching device connected events. /// </summary> /// <param name="drive">The drive where the device is connected.</param> /// <param name="device">Info on the connected device.</param> protected void OnDeviceConnected(DriveInfo drive, Device device) { if (DeviceConnected != null) { CDMEventArgs args = new CDMEventArgs(drive, device); DeviceConnected(this, args); } }
/// <summary> /// Checks if an iTunes playlist exists. /// </summary> /// <param name="device">Device to check playlist for.</param> /// <returns>The playlist if it exists, null otherwise.</returns> private List<IITPlaylist> PlaylistExists(Device device) { string playlistDelimString = (device.Playlist == null || device.Playlist.Length == 0) ? device.Name : device.Playlist; List<string> aplList = new List<string>(playlistDelimString.Split('|')); List<IITPlaylist> aplIIT = new List<IITPlaylist>(); bool retry = true; while (retry) { try { for (int i = 0; i < aplList.Count; i++) { foreach (IITPlaylist playlist in itunes.LibrarySource.Playlists) { if (playlist.Name == aplList[i]) { aplIIT.Add(playlist); break; } } } return aplIIT; } catch (Exception comex) { l.Warn(comex); if (MessageBox.Show("Failed to get playlists from iTunes. This is most likely due to iTunes being busy or an open dialog. Do you want to try again?\n\n(" + comex.Message + ")", "Communication error", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { retry = true; } else { retry = false; } continue; } } return null; }
/// <summary> /// Remove a Device. /// </summary> /// <param name="d">Device to remove.</param> /// <returns></returns> public bool RemoveDevice(Device d) { return devices.Remove(d); }
/// <summary> /// Add a device. /// </summary> /// <param name="d">Device to add.</param> public void AddDevice(Device d) { devices.Add(d); }
/// <summary> /// Event handler for the save button. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonSave_Click(object sender, EventArgs e) { string deviceName = textDeviceName.Text; string syncPattern = (string)comboSyncPatterns.SelectedItem; string mediaroot = textMediaRoot.Text; string recognizePattern = textRecognizePattern.Text; string associatedPlaylist = (string)comboAssociatePlaylist.SelectedItem; if (deviceName.Length == 0) { MessageBox.Show(this, "Please enter a name for the device.", "Missing information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (syncPattern == null) { MessageBox.Show(this, "Please select a synchronize pattern for the device.", "Missing information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (recognizePattern.Length == 0) { MessageBox.Show(this, "Please enter a recognize pattern for the device.", "Missing information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } Device newDevice = new Device(); newDevice.Name = deviceName; newDevice.MediaRoot = mediaroot; newDevice.RecognizePattern = recognizePattern; newDevice.Playlist = (associatedPlaylist == "Use device name..." ? "" : associatedPlaylist); foreach (SyncPattern sp in deviceConfiguration.SyncPatterns) { if (sp.Name != syncPattern) continue; newDevice.SyncPattern = sp.Identifier; } //Add new device if (textDeviceName.Enabled) { //Check that the name does not already exist foreach (Device device in deviceConfiguration.Devices) { if (device.Name != deviceName) continue; MessageBox.Show(this, "There is already a device configuration with the name '" + deviceName + "'. Please enter a unique name.", "Missing information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } deviceConfiguration.AddDevice(newDevice); listDevices.Items.Add(newDevice.Name); } //Update existing device else { foreach (Device device in deviceConfiguration.Devices) { if (device.Name != newDevice.Name) continue; device.MediaRoot = newDevice.MediaRoot; device.RecognizePattern = newDevice.RecognizePattern; device.SyncPattern = newDevice.SyncPattern; device.Playlist = newDevice.Playlist; break; } } deviceConfigurationChanged = true; SaveDeviceConfiguration(); PrepareForNewDeviceConfiguration(); DisableEditFields(); MessageBox.Show(this, "New device configuration registered successfully.", "New device configuration", MessageBoxButtons.OK, MessageBoxIcon.Information); }
/// <summary> /// <see cref="Notpod.ISynchronizer#SynchronizeDevice(IITUserPlaylist, string, Device)"/> /// </summary> public void SynchronizeDevice(IITUserPlaylist playlist, string drive, Device device) { //Check that configuration has been set. if (configuration == null) throw new SynchronizeException("Configuration has not been set."); DirectoryInfo di = new DirectoryInfo(drive + device.MediaRoot); // Check if the media root directory actually exists // Thanks to Robert Grabowski for the contribution. try { if (!di.Exists) di.Create(); } catch (IOException ex) { string message = "Could not create directory '" + di.Name + "' for device '" + device.Name + "'. Unable to complete synchronization."; l.Error(message, ex); syncForm.AddLogText(message, Color.Red); } // Perform a write check to make sure Notpod has write // access to the music folder of the device. String writeCheckPath = drive + device.MediaRoot + "\\wrtchk.ita"; try { FileStream writeCheckStream = File.Create(writeCheckPath); writeCheckStream.Close(); File.Delete(writeCheckPath); } catch (Exception e) { l.Error("Could not write " + writeCheckPath + ".", e); String message = "Error: I am unable to write to the music folder of " + "your device. Please make sure I have the proper permissions" + " and try again."; syncForm.AddLogText(message, Color.Red); return; } FileInfo[] files = FileHelper.GetFilesRecursive(di.ToString()).ToArray(); //Find correct synchronize pattern for the device. SyncPattern devicePattern = null; foreach (SyncPattern sp in configuration.SyncPatterns) { if (sp.Identifier == device.SyncPattern) devicePattern = sp; } //Throw an exception if the pattern could not be found. if (devicePattern == null) { OnSynchronizeError(device, "Illegal synchronize pattern '" + device.SyncPattern + "' for device '" + device.Name + "'. Unable to complete synchronization."); return; } syncForm.AddLogText("Synchronizing '" + device.Name + "'..."); syncForm.SetDeviceName(device.Name, drive); syncForm.SetCurrentStatus("Initializing..."); syncForm.SetMaxProgressValue(playlist.Tracks.Count); syncForm.SetProgressValue(0); // maintain a filename -> track object dictionary for the tracks to be copied onto the device // Thanks to Robert Grabowski for the contribution. Dictionary<string, IITFileOrCDTrack> syncList = new Dictionary<string, IITFileOrCDTrack>(); string deviceMediaRoot = drive + (device.MediaRoot.Length > 0 ? device.MediaRoot + "\\" : ""); try { foreach (IITTrack track in playlist.Tracks) { if (syncForm.GetOperationCancelled()) { syncForm.SetCurrentStatus("Synchronization cancelled. 0 tracks added, 0 tracks removed."); syncForm.AddLogText("Synchronization cancelled.", Color.OrangeRed); OnSynchronizeCancelled(); return; } syncForm.SetProgressValue(syncForm.GetProgressValue() + 1); //Continue if the track is not of kind "file" or the track is one of the initial tracks on the device. if (track.Kind != ITTrackKind.ITTrackKindFile || device.InitialTracks.Contains(track)) continue; string pathOnDevice = ""; IITTrack addTrack = track; try { pathOnDevice = SyncPatternTranslator.Translate(devicePattern, (IITFileOrCDTrack)addTrack); } catch (Exception ex) { syncForm.AddLogText("An error occured while working with \"" + track.Artist + " - " + track.Name + "\". This may be because the track has been deleted from disk. Look for an exclamation mark" + "next to the track in your playlist.", Color.Orange); continue; } string fullPath = deviceMediaRoot + pathOnDevice; l.Debug(fullPath); // Check if the list already contains a key - this happens in cases where there are duplicate // entries in the playlist for the same track. Although the track may have different locations on // the user's computer, Notpod will not handle this. if (syncList.ContainsKey(fullPath)) { syncForm.AddLogText("You have duplicate listings for " + track.Artist + " - " + track.Name + " in your playlist. I will continue for now, but you should remove any duplicates " + "when the synchronization is complete.", Color.Orange); continue; } syncList.Add(fullPath, (IITFileOrCDTrack)addTrack); } } catch (Exception ex) { syncForm.SetCurrentStatus(""); String message = "Error occured while initializing: " + ex.Message; syncForm.AddLogText(message, Color.Red); syncForm.DisableCancelButton(); syncForm.SetProgressValue(0); OnSynchronizeError(device, message); l.Error(message, ex); return; } syncForm.AddLogText("Initialization completed."); syncForm.SetCurrentStatus("Checking tracks. Removing those that are no longer in the playlist..."); int totalTracks = files.Length; syncForm.SetMaxProgressValue(totalTracks); syncForm.SetProgressValue(0); int tracksRemoved = 0; int tracksAdded = 0; long existingSize = 0; try { //Remove tracks from device which are no longer in the playlist. foreach (FileInfo file in files) { l.Debug("Checking file: " + file.FullName); // Check for cancelled operation. if (syncForm.GetOperationCancelled()) { syncForm.SetCurrentStatus("Synchronization cancelled. " + tracksAdded + " track(s) added, " + tracksRemoved + " track(s) removed."); syncForm.AddLogText("Synchronization cancelled.", Color.OrangeRed); OnSynchronizeCancelled(); return; } //Increase progress bar syncForm.SetProgressValue(syncForm.GetProgressValue() + 1); //Continue with next track if it is not of a supported extension. //if (file.Extension != ".mp3" && file.Extension != ".acc" && file.Extension != ".m4p" && file.Extension != ".m4a") if (!extensions.Contains(file.Extension)) continue; if (syncList.ContainsKey(file.FullName)) { FileInfo fi = new FileInfo(file.FullName); existingSize += fi.Length; continue; } //If the track was not found --- delete it! string fileFullName = file.FullName; file.Delete(); l.Debug("Removing file no longer in playlist: " + fileFullName); CheckAndRemoveFolders(fileFullName, drive, device); tracksRemoved++; } syncForm.AddLogText(tracksRemoved + " track(s) was removed from the device.", Color.Orange); } catch (MissingTrackException ex) { syncForm.SetCurrentStatus(""); String message = "You have a missing file in your library. Please clean up " + "your playlist and remove the track '" + ex.Track.Artist + " - " + ex.Track.Name + "' before re-synchronizing."; syncForm.AddLogText(message, Color.Red); syncForm.DisableCancelButton(); syncForm.SetProgressValue(0); OnSynchronizeError(device, message); l.Error(message, ex); return; } catch (Exception ex) { syncForm.SetCurrentStatus(""); String message = "Error occured while checking for deleted tracks: " + ex.Message; syncForm.AddLogText(message, Color.Red); syncForm.DisableCancelButton(); syncForm.SetProgressValue(0); OnSynchronizeError(device, message); l.Error(message, ex); return; } files = null; // Check free space on the device double playlistSize = playlist.Size; DriveInfo driveInfo = new DriveInfo(drive.Substring(0, 1)); long freeOnDisk = driveInfo.AvailableFreeSpace; if (freeOnDisk < playlistSize - existingSize) { string message = "There is not enough space on your device to synchronize the playlist."; OnSynchronizeError(device, message); syncForm.AddLogText(message, Color.Red); syncForm.SetCurrentStatus(message); syncForm.DisableCancelButton(); syncForm.SetProgressValue(0); return; } try { syncForm.SetCurrentStatus("Copying new files..."); syncForm.AddLogText("Preparing to copy new files.", Color.Black); syncForm.SetMaxProgressValue(syncList.Count); syncForm.SetProgressValue(0); //Check for new track in the playlist which should be copied to the device // NEW foreach: traverse synchronization list instead of playlist // Thanks to Robert Grabowski. foreach (string filePath in syncList.Keys) { IITTrack track = syncList[filePath]; // Check for cancelled operation. if (syncForm.GetOperationCancelled()) { syncForm.SetCurrentStatus("Synchronization cancelled. " + tracksAdded + " track(s) added, " + tracksRemoved + " track(s) removed."); syncForm.AddLogText("Synchronization cancelled.", Color.OrangeRed); syncForm.DisableCancelButton(); syncForm.SetProgressValue(0); OnSynchronizeCancelled(); return; } //Increase progress bar syncForm.SetProgressValue(syncForm.GetProgressValue() + 1); string trackPath = filePath.Substring(deviceMediaRoot.Length); // hack: cut out media root l.Debug("Checking for copy: " + filePath); if (File.Exists(filePath)) continue; try { CheckAndCreateFolders(trackPath, drive, device); syncForm.SetCurrentStatus("Copying " + filePath + " (" + syncForm.GetProgressValue() + "/" + syncForm.GetMaxProgressValue() + ")"); File.Copy(((IITFileOrCDTrack)track).Location, filePath, true); File.SetAttributes(filePath, FileAttributes.Normal); syncForm.AddLogText(filePath + " copied successfully.", Color.Green); l.Debug("Copied: " + filePath); } catch (Exception ex) { String message = "Failed to copy " + filePath + ".\n-> " + ex.Message; syncForm.AddLogText(message, Color.Red); OnSynchronizeError(device, message); l.Error(message, ex); return; } tracksAdded++; } } catch (MissingTrackException ex) { syncForm.SetCurrentStatus(""); String message = "You have a missing file in your library. Please remove the track '" + ex.Track.Artist + " - " + ex.Track.Name + "' and try again. I am sorry for the inconvenience."; syncForm.AddLogText(message, Color.Red); syncForm.DisableCancelButton(); syncForm.SetProgressValue(0); OnSynchronizeError(device, message); l.Error(message, ex); return; } catch (Exception ex) { string message = "An error occured while copying new tracks: " + ex.Message; syncForm.SetCurrentStatus(""); syncForm.AddLogText(message, Color.Red); syncForm.DisableCancelButton(); syncForm.SetProgressValue(0); OnSynchronizeError(device, message); l.Error(message, ex); return; } syncForm.SetCurrentStatus("Synchronization completed. " + tracksAdded + " track(s) added, " + tracksRemoved + " track(s) removed."); syncForm.AddLogText("Completed. " + tracksAdded + " track(s) copied to your device.", Color.Green); syncForm.DisableCancelButton(); OnSynchronizeComplete(); }
/// <summary> /// Event dispatcher for SynchronizeError events. /// </summary> /// <param name="device">The device that failed.</param> /// <param name="message">An error message.</param> protected void OnSynchronizeError(Device device, string message) { if (SynchronizeError != null) { SyncErrorArgs args = new SyncErrorArgs(); args.Device = device; args.ErrorMessage = message; SynchronizeError(this, args); } }
/// <summary> /// Check if the folder for the current artist/album is empty. If it is, then remove it. /// </summary> /// <param name="trackPath"></param> /// <param name="drive"></param> /// <param name="device"></param> private void CheckAndRemoveFolders(string trackPath, string drive, Device device) { if (device.MediaRoot.Length == 0) trackPath = trackPath.Replace(drive, ""); else trackPath = trackPath.Replace(drive + device.MediaRoot + "\\", ""); string[] folders = trackPath.Split('\\'); string directoryPath = drive + device.MediaRoot; for (int f = folders.Length - 2; f >= 0; f--) { string parents = ""; for (int pf = 0; pf < f; pf++) parents += "\\" + folders[pf]; string dirToDelete = directoryPath + parents + "\\" + folders[f]; try { DirectoryInfo di = new DirectoryInfo(dirToDelete); if (di.GetFiles().Length == 0 && di.GetDirectories().Length == 0) di.Delete(); } catch (Exception ex) { l.Error(ex); throw new SynchronizeException("Unable to delete empty folder on device.", ex); } } }
/// <summary> /// Check if the necessary folders for the given track exists. If not, create them. /// </summary> /// <param name="trackPath">The path of the track, relative to the device media root.</param> /// <param name="drive">The drive where the device is located.</param> /// <param name="device">Device information.</param> private void CheckAndCreateFolders(string trackPath, string drive, Device device) { string[] folders = trackPath.Split('\\'); string directoryPath = drive + device.MediaRoot; for (int f = 0; f < folders.Length - 1; f++) { string folder = folders[f]; directoryPath += "\\" + folder; if (Directory.Exists(directoryPath)) continue; l.Debug("Creating folder " + directoryPath); Directory.CreateDirectory(directoryPath); } }
/// <summary> /// Checks if an iTunes playlist exists. /// </summary> /// <param name="device">Device to check playlist for.</param> /// <returns>The playlist if it exists, null otherwise.</returns> private IITPlaylist PlaylistExists(Device device) { string name = (device.Playlist == null || device.Playlist.Length == 0) ? device.Name : device.Playlist; bool retry = true; while (retry) { try { foreach (IITPlaylist playlist in itunes.LibrarySource.Playlists) { if (playlist.Name == name) return playlist; } return null; } catch (Exception comex) { l.Warn(comex); if (MessageBox.Show("Failed to get playlists from iTunes. This is most likely due to iTunes being busy or an open dialog. Do you want to try again?\n\n(" + comex.Message + ")", "Communication error", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { retry = true; } else { retry = false; } continue; } } return null; }
/// <summary> /// Method for sending out device disconnected events. /// </summary> /// <param name="driveName">The name of the drive where the device was located.</param> /// <param name="device">Device object describing the device that was disconnected.</param> protected void OnDeviceDisconnected(string driveName, Device device) { if (DeviceDisconnected != null) { CDMEventArgs args = new CDMEventArgs(null, device); DeviceDisconnected(this, driveName, args); } }