some more fixes for http streamer
authormarcelveldt <marcelvanderveldt@MacBook-Pro.local>
Sun, 19 May 2019 22:28:24 +0000 (00:28 +0200)
committermarcelveldt <marcelvanderveldt@MacBook-Pro.local>
Sun, 19 May 2019 22:28:24 +0000 (00:28 +0200)
music_assistant/modules/musicproviders/file.py
music_assistant/modules/player.py
music_assistant/modules/playerproviders/chromecast.py
music_assistant/modules/playerproviders/lms.py

index 7d883c40a5ff78e9bb7965ddd3bd130375561964..8f7900abe66060c62f31761cfd1da10edbb2ef9d 100644 (file)
@@ -11,6 +11,7 @@ from models import MusicProvider, MediaType, TrackQuality, AlbumType, Artist, Al
 from constants import CONF_ENABLED
 import taglib
 from modules.cache import use_cache
+import base64
 
 
 def setup(mass):
@@ -104,34 +105,37 @@ class FileProvider(MusicProvider):
 
     async def get_artist(self, prov_item_id) -> Artist:
         ''' get full artist details by id '''
-        if not os.path.isdir(prov_item_id):
-            LOGGER.error("artist path does not exist: %s" % prov_item_id)
-            return None
-        if "\\" in prov_item_id:
-            name = prov_item_id.split("\\")[-1]
+        if not os.sep in prov_item_id:
+            itempath = base64.b64decode(prov_item_id).decode('utf-8')
         else:
-            name = prov_item_id.split("/")[-1]
+            itempath = prov_item_id
+            prov_item_id = base64.b64encode(itempath.encode('utf-8')).decode('utf-8')
+        if not os.path.isdir(itempath):
+            LOGGER.error("artist path does not exist: %s" % itempath)
+            return None
+        name = itempath.split(os.sep)[-1]
         artist = Artist()
         artist.item_id = prov_item_id
         artist.provider = self.prov_id
         artist.name = name
         artist.provider_ids.append({
             "provider": self.prov_id,
-            "item_id": prov_item_id
+            "item_id": artist.item_id
         })
         return artist
         
     async def get_album(self, prov_item_id) -> Album:
         ''' get full album details by id '''
-        if not os.path.isdir(prov_item_id):
-            LOGGER.error("album path does not exist: %s" % prov_item_id)
-            return None
-        if "\\" in prov_item_id:
-            name = prov_item_id.split("\\")[-1]
-            artistpath = prov_item_id.rsplit("\\", 1)[0]
+        if not os.sep in prov_item_id:
+            itempath = base64.b64decode(prov_item_id).decode('utf-8')
         else:
-            name = prov_item_id.split("/")[-1]
-            artistpath = prov_item_id.rsplit("/", 1)[0]
+            itempath = prov_item_id
+            prov_item_id = base64.b64encode(itempath.encode('utf-8')).decode('utf-8')
+        if not os.path.isdir(itempath):
+            LOGGER.error("album path does not exist: %s" % itempath)
+            return None
+        name = itempath.split(os.sep)[-1]
+        artistpath = itempath.rsplit(os.sep, 1)[0]
         album = Album()
         album.item_id = prov_item_id
         album.provider = self.prov_id
@@ -147,25 +151,33 @@ class FileProvider(MusicProvider):
 
     async def get_track(self, prov_item_id) -> Track:
         ''' get full track details by id '''
-        if not os.path.isfile(prov_item_id):
-            LOGGER.error("track path does not exist: %s" % prov_item_id)
+        if not os.sep in prov_item_id:
+            itempath = base64.b64decode(prov_item_id).decode('utf-8')
+        else:
+            itempath = prov_item_id
+        if not os.path.isfile(itempath):
+            LOGGER.error("track path does not exist: %s" % itempath)
             return None
-        return await self.__parse_track(prov_item_id)
+        return await self.__parse_track(itempath)
 
     async def get_playlist(self, prov_item_id) -> Playlist:
         ''' get full playlist details by id '''
-        if not os.path.isfile(prov_item_id):
-            LOGGER.error("playlist path does not exist: %s" % prov_item_id)
+        if not os.sep in prov_item_id:
+            itempath = base64.b64decode(prov_item_id).decode('utf-8')
+        else:
+            itempath = prov_item_id
+            prov_item_id = base64.b64encode(itempath.encode('utf-8')).decode('utf-8')
+        if not os.path.isfile(itempath):
+            LOGGER.error("playlist path does not exist: %s" % itempath)
             return None
-        filepath = prov_item_id
         playlist = Playlist()
-        playlist.item_id = filepath
+        playlist.item_id = prov_item_id
         playlist.provider = self.prov_id
-        playlist.name = filepath.split('\\')[-1].split('/')[-1].replace('.m3u', '')
+        playlist.name = itempath.split(os.sep)[-1].replace('.m3u', '')
         playlist.is_editable = True
         playlist.provider_ids.append({
             "provider": self.prov_id,
-            "item_id": filepath
+            "item_id": prov_item_id
         })
         playlist.owner = 'disk'
         return playlist
