Skip to content

Commit

Permalink
airplay: Fix AirPlay major version detection
Browse files Browse the repository at this point in the history
Fixes #2075
  • Loading branch information
postlund committed Jun 27, 2023
1 parent 70ce630 commit 20a7524
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 11 deletions.
19 changes: 13 additions & 6 deletions pyatv/protocols/airplay/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ def _get_flags(properties: Mapping[str, str]) -> int:
return int(flags, 16)


# These flags have been imported from here:
# https://emanuelecozzi.net/docs/airplay2/features/
# Should be the same as the ones from here:
# https://openairplay.github.io/airplay-spec/features.html
# But seems to be some inconsistencies. Worth to keep in mind.
class AirPlayFlags(IntFlag):
"""Features supported by AirPlay."""

Expand Down Expand Up @@ -222,17 +227,19 @@ def log_response(logger, response: HttpResponse, message_prefix="") -> None:

# TODO: I don't know how to properly detect if a receiver support AirPlay 2 or not,
# so I'm guessing until I know better. The basic idea here is simple: the service
# should have the "features" flag (either "features" or "ft") and have all 64 bits
# present. Not sure if that is good enough, but will do for now.
# should have the "features" flag (either "features" or "ft") and either bit 38 or
# bit 48 should be present.
def get_protocol_version(service: BaseService) -> AirPlayMajorVersion:
"""Return major AirPlay version supported by a service."""
features = service.properties.get("ft")
if not features:
features = service.properties.get("features", "")
features = service.properties.get("features", "0x0")

# Ensure feature flag is valid and all flags present (i.e.
# 0xAABBCCDD,0xAABBCCDD, thus looking for ",")
if features.count(",") == 1:
parsed_features = parse_features(features)
if (
AirPlayFlags.SupportsUnifiedMediaControl in parsed_features
or AirPlayFlags.SupportsCoreUtilsPairingAndEncryption in parsed_features
):
return AirPlayMajorVersion.AirPlayV2
return AirPlayMajorVersion.AirPlayV1

Expand Down
18 changes: 13 additions & 5 deletions tests/protocols/airplay/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,20 @@ def test_is_remote_control_supported(props, credentials, expected_supported):
@pytest.mark.parametrize(
"props, expected_version",
[
# Fallback
({}, AirPlayMajorVersion.AirPlayV1),
# Used by RAOP
({"ft": "0xAABBCCDD"}, AirPlayMajorVersion.AirPlayV1),
({"ft": "0xAABBCCDD,0xAABBCCDD"}, AirPlayMajorVersion.AirPlayV2),
# User by AirPlay
({"features": "0xAABBCCDD"}, AirPlayMajorVersion.AirPlayV1),
({"features": "0xAABBCCDD,0xAABBCCDD"}, AirPlayMajorVersion.AirPlayV2),
({"ft": "0x5A7FFFF7,0xE"}, AirPlayMajorVersion.AirPlayV1), # Apple TV 3
(
{"ft": "0x4A7FCA00,0xBC354BD0"},
AirPlayMajorVersion.AirPlayV2,
), # HomePod Mini
# Used by AirPlay
({"features": "0x5A7FFFF7,0xE"}, AirPlayMajorVersion.AirPlayV1), # Apple TV 3
(
{"features": "0x4A7FCA00,0xBC354BD0"},
AirPlayMajorVersion.AirPlayV2,
), # HomePod Mini
],
)
def test_get_protocol_version(props, expected_version):
Expand Down

0 comments on commit 20a7524

Please sign in to comment.