remotivelabs-cli 0.0.25__py3-none-any.whl → 0.0.26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli/broker/brokers.py +2 -2
- cli/broker/export.py +5 -5
- cli/broker/files.py +5 -5
- cli/broker/lib/broker.py +59 -49
- cli/broker/license_flows.py +11 -9
- cli/broker/licenses.py +2 -2
- cli/broker/playback.py +14 -34
- cli/broker/record.py +3 -3
- cli/broker/scripting.py +4 -4
- cli/broker/signals.py +11 -11
- cli/cloud/__init__.py +0 -1
- cli/cloud/auth.py +40 -35
- cli/cloud/auth_tokens.py +39 -36
- cli/cloud/brokers.py +24 -33
- cli/cloud/cloud_cli.py +9 -6
- cli/cloud/configs.py +19 -11
- cli/cloud/filestorage.py +63 -51
- cli/cloud/projects.py +10 -7
- cli/cloud/recordings.py +127 -108
- cli/cloud/recordings_playback.py +52 -39
- cli/cloud/rest_helper.py +247 -196
- cli/cloud/resumable_upload.py +9 -8
- cli/cloud/sample_recordings.py +5 -5
- cli/cloud/service_account_tokens.py +18 -16
- cli/cloud/service_accounts.py +9 -9
- cli/connect/__init__.py +0 -1
- cli/connect/connect.py +7 -6
- cli/connect/protopie/protopie.py +32 -16
- cli/errors.py +6 -5
- cli/remotive.py +13 -9
- cli/requirements.txt +4 -1
- cli/settings.py +9 -9
- cli/tools/__init__.py +0 -1
- cli/tools/can/__init__.py +0 -1
- cli/tools/can/can.py +8 -8
- cli/tools/tools.py +2 -2
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/METADATA +5 -3
- remotivelabs_cli-0.0.26.dist-info/RECORD +44 -0
- remotivelabs_cli-0.0.25.dist-info/RECORD +0 -44
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/entry_points.txt +0 -0
cli/cloud/recordings.py
CHANGED
@@ -9,32 +9,34 @@ import tempfile
|
|
9
9
|
import time
|
10
10
|
import urllib.parse
|
11
11
|
from pathlib import Path
|
12
|
-
from typing import Dict, List, Union
|
12
|
+
from typing import Any, Dict, List, Tuple, Union
|
13
|
+
from urllib.parse import quote
|
13
14
|
|
14
15
|
import grpc
|
15
16
|
import requests
|
16
17
|
import typer
|
17
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn, track
|
18
|
+
from rich.progress import Progress, SpinnerColumn, TaskID, TextColumn, track
|
18
19
|
|
19
20
|
from cli.errors import ErrorPrinter
|
20
21
|
|
21
22
|
from ..broker.lib.broker import Broker
|
22
|
-
from . import rest_helper as rest
|
23
23
|
from .recordings_playback import app as playback_app
|
24
|
+
from .rest_helper import RestHelper as Rest
|
25
|
+
from .rest_helper import err_console
|
24
26
|
|
25
27
|
app = typer.Typer()
|
26
28
|
app.add_typer(playback_app, name="playback")
|
27
29
|
|
28
30
|
|
29
|
-
def uid(p):
|
31
|
+
def uid(p: Any) -> Any:
|
30
32
|
print(p)
|
31
33
|
return p["uid"]
|
32
34
|
|
33
35
|
|
34
36
|
# to be used in options
|
35
37
|
# autocompletion=project_names)
|
36
|
-
def project_names():
|
37
|
-
r = requests.get(f"{
|
38
|
+
def project_names() -> Any:
|
39
|
+
r = requests.get(f"{Rest.get_base_url()}/api/bu/{Rest.get_org()}/project", headers=Rest.get_headers(), timeout=60)
|
38
40
|
# sys.stderr.write(r.text)
|
39
41
|
if r.status_code == 200:
|
40
42
|
projects = r.json()
|
@@ -48,7 +50,7 @@ def project_names():
|
|
48
50
|
def list_recordings(
|
49
51
|
is_processing: bool = typer.Option(default=False, help="Use this option to see only those that are beeing processed or are invalid"),
|
50
52
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
51
|
-
):
|
53
|
+
) -> None:
|
52
54
|
"""
|
53
55
|
List all recording sessions in a project. You can choose to see all valid recordings (default) or use
|
54
56
|
--is-processing and you will get those that are currently beeing processed or that failed to be validated.
|
@@ -56,35 +58,35 @@ def list_recordings(
|
|
56
58
|
"""
|
57
59
|
|
58
60
|
if is_processing:
|
59
|
-
|
61
|
+
Rest.handle_get(f"/api/project/{project}/files/recording/processing")
|
60
62
|
else:
|
61
|
-
|
63
|
+
Rest.handle_get(f"/api/project/{project}/files/recording")
|
62
64
|
|
63
65
|
|
64
66
|
@app.command(help="Shows details about a specific recording in project")
|
65
67
|
def describe(
|
66
68
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
67
69
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
68
|
-
):
|
69
|
-
|
70
|
+
) -> None:
|
71
|
+
Rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}")
|
70
72
|
|
71
73
|
|
72
74
|
# @app.command(help="Shows details about a specific recording in project")
|
73
75
|
# def copy(recording_session: str = typer.Argument(..., help="Recording session id"),
|
74
76
|
# target_project: str = typer.Option(..., help="Which project to copy the recording to"),
|
75
77
|
# project: str = typer.Option(..., help="Project ID", envvar='REMOTIVE_CLOUD_PROJECT')):
|
76
|
-
#
|
78
|
+
# Rest.handle_post(url=f"/api/project/{project}/files/recording/{recording_session}/copy",
|
77
79
|
# body=json.dumps({'projectUid': target_project}))
|
78
80
|
|
79
81
|
|
80
|
-
def do_start(name: str, project: str, api_key: str, return_response: bool = False):
|
82
|
+
def do_start(name: str, project: str, api_key: str, return_response: bool = False) -> requests.Response | None:
|
81
83
|
if api_key == "":
|
82
84
|
body = {"size": "S"}
|
83
85
|
else:
|
84
86
|
body = {"size": "S", "apiKey": api_key}
|
85
87
|
|
86
88
|
name = name if name is not None else "personal"
|
87
|
-
return
|
89
|
+
return Rest.handle_post(
|
88
90
|
f"/api/project/{project}/brokers/{name}",
|
89
91
|
body=json.dumps(body),
|
90
92
|
return_response=return_response,
|
@@ -93,19 +95,20 @@ def do_start(name: str, project: str, api_key: str, return_response: bool = Fals
|
|
93
95
|
|
94
96
|
|
95
97
|
@app.command(help="Prepares all recording files and transformations to be available for playback")
|
96
|
-
def mount(
|
98
|
+
def mount( # noqa: C901
|
97
99
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
98
100
|
broker: str = typer.Option(None, help="Broker to use"),
|
99
101
|
ensure_broker_started: bool = typer.Option(default=False, help="Ensure broker exists, start otherwise"),
|
100
102
|
transformation_name: str = typer.Option("default", help="Specify a custom signal transformation to use"),
|
101
103
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
102
|
-
):
|
103
|
-
|
104
|
+
) -> None:
|
105
|
+
# pylint: disable=R0912
|
106
|
+
Rest.ensure_auth_token()
|
104
107
|
|
105
|
-
|
108
|
+
Rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}", return_response=True)
|
106
109
|
|
107
110
|
if broker is None:
|
108
|
-
r =
|
111
|
+
r = Rest.handle_get(url=f"/api/project/{project}/brokers/personal", return_response=True, allow_status_codes=[404])
|
109
112
|
|
110
113
|
if r.status_code == 200:
|
111
114
|
broker = r.json()["shortName"]
|
@@ -114,32 +117,35 @@ def mount(
|
|
114
117
|
r = do_start(None, project, "", return_response=True)
|
115
118
|
if r.status_code != 200:
|
116
119
|
print(r.text)
|
117
|
-
exit(0)
|
120
|
+
sys.exit(0)
|
118
121
|
broker = r.json()["shortName"]
|
119
122
|
else:
|
120
123
|
sys.stderr.write(f"Got http status code {r.status_code}")
|
121
|
-
typer.Exit(0)
|
124
|
+
raise typer.Exit(0)
|
122
125
|
else:
|
123
|
-
r =
|
126
|
+
r = Rest.handle_get(url=f"/api/project/{project}/brokers/{broker}", return_response=True, allow_status_codes=[404])
|
124
127
|
|
128
|
+
if r is None:
|
129
|
+
sys.exit(1)
|
125
130
|
if r.status_code == 404:
|
126
131
|
if ensure_broker_started:
|
127
132
|
r = do_start(broker, project, "", return_response=True)
|
128
|
-
|
133
|
+
if r is None:
|
134
|
+
sys.exit(1)
|
129
135
|
if r.status_code != 200:
|
130
136
|
print(r.text)
|
131
|
-
exit(1)
|
137
|
+
sys.exit(1)
|
132
138
|
else:
|
133
139
|
ErrorPrinter.print_generic_error(f"Broker {broker} not running")
|
134
|
-
exit(1)
|
140
|
+
sys.exit(1)
|
135
141
|
elif r.status_code != 200:
|
136
142
|
sys.stderr.write(f"Got http status code {r.status_code}")
|
137
|
-
typer.Exit(1)
|
143
|
+
raise typer.Exit(1)
|
138
144
|
broker_config_query = ""
|
139
145
|
if transformation_name != "default":
|
140
146
|
broker_config_query = f"?brokerConfigName={transformation_name}"
|
141
147
|
|
142
|
-
|
148
|
+
Rest.handle_get(
|
143
149
|
f"/api/project/{project}/files/recording/{recording_session}/upload{broker_config_query}",
|
144
150
|
params={"brokerName": broker},
|
145
151
|
return_response=True,
|
@@ -155,29 +161,33 @@ def download_recording_file(
|
|
155
161
|
..., help="Recording session id that this file belongs to", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"
|
156
162
|
),
|
157
163
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
158
|
-
):
|
159
|
-
|
164
|
+
) -> None:
|
165
|
+
Rest.ensure_auth_token()
|
166
|
+
recording_file_name_qouted = quote(recording_file_name, safe="")
|
160
167
|
get_signed_url_resp = requests.get(
|
161
|
-
f"{
|
162
|
-
headers=
|
168
|
+
f"{Rest.get_base_url()}/api/project/{project}/files/recording/{recording_session}/recording-file/{recording_file_name_qouted}",
|
169
|
+
headers=Rest.get_headers(),
|
163
170
|
allow_redirects=True,
|
171
|
+
timeout=60,
|
164
172
|
)
|
165
173
|
if get_signed_url_resp.status_code == 200:
|
166
174
|
# Next download the actual file
|
167
|
-
|
175
|
+
Rest.download_file(recording_file_name, get_signed_url_resp.json()["downloadUrl"])
|
168
176
|
print(f"Downloaded {recording_file_name}")
|
177
|
+
else:
|
178
|
+
print(get_signed_url_resp)
|
169
179
|
|
170
180
|
|
171
181
|
@app.command(name="delete")
|
172
182
|
def delete(
|
173
183
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
174
184
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
175
|
-
):
|
185
|
+
) -> None:
|
176
186
|
"""
|
177
187
|
Deletes the specified recording session including all media files and configurations.
|
178
188
|
|
179
189
|
"""
|
180
|
-
|
190
|
+
Rest.handle_delete(f"/api/project/{project}/files/recording/{recording_session}")
|
181
191
|
|
182
192
|
|
183
193
|
@app.command(name="delete-recording-file")
|
@@ -187,12 +197,12 @@ def delete_recording_file(
|
|
187
197
|
..., help="Recording session id that this file belongs to", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"
|
188
198
|
),
|
189
199
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
190
|
-
):
|
200
|
+
) -> None:
|
191
201
|
"""
|
192
202
|
Deletes the specified recording file
|
193
203
|
|
194
204
|
"""
|
195
|
-
|
205
|
+
Rest.handle_delete(f"/api/project/{project}/files/recording/{recording_session}/recording-file/{recording_file_name}")
|
196
206
|
|
197
207
|
|
198
208
|
@app.command()
|
@@ -209,19 +219,22 @@ def upload( # noqa: C901
|
|
209
219
|
),
|
210
220
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
211
221
|
recording_session: str = typer.Option(default=None, help="Optional existing recording to upload file to"),
|
212
|
-
):
|
222
|
+
) -> None:
|
223
|
+
# pylint: disable=R0912,R0914,R0915
|
213
224
|
"""
|
214
225
|
Uploads a recording to RemotiveCloud.
|
215
226
|
Except for recordings from RemotiveBroker you can also upload Can ASC (.asc), Can BLF(.blf) and Can LOG (.log, .txt)
|
216
227
|
"""
|
217
228
|
|
218
229
|
filename = os.path.basename(path.name)
|
219
|
-
|
230
|
+
Rest.ensure_auth_token()
|
220
231
|
|
221
232
|
if recording_session is None:
|
222
|
-
r =
|
233
|
+
r = Rest.handle_post(f"/api/project/{project}/files/recording/{filename}", return_response=True)
|
223
234
|
else:
|
224
|
-
r =
|
235
|
+
r = Rest.handle_post(f"/api/project/{project}/files/recording/{recording_session}/recording-file/{filename}", return_response=True)
|
236
|
+
if r is None:
|
237
|
+
return
|
225
238
|
|
226
239
|
upload_url = r.text
|
227
240
|
url_path = urllib.parse.urlparse(upload_url).path
|
@@ -234,14 +247,18 @@ def upload( # noqa: C901
|
|
234
247
|
"Something went wrong, please try again. Please contact RemotiveLabs support if this problem remains"
|
235
248
|
)
|
236
249
|
ErrorPrinter.print_hint("Please make sure to use the latest version of RemotiveCLI")
|
237
|
-
exit(1)
|
250
|
+
sys.exit(1)
|
238
251
|
|
239
|
-
upload_response =
|
252
|
+
upload_response = Rest.upload_file_with_signed_url(
|
240
253
|
path=path, url=upload_url, upload_headers={"Content-Type": "application/x-www-form-urlencoded"}, return_response=True
|
241
254
|
)
|
242
255
|
|
256
|
+
if upload_response is None:
|
257
|
+
return
|
258
|
+
|
243
259
|
# Exact same as in cloud console
|
244
260
|
def get_processing_message(step: str) -> str:
|
261
|
+
# pylint: disable=R0911
|
245
262
|
if step == "REQUESTED":
|
246
263
|
return "Preparing file..."
|
247
264
|
if step == "VALIDATING":
|
@@ -264,10 +281,12 @@ def upload( # noqa: C901
|
|
264
281
|
t = p.add_task("Processing...", total=1)
|
265
282
|
while True:
|
266
283
|
time.sleep(1)
|
267
|
-
r =
|
284
|
+
r = Rest.handle_get(
|
268
285
|
f"/api/project/{project}/files/recording/processing", return_response=True, use_progress_indicator=False
|
269
286
|
)
|
270
|
-
|
287
|
+
if r is None:
|
288
|
+
return
|
289
|
+
status_list: List[Dict[str, Any]] = r.json()
|
271
290
|
res = list(filter(lambda s: s["uploadId"] == upload_id, status_list))
|
272
291
|
if len(res) == 1:
|
273
292
|
tracking_state = res[0]
|
@@ -284,19 +303,20 @@ def upload( # noqa: C901
|
|
284
303
|
break
|
285
304
|
if error_message is not None:
|
286
305
|
ErrorPrinter.print_generic_error(error_message)
|
287
|
-
exit(1)
|
306
|
+
sys.exit(1)
|
288
307
|
|
289
308
|
else:
|
290
|
-
|
309
|
+
err_console.print(f":boom: [bold red]Got status code[/bold red]: {upload_response.status_code} {upload_response.text}")
|
291
310
|
|
292
311
|
|
293
312
|
@app.command()
|
294
|
-
def upload_broker_configuration(
|
313
|
+
def upload_broker_configuration( # noqa: C901
|
295
314
|
directory: str = typer.Argument(..., help="Configuration directory"),
|
296
315
|
recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
297
316
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
298
317
|
overwrite: bool = typer.Option(False, help="Overwrite existing configuration if it exists"),
|
299
|
-
):
|
318
|
+
) -> None:
|
319
|
+
# pylint: disable=R0914,R0915
|
300
320
|
# Must end with /
|
301
321
|
if not directory.endswith("/"):
|
302
322
|
directory = f"{directory}/"
|
@@ -319,14 +339,16 @@ def upload_broker_configuration(
|
|
319
339
|
# name already exists
|
320
340
|
#
|
321
341
|
# task = progress.add_task(description=f"Preparing upload of {broker_config_dir_name}", total=1)
|
322
|
-
details_resp =
|
342
|
+
details_resp = Rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}", return_response=True)
|
343
|
+
if details_resp is None:
|
344
|
+
return
|
323
345
|
details = details_resp.json()
|
324
346
|
existing_configs = details["brokerConfigurations"]
|
325
347
|
if len(existing_configs) > 0:
|
326
348
|
data = list(filter(lambda x: x["name"] == broker_config_dir_name, existing_configs))
|
327
349
|
if len(data) > 0:
|
328
350
|
if overwrite:
|
329
|
-
|
351
|
+
Rest.handle_delete(
|
330
352
|
f"/api/project/{project}/files/recording/{recording_session}/configuration/{broker_config_dir_name}", quiet=True
|
331
353
|
)
|
332
354
|
else:
|
@@ -349,11 +371,13 @@ def upload_broker_configuration(
|
|
349
371
|
#
|
350
372
|
json_request_upload_urls_req = {"name": "not_used", "paths": list(map(lambda x: x["remote_path"], file_infos))}
|
351
373
|
|
352
|
-
response =
|
374
|
+
response = Rest.handle_put(
|
353
375
|
url=f"/api/project/{project}/files/recording/{recording_session}/configuration",
|
354
376
|
return_response=True,
|
355
377
|
body=json.dumps(json_request_upload_urls_req),
|
356
378
|
)
|
379
|
+
if response is None:
|
380
|
+
return
|
357
381
|
if response.status_code != 200:
|
358
382
|
print("Failed to prepare configuration upload")
|
359
383
|
print(f"{response.text} - {response.status_code}")
|
@@ -371,12 +395,12 @@ def upload_broker_configuration(
|
|
371
395
|
path = file["local_path"]
|
372
396
|
url = upload_urls[key]
|
373
397
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
374
|
-
r = requests.put(url, open(path, "rb"), headers=headers)
|
398
|
+
r = requests.put(url, open(path, "rb"), headers=headers, timeout=60) # pylint: disable=R1732
|
375
399
|
if r.status_code != 200:
|
376
400
|
print("Failed to upload broker configuration")
|
377
401
|
print(r.status_code)
|
378
402
|
print(r.text)
|
379
|
-
typer.Exit(1)
|
403
|
+
raise typer.Exit(1)
|
380
404
|
|
381
405
|
print(f"Successfully uploaded broker configuration {broker_config_dir_name}")
|
382
406
|
|
@@ -386,32 +410,36 @@ def download_configuration(
|
|
386
410
|
broker_config_name: str = typer.Argument(..., help="Broker config name"),
|
387
411
|
recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
388
412
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
389
|
-
):
|
390
|
-
|
391
|
-
r =
|
413
|
+
) -> None:
|
414
|
+
Rest.ensure_auth_token()
|
415
|
+
r = Rest.handle_get(
|
392
416
|
url=f"/api/project/{project}/files/recording/{recording_session}/configuration/{broker_config_name}", return_response=True
|
393
417
|
)
|
418
|
+
if r is None:
|
419
|
+
return
|
394
420
|
filename = get_filename_from_cd(r.headers.get("content-disposition"))
|
395
|
-
|
396
|
-
|
421
|
+
if filename is not None:
|
422
|
+
with open(filename, "wb") as f:
|
423
|
+
f.write(r.content)
|
424
|
+
print(f"Downloaded file {filename}")
|
397
425
|
|
398
426
|
|
399
|
-
@app.command(help="
|
427
|
+
@app.command(help="Delete the specified broker configuration")
|
400
428
|
def delete_configuration(
|
401
429
|
broker_config_name: str = typer.Argument(..., help="Broker config name"),
|
402
430
|
recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
403
431
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
404
|
-
):
|
405
|
-
|
432
|
+
) -> None:
|
433
|
+
Rest.handle_delete(url=f"/api/project/{project}/files/recording/{recording_session}/configuration/{broker_config_name}")
|
406
434
|
|
407
435
|
|
408
436
|
@app.command(help="Copy recording to another project")
|
409
437
|
def copy(
|
410
438
|
recording_session: str = typer.Argument(..., help="Recording session id"),
|
411
|
-
project: str = typer.Option(..., help="
|
439
|
+
project: str = typer.Option(..., help="Source project", envvar="REMOTIVE_CLOUD_PROJECT"),
|
412
440
|
destination_project: str = typer.Option(..., help="Destination project"),
|
413
|
-
):
|
414
|
-
|
441
|
+
) -> None:
|
442
|
+
Rest.handle_post(
|
415
443
|
url=f"/api/project/{project}/files/recording/{recording_session}/copy", body=json.dumps({"projectUid": destination_project})
|
416
444
|
)
|
417
445
|
|
@@ -421,7 +449,7 @@ def play(
|
|
421
449
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
422
450
|
broker: str = typer.Option(None, help="Broker to use"),
|
423
451
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
424
|
-
):
|
452
|
+
) -> None:
|
425
453
|
"""
|
426
454
|
Plays a recording (Deprecated - Use recordings playback play)"
|
427
455
|
"""
|
@@ -433,32 +461,20 @@ def pause(
|
|
433
461
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
434
462
|
broker: str = typer.Option(None, help="Broker to use"),
|
435
463
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
436
|
-
):
|
464
|
+
) -> None:
|
437
465
|
"""
|
438
466
|
Pause recording (Deprecated - Use recordings playback pause")
|
439
467
|
"""
|
440
468
|
_do_change_playback_mode("pause", recording_session, broker, project)
|
441
469
|
|
442
470
|
|
443
|
-
@app.command(name="playback-status", deprecated=True)
|
444
|
-
def playback_status(
|
445
|
-
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
446
|
-
broker: str = typer.Option(None, help="Broker to use"),
|
447
|
-
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
448
|
-
):
|
449
|
-
"""
|
450
|
-
Playback progress (Deprecated - Use recordings playback progress)
|
451
|
-
"""
|
452
|
-
_do_change_playback_mode("status", recording_session, broker, project)
|
453
|
-
|
454
|
-
|
455
471
|
@app.command(deprecated=True)
|
456
472
|
def seek(
|
457
473
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
458
474
|
seconds: int = typer.Option(..., min=0, help="Target offset in seconds"),
|
459
475
|
broker: str = typer.Option(None, help="Broker to use"),
|
460
476
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
461
|
-
):
|
477
|
+
) -> None:
|
462
478
|
"""
|
463
479
|
Seek into recording (Deprecated - Use recordings playback seek)
|
464
480
|
"""
|
@@ -470,30 +486,36 @@ def stop(
|
|
470
486
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
471
487
|
broker: str = typer.Option(None, help="Broker to use"),
|
472
488
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
473
|
-
):
|
489
|
+
) -> None:
|
474
490
|
"""
|
475
491
|
Stop recording (Deprecated - Use recordings playback stop)
|
476
492
|
"""
|
477
493
|
_do_change_playback_mode("stop", recording_session, broker, project)
|
478
494
|
|
479
495
|
|
480
|
-
|
481
|
-
|
496
|
+
# pylint: disable-next=C0301
|
497
|
+
def _do_change_playback_mode(mode: str, recording_session: str, broker_name: str, project: str, seconds: int | None = None) -> None: # noqa: C901
|
498
|
+
# pylint: disable=R0912,R0914,R0915
|
499
|
+
response = Rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}", return_response=True)
|
500
|
+
if response is None:
|
501
|
+
return
|
482
502
|
r = json.loads(response.text)
|
483
|
-
recordings:
|
503
|
+
recordings: List[Any] = r["recordings"]
|
484
504
|
files = list(map(lambda rec: {"recording": rec["fileName"], "namespace": rec["metadata"]["namespace"]}, recordings))
|
485
505
|
|
486
|
-
if
|
487
|
-
response =
|
506
|
+
if broker_name is not None:
|
507
|
+
response = Rest.handle_get(f"/api/project/{project}/brokers/{broker_name}", return_response=True, allow_status_codes=[404])
|
488
508
|
else:
|
489
|
-
response =
|
509
|
+
response = Rest.handle_get(f"/api/project/{project}/brokers/personal", return_response=True, allow_status_codes=[404])
|
510
|
+
if response is None:
|
511
|
+
return
|
490
512
|
if response.status_code == 404:
|
491
513
|
broker_arg = ""
|
492
|
-
if
|
493
|
-
broker_arg = f" --broker {
|
514
|
+
if broker_name is not None:
|
515
|
+
broker_arg = f" --broker {broker_name} --ensure-broker-started"
|
494
516
|
ErrorPrinter.print_generic_error("You need to mount the recording before you play")
|
495
517
|
ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session}{broker_arg} --project {project}")
|
496
|
-
exit(1)
|
518
|
+
sys.exit(1)
|
497
519
|
|
498
520
|
broker_info = json.loads(response.text)
|
499
521
|
broker = Broker(broker_info["url"], None)
|
@@ -503,7 +525,7 @@ def _do_change_playback_mode(mode: str, recording_session: str, broker: str, pro
|
|
503
525
|
# error messages
|
504
526
|
tmp = os.path.join(tempfile.gettempdir(), os.urandom(24).hex())
|
505
527
|
broker.download(".cloud.context", tmp, True)
|
506
|
-
with open(tmp, "r") as f:
|
528
|
+
with open(tmp, "r", encoding="utf8") as f:
|
507
529
|
json_context = json.loads(f.read())
|
508
530
|
if json_context["recordingSessionId"] != recording_session:
|
509
531
|
ErrorPrinter.print_generic_error(
|
@@ -511,33 +533,30 @@ def _do_change_playback_mode(mode: str, recording_session: str, broker: str, pro
|
|
511
533
|
f"which not the same as you are trying to {mode}, use cmd below to mount this recording"
|
512
534
|
)
|
513
535
|
ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
514
|
-
exit(1)
|
536
|
+
sys.exit(1)
|
515
537
|
except grpc.RpcError as rpc_error:
|
516
|
-
if rpc_error.code() == grpc.StatusCode.NOT_FOUND:
|
517
|
-
|
518
|
-
|
519
|
-
else:
|
520
|
-
|
521
|
-
exit(1)
|
538
|
+
# if rpc_error.code() == grpc.StatusCode.NOT_FOUND:
|
539
|
+
# ErrorPrinter.print_generic_error(f"You must use mount to prepare a recording before you can use {mode}")
|
540
|
+
# ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
541
|
+
# else:
|
542
|
+
ErrorPrinter.print_grpc_error(rpc_error)
|
543
|
+
sys.exit(1)
|
522
544
|
if mode == "pause":
|
523
545
|
broker.pause_play(files, True)
|
524
546
|
elif mode == "play":
|
525
547
|
r = broker.play(files, True)
|
526
548
|
elif mode == "seek":
|
527
|
-
|
549
|
+
if seconds is not None:
|
550
|
+
broker.seek(files, int(seconds * 1000000), True)
|
551
|
+
else:
|
552
|
+
broker.seek(files, 0, True)
|
528
553
|
elif mode == "stop":
|
529
554
|
broker.seek(files, 0, True)
|
530
|
-
elif mode == "status":
|
531
|
-
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True)
|
532
|
-
t = p.add_task("label", total=1)
|
533
|
-
|
534
|
-
with p:
|
535
|
-
broker.listen_on_playback(lambda c: p.update(t, description=str(c)))
|
536
555
|
else:
|
537
|
-
raise
|
556
|
+
raise ValueError(f"Illegal command {mode}")
|
538
557
|
|
539
558
|
|
540
|
-
def get_filename_from_cd(cd):
|
559
|
+
def get_filename_from_cd(cd: Union[str, None]) -> Union[str, None]:
|
541
560
|
"""
|
542
561
|
Get filename from content-disposition
|
543
562
|
"""
|
@@ -546,10 +565,10 @@ def get_filename_from_cd(cd):
|
|
546
565
|
fname = re.findall("filename=(.+)", cd)
|
547
566
|
if len(fname) == 0:
|
548
567
|
return None
|
549
|
-
return fname[0]
|
568
|
+
return str(fname[0])
|
550
569
|
|
551
570
|
|
552
|
-
def use_progress(label: str):
|
571
|
+
def use_progress(label: str) -> Tuple[Progress, TaskID]:
|
553
572
|
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True)
|
554
573
|
t = p.add_task(label, total=1)
|
555
574
|
return (p, t)
|