]> code.delx.au - notipod/blobdiff - NotiPod.py
Fixed playlist
[notipod] / NotiPod.py
index bb810ba38c50b4c0ff3c077ed8d2794da2b728b3..4bb501dfee0c2ec870f717da1a3fc582cdb02b84 100644 (file)
@@ -2,6 +2,8 @@
 # Copyright 2009 James Bunton <jamesbunton@fastmail.fm>
 # Licensed for distribution under the GPL version 2, check COPYING for details
 
+import logging
+
 import objc
 from Foundation import *
 from AppKit import *
@@ -10,126 +12,216 @@ from PyObjCTools import AppHelper
 import libsyncitunes
 
 
-class PlaylistTableModel(NSObject):
-       tableView = objc.IBOutlet()
+class PlaylistModel(NSObject):
+       outlineView = objc.IBOutlet()
 
        def awakeFromNib(self):
-               self.playlists = []
-               self.tableView.setDataSource_(self)
-       
+               self.root = []
+               self.playlists = {}
+               self.outlineView.setDataSource_(self)
+
        def setPlaylists(self, playlists):
+               self.root = []
                self.playlists = playlists
-               self.tableView.reloadData()
-
-       def tableView_objectValueForTableColumn_row_(self, tableView, col, row):
-               assert 0 <= row < len(self.playlists)
-               return self.playlists[row]
-
-       def numberOfRowsInTableView_(self, tableView):
-               print "get number of rows", len(self.playlists)
-               return len(self.playlists)
-
-
-
-class NotiPodController(NSObject):
-       playlistModel = objc.IBOutlet()
-       folderPopup = objc.IBOutlet()
+               for playlist in self.playlists:
+                       if playlist.parent is None:
+                               self.root.append(playlist)
+               self.outlineView.reloadData()
+               self.outlineView.expandItem_expandChildren_(None, True)
+
+       def outlineView_child_ofItem_(self, _, childIndex, playlist):
+               if playlist == None:
+                       return self.root[childIndex]
+               else:
+                       return playlist.children[childIndex]
+
+       def outlineView_isItemExpandable_(self, _, playlist):
+               if playlist == None:
+                       return True
+               else:
+                       return len(playlist.children) > 0
+
+       def outlineView_numberOfChildrenOfItem_(self, _, playlist):
+               if playlist == None:
+                       return len(self.root)
+               else:
+                       return len(playlist.children)
+
+       def outlineView_objectValueForTableColumn_byItem_(self, _, col, playlist):
+               col = col.identifier() if col else "playlist"
+
+               if col == "selected":
+                       selected = NSApp.delegate().playlists()
+                       return playlist.pid in selected
+               if col == None or col == "playlist":
+                       return playlist.name
+
+       def outlineView_setObjectValue_forTableColumn_byItem_(self, _, v, col, playlist):
+               col = col.identifier() if col else "playlist"
+
+               if col != "selected":
+                       return
+               NSApp.delegate().setPlaylist_selected_(playlist.pid, v)
+
+
+class FolderModel(NSObject):
        window = objc.IBOutlet()
-       loadingSheet = objc.IBOutlet()
-
+       folderPopup = objc.IBOutlet()
 
        def awakeFromNib(self):
-               self.performSelectorInBackground_withObject_(self.loadLibrary, None)
-               print "awakeFromNib"
-       
-       def finishLoading(self):
-               self.playlistModel.setPlaylists(self.library.list_playlists())
                self.folderPopup.addItemsWithTitles_(NSApp.delegate().folders())
+               self.folderPopup.selectItemAtIndex_(2)
+               self.lastIndex = 2
 
-       def loadLibrary(self):
-               pool = NSAutoreleasePool.alloc().init()
-               NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(self.loadingSheet, self.window, None, None, None)
-               self.library = libsyncitunes.ITunesLibrary.alloc().init()
-               self.loadingSheet.close()
-               self.performSelectorOnMainThread_withObject_waitUntilDone_(self.finishLoading, None, True)
-               del pool
+       @objc.IBAction
+       def doSelectFolder_(self, sender):
+               currentIndex = self.folderPopup.indexOfSelectedItem()
+               if currentIndex >= 2:
+                       self.lastIndex = currentIndex
+                       NSApp.delegate().addFolder_(self.folderPopup.titleOfSelectedItem())
+                       return
+               panel = NSOpenPanel.openPanel()
+               panel.setCanChooseFiles_(False)
+               panel.setCanChooseDirectories_(True)
+               panel.setAllowsMultipleSelection_(False)
+               panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
+                       None, None, [], self.window, self, self.selectFolderEnd_returnCode_contextInfo_, None)
 
        @objc.signature("v@:@ii")
        def selectFolderEnd_returnCode_contextInfo_(self, panel, ret, _):
                if ret == NSOKButton:
                        assert len(panel.filenames()) == 1
                        folder = panel.filenames()[0]
-###                    NSApp.delegate().addFolder_(folder)
+                       NSApp.delegate().addFolder_(folder)
                        self.folderPopup.insertItemWithTitle_atIndex_(folder, 2)
                        self.folderPopup.selectItemAtIndex_(2)
