summaryrefslogtreecommitdiff
path: root/src/home/media/node_client.py
blob: 18f0779897933e5f1ef54277a8d111bdcbb9083e (plain)
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
import requests
import shutil
import logging

from typing import Optional, Union
from .storage import RecordFile
from ..util import Addr
from ..camera.types import CameraType
from ..api.errors import ApiResponseError


class MediaNodeClient:
    def __init__(self, addr: Addr):
        self.endpoint = f'http://{addr[0]}:{addr[1]}'
        self.logger = logging.getLogger(self.__class__.__name__)

    def record(self, duration: int):
        return self._call('record/', params={"duration": duration})

    def record_info(self, record_id: int):
        return self._call(f'record/info/{record_id}/')

    def record_forget(self, record_id: int):
        return self._call(f'record/forget/{record_id}/')

    def record_download(self, record_id: int, output: str):
        return self._call(f'record/download/{record_id}/', save_to=output)

    def storage_list(self, extended=False, as_objects=False) -> Union[list[str], list[dict], list[RecordFile]]:
        r = self._call('storage/list/', params={'extended': int(extended)})
        files = r['files']
        if as_objects:
            return self.record_list_from_serialized(files)
        return files

    @staticmethod
    def record_list_from_serialized(files: Union[list[str], list[dict]]):
        new_files = []
        for f in files:
            kwargs = {'remote': True}
            if isinstance(f, dict):
                name = f['filename']
                kwargs['remote_filesize'] = f['filesize']
            else:
                name = f
            item = RecordFile.create(name, **kwargs)
            new_files.append(item)
        return new_files

    def storage_delete(self, file_id: str):
        return self._call('storage/delete/', params={'file_id': file_id})

    def storage_download(self, file_id: str, output: str):
        return self._call('storage/download/', params={'file_id': file_id}, save_to=output)

    def _call(self,
              method: str,
              params: dict = None,
              save_to: Optional[str] = None):
        kwargs = {}
        if isinstance(params, dict):
            kwargs['params'] = params
        if save_to:
            kwargs['stream'] = True

        url = f'{self.endpoint}/{method}'
        self.logger.debug(f'calling {url}, kwargs: {kwargs}')

        r = requests.get(url, **kwargs)
        if r.status_code != 200:
            response = r.json()
            raise ApiResponseError(status_code=r.status_code,
                                   error_type=response['error'],
                                   error_message=response['message'] or None,
                                   error_stacktrace=response['stacktrace'] if 'stacktrace' in response else None)

        if save_to:
            r.raise_for_status()
            with open(save_to, 'wb') as f:
                shutil.copyfileobj(r.raw, f)
            return True

        return r.json()['response']


class SoundNodeClient(MediaNodeClient):
    def amixer_get_all(self):
        return self._call('amixer/get-all/')

    def amixer_get(self, control: str):
        return self._call(f'amixer/get/{control}/')

    def amixer_incr(self, control: str, step: Optional[int] = None):
        params = {'step': step} if step is not None else None
        return self._call(f'amixer/incr/{control}/', params=params)

    def amixer_decr(self, control: str, step: Optional[int] = None):
        params = {'step': step} if step is not None else None
        return self._call(f'amixer/decr/{control}/', params=params)

    def amixer_mute(self, control: str):
        return self._call(f'amixer/mute/{control}/')

    def amixer_unmute(self, control: str):
        return self._call(f'amixer/unmute/{control}/')

    def amixer_cap(self, control: str):
        return self._call(f'amixer/cap/{control}/')

    def amixer_nocap(self, control: str):
        return self._call(f'amixer/nocap/{control}/')


class CameraNodeClient(MediaNodeClient):
    def capture(self,
                save_to: str,
                with_flash: bool = False):
        return self._call('capture/',
                          {'with_flash': int(with_flash)},
                          save_to=save_to)