ffmpeg-python-helper 3.1.0__tar.gz → 4.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,720 @@
1
+ Metadata-Version: 2.3
2
+ Name: ffmpeg-python-helper
3
+ Version: 4.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 <petmalu.marjon@gmail.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
+ - 🎵 **Audio Extraction** - Extract audio tracks from videos without re-encoding
20
+ - 🧠 **In-Memory Processing** - Process video/audio data directly from bytes without temporary files
21
+ - ✂️ **Video Trimming** - Trim videos with precise start time and duration control
22
+ - 🔍 **Video Metadata Analysis** - Extract video information and metadata using FFProbe
23
+ - ⚡ **Asynchronous Operations** - Non-blocking async API for responsive applications
24
+ - 🐍 **Pythonic API** - Clean, object-oriented interface with proper error handling
25
+ - 📁 **File Validation** - Automatic input file existence checking
26
+
27
+ ## Installation
28
+
29
+ ### Prerequisites
30
+ - Python 3.14 or higher
31
+ - FFMPEG installed and available in your system PATH
32
+
33
+ ### Install FFMPEG Python Helper
34
+
35
+ ```bash
36
+ pip install ffmpeg-python-helper
37
+ ```
38
+
39
+ Or install from source:
40
+
41
+ ```bash
42
+ git clone https://github.com/yourusername/ffmpeg-python-helper.git
43
+ cd ffmpeg-python-helper
44
+ pip install -e .
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```python
50
+ from ffmpeg_python_helper import FFMPEG
51
+
52
+ # Initialize the FFMPEG wrapper
53
+ ffmpeg = FFMPEG()
54
+
55
+ # Check if FFMPEG is available
56
+ print(f"FFMPEG executable found at: {ffmpeg.executable}")
57
+
58
+ # Convert a video file
59
+ output = ffmpeg.reformat("input.mp4", "output.avi")
60
+ print(output.decode())
61
+
62
+ # Create a GIF from video
63
+ ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
64
+
65
+ # Trim a video
66
+ ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
67
+
68
+ # Extract audio from video
69
+ ffmpeg.extract_audio("video.mp4", "audio.m4a")
70
+
71
+ # Create GIF from in-memory video data
72
+ with open("video.mp4", "rb") as f:
73
+ video_data = f.read()
74
+ gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
75
+ with open("memory.gif", "wb") as f:
76
+ f.write(gif_data)
77
+
78
+ # Trim video in memory
79
+ trimmed_data = ffmpeg.trims(video_data, start=0, duration=30)
80
+ with open("trimmed.mp4", "wb") as f:
81
+ f.write(trimmed_data)
82
+
83
+ # Extract audio in memory
84
+ audio_data = ffmpeg.extract_audios(video_data, output_format="m4a")
85
+ with open("audio.m4a", "wb") as f:
86
+ f.write(audio_data)
87
+
88
+ # Analyze video metadata with FFProbe
89
+ from ffmpeg_python_helper import FFProbe
90
+ import json
91
+
92
+ ffprobe = FFProbe()
93
+ print(f"FFProbe executable found at: {ffprobe.executable}")
94
+
95
+ # Check if video is short enough for social media
96
+ if ffprobe.is_max_length("video.mp4", max_length=5.0):
97
+ print("Video is perfect for Instagram Reels!")
98
+ else:
99
+ print("Video needs trimming for short-form content")
100
+
101
+ # Get detailed video metadata
102
+ stdout, stderr = ffprobe.execute("-v", "quiet", "-print_format", "json",
103
+ "-show_format", "-show_streams", "video.mp4")
104
+ metadata = json.loads(stdout.decode())
105
+ print(f"Video duration: {metadata['format']['duration']} seconds")
106
+ print(f"Video dimensions: {metadata['streams'][0]['width']}x{metadata['streams'][0]['height']}")
107
+
108
+ # Use AsyncFFMPEG for non-blocking operations
109
+ import asyncio
110
+ from ffmpeg_python_helper import AsyncFFMPEG
111
+
112
+ async def process_video_async():
113
+ async_ffmpeg = AsyncFFMPEG()
114
+ print(f"AsyncFFMPEG executable found at: {async_ffmpeg.executable}")
115
+
116
+ # Convert video asynchronously
117
+ output = await async_ffmpeg.reformat("input.mp4", "output_async.avi")
118
+ print(f"Async conversion output: {output.decode()[:50]}...")
119
+
120
+ # Create GIF asynchronously
121
+ await async_ffmpeg.gif("video.mp4", "animation_async.gif", fps=15, scale=480)
122
+ print("Async GIF creation completed!")
123
+
124
+ # Run the async function
125
+ asyncio.run(process_video_async())
126
+ ```
127
+
128
+ ## API Reference
129
+
130
+ ### `FFMPEG` Class
131
+
132
+ The main class that wraps FFMPEG functionality.
133
+
134
+ #### Constructor
135
+ ```python
136
+ FFMPEG()
137
+ ```
138
+ Creates a new FFMPEG instance. Automatically searches for FFMPEG in the system PATH.
139
+ - **Raises**: `FileNotFoundError` if FFMPEG is not found in PATH
140
+
141
+ #### Properties
142
+ - `executable` (str): The path to the FFMPEG executable found in the system
143
+
144
+ #### Class Methods
145
+ ```python
146
+ @classmethod
147
+ def api(cls) -> "FFMPEG"
148
+ ```
149
+ Factory method that returns a new FFMPEG instance.
150
+ - **Returns**: `FFMPEG` instance
151
+
152
+ #### Instance Methods
153
+
154
+ ##### `execute(*args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]`
155
+ Execute raw FFMPEG commands with the given arguments.
156
+
157
+ **Parameters:**
158
+ - `*args` (str): FFMPEG command-line arguments
159
+ - `input_data` (bytes | None, optional): Optional bytes to send to FFMPEG's stdin
160
+
161
+ **Returns:**
162
+ - `tuple[bytes, bytes]`: A tuple containing (stdout, stderr) as bytes
163
+
164
+ **Raises:**
165
+ - `FileNotFoundError`: If FFMPEG executable is not found
166
+ - `RuntimeError`: If FFMPEG command returns a non-zero exit code
167
+
168
+ **Example:**
169
+ ```python
170
+ stdout, stderr = ffmpeg.execute("-version")
171
+ print(stdout.decode())
172
+
173
+ # Process data from memory
174
+ video_data = b"...video bytes..."
175
+ stdout, stderr = ffmpeg.execute("-i", "pipe:0", "-f", "null", "-", input_data=video_data)
176
+ ```
177
+
178
+ ##### `reformat(input_file: str, output_file: str) -> bytes`
179
+ Convert a video file from one format to another.
180
+
181
+ **Parameters:**
182
+ - `input_file` (str): Path to the input video file
183
+ - `output_file` (str): Path for the output video file
184
+
185
+ **Returns:**
186
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
187
+
188
+ **Raises:**
189
+ - `FileNotFoundError`: If input file doesn't exist
190
+
191
+ **Example:**
192
+ ```python
193
+ output = ffmpeg.reformat("input.mov", "output.mp4")
194
+ print(output.decode())
195
+ ```
196
+
197
+ ##### `gif(input_file: str, output_file: str, fps: int = 10, scale: int = 320) -> bytes`
198
+ Convert a video file to an optimized GIF.
199
+
200
+ **Parameters:**
201
+ - `input_file` (str): Path to the input video file
202
+ - `output_file` (str): Path for the output GIF file
203
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
204
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
205
+
206
+ **Returns:**
207
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
208
+
209
+ **Raises:**
210
+ - `FileNotFoundError`: If input file doesn't exist
211
+ - `RuntimeError`: If GIF file was not created successfully
212
+
213
+ **Example:**
214
+ ```python
215
+ output = ffmpeg.gif("video.mp4", "output.gif", fps=15, scale=640)
216
+ print(output.decode())
217
+ ```
218
+
219
+ ##### `gifs(input_byte: bytes, fps: int = 10, scale: int = 320) -> bytes`
220
+ Convert video data from bytes to an optimized GIF (in-memory processing).
221
+
222
+ **Parameters:**
223
+ - `input_byte` (bytes): Video data as bytes to convert to GIF
224
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
225
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
226
+
227
+ **Returns:**
228
+ - `bytes`: The generated GIF data as bytes
229
+
230
+ **Raises:**
231
+ - `RuntimeError`: If GIF conversion fails
232
+
233
+ **Example:**
234
+ ```python
235
+ # Read video data from a file
236
+ with open("video.mp4", "rb") as f:
237
+ video_data = f.read()
238
+
239
+ # Convert to GIF in memory
240
+ gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
241
+
242
+ # Save the GIF
243
+ with open("output.gif", "wb") as f:
244
+ f.write(gif_data)
245
+ ```
246
+
247
+ ##### `trim(input_file: str, output_file: str, start: float = 0, duration: float | None = None) -> bytes`
248
+ Trim a video file.
249
+
250
+ **Parameters:**
251
+ - `input_file` (str): Path to the input video file
252
+ - `output_file` (str): Path for the output trimmed video
253
+ - `start` (float, optional): Start time in seconds (default: 0)
254
+ - `duration` (float | None, optional): Duration in seconds, or None for remaining video (default: None)
255
+
256
+ **Returns:**
257
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
258
+
259
+ **Raises:**
260
+ - `FileNotFoundError`: If input file doesn't exist
261
+ - `ValueError`: If start is negative or duration is non-positive
262
+ - `RuntimeError`: If trimmed video was not created successfully
263
+
264
+ **Example:**
265
+ ```python
266
+ # Trim from 5 seconds to 10 seconds (5-second clip)
267
+ output = ffmpeg.trim("video.mp4", "clip.mp4", start=5, duration=5)
268
+ print(output.decode())
269
+
270
+ # Trim from 10 seconds to the end of video
271
+ output = ffmpeg.trim("video.mp4", "ending.mp4", start=10)
272
+ print(output.decode())
273
+ ```
274
+
275
+ ### `FFProbe` Class
276
+
277
+ A Python wrapper for FFProbe (the FFMPEG multimedia stream analyzer) that provides video metadata analysis capabilities.
278
+
279
+ #### Constructor
280
+ ```python
281
+ FFProbe()
282
+ ```
283
+ Creates a new FFProbe instance. Automatically searches for FFProbe in the system PATH.
284
+ - **Raises**: `FileNotFoundError` if FFProbe is not found in PATH
285
+
286
+ #### Properties
287
+ - `executable` (str): The path to the FFProbe executable found in the system
288
+
289
+ #### Class Methods
290
+ ```python
291
+ @classmethod
292
+ def api(cls) -> "FFProbe"
293
+ ```
294
+ Factory method that returns a new FFProbe instance.
295
+ - **Returns**: `FFProbe` instance
296
+
297
+ **Example:**
298
+ ```python
299
+ ffprobe = FFProbe.api()
300
+ print(f"FFProbe executable found at: {ffprobe.executable}")
301
+ ```
302
+
303
+ #### Instance Methods
304
+
305
+ ##### `execute(*args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]`
306
+ Execute raw FFProbe commands with the given arguments.
307
+
308
+ **Parameters:**
309
+ - `*args` (str): FFProbe command-line arguments as strings
310
+ - `input_data` (bytes | None, optional): Optional bytes to send to FFProbe's stdin
311
+
312
+ **Returns:**
313
+ - `tuple[bytes, bytes]`: A tuple containing (stdout, stderr) as bytes
314
+
315
+ **Raises:**
316
+ - `FileNotFoundError`: If FFProbe executable is not found
317
+ - `RuntimeError`: If FFProbe command returns a non-zero exit code
318
+
319
+ **Example:**
320
+ ```python
321
+ # Get FFProbe version
322
+ stdout, stderr = ffprobe.execute("-version")
323
+ print(stdout.decode())
324
+
325
+ # Get video metadata in JSON format
326
+ stdout, stderr = ffprobe.execute("-v", "quiet", "-print_format", "json",
327
+ "-show_format", "-show_streams", "video.mp4")
328
+ metadata = json.loads(stdout.decode())
329
+ print(f"Video duration: {metadata['format']['duration']} seconds")
330
+
331
+ # Get video dimensions
332
+ stdout, stderr = ffprobe.execute("-v", "error", "-select_streams", "v:0",
333
+ "-show_entries", "stream=width,height",
334
+ "-of", "csv=p=0", "video.mp4")
335
+ print(f"Video dimensions: {stdout.decode().strip()}")
336
+ ```
337
+
338
+ ##### `is_max_length(input_file: str, max_length: float = 5.0) -> bool`
339
+ Check if a video file's duration is less than or equal to a specified maximum length.
340
+
341
+ **Parameters:**
342
+ - `input_file` (str): Path to the input video file
343
+ - `max_length` (float, optional): Maximum allowed duration in seconds (default: 5.0)
344
+
345
+ **Returns:**
346
+ - `bool`: `True` if video duration ≤ max_length, `False` otherwise
347
+
348
+ **Raises:**
349
+ - `FileNotFoundError`: If input file doesn't exist
350
+ - `RuntimeError`: If FFProbe command fails
351
+
352
+ **Example:**
353
+ ```python
354
+ # Check if video is shorter than 10 seconds
355
+ if ffprobe.is_max_length("video.mp4", max_length=10.0):
356
+ print("Video is short enough for social media upload")
357
+ else:
358
+ print("Video is too long, needs trimming")
359
+
360
+ # Check multiple videos for length compliance
361
+ videos = ["clip1.mp4", "clip2.mp4", "clip3.mp4"]
362
+ for video in videos:
363
+ if ffprobe.is_max_length(video, max_length=5.0):
364
+ print(f"{video}: OK (≤ 5 seconds)")
365
+ else:
366
+ print(f"{video}: Too long (> 5 seconds)")
367
+ ```
368
+
369
+ ### `AsyncFFMPEG` Class
370
+
371
+ An asynchronous Python wrapper for FFMPEG that provides a simple, intuitive API for common video processing tasks with non-blocking operations.
372
+
373
+ This class provides all the same functionality as the `FFMPEG` class but with asynchronous methods, allowing you to perform video processing operations without blocking your application. This is especially useful for web applications, GUI applications, or any scenario where you need to maintain responsiveness while performing video processing tasks.
374
+
375
+ #### Constructor
376
+ ```python
377
+ AsyncFFMPEG()
378
+ ```
379
+ Creates a new AsyncFFMPEG instance. Automatically searches for FFMPEG in the system PATH.
380
+ - **Raises**: `FileNotFoundError` if FFMPEG is not found in PATH
381
+
382
+ #### Properties
383
+ - `executable` (str): The path to the FFMPEG executable found in the system
384
+
385
+ #### Class Methods
386
+ ```python
387
+ @classmethod
388
+ def api(cls) -> "AsyncFFMPEG"
389
+ ```
390
+ Factory method that returns a new AsyncFFMPEG instance.
391
+ - **Returns**: `AsyncFFMPEG` instance
392
+
393
+ #### Instance Methods
394
+
395
+ All methods are asynchronous and must be awaited. The API mirrors the synchronous `FFMPEG` class but with `async`/`await` syntax.
396
+
397
+ ##### `async execute(*args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]`
398
+ Execute raw FFMPEG commands asynchronously with the given arguments.
399
+
400
+ **Parameters:**
401
+ - `*args` (str): FFMPEG command-line arguments
402
+ - `input_data` (bytes | None, optional): Optional bytes to send to FFMPEG's stdin
403
+
404
+ **Returns:**
405
+ - `tuple[bytes, bytes]`: A tuple containing (stdout, stderr) as bytes
406
+
407
+ **Raises:**
408
+ - `FileNotFoundError`: If FFMPEG executable is not found
409
+ - `RuntimeError`: If FFMPEG command returns a non-zero exit code
410
+
411
+ **Example:**
412
+ ```python
413
+ # Must be called within an async context
414
+ stdout, stderr = await async_ffmpeg.execute("-version")
415
+ print(stdout.decode())
416
+
417
+ # Process data from memory asynchronously
418
+ video_data = b"...video bytes..."
419
+ stdout, stderr = await async_ffmpeg.execute("-i", "pipe:0", "-f", "null", "-", input_data=video_data)
420
+ ```
421
+
422
+ ##### `async reformat(input_file: str, output_file: str) -> bytes`
423
+ Asynchronously convert a video file from one format to another.
424
+
425
+ **Parameters:**
426
+ - `input_file` (str): Path to the input video file
427
+ - `output_file` (str): Path for the output video file
428
+
429
+ **Returns:**
430
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
431
+
432
+ **Raises:**
433
+ - `FileNotFoundError`: If input file doesn't exist
434
+
435
+ **Example:**
436
+ ```python
437
+ output = await async_ffmpeg.reformat("input.mov", "output.mp4")
438
+ print(output.decode())
439
+ ```
440
+
441
+ ##### `async gif(input_file: str, output_file: str, fps: int = 10, scale: int = 320) -> bytes`
442
+ Asynchronously convert a video file to an optimized GIF.
443
+
444
+ **Parameters:**
445
+ - `input_file` (str): Path to the input video file
446
+ - `output_file` (str): Path for the output GIF file
447
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
448
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
449
+
450
+ **Returns:**
451
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
452
+
453
+ **Raises:**
454
+ - `FileNotFoundError`: If input file doesn't exist
455
+ - `RuntimeError`: If GIF file was not created successfully
456
+
457
+ **Example:**
458
+ ```python
459
+ output = await async_ffmpeg.gif("video.mp4", "output.gif", fps=15, scale=640)
460
+ print(output.decode())
461
+ ```
462
+
463
+ ##### `async gifs(input_byte: bytes, fps: int = 10, scale: int = 320) -> bytes`
464
+ Asynchronously convert video data from bytes to an optimized GIF (in-memory processing).
465
+
466
+ **Parameters:**
467
+ - `input_byte` (bytes): Video data as bytes to convert to GIF
468
+ - `fps` (int, optional): Frames per second for the GIF (default: 10)
469
+ - `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
470
+
471
+ **Returns:**
472
+ - `bytes`: The generated GIF data as bytes
473
+
474
+ **Raises:**
475
+ - `RuntimeError`: If GIF conversion fails
476
+
477
+ **Example:**
478
+ ```python
479
+ # Read video data from a file
480
+ with open("video.mp4", "rb") as f:
481
+ video_data = f.read()
482
+
483
+ # Convert to GIF in memory asynchronously
484
+ gif_data = await async_ffmpeg.gifs(video_data, fps=15, scale=480)
485
+
486
+ # Save the GIF
487
+ with open("output.gif", "wb") as f:
488
+ f.write(gif_data)
489
+ ```
490
+
491
+ ##### `async trim(input_file: str, output_file: str, start: float = 0, duration: float | None = None) -> bytes`
492
+ Asynchronously trim a video file.
493
+
494
+ **Parameters:**
495
+ - `input_file` (str): Path to the input video file
496
+ - `output_file` (str): Path for the output trimmed video
497
+ - `start` (float, optional): Start time in seconds (default: 0)
498
+ - `duration` (float | None, optional): Duration in seconds, or None for remaining video (default: None)
499
+
500
+ **Returns:**
501
+ - `bytes`: FFMPEG output (stdout or stderr) as bytes
502
+
503
+ **Raises:**
504
+ - `FileNotFoundError`: If input file doesn't exist
505
+ - `ValueError`: If start is negative or duration is non-positive
506
+ - `RuntimeError`: If trimmed video was not created successfully
507
+
508
+ **Example:**
509
+ ```python
510
+ # Trim from 5 seconds to 10 seconds (5-second clip) asynchronously
511
+ output = await async_ffmpeg.trim("video.mp4", "clip.mp4", start=5, duration=5)
512
+ print(output.decode())
513
+
514
+ # Trim from 10 seconds to the end of video asynchronously
515
+ output = await async_ffmpeg.trim("video.mp4", "ending.mp4", start=10)
516
+ print(output.decode())
517
+ ```
518
+
519
+ ## Advanced Usage
520
+
521
+ ### Custom FFMPEG Commands
522
+ For operations not covered by the built-in methods, use the `execute` method:
523
+
524
+ ```python
525
+ # Extract audio from video
526
+ ffmpeg.execute("-i", "video.mp4", "-q:a", "0", "-map", "a", "audio.mp3")
527
+
528
+ # Add watermark to video
529
+ ffmpeg.execute("-i", "video.mp4", "-i", "watermark.png",
530
+ "-filter_complex", "overlay=10:10", "output.mp4")
531
+
532
+ # Change video bitrate
533
+ ffmpeg.execute("-i", "input.mp4", "-b:v", "1M", "output.mp4")
534
+ ```
535
+
536
+ ### Advanced FFProbe Usage
537
+ FFProbe provides powerful video metadata analysis capabilities:
538
+
539
+ ```python
540
+ from ffmpeg_python_helper import FFProbe
541
+ import json
542
+
543
+ ffprobe = FFProbe()
544
+
545
+ # Get multiple video metadata properties at once
546
+ stdout, stderr = ffprobe.execute(
547
+ "-v", "error",
548
+ "-select_streams", "v:0",
549
+ "-show_entries", "stream=width,height,duration,bit_rate,codec_name",
550
+ "-of", "json",
551
+ "video.mp4"
552
+ )
553
+ video_info = json.loads(stdout.decode())
554
+ print(f"Video codec: {video_info['streams'][0]['codec_name']}")
555
+ print(f"Video bitrate: {video_info['streams'][0]['bit_rate']} bps")
556
+
557
+ # Check frame rate
558
+ stdout, stderr = ffprobe.execute(
559
+ "-v", "error",
560
+ "-select_streams", "v:0",
561
+ "-show_entries", "stream=r_frame_rate",
562
+ "-of", "default=noprint_wrappers=1:nokey=1",
563
+ "video.mp4"
564
+ )
565
+ print(f"Frame rate: {stdout.decode().strip()}")
566
+
567
+ # Get audio stream information
568
+ stdout, stderr = ffprobe.execute(
569
+ "-v", "error",
570
+ "-select_streams", "a:0",
571
+ "-show_entries", "stream=codec_name,channels,sample_rate",
572
+ "-of", "json",
573
+ "video.mp4"
574
+ )
575
+ audio_info = json.loads(stdout.decode())
576
+ if audio_info['streams']:
577
+ print(f"Audio codec: {audio_info['streams'][0]['codec_name']}")
578
+ print(f"Audio channels: {audio_info['streams'][0]['channels']}")
579
+ print(f"Sample rate: {audio_info['streams'][0]['sample_rate']} Hz")
580
+ ```
581
+
582
+ ### Asynchronous Batch Processing with AsyncFFMPEG
583
+ AsyncFFMPEG is ideal for batch processing and web applications where you need to maintain responsiveness:
584
+
585
+ ```python
586
+ import asyncio
587
+ from ffmpeg_python_helper import AsyncFFMPEG
588
+
589
+ async def process_videos_concurrently():
590
+ async_ffmpeg = AsyncFFMPEG()
591
+ videos = ["video1.mp4", "video2.mp4", "video3.mp4"]
592
+
593
+ # Process multiple videos concurrently
594
+ tasks = []
595
+ for video in videos:
596
+ task = async_ffmpeg.gif(video, f"{video}_async.gif", fps=12, scale=400)
597
+ tasks.append(task)
598
+
599
+ # Wait for all async tasks to complete
600
+ results = await asyncio.gather(*tasks, return_exceptions=True)
601
+
602
+ # Handle results
603
+ for video, result in zip(videos, results):
604
+ if isinstance(result, Exception):
605
+ print(f"Failed to process {video}: {result}")
606
+ else:
607
+ print(f"Successfully processed {video}: {len(result)} bytes output")
608
+
609
+ # Run concurrent processing
610
+ asyncio.run(process_videos_concurrently())
611
+ ```
612
+
613
+ ### Error Handling
614
+ ```python
615
+ from ffmpeg_python_helper import FFMPEG
616
+ import sys
617
+
618
+ try:
619
+ ffmpeg = FFMPEG()
620
+ ffmpeg.gif("video.mp4", "output.gif")
621
+ except FileNotFoundError as e:
622
+ print(f"FFMPEG not found: {e}", file=sys.stderr)
623
+ sys.exit(1)
624
+ except RuntimeError as e:
625
+ print(f"Processing failed: {e}", file=sys.stderr)
626
+ sys.exit(1)
627
+ ```
628
+
629
+ ## Common Use Cases
630
+
631
+ ### Batch Processing
632
+ ```python
633
+ import os
634
+ from ffmpeg_python_helper import FFMPEG
635
+
636
+ ffmpeg = FFMPEG()
637
+ videos = ["video1.mp4", "video2.mp4", "video3.mp4"]
638
+
639
+ for video in videos:
640
+ if os.path.exists(video):
641
+ base_name = os.path.splitext(video)[0]
642
+ ffmpeg.gif(video, f"{base_name}.gif", fps=12, scale=400)
643
+ ```
644
+
645
+ ### Video Compilation
646
+ ```python
647
+ from ffmpeg_python_helper import FFMPEG
648
+
649
+ ffmpeg = FFMPEG()
650
+
651
+ # Trim interesting parts
652
+ ffmpeg.trim("concert.mp4", "intro.mp4", start=0, duration=30)
653
+ ffmpeg.trim("concert.mp4", "chorus.mp4", start=120, duration=45)
654
+ ffmpeg.trim("concert.mp4", "finale.mp4", start=300, duration=60)
655
+
656
+ # Later, use FFMPEG to concatenate trimmed parts
657
+ ffmpeg.execute("-f", "concat", "-safe", "0", "-i", "parts.txt", "highlight_reel.mp4")
658
+ ```
659
+
660
+ ## Troubleshooting
661
+
662
+ ### FFMPEG Not Found
663
+ If you get `FileNotFoundError` when creating an FFMPEG instance:
664
+
665
+ 1. **Install FFMPEG**:
666
+ - **Windows**: Download from [ffmpeg.org](https://ffmpeg.org/download.html)
667
+ - **macOS**: `brew install ffmpeg`
668
+ - **Linux**: `sudo apt install ffmpeg` (Ubuntu/Debian) or `sudo yum install ffmpeg` (Fedora/RHEL)
669
+
670
+ 2. **Add to PATH**:
671
+ - Ensure FFMPEG is in your system PATH
672
+ - Test with `ffmpeg -version` in your terminal
673
+
674
+ ### File Not Found Errors
675
+ - Ensure input file paths are correct and files exist
676
+ - Use absolute paths if working with files in different directories
677
+ - Check file permissions
678
+
679
+ ### GIF Creation Issues
680
+ - Lower FPS or scale if GIF file is too large
681
+ - Ensure input video has sufficient quality
682
+ - Check available disk space
683
+
684
+ ## Development
685
+
686
+ ### Running Tests
687
+ ```bash
688
+ python -m pytest tests/
689
+ ```
690
+
691
+ ### Building Documentation
692
+ ```bash
693
+ # Install documentation dependencies
694
+ pip install pdoc3
695
+
696
+ # Generate API documentation
697
+ pdoc --html ffmpeg_python_helper --output-dir docs
698
+ ```
699
+
700
+ ### Contributing
701
+ 1. Fork the repository
702
+ 2. Create a feature branch
703
+ 3. Add tests for your changes
704
+ 4. Ensure all tests pass
705
+ 5. Submit a pull request
706
+
707
+ ## License
708
+
709
+ MIT License - see LICENSE file for details
710
+
711
+ ## Support
712
+
713
+ - **Issues**: [GitHub Issues](https://github.com/yourusername/ffmpeg-python-helper/issues)
714
+ - **Documentation**: [ReadTheDocs](https://ffmpeg-python-helper.readthedocs.io)
715
+ - **Email**: marjongodito@gmanmi.com
716
+
717
+ ## Acknowledgments
718
+
719
+ - FFMPEG team for the amazing multimedia framework
720
+ - Python community for excellent tooling and libraries