From 62207ae1bc831ac192c72b15f0db54264338cbc2 Mon Sep 17 00:00:00 2001 From: Manikya Rathore Date: Wed, 12 Nov 2025 21:23:43 +0530 Subject: [PATCH 1/2] Validate input/output file formats for speech/text operations --- my-project/README.md | 36 +++++++++++++++++++++++++++++++++++ my-project/package.json | 20 +++++++++++++++++++ my-project/src/app.ts | 18 ++++++++++++++++++ my-project/src/types/index.ts | 17 +++++++++++++++++ my-project/tsconfig.json | 14 ++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 my-project/README.md create mode 100644 my-project/package.json create mode 100644 my-project/src/app.ts create mode 100644 my-project/src/types/index.ts create mode 100644 my-project/tsconfig.json diff --git a/my-project/README.md b/my-project/README.md new file mode 100644 index 00000000..274645d5 --- /dev/null +++ b/my-project/README.md @@ -0,0 +1,36 @@ +# My Project + +## Overview +This project is a TypeScript application that serves as an entry point for implementing application logic, configurations, and middleware setup. + +## Project Structure +``` +my-project +├── src +│ ├── app.ts # Entry point of the application +│ └── types +│ └── index.ts # Type definitions for better type safety +├── package.json # npm configuration file +├── tsconfig.json # TypeScript configuration file +└── README.md # Documentation for the project +``` + +## Installation +To install the necessary dependencies, run the following command: + +``` +npm install +``` + +## Usage +To start the application, use the following command: + +``` +npm start +``` + +## Contributing +Contributions are welcome! Please open an issue or submit a pull request for any improvements or bug fixes. + +## License +This project is licensed under the MIT License. \ No newline at end of file diff --git a/my-project/package.json b/my-project/package.json new file mode 100644 index 00000000..669b5656 --- /dev/null +++ b/my-project/package.json @@ -0,0 +1,20 @@ +{ + "name": "my-project", + "version": "1.0.0", + "description": "A TypeScript project.", + "main": "src/app.ts", + "scripts": { + "start": "ts-node src/app.ts", + "build": "tsc", + "test": "jest" + }, + "dependencies": {}, + "devDependencies": { + "ts-node": "^10.0.0", + "typescript": "^4.0.0", + "jest": "^27.0.0", + "@types/jest": "^27.0.0" + }, + "author": "", + "license": "ISC" +} \ No newline at end of file diff --git a/my-project/src/app.ts b/my-project/src/app.ts new file mode 100644 index 00000000..a108f429 --- /dev/null +++ b/my-project/src/app.ts @@ -0,0 +1,18 @@ +import express from 'express'; +import { SomeType } from './types/index'; + +const app = express(); +const PORT = process.env.PORT || 3000; + +// Middleware setup +app.use(express.json()); + +// Application logic +app.get('/', (req, res) => { + res.send('Hello, World!'); +}); + +// Start the server +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); \ No newline at end of file diff --git a/my-project/src/types/index.ts b/my-project/src/types/index.ts new file mode 100644 index 00000000..73bce0de --- /dev/null +++ b/my-project/src/types/index.ts @@ -0,0 +1,17 @@ +export interface User { + id: number; + name: string; + email: string; +} + +export interface Post { + id: number; + title: string; + content: string; + authorId: number; +} + +export type Response = { + data: T; + error?: string; +}; \ No newline at end of file diff --git a/my-project/tsconfig.json b/my-project/tsconfig.json new file mode 100644 index 00000000..654c1189 --- /dev/null +++ b/my-project/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.spec.ts"] +} \ No newline at end of file From 54d9e94e2dcc2bf8f3d46ca9d2f0cd3e965e69c4 Mon Sep 17 00:00:00 2001 From: Manikya Rathore Date: Wed, 12 Nov 2025 21:38:30 +0530 Subject: [PATCH 2/2] Validate input/output file formats for speech/text operations --- my-project/README.md | 36 --------- my-project/package.json | 20 ----- my-project/src/app.ts | 18 ----- my-project/src/types/index.ts | 17 ---- my-project/tsconfig.json | 14 ---- src/artbox/cli.py | 29 +++++++ src/artbox/validators.py | 141 ++++++++++++++++++++++++++++++++++ 7 files changed, 170 insertions(+), 105 deletions(-) delete mode 100644 my-project/README.md delete mode 100644 my-project/package.json delete mode 100644 my-project/src/app.ts delete mode 100644 my-project/src/types/index.ts delete mode 100644 my-project/tsconfig.json create mode 100644 src/artbox/cli.py create mode 100644 src/artbox/validators.py diff --git a/my-project/README.md b/my-project/README.md deleted file mode 100644 index 274645d5..00000000 --- a/my-project/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# My Project - -## Overview -This project is a TypeScript application that serves as an entry point for implementing application logic, configurations, and middleware setup. - -## Project Structure -``` -my-project -├── src -│ ├── app.ts # Entry point of the application -│ └── types -│ └── index.ts # Type definitions for better type safety -├── package.json # npm configuration file -├── tsconfig.json # TypeScript configuration file -└── README.md # Documentation for the project -``` - -## Installation -To install the necessary dependencies, run the following command: - -``` -npm install -``` - -## Usage -To start the application, use the following command: - -``` -npm start -``` - -## Contributing -Contributions are welcome! Please open an issue or submit a pull request for any improvements or bug fixes. - -## License -This project is licensed under the MIT License. \ No newline at end of file diff --git a/my-project/package.json b/my-project/package.json deleted file mode 100644 index 669b5656..00000000 --- a/my-project/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "my-project", - "version": "1.0.0", - "description": "A TypeScript project.", - "main": "src/app.ts", - "scripts": { - "start": "ts-node src/app.ts", - "build": "tsc", - "test": "jest" - }, - "dependencies": {}, - "devDependencies": { - "ts-node": "^10.0.0", - "typescript": "^4.0.0", - "jest": "^27.0.0", - "@types/jest": "^27.0.0" - }, - "author": "", - "license": "ISC" -} \ No newline at end of file diff --git a/my-project/src/app.ts b/my-project/src/app.ts deleted file mode 100644 index a108f429..00000000 --- a/my-project/src/app.ts +++ /dev/null @@ -1,18 +0,0 @@ -import express from 'express'; -import { SomeType } from './types/index'; - -const app = express(); -const PORT = process.env.PORT || 3000; - -// Middleware setup -app.use(express.json()); - -// Application logic -app.get('/', (req, res) => { - res.send('Hello, World!'); -}); - -// Start the server -app.listen(PORT, () => { - console.log(`Server is running on http://localhost:${PORT}`); -}); \ No newline at end of file diff --git a/my-project/src/types/index.ts b/my-project/src/types/index.ts deleted file mode 100644 index 73bce0de..00000000 --- a/my-project/src/types/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface User { - id: number; - name: string; - email: string; -} - -export interface Post { - id: number; - title: string; - content: string; - authorId: number; -} - -export type Response = { - data: T; - error?: string; -}; \ No newline at end of file diff --git a/my-project/tsconfig.json b/my-project/tsconfig.json deleted file mode 100644 index 654c1189..00000000 --- a/my-project/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "**/*.spec.ts"] -} \ No newline at end of file diff --git a/src/artbox/cli.py b/src/artbox/cli.py new file mode 100644 index 00000000..b49529ae --- /dev/null +++ b/src/artbox/cli.py @@ -0,0 +1,29 @@ +# ...existing code... +from artbox.validators import validate_io_paths +import sys +# ...existing code... + +# INSERT: extract common arg names and validate extensions +input_path = getattr(args, "input_path", None) or getattr(args, "input", None) or None +output_path = getattr(args, "output_path", None) or getattr(args, "output", None) or None + +operation = None +for attr in ("operation", "op", "command", "cmd", "subcommand", "mode"): + val = getattr(args, attr, None) + if isinstance(val, str) and val: + operation = val + break + +if isinstance(operation, str): + ol = operation.lower() + if ol in ("tts", "text-to-speech", "text->speech"): + operation = "text-to-speech" + elif ol in ("stt", "speech-to-text", "speech->text"): + operation = "speech-to-text" + +try: + validate_io_paths(input_path, output_path, operation) +except ValueError as e: + print(f"Error: {e}", file=sys.stderr) + raise SystemExit(2) +# ...existing code... \ No newline at end of file diff --git a/src/artbox/validators.py b/src/artbox/validators.py new file mode 100644 index 00000000..07c372cd --- /dev/null +++ b/src/artbox/validators.py @@ -0,0 +1,141 @@ +from pathlib import Path +from typing import Optional + +AUDIO_EXTS = {".mp3", ".wav", ".ogg", ".flac", ".m4a", ".aac"} +TEXT_EXTS = {".txt", ".md", ".srt", ".vtt", ".json"} + + +def _ext_of(path: Optional[str]) -> str: + return Path(path).suffix.lower() if path else "" + + +def validate_io_paths(input_path: Optional[str], output_path: Optional[str], operation: Optional[str] = None) -> None: + """ + Validate input/output extensions for common operations. + + operation can be: + - 'text-to-speech' / 'text->speech' / 'tts' + - 'speech-to-text' / 'speech->text' / 'stt' + - None (best-effort inference) + + Raises ValueError on invalid combinations. + """ + op = (operation or "").lower() + in_ext = _ext_of(input_path) + out_ext = _ext_of(output_path) + + tts_ops = {"text-to-speech", "text->speech", "tts"} + stt_ops = {"speech-to-text", "speech->text", "stt"} + + if op in tts_ops: + if not output_path or out_ext == "": + raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).") + if out_ext not in AUDIO_EXTS: + raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}") + + elif op in stt_ops: + if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS: + raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).") + if not output_path or out_ext == "" or out_ext not in TEXT_EXTS: + raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).") + + else: + # Infer operation by extensions and validate. + inferred_op = None + if in_ext: + if in_ext in AUDIO_EXTS: + inferred_op = "speech-to-text" + elif in_ext in TEXT_EXTS: + inferred_op = "text-to-speech" + if not inferred_op and out_ext: + if out_ext in AUDIO_EXTS: + inferred_op = "text-to-speech" + elif out_ext in TEXT_EXTS: + inferred_op = "speech-to-text" + + if inferred_op == "text-to-speech": + if not output_path or out_ext == "": + raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).") + if out_ext not in AUDIO_EXTS: + raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}") + elif inferred_op == "speech-to-text": + if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS: + raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).") + if not output_path or out_ext == "" or out_ext not in TEXT_EXTS: + raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).") + else: + if input_path and in_ext and in_ext not in AUDIO_EXTS.union(TEXT_EXTS): + raise ValueError(f"Unsupported input extension '{in_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.") + if output_path and out_ext and out_ext not in AUDIO_EXTS.union(TEXT_EXTS): + raise ValueError(f"Unsupported output extension '{out_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.") + +from pathlib import Path +from typing import Optional + +AUDIO_EXTS = {".mp3", ".wav", ".ogg", ".flac", ".m4a", ".aac"} +TEXT_EXTS = {".txt", ".md", ".srt", ".vtt", ".json"} + + +def _ext_of(path: Optional[str]) -> str: + return Path(path).suffix.lower() if path else "" + + +def validate_io_paths(input_path: Optional[str], output_path: Optional[str], operation: Optional[str] = None) -> None: + """ + Validate input/output extensions for common operations. + + operation can be: + - 'text-to-speech' / 'text->speech' / 'tts' + - 'speech-to-text' / 'speech->text' / 'stt' + - None (best-effort inference) + + Raises ValueError on invalid combinations. + """ + op = (operation or "").lower() + in_ext = _ext_of(input_path) + out_ext = _ext_of(output_path) + + tts_ops = {"text-to-speech", "text->speech", "tts"} + stt_ops = {"speech-to-text", "speech->text", "stt"} + + if op in tts_ops: + if not output_path or out_ext == "": + raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).") + if out_ext not in AUDIO_EXTS: + raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}") + + elif op in stt_ops: + if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS: + raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).") + if not output_path or out_ext == "" or out_ext not in TEXT_EXTS: + raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).") + + else: + # Infer operation by extensions and validate. + inferred_op = None + if in_ext: + if in_ext in AUDIO_EXTS: + inferred_op = "speech-to-text" + elif in_ext in TEXT_EXTS: + inferred_op = "text-to-speech" + if not inferred_op and out_ext: + if out_ext in AUDIO_EXTS: + inferred_op = "text-to-speech" + elif out_ext in TEXT_EXTS: + inferred_op = "speech-to-text" + + if inferred_op == "text-to-speech": + if not output_path or out_ext == "": + raise ValueError("text->speech requires an audio output file (e.g. --output-path out.mp3).") + if out_ext not in AUDIO_EXTS: + raise ValueError(f"Invalid output audio format '{out_ext}'. Supported: {', '.join(sorted(AUDIO_EXTS))}") + elif inferred_op == "speech-to-text": + if not input_path or in_ext == "" or in_ext not in AUDIO_EXTS: + raise ValueError(f"speech->text requires an audio input file (supported: {', '.join(sorted(AUDIO_EXTS))}).") + if not output_path or out_ext == "" or out_ext not in TEXT_EXTS: + raise ValueError(f"speech->text requires a text output file (supported: {', '.join(sorted(TEXT_EXTS))}).") + else: + if input_path and in_ext and in_ext not in AUDIO_EXTS.union(TEXT_EXTS): + raise ValueError(f"Unsupported input extension '{in_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.") + if output_path and out_ext and out_ext not in AUDIO_EXTS.union(TEXT_EXTS): + raise ValueError(f"Unsupported output extension '{out_ext}'. Supported: audio {', '.join(sorted(AUDIO_EXTS))} or text {', '.join(sorted(TEXT_EXTS))}.") \ No newline at end of file