CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-mopidy

Mopidy is an extensible music server written in Python

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

extension-system.mddocs/

Extension System

Mopidy's extension system provides a plugin architecture that allows third-party developers to add new music sources, frontends, and other functionality. Extensions integrate seamlessly with Mopidy's configuration system, dependency management, and component registry.

Capabilities

Extension Base Class

The foundational class for creating Mopidy extensions with configuration, lifecycle management, and component registration.

class Extension:
    """
    Base class for Mopidy extensions providing plugin functionality.
    
    Attributes:
    - dist_name (str): Distribution name as registered on PyPI
    - ext_name (str): Extension short name for configuration  
    - version (str): Extension version string
    """
    dist_name: str  # e.g., "Mopidy-Spotify"
    ext_name: str   # e.g., "spotify"
    version: str    # e.g., "4.1.1"
    
    def get_default_config(self):
        """
        Get default configuration for this extension.
        
        Returns:
        - str: Default configuration as INI format string
        """
        ...
    
    def get_config_schema(self):
        """
        Get configuration schema for validation.
        
        Returns:
        - ConfigSchema: Schema for extension configuration
        """
        ...
    
    def validate_environment(self):
        """
        Validate that extension can run in current environment.
        Raises ExtensionError if environment is invalid.
        """
        ...
    
    def setup(self, registry):
        """
        Register extension components with Mopidy.
        
        Parameters:
        - registry: Component registry for registration
        """
        ...
    
    def get_cache_dir(self, config):
        """
        Get cache directory for this extension.
        
        Parameters:
        - config: Mopidy configuration
        
        Returns:
        - pathlib.Path: Cache directory path
        """
        ...
    
    def get_config_dir(self, config):
        """
        Get configuration directory for this extension.
        
        Parameters:
        - config: Mopidy configuration
        
        Returns:
        - pathlib.Path: Configuration directory path
        """
        ...
    
    def get_data_dir(self, config):
        """
        Get data directory for this extension.
        
        Parameters:
        - config: Mopidy configuration
        
        Returns:
        - pathlib.Path: Data directory path
        """
        ...

Usage example:

from mopidy import config
from mopidy.ext import Extension

class SpotifyExtension(Extension):
    dist_name = "Mopidy-Spotify"
    ext_name = "spotify"
    version = "4.1.1"
    
    def get_default_config(self):
        return """\
[spotify]
enabled = true
username = 
password = 
client_id = 
client_secret = 
"""
    
    def get_config_schema(self):
        schema = super().get_config_schema()
        schema["username"] = config.String()
        schema["password"] = config.Secret()
        schema["client_id"] = config.String()
        schema["client_secret"] = config.Secret()
        return schema
    
    def validate_environment(self):
        try:
            import spotipy
        except ImportError as e:
            raise ExtensionError("Spotify extension requires spotipy library") from e
    
    def setup(self, registry):
        from .backend import SpotifyBackend
        from .frontend import SpotifyWebApp
        
        registry.add("backend", SpotifyBackend)
        registry.add("frontend", SpotifyWebApp)

Extension Metadata Container

Data structure containing extension information and configuration for the system.

class ExtensionData(NamedTuple):
    """
    Container for extension metadata and configuration.
    
    Attributes:
    - extension (Extension): Extension instance
    - entry_point: setuptools entry point object
    - config_schema (ConfigSchema): Extension configuration schema
    - config_defaults: Default configuration values
    - command (Command, optional): Extension CLI command
    """
    extension: Extension
    entry_point: Any
    config_schema: ConfigSchema
    config_defaults: Any
    command: Optional[Command]

Extension Discovery and Loading

Functions for discovering, loading, and validating extensions at runtime.

def load_extensions():
    """
    Load all available Mopidy extensions.
    
    Returns:
    - list[ExtensionData]: Loaded extension data
    """
    ...

def get_extensions_by_name():
    """
    Get extensions mapped by name.
    
    Returns:
    - dict[str, ExtensionData]: Extensions by name
    """
    ...

def validate_extension(extension_data):
    """
    Validate that extension can be used.
    
    Parameters:
    - extension_data (ExtensionData): Extension to validate
    
    Returns:
    - bool: True if extension is valid
    """
    ...

Component Registry

Registry system for managing extension-provided components like backends, frontends, and mixers.

class Registry:
    """Registry for extension-provided components."""
    
    def add(self, component_type, component_class):
        """
        Register a component class.
        
        Parameters:
        - component_type (str): Type of component ('backend', 'frontend', 'mixer')
        - component_class: Component class to register
        """
        ...
    
    def get(self, component_type):
        """
        Get all registered components of a type.
        
        Parameters:
        - component_type (str): Component type to retrieve
        
        Returns:
        - list: Registered component classes
        """
        ...
    
    def get_by_name(self, component_type, name):
        """
        Get specific component by name.
        
        Parameters:
        - component_type (str): Component type
        - name (str): Component name
        
        Returns:
        - Component class or None
        """
        ...

Usage example:

def setup(self, registry):
    from .backend import MyBackend
    from .frontend import MyFrontend
    from .mixer import MyMixer
    
    registry.add("backend", MyBackend)
    registry.add("frontend", MyFrontend) 
    registry.add("mixer", MyMixer)

Extension Configuration Integration