@@ -173,7 +185,10 @@ class FileProvider(MusicProvider):
     async def get_album_tracks(self, prov_album_id) -> List[Track]:
         ''' get album tracks for given album id '''
         result = []
-        albumpath = prov_album_id
+        if not os.sep in prov_album_id:
+            albumpath = base64.b64decode(prov_album_id).decode('utf-8')
+        else:
+            albumpath = prov_album_id
         if not os.path.isdir(albumpath):
             LOGGER.error("album path does not exist: %s" % albumpath)
             return []
@@ -190,11 +205,15 @@ class FileProvider(MusicProvider):
     async def get_playlist_tracks(self, prov_playlist_id, limit=50, offset=0) -> List[Track]:
         ''' get playlist tracks for given playlist id '''
         tracks = []
-        if not os.path.isfile(prov_playlist_id):
-            LOGGER.error("playlist path does not exist: %s" % prov_playlist_id)
+        if not os.sep in prov_playlist_id:
+            itempath = base64.b64decode(prov_playlist_id).decode('utf-8')
+        else:
+            itempath = prov_playlist_id
+        if not os.path.isfile(itempath):
+            LOGGER.error("playlist path does not exist: %s" % itempath)
             return []
         counter = 0
-        with open(prov_playlist_id) as f:
+        with open(itempath) as f:
             for line in f.readlines():
                 line = line.strip()
                 if line and not line.startswith('#'):
@@ -210,7 +229,10 @@ class FileProvider(MusicProvider):
     async def get_artist_albums(self, prov_artist_id) -> List[Album]:
         ''' get a list of albums for the given artist '''
         result = []
-        artistpath = prov_artist_id
+        if not os.sep in prov_artist_id:
+            artistpath = base64.b64decode(prov_artist_id).decode('utf-8')
+        else:
+            artistpath = prov_artist_id
         if not os.path.isdir(artistpath):
             LOGGER.error("artist path does not exist: %s" % artistpath)
             return []
@@ -231,10 +253,14 @@ class FileProvider(MusicProvider):
 
     async def get_stream_content_type(self, track_id):
         ''' return the content type for the given track when it will be streamed'''
+        if not os.sep in track_id:
+            track_id = base64.b64decode(track_id).decode('utf-8')
         return track_id.split('.')[-1]
     
-    async def get_stream(self, track_id):
+    async def get_audio_stream(self, track_id):
         ''' get audio stream for a track '''
+        if not os.sep in track_id:
+            track_id = base64.b64decode(track_id).decode('utf-8')
         with open(track_id) as f:
             while True:
                 line = f.readline()
@@ -250,15 +276,13 @@ class FileProvider(MusicProvider):
             song = taglib.File(filename)
         except:
             return None # not a media file ?
+        prov_item_id = base64.b64encode(filename.encode('utf-8')).decode('utf-8')
         track.duration = song.length
-        track.item_id = filename
+        track.item_id = prov_item_id
         track.provider = self.prov_id
         name = song.tags['TITLE'][0]
         track.name, track.version = parse_track_title(name)
-        if "\\" in filename:
-            albumpath = filename.rsplit("\\",1)[0]
-        else:
-            albumpath = filename.rsplit("/",1)[0]
+        albumpath = filename.rsplit(os.sep,1)[0]
         track.album = await self.get_album(albumpath)
         artists = []
         for artist_str in song.tags['ARTIST']:
@@ -272,7 +296,7 @@ class FileProvider(MusicProvider):
                 artist.item_id = fake_artistpath # temporary id
                 artist.provider_ids.append({
                         "provider": self.prov_id,
-                        "item_id": fake_artistpath
+                        "item_id": base64.b64encode(fake_artistpath.encode('utf-8')).decode('utf-8')
                     })
             artists.append(artist)
         track.artists = artists
@@ -306,7 +330,7 @@ class FileProvider(MusicProvider):
             quality_details = "%s kbps" % (song.bitrate)
         track.provider_ids.append({
             "provider": self.prov_id,
-            "item_id": filename,
+            "item_id": prov_item_id,
             "quality": quality,
             "details": quality_details
         })
index 31a55697593672a80712011ac64484ba408510b4..1ec7739a17c70674f851c679952dc20995b6b538 100755 (executable)
@@ -161,6 +161,24 @@ class Player():
         player.volume_level = new_volume
         return True
 
+    async def __check_player_group_power(self, player_details, player_childs):
+        ''' handle group power '''
+        childs_powered = False
+        for child_player in player_childs:
+            if child_player.powered:
+                childs_powered = True
+                break
+        if player_details.powered and not childs_powered:
+            # all childs turned off so turn off group player
+            LOGGER.info('all childs turned off so turn off group player %s' % player_details.name)
+            await self.player_command(player_details.player_id, 'power', 'off')
+            player_details.powered = False
+        elif not player_details.powered and childs_powered:
+            # all childs turned off but group player still off, so turn it on
+            LOGGER.info('child(s) turned on but group player still off, so turn it on %s' % player_details.name)
+            await self.player_command(player_details.player_id, 'power', 'on')
+            player_details.powered = True
+    
     async def remove_player(self, player_id):
         ''' handle a player remove '''
         self._players.pop(player_id, None)
