2 # Copyright 2009 James Bunton <jamesbunton@fastmail.fm>
3 # Licensed for distribution under the GPL version 2, check COPYING for details
8 from Foundation
import *
10 from PyObjCTools
import AppHelper
15 class PlaylistModel(NSObject
):
16 outlineView
= objc
.IBOutlet()
18 def awakeFromNib(self
):
21 self
.outlineView
.setDataSource_(self
)
23 def setPlaylists(self
, playlists
):
25 self
.playlists
= playlists
26 for playlist
in self
.playlists
:
27 if playlist
.parent
is None:
28 self
.root
.append(playlist
)
29 self
.outlineView
.reloadData()
30 self
.outlineView
.expandItem_expandChildren_(None, True)
32 def outlineView_child_ofItem_(self
, _
, childIndex
, playlist
):
34 return self
.root
[childIndex
]
36 return playlist
.children
[childIndex
]
38 def outlineView_isItemExpandable_(self
, _
, playlist
):
42 return len(playlist
.children
) > 0
44 def outlineView_numberOfChildrenOfItem_(self
, _
, playlist
):
48 return len(playlist
.children
)
50 def outlineView_objectValueForTableColumn_byItem_(self
, _
, col
, playlist
):
53 col
= col
.identifier()
56 selected
= NSApp
.delegate().playlists()
57 return playlist
.pid
in selected
59 return NSImage
.imageNamed_("playlist-" + playlist
.ptype
)
63 def outlineView_setObjectValue_forTableColumn_byItem_(self
, _
, v
, col
, playlist
):
66 col
= col
.identifier()
71 NSApp
.delegate().setPlaylist_selected_(playlist
.pid
, v
)
74 class FolderModel(NSObject
):
75 window
= objc
.IBOutlet()
76 folderPopup
= objc
.IBOutlet()
78 def awakeFromNib(self
):
79 folders
= NSApp
.delegate().folders()
80 self
.folderPopup
.addItemsWithTitles_(folders
)
82 self
.folderPopup
.selectItemAtIndex_(2)
88 def doSelectFolder_(self
, sender
):
89 currentIndex
= self
.folderPopup
.indexOfSelectedItem()
91 self
.lastIndex
= currentIndex
92 NSApp
.delegate().addFolder_(self
.folderPopup
.titleOfSelectedItem())
94 panel
= NSOpenPanel
.openPanel()
95 panel
.setCanChooseFiles_(False)
96 panel
.setCanChooseDirectories_(True)
97 panel
.setAllowsMultipleSelection_(False)
98 panel
.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
99 None, None, [], self
.window
, self
, self
.selectFolderEnd_returnCode_contextInfo_
, None)
101 @objc.signature("v@:@ii")
102 def selectFolderEnd_returnCode_contextInfo_(self
, panel
, ret
, _
):
103 if ret
== NSOKButton
:
104 assert len(panel
.filenames()) == 1
105 folder
= panel
.filenames()[0]
106 NSApp
.delegate().addFolder_(folder
)
107 self
.folderPopup
.insertItemWithTitle_atIndex_(folder
, 2)
108 self
.folderPopup
.selectItemAtIndex_(2)
110 self
.folderPopup
.selectItemAtIndex_(self
.lastIndex
)
113 class NotiPodController(NSObject
):
114 window
= objc
.IBOutlet()
115 playlistModel
= objc
.IBOutlet()
116 folderModel
= objc
.IBOutlet()
117 loadingSheet
= objc
.IBOutlet()
118 loadingLabel
= objc
.IBOutlet()
120 def awakeFromNib(self
):
121 self
.runningGenerator
= False
124 def applicationWillFinishLaunching_(self
, _
):
127 def applicationDidFinishLaunching_(self
, _
):
128 self
.library
= libnotipod
.ITunesLibrary
.alloc().init()
130 self
.playlistModel
.setPlaylists(self
.library
.get_playlists())
131 self
.runGenerator(lambda: self
.library
.load_(None), finish
)
133 def applicationWillTerminate_(self
, _
):
134 self
.prefs().synchronize()
136 def applicationShouldTerminateAfterLastWindowClosed_(self
, _
):
141 def runGenerator(self
, func
, finish
):
142 assert not self
.runningGenerator
143 self
.runningGenerator
= True
144 NSApp
.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(self
.loadingSheet
, self
.window
, None, None, None)
145 arg
= (func(), finish
)
146 self
.performSelectorInBackground_withObject_(self
.runGeneratorThread
, arg
)
148 def runGeneratorThread(self
, (gen
, finish
)):
149 pool
= NSAutoreleasePool
.alloc().init()
151 if not self
.runningGenerator
:
153 self
.loadingLabel
.performSelectorOnMainThread_withObject_waitUntilDone_(
154 self
.loadingLabel
.setStringValue_
, msg
, True)
155 self
.performSelectorOnMainThread_withObject_waitUntilDone_(
156 self
.stopGenerator
, finish
, True)
157 self
.runningGenerator
= False
160 def stopGenerator(self
, finish
):
161 self
.runningGenerator
= False
162 NSApp
.endSheet_(self
.loadingSheet
)
163 self
.loadingSheet
.orderOut_(self
)
167 def doCancel_(self
, sender
):
168 self
.runningGenerator
= False
171 def doSync_(self
, sender
):
172 folder
= self
.folders()[0]
173 playlists
= [self
.library
.get_playlist_pid(pid
) for pid
in self
.playlists()]
176 for playlist
in playlists
:
177 all_tracks
.extend(playlist
.tracks
)
178 libnotipod
.export_m3u(dry_run
=False, dest
=folder
, path_prefix
="",
179 playlist_name
=playlist
.name
, files
=playlist
.tracks
)
182 NSRunAlertPanel("Complete!", "Synchronisation is complete", "Ok", None, None)
187 source
=self
.library
.folder
,
189 files_to_copy
=all_tracks
199 return NSUserDefaults
.standardUserDefaults()
201 def _getArray(self
, key
):
202 res
= self
.prefs().stringArrayForKey_(key
)
203 return list(res
) if res
else []
205 def _saveArray(self
, key
, array
):
206 self
.prefs().setObject_forKey_(array
, key
)
209 return self
._getArray
("playlists")
212 return self
._getArray
("folders")
214 def addFolder_(self
, folder
):
215 folders
= self
.folders()
216 while folder
in folders
:
217 folders
.remove(folder
)
218 folders
.insert(0, folder
)
219 folders
= folders
[:10]
220 self
._saveArray
("folders", folders
)
222 def setPlaylist_selected_(self
, playlist
, selected
):
223 playlists
= self
.playlists()
225 playlists
.append(playlist
)
227 playlists
.remove(playlist
)
228 playlists
= list(set(playlists
))
229 self
._saveArray
("playlists", list(set(playlists
)))
233 ### logging.basicConfig(format="%(levelname)s: %(message)s")
234 ### logging.getLogger().setLevel(logging.DEBUG)
235 AppHelper
.runEventLoop()
237 if __name__
== "__main__":