Skip to content

Commit 54d9e94

Browse files
Validate input/output file formats for speech/text operations
1 parent 62207ae commit 54d9e94

File tree

7 files changed

+170
-105
lines changed

7 files changed

+170
-105
lines changed

my-project/README.md

Lines changed: 0 additions & 36 deletions
This file was deleted.

my-project/package.json

Lines changed: 0 additions & 20 deletions
This file was deleted.

my-project/src/app.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

my-project/src/types/index.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

my-project/tsconfig.json

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/artbox/cli.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# ...existing code...
2+
from artbox.validators import validate_io_paths
3+
import sys
4+
# ...existing code...
5+
6+
# INSERT: extract common arg names and validate extensions
7+
input_path = getattr(args, "input_path", None) or getattr(args, "input", None) or None
8+
output_path = getattr(args, "output_path", None) or getattr(args, "output", None) or None
9+
10+
operation = None
11+
for attr in ("operation", "op", "command", "cmd", "subcommand", "mode"):
12+
val = getattr(args, attr, None)
13+
if isinstance(val, str) and val:
14+
operation = val
15+
break
16+
17+
if isinstance(operation, str):
18+
ol = operation.lower()
19+
if ol in ("tts", "text-to-speech", "text->speech"):
20+
operation = "text-to-speech"
21+
elif ol in ("stt", "speech-to-text", "speech->text"):
22+
operation = "speech-to-text"
23+
24+
try:
25+
validate_io_paths(input_path, output_path, operation)
26+
except ValueError as e:
27+
print(f"Error: {e}", file=sys.stderr)
28+
raise SystemExit(2)
29+
# ...existing code...

src/artbox/validators.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from pathlib import Path
2+
from typing import Optional
3+
4+
AUDIO_EXTS = {".mp3", ".wav", ".ogg", ".flac", ".m4a", ".aac"}
5+
TEXT_EXTS = {".txt", ".md", ".srt", ".vtt", ".json"}
6+
7+
8+
def _ext_of(path: Optional[str]) -> str:
9+
return Path(path).suffix.lower() if path else ""
10+
11+
12+
def validate_io_paths(input_path: Optional[str], output_path: Optional[str], operation: Optional[str] = None) -> None:
13+
"""
14+
Validate input/output extensions for common operations.
15+
16+
operation can be:
17+
- 'text-to-speech' / 'text->speech' / 'tts'
18+
- 'speech-to-text' / 'speech->text' / 'stt'
19+
- None (best-effort inference)
20+
21+
Raises ValueError on invalid combinations.
22+
"""
23+
op = (operation or "").lower()
24+
in_ext = _ext_of(input_path)
25+
out_ext = _ext_of(output_path)
26+
27+
tts_ops = {"text-to-speech", "text->speech", "tts"}
28+
stt_ops = {"speech-to-text", "speech->text", "stt"}
29+
30+
if op in tts_ops:
31+
if not output_path or out_ext == "":
32+
raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).")
33+
if out_ext not in AUDIO_EXTS:
34+
raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}")
35+
36+
elif op in stt_ops:
37+
if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS:
38+
raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).")
39+
if not output_path or out_ext == "" or out_ext not in TEXT_EXTS:
40+
raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).")
41+
42+
else:
43+
# Infer operation by extensions and validate.
44+
inferred_op = None
45+
if in_ext:
46+
if in_ext in AUDIO_EXTS:
47+
inferred_op = "speech-to-text"
48+
elif in_ext in TEXT_EXTS:
49+
inferred_op = "text-to-speech"
50+
if not inferred_op and out_ext:
51+
if out_ext in AUDIO_EXTS:
52+
inferred_op = "text-to-speech"
53+
elif out_ext in TEXT_EXTS:
54+
inferred_op = "speech-to-text"
55+
56+
if inferred_op == "text-to-speech":
57+
if not output_path or out_ext == "":
58+
raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).")
59+
if out_ext not in AUDIO_EXTS:
60+
raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}")
61+
elif inferred_op == "speech-to-text":
62+
if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS:
63+
raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).")
64+
if not output_path or out_ext == "" or out_ext not in TEXT_EXTS:
65+
raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).")
66+
else:
67+
if input_path and in_ext and in_ext not in AUDIO_EXTS.union(TEXT_EXTS):
68+
raise ValueError(f"Unsupported input extension '{in_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.")
69+
if output_path and out_ext and out_ext not in AUDIO_EXTS.union(TEXT_EXTS):
70+
raise ValueError(f"Unsupported output extension '{out_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.")
71+
72+
from pathlib import Path
73+
from typing import Optional
74+
75+
AUDIO_EXTS = {".mp3", ".wav", ".ogg", ".flac", ".m4a", ".aac"}
76+
TEXT_EXTS = {".txt", ".md", ".srt", ".vtt", ".json"}
77+
78+
79+
def _ext_of(path: Optional[str]) -> str:
80+
return Path(path).suffix.lower() if path else ""
81+
82+
83+
def validate_io_paths(input_path: Optional[str], output_path: Optional[str], operation: Optional[str] = None) -> None:
84+
"""
85+
Validate input/output extensions for common operations.
86+
87+
operation can be:
88+
- 'text-to-speech' / 'text->speech' / 'tts'
89+
- 'speech-to-text' / 'speech->text' / 'stt'
90+
- None (best-effort inference)
91+
92+
Raises ValueError on invalid combinations.
93+
"""
94+
op = (operation or "").lower()
95+
in_ext = _ext_of(input_path)
96+
out_ext = _ext_of(output_path)
97+
98+
tts_ops = {"text-to-speech", "text->speech", "tts"}
99+
stt_ops = {"speech-to-text", "speech->text", "stt"}
100+
101+
if op in tts_ops:
102+
if not output_path or out_ext == "":
103+
raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).")
104+
if out_ext not in AUDIO_EXTS:
105+
raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}")
106+
107+
elif op in stt_ops:
108+
if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS:
109+
raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).")
110+
if not output_path or out_ext == "" or out_ext not in TEXT_EXTS:
111+
raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).")
112+
113+
else:
114+
# Infer operation by extensions and validate.
115+
inferred_op = None
116+
if in_ext:
117+
if in_ext in AUDIO_EXTS:
118+
inferred_op = "speech-to-text"
119+
elif in_ext in TEXT_EXTS:
120+
inferred_op = "text-to-speech"
121+
if not inferred_op and out_ext:
122+
if out_ext in AUDIO_EXTS:
123+
inferred_op = "text-to-speech"
124+
elif out_ext in TEXT_EXTS:
125+
inferred_op = "speech-to-text"
126+
127+
if inferred_op == "text-to-speech":
128+
if not output_path or out_ext == "":
129+
raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).")
130+
if out_ext not in AUDIO_EXTS:
131+
raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}")
132+
elif inferred_op == "speech-to-text":
133+
if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS:
134+
raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).")
135+
if not output_path or out_ext == "" or out_ext not in TEXT_EXTS:
136+
raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).")
137+
else:
138+
if input_path and in_ext and in_ext not in AUDIO_EXTS.union(TEXT_EXTS):
139+
raise ValueError(f"Unsupported input extension '{in_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.")
140+
if output_path and out_ext and out_ext not in AUDIO_EXTS.union(TEXT_EXTS):
141+
raise ValueError(f"Unsupported output extension '{out_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.")

0 commit comments

Comments
 (0)