ffmpeg-python-helper 0.1.0__tar.gz → 2.0.0__tar.gz

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,346 @@
1
+ Metadata-Version: 2.3
2
+ Name: ffmpeg-python-helper
3
+ Version: 2.0.0
4
+ Summary: A Python wrapper for FFMPEG that provides a simple, intuitive API for common video processing tasks including format conversion, GIF creation, and video trimming.
5
+ Author: marjon
6
+ Author-email: marjon <marjongodito@gmanmi.com>
7
+ Requires-Python: >=3.14
8
+ Description-Content-Type: text/markdown
9
+
10
+ # FFMPEG Python Helper
11
+
12
+ A Python wrapper for FFMPEG that provides a simple, intuitive API for common video processing tasks.
13
+
14
+ ## Features
15
+
16
+ - 🔧 **Easy FFMPEG Integration** - Automatically detects FFMPEG installation
17
+ - 🎥 **Video Processing** - Reformat videos between formats
18
+ - 🎞️ **GIF Creation** - Convert videos to optimized GIFs with customizable settings
19
+ - 🧠 **In-Memory Processing** - Process video data directly from bytes without temporary files
20
+ - ✂️ **Video Trimming** - Trim videos with precise start time and duration control
21
+ - 🐍 **Pythonic API** - Clean, object-oriented interface with proper error handling
22
+ - 📁 **File Validation** - Automatic input file existence checking
23
+
24
+ ## Installation
25
+
26
+ ### Prerequisites
27
+ - Python 3.14 or higher
28
+ - FFMPEG installed and available in your system PATH
29
+
30
+ ### Install FFMPEG Python Helper
31
+
32
+ ```bash
33
+ pip install ffmpeg-python-helper
34
+ ```
35
+
36
+ Or install from source:
37
+
38
+ ```bash
39
+ git clone https://github.com/yourusername/ffmpeg-python-helper.git
40
+ cd ffmpeg-python-helper
41
+ pip install -e .
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ ```python
47
+ from ffmpeg_python_helper import FFMPEG
48
+
49
+ # Initialize the FFMPEG wrapper
50
+ ffmpeg = FFMPEG()
51
+
52
+ # Check if FFMPEG is available
53
+ print(f"FFMPEG executable found at: {ffmpeg.executable}")
54
+
55
+ # Convert a video file
56
+ output = ffmpeg.reformat("input.mp4", "output.avi")
57
+ print(output.decode())
58
+
59
+ # Create a GIF from video
60
+ output = ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
61
+ print(output.decode())
62
+
63
+ # Trim a video
64
+ output = ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
65
+ print(output.decode())
66
+
67
+ # Create GIF from in-memory video data
68
+ with open("video.mp4", "rb") as f:
69
+ video_data = f.read()
70
+ gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
71
+ with open("memory.gif", "wb") as f:
72
+ f.write(gif_data)
73
+ ```
74
+
75
+ ## API Reference
76
+
77
+ ### `FFMPEG` Class
78
+
79
+ The main class that wraps FFMPEG functionality.
80
+
81
+ #### Constructor
82
+ ```python
83
+ FFMPEG()
84
+ ```
85
+ Creates a new FFMPEG instance. Automatically searches for FFMPEG in the system PATH.
86
+ - **Raises**: `FileNotFoundError` if FFMPEG is not found in PATH
87
+
88
+ #### Properties
89
+ - `executable` (str): The path to the FFMPEG executable found in the system
90
+
91
+ #### Class Methods
92
+ ```python
93
+ @classmethod
94
+ def api(cls) -> "FFMPEG"
95
+ ```
96
+ Factory method that returns a new FFMPEG instance.
97
+ - **Returns**: `FFMPEG` instance
98
+
99
+ #### Instance Methods
100
+
101
+ ##### `execute(*args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]`
102
+ Execute raw FFMPEG commands with the given arguments.
103
+
104
+ **Parameters:**
105
+ - `*args` (str): FFMPEG command-line arguments
106
+ - `input_data` (bytes | None, optional): Optional bytes to send to FFMPEG's stdin
107
+
108
+ **Returns:**
109
+ - `tuple[bytes, bytes]`: A tuple containing (stdout, stderr) as bytes
110
+
111
+ **Raises:**
112
+ - `FileNotFoundError`: If FFMPEG executable is not found
113
+ - `RuntimeError`: If FFMPEG command returns a non-zero exit code
114
+
115
+ **Example:**
116
+ ```python
117
+ stdout, stderr = ffmpeg.execute("-version")
118
+ print(stdout.decode())
119
+
120
+ # Process data from memory
121
+ video_data = b"...video bytes..."
122
+ stdout, stderr = ffmpeg.execute("-i", "pipe:0", "-f", "null", "-", input_data=video_data)
123
+ ```
124
+
125
+ ##### `reformat(input_file: str, output_file: str) -> bytes`
126
+ Convert a video file from one format to another.
127
+
128
+ **Parameters:**
129
+ - `input_file` (str): Path to the input video file
130
+ - `output_file` (str): Path for the output video file
131
+
132
+ **Returns:**
133
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
134
+
135
+ **Raises:**
136
+ - `FileNotFoundError`: If input file doesn't exist
137
+
138
+ **Example:**
139
+ ```python
140
+ output = ffmpeg.reformat("input.mov", "output.mp4")
141
+ print(output.decode())
142
+ ```
143
+
144
+ ##### `gif(input_file: str, output_file: str, fps: int = 10, scale: int = 320) -> bytes`
145
+ Convert a video file to an optimized GIF.
146
+
147
+ **Parameters:**
148
+ - `input_file` (str): Path to the input video file
149
+ - `output_file` (str): Path for the output GIF file
150
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
151
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
152
+
153
+ **Returns:**
154
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
155
+
156
+ **Raises:**
157
+ - `FileNotFoundError`: If input file doesn't exist
158
+ - `RuntimeError`: If GIF file was not created successfully
159
+
160
+ **Example:**
161
+ ```python
162
+ output = ffmpeg.gif("video.mp4", "output.gif", fps=15, scale=640)
163
+ print(output.decode())
164
+ ```
165
+
166
+ ##### `gifs(input_byte: bytes, fps: int = 10, scale: int = 320) -> bytes`
167
+ Convert video data from bytes to an optimized GIF (in-memory processing).
168
+
169
+ **Parameters:**
170
+ - `input_byte` (bytes): Video data as bytes to convert to GIF
171
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
172
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
173
+
174
+ **Returns:**
175
+ - `bytes`: The generated GIF data as bytes
176
+
177
+ **Raises:**
178
+ - `RuntimeError`: If GIF conversion fails
179
+
180
+ **Example:**
181
+ ```python
182
+ # Read video data from a file
183
+ with open("video.mp4", "rb") as f:
184
+ video_data = f.read()
185
+
186
+ # Convert to GIF in memory
187
+ gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
188
+
189
+ # Save the GIF
190
+ with open("output.gif", "wb") as f:
191
+ f.write(gif_data)
192
+ ```
193
+
194
+ ##### `trim(input_file: str, output_file: str, start: float = 0, duration: float | None = None) -> bytes`
195
+ Trim a video file.
196
+
197
+ **Parameters:**
198
+ - `input_file` (str): Path to the input video file
199
+ - `output_file` (str): Path for the output trimmed video
200
+ - `start` (float, optional): Start time in seconds (default: 0)
201
+ - `duration` (float | None, optional): Duration in seconds, or None for remaining video (default: None)
202
+
203
+ **Returns:**
204
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
205
+
206
+ **Raises:**
207
+ - `FileNotFoundError`: If input file doesn't exist
208
+ - `ValueError`: If start is negative or duration is non-positive
209
+ - `RuntimeError`: If trimmed video was not created successfully
210
+
211
+ **Example:**
212
+ ```python
213
+ # Trim from 5 seconds to 10 seconds (5-second clip)
214
+ output = ffmpeg.trim("video.mp4", "clip.mp4", start=5, duration=5)
215
+ print(output.decode())
216
+
217
+ # Trim from 10 seconds to the end of video
218
+ output = ffmpeg.trim("video.mp4", "ending.mp4", start=10)
219
+ print(output.decode())
220
+ ```
221
+
222
+ ## Advanced Usage
223
+
224
+ ### Custom FFMPEG Commands
225
+ For operations not covered by the built-in methods, use the `execute` method:
226
+
227
+ ```python
228
+ # Extract audio from video
229
+ ffmpeg.execute("-i", "video.mp4", "-q:a", "0", "-map", "a", "audio.mp3")
230
+
231
+ # Add watermark to video
232
+ ffmpeg.execute("-i", "video.mp4", "-i", "watermark.png",
233
+ "-filter_complex", "overlay=10:10", "output.mp4")
234
+
235
+ # Change video bitrate
236
+ ffmpeg.execute("-i", "input.mp4", "-b:v", "1M", "output.mp4")
237
+ ```
238
+
239
+ ### Error Handling
240
+ ```python
241
+ from ffmpeg_python_helper import FFMPEG
242
+ import sys
243
+
244
+ try:
245
+ ffmpeg = FFMPEG()
246
+ ffmpeg.gif("video.mp4", "output.gif")
247
+ except FileNotFoundError as e:
248
+ print(f"FFMPEG not found: {e}", file=sys.stderr)
249
+ sys.exit(1)
250
+ except RuntimeError as e:
251
+ print(f"Processing failed: {e}", file=sys.stderr)
252
+ sys.exit(1)
253
+ ```
254
+
255
+ ## Common Use Cases
256
+
257
+ ### Batch Processing
258
+ ```python
259
+ import os
260
+ from ffmpeg_python_helper import FFMPEG
261
+
262
+ ffmpeg = FFMPEG()
263
+ videos = ["video1.mp4", "video2.mp4", "video3.mp4"]
264
+
265
+ for video in videos:
266
+ if os.path.exists(video):
267
+ base_name = os.path.splitext(video)[0]
268
+ ffmpeg.gif(video, f"{base_name}.gif", fps=12, scale=400)
269
+ ```
270
+
271
+ ### Video Compilation
272
+ ```python
273
+ from ffmpeg_python_helper import FFMPEG
274
+
275
+ ffmpeg = FFMPEG()
276
+
277
+ # Trim interesting parts
278
+ ffmpeg.trim("concert.mp4", "intro.mp4", start=0, duration=30)
279
+ ffmpeg.trim("concert.mp4", "chorus.mp4", start=120, duration=45)
280
+ ffmpeg.trim("concert.mp4", "finale.mp4", start=300, duration=60)
281
+
282
+ # Later, use FFMPEG to concatenate trimmed parts
283
+ ffmpeg.execute("-f", "concat", "-safe", "0", "-i", "parts.txt", "highlight_reel.mp4")
284
+ ```
285
+
286
+ ## Troubleshooting
287
+
288
+ ### FFMPEG Not Found
289
+ If you get `FileNotFoundError` when creating an FFMPEG instance:
290
+
291
+ 1. **Install FFMPEG**:
292
+ - **Windows**: Download from [ffmpeg.org](https://ffmpeg.org/download.html)
293
+ - **macOS**: `brew install ffmpeg`
294
+ - **Linux**: `sudo apt install ffmpeg` (Ubuntu/Debian) or `sudo yum install ffmpeg` (Fedora/RHEL)
295
+
296
+ 2. **Add to PATH**:
297
+ - Ensure FFMPEG is in your system PATH
298
+ - Test with `ffmpeg -version` in your terminal
299
+
300
+ ### File Not Found Errors
301
+ - Ensure input file paths are correct and files exist
302
+ - Use absolute paths if working with files in different directories
303
+ - Check file permissions
304
+
305
+ ### GIF Creation Issues
306
+ - Lower FPS or scale if GIF file is too large
307
+ - Ensure input video has sufficient quality
308
+ - Check available disk space
309
+
310
+ ## Development
311
+
312
+ ### Running Tests
313
+ ```bash
314
+ python -m pytest tests/
315
+ ```
316
+
317
+ ### Building Documentation
318
+ ```bash
319
+ # Install documentation dependencies
320
+ pip install pdoc3
321
+
322
+ # Generate API documentation
323
+ pdoc --html ffmpeg_python_helper --output-dir docs
324
+ ```
325
+
326
+ ### Contributing
327
+ 1. Fork the repository
328
+ 2. Create a feature branch
329
+ 3. Add tests for your changes
330
+ 4. Ensure all tests pass
331
+ 5. Submit a pull request
332
+
333
+ ## License
334
+
335
+ MIT License - see LICENSE file for details
336
+
337
+ ## Support
338
+
339
+ - **Issues**: [GitHub Issues](https://github.com/yourusername/ffmpeg-python-helper/issues)
340
+ - **Documentation**: [ReadTheDocs](https://ffmpeg-python-helper.readthedocs.io)
341
+ - **Email**: marjongodito@gmanmi.com
342
+
343
+ ## Acknowledgments
344
+
345
+ - FFMPEG team for the amazing multimedia framework
346
+ - Python community for excellent tooling and libraries
@@ -0,0 +1,337 @@
1
+ # FFMPEG Python Helper
2
+
3
+ A Python wrapper for FFMPEG that provides a simple, intuitive API for common video processing tasks.
4
+
5
+ ## Features
6
+
7
+ - 🔧 **Easy FFMPEG Integration** - Automatically detects FFMPEG installation
8
+ - 🎥 **Video Processing** - Reformat videos between formats
9
+ - 🎞️ **GIF Creation** - Convert videos to optimized GIFs with customizable settings
10
+ - 🧠 **In-Memory Processing** - Process video data directly from bytes without temporary files
11
+ - ✂️ **Video Trimming** - Trim videos with precise start time and duration control
12
+ - 🐍 **Pythonic API** - Clean, object-oriented interface with proper error handling
13
+ - 📁 **File Validation** - Automatic input file existence checking
14
+
15
+ ## Installation
16
+
17
+ ### Prerequisites
18
+ - Python 3.14 or higher
19
+ - FFMPEG installed and available in your system PATH
20
+
21
+ ### Install FFMPEG Python Helper
22
+
23
+ ```bash
24
+ pip install ffmpeg-python-helper
25
+ ```
26
+
27
+ Or install from source:
28
+
29
+ ```bash
30
+ git clone https://github.com/yourusername/ffmpeg-python-helper.git
31
+ cd ffmpeg-python-helper
32
+ pip install -e .
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```python
38
+ from ffmpeg_python_helper import FFMPEG
39
+
40
+ # Initialize the FFMPEG wrapper
41
+ ffmpeg = FFMPEG()
42
+
43
+ # Check if FFMPEG is available
44
+ print(f"FFMPEG executable found at: {ffmpeg.executable}")
45
+
46
+ # Convert a video file
47
+ output = ffmpeg.reformat("input.mp4", "output.avi")
48
+ print(output.decode())
49
+
50
+ # Create a GIF from video
51
+ output = ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
52
+ print(output.decode())
53
+
54
+ # Trim a video
55
+ output = ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
56
+ print(output.decode())
57
+
58
+ # Create GIF from in-memory video data
59
+ with open("video.mp4", "rb") as f:
60
+ video_data = f.read()
61
+ gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
62
+ with open("memory.gif", "wb") as f:
63
+ f.write(gif_data)
64
+ ```
65
+
66
+ ## API Reference
67
+
68
+ ### `FFMPEG` Class
69
+
70
+ The main class that wraps FFMPEG functionality.
71
+
72
+ #### Constructor
73
+ ```python
74
+ FFMPEG()
75
+ ```
76
+ Creates a new FFMPEG instance. Automatically searches for FFMPEG in the system PATH.
77
+ - **Raises**: `FileNotFoundError` if FFMPEG is not found in PATH
78
+
79
+ #### Properties
80
+ - `executable` (str): The path to the FFMPEG executable found in the system
81
+
82
+ #### Class Methods
83
+ ```python
84
+ @classmethod
85
+ def api(cls) -> "FFMPEG"
86
+ ```
87
+ Factory method that returns a new FFMPEG instance.
88
+ - **Returns**: `FFMPEG` instance
89
+
90
+ #### Instance Methods
91
+
92
+ ##### `execute(*args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]`
93
+ Execute raw FFMPEG commands with the given arguments.
94
+
95
+ **Parameters:**
96
+ - `*args` (str): FFMPEG command-line arguments
97
+ - `input_data` (bytes | None, optional): Optional bytes to send to FFMPEG's stdin
98
+
99
+ **Returns:**
100
+ - `tuple[bytes, bytes]`: A tuple containing (stdout, stderr) as bytes
101
+
102
+ **Raises:**
103
+ - `FileNotFoundError`: If FFMPEG executable is not found
104
+ - `RuntimeError`: If FFMPEG command returns a non-zero exit code
105
+
106
+ **Example:**
107
+ ```python
108
+ stdout, stderr = ffmpeg.execute("-version")
109
+ print(stdout.decode())
110
+
111
+ # Process data from memory
112
+ video_data = b"...video bytes..."
113
+ stdout, stderr = ffmpeg.execute("-i", "pipe:0", "-f", "null", "-", input_data=video_data)
114
+ ```
115
+
116
+ ##### `reformat(input_file: str, output_file: str) -> bytes`
117
+ Convert a video file from one format to another.
118
+
119
+ **Parameters:**
120
+ - `input_file` (str): Path to the input video file
121
+ - `output_file` (str): Path for the output video file
122
+
123
+ **Returns:**
124
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
125
+
126
+ **Raises:**
127
+ - `FileNotFoundError`: If input file doesn't exist
128
+
129
+ **Example:**
130
+ ```python
131
+ output = ffmpeg.reformat("input.mov", "output.mp4")
132
+ print(output.decode())
133
+ ```
134
+
135
+ ##### `gif(input_file: str, output_file: str, fps: int = 10, scale: int = 320) -> bytes`
136
+ Convert a video file to an optimized GIF.
137
+
138
+ **Parameters:**
139
+ - `input_file` (str): Path to the input video file
140
+ - `output_file` (str): Path for the output GIF file
141
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
142
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
143
+
144
+ **Returns:**
145
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
146
+
147
+ **Raises:**
148
+ - `FileNotFoundError`: If input file doesn't exist
149
+ - `RuntimeError`: If GIF file was not created successfully
150
+
151
+ **Example:**
152
+ ```python
153
+ output = ffmpeg.gif("video.mp4", "output.gif", fps=15, scale=640)
154
+ print(output.decode())
155
+ ```
156
+
157
+ ##### `gifs(input_byte: bytes, fps: int = 10, scale: int = 320) -> bytes`
158
+ Convert video data from bytes to an optimized GIF (in-memory processing).
159
+
160
+ **Parameters:**
161
+ - `input_byte` (bytes): Video data as bytes to convert to GIF
162
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
163
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
164
+
165
+ **Returns:**
166
+ - `bytes`: The generated GIF data as bytes
167
+
168
+ **Raises:**
169
+ - `RuntimeError`: If GIF conversion fails
170
+
171
+ **Example:**
172
+ ```python
173
+ # Read video data from a file
174
+ with open("video.mp4", "rb") as f:
175
+ video_data = f.read()
176
+
177
+ # Convert to GIF in memory
178
+ gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
179
+
180
+ # Save the GIF
181
+ with open("output.gif", "wb") as f:
182
+ f.write(gif_data)
183
+ ```
184
+
185
+ ##### `trim(input_file: str, output_file: str, start: float = 0, duration: float | None = None) -> bytes`
186
+ Trim a video file.
187
+
188
+ **Parameters:**
189
+ - `input_file` (str): Path to the input video file
190
+ - `output_file` (str): Path for the output trimmed video
191
+ - `start` (float, optional): Start time in seconds (default: 0)
192
+ - `duration` (float | None, optional): Duration in seconds, or None for remaining video (default: None)
193
+
194
+ **Returns:**
195
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
196
+
197
+ **Raises:**
198
+ - `FileNotFoundError`: If input file doesn't exist
199
+ - `ValueError`: If start is negative or duration is non-positive
200
+ - `RuntimeError`: If trimmed video was not created successfully
201
+
202
+ **Example:**
203
+ ```python
204
+ # Trim from 5 seconds to 10 seconds (5-second clip)
205
+ output = ffmpeg.trim("video.mp4", "clip.mp4", start=5, duration=5)
206
+ print(output.decode())
207
+
208
+ # Trim from 10 seconds to the end of video
209
+ output = ffmpeg.trim("video.mp4", "ending.mp4", start=10)
210
+ print(output.decode())
211
+ ```
212
+
213
+ ## Advanced Usage
214
+
215
+ ### Custom FFMPEG Commands
216
+ For operations not covered by the built-in methods, use the `execute` method:
217
+
218
+ ```python
219
+ # Extract audio from video
220
+ ffmpeg.execute("-i", "video.mp4", "-q:a", "0", "-map", "a", "audio.mp3")
221
+
222
+ # Add watermark to video
223
+ ffmpeg.execute("-i", "video.mp4", "-i", "watermark.png",
224
+ "-filter_complex", "overlay=10:10", "output.mp4")
225
+
226
+ # Change video bitrate
227
+ ffmpeg.execute("-i", "input.mp4", "-b:v", "1M", "output.mp4")
228
+ ```
229
+
230
+ ### Error Handling
231
+ ```python
232
+ from ffmpeg_python_helper import FFMPEG
233
+ import sys
234
+
235
+ try:
236
+ ffmpeg = FFMPEG()
237
+ ffmpeg.gif("video.mp4", "output.gif")
238
+ except FileNotFoundError as e:
239
+ print(f"FFMPEG not found: {e}", file=sys.stderr)
240
+ sys.exit(1)
241
+ except RuntimeError as e:
242
+ print(f"Processing failed: {e}", file=sys.stderr)
243
+ sys.exit(1)
244
+ ```
245
+
246
+ ## Common Use Cases
247
+
248
+ ### Batch Processing
249
+ ```python
250
+ import os
251
+ from ffmpeg_python_helper import FFMPEG
252
+
253
+ ffmpeg = FFMPEG()
254
+ videos = ["video1.mp4", "video2.mp4", "video3.mp4"]
255
+
256
+ for video in videos:
257
+ if os.path.exists(video):
258
+ base_name = os.path.splitext(video)[0]
259
+ ffmpeg.gif(video, f"{base_name}.gif", fps=12, scale=400)
260
+ ```
261
+
262
+ ### Video Compilation
263
+ ```python
264
+ from ffmpeg_python_helper import FFMPEG
265
+
266
+ ffmpeg = FFMPEG()
267
+
268
+ # Trim interesting parts
269
+ ffmpeg.trim("concert.mp4", "intro.mp4", start=0, duration=30)
270
+ ffmpeg.trim("concert.mp4", "chorus.mp4", start=120, duration=45)
271
+ ffmpeg.trim("concert.mp4", "finale.mp4", start=300, duration=60)
272
+
273
+ # Later, use FFMPEG to concatenate trimmed parts
274
+ ffmpeg.execute("-f", "concat", "-safe", "0", "-i", "parts.txt", "highlight_reel.mp4")
275
+ ```
276
+
277
+ ## Troubleshooting
278
+
279
+ ### FFMPEG Not Found
280
+ If you get `FileNotFoundError` when creating an FFMPEG instance:
281
+
282
+ 1. **Install FFMPEG**:
283
+ - **Windows**: Download from [ffmpeg.org](https://ffmpeg.org/download.html)
284
+ - **macOS**: `brew install ffmpeg`
285
+ - **Linux**: `sudo apt install ffmpeg` (Ubuntu/Debian) or `sudo yum install ffmpeg` (Fedora/RHEL)
286
+
287
+ 2. **Add to PATH**:
288
+ - Ensure FFMPEG is in your system PATH
289
+ - Test with `ffmpeg -version` in your terminal
290
+
291
+ ### File Not Found Errors
292
+ - Ensure input file paths are correct and files exist
293
+ - Use absolute paths if working with files in different directories
294
+ - Check file permissions
295
+
296
+ ### GIF Creation Issues
297
+ - Lower FPS or scale if GIF file is too large
298
+ - Ensure input video has sufficient quality
299
+ - Check available disk space
300
+
301
+ ## Development
302
+
303
+ ### Running Tests
304
+ ```bash
305
+ python -m pytest tests/
306
+ ```
307
+
308
+ ### Building Documentation
309
+ ```bash
310
+ # Install documentation dependencies
311
+ pip install pdoc3
312
+
313
+ # Generate API documentation
314
+ pdoc --html ffmpeg_python_helper --output-dir docs
315
+ ```
316
+
317
+ ### Contributing
318
+ 1. Fork the repository
319
+ 2. Create a feature branch
320
+ 3. Add tests for your changes
321
+ 4. Ensure all tests pass
322
+ 5. Submit a pull request
323
+
324
+ ## License
325
+
326
+ MIT License - see LICENSE file for details
327
+
328
+ ## Support
329
+
330
+ - **Issues**: [GitHub Issues](https://github.com/yourusername/ffmpeg-python-helper/issues)
331
+ - **Documentation**: [ReadTheDocs](https://ffmpeg-python-helper.readthedocs.io)
332
+ - **Email**: marjongodito@gmanmi.com
333
+
334
+ ## Acknowledgments
335
+
336
+ - FFMPEG team for the amazing multimedia framework
337
+ - Python community for excellent tooling and libraries
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "ffmpeg-python-helper"
3
- version = "0.1.0"
4
- description = "Add your description here"
3
+ version = "2.0.0"
4
+ description = "A Python wrapper for FFMPEG that provides a simple, intuitive API for common video processing tasks including format conversion, GIF creation, and video trimming."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
7
7
  dependencies = []
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "ffmpeg-python-helper"
3
- version = "0.1.0"
4
- description = "Add your description here"
3
+ version = "2.0.0"
4
+ description = "A Python wrapper for FFMPEG that provides a simple, intuitive API for common video processing tasks including format conversion, GIF creation, and video trimming."
5
5
  readme = "README.md"
6
6
  authors = [
7
7
  { name = "marjon", email = "marjongodito@gmanmi.com" }
@@ -0,0 +1,51 @@
1
+ """
2
+ FFMPEG Python Helper - A Python wrapper for FFMPEG video processing.
3
+
4
+ This package provides a simple, intuitive API for common video processing tasks
5
+ including format conversion, GIF creation, video trimming, and in-memory processing.
6
+
7
+ Example:
8
+ >>> from ffmpeg_python_helper import FFMPEG
9
+ >>> ffmpeg = FFMPEG()
10
+ >>> ffmpeg.reformat("input.mp4", "output.avi")
11
+ >>> ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
12
+ >>> ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
13
+ >>>
14
+ >>> # In-memory processing
15
+ >>> with open("video.mp4", "rb") as f:
16
+ ... video_data = f.read()
17
+ >>> gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
18
+
19
+ For detailed API documentation, see:
20
+ - FFMPEG class documentation
21
+ - README.md for usage examples and tutorials
22
+ """
23
+
24
+ from .ffmpeg_api import FFMPEG
25
+
26
+ __all__ = [
27
+ 'FFMPEG'
28
+ ]
29
+
30
+ __version__ = "0.1.0"
31
+ __author__ = "marjon <marjongodito@gmanmi.com>"
32
+
33
+
34
+ def main() -> None:
35
+ """
36
+ Command-line entry point for the FFMPEG Python Helper.
37
+
38
+ This function is called when the package is executed as a script.
39
+ It demonstrates basic functionality by:
40
+ 1. Creating an FFMPEG instance
41
+ 2. Printing the path to the FFMPEG executable
42
+
43
+ Example:
44
+ $ python -m ffmpeg_python_helper
45
+ FFMPEG executable found at: /usr/bin/ffmpeg
46
+
47
+ Raises:
48
+ FileNotFoundError: If FFMPEG is not found in PATH.
49
+ """
50
+ ffmpeg = FFMPEG()
51
+ print(f"FFMPEG executable found at: {ffmpeg.executable}")
@@ -0,0 +1,380 @@
1
+ import subprocess
2
+ from pathlib import Path
3
+
4
+
5
+ class FFMPEG:
6
+ """
7
+ A Python wrapper for FFMPEG that provides a simple, intuitive API for
8
+ common video processing tasks.
9
+
10
+ This class automatically detects FFMPEG installation in the system PATH
11
+ and provides methods for video conversion, GIF creation, and video trimming.
12
+ It supports both file-based operations and in-memory data processing.
13
+
14
+ Example:
15
+ >>> from ffmpeg_python_helper import FFMPEG
16
+ >>> ffmpeg = FFMPEG()
17
+ >>> ffmpeg.reformat("input.mp4", "output.avi")
18
+ >>> ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
19
+ >>> ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
20
+ >>>
21
+ >>> # In-memory processing
22
+ >>> with open("video.mp4", "rb") as f:
23
+ ... video_data = f.read()
24
+ >>> gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
25
+
26
+ Attributes:
27
+ executable (str): The path to the FFMPEG executable found in the system PATH.
28
+ """
29
+
30
+ def __init__(self) -> None:
31
+ """
32
+ Initialize a new FFMPEG instance.
33
+
34
+ Automatically searches for FFMPEG in the system PATH.
35
+
36
+ Raises:
37
+ FileNotFoundError: If FFMPEG is not found in the system PATH.
38
+
39
+ Example:
40
+ >>> try:
41
+ ... ffmpeg = FFMPEG()
42
+ ... print(f"FFMPEG found at: {ffmpeg.executable}")
43
+ ... except FileNotFoundError as e:
44
+ ... print(f"FFMPEG not found: {e}")
45
+ """
46
+ import shutil
47
+ self.executable = shutil.which('ffmpeg')
48
+
49
+ if self.executable is None:
50
+ raise FileNotFoundError("""
51
+ FFmpeg is not installed or could not be found in PATH.
52
+ Please install FFmpeg and make sure it is available
53
+ in your system PATH.
54
+ """)
55
+
56
+ @classmethod
57
+ def api(cls) -> "FFMPEG":
58
+ """
59
+ Factory method that returns a new FFMPEG instance.
60
+
61
+ Returns:
62
+ FFMPEG: A new instance of the FFMPEG class.
63
+
64
+ Example:
65
+ >>> ffmpeg = FFMPEG.api()
66
+ >>> ffmpeg.execute("-version")
67
+ """
68
+ return cls()
69
+
70
+ def execute(self, *args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]:
71
+ """
72
+ Execute raw FFMPEG commands with the given arguments.
73
+
74
+ This method allows you to run any FFMPEG command directly,
75
+ providing maximum flexibility for operations not covered
76
+ by the built-in methods.
77
+
78
+ Args:
79
+ *args: FFMPEG command-line arguments as strings.
80
+ input_data: Optional bytes to send to FFMPEG's stdin. Useful for
81
+ piping data directly to FFMPEG without intermediate files.
82
+
83
+ Returns:
84
+ tuple[bytes, bytes]: A tuple containing (stdout, stderr) from FFMPEG
85
+ as bytes objects.
86
+
87
+ Raises:
88
+ FileNotFoundError: If FFMPEG executable is not found.
89
+ RuntimeError: If FFMPEG command returns a non-zero exit code.
90
+
91
+ Example:
92
+ >>> stdout, stderr = ffmpeg.execute("-version")
93
+ >>> print(stdout.decode())
94
+
95
+ >>> # Extract audio from video
96
+ >>> ffmpeg.execute("-i", "video.mp4", "-q:a", "0", "-map", "a", "audio.mp3")
97
+
98
+ >>> # Add watermark to video
99
+ >>> ffmpeg.execute("-i", "video.mp4", "-i", "watermark.png",
100
+ ... "-filter_complex", "overlay=10:10", "output.mp4")
101
+
102
+ >>> # Process data from memory
103
+ >>> video_data = b"...video bytes..."
104
+ >>> stdout, stderr = ffmpeg.execute("-i", "pipe:0", "-f", "null", "-",
105
+ ... input_data=video_data)
106
+ """
107
+ if self.executable:
108
+ result = subprocess.run(
109
+ [self.executable, *args],
110
+ input=input_data,
111
+ stdout=subprocess.PIPE,
112
+ stderr=subprocess.PIPE,
113
+ check=False
114
+ )
115
+
116
+ if result.returncode != 0:
117
+ raise RuntimeError(
118
+ result.stderr.decode(errors="replace")
119
+ )
120
+
121
+ return result.stdout, result.stderr
122
+
123
+ raise FileNotFoundError("""
124
+ No ffmpeg executable found.
125
+
126
+ Please Install ffmpeg first.
127
+ """)
128
+
129
+ def reformat(self, input_file: str, output_file: str) -> bytes:
130
+ """
131
+ Convert a video file from one format to another.
132
+
133
+ This method performs a simple format conversion without
134
+ modifying video quality or other parameters.
135
+
136
+ Args:
137
+ input_file: Path to the input video file.
138
+ output_file: Path for the output video file.
139
+
140
+ Returns:
141
+ bytes: FFMPEG output (stdout or stderr) as bytes.
142
+
143
+ Raises:
144
+ FileNotFoundError: If input file doesn't exist.
145
+
146
+ Example:
147
+ >>> output = ffmpeg.reformat("input.mov", "output.mp4")
148
+ >>> print(output.decode())
149
+ >>>
150
+ >>> output = ffmpeg.reformat("video.avi", "video.mkv")
151
+ >>> print(output.decode())
152
+ """
153
+ if not Path(input_file).exists():
154
+ raise FileNotFoundError(f"""
155
+ Input file {input_file} did not found .
156
+
157
+ Please check filename and directory if correct.
158
+ """)
159
+
160
+ stdout, stderr = self.execute("-i", input_file, output_file)
161
+
162
+ if stdout:
163
+ return stdout
164
+ else:
165
+ return stderr
166
+
167
+ def gifs(self,
168
+ input_byte: bytes,
169
+ fps: int = 10,
170
+ scale: int = 320) -> bytes:
171
+ """
172
+ Convert video data from bytes to an optimized GIF.
173
+
174
+ Creates a high-quality GIF from in-memory video data using FFMPEG's
175
+ palette optimization. This method is useful when you have video data
176
+ in memory and want to avoid writing temporary files.
177
+
178
+ Args:
179
+ input_byte: Video data as bytes to convert to GIF.
180
+ fps: Frames per second for the GIF. Defaults to 10.
181
+ scale: Width of the GIF in pixels. Height is auto-scaled
182
+ to maintain aspect ratio. Defaults to 320.
183
+
184
+ Returns:
185
+ bytes: The generated GIF data as bytes.
186
+
187
+ Raises:
188
+ RuntimeError: If GIF conversion fails.
189
+
190
+ Example:
191
+ >>> # Read video data from a file
192
+ >>> with open("video.mp4", "rb") as f:
193
+ ... video_data = f.read()
194
+ >>>
195
+ >>> # Convert to GIF in memory
196
+ >>> gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
197
+ >>>
198
+ >>> # Save the GIF
199
+ >>> with open("output.gif", "wb") as f:
200
+ ... f.write(gif_data)
201
+
202
+ >>> # Process video from network or database
203
+ >>> # video_bytes = download_video_from_url(url)
204
+ >>> # gif_bytes = ffmpeg.gifs(video_bytes, scale=320)
205
+ """
206
+ filter_graph: str = (
207
+ f"fps={fps},"
208
+ f"scale={scale}:-1:flags=lanczos,"
209
+ "split[s0][s1];"
210
+ "[s0]palettegen[p];"
211
+ "[s1][p]paletteuse"
212
+ )
213
+
214
+ stdout, stderr = self.execute(
215
+ "-y",
216
+ "-i", "pipe:0",
217
+ "-vf", filter_graph,
218
+ "-f", "gif",
219
+ "pipe:1",
220
+ input_data=input_byte
221
+ )
222
+
223
+ return stdout
224
+
225
+ def gif(self,
226
+ input_file: str,
227
+ output_file: str,
228
+ fps: int = 10,
229
+ scale: int = 320
230
+ ) -> bytes:
231
+ """
232
+ Convert a video file to an optimized GIF.
233
+
234
+ Creates a high-quality GIF using FFMPEG's palette optimization
235
+ for better color reproduction and smaller file sizes.
236
+
237
+ Args:
238
+ input_file: Path to the input video file.
239
+ output_file: Path for the output GIF file.
240
+ fps: Frames per second for the GIF. Defaults to 10.
241
+ scale: Width of the GIF in pixels. Height is auto-scaled
242
+ to maintain aspect ratio. Defaults to 320.
243
+
244
+ Returns:
245
+ bytes: FFMPEG output (stdout or stderr) as bytes.
246
+
247
+ Raises:
248
+ FileNotFoundError: If input file doesn't exist.
249
+ RuntimeError: If GIF file was not created successfully.
250
+
251
+ Example:
252
+ >>> # Create a standard GIF
253
+ >>> output = ffmpeg.gif("video.mp4", "output.gif")
254
+ >>> print(output.decode())
255
+
256
+ >>> # Create a higher quality GIF with custom settings
257
+ >>> output = ffmpeg.gif("video.mp4", "output.gif",
258
+ ... fps=15, scale=640)
259
+ >>> print(output.decode())
260
+
261
+ >>> # Create a small thumbnail GIF
262
+ >>> output = ffmpeg.gif("video.mp4", "thumbnail.gif",
263
+ ... fps=5, scale=160)
264
+ >>> print(output.decode())
265
+ """
266
+ input_path = Path(input_file)
267
+ output_path = Path(output_file)
268
+
269
+ if not input_path.exists():
270
+ raise FileNotFoundError(
271
+ f"Input file {input_file} was not found."
272
+ )
273
+
274
+ filter_graph: str = (
275
+ f"fps={fps},"
276
+ f"scale={scale}:-1:flags=lanczos,"
277
+ "split[s0][s1];"
278
+ "[s0]palettegen[p];"
279
+ "[s1][p]paletteuse"
280
+ )
281
+
282
+ stdout, stderr = self.execute(
283
+ "-y",
284
+ "-i", str(input_path),
285
+ "-vf", filter_graph,
286
+ str(output_path),
287
+ )
288
+
289
+ if not output_path.exists():
290
+ raise RuntimeError(
291
+ f"GIF was not created: {output_file}"
292
+ )
293
+
294
+ if stderr:
295
+ return stderr
296
+
297
+ return stdout
298
+
299
+ def trim(
300
+ self,
301
+ input_file: str,
302
+ output_file: str,
303
+ start: float = 0,
304
+ duration: float | None = None,
305
+ ) -> bytes:
306
+ """
307
+ Trim a video file.
308
+
309
+ Extracts a segment from a video file starting at a specified time
310
+ and optionally ending after a specified duration.
311
+
312
+ Args:
313
+ input_file: Path to the input video file.
314
+ output_file: Path for the output trimmed video.
315
+ start: Start time in seconds. Defaults to 0.
316
+ duration: Duration in seconds, or None for remaining video.
317
+ Defaults to None.
318
+
319
+ Returns:
320
+ bytes: FFMPEG output (stdout or stderr) as bytes.
321
+
322
+ Raises:
323
+ FileNotFoundError: If input file doesn't exist.
324
+ ValueError: If start is negative or duration is non-positive.
325
+ RuntimeError: If trimmed video was not created successfully.
326
+
327
+ Example:
328
+ >>> # Trim from 5 seconds to 10 seconds (5-second clip)
329
+ >>> output = ffmpeg.trim("video.mp4", "clip.mp4",
330
+ ... start=5, duration=5)
331
+ >>> print(output.decode())
332
+
333
+ >>> # Trim from 10 seconds to the end of video
334
+ >>> output = ffmpeg.trim("video.mp4", "ending.mp4",
335
+ ... start=10)
336
+ >>> print(output.decode())
337
+
338
+ >>> # Trim first 30 seconds of video
339
+ >>> output = ffmpeg.trim("video.mp4", "intro.mp4",
340
+ ... start=0, duration=30)
341
+ >>> print(output.decode())
342
+
343
+ >>> # Trim with floating point precision
344
+ >>> output = ffmpeg.trim("video.mp4", "precise.mp4",
345
+ ... start=2.5, duration=3.75)
346
+ >>> print(output.decode())
347
+ """
348
+ input_path = Path(input_file)
349
+ output_path = Path(output_file)
350
+
351
+ if not input_path.exists():
352
+ raise FileNotFoundError(
353
+ f"Input file {input_file} was not found."
354
+ )
355
+
356
+ if start < 0:
357
+ raise ValueError("start must be greater than or equal to 0")
358
+
359
+ if duration is not None and duration <= 0:
360
+ raise ValueError("duration must be greater than 0")
361
+
362
+ args = [
363
+ "-y",
364
+ "-ss", str(start),
365
+ "-i", str(input_path),
366
+ ]
367
+
368
+ if duration is not None:
369
+ args.extend(["-t", str(duration)])
370
+
371
+ args.append(str(output_path))
372
+
373
+ stdout, stderr = self.execute(*args)
374
+
375
+ if not output_path.exists():
376
+ raise RuntimeError(
377
+ f"Trimmed video was not created: {output_file}"
378
+ )
379
+
380
+ return stdout if stdout else stderr
@@ -1,9 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: ffmpeg-python-helper
3
- Version: 0.1.0
4
- Summary: Add your description here
5
- Author: marjon
6
- Author-email: marjon <marjongodito@gmanmi.com>
7
- Requires-Python: >=3.14
8
- Description-Content-Type: text/markdown
9
-
File without changes
@@ -1,9 +0,0 @@
1
- from .ffmpeg_api import FFMPEG
2
-
3
- __all__ = [
4
- 'FFMPEG'
5
- ]
6
-
7
- def main():
8
- ffmpeg = FFMPEG()
9
- print(ffmpeg.executable)
@@ -1,4 +0,0 @@
1
- [diffend] Oversized file quarantined before diffing.
2
- name: ffmpeg_python_helper-0.1.0/src/ffmpeg_python_helper/_bundled/ffmpeg.exe
3
- size: 105674240 bytes
4
- sha256: f1dd57a9afb2893250fe3ea227899eb4779cbe8c2a2d0915761f2318a09140ca
@@ -1,121 +0,0 @@
1
- import subprocess
2
- from pathlib import Path
3
-
4
- class FFMPEG:
5
- def __init__(self) -> None:
6
- import shutil
7
- self.executable = shutil.which('ffmpeg')
8
-
9
- if self.executable is None:
10
- raise FileNotFoundError("""
11
- FFmpeg is not installed or could not be found in PATH.
12
- Please install FFmpeg and make sure it is available
13
- in your system PATH.
14
- """)
15
-
16
- @classmethod
17
- def api(cls):
18
- return cls()
19
-
20
- def execute(self, *args : str):
21
- if self.executable:
22
- result = subprocess.run([self.executable, *args], capture_output=True, text=True)
23
- return result.stdout.strip(), result.stderr.strip()
24
-
25
- raise FileNotFoundError("""
26
- No ffmpeg executable found.
27
-
28
- Please Install ffmpeg first.
29
- """)
30
-
31
- def reformat(self, input_file : str, output_file : str):
32
- if not Path(input_file).exists():
33
- raise FileNotFoundError(f"""
34
- Input file {input_file} did not found .
35
-
36
- Please check filename and directory if correct.
37
- """)
38
-
39
- stdout, stderr = self.execute("-i", input_file, output_file)
40
-
41
- if stdout:
42
- print(stdout)
43
- else:
44
- print(stderr)
45
-
46
- def gif(self,
47
- input_file : str,
48
- output_file : str,
49
- fps = 10,
50
- scale = 320
51
- ):
52
- input_path = Path(input_file)
53
- output_path = Path(output_file)
54
-
55
- if not input_path.exists():
56
- raise FileNotFoundError(
57
- f"Input file {input_file} was not found."
58
- )
59
-
60
- filter_graph : str = (
61
- f"fps={fps},"
62
- f"scale={scale}:-1:flags=lanczos,"
63
- "split[s0][s1];"
64
- "[s0]palettegen[p];"
65
- "[s1][p]paletteuse"
66
- )
67
-
68
- stdout, stderr = self.execute(
69
- "-y",
70
- "-i", str(input_path),
71
- "-vf", filter_graph,
72
- str(output_path),
73
- )
74
-
75
- if not output_path.exists():
76
- raise RuntimeError(
77
- f"GIF was not created: {output_file}"
78
- )
79
-
80
- return stdout, stderr
81
-
82
- def trim(
83
- self,
84
- input_file: str,
85
- output_file: str,
86
- start: float = 0,
87
- duration: float | None = None,
88
- ):
89
- input_path = Path(input_file)
90
- output_path = Path(output_file)
91
-
92
- if not input_path.exists():
93
- raise FileNotFoundError(
94
- f"Input file {input_file} was not found."
95
- )
96
-
97
- if start < 0:
98
- raise ValueError("start must be greater than or equal to 0")
99
-
100
- if duration is not None and duration <= 0:
101
- raise ValueError("duration must be greater than 0")
102
-
103
- args = [
104
- "-y",
105
- "-ss", str(start),
106
- "-i", str(input_path),
107
- ]
108
-
109
- if duration is not None:
110
- args.extend(["-t", str(duration)])
111
-
112
- args.append(str(output_path))
113
-
114
- stdout, stderr = self.execute(*args)
115
-
116
- if not output_path.exists():
117
- raise RuntimeError(
118
- f"Trimmed video was not created: {output_file}"
119
- )
120
-
121
- return stdout, stderr