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 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.
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/videokit.svg)](https://pypi.org/project/videokit/)
34
+ [![Python versions](https://img.shields.io/pypi/pyversions/videokit.svg)](https://pypi.org/project/videokit/)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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.