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