LOGGER.info('added track %s (%s) to database: %s' %(track.name, track.provider_ids, track_id))
return track_id
+ async def update_track(self, track_id, column_key, column_value):
+ ''' update column of existing track '''
+ async with aiosqlite.connect(self.dbfile, timeout=20) as db:
+ sql_query = 'UPDATE tracks SET %s=%s WHERE track_id=%s;' %(column_key, column_value, track_id)
+ await db.execute(sql_query)
+ await db.commit()
+
async def artist_tracks(self, artist_id, limit=1000000, offset=0, orderby='name') -> List[Track]:
''' get all library tracks for the given artist '''
artist_id = try_parse_int(artist_id)
await resp.write(chunk)
queue.task_done()
LOGGER.info("stream_track fininished for %s" % track_id)
- except asyncio.CancelledError:
+ except (asyncio.CancelledError, asyncio.TimeoutError):
cancelled.set()
LOGGER.info("stream_track interrupted for %s" % track_id)
raise asyncio.CancelledError()
async def stream_queue(self, http_request):
'''
- streamm all tracks in queue from player with http
- loads audiodata in memory so only recommended for high performance servers
+ stream all tracks in queue from player with http
+ loads large part of audiodata in memory so only recommended for high performance servers
use case is enable crossfade support for chromecast devices
'''
player_id = http_request.query.get('player_id')
queue_index = startindex
last_fadeout_data = b''
+ self.mass.event_loop.create_task(self.mass.player.player_queue_stream_move(player_id, queue_index, True))
while True:
# get the (next) track in queue
try:
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))
+ 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))
queue_index += 1
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):
os.remove(tmpfile)
''' 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):
+ 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)
player = self._players[player_id]
- return await self.providers[player.player_provider].player_queue_stream_move(player_id, new_index)
+ return await self.providers[player.player_provider].player_queue_stream_move(player_id, new_index, is_start)
def load_providers(self):
''' dynamically load providers '''
self._players = {}
self._chromecasts = {}
self._player_queue = {}
+ self._player_queue_startindex = {}
self.supported_musicproviders = ['http']
asyncio.ensure_future(self.__discover_chromecasts())
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].media_controller.stop() # power is not supported so send stop instead
+ self._chromecasts[player_id].quit_app() # power is not supported so send quit_app instead
await self.mass.player.update_player(self._players[player_id])
elif cmd == 'power':
self._players[player_id].powered = True
if not enable_crossfade:
await self.__queue_insert(player_id, media_items)
- async def player_queue_stream_move(self, player_id, new_index):
+ 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
+ if is_start:
+ self._player_queue_startindex[player_id] = new_index
chromecast = self._chromecasts[player_id]
- mediastatus = chromecast.media_controller.status
- await self.__handle_player_state(chromecast, mediastatus=mediastatus)
- LOGGER.info("player_queue_stream_move")
+ # 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)
### Provider specific (helper) methods #####
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
- cur_queue_index = player.cur_queue_index
- player.cur_item = self._player_queue[player_id][cur_queue_index]
- cur_time = mediastatus.adjusted_current_time
- while cur_time > player.cur_item.duration-10:
- cur_queue_index -=1
- prev_track = self._player_queue[player_id][cur_queue_index]
- cur_time -= prev_track.duration
- player.cur_item_time = cur_time
+ cur_time_queue = mediastatus.adjusted_current_time
+ total_time = 0
+ track_time = 0
+ queue_index = self._player_queue_startindex[player_id]
+ queue_track = None
+ while True:
+ queue_track = self._player_queue[player_id][queue_index]
+ if cur_time_queue > (queue_track.duration + total_time):
+ total_time += queue_track.duration
+ queue_index += 1
+ else:
+ track_time = cur_time_queue - total_time
+ break
+ player.cur_item = queue_track
+ player.cur_item_time = track_time
await self.mass.player.update_player(player)
async def __parse_track(self, mediastatus):
self._player_queue[player_id] = []
chromecast.wait()
- @run_periodic(3600)
+ @run_periodic(600)
async def __discover_chromecasts(self):
''' discover chromecasts on the network '''
LOGGER.info('Running Chromecast discovery...')