mvsep-cli 1.0.0__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.
mvsep_cli/__init__.py ADDED
File without changes
mvsep_cli/__main__.py ADDED
@@ -0,0 +1,527 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import sys
4
+ import os
5
+ import time
6
+
7
+ from mvsep_cli.api import MVSEP_API
8
+ from mvsep_cli.config import Config, get_api_token
9
+
10
+
11
+ def cmd_upload(args):
12
+ import sys
13
+
14
+ api = MVSEP_API(get_api_token())
15
+ config = Config()
16
+
17
+ output_format = (
18
+ args.output_format
19
+ if args.output_format is not None
20
+ else config.default_output_format
21
+ )
22
+ sep_type = args.sep_type if args.sep_type is not None else config.default_sep_type
23
+
24
+ print(f"Audio file: {args.audio_file}", flush=True)
25
+ print(f"Separation type: {sep_type}", flush=True)
26
+ print(f"Output format: {output_format}", flush=True)
27
+ print("Note: Uploading large file, please wait...", flush=True)
28
+
29
+ result = api.create_task(
30
+ audio_file=args.audio_file,
31
+ sep_type=sep_type,
32
+ output_format=output_format,
33
+ add_opt1=args.add_opt1,
34
+ add_opt2=args.add_opt2,
35
+ is_demo=args.is_demo,
36
+ )
37
+
38
+ hash_val = result["hash"]
39
+ print(f"\nTask created!", flush=True)
40
+ print(f"Hash: {hash_val}", flush=True)
41
+ print(f"Link: {result['link']}", flush=True)
42
+ print(f"\nUse following commands to check status:", flush=True)
43
+ print(f" mvsep status {hash_val}", flush=True)
44
+ print(f" mvsep wait {hash_val}", flush=True)
45
+ print(f" mvsep download {hash_val}", flush=True)
46
+
47
+ if args.wait:
48
+ interval = args.interval if args.interval is not None else config.poll_interval
49
+ print("\nWaiting for completion...")
50
+ try:
51
+ final_result = api.wait_for_completion(
52
+ hash_val, interval=interval, max_wait=args.timeout
53
+ )
54
+ status = final_result.get("status")
55
+ if status == "done":
56
+ output_dir = (
57
+ args.output_dir if args.output_dir else config.default_output_dir
58
+ )
59
+ if not output_dir:
60
+ output_dir = os.path.dirname(os.path.abspath(args.audio_file))
61
+ downloaded = api.download_results(hash_val, output_dir)
62
+ print(f"\nDownloaded {len(downloaded)} files:")
63
+ for f in downloaded:
64
+ print(f" - {f}")
65
+ return 0
66
+ except TimeoutError:
67
+ print(
68
+ f"\nTimeout, but task is still running. Use 'mvsep wait {hash_val}' to continue waiting."
69
+ )
70
+
71
+ return 0
72
+
73
+
74
+ def cmd_status(args):
75
+ api = MVSEP_API(get_api_token())
76
+
77
+ result = api.get_status(args.hash)
78
+ status = result.get("status")
79
+
80
+ print(f"Task hash: {args.hash}")
81
+ print(f"Status: {status}")
82
+
83
+ if status == "done":
84
+ data = result.get("data", {})
85
+ print(f"Algorithm: {data.get('algorithm')}")
86
+ print(f"Output format: {data.get('output_format')}")
87
+ files = data.get("files", [])
88
+ print(f"Output files ({len(files)}):")
89
+ for f in files:
90
+ name = f.get("name") or "unknown"
91
+ url = f.get("url", "N/A")
92
+ print(f" - {name}: {url}")
93
+ elif status == "failed":
94
+ msg = result.get("data", {}).get("message", "Unknown error")
95
+ print(f"Error: {msg}")
96
+ elif status in ("waiting", "distributing"):
97
+ queue = result.get("data", {}).get("queue_count", "?")
98
+ order = result.get("data", {}).get("current_order", "?")
99
+ print(f"Queue count: {queue}")
100
+ print(f"Current order: {order}")
101
+ elif status == "processing":
102
+ print("Task is being processed...")
103
+ elif status == "merging":
104
+ chunks = result.get("data", {}).get("finished_chunks", "?")
105
+ total = result.get("data", {}).get("all_chunks", "?")
106
+ print(f"Merging: {chunks}/{total} chunks")
107
+
108
+ return 0 if status == "done" else 1
109
+
110
+
111
+ def cmd_wait(args):
112
+ api = MVSEP_API(get_api_token())
113
+ config = Config()
114
+
115
+ interval = args.interval if args.interval is not None else config.poll_interval
116
+
117
+ print(f"Waiting for task {args.hash} to complete...")
118
+ print(f"Polling interval: {interval}s")
119
+
120
+ try:
121
+ result = api.wait_for_completion(
122
+ args.hash, interval=interval, max_wait=args.timeout
123
+ )
124
+
125
+ status = result.get("status")
126
+
127
+ if status == "done":
128
+ print("\nTask completed successfully!")
129
+ data = result.get("data", {})
130
+ print(f"Algorithm: {data.get('algorithm')}")
131
+ files = data.get("files", [])
132
+ print(f"Output files: {len(files)}")
133
+ return 0
134
+ elif status == "failed":
135
+ msg = result.get("data", {}).get("message", "Unknown error")
136
+ print(f"\nTask failed: {msg}")
137
+ return 1
138
+ else:
139
+ print(f"\nTask status: {status}")
140
+ return 1
141
+
142
+ except TimeoutError as e:
143
+ print(f"\nTimeout: {e}")
144
+ return 1
145
+
146
+
147
+ def cmd_download(args):
148
+ api = MVSEP_API(get_api_token())
149
+ config = Config()
150
+
151
+ output_dir = args.output_dir if args.output_dir else config.default_output_dir
152
+
153
+ print(f"Downloading results for {args.hash}...")
154
+
155
+ downloaded = api.download_results(args.hash, output_dir)
156
+
157
+ print(f"\nDownloaded {len(downloaded)} files:")
158
+ for f in downloaded:
159
+ print(f" - {f}")
160
+
161
+ return 0
162
+
163
+
164
+ def cmd_run(args):
165
+ api = MVSEP_API(get_api_token())
166
+ config = Config()
167
+
168
+ # Use args value if provided, otherwise use config default
169
+ output_format = (
170
+ args.output_format
171
+ if args.output_format is not None
172
+ else config.default_output_format
173
+ )
174
+ # sep_type is required, no default
175
+ sep_type = args.sep_type if args.sep_type is not None else None
176
+ interval = args.interval if args.interval is not None else config.poll_interval
177
+
178
+ if sep_type is None:
179
+ print(
180
+ "Error: sep_type is required. Use -t <type> or mvsep list to see available types."
181
+ )
182
+ return 1
183
+
184
+ print(f"Starting separation task for: {args.audio_file}")
185
+ print(f"Separation type: {sep_type}")
186
+ print(f"Output format: {output_format}")
187
+
188
+ result = api.create_task(
189
+ audio_file=args.audio_file,
190
+ sep_type=sep_type,
191
+ output_format=output_format,
192
+ add_opt1=args.add_opt1,
193
+ add_opt2=args.add_opt2,
194
+ is_demo=args.is_demo,
195
+ )
196
+
197
+ hash_value = result["hash"]
198
+ print(f"\nTask created! Hash: {hash_value}")
199
+ print("Waiting for completion...")
200
+
201
+ try:
202
+ final_result = api.wait_for_completion(
203
+ hash_value, interval=interval, max_wait=args.timeout
204
+ )
205
+
206
+ status = final_result.get("status")
207
+
208
+ if status == "done":
209
+ print("\nSeparation completed!")
210
+
211
+ output_dir = (
212
+ args.output_dir if args.output_dir else config.default_output_dir
213
+ )
214
+ if not output_dir:
215
+ output_dir = os.path.dirname(os.path.abspath(args.audio_file))
216
+ downloaded = api.download_results(hash_value, output_dir)
217
+
218
+ print(f"\nDownloaded {len(downloaded)} files:")
219
+ for f in downloaded:
220
+ print(f" - {f}")
221
+
222
+ return 0
223
+ else:
224
+ print(f"\nTask ended with status: {status}")
225
+ return 1
226
+
227
+ except TimeoutError as e:
228
+ print(f"\nTimeout: {e}")
229
+ return 1
230
+
231
+
232
+ def cmd_config(args):
233
+ config = Config()
234
+
235
+ if not args.subcommand:
236
+ print(
237
+ "Usage: mvsep config <show|set-token|set-mirror|set-output-dir|set-output-format|set-interval>"
238
+ )
239
+ print("\nCommands:")
240
+ print(" show Show current config")
241
+ print(" set-token <token> Set API token")
242
+ print(" set-mirror <mirror> Set mirror (main/mirror)")
243
+ print(" set-output-dir <dir> Set default output directory")
244
+ print(" set-output-format <val> Set default output format")
245
+ print(" set-interval <val> Set poll interval")
246
+ return 0
247
+
248
+ if args.subcommand == "show":
249
+ print(
250
+ f"API Token: {'*' * 8}{config.api_token[-4:] if config.api_token else 'Not set'}"
251
+ )
252
+ print(f"Mirror: {config.mirror} ({config.base_url})")
253
+ print(f"Default output_dir: {config.default_output_dir}")
254
+ print(f"Default output_format: {config.default_output_format}")
255
+ print(f"Poll interval: {config.poll_interval}s")
256
+
257
+ elif args.subcommand == "set-token":
258
+ if not args.token:
259
+ print("Error: Token required. Usage: mvsep config set-token <token>")
260
+ return 1
261
+ config.api_token = args.token
262
+ print("API token saved!")
263
+
264
+ elif args.subcommand == "set-mirror":
265
+ if not args.value:
266
+ print(
267
+ "Error: Mirror required. Usage: mvsep config set-mirror <main|mirror>"
268
+ )
269
+ return 1
270
+ try:
271
+ config.mirror = args.value
272
+ print(f"Mirror set to: {args.value} ({config.base_url})")
273
+ except ValueError as e:
274
+ print(f"Error: {e}")
275
+ return 1
276
+
277
+ elif args.subcommand == "set-output-dir":
278
+ config.default_output_dir = args.value
279
+ print(f"Default output_dir set to: {args.value}")
280
+
281
+ elif args.subcommand == "set-output-format":
282
+ config.default_output_format = args.value
283
+ print(f"Default output_format set to: {args.value}")
284
+
285
+ elif args.subcommand == "set-interval":
286
+ config.poll_interval = args.value
287
+ print(f"Poll interval set to: {args.value}s")
288
+
289
+ return 0
290
+
291
+
292
+ def cmd_history(args):
293
+ import json
294
+ from mvsep_cli.api import TASK_META_FILE
295
+
296
+ if not os.path.exists(TASK_META_FILE):
297
+ print("No task history found.")
298
+ return 0
299
+
300
+ with open(TASK_META_FILE, "r") as f:
301
+ meta = json.load(f)
302
+
303
+ if not meta:
304
+ print("No task history found.")
305
+ return 0
306
+
307
+ print(f"Task history ({len(meta)} tasks):")
308
+ print()
309
+ for hash_val, info in meta.items():
310
+ original_name = info.get("original_name", "unknown")
311
+ print(f" {hash_val}")
312
+ print(f" Original file: {original_name}")
313
+ print(f" Download: mvsep download {hash_val}")
314
+ print()
315
+
316
+
317
+ def cmd_list_types(args):
318
+ config = Config()
319
+
320
+ if args.models:
321
+ sep_type = args.models
322
+ model_opts = config.get_model_options(sep_type)
323
+ sep_name = config.SEP_TYPES.get(sep_type, "Unknown")
324
+
325
+ print(f"Separation type: {sep_type} - {sep_name}")
326
+
327
+ if not model_opts:
328
+ print("No additional model options for this separation type.")
329
+ return 0
330
+
331
+ for opt_key, opt_list in model_opts.items():
332
+ print(f"\n{opt_key}:")
333
+ for opt in opt_list:
334
+ print(f" {opt['value']:>3} - {opt['name']}")
335
+ return 0
336
+
337
+ if args.search:
338
+ keyword = args.search.lower()
339
+ results = [
340
+ (val, name)
341
+ for val, name in Config.SEP_TYPES.items()
342
+ if keyword in name.lower()
343
+ ]
344
+ if results:
345
+ print(f"Search results for '{args.search}':")
346
+ for val, name in results:
347
+ print(f" {val}: {name}")
348
+ else:
349
+ print(f"No results found for '{args.search}'")
350
+ return 0
351
+
352
+ if args.popular:
353
+ print("Popular separation types:")
354
+ for val, name in Config.POPULAR_SEP_TYPES.items():
355
+ print(f" {val}: {name}")
356
+ elif args.formats:
357
+ print("All output formats:")
358
+ for val, name in Config.OUTPUT_FORMATS.items():
359
+ print(f" {val}: {name}")
360
+ else:
361
+ print("All separation types:")
362
+ for val, name in Config.SEP_TYPES.items():
363
+ print(f" {val}: {name}")
364
+
365
+ return 0
366
+
367
+
368
+ def main():
369
+ parser = argparse.ArgumentParser(
370
+ description="MVSEP CLI - Music separation tool",
371
+ formatter_class=argparse.RawDescriptionHelpFormatter,
372
+ )
373
+
374
+ subparsers = parser.add_subparsers(dest="command", help="Commands")
375
+
376
+ p_upload = subparsers.add_parser(
377
+ "upload", help="Upload audio and create separation task"
378
+ )
379
+ p_upload.add_argument("audio_file", help="Audio file to process")
380
+ p_upload.add_argument(
381
+ "-t",
382
+ "--sep-type",
383
+ type=int,
384
+ default=None,
385
+ help="Separation type (default: config value)",
386
+ )
387
+ p_upload.add_argument(
388
+ "-f",
389
+ "--output-format",
390
+ type=int,
391
+ default=None,
392
+ help="Output format (default: config value)",
393
+ )
394
+ p_upload.add_argument("--add-opt1", help="Additional option 1")
395
+ p_upload.add_argument("--add-opt2", help="Additional option 2")
396
+ p_upload.add_argument(
397
+ "--demo", dest="is_demo", action="store_true", help="Publish to demo page"
398
+ )
399
+ p_upload.add_argument(
400
+ "--wait", action="store_true", help="Wait for completion after upload"
401
+ )
402
+ p_upload.add_argument(
403
+ "-o",
404
+ "--output-dir",
405
+ type=str,
406
+ default=None,
407
+ help="Output directory for downloaded files",
408
+ )
409
+ p_upload.add_argument(
410
+ "-i", "--interval", type=int, default=None, help="Poll interval in seconds"
411
+ )
412
+ p_upload.add_argument("--timeout", type=int, help="Max wait time in seconds")
413
+ p_upload.set_defaults(func=cmd_upload)
414
+
415
+ p_status = subparsers.add_parser("status", help="Check task status")
416
+ p_status.add_argument("hash", help="Task hash")
417
+ p_status.set_defaults(func=cmd_status)
418
+
419
+ p_wait = subparsers.add_parser("wait", help="Wait for task completion")
420
+ p_wait.add_argument("hash", help="Task hash")
421
+ p_wait.add_argument(
422
+ "-i", "--interval", type=int, default=None, help="Poll interval in seconds"
423
+ )
424
+ p_wait.add_argument("--timeout", type=int, help="Max wait time in seconds")
425
+ p_wait.set_defaults(func=cmd_wait)
426
+
427
+ p_download = subparsers.add_parser("download", help="Download results")
428
+ p_download.add_argument("hash", help="Task hash")
429
+ p_download.add_argument("-o", "--output-dir", default=None, help="Output directory")
430
+ p_download.set_defaults(func=cmd_download)
431
+
432
+ p_run = subparsers.add_parser(
433
+ "run", help="Run full workflow (upload + wait + download)"
434
+ )
435
+ p_run.add_argument("audio_file", help="Audio file to process")
436
+ p_run.add_argument(
437
+ "-t",
438
+ "--sep-type",
439
+ type=int,
440
+ default=None,
441
+ help="Separation type (default: config value)",
442
+ )
443
+ p_run.add_argument(
444
+ "-f",
445
+ "--output-format",
446
+ type=int,
447
+ default=None,
448
+ help="Output format (default: config value)",
449
+ )
450
+ p_run.add_argument("--add-opt1", help="Additional option 1")
451
+ p_run.add_argument("--add-opt2", help="Additional option 2")
452
+ p_run.add_argument(
453
+ "--demo", dest="is_demo", action="store_true", help="Publish to demo page"
454
+ )
455
+ p_run.add_argument(
456
+ "-o", "--output-dir", type=str, default=None, help="Output directory"
457
+ )
458
+ p_run.add_argument(
459
+ "-i", "--interval", type=int, default=None, help="Poll interval in seconds"
460
+ )
461
+ p_run.add_argument("--timeout", type=int, help="Max wait time in seconds")
462
+ p_run.set_defaults(func=cmd_run)
463
+
464
+ p_config = subparsers.add_parser("config", help="Configuration management")
465
+ p_config_sub = p_config.add_subparsers(dest="subcommand", help="Config commands")
466
+ p_config_sub.add_parser("show", help="Show current config")
467
+ p_config_set = p_config_sub.add_parser("set-token", help="Set API token")
468
+ p_config_set.add_argument("token", help="API token")
469
+
470
+ p_set_dir = p_config_sub.add_parser(
471
+ "set-output-dir", help="Set default output directory"
472
+ )
473
+ p_set_dir.add_argument("value", type=str, help="Directory path")
474
+
475
+ p_set_mirror = p_config_sub.add_parser(
476
+ "set-mirror", help="Set mirror (main or mirror)"
477
+ )
478
+ p_set_mirror.add_argument("value", type=str, help="Mirror: main or mirror")
479
+
480
+ p_set_fmt = p_config_sub.add_parser(
481
+ "set-output-format", help="Set default output format"
482
+ )
483
+ p_set_fmt.add_argument("value", type=int, help="Value")
484
+
485
+ p_set_int = p_config_sub.add_parser("set-interval", help="Set poll interval")
486
+ p_set_int.add_argument("value", type=int, help="Value")
487
+
488
+ p_config.set_defaults(func=cmd_config)
489
+
490
+ p_list = subparsers.add_parser("list", help="List available options")
491
+ p_list.add_argument(
492
+ "--popular", action="store_true", help="Show popular separation types"
493
+ )
494
+ p_list.add_argument("--formats", action="store_true", help="Show output formats")
495
+ p_list.add_argument(
496
+ "--models",
497
+ type=int,
498
+ metavar="SEP_TYPE",
499
+ help="Show model options for a separation type",
500
+ )
501
+ p_list.add_argument(
502
+ "-s",
503
+ "--search",
504
+ type=str,
505
+ metavar="KEYWORD",
506
+ help="Search separation types by keyword",
507
+ )
508
+ p_list.set_defaults(func=cmd_list_types)
509
+
510
+ p_history = subparsers.add_parser("history", help="Show task history")
511
+ p_history.set_defaults(func=cmd_history)
512
+
513
+ args = parser.parse_args()
514
+
515
+ if not args.command:
516
+ parser.print_help()
517
+ return 1
518
+
519
+ try:
520
+ return args.func(args) or 0
521
+ except Exception as e:
522
+ print(f"Error: {e}", file=sys.stderr)
523
+ return 1
524
+
525
+
526
+ if __name__ == "__main__":
527
+ sys.exit(main())