openconvert 0.1.0__py3-none-any.whl → 1.1.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.
- openconvert/__init__.py +96 -3
- openconvert/__main__.py +13 -0
- openconvert/client.py +392 -0
- openconvert/openconvert_cli.py +540 -0
- openconvert-1.1.0.dist-info/METADATA +504 -0
- openconvert-1.1.0.dist-info/RECORD +10 -0
- openconvert-1.1.0.dist-info/entry_points.txt +2 -0
- openconvert-1.1.0.dist-info/licenses/LICENSE +21 -0
- openconvert/cli.py +0 -145
- openconvert/converter.py +0 -152
- openconvert/converters/__init__.py +0 -3
- openconvert/converters/archive_converter.py +0 -277
- openconvert/converters/audio_converter.py +0 -223
- openconvert/converters/code_converter.py +0 -412
- openconvert/converters/document_converter.py +0 -596
- openconvert/converters/image_converter.py +0 -214
- openconvert/converters/model_converter.py +0 -208
- openconvert/converters/video_converter.py +0 -259
- openconvert/launcher.py +0 -0
- openconvert-0.1.0.dist-info/METADATA +0 -232
- openconvert-0.1.0.dist-info/RECORD +0 -17
- openconvert-0.1.0.dist-info/entry_points.txt +0 -2
- {openconvert-0.1.0.dist-info → openconvert-1.1.0.dist-info}/WHEEL +0 -0
- {openconvert-0.1.0.dist-info → openconvert-1.1.0.dist-info}/top_level.txt +0 -0
openconvert/converter.py
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Main converter module that provides the open_convert function.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import os
|
6
|
-
import logging
|
7
|
-
from pathlib import Path
|
8
|
-
from typing import Optional, Union, List
|
9
|
-
|
10
|
-
from .converters import (
|
11
|
-
image_converter,
|
12
|
-
document_converter,
|
13
|
-
audio_converter,
|
14
|
-
video_converter,
|
15
|
-
archive_converter,
|
16
|
-
model_converter,
|
17
|
-
code_converter
|
18
|
-
)
|
19
|
-
|
20
|
-
# Configure logging
|
21
|
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
22
|
-
logger = logging.getLogger(__name__)
|
23
|
-
|
24
|
-
# Define conversion categories
|
25
|
-
CONVERSION_CATEGORIES = {
|
26
|
-
# Image formats
|
27
|
-
'png': 'image', 'jpg': 'image', 'jpeg': 'image', 'bmp': 'image', 'tiff': 'image',
|
28
|
-
'gif': 'image', 'ico': 'image', 'svg': 'image', 'webp': 'image',
|
29
|
-
|
30
|
-
# Document formats
|
31
|
-
'txt': 'document', 'pdf': 'document', 'docx': 'document', 'rtf': 'document',
|
32
|
-
'md': 'document', 'html': 'document', 'csv': 'document', 'xlsx': 'document',
|
33
|
-
'epub': 'document',
|
34
|
-
|
35
|
-
# Audio formats
|
36
|
-
'mp3': 'audio', 'wav': 'audio', 'ogg': 'audio', 'flac': 'audio', 'aac': 'audio',
|
37
|
-
|
38
|
-
# Video formats
|
39
|
-
'mp4': 'video', 'avi': 'video', 'mkv': 'video', 'mov': 'video',
|
40
|
-
|
41
|
-
# Archive formats
|
42
|
-
'zip': 'archive', 'rar': 'archive', '7z': 'archive', 'tar': 'archive', 'gz': 'archive',
|
43
|
-
|
44
|
-
# 3D Model formats
|
45
|
-
'stl': 'model', 'obj': 'model', 'fbx': 'model', 'ply': 'model',
|
46
|
-
|
47
|
-
# Code and markup formats
|
48
|
-
'json': 'code', 'yaml': 'code', 'xml': 'code', 'latex': 'code'
|
49
|
-
}
|
50
|
-
|
51
|
-
def open_convert(
|
52
|
-
filepath: Union[str, Path],
|
53
|
-
source_format: Optional[str] = None,
|
54
|
-
target_format: str = None,
|
55
|
-
output_path: Optional[Union[str, Path]] = None,
|
56
|
-
options: Optional[dict] = None
|
57
|
-
) -> str:
|
58
|
-
"""
|
59
|
-
Convert a file from one format to another.
|
60
|
-
|
61
|
-
Args:
|
62
|
-
filepath: Path to the source file
|
63
|
-
source_format: Source file format (if None, will be inferred from file extension)
|
64
|
-
target_format: Target file format
|
65
|
-
output_path: Path to save the converted file (if None, will use the same directory as source)
|
66
|
-
options: Additional conversion options
|
67
|
-
|
68
|
-
Returns:
|
69
|
-
Path to the converted file
|
70
|
-
|
71
|
-
Raises:
|
72
|
-
ValueError: If the conversion is not supported or parameters are invalid
|
73
|
-
FileNotFoundError: If the source file doesn't exist
|
74
|
-
RuntimeError: If the conversion fails
|
75
|
-
"""
|
76
|
-
if options is None:
|
77
|
-
options = {}
|
78
|
-
|
79
|
-
# Convert filepath to Path object
|
80
|
-
filepath = Path(filepath)
|
81
|
-
|
82
|
-
# Check if file exists
|
83
|
-
if not filepath.exists():
|
84
|
-
raise FileNotFoundError(f"Source file not found: {filepath}")
|
85
|
-
|
86
|
-
# Infer source format from file extension if not provided
|
87
|
-
if source_format is None:
|
88
|
-
source_format = filepath.suffix.lower().lstrip('.')
|
89
|
-
if not source_format:
|
90
|
-
raise ValueError("Could not determine source format from file extension. Please specify source_format.")
|
91
|
-
else:
|
92
|
-
source_format = source_format.lower()
|
93
|
-
|
94
|
-
# Ensure target format is provided and lowercase
|
95
|
-
if target_format is None:
|
96
|
-
raise ValueError("Target format must be specified")
|
97
|
-
target_format = target_format.lower()
|
98
|
-
|
99
|
-
# Check if formats are supported
|
100
|
-
if source_format not in CONVERSION_CATEGORIES:
|
101
|
-
raise ValueError(f"Unsupported source format: {source_format}")
|
102
|
-
if target_format not in CONVERSION_CATEGORIES:
|
103
|
-
raise ValueError(f"Unsupported target format: {target_format}")
|
104
|
-
|
105
|
-
# Determine output path if not provided
|
106
|
-
if output_path is None:
|
107
|
-
output_path = filepath.with_suffix(f".{target_format}")
|
108
|
-
else:
|
109
|
-
output_path = Path(output_path)
|
110
|
-
# If output_path is a directory, use the original filename with new extension
|
111
|
-
if output_path.is_dir():
|
112
|
-
output_path = output_path / filepath.with_suffix(f".{target_format}").name
|
113
|
-
|
114
|
-
logger.info(f"Converting {filepath} from {source_format} to {target_format}")
|
115
|
-
|
116
|
-
# Determine conversion category and route to appropriate converter
|
117
|
-
source_category = CONVERSION_CATEGORIES[source_format]
|
118
|
-
target_category = CONVERSION_CATEGORIES[target_format]
|
119
|
-
|
120
|
-
# Handle conversion based on categories
|
121
|
-
if source_category == 'image':
|
122
|
-
if target_category == 'image' or target_format == 'pdf':
|
123
|
-
return image_converter.convert(filepath, source_format, target_format, output_path, options)
|
124
|
-
|
125
|
-
elif source_category == 'document':
|
126
|
-
return document_converter.convert(filepath, source_format, target_format, output_path, options)
|
127
|
-
|
128
|
-
elif source_category == 'audio':
|
129
|
-
if target_category == 'audio':
|
130
|
-
return audio_converter.convert(filepath, source_format, target_format, output_path, options)
|
131
|
-
elif target_format == 'txt': # Speech-to-text
|
132
|
-
return audio_converter.speech_to_text(filepath, output_path, options)
|
133
|
-
|
134
|
-
elif source_category == 'video':
|
135
|
-
if target_category == 'video' or target_format == 'gif':
|
136
|
-
return video_converter.convert(filepath, source_format, target_format, output_path, options)
|
137
|
-
elif target_format == 'mp3': # Audio extraction
|
138
|
-
return video_converter.extract_audio(filepath, output_path, options)
|
139
|
-
elif target_format in ['png', 'jpg']: # Frame extraction
|
140
|
-
return video_converter.extract_frames(filepath, target_format, output_path, options)
|
141
|
-
|
142
|
-
elif source_category == 'archive':
|
143
|
-
return archive_converter.convert(filepath, source_format, target_format, output_path, options)
|
144
|
-
|
145
|
-
elif source_category == 'model':
|
146
|
-
return model_converter.convert(filepath, source_format, target_format, output_path, options)
|
147
|
-
|
148
|
-
elif source_category == 'code':
|
149
|
-
return code_converter.convert(filepath, source_format, target_format, output_path, options)
|
150
|
-
|
151
|
-
# If we get here, the specific conversion is not supported
|
152
|
-
raise ValueError(f"Conversion from {source_format} to {target_format} is not supported")
|
@@ -1,277 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Archive converter module for handling archive format conversions.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import logging
|
6
|
-
import os
|
7
|
-
import shutil
|
8
|
-
import tempfile
|
9
|
-
from pathlib import Path
|
10
|
-
from typing import Union, Optional, Dict, Any
|
11
|
-
|
12
|
-
logger = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
# Define supported conversions
|
15
|
-
SUPPORTED_CONVERSIONS = {
|
16
|
-
'zip': ['rar', '7z', 'tar', 'gz'],
|
17
|
-
'rar': ['zip', '7z', 'tar', 'gz'],
|
18
|
-
'7z': ['zip', 'rar', 'tar', 'gz'],
|
19
|
-
'tar': ['zip', 'rar', '7z', 'gz'],
|
20
|
-
'gz': ['zip', 'rar', '7z', 'tar']
|
21
|
-
}
|
22
|
-
|
23
|
-
def convert(
|
24
|
-
filepath: Union[str, Path],
|
25
|
-
source_format: str,
|
26
|
-
target_format: str,
|
27
|
-
output_path: Union[str, Path],
|
28
|
-
options: Optional[Dict[str, Any]] = None
|
29
|
-
) -> str:
|
30
|
-
"""
|
31
|
-
Convert an archive from one format to another.
|
32
|
-
|
33
|
-
Args:
|
34
|
-
filepath: Path to the source archive file
|
35
|
-
source_format: Source archive format
|
36
|
-
target_format: Target archive format
|
37
|
-
output_path: Path to save the converted archive
|
38
|
-
options: Additional conversion options
|
39
|
-
|
40
|
-
Returns:
|
41
|
-
Path to the converted archive
|
42
|
-
|
43
|
-
Raises:
|
44
|
-
ValueError: If the conversion is not supported
|
45
|
-
RuntimeError: If the conversion fails
|
46
|
-
"""
|
47
|
-
if options is None:
|
48
|
-
options = {}
|
49
|
-
|
50
|
-
# Check if conversion is supported
|
51
|
-
if target_format not in SUPPORTED_CONVERSIONS.get(source_format, []):
|
52
|
-
raise ValueError(f"Conversion from {source_format} to {target_format} is not supported")
|
53
|
-
|
54
|
-
filepath = Path(filepath)
|
55
|
-
output_path = Path(output_path)
|
56
|
-
|
57
|
-
try:
|
58
|
-
# Create a temporary directory for extraction
|
59
|
-
with tempfile.TemporaryDirectory() as temp_dir:
|
60
|
-
temp_dir_path = Path(temp_dir)
|
61
|
-
|
62
|
-
# Extract the source archive
|
63
|
-
extract_dir = _extract_archive(filepath, source_format, temp_dir_path)
|
64
|
-
|
65
|
-
# Create the target archive
|
66
|
-
return _create_archive(extract_dir, target_format, output_path, options)
|
67
|
-
|
68
|
-
except Exception as e:
|
69
|
-
logger.error(f"Error converting {filepath} to {target_format}: {str(e)}")
|
70
|
-
raise RuntimeError(f"Failed to convert {filepath} to {target_format}: {str(e)}")
|
71
|
-
|
72
|
-
def _extract_archive(
|
73
|
-
filepath: Path,
|
74
|
-
format: str,
|
75
|
-
output_dir: Path
|
76
|
-
) -> Path:
|
77
|
-
"""Extract an archive to a directory."""
|
78
|
-
if format == 'zip':
|
79
|
-
return _extract_zip(filepath, output_dir)
|
80
|
-
elif format == 'rar':
|
81
|
-
return _extract_rar(filepath, output_dir)
|
82
|
-
elif format == '7z':
|
83
|
-
return _extract_7z(filepath, output_dir)
|
84
|
-
elif format == 'tar':
|
85
|
-
return _extract_tar(filepath, output_dir)
|
86
|
-
elif format == 'gz':
|
87
|
-
return _extract_gz(filepath, output_dir)
|
88
|
-
else:
|
89
|
-
raise ValueError(f"Unsupported archive format for extraction: {format}")
|
90
|
-
|
91
|
-
def _create_archive(
|
92
|
-
source_dir: Path,
|
93
|
-
format: str,
|
94
|
-
output_path: Path,
|
95
|
-
options: Dict[str, Any]
|
96
|
-
) -> str:
|
97
|
-
"""Create an archive from a directory."""
|
98
|
-
if format == 'zip':
|
99
|
-
return _create_zip(source_dir, output_path, options)
|
100
|
-
elif format == 'rar':
|
101
|
-
return _create_rar(source_dir, output_path, options)
|
102
|
-
elif format == '7z':
|
103
|
-
return _create_7z(source_dir, output_path, options)
|
104
|
-
elif format == 'tar':
|
105
|
-
return _create_tar(source_dir, output_path, options)
|
106
|
-
elif format == 'gz':
|
107
|
-
return _create_gz(source_dir, output_path, options)
|
108
|
-
else:
|
109
|
-
raise ValueError(f"Unsupported archive format for creation: {format}")
|
110
|
-
|
111
|
-
def _extract_zip(filepath: Path, output_dir: Path) -> Path:
|
112
|
-
"""Extract a ZIP archive."""
|
113
|
-
import zipfile
|
114
|
-
|
115
|
-
with zipfile.ZipFile(filepath, 'r') as zip_ref:
|
116
|
-
zip_ref.extractall(output_dir)
|
117
|
-
|
118
|
-
return output_dir
|
119
|
-
|
120
|
-
def _extract_rar(filepath: Path, output_dir: Path) -> Path:
|
121
|
-
"""Extract a RAR archive."""
|
122
|
-
try:
|
123
|
-
import rarfile
|
124
|
-
|
125
|
-
with rarfile.RarFile(filepath) as rf:
|
126
|
-
rf.extractall(output_dir)
|
127
|
-
|
128
|
-
return output_dir
|
129
|
-
|
130
|
-
except ImportError:
|
131
|
-
# Try using unrar command-line tool
|
132
|
-
import subprocess
|
133
|
-
|
134
|
-
try:
|
135
|
-
cmd = ['unrar', 'x', str(filepath), str(output_dir)]
|
136
|
-
subprocess.run(cmd, check=True, capture_output=True)
|
137
|
-
return output_dir
|
138
|
-
|
139
|
-
except subprocess.SubprocessError as e:
|
140
|
-
raise RuntimeError(f"Failed to extract RAR archive: {str(e)}. Please install rarfile or unrar.")
|
141
|
-
|
142
|
-
def _extract_7z(filepath: Path, output_dir: Path) -> Path:
|
143
|
-
"""Extract a 7Z archive."""
|
144
|
-
try:
|
145
|
-
import py7zr
|
146
|
-
|
147
|
-
with py7zr.SevenZipFile(filepath, mode='r') as z:
|
148
|
-
z.extractall(output_dir)
|
149
|
-
|
150
|
-
return output_dir
|
151
|
-
|
152
|
-
except ImportError:
|
153
|
-
# Try using 7z command-line tool
|
154
|
-
import subprocess
|
155
|
-
|
156
|
-
try:
|
157
|
-
cmd = ['7z', 'x', str(filepath), f'-o{output_dir}']
|
158
|
-
subprocess.run(cmd, check=True, capture_output=True)
|
159
|
-
return output_dir
|
160
|
-
|
161
|
-
except subprocess.SubprocessError as e:
|
162
|
-
raise RuntimeError(f"Failed to extract 7Z archive: {str(e)}. Please install py7zr or 7z.")
|
163
|
-
|
164
|
-
def _extract_tar(filepath: Path, output_dir: Path) -> Path:
|
165
|
-
"""Extract a TAR archive."""
|
166
|
-
import tarfile
|
167
|
-
|
168
|
-
with tarfile.open(filepath, 'r') as tar:
|
169
|
-
tar.extractall(output_dir)
|
170
|
-
|
171
|
-
return output_dir
|
172
|
-
|
173
|
-
def _extract_gz(filepath: Path, output_dir: Path) -> Path:
|
174
|
-
"""Extract a GZ archive."""
|
175
|
-
import gzip
|
176
|
-
import tarfile
|
177
|
-
|
178
|
-
# Check if it's a tar.gz file
|
179
|
-
if filepath.name.endswith('.tar.gz') or filepath.name.endswith('.tgz'):
|
180
|
-
with tarfile.open(filepath, 'r:gz') as tar:
|
181
|
-
tar.extractall(output_dir)
|
182
|
-
return output_dir
|
183
|
-
|
184
|
-
# Otherwise, it's a single gzipped file
|
185
|
-
with gzip.open(filepath, 'rb') as f_in:
|
186
|
-
output_file = output_dir / filepath.with_suffix('').name
|
187
|
-
with open(output_file, 'wb') as f_out:
|
188
|
-
shutil.copyfileobj(f_in, f_out)
|
189
|
-
|
190
|
-
return output_dir
|
191
|
-
|
192
|
-
def _create_zip(source_dir: Path, output_path: Path, options: Dict[str, Any]) -> str:
|
193
|
-
"""Create a ZIP archive."""
|
194
|
-
import zipfile
|
195
|
-
|
196
|
-
compression = options.get('compression', zipfile.ZIP_DEFLATED)
|
197
|
-
compression_level = options.get('compression_level', 9) # 0-9, 9 is highest
|
198
|
-
|
199
|
-
with zipfile.ZipFile(output_path, 'w', compression=compression, compresslevel=compression_level) as zipf:
|
200
|
-
for root, _, files in os.walk(source_dir):
|
201
|
-
for file in files:
|
202
|
-
file_path = Path(root) / file
|
203
|
-
arcname = file_path.relative_to(source_dir)
|
204
|
-
zipf.write(file_path, arcname)
|
205
|
-
|
206
|
-
return str(output_path)
|
207
|
-
|
208
|
-
def _create_rar(source_dir: Path, output_path: Path, options: Dict[str, Any]) -> str:
|
209
|
-
"""Create a RAR archive."""
|
210
|
-
# RAR creation requires command-line tool
|
211
|
-
import subprocess
|
212
|
-
|
213
|
-
try:
|
214
|
-
# Determine compression level
|
215
|
-
compression_level = options.get('compression_level', 5) # 0-5, 5 is highest
|
216
|
-
|
217
|
-
cmd = ['rar', f'a{compression_level}', str(output_path), str(source_dir)]
|
218
|
-
subprocess.run(cmd, check=True, capture_output=True)
|
219
|
-
return str(output_path)
|
220
|
-
|
221
|
-
except subprocess.SubprocessError as e:
|
222
|
-
raise RuntimeError(f"Failed to create RAR archive: {str(e)}. Please install rar command-line tool.")
|
223
|
-
|
224
|
-
def _create_7z(source_dir: Path, output_path: Path, options: Dict[str, Any]) -> str:
|
225
|
-
"""Create a 7Z archive."""
|
226
|
-
try:
|
227
|
-
import py7zr
|
228
|
-
|
229
|
-
compression_level = options.get('compression_level', 9) # 0-9, 9 is highest
|
230
|
-
|
231
|
-
with py7zr.SevenZipFile(output_path, 'w', compression_level=compression_level) as z:
|
232
|
-
z.writeall(source_dir)
|
233
|
-
|
234
|
-
return str(output_path)
|
235
|
-
|
236
|
-
except ImportError:
|
237
|
-
# Try using 7z command-line tool
|
238
|
-
import subprocess
|
239
|
-
|
240
|
-
try:
|
241
|
-
compression_level = options.get('compression_level', 9) # 0-9, 9 is highest
|
242
|
-
|
243
|
-
cmd = ['7z', 'a', f'-mx={compression_level}', str(output_path), f'{source_dir}/*']
|
244
|
-
subprocess.run(cmd, check=True, capture_output=True, shell=True)
|
245
|
-
return str(output_path)
|
246
|
-
|
247
|
-
except subprocess.SubprocessError as e:
|
248
|
-
raise RuntimeError(f"Failed to create 7Z archive: {str(e)}. Please install py7zr or 7z.")
|
249
|
-
|
250
|
-
def _create_tar(source_dir: Path, output_path: Path, options: Dict[str, Any]) -> str:
|
251
|
-
"""Create a TAR archive."""
|
252
|
-
import tarfile
|
253
|
-
|
254
|
-
with tarfile.open(output_path, 'w') as tar:
|
255
|
-
tar.add(source_dir, arcname=os.path.basename(source_dir))
|
256
|
-
|
257
|
-
return str(output_path)
|
258
|
-
|
259
|
-
def _create_gz(source_dir: Path, output_path: Path, options: Dict[str, Any]) -> str:
|
260
|
-
"""Create a GZ archive."""
|
261
|
-
import tarfile
|
262
|
-
|
263
|
-
# If there are multiple files, create a tar.gz
|
264
|
-
files = list(source_dir.glob('*'))
|
265
|
-
if len(files) > 1 or any(f.is_dir() for f in files):
|
266
|
-
with tarfile.open(output_path, 'w:gz') as tar:
|
267
|
-
tar.add(source_dir, arcname='')
|
268
|
-
else:
|
269
|
-
# If there's only one file, create a simple .gz
|
270
|
-
import gzip
|
271
|
-
|
272
|
-
input_file = files[0]
|
273
|
-
with open(input_file, 'rb') as f_in:
|
274
|
-
with gzip.open(output_path, 'wb') as f_out:
|
275
|
-
shutil.copyfileobj(f_in, f_out)
|
276
|
-
|
277
|
-
return str(output_path)
|
@@ -1,223 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Audio converter module for handling audio format conversions.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import logging
|
6
|
-
import os
|
7
|
-
import tempfile
|
8
|
-
from pathlib import Path
|
9
|
-
from typing import Union, Optional, Dict, Any
|
10
|
-
|
11
|
-
logger = logging.getLogger(__name__)
|
12
|
-
|
13
|
-
# Define supported conversions
|
14
|
-
SUPPORTED_CONVERSIONS = {
|
15
|
-
'mp3': ['wav', 'ogg', 'flac', 'aac'],
|
16
|
-
'wav': ['mp3', 'ogg', 'flac', 'aac'],
|
17
|
-
'ogg': ['mp3', 'wav', 'flac', 'aac'],
|
18
|
-
'flac': ['mp3', 'wav', 'ogg', 'aac'],
|
19
|
-
'aac': ['mp3', 'wav', 'ogg', 'flac']
|
20
|
-
}
|
21
|
-
|
22
|
-
def convert(
|
23
|
-
filepath: Union[str, Path],
|
24
|
-
source_format: str,
|
25
|
-
target_format: str,
|
26
|
-
output_path: Union[str, Path],
|
27
|
-
options: Optional[Dict[str, Any]] = None
|
28
|
-
) -> str:
|
29
|
-
"""
|
30
|
-
Convert an audio file from one format to another.
|
31
|
-
|
32
|
-
Args:
|
33
|
-
filepath: Path to the source audio file
|
34
|
-
source_format: Source audio format
|
35
|
-
target_format: Target audio format
|
36
|
-
output_path: Path to save the converted audio
|
37
|
-
options: Additional conversion options
|
38
|
-
|
39
|
-
Returns:
|
40
|
-
Path to the converted audio
|
41
|
-
|
42
|
-
Raises:
|
43
|
-
ValueError: If the conversion is not supported
|
44
|
-
RuntimeError: If the conversion fails
|
45
|
-
"""
|
46
|
-
if options is None:
|
47
|
-
options = {}
|
48
|
-
|
49
|
-
# Check if conversion is supported
|
50
|
-
if target_format not in SUPPORTED_CONVERSIONS.get(source_format, []):
|
51
|
-
raise ValueError(f"Conversion from {source_format} to {target_format} is not supported")
|
52
|
-
|
53
|
-
filepath = Path(filepath)
|
54
|
-
output_path = Path(output_path)
|
55
|
-
|
56
|
-
try:
|
57
|
-
# Try using pydub for audio conversion
|
58
|
-
return _convert_with_pydub(filepath, source_format, target_format, output_path, options)
|
59
|
-
except ImportError:
|
60
|
-
logger.warning("pydub not installed. Trying alternative method...")
|
61
|
-
|
62
|
-
# Try using ffmpeg directly
|
63
|
-
return _convert_with_ffmpeg(filepath, source_format, target_format, output_path, options)
|
64
|
-
|
65
|
-
def speech_to_text(
|
66
|
-
filepath: Union[str, Path],
|
67
|
-
output_path: Union[str, Path],
|
68
|
-
options: Optional[Dict[str, Any]] = None
|
69
|
-
) -> str:
|
70
|
-
"""
|
71
|
-
Convert speech audio to text.
|
72
|
-
|
73
|
-
Args:
|
74
|
-
filepath: Path to the source audio file
|
75
|
-
output_path: Path to save the text file
|
76
|
-
options: Additional conversion options
|
77
|
-
|
78
|
-
Returns:
|
79
|
-
Path to the text file
|
80
|
-
|
81
|
-
Raises:
|
82
|
-
RuntimeError: If the conversion fails
|
83
|
-
"""
|
84
|
-
if options is None:
|
85
|
-
options = {}
|
86
|
-
|
87
|
-
filepath = Path(filepath)
|
88
|
-
output_path = Path(output_path)
|
89
|
-
|
90
|
-
try:
|
91
|
-
# Try using SpeechRecognition library
|
92
|
-
import speech_recognition as sr
|
93
|
-
|
94
|
-
# Initialize recognizer
|
95
|
-
recognizer = sr.Recognizer()
|
96
|
-
|
97
|
-
# Load the audio file
|
98
|
-
with sr.AudioFile(str(filepath)) as source:
|
99
|
-
# Adjust for ambient noise
|
100
|
-
recognizer.adjust_for_ambient_noise(source)
|
101
|
-
|
102
|
-
# Record the audio
|
103
|
-
audio_data = recognizer.record(source)
|
104
|
-
|
105
|
-
# Recognize speech using Google Speech Recognition
|
106
|
-
language = options.get('language', 'en-US')
|
107
|
-
text = recognizer.recognize_google(audio_data, language=language)
|
108
|
-
|
109
|
-
# Write the text to the output file
|
110
|
-
with open(output_path, 'w', encoding='utf-8') as f:
|
111
|
-
f.write(text)
|
112
|
-
|
113
|
-
return str(output_path)
|
114
|
-
|
115
|
-
except ImportError:
|
116
|
-
logger.error("SpeechRecognition library is required for speech-to-text conversion")
|
117
|
-
raise RuntimeError("SpeechRecognition library is required for speech-to-text conversion. Please install it.")
|
118
|
-
|
119
|
-
except Exception as e:
|
120
|
-
logger.error(f"Error in speech-to-text conversion: {str(e)}")
|
121
|
-
raise RuntimeError(f"Failed to convert speech to text: {str(e)}")
|
122
|
-
|
123
|
-
def _convert_with_pydub(
|
124
|
-
filepath: Path,
|
125
|
-
source_format: str,
|
126
|
-
target_format: str,
|
127
|
-
output_path: Path,
|
128
|
-
options: Dict[str, Any]
|
129
|
-
) -> str:
|
130
|
-
"""Convert audio using pydub library."""
|
131
|
-
from pydub import AudioSegment
|
132
|
-
|
133
|
-
# Load the audio file
|
134
|
-
audio = AudioSegment.from_file(str(filepath), format=source_format)
|
135
|
-
|
136
|
-
# Apply audio processing options
|
137
|
-
if 'volume' in options:
|
138
|
-
# Adjust volume (in dB)
|
139
|
-
audio = audio + options['volume']
|
140
|
-
|
141
|
-
if 'speed' in options:
|
142
|
-
# Change speed (requires ffmpeg with rubberband)
|
143
|
-
speed = options['speed']
|
144
|
-
audio = audio._spawn(audio.raw_data, overrides={
|
145
|
-
"frame_rate": int(audio.frame_rate * speed)
|
146
|
-
})
|
147
|
-
|
148
|
-
if 'sample_rate' in options:
|
149
|
-
# Change sample rate
|
150
|
-
audio = audio.set_frame_rate(options['sample_rate'])
|
151
|
-
|
152
|
-
if 'channels' in options:
|
153
|
-
# Change number of channels
|
154
|
-
if options['channels'] == 1:
|
155
|
-
audio = audio.set_channels(1)
|
156
|
-
elif options['channels'] == 2:
|
157
|
-
audio = audio.set_channels(2)
|
158
|
-
|
159
|
-
if 'trim' in options:
|
160
|
-
# Trim audio (start_ms, end_ms)
|
161
|
-
start_ms, end_ms = options['trim']
|
162
|
-
audio = audio[start_ms:end_ms]
|
163
|
-
|
164
|
-
# Set export parameters
|
165
|
-
export_params = {}
|
166
|
-
|
167
|
-
if 'bitrate' in options:
|
168
|
-
export_params['bitrate'] = options['bitrate']
|
169
|
-
|
170
|
-
# Export to target format
|
171
|
-
audio.export(str(output_path), format=target_format, **export_params)
|
172
|
-
|
173
|
-
return str(output_path)
|
174
|
-
|
175
|
-
def _convert_with_ffmpeg(
|
176
|
-
filepath: Path,
|
177
|
-
source_format: str,
|
178
|
-
target_format: str,
|
179
|
-
output_path: Path,
|
180
|
-
options: Dict[str, Any]
|
181
|
-
) -> str:
|
182
|
-
"""Convert audio using ffmpeg directly."""
|
183
|
-
import subprocess
|
184
|
-
|
185
|
-
# Basic ffmpeg command
|
186
|
-
cmd = ['ffmpeg', '-i', str(filepath)]
|
187
|
-
|
188
|
-
# Add options
|
189
|
-
if 'volume' in options:
|
190
|
-
# Volume adjustment in dB
|
191
|
-
cmd.extend(['-filter:a', f'volume={options["volume"]}dB'])
|
192
|
-
|
193
|
-
if 'speed' in options:
|
194
|
-
# Speed adjustment
|
195
|
-
cmd.extend(['-filter:a', f'atempo={options["speed"]}'])
|
196
|
-
|
197
|
-
if 'sample_rate' in options:
|
198
|
-
# Sample rate
|
199
|
-
cmd.extend(['-ar', str(options['sample_rate'])])
|
200
|
-
|
201
|
-
if 'channels' in options:
|
202
|
-
# Number of channels
|
203
|
-
cmd.extend(['-ac', str(options['channels'])])
|
204
|
-
|
205
|
-
if 'bitrate' in options:
|
206
|
-
# Bitrate
|
207
|
-
cmd.extend(['-b:a', options['bitrate']])
|
208
|
-
|
209
|
-
if 'trim' in options:
|
210
|
-
# Trim (start_seconds, duration_seconds)
|
211
|
-
start_sec, duration_sec = options['trim']
|
212
|
-
cmd.extend(['-ss', str(start_sec), '-t', str(duration_sec)])
|
213
|
-
|
214
|
-
# Add output file
|
215
|
-
cmd.append(str(output_path))
|
216
|
-
|
217
|
-
# Run ffmpeg
|
218
|
-
try:
|
219
|
-
subprocess.run(cmd, check=True, capture_output=True)
|
220
|
-
return str(output_path)
|
221
|
-
except subprocess.SubprocessError as e:
|
222
|
-
logger.error(f"Error running ffmpeg: {str(e)}")
|
223
|
-
raise RuntimeError(f"Failed to convert audio with ffmpeg: {str(e)}")
|