# 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 *
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()