"""All constants for Music Assistant."""
-__version__ = "0.0.56"
+__version__ = "0.0.57"
REQUIRED_PYTHON_VER = "3.7"
# configuration keys/attributes
CONF_GROUP_DELAY = "group_delay"
CONF_VOLUME_CONTROL = "volume_control"
CONF_POWER_CONTROL = "power_control"
-CONF_HTTP_PORT = "http_port"
-CONF_HTTPS_PORT = "https_port"
CONF_MAX_SAMPLE_RATE = "max_sample_rate"
CONF_VOLUME_NORMALISATION = "volume_normalisation"
CONF_TARGET_VOLUME = "target_volume"
from music_assistant.constants import (
CONF_CROSSFADE_DURATION,
CONF_ENABLED,
- CONF_EXTERNAL_URL,
CONF_FALLBACK_GAIN_CORRECT,
CONF_GROUP_DELAY,
- CONF_HTTP_PORT,
- CONF_HTTPS_PORT,
CONF_KEY_BASE,
CONF_KEY_BASE_SECURITY,
- CONF_KEY_BASE_WEBSERVER,
CONF_KEY_METADATA_PROVIDERS,
CONF_KEY_MUSIC_PROVIDERS,
CONF_KEY_PLAYER_PROVIDERS,
CONF_NAME,
CONF_PASSWORD,
CONF_POWER_CONTROL,
- CONF_SSL_CERTIFICATE,
- CONF_SSL_KEY,
CONF_TARGET_VOLUME,
CONF_USERNAME,
CONF_VOLUME_CONTROL,
)
from music_assistant.helpers.encryption import decrypt_string, encrypt_string
from music_assistant.helpers.typing import MusicAssistantType
-from music_assistant.helpers.util import get_external_ip, merge_dict, try_load_json_file
+from music_assistant.helpers.util import merge_dict, try_load_json_file
from music_assistant.models.config_entry import ConfigEntry, ConfigEntryType
from music_assistant.models.player import PlayerControlType
from music_assistant.models.provider import ProviderType
]
DEFAULT_BASE_CONFIG_ENTRIES = {
- CONF_KEY_BASE_WEBSERVER: [
- ConfigEntry(
- entry_key="__name__",
- entry_type=ConfigEntryType.LABEL,
- label=CONF_KEY_BASE_WEBSERVER,
- hidden=True,
- ),
- ConfigEntry(
- entry_key=CONF_HTTP_PORT,
- entry_type=ConfigEntryType.INT,
- default_value=8095,
- label=CONF_HTTP_PORT,
- description="desc_http_port",
- ),
- ConfigEntry(
- entry_key=CONF_HTTPS_PORT,
- entry_type=ConfigEntryType.INT,
- default_value=8096,
- label=CONF_HTTPS_PORT,
- description="desc_https_port",
- ),
- ConfigEntry(
- entry_key=CONF_SSL_CERTIFICATE,
- entry_type=ConfigEntryType.STRING,
- default_value="",
- label=CONF_SSL_CERTIFICATE,
- description="desc_ssl_certificate",
- ),
- ConfigEntry(
- entry_key=CONF_SSL_KEY,
- entry_type=ConfigEntryType.STRING,
- default_value="",
- label=CONF_SSL_KEY,
- description="desc_ssl_key",
- ),
- ConfigEntry(
- entry_key=CONF_EXTERNAL_URL,
- entry_type=ConfigEntryType.STRING,
- default_value=f"http://{get_external_ip()}:8095",
- label=CONF_EXTERNAL_URL,
- description="desc_external_url",
- ),
- ],
CONF_KEY_BASE_SECURITY: [
ConfigEntry(
entry_key="__name__",
queue_item = QueueItem(track)
# generate uri for this queue item
queue_item.uri = "%s/stream/queue/%s/%s" % (
- self.mass.web.internal_url,
+ self.mass.web.url,
player_id,
queue_item.queue_item_id,
)
)
# generate uri for this queue item
queue_item.uri = "%s/stream/%s/%s" % (
- self.mass.web.internal_url,
+ self.mass.web.url,
player_id,
queue_item.queue_item_id,
)
class MusicAssistant:
"""Main MusicAssistant object."""
- def __init__(self, datapath: str, debug: bool = False):
+ def __init__(self, datapath: str, debug: bool = False, port: int = 8095):
"""
Create an instance of MusicAssistant.
self._database = DatabaseManager(self)
self._cache = Cache(self)
self._metadata = MetaDataManager(self)
- self._web = WebServer(self)
+ self._web = WebServer(self, port)
self._music = MusicManager(self)
self._players = PlayerManager(self)
self._streams = StreamManager(self)
zeroconf_type,
name=f"{name}.{zeroconf_type}",
addresses=[get_ip_pton()],
- port=discovery_info["http_port"],
+ port=discovery_info["port"],
properties=discovery_info,
)
LOGGER.debug("Starting Zeroconf broadcast...")
def get_stream_url(self) -> str:
"""Return the full stream url for this QueueStream."""
- uri = f"{self.mass.web.internal_url}/stream/queue/{self.player_id}"
+ uri = f"{self.mass.web.url}/stream/queue/{self.player_id}"
# we set the checksum just to invalidate cache stuf
uri += f"?checksum={time.time()}"
return uri
except (
pychromecast.NotConnected,
pychromecast.ChromecastConnectionError,
+ pychromecast.error.PyChromecastStopped,
) as exc:
LOGGER.warning(
"Error while executing command %s on player %s: %s",
async def async_start_discovery(self):
"""Start discovery for players."""
transport, _ = await self.mass.loop.create_datagram_endpoint(
- lambda: DiscoveryProtocol(self.mass.web.http_port),
+ lambda: DiscoveryProtocol(self.mass.web.port),
local_addr=("0.0.0.0", 3483),
)
try:
for child_player_id in self.group_childs:
child_player = self.mass.players.get_player(child_player_id)
if child_player:
- queue_stream_uri = f"{self.mass.web.internal_url}/stream/group/{self.player_id}?player_id={child_player_id}"
+ queue_stream_uri = f"{self.mass.web.url}/stream/group/{self.player_id}?player_id={child_player_id}"
await child_player.async_cmd_play_uri(queue_stream_uri)
self.update_state()
self.stream_task = self.mass.add_job(self.async_queue_stream_task())
"fallback_gain_correct": "Fallback gain correction level",
"desc_player_name": "Set a custom name for this player.",
"crossfade_duration": "Enable crossfade",
- "http_port": "HTTP Port",
- "https_port": "HTTPS Port",
- "ssl_certificate": "SSL Certificate file location",
- "ssl_key": "Path to certificate key file",
- "external_url": "External URL",
"group_delay": "Correction of groupdelay",
- "web": "Webserver",
"security": "Security",
"desc_sample_rate": "Set the maximum sample rate this player can handle.",
"desc_gain_correct": "Set a fallback gain correction when there is no R128 measurement available.",
"desc_crossfade": "Enable crossfading of Queue tracks by setting a crossfade duration in seconds.",
"desc_enable_provider": "Enable this provider.",
- "desc_http_port": "The port on which to run the HTTP (internal) server.",
- "desc_https_port": "The port on which to run the HTTPS (external) server. The HTTPS Server will only be enabled if correct certificate details are also set",
- "desc_ssl_certificate": "Supply the full path to a certificate file (PEM).",
- "desc_ssl_key": "Supply the full path to the file containing the private key.",
- "desc_external_url": "Supply the full URL how this Music Assistant instance can be accessed from outside. Make sure this matches the common name of the certificate.",
"desc_base_username": "Username to access this Music Assistant server.",
"desc_base_password": "A password to protect this Music Assistant server. Can be left blank but this is extremely dangerous if this server is reachable from outside.",
"desc_group_delay": "Only used on grouped playback. Adjust the delay of the grouped playback on this player"
"fallback_gain_correct": "Fallback gain correctie niveau",
"desc_player_name": "Stel een aangepaste naam in voor deze speler.",
"crossfade_duration": "Crossfade inschakelen",
- "http_port": "HTTP Port",
- "https_port": "HTTPS Port",
- "ssl_certificate": "SSL Certificaat bestandslocatie",
- "ssl_key": "Pad naar het certificaat key bestand",
- "external_url": "External URL",
- "web": "Webserver",
"security": "Beveiliging",
"group_delay": "Correctie van groepsvertraging",
"desc_gain_correct": "Stel een fallback gain correctie in als er geen R128 meting beschikbaar is.",
"desc_crossfade": "Crossfade inschakelen door het instellen van een crossfade duur in seconden.",
"desc_enable_provider": "Deze provider inschakelen.",
- "desc_http_port": "De TCP poort waarop de HTTP webserver gestart mag worden.",
- "desc_https_port": "De TCP poort waarop de HTTPS webserver gestart mag worden. De HTTPS Server wordt alleen ingeschakeld indien er ook valide certificaat gegevens worden opgegegven.",
- "desc_ssl_certificate": "Geef het pad op naar het certificaat bestand (PEM).",
- "desc_ssl_key": "Geef het pad om naar het bestand met de private key.",
- "desc_external_url": "Geef de URL waarop deze Music Assistant server extern te benaderen is. Zorg dat dit overeenomst met het certificaat.",
"desc_base_username": "Gebruikersnaam waarmee deze server beveiligd moet worden.",
"desc_base_password": "Wachtwoord waarmee deze server beveiligd moet worden. Mag worden leeggelaten maar dit is extreem gevaarlijk indien je besluit de server extern toegankelijk te maken.",
"desc_group_delay": "Gebruikt bij afspelen in groep. Pas de vertraging aan voor deze player."
"""The web module handles serving the frontend and the rest/websocket api's."""
import logging
import os
-import ssl
import uuid
import aiohttp_cors
from aiohttp import web
from aiohttp_jwt import JWTMiddleware
from music_assistant.constants import __version__ as MASS_VERSION
+from music_assistant.helpers.typing import MusicAssistantType
from music_assistant.helpers.util import get_hostname, get_ip, json_serializer
from .endpoints import (
class WebServer:
"""Webserver and json/websocket api."""
- def __init__(self, mass):
+ def __init__(self, mass: MusicAssistantType, port: int):
"""Initialize class."""
self.mass = mass
+ self._port = port
# load/create/update config
self._local_ip = get_ip()
self._device_id = f"{uuid.getnode()}_{get_hostname()}"
self.config = mass.config.base["web"]
self._runner = None
- enable_ssl = self.config["ssl_certificate"] and self.config["ssl_key"]
- if self.config["ssl_certificate"] and not os.path.isfile(
- self.config["ssl_certificate"]
- ):
- enable_ssl = False
- LOGGER.warning(
- "SSL certificate file not found: %s", self.config["ssl_certificate"]
- )
- if self.config["ssl_key"] and not os.path.isfile(self.config["ssl_key"]):
- enable_ssl = False
- LOGGER.warning(
- "SSL certificate key file not found: %s", self.config["ssl_key"]
- )
- if not self.config.get("external_url"):
- enable_ssl = False
- self._enable_ssl = enable_ssl
-
async def async_setup(self):
"""Perform async setup."""
cors.add(route)
self._runner = web.AppRunner(app, access_log=None)
await self._runner.setup()
- http_site = web.TCPSite(self._runner, "0.0.0.0", self.http_port)
+ http_site = web.TCPSite(self._runner, "0.0.0.0", self.port)
await http_site.start()
- LOGGER.info("Started HTTP webserver on port %s", self.http_port)
- if self._enable_ssl:
- ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
- ssl_context.load_cert_chain(
- self.config["ssl_certificate"], self.config["ssl_key"]
- )
- https_site = web.TCPSite(
- self._runner, "0.0.0.0", self.https_port, ssl_context=ssl_context
- )
- await https_site.start()
- LOGGER.info(
- "Started HTTPS webserver on port %s - serving at FQDN %s",
- self.https_port,
- self.external_url,
- )
+ LOGGER.info("Started HTTP webserver on port %s", self.port)
async def async_stop(self):
"""Stop the webserver."""
# await self._runner.cleanup()
@property
- def internal_ip(self):
- """Return the local IP address for this Music Assistant instance."""
+ def host(self):
+ """Return the local IP address/host for this Music Assistant instance."""
return self._local_ip
@property
- def http_port(self):
- """Return the HTTP port for this Music Assistant instance."""
- return self.config.get("http_port", 8095)
-
- @property
- def https_port(self):
- """Return the HTTPS port for this Music Assistant instance."""
- return self.config.get("https_port", 8096)
-
- @property
- def internal_url(self):
- """Return the internal URL for this Music Assistant instance."""
- return f"http://{self._local_ip}:{self.http_port}"
+ def port(self):
+ """Return the port for this Music Assistant instance."""
+ return self._port
@property
- def external_url(self):
- """Return the internal URL for this Music Assistant instance."""
- if self._enable_ssl and self.config.get("external_url"):
- return self.config["external_url"]
- return self.internal_url
+ def url(self):
+ """Return the URL for this Music Assistant instance."""
+ return f"http://{self.host}:{self.port}"
@property
def device_id(self):
"""Return (discovery) info about this instance."""
return {
"id": self._device_id,
- "external_url": self.external_url,
- "internal_url": self.internal_url,
- "host": self.internal_ip,
- "http_port": self.http_port,
- "https_port": self.https_port,
- "ssl_enabled": self._enable_ssl,
+ "url": self.url,
+ "host": self.host,
+ "port": self.port,
"version": MASS_VERSION,
}