videokit 0.1.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.
- videokit-0.1.0/LICENSE +21 -0
- videokit-0.1.0/PKG-INFO +463 -0
- videokit-0.1.0/README.md +433 -0
- videokit-0.1.0/pyproject.toml +46 -0
- videokit-0.1.0/setup.cfg +4 -0
- videokit-0.1.0/src/videokit/__init__.py +20 -0
- videokit-0.1.0/src/videokit/_ffmpeg.py +61 -0
- videokit-0.1.0/src/videokit/_vscode.py +288 -0
- videokit-0.1.0/src/videokit/audio.py +156 -0
- videokit-0.1.0/src/videokit/exceptions.py +13 -0
- videokit-0.1.0/src/videokit/py.typed +0 -0
- videokit-0.1.0/src/videokit/render.py +51 -0
- videokit-0.1.0/src/videokit/timeline.py +280 -0
- videokit-0.1.0/src/videokit/video.py +654 -0
- videokit-0.1.0/src/videokit.egg-info/PKG-INFO +463 -0
- videokit-0.1.0/src/videokit.egg-info/SOURCES.txt +18 -0
- videokit-0.1.0/src/videokit.egg-info/dependency_links.txt +1 -0
- videokit-0.1.0/src/videokit.egg-info/requires.txt +4 -0
- videokit-0.1.0/src/videokit.egg-info/top_level.txt +1 -0
- videokit-0.1.0/tests/test_video.py +106 -0
videokit-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dharshan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
videokit-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: videokit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A beginner-friendly Python video editing library powered by FFmpeg
|
|
5
|
+
Author: Dharshan
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dharshanlastone-tech/VideoKIT
|
|
8
|
+
Project-URL: Repository, https://github.com/dharshanlastone-tech/VideoKIT
|
|
9
|
+
Project-URL: Issues, https://github.com/dharshanlastone-tech/VideoKIT/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/dharshanlastone-tech/VideoKIT/blob/main/python-package/CHANGELOG.md
|
|
11
|
+
Project-URL: VS Code Extension, https://marketplace.visualstudio.com/items?itemName=dharshanlastone-tech.videokit
|
|
12
|
+
Keywords: video,audio,ffmpeg,editing,media
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Multimedia :: Video
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: release
|
|
27
|
+
Requires-Dist: build>=1.2; extra == "release"
|
|
28
|
+
Requires-Dist: twine>=5; extra == "release"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# VideoKit
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/videokit/)
|
|
34
|
+
[](https://pypi.org/project/videokit/)
|
|
35
|
+
[](https://github.com/dharshanlastone-tech/VideoKIT/blob/main/python-package/LICENSE)
|
|
36
|
+
|
|
37
|
+
[GitHub repository](https://github.com/dharshanlastone-tech/VideoKIT) ·
|
|
38
|
+
[VS Code extension](https://marketplace.visualstudio.com/items?itemName=dharshanlastone-tech.videokit)
|
|
39
|
+
|
|
40
|
+
VideoKit is a small Python library for common video-editing tasks. It lets you
|
|
41
|
+
inspect a media file, describe edits in Python, and use FFmpeg to render the
|
|
42
|
+
result. Edits are non-destructive: methods such as `cut()`, `speed()`, and
|
|
43
|
+
`fade_in()` return new clip descriptions and leave your source files alone.
|
|
44
|
+
|
|
45
|
+
The package works on its own. The optional VS Code extension reads the installed
|
|
46
|
+
package's API to provide completions, hover documentation, signature help,
|
|
47
|
+
snippets, and video-path previews. It does not render video.
|
|
48
|
+
|
|
49
|
+
## What works today
|
|
50
|
+
|
|
51
|
+
- Read video and audio duration, video size, frame rate, and codec information.
|
|
52
|
+
- Select a time range from video or audio.
|
|
53
|
+
- Export a video cut accurately by default, or stream-copy it quickly.
|
|
54
|
+
- Convert a video to 9:16 portrait or 16:9 landscape framing.
|
|
55
|
+
- Choose draft, balanced, or high quality for final timeline renders.
|
|
56
|
+
- Change clip playback speed.
|
|
57
|
+
- Rotate a clip for a selected time interval.
|
|
58
|
+
- Resize and crop, flip, and adjust brightness, contrast, and saturation.
|
|
59
|
+
- Convert video to grayscale.
|
|
60
|
+
- Fade a clip's picture and audio in or out.
|
|
61
|
+
- Extract a still frame and make a bounded-size thumbnail.
|
|
62
|
+
- Adjust audio volume and speed, add audio fades, and export audio.
|
|
63
|
+
- Arrange video clips on a sequential timeline, using direct cuts or crossfades.
|
|
64
|
+
- Add positioned audio tracks to a timeline.
|
|
65
|
+
|
|
66
|
+
There is no standalone graphical editor. Audio pitch correction, text and image
|
|
67
|
+
overlays, and arbitrary clip positioning are not currently implemented. The
|
|
68
|
+
separate VS Code extension provides API-aware coding help and video previews;
|
|
69
|
+
it does not render video.
|
|
70
|
+
|
|
71
|
+
## Find what you need
|
|
72
|
+
|
|
73
|
+
- [Install](#install)
|
|
74
|
+
- [VS Code extension](#vs-code-extension)
|
|
75
|
+
- [Quick start](#quick-start)
|
|
76
|
+
- [Choose your workflow](#choose-your-workflow)
|
|
77
|
+
- [Inspect a video](#video-metadata)
|
|
78
|
+
- [Edit a clip](#cut-edit-and-export-a-clip)
|
|
79
|
+
- [Convert between portrait and landscape](#convert-between-portrait-and-landscape)
|
|
80
|
+
- [Choose final-render quality](#final-render-quality)
|
|
81
|
+
- [Extract frames and thumbnails](#extract-a-frame)
|
|
82
|
+
- [Edit audio](#audio-editing)
|
|
83
|
+
- [Join clips and mix audio](#timeline-cuts-and-crossfades)
|
|
84
|
+
- [Errors and quality notes](#errors-and-validation)
|
|
85
|
+
- [Current API limits](#public-api-and-current-limits)
|
|
86
|
+
|
|
87
|
+
## Requirements
|
|
88
|
+
|
|
89
|
+
- Python 3.10 or newer
|
|
90
|
+
- FFmpeg **and** FFprobe installed and available on `PATH`
|
|
91
|
+
|
|
92
|
+
VideoKit has no mandatory Python runtime dependencies. FFmpeg is a separate
|
|
93
|
+
program; VideoKit does not download or bundle it. Check your installation from
|
|
94
|
+
a terminal with `ffmpeg -version` and `ffprobe -version`. If either tool is
|
|
95
|
+
missing, VideoKit raises `FFmpegError` with installation guidance.
|
|
96
|
+
|
|
97
|
+
The package is licensed under MIT; see
|
|
98
|
+
[LICENSE](https://github.com/dharshanlastone-tech/VideoKIT/blob/main/python-package/LICENSE).
|
|
99
|
+
|
|
100
|
+
## Install
|
|
101
|
+
|
|
102
|
+
```powershell
|
|
103
|
+
python -m pip install videokit
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
VideoKit has no mandatory Python runtime dependencies. Install FFmpeg and
|
|
107
|
+
FFprobe separately and confirm that both commands work in your terminal.
|
|
108
|
+
|
|
109
|
+
To work on a local checkout instead, open a terminal in `python-package` and
|
|
110
|
+
run:
|
|
111
|
+
|
|
112
|
+
```powershell
|
|
113
|
+
python -m pip install -e .
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## VS Code extension
|
|
117
|
+
|
|
118
|
+
The optional extension is published as
|
|
119
|
+
`dharshanlastone-tech.videokit`. Install it from the
|
|
120
|
+
[Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=dharshanlastone-tech.videokit),
|
|
121
|
+
from the Extensions view in VS Code, or with:
|
|
122
|
+
|
|
123
|
+
```powershell
|
|
124
|
+
code --install-extension dharshanlastone-tech.videokit
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Select the Python environment that has VideoKit installed to enable its
|
|
128
|
+
API-aware features. The extension is purely an editing aid; it does not replace
|
|
129
|
+
the Python package or FFmpeg.
|
|
130
|
+
|
|
131
|
+
## Quick start
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from videokit import Video
|
|
135
|
+
|
|
136
|
+
video = Video("movie.mp4")
|
|
137
|
+
print(f"Duration: {video.duration:.2f} seconds")
|
|
138
|
+
print(f"Size: {video.width} x {video.height}")
|
|
139
|
+
print(f"Frame rate: {video.fps}")
|
|
140
|
+
print(f"Video codec: {video.codec}")
|
|
141
|
+
print(f"Has audio: {video.has_audio}")
|
|
142
|
+
|
|
143
|
+
scene = video.cut(5, 15)
|
|
144
|
+
scene.export("scene.mp4")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
All time values are seconds. Paths can be strings or `pathlib.Path` objects.
|
|
148
|
+
`Video` resolves the path and reads actual stream metadata with FFprobe; missing
|
|
149
|
+
or invalid media is reported instead of filled with guessed values.
|
|
150
|
+
|
|
151
|
+
## Choose your workflow
|
|
152
|
+
|
|
153
|
+
| What you want to do | Start here |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| Read a video's size or duration | `video = Video("input.mp4")` |
|
|
156
|
+
| Save one section | `video.cut(start, end).export("clip.mp4")` |
|
|
157
|
+
| Edit speed, color, framing, or fades | Chain methods on `video.cut(start, end)` |
|
|
158
|
+
| Make a portrait/social video | `video.to_vertical().export("portrait.mp4")` |
|
|
159
|
+
| Make a landscape video | `video.to_horizontal().export("landscape.mp4")` |
|
|
160
|
+
| Join clips or add music | `Timeline()` then `.render("final.mp4")` |
|
|
161
|
+
| Make an image preview | `video.thumbnail("preview.jpg")` |
|
|
162
|
+
|
|
163
|
+
For best-quality final output, use `quality="high"` when rendering a timeline.
|
|
164
|
+
For fast drafts, use `quality="draft"`.
|
|
165
|
+
|
|
166
|
+
## Video metadata
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from videokit import Video
|
|
170
|
+
|
|
171
|
+
video = Video("movie.mp4")
|
|
172
|
+
|
|
173
|
+
video.path # absolute source path
|
|
174
|
+
video.duration # seconds
|
|
175
|
+
video.width # pixels
|
|
176
|
+
video.height # pixels
|
|
177
|
+
video.resolution # (width, height)
|
|
178
|
+
video.fps # frames per second, or None if FFprobe has no usable rate
|
|
179
|
+
video.codec # FFmpeg codec name, or None if unavailable
|
|
180
|
+
video.has_audio # whether the file has an audio stream
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Cut, edit, and export a clip
|
|
184
|
+
|
|
185
|
+
`cut(start, end)` selects the half-open interval from `start` up to (but not
|
|
186
|
+
including) `end`. The range must be positive and within the source duration.
|
|
187
|
+
Editing calls can be chained; each returns a new `Clip`.
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from videokit import Video
|
|
191
|
+
|
|
192
|
+
video = Video("movie.mp4")
|
|
193
|
+
clip = (
|
|
194
|
+
video.cut(5, 15)
|
|
195
|
+
.speed(1.25)
|
|
196
|
+
.resize(1280, 720)
|
|
197
|
+
.brightness(0.05)
|
|
198
|
+
.fade_in(0.5)
|
|
199
|
+
.fade_out(1.0)
|
|
200
|
+
)
|
|
201
|
+
print(clip.duration) # 8 seconds after the speed change
|
|
202
|
+
clip.export("scene.mp4")
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`Clip.export()` re-encodes by default so cuts and edits can be honored at the
|
|
206
|
+
requested timestamps. The default output video codec is H.264 and audio is AAC.
|
|
207
|
+
For an unedited cut, `clip.export("scene.mp4", accurate=False)` uses stream
|
|
208
|
+
copying for speed; most codecs can only cut at nearby keyframes in that mode.
|
|
209
|
+
Once a clip has edits, VideoKit encodes it even if `accurate=False`.
|
|
210
|
+
|
|
211
|
+
Outputs are not overwritten by default. Pass `overwrite=True` to replace an
|
|
212
|
+
existing output file.
|
|
213
|
+
|
|
214
|
+
## Convert between portrait and landscape
|
|
215
|
+
|
|
216
|
+
Use the convenience methods directly on a `Video`, or on a selected `Clip`.
|
|
217
|
+
Both methods return a clip you can continue editing and export:
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from videokit import Video
|
|
221
|
+
|
|
222
|
+
video = Video("wide-video.mp4")
|
|
223
|
+
|
|
224
|
+
# Make a vertical 9:16 video (default 1080 x 1920).
|
|
225
|
+
portrait = video.to_vertical()
|
|
226
|
+
portrait.export("portrait.mp4")
|
|
227
|
+
|
|
228
|
+
# Convert that clip back to a horizontal 16:9 video.
|
|
229
|
+
landscape = portrait.to_horizontal()
|
|
230
|
+
landscape.export("landscape.mp4")
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
By default, conversion **fills the new frame** (`mode="crop"`). This produces
|
|
234
|
+
the requested aspect ratio without bars, but part of the original picture must
|
|
235
|
+
be cropped. For a 16:9-to-9:16 conversion, the default crop is centered.
|
|
236
|
+
Choose a different crop focus when the subject is not centered:
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
# Keep a subject closer to the right side of the original landscape frame.
|
|
240
|
+
portrait = video.to_vertical(focus_x=0.75)
|
|
241
|
+
portrait.export("portrait-focused.mp4")
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`focus_x` and `focus_y` are positions from `0` to `1`: `0` favors the left or
|
|
245
|
+
top edge, `0.5` centers the crop, and `1` favors the right or bottom edge.
|
|
246
|
+
These settings choose where the crop is anchored; they do not track a moving
|
|
247
|
+
subject.
|
|
248
|
+
|
|
249
|
+
If you want to keep **all** of the original picture instead, use `mode="fit"`.
|
|
250
|
+
VideoKit adds black bars where needed:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
portrait_with_bars = video.to_vertical(mode="fit")
|
|
254
|
+
portrait_with_bars.export("portrait-full-picture.mp4")
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Output size defaults to `1080 x 1920` for portrait and `1920 x 1080` for
|
|
258
|
+
landscape. You can choose a smaller even-sized output for faster renders:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
portrait_720p = video.to_vertical(width=720, height=1280)
|
|
262
|
+
landscape_720p = portrait_720p.to_horizontal(width=1280, height=720)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Dimensions must match the requested ratio and be even integers. Reframing is
|
|
266
|
+
performed at render time and does not alter the source video.
|
|
267
|
+
|
|
268
|
+
## Final render quality
|
|
269
|
+
|
|
270
|
+
Use `Timeline.render()` for a clear final-output step. It renders all timeline
|
|
271
|
+
clips, crossfades, and audio tracks into one H.264/AAC video:
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from videokit import Audio, RenderSettings, Timeline, Video
|
|
275
|
+
|
|
276
|
+
timeline = Timeline()
|
|
277
|
+
timeline.add(Video("intro.mp4").cut(0, 5))
|
|
278
|
+
timeline.add(Video("main.mp4").cut(2, 20), crossfade=0.75)
|
|
279
|
+
timeline.add_audio(Audio("music.mp3").cut(0, 22).volume(0.2))
|
|
280
|
+
|
|
281
|
+
# Simple option:
|
|
282
|
+
timeline.render("final.mp4", quality="high")
|
|
283
|
+
|
|
284
|
+
# Or reuse an explicit settings object:
|
|
285
|
+
settings = RenderSettings(quality="balanced")
|
|
286
|
+
timeline.export("review.mp4", settings=settings)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
| Quality | FFmpeg preset | CRF | Best for |
|
|
290
|
+
| --- | --- | --- | --- |
|
|
291
|
+
| `"draft"` | `veryfast` | 28 | Quick checks and rough drafts |
|
|
292
|
+
| `"balanced"` | `medium` | 22 | Everyday output (default) |
|
|
293
|
+
| `"high"` | `slow` | 18 | Higher image quality; takes longer and usually creates larger files |
|
|
294
|
+
|
|
295
|
+
CRF is FFmpeg's quality scale: lower numbers preserve more image detail and
|
|
296
|
+
usually produce larger files. The settings use H.264 video and 192 kb/s AAC
|
|
297
|
+
audio. For individual edited clips, pass the same settings to `Clip.export()`:
|
|
298
|
+
`clip.export("scene.mp4", settings=RenderSettings(quality="high"))`.
|
|
299
|
+
`Timeline.export()` remains available; it accepts `settings=...` and continues
|
|
300
|
+
to accept the output size and frame rate options.
|
|
301
|
+
|
|
302
|
+
Rendering involves encoding the complete result, so it takes longer than
|
|
303
|
+
stream-copying an unedited clip. For a fast preview, use `quality="draft"`; for
|
|
304
|
+
the final delivery, choose `"balanced"` or `"high"` based on your needs.
|
|
305
|
+
|
|
306
|
+
### Clip operations
|
|
307
|
+
|
|
308
|
+
| Method | Behavior |
|
|
309
|
+
| --- | --- |
|
|
310
|
+
| `clip.speed(factor)` | Multiply playback speed. `2` is twice as fast; `0.5` is half speed. Audio pitch changes with speed. |
|
|
311
|
+
| `clip.rotate(degrees, start=0, end=None)` | Rotate clockwise during a time interval relative to the clip's playback. Exposed pixels are black. |
|
|
312
|
+
| `clip.resize(width, height)` | Fit inside the requested even-sized frame and pad unused space with black; aspect ratio is preserved. |
|
|
313
|
+
| `clip.to_vertical(...)` / `clip.to_horizontal(...)` | Reframe to 9:16 portrait or 16:9 landscape using centered cropping or black-bar fitting. |
|
|
314
|
+
| `clip.crop(width, height, x=0, y=0)` | Crop an even-sized pixel rectangle from the current frame. |
|
|
315
|
+
| `clip.flip(horizontal=False, vertical=False)` | Flip one or both directions; at least one direction must be enabled. |
|
|
316
|
+
| `clip.brightness(amount)` | Set brightness in the range `-1` to `1`; `0` is unchanged. |
|
|
317
|
+
| `clip.contrast(amount)` | Set contrast in the range `0` to `4`; `1` is unchanged. |
|
|
318
|
+
| `clip.saturation(amount)` | Set saturation in the range `0` to `3`; `1` is unchanged. |
|
|
319
|
+
| `clip.grayscale()` | Remove color. |
|
|
320
|
+
| `clip.fade_in(duration)` / `clip.fade_out(duration)` | Fade picture from/to black and audio from/to silence. |
|
|
321
|
+
|
|
322
|
+
For example, rotate a clip only from second 2 through second 4 of its playback:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
portrait = video.cut(0, 8).rotate(90, start=2, end=4)
|
|
326
|
+
portrait.export("portrait.mp4")
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`rotate()` accepts any finite angle in degrees. Rotation can increase the output
|
|
330
|
+
frame dimensions to keep the rotated picture visible. Crop dimensions and
|
|
331
|
+
positions are checked against the original video size.
|
|
332
|
+
|
|
333
|
+
## Extract a frame
|
|
334
|
+
|
|
335
|
+
`extract_frame()` writes one still image and returns its absolute path. Choose
|
|
336
|
+
the image extension, for example `.png` or `.jpg`, in the output path.
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
video = Video("movie.mp4")
|
|
340
|
+
thumbnail = video.extract_frame(3.5, "thumbnail.png")
|
|
341
|
+
small_preview = video.thumbnail("preview.jpg", max_width=320, max_height=180)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
The timestamp must be inside the video. Frame decoding is done by FFmpeg; the
|
|
345
|
+
whole video is not loaded into Python memory.
|
|
346
|
+
`thumbnail()` defaults to the middle of the video and preserves aspect ratio
|
|
347
|
+
within the requested maximum dimensions.
|
|
348
|
+
|
|
349
|
+
## Audio editing
|
|
350
|
+
|
|
351
|
+
```python
|
|
352
|
+
from videokit import Audio
|
|
353
|
+
|
|
354
|
+
music = Audio("background.mp3")
|
|
355
|
+
print(music.duration, music.codec, music.sample_rate, music.channels)
|
|
356
|
+
|
|
357
|
+
intro = music.cut(0, 20).volume(0.35).fade_in(1).fade_out(2)
|
|
358
|
+
intro.export("intro.mp3")
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
| Method | Behavior |
|
|
362
|
+
| --- | --- |
|
|
363
|
+
| `audio.cut(start, end)` | Select a valid time range, returning an `AudioClip`. |
|
|
364
|
+
| `clip.volume(level)` | Set volume from `0` (silent) to `4` (four times the source level). |
|
|
365
|
+
| `clip.speed(factor)` | Change duration and pitch by changing playback speed. |
|
|
366
|
+
| `clip.fade_in(duration)` / `clip.fade_out(duration)` | Apply an audio fade. |
|
|
367
|
+
| `clip.export(path)` | Export as MP3 for `.mp3`, WAV for `.wav`, otherwise AAC. |
|
|
368
|
+
|
|
369
|
+
Audio re-encoding is required for precise cuts and processing. The selected
|
|
370
|
+
extension determines the output encoding; use `.mp3`, `.wav`, `.m4a`, or another
|
|
371
|
+
format supported by the installed FFmpeg build.
|
|
372
|
+
|
|
373
|
+
## Timeline, cuts, and crossfades
|
|
374
|
+
|
|
375
|
+
Add video clips in playback order. Consecutive clips meet with a direct cut by
|
|
376
|
+
default; `crossfade` specifies the overlap with the preceding clip in seconds.
|
|
377
|
+
The first clip cannot have a crossfade.
|
|
378
|
+
|
|
379
|
+
```python
|
|
380
|
+
from videokit import Audio, Timeline, Video
|
|
381
|
+
|
|
382
|
+
first_video = Video("first.mp4")
|
|
383
|
+
second_video = Video("second.mp4")
|
|
384
|
+
|
|
385
|
+
timeline = Timeline()
|
|
386
|
+
timeline.add(first_video.cut(0, 8))
|
|
387
|
+
timeline.add(second_video.cut(2, 10), crossfade=1.0)
|
|
388
|
+
|
|
389
|
+
music = Audio("music.mp3").cut(0, 16).volume(0.2).fade_in(1)
|
|
390
|
+
timeline.add_audio(music, at=0)
|
|
391
|
+
|
|
392
|
+
print(timeline.duration)
|
|
393
|
+
timeline.export("finished.mp4")
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The output uses the first video clip's dimensions and frame rate by default.
|
|
397
|
+
Clips with differing dimensions are fitted and padded to that size. If the
|
|
398
|
+
first clip has no usable frame rate, timeline export uses 30 fps. You can set
|
|
399
|
+
the output explicitly:
|
|
400
|
+
|
|
401
|
+
```python
|
|
402
|
+
timeline.export("finished.mp4", width=1920, height=1080, fps=30)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Width and height must be even integers of at least two pixels. Video from each
|
|
406
|
+
clip is encoded as H.264; audio is mixed and encoded as AAC. Clips without
|
|
407
|
+
source audio contribute silence, so music remains synchronized through cuts and
|
|
408
|
+
crossfades. Audio added with `add_audio()` starts at the `at` position and is
|
|
409
|
+
mixed with any source audio.
|
|
410
|
+
|
|
411
|
+
## Errors and validation
|
|
412
|
+
|
|
413
|
+
- `MediaError`: missing or unreadable media, missing media streams, or invalid
|
|
414
|
+
required metadata.
|
|
415
|
+
- `FFmpegError`: FFmpeg/FFprobe is unavailable or the requested media operation
|
|
416
|
+
fails. The FFmpeg error output is included to help diagnose codec issues.
|
|
417
|
+
- `ValueError`: invalid timestamps, dimensions, speed, effects, or output
|
|
418
|
+
settings.
|
|
419
|
+
- `FileExistsError`: output already exists and `overwrite=True` was not set.
|
|
420
|
+
|
|
421
|
+
FFmpeg filter availability and media codec support depend on the FFmpeg build
|
|
422
|
+
installed on your computer. VideoKit surfaces FFmpeg failures rather than
|
|
423
|
+
pretending an unsupported operation succeeded.
|
|
424
|
+
|
|
425
|
+
## Performance and quality
|
|
426
|
+
|
|
427
|
+
- Metadata and frame extraction are delegated to FFprobe/FFmpeg.
|
|
428
|
+
- Video and audio files are not read wholly into Python memory.
|
|
429
|
+
- Stream-copy exports are fast and avoid generation loss, but cuts may be
|
|
430
|
+
keyframe-aligned rather than frame-exact.
|
|
431
|
+
- Accurate cuts, visual edits, speed changes, and timeline composition encode
|
|
432
|
+
media and take longer. Timeline export uses the selected `RenderSettings`
|
|
433
|
+
quality preset; the default `balanced` preset is H.264 `medium` with CRF 22
|
|
434
|
+
and 192 kb/s AAC audio.
|
|
435
|
+
- Changing playback speed also changes audio pitch. Pitch-preserving
|
|
436
|
+
time-stretching is not implemented.
|
|
437
|
+
- `crossfade` blends picture and audio for the selected overlap. Other
|
|
438
|
+
transition styles are not implemented yet.
|
|
439
|
+
|
|
440
|
+
## Public API and current limits
|
|
441
|
+
|
|
442
|
+
Import supported types from `videokit`: `Video`, `Clip`, `Audio`, `AudioClip`,
|
|
443
|
+
and `Timeline`. The package also exports `VideoKitError`, `MediaError`, and
|
|
444
|
+
`FFmpegError`.
|
|
445
|
+
|
|
446
|
+
There is currently no image overlay, text rendering, general-purpose visual
|
|
447
|
+
effect plug-in API, arbitrary timeline positioning of video clips, cancellation
|
|
448
|
+
interface, or GUI. The separate VS Code extension provides coding assistance and
|
|
449
|
+
previews; it is not a renderer. New features should be added only with a
|
|
450
|
+
well-defined API and matching implementation/documentation.
|
|
451
|
+
|
|
452
|
+
## Development
|
|
453
|
+
|
|
454
|
+
Run the package's focused tests from this directory:
|
|
455
|
+
|
|
456
|
+
```powershell
|
|
457
|
+
$env:PYTHONPATH = "src"
|
|
458
|
+
python -m unittest discover -s tests -v
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
The tests cover metadata parsing, validation, reframing, render presets, and the
|
|
462
|
+
VS Code helper. For media output, also try the examples above with a local
|
|
463
|
+
FFmpeg installation and representative files from the codecs you expect to use.
|