diff --git a/pyatv/protocols/mrp/protobuf/re_protos/foundation/Color.proto b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Color.proto new file mode 100644 index 000000000..ac76c021e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Color.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + + +message Color { + optional float red = 1; + optional float green = 2; + optional float blue = 3; + optional float alpha = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/foundation/Common.proto b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Common.proto new file mode 100644 index 000000000..75d5605d9 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Common.proto @@ -0,0 +1,264 @@ +syntax = "proto2"; + +message RepeatMode { + enum Enum { + Unknown = 0; + Off = 1; + One = 2; + All = 3; + } +} + +message ShuffleMode { + enum Enum { + Unknown = 0; + Off = 1; + Albums = 2; + Songs = 3; + } +} + +message DeviceClass { + enum Enum { + Invalid = 0; + iPhone = 1; + iPod = 2; + iPad = 3; + AppleTV = 4; + iFPGA = 5; + Watch = 6; + Accessory = 7; + Bridge = 8; + Mac = 9; + Android = 10; + Web = 11; + AppleDisplay = 12; + RealityDevice = 13; + } +} + +message DeviceType { + enum Enum { + Unknown = 0; + AirPlay = 1; + Bluetooth = 2; + CarPlay = 3; + BuiltIn = 4; + Wired = 5; + } +} + +message DeviceSubType { + enum Enum { + Default = 0; + Speaker = 1; + Headphones = 2; + Headset = 3; + Receiver = 4; + LineOut = 5; + USB = 6; + DisplayPort = 7; + HDMI = 8; + LowEnergy = 9; + SPDIF = 10; + TV = 11; + HomePod = 12; + AppleTV = 13; + Vehicle = 14; + Cluster = 15; + SetTopBox = 16; + TVStick = 17; + Mac = 18; + iOS = 19; + Watch = 20; + Vision = 21; + } +} + +message PlaybackState { + enum Enum { + Unknown = 0; + Playing = 1; + Paused = 2; + Stopped = 3; + Interrupted = 4; + Seeking = 5; + } +} + + +message AudioTier { + enum Enum { + LowBandwidthStereo = 1; + HighQualityStereo = 2; + Lossless = 3; + HighResolutionLossless = 4; + Spatial = 5; + } +} + +message SongTraits { + enum Enum { + None = 0; + AppleDigitalMaster = 1; + Lossless = 2; + HighResolutionLossless = 4; + Spatial = 8; + Atmos = 16; + Surround = 32; + } +} + +message AlbumTraits { + enum Enum { + None = 0; + AppleDigitalMaster = 1; + Lossless = 2; + HighResolutionLossless = 4; + Spatial = 8; + Atmos = 16; + Surround = 32; + } +} + +message PlaylistTraits { + enum Enum { + None = 0; + Spatial = 8; + Atmos = 16; + Surround = 32; + } +} + +message ActiveFormatJustification { + enum Enum { + Unknown = 0; + Unavailable = 1; + UserPreference = 100; + UserDownload = 101; + RouteIncompatible = 500; + RouteUnknownCompatibility = 501; + BandwidthInsufficient = 1000; + } +} + +message FormatTier { + enum Enum { + LowBandwidthStereo = 1; + HighQualityStereo = 2; + Lossless = 4; + HighResolutionLossless = 8; + Spatial = 16; + } +} + +message AudioRouteType { + enum Enum { + Unknown = 0; + DevicesSpeaker = 1; + LineOut = 2; + Headphones = 3; + BluetoothHeadphones = 4; + BluetoothSpeaker = 5; + USBAudio = 6; + CarAudio = 7; + HDMI = 8; + AirPlay = 9; + } +} +message GroupSessionRouteType { + + enum Enum{ + Unknown = 0; + CarKit = 1; + CarPlay = 2; + Speaker = 5; + HomePod = 8; + HomePodMini = 9; + AppleTV = 11; + } +} +message EndpointOptions { +enum Enum { + None = 0; + UpdateActiveEndpoint = 1; + FallbackToAddOutputDevices = 2; + AllowMigrateToGroup = 4; + AllowMigrateFromGroup = 8; +} + } + + +message PlayerOptions { + enum Enum { + None = 0; + RestoreDestinationPlaybackState = 1; + PlayDestination = 2; + PauseSource = 4; + RestorePlaybackPosition = 8; + RestorePlaybackRate = 16; +} +} +message RecipeType { + enum Enum { + NotPossible = 0; + Legacy = 1; + OneShot = 2; + } +} + +message PlaybackSessionMigrateRequestEventRole { + enum Enum { + Unknown = 0; + Receptionist = 1; + Source = 2; + Destination = 3; + InApp = 4; + Hijacked = 5; + Group = 6; + } +} + +message ClusterType { + enum Enum { + None = 0; + StereoPair = 1; + HomeTheater = 2; + GenericAudio = 3; + } +} + +message VirtualTouchPhase { + enum Enum { + Unknown = 0; + Began = 1; + Moved = 2; + Stationary = 3; + Ended = 4; + Cancelled = 5; + } +} + +message ReplaceIntent { + enum Enum { + NonDestructive = 1; + ClearUpNext = 2; + KeepUpNext = 3; + LeaveSharedSession = 4; + } +} +message PlaybackQueueType { + enum Enum { + Unknown = 1; + Generic = 2; + Custom = 3; + Empty = 4; + } +} + +message ChangeType { + enum Enum { + Immediate = 0; + Deferrable = 1; + } +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/foundation/Dictionary.proto b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Dictionary.proto new file mode 100644 index 000000000..a900e73c8 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Dictionary.proto @@ -0,0 +1,79 @@ +syntax = "proto2"; + +/* + * Dictionary + * + * A dictionary implemented as a repeated list of key/value pairs. + * Keys are strings. + * Values are dynamically typed via MRValueProtobuf. + */ +message Dictionary { + + // Repeated key/value entries + repeated KeyValuePair pair = 1; +} + +/* + * KeyValuePair + * + * Represents a single dictionary entry. + * - key is REQUIRED (writer crashes if missing) + * - value is REQUIRED (writer crashes if missing) + */ +message KeyValuePair { + + // Dictionary key + required string key = 1; + + // Dictionary value (variant type) + required Value value = 2; +} + +/* + * Value + * + * Dynamically typed value container. + * + * This is NOT a proto3 oneof. + * Presence is tracked via a private _has bitfield. + * + * Supported types: + * - string + * - int64 + * - float + * - double + * - bytes + * - bool + * - date (string-encoded) + * - array (repeated MRValueProtobuf) + * - dictionary (MRDictionaryProtobuf) + */ +message Value { + + // String value + optional string stringValue = 1; + + // Integer value + optional int64 int64Value = 2; + + // Float value + optional float floatValue = 3; + + // Double value + optional double doubleValue = 4; + + // Raw data value + optional bytes dataValue = 5; + + // Boolean value + optional bool boolValue = 6; + + // Date value (string-encoded, likely ISO-8601) + optional string dateValue = 7; + + // Array of values + repeated Value arrayValues = 8; + + // Nested dictionary + optional Dictionary dictionaryValue = 9; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/foundation/Error.proto b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Error.proto new file mode 100644 index 000000000..b8b99fcec --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/foundation/Error.proto @@ -0,0 +1,176 @@ +syntax = "proto2"; + +import "foundation/Dictionary.proto"; + +message Error { + optional string domain = 1; + optional int32 code = 2; + optional string localizedDescription = 3; + optional string localizedFailureReason = 4; + repeated Error underlyingErrors = 5; + optional string debugMessage = 6; + optional Dictionary userInfo = 100; +} + +enum ErrorCodes { + NoError = 0; // "No Error" + UnknownError = 1; // "Unknown Error" + InvalidOperation = 2; // "Invalid Operation" + OperationNotPermitted = 3; // "Operation not permitted" + ClientDoesNotExist = 4; // "Client does not exist" + OriginDoesNotExist = 5; // "Origin does not exist" + UnsupportedOperation = 6; // "Unsupported operation" + FailedToSetPickedRoute = 7; // "Failed to set picked route" + FailedToRegisterCustomOrigin = 8; // "Failed to register custom origin" + FailedToRemoveCustomOrigin = 9; // "Failed to remove custom origin" + ApplicationActivityDoesNotExist = 10; // "The application activity does not exist" + AppMissingBrowsableContentEndpoint = 11; // "The app has not setup a browsable content endpoint" + BrowsableContentAPINotSupported = 12; // "The requested browsable content API is not supported" + NotificationNotAllowedByServer = 13; // "The notification is not allowed by the server." + RequiresClientCallbackRegistered = 14; // "Operation requires a client callback to have been registered" + RequiresClientDataSourceRegistered = 15; // "Operation requires a client data source to have been registered." + RequestedDataOutOfDate = 16; // "Requested data is out of date and should be requested again" + DeviceEnforcedVolumeLimitExceeded = 17; // "The device's enforced volume limit has been exceeded" + VolumeValueOutOfRange = 18; // "Volume value is out of range (must be between 0.0 and 1.0, where 1.0 is the loudest)." + VolumeAlreadyAtMaximum = 19; // "Volume is already at the maximum value." + VolumeAlreadyMuted = 20; // "Volume is already muted." + VoiceInputEndpointDoesNotExist = 21; // "Voice input endpoint does not exist." + VoiceInputDeviceNotRegistered = 22; // "The voice input device is not registered or does not exist." + EncryptionFailure = 23; // "Encryption failure." + EndpointDoesNotExist = 24; // "Endpoint does not exist." + ClientApplicationCancelled = 25; // "The client's application cancelled the operation." + OperationTimedOut = 26; // "The operation timed out." + InvalidPlayerPathObject = 27; // "The specified player path object was invalid. (No Now Playing Client?)" + AVOutputContextDeviceChangeNotAllowed = 28; // "Adding or removing devices from the AV output context has failed." + CouldNotFindNowPlayingPlayer = 29; // "Could not find the specified now playing player" + ContentItemDoesNotExist = 30; // "The specified content item does not exist." + InvalidOffset = 31; // "The specified offset is invalid." + InvalidOutputContext = 32; // "The specified output context is invalid." + OutputDevicesNotGroupable = 33; // "One or more specified output devices are not groupable." + OutputContextDoesNotSupportAdditions = 34; // "The specified output context does not support adding more than one output device." + CouldNotFindNowPlayingClient = 35; // "Could not find the specified now playing client" + EndpointVolumeControlRequiresEndpoint = 36; // "Endpoint volume control is only possible if the endpoint is picked or remote controllable." + OutputDeviceVolumeControlRequiresDevice = 37; // "Output device volume control is only possible if the endpoint is picked or remote controllable." + CoderMustSupportKVC = 38; // "Coder must support key-value coding" + CouldNotFindGivenOutputDevice = 39; // "Could not find the given outputDevice" + FailedToPerformTopologyChanges = 40; // "Failed to perform topology changes" + PlayerCouldNotBeSetActive = 41; // "Player could not be set as active as its currently has Picture in Picture enabled and another player has higher priority" + PlayerPathUnresolvedAfterResolution = 42; // "Player path remained unresolved after resolution" + FailedToSetListeningMode = 43; // "Failed to set listening mode." + CommandDidNotProvideStatus = 44; // "The command did not provide a status" + QueueModificationProhibited = 45; // "Queue modification is prohibited based on the content" + SystemAtPeakCapacity = 46; // "The system is already at peak capacity and cannot accommodate adding another stream." + CouldNotDiscoverOutputDevice = 47; // "Could not discover outputDevice" + CouldNotDiscoverClusterLeader = 48; // "Could not discover cluster leader" + FailedToSetConversationDetection = 49; // "Failed to set conversation detection." + // 50..99 are pointers to cf_UnknownError + Reserved_50_99 = 50; // "Unknown error (50)" + + + FailedToConnectToRemoteDevice = 100; // "Failed to connect to remote device." + AuthenticationTokenInvalid = 101; // "Authentication token is invalid." + RecordingSessionAlreadyInProgress = 102; // "Recording session is already in progress on this device." + DeviceNotCurrentlyRecording = 103; // "The device is not currently recording." + ClientDisconnected = 104; // "The client has disconnected." + ServerDisconnected = 105; // "The server has disconnected." + ConnectionCancelledByClient = 106; // "The connection has been cancelled by the client." + PairingLockedDueToSecurity = 107; // "Pairing functionality is locked due to security reasons." + ClientOSVersionTooOld = 108; // "The client's operating system version is too old." + ClientAppVersionTooOld = 109; // "The client's application version is too old." + DeviceNotPaired = 110; // "The device is not paired" + PinDialogRemovedByUser = 111; // "The pin pairing dialog was removed by the user before pairing occoured" + PinDialogRemovedByTimeout = 112; // "The pin pairing dialog was removed by a timeout before pairing occoured" + PairingConnectionTimedOut = 113; // "The connection timed out" + PairingBlocked = 114; // "Pairing with this device is blocked" + DeviceGoingToSleep = 115; // "The device is going to sleep" + ConnectionBlockedByServer = 116; // "Connection blocked by server." + AVEndpointDeallocatedWaiting = 117; // "MRAVEndpoint was deallocated while waiting for device to connect." + AVEndpointDeallocatedTopology = 118; // "MRAVEndpoint was deallocated while making topology changes." + ConnectionFailedAuthRequest = 119; // "Connection failed because authorization request was skipped." + MessageParserException = 120; // "The message parser threw an exception" + CommunicationChannelClosed = 121; // "The communication channel was closed" + FailedToModifyRemoteControlOutputContext = 122; // "Failed to modify the remote control output context" + ExternalDeviceDeallocated = 123; // "External device was deallocated" + ConnectionClosedDueToInactivity = 124; // "The connection was manually closed due to inactivity" + OutputContextModificationTimedOut = 125; // "OutputContext modification failed due to a timeout" + ApplicationRequiresUserInteraction = 126; // "Application requires user interaction before can be programatically launched" + CannotFindExternalDevice = 127; // "Cannot find externalDevice" + ExternalDeviceToBeDestroyed = 128; // "ExternalDeviceToBeDestroyed" + CannotDiscoverOutputDeviceInGroup = 129; // "CannotDiscoverOutputDeviceInGroup" + ClientNotEntitled = 130; // "ClientNotEntitled" + NoLocalEndpointConnectionRecentReboot = 131; // "NoLocalEndpointConnection (recent reboot detected)" + FeatureNotEnabled = 132; // "FeatureNotEnabled" + Interrupted_0 = 133; // "Interrupted" + Interrupted_1 = 134; // "Interrupted" (duplicate) + AirPlayClientNotAvailable = 135; // "AirPlayClientNotAvailable" + NewPlayerUsedInstead = 136; // "NewPlayerUsedInstead" + DuplicateCommandContextID = 137; // "DuplicateCommandContextID" + MissingNativeGroup = 138; // "MissingNativeGroup" + ComponentNoLongerValid = 139; // "The component may still be valid but it is not longer a UGL" + NoLocalEndpointConnectionNoRecentReboot = 140; // "NoLocalEndpointConnection (without recent reboot detected)" + NoMatchingOutputDevicesFound = 141; // "NoMatchingOutputDevicesFound" + MediaServerDeath = 142; // "MediaServerDeath" + + // 143..144 are UnknownError in your dump. + Reserved_143_144 = 143; + + InvalidRequest = 145; // "InvalidRequest" + NoSuitableDeviceAvailable = 146; // "NoSuitableDeviceAvailable" + Aggregate = 147; // "Aggregate" + + // 148..149 are UnknownError in your dump. + Reserved_148_149 = 148; + + AllowMigrateFromGroupMissing = 150; // "AllowMigrateFromGroup not present and source contains an AirPlay device" + AllowMigrateToGroupMissing = 151; // "AllowMigrateToGroup not present and destination contains more than 1 logical device" + SourceNoSetPlaybackSession = 152; // "Source does not support SetPlaybackSession command" + DestinationNoSetPlaybackSession = 153; // "Destination does not support SetPlaybackSession" + SupportedCommandsDoNotMatch = 154; // "Source and destination supported commands do not match" + ResolvedPlayerPathIsAirPlayDestination = 155; // "Resolved playerPath is an AirPlay destination" + FailedToResolveSourcePlayerPath = 156; // "Failed to resolve source player path" + FailedToResolveDestinationPlayerPath = 157; // "Failed to resolve destination player path" + +// 158..169 are UnknownError in your dump. + Reserved_158_169 = 158; + + CouldNotSourceEventsFromCoreDuetDB = 170; // "Could not source an array of events from Core Duet database." + OriginatingAppFailedToConfirmIntent = 171; // "The originating application failed to confirm the intent." + OriginatingAppFailedToHandleIntent = 172; // "The originating application failed to handle the intent." + OriginatingAppConnectionCInterrupted= 173; // "The connection to the originating application was interrupted." + ExpanseMigrationFailed = 175; // "Expanse migration failed." + SessionNotConfigured = 176; // "SessionNotConfigured" + AppConnectionRemoteServerLost = 177; // "AppConnectionRemoteServerConnectionLost" + AppConnectionRemoteClientLost = 178; // "AppConnectionRemoteClientConnectionLost" + AppConnectionServerEndpointLost = 179; // "AppConnectionServerEndpointLost" + AppConnectionClientEndpointLost = 180; // "AppConnectionClientEndpointLost" + AppConnectionNotSupportedByDestination = 181; // "AppConnectionNotSupportedByDestination" + AppConnectionClosedByClient = 182; // "AppConnectionClosedByClient" + AppConnectionClosedByServer = 183; // "AppConnectionClosedByServer" + AppConnectionRejectedByServer = 184; // "AppConnectionRejectedByServer" + HandoffSessionInvalidated = 185; // "HandoffSessionInvalidated" + HandoffSessionNotFound = 186; // "HandoffSessionNotFound" + MultipleRequestsNotSupported = 187; // "MultipleRequestsNotSupported" + PlayerCommandFailed = 188; // "PlayerCommandFailed (The app itself reported failure)" + SetVolumeFailed = 189; // "SetVolumeFailed" + +// 190..200 are UnknownError in your dump. + Reserved_190_200 = 190; + + OutputContextModCausedDeviceToNotBeProxy = 201; // "Output context modification caused a device to no longer be a proxy group player." + OutputContextModCausedDeviceToBecomeAProxy = 202; // "Output context modification caused a device to become a proxy group player." + OutputContextModRequestedNotTopoChange = 203; // "Output context modification requested no topology change." + +// 204..300 are UnknownError in your dump. + Reserved_204_300 = 204; + + CommandIgnored = 301; // "CommandIgnored" + CouldNotLaunchApplication = 302; // "CouldNotLaunchApplication" + ApplicationNotInstalled = 303; // "ApplicationNotInstalled" + CommandNotSupported = 304; // "CommandNotSupported" + XPC = 305; // "XPC" + ApplicationRequiresPreflight = 306; // "ApplicationRequiresPreflight" + ApplicationDisabled = 307; // "ApplicationDisabled" + TimedOutWaitingForCanBeNowPlaying = 308; // "TimedoutWaitingForCanBeNowPlaying" + ApplicationTerminated = 309; // "ApplicationTerminated" + ReplyDeallocated = 310; // "ReplyDeallocated" + }; diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AdjustVolumeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AdjustVolumeMessage.proto new file mode 100644 index 000000000..c9ae74198 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AdjustVolumeMessage.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/session/RequestDetails.proto"; + +extend ProtocolMessage { + optional AdjustVolumeMessage adjustVolumeMessage = 97; +} + +// Adjust volume by a relative amount +message AdjustVolumeMessage { + // From StringAsAdjustment: function + enum Adjustment { + IncrementSmall = 1; // cf_IncrementSmall + IncrementMedium = 2; // cf_IncrementMedium + IncrementLarge = 3; // cf_IncrementLarge + DecrementSmall = 4; // cf_DecrementSmall + DecrementMedium = 5; // cf_DecrementMedium + DecrementLarge = 6; // cf_DecrementLarge + } + optional Adjustment adjustment = 1; + optional string outputDeviceUID = 2; + optional RequestDetails details = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AudioFadeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AudioFadeMessage.proto new file mode 100644 index 000000000..1387254e0 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AudioFadeMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional AudioFadeMessage audioFadeMessage = 88; +} + +message AudioFadeMessage { + optional PlayerPath playerPath = 1; + optional int32 fadeType = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AudioFadeResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AudioFadeResponseMessage.proto new file mode 100644 index 000000000..9d95eb458 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/AudioFadeResponseMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional AudioFadeResponseMessage audioFadeResponseMessage = 89; +} + +message AudioFadeResponseMessage { + optional int64 fadeDuration = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVoiceInputDevicesMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVoiceInputDevicesMessage.proto new file mode 100644 index 000000000..46e110e92 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVoiceInputDevicesMessage.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetVoiceInputDevicesMessage getVoiceInputDevicesMessage = 31; +} + +message GetVoiceInputDevicesMessage { +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVoiceInputDevicesResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVoiceInputDevicesResponseMessage.proto new file mode 100644 index 000000000..06aa6b22a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVoiceInputDevicesResponseMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetVoiceInputDevicesResponseMessage getVoiceInputDevicesResponseMessage = 32; +} + +message GetVoiceInputDevicesResponseMessage { + repeated uint32 deviceIDs = 1; + optional uint32 errorCode = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeControlCapabilitiesMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeControlCapabilitiesMessage.proto new file mode 100644 index 000000000..ae039b65a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeControlCapabilitiesMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetVolumeControlCapabilitiesMessage getVolumeControlCapabilitiesMessage = 66; +} + +message GetVolumeControlCapabilitiesMessage { + optional string outputDeviceUID = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeControlCapabilitiesResultMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeControlCapabilitiesResultMessage.proto new file mode 100644 index 000000000..628a0a368 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeControlCapabilitiesResultMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "messages/audio/VolumeControlAvailabilityMessage.proto"; + +extend ProtocolMessage { + optional GetVolumeControlCapabilitiesResultMessage getVolumeControlCapabilitiesResultMessage = 67; +} + +message GetVolumeControlCapabilitiesResultMessage { + optional VolumeControlAvailabilityMessage capabilities = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMessage.proto new file mode 100644 index 000000000..f8f1cd269 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetVolumeMessage getVolumeMessage = 53; +} + +message GetVolumeMessage { + optional string outputDeviceUID = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMutedMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMutedMessage.proto new file mode 100644 index 000000000..5a9c09664 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMutedMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetVolumeMutedMessage getVolumeMutedMessage = 98; +} + +// Request to get muted state of an output device +message GetVolumeMutedMessage { + optional string outputDeviceUID = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMutedResultMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMutedResultMessage.proto new file mode 100644 index 000000000..10177d587 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeMutedResultMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetVolumeMutedResultMessage getVolumeMutedResultMessage = 99; +} + +// Response with muted state of an output device +message GetVolumeMutedResultMessage { + optional bool isMuted = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeResultMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeResultMessage.proto new file mode 100644 index 000000000..865b8feab --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/GetVolumeResultMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetVolumeResultMessage getVolumeResultMessage = 54; +} + +message GetVolumeResultMessage { + optional float volume = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/RegisterVoiceInputDeviceMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/RegisterVoiceInputDeviceMessage.proto new file mode 100644 index 000000000..13837fa1e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/RegisterVoiceInputDeviceMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/audio/VoiceInputDeviceDescriptorMessage.proto"; + +extend ProtocolMessage { + optional RegisterVoiceInputDeviceMessage registerVoiceInputDeviceMessage = 33; +} + +message RegisterVoiceInputDeviceMessage { + optional VoiceInputDeviceDescriptor deviceDescriptor = 1; // NOTE(RE): field name is "descriptor" in RE proto +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/RegisterVoiceInputDeviceResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/RegisterVoiceInputDeviceResponseMessage.proto new file mode 100644 index 000000000..610a25232 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/RegisterVoiceInputDeviceResponseMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RegisterVoiceInputDeviceResponseMessage registerVoiceInputDeviceResponseMessage = 34; +} + +message RegisterVoiceInputDeviceResponseMessage { + optional int32 deviceID = 1; + optional int32 errorCode = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SendVoiceInputMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SendVoiceInputMessage.proto new file mode 100644 index 000000000..fc2f692f8 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SendVoiceInputMessage.proto @@ -0,0 +1,38 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/audio/AudioFormatSettingsMessage.proto"; + +extend ProtocolMessage { + optional SendVoiceInputMessage sendVoiceInputMessage = 36; +} + +message AudioStreamPacketDescription { + optional int64 startOffset = 1; + optional uint32 variableFramesInPacket = 2; + optional uint32 dataByteSize = 3; +} + +message AudioBuffer { + optional AudioFormatSettings formatSettings = 1; + optional int64 packetCapacity = 2; + optional int64 maximumPacketSize = 3; + optional int64 packetCount = 4; + optional bytes contents = 5; + repeated AudioStreamPacketDescription packetDescriptions = 6; +} + +message AudioTime { + optional double timestamp = 1; + optional double sampleRate = 2; +} + +message AudioDataBlock { + optional AudioBuffer buffer = 1; + optional AudioTime time = 2; + optional double gain = 3; +} + +message SendVoiceInputMessage { + optional AudioDataBlock dataBlock = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetConversationDetectionEnabledMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetConversationDetectionEnabledMessage.proto new file mode 100644 index 000000000..b7a5f9288 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetConversationDetectionEnabledMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetConversationDetectionEnabledMessage setConversationDetectionEnabledMessage = 102; +} + +// Enable or disable conversation detection on an output device +message SetConversationDetectionEnabledMessage { + optional bool enabled = 1; + optional string outputDeviceUID = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetListeningModeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetListeningModeMessage.proto new file mode 100644 index 000000000..180e66ed4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetListeningModeMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetListeningModeMessage setListeningModeMessage = 92; +} + +message SetListeningModeMessage { + optional string listeningMode = 1; + optional string outputDeviceUID = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetRecordingStateMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetRecordingStateMessage.proto new file mode 100644 index 000000000..d2ac894ff --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetRecordingStateMessage.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetRecordingStateMessage setRecordingStateMessage = 35; +} + +message SetRecordingStateMessage { + enum RecordingState { + Unknown = 0; + Recording = 1; + NotRecording = 2; + } + + optional RecordingState state = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetVolumeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetVolumeMessage.proto new file mode 100644 index 000000000..3590e0f68 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetVolumeMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/session/RequestDetails.proto"; +extend ProtocolMessage { + optional SetVolumeMessage setVolumeMessage = 55; +} + +message SetVolumeMessage { + optional float volume = 1; + optional string outputDeviceUID = 2; + optional RequestDetails details = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetVolumeMutedMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetVolumeMutedMessage.proto new file mode 100644 index 000000000..8fa2fb242 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/SetVolumeMutedMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/session/RequestDetails.proto"; + +extend ProtocolMessage { + optional SetVolumeMutedMessage setVolumeMutedMessage = 100; +} + +// Set muted state of an output device +message SetVolumeMutedMessage { + optional bool isMuted = 1; + optional string outputDeviceUID = 2; + optional RequestDetails details = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeControlAvailabilityMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeControlAvailabilityMessage.proto new file mode 100644 index 000000000..7139a0def --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeControlAvailabilityMessage.proto @@ -0,0 +1,32 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional VolumeControlAvailabilityMessage volumeControlAvailabilityMessage = 22; +} + + +message VolumeControlAvailabilityMessage { + optional bool volumeControlAvailable = 1; + + // Bitmask of volume control capabilities. + // + // This field is encoded by Apple as int32 (varint on the wire). + // Multiple bits may be set simultaneously. + // + // Bit layout: + // 0x0 (0) = None + // 0x1 (1) = Mute + // 0x2 (2) = Relative (increment/decrement style) + // 0x4 (4) = Absolute (direct volume set) + // 0x8 (8) = Adjustment (route/device-level adjustment) + // + // Example values: + // 0 -> None + // 2 -> Relative + // 5 -> Mute | Absolute + // 15 -> All capabilities + // + optional int32 volumeCapabilities = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeControlCapabilitiesDidChangeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeControlCapabilitiesDidChangeMessage.proto new file mode 100644 index 000000000..fd94e1717 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeControlCapabilitiesDidChangeMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "messages/audio/VolumeControlAvailabilityMessage.proto"; + +extend ProtocolMessage { + optional VolumeControlCapabilitiesDidChangeMessage volumeControlCapabilitiesDidChangeMessage = 68; +} + +// NOTE(RE): matches MrVolumeControlCapabilitiesDidChangeMessage.proto - no differences found +message VolumeControlCapabilitiesDidChangeMessage { + optional VolumeControlAvailabilityMessage capabilities = 1; + optional string endpointUID = 3; + optional string outputDeviceUID = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeDidChangeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeDidChangeMessage.proto new file mode 100644 index 000000000..4c2228c31 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeDidChangeMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional VolumeDidChangeMessage volumeDidChangeMessage = 56; +} + +message VolumeDidChangeMessage { + optional float volume = 1; + optional string endpointUID = 2; + optional string outputDeviceUID = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeMutedDidChangeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeMutedDidChangeMessage.proto new file mode 100644 index 000000000..1993e98cc --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/audio/VolumeMutedDidChangeMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional VolumeMutedDidChangeMessage volumeMutedDidChangeMessage = 101; +} + +// Notification that mute state changed on an output device +message VolumeMutedDidChangeMessage { + optional bool isMuted = 1; + optional string outputDeviceUID = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/ClientUpdatesConfigMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/ClientUpdatesConfigMessage.proto new file mode 100644 index 000000000..562dff11d --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/ClientUpdatesConfigMessage.proto @@ -0,0 +1,20 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional ClientUpdatesConfigMessage clientUpdatesConfigMessage = 21; +} + +message ClientUpdatesConfigMessage { + // NOTE: Fields 1 & 2 were swapped in previous HC version. Corrected per decompiled + // __MRClientUpdatesConfigurationProtobufReadFrom / writeTo__ (confirmed field numbers). + optional bool nowPlayingUpdates = 1; + optional bool artworkUpdates = 2; + optional bool volumeUpdates = 3; + optional bool keyboardUpdates = 4; + optional bool outputDeviceUpdates = 5; + optional bool systemEndpointUpdates = 6; + repeated PlayerPath subscribedPlayerPaths = 7; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/GetStateMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/GetStateMessage.proto new file mode 100644 index 000000000..e9428b4b4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/GetStateMessage.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetStateMessage getStateMessage = 8; +} + +message GetStateMessage { +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/NotificationMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/NotificationMessage.proto new file mode 100644 index 000000000..510d12239 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/NotificationMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional NotificationMessage notificationMessage = 16; +} + +message NotificationMessage { + repeated string notifications = 1; + repeated bytes userInfos = 2; + repeated PlayerPath playerPaths = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/OriginClientPropertiesMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/OriginClientPropertiesMessage.proto new file mode 100644 index 000000000..6fab80321 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/OriginClientPropertiesMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional OriginClientPropertiesMessage originClientPropertiesMessage = 87; +} + +message OriginClientPropertiesMessage { + optional double lastPlayingTimestamp = 1; + optional string devicePlaybackSessionId = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/PlayerClientPropertiesMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/PlayerClientPropertiesMessage.proto new file mode 100644 index 000000000..e3b14dedd --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/PlayerClientPropertiesMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional PlayerClientPropertiesMessage playerClientPropertiesMessage = 86; +} + +message PlayerClientPropertiesMessage { + optional PlayerPath playerPath = 1; + optional double lastPlayingTimestamp = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/RemoveClientMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/RemoveClientMessage.proto new file mode 100644 index 000000000..07ca92644 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/RemoveClientMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/NowPlayingClient.proto"; + +extend ProtocolMessage { + optional RemoveClientMessage removeClientMessage = 57; +} + +message RemoveClientMessage { + optional NowPlayingClient client = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/RemovePlayerMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/RemovePlayerMessage.proto new file mode 100644 index 000000000..92f295f98 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/RemovePlayerMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional RemovePlayerMessage removePlayerMessage = 58; +} + +message RemovePlayerMessage { + optional PlayerPath playerPath = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/SetHiliteModeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/SetHiliteModeMessage.proto new file mode 100644 index 000000000..723391e92 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/SetHiliteModeMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetHiliteModeMessage setHiliteModeMessage = 44; +} + +message SetHiliteModeMessage { + optional bool hiliteMode = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/SetReadyStateMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/SetReadyStateMessage.proto new file mode 100644 index 000000000..5971c2d22 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/SetReadyStateMessage.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetReadyStateMessage setReadyStateMessage = 41; +} + +message SetReadyStateMessage { +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/UpdateClientMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/UpdateClientMessage.proto new file mode 100644 index 000000000..687f241b2 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/UpdateClientMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/NowPlayingClient.proto"; + +extend ProtocolMessage { + optional UpdateClientMessage updateClientMessage = 59; +} + +message UpdateClientMessage { + optional NowPlayingClient client = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/client/UpdatePlayerMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/UpdatePlayerMessage.proto new file mode 100644 index 000000000..435b28bc9 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/client/UpdatePlayerMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional UpdatePlayerMessage updatePlayerMessage = 62; +} + +message UpdatePlayerMessage { + optional PlayerPath playerPath = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/ApplicationConnectionProtocolMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/ApplicationConnectionProtocolMessage.proto new file mode 100644 index 000000000..f3418261d --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/ApplicationConnectionProtocolMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "types/connection/ApplicationConnectionMessage.proto"; +import "types/connection/ApplicationConnectionContext.proto"; +import "messages/connection/CreateApplicationConnectionMessage.proto"; +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional ApplicationConnectionProtocolMessage applicationConnectionProtocolMessage = 106; +} + +message ApplicationConnectionProtocolMessage { + optional ApplicationConnectionMessage application_message = 1; + optional ApplicationConnectionContext context = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/CreateApplicationConnectionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/CreateApplicationConnectionMessage.proto new file mode 100644 index 000000000..fdcd94e2b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/CreateApplicationConnectionMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/connection/ApplicationConnectionContext.proto"; +import "types/connection/ApplicationConnectionRequestInfo.proto"; + +extend ProtocolMessage { + optional CreateApplicationConnectionMessage createApplicationConnectionMessage = 105; +} + +message CreateApplicationConnectionMessage { + optional ApplicationConnectionContext context = 1; + optional ApplicationConnectionRequestInfo requestInfo = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/CryptoPairingMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/CryptoPairingMessage.proto new file mode 100644 index 000000000..e2c967743 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/CryptoPairingMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional CryptoPairingMessage cryptoPairingMessage = 39; +} + +message CryptoPairingMessage { + optional bytes pairingData = 1; + optional int32 status = 2; + optional bool isRetrying = 3; + optional bool isUsingSystemPairing = 4; + optional int32 state = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/InvalidateApplicationConnectionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/InvalidateApplicationConnectionMessage.proto new file mode 100644 index 000000000..299bbe156 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/connection/InvalidateApplicationConnectionMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; +import "types/playback/PlayerPath.proto"; +import "protocol/ProtocolMessage.proto"; +extend ProtocolMessage { + optional InvalidateApplicationConnectionMessage invalidateApplicationConnectionMessage = 107; +} + +// Same as ApplicationConnectionContext +message InvalidateApplicationConnectionMessage { + optional string identifier = 1; + optional string service_name = 2; + optional PlayerPath destination_player_path = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/ConfigureConnectionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/ConfigureConnectionMessage.proto new file mode 100644 index 000000000..2cb5f2157 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/ConfigureConnectionMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional ConfigureConnectionMessage configureConnectionMessage = 94; +} + +message ConfigureConnectionMessage { + optional string groupID = 1; + optional string serviceName = 2; + optional string sourceOutputDeviceUID = 3; + optional string sourceOutputDeviceName = 4; + optional string destinationOutputDeviceUid = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/CreateHostedEndpointRequestMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/CreateHostedEndpointRequestMessage.proto new file mode 100644 index 000000000..ff9f235dc --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/CreateHostedEndpointRequestMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional CreateHostedEndpointRequestMessage createHostedEndpointRequestMessage = 95; +} + +// Request to create a hosted endpoint with specified output devices +message CreateHostedEndpointRequestMessage { + repeated string outputDeviceUIDs = 1; +} +// CreatedHostedEndpointRequestMessage.ts +// CreateHostedEndpointRequestMessage.ts diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/CreateHostedEndpointResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/CreateHostedEndpointResponseMessage.proto new file mode 100644 index 000000000..2826a5cdd --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/CreateHostedEndpointResponseMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional CreateHostedEndpointResponseMessage createHostedEndpointResponseMessage = 96; +} + +// Response after creating a hosted endpoint +message CreateHostedEndpointResponseMessage { + optional string groupUID = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DeviceInfoMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DeviceInfoMessage.proto new file mode 100644 index 000000000..ccfdcce3b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DeviceInfoMessage.proto @@ -0,0 +1,78 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "foundation/Common.proto"; +import "types/session/GroupSessionToken.proto"; + +extend ProtocolMessage { + optional DeviceInfoMessage deviceInfoMessage = 20; +} + +message PreferredEncoding { + enum Enum { + Default = 0; + JSON = 1; + } +} + +message DeviceInfoMessage { + optional string uniqueIdentifier = 1; + required string name = 2; + optional string localizedModelName = 3; + optional string systemBuildVersion = 4; + optional string applicationBundleIdentifier = 5; + optional string applicationBundleVersion = 6; + optional int32 protocolVersion = 7; + optional uint32 lastSupportedMessageType = 8; + optional bool supportsSystemPairing = 9; + optional bool allowsPairing = 10; + optional bool connected = 11; + optional string systemMediaApplication = 12; + optional bool supportsACL = 13; + optional bool supportsSharedQueue = 14; + optional bool supportsExtendedMotion = 15; + optional bytes bluetoothAddress = 16; + optional uint32 sharedQueueVersion = 17; + optional string deviceUID = 19; + optional string managedConfigDeviceID = 20; + optional DeviceClass.Enum deviceClass = 21; + optional uint32 logicalDeviceCount = 22; + optional bool tightlySyncedGroup = 23; + optional bool isProxyGroupPlayer = 24; + optional string tightSyncUID = 25; + optional string groupUID = 26; + optional string groupName = 27; + repeated DeviceInfoMessage groupedDevices = 28; + optional bool isGroupLeader = 29; + optional bool isAirplayActive = 30; + optional string systemPodcastApplication = 31; + optional string senderDefaultGroupUID = 32; + repeated string airplayReceivers = 33; + optional string linkAgent = 34; + optional string clusterID = 35; + optional string clusterLeaderID = 36; + optional uint32 clusterType = 37; + optional bool isClusterAware = 38; + optional string modelID = 39; + optional bool supportsMultiplayer = 40; + optional string routingContextID = 41; + optional string airPlayGroupID = 42; + optional string systemBooksApplication = 43; + // Directly clustered peer devices -- Guess + repeated DeviceInfoMessage clusteredDevices = 44; + optional uint32 parentGroupContainsDiscoverableGroupLeader = 45; + optional uint32 groupContainsDiscoverableGroupLeader = 46; + optional uint32 lastKnownClusterType = 47; + // All devices participating in the cluster (including indirect/proxy) -- Guess + repeated DeviceInfoMessage allClusteredDevices = 48; + optional bool supportsOutputContextSync = 49; + optional string computerName = 50; + optional uint32 configuredClusterSize = 51; + optional PreferredEncoding.Enum preferredEncoding = 52; + optional GroupSessionToken groupSessionToken = 53; + optional DeviceInfoMessage leaderDeviceInfo = 54; + optional bool isClusterLeader = 55; + optional string activeSystemEndpointUid = 56; + optional bool isEligibleForHostingGroupSessionExcludingAcknowledgements = 57; + optional bool parentGroupSupportsGroupCohesion = 58; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DiscoveryUpdateEndpointsMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DiscoveryUpdateEndpointsMessage.proto new file mode 100644 index 000000000..fadae893e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DiscoveryUpdateEndpointsMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/MRAVEndpointDescriptor.proto"; +import "types/device/DiscoverySessionConfiguration.proto"; + +extend ProtocolMessage { + optional DiscoveryUpdateEndpointsMessage discoveryUpdateEndpointsMessage = 90; +} + +// From: MRDiscoveryUpdateEndpointsProtobufMessage +message DiscoveryUpdateEndpointsMessage { + repeated MRAVEndpointDescriptor endpoints = 1; + optional DiscoverySessionConfiguration configuration = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DiscoveryUpdateOutputDevicesMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DiscoveryUpdateOutputDevicesMessage.proto new file mode 100644 index 000000000..7382b98d5 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/DiscoveryUpdateOutputDevicesMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/AVOutputDeviceDescriptor.proto"; +import "types/device/DiscoverySessionConfiguration.proto"; + +extend ProtocolMessage { + optional DiscoveryUpdateOutputDevicesMessage discoveryUpdateOutputDevicesMessage = 91; +} + +// Discovery update for output devices +message DiscoveryUpdateOutputDevicesMessage { + repeated AVOutputDeviceDescriptor outputDevices = 1; + optional DiscoverySessionConfiguration configuration = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/ModifyOutputContextRequestMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/ModifyOutputContextRequestMessage.proto new file mode 100644 index 000000000..0ac232d9d --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/ModifyOutputContextRequestMessage.proto @@ -0,0 +1,28 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/GroupTopologyModificationRequest.proto"; +extend ProtocolMessage { + optional ModifyOutputContextRequestMessage modifyOutputContextRequestMessage = 52; +} + + + +message ModifyOutputContextRequestMessage { + enum Type { + Unknown = 0; + SharedAudioPresentation = 1; + SharedSystemAudio = 2; + SharedSystemScreen = 3; + iTunesAudio = 4; + Auxiliary = 5; + } + optional Type type = 1; + repeated string addingDevices = 2; + repeated string removingDevices = 3; + repeated string settingDevices = 4; + repeated string clusterAwareAddingDevices = 5; + repeated string clusterAwareRemovingDevices = 6; + repeated string clusterAwareSettingDevices = 7; + optional GroupTopologyModificationRequest request = 8; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PresentRouteAuthorizationStatusMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PresentRouteAuthorizationStatusMessage.proto new file mode 100644 index 000000000..b45263c01 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PresentRouteAuthorizationStatusMessage.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/AVOutputDeviceDescriptor.proto"; +extend ProtocolMessage { + optional PresentRouteAuthorizationStatusMessage presentRouteAuthorizationStatusMessage = 65; +} + +message PresentRouteAuthorizationStatusMessage { + enum PresentRouteAuthorizationStatus { + OK = 0; + AuthenticationRequired = 1; + AuthenticationFailed = 2; + Busy = 3; + OutOfRange = 4; + UnknownError = 5; + NotConnected = 6; + IPAliasingNotSupported = 7; + RelayFailed2GHzNetwork = 8; + RelayFailedMultiDFSNetwork = 9; + } + optional AVOutputDeviceDescriptor route = 1; + optional PresentRouteAuthorizationStatus status = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PromptForRouteAuthorizationMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PromptForRouteAuthorizationMessage.proto new file mode 100644 index 000000000..3ddcb0748 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PromptForRouteAuthorizationMessage.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/AVOutputDeviceDescriptor.proto"; + +extend ProtocolMessage { + optional PromptForRouteAuthorizationMessage promptForRouteAuthorizationMessage = 63; +} + +message PromptForRouteAuthorizationMessage { + enum InputType { + None = 0; + Alphabet = 1; + Number = 2; + Email = 3; + } + optional AVOutputDeviceDescriptor route = 1; + optional InputType inputType = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PromptForRouteAuthorizationResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PromptForRouteAuthorizationResponseMessage.proto new file mode 100644 index 000000000..ca33744e1 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/PromptForRouteAuthorizationResponseMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional PromptForRouteAuthorizationResponseMessage promptForRouteAuthorizationResponseMessage = 64; +} + +message PromptForRouteAuthorizationResponseMessage { + optional string response = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RemoveEndpointsMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RemoveEndpointsMessage.proto new file mode 100644 index 000000000..f026295af --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RemoveEndpointsMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RemoveEndpointsMessage removeEndpointsMessage = 84; +} + +message RemoveEndpointsMessage { + repeated string endpointUIDs = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RemoveOutputDevicesMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RemoveOutputDevicesMessage.proto new file mode 100644 index 000000000..1a9a30993 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RemoveOutputDevicesMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RemoveOutputDevicesMessage removeOutputDevicesMessage = 70; +} + +message RemoveOutputDevicesMessage { + repeated string outputDeviceUIDs = 1; + optional string endpointUID = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RequestGroupSessionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RequestGroupSessionMessage.proto new file mode 100644 index 000000000..023439fe6 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/RequestGroupSessionMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; +import "types/session/RequestDetails.proto"; +import "protocol/ProtocolMessage.proto"; + + + +extend ProtocolMessage { + optional RequestGroupSessionMessage requestGroupSessionMessage = 104; +} + +message RequestGroupSessionMessage { + optional RequestDetails details = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/SetConnectionStateMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/SetConnectionStateMessage.proto new file mode 100644 index 000000000..6755cd171 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/SetConnectionStateMessage.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetConnectionStateMessage setConnectionStateMessage = 42; +} + +message SetConnectionStateMessage { + enum ConnectionState { + None = 0; + Connecting = 1; + Connected = 2; + Disconnected = 3; + } + + optional ConnectionState state = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/SetDiscoveryModeMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/SetDiscoveryModeMessage.proto new file mode 100644 index 000000000..0f14fa448 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/SetDiscoveryModeMessage.proto @@ -0,0 +1,20 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/DiscoverySessionConfiguration.proto"; +extend ProtocolMessage { + optional SetDiscoveryModeMessage setDiscoveryModeMessage = 82; +} + +message SetDiscoveryModeMessage { + enum Mode { + Disabled = 0; + Presence = 1; + InfraOnly = 2; + Detailed = 3; + } + optional Mode mode = 1; + // Bitmask of Features enum values (Audio=1, Screen=2, Video=4, RemoteControl=8, Companion=256) + optional int32 features = 2; + optional DiscoverySessionConfiguration configuration = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateActiveSystemEndpointMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateActiveSystemEndpointMessage.proto new file mode 100644 index 000000000..4184f339d --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateActiveSystemEndpointMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/UpdateActiveSystemEndpointRequest.proto"; +extend ProtocolMessage { + optional UpdateActiveSystemEndpointMessage updateActiveSystemEndpointMessage = 81; +} + +message UpdateActiveSystemEndpointMessage { + optional UpdateActiveSystemEndpointRequest request = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateEndPointsMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateEndPointsMessage.proto new file mode 100644 index 000000000..41207164c --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateEndPointsMessage.proto @@ -0,0 +1,26 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/device/AVOutputDeviceDescriptor.proto"; + +extend ProtocolMessage { + optional UpdateEndPointsMessage updateEndPointsMessage = 83; +} + +message AVEndpointDescriptor { + optional string name = 1; + optional string uniqueIdentifier = 2; + repeated AVOutputDeviceDescriptor outputDevices = 3; + optional AVOutputDeviceDescriptor designatedGroupLeader = 4; + optional bool isLocalEndpoint = 5; + optional string instanceIdentifier = 6; + optional bool isProxyGroupPlayer = 7; + optional int32 connectionType = 8; + optional bool canModifyGroupMembership = 9; + repeated AVOutputDeviceDescriptor personalOutputDevices = 10; +} + +message UpdateEndPointsMessage { + optional AVEndpointDescriptor endpoints = 1; // NOTE(RE): cardinality is "repeated" in RE proto + optional int32 endpointFeatures = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateOutputDeviceMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateOutputDeviceMessage.proto new file mode 100644 index 000000000..cab49656b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/UpdateOutputDeviceMessage.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "foundation/Common.proto"; +import "types/device/AVOutputDeviceDescriptor.proto"; + +extend ProtocolMessage { + optional UpdateOutputDeviceMessage updateOutputDeviceMessage = 69; +} + + +message UpdateOutputDeviceMessage { + repeated AVOutputDeviceDescriptor outputDevices = 1; + optional string endpointUID = 2; + repeated AVOutputDeviceDescriptor clusterAwareOutputDevices = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/device/WakeDeviceMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/WakeDeviceMessage.proto new file mode 100644 index 000000000..a45a3899b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/device/WakeDeviceMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional WakeDeviceMessage wakeDeviceMessage = 45; +} + +message WakeDeviceMessage { + +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GameControllerMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GameControllerMessage.proto new file mode 100644 index 000000000..473329d10 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GameControllerMessage.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +import "types/input/GameControllerButtons.proto"; +import "types/input/GameControllerDigitizer.proto"; +import "types/input/GameControllerMotion.proto"; + +extend ProtocolMessage { + optional GameControllerMessage gameControllerMessage = 23; +} + +message GameControllerMessage { + optional uint64 controllerID = 1; + optional GameControllerMotion motion = 2; + optional GameControllerButtons buttons = 3; + optional GameControllerDigitizer digitizer = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GameControllerPropertiesMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GameControllerPropertiesMessage.proto new file mode 100644 index 000000000..68ee3ac88 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GameControllerPropertiesMessage.proto @@ -0,0 +1,20 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GameControllerPropertiesMessage gameControllerPropertiesMessage = 40; +} + +message GameControllerProperties { + optional uint32 playerIndex = 1; + optional string vendorName = 2; + optional int32 buttonAUpDelay = 3; + optional int32 profile = 4; + optional bool supportsExtendedMotion = 5; +} + +message GameControllerPropertiesMessage { + optional uint64 controllerID = 1; + optional GameControllerProperties properties = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GetKeyboardSessionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GetKeyboardSessionMessage.proto new file mode 100644 index 000000000..93d34e0c0 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GetKeyboardSessionMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional string getKeyboardSessionMessage = 29; +} + +message GetKeyboardSessionMessage { + +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GetRemoteTextInputSessionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GetRemoteTextInputSessionMessage.proto new file mode 100644 index 000000000..ffebbd249 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/GetRemoteTextInputSessionMessage.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GetRemoteTextInputSessionMessage getRemoteTextInputSessionMessage = 72; +} + +message GetRemoteTextInputSessionMessage { +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/KeyboardMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/KeyboardMessage.proto new file mode 100644 index 000000000..8db6c3bdc --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/KeyboardMessage.proto @@ -0,0 +1,88 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional KeyboardMessage keyboardMessage = 28; +} + +message KeyboardState { + enum Enum { + Unknown = 0; + NotEditing = 1; + DidBeginEditing = 2; + Editing = 3; + TextDidChange = 4; + DidEndEditing = 5; + Response = 6; + } +} + +message AutocapitalizationType { + enum Enum { + None = 0; + Words = 1; + Sentences = 2; + AllCharacters = 3; + } +} + +message KeyboardType { + enum Enum { + Default = 0; + ASCII_Capable = 1; + NumbersAndPunctuation = 2; + URL = 3; + NumberPad = 4; + PhonePad = 5; + NamePhonePad = 6; + EmailAddress = 7; + DecimalPad = 8; + Twitter = 9; + WebSearch = 10; + Alphanet = 11; + PasscodePad = 12; + } +} + +message ReturnKeyType { + enum Enum { + Default = 0; + Go = 1; + Google = 2; + Join = 3; + Next = 4; + Route = 5; + Search = 6; + Send = 7; + Yahoo = 8; + Done = 9; + EmergencyCall = 10; + Continue = 11; + } +} + +message TextInputTraits { + optional AutocapitalizationType.Enum autocapitalizationType = 1; + optional KeyboardType.Enum keyboardType = 2; + optional ReturnKeyType.Enum returnKeyType = 3; + optional bool autocorrection = 4; + optional bool spellchecking = 5; + optional bool enablesReturnKeyAutomatically = 6; + optional bool secureTextEntry = 7; + optional uint64 validTextRangeLocation = 8; + optional uint64 validTextRangeLength = 9; + repeated uint64 pINEntrySeparatorIndexes = 10; +} + +message TextEditingAttributes { + optional string title = 1; + optional string prompt = 2; + optional TextInputTraits inputTraits = 3; +} + +message KeyboardMessage { + optional KeyboardState.Enum state = 1; + optional TextEditingAttributes attributes = 3; + optional bytes encryptedTextCyphertext = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/MicrophoneConnectionRequestMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/MicrophoneConnectionRequestMessage.proto new file mode 100644 index 000000000..f0b943ad0 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/MicrophoneConnectionRequestMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/session/RequestDetails.proto"; + +extend ProtocolMessage { + optional MicrophoneConnectionRequestMessage microphoneConnectionRequestMessage = 108; +} + +message MicrophoneConnectionRequestMessage { + optional RequestDetails details = 1; + optional string rapportIdentifier = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/MicrophoneConnectionResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/MicrophoneConnectionResponseMessage.proto new file mode 100644 index 000000000..9233ddafa --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/MicrophoneConnectionResponseMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional MicrophoneConnectionResponseMessage microphoneConnectionResponseMessage = 109; +} + +message MicrophoneConnectionResponseMessage { + optional uint32 result = 1; + optional string rapportIdentifier = 2; + optional bytes pairingData = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterForGameControllerEventsMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterForGameControllerEventsMessage.proto new file mode 100644 index 000000000..bd9a6b124 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterForGameControllerEventsMessage.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RegisterForGameControllerEventsMessage registerForGameControllerEventsMessage = 27; +} + +message RegisterForGameControllerEventsMessage { + enum InputModeFlags { + None = 0; + Motion = 1; + Buttons = 2; + Digitizer = 3; + } + + optional InputModeFlags inputModeFlags = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterGameControllerMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterGameControllerMessage.proto new file mode 100644 index 000000000..35cce80db --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterGameControllerMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "messages/input/GameControllerPropertiesMessage.proto"; +extend ProtocolMessage { + optional RegisterGameControllerMessage registerGameControllerMessage = 24; +} + +message RegisterGameControllerMessage { + optional GameControllerProperties properties = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterGameControllerResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterGameControllerResponseMessage.proto new file mode 100644 index 000000000..acbcb0f1a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterGameControllerResponseMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RegisterGameControllerResponseMessage registerGameControllerResponseMessage = 25; +} + +message RegisterGameControllerResponseMessage { + optional uint64 controllerID = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterHIDDeviceMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterHIDDeviceMessage.proto new file mode 100644 index 000000000..9bb9570e7 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterHIDDeviceMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/input/VirtualTouchDeviceDescriptorMessage.proto"; + +extend ProtocolMessage { + optional RegisterHIDDeviceMessage registerHIDDeviceMessage = 11; +} + +message RegisterHIDDeviceMessage { + optional VirtualTouchDeviceDescriptor deviceDescriptor = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterHIDDeviceResultMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterHIDDeviceResultMessage.proto new file mode 100644 index 000000000..0173a7138 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RegisterHIDDeviceResultMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RegisterHIDDeviceResultMessage registerHIDDeviceResultMessage = 12; +} + +message RegisterHIDDeviceResultMessage { + optional int32 errorCode = 1; + optional int32 deviceIdentifier = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RemoteTextInputMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RemoteTextInputMessage.proto new file mode 100644 index 000000000..f6cffb7ca --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/RemoteTextInputMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RemoteTextInputMessage remoteTextInputMessage = 71; +} + +message RemoteTextInputMessage { + optional double timestamp = 1; + optional uint64 version = 2; + optional bytes data = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendButtonEventMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendButtonEventMessage.proto new file mode 100644 index 000000000..7f1184487 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendButtonEventMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendButtonEventMessage sendButtonEventMessage = 43; +} + +message SendButtonEventMessage { + optional uint32 usagePage = 1; + optional uint32 usage = 2; + optional bool buttonDown = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendHIDEventMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendHIDEventMessage.proto new file mode 100644 index 000000000..4189e875c --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendHIDEventMessage.proto @@ -0,0 +1,41 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendHIDEventMessage sendHIDEventMessage = 13; +} + +message SendHIDEventMessage { + // This data corresponds to a "keyboardEvent" in IOHIDEvent.h encoded as raw + // data. Here is one source: + // + // https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-308/IOHIDFamily/IOHIDEvent.h.auto.html + // + // The interesting parts are: + // - usagePage (UInt32) + // - usage (Uint32) + // - down (bool) + // + // The parameters usagePage and usage corresponds to the key being pressed. + // It is mapped to the USB HID values, which can be found here: + // + // https://github.com/Daij-Djan/DDHidLib/blob/master/usb_hid_usages.txt + // + // Pressing left key would for instance map to usagePage=0x01, usage=0x8B. In + // the hid data, these values are stored as big endian uint16 values in the + // mentioned order. So the same example would be: 0x0001008B0001, assuming + // down = true (key being pressed). For each key press, the same usagePage + // and usage are sent with down=true and down=false (key down + key up). + // + // There is a bit of magic in the raw data that's just not decoded yet, but + // that doesn't matter. Just use this and it will work: + // + // 438922cf0802000000000000000000000100000000000000020000002000000003000000010000000000000000000000000001000000 + // + // corresponds to the values above, e.g. 0001008B0001. The first 8 + // bytes is a timestamp (mach AbsoluteTime). It's a bit tricky to derive but + // tvOS seems to accept old timestamps here. So it's probably fine to send + // anything. + optional bytes hidEventData = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendHIDReportMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendHIDReportMessage.proto new file mode 100644 index 000000000..d4521b54b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendHIDReportMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendHIDReportMessage sendHIDReportMessage = 14; +} + +message SendHIDReportMessage { + optional string virtualDeviceID = 1; + optional bytes report = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendPackedVirtualTouchEventMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendPackedVirtualTouchEventMessage.proto new file mode 100644 index 000000000..874de0fab --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendPackedVirtualTouchEventMessage.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendPackedVirtualTouchEventMessage sendPackedVirtualTouchEventMessage = 47; +} + +message SendPackedVirtualTouchEventMessage { + + // Corresponds to "phase" in data + + // The packed version of VirtualTouchEvent contains X, Y, phase, deviceID + // and finger stored as a byte array. Each value is written as 16bit little + // endian integers. + optional bytes data = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendVirtualTouchEventMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendVirtualTouchEventMessage.proto new file mode 100644 index 000000000..a09459890 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/SendVirtualTouchEventMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/input/VirtualTouchEvent.proto"; +extend ProtocolMessage { + optional SendVirtualTouchEventMessage sendVirtualTouchEventMessage = 15; +} + +message SendVirtualTouchEventMessage { + optional uint64 virtualDeviceID = 1; + optional VirtualTouchEvent event = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/TextInputMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/TextInputMessage.proto new file mode 100644 index 000000000..1b35d9991 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/TextInputMessage.proto @@ -0,0 +1,23 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional TextInputMessage textInputMessage = 30; +} + +message ActionType { + enum Enum { + Unknown = 0; + Insert = 1; + Set = 2; + Delete = 3; + ClearAction = 4; + } +} + +message TextInputMessage { + optional double timestamp = 1; + optional string text = 2; + optional ActionType.Enum actionType = 3; // NOTE(RE): type is int32 in RE proto +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/input/UnregisterGameControllerMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/UnregisterGameControllerMessage.proto new file mode 100644 index 000000000..7a3463a1c --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/input/UnregisterGameControllerMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional UnregisterGameControllerMessage unregisterGameControllerMessage = 26; +} + +message UnregisterGameControllerMessage { + optional uint64 controllerID = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/ContentItemsChangedNotificationMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/ContentItemsChangedNotificationMessage.proto new file mode 100644 index 000000000..d0bf995ca --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/ContentItemsChangedNotificationMessage.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional ContentItemsChangedNotificationMessage contentItemsChangedNotificationMessage = 17; +} + +message ContentItemsChangedNotificationMessage { +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackQueueRequestMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackQueueRequestMessage.proto new file mode 100644 index 000000000..12a47cd48 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackQueueRequestMessage.proto @@ -0,0 +1,36 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlaybackQueueContext.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional PlaybackQueueRequestMessage playbackQueueRequestMessage = 37; +} + +message PlaybackQueueRequestMessage { + optional int32 location = 1; + optional int32 length = 2; + optional bool includeMetadata = 3; + optional double artworkWidth = 4; + optional double artworkHeight = 5; + optional bool includeLyrics = 6; + optional bool includeSections = 7; + optional bool includeInfo = 8; + optional bool includeLanguageOptions = 9; + optional PlaybackQueueContext context = 10; + optional string requestID = 11; + repeated string contentItemIdentifiers = 12; + optional bool returnContentItemAssetsInUserCompletion = 13; + optional PlayerPath playerPath = 14; + optional int32 cachingPolicy = 15; + optional string label = 16; + optional bool isLegacyNowPlayingInfoRequest = 17; + optional bool includeParticipants = 18; + optional bool includeAvailableArtworkFormats = 19; + repeated string requestedArtworkFormats = 20; + repeated string requestedRemoteArtworkFormats = 21; + optional bool includeAlignments = 22; + repeated string requestedAnimatedArtworkPreviewFrameFormats = 23; + repeated string requestedAnimatedArtworkAssetUrlFormats = 24; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateBeginMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateBeginMessage.proto new file mode 100644 index 000000000..e6d951d67 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateBeginMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; +import "messages/playback/PlaybackSessionRequestMessage.proto"; + +extend ProtocolMessage { + optional PlaybackSessionMigrateBeginMessage playbackSessionMigrateBeginMessage = 79; +} + +message PlaybackSessionMigrateBeginMessage { + optional PlaybackSessionRequestMessage request = 1; + optional PlayerPath playerPath = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateEndMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateEndMessage.proto new file mode 100644 index 000000000..9af14d9e4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateEndMessage.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; +import "messages/playback/SendCommandResultMessage.proto"; +import "types/playback/PlaybackSessionRequest.proto"; +import "foundation/Error.proto"; +extend ProtocolMessage { + optional PlaybackSessionMigrateEndMessage playbackSessionMigrateEndMessage = 80; +} + +message PlaybackSessionMigrateEndMessage { + optional PlaybackSessionRequest request = 1; + optional PlayerPath playerPath = 2; + optional int64 errorCode = 3; + optional string errorDescription = 4; + optional Error error = 5; + optional SendCommandResultStatus setPlaybackSessionCommandStatus = 6; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigratePostMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigratePostMessage.proto new file mode 100644 index 000000000..39535a626 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigratePostMessage.proto @@ -0,0 +1,21 @@ + + +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlaybackSessionMigrateRequest.proto"; +import "foundation/Dictionary.proto"; +import "foundation/Error.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional PlaybackSessionMigratePostMessage playbackSessionMigratePostMessage = 110; +} + +message PlaybackSessionMigratePostMessage { + optional PlaybackSessionMigrateRequest request = 1; + optional PlayerPath playerPath = 2; + optional string setPlaybackSessionCommandId = 3; + optional Dictionary metrics = 4; + optional Error error = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateRequestMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateRequestMessage.proto new file mode 100644 index 000000000..7a89f152a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateRequestMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; +import "types/playback/PlaybackSession.proto"; +import "types/playback/PlaybackSessionMigrateRequest.proto"; +extend ProtocolMessage { + optional PlaybackSessionMigrateRequestMessage playbackSessionMigrateRequestMessage = 76; +} + +message PlaybackSessionMigrateRequestMessage { + optional PlaybackSession playbackSession = 1; + optional PlaybackSessionMigrateRequest request = 2; + optional PlayerPath playerPath = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateResponseMessage.proto new file mode 100644 index 000000000..68b3d52cd --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionMigrateResponseMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlaybackSessionMigrateRequest.proto"; +extend ProtocolMessage { + optional PlaybackSessionMigrateResponseMessage playbackSessionMigrateResponseMessage = 77; +} + +message PlaybackSessionMigrateResponseMessage { + optional PlaybackSessionMigrateRequest request = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionRequestMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionRequestMessage.proto new file mode 100644 index 000000000..710975c91 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionRequestMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; +import "types/playback/PlaybackSessionRequest.proto"; +extend ProtocolMessage { + optional PlaybackSessionRequestMessage playbackSessionRequestMessage = 73; +} + +message PlaybackSessionRequestMessage { + optional PlaybackSessionRequest request = 1; + optional PlayerPath playerPath = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionResponseMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionResponseMessage.proto new file mode 100644 index 000000000..ab76ceab7 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlaybackSessionResponseMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlaybackSession.proto"; +import "types/playback/PlaybackSessionMigrateRequest.proto"; + +extend ProtocolMessage { + optional PlaybackSessionResponseMessage playbackSessionResponseMessage = 74; +} + +message PlaybackSessionResponseMessage { + optional PlaybackSession playbackSession = 1; + optional PlaybackSessionMigrateRequest request = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlayerClientParticipantsUpdateMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlayerClientParticipantsUpdateMessage.proto new file mode 100644 index 000000000..5aaa9e271 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/PlayerClientParticipantsUpdateMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; +import "types/playback/PlaybackQueueParticipant.proto"; + +extend ProtocolMessage { + optional PlayerClientParticipantsUpdateMessage playerClientParticipantsUpdateMessage = 103; +} + +// Update participants in a playback queue for a specific player +message PlayerClientParticipantsUpdateMessage { + optional PlayerPath playerPath = 1; + repeated PlaybackQueueParticipant participants = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendCommandMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendCommandMessage.proto new file mode 100644 index 000000000..c0dd3837e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendCommandMessage.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/media/CommandInfo.proto"; +import "types/media/CommandOptions.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional SendCommandMessage sendCommandMessage = 6; +} + +message SendCommandMessage { + optional Command command = 1; + optional CommandOptions options = 2; + optional PlayerPath playerPath = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendCommandResultMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendCommandResultMessage.proto new file mode 100644 index 000000000..1fc315131 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendCommandResultMessage.proto @@ -0,0 +1,110 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; +import "foundation/Error.proto"; +import "types/media/SendCommandResultHandlerDialog.proto"; + +extend ProtocolMessage { + optional SendCommandResultMessage sendCommandResultMessage = 7; +} + +message SendError { + enum Enum { + NoError = 0; + ApplicationNotFound = 1; + ConnectionFailed = 2; + Ignored = 3; + CouldNotLaunchApplication = 4; + TimedOut = 5; + OriginDoesNotExist = 6; + InvalidOptions = 7; + NoCommandHandlers = 8; + ApplicationNotInstalled = 9; + NotSupported = 10; + } +} + +message HandlerReturnStatus { + enum Enum { + Success = 0; + NoSuchContent = 1; + CommandFailed = 2; + UIKitLegacy = 3; + NoActionableNowPlayingItem = 10; + DeviceNotFound = 20; + SkipAdProhibited = 100; + QueueIsUserCurated = 101; + UserModifiedQueueDisable = 102; + UserQueueModificationNotSupportedForCurrentItem = 103; + SubscriptionRequiredForSharedQueue = 104; + InsertionPositionNotSpecified = 105; + InvalidInsertionPosition = 106; + RequestParametersOutOfBounds = 107; + SkipLimitReached = 108; // 0x6C + CannotModifyQueueWithPlaybackTokenItems = 109; + AuthenticationFailure = 401; + UnsupportedCommand = 404; + MediaServicesUnavailable = 501; + Timeout = 555; + } +} + +message SendCommandStatusCode { + enum Enum { + Success = 0; + NoSuchContent = 1; + CommandFailed = 2; + UIKitLegacy = 3; + NoActionableNowPlayingItem = 10; + DeviceNotFound = 20; + SkipAdProhibited = 100; + QueueIsUserCurated = 101; + UserModifiedQueueDisable = 102; + UserQueueModificationNotSupportedForCurrentItem = 103; + SubscriptionRequiredForSharedQueue = 104; + InsertionPositionNotSpecified = 105; + InvalidInsertionPosition = 106; + RequestParametersOutOfBounds = 107; + SkipLimitReached = 108; // 0x6C + CannotModifyQueueWithPlaybackTokenItems = 109; + AuthenticationFailure = 401; + UnsupportedCommand = 404; + MediaServicesUnavailable = 501; + Timeout = 555; + } +} + +message SendCommandResultType { + enum Enum { + Dialog = 1; + Error = 2; + Custom = 999; + } +} + +message SendCommandResultStatus { + optional SendCommandStatusCode.Enum statusCode = 1; + optional SendCommandResultType.Enum type = 2; + optional SendCommandResultHandlerDialog dialog = 3; + optional Error error = 4; + optional bytes customData = 5; + optional string customDataType = 6; +} + +message SendCommandResult { + optional PlayerPath playerPath = 1; + optional SendError.Enum sendError = 2; + repeated SendCommandResultStatus statuses = 3; + optional string sendErrorDescription = 4; + optional Error error = 5; +} + +message SendCommandResultMessage { + optional SendError.Enum sendError = 1; + optional HandlerReturnStatus.Enum handlerReturnStatus = 2; + repeated bytes handlerReturnStatusDatas = 3; + optional string commandID = 4; + optional PlayerPath playerPath = 5; + optional SendCommandResult commandResult = 6; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendLyricsEventMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendLyricsEventMessage.proto new file mode 100644 index 000000000..efeda2dea --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SendLyricsEventMessage.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendLyricsEventMessage sendLyricsEventMessage = 48; +} + +message SendLyricsEventMessage { +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetArtworkMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetArtworkMessage.proto new file mode 100644 index 000000000..3a33501bc --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetArtworkMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetArtworkMessage setArtworkMessage = 10; +} + +message SetArtworkMessage { + optional bytes jpegData = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetDefaultSupportedCommandsMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetDefaultSupportedCommandsMessage.proto new file mode 100644 index 000000000..c2a8e9ca5 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetDefaultSupportedCommandsMessage.proto @@ -0,0 +1,27 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/media/NowPlayingInfo.proto"; +import "types/playback/PlaybackQueue.proto"; +import "types/media/SupportedCommands.proto"; +import "types/playback/PlaybackQueueCapabilities.proto"; +import "types/playback/PlayerPath.proto"; +import "messages/playback/PlaybackQueueRequestMessage.proto"; +import "foundation/Common.proto"; + +extend ProtocolMessage { + optional SetDefaultSupportedCommandsMessage setDefaultSupportedCommandsMessage = 75; +} + +message SetDefaultSupportedCommandsMessage { + optional NowPlayingInfo nowPlayingInfo = 1; + optional SupportedCommands supportedCommands = 2; + optional PlaybackQueue playbackQueue = 3; + optional string displayID = 4; + optional string displayName = 5; + optional PlaybackState.Enum playbackState = 6; + optional PlaybackQueueCapabilities playbackQueueCapabilities = 8; + optional PlayerPath playerPath = 9; + optional PlaybackQueueRequestMessage request = 10; + optional double playbackStateTimestamp = 11; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetNowPlayingClientMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetNowPlayingClientMessage.proto new file mode 100644 index 000000000..cae317326 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetNowPlayingClientMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/NowPlayingClient.proto"; + +extend ProtocolMessage { + optional SetNowPlayingClientMessage setNowPlayingClientMessage = 50; +} + +message SetNowPlayingClientMessage { + optional NowPlayingClient client = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetNowPlayingPlayerMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetNowPlayingPlayerMessage.proto new file mode 100644 index 000000000..103ac68bc --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetNowPlayingPlayerMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional SetNowPlayingPlayerMessage setNowPlayingPlayerMessage = 51; +} + +message SetNowPlayingPlayerMessage { + optional PlayerPath playerPath = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetStateMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetStateMessage.proto new file mode 100644 index 000000000..7f40dbd1e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/SetStateMessage.proto @@ -0,0 +1,27 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/media/NowPlayingInfo.proto"; +import "types/playback/PlaybackQueue.proto"; +import "types/media/SupportedCommands.proto"; +import "types/playback/PlaybackQueueCapabilities.proto"; +import "types/playback/PlayerPath.proto"; +import "messages/playback/PlaybackQueueRequestMessage.proto"; +import "foundation/Common.proto"; + +extend ProtocolMessage { + optional SetStateMessage setStateMessage = 9; +} + +message SetStateMessage { + optional NowPlayingInfo nowPlayingInfo = 1; + optional SupportedCommands supportedCommands = 2; + optional PlaybackQueue playbackQueue = 3; + optional string displayID = 4; + optional string displayName = 5; + optional PlaybackState.Enum playbackState = 6; // NOTE(RE): type is int32 in RE proto + optional PlaybackQueueCapabilities playbackQueueCapabilities = 8; + optional PlayerPath playerPath = 9; + optional PlaybackQueueRequestMessage request = 10; + optional double playbackStateTimestamp = 11; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/UpdateContentItemArtworkMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/UpdateContentItemArtworkMessage.proto new file mode 100644 index 000000000..df0a86ae4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/UpdateContentItemArtworkMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/media/ContentItem.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional UpdateContentItemArtworkMessage updateContentItemArtworkMessage = 61; +} + +message UpdateContentItemArtworkMessage { + repeated ContentItem contentItems = 1; + optional PlayerPath playerPath = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/UpdateContentItemMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/UpdateContentItemMessage.proto new file mode 100644 index 000000000..e8a7b0322 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/playback/UpdateContentItemMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; +import "types/media/ContentItem.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional UpdateContentItemMessage updateContentItemMessage = 60; +} + +message UpdateContentItemMessage { + repeated ContentItem contentItems = 1; + optional PlayerPath playerPath = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/protocol/GenericMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/protocol/GenericMessage.proto new file mode 100644 index 000000000..78fc9f5e2 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/protocol/GenericMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "protocol/ProtocolMessage.proto"; + +extend ProtocolMessage { + optional GenericMessage genericMessage = 46; +} + +message GenericMessage { + optional string key = 1; + optional bytes value = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/messages/protocol/TransactionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/messages/protocol/TransactionMessage.proto new file mode 100644 index 000000000..9a8b00cd0 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/messages/protocol/TransactionMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "types/transaction/TransactionPackets.proto"; +import "protocol/ProtocolMessage.proto"; +import "types/playback/PlayerPath.proto"; + +extend ProtocolMessage { + optional TransactionMessage transactionMessage = 38; +} + +message TransactionMessage { + optional uint64 name = 1; + optional TransactionPackets packets = 2; + optional PlayerPath playerPath = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/protocol/ProtocolMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/protocol/ProtocolMessage.proto new file mode 100644 index 000000000..a45c5ebe0 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/protocol/ProtocolMessage.proto @@ -0,0 +1,192 @@ +syntax = "proto2"; + +message ErrorCode { + enum Enum { + NoError = 0; + UnknownError = 1; + InvalidOperation = 2; + OperationNotPermitted = 3; + ClientDoesNotExist = 4; + OriginDoesNotExist = 5; + UnsupportedOperation = 6; + FailedToSetPickedRoute = 7; + FailedToRegisterCustomOrigin = 8; + FailedToRemoveCustomOrigin = 9; + TheApplicationActivityDoesNotExist = 10; + TheAppHasNotSetupABrowsableContentEndpoint = 11; + TheRequestedBrowsableContentApiIsNotSupportedByTheApplication = 12; + TheNotficationHasNotBeenWhitelistedByTheServer = 13; + OperationRequiresAClientCallbackToHaveBeenRegistered = 14; + OperationRequiresAClientDataSourceToHaveBeenRegistered = 15; + RequestedDataIsOutOfDateAndShouldBeRequestedAgain = 16; + TheDevicesEnforcedVolumeLimitHasBeenExceeded = 17; + VolumeValueIsOutOfRange = 18; + VolumeIsAlreadyAtTheMaximumValue = 19; + VolumeIsAlreadyMuted = 20; + VoiceInputEndpointDoesNotExist = 21; + TheVoiceInputDeviceIsNotRegisteredOrDoesNotExist = 22; + EncryptionFailure = 23; + EndpointDoesNotExist = 24; + TheClientsApplicationCancelledTheOperation = 25; + TheOperationTimedOut = 26; + TheSpecifiedPlayerPathObjectWasInvalid = 27; + AddingOrRemovingDevicesFromTheAvOutputContextHasFailed = 28; + CouldNotFindTheSpecifiedNowPlayingPlayer = 29; + TheSpecifiedContentItemDoesNotExist = 30; + TheSpecifiedOffsetIsInvalid = 31; + TheSpecifiedOutputContextIsInvalid = 32; + OneOrMoreSpecifiedOutputDevicesAreNotGroupable = 33; + TheSpecifiedOutputContextDoesNotSupportAddingMoreThanOneOutputDevice = 34; + CouldNotFindTheSpecifiedNowPlayingClient = 35; + EndpointVolumeControlIsOnlyPossibleIfTheEndpointIsPickedOrRemoteControllable = 36; + OutputDeviceVolumeControlIsOnlyPossibleIfTheEndpointIsPickedOrRemoteControllable = 37; + CoderMustSupportKeyValueCoding = 38; + CouldNotFindTheGivenOutputdevice = 39; + + FailedToConnectToRemoteDevice = 100; + AuthenticationTokenIsInvalid = 101; + RecordingSessionIsAlreadyInProgressOnThisDevice = 102; + TheDeviceIsNotCurrentlyRecording = 103; + TheClientHasDisconnected = 104; + TheServerHasDisconnected = 105; + TheConnectionHasBeenCancelledByTheClient = 106; + PairingFunctionalityIsLockedDueToSecurityReasons = 107; + TheClientsOperatingSystemVersionIsTooOld = 108; + TheClientsApplicationVersionIsTooOld = 109; + TheDeviceIsNotPaired = 110; + ThePinPairingDialogWasRemovedByTheUserBeforePairingOccoured = 111; + ThePinPairingDialogWasRemovedByATimeoutBeforePairingOccoured = 112; + TheConnectionTimedout = 113; + PairingWithThisDeviceIsBlocked = 114; + TheDeviceIsGoingToSleep = 115; + ConnectionBlockedByServer = 116; + MravendpointWasDeallocatedWhileWaitingForDeviceToConnect = 117; + + OutputContextModificationCausedADeviceToNoLongerBeAProxyGroupPlayer = 200; + OutputContextModificationCausedADeviceToBecomeAProxyGroupPlayer = 201; + OutputContextModificationRequestedNoTopologyChange = 202; + + OtherUnknownError = 299; + } +} + +message ProtocolMessage { + extensions 6 to 77; + extensions 79 to 84; + extensions 86 to max; + + enum Type { + UNKNOWN_MESSAGE = 0; + SEND_COMMAND_MESSAGE = 1; + SEND_COMMAND_RESULT_MESSAGE = 2; + GET_STATE_MESSAGE = 3; + SET_STATE_MESSAGE = 4; + SET_ARTWORK_MESSAGE = 5; + REGISTER_HID_DEVICE_MESSAGE = 6; + REGISTER_HID_DEVICE_RESULT_MESSAGE = 7; + SEND_HID_EVENT_MESSAGE = 8; + SEND_HID_REPORT_MESSAGE = 9; + SEND_VIRTUAL_TOUCH_EVENT_MESSAGE = 10; + NOTIFICATION_MESSAGE = 11; + // Seems deprecated + CONTENT_ITEMS_CHANGED_NOTIFICATION_MESSAGE = 12; + DEVICE_INFO_MESSAGE = 15; + CLIENT_UPDATES_CONFIG_MESSAGE = 16; + VOLUME_CONTROL_AVAILABILITY_MESSAGE = 17; + GAME_CONTROLLER_MESSAGE = 18; + REGISTER_GAME_CONTROLLER_MESSAGE = 19; + REGISTER_GAME_CONTROLLER_RESPONSE_MESSAGE = 20; + UNREGISTER_GAME_CONTROLLER_MESSAGE = 21; + REGISTER_FOR_GAME_CONTROLLER_EVENTS_MESSAGE = 22; + KEYBOARD_MESSAGE = 23; + GET_KEYBOARD_SESSION_MESSAGE = 24; + TEXT_INPUT_MESSAGE = 25; + GET_VOICE_INPUT_DEVICES_MESSAGE = 26; + GET_VOICE_INPUT_DEVICES_RESPONSE_MESSAGE = 27; + REGISTER_VOICE_INPUT_DEVICE_MESSAGE = 28; + REGISTER_VOICE_INPUT_DEVICE_RESPONSE_MESSAGE = 29; + SET_RECORDING_STATE_MESSAGE = 30; + SEND_VOICE_INPUT_MESSAGE = 31; + PLAYBACK_QUEUE_REQUEST_MESSAGE = 32; + TRANSACTION_MESSAGE = 33; + CRYPTO_PAIRING_MESSAGE = 34; + GAME_CONTROLLER_PROPERTIES_MESSAGE = 35; + SET_READY_STATE_MESSAGE = 36; + DEVICE_INFO_UPDATE_MESSAGE = 37; + SET_CONNECTION_STATE_MESSAGE = 38; + SEND_BUTTON_EVENT_MESSAGE = 39; + SET_HILITE_MODE_MESSAGE = 40; + WAKE_DEVICE_MESSAGE = 41; + GENERIC_MESSAGE = 42; + SEND_PACKED_VIRTUAL_TOUCH_EVENT_MESSAGE = 43; + SEND_LYRICS_EVENT = 44; + SET_NOW_PLAYING_CLIENT_MESSAGE = 46; + SET_NOW_PLAYING_PLAYER_MESSAGE = 47; + MODIFY_OUTPUT_CONTEXT_REQUEST_MESSAGE = 48; + GET_VOLUME_MESSAGE = 49; + GET_VOLUME_RESULT_MESSAGE = 50; + SET_VOLUME_MESSAGE = 51; + VOLUME_DID_CHANGE_MESSAGE = 52; + REMOVE_CLIENT_MESSAGE = 53; + REMOVE_PLAYER_MESSAGE = 54; + UPDATE_CLIENT_MESSAGE = 55; + UPDATE_CONTENT_ITEM_MESSAGE = 56; + UPDATE_CONTENT_ITEM_ARTWORK_MESSAGE = 57; + UPDATE_PLAYER_MESSAGE = 58; + PROMPT_FOR_ROUTE_AUTHORIZATION_MESSAGE = 59; + PROMPT_FOR_ROUTE_AUTHORIZATION_RESPONSE_MESSAGE = 60; + PRESENT_ROUTE_AUTHORIZATION_STATUS_MESSAGE = 61; + GET_VOLUME_CONTROL_CAPABILITIES_MESSAGE = 62; + GET_VOLUME_CONTROL_CAPABILITIES_RESULT_MESSAGE = 63; + VOLUME_CONTROL_CAPABILITIES_DID_CHANGE_MESSAGE = 64; + UPDATE_OUTPUT_DEVICE_MESSAGE = 65; + REMOVE_OUTPUT_DEVICES_MESSAGE = 66; + REMOTE_TEXT_INPUT_MESSAGE = 67; + GET_REMOTE_TEXT_INPUT_SESSION_MESSAGE = 68; + REMOVE_FROM_PARENT_GROUP_MESSAGE = 69; // Yes Same as 66 + PLAYBACK_SESSION_REQUEST_MESSAGE = 70; + PLAYBACK_SESSION_RESPONSE_MESSAGE = 71; + SET_DEFAULT_SUPPORTED_COMMANDS_MESSAGE = 72; + PLAYBACK_SESSION_MIGRATE_REQUEST_MESSAGE = 73; + PLAYBACK_SESSION_MIGRATE_RESPONSE_MESSAGE = 74; + PLAYBACK_SESSION_MIGRATE_BEGIN_MESSAGE = 75; + PLAYBACK_SESSION_MIGRATE_END_MESSAGE = 76; + UPDATE_ACTIVE_SYSTEM_ENDPOINT_MESSAGE = 77; + SET_DISCOVERY_MODE_MESSAGE = 101; + UPDATE_END_POINTS_MESSAGE = 102; + REMOVE_ENDPOINTS_MESSAGE = 103; + PLAYER_CLIENT_PROPERTIES_MESSAGE = 104; + ORIGIN_CLIENT_PROPERTIES_MESSAGE = 105; + AUDIO_FADE_MESSAGE = 106; + AUDIO_FADE_RESPONSE_MESSAGE = 107; + CONFIGURE_CONNECTION_MESSAGE = 120; + PLAYBACK_SESSION_MIGRATE_POST_MESSAGE = 78; + DISCOVERY_UPDATE_ENDPOINTS_MESSAGE = 108; + DISCOVERY_UPDATE_OUTPUT_DEVICES_MESSAGE = 109; + SET_LISTENING_MODE_MESSAGE = 110; + CREATE_HOSTED_ENDPOINT_REQUEST_MESSAGE = 121; + CREATE_HOSTED_ENDPOINT_RESPONSE_MESSAGE = 122; + ADJUST_VOLUME_MESSAGE = 125; + GET_VOLUME_MUTED_MESSAGE = 126; + GET_VOLUME_MUTED_RESULT_MESSAGE = 127; + SET_VOLUME_MUTED_MESSAGE = 128; + VOLUME_MUTED_DID_CHANGE_MESSAGE = 129; + SET_CONVERSATION_DETECTION_ENABLED_MESSAGE = 130; + PLAYER_CLIENT_PARTICIPANTS_UPDATE_MESSAGE = 131; + REQUEST_GROUP_SESSION_MESSAGE = 132; + CONFIGURE_CONNECTION_SERVICE_MESSAGE = 133; + CREATE_APPLICATION_CONNECTION_MESSAGE = 134; + APPLICATION_CONNECTION_PROTOCOL_MESSAGE = 135; + INVALIDATE_APPLICATION_CONNECTION_MESSAGE = 136; + MICROPHONE_CONNECTION_REQUEST_MESSAGE = 137; + MICROPHONE_CONNECTION_RESPONSE_MESSAGE = 138; + } + + optional Type type = 1; + optional string identifier = 2; + optional string authenticationToken = 3; + optional ErrorCode.Enum errorCode = 4; + optional uint64 timestamp = 5; + optional string errorDescription = 78; + optional string uniqueIdentifier = 85; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioFormat.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioFormat.proto new file mode 100644 index 000000000..d85d1419b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioFormat.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +import "foundation/Common.proto"; + +message AudioFormat { + optional AudioTier.Enum tier = 1; + optional int64 bitrate = 2; + optional int64 sampleRate = 3; + optional int64 bitDepth = 4; + optional uint32 codec = 5; + optional bool spatialized = 6; + optional bool multiChannel = 7; + optional uint32 channelLayout = 8; + optional string audioChannelLayoutDescription = 9; + optional string groupID = 10; + optional string stableVariantID = 11; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioFormatSettingsMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioFormatSettingsMessage.proto new file mode 100644 index 000000000..3e7b614b2 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioFormatSettingsMessage.proto @@ -0,0 +1,5 @@ +syntax = "proto2"; + +message AudioFormatSettings { + optional bytes formatSettingsPlistData = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioRoute.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioRoute.proto new file mode 100644 index 000000000..c7dcb05db --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/AudioRoute.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +import "foundation/Common.proto"; + +message AudioRoute { + optional AudioRouteType.Enum type = 1; + optional string name = 2; + optional bool supportsSpatialization = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/audio/VoiceInputDeviceDescriptorMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/VoiceInputDeviceDescriptorMessage.proto new file mode 100644 index 000000000..4e5c02c92 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/audio/VoiceInputDeviceDescriptorMessage.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +import "types/audio/AudioFormatSettingsMessage.proto"; + +message VoiceInputDeviceDescriptor { + repeated AudioFormatSettings supported_formats = 1; + optional AudioFormatSettings default_format = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionContext.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionContext.proto new file mode 100644 index 000000000..281e410f4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionContext.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; +import "types/playback/PlayerPath.proto"; +message ApplicationConnectionContext { + optional string identifier = 1; + optional string service_name = 2; + optional PlayerPath destination_player_path = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionMessage.proto new file mode 100644 index 000000000..f3fa27783 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionMessage.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; +import "protocol/ProtocolMessage.proto"; + + +// not fully implemented yet? +message ApplicationConnectionMessage { + optional bytes header = 1; + optional bytes underlyingMessage = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionRequestInfo.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionRequestInfo.proto new file mode 100644 index 000000000..2118d4287 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/connection/ApplicationConnectionRequestInfo.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +import "messages/device/DeviceInfoMessage.proto"; +import "types/playback/NowPlayingClient.proto"; + +message ApplicationConnectionRequestInfo { + optional DeviceInfoMessage device_info = 1; + optional NowPlayingClient client = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/device/AVOutputDeviceDescriptor.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/device/AVOutputDeviceDescriptor.proto new file mode 100644 index 000000000..199f6b350 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/device/AVOutputDeviceDescriptor.proto @@ -0,0 +1,135 @@ +syntax = "proto2"; + +import "types/device/AVOutputDeviceSourceInfo.proto"; +import "types/session/GroupSessionInfo.proto"; +import "foundation/Common.proto"; + + +// From: MRAVOutputDeviceDescriptorProtobuf +message AVOutputDeviceDescriptor { + + + + + + enum TransportType { + None = 0; // "None" + AirPlay = 1; // "AirPlay" + Rapport = 2; // "Rapport" + Companion = 3; // "Companion" + IDS = 4; // "IDS" + OutputContext = 5; // "OutputContext" + GroupSession = 6; // "GroupSession" + SystemGroupSession = 7; // "SystemGroupSession" + MRRelay = 8; // "MRRelay" + } + + + // Identity + optional string name = 1; + optional string uniqueIdentifier = 2; + optional string groupID = 3; + optional string modelID = 4; + optional bytes macAddress = 5; + + // Capabilities / flags + optional bool canAccessRemoteAssets = 6; + optional bool isRemoteControllable = 7; + optional bool isGroupLeader = 8; + optional bool isGroupable = 9; + + // Type + optional DeviceType.Enum deviceType = 10; + optional DeviceSubType.Enum deviceSubType = 11; + + // Misc + optional bytes modelSpecificInfoData = 12; + optional float batteryLevel = 13; + optional bool isLocalDevice = 14; + optional bool supportsExternalScreen = 15; + optional bool requiresAuthorization = 16; + optional bool shouldForceRemoteControlabillity = 17; + + // Nested + optional AVOutputDeviceSourceInfo sourceInfo = 18; + + // More flags / fields + optional bool canRelayCommunicationChannel = 20; + optional string logicalDeviceID = 21; + optional bool isProxyGroupPlayer = 22; + optional string firmwareVersion = 23; + optional float volume = 24; + optional bool isVolumeControlAvailable = 25; + optional bool canAccessAppleMusic = 26; + optional bool canAccessiCloudMusicLibrary = 27; + optional bool groupContainsGroupLeader = 28; + optional bool supportsBufferedAirPlay = 29; + optional bool canPlayEncryptedProgressiveDownloadAssets = 30; + optional bool canFetchMediaDataFromSender = 31; + optional bool presentsOptimizedUserInterfaceWhenPlayingFetchedAudioOnlyAssets = 32; + optional bool isAirPlayReceiverSessionActive = 33; + optional string parentGroupIdentifier = 34; + optional bool parentGroupContainsDiscoverableLeader = 35; + optional bool isAddedToHomeKit = 36; + // bitmask see VolumeControlAvailabilityMessage.proto + optional int32 volumeCapabilities = 37; + optional string bluetoothID = 38; + optional bool supportsHAP = 39; + optional bool usingJSONProtocol = 40; + + // Repeated submessages (same type as this message) + repeated AVOutputDeviceDescriptor clusterCompositions = 41; + + optional ClusterType.Enum clusterType = 42; + optional string primaryUID = 43; + optional uint32 configuredClusterSize = 44; + optional bool supportsRapportRemoteControlTransport = 45; + + optional string currentBluetoothListeningMode = 46; + repeated string availableBluetoothListeningModes = 47; + + optional bool supportsMultiplayer = 48; + optional bool producesLowFidelityAudio = 49; + optional DeviceClass.Enum hostDeviceClass = 50; + optional string airPlayGroupID = 51; + optional bool supportsSharePlayHandoff = 52; + optional float distance = 53; + optional bool discoveredOnSameInfra = 54; + + // More repeated submessages (same type) + repeated AVOutputDeviceDescriptor activatedClusterMembers = 55; + + optional bool pickable = 56; + optional TransportType transportType = 57; + optional string clusterID = 58; + optional bool isClusterLeader = 59; + optional bool isAppleAccessory = 60; + optional string parentUniqueIdentifier = 61; + optional string roomID = 62; + optional string roomName = 63; + + // More repeated submessages (same type) + repeated AVOutputDeviceDescriptor allClusterMembers = 64; + + optional bool supportsConversationDetection = 65; + optional bool conversationDetectionEnabled = 66; + optional bool engageOnClusterActivate = 67; + optional bool volumeMuted = 68; + + optional GroupSessionInfo groupSessionInfo = 69; + + optional bool supportsBluetoothSharing = 70; + optional string deviceEnclosureColor = 71; + optional string playingPairedDeviceName = 72; + optional bool supportsRapport = 73; + optional bool isPickedOnPairedDevice = 74; + optional bool supportsHeadTrackedSpatialAudio = 75; + optional bool allowsHeadTrackedSpatialAudio = 76; + optional bool isHeadTrackedSpatialAudioActive = 77; + optional string headTrackedSpatialAudioMode = 78; + repeated string dnsNames = 79; + optional string alternateTransportType = 80; + optional bool deviceIsPlaying = 81; + optional bool wasDiscoveredInCache = 82; + optional bool representsUGLSender = 83; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/device/AVOutputDeviceSourceInfo.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/device/AVOutputDeviceSourceInfo.proto new file mode 100644 index 000000000..c85f507ce --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/device/AVOutputDeviceSourceInfo.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; + +message AVOutputDeviceSourceInfo { + optional string routingContextUID = 1; + optional bool multipleBuiltInDevices = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/device/DiscoverySessionConfiguration.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/device/DiscoverySessionConfiguration.proto new file mode 100644 index 000000000..27dcc73f9 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/device/DiscoverySessionConfiguration.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +message DiscoverySessionConfiguration { + // Bitmask of discovery features Audio=1, Screen=2, Video=4, RemoteControl=8, Companion=256 + optional int32 features = 1; + optional string routing_context_uid = 2; + optional bool enable_throttling = 3; + optional bool always_allow_updates = 4; + optional bool populates_external_device = 5; + optional string output_device_uid = 6; + optional uint32 target_session_id = 7; + optional bool cached_discovery_enabled = 8; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/device/GroupTopologyModificationRequest.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/device/GroupTopologyModificationRequest.proto new file mode 100644 index 000000000..1d919b20d --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/device/GroupTopologyModificationRequest.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; + +import "types/session/RequestDetails.proto"; + +message GroupTopologyModificationRequest { + + enum Type { + Unknown = 0; + Add = 1; + Remove = 2; + Set = 3; + } + + optional RequestDetails details = 1; + optional Type type = 2; + repeated string outputDeviceUIDs = 3; + optional bool fadeAudio = 4; + optional string password = 5; + optional bool suppressErrorDialog = 6; + optional bool shouldNotPauseIfLastDeviceRemoved = 7; + optional bool muteUntilFinished = 8; + optional bool shouldModifyPredictedRoutes = 9; + optional bool shouldClearPredictedRoutes = 10; + optional bool shouldWaitForUpdatedOutputDevices = 11; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/device/MRAVEndpointDescriptor.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/device/MRAVEndpointDescriptor.proto new file mode 100644 index 000000000..39c4761d4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/device/MRAVEndpointDescriptor.proto @@ -0,0 +1,27 @@ +syntax = "proto2"; + +import "types/device/AVOutputDeviceDescriptor.proto"; + +message MRAVEndpointDescriptor { + optional string name = 1; + optional string uniqueIdentifier = 2; + repeated AVOutputDeviceDescriptor outputDevices = 3; + optional AVOutputDeviceDescriptor designatedGroupLeader = 4; + optional bool isLocalEndpoint = 5; + optional string instanceIdentifier = 6; + optional bool isProxyGroupPlayer = 7; + + enum ConnectionType { + Unknown = 0; + Local = 1; + Direct = 2; + Relay = 3; + AirPlaySender = 4; + RemoteHosted = 5; + LocalHosted = 6; + } + optional ConnectionType connectionType = 8; + + optional bool canModifyGroupMembership = 9; + repeated AVOutputDeviceDescriptor personalOutputDevices = 10; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/device/UpdateActiveSystemEndpointRequest.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/device/UpdateActiveSystemEndpointRequest.proto new file mode 100644 index 000000000..789e7dc2e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/device/UpdateActiveSystemEndpointRequest.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; +import "foundation/Common.proto"; + +message UpdateActiveSystemEndpointRequest { + optional string outputDeviceUid = 1; + optional ChangeType.Enum changeType = 2; + optional string reason = 3; + optional bool pairedDeviceSync = 4; + optional uint64 type = 5; + optional double disableDuration = 6; + optional bool demoteWhenSyncingToCompanion = 7; + optional string previousOutputDeviceUid = 8; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerButtons.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerButtons.proto new file mode 100644 index 000000000..aef48c960 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerButtons.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; + +message GameControllerButtons { + optional float dPadX = 1; + optional float dPadY = 2; + + optional float a = 3; + optional float b = 4; + optional float x = 5; + optional float y = 6; + + optional float leftShoulder = 7; + optional float rightShoulder = 8; + + optional float leftThumbstickX = 9; + optional float leftThumbstickY = 10; + optional float rightThumbstickX = 11; + optional float rightThumbstickY = 12; + + optional float leftTrigger = 13; + optional float rightTrigger = 14; + + optional float pause = 15; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerDigitizer.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerDigitizer.proto new file mode 100644 index 000000000..d44b1717b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerDigitizer.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +message GameControllerDigitizer { + optional double x = 1; + optional double y = 2; + + optional bool touchDown = 3; + optional uint64 timestamp = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerMotion.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerMotion.proto new file mode 100644 index 000000000..f0fb3f80c --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/input/GameControllerMotion.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +message GameControllerAcceleration { + optional bytes data = 1; + + optional float x = 2; + optional float y = 3; + optional float z = 4; + optional float w = 5; +} + +message GameControllerMotion { + optional GameControllerAcceleration gravity = 1; + optional GameControllerAcceleration userAcceleration = 2; + optional GameControllerAcceleration attitude = 3; + optional GameControllerAcceleration rotation = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/input/VirtualTouchDeviceDescriptorMessage.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/input/VirtualTouchDeviceDescriptorMessage.proto new file mode 100644 index 000000000..bd0908d4b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/input/VirtualTouchDeviceDescriptorMessage.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +message VirtualTouchDeviceDescriptor { + optional bool absolute = 1; + optional bool integratedDisplay = 2; + optional float screenSizeWidth = 3; + optional float screenSizeHeight = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/input/VirtualTouchEvent.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/input/VirtualTouchEvent.proto new file mode 100644 index 000000000..0a104156c --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/input/VirtualTouchEvent.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + + + +import "foundation/Common.proto"; + +message VirtualTouchEvent { + optional double x = 1; + optional double y = 2; + optional VirtualTouchPhase.Enum phase = 3; + optional int32 finger = 4; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/AnimatedArtwork.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/AnimatedArtwork.proto new file mode 100644 index 000000000..ac599b0e6 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/AnimatedArtwork.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +import "types/media/DataArtwork.proto"; + +message AnimatedArtwork { + repeated DataArtwork frames = 1; + optional double frame_duration = 2; + optional bool loop = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/CommandInfo.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/CommandInfo.proto new file mode 100644 index 000000000..8a6692833 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/CommandInfo.proto @@ -0,0 +1,138 @@ +syntax = "proto2"; + +import "foundation/Common.proto"; +import "foundation/Dictionary.proto"; +import "types/media/CommandOptions.proto"; + +enum Command { + Unknown = 0; + Play = 1; + Pause = 2; + TogglePlayPause = 3; + Stop = 4; + NextTrack = 5; + PreviousTrack = 6; + AdvanceShuffleMode = 7; + AdvanceRepeatMode = 8; + BeginFastForward = 9; + EndFastForward = 10; + BeginRewind = 11; + EndRewind = 12; + Rewind15Seconds = 13; + FastForward15Seconds = 14; + Rewind30Seconds = 15; + FastForward30Seconds = 16; + SkipForward = 18; + SkipBackward = 19; + ChangePlaybackRate = 20; + RateTrack = 21; + LikeTrack = 22; + DislikeTrack = 23; + BookmarkTrack = 24; + SeekToPlaybackPosition = 45; + ChangeRepeatMode = 46; + ChangeShuffleMode = 47; + EnableLanguageOption = 53; + DisableLanguageOption = 54; + NextChapter = 25; + PreviousChapter = 26; + NextAlbum = 27; + PreviousAlbum = 28; + NextPlaylist = 29; + PreviousPlaylist = 30; + BanTrack = 31; + AddTrackToWishList = 32; + RemoveTrackFromWishList = 33; + NextInContext = 34; + PreviousInContext = 35; + ResetPlaybackTimeout = 41; + SetPlaybackQueue = 48; + AddNowPlayingItemToLibrary = 49; + CreateRadioStation = 50; + AddItemToLibrary = 51; + InsertIntoPlaybackQueue = 52; + ReorderPlaybackQueue = 55; + RemoveFromPlaybackQueue = 56; + PlayItemInPlaybackQueue = 57; + PrepareForSetQueue = 58; + SetPlaybackSession = 59; + PreloadedPlaybackSession = 60; + SetPriorityForPlaybackSession = 61; + DiscardPlaybackSession = 62; + Reshuffle = 63; + ChangeQueueEndAction = 135; +} + +message QueueEndAction { + enum Enum { + ClearAction = 0; + None = 1; + Reset = 2; + AutoPlay = 3; + } +} + +message DisableReason { + enum Enum { + Unknown = 0; + AdPlayback = 1; + SkipLimitReached = 2; + InvalidAdRanges = 3; + } +} + +message PreloadedPlaybackSessionInfo { + optional string playbackSessionIdentifier = 1; + optional string playbackSessionRevision = 2; + optional int32 playbackSessionPriority = 3; +} + +message CommandInfo { + optional Command command = 1; // varint enum + optional bool enabled = 2; // varint bool + optional bool active = 3; // varint bool + repeated double preferredIntervals = 4 [packed = true]; // fixed64; reader supports packed/unpacked + optional string localizedTitle = 5; + optional string localizedShortTitle = 9; + optional float minimumRating = 6; // fixed32 float + optional float maximumRating = 7; // fixed32 float + repeated float supportedRates = 8 [packed = true]; // fixed32; packed/unpacked supported + repeated float extendedSupportedRates = 47 [packed = true]; + optional RepeatMode.Enum repeatMode = 10; // varint enum + optional ShuffleMode.Enum shuffleMode = 11; // varint enum + optional int32 presentationStyle = 12; // varint int32/enum-ish + optional int32 transitionStyle = 48; // varint int32/enum-ish + optional int32 skipInterval = 13; // field 13 is read as varint int32 (not float/double in this build). + optional int32 numAvailableSkips = 14; + optional int32 skipFrequency = 15; + optional bool canScrub = 16; + repeated int32 supportedPlaybackQueueTypes = 17 [packed = true]; // varint; packed/unpacked supported + repeated string supportedCustomQueueIdentifiers = 18; + repeated int32 supportedInsertionPositions = 19 [packed = true]; // varint; packed/unpacked supported +// optional bool supportsSharedQueue = 20; + optional int32 upNextItemCount = 21; + optional float preferredPlaybackRate = 22; // fixed32 float + repeated string supportedPlaybackSessionTypes = 23; // reads these as strings (PBReaderReadString), not int32 enums. + repeated string currentPlaybackSessionTypes = 24; // reads these as strings (PBReaderReadString), not int32 enums. + optional string playbackSessionIdentifier = 25; + optional QueueEndAction.Enum currentQueueEndAction = 26; + repeated QueueEndAction.Enum supportedQueueEndActions = 27 [packed = true]; + optional DisableReason.Enum disabledReason = 28; + repeated PreloadedPlaybackSessionInfo supportedPlaybackSessionIdentifiers = 29; + optional CommandOptions proactiveCommandOptions = 30; + optional bool vocalsControlActive = 31; + optional float vocalsControlLevel = 32; + optional float vocalsControlMaxLevel = 33; + optional float vocalsControlMinLevel = 34; + optional bool vocalsControlContinuous = 35; + optional double sleepTimerTime = 36; // fixed64 double + optional int32 sleepTimerStopMode = 38; + optional double sleepTimerFireDate = 39; // fixed64 double + optional Dictionary dialogOptions = 41; + optional string lastSectionContentItemID = 42; + optional bool supportsReferencePosition = 43; + optional Dictionary playbackSessionRequirements = 44; + + // Fields this reader explicitly skips (reserved to avoid accidental reuse): + reserved 20, 37, 40, 45, 46; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/CommandOptions.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/CommandOptions.proto new file mode 100644 index 000000000..c53a5662c --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/CommandOptions.proto @@ -0,0 +1,96 @@ +syntax = "proto2"; + +import "foundation/Common.proto"; +import "types/media/SystemPlaybackQueue.proto"; +import "foundation/Dictionary.proto"; + +message CommandOptions { + optional string sourceId = 2; + optional string mediaType = 3; + optional bool externalPlayerCommand = 4; + optional float skipInterval = 5; + optional float playbackRate = 6; + optional float rating = 7; + optional bool negative = 8; + optional double playbackPosition = 9; + optional RepeatMode.Enum repeatMode = 10; + optional ShuffleMode.Enum shuffleMode = 11; + optional uint64 trackID = 12; + optional int64 radioStationID = 13; + optional string radioStationHash = 14; + optional bytes systemAppPlaybackQueueData = 15; + optional string destinationAppDisplayID = 16; + optional uint32 sendOptions = 17; + optional bool requestDefermentToPlaybackQueuePosition = 18; + optional string contextID = 19; + optional bool shouldOverrideManuallyCuratedQueue = 20; + optional string stationURL = 21; + optional bool shouldBeginRadioPlayback = 22; + optional int32 playbackQueueInsertionPosition = 23; + optional string contentItemID = 24; + optional int32 playbackQueueOffset = 25; + optional int32 playbackQueueDestinationOffset = 26; + optional bytes languageOption = 27; + optional bytes playbackQueueContext = 28; + optional string insertAfterContentItemID = 29; + optional string nowPlayingContentItemID = 30; + optional int32 replaceIntent = 31; + optional string commandId = 32; + optional string senderId = 33; + optional string remoteControlInterface = 34; + optional bool beginSeek = 40; + optional bool endSeek = 41; + optional bytes playbackSession = 42; + optional bytes userIdentityData = 43; + optional string insertBeforeContentItemID = 44; + optional int32 queueEndAction = 45; + optional bool preservesRepeatMode = 46; + optional bool preservesShuffleMode = 47; + optional bool preservesQueueEndAction = 48; + optional string homeKitUserIdentifier = 49; + optional bool verifySupportedCommands = 50; + optional string playbackSessionIdentifier = 51; + optional int32 playbackSessionPriority = 52; + optional string playbackSessionFilePath = 53; + optional string playbackSessionRevision = 54; + optional bytes playbackSessionMetadata = 55; + optional string playbackSessionType = 56; + + optional bool trueCompletion = 57; + optional string playbackAuthorizationToken = 58; + optional string eventNoticeType = 59; + optional string eventNoticeIdentifier = 60; + optional string sharedPlaybackSessionIdentifier = 61; + optional double commandTimeout = 62; + optional double assistantTtsEndTimestamp = 63; + optional double assistantCommandSendTimestamp = 64; + optional string originatingDeviceUID = 65; + optional bytes destinationDeviceUIDs = 66; + optional string desiredSessionId = 67; + optional bool alwaysIgnoreDuringCall = 68; + optional bool alwaysIgnoreDuringSharePlay = 69; + optional string commandSequenceUuid = 70; + optional bool originatedFromRemoteDevice = 71; + optional string siriTurnIdentifier = 72; + optional string siriSearchDataSetIdentifier = 73; + optional bool prepareForSetQueueIsProactive = 74; + optional string prepareForSetQueueProactiveReason = 75; + optional int32 prepareForSetQueueProactiveReasonType = 76; + optional bytes applicationUserIdentity = 77; + optional SystemPlaybackQueue systemAppPlaybackQueue = 78; + optional bool vocalsControlActive = 79; + optional float vocalsControlLevel = 80; + optional float vocalsControlMinLevel = 81; + optional float vocalsControlMaxLevel = 82; + optional bool vocalsControlContinuous = 83; + optional string associatedParticipantIdentifier = 84; + optional double sleepTimerTime = 85; + optional int32 sleepTimerStopMode = 86; + optional Dictionary dialogOptions = 87; + optional string clientPreferredLanguages = 88; + optional double referencePosition = 89; + optional bytes delegateAccountData = 90; + optional string delegateAccountDataType = 91; + optional bool enhanceDialogueActive = 92; + +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/ContentItem.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/ContentItem.proto new file mode 100644 index 000000000..61c8e1a2a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/ContentItem.proto @@ -0,0 +1,35 @@ +syntax = "proto2"; + +import "types/media/LyricItem.proto"; +import "types/media/LanguageOption.proto"; +import "types/media/ContentItemMetadata.proto"; +import "types/media/RemoteArtwork.proto"; +import "types/media/DataArtwork.proto"; +import "types/media/AnimatedArtwork.proto"; +import "types/media/TranscriptAlignment.proto"; + +message ContentItem { + optional string identifier = 1; + optional ContentItemMetadata metadata = 2; + optional bytes artworkData = 3; + optional string info = 4; + repeated LanguageOptionGroup availableLanguageOptions = 5; + repeated LanguageOption currentLanguageOptions = 6; + optional LyricsItem lyrics = 7; + repeated ContentItem sections = 8; + optional string parentIdentifier = 9; + optional string ancestorIdentifier = 10; + optional string queueIdentifier = 11; + optional string requestIdentifier = 12; + optional int32 artworkDataWidth = 13; + optional int32 artworkDataHeight = 14; + optional string associatedParticipantIdentifier = 15; + repeated string availableArtworkFormats = 16; + repeated string availableRemoteArtworkFormats = 17; + repeated DataArtwork dataArtworks = 18; + repeated RemoteArtwork remoteArtworks = 19; + repeated TranscriptAlignment transcriptAlignments = 20; + repeated string availableAnimatedArtworkFormats = 21; + repeated DataArtwork animatedArtworkPreviewFrames = 22; + repeated AnimatedArtwork animatedArtworks = 23; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/ContentItemMetadata.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/ContentItemMetadata.proto new file mode 100644 index 000000000..75aab1a9d --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/ContentItemMetadata.proto @@ -0,0 +1,132 @@ +syntax = "proto2"; + +import "foundation/Common.proto"; +import "types/audio/AudioFormat.proto"; +import "types/audio/AudioRoute.proto"; + +message ContentItemMetadata { + enum MediaType { + UnknownMediaType = 0; + Audio = 1; + Video = 2; + } + enum MediaSubType { + UnknownMediaSubType = 0; + Music = 1; + Podcast = 4; + AudioBook = 5; + ITunesU = 6; + } + optional string title = 1; + optional string subtitle = 2; + optional bool isContainer = 3; + optional bool isPlayable = 4; + optional float playbackProgress = 5; + optional string albumName = 6; + optional string trackArtistName = 7; + optional string albumArtistName = 8; + optional string directorName = 9; + optional int32 seasonNumber = 10; + optional int32 episodeNumber = 11; + optional double releaseDate = 12; + optional int32 playCount = 13; + optional double duration = 14; + optional string localizedContentRating = 15; + optional bool isExplicitItem = 16; + optional int32 playlistType = 17; + optional int32 radioStationType = 18; + optional bool artworkAvailable = 19; + + optional bool infoAvailable = 21; + optional bool languageOptionsAvailable = 22; + optional int32 numberOfSections = 23; + optional bool lyricsAvailable = 24; + optional int32 editingStyleFlags = 25; + optional bool isStreamingContent = 26; + optional bool isCurrentlyPlaying = 27; + optional string collectionIdentifier = 28; + optional string profileIdentifier = 29; + optional double startTime = 30; + optional string artworkMIMEType = 31; + optional string assetURLString = 32; + optional string composer = 33; + optional int32 discNumber = 34; + optional double elapsedTime = 35; + optional string genre = 36; + optional bool isAlwaysLive = 37; + + optional float playbackRate = 39; + optional int32 chapterCount = 40; + optional int32 totalDiscCount = 41; + optional int32 totalTrackCount = 42; + optional int32 trackNumber = 43; + optional string contentIdentifier = 44; + + optional bool isSharable = 46; + + optional bool isLiked = 48; + optional bool isInWishList = 49; + optional int64 radioStationIdentifier = 50; + + optional string radioStationName = 52; + optional string radioStationString = 53; + optional int64 iTunesStoreIdentifier = 54; + optional int64 iTunesStoreSubscriptionIdentifier = 55; + optional int64 iTunesStoreArtistIdentifier = 56; + optional int64 iTunesStoreAlbumIdentifier = 57; + optional bytes purchaseInfoData = 58; + optional float defaultPlaybackRate = 59; + optional int32 downloadState = 60; + optional float downloadProgress = 61; + optional bytes appMetricsData = 62; + optional string seriesName = 63; + optional MediaType mediaType = 64; + optional MediaSubType mediaSubType = 65; + optional bytes nowPlayingInfoData = 67; + optional bytes userInfoData = 68; + optional bool isSteerable = 69; + optional string artworkURL = 70; + optional string lyricsURL = 71; + optional bytes deviceSpecificUserInfoData = 72; + optional bytes collectionInfoData = 73; + optional double elapsedTimeTimestamp = 74; + optional double inferredTimestamp = 75; + optional string serviceIdentifier = 76; + optional int32 artworkDataWidth = 77; + optional int32 artworkDataHeight = 78; + optional bytes currentPlaybackDateData = 79; + optional string artworkIdentifier = 80; + optional bool isLoading = 81; + optional bytes artworkURLTemplatesData = 82; + optional int64 legacyUniqueIdentifier = 83; + optional int32 episodeType = 84; + optional string artworkFileURL = 85; + optional string brandIdentifier = 86; + optional string localizedDurationString = 87; + optional string albumYear = 88; + optional SongTraits.Enum songTraits = 89; + optional AlbumTraits.Enum albumTraits = 90; + optional PlaylistTraits.Enum playlistTraits = 91; + optional AudioFormat preferredFormat = 92; + optional AudioFormat activeFormat = 93; + optional ActiveFormatJustification.Enum activeFormatJustification = 94; + optional FormatTier.Enum formatTierPreference = 95; + optional AudioRoute audioRoute = 96; + repeated AudioFormat alternativeFormats = 97; + optional bool isAdvertisement = 98; + optional bool hasAlternativeFormats = 99; + optional string participantName = 100; + optional string participantIdentifier = 101; + optional string classicalWork = 102; + optional int64 reportingAdamId = 103; + optional int64 lyricsAdamId = 104; + optional int64 iTunesStoreAlbumArtistIdentifier = 105; + optional string durationStringLocalizationKey = 106; + optional bool isResolvableParticipant = 107; + optional string internationalStandardRecordingCode = 108; + optional bool isInTransition = 109; + optional bool excludeFromSuggestions = 110; + optional bool transcriptAlignmentsAvailable = 111; + optional string subtitleShort = 112; + optional bytes transitionInfoData = 113; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/DataArtwork.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/DataArtwork.proto new file mode 100644 index 000000000..f5c6932ad --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/DataArtwork.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; + +message DataArtwork { + optional string type = 1; + optional bytes image_data = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/LanguageOption.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/LanguageOption.proto new file mode 100644 index 000000000..633f2c18a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/LanguageOption.proto @@ -0,0 +1,37 @@ +syntax = "proto2"; + + + +message LanguageOptionGroup{ + optional bool allowEmptySelection = 1; + optional LanguageOption defaultLanguageOption = 2; + repeated LanguageOption languageOptions = 3; +} + + + +message LanguageOption { + + enum Type { + Audible = 0; + Legible = 1; + } + + optional LanguageOption.Type type = 1; + optional string languageTag = 2; + + // I think these are all the possible values for characteristics + // "public.main-program-content" + // "public.auxiliary-content" + // "public.subtitles.forced-only" + // "public.accessibility.transcribes-spoken-dialog" + // "public.accessibility.describes-music-and-sound" + // "public.accessibility.describes-video" + // "public.easy-to-read" + // "public.translation" + // "public.translation.dubbed" + // "public.translation.voice-over" + repeated string characteristics = 3; + optional string displayName = 4; + optional string identifier = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/LyricItem.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/LyricItem.proto new file mode 100644 index 000000000..90399473c --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/LyricItem.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + + +import "types/media/LyricsToken.proto"; + + +message LyricsItem { + optional string lyrics = 1; + optional bool userProvided = 2; + optional LyricsToken token = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/LyricsToken.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/LyricsToken.proto new file mode 100644 index 000000000..aee8c5411 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/LyricsToken.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + + +message LyricsToken { + optional string identifier = 1; + optional bytes userData = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/NowPlayingInfo.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/NowPlayingInfo.proto new file mode 100644 index 000000000..51a0e519e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/NowPlayingInfo.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; + +import "foundation/Common.proto"; + +message NowPlayingInfo { + optional string album = 1; + optional string artist = 2; + optional double duration = 3; + optional double elapsedTime = 4; + optional float playbackRate = 5; + optional RepeatMode.Enum repeatMode = 6; + optional ShuffleMode.Enum shuffleMode = 7; + optional double timestamp = 8; + optional string title = 9; + optional uint64 uniqueIdentifier = 10; + optional bool isExplicitTrack = 11; + optional bool isMusicApp = 12; + optional int64 radioStationIdentifier = 13; + optional string radioStationHash = 14; + optional string radioStationName = 15; + optional bytes artworkDataDigest = 16; + optional bool isAlwaysLive = 17; + optional bool isAdvertisement = 18; + optional bool isInTransition = 19; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/RemoteArtwork.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/RemoteArtwork.proto new file mode 100644 index 000000000..24403fb7f --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/RemoteArtwork.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; + +message RemoteArtwork { + optional string url = 1; + optional string type = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/SendCommandResultHandlerDialog.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SendCommandResultHandlerDialog.proto new file mode 100644 index 000000000..54745bee1 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SendCommandResultHandlerDialog.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "messages/playback/SendCommandMessage.proto"; + +message SendCommandResultHandlerDialog { + repeated SendCommandResultHandlerDialogAction actions = 1; + optional string localizedTitle = 2; + optional string localizedMessage = 3; +} + +message SendCommandResultHandlerDialogAction { + optional int32 type = 1; + required string title = 2; + optional SendCommandMessage event = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/SupportedCommands.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SupportedCommands.proto new file mode 100644 index 000000000..fe3bfdb15 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SupportedCommands.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import "types/media/CommandInfo.proto"; + +message SupportedCommands { + repeated CommandInfo supportedCommands = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackCustomDataQueue.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackCustomDataQueue.proto new file mode 100644 index 000000000..57e0db4da --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackCustomDataQueue.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + + + +message SystemPlaybackCustomDataQueue { + required string identifier = 1; + optional bytes data = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackGenericTrackListQueue.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackGenericTrackListQueue.proto new file mode 100644 index 000000000..d62cd677e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackGenericTrackListQueue.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +import "foundation/Common.proto"; + +message SystemPlaybackGenericTracklistQueue { + + optional string firstTrackIdentifier = 1; + + // Encoded list of track identifiers (NOT repeated). + // Likely serialized or delimited externally. + optional string trackIdentifiers = 2; + + optional bytes collectionIdentifierSet = 3; + + optional ShuffleMode.Enum shuffleMode = 4; + + optional RepeatMode.Enum repeatMode = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackQueue.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackQueue.proto new file mode 100644 index 000000000..7da77b981 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/SystemPlaybackQueue.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; + + + +import "types/media/SystemPlaybackCustomDataQueue.proto"; +import "types/media/SystemPlaybackGenericTrackListQueue.proto"; +import "foundation/Common.proto"; + + +message SystemPlaybackQueue { + optional PlaybackQueueType.Enum type = 1; + optional ReplaceIntent.Enum replaceIntent = 2; + optional bool isRequestingImmediatePlayback = 3; + optional string featureName = 4; + optional SystemPlaybackCustomDataQueue customData = 6; + optional SystemPlaybackGenericTracklistQueue tracklist = 7; + optional bytes userInfo = 8; + optional bytes metrics = 9; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/media/TranscriptAlignment.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/media/TranscriptAlignment.proto new file mode 100644 index 000000000..b9513f21d --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/media/TranscriptAlignment.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +message TranscriptAlignment { + optional double start_time = 1; + optional double end_time = 2; + optional string text = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/NowPlayingClient.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/NowPlayingClient.proto new file mode 100644 index 000000000..4403173e9 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/NowPlayingClient.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +import "foundation/Color.proto"; + +message NowPlayingClient { + optional int32 processIdentifier = 1; + optional string bundleIdentifier = 2; + optional string parentApplicationBundleIdentifier = 3; + optional int32 processUserIdentifier = 4; + optional int32 nowPlayingVisibility = 5; + optional Color tintColor = 6; + optional string displayName = 7; + repeated string extendedBundleIdentifierHierarchys = 8; + optional bool isEmptyDeprecated = 9; + optional string iconUrl = 10; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/NowPlayingPlayer.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/NowPlayingPlayer.proto new file mode 100644 index 000000000..a6a9ae8a4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/NowPlayingPlayer.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +message NowPlayingPlayer { + enum AudioSessionType { + Default = 0; + LongForm = 1; + Independent = 2; + } + optional string identifier = 1; + optional string displayName = 2; + optional bool isDefaultPlayer = 3; // NOTE(RE): field 3 (isDefaultPlayer) is absent in decompiled code + optional AudioSessionType audioSessionType = 4; + repeated int64 mxSessionIDs = 5; + optional uint32 audioSessionID = 6; + optional string iconURL = 7; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueue.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueue.proto new file mode 100644 index 000000000..da23eaf83 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueue.proto @@ -0,0 +1,21 @@ +syntax = "proto2"; + +import "types/media/ContentItem.proto"; +import "types/playback/PlaybackQueueContext.proto"; +import "types/playback/PlayerPath.proto"; +import "foundation/Dictionary.proto"; +import "types/playback/PlaybackQueueParticipant.proto"; + +message PlaybackQueue { + optional int32 location = 1; + repeated ContentItem contentItems = 2; + optional PlaybackQueueContext context = 3; + optional string requestId = 4; + optional PlayerPath resolvedPlayerPath = 5; + optional bool sendingPlaybackQueueTransaction = 6; + optional string queueIdentifier = 7; + repeated PlaybackQueueParticipant participants = 8; + repeated string homeUserIdentifiers = 9; + optional Dictionary properties = 10; + optional Dictionary auxiliaryNowPlayingInfo = 11; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueCapabilities.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueCapabilities.proto new file mode 100644 index 000000000..58203d785 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueCapabilities.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +message PlaybackQueueCapabilities { + optional bool requestByRange = 1; + optional bool requestByIdentifiers = 2; + optional bool requestByRequest = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueContext.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueContext.proto new file mode 100644 index 000000000..32742f8a7 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueContext.proto @@ -0,0 +1,5 @@ +syntax = "proto2"; + +message PlaybackQueueContext { + optional string revision = 1; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueParticipant.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueParticipant.proto new file mode 100644 index 000000000..be3fce86a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackQueueParticipant.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +import "types/session/UserIdentity.proto"; + +message PlaybackQueueParticipant { + optional string identifier = 1; + optional UserIdentity identity = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSession.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSession.proto new file mode 100644 index 000000000..879242c5a --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSession.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +message PlaybackSession { + optional bytes playbackSessionData = 1; + optional string identifier = 2; + optional string type = 3; + optional string revision = 4; + optional bytes metadata = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionMigrateRequest.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionMigrateRequest.proto new file mode 100644 index 000000000..451a30c42 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionMigrateRequest.proto @@ -0,0 +1,30 @@ +syntax = "proto2"; + +import "types/media/ContentItem.proto"; +import "types/playback/PlayerPath.proto"; +import "types/playback/PlaybackSessionMigrateRequestEvent.proto"; +import "types/playback/PlaybackSessionRequest.proto"; +import "messages/playback/SendCommandResultMessage.proto"; +import "foundation/Common.proto"; + +message PlaybackSessionMigrateRequest { + + + optional string requestId = 1; + optional int32 playerOptions = 2; // bitmask of PlayerOptions.Enum + optional EndpointOptions.Enum endpointOptions = 3; + repeated PlaybackSessionMigrateRequestEvent events = 4; + optional double playbackPosition = 5; + optional ContentItem contentItem = 6; + optional PlayerPath playerPath = 7; + optional PlaybackState.Enum playbackState = 8; + optional double playbackRate = 9; + optional PlaybackSessionRequest playbackSessionRequest = 10; + optional bool allowFadeTransition = 11; + optional uint32 originatorType = 12; + optional uint32 destinationTypes = 13; + optional string initiator = 14; + optional PlayerPath resolvedPlayerPath = 15; + optional SendCommandResultStatus setPlaybackSessionCommandStatus = 16; + optional RecipeType.Enum recipeType = 17; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionMigrateRequestEvent.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionMigrateRequestEvent.proto new file mode 100644 index 000000000..d3b7c0d63 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionMigrateRequestEvent.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; + +import "foundation/Dictionary.proto"; +import "foundation/Error.proto"; +import "foundation/Common.proto"; + +message PlaybackSessionMigrateRequestEvent { + optional string name = 1; + optional double startTimestamp = 2; + optional double endTimestamp = 3; + optional int64 errorCode = 5; + optional string errorDescription = 6; + optional Error error = 7; + optional PlaybackSessionMigrateRequestEventRole.Enum role = 8; + optional Dictionary input = 9; + optional Dictionary output = 10; + repeated PlaybackSessionMigrateRequestEvent events = 11; + optional uint32 identifier = 100; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionRequest.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionRequest.proto new file mode 100644 index 000000000..51eeb7e8e --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlaybackSessionRequest.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; +import "foundation/Dictionary.proto"; +import "types/playback/PlayerPath.proto"; +message PlaybackSessionRequest { + optional string requestID = 1; + optional string identifier = 2; + optional string type = 5; + optional PlayerPath destinationPlayerPath = 6; + optional Dictionary destinationCommandInfo = 7; + optional bool isPreflight = 8; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlayerPath.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlayerPath.proto new file mode 100644 index 000000000..bd0d9abd6 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/playback/PlayerPath.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "types/session/Origin.proto"; +import "types/playback/NowPlayingClient.proto"; +import "types/playback/NowPlayingPlayer.proto"; + +message PlayerPath { + optional Origin origin = 1; + optional NowPlayingClient client = 2; + optional NowPlayingPlayer player = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/session/GroupSessionInfo.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/session/GroupSessionInfo.proto new file mode 100644 index 000000000..8ab0015f1 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/session/GroupSessionInfo.proto @@ -0,0 +1,14 @@ +// GroupSessionInfo.proto +syntax = "proto2"; + +import "foundation/Common.proto"; + +message GroupSessionInfo { + required string identifier = 1; + // NOTE: In the provided writeTo, fields 3 and 4 are only written if hostDisplayName is non-null. + optional string hostDisplayName = 2; + optional GroupSessionRouteType.Enum routeType = 3; + optional bool hosted = 4; + optional string equivalentMediaIdentifier = 5; + optional bool placeholder = 6; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/session/GroupSessionToken.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/session/GroupSessionToken.proto new file mode 100644 index 000000000..4f4ab9ee4 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/session/GroupSessionToken.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + + +message GroupSessionToken { + required bytes invitationData = 1; + required int32 routeType = 2; + + optional string displayName = 3; + optional string sessionIdentifier = 4; + optional string sharedSecret = 5; + optional string equivalentMediaIdentifier = 6; + + optional uint32 version = 7; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/session/Origin.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/session/Origin.proto new file mode 100644 index 000000000..8cc1333a8 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/session/Origin.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +import "messages/device/DeviceInfoMessage.proto"; + +message Origin { + enum Type { + Unknown = 0; + Local = 1; + Custom = 2; + } + + optional Type type = 1; + optional string displayName = 2; + optional int32 identifier = 3; + optional DeviceInfoMessage deviceInfo = 4; // named deviceInfoDeprecated in decompiled sources + optional bool isLocallyHosted = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/session/RequestDetails.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/session/RequestDetails.proto new file mode 100644 index 000000000..fde17fed2 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/session/RequestDetails.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + + +message RequestDetails { + optional string initiator = 1; + optional string requestID = 2; + optional string reason = 3; + + optional uint32 qos = 4; + optional uint64 startDate = 5; + + optional bool userInitiated = 6; + optional bool initiatorWasInferred = 7; + + optional string originatingBundleID = 8; + optional string operationID = 9; + optional string surface = 10; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/session/UserIdentity.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/session/UserIdentity.proto new file mode 100644 index 000000000..014b7fbf6 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/session/UserIdentity.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + + +message UserIdentity { + + enum Type { + Basic = 0; + Resolvable = 1; + } + + optional string identifier = 1; + optional string displayName = 2; + optional Type type = 3; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionKey.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionKey.proto new file mode 100644 index 000000000..08bc8b542 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionKey.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; + +message TransactionKey { + optional string identifier = 1; + optional bytes userData = 2; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionPacket.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionPacket.proto new file mode 100644 index 000000000..12d730c90 --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionPacket.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "types/transaction/TransactionKey.proto"; + +message TransactionPacket { + optional TransactionKey key = 1; + optional bytes packetData = 2; + optional string identifier = 3; + optional uint64 totalLength = 4; + optional uint64 totalWritePosition = 5; +} diff --git a/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionPackets.proto b/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionPackets.proto new file mode 100644 index 000000000..20766677b --- /dev/null +++ b/pyatv/protocols/mrp/protobuf/re_protos/types/transaction/TransactionPackets.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import "types/transaction/TransactionPacket.proto"; + +message TransactionPackets { + repeated TransactionPacket packets = 1; +}