From: Lasse Bang Mikkelsen Date: Tue, 15 Apr 2025 11:32:53 +0000 (+0200) Subject: Fix AirPlay branding (#2127) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=58f8d2e0f6542d74d7c0446e0adb7df433faa3f6;p=music-assistant-server.git Fix AirPlay branding (#2127) The correct Apple branding is "AirPlay". Also fixed "macOS" a few places. --- diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index ec29152e..c5e65c9f 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -6,7 +6,7 @@ Developer docs * Python 3.12 is minimal required, 3.12 recommended (or check the pyproject for current required version) * [Python venv](https://docs.python.org/3/library/venv.html) -We recommend developing on a (recent) MacOS or Linux machine. +We recommend developing on a (recent) macOS or Linux machine. It is recommended to use Visual Studio Code as your IDE, since launch files to start Music Assistant are provided as part of the repository. Furthermore, the current code base is not verified to work on a native Windows machine. If you would like to develop on a Windows machine, install [WSL2](https://code.visualstudio.com/blogs/2019/09/03/wsl2) to increase your swag-level 🤘. ## 🚀 Setting up your development environment @@ -25,7 +25,7 @@ NOTE: Always re-run the setup script after you fetch the latest code because req ### Using Devcontainer/Codespace We removed support for devcontainers because we do not have anyone willing to maintain it. It also is not very convenient due to all the port requirements, binaries etc. -If somebody is willing to create and maintain a devcontainer with host networking and based on our base alpine image, we will add the support back. Until then: Develop with Python venv on a Linux or MacOS machine (see above). +If somebody is willing to create and maintain a devcontainer with host networking and based on our base alpine image, we will add the support back. Until then: Develop with Python venv on a Linux or macOS machine (see above). ## Note on async Python The Music Assistant server is fully built in Python. The Python language has no real supported for multi-threading. This is why Music Assistant heavily relies on asyncio to handle blocking IO. It is important to get a good understanding of asynchronous programming before building your first provider. [This](https://www.youtube.com/watch?v=M-UcUs7IMIM) video is an excellent first step in the world of asyncio. @@ -65,7 +65,7 @@ Create a file called `__init__.py` inside the folder of your provider. This file ## ▶️ Building your own Player Provider -A Player Provider is the provider type that adds support for a 'target of playback' to Music Assistant. Sonos, Chromecast and Airplay are examples of a Player Provider. +A Player Provider is the provider type that adds support for a 'target of playback' to Music Assistant. Sonos, Chromecast and AirPlay are examples of a Player Provider. All Providers (of all types) can be found in the `music_assistant/providers` folder. TIP: We have created a template/stub provider in `music_assistant/providers/_template_player_provider` to get you started fast! diff --git a/music_assistant/providers/_template_music_provider/__init__.py b/music_assistant/providers/_template_music_provider/__init__.py index e00cde9d..adfbe52e 100644 --- a/music_assistant/providers/_template_music_provider/__init__.py +++ b/music_assistant/providers/_template_music_provider/__init__.py @@ -28,7 +28,7 @@ Optional is an icon.svg file that will be used as the icon for the provider in t but we also support that you specify a material design icon in the manifest.json file. IMPORTANT NOTE: -We strongly recommend developing on either MacOS or Linux and start your development +We strongly recommend developing on either macOS or Linux and start your development environment by running the setup.sh script in the scripts folder of the repository. This will create a virtual environment and install all dependencies needed for development. See also our general DEVELOPMENT.md guide in the repository for more information. diff --git a/music_assistant/providers/_template_player_provider/__init__.py b/music_assistant/providers/_template_player_provider/__init__.py index d6b8cc84..a2c37b05 100644 --- a/music_assistant/providers/_template_player_provider/__init__.py +++ b/music_assistant/providers/_template_player_provider/__init__.py @@ -22,7 +22,7 @@ Optional is an icon.svg file that will be used as the icon for the provider in t but we also support that you specify a material design icon in the manifest.json file. IMPORTANT NOTE: -We strongly recommend developing on either MacOS or Linux and start your development +We strongly recommend developing on either macOS or Linux and start your development environment by running the setup.sh scripts in the scripts folder of the repository. This will create a virtual environment and install all dependencies needed for development. See also our general DEVELOPMENT.md guide in the repository for more information. @@ -323,7 +323,7 @@ class MyDemoPlayerprovider(PlayerProvider): # all songs in the playback queue into a single stream and send that to the player. # In that case the URI (and metadata) received here is that of the 'flow mode' stream. - # Examples of player providers that use flow mode for playback by default are Airplay, + # Examples of player providers that use flow mode for playback by default are AirPlay, # SnapCast and Fully Kiosk. # Examples of player providers that optionally use 'flow mode' are Google Cast and diff --git a/music_assistant/providers/_template_plugin_provider/__init__.py b/music_assistant/providers/_template_plugin_provider/__init__.py index fbed2400..27d9f670 100644 --- a/music_assistant/providers/_template_plugin_provider/__init__.py +++ b/music_assistant/providers/_template_plugin_provider/__init__.py @@ -25,7 +25,7 @@ Optional is an icon.svg file that will be used as the icon for the provider in t but we also support that you specify a material design icon in the manifest.json file. IMPORTANT NOTE: -We strongly recommend developing on either MacOS or Linux and start your development +We strongly recommend developing on either macOS or Linux and start your development environment by running the setup.sh scripts in the scripts folder of the repository. This will create a virtual environment and install all dependencies needed for development. See also our general DEVELOPMENT.md guide in the repository for more information. diff --git a/music_assistant/providers/airplay/__init__.py b/music_assistant/providers/airplay/__init__.py index 752d2146..ad7dfce1 100644 --- a/music_assistant/providers/airplay/__init__.py +++ b/music_assistant/providers/airplay/__init__.py @@ -1,4 +1,4 @@ -"""Airplay Player provider for Music Assistant.""" +"""AirPlay Player provider for Music Assistant.""" from __future__ import annotations @@ -11,7 +11,7 @@ from music_assistant_models.provider import ProviderManifest from music_assistant.mass import MusicAssistant from .const import CONF_BIND_INTERFACE -from .provider import AirplayProvider +from .provider import AirPlayProvider if TYPE_CHECKING: from music_assistant_models.config_entries import ProviderConfig @@ -40,7 +40,7 @@ async def get_config_entries( type=ConfigEntryType.STRING, default_value=cast("str", mass.streams.publish_ip), label="Bind interface", - description="Interface to bind to for Airplay streaming.", + description="Interface to bind to for AirPlay streaming.", category="advanced", ), ) @@ -50,4 +50,4 @@ async def setup( mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig ) -> ProviderInstanceType: """Initialize provider(instance) with given configuration.""" - return AirplayProvider(mass, manifest, config) + return AirPlayProvider(mass, manifest, config) diff --git a/music_assistant/providers/airplay/helpers.py b/music_assistant/providers/airplay/helpers.py index 693cbe50..f7f47f9c 100644 --- a/music_assistant/providers/airplay/helpers.py +++ b/music_assistant/providers/airplay/helpers.py @@ -1,4 +1,4 @@ -"""Various helpers/utilities for the Airplay provider.""" +"""Various helpers/utilities for the AirPlay provider.""" from __future__ import annotations @@ -16,7 +16,7 @@ if TYPE_CHECKING: def convert_airplay_volume(value: float) -> int: - """Remap Airplay Volume to 0..100 scale.""" + """Remap AirPlay Volume to 0..100 scale.""" airplay_min = -30 airplay_max = 0 normal_min = 0 @@ -65,7 +65,7 @@ def get_model_info(info: AsyncServiceInfo) -> tuple[str, str]: model = "Apple TV" manufacturer = "Apple" - return (manufacturer or "Airplay", model) + return (manufacturer or "AirPlay", model) def get_primary_ip_address(discovery_info: AsyncServiceInfo) -> str | None: diff --git a/music_assistant/providers/airplay/manifest.json b/music_assistant/providers/airplay/manifest.json index f465e59d..c67cd3eb 100644 --- a/music_assistant/providers/airplay/manifest.json +++ b/music_assistant/providers/airplay/manifest.json @@ -1,8 +1,8 @@ { "type": "player", "domain": "airplay", - "name": "Airplay", - "description": "Support for players that support the Airplay protocol.", + "name": "AirPlay", + "description": "Support for players that support the AirPlay protocol.", "codeowners": ["@music-assistant"], "requirements": [], "documentation": "https://music-assistant.io/player-support/airplay/", diff --git a/music_assistant/providers/airplay/player.py b/music_assistant/providers/airplay/player.py index d13f7d72..84ea4ef1 100644 --- a/music_assistant/providers/airplay/player.py +++ b/music_assistant/providers/airplay/player.py @@ -10,15 +10,15 @@ from music_assistant_models.enums import PlayerState if TYPE_CHECKING: from zeroconf.asyncio import AsyncServiceInfo - from .provider import AirplayProvider + from .provider import AirPlayProvider from .raop import RaopStream class AirPlayPlayer: - """Holds the details of the (discovered) Airplay (RAOP) player.""" + """Holds the details of the (discovered) AirPlay (RAOP) player.""" def __init__( - self, prov: AirplayProvider, player_id: str, discovery_info: AsyncServiceInfo, address: str + self, prov: AirPlayProvider, player_id: str, discovery_info: AsyncServiceInfo, address: str ) -> None: """Initialize AirPlayPlayer.""" self.prov = prov diff --git a/music_assistant/providers/airplay/provider.py b/music_assistant/providers/airplay/provider.py index e514398f..33d7011e 100644 --- a/music_assistant/providers/airplay/provider.py +++ b/music_assistant/providers/airplay/provider.py @@ -1,4 +1,4 @@ -"""Airplay Player provider for Music Assistant.""" +"""AirPlay Player provider for Music Assistant.""" from __future__ import annotations @@ -116,7 +116,7 @@ PLAYER_CONFIG_ENTRIES = ( type=ConfigEntryType.BOOLEAN, default_value=False, label="Ignore volume reports sent by the device itself", - description="The Airplay protocol allows devices to report their own volume level. \n" + description="The AirPlay protocol allows devices to report their own volume level. \n" "For some devices this is not reliable and can cause unexpected volume changes. \n" "Enable this option to ignore these reports.", category="airplay", @@ -128,22 +128,22 @@ BROKEN_RAOP_WARN = ConfigEntry( type=ConfigEntryType.ALERT, default_value=None, required=False, - label="This player is known to have broken Airplay 1 (RAOP) support. " + label="This player is known to have broken AirPlay 1 (RAOP) support. " "Playback may fail or simply be silent. There is no workaround for this issue at the moment.", ) -# TODO: Airplay provider +# TODO: AirPlay provider # - Implement authentication for Apple TV # - Implement volume control for Apple devices using pyatv # - Implement metadata for Apple Apple devices using pyatv # - Use pyatv for communicating with original Apple devices (and use cliraop for actual streaming) -# - Implement Airplay 2 support +# - Implement AirPlay 2 support # - Implement late joining to existing stream (instead of restarting it) -class AirplayProvider(PlayerProvider): - """Player provider for Airplay based players.""" +class AirPlayProvider(PlayerProvider): + """Player provider for AirPlay based players.""" cliraop_bin: str | None _players: dict[str, AirPlayPlayer] @@ -478,7 +478,7 @@ class AirplayProvider(PlayerProvider): address = get_primary_ip_address(info) if address is None: return - self.logger.debug("Discovered Airplay device %s on %s", display_name, address) + self.logger.debug("Discovered AirPlay device %s on %s", display_name, address) # prefer airplay mdns info as it has more details # fallback to raop info if airplay info is not available @@ -506,7 +506,7 @@ class AirplayProvider(PlayerProvider): # append airplay to the default display name for generic (non-apple) devices # this makes it easier for users to distinguish between airplay and non-airplay devices if manufacturer.lower() != "apple" and "airplay" not in display_name.lower(): - display_name += " (Airplay)" + display_name += " (AirPlay)" self._players[player_id] = AirPlayPlayer(self, player_id, info, address) if not (volume := await self.mass.cache.get(player_id, base_key=CACHE_KEY_PREV_VOLUME)): diff --git a/music_assistant/providers/airplay/raop.py b/music_assistant/providers/airplay/raop.py index 93151f2b..ad18e568 100644 --- a/music_assistant/providers/airplay/raop.py +++ b/music_assistant/providers/airplay/raop.py @@ -1,4 +1,4 @@ -"""Logic for RAOP (AirPlay 1) audio streaming to Airplay devices.""" +"""Logic for RAOP (AirPlay 1) audio streaming to AirPlay devices.""" from __future__ import annotations @@ -35,7 +35,7 @@ if TYPE_CHECKING: from music_assistant_models.player_queue import PlayerQueue from .player import AirPlayPlayer - from .provider import AirplayProvider + from .provider import AirPlayProvider class RaopStreamSession: @@ -43,7 +43,7 @@ class RaopStreamSession: def __init__( self, - airplay_provider: AirplayProvider, + airplay_provider: AirPlayProvider, sync_clients: list[AirPlayPlayer], input_format: AudioFormat, audio_source: AsyncGenerator[bytes, None], @@ -169,7 +169,7 @@ class RaopStreamSession: class RaopStream: """ - RAOP (Airplay 1) Audio Streamer. + RAOP (AirPlay 1) Audio Streamer. Python is not suitable for realtime audio streaming so we do the actual streaming of (RAOP) audio using a small executable written in C based on libraop to do diff --git a/music_assistant/providers/filesystem_smb/__init__.py b/music_assistant/providers/filesystem_smb/__init__.py index 606582c0..130aeb6b 100644 --- a/music_assistant/providers/filesystem_smb/__init__.py +++ b/music_assistant/providers/filesystem_smb/__init__.py @@ -199,7 +199,7 @@ class SMBFileSystemProvider(LocalFileSystemProvider): } if platform.system() == "Darwin": - # NOTE: MacOS does not support special characters in the username/password + # NOTE: macOS does not support special characters in the username/password password_str = f":{password}" if password else "" mount_cmd = [ "mount", diff --git a/music_assistant/providers/sonos/const.py b/music_assistant/providers/sonos/const.py index cd8afb3b..fd3e91bb 100644 --- a/music_assistant/providers/sonos/const.py +++ b/music_assistant/providers/sonos/const.py @@ -52,7 +52,7 @@ PLAYER_SOURCE_MAP = { ), SOURCE_AIRPLAY: PlayerSource( id=SOURCE_AIRPLAY, - name="Airplay", + name="AirPlay", passive=True, can_play_pause=True, can_next_previous=True, diff --git a/music_assistant/providers/sonos/provider.py b/music_assistant/providers/sonos/provider.py index e7cf3251..1e056d05 100644 --- a/music_assistant/providers/sonos/provider.py +++ b/music_assistant/providers/sonos/provider.py @@ -173,17 +173,17 @@ class SonosPlayerProvider(PlayerProvider): ConfigEntry( key=CONF_AIRPLAY_MODE, type=ConfigEntryType.BOOLEAN, - label="Enable Airplay mode", - description="Almost all newer Sonos speakers have Airplay support. " - "If you have the Airplay provider enabled in Music Assistant, " - "your Sonos speaker will also be detected as a Airplay speaker, meaning " - "you can group them with other Airplay speakers.\n\n" + label="Enable AirPlay mode", + description="Almost all newer Sonos speakers have AirPlay support. " + "If you have the AirPlay provider enabled in Music Assistant, " + "your Sonos speaker will also be detected as a AirPlay speaker, meaning " + "you can group them with other AirPlay speakers.\n\n" "By default, Music Assistant uses the Sonos protocol for playback but with this " - "feature enabled, it will use the Airplay protocol instead by redirecting " - "the playback related commands to the linked Airplay player in Music Assistant, " - "allowing you to mix and match Sonos speakers with Airplay speakers. \n\n" - "NOTE: You need to have the Airplay provider enabled as well as " - "the Airplay version of this player.", + "feature enabled, it will use the AirPlay protocol instead by redirecting " + "the playback related commands to the linked AirPlay player in Music Assistant, " + "allowing you to mix and match Sonos speakers with AirPlay speakers. \n\n" + "NOTE: You need to have the AirPlay provider enabled as well as " + "the AirPlay version of this player.", required=False, default_value=False, depends_on="airplay_detected", @@ -256,7 +256,7 @@ class SonosPlayerProvider(PlayerProvider): sonos_player = self.sonos_players[target_player] if airplay_player := sonos_player.get_linked_airplay_player(False): # if airplay mode is enabled, we could possibly receive child player id's that are - # not Sonos players, but Airplay players. We redirect those. + # not Sonos players, but AirPlay players. We redirect those. airplay_child_ids = [x for x in child_player_ids if x.startswith("ap")] child_player_ids = [x for x in child_player_ids if x not in airplay_child_ids] if airplay_child_ids: diff --git a/pyproject.toml b/pyproject.toml index bb399f8e..3cd71b2f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,7 +162,7 @@ warn_unused_configs = true warn_unused_ignores = true [tool.ruff.format] -# Force Linux/MacOS line endings +# Force Linux/macOS line endings line-ending = "lf" [tool.pytest.ini_options]