PyVideoKit-CLI 0.2.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.
File without changes
@@ -0,0 +1,54 @@
1
+ from collections.abc import Callable, Generator
2
+ from contextlib import contextmanager
3
+
4
+ import click
5
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
6
+
7
+ from pyvideokit_libs import FFmpegError, parse_time_to_seconds
8
+
9
+
10
+ @contextmanager
11
+ def track_progress(label: str) -> Generator[Callable[[float], None], None, None]:
12
+ with Progress(
13
+ SpinnerColumn(),
14
+ TextColumn("[bold]{task.description}"),
15
+ BarColumn(),
16
+ TextColumn("{task.percentage:>5.1f}%"),
17
+ TimeElapsedColumn(),
18
+ transient=True,
19
+ ) as progress:
20
+ task = progress.add_task(label, total=100.0)
21
+
22
+ def on_progress(pct: float) -> None:
23
+ progress.update(task, completed=pct)
24
+
25
+ yield on_progress
26
+
27
+
28
+ @contextmanager
29
+ def handle_errors() -> Generator[None, None, None]:
30
+ try:
31
+ yield
32
+ except (FileNotFoundError, ValueError, RuntimeError) as e:
33
+ raise click.ClickException(str(e))
34
+ except FFmpegError as e:
35
+ click.echo(f"FFmpeg failed (exit {e.returncode}):", err=True)
36
+ for line in e.error_lines:
37
+ click.echo(f" {line}", err=True)
38
+ raise click.exceptions.Exit(e.returncode)
39
+
40
+
41
+ class TimeParam(click.ParamType):
42
+ name = "TIME"
43
+
44
+ def convert(self, value, param, ctx):
45
+ try:
46
+ result = parse_time_to_seconds(str(value))
47
+ except ValueError as e:
48
+ self.fail(str(e), param, ctx)
49
+ if result is None:
50
+ self.fail("Empty time value", param, ctx)
51
+ return result
52
+
53
+
54
+ TIME = TimeParam()
@@ -0,0 +1,24 @@
1
+ import click
2
+
3
+ from .apply_vhs_effect import cmd as cmd_apply
4
+ from .concat_videos import cmd as cmd_concat
5
+ from .convert_to_ffv1 import cmd as cmd_convert
6
+ from .extract_audio import cmd as cmd_extract
7
+ from .fade_video import cmd as cmd_fade
8
+ from .prepare_youtube import cmd as cmd_prepare
9
+ from .trim_video import cmd as cmd_trim
10
+
11
+
12
+ @click.group()
13
+ @click.version_option("0.1.0")
14
+ def cli():
15
+ """PyVideoKit CLI — FFmpeg-based video processing tools."""
16
+
17
+
18
+ cli.add_command(cmd_apply, "apply-vhs-effect")
19
+ cli.add_command(cmd_concat, "concat-videos")
20
+ cli.add_command(cmd_convert, "convert-to-ffv1")
21
+ cli.add_command(cmd_extract, "extract-audio")
22
+ cli.add_command(cmd_fade, "fade-video")
23
+ cli.add_command(cmd_prepare, "prepare-youtube")
24
+ cli.add_command(cmd_trim, "trim-video")
@@ -0,0 +1,21 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from pyvideokit_libs import apply_vhs_effect
6
+
7
+ from ._helpers import handle_errors, track_progress
8
+
9
+
10
+ @click.command("apply-vhs-effect")
11
+ @click.argument("input", type=click.Path(exists=True))
12
+ @click.option("-o", "--output", default=None, help="Output file or directory.")
13
+ def cmd(input, output):
14
+ """Apply VHS visual and audio effect to a video."""
15
+ with handle_errors():
16
+ with track_progress("Applying VHS effect") as on_progress:
17
+ out = apply_vhs_effect(Path(input), output=output, on_progress=on_progress)
18
+ click.echo(str(out))
19
+
20
+
21
+ main = cmd
@@ -0,0 +1,23 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from pyvideokit_libs import join_videos
6
+
7
+ from ._helpers import handle_errors, track_progress
8
+
9
+
10
+ @click.command("concat-videos")
11
+ @click.argument("videos", nargs=-1, type=click.Path(exists=True), required=True)
12
+ @click.option("-o", "--output", default=None, help="Output file or directory.")
13
+ def cmd(videos, output):
14
+ """Concatenate two or more videos (lossless stream copy)."""
15
+ if len(videos) < 2:
16
+ raise click.UsageError("Provide at least two input videos.")
17
+ with handle_errors():
18
+ with track_progress("Concatenating") as on_progress:
19
+ out = join_videos([Path(v) for v in videos], output=output, on_progress=on_progress)
20
+ click.echo(str(out))
21
+
22
+
23
+ main = cmd
@@ -0,0 +1,22 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from pyvideokit_libs import convert_to_ffv1
6
+
7
+ from ._helpers import handle_errors, track_progress
8
+
9
+
10
+ @click.command("convert-to-ffv1")
11
+ @click.argument("input", type=click.Path(exists=True))
12
+ @click.option("--fps", default=60, show_default=True, help="Output frame rate.")
13
+ @click.option("-o", "--output", default=None, help="Output file or directory.")
14
+ def cmd(input, fps, output):
15
+ """Convert video to lossless FFV1/MKV format."""
16
+ with handle_errors():
17
+ with track_progress("Converting to FFV1") as on_progress:
18
+ out = convert_to_ffv1(Path(input), fps=fps, output=output, on_progress=on_progress)
19
+ click.echo(str(out))
20
+
21
+
22
+ main = cmd
@@ -0,0 +1,21 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from pyvideokit_libs import extract_audio
6
+
7
+ from ._helpers import handle_errors, track_progress
8
+
9
+
10
+ @click.command("extract-audio")
11
+ @click.argument("input", type=click.Path(exists=True))
12
+ @click.option("-o", "--output", default=None, help="Output file or directory.")
13
+ def cmd(input, output):
14
+ """Extract audio track to WAV (pcm_s16le)."""
15
+ with handle_errors():
16
+ with track_progress("Extracting audio") as on_progress:
17
+ out = extract_audio(Path(input), output=output, on_progress=on_progress)
18
+ click.echo(str(out))
19
+
20
+
21
+ main = cmd
@@ -0,0 +1,35 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from pyvideokit_libs import fade_video
6
+
7
+ from ._helpers import handle_errors, track_progress
8
+
9
+
10
+ @click.command("fade-video")
11
+ @click.argument("input", type=click.Path(exists=True))
12
+ @click.option("--fade", default=None, type=float, help="Fade-in and fade-out duration in seconds (shorthand).")
13
+ @click.option("--fade-in", "fade_in", default=None, type=float, help="Fade-in duration in seconds.")
14
+ @click.option("--fade-out", "fade_out", default=None, type=float, help="Fade-out duration in seconds.")
15
+ @click.option("--fps", default=60, show_default=True, help="Output frame rate.")
16
+ @click.option("-o", "--output", default=None, help="Output file or directory.")
17
+ def cmd(input, fade, fade_in, fade_out, fps, output):
18
+ """Add fade-in and/or fade-out to an FFV1 video."""
19
+ if fade is not None:
20
+ fade_in = fade_in if fade_in is not None else fade
21
+ fade_out = fade_out if fade_out is not None else fade
22
+ with handle_errors():
23
+ with track_progress("Applying fade") as on_progress:
24
+ out = fade_video(
25
+ Path(input),
26
+ fade_in=fade_in,
27
+ fade_out=fade_out,
28
+ fps=fps,
29
+ output=output,
30
+ on_progress=on_progress,
31
+ )
32
+ click.echo(str(out))
33
+
34
+
35
+ main = cmd
@@ -0,0 +1,21 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from pyvideokit_libs import prepare_youtube
6
+
7
+ from ._helpers import handle_errors, track_progress
8
+
9
+
10
+ @click.command("prepare-youtube")
11
+ @click.argument("input", type=click.Path(exists=True))
12
+ @click.option("-o", "--output", default=None, help="Output file or directory.")
13
+ def cmd(input, output):
14
+ """Encode FFV1 master to ProRes 422 HQ MOV for YouTube upload."""
15
+ with handle_errors():
16
+ with track_progress("Encoding for YouTube") as on_progress:
17
+ out = prepare_youtube(Path(input), output=output, on_progress=on_progress)
18
+ click.echo(str(out))
19
+
20
+
21
+ main = cmd
@@ -0,0 +1,23 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from pyvideokit_libs import trim_video
6
+
7
+ from ._helpers import TIME, handle_errors, track_progress
8
+
9
+
10
+ @click.command("trim-video")
11
+ @click.argument("input", type=click.Path(exists=True))
12
+ @click.option("--start", required=True, type=TIME, help="Start time (seconds or HH:MM:SS).")
13
+ @click.option("--end", required=True, type=TIME, help="End time (seconds or HH:MM:SS).")
14
+ @click.option("-o", "--output", default=None, help="Output file or directory.")
15
+ def cmd(input, start, end, output):
16
+ """Cut a segment from a video (stream copy, no re-encoding)."""
17
+ with handle_errors():
18
+ with track_progress("Trimming") as on_progress:
19
+ out = trim_video(Path(input), start=start, end=end, output=output, on_progress=on_progress)
20
+ click.echo(str(out))
21
+
22
+
23
+ main = cmd