-
-       @objc.IBAction
-       def doSelectFolder_(self, sender):
-               print "select folder"
-               try:
-                       folders = NSApp.delegate().folders()
-                       if len(folders) > 0:
-                               folder = folders[0]
-                       else:
-                               folder = None
-                       panel = NSOpenPanel.openPanel()
-                       panel.setCanChooseFiles_(False)
-                       panel.setCanChooseDirectories_(True)
-                       panel.setAllowsMultipleSelection_(False)
-                       panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_(folder, None, None, self.window, self, self.selectFolderEnd_returnCode_contextInfo_, None)
-               except:
-                       import traceback
-                       traceback.print_exc()
-
-       @objc.IBAction
-       def doSync_(self, sender):
-               print "hello me"
+               else:
+                       self.folderPopup.selectItemAtIndex_(self.lastIndex)
 
 
 class NotiPodAppDelegate(NSObject):
+       window = objc.IBOutlet()
+       playlistModel = objc.IBOutlet()
+       folderModel = objc.IBOutlet()
+       loadingSheet = objc.IBOutlet()
+       loadingLabel = objc.IBOutlet()
 
-       # Delegate methods
+       def awakeFromNib(self):
+               self.gen = None
 
+       # Delegate methods
        def applicationWillFinishLaunching_(self, _):
-               print "loading prefs"
-               prefs = NSUserDefaults.standardUserDefaults()
-               self._playlists = prefs.stringArrayForKey_("playlists")
-               self._folders = prefs.stringArrayForKey_("folders")
-               print "folders", self._folders
-               print "playlists", self._playlists
+               pass
 
        def applicationDidFinishLaunching_(self, _):
-               pass
+               self.library = libsyncitunes.ITunesLibrary.alloc().init()
+               def finish():
+                       self.playlistModel.setPlaylists(self.library.get_playlists())
+               self.runGenerator(lambda: self.library.load_(None), finish)
 
        def applicationWillTerminate_(self, _):
-               prefs = NSUserDefaults.standardUserDefaults()
-               prefs.synchronize()
+               self.prefs().synchronize()
 
        def applicationShouldTerminateAfterLastWindowClosed_(self, _):
                return True
 
 
-       # Public accessors
+       # Utility methods
+       def runGenerator(self, func, finish):
+               NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(self.loadingSheet, self.window, None, None, None)
+               self.gen = func()
+               self.finish = finish
+               self.runGeneratorNext()
 
-       def playlists(self):
-               return self._playlists
+       def runGeneratorNext(self):
+               if self.gen is None or self.finish is None:
+                       return
+               try:
+                       msg = self.gen.next()
+                       self.loadingLabel.setStringValue_(msg)
+                       self.performSelector_withObject_afterDelay_(
+                               self.runGeneratorNext, None, 0)
+               except StopIteration:
+                       self.stopGenerator()
+
+       def stopGenerator(self):
+               self.gen = None
+               NSApp.endSheet_(self.loadingSheet)
+               self.loadingSheet.orderOut_(self)
+               self.finish()
+               self.finish = None
 
-       def folders(self):
-               return self._folders or []
+       @objc.IBAction
+       def doCancel_(self, sender):
+               self.stopGenerator()
 
-       def addFolder_(self, folder):
-               self._folders.insert(0, folder)
+       @objc.IBAction
+       def doSync_(self, sender):
+               folder = self.folders()[0]
+               playlists = [self.library.get_playlist_pid(pid) for pid in self.playlists()]
+
+               all_tracks = []
+               for playlist in playlists:
+                       all_tracks.extend(playlist.tracks)
+                       libsyncitunes.export_m3u(dry_run=False, dest=folder, path_prefix="",
+                                       playlist_name=playlist.name, files=playlist.tracks)
+
+               def finish():
+                       NSRunAlertPanel("Complete!", "Synchronisation is complete", "Ok", None, None)
+               self.runGenerator(
+                       lambda:
+                               libsyncitunes.sync(
+                                       dry_run=False,
+                                       source=self.library.folder,
+                                       dest=folder, 
+                                       files_to_copy=all_tracks
+                               )
+                       ,
+                       finish
+               )
+
+
+       # Public accessors
+
+       def prefs(self):
+               return NSUserDefaults.standardUserDefaults()
+
+       def _getArray(self, key):
+               res = self.prefs().stringArrayForKey_(key)
+               return list(res) if res else []
 
-       def hasPlaylist_(self, playlist):
-               return playlist in self._playlists
-       
-       def removePlaylist_(self, playlist):
-               self._playlists.remove(playlist)
-       
-       def addPlaylist_(self, playlist):
-               self._playlists.append(playlist)
+       def _saveArray(self, key, array):
+               self.prefs().setObject_forKey_(array, key)
 
+       def playlists(self):
+               return self._getArray("playlists")
 
+       def folders(self):
+               return self._getArray("folders")
 
-AppHelper.runEventLoop()
+       def addFolder_(self, folder):
+               folders = self.folders()
+               while folder in folders:
+                       folders.remove(folder)
+               folders.insert(0, folder)
+               folders = folders[:10]
+               self._saveArray("folders", folders)
+
+       def setPlaylist_selected_(self, playlist, selected):
+               playlists = self.playlists()
+               if selected:
+                       playlists.append(playlist)
+               else:
+                       playlists.remove(playlist)
+               playlists = list(set(playlists))
+               self._saveArray("playlists", list(set(playlists)))
+
+
+def main():
+###    logging.basicConfig(format="%(levelname)s: %(message)s")
+###    logging.getLogger().setLevel(logging.DEBUG)
+       AppHelper.runEventLoop()
+
+if __name__ == "__main__":
+       main()