Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
be28a7f
feat(dlna_receiver): add dlna_receiver provider v1.0.0
github-actions[bot] Apr 7, 2026
3a67897
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.0.0
github-actions[bot] Apr 7, 2026
1c55a75
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 16, 2026
b219492
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.0
github-actions[bot] Apr 21, 2026
a6e1f56
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 21, 2026
2edba54
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.1
github-actions[bot] Apr 21, 2026
eb1fd2e
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.1
github-actions[bot] Apr 21, 2026
25e59f1
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.3
github-actions[bot] Apr 21, 2026
e524d61
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 21, 2026
056a623
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.4
github-actions[bot] Apr 22, 2026
440f93c
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 22, 2026
9d23c94
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.5
github-actions[bot] Apr 22, 2026
90c98e4
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 23, 2026
c1faa46
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.6
github-actions[bot] Apr 23, 2026
f1ed739
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 23, 2026
dcdcc4e
feat(dlna_receiver): sync provider from ma-provider-dlna-receiver v1.1.7
github-actions[bot] Apr 23, 2026
707eeae
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 28, 2026
8f6cf53
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 28, 2026
abcae31
Merge branch 'dev' into upstream/dlna_receiver
trudenboy Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions music_assistant/providers/dlna_receiver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
DLNA Receiver — Music Assistant Plugin Provider.

Exposes Music Assistant as a UPnP/DLNA MediaRenderer so that external
applications (Qobuz, BubbleUPnP, foobar2000, mconnect, etc.) can discover
and cast audio streams to any MA player.

Architecture
~~~~~~~~~~~~
1. SSDP advertisement — announces virtual MediaRenderers on the LAN
2. UPnP HTTP server — serves device/service XML descriptions and
accepts SOAP control actions (AVTransport,
RenderingControl, ConnectionManager)
3. PluginSource bridge — received audio URL is fed into the MA streaming
pipeline as a PluginSource, routed to the
corresponding target player

Multi-player mode
~~~~~~~~~~~~~~~~~
When ``target_players`` contains multiple comma-separated player_id values
(or the special value ``*``), the provider creates one virtual DLNA
renderer per player, each with a unique UDN and HTTP port. DLNA control
points see each renderer as a separate device — e.g.
"Music Assistant — Kitchen", "Music Assistant — Living Room".
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import ConfigEntryType

from .constants import (
CONF_BIND_IP,
CONF_FRIENDLY_NAME,
CONF_HTTP_PORT,
CONF_TARGET_PLAYERS,
DEFAULT_FRIENDLY_NAME,
DEFAULT_HTTP_PORT,
)

if TYPE_CHECKING:
from music_assistant_models.config_entries import ProviderConfig
from music_assistant_models.provider import ProviderManifest

from music_assistant.mass import MusicAssistant
from music_assistant.models import ProviderInstanceType


async def get_config_entries(
mass: MusicAssistant, # noqa: ARG001
instance_id: str | None = None, # noqa: ARG001
action: str | None = None, # noqa: ARG001
values: dict[str, ConfigValueType] | None = None, # noqa: ARG001
) -> tuple[ConfigEntry, ...]:
"""Return Config entries to setup this provider."""
return (
ConfigEntry(
key=CONF_FRIENDLY_NAME,
type=ConfigEntryType.STRING,
label="Friendly name prefix",
description=(
"Prefix for DLNA renderer names shown on the network. "
"Player name is appended automatically in multi-player mode."
),
default_value=DEFAULT_FRIENDLY_NAME,
required=True,
),
ConfigEntry(
key=CONF_TARGET_PLAYERS,
type=ConfigEntryType.STRING,
label="Target players",
description=(
"Comma-separated MA player_ids to expose as DLNA renderers. "
'Use "*" to auto-create a renderer for every MA player. '
"Leave empty for a single renderer without a fixed target."
),
required=False,
),
ConfigEntry(
key=CONF_BIND_IP,
type=ConfigEntryType.STRING,
label="Bind IP address",
description=(
"IP address to bind the UPnP HTTP server and SSDP listener. "
"Leave empty to auto-detect."
),
required=False,
),
ConfigEntry(
key=CONF_HTTP_PORT,
type=ConfigEntryType.INTEGER,
label="HTTP base port",
description=(
"Base port for UPnP HTTP servers. In multi-player mode, "
"each renderer uses an incrementing port (8298, 8299, …)."
),
default_value=DEFAULT_HTTP_PORT,
required=True,
),
)


async def setup(
mass: MusicAssistant,
manifest: ProviderManifest,
config: ProviderConfig,
) -> ProviderInstanceType:
"""Set up the DLNA Receiver provider."""
# Deferred to avoid loading music_assistant internals at module import time.
from .provider import DLNAReceiverProvider # noqa: PLC0415, RUF100

return DLNAReceiverProvider(mass, manifest, config)
51 changes: 51 additions & 0 deletions music_assistant/providers/dlna_receiver/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Constants for the DLNA Receiver plugin provider."""

from __future__ import annotations

# UPnP device and service identifiers
UPNP_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaRenderer:1"
UPNP_SERVICE_AV_TRANSPORT = "urn:schemas-upnp-org:service:AVTransport:1"
UPNP_SERVICE_RENDERING_CONTROL = "urn:schemas-upnp-org:service:RenderingControl:1"
UPNP_SERVICE_CONNECTION_MANAGER = "urn:schemas-upnp-org:service:ConnectionManager:1"

# SSDP
SSDP_MULTICAST_ADDR = "239.255.255.250"
SSDP_PORT = 1900
SSDP_MAX_AGE = 1800 # seconds

# Config entry keys
CONF_FRIENDLY_NAME = "friendly_name"
CONF_TARGET_PLAYER = "target_player"
CONF_TARGET_PLAYERS = "target_players"
CONF_BIND_IP = "bind_ip"
CONF_HTTP_PORT = "http_port"

# Defaults
DEFAULT_FRIENDLY_NAME = "Music Assistant"
DEFAULT_HTTP_PORT = 8298 # UPnP renderer HTTP port (separate from MA stream port)

# Supported MIME types for incoming streams
SUPPORTED_MIME_TYPES = [
"audio/flac",
"audio/x-flac",
"audio/wav",
"audio/x-wav",
"audio/mpeg",
"audio/mp3",
"audio/mp4",
"audio/aac",
"audio/ogg",
"audio/vorbis",
"audio/L16",
"audio/*",
]

# UPnP transport states
TRANSPORT_STATE_STOPPED = "STOPPED"
TRANSPORT_STATE_PLAYING = "PLAYING"
TRANSPORT_STATE_PAUSED = "PAUSED_PLAYBACK"
TRANSPORT_STATE_TRANSITIONING = "TRANSITIONING"
TRANSPORT_STATE_NO_MEDIA = "NO_MEDIA_PRESENT"

# UUID namespace for deterministic UDN generation
UDN_NAMESPACE = "ma-dlna-receiver"
Loading
Loading