openconvert 0.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.
@@ -0,0 +1,7 @@
1
+ """
2
+ OpenConvert - A versatile file conversion library
3
+ """
4
+
5
+ __version__ = '0.1.0'
6
+
7
+ from .converter import open_convert
openconvert/cli.py ADDED
@@ -0,0 +1,145 @@
1
+ """
2
+ Command-line interface for AGConvert.
3
+ """
4
+
5
+ import argparse
6
+ import json
7
+ import logging
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Dict, Any, Optional
12
+
13
+ from . import __version__
14
+ from .converter import open_convert
15
+
16
+ # Configure logging
17
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
18
+ logger = logging.getLogger(__name__)
19
+
20
+ def parse_args() -> argparse.Namespace:
21
+ """Parse command-line arguments."""
22
+ parser = argparse.ArgumentParser(
23
+ description="AGConvert - A versatile file and data conversion tool",
24
+ formatter_class=argparse.RawDescriptionHelpFormatter,
25
+ epilog="""
26
+ Examples:
27
+ agconvert image.png image.jpg # Convert PNG to JPG
28
+ agconvert document.docx document.pdf # Convert DOCX to PDF
29
+ agconvert audio.mp3 audio.wav # Convert MP3 to WAV
30
+ agconvert video.mp4 video.gif # Convert MP4 to GIF
31
+ agconvert data.json data.yaml # Convert JSON to YAML
32
+ agconvert --options '{"quality": 90}' image.png image.jpg # With options
33
+ """
34
+ )
35
+
36
+ parser.add_argument(
37
+ "input_file",
38
+ help="Path to the input file"
39
+ )
40
+
41
+ parser.add_argument(
42
+ "output_file",
43
+ help="Path to the output file"
44
+ )
45
+
46
+ parser.add_argument(
47
+ "--source-format",
48
+ help="Source file format (if not specified, will be inferred from input file extension)"
49
+ )
50
+
51
+ parser.add_argument(
52
+ "--target-format",
53
+ help="Target file format (if not specified, will be inferred from output file extension)"
54
+ )
55
+
56
+ parser.add_argument(
57
+ "--options",
58
+ help="Additional conversion options as JSON string",
59
+ default="{}"
60
+ )
61
+
62
+ parser.add_argument(
63
+ "--verbose", "-v",
64
+ action="store_true",
65
+ help="Enable verbose output"
66
+ )
67
+
68
+ parser.add_argument(
69
+ "--version",
70
+ action="version",
71
+ version=f"AGConvert {__version__}"
72
+ )
73
+
74
+ return parser.parse_args()
75
+
76
+ def parse_options(options_str: str) -> Dict[str, Any]:
77
+ """Parse options from JSON string."""
78
+ try:
79
+ return json.loads(options_str)
80
+ except json.JSONDecodeError as e:
81
+ logger.error(f"Error parsing options: {str(e)}")
82
+ logger.error("Options must be a valid JSON string")
83
+ sys.exit(1)
84
+
85
+ def infer_format(filepath: str) -> str:
86
+ """Infer file format from file extension."""
87
+ ext = os.path.splitext(filepath)[1].lower().lstrip('.')
88
+
89
+ # Handle special cases
90
+ if ext == 'jpeg':
91
+ return 'jpg'
92
+ elif ext == 'tif':
93
+ return 'tiff'
94
+ elif ext == 'yml':
95
+ return 'yaml'
96
+ elif ext == 'htm':
97
+ return 'html'
98
+ elif ext == 'markdown':
99
+ return 'md'
100
+ elif ext == 'tex':
101
+ return 'latex'
102
+
103
+ return ext
104
+
105
+ def main() -> None:
106
+ """Main entry point for the CLI."""
107
+ args = parse_args()
108
+
109
+ # Set logging level
110
+ if args.verbose:
111
+ logging.getLogger().setLevel(logging.DEBUG)
112
+
113
+ # Parse options
114
+ options = parse_options(args.options)
115
+
116
+ # Infer formats if not specified
117
+ source_format = args.source_format or infer_format(args.input_file)
118
+ target_format = args.target_format or infer_format(args.output_file)
119
+
120
+ if not source_format:
121
+ logger.error("Could not determine source format. Please specify --source-format")
122
+ sys.exit(1)
123
+
124
+ if not target_format:
125
+ logger.error("Could not determine target format. Please specify --target-format")
126
+ sys.exit(1)
127
+
128
+ try:
129
+ # Perform the conversion
130
+ output_path = open_convert(
131
+ filepath=args.input_file,
132
+ source_format=source_format,
133
+ target_format=target_format,
134
+ output_path=args.output_file,
135
+ options=options
136
+ )
137
+
138
+ logger.info(f"Conversion successful: {args.input_file} → {output_path}")
139
+
140
+ except Exception as e:
141
+ logger.error(f"Conversion failed: {str(e)}")
142
+ sys.exit(1)
143
+
144
+ if __name__ == "__main__":
145
+ main()
@@ -0,0 +1,152 @@
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")
@@ -0,0 +1,3 @@
1
+ """
2
+ Converters package for handling different types of file conversions.
3
+ """
@@ -0,0 +1,277 @@
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)