Extensions integrate with Mopidy's configuration system through schemas and defaults.

def get_config_schemas(extensions_data):
    """
    Get configuration schemas from extensions.
    
    Parameters:
    - extensions_data (list[ExtensionData]): Extension data
    
    Returns:
    - list[ConfigSchema]: Configuration schemas
    """
    ...

def get_config_defaults(extensions_data):
    """
    Get default configuration from extensions.
    
    Parameters:
    - extensions_data (list[ExtensionData]): Extension data  
    
    Returns:
    - list[str]: Default configuration strings
    """
    ...

Extension CLI Commands

Extensions can provide command-line interface commands that integrate with Mopidy's CLI system.

class Command:
    """Base class for extension CLI commands."""
    
    help: str  # Help text for the command
    
    def __init__(self):
        ...
    
    def add_to_parser(self, parser):
        """
        Add command arguments to argument parser.
        
        Parameters:
        - parser: Argument parser instance
        """
        ...
    
    def run(self, args, config, extensions_data):
        """
        Execute the command.
        
        Parameters:
        - args: Parsed command arguments
        - config: Mopidy configuration
        - extensions_data (list[ExtensionData]): Available extensions
        
        Returns:
        - int: Exit code
        """
        ...

Usage example:

from mopidy.commands import Command

class ScanCommand(Command):
    help = "Scan local music library"
    
    def add_to_parser(self, parser):
        parser.add_argument(
            "--force", 
            action="store_true",
            help="Force full rescan"
        )
    
    def run(self, args, config, extensions_data):
        print("Scanning music library...")
        # Implement scan logic
        return 0

# In extension setup
def get_command(self):
    return ScanCommand()

Extension Installation and Setup

Guidelines for packaging and distributing extensions.

# setup.py example for extensions
from setuptools import setup

setup(
    name="Mopidy-MyExtension",
    version="1.0.0",
    packages=["mopidy_myextension"],
    entry_points={
        "mopidy.ext": [
            "myextension = mopidy_myextension:Extension"
        ]
    },
    install_requires=[
        "Mopidy >= 3.0.0",
        "Pykka >= 2.0.1",
        # Extension-specific dependencies
    ]
)

Environment Validation

Best practices for validating extension environments and dependencies.

def validate_environment(self):
    """Validate extension environment and dependencies."""
    
    # Check required dependencies
    try:
        import required_library
    except ImportError as e:
        raise ExtensionError(
            f"Extension requires 'required_library' package"
        ) from e
    
    # Check version requirements  
    if required_library.__version__ < "2.0.0":
        raise ExtensionError(
            f"Extension requires required_library >= 2.0.0, "
            f"found {required_library.__version__}"
        )
    
    # Check system requirements
    if not os.path.exists("/dev/audio"):
        raise ExtensionError("Extension requires audio device")
    
    # Check configuration
    config = self.get_config()
    if not config["api_key"]:
        raise ExtensionError("Extension requires API key configuration")

Extension Lifecycle Events

Extensions can respond to system lifecycle events for initialization and cleanup.

class Extension:
    def on_start(self):
        """Called when Mopidy starts up."""
        ...
    
    def on_stop(self):
        """Called when Mopidy shuts down."""
        ...

Built-in Extensions

Mopidy includes several built-in extensions that demonstrate the extension system:

HTTP Extension

class HttpExtension(Extension):
    """HTTP frontend providing web interface and API."""
    dist_name = "Mopidy"
    ext_name = "http"

File Extension

class FileExtension(Extension):
    """Local file backend for playing music files."""
    dist_name = "Mopidy"  
    ext_name = "file"

M3U Extension

class M3uExtension(Extension):
    """M3U playlist backend for .m3u playlist files."""
    dist_name = "Mopidy"
    ext_name = "m3u"

Stream Extension

class StreamExtension(Extension):
    """Stream backend for internet radio and streaming URLs."""
    dist_name = "Mopidy"
    ext_name = "stream"

Software Mixer Extension

class SoftwareMixerExtension(Extension):
    """Software-based audio mixer implementation."""
    dist_name = "Mopidy"
    ext_name = "softwaremixer"

Extension Development Best Practices

Configuration Schema Design

def get_config_schema(self):
    schema = super().get_config_schema()
    
    # Use appropriate config types
    schema["api_endpoint"] = config.String()
    schema["api_key"] = config.Secret()  # For sensitive data
    schema["timeout"] = config.Integer(minimum=1, maximum=300)
    schema["enabled_features"] = config.List()
    schema["cache_size"] = config.Integer(minimum=0)
    
    return schema

Error Handling

from mopidy.exceptions import ExtensionError

class MyExtension(Extension):
    def validate_environment(self):
        try:
            self._check_dependencies()
            self._check_credentials()
        except Exception as e:
            raise ExtensionError(f"MyExtension setup failed: {e}") from e

Logging

import logging

logger = logging.getLogger(__name__)

class MyExtension(Extension):
    def setup(self, registry):
        logger.info("Setting up MyExtension")
        try:
            # Setup logic
            registry.add("backend", MyBackend)
            logger.info("MyExtension setup complete")
        except Exception as e:
            logger.error(f"MyExtension setup failed: {e}")
            raise

docs

audio-system.md

backend-system.md

configuration.md

core-controllers.md

extension-system.md

index.md

models.md

tile.json