]> code.delx.au - notipod/blob - NotiPod.py
610d4059353bc5aacd044bd70b4e0f197fe9baa3
[notipod] / NotiPod.py
1 #!/usr/bin/env python
2 # Copyright 2009 James Bunton <jamesbunton@fastmail.fm>
3 # Licensed for distribution under the GPL version 2, check COPYING for details
4
5 import logging
6
7 import objc
8 from Foundation import *
9 from AppKit import *
10 from PyObjCTools import AppHelper
11
12 import libsyncitunes
13
14
15 class PlaylistModel(NSObject):
16 outlineView = objc.IBOutlet()
17
18 def awakeFromNib(self):
19 self.root = []
20 self.playlists = {}
21 self.outlineView.setDataSource_(self)
22
23 def setPlaylists(self, playlists):
24 self.root = []
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)
31
32 def outlineView_child_ofItem_(self, _, childIndex, playlist):
33 if playlist == None:
34 return self.root[childIndex]
35 else:
36 return playlist.children[childIndex]
37
38 def outlineView_isItemExpandable_(self, _, playlist):
39 if playlist == None:
40 return True
41 else:
42 return len(playlist.children) > 0
43
44 def outlineView_numberOfChildrenOfItem_(self, _, playlist):
45 if playlist == None:
46 return len(self.root)
47 else:
48 return len(playlist.children)
49
50 def outlineView_objectValueForTableColumn_byItem_(self, _, col, playlist):
51 col = col.identifier() if col else "playlist"
52
53 if col == "selected":
54 selected = NSApp.delegate().playlists()
55 return playlist.pid in selected
56 if col == None or col == "playlist":
57 return playlist.name
58
59 def outlineView_setObjectValue_forTableColumn_byItem_(self, _, v, col, playlist):
60 col = col.identifier() if col else "playlist"
61
62 if col != "selected":
63 return
64 NSApp.delegate().setPlaylist_selected_(playlist.pid, v)
65
66
67 class FolderModel(NSObject):
68 window = objc.IBOutlet()
69 folderPopup = objc.IBOutlet()
70
71 def awakeFromNib(self):
72 self.folderPopup.addItemsWithTitles_(NSApp.delegate().folders())
73 self.folderPopup.selectItemAtIndex_(2)
74
75 @objc.IBAction
76 def doSelectFolder_(self, sender):
77 if self.folderPopup.indexOfSelectedItem() >= 2:
78 NSApp.delegate().addFolder_(self.folderPopup.titleOfSelectedItem())
79 return
80 panel = NSOpenPanel.openPanel()
81 panel.setCanChooseFiles_(False)
82 panel.setCanChooseDirectories_(True)
83 panel.setAllowsMultipleSelection_(False)
84 panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
85 None, None, [], self.window, self, self.selectFolderEnd_returnCode_contextInfo_, None)
86
87 @objc.signature("v@:@ii")
88 def selectFolderEnd_returnCode_contextInfo_(self, panel, ret, _):
89 if ret == NSOKButton:
90 assert len(panel.filenames()) == 1
91 folder = panel.filenames()[0]
92 NSApp.delegate().addFolder_(folder)
93 self.folderPopup.insertItemWithTitle_atIndex_(folder, 2)
94 self.folderPopup.selectItemAtIndex_(2)
95
96
97 class NotiPodAppDelegate(NSObject):
98 window = objc.IBOutlet()
99 playlistModel = objc.IBOutlet()
100 folderModel = objc.IBOutlet()
101 loadingSheet = objc.IBOutlet()
102
103
104 # Delegate methods
105 def applicationWillFinishLaunching_(self, _):
106 pass
107
108 def applicationDidFinishLaunching_(self, _):
109 NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(self.loadingSheet, self.window, None, None, None)
110 self.performSelectorInBackground_withObject_(self.loadFromThread, None)
111
112 def applicationWillTerminate_(self, _):
113 self.prefs().synchronize()
114
115 def applicationShouldTerminateAfterLastWindowClosed_(self, _):
116 return True
117
118
119 def loadFromThread(self):
120 pool = NSAutoreleasePool.alloc().init()
121 self.library = libsyncitunes.ITunesLibrary.alloc().init()
122 self.performSelectorOnMainThread_withObject_waitUntilDone_(self.finishLoading, None, True)
123 del pool
124
125 def finishLoading(self):
126 self.playlistModel.setPlaylists(self.library.get_playlists())
127 NSApp.endSheet_(self.loadingSheet)
128 self.loadingSheet.close()
129
130 @objc.IBAction
131 def doSync_(self, sender):
132 folder = self.folders()[0]
133 playlists = [self.library.get_playlist_pid(pid) for pid in self.playlists()]
134
135 all_tracks = []
136 for playlist in playlists:
137 print playlist.name, playlist.tracks
138 all_tracks.extend(playlist.tracks)
139 libsyncitunes.export_m3u(dry_run=True, dest=folder, path_prefix="",
140 playlist_name=playlist.name, files=playlist.tracks)
141
142 libsyncitunes.sync(dry_run=True,
143 source=self.library.folder, dest=folder, files=all_tracks)
144
145
146 # Public accessors
147
148 def prefs(self):
149 return NSUserDefaults.standardUserDefaults()
150
151 def _getArray(self, key):
152 res = self.prefs().stringArrayForKey_(key)
153 return list(res) if res else []
154
155 def _saveArray(self, key, array):
156 self.prefs().setObject_forKey_(array, key)
157
158 def playlists(self):
159 return self._getArray("playlists")
160
161 def folders(self):
162 return self._getArray("folders")
163
164 def addFolder_(self, folder):
165 folders = self.folders()
166 while folder in folders:
167 folders.remove(folder)
168 folders.insert(0, folder)
169 folders = folders[:10]
170 self._saveArray("folders", folders)
171
172 def setPlaylist_selected_(self, playlist, selected):
173 playlists = self.playlists()
174 if selected:
175 playlists.append(playlist)
176 else:
177 playlists.remove(playlist)
178 playlists = list(set(playlists))
179 self._saveArray("playlists", list(set(playlists)))
180
181
182 def main():
183 logging.basicConfig(format="%(levelname)s: %(message)s")
184 logging.getLogger().setLevel(logging.DEBUG)
185 AppHelper.runEventLoop()
186
187 if __name__ == "__main__":
188 main()
189