Skip to content

Commit

Permalink
Merge 563476f into 53fd7bf
Browse files Browse the repository at this point in the history
  • Loading branch information
postlund committed Dec 7, 2018
2 parents 53fd7bf + 563476f commit 52006fc
Show file tree
Hide file tree
Showing 46 changed files with 3,453 additions and 1,512 deletions.
10 changes: 9 additions & 1 deletion pyatv/__main__.py
Expand Up @@ -368,13 +368,21 @@ def _extract_command_with_args(cmd):
all the additional arguments are passed as arguments to the target
method.
"""
def _isint(value):
try:
int(value)
return True
except ValueError:
return False

equal_sign = cmd.find('=')
if equal_sign == -1:
return cmd, []

command = cmd[0:equal_sign]
args = cmd[equal_sign+1:].split(',')
return command, args
converted = [x if not _isint(x) else int(x) for x in args]
return command, converted


async def _handle_commands(args, loop):
Expand Down
59 changes: 40 additions & 19 deletions pyatv/mrp/__init__.py
Expand Up @@ -7,6 +7,7 @@
from pyatv.mrp.srp import SRPAuthHandler
from pyatv.mrp.connection import MrpConnection
from pyatv.mrp.protocol import MrpProtocol
from pyatv.mrp.protobuf import CommandInfo_pb2
from pyatv.mrp.pairing import MrpPairingProcedure
from pyatv.interface import (AppleTV, RemoteControl, Metadata,
Playing, PushUpdater, PairingHandler)
Expand All @@ -21,8 +22,6 @@
'down': [1, 0x8D],
'left': [1, 0x8B],
'right': [1, 0x8A],
'play': [12, 0xB0],
'pause': [12, 0xB1],
'stop': [12, 0xB7],
'next': [12, 0xB5],
'previous': [12, 0xB6],
Expand Down Expand Up @@ -71,23 +70,24 @@ def right(self):

def play(self):
"""Press key play."""
return self._press_key('play')
return self.protocol.send(messages.command(CommandInfo_pb2.Play))

def pause(self):
"""Press key play."""
return self._press_key('pause')
return self.protocol.send(messages.command(CommandInfo_pb2.Pause))

def stop(self):
"""Press key stop."""
return self._press_key('stop')
return self.protocol.send(messages.command(CommandInfo_pb2.Stop))

def next(self):
"""Press key next."""
return self._press_key('next')
return self.protocol.send(messages.command(CommandInfo_pb2.NextTrack))

def previous(self):
"""Press key previous."""
return self._press_key('previous')
return self.protocol.send(
messages.command(CommandInfo_pb2.PreviousTrack))

def select(self):
"""Press key select."""
Expand All @@ -107,15 +107,25 @@ def suspend(self):

def set_position(self, pos):
"""Seek in the current playing media."""
raise exceptions.NotSupportedError
return self.protocol.send(messages.seek_to_position(pos))

async def set_shuffle(self, is_on):
def set_shuffle(self, is_on):
"""Change shuffle mode to on or off."""
raise exceptions.NotSupportedError
return self.protocol.send(messages.shuffle(is_on))

async def set_repeat(self, repeat_mode):
def set_repeat(self, repeat_mode):
"""Change repeat mode."""
raise exceptions.NotSupportedError
# TODO: extract to convert module
if int(repeat_mode) == const.REPEAT_STATE_OFF:
state = 1
elif int(repeat_mode) == const.REPEAT_STATE_ALL:
state = 2
elif int(repeat_mode) == const.REPEAT_STATE_TRACK:
state = 3
else:
raise ValueError('Invalid repeat mode: ' + str(repeat_mode))

return self.protocol.send(messages.repeat(state))


class MrpPlaying(Playing):
Expand All @@ -141,8 +151,7 @@ def play_state(self):
if state == 2:
return const.PLAY_STATE_PAUSED

raise exceptions.UnknownPlayState(
'Unknown playstate: ' + str(state))
return const.PLAY_STATE_PAUSED

@property
def title(self):
Expand All @@ -162,7 +171,11 @@ def album(self):
@property
def genre(self):
"""Genre of the currently playing song."""
return None
if self._metadata:
from pyatv.mrp.protobuf import ContentItem_pb2
transaction = ContentItem_pb2.ContentItem()
transaction.ParseFromString(self._metadata)
# TODO: implement this

@property
def total_time(self):
Expand All @@ -182,15 +195,23 @@ def position(self):

return None

def _get_command_info(self, command):
for cmd in self._setstate.supportedCommands.supportedCommands:
if cmd.command == command:
return cmd
return None

@property
def shuffle(self):
"""If shuffle is enabled or not."""
return None
info = self._get_command_info(CommandInfo_pb2.ChangeShuffleMode)
return None if info is None else info.shuffleMode

@property
def repeat(self):
"""Repeat mode."""
return None
info = self._get_command_info(CommandInfo_pb2.ChangeRepeatMode)
return None if info is None else info.repeatMode


class MrpMetadata(Metadata):
Expand All @@ -210,8 +231,8 @@ async def _handle_set_state(self, message, _):
self._setstate = message.inner()

async def _handle_transaction(self, message, _):
packet = message.inner().packets[0].packet
self._nowplaying = packet.contentItem.metadata.nowPlayingInfo
packet = message.inner().packets.packets[0].packetData
self._nowplaying = packet # .contentItem.metadata.nowPlayingInfo

@property
def device_id(self):
Expand Down
42 changes: 42 additions & 0 deletions pyatv/mrp/messages.py
Expand Up @@ -27,6 +27,8 @@ def device_information(name, identifier):
info.applicationBundleIdentifier = 'com.apple.TVRemote'
info.applicationBundleVersion = '273.12'
info.protocolVersion = 1
info.lastSupportedMessageType = 58
info.supportsExtendedMotion = True
return message


Expand All @@ -35,6 +37,13 @@ def set_ready_state():
return create(protobuf.ProtocolMessage.SET_READY_STATE_MESSAGE)


def set_connection_state():
"""Create a new SET_CONNECTION_STATE."""
message = create(protobuf.ProtocolMessage.SET_CONNECTION_STATE_MESSAGE)
message.inner().state = protobuf.SetConnectionStateMessage.Connected
return message


def crypto_pairing(pairing_data):
"""Create a new CRYPTO_PAIRING_MESSAGE."""
message = create(protobuf.CRYPTO_PAIRING_MESSAGE)
Expand Down Expand Up @@ -113,3 +122,36 @@ def send_hid_event(use_page, usage, down):
binascii.unhexlify(b'0000000000000001000000')

return message


def command(cmd):
"""Playback command request."""
message = create(protobuf.SEND_COMMAND_MESSAGE)
send_command = message.inner()
send_command.command = cmd
return message


def repeat(mode):
"""Change repeat mode of current player."""
message = command(protobuf.CommandInfo_pb2.ChangeShuffleMode)
send_command = message.inner()
send_command.options.externalPlayerCommand = True
send_command.options.repeatMode = mode
return message


def shuffle(enable):
"""Change shuffle mode of current player."""
message = command(protobuf.CommandInfo_pb2.ChangeShuffleMode)
send_command = message.inner()
send_command.options.shuffleMode = 3 if enable else 1
return message


def seek_to_position(position):
"""Seek to an absolute position in stream."""
message = command(protobuf.CommandInfo_pb2.SeekToPlaybackPosition)
send_command = message.inner()
send_command.options.playbackPosition = position
return message
76 changes: 76 additions & 0 deletions pyatv/mrp/protobuf/CommandInfo.proto
@@ -0,0 +1,76 @@
syntax = "proto2";

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;
}

message CommandInfo {
optional Command command = 1;
optional bool enabled = 2;
optional bool active = 3;
repeated double preferredIntervals = 4;
optional string localizedTitle = 5;
optional float minimumRating = 6;
optional float maximumRating = 7;
repeated float supportedRates = 8;
optional string localizedShortTitle = 9;
optional int32 repeatMode = 10;
optional int32 shuffleMode = 11;
optional int32 presentationStyle = 12;
optional int32 skipInterval = 13;
optional int32 numAvailableSkips = 14;
optional int32 skipFrequency = 15;
optional int32 canScrub = 16;
repeated int32 supportedPlaybackQueueTypes = 17;
repeated string supportedCustomQueueIdentifiers = 18;
repeated int32 supportedInsertionPositions = 19;
optional bool supportsSharedQueue = 20;
}

0 comments on commit 52006fc

Please sign in to comment.