ffmpeg-python-helper 0.1.0__py3-none-any.whl → 3.0.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.
- ffmpeg_python_helper/__init__.py +61 -3
- ffmpeg_python_helper/ffmpeg_api.py +425 -20
- ffmpeg_python_helper/pipe_helper.py +114 -0
- ffmpeg_python_helper-3.0.0.dist-info/METADATA +358 -0
- ffmpeg_python_helper-3.0.0.dist-info/RECORD +7 -0
- ffmpeg_python_helper/_bundled/ffmpeg.exe +0 -4
- ffmpeg_python_helper-0.1.0.dist-info/METADATA +0 -9
- ffmpeg_python_helper-0.1.0.dist-info/RECORD +0 -7
- {ffmpeg_python_helper-0.1.0.dist-info → ffmpeg_python_helper-3.0.0.dist-info}/WHEEL +0 -0
- {ffmpeg_python_helper-0.1.0.dist-info → ffmpeg_python_helper-3.0.0.dist-info}/entry_points.txt +0 -0
ffmpeg_python_helper/__init__.py
CHANGED
|
@@ -1,9 +1,67 @@
|
|
|
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, audio extraction,
|
|
6
|
+
in-memory processing, and data pipeline utilities.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from ffmpeg_python_helper import FFMPEG, Pipe
|
|
10
|
+
>>> ffmpeg = FFMPEG()
|
|
11
|
+
>>> ffmpeg.reformat("input.mp4", "output.avi")
|
|
12
|
+
>>> ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
|
|
13
|
+
>>> ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
|
|
14
|
+
>>> ffmpeg.extract_audio("video.mp4", "audio.m4a")
|
|
15
|
+
>>>
|
|
16
|
+
>>> # In-memory processing
|
|
17
|
+
>>> with open("video.mp4", "rb") as f:
|
|
18
|
+
... video_data = f.read()
|
|
19
|
+
>>> gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
|
|
20
|
+
>>> trimmed_data = ffmpeg.trims(video_data, start=0, duration=30)
|
|
21
|
+
>>> audio_data = ffmpeg.extract_audios(video_data, output_format="m4a")
|
|
22
|
+
>>>
|
|
23
|
+
>>> # Pipeline processing
|
|
24
|
+
>>> def process_video(data: bytes) -> bytes:
|
|
25
|
+
... return ffmpeg.trims(data, start=0, duration=30)
|
|
26
|
+
>>>
|
|
27
|
+
>>> def convert_to_gif(data: bytes) -> bytes:
|
|
28
|
+
... return ffmpeg.gifs(data, fps=15, scale=480)
|
|
29
|
+
>>>
|
|
30
|
+
>>> processed_data = Pipe.pipe_bytes(video_data, process_video, convert_to_gif)
|
|
31
|
+
|
|
32
|
+
For detailed API documentation, see:
|
|
33
|
+
- FFMPEG class documentation
|
|
34
|
+
- Pipe class documentation
|
|
35
|
+
- README.md for usage examples and tutorials
|
|
36
|
+
"""
|
|
37
|
+
|
|
1
38
|
from .ffmpeg_api import FFMPEG
|
|
39
|
+
from .pipe_helper import Pipe
|
|
2
40
|
|
|
3
41
|
__all__ = [
|
|
4
|
-
'FFMPEG'
|
|
42
|
+
'FFMPEG',
|
|
43
|
+
'Pipe'
|
|
5
44
|
]
|
|
6
45
|
|
|
7
|
-
|
|
46
|
+
__version__ = "0.1.0"
|
|
47
|
+
__author__ = "marjon <marjongodito@gmanmi.com>"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main() -> None:
|
|
51
|
+
"""
|
|
52
|
+
Command-line entry point for the FFMPEG Python Helper.
|
|
53
|
+
|
|
54
|
+
This function is called when the package is executed as a script.
|
|
55
|
+
It demonstrates basic functionality by:
|
|
56
|
+
1. Creating an FFMPEG instance
|
|
57
|
+
2. Printing the path to the FFMPEG executable
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
$ python -m ffmpeg_python_helper
|
|
61
|
+
FFMPEG executable found at: /usr/bin/ffmpeg
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
FileNotFoundError: If FFMPEG is not found in PATH.
|
|
65
|
+
"""
|
|
8
66
|
ffmpeg = FFMPEG()
|
|
9
|
-
print(ffmpeg.executable)
|
|
67
|
+
print(f"FFMPEG executable found at: {ffmpeg.executable}")
|
|
@@ -1,8 +1,52 @@
|
|
|
1
1
|
import subprocess
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
+
|
|
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, video trimming,
|
|
12
|
+
and audio extraction. It supports both file-based operations and
|
|
13
|
+
in-memory data processing.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> from ffmpeg_python_helper import FFMPEG
|
|
17
|
+
>>> ffmpeg = FFMPEG()
|
|
18
|
+
>>> ffmpeg.reformat("input.mp4", "output.avi")
|
|
19
|
+
>>> ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
|
|
20
|
+
>>> ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
|
|
21
|
+
>>> ffmpeg.extract_audio("video.mp4", "audio.m4a")
|
|
22
|
+
>>>
|
|
23
|
+
>>> # In-memory processing
|
|
24
|
+
>>> with open("video.mp4", "rb") as f:
|
|
25
|
+
... video_data = f.read()
|
|
26
|
+
>>> gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
|
|
27
|
+
>>> trimmed_data = ffmpeg.trims(video_data, start=0, duration=30)
|
|
28
|
+
>>> audio_data = ffmpeg.extract_audios(video_data, output_format="m4a")
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
executable (str): The path to the FFMPEG executable found in the system PATH.
|
|
32
|
+
"""
|
|
33
|
+
|
|
5
34
|
def __init__(self) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Initialize a new FFMPEG instance.
|
|
37
|
+
|
|
38
|
+
Automatically searches for FFMPEG in the system PATH.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
FileNotFoundError: If FFMPEG is not found in the system PATH.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> try:
|
|
45
|
+
... ffmpeg = FFMPEG()
|
|
46
|
+
... print(f"FFMPEG found at: {ffmpeg.executable}")
|
|
47
|
+
... except FileNotFoundError as e:
|
|
48
|
+
... print(f"FFMPEG not found: {e}")
|
|
49
|
+
"""
|
|
6
50
|
import shutil
|
|
7
51
|
self.executable = shutil.which('ffmpeg')
|
|
8
52
|
|
|
@@ -14,13 +58,71 @@ in your system PATH.
|
|
|
14
58
|
""")
|
|
15
59
|
|
|
16
60
|
@classmethod
|
|
17
|
-
def api(cls):
|
|
61
|
+
def api(cls) -> "FFMPEG":
|
|
62
|
+
"""
|
|
63
|
+
Factory method that returns a new FFMPEG instance.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
FFMPEG: A new instance of the FFMPEG class.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
>>> ffmpeg = FFMPEG.api()
|
|
70
|
+
>>> ffmpeg.execute("-version")
|
|
71
|
+
"""
|
|
18
72
|
return cls()
|
|
19
73
|
|
|
20
|
-
def execute(self, *args :
|
|
74
|
+
def execute(self, *args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]:
|
|
75
|
+
"""
|
|
76
|
+
Execute raw FFMPEG commands with the given arguments.
|
|
77
|
+
|
|
78
|
+
This method allows you to run any FFMPEG command directly,
|
|
79
|
+
providing maximum flexibility for operations not covered
|
|
80
|
+
by the built-in methods.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
*args: FFMPEG command-line arguments as strings.
|
|
84
|
+
input_data: Optional bytes to send to FFMPEG's stdin. Useful for
|
|
85
|
+
piping data directly to FFMPEG without intermediate files.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
tuple[bytes, bytes]: A tuple containing (stdout, stderr) from FFMPEG
|
|
89
|
+
as bytes objects.
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
FileNotFoundError: If FFMPEG executable is not found.
|
|
93
|
+
RuntimeError: If FFMPEG command returns a non-zero exit code.
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
>>> stdout, stderr = ffmpeg.execute("-version")
|
|
97
|
+
>>> print(stdout.decode())
|
|
98
|
+
|
|
99
|
+
>>> # Extract audio from video
|
|
100
|
+
>>> ffmpeg.execute("-i", "video.mp4", "-q:a", "0", "-map", "a", "audio.mp3")
|
|
101
|
+
|
|
102
|
+
>>> # Add watermark to video
|
|
103
|
+
>>> ffmpeg.execute("-i", "video.mp4", "-i", "watermark.png",
|
|
104
|
+
... "-filter_complex", "overlay=10:10", "output.mp4")
|
|
105
|
+
|
|
106
|
+
>>> # Process data from memory
|
|
107
|
+
>>> video_data = b"...video bytes..."
|
|
108
|
+
>>> stdout, stderr = ffmpeg.execute("-i", "pipe:0", "-f", "null", "-",
|
|
109
|
+
... input_data=video_data)
|
|
110
|
+
"""
|
|
21
111
|
if self.executable:
|
|
22
|
-
result = subprocess.run(
|
|
23
|
-
|
|
112
|
+
result = subprocess.run(
|
|
113
|
+
[self.executable, *args],
|
|
114
|
+
input=input_data,
|
|
115
|
+
stdout=subprocess.PIPE,
|
|
116
|
+
stderr=subprocess.PIPE,
|
|
117
|
+
check=False
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if result.returncode != 0:
|
|
121
|
+
raise RuntimeError(
|
|
122
|
+
result.stderr.decode(errors="replace")
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return result.stdout, result.stderr
|
|
24
126
|
|
|
25
127
|
raise FileNotFoundError("""
|
|
26
128
|
No ffmpeg executable found.
|
|
@@ -28,7 +130,30 @@ in your system PATH.
|
|
|
28
130
|
Please Install ffmpeg first.
|
|
29
131
|
""")
|
|
30
132
|
|
|
31
|
-
def reformat(self, input_file
|
|
133
|
+
def reformat(self, input_file: str, output_file: str) -> bytes:
|
|
134
|
+
"""
|
|
135
|
+
Convert a video file from one format to another.
|
|
136
|
+
|
|
137
|
+
This method performs a simple format conversion without
|
|
138
|
+
modifying video quality or other parameters.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
input_file: Path to the input video file.
|
|
142
|
+
output_file: Path for the output video file.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
bytes: FFMPEG output (stdout or stderr) as bytes.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
FileNotFoundError: If input file doesn't exist.
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
>>> output = ffmpeg.reformat("input.mov", "output.mp4")
|
|
152
|
+
>>> print(output.decode())
|
|
153
|
+
>>>
|
|
154
|
+
>>> output = ffmpeg.reformat("video.avi", "video.mkv")
|
|
155
|
+
>>> print(output.decode())
|
|
156
|
+
"""
|
|
32
157
|
if not Path(input_file).exists():
|
|
33
158
|
raise FileNotFoundError(f"""
|
|
34
159
|
Input file {input_file} did not found .
|
|
@@ -39,16 +164,103 @@ in your system PATH.
|
|
|
39
164
|
stdout, stderr = self.execute("-i", input_file, output_file)
|
|
40
165
|
|
|
41
166
|
if stdout:
|
|
42
|
-
|
|
167
|
+
return stdout
|
|
43
168
|
else:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
169
|
+
return stderr
|
|
170
|
+
|
|
171
|
+
def gifs(self,
|
|
172
|
+
input_bytes: bytes,
|
|
173
|
+
fps: int = 10,
|
|
174
|
+
scale: int = 320) -> bytes:
|
|
175
|
+
"""
|
|
176
|
+
Convert video data from bytes to an optimized GIF.
|
|
177
|
+
|
|
178
|
+
Creates a high-quality GIF from in-memory video data using FFMPEG's
|
|
179
|
+
palette optimization. This method is useful when you have video data
|
|
180
|
+
in memory and want to avoid writing temporary files.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
input_byte: Video data as bytes to convert to GIF.
|
|
184
|
+
fps: Frames per second for the GIF. Defaults to 10.
|
|
185
|
+
scale: Width of the GIF in pixels. Height is auto-scaled
|
|
186
|
+
to maintain aspect ratio. Defaults to 320.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
bytes: The generated GIF data as bytes.
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
RuntimeError: If GIF conversion fails.
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
>>> # Read video data from a file
|
|
196
|
+
>>> with open("video.mp4", "rb") as f:
|
|
197
|
+
... video_data = f.read()
|
|
198
|
+
>>>
|
|
199
|
+
>>> # Convert to GIF in memory
|
|
200
|
+
>>> gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
|
|
201
|
+
>>>
|
|
202
|
+
>>> # Save the GIF
|
|
203
|
+
>>> with open("output.gif", "wb") as f:
|
|
204
|
+
... f.write(gif_data)
|
|
205
|
+
|
|
206
|
+
>>> # Process video from network or database
|
|
207
|
+
>>> # video_bytes = download_video_from_url(url)
|
|
208
|
+
>>> # gif_bytes = ffmpeg.gifs(video_bytes, scale=320)
|
|
209
|
+
"""
|
|
210
|
+
filter_graph: str = (
|
|
211
|
+
f"fps={fps},"
|
|
212
|
+
f"scale={scale}:-1:flags=lanczos,"
|
|
213
|
+
"split[s0][s1];"
|
|
214
|
+
"[s0]palettegen[p];"
|
|
215
|
+
"[s1][p]paletteuse"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
stdout, stderr = self.execute(
|
|
219
|
+
"-y",
|
|
220
|
+
"-i", "pipe:0",
|
|
221
|
+
"-vf", filter_graph,
|
|
222
|
+
"-f", "gif",
|
|
223
|
+
"pipe:1",
|
|
224
|
+
input_data=input_bytes
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
return stdout
|
|
228
|
+
|
|
229
|
+
def gif(self,
|
|
230
|
+
input_file: str,
|
|
231
|
+
output_file: str,
|
|
232
|
+
fps: int = 10,
|
|
233
|
+
scale: int = 320
|
|
234
|
+
) -> None:
|
|
235
|
+
"""
|
|
236
|
+
Convert a video file to an optimized GIF.
|
|
237
|
+
|
|
238
|
+
Creates a high-quality GIF using FFMPEG's palette optimization
|
|
239
|
+
for better color reproduction and smaller file sizes.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
input_file: Path to the input video file.
|
|
243
|
+
output_file: Path for the output GIF file.
|
|
244
|
+
fps: Frames per second for the GIF. Defaults to 10.
|
|
245
|
+
scale: Width of the GIF in pixels. Height is auto-scaled
|
|
246
|
+
to maintain aspect ratio. Defaults to 320.
|
|
247
|
+
|
|
248
|
+
Raises:
|
|
249
|
+
FileNotFoundError: If input file doesn't exist.
|
|
250
|
+
RuntimeError: If GIF file was not created successfully.
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
>>> # Create a standard GIF
|
|
254
|
+
>>> ffmpeg.gif("video.mp4", "output.gif")
|
|
255
|
+
|
|
256
|
+
>>> # Create a higher quality GIF with custom settings
|
|
257
|
+
>>> ffmpeg.gif("video.mp4", "output.gif",
|
|
258
|
+
... fps=15, scale=640)
|
|
259
|
+
|
|
260
|
+
>>> # Create a small thumbnail GIF
|
|
261
|
+
>>> ffmpeg.gif("video.mp4", "thumbnail.gif",
|
|
262
|
+
... fps=5, scale=160)
|
|
263
|
+
"""
|
|
52
264
|
input_path = Path(input_file)
|
|
53
265
|
output_path = Path(output_file)
|
|
54
266
|
|
|
@@ -57,7 +269,7 @@ in your system PATH.
|
|
|
57
269
|
f"Input file {input_file} was not found."
|
|
58
270
|
)
|
|
59
271
|
|
|
60
|
-
filter_graph
|
|
272
|
+
filter_graph: str = (
|
|
61
273
|
f"fps={fps},"
|
|
62
274
|
f"scale={scale}:-1:flags=lanczos,"
|
|
63
275
|
"split[s0][s1];"
|
|
@@ -65,7 +277,7 @@ in your system PATH.
|
|
|
65
277
|
"[s1][p]paletteuse"
|
|
66
278
|
)
|
|
67
279
|
|
|
68
|
-
|
|
280
|
+
self.execute(
|
|
69
281
|
"-y",
|
|
70
282
|
"-i", str(input_path),
|
|
71
283
|
"-vf", filter_graph,
|
|
@@ -77,15 +289,48 @@ in your system PATH.
|
|
|
77
289
|
f"GIF was not created: {output_file}"
|
|
78
290
|
)
|
|
79
291
|
|
|
80
|
-
return stdout, stderr
|
|
81
|
-
|
|
82
292
|
def trim(
|
|
83
293
|
self,
|
|
84
294
|
input_file: str,
|
|
85
295
|
output_file: str,
|
|
86
296
|
start: float = 0,
|
|
87
297
|
duration: float | None = None,
|
|
88
|
-
):
|
|
298
|
+
) -> None:
|
|
299
|
+
"""
|
|
300
|
+
Trim a video file.
|
|
301
|
+
|
|
302
|
+
Extracts a segment from a video file starting at a specified time
|
|
303
|
+
and optionally ending after a specified duration.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
input_file: Path to the input video file.
|
|
307
|
+
output_file: Path for the output trimmed video.
|
|
308
|
+
start: Start time in seconds. Defaults to 0.
|
|
309
|
+
duration: Duration in seconds, or None for remaining video.
|
|
310
|
+
Defaults to None.
|
|
311
|
+
|
|
312
|
+
Raises:
|
|
313
|
+
FileNotFoundError: If input file doesn't exist.
|
|
314
|
+
ValueError: If start is negative or duration is non-positive.
|
|
315
|
+
RuntimeError: If trimmed video was not created successfully.
|
|
316
|
+
|
|
317
|
+
Example:
|
|
318
|
+
>>> # Trim from 5 seconds to 10 seconds (5-second clip)
|
|
319
|
+
>>> ffmpeg.trim("video.mp4", "clip.mp4",
|
|
320
|
+
... start=5, duration=5)
|
|
321
|
+
|
|
322
|
+
>>> # Trim from 10 seconds to the end of video
|
|
323
|
+
>>> ffmpeg.trim("video.mp4", "ending.mp4",
|
|
324
|
+
... start=10)
|
|
325
|
+
|
|
326
|
+
>>> # Trim first 30 seconds of video
|
|
327
|
+
>>> ffmpeg.trim("video.mp4", "intro.mp4",
|
|
328
|
+
... start=0, duration=30)
|
|
329
|
+
|
|
330
|
+
>>> # Trim with floating point precision
|
|
331
|
+
>>> ffmpeg.trim("video.mp4", "precise.mp4",
|
|
332
|
+
... start=2.5, duration=3.75)
|
|
333
|
+
"""
|
|
89
334
|
input_path = Path(input_file)
|
|
90
335
|
output_path = Path(output_file)
|
|
91
336
|
|
|
@@ -118,4 +363,164 @@ in your system PATH.
|
|
|
118
363
|
f"Trimmed video was not created: {output_file}"
|
|
119
364
|
)
|
|
120
365
|
|
|
121
|
-
|
|
366
|
+
def trims(self, input_bytes: bytes,
|
|
367
|
+
start: float = 0,
|
|
368
|
+
duration: float | None = None,
|
|
369
|
+
format_type: str = "mp4") -> bytes:
|
|
370
|
+
"""
|
|
371
|
+
Trim video data from bytes (in-memory processing).
|
|
372
|
+
|
|
373
|
+
Extracts a segment from in-memory video data starting at a specified time
|
|
374
|
+
and optionally ending after a specified duration. This method is useful
|
|
375
|
+
when you have video data in memory and want to avoid writing temporary files.
|
|
376
|
+
|
|
377
|
+
Note: For MP4 format, this method uses libx264 video codec and AAC audio codec
|
|
378
|
+
with fragmented MP4 output for better streaming compatibility.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
input_bytes: Video data as bytes to trim.
|
|
382
|
+
start: Start time in seconds. Defaults to 0.
|
|
383
|
+
duration: Duration in seconds, or None for remaining video.
|
|
384
|
+
Defaults to None.
|
|
385
|
+
format_type: Output format (e.g., 'mp4', 'avi', 'mov'). Defaults to 'mp4'.
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
bytes: The trimmed video data as bytes.
|
|
389
|
+
|
|
390
|
+
Raises:
|
|
391
|
+
RuntimeError: If video trimming fails.
|
|
392
|
+
|
|
393
|
+
Example:
|
|
394
|
+
>>> # Read video data from a file
|
|
395
|
+
>>> with open("video.mp4", "rb") as f:
|
|
396
|
+
... video_data = f.read()
|
|
397
|
+
>>>
|
|
398
|
+
>>> # Trim first 30 seconds in memory
|
|
399
|
+
>>> trimmed_data = ffmpeg.trims(video_data, start=0, duration=30)
|
|
400
|
+
>>>
|
|
401
|
+
>>> # Save the trimmed video
|
|
402
|
+
>>> with open("intro.mp4", "wb") as f:
|
|
403
|
+
... f.write(trimmed_data)
|
|
404
|
+
>>>
|
|
405
|
+
>>> # Trim with different format
|
|
406
|
+
>>> webm_data = ffmpeg.trims(video_data, start=10, duration=5, format_type="webm")
|
|
407
|
+
>>> with open("clip.webm", "wb") as f:
|
|
408
|
+
... f.write(webm_data)
|
|
409
|
+
>>>
|
|
410
|
+
>>> # Trim with specific codec settings
|
|
411
|
+
>>> # For non-MP4 formats, FFMPEG will use default codecs
|
|
412
|
+
>>> avi_data = ffmpeg.trims(video_data, start=5, duration=10, format_type="avi")
|
|
413
|
+
"""
|
|
414
|
+
args = [
|
|
415
|
+
"-y",
|
|
416
|
+
"-i", "pipe:0",
|
|
417
|
+
"-ss", str(start)
|
|
418
|
+
]
|
|
419
|
+
|
|
420
|
+
if duration is not None:
|
|
421
|
+
args.extend(["-t", str(duration)])
|
|
422
|
+
|
|
423
|
+
if format_type == 'mp4':
|
|
424
|
+
args.extend([
|
|
425
|
+
"-c:v", "libx264",
|
|
426
|
+
"-pix_fmt", "yuv420p",
|
|
427
|
+
"-c:a", "aac",
|
|
428
|
+
"-movflags", "frag_keyframe+empty_moov"
|
|
429
|
+
])
|
|
430
|
+
|
|
431
|
+
args.extend([
|
|
432
|
+
"-f", format_type,
|
|
433
|
+
"pipe:1"
|
|
434
|
+
])
|
|
435
|
+
|
|
436
|
+
stdout, stderr = self.execute(*args, input_data=input_bytes)
|
|
437
|
+
|
|
438
|
+
return stdout
|
|
439
|
+
|
|
440
|
+
def extract_audio(self, input_file: str, output_file: str) -> None:
|
|
441
|
+
"""
|
|
442
|
+
Extract audio from a video file.
|
|
443
|
+
|
|
444
|
+
Extracts the audio track from a video file without re-encoding,
|
|
445
|
+
preserving the original audio quality. The output file extension
|
|
446
|
+
should match the audio codec (e.g., .m4a for AAC, .mp3 for MP3,
|
|
447
|
+
.ogg for Vorbis).
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
input_file: Path to the input video file.
|
|
451
|
+
output_file: Path for the output audio file.
|
|
452
|
+
|
|
453
|
+
Raises:
|
|
454
|
+
FileNotFoundError: If input file doesn't exist.
|
|
455
|
+
RuntimeError: If audio extraction fails.
|
|
456
|
+
|
|
457
|
+
Example:
|
|
458
|
+
>>> # Extract audio from MP4 video
|
|
459
|
+
>>> ffmpeg.extract_audio("video.mp4", "audio.m4a")
|
|
460
|
+
>>>
|
|
461
|
+
>>> # Extract audio and convert to MP3
|
|
462
|
+
>>> ffmpeg.execute("-i", "video.mp4", "-q:a", "0", "-map", "a", "audio.mp3")
|
|
463
|
+
>>>
|
|
464
|
+
>>> # Extract audio from multiple formats
|
|
465
|
+
>>> ffmpeg.extract_audio("movie.mkv", "audio.m4a")
|
|
466
|
+
>>> ffmpeg.extract_audio("clip.avi", "audio.mp3")
|
|
467
|
+
"""
|
|
468
|
+
input_path = Path(input_file)
|
|
469
|
+
output_path = Path(output_file)
|
|
470
|
+
|
|
471
|
+
if not input_path.exists():
|
|
472
|
+
raise FileNotFoundError(
|
|
473
|
+
f"Input file {input_file} was not found."
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
self.execute(
|
|
477
|
+
"-i", str(input_path), # Specifies the input video file.
|
|
478
|
+
"-vn", # Disables the video stream (drops the visual data).
|
|
479
|
+
"-c:a", "copy", # Copies the audio track as-is without re-encoding.
|
|
480
|
+
str(output_path) # Output file. Extension should match audio codec.
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
def extract_audios(self, input_bytes: bytes, output_format: str = "m4a") -> bytes:
|
|
484
|
+
"""
|
|
485
|
+
Extract audio from video data (in-memory processing).
|
|
486
|
+
|
|
487
|
+
Extracts the audio track from in-memory video data without re-encoding,
|
|
488
|
+
preserving the original audio quality. This method is useful when you
|
|
489
|
+
have video data in memory and want to avoid writing temporary files.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
input_bytes: Video data as bytes to extract audio from.
|
|
493
|
+
output_format: Audio output format (e.g., 'm4a', 'mp3', 'ogg', 'wav').
|
|
494
|
+
Defaults to 'm4a'.
|
|
495
|
+
|
|
496
|
+
Returns:
|
|
497
|
+
bytes: The extracted audio data as bytes.
|
|
498
|
+
|
|
499
|
+
Raises:
|
|
500
|
+
RuntimeError: If audio extraction fails.
|
|
501
|
+
|
|
502
|
+
Example:
|
|
503
|
+
>>> # Read video data from a file
|
|
504
|
+
>>> with open("video.mp4", "rb") as f:
|
|
505
|
+
... video_data = f.read()
|
|
506
|
+
>>>
|
|
507
|
+
>>> # Extract audio in memory
|
|
508
|
+
>>> audio_data = ffmpeg.extract_audios(video_data, output_format="m4a")
|
|
509
|
+
>>>
|
|
510
|
+
>>> # Save the extracted audio
|
|
511
|
+
>>> with open("audio.m4a", "wb") as f:
|
|
512
|
+
... f.write(audio_data)
|
|
513
|
+
>>>
|
|
514
|
+
>>> # Extract audio in different formats
|
|
515
|
+
>>> mp3_data = ffmpeg.extract_audios(video_data, output_format="mp3")
|
|
516
|
+
>>> ogg_data = ffmpeg.extract_audios(video_data, output_format="ogg")
|
|
517
|
+
"""
|
|
518
|
+
args = [
|
|
519
|
+
"-i", "pipe:0",
|
|
520
|
+
"-vn",
|
|
521
|
+
"-c:a", "copy",
|
|
522
|
+
"-f", output_format,
|
|
523
|
+
"pipe:1"
|
|
524
|
+
]
|
|
525
|
+
|
|
526
|
+
return self.execute(*args, input_data=input_bytes)[0]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Pipe:
|
|
5
|
+
"""
|
|
6
|
+
A utility class for piping bytes through multiple processing functions.
|
|
7
|
+
|
|
8
|
+
This class provides a convenient way to chain multiple byte-processing
|
|
9
|
+
operations together, creating a pipeline that transforms data step by step.
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> from ffmpeg_python_helper import FFMPEG, Pipe
|
|
13
|
+
>>> ffmpeg = FFMPEG()
|
|
14
|
+
>>>
|
|
15
|
+
>>> # Define processing functions
|
|
16
|
+
>>> def trim_first_30s(data: bytes) -> bytes:
|
|
17
|
+
... return ffmpeg.trims(data, start=0, duration=30)
|
|
18
|
+
>>>
|
|
19
|
+
>>> def convert_to_gif(data: bytes) -> bytes:
|
|
20
|
+
... return ffmpeg.gifs(data, fps=15, scale=480)
|
|
21
|
+
>>>
|
|
22
|
+
>>> def extract_audio(data: bytes) -> bytes:
|
|
23
|
+
... return ffmpeg.extract_audios(data, output_format="m4a")
|
|
24
|
+
>>>
|
|
25
|
+
>>> # Read video data
|
|
26
|
+
>>> with open("video.mp4", "rb") as f:
|
|
27
|
+
... video_data = f.read()
|
|
28
|
+
>>>
|
|
29
|
+
>>> # Create a processing pipeline
|
|
30
|
+
>>> # Trim → Convert to GIF → Extract audio
|
|
31
|
+
>>> result = Pipe.pipe_bytes(
|
|
32
|
+
... video_data,
|
|
33
|
+
... trim_first_30s,
|
|
34
|
+
... convert_to_gif,
|
|
35
|
+
... extract_audio
|
|
36
|
+
... )
|
|
37
|
+
>>>
|
|
38
|
+
>>> # Save the final result (audio from trimmed GIF-converted video)
|
|
39
|
+
>>> with open("processed_audio.m4a", "wb") as f:
|
|
40
|
+
... f.write(result)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def pipe_bytes(initial_value: bytes, *fn: Callable[[bytes], bytes]) -> bytes:
|
|
45
|
+
"""
|
|
46
|
+
Pipe bytes through multiple processing functions.
|
|
47
|
+
|
|
48
|
+
This method takes an initial bytes value and passes it through
|
|
49
|
+
a series of functions, using the output of each function as the
|
|
50
|
+
input to the next function in the chain.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
initial_value: The initial bytes to start the pipeline with.
|
|
54
|
+
*fn: One or more callable functions that take bytes as input
|
|
55
|
+
and return bytes as output. Functions are applied in
|
|
56
|
+
the order they are provided.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
bytes: The final result after passing through all functions.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
TypeError: If any function is not callable or doesn't accept bytes.
|
|
63
|
+
RuntimeError: If any function in the pipeline fails.
|
|
64
|
+
|
|
65
|
+
Example:
|
|
66
|
+
>>> # Simple pipeline: read, process, save
|
|
67
|
+
>>> def add_header(data: bytes) -> bytes:
|
|
68
|
+
... return b"HEADER" + data
|
|
69
|
+
>>>
|
|
70
|
+
>>> def add_footer(data: bytes) -> bytes:
|
|
71
|
+
... return data + b"FOOTER"
|
|
72
|
+
>>>
|
|
73
|
+
>>> def uppercase_data(data: bytes) -> bytes:
|
|
74
|
+
... return data.upper()
|
|
75
|
+
>>>
|
|
76
|
+
>>> # Create pipeline
|
|
77
|
+
>>> result = Pipe.pipe_bytes(
|
|
78
|
+
... b"hello world",
|
|
79
|
+
... add_header,
|
|
80
|
+
... uppercase_data,
|
|
81
|
+
... add_footer
|
|
82
|
+
... )
|
|
83
|
+
>>>
|
|
84
|
+
>>> print(result)
|
|
85
|
+
b'HEADERHELLO WORLD FOOTER'
|
|
86
|
+
|
|
87
|
+
>>> # FFMPEG processing pipeline
|
|
88
|
+
>>> from ffmpeg_python_helper import FFMPEG
|
|
89
|
+
>>> ffmpeg = FFMPEG()
|
|
90
|
+
>>>
|
|
91
|
+
>>> def trim_video(data: bytes) -> bytes:
|
|
92
|
+
... return ffmpeg.trims(data, start=10, duration=5)
|
|
93
|
+
>>>
|
|
94
|
+
>>> def convert_to_webm(data: bytes) -> bytes:
|
|
95
|
+
... return ffmpeg.trims(data, format_type="webm") # Re-encode as webm
|
|
96
|
+
>>>
|
|
97
|
+
>>> # Process video through pipeline
|
|
98
|
+
>>> with open("input.mp4", "rb") as f:
|
|
99
|
+
... video_data = f.read()
|
|
100
|
+
>>>
|
|
101
|
+
>>> processed_data = Pipe.pipe_bytes(
|
|
102
|
+
... video_data,
|
|
103
|
+
... trim_video,
|
|
104
|
+
... convert_to_webm
|
|
105
|
+
... )
|
|
106
|
+
>>>
|
|
107
|
+
>>> with open("output.webm", "wb") as f:
|
|
108
|
+
... f.write(processed_data)
|
|
109
|
+
"""
|
|
110
|
+
current: bytes = initial_value
|
|
111
|
+
for callable in fn:
|
|
112
|
+
current = callable(current)
|
|
113
|
+
|
|
114
|
+
return current
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ffmpeg-python-helper
|
|
3
|
+
Version: 3.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
|
+
- 🎵 **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
|
+
- 🐍 **Pythonic API** - Clean, object-oriented interface with proper error handling
|
|
23
|
+
- 📁 **File Validation** - Automatic input file existence checking
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Prerequisites
|
|
28
|
+
- Python 3.14 or higher
|
|
29
|
+
- FFMPEG installed and available in your system PATH
|
|
30
|
+
|
|
31
|
+
### Install FFMPEG Python Helper
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install ffmpeg-python-helper
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or install from source:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/yourusername/ffmpeg-python-helper.git
|
|
41
|
+
cd ffmpeg-python-helper
|
|
42
|
+
pip install -e .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from ffmpeg_python_helper import FFMPEG
|
|
49
|
+
|
|
50
|
+
# Initialize the FFMPEG wrapper
|
|
51
|
+
ffmpeg = FFMPEG()
|
|
52
|
+
|
|
53
|
+
# Check if FFMPEG is available
|
|
54
|
+
print(f"FFMPEG executable found at: {ffmpeg.executable}")
|
|
55
|
+
|
|
56
|
+
# Convert a video file
|
|
57
|
+
output = ffmpeg.reformat("input.mp4", "output.avi")
|
|
58
|
+
print(output.decode())
|
|
59
|
+
|
|
60
|
+
# Create a GIF from video
|
|
61
|
+
ffmpeg.gif("video.mp4", "animation.gif", fps=15, scale=480)
|
|
62
|
+
|
|
63
|
+
# Trim a video
|
|
64
|
+
ffmpeg.trim("video.mp4", "short_clip.mp4", start=10.5, duration=5.0)
|
|
65
|
+
|
|
66
|
+
# Extract audio from video
|
|
67
|
+
ffmpeg.extract_audio("video.mp4", "audio.m4a")
|
|
68
|
+
|
|
69
|
+
# Create GIF from in-memory video data
|
|
70
|
+
with open("video.mp4", "rb") as f:
|
|
71
|
+
video_data = f.read()
|
|
72
|
+
gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
|
|
73
|
+
with open("memory.gif", "wb") as f:
|
|
74
|
+
f.write(gif_data)
|
|
75
|
+
|
|
76
|
+
# Trim video in memory
|
|
77
|
+
trimmed_data = ffmpeg.trims(video_data, start=0, duration=30)
|
|
78
|
+
with open("trimmed.mp4", "wb") as f:
|
|
79
|
+
f.write(trimmed_data)
|
|
80
|
+
|
|
81
|
+
# Extract audio in memory
|
|
82
|
+
audio_data = ffmpeg.extract_audios(video_data, output_format="m4a")
|
|
83
|
+
with open("audio.m4a", "wb") as f:
|
|
84
|
+
f.write(audio_data)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API Reference
|
|
88
|
+
|
|
89
|
+
### `FFMPEG` Class
|
|
90
|
+
|
|
91
|
+
The main class that wraps FFMPEG functionality.
|
|
92
|
+
|
|
93
|
+
#### Constructor
|
|
94
|
+
```python
|
|
95
|
+
FFMPEG()
|
|
96
|
+
```
|
|
97
|
+
Creates a new FFMPEG instance. Automatically searches for FFMPEG in the system PATH.
|
|
98
|
+
- **Raises**: `FileNotFoundError` if FFMPEG is not found in PATH
|
|
99
|
+
|
|
100
|
+
#### Properties
|
|
101
|
+
- `executable` (str): The path to the FFMPEG executable found in the system
|
|
102
|
+
|
|
103
|
+
#### Class Methods
|
|
104
|
+
```python
|
|
105
|
+
@classmethod
|
|
106
|
+
def api(cls) -> "FFMPEG"
|
|
107
|
+
```
|
|
108
|
+
Factory method that returns a new FFMPEG instance.
|
|
109
|
+
- **Returns**: `FFMPEG` instance
|
|
110
|
+
|
|
111
|
+
#### Instance Methods
|
|
112
|
+
|
|
113
|
+
##### `execute(*args: str, input_data: bytes | None = None) -> tuple[bytes, bytes]`
|
|
114
|
+
Execute raw FFMPEG commands with the given arguments.
|
|
115
|
+
|
|
116
|
+
**Parameters:**
|
|
117
|
+
- `*args` (str): FFMPEG command-line arguments
|
|
118
|
+
- `input_data` (bytes | None, optional): Optional bytes to send to FFMPEG's stdin
|
|
119
|
+
|
|
120
|
+
**Returns:**
|
|
121
|
+
- `tuple[bytes, bytes]`: A tuple containing (stdout, stderr) as bytes
|
|
122
|
+
|
|
123
|
+
**Raises:**
|
|
124
|
+
- `FileNotFoundError`: If FFMPEG executable is not found
|
|
125
|
+
- `RuntimeError`: If FFMPEG command returns a non-zero exit code
|
|
126
|
+
|
|
127
|
+
**Example:**
|
|
128
|
+
```python
|
|
129
|
+
stdout, stderr = ffmpeg.execute("-version")
|
|
130
|
+
print(stdout.decode())
|
|
131
|
+
|
|
132
|
+
# Process data from memory
|
|
133
|
+
video_data = b"...video bytes..."
|
|
134
|
+
stdout, stderr = ffmpeg.execute("-i", "pipe:0", "-f", "null", "-", input_data=video_data)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
##### `reformat(input_file: str, output_file: str) -> bytes`
|
|
138
|
+
Convert a video file from one format to another.
|
|
139
|
+
|
|
140
|
+
**Parameters:**
|
|
141
|
+
- `input_file` (str): Path to the input video file
|
|
142
|
+
- `output_file` (str): Path for the output video file
|
|
143
|
+
|
|
144
|
+
**Returns:**
|
|
145
|
+
- `bytes`: FFMPEG output (stdout or stderr) as bytes
|
|
146
|
+
|
|
147
|
+
**Raises:**
|
|
148
|
+
- `FileNotFoundError`: If input file doesn't exist
|
|
149
|
+
|
|
150
|
+
**Example:**
|
|
151
|
+
```python
|
|
152
|
+
output = ffmpeg.reformat("input.mov", "output.mp4")
|
|
153
|
+
print(output.decode())
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
##### `gif(input_file: str, output_file: str, fps: int = 10, scale: int = 320) -> bytes`
|
|
157
|
+
Convert a video file to an optimized GIF.
|
|
158
|
+
|
|
159
|
+
**Parameters:**
|
|
160
|
+
- `input_file` (str): Path to the input video file
|
|
161
|
+
- `output_file` (str): Path for the output GIF file
|
|
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`: FFMPEG output (stdout or stderr) as bytes
|
|
167
|
+
|
|
168
|
+
**Raises:**
|
|
169
|
+
- `FileNotFoundError`: If input file doesn't exist
|
|
170
|
+
- `RuntimeError`: If GIF file was not created successfully
|
|
171
|
+
|
|
172
|
+
**Example:**
|
|
173
|
+
```python
|
|
174
|
+
output = ffmpeg.gif("video.mp4", "output.gif", fps=15, scale=640)
|
|
175
|
+
print(output.decode())
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
##### `gifs(input_byte: bytes, fps: int = 10, scale: int = 320) -> bytes`
|
|
179
|
+
Convert video data from bytes to an optimized GIF (in-memory processing).
|
|
180
|
+
|
|
181
|
+
**Parameters:**
|
|
182
|
+
- `input_byte` (bytes): Video data as bytes to convert to GIF
|
|
183
|
+
- `fps` (int, optional): Frames per second for the GIF (default: 10)
|
|
184
|
+
- `scale` (int, optional): Width of the GIF in pixels, height is auto-scaled (default: 320)
|
|
185
|
+
|
|
186
|
+
**Returns:**
|
|
187
|
+
- `bytes`: The generated GIF data as bytes
|
|
188
|
+
|
|
189
|
+
**Raises:**
|
|
190
|
+
- `RuntimeError`: If GIF conversion fails
|
|
191
|
+
|
|
192
|
+
**Example:**
|
|
193
|
+
```python
|
|
194
|
+
# Read video data from a file
|
|
195
|
+
with open("video.mp4", "rb") as f:
|
|
196
|
+
video_data = f.read()
|
|
197
|
+
|
|
198
|
+
# Convert to GIF in memory
|
|
199
|
+
gif_data = ffmpeg.gifs(video_data, fps=15, scale=480)
|
|
200
|
+
|
|
201
|
+
# Save the GIF
|
|
202
|
+
with open("output.gif", "wb") as f:
|
|
203
|
+
f.write(gif_data)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
##### `trim(input_file: str, output_file: str, start: float = 0, duration: float | None = None) -> bytes`
|
|
207
|
+
Trim a video file.
|
|
208
|
+
|
|
209
|
+
**Parameters:**
|
|
210
|
+
- `input_file` (str): Path to the input video file
|
|
211
|
+
- `output_file` (str): Path for the output trimmed video
|
|
212
|
+
- `start` (float, optional): Start time in seconds (default: 0)
|
|
213
|
+
- `duration` (float | None, optional): Duration in seconds, or None for remaining video (default: None)
|
|
214
|
+
|
|
215
|
+
**Returns:**
|
|
216
|
+
- `bytes`: FFMPEG output (stdout or stderr) as bytes
|
|
217
|
+
|
|
218
|
+
**Raises:**
|
|
219
|
+
- `FileNotFoundError`: If input file doesn't exist
|
|
220
|
+
- `ValueError`: If start is negative or duration is non-positive
|
|
221
|
+
- `RuntimeError`: If trimmed video was not created successfully
|
|
222
|
+
|
|
223
|
+
**Example:**
|
|
224
|
+
```python
|
|
225
|
+
# Trim from 5 seconds to 10 seconds (5-second clip)
|
|
226
|
+
output = ffmpeg.trim("video.mp4", "clip.mp4", start=5, duration=5)
|
|
227
|
+
print(output.decode())
|
|
228
|
+
|
|
229
|
+
# Trim from 10 seconds to the end of video
|
|
230
|
+
output = ffmpeg.trim("video.mp4", "ending.mp4", start=10)
|
|
231
|
+
print(output.decode())
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Advanced Usage
|
|
235
|
+
|
|
236
|
+
### Custom FFMPEG Commands
|
|
237
|
+
For operations not covered by the built-in methods, use the `execute` method:
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
# Extract audio from video
|
|
241
|
+
ffmpeg.execute("-i", "video.mp4", "-q:a", "0", "-map", "a", "audio.mp3")
|
|
242
|
+
|
|
243
|
+
# Add watermark to video
|
|
244
|
+
ffmpeg.execute("-i", "video.mp4", "-i", "watermark.png",
|
|
245
|
+
"-filter_complex", "overlay=10:10", "output.mp4")
|
|
246
|
+
|
|
247
|
+
# Change video bitrate
|
|
248
|
+
ffmpeg.execute("-i", "input.mp4", "-b:v", "1M", "output.mp4")
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Error Handling
|
|
252
|
+
```python
|
|
253
|
+
from ffmpeg_python_helper import FFMPEG
|
|
254
|
+
import sys
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
ffmpeg = FFMPEG()
|
|
258
|
+
ffmpeg.gif("video.mp4", "output.gif")
|
|
259
|
+
except FileNotFoundError as e:
|
|
260
|
+
print(f"FFMPEG not found: {e}", file=sys.stderr)
|
|
261
|
+
sys.exit(1)
|
|
262
|
+
except RuntimeError as e:
|
|
263
|
+
print(f"Processing failed: {e}", file=sys.stderr)
|
|
264
|
+
sys.exit(1)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Common Use Cases
|
|
268
|
+
|
|
269
|
+
### Batch Processing
|
|
270
|
+
```python
|
|
271
|
+
import os
|
|
272
|
+
from ffmpeg_python_helper import FFMPEG
|
|
273
|
+
|
|
274
|
+
ffmpeg = FFMPEG()
|
|
275
|
+
videos = ["video1.mp4", "video2.mp4", "video3.mp4"]
|
|
276
|
+
|
|
277
|
+
for video in videos:
|
|
278
|
+
if os.path.exists(video):
|
|
279
|
+
base_name = os.path.splitext(video)[0]
|
|
280
|
+
ffmpeg.gif(video, f"{base_name}.gif", fps=12, scale=400)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Video Compilation
|
|
284
|
+
```python
|
|
285
|
+
from ffmpeg_python_helper import FFMPEG
|
|
286
|
+
|
|
287
|
+
ffmpeg = FFMPEG()
|
|
288
|
+
|
|
289
|
+
# Trim interesting parts
|
|
290
|
+
ffmpeg.trim("concert.mp4", "intro.mp4", start=0, duration=30)
|
|
291
|
+
ffmpeg.trim("concert.mp4", "chorus.mp4", start=120, duration=45)
|
|
292
|
+
ffmpeg.trim("concert.mp4", "finale.mp4", start=300, duration=60)
|
|
293
|
+
|
|
294
|
+
# Later, use FFMPEG to concatenate trimmed parts
|
|
295
|
+
ffmpeg.execute("-f", "concat", "-safe", "0", "-i", "parts.txt", "highlight_reel.mp4")
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Troubleshooting
|
|
299
|
+
|
|
300
|
+
### FFMPEG Not Found
|
|
301
|
+
If you get `FileNotFoundError` when creating an FFMPEG instance:
|
|
302
|
+
|
|
303
|
+
1. **Install FFMPEG**:
|
|
304
|
+
- **Windows**: Download from [ffmpeg.org](https://ffmpeg.org/download.html)
|
|
305
|
+
- **macOS**: `brew install ffmpeg`
|
|
306
|
+
- **Linux**: `sudo apt install ffmpeg` (Ubuntu/Debian) or `sudo yum install ffmpeg` (Fedora/RHEL)
|
|
307
|
+
|
|
308
|
+
2. **Add to PATH**:
|
|
309
|
+
- Ensure FFMPEG is in your system PATH
|
|
310
|
+
- Test with `ffmpeg -version` in your terminal
|
|
311
|
+
|
|
312
|
+
### File Not Found Errors
|
|
313
|
+
- Ensure input file paths are correct and files exist
|
|
314
|
+
- Use absolute paths if working with files in different directories
|
|
315
|
+
- Check file permissions
|
|
316
|
+
|
|
317
|
+
### GIF Creation Issues
|
|
318
|
+
- Lower FPS or scale if GIF file is too large
|
|
319
|
+
- Ensure input video has sufficient quality
|
|
320
|
+
- Check available disk space
|
|
321
|
+
|
|
322
|
+
## Development
|
|
323
|
+
|
|
324
|
+
### Running Tests
|
|
325
|
+
```bash
|
|
326
|
+
python -m pytest tests/
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Building Documentation
|
|
330
|
+
```bash
|
|
331
|
+
# Install documentation dependencies
|
|
332
|
+
pip install pdoc3
|
|
333
|
+
|
|
334
|
+
# Generate API documentation
|
|
335
|
+
pdoc --html ffmpeg_python_helper --output-dir docs
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Contributing
|
|
339
|
+
1. Fork the repository
|
|
340
|
+
2. Create a feature branch
|
|
341
|
+
3. Add tests for your changes
|
|
342
|
+
4. Ensure all tests pass
|
|
343
|
+
5. Submit a pull request
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
|
|
347
|
+
MIT License - see LICENSE file for details
|
|
348
|
+
|
|
349
|
+
## Support
|
|
350
|
+
|
|
351
|
+
- **Issues**: [GitHub Issues](https://github.com/yourusername/ffmpeg-python-helper/issues)
|
|
352
|
+
- **Documentation**: [ReadTheDocs](https://ffmpeg-python-helper.readthedocs.io)
|
|
353
|
+
- **Email**: marjongodito@gmanmi.com
|
|
354
|
+
|
|
355
|
+
## Acknowledgments
|
|
356
|
+
|
|
357
|
+
- FFMPEG team for the amazing multimedia framework
|
|
358
|
+
- Python community for excellent tooling and libraries
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ffmpeg_python_helper/__init__.py,sha256=aPK2SEFdtbeRyoCz_FOn0ZF35cpPhHBSFUsqd2T7DVk,2200
|
|
2
|
+
ffmpeg_python_helper/ffmpeg_api.py,sha256=UTDZ8JdizWjLdU0xfFhwiwDbUBiZ7H9a5vA7aydFoOs,18513
|
|
3
|
+
ffmpeg_python_helper/pipe_helper.py,sha256=fmEQwKNWuRU78bdTuS2-UIYWF8YcXjdHuR--FR8EDNw,4202
|
|
4
|
+
ffmpeg_python_helper-3.0.0.dist-info/WHEEL,sha256=y6e-a5KI2W-qDAJKfh9Xr81bin8NgUdfWgz3VSXXpe4,80
|
|
5
|
+
ffmpeg_python_helper-3.0.0.dist-info/entry_points.txt,sha256=M9t-tNA-GePbZIBMosX7vgLqg88pjImAYzSQ_fErZzA,68
|
|
6
|
+
ffmpeg_python_helper-3.0.0.dist-info/METADATA,sha256=2JO6yyaV-D2d152tFRE5IHd2hdR7gu2BmeELUy3e93s,10007
|
|
7
|
+
ffmpeg_python_helper-3.0.0.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
ffmpeg_python_helper/__init__.py,sha256=_8jV2YNFzNWRGPEeiXCPrNqgW6w1sdJ9XWoPjK5h33Q,123
|
|
2
|
-
ffmpeg_python_helper/_bundled/ffmpeg.exe,sha256=8d1Xqa-yiTJQ_j6iJ4metHecvowqLQkVdh8jGKCRQMo,105674240
|
|
3
|
-
ffmpeg_python_helper/ffmpeg_api.py,sha256=BbOzeaAepWrGvi--YmFJkQedsSMJ-NT4XRYRp2JRMxY,3317
|
|
4
|
-
ffmpeg_python_helper-0.1.0.dist-info/WHEEL,sha256=y6e-a5KI2W-qDAJKfh9Xr81bin8NgUdfWgz3VSXXpe4,80
|
|
5
|
-
ffmpeg_python_helper-0.1.0.dist-info/entry_points.txt,sha256=M9t-tNA-GePbZIBMosX7vgLqg88pjImAYzSQ_fErZzA,68
|
|
6
|
-
ffmpeg_python_helper-0.1.0.dist-info/METADATA,sha256=A9Yu67-lbGGrN8Fj8O8nKv5O2ODInjKhETTvbU7P_bw,226
|
|
7
|
-
ffmpeg_python_helper-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
{ffmpeg_python_helper-0.1.0.dist-info → ffmpeg_python_helper-3.0.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|