@@ -207,7 +225,7 @@ class Player():
         if player_details.is_group and player.settings['apply_group_volume']:
             await self.__update_player_group_volume(player_details, player_childs)
         if player_details.is_group and player.settings['apply_group_power']:
-            await self.__update_player_group_power(player_details, player_childs)
+            await self.__check_player_group_power(player_details, player_childs)
         # compare values to detect changes
         if player.cur_item and player_details.cur_item and player.cur_item.name != player_details.cur_item.name:
             player_changed = True
@@ -265,24 +283,6 @@ class Player():
         group_volume = group_volume / active_players if active_players else 0
         player_details.volume_level = group_volume
     
-    async def __update_player_group_power(self, player_details, player_childs):
-        ''' handle group power '''
-        player_powered = False
-        for child_player in player_childs:
-            if child_player.powered:
-                player_powered = True
-                break
-        if player_details.powered and not player_powered:
-            # all childs turned off so turn off group player
-            LOGGER.info('all childs turned off so turn off group player %s' % player_details.name)
-            await self. player_command(player_details.player_id, 'power', 'off')
-            player_details.powered = False
-        elif not player_details.powered and player_powered:
-            # all childs turned off but group player still off, so turn it on
-            LOGGER.info('all childs turned off but group player still off, so turn it on %s' % player_details.name)
-            await self. player_command(player_details.player_id, 'power', 'on')
-            player_details.powered = True
-
     async def __get_player_settings(self, player_id):
         ''' get (or create) player config '''
         player_settings = self.mass.config['player_settings'].get(player_id,{})
@@ -415,7 +415,7 @@ class Player():
                 cmd = '%s %s --xml --ebu -f %s' % (bs1770_binary, tmpfile, analysis_file)
                 process = await asyncio.create_subprocess_shell(cmd)
                 await process.wait()
-            if self.mass.config['base']['http_streamer']['enable_cache']:
+            if self.mass.config['base']['http_streamer']['enable_cache'] and not os.path.isfile(cachefile):
                 # use sox to store cache file (optionally strip silence from start and end)
                 if self.mass.config['base']['http_streamer']['trim_silence']:
                     cmd = 'sox -t %s %s -t flac -C5 %s silence 1 0.1 1%% reverse silence 1 0.1 1%% reverse' %(content_type, tmpfile, cachefile)
index e3602ac788e22f9197d2b516c1df2cd69b90ed65..b0568d5ac62cff33441b04947940c174065984b3 100644 (file)
@@ -74,10 +74,11 @@ class ChromecastProvider(PlayerProvider):
             self._chromecasts[player_id].media_controller.queue_prev()
         elif cmd == 'power' and cmd_args == 'off':
             self._players[player_id].powered = False
-            self._chromecasts[player_id].quit_app() # power is not supported so send quit app instead
+            self.mass.event_loop.create_task(self.mass.player.trigger_update(player_id))
+            self._chromecasts[player_id].media_controller.stop() # power is not supported so send stop instead
         elif cmd == 'power':
             self._players[player_id].powered = True
-            self._chromecasts[player_id].media_controller.launch()
+            self.mass.event_loop.create_task(self.mass.player.trigger_update(player_id))
         elif cmd == 'volume':
             self._chromecasts[player_id].set_volume(try_parse_int(cmd_args)/100)
         elif cmd == 'mute' and cmd_args == 'off':
@@ -286,8 +287,8 @@ class ChromecastProvider(PlayerProvider):
             track = await self.mass.music.providers['qobuz'].track(track_id)
         elif uri.startswith('http') and '/stream' in uri:
             params = urllib.parse.parse_qs(uri.split('?')[1])
-            track_id = params['track_id']
-            provider = params['provider']
+            track_id = params['track_id'][0]
+            provider = params['provider'][0]
             track = await self.mass.music.providers[provider].track(track_id)
         return track
 
index 2b2200b01356638d7842bbb63a9e6bb92a7caa1d..47ff77e8079b025f007a623d45f7cc3875707898 100644 (file)
@@ -219,8 +219,8 @@ class LMSProvider(PlayerProvider):
                 LOGGER.error(exc)
         elif track_url.startswith('http') and '/stream' in track_url:
             params = urllib.parse.parse_qs(track_url.split('?')[1])
-            track_id = params['track_id']
-            provider = params['provider']
+            track_id = params['track_id'][0]
+            provider = params['provider'][0]
             return await self.mass.music.providers[provider].track(track_id)
         # fallback to a generic track
         track = Track()