def _handle_autoplay(self, url=None): if self.autoplay and models.QueuedSong.objects.count() == 0: if url is None: # if no url was specified, use the one of the current song try: current_song = models.CurrentSong.objects.get() url = current_song.external_url except (models.CurrentSong.DoesNotExist, models.CurrentSong.MultipleObjectsReturned): return provider = SongProvider.create(self.musiq, external_url=url) try: suggestion = provider.get_suggestion() except Exception as e: self.musiq.base.logger.error('error during suggestions for ' + url) self.musiq.base.logger.error(e) else: self.musiq._request_music('', suggestion, None, False, provider.type, archive=False, manually_requested=False)
def _handle_autoplay(self, url: Optional[str] = None) -> None: if self.autoplay and models.QueuedSong.objects.count() == 0: if url is None: # if no url was specified, use the one of the current song try: current_song = models.CurrentSong.objects.get() url = current_song.external_url except ( models.CurrentSong.DoesNotExist, models.CurrentSong.MultipleObjectsReturned, ): return provider = SongProvider.create(self.musiq, external_url=url) try: suggestion = provider.get_suggestion() # The player loop is not restarted after error automatically. # As this function can raise several exceptions (it might do networking) # we catch every exception to make sure the loop keeps running except Exception as e: # pylint: disable=broad-except logging.exception("error during suggestions for " + url) else: self.musiq.do_request_music( "", suggestion, None, False, provider.type, archive=False, manually_requested=False, )
def _request_music(self, ip, query, key, playlist, platform, archive=True, manually_requested=True): providers = [] if playlist: if key is not None: # an archived song was requested. The key determines the SongProvider (Youtube or Spotify) provider = PlaylistProvider.create(self, query, key) if provider is None: return HttpResponseBadRequest('No provider found for requested playlist') providers.append(provider) else: # try to use spotify if the user did not specifically request youtube if platform is None or platform == 'spotify': if self.base.settings.spotify_enabled: providers.append(SpotifyPlaylistProvider(self, query, key)) # use Youtube as a fallback providers.append(YoutubePlaylistProvider(self, query, key)) else: if key is not None: # an archived song was requested. The key determines the SongProvider (Youtube or Spotify) provider = SongProvider.create(self, query, key) if provider is None: return HttpResponseBadRequest('No provider found for requested song') providers.append(provider) else: # try to use spotify if the user did not specifically request youtube if platform is None or platform == 'spotify': if self.base.settings.spotify_enabled: providers.append(SpotifySongProvider(self, query, key)) # use Youtube as a fallback providers.append(YoutubeSongProvider(self, query, key)) fallback = False used_provider = None for i, provider in enumerate(providers): if not provider.check_cached(): if not provider.check_downloadable(): # this provider cannot provide this song, use the next provider # if this was the last provider, show its error if i == len(providers) - 1: return HttpResponseBadRequest(provider.error) fallback = True continue if not provider.download(ip, archive=archive, manually_requested=manually_requested): return HttpResponseBadRequest(provider.error) else: provider.enqueue(ip, archive=archive, manually_requested=manually_requested) # the current provider could provide the song, don't try the other ones used_provider = provider break message = used_provider.ok_message if fallback: message = message + ' (used fallback)' return HttpResponse(message)
def request_radio(self, request): # only get ip on user requests if self.base.settings.logging_enabled: ip, is_routable = ipware.get_client_ip(request) if ip is None: ip = '' else: ip = '' try: current_song = CurrentSong.objects.get() except CurrentSong.DoesNotExist: return HttpResponseBadRequest('Need a song to play the radio') provider = SongProvider.create(self, external_url=current_song.external_url) return provider.request_radio(ip)
def request_radio(self, request: WSGIRequest) -> HttpResponse: """Endpoint to request radio for the current song.""" # only get ip on user requests if self.base.settings.logging_enabled: request_ip, _ = ipware.get_client_ip(request) if request_ip is None: request_ip = "" else: request_ip = "" try: current_song = CurrentSong.objects.get() except CurrentSong.DoesNotExist: return HttpResponseBadRequest("Need a song to play the radio") provider = SongProvider.create(self, external_url=current_song.external_url) return provider.request_radio(request_ip)
def get_suggestions(self, request: WSGIRequest) -> JsonResponse: """Returns suggestions for a given query. Combines online and offline suggestions.""" terms = request.GET["term"].split() suggest_playlist = request.GET["playlist"] == "true" results: List[Dict[str, Union[str, int]]] = [] if self.musiq.base.settings.has_internet: if self.musiq.base.settings.spotify_enabled: spotify_suggestions = Spotify().get_search_suggestions( " ".join(terms), suggest_playlist) spotify_suggestions = spotify_suggestions[:2] for suggestion, external_url in spotify_suggestions: results.append({ "key": external_url, "value": suggestion, "type": "spotify-online", }) if self.musiq.base.settings.soundcloud_enabled: spotify_suggestions = Soundcloud().get_search_suggestions( " ".join(terms)) soundcloud_suggestions = spotify_suggestions[:2] for suggestion in soundcloud_suggestions: results.append({ "key": -1, "value": suggestion, "type": "soundcloud-online" }) if self.musiq.base.settings.youtube_enabled: youtube_suggestions = Youtube().get_search_suggestions( " ".join(terms)) # limit to the first three online suggestions youtube_suggestions = youtube_suggestions[:2] for suggestion in youtube_suggestions: results.append({ "key": -1, "value": suggestion, "type": "youtube-online" }) # The following query is roughly equivalent to the following SQL code: # SELECT DISTINCT id, title, artist, counter # FROM core_archivedsong s LEFT JOIN core_archivedquery q ON q.song # WHERE forall term in terms: term in q.query or term in s.artist or term in s.title # ORDER BY -counter if suggest_playlist: remaining_playlists = ArchivedPlaylist.objects.prefetch_related( "queries") # exclude radios from suggestions remaining_playlists = remaining_playlists.exclude( list_id__startswith="RD").exclude(list_id__contains="&list=RD") for term in terms: remaining_playlists = remaining_playlists.filter( Q(title__icontains=term) | Q(queries__query__icontains=term)) playlist_suggestions = (remaining_playlists.values( "id", "title", "counter").distinct().order_by("-counter")[:20]) for playlist in playlist_suggestions: archived_playlist = ArchivedPlaylist.objects.get( id=playlist["id"]) result_dict: Dict[str, Union[str, int]] = { "key": playlist["id"], "value": playlist["title"], "counter": playlist["counter"], "type": song_utils.determine_playlist_type(archived_playlist), } results.append(result_dict) else: remaining_songs = ArchivedSong.objects.prefetch_related("queries") for term in terms: remaining_songs = remaining_songs.filter( Q(title__icontains=term) | Q(artist__icontains=term) | Q(queries__query__icontains=term)) song_suggestions = (remaining_songs.values( "id", "title", "url", "artist", "counter").distinct().order_by("-counter")[:20]) for song in song_suggestions: provider = SongProvider.create(self.musiq, external_url=song["url"]) cached = provider.check_cached() # don't suggest local songs if they are not cached (=not at expected location) if not cached and provider.type == "local": continue # don't suggest online songs when we don't have internet if not self.musiq.base.settings.has_internet and not cached: continue # don't suggest youtube songs if it was disabled if (not self.musiq.base.settings.youtube_enabled and provider.type == "youtube"): continue # don't suggest spotify songs if we are not logged in if (not self.musiq.base.settings.spotify_enabled and provider.type == "spotify"): continue # don't suggest soundcloud songs if we are not logged in if (not self.musiq.base.settings.soundcloud_enabled and provider.type == "soundcloud"): continue result_dict = { "key": song["id"], "value": song_utils.displayname(song["artist"], song["title"]), "counter": song["counter"], "type": provider.type, } results.append(result_dict) return JsonResponse(results, safe=False)
def do_request_music( self, request_ip: str, query: str, key: Optional[int], playlist: bool, platform: str, archive: bool = True, manually_requested: bool = True, ) -> Tuple[bool, str, Optional[int]]: """Performs the actual requesting of the music, not an endpoint. Enqueues the requested song or playlist into the queue, using appropriate providers. Returns a 3-tuple: successful, message, queue_key""" providers: List[MusicProvider] = [] provider: MusicProvider if playlist: if key is not None: # an archived song was requested. # The key determines the PlaylistProvider provider = PlaylistProvider.create(self, query, key) if provider is None: return False, "No provider found for requested playlist", None providers.append(provider) else: # try to use spotify if the user did not specifically request youtube if self.base.settings.soundcloud_enabled: soundcloud_provider = SoundcloudPlaylistProvider(self, query, key) if platform == "soundcloud": providers.insert(0, soundcloud_provider) else: providers.append(soundcloud_provider) if self.base.settings.spotify_enabled: spotify_provider = SpotifyPlaylistProvider(self, query, key) if platform == "spotify": providers.insert(0, spotify_provider) else: providers.append(spotify_provider) if self.base.settings.youtube_enabled: youtube_provider = YoutubePlaylistProvider(self, query, key) if platform == "youtube": providers.insert(0, youtube_provider) else: providers.append(youtube_provider) else: if key is not None: # an archived song was requested. # The key determines the SongProvider provider = SongProvider.create(self, query, key) if provider is None: return False, "No provider found for requested song", None providers.append(provider) else: if platform == "local": # if a local provider was requested, # use only this one as its only possible source is the database providers.append(LocalSongProvider(self, query, key)) else: if self.base.settings.soundcloud_enabled: try: soundcloud_provider = SoundcloudSongProvider( self, query, key ) if platform == "soundcloud": providers.insert(0, soundcloud_provider) else: providers.append(soundcloud_provider) except WrongUrlError: pass if self.base.settings.spotify_enabled: try: spotify_provider = SpotifySongProvider(self, query, key) if platform == "spotify": providers.insert(0, spotify_provider) else: providers.append(spotify_provider) except WrongUrlError: pass if self.base.settings.youtube_enabled: try: youtube_provider = YoutubeSongProvider(self, query, key) if platform == "youtube": providers.insert(0, youtube_provider) else: providers.append(youtube_provider) except WrongUrlError: pass if not len(providers): return False, "No backend configured to handle your request.", None fallback = False for i, provider in enumerate(providers): try: provider.request( request_ip, archive=archive, manually_requested=manually_requested ) # the current provider could provide the song, don't try the other ones break except ProviderError: # this provider cannot provide this song, use the next provider # if this was the last provider, show its error if i == len(providers) - 1: return False, provider.error, None fallback = True message = provider.ok_message queue_key = None if not playlist: queue_key = provider.queued_song.id if fallback: message += " (used fallback)" return True, message, queue_key
def get_suggestions(self, request: WSGIRequest) -> JsonResponse: """Returns suggestions for a given query. Combines online and offline suggestions.""" query = request.GET["term"] suggest_playlist = request.GET["playlist"] == "true" results: List[Dict[str, Union[str, int]]] = [] if (self.musiq.base.settings.online_suggestions and self.musiq.base.settings.has_internet): number_of_suggestions = 2 if [ self.musiq.base.settings.spotify_enabled, self.musiq.base.settings.soundcloud_enabled, self.musiq.base.settings.youtube_enabled, ].count(True) > 1: # If there is more than one active service, # only offer one online suggestion per service number_of_suggestions = 1 if self.musiq.base.settings.spotify_enabled: spotify_suggestions = Spotify().get_search_suggestions( query, suggest_playlist) spotify_suggestions = spotify_suggestions[: number_of_suggestions] for suggestion, external_url in spotify_suggestions: results.append({ "key": external_url, "value": suggestion, "type": "spotify-online", }) if self.musiq.base.settings.soundcloud_enabled: soundcloud_suggestions = Soundcloud().get_search_suggestions( query) soundcloud_suggestions = soundcloud_suggestions[: number_of_suggestions] for suggestion in soundcloud_suggestions: results.append({ "key": -1, "value": suggestion, "type": "soundcloud-online" }) if self.musiq.base.settings.youtube_enabled: youtube_suggestions = Youtube().get_search_suggestions(query) # limit to the first three online suggestions youtube_suggestions = youtube_suggestions[: number_of_suggestions] for suggestion in youtube_suggestions: results.append({ "key": -1, "value": suggestion, "type": "youtube-online" }) if suggest_playlist: search_results = watson.search(query, models=(ArchivedPlaylist, ))[:20] for playlist in search_results: playlist_info = playlist.meta archived_playlist = ArchivedPlaylist.objects.get( id=playlist_info["id"]) result_dict: Dict[str, Union[str, int]] = { "key": playlist_info["id"], "value": playlist_info["title"], "counter": playlist_info["counter"], "type": song_utils.determine_playlist_type(archived_playlist), } results.append(result_dict) else: search_results = watson.search(query, models=(ArchivedSong, ))[:20] for search_result in search_results: song_info = search_result.meta provider = SongProvider.create(self.musiq, external_url=song_info["url"]) cached = provider.check_cached() # don't suggest local songs if they are not cached (=not at expected location) if not cached and provider.type == "local": continue # don't suggest online songs when we don't have internet if not self.musiq.base.settings.has_internet and not cached: continue # don't suggest youtube songs if it was disabled if (not self.musiq.base.settings.youtube_enabled and provider.type == "youtube"): continue # don't suggest spotify songs if we are not logged in if (not self.musiq.base.settings.spotify_enabled and provider.type == "spotify"): continue # don't suggest soundcloud songs if we are not logged in if (not self.musiq.base.settings.soundcloud_enabled and provider.type == "soundcloud"): continue result_dict = { "key": song_info["id"], "value": song_utils.displayname(song_info["artist"], song_info["title"]), "counter": song_info["counter"], "type": provider.type, } results.append(result_dict) return JsonResponse(results, safe=False)
def get_suggestions(self, request: WSGIRequest) -> JsonResponse: """Returns suggestions for a given query. Combines online and offline suggestions.""" query = request.GET["term"] suggest_playlist = request.GET["playlist"] == "true" results = self._online_suggestions(query, suggest_playlist) basic_settings = self.musiq.base.settings.basic if suggest_playlist: search_results = watson.search(query, models=( ArchivedPlaylist, ))[:basic_settings.number_of_suggestions] for playlist in search_results: playlist_info = playlist.meta archived_playlist = ArchivedPlaylist.objects.get( id=playlist_info["id"]) result_dict: Dict[str, Union[str, int]] = { "key": playlist_info["id"], "value": playlist_info["title"], "counter": playlist.object.counter, "type": song_utils.determine_playlist_type(archived_playlist), } results.append(result_dict) else: search_results = watson.search( query, models=(ArchivedSong, ))[:basic_settings.number_of_suggestions] for search_result in search_results: song_info = search_result.meta provider = SongProvider.create(self.musiq, external_url=song_info["url"]) cached = provider.check_cached() # don't suggest local songs if they are not cached (=not at expected location) if not cached and provider.type == "local": continue # don't suggest online songs when we don't have internet if not self.musiq.base.settings.basic.has_internet and not cached: continue # don't suggest youtube songs if it was disabled if (not self.musiq.base.settings.platforms.youtube_enabled and provider.type == "youtube"): continue # don't suggest spotify songs if we are not logged in if (not self.musiq.base.settings.platforms.spotify_enabled and provider.type == "spotify"): continue # don't suggest soundcloud songs if we are not logged in if (not self.musiq.base.settings.platforms.soundcloud_enabled and provider.type == "soundcloud"): continue result_dict = { "key": song_info["id"], "value": song_utils.displayname(song_info["artist"], song_info["title"]), "counter": search_result.object.counter, "type": provider.type, } results.append(result_dict) return JsonResponse(results, safe=False)
def do_request_music( self, request_ip: str, query: str, key: Optional[int], playlist: bool, platform: str, archive: bool = True, manually_requested: bool = True, ) -> HttpResponse: """Performs the actual requesting of the music, not an endpoint. Enqueues the requested song or playlist into the queue, using appropriate providers.""" providers: List[MusicProvider] = [] provider: MusicProvider if playlist: if key is not None: # an archived song was requested. # The key determines the SongProvider (Youtube or Spotify) provider = PlaylistProvider.create(self, query, key) if provider is None: return HttpResponseBadRequest( "No provider found for requested playlist") providers.append(provider) else: # try to use spotify if the user did not specifically request youtube if platform is None or platform == "spotify": if self.base.settings.spotify_enabled: providers.append( SpotifyPlaylistProvider(self, query, key)) if self.base.settings.youtube_enabled: # use Youtube as a fallback providers.append(YoutubePlaylistProvider(self, query, key)) else: if key is not None: # an archived song was requested. # The key determines the SongProvider (Youtube or Spotify) provider = SongProvider.create(self, query, key) if provider is None: return HttpResponseBadRequest( "No provider found for requested song") providers.append(provider) else: if platform == "local": # if a local provider was requested, # use only this one as its only possible source is the database providers.append(LocalSongProvider(self, query, key)) else: # try to use spotify if the user did not specifically request youtube if platform is None or platform == "spotify": if self.base.settings.spotify_enabled: try: providers.append( SpotifySongProvider(self, query, key)) except ValueError: pass if self.base.settings.youtube_enabled: # use Youtube as a fallback providers.append(YoutubeSongProvider(self, query, key)) if not len(providers): return HttpResponseBadRequest( "No backend configured to handle your request.") fallback = False for i, provider in enumerate(providers): if not provider.check_cached(): if not provider.check_downloadable(): # this provider cannot provide this song, use the next provider # if this was the last provider, show its error if i == len(providers) - 1: return HttpResponseBadRequest(provider.error) fallback = True continue if not provider.download( request_ip, archive=archive, manually_requested=manually_requested): return HttpResponseBadRequest(provider.error) else: provider.enqueue(request_ip, archive=archive, manually_requested=manually_requested) # the current provider could provide the song, don't try the other ones used_provider = provider break message = provider.ok_message if fallback: message += " (used fallback)" return HttpResponse(message)
def get_suggestions(self, request): terms = request.GET['term'].split() suggest_playlist = request.GET['playlist'] == 'true' results = [] if suggest_playlist: remaining_playlists = ArchivedPlaylist.objects.prefetch_related( 'queries') # exclude radios from suggestions remaining_playlists = remaining_playlists.exclude( list_id__startswith='RD').exclude(list_id__contains='&list=RD') for term in terms: remaining_playlists = remaining_playlists.filter( Q(title__icontains=term) | Q(queries__query__icontains=term)) remaining_playlists = remaining_playlists \ .values('id', 'title', 'counter') \ .distinct() \ .order_by('-counter') \ [:20] for playlist in remaining_playlists: cached = False archived_playlist = ArchivedPlaylist.objects.get( id=playlist['id']) result_dict = { 'key': playlist['id'], 'value': playlist['title'], 'counter': playlist['counter'], 'type': song_utils.determine_playlist_type(archived_playlist), } results.append(result_dict) else: remaining_songs = ArchivedSong.objects.prefetch_related('queries') for term in terms: remaining_songs = remaining_songs.filter( Q(title__icontains=term) | Q(artist__icontains=term) | Q(queries__query__icontains=term)) remaining_songs = remaining_songs \ .values('id', 'title', 'url', 'artist', 'counter') \ .distinct() \ .order_by('-counter') \ [:20] for song in remaining_songs: provider = SongProvider.create(self.musiq, external_url=song['url']) cached = provider.check_cached() # don't suggest local songs if they are not cached (=not at expected location) if not cached and provider.type == 'local': continue # don't suggest online songs when we don't have internet if not self.musiq.base.settings.has_internet and not cached: continue # don't suggest spotify songs if we are not logged in if not self.musiq.base.settings.spotify_enabled and provider.type == 'spotify': continue result_dict = { 'key': song['id'], 'value': song_utils.displayname(song['artist'], song['title']), 'counter': song['counter'], 'type': provider.type } results.append(result_dict) return HttpResponse(json.dumps(results)) """ query for the suggestions
def _loop(self): while True: catch_up = None if models.CurrentSong.objects.exists(): # recover interrupted song from database current_song = models.CurrentSong.objects.get() # continue with the current song (approximately) where we last left song_provider = SongProvider.create(self.musiq, internal_url=current_song.internal_url) duration = song_provider.get_metadata()['duration'] catch_up = round((timezone.now() - current_song.created).total_seconds() * 1000) if catch_up > duration * 1000: catch_up = -1 else: self.queue_semaphore.acquire() # select the next song depending on settings if self.musiq.base.settings.voting_system: with transaction.atomic(): song = self.queue.all().order_by('-votes', 'index')[0] song_id = song.id self.queue.remove(song.id) elif self.shuffle: index = random.randint(0,models.QueuedSong.objects.count() - 1) song_id = models.QueuedSong.objects.all()[index].id song = self.queue.remove(song_id) else: # move the first song in the queue into the current song song_id, song = self.queue.dequeue() if song is None: # either the semaphore didn't match up with the actual count of songs in the queue or a race condition occured self.musiq.base.logger.info('dequeued on empty list') continue current_song = models.CurrentSong.objects.create( queue_key=song_id, manually_requested=song.manually_requested, votes=song.votes, internal_url=song.internal_url, external_url=song.external_url, artist=song.artist, title=song.title, duration=song.duration, ) self._handle_autoplay() try: archived_song = models.ArchivedSong.objects.get(url=current_song.external_url) if self.musiq.base.settings.voting_system: votes = current_song.votes else: votes = None if self.musiq.base.settings.logging_enabled: models.PlayLog.objects.create( song=archived_song, manually_requested=current_song.manually_requested, votes=votes) except (models.ArchivedSong.DoesNotExist, models.ArchivedSong.MultipleObjectsReturned): pass self.musiq.update_state() playing = Event() @self.player.on_event('playback_state_changed') def on_playback_state_changed(event): playing.set() with self.mopidy_command(important=True): # after a restart consume may be set to False again, so make sure it is on self.player.tracklist.clear() self.player.tracklist.set_consume(True) self.player.tracklist.add(uris=[current_song.internal_url]) self.player.playback.play() # mopidy can only seek when the song is playing playing.wait(timeout=1) if catch_up is not None and catch_up >= 0: self.player.playback.seek(catch_up) self.musiq.update_state() if catch_up is None or catch_up >= 0: if not self._wait_until_song_end(): # there was a ConnectionError during waiting for the song to end # thus, we do not delete the current song but recover its state by restarting the loop continue current_song.delete() if self.repeat: song_provider = SongProvider.create(self.musiq, internal_url=current_song.internal_url) self.queue.enqueue(song_provider.get_metadata(), False) self.queue_semaphore.release() else: # the song left the queue, we can delete big downloads song_utils.decide_deletion(current_song.internal_url) self.musiq.update_state() if self.musiq.base.user_manager.partymode_enabled() and random.random() < self.musiq.base.settings.alarm_probability: self.alarm_playing.set() self.musiq.base.lights.alarm_started() self.musiq.update_state() with self.mopidy_command(important=True): self.player.tracklist.add(uris=['file://'+os.path.join(settings.BASE_DIR, 'config/sounds/alarm.m4a')]) self.player.playback.play() playing.clear() playing.wait(timeout=1) self._wait_until_song_end() self.musiq.base.lights.alarm_stopped() self.musiq.update_state() self.alarm_playing.clear()
def _loop(self) -> None: """The main loop of the player. Takes a song from the queue and plays it until it is finished.""" while True: catch_up = None if models.CurrentSong.objects.exists(): # recover interrupted song from database current_song = models.CurrentSong.objects.get() # continue with the current song (approximately) where we last left song_provider = SongProvider.create( self.musiq, external_url=current_song.external_url) duration = int(song_provider.get_metadata()["duration"]) catch_up = round( (timezone.now() - current_song.created).total_seconds() * 1000) if catch_up > duration * 1000: catch_up = -1 else: self.queue_semaphore.acquire() if not self.running: break # select the next song depending on settings song: Optional[models.QueuedSong] if self.musiq.base.settings.basic.voting_system: with transaction.atomic(): song = self.queue.confirmed().order_by( "-votes", "index")[0] song_id = song.id self.queue.remove(song.id) elif self.musiq.controller.shuffle: confirmed = self.queue.confirmed() index = random.randint(0, confirmed.count() - 1) song_id = confirmed[index].id song = self.queue.remove(song_id) else: # move the first song in the queue into the current song song_id, song = self.queue.dequeue() if song is None: # either the semaphore didn't match up with the actual count # of songs in the queue or a race condition occured logging.warning("dequeued on empty list") continue current_song = models.CurrentSong.objects.create( queue_key=song_id, manually_requested=song.manually_requested, votes=song.votes, internal_url=song.internal_url, external_url=song.external_url, artist=song.artist, title=song.title, duration=song.duration, ) self.handle_autoplay() try: archived_song = models.ArchivedSong.objects.get( url=current_song.external_url) votes: Optional[int] if self.musiq.base.settings.basic.voting_system: votes = current_song.votes else: votes = None if self.musiq.base.settings.basic.logging_enabled: models.PlayLog.objects.create( song=archived_song, manually_requested=current_song.manually_requested, votes=votes, ) except ( models.ArchivedSong.DoesNotExist, models.ArchivedSong.MultipleObjectsReturned, ): pass self.musiq.update_state() playing = Event() @self.player.on_event("playback_state_changed") def _on_playback_state_changed(_event): playing.set() with self.mopidy_command(important=True): # after a restart consume may be set to False again, so make sure it is on self.player.tracklist.clear() self.player.tracklist.set_consume(True) self.player.tracklist.add(uris=[current_song.internal_url]) self.player.playback.play() # mopidy can only seek when the song is playing playing.wait(timeout=1) if catch_up is not None and catch_up >= 0: self.player.playback.seek(catch_up) self.musiq.update_state() if catch_up is None or catch_up >= 0: if not self._wait_until_song_end(): # there was a ConnectionError during waiting for the song to end # we do not delete the current song but recover its state by restarting the loop continue current_song.delete() if self.musiq.controller.repeat: song_provider = SongProvider.create( self.musiq, external_url=current_song.external_url) self.queue.enqueue(song_provider.get_metadata(), False) self.queue_semaphore.release() self.musiq.update_state() if (self.musiq.base.user_manager.partymode_enabled() and random.random() < self.musiq.base.settings.basic.alarm_probability): self.alarm_playing.set() self.musiq.base.lights.alarm_started() self.musiq.update_state() with self.mopidy_command(important=True): self.player.tracklist.add(uris=[ "file://" + os.path.join(settings.BASE_DIR, "config/sounds/alarm.m4a") ]) self.player.playback.play() playing.clear() playing.wait(timeout=1) self._wait_until_song_end() self.musiq.base.lights.alarm_stopped() self.musiq.update_state() self.alarm_playing.clear()