Skip to content

providers

Defines base provider classes and give default lyrics, info and audio providers. For more information on Downmixer's providers system, refer to the Providers page.

MODULE DESCRIPTION
connections

Connection classes for managing provider service connections.

protocols

Protocol definitions for music provider capabilities.

CLASS DESCRIPTION
BaseProvider

Base class for all music providers.

ProviderInformation

Information about a provider including its supported protocols and connections. This class facilitates

BaseProvider

BaseProvider(
    connection: Connection,
    options: dict | None = None,
    logger: "LoggerLike" = ConsoleLogger(),
)

Base class for all music providers.

Providers are the main interface for interacting with music services. Each provider must define a _name, _pretty_name (publicly accessible via properties), and implement the required protocol methods for their supported capabilities.

Additionally, a provider can specify default options dictionary with the _default_options parameter - this value will be merged with the options passed by the user using the merge_dicts_with_priority function.

ATTRIBUTE DESCRIPTION
connection

The active connection to the provider's service.

TYPE: Connection

client

The underlying API client from the connection.

TYPE: object

Initializes the provider object. If applicable, does not authenticate to the service.

PARAMETER DESCRIPTION

options

Dictionary of options to pass to the provider. See documentation for each provider for available options.

TYPE: dict | None DEFAULT: None

logger

Logger-like object to use for logging. If None, uses the default logger.

TYPE: 'LoggerLike' DEFAULT: ConsoleLogger()

Source code in src/downmixer/providers/__init__.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def __init__(
    self,
    connection: Connection,
    options: Optional[dict] = None,
    logger: "LoggerLike" = ConsoleLogger(),
):
    """Initializes the provider object. If applicable, does not authenticate to the service.

    Args:
        options: Dictionary of options to pass to the provider. See documentation for each provider for
            available options.
        logger: Logger-like object to use for logging. If None, uses the default logger.
    """
    if any(char.isspace() for char in self.name):
        raise ValueError(
            "Provider name cannot contain whitespace characters. Use the 'pretty_name' attribute for human readable names."
        )

    self.options = utils.merge_dicts_with_priority(self._default_options, options)
    self.logger = logger

    self.connection = connection
    self.client = self.connection.client
    if not self.connection.ready:
        NotInitializedError()

name property

name: str

The internal identifier for this provider.

pretty_name property

pretty_name: str

The human-readable name for this provider.

options instance-attribute

options = merge_dicts_with_priority(
    _default_options, options
)

connection class-attribute instance-attribute

connection: Connection = connection

client class-attribute instance-attribute

client: object = client

change_connection

change_connection(new: Connection) -> None

Change the provider's Connection object, ensuring that it's ready attributes are set.

Warning

It's not guaranteed that a connection change will go well. None of the built-in providers have issues with this, however be careful when using third-party or custom providers.

PARAMETER DESCRIPTION

new

Connection object to be changed.

TYPE: Connection

Source code in src/downmixer/providers/__init__.py
82
83
84
85
86
87
88
89
90
91
92
93
def change_connection(self, new: Connection) -> None:
    """Change the provider's Connection object, ensuring that it's ready attributes are set.

    !!! warning
        It's not guaranteed that a connection change will go well. None of the built-in providers have issues with
        this, however be careful when using third-party or custom providers.

    Args:
        new (Connection): Connection object to be changed.
    """
    self.connection = new
    self.client = self.connection.client

get_connections classmethod

get_connections() -> list[type[Connection]]

Return the list of connection types supported by this provider.

RETURNS DESCRIPTION
list[type[Connection]]

A list of Connection subclasses that can be used with this provider.

Source code in src/downmixer/providers/__init__.py
 95
 96
 97
 98
 99
100
101
102
@classmethod
def get_connections(cls) -> list[type[Connection]]:
    """Return the list of connection types supported by this provider.

    Returns:
        A list of Connection subclasses that can be used with this provider.
    """
    raise NotImplementedError

__getattr__

__getattr__(name: str)

Proxy attribute access to the underlying connection object.

Source code in src/downmixer/providers/__init__.py
104
105
106
def __getattr__(self, name: str):
    """Proxy attribute access to the underlying connection object."""
    return getattr(self.connection, name)

ProviderInformation dataclass

ProviderInformation(
    class_name: str,
    class_ref: type,
    protocols: list[type[Protocol]] = list(),
    connections: list[type[Connection]] = list(),
)

Information about a provider including its supported protocols and connections. This class facilitates parsing of providers and their supported protocols.

ATTRIBUTE DESCRIPTION
class_name

The name of the provider class.

TYPE: str

class_ref

A reference to the provider class itself.

TYPE: type

protocols

List of protocol classes the provider implements.

TYPE: list[type[Protocol]]

