# Licensed for distribution under the GPL version 2, check COPYING for details
import logging
+import os
+import sys
+import traceback
import objc
from Foundation import *
panel = NSOpenPanel.openPanel()
panel.setCanChooseFiles_(False)
panel.setCanChooseDirectories_(True)
+ panel.setCanCreateDirectories_(True)
panel.setAllowsMultipleSelection_(False)
panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
None, None, [], self.window, self, self.selectFolderEnd_returnCode_contextInfo_, None)
class NotiPodController(NSObject):
window = objc.IBOutlet()
- playlistModel = objc.IBOutlet()
- folderModel = objc.IBOutlet()
+
loadingSheet = objc.IBOutlet()
loadingLabel = objc.IBOutlet()
+ loadingIndicator = objc.IBOutlet()
+
+ previewWindow = objc.IBOutlet()
+ previewText = objc.IBOutlet()
+
+ playlistModel = objc.IBOutlet()
+ folderModel = objc.IBOutlet()
+
def awakeFromNib(self):
self.runningGenerator = False
self.library = libnotipod.ITunesLibrary.alloc().init()
def finish():
self.playlistModel.setPlaylists(self.library.get_playlists())
- self.runGenerator(lambda: self.library.load_(None), finish)
+ def fail():
+ sys.exit(0)
+ self.runGenerator(lambda: self.library.load_(None), finish, fail)
def applicationWillTerminate_(self, _):
self.prefs().synchronize()
# Utility methods
- def runGenerator(self, func, finish):
+ def runGenerator(self, func, finish, fail):
assert not self.runningGenerator
self.runningGenerator = True
+ self.loadingIndicator.startAnimation_(self)
NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(self.loadingSheet, self.window, None, None, None)
- arg = (func(), finish)
+ arg = (func(), finish, fail)
self.performSelectorInBackground_withObject_(self.runGeneratorThread, arg)
- def runGeneratorThread(self, (gen, finish)):
+ def runGeneratorThread(self, (gen, finish, fail)):
pool = NSAutoreleasePool.alloc().init()
- for msg in gen:
- if not self.runningGenerator:
- break
- self.loadingLabel.performSelectorOnMainThread_withObject_waitUntilDone_(
- self.loadingLabel.setStringValue_, msg, True)
+ try:
+ for msg in gen:
+ if not self.runningGenerator:
+ break
+ self.loadingLabel.performSelectorOnMainThread_withObject_waitUntilDone_(
+ self.loadingLabel.setStringValue_, msg, True)
+ except Exception, e:
+ NSRunAlertPanel("Error!", str(e), "Ok", None, None)
+ traceback.print_exc()
+ finish = fail
self.performSelectorOnMainThread_withObject_waitUntilDone_(
self.stopGenerator, finish, True)
self.runningGenerator = False
- del pool
def stopGenerator(self, finish):
self.runningGenerator = False
NSApp.endSheet_(self.loadingSheet)
self.loadingSheet.orderOut_(self)
- finish()
+ self.loadingIndicator.stopAnimation_(self)
+ if finish:
+ finish()
@objc.IBAction
def doCancel_(self, sender):
self.runningGenerator = False
+ def getDestFolder(self):
+ folders = self.folders()
+ if not folders:
+ NSRunAlertPanel("Error!", "You must choose a folder first!", "Ok", None, None)
+ return
+ folder = folders[0]
+ if not os.path.isdir(folder.encode("utf-8")):
+ NSRunAlertPanel("Error!", "Destination " + folder + " does not exist, try mounting it first?", "Ok", None, None)
+ return
+ return folder
+
+ def doPreviewThread(self):
+ yield "Calculating changes..."
+
+ folder = self.getDestFolder()
+ if not folder:
+ return
+
+ all_tracks = set()
+ for playlist_id in self.playlists():
+ playlist = self.library.get_playlist_pid(playlist_id)
+ if playlist is not None:
+ all_tracks.update(set(playlist.tracks))
+
+ all_filenames = []
+ for trackID in all_tracks:
+ all_filenames.append(self.library.get_track_filename(trackID))
+
+ gen = libnotipod.sync(
+ dry_run=True,
+ source=self.library.folder,
+ dest=folder,
+ files_to_copy=all_filenames,
+ )
+ self.previewResult = "\n".join(gen)
+
+ @objc.IBAction
+ def doPreview_(self, sender):
+ self.previewResult = ""
+ self.previewWindow.orderOut_(self)
+
+ def finish():
+ self.previewText.textStorage().mutableString().setString_(self.previewResult)
+ self.previewWindow.center()
+ self.previewWindow.makeKeyAndOrderFront_(self)
+
+ self.runGenerator(self.doPreviewThread, finish, None)
+
@objc.IBAction
def doSync_(self, sender):
- folder = self.folders()[0]
- playlists = [self.library.get_playlist_pid(pid) for pid in self.playlists()]
+ folder = self.getDestFolder()
+ if not folder:
+ return
- all_tracks = []
- for playlist in playlists:
- all_tracks.extend(playlist.tracks)
+ all_tracks = set()
+ orig_playlists = set(self.playlists())
+ all_playlists = orig_playlists.copy()
+ for playlist_id in all_playlists:
+ playlist = self.library.get_playlist_pid(playlist_id)
+ if playlist is None:
+ print "Forgetting unknown playlist:", playlist_id
+ self.setPlaylist_selected_(playlist_id, False)
+ continue
+ all_tracks.update(set(playlist.tracks))
+
+ all_filenames = []
+ for trackID in all_tracks:
+ all_filenames.append(self.library.get_track_filename(trackID))
+ all_playlists.update(self.library.get_track_playlists(trackID))
+
+ for playlist_id in all_playlists:
+ playlist = self.library.get_playlist_pid(playlist_id)
+ if playlist is None:
+ continue
+ tracks = []
+ for trackID in playlist.tracks:
+ if trackID in all_tracks:
+ tracks.append(self.library.get_track_filename(trackID))
+ if playlist_id not in orig_playlists and len(tracks) < 10:
+ continue
libnotipod.export_m3u(dry_run=False, dest=folder, path_prefix="",
- playlist_name=playlist.name, files=playlist.tracks)
+ playlist_name=playlist.name, files=tracks)
def finish():
NSRunAlertPanel("Complete!", "Synchronisation is complete", "Ok", None, None)
dry_run=False,
source=self.library.folder,
dest=folder,
- files_to_copy=all_tracks
+ files_to_copy=all_filenames,
)
,
- finish
+ finish,
+ None
)