def do_list(self, line): container = self._session.playlistcontainer() if not container.is_loaded(): checker = BulkConditionChecker() #Wait until the container is loaded checker.add_condition(container.is_loaded) callbacks = JukeboxPlaylistContainerCallbacks(checker) container.add_callbacks(callbacks) checker.complete_wait() for item in container.playlists(): item.set_in_ram(self._session, True) if not line: #Print all playlists print "%d playlists:" % container.num_playlists() for k, item in enumerate(container.playlists()): if item.is_loaded(): print "playlist #%d: %s" % (k, item.name()) else: print "playlist #%d: loading..." % k else: pos = int(line) pl = container.playlist(pos) print "playlist #%d, %d tracks:" % (pos, pl.num_tracks()) for index,item in enumerate(pl.tracks()): if item.is_loaded(): print "track #%d: %s" % (index, item.name()) #print item.album().cover() else: print "track #%d: loading..." % index
def _load_toplist(self, type, region, user): checker = BulkConditionChecker() callbacks = ToplistbrowseLoadCallbacks(checker) toplistbrowse_obj = toplistbrowse.Toplistbrowse( self._session, type, region, user, callbacks) checker.add_condition(toplistbrowse_obj.is_loaded) checker.complete_wait(10) return toplistbrowse_obj
def _load_toplist(self, type, region, user): checker = BulkConditionChecker() callbacks = ToplistbrowseLoadCallbacks(checker) toplistbrowse_obj = toplistbrowse.Toplistbrowse( self._session, type, region, user, callbacks ) checker.add_condition(toplistbrowse_obj.is_loaded) checker.complete_wait(10) return toplistbrowse_obj
def _load_radio(self, from_year, to_year, genres): checker = BulkConditionChecker() callbacks = SearchLoadCallbacks(checker) radio_obj = radio.RadioSearch( self._session, from_year, to_year, genres, callbacks ) checker.add_condition(radio_obj.is_loaded) checker.complete_wait(10) return radio_obj
def _load_search(self, query): checker = BulkConditionChecker() callbacks = SearchLoadCallbacks(checker) search_obj = search.Search( self._session, query, track_count=100, album_count=100, artist_count=100, callbacks=callbacks ) checker.add_condition(search_obj.is_loaded) checker.complete_wait(10) return search_obj
def _load_artist(self, id): full_id = "spotify:artist:%s" % id checker = BulkConditionChecker() #Get the artist object link_obj = link.create_from_string(full_id) artist_obj = link_obj.as_artist() #Now initialize the artistbrowse load stuff callbacks = ArtistbrowseLoadCallbacks(checker) artistbrowse_obj = artistbrowse.Artistbrowse(self._session, artist_obj, callbacks) checker.add_condition(artistbrowse_obj.is_loaded) checker.complete_wait(10) return artist_obj, artistbrowse_obj
def _load_album(self, id): import time full_id = "spotify:album:%s" % id checker = BulkConditionChecker() #All the album loading stuff link_obj = link.create_from_string(full_id) album_obj = link_obj.as_album() #Now the albumbrowse object callbacks = AlbumbrowseLoadCallbacks(checker) albumbrowse_obj = albumbrowse.Albumbrowse(self._session, album_obj, callbacks) checker.add_condition(albumbrowse_obj.is_loaded) checker.complete_wait(10) return album_obj, albumbrowse_obj
def _load_artist(self, id): full_id = "spotify:artist:%s" % id checker = BulkConditionChecker() #Get the artist object link_obj = link.create_from_string(full_id) artist_obj = link_obj.as_artist() #Now initialize the artistbrowse load stuff callbacks = ArtistbrowseLoadCallbacks(checker) artistbrowse_obj = artistbrowse.Artistbrowse( self._session, artist_obj, callbacks ) checker.add_condition(artistbrowse_obj.is_loaded) checker.complete_wait(10) return artist_obj, artistbrowse_obj
def _load_album(self, id): import time full_id = "spotify:album:%s" % id checker = BulkConditionChecker() #All the album loading stuff link_obj = link.create_from_string(full_id) album_obj = link_obj.as_album() #Now the albumbrowse object callbacks = AlbumbrowseLoadCallbacks(checker) albumbrowse_obj = albumbrowse.Albumbrowse( self._session, album_obj, callbacks ) checker.add_condition(albumbrowse_obj.is_loaded) checker.complete_wait(10) return album_obj, albumbrowse_obj
def load_track(sess_obj, track_obj): #FIXME: Doing all these things is just overkill! if not track_obj.is_loaded(): #Set callbacks for loading the track checker = BulkConditionChecker() checker.add_condition(track_obj.is_loaded) callbacks = TrackLoadCallback(checker) sess_obj.add_callbacks(callbacks) try: #Wait until it's done (should be enough) checker.complete_wait(10) finally: #Remove that callback, or will be around forever sess_obj.remove_callbacks(callbacks) return track_obj
class ArtistAlbumLoader: __checker = None __session = None __artist = None __album_data = None __artistbrowse = None __is_loaded = None __sorted_albums = None def __init__(self, session, artist): self.__checker = BulkConditionChecker() self.__session = session self.__artist = artist self.__album_data = {} cb = ArtistCallbacks(self) self.__artistbrowse = artistbrowse.Artistbrowse( session, artist, BrowseType.NoTracks, cb) self.__is_loaded = False #Avoid locking this thread and continue in another one self.continue_in_background() def check(self): self.__checker.check_conditions() def _wait_for_album_info(self, album_info, checker): def info_is_loaded(): return album_info.is_loaded() if not info_is_loaded(): checker.add_condition(info_is_loaded) checker.complete_wait(10) #Should be enough for an album def _num_available_tracks(self, album_info): count = 0 #Return true if it has at least one playable track for track in album_info.tracks(): track_status = track.get_availability(self.__session) if track_status == TrackAvailability.Available: count += 1 return count def _is_same_artist(self, artist1, artist2): album1_str = link.create_from_artist(artist1).as_string() album2_str = link.create_from_artist(artist2).as_string() return album1_str == album2_str def _get_album_type(self, album): if album.type() == SpotifyAlbumType.Single: return AlbumType.Single elif album.type() == SpotifyAlbumType.Compilation: return AlbumType.Compilation if not self._is_same_artist(self.__artist, album.artist()): return AlbumType.AppearsIn else: return AlbumType.Album def get_album_info(self, index): album = self.__artistbrowse.album(index) checker = BulkConditionChecker() cb = AlbumCallbacks(checker) album_info = albumbrowse.Albumbrowse(self.__session, album, cb) #Wait until it's loaded self._wait_for_album_info(album_info, checker) return album_info @run_in_thread(threads_per_class=5) def load_album_info(self, index, album): #Directly discard unavailable albums if not album.is_available(): self.__album_data[index] = { 'available_tracks': 0, 'type': self._get_album_type(album), } #Otherwise load it's data else: #A checker for a single condition? Overkill! checker = BulkConditionChecker() cb = AlbumCallbacks(checker) album_info = albumbrowse.Albumbrowse(self.__session, album, cb) #Now wait until it's loaded self._wait_for_album_info(album_info, checker) #Populate it's data self.__album_data[index] = { 'available_tracks': self._num_available_tracks(album_info), 'type': self._get_album_type(album), } #Tell that we've done self.__checker.check_conditions() def _wait_for_album_list(self): if not self.__artistbrowse.is_loaded(): self.__checker.add_condition(self.__artistbrowse.is_loaded) self.__checker.complete_wait(60) #Should be enough? def _add_album_processed_check(self, index): def album_is_processed(): return index in self.__album_data if not album_is_processed(): self.__checker.add_condition(album_is_processed) @run_in_thread(threads_per_class=1) def continue_in_background(self): #Wait until the album list got loaded self._wait_for_album_list() #Now load albumbrowse data from each one for index, album in enumerate(self.__artistbrowse.albums()): #Add a condition for the next wait self._add_album_processed_check(index) #Start loading the info in the background self.load_album_info(index, album) #Now wait until all info gets loaded self.__checker.complete_wait(60) #Final steps... self.__is_loaded = True xbmc.executebuiltin("Action(Noop)") def is_loaded(self): return self.__is_loaded def get_album_available_tracks(self, index): return self.__album_data[index]['available_tracks'] def get_album_type(self, index): return self.__album_data[index]['type'] def get_album(self, index): return self.__artistbrowse.album(index) def get_non_similar_albums(self): name_dict = {} for index, album in self.get_albums(): name = album.name() available_tracks = self.get_album_available_tracks(index) #If that name is new to us just store it if name not in name_dict: name_dict[name] = (index, available_tracks) #If the album has more playable tracks than the stored one elif available_tracks > name_dict[name][1]: name_dict[name] = (index, available_tracks) #Now return the list if indexes return [item[0] for item in name_dict.itervalues()] def get_albums(self): def sort_func(album_index): #Sort by album type and then by year (desc) return (self.get_album_type(album_index), -self.__artistbrowse.album(album_index).year()) #Do nothing if is loading if self.is_loaded(): #Build the sorted album list if needed if self.__sorted_albums is None: album_indexes = self.__album_data.keys() sorted_indexes = sorted(album_indexes, key=sort_func) ab = self.__artistbrowse self.__sorted_albums = [(index, ab.album(index)) for index in sorted_indexes] print self.__sorted_albums return self.__sorted_albums
class ContainerLoader: __session = None __container = None __playlist_manager = None __playlists = None __checker = None __loading_lock = None __list_lock = None __is_loaded = None def __init__(self, session, container, playlist_manager): self.__session = session self.__container = container self.__playlist_manager = playlist_manager self.__playlists = [] self.__checker = BulkConditionChecker() self.__loading_lock = threading.RLock() self.__list_lock = threading.RLock() self.__is_loaded = False #Load the rest in the background self._start_load() def _fill_spaces(self, position): try: self.__list_lock.acquire() if position >= len(self.__playlists): for idx in range(len(self.__playlists), position + 1): self.__playlists.append(None) finally: self.__list_lock.release() def is_playlist(self, position): playlist_type = self.__container.playlist_type(position) return playlist_type == playlist.PlaylistType.Playlist def add_playlist(self, playlist, position): try: self.__list_lock.acquire() #Ensure that it gets added in the correct position self._fill_spaces(position - 1) #Instantiate a loader if it's a real playlist if self.is_playlist(position): item = ContainerPlaylistLoader( self.__session, playlist, self.__playlist_manager, self ) #Ignore if it's not a real playlist else: item = None #Insert the generated item self.__playlists.insert(position, item) finally: self.__list_lock.release() def remove_playlist(self, position): try: self.__list_lock.acquire() del self.__playlists[position] finally: self.__list_lock.release() def move_playlist(self, position, new_position): try: self.__list_lock.acquire() self.__playlists.insert(new_position, self.__playlists[position]) #Calculate new position if position > new_position: position += 1 del self.__playlists[position] finally: self.__list_lock.release() def _add_missing_playlists(self): #Ensure that the container and loader length is the same self._fill_spaces(self.__container.num_playlists() - 1) #Iterate over the container to add the missing ones for pos, item in enumerate(self.__container.playlists()): if self.is_playlist(pos) and self.__playlists[pos] is None: self.add_playlist(item, pos) def _check_playlist(self, playlist): def is_playlist_loaded(): #If it has errors, say yes. if playlist.has_errors(): return True #And if it was loaded, say yes if playlist.is_loaded(): return True self.__checker.add_condition(is_playlist_loaded) def _load_container(self): #Wait for the container to be fully loaded self.__checker.add_condition(self.__container.is_loaded) self.__checker.complete_wait() #Fill the container with unseen playlists self._add_missing_playlists() #Add a load check for each playlist for item in self.__playlists: if item is not None and not item.is_loaded(): self._check_playlist(item) #Wait until all conditions become true self.__checker.complete_wait(self.__container.num_playlists() * 5) #Set the status of the loader self.__is_loaded = True #Check and print errors for not loaded playlists for idx, item in enumerate(self.__playlists): if item is not None and item.has_errors(): xbmc.log('Playlist #%s failed loading.' % idx, xbmc.LOGERROR) #Finally tell the gui we are done xbmc.executebuiltin("Action(Noop)") @run_in_thread def _start_update(self): """ Try gaining a lock. If we can't get one, another update is running, so do nothing. """ if self.__loading_lock.acquire(False): try: self._load_container() finally: self.__loading_lock.release() @run_in_thread def _start_load(self): self.__loading_lock.acquire() try: """ Register container callbacks so they start loading after this point. Too bad if they reach before. """ self.__container.add_callbacks(ContainerCallbacks(self)) #And take care of the rest self._load_container() finally: self.__loading_lock.release() def update(self): self.__checker.check_conditions() self._start_update() def is_loaded(self): return self.__is_loaded def playlist(self, index): return self.__playlists[index] def num_playlists(self): return len(self.__playlists) def playlists(self): return CallbackIterator(self.num_playlists, self.playlist) def get_container(self): return self.__container def __del__(self): print "ContainerLoader __del__"
class BasePlaylistLoader: __playlist = None __playlist_manager = None __checker = None #Playlist attributes __name = None __num_tracks = None __thumbnails = None __is_collaborative = None __is_loaded = None __has_errors = None __has_changes = None def __init__(self, session, playlist, playlist_manager): #Initialize all instance vars self.__playlist = playlist self.__playlist_manager = playlist_manager self.__checker = BulkConditionChecker() self.__is_loaded = False self.__has_errors = False self.__thumbnails = [] #Add the callbacks we are interested in playlist.add_callbacks(PlaylistCallbacks(self)) #Fire playlist loading if neccesary if not playlist.is_in_ram(session): playlist.set_in_ram(session, True) #Finish the rest in the background self.load_in_background() @run_in_thread(threads_per_class=10, single_instance=True) def load_in_background(self): try: #Reset change flag self._set_changes(False) #And call the method that does the actual loading task self._start_loading() #Clear previous error flags (if any) if self.has_errors(): self._set_error(False) except: #Mark this playlist self._set_error(True) finally: #If changes or errors were detected if self.has_changes() or self.has_errors(): self.end_loading() def get_playlist(self): return self.__playlist def _set_thumbnails(self, thumbnails): self.__thumbnails = thumbnails def get_thumbnails(self): return self.__thumbnails def _set_name(self, name): self.__name = name def get_name(self): return self.__name def get_num_tracks(self): return self.__num_tracks def get_tracks(self): return self.__playlist.tracks() def get_track(self, index): track_list = self.get_tracks() return track_list[index] def get_is_collaborative(self): return self.__is_collaborative def _track_is_ready(self, track, test_album=True, test_artists=True): def album_is_loaded(): album = track.album() return album is not None and album.is_loaded() def artists_are_loaded(): for item in track.artists(): if item is None or not item.is_loaded(): return False return True #If track has an error stop further processing if track.error() not in [ErrorType.Ok, ErrorType.IsLoading]: return True #Always test for the track data if not track.is_loaded(): return False #If album data was requested elif test_album and not album_is_loaded(): return False #If artist data was requested elif test_artists and not artists_are_loaded(): return False #Otherwise everything was ok else: return True def _wait_for_playlist(self): if not self.__playlist.is_loaded(): self.__checker.add_condition(self.__playlist.is_loaded) self.__checker.complete_wait(10) def _wait_for_track_metadata(self, track): def test_is_loaded(): return self._track_is_ready( track, test_album=True, test_artists=False ) if not test_is_loaded(): self.__checker.add_condition(test_is_loaded) self.__checker.complete_wait(10) def _load_thumbnails(self): pm = self.__playlist_manager #If playlist has an image playlist_image = self.__playlist.get_image() if playlist_image is not None: thumbnails = [pm.get_image_url(playlist_image)] #Otherwise get them from the album covers else: thumbnails = [] for item in self.__playlist.tracks(): #Wait until this track is fully loaded self._wait_for_track_metadata(item) #Check if item was loaded without errors if item.is_loaded() and item.error() == 0: #Append the cover if it's new image_id = item.album().cover() image_url = pm.get_image_url(image_id) if image_url not in thumbnails: thumbnails.append(image_url) #If we reached to the desired thumbnail count... if len(thumbnails) == 4: break #If the thumnbail count is still zero... if len(thumbnails) == 0: self.__thumbnails = ['common/pl-default.png'] return True #If the stored thumbnail data changed... if self.__thumbnails != thumbnails: self.__thumbnails = thumbnails return True def _load_name(self): if self.__name != self.__playlist.name(): self.__name = self.__playlist.name() return True else: return False def _load_num_tracks(self): if self.__num_tracks != self.__playlist.num_tracks(): self.__num_tracks = self.__playlist.num_tracks() return True else: return False def _load_is_collaborative(self): if self.__is_collaborative != self.__playlist.is_collaborative(): self.__is_collaborative = self.__playlist.is_collaborative() return True else: return False def _load_attributes(self): #Now check for changes has_changes = False if self._load_name(): has_changes = True if self._load_num_tracks(): has_changes = True if self._load_is_collaborative(): has_changes = True #If we detected something different return has_changes def _add_condition(self, condition): self.__checker.add_condition(condition) def _wait_for_conditions(self, timeout): self.__checker.complete_wait(timeout) def check(self): self.__checker.check_conditions() def _set_loaded(self, status): self.__is_loaded = status def is_loaded(self): return self.__is_loaded def _set_error(self, status): self.__has_errors = status def has_errors(self): return self.__has_errors def _set_changes(self, status): self.__has_changes = status def has_changes(self): return self.__has_changes def _start_loading(self): raise NotImplementedError() def end_loading(self): pass def __del__(self): print "PlaylistLoader __del__"
class ArtistAlbumLoader: __checker = None __session = None __artist = None __album_data = None __artistbrowse = None __is_loaded = None __sorted_albums = None def __init__(self, session, artist): self.__checker = BulkConditionChecker() self.__session = session self.__artist = artist self.__album_data = {} cb = ArtistCallbacks(self) self.__artistbrowse = artistbrowse.Artistbrowse( session, artist, BrowseType.NoTracks, cb ) self.__is_loaded = False #Avoid locking this thread and continue in another one self.continue_in_background() def check(self): self.__checker.check_conditions() def _wait_for_album_info(self, album_info, checker): def info_is_loaded(): return album_info.is_loaded() if not info_is_loaded(): checker.add_condition(info_is_loaded) checker.complete_wait(10) #Should be enough for an album def _num_available_tracks(self, album_info): count = 0 #Return true if it has at least one playable track for track in album_info.tracks(): track_status = track.get_availability(self.__session) if track_status == TrackAvailability.Available: count += 1 return count def _is_same_artist(self, artist1, artist2): album1_str = link.create_from_artist(artist1).as_string() album2_str = link.create_from_artist(artist2).as_string() return album1_str == album2_str def _get_album_type(self, album): if album.type() == SpotifyAlbumType.Single: return AlbumType.Single elif album.type() == SpotifyAlbumType.Compilation: return AlbumType.Compilation if not self._is_same_artist(self.__artist, album.artist()): return AlbumType.AppearsIn else: return AlbumType.Album @run_in_thread(threads_per_class=5) def load_album_info(self, index, album): #Directly discard unavailable albums if not album.is_available(): self.__album_data[index] = { 'available_tracks': 0, 'type': self._get_album_type(album), } #Otherwise load it's data else: #A checker for a single condition? Overkill! checker = BulkConditionChecker() cb = AlbumCallbacks(checker) album_info = albumbrowse.Albumbrowse(self.__session, album, cb) #Now wait until it's loaded self._wait_for_album_info(album_info, checker) #Populate it's data self.__album_data[index] = { 'available_tracks': self._num_available_tracks(album_info), 'type': self._get_album_type(album), } #Tell that we've done self.__checker.check_conditions() def _wait_for_album_list(self): if not self.__artistbrowse.is_loaded(): self.__checker.add_condition(self.__artistbrowse.is_loaded) self.__checker.complete_wait(60) #Should be enough? def _add_album_processed_check(self, index): def album_is_processed(): return index in self.__album_data if not album_is_processed(): self.__checker.add_condition(album_is_processed) @run_in_thread(threads_per_class=1) def continue_in_background(self): #Wait until the album list got loaded self._wait_for_album_list() #Now load albumbrowse data from each one for index, album in enumerate(self.__artistbrowse.albums()): #Add a condition for the next wait self._add_album_processed_check(index) #Start loading the info in the background self.load_album_info(index, album) #Now wait until all info gets loaded self.__checker.complete_wait(60) #Final steps... self.__is_loaded = True xbmc.executebuiltin("Action(Noop)") def is_loaded(self): return self.__is_loaded def get_album_available_tracks(self, index): return self.__album_data[index]['available_tracks'] def get_album_type(self, index): return self.__album_data[index]['type'] def get_album(self, index): return self.__artistbrowse.album(index) def get_non_similar_albums(self): name_dict = {} for index, album in self.get_albums(): name = album.name() available_tracks = self.get_album_available_tracks(index) #If that name is new to us just store it if name not in name_dict: name_dict[name] = (index, available_tracks) #If the album has more playable tracks than the stored one elif available_tracks > name_dict[name][1]: name_dict[name] = (index, available_tracks) #Now return the list if indexes return [item[0] for item in name_dict.itervalues()] def get_albums(self): def sort_func(album_index): #Sort by album type and then by year (desc) return ( self.get_album_type(album_index), -self.__artistbrowse.album(album_index).year() ) #Do nothing if is loading if self.is_loaded(): #Build the sorted album list if needed if self.__sorted_albums is None: album_indexes = self.__album_data.keys() sorted_indexes = sorted(album_indexes, key=sort_func) ab = self.__artistbrowse self.__sorted_albums = [ (index, ab.album(index)) for index in sorted_indexes ] print self.__sorted_albums return self.__sorted_albums
class ContainerLoader: __session = None __container = None __playlist_manager = None __playlists = None __checker = None __loading_lock = None __list_lock = None __is_loaded = None def __init__(self, session, container, playlist_manager): self.__session = session self.__container = container self.__playlist_manager = playlist_manager self.__playlists = [] self.__checker = BulkConditionChecker() self.__loading_lock = threading.RLock() self.__list_lock = threading.RLock() self.__is_loaded = False #Load the rest in the background self._start_load() def _fill_spaces(self, position): try: self.__list_lock.acquire() if position >= len(self.__playlists): for idx in range(len(self.__playlists), position + 1): self.__playlists.append(None) finally: self.__list_lock.release() def is_playlist(self, position): playlist_type = self.__container.playlist_type(position) return playlist_type == playlist.PlaylistType.Playlist def add_playlist(self, playlist, position): try: self.__list_lock.acquire() #Ensure that it gets added in the correct position self._fill_spaces(position - 1) #Instantiate a loader if it's a real playlist if self.is_playlist(position): item = ContainerPlaylistLoader(self.__session, playlist, self.__playlist_manager, self) #Ignore if it's not a real playlist else: item = None #Insert the generated item self.__playlists.insert(position, item) finally: self.__list_lock.release() def remove_playlist(self, position): try: self.__list_lock.acquire() del self.__playlists[position] finally: self.__list_lock.release() def move_playlist(self, position, new_position): try: self.__list_lock.acquire() self.__playlists.insert(new_position, self.__playlists[position]) #Calculate new position if position > new_position: position += 1 del self.__playlists[position] finally: self.__list_lock.release() def _add_missing_playlists(self): #Ensure that the container and loader length is the same self._fill_spaces(self.__container.num_playlists() - 1) #Iterate over the container to add the missing ones for pos, item in enumerate(self.__container.playlists()): if self.is_playlist(pos) and self.__playlists[pos] is None: self.add_playlist(item, pos) def _check_playlist(self, playlist): def is_playlist_loaded(): #If it has errors, say yes. if playlist.has_errors(): return True #And if it was loaded, say yes if playlist.is_loaded(): return True self.__checker.add_condition(is_playlist_loaded) def _load_container(self): #Wait for the container to be fully loaded self.__checker.add_condition(self.__container.is_loaded) self.__checker.complete_wait() #Fill the container with unseen playlists self._add_missing_playlists() #Add a load check for each playlist for item in self.__playlists: if item is not None and not item.is_loaded(): self._check_playlist(item) #Wait until all conditions become true self.__checker.complete_wait(self.__container.num_playlists() * 5) #Set the status of the loader self.__is_loaded = True #Check and print errors for not loaded playlists for idx, item in enumerate(self.__playlists): if item is not None and item.has_errors(): xbmc.log('Playlist #%s failed loading.' % idx, xbmc.LOGERROR) #Finally tell the gui we are done xbmc.executebuiltin("Action(Noop)") @run_in_thread def _start_update(self): """ Try gaining a lock. If we can't get one, another update is running, so do nothing. """ if self.__loading_lock.acquire(False): try: self._load_container() finally: self.__loading_lock.release() @run_in_thread def _start_load(self): self.__loading_lock.acquire() try: """ Register container callbacks so they start loading after this point. Too bad if they reach before. """ self.__container.add_callbacks(ContainerCallbacks(self)) #And take care of the rest self._load_container() finally: self.__loading_lock.release() def update(self): self.__checker.check_conditions() self._start_update() def is_loaded(self): return self.__is_loaded def playlist(self, index): return self.__playlists[index] def num_playlists(self): return len(self.__playlists) def playlists(self): return CallbackIterator(self.num_playlists, self.playlist) def get_container(self): return self.__container def __del__(self): print "ContainerLoader __del__"
class BasePlaylistLoader: __playlist = None __playlist_manager = None __checker = None #Playlist attributes __name = None __num_tracks = None __thumbnails = None __is_collaborative = None __is_loaded = None __has_errors = None __has_changes = None def __init__(self, session, playlist, playlist_manager): #Initialize all instance vars self.__playlist = playlist self.__playlist_manager = playlist_manager self.__checker = BulkConditionChecker() self.__is_loaded = False self.__has_errors = False self.__thumbnails = [] #Add the callbacks we are interested in playlist.add_callbacks(PlaylistCallbacks(self)) #Fire playlist loading if neccesary if not playlist.is_in_ram(session): playlist.set_in_ram(session, True) #Finish the rest in the background self.load_in_background() @run_in_thread(threads_per_class=10, single_instance=True) def load_in_background(self): try: #Reset change flag self._set_changes(False) #And call the method that does the actual loading task self._start_loading() #Clear previous error flags (if any) if self.has_errors(): self._set_error(False) except: #Mark this playlist self._set_error(True) finally: #If changes or errors were detected if self.has_changes() or self.has_errors(): self.end_loading() def get_playlist(self): return self.__playlist def _set_thumbnails(self, thumbnails): self.__thumbnails = thumbnails def get_thumbnails(self): return self.__thumbnails def _set_name(self, name): self.__name = name def get_name(self): return self.__name def get_num_tracks(self): return self.__num_tracks def get_tracks(self): return self.__playlist.tracks() def get_track(self, index): track_list = self.get_tracks() return track_list[index] def get_is_collaborative(self): return self.__is_collaborative def _track_is_ready(self, track, test_album=True, test_artists=True): def album_is_loaded(): album = track.album() return album is not None and album.is_loaded() def artists_are_loaded(): for item in track.artists(): if item is None or not item.is_loaded(): return False return True #If track has an error stop further processing if track.error() not in [ErrorType.Ok, ErrorType.IsLoading]: return True #Always test for the track data if not track.is_loaded(): return False #If album data was requested elif test_album and not album_is_loaded(): return False #If artist data was requested elif test_artists and not artists_are_loaded(): return False #Otherwise everything was ok else: return True def _wait_for_playlist(self): if not self.__playlist.is_loaded(): self.__checker.add_condition(self.__playlist.is_loaded) self.__checker.complete_wait(10) def _wait_for_track_metadata(self, track): def test_is_loaded(): return self._track_is_ready(track, test_album=True, test_artists=False) if not test_is_loaded(): self.__checker.add_condition(test_is_loaded) self.__checker.complete_wait(10) def _load_thumbnails(self): pm = self.__playlist_manager #If playlist has an image playlist_image = self.__playlist.get_image() if playlist_image is not None: thumbnails = [pm.get_image_url(playlist_image)] #Otherwise get them from the album covers else: thumbnails = [] for item in self.__playlist.tracks(): #Wait until this track is fully loaded self._wait_for_track_metadata(item) #Check if item was loaded without errors if item.is_loaded() and item.error() == 0: #Append the cover if it's new image_id = item.album().cover() image_url = pm.get_image_url(image_id) if image_url not in thumbnails: thumbnails.append(image_url) #If we reached to the desired thumbnail count... if len(thumbnails) == 4: break #If the thumnbail count is still zero... if len(thumbnails) == 0: self.__thumbnails = ['common/pl-default.png'] return True #If the stored thumbnail data changed... if self.__thumbnails != thumbnails: self.__thumbnails = thumbnails return True def _load_name(self): if self.__name != self.__playlist.name(): self.__name = self.__playlist.name() return True else: return False def _load_num_tracks(self): if self.__num_tracks != self.__playlist.num_tracks(): self.__num_tracks = self.__playlist.num_tracks() return True else: return False def _load_is_collaborative(self): if self.__is_collaborative != self.__playlist.is_collaborative(): self.__is_collaborative = self.__playlist.is_collaborative() return True else: return False def _load_attributes(self): #Now check for changes has_changes = False if self._load_name(): has_changes = True if self._load_num_tracks(): has_changes = True if self._load_is_collaborative(): has_changes = True #If we detected something different return has_changes def _add_condition(self, condition): self.__checker.add_condition(condition) def _wait_for_conditions(self, timeout): self.__checker.complete_wait(timeout) def check(self): self.__checker.check_conditions() def _set_loaded(self, status): self.__is_loaded = status def is_loaded(self): return self.__is_loaded def _set_error(self, status): self.__has_errors = status def has_errors(self): return self.__has_errors def _set_changes(self, status): self.__has_changes = status def has_changes(self): return self.__has_changes def _start_loading(self): raise NotImplementedError() def end_loading(self): pass def __del__(self): print "PlaylistLoader __del__"