some more improvements
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 10 Jun 2019 09:00:54 +0000 (11:00 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 10 Jun 2019 09:00:54 +0000 (11:00 +0200)
music_assistant/modules/http_streamer.py
music_assistant/modules/player.py
music_assistant/modules/playerproviders/chromecast.py

index 8922e8f2accb70b234a4a4ceebb716b429315982..09e640666dc79a3728307ae97a0be68442fb39e8 100755 (executable)
@@ -134,7 +134,6 @@ class HTTPStreamer():
             use case is enable crossfade support for chromecast devices 
         '''
         player_id = http_request.query.get('player_id')
-        startindex = int(http_request.query.get('startindex',0))
         cancelled = threading.Event()
         resp = web.StreamResponse(status=200,
                                  reason='OK',
@@ -146,7 +145,7 @@ class HTTPStreamer():
             cancelled = threading.Event()
             run_async_background_task(
                 self.mass.bg_executor, 
-                self.__stream_queue, player_id, startindex, queue, cancelled)
+                self.__stream_queue, player_id, queue, cancelled)
             try:
                 while True:
                     chunk = await queue.get()
@@ -161,7 +160,7 @@ class HTTPStreamer():
                 raise asyncio.CancelledError()
         return resp
 
-    async def __stream_queue(self, player_id, startindex, buffer, cancelled):
+    async def __stream_queue(self, player_id, buffer, cancelled):
         ''' start streaming all queue tracks '''
         sample_rate = self.mass.config['player_settings'][player_id]['max_sample_rate']
         fade_length = self.mass.config['player_settings'][player_id]["crossfade_duration"]
@@ -179,9 +178,10 @@ class HTTPStreamer():
             await buffer.put(b'') # indicate EOF
         asyncio.create_task(fill_buffer())
 
-        queue_index = startindex
+        player = await self.mass.player.player(player_id)
+        queue_index = player.cur_queue_index
         last_fadeout_data = b''
-        self.mass.event_loop.create_task(self.mass.player.player_queue_stream_move(player_id, queue_index, True))
+        self.mass.event_loop.create_task(self.mass.player.player_queue_stream_update(player_id, queue_index, True))
         while True:
             # get the (next) track in queue
             try:
@@ -199,13 +199,14 @@ class HTTPStreamer():
             fade_in_part = b''
             cur_chunk = 0
             prev_chunk = None
-            
+            bytes_written = 0
             async for is_last_chunk, chunk in self.__get_raw_audio(track_id, provider, sample_rate, fade_bytes):
                 cur_chunk += 1
                 if cur_chunk == 1 and not last_fadeout_data:
                     # fade-in part but this is the first track so just pass it to the final file
                     sox_proc.stdin.write(chunk)
                     await sox_proc.stdin.drain()
+                    bytes_written += len(chunk)
                 elif cur_chunk == 1 and last_fadeout_data:
                     # create fade-out part
                     args = 'sox --ignore-length -t %s - -t %s - reverse fade t %s reverse' % (pcm_args, pcm_args, fade_length)
@@ -233,6 +234,7 @@ class HTTPStreamer():
                     # write the crossfade part to the sox player
                     sox_proc.stdin.write(crossfade_part)
                     await sox_proc.stdin.drain()
+                    bytes_written += len(crossfade_part)
                     fadeinfile.close()
                     fadeoutfile.close()
                     del crossfade_part
@@ -244,6 +246,7 @@ class HTTPStreamer():
                     last_fadeout_data = last_part[-fade_bytes:]
                     bytes_remaining = last_part[:-fade_bytes]
                     sox_proc.stdin.write(bytes_remaining)
+                    bytes_written += len(bytes_remaining)
                     await sox_proc.stdin.drain()
                 else:
                     # middle part of the track
@@ -251,6 +254,7 @@ class HTTPStreamer():
                     if prev_chunk:
                         sox_proc.stdin.write(prev_chunk)
                         await sox_proc.stdin.drain()
+                        bytes_written += len(prev_chunk)
                         prev_chunk = chunk
                     else:
                         prev_chunk = chunk
@@ -259,12 +263,15 @@ class HTTPStreamer():
                 # and it helps a bit in the quest to follow where we are in the queue
                 while buffer.qsize() > 1 and not cancelled.is_set():
                     await asyncio.sleep(1)
-                if cur_chunk == 1:
-                    # report start stream of current queue index
-                    self.mass.event_loop.create_task(self.mass.player.player_queue_stream_move(player_id, queue_index, False))
             # end of the track reached
             LOGGER.info("Finished Streaming queue track: %s - %s" % (track_id, queue_track.name))
+            # update actual duration to the queue for more accurate now playing info
+            accurate_duration = bytes_written / int(sample_rate * 4 * 2)
+            queue_track.duration = accurate_duration
+            self.mass.player.providers[player.player_provider]._player_queue[player_id][queue_index] = queue_track
+            # move to next queue index
             queue_index += 1
+            self.mass.event_loop.create_task(self.mass.player.player_queue_stream_update(player_id, queue_index, False))
             # break out the loop if the http session is cancelled
             if cancelled.is_set():
                 break
@@ -410,23 +417,10 @@ class HTTPStreamer():
                 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)
                 process = await asyncio.create_subprocess_shell(cmd)
                 await process.wait()
-            # retrieve accurate track duration
-            cmd = 'soxi -d "%s"' %(cachefile)
-            process = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.PIPE)
-            stdout, stderr = await process.communicate()
-            durationstr = stdout.decode().split()[0]
-            hours = int(durationstr.split(":")[0])
-            minutes = int(durationstr.split(":")[1])
-            seconds = float(durationstr.split(":")[0])
-            total_duration = (hours*60*60) + (minutes*60) + seconds
-            LOGGER.info("track duration for track %s is %s" %(track_id, total_duration))
-            item_id = await self.mass.db.get_database_id(provider, track_id, MediaType.Track)
-            await self.mass.db.update_track(item_id, "duration", total_duration)
 
         # always clean up temp file
-        while os.path.isfile(tmpfile):
+        if os.path.isfile(tmpfile):
             os.remove(tmpfile)
-            await asyncio.sleep(0.5)
         LOGGER.info('Fininished analyzing file %s' % tmpfile)
     
     async def __get_track_gain_correct(self, track_id, provider):
index a2e1725167949ddbbeccde32c033cdbd9b778799..add13064cab62ce07dbcf0027cff0c7964a5c0df 100755 (executable)
@@ -394,11 +394,10 @@ class Player():
         ''' get current index of the player's queue '''
         return self._players[player_id].cur_queue_index
 
-    async def player_queue_stream_move(self, player_id, new_index, is_start):
-        ''' called by our queue streamer when it's loading a new track '''
-        new_index = int(new_index)
+    async def player_queue_stream_update(self, player_id, cur_index, is_start=False):
+        ''' called by our queue streamer when it started playing the queue from position x '''
         player = self._players[player_id]
-        return await self.providers[player.player_provider].player_queue_stream_move(player_id, new_index, is_start)
+        return await self.providers[player.player_provider].player_queue_stream_update(player_id, cur_index, is_start)
 
     def load_providers(self):
         ''' dynamically load providers '''
index 18fa9d0144b35d7ef30f775498ddc7b243de0d9c..9f168d970135de93bd3ba3718430c084732f006d 100644 (file)
@@ -87,12 +87,14 @@ class ChromecastProvider(PlayerProvider):
         elif cmd == 'next':
             enable_crossfade = self.mass.config['player_settings'][player_id]["crossfade_duration"] > 0
             if enable_crossfade:
-                await self.__play_stream_queue(player_id, self._players[player_id].cur_queue_index+1)
+                self._players[player_id].cur_queue_index+=1
+                await self.__play_stream_queue(player_id, self._players[player_id].cur_queue_index)
             else:
                 self._chromecasts[player_id].media_controller.queue_next()
         elif cmd == 'previous':
             if enable_crossfade:
-                await self.__play_stream_queue(player_id, self._players[player_id].cur_queue_index-1)
+                self._players[player_id].cur_queue_index-=1
+                await self.__play_stream_queue(player_id, self._players[player_id].cur_queue_index)
             else:
                 self._chromecasts[player_id].media_controller.queue_prev()
         elif cmd == 'power' and cmd_args == 'off':
@@ -120,7 +122,7 @@ class ChromecastProvider(PlayerProvider):
             play media on a player
         '''
         castplayer = self._chromecasts[player_id]
-        cur_queue_index = await self.__get_cur_queue_index(player_id)
+        cur_queue_index = self._players[player_id].cur_queue_index
         enable_crossfade = self.mass.config['player_settings'][player_id]["crossfade_duration"] > 0
 
         if queue_opt == 'replace' or not self._player_queue[player_id]:
@@ -158,29 +160,24 @@ class ChromecastProvider(PlayerProvider):
             if not enable_crossfade:
                 await self.__queue_insert(player_id, media_items)
 
-    async def player_queue_stream_move(self, player_id, new_index, is_start):
-        ''' called by the queue streamer when it's loading a new track '''
-        self._players[player_id].cur_queue_index = new_index
-        # trigger update
+    async def player_queue_stream_update(self, player_id, cur_index, is_start=False):
+        ''' called by our queue streamer when it started playing a track in the queue at index X '''
         if is_start:
-            self._player_queue_startindex[player_id] = new_index
-        chromecast = self._chromecasts[player_id]
-        # fire update a few times as we can't predict the precaching exactly
-        for i in range(0, 5):
-            mediastatus = chromecast.media_controller.status
-            await self.__handle_player_state(chromecast, mediastatus=mediastatus)
-            await asyncio.sleep(5)
-
+            self._player_queue_startindex[player_id] = cur_index
+        # schedule update a few times as we don't know how much time is prebuffered
+        for i in range(0, 20):
+            castplayer = self._chromecasts[player_id]
+            status = castplayer.media_controller.status
+            await self.__handle_player_state(castplayer, mediastatus=status)
+            await asyncio.sleep(2)
+    
     ### Provider specific (helper) methods #####
 
-    async def __get_cur_queue_index(self, player_id):
+    async def __get_cur_queue_index(self, player_id, current_uri):
         ''' retrieve index of current item in the player queue '''
-        enable_crossfade = self.mass.config['player_settings'][player_id]["crossfade_duration"] > 0
-        if enable_crossfade:
-            return self._players[player_id].cur_queue_index
         cur_index = 0
         for index, track in enumerate(self._player_queue[player_id]):
-            if track.uri == self._chromecasts[player_id].media_controller.status.content_id:
+            if track.uri == current_uri:
                 cur_index = index
                 break
         return cur_index
@@ -316,7 +313,7 @@ class ChromecastProvider(PlayerProvider):
             if not 'stream_queue' in mediastatus.content_id:
                 player.cur_item = await self.__parse_track(mediastatus)
                 player.cur_item_time =  mediastatus.adjusted_current_time
-                player.cur_queue_index = await self.__get_cur_queue_index(player_id)
+                player.cur_queue_index = await self.__get_cur_queue_index(player_id, mediastatus.content_id)
             else:
                 # try to work out the current time
                 # player is playing a constant stream of the queue so we need to do this the hard way
@@ -335,6 +332,7 @@ class ChromecastProvider(PlayerProvider):
                         break
                 player.cur_item = queue_track
                 player.cur_item_time = track_time
+                player.cur_queue_index = queue_index
         await self.mass.player.update_player(player)
 
     async def __parse_track(self, mediastatus):