using System; using System.IO; using System.Threading; using Muine.PluginLib; using IPod; using Gtk; public class Ipod : Plugin { private IPlayer player; private ThreadNotify notify; private Exception syncError = null; private Device deviceToSync = null; public override void Initialize (IPlayer player) { this.player = player; // Add the menu item. Thanks to Brian Nickel and his Cuckoo plugin for showing me The Way. ActionEntry [] action_entries = new Gtk.ActionEntry [] { new ActionEntry ("FillIpod", Gtk.Stock.Properties, "Fill _iPod ", "I", null, new EventHandler (OnFillIpod)) }; ActionGroup action_group = new ActionGroup ("IpodPluginActions"); action_group.Add (action_entries); player.UIManager.InsertActionGroup (action_group, -1); player.UIManager.AddUi (this.player.UIManager.NewMergeId (), "/MenuBar/FileMenu/ExtraFileActions", "FillIpodMenuItem", "FillIpod", UIManagerItemType.Menuitem, false); notify = new ThreadNotify (new ReadyEvent (OnNotified)); } private bool IsEmpty () { if (player.Playlist.Length == 0) { syncError = new ApplicationException ("Please add at least one song to the playlist."); OnNotified (); return true; } else return false; } private bool IpodPresent () { Device [] devices = Device.ListDevices (); if (devices.Length == 0) { deviceToSync = null; syncError = new ApplicationException ("Please plug in your iPod."); OnNotified (); return false; } else return true; } private void GetDevice () { DeviceCombo combo = new DeviceCombo (); if (combo.Model.IterNChildren () > 1) { DeviceDialog dialog = new DeviceDialog (player.Window, Gtk.DialogFlags.DestroyWithParent); dialog.VBox.PackStart (combo); dialog.ShowAll (); int response = dialog.Run (); if (response == (int)ResponseType.Ok) deviceToSync = combo.ActiveDevice; else deviceToSync = null; dialog.Destroy (); } else deviceToSync = combo.ActiveDevice; } private void OnFillIpod (object sender, EventArgs e) { // No use syncing if there's nothing to sync or no iPod is plugged... if (IsEmpty () || !IpodPresent ()) return; GetDevice (); Thread thr = new Thread (new ThreadStart (FillIpod)); thr.Start (); } private void FillIpod () { try { if (deviceToSync != null) { lock (deviceToSync) { Fill (); } } } catch (Exception e) { Console.WriteLine (e); syncError = e; notify.WakeupMain (); } finally { notify.WakeupMain (); } } private void Fill () { player.BusyLevel++; TrackDatabase db = deviceToSync.TrackDatabase; Playlist playlist = GetPlaylist (db); // Remove everything in the playlist foreach (Track track in playlist.Tracks) { db.RemoveTrack (track); } foreach (ISong song in player.Playlist) { string ext = Path.GetExtension (song.Filename); // Check for supported extensions. // I couldn't find a method in ipod-sharp to check what is exactly supported. if ((ext.ToLower () == ".mp3") || (ext.ToLower () == ".mp4") || (ext.ToLower () == ".m4a")) { Track track = db.CreateTrack (); if (song.Artists.Length == 0) track.Artist = "Unknown"; else // I know it's ugly but I don't have a better idea about how to handle multiple artists. track.Artist = String.Join (" & ", song.Artists); if (song.Title == String.Empty) track.Title = "Unknown"; else track.Title = song.Title; if (song.Album == String.Empty) track.Album = "Unknown"; else track.Album = song.Album; track.FileName = song.Filename; track.Duration = new TimeSpan (0, 0, song.Duration); track.TrackNumber = song.TrackNumber; // FIXME : Add cover art support. // I don't really care myself (crappy iPod mini 1st gen), but it doesn't look very hard to do. playlist.AddTrack (track); } } // The progress dialog automatically shows up as we save the db. ProgressDialog pdialog = new ProgressDialog (player.Window); pdialog.TrackDatabase = db; SaveDB (db); pdialog.Destroy (); player.BusyLevel--; } private Playlist GetPlaylist (TrackDatabase db) { Playlist playlist = db.LookupPlaylist ("Muine"); if (playlist == null) return db.CreatePlaylist ("Muine"); else return playlist; } private void SaveDB (TrackDatabase db) { lock (db) { db.Save (); } } private void OnNotified () { if (syncError != null) { lock (this) { ErrorDialog dialog = new ErrorDialog (syncError.Message, player.Window); dialog.Run (); syncError = null; } } } } public class DeviceDialog : Dialog { public DeviceDialog (Window w, DialogFlags f) : base ("Choose your device", w, f) { this.Modal = true; this.AddButton ("Ok", ResponseType.Ok); this.AddButton ("Cancel", ResponseType.Cancel); } } public class ErrorDialog { MessageDialog dialog; public void Setup (string text) { string heading = "An error occurred:"; string full_text = heading + "\n\n" + text; dialog = new MessageDialog (null, DialogFlags.DestroyWithParent, MessageType.Error, ButtonsType.Close, full_text); } public ErrorDialog (string text) { Setup (text); } public ErrorDialog (string text, Window parent) { Setup (text); dialog.TransientFor = parent; } public void Run () { dialog.Run (); dialog.Destroy (); } }