connections

List of connection classes the provider supports.

TYPE: list[type[Connection]]

class_name instance-attribute

class_name: str

class_ref instance-attribute

class_ref: type

protocols class-attribute instance-attribute

protocols: list[type[Protocol]] = field(
    default_factory=list
)

connections class-attribute instance-attribute

connections: list[type[Connection]] = field(
    default_factory=list
)

__contains__

__contains__(item)
Source code in src/downmixer/providers/__init__.py
179
180
181
182
183
184
185
186
187
def __contains__(self, item):
    if isinstance(item, type):
        return item in self.protocols
    elif isinstance(item, Connection):
        return type(item) in self.connections
    elif isinstance(item, str):
        return type(item) in self.protocols
    else:
        return False

__hash__

__hash__()
Source code in src/downmixer/providers/__init__.py
189
190
def __hash__(self):
    return hash(self.class_name) + hash(self.class_ref)

__eq__

__eq__(other)
Source code in src/downmixer/providers/__init__.py
192
193
194
195
def __eq__(self, other):
    if self.class_name != other.class_name or self.class_ref != other.class_ref:
        return False
    return True

list_providers

list_providers() -> list[type[BaseProvider]]

Discover and return all available provider classes.

Scans the providers package for subpackages containing a get_provider function and collects all provider classes.

RETURNS DESCRIPTION
list[type[BaseProvider]]

A list of BaseProvider subclasses found in the providers package.

Source code in src/downmixer/providers/__init__.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def list_providers() -> list[type[BaseProvider]]:
    """Discover and return all available provider classes.

    Scans the providers package for subpackages containing a `get_provider` function
    and collects all provider classes.

    Returns:
        A list of BaseProvider subclasses found in the providers package.
    """
    providers_path = str(Path(__file__).parent.absolute())
    current_package_name = sys.modules[__name__].__name__

    loaded: list[type[BaseProvider]] = []
    for loader, name, is_pkg in pkgutil.walk_packages([providers_path]):
        if is_pkg:
            imported = importlib.import_module(f"{current_package_name}.{name}")
            try:
                func = getattr(imported, "get_provider")
                loaded.append(func())
            except AttributeError:
                logger.error(
                    f"Module {current_package_name}.{name} does not contain a get_provider function"
                )
                continue

    return loaded

list_protocols

list_protocols() -> list[type[Protocol]]

Return all protocol classes defined in the protocols module.

RETURNS DESCRIPTION
list[type[Protocol]]

A list of Protocol subclasses (e.g., SupportsMetadata, SupportsAudioDownload).

Source code in src/downmixer/providers/__init__.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def list_protocols() -> list[type[Protocol]]:
    """Return all protocol classes defined in the protocols module.

    Returns:
        A list of Protocol subclasses (e.g., SupportsMetadata, SupportsAudioDownload).
    """
    current_package_name = sys.modules[__name__].__name__

    imported = importlib.import_module(f"{current_package_name}.protocols")
    found = []
    for name, obj in imported.__dict__.items():
        if not inspect.isclass(obj):
            continue

        if (
            issubclass(obj, Protocol)
            and obj is not Protocol
            and obj.__module__.startswith("downmixer")
        ):
            found.append(obj)

    return found

get_provider_info

get_provider_info(
    provider: type[BaseProvider],
) -> ProviderInformation
Source code in src/downmixer/providers/__init__.py
198
199
200
201
202
203
204
205
206
def get_provider_info(provider: type[BaseProvider]) -> ProviderInformation:
    protocol_list = list_protocols()

    return ProviderInformation(
        class_name=provider.__class__.__name__,
        class_ref=provider,
        protocols=[x for x in protocol_list if issubclass(provider, x)],
        connections=provider.get_connections(),
    )

get_all_providers_info

get_all_providers_info() -> list[ProviderInformation]

Get detailed information about all available providers.

RETURNS DESCRIPTION
list[ProviderInformation]

A list of ProviderInformation objects, each containing the provider's

list[ProviderInformation]

class name, class reference, implemented protocols, and supported connections.

Source code in src/downmixer/providers/__init__.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
def get_all_providers_info() -> list[ProviderInformation]:
    """Get detailed information about all available providers.

    Returns:
        A list of ProviderInformation objects, each containing the provider's
        class name, class reference, implemented protocols, and supported connections.
    """
    protocol_list = list_protocols()
    provider_list = list_providers()

    found: list[ProviderInformation] = []
    for provider in provider_list:
        found.append(
            ProviderInformation(
                class_name=provider.__class__.__name__,
                class_ref=provider,
                protocols=[x for x in protocol_list if issubclass(provider, x)],
                connections=provider.get_connections(),
            )
        )

    return found