~martijnbraam/pyatem

afa55331d7debe3aeaaca3a7eb6be1238950e896 — Martijn Braam 2 months ago 79f3a8e
Make download() work on macros
3 files changed, 60 insertions(+), 7 deletions(-)

M pyatem/command.py
M pyatem/field.py
M pyatem/protocol.py
M pyatem/command.py => pyatem/command.py +8 -4
@@ 1926,10 1926,14 @@ class TransferDownloadRequestCommand(Command):
        self.slot = slot

    def get_command(self):
        u1 = 0
        u2 = 0
        u3 = 0
        u4 = 0xff
        # Special case for macro downloads, not sure why
        if self.store == 0xffff:
            u1 = 0x03
        else:
            u1 = 0x00
        u2 = 0xd0
        u3 = 0x9b
        u4 = 0x8c
        data = struct.pack('>HHI 4B', self.transfer, self.store, self.slot, u1, u2, u3, u4)
        return self._make_command('FTSU', data)


M pyatem/field.py => pyatem/field.py +42 -0
@@ 2229,3 2229,45 @@ class FileTransferDataCompleteField(FieldBase):

    def __repr__(self):
        return '<file-transfer-complete transfer={} u1={} u2={}>'.format(self.transfer, self.u1, self.u2)


class MacroPropertiesField(FieldBase):
    """
    Data from the `MPrp`. This is the metadata about a stored macro

    ====== ==== ====== ===========
    Offset Size Type   Description
    ====== ==== ====== ===========
    0      2    u16    Macro slot index
    2      1    bool   is used
    3      1    bool   has invalid commands
    4      2    u16    Name length
    6      2    u16    Description length
    8      ?    char[] Name
    ?      ?    char[] Description
    ====== ==== ====== ===========

    After parsing:

    :ivar index: Macro slot index
    :ivar is_used: Slot contains data
    :ivar is_invalid: Slot contains invalid data
    :ivar name: Name of the macro
    :ivar description: Description of the macro
    """

    CODE = "MPrp"

    def __init__(self, raw):
        self.raw = raw
        field = struct.unpack_from('>H ?? H H', raw, 0)
        self.index = field[0]
        self.is_used = field[1]
        self.is_invalid = field[2]
        name_length = field[3]
        desc_length = field[4]
        self.name, self.description = struct.unpack_from('>{}s {}s'.format(name_length, desc_length), raw, 8)

    def __repr__(self):
        return '<macro-properties: index={} used={} name={}>'.format(self.index, self.is_used,
                                                                     self.name)

M pyatem/protocol.py => pyatem/protocol.py +10 -3
@@ 240,6 240,8 @@ class AtemProtocol:
            return
        elif key == 'file-transfer-data-complete':
            logging.debug('Transfer complete')
            if contents.transfer != self.transfer_id:
                return
            # Remove current item from the transfer queue
            queue = self.transfer_queue[self.transfer_store]
            self.transfer_queue[self.transfer_store] = queue[1:]


@@ 350,7 352,7 @@ class AtemProtocol:
            return

        # Request a lock if needed
        if next[0] not in self.locks or not self.locks[next[0]]:
        if next[0] != 0xffff and (next[0] not in self.locks or not self.locks[next[0]]):
            logging.info('Requesting lock for {}'.format(next[0]))
            cmd = LockCommand(next[0], True)
            self.send_commands([cmd])


@@ 392,12 394,17 @@ if __name__ == '__main__':


    def connected():
        for mid in testmixer.mixerstate['macro-properties']:
            macro = testmixer.mixerstate['macro-properties'][mid]
            if macro.is_used:
                testmixer.download(0xffff, macro.index)
        return
        for sid in testmixer.mixerstate['mediaplayer-file-info']:
            still = testmixer.mixerstate['mediaplayer-file-info'][sid]
            if not still.is_used:
                continue
            print("Fetching {}".format(still.name))
            testmixer.download(0, still.index)
            # print("Fetching {}".format(still.name))
            # testmixer.download(0, still.index)


    def downloaded(store, slot, data):