~martijnbraam/pyatem

ref: d2dc828df01451fe3677dd31765ebe8fac3d4b56 pyatem/pyhyperdeckemu.py -rw-r--r-- 6.6 KiB
d2dc828dJan Kundrát proxy: mqtt: allow arbitrary writes 7 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import socketserver
import logging
import time


class VirtualHyperdeck(socketserver.BaseRequestHandler):
    def setup(self):
        self.remote = True
        self.uid = '1234'

        self.notify_transport = False
        self.notify_slot = False
        self.notify_remote = False
        self.notify_configuration = False

        self.transport_status = 'stopped'
        self.transport_speed = 0
        self.transport_slot_id = 1
        self.timecode = "01:00:00:00"
        self.display_timecode = "01:00:00:00"
        self.clip_id = 0
        self.video_format = "720p50"
        self.loop = False

        self.clips = [
            (1, "Test clip", 30),
            (2, "Another clip", 45)
        ]

    def handle(self):
        logging.info('connected')
        self.send(b'500 connection info:\r\nprotocol version: 1.9\r\nmodel: Linux\r\n\r\n')
        while True:
            data = self.request.recv(1024)
            if len(data) == 0:
                logging.info('disconnected')
                break
            logging.debug('< ' + data.decode())
            self.handle_line(data.decode().strip())

    def send(self, data):
        logging.debug('> ' + data.decode())
        self.request.sendall(data)

    def parse_line(self, line):
        parameters = {}
        parts = line.split(' ')
        name = parts[0].rstrip(':')
        key = None
        for part in parts[1:]:
            if part[-1] == ':':
                key = part.rstrip(':')
            else:
                if key not in parameters:
                    parameters[key] = ''
                parameters[key] += part
                parameters[key] = parameters[key].strip()
        return name, parameters

    def handle_line(self, line):
        if line.startswith('notify:'):
            _, parameters = self.parse_line(line)
            if 'transport' in parameters:
                self.notify_transport = parameters['transport'] == 'true'
            if 'slot' in parameters:
                self.notify_slot = parameters['slot'] == 'true'
            if 'remote' in parameters:
                self.notify_remote = parameters['remote'] == 'true'
            if 'configuration' in parameters:
                self.notify_configuration = parameters['configuration'] == 'true'

            self._send_notify(sync=True)

        elif line.startswith('transport info'):
            self._send_transport_info(sync=True)
        elif line == 'remote':
            self._send_remote_info(sync=True)
        elif line == 'clips count':
            self._send_clips_count()
        elif line.startswith('slot info'):
            _, parameters = self.parse_line(line)
            self._send_slot_info(sync=True)
        elif line.startswith('clips get: clip'):
            _, parameters = self.parse_line(line)
            self._send_clip_info(sync=True, clip=int(parameters['id']))
        elif line.startswith('goto: clip'):
            _, parameters = self.parse_line(line)
            self.clip_id = int(parameters['id'])
            self.send(b'200 ok\r\n')
            if self.notify_transport:
                self._send_transport_info(sync=False)
        else:
            time.sleep(0.5)

    def _send_notify(self, sync=True):
        if sync:
            response = b'209 notify:\r\n'
        else:
            response = b'509 remote info:\r\n'
        response += 'transport: {}\r\n'.format('true' if self.notify_transport else 'false').encode()
        response += 'slot: {}\r\n'.format('true' if self.notify_slot else 'false').encode()
        response += 'remote: {}\r\n'.format('true' if self.notify_remote else 'false').encode()
        response += 'configuration: {}\r\n'.format('true' if self.notify_configuration else 'false').encode()
        response += b'\r\n'
        self.send(response)

    def _send_remote_info(self, sync=True):
        if sync:
            response = b'210 remote info:\r\n'
        else:
            response = b'510 remote info:\r\n'
        response += b'enabled: true\r\n'
        response += b'override: true\r\n'
        response += b'\r\n'
        self.send(response)

    def _send_transport_info(self, sync=True):
        if sync:
            response = b'208 transport info:\r\n'
        else:
            response = b'508 transport info:\r\n'

        response += 'status: {}\r\n'.format(self.transport_status).encode()
        response += 'speed: {}\r\n'.format(self.transport_speed).encode()
        response += 'slot id: {}\r\n'.format(self.transport_slot_id or 'none').encode()
        response += 'timecode: {}\r\n'.format(self.timecode).encode()
        response += 'display timecode: {}\r\n'.format(self.display_timecode).encode()
        response += 'clip id: {}\r\n'.format(self.clip_id).encode()
        response += 'video format: {}\r\n'.format(self.video_format or 'none').encode()
        response += 'loop: {}\r\n'.format('true' if self.loop else 'false').encode()
        response += b'\r\n'
        self.send(response)

    def _send_slot_info(self, sync=True):
        if sync:
            response = b'202 slot info:\r\n'
        else:
            response = b'502 slot info:\r\n'
        response += b'slot id: 1\r\n'
        response += b'status: mounted\r\n'
        response += b'volume name: SD\r\n'
        response += b'recording time: 0\r\n'
        response += 'video format: {}\r\n'.format(self.video_format).encode()
        response += b'\r\n'
        self.send(response)

    def _send_configuration(self, sync=True):
        if sync:
            response = b'211 configuration:\r\n'
        else:
            response = b'511 configuration:\r\n'
        response += b'audio input: embedded\r\n'
        response += b'video input: HDMI\r\n'
        response += b'file format: H.264High\r\n'
        response += b'\r\n'
        self.send(response)

    def _send_clips_count(self):
        response = b'214 clips count:\r\n'
        response += 'clip count: {}\r\n'.format(len(self.clips)).encode()
        response += b'\r\n'
        self.send(response)

    def _send_clip_info(self, sync=True, clip=0):
        response = b'205 clips info:\r\n'
        response += 'clip count: {}\r\n'.format(len(self.clips)).encode()
        for clip in self.clips:
            cliplen = '00:00:{}:00'.format(clip[2])
            response += '{}: {} 00:00:00:00 {}\r\n'.format(clip[0], clip[1], cliplen).encode()
        response += b'\r\n'
        self.send(response)


if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    server = socketserver.TCPServer(('0.0.0.0', 9993), VirtualHyperdeck, bind_and_activate=False)
    server.allow_reuse_address = True
    server.server_bind()
    server.server_activate()
    server.serve_forever()