def __init__(self, session, artist): self.__condition_list = ConditionList() self.__session = session self.__artist = artist self.__album_data = {} self.__is_loaded = False self.__loader_task = None #Avoid locking this thread and continue in another one self.continue_in_background()
def __init__(self, session, container, playlist_manager): self.__session = session self.__container = container self.__playlist_manager = playlist_manager self.__playlists = [] self.__conditions = ConditionList() self.__loader_lock = threading.RLock() self.__list_lock = threading.RLock() self.__is_loaded = False #Register the callbacks self.__container.add_callbacks(ContainerCallbacks(self)) #Load the rest in the background self.load_in_background()
def __init__(self, session, playlist, playlist_manager): #Initialize all instance vars self.__playlist = playlist self.__playlist_manager = playlist_manager self.__conditions = ConditionList() self.__loader_lock = threading.Lock() self.__is_loaded = False self.__has_errors = False self.__thumbnails = [] #Fire playlist loading if neccesary if not playlist.is_in_ram(session): playlist.set_in_ram(session, True) #Add the playlist callbacks self.__playlist.add_callbacks(PlaylistCallbacks(self)) #Finish the rest in the background self.load_in_background()
class ArtistAlbumLoader: __condition_list = None __session = None __artist = None __album_data = None __artistbrowse = None __is_loaded = None __sorted_albums = None __loader_task = None def __init__(self, session, artist): self.__condition_list = ConditionList() self.__session = session self.__artist = artist self.__album_data = {} self.__is_loaded = False self.__loader_task = None #Avoid locking this thread and continue in another one self.continue_in_background() def check(self): self.__loader_task.notify() def _wait_for_album_info(self, album_info): def info_is_loaded(): return album_info.is_loaded() if not info_is_loaded(): current_task().condition_wait(info_is_loaded, 10) 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(group='load_artist_albums', max_concurrency=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: cb = AlbumCallbacks(current_task()) album_info = albumbrowse.Albumbrowse(self.__session, album, cb) #Now wait until it's loaded self._wait_for_album_info(album_info) #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.check() def _wait_for_album_list(self): #Add the artistbrowse callbacks self.__artistbrowse = artistbrowse.Artistbrowse( self.__session, self.__artist, BrowseType.NoTracks, ArtistCallbacks(self) ) if not self.__artistbrowse.is_loaded(): current_task().condition_wait( self.__artistbrowse.is_loaded, 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.__condition_list.add_condition(album_is_processed) @run_in_thread(group='load_artist_albums',max_concurrency=1) def continue_in_background(self): #Set the reference to the current task self.__loader_task = current_task() #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 current_task().condition_wait(self.__condition_list, 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 ] return self.__sorted_albums
class BasePlaylistLoader: __playlist = None __playlist_manager = None __conditions = None __loader_task = None __loader_lock = 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.__conditions = ConditionList() self.__loader_lock = threading.Lock() self.__is_loaded = False self.__has_errors = False self.__thumbnails = [] #Fire playlist loading if neccesary if not playlist.is_in_ram(session): playlist.set_in_ram(session, True) #Add the playlist callbacks self.__playlist.add_callbacks(PlaylistCallbacks(self)) #Finish the rest in the background self.load_in_background() @run_in_thread(group='load_playlists', max_concurrency=10) def load_in_background(self): #Avoid entering this loop multiple times if self.__loader_lock.acquire(False): try: #Set the current task object self.__loader_task = current_task() #Reset everyting self._set_changes(False) self._set_error(False) #And call the method that does the actual loading task self._load() except: #Set the playlist's error flag self._set_error(True) finally: #Release and clear everything self.__loader_task = None self.__loader_lock.release() #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.__conditions.add_condition(self.__playlist.is_loaded) current_task.condition_wait(self.__conditions, 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.__conditions.add_condition(test_is_loaded) current_task.condition_wait(self.__conditions, 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.__conditions.add_condition(condition) def _wait_for_conditions(self, timeout): current_task().condition_wait(self.__conditions, timeout) def check(self): #If a loading process was not active, start a new one if self.__loader_lock.acquire(False): try: self.load_in_background() finally: self.__loader_lock.release() #Otherwise notify the task else: try: self.__loader_task.notify() except: pass 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 _load(self): raise NotImplementedError() def end_loading(self): pass
class ContainerLoader: __session = None __container = None __playlist_manager = None __playlists = None __checker = None __loader_task = None __loader_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.__conditions = ConditionList() self.__loader_lock = threading.RLock() self.__list_lock = threading.RLock() self.__is_loaded = False #Register the callbacks self.__container.add_callbacks(ContainerCallbacks(self)) #Load the rest in the background self.load_in_background() 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()): #Check if we should continue current_task().check_status() 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.__conditions.add_condition(is_playlist_loaded) def _load_container(self): #Wait for the container to be fully loaded self.__conditions.add_condition(self.__container.is_loaded) current_task().condition_wait(self.__conditions) #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 current_task().condition_wait( self.__conditions, self.__container.num_playlists() * 5 ) #Set the status of the loader self.__is_loaded = True #Check and log errors for not loaded playlists for idx, item in enumerate(self.__playlists): if item is not None and item.has_errors(): get_logger().error('Playlist #%s failed loading.' % idx) #Finally tell the gui we are done xbmc.executebuiltin("Action(Noop)") @run_in_thread(group='load_playlists', max_concurrency=10) def load_in_background(self): #Avoid entering here multiple times if self.__loader_lock.acquire(False): try: #Set the current task object self.__loader_task = current_task() #And take care of the rest self._load_container() finally: #Release and clear everything self.__loader_task = None self.__loader_lock.release() def check(self): #If a loading process was not active, start a new one if self.__loader_lock.acquire(False): try: self.load_in_background() finally: self.__loader_lock.release() #Otherwise notify the task else: try: self.__loader_task.notify() except: pass 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
class ArtistAlbumLoader: __condition_list = None __session = None __artist = None __album_data = None __artistbrowse = None __is_loaded = None __sorted_albums = None __loader_task = None def __init__(self, session, artist): self.__condition_list = ConditionList() self.__session = session self.__artist = artist self.__album_data = {} self.__is_loaded = False self.__loader_task = None #Avoid locking this thread and continue in another one self.continue_in_background() def check(self): self.__loader_task.notify() def _wait_for_album_info(self, album_info): def info_is_loaded(): return album_info.is_loaded() if not info_is_loaded(): current_task().condition_wait(info_is_loaded, 10) 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(group='load_artist_albums', max_concurrency=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 its data else: cb = AlbumCallbacks(current_task()) album_info = albumbrowse.Albumbrowse(self.__session, album, cb) # Now wait until it's loaded self._wait_for_album_info(album_info) # Populate its data self.__album_data[index] = { 'available_tracks': self._num_available_tracks(album_info), 'type': self._get_album_type(album), } # Tell that we're done self.check() def _wait_for_album_list(self): #Add the artistbrowse callbacks self.__artistbrowse = artistbrowse.Artistbrowse( self.__session, self.__artist, BrowseType.NoTracks, ArtistCallbacks(self) ) if not self.__artistbrowse.is_loaded(): current_task().condition_wait( self.__artistbrowse.is_loaded, 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.__condition_list.add_condition(album_is_processed) @run_in_thread(group='load_artist_albums', max_concurrency=1) def continue_in_background(self): # Set the reference to the current task self.__loader_task = current_task() # 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 current_task().condition_wait(self.__condition_list, 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 ] return self.__sorted_albums
class BasePlaylistLoader: __playlist = None __playlist_manager = None __conditions = None __loader_task = None __loader_lock = 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.__conditions = ConditionList() self.__loader_lock = threading.Lock() self.__is_loaded = False self.__has_errors = False self.__thumbnails = [] #Fire playlist loading if neccesary if not playlist.is_in_ram(session): playlist.set_in_ram(session, True) #Add the playlist callbacks self.__playlist.add_callbacks(PlaylistCallbacks(self)) #Finish the rest in the background self.load_in_background() @run_in_thread(group='load_playlists', max_concurrency=10) def load_in_background(self): #Avoid entering this loop multiple times if self.__loader_lock.acquire(False): try: #Set the current task object self.__loader_task = current_task() #Reset everyting self._set_changes(False) self._set_error(False) #And call the method that does the actual loading task self._load() except: #Set the playlist's error flag self._set_error(True) finally: #Release and clear everything self.__loader_task = None self.__loader_lock.release() #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.__conditions.add_condition(self.__playlist.is_loaded) current_task.condition_wait(self.__conditions, 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.__conditions.add_condition(test_is_loaded) current_task.condition_wait(self.__conditions, 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.__conditions.add_condition(condition) def _wait_for_conditions(self, timeout): current_task().condition_wait(self.__conditions, timeout) def check(self): #If a loading process was not active, start a new one if self.__loader_lock.acquire(False): try: self.load_in_background() finally: self.__loader_lock.release() #Otherwise notify the task else: try: self.__loader_task.notify() except: pass 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 _load(self): raise NotImplementedError() def end_loading(self): pass
class ContainerLoader: __session = None __container = None __playlist_manager = None __playlists = None __checker = None __loader_task = None __loader_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.__conditions = ConditionList() self.__loader_lock = threading.RLock() self.__list_lock = threading.RLock() self.__is_loaded = False #Register the callbacks self.__container.add_callbacks(ContainerCallbacks(self)) #Load the rest in the background self.load_in_background() 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()): #Check if we should continue current_task().check_status() 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.__conditions.add_condition(is_playlist_loaded) def _load_container(self): #Wait for the container to be fully loaded self.__conditions.add_condition(self.__container.is_loaded) current_task().condition_wait(self.__conditions) #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 current_task().condition_wait(self.__conditions, self.__container.num_playlists() * 5) #Set the status of the loader self.__is_loaded = True #Check and log errors for not loaded playlists for idx, item in enumerate(self.__playlists): if item is not None and item.has_errors(): get_logger().error('Playlist #%s failed loading.' % idx) #Finally tell the gui we are done xbmc.executebuiltin("Action(Noop)") @run_in_thread(group='load_playlists', max_concurrency=10) def load_in_background(self): #Avoid entering here multiple times if self.__loader_lock.acquire(False): try: #Set the current task object self.__loader_task = current_task() #And take care of the rest self._load_container() finally: #Release and clear everything self.__loader_task = None self.__loader_lock.release() def check(self): #If a loading process was not active, start a new one if self.__loader_lock.acquire(False): try: self.load_in_background() finally: self.__loader_lock.release() #Otherwise notify the task else: try: self.__loader_task.notify() except: pass 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