Skip to content

Commit 955a3db

Browse files
committed
shared http clientsession
1 parent 136721d commit 955a3db

File tree

11 files changed

+170
-223
lines changed

11 files changed

+170
-223
lines changed

music_assistant/http_streamer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ def fill_buffer():
247247
cur_chunk += 1
248248

249249
# HANDLE FIRST PART OF TRACK
250-
if cur_chunk == 1 and is_last_chunk:
250+
if not chunk and cur_chunk == 1 and is_last_chunk:
251251
LOGGER.warning("Stream error, skip track %s", queue_track.item_id)
252252
break
253253
if cur_chunk <= 2 and not last_fadeout_data:

music_assistant/mass.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import threading
99
from typing import Any, Awaitable, Callable, List, Optional, Union
1010

11+
import aiohttp
1112
from music_assistant.cache import Cache
1213
from music_assistant.config import MassConfig
1314
from music_assistant.constants import (
@@ -40,6 +41,7 @@ def __init__(self, datapath):
4041
"""
4142

4243
self.loop = None
44+
self._http_session = None
4345
self._event_listeners = []
4446
self._providers = {}
4547
self.config = MassConfig(self, datapath)
@@ -57,13 +59,18 @@ def __init__(self, datapath):
5759

5860
async def async_start(self):
5961
"""Start running the music assistant server."""
62+
# initialize loop
6063
self.loop = asyncio.get_event_loop()
6164
self.loop.set_exception_handler(self.__handle_exception)
6265
if LOGGER.level == logging.DEBUG:
6366
self.loop.set_debug(True)
67+
# create shared aiohttp ClientSession
68+
self._http_session = aiohttp.ClientSession(
69+
loop=self.loop,
70+
connector=aiohttp.TCPConnector(enable_cleanup_closed=True, ssl=False),
71+
)
6472
await self.database.async_setup()
6573
await self.cache.async_setup()
66-
await self.metadata.async_setup()
6774
await self.music_manager.async_setup()
6875
await self.player_manager.async_setup()
6976
await self.web.async_setup()
@@ -79,6 +86,13 @@ async def async_stop(self):
7986
for prov in self._providers.values():
8087
await prov.async_on_stop()
8188
await self.player_manager.async_close()
89+
await self._http_session.connector.close()
90+
self._http_session.detach()
91+
92+
@property
93+
def http_session(self):
94+
"""Return the default http session."""
95+
return self._http_session
8296

8397
async def async_register_provider(self, provider: Provider):
8498
"""Register a new Provider/Plugin."""
@@ -184,6 +198,7 @@ def remove_listener():
184198

185199
return remove_listener
186200

201+
@callback
187202
def add_job(
188203
self, target: Callable[..., Any], *args: Any
189204
) -> Optional[asyncio.Future]:
@@ -205,9 +220,7 @@ def add_job(
205220
if threading.current_thread() is not threading.main_thread():
206221
# called from other thread
207222
if asyncio.iscoroutine(check_target):
208-
task = asyncio.run_coroutine_threadsafe(
209-
target, self.loop
210-
) # type: ignore
223+
task = asyncio.run_coroutine_threadsafe(target, self.loop) # type: ignore
211224
elif asyncio.iscoroutinefunction(check_target):
212225
task = asyncio.run_coroutine_threadsafe(target(*args), self.loop)
213226
elif is_callback(check_target):

music_assistant/metadata.py

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ def __init__(self, mass):
2525
self.musicbrainz = MusicBrainz(mass)
2626
self.fanarttv = FanartTv(mass)
2727

28-
async def async_setup(self):
29-
"""Async setup of metadata module."""
30-
await self.musicbrainz.async_setup()
31-
await self.fanarttv.async_setup()
32-
3328
async def async_get_artist_metadata(self, mb_artist_id, cur_metadata):
3429
"""Get/update rich metadata for an artist by providing the musicbrainz artist id."""
3530
metadata = cur_metadata
@@ -120,14 +115,6 @@ def __init__(self, mass):
120115
"""Initialize class."""
121116
self.mass = mass
122117
self.cache = mass.cache
123-
self.throttler = None
124-
self._http_session = None
125-
126-
async def async_setup(self):
127-
"""Perform async setup."""
128-
self._http_session = aiohttp.ClientSession(
129-
loop=self.mass.loop, connector=aiohttp.TCPConnector()
130-
)
131118
self.throttler = Throttler(rate_limit=1, period=1)
132119

133120
async def async_search_artist_by_album(
@@ -209,7 +196,7 @@ async def async_get_data(self, endpoint: str, params: Optional[dict] = None):
209196
headers = {"User-Agent": "Music Assistant/1.0.0 https://github.com/marcelveldt"}
210197
params["fmt"] = "json"
211198
async with self.throttler:
212-
async with self._http_session.get(
199+
async with self.mass.http_session.get(
213200
url, headers=headers, params=params, verify_ssl=False
214201
) as response:
215202
try:
@@ -231,14 +218,6 @@ def __init__(self, mass):
231218
"""Initialize class."""
232219
self.mass = mass
233220
self.cache = mass.cache
234-
self._http_session = None
235-
self.throttler = None
236-
237-
async def async_setup(self):
238-
"""Perform async setup."""
239-
self._http_session = aiohttp.ClientSession(
240-
loop=self.mass.loop, connector=aiohttp.TCPConnector()
241-
)
242221
self.throttler = Throttler(rate_limit=1, period=2)
243222

244223
async def async_get_artist_images(self, mb_artist_id):
@@ -271,7 +250,7 @@ async def async_get_data(self, endpoint, params=None):
271250
url = "http://webservice.fanart.tv/v3/%s" % endpoint
272251
params["api_key"] = "639191cb0774661597f28a47e7e2bad5"
273252
async with self.throttler:
274-
async with self._http_session.get(
253+
async with self.mass.http_session.get(
275254
url, params=params, verify_ssl=False
276255
) as response:
277256
try:

music_assistant/models/player_queue.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@ def use_queue_stream(self):
211211
it will send a constant stream of audio to the player with all tracks.
212212
"""
213213
supports_crossfade = PlayerFeature.CROSSFADE in self.player.features
214-
return self.crossfade_enabled and not supports_crossfade
214+
supports_queue = PlayerFeature.QUEUE in self.player.features
215+
return not supports_crossfade if self.crossfade_enabled else not supports_queue
215216

216217
@callback
217218
def get_item(self, index):

music_assistant/models/provider.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ async def async_on_start(self) -> bool:
5454
@abstractmethod
5555
async def async_on_stop(self):
5656
"""Handle correct close/cleanup of the provider on exit. Called on shutdown."""
57-
raise NotImplementedError
5857

5958
async def async_on_reload(self):
6059
"""Handle configuration changes for this provider. Called on reload."""

music_assistant/player_manager.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
EVENT_PLAYER_REMOVED,
1515
)
1616
from music_assistant.models.config_entry import ConfigEntry, ConfigEntryType
17-
from music_assistant.models.media_types import MediaItem, MediaType
17+
from music_assistant.models.media_types import MediaItem, MediaType, Track
1818
from music_assistant.models.player import (
1919
Player,
2020
PlayerControl,
@@ -24,6 +24,7 @@
2424
from music_assistant.models.player_queue import PlayerQueue, QueueItem, QueueOption
2525
from music_assistant.models.playerprovider import PlayerProvider
2626
from music_assistant.models.provider import ProviderType
27+
from music_assistant.models.streamdetails import ContentType, StreamDetails, StreamType
2728
from music_assistant.utils import (
2829
async_iter_items,
2930
callback,
@@ -251,6 +252,45 @@ async def async_play_media(
251252
if queue_opt == QueueOption.Add:
252253
return await player_queue.async_append(queue_items)
253254

255+
async def async_cmd_play_uri(self, player_id: str, uri: str):
256+
"""
257+
Play the specified uri/url on the given player.
258+
259+
Will create a fake track on the queue.
260+
261+
:param player_id: player_id of the player to handle the command.
262+
:param uri: Url/Uri that can be played by a player.
263+
:param queue_opt:
264+
QueueOption.Play -> Insert new items in queue and start playing at inserted position
265+
QueueOption.Replace -> Replace queue contents with these items
266+
QueueOption.Next -> Play item(s) after current playing item
267+
QueueOption.Add -> Append new items at end of the queue
268+
"""
269+
player = self._players[player_id]
270+
if not player:
271+
return
272+
queue_item = QueueItem(
273+
Track(
274+
item_id=uri,
275+
provider="",
276+
name="uri",
277+
)
278+
)
279+
queue_item.streamdetails = StreamDetails(
280+
type=StreamType.URL,
281+
provider="",
282+
item_id=uri,
283+
path=uri,
284+
content_type=ContentType(uri.split(".")[-1]),
285+
sample_rate=44100,
286+
bit_depth=16,
287+
)
288+
# turn on player
289+
await self.async_cmd_power_on(player_id)
290+
# load item into the queue
291+
player_queue = self.get_player_queue(player_id)
292+
return await player_queue.async_insert([queue_item], 0)
293+
254294
async def async_cmd_stop(self, player_id: str) -> None:
255295
"""
256296
Send STOP command to given player.

0 commit comments

Comments
 (0)