clipsmith


Nameclipsmith JSON
Version 0.1.4 PyPI version JSON
download
home_pagehttps://github.com/mm21/clipsmith
SummaryUtility to work with video clips, especially suited for creating timelapses from dashcam footage
upload_time2024-12-17 05:10:33
maintainerNone
docs_urlNone
authormm21
requires_python<3.14,>=3.11
licenseNone
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <!-- TODO: logo using glyphsynth -->

# ClipSmith
Utility to work with video clips, especially suited for dashcam footage

[![Python versions](https://img.shields.io/pypi/pyversions/clipsmith.svg)](https://pypi.org/project/clipsmith)
[![PyPI](https://img.shields.io/pypi/v/clipsmith?color=%2334D058&label=pypi%20package)](https://pypi.org/project/clipsmith)
[![Tests](./badges/tests.svg?dummy=8484744)]()
[![Coverage](./badges/cov.svg?dummy=8484744)]()
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

- [ClipSmith](#clipsmith)
  - [Overview](#overview)
  - [Getting started](#getting-started)
  - [CLI](#cli)
    - [Forging clips](#forging-clips)
      - [Concatenating](#concatenating)
      - [Trimming](#trimming)
      - [Rescaling](#rescaling)
    - [Input folder caching](#input-folder-caching)
  - [API](#api)
    - [Context](#context)
    - [Clips](#clips)

## Overview

This project leverages [FFmpeg](https://ffmpeg.org/) and [doit](https://pydoit.org/) to provide a user-friendly utility for working with video clips and orchestrating pipelines of video editing operations. Clips can be readily concatenated, trimmed, and/or rescaled in a single command. This is especially useful for working with dashcam footage wherein there are many short video files to manage.

## Getting started

First, ensure you have ffmpeg installed:

```bash
# Ubuntu/Debian
sudo apt install ffmpeg

# macOS
brew install ffmpeg

# Windows
# Download from https://ffmpeg.org/download.html
```

Then install using pip:

```bash
pip install clipsmith
```

## CLI

### Forging clips

The command `clipsmith forge` is the entry point for creating new clips. Operations for concatenation, duration trimming, duration scaling, resolution scaling, and audio can be specified together in one command.

<!-- include doc/cli/forge.md -->
```
Usage: clipsmith forge [OPTIONS] INPUTS... OUTPUT                                                                                                            
                                                                                                                                                              
 Create a video from one or more videos with specified operations applied                                                                                     
                                                                                                                                                              
╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ *    inputs      INPUTS...  One or more paths to input video(s) or folder(s) of videos [default: None] [required]                                          │
│ *    output      PATH       Path to output video [default: None] [required]                                                                                │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --trim-start                  FLOAT  Start offset (seconds) in input file(s) [default: None]                                                               │
│ --trim-end                    FLOAT  End offset (seconds) in input file(s) [default: None]                                                                 │
│ --dur-scale                   FLOAT  Scale duration by scale factor [default: None]                                                                        │
│ --dur-target                  FLOAT  Scale duration to target (seconds) [default: None]                                                                    │
│ --res-scale                   FLOAT  Scale resolution by scale factor [default: None]                                                                      │
│ --res-target                  TEXT   Scale resolution to target as WIDTH:HEIGHT [default: None]                                                            │
│ --audio         --no-audio           Whether to pass through audio to output (not yet supported with time scaling) [default: audio]                        │
│ --cache         --no-cache           Whether to store a cache of video metadata in input folders [default: no-cache]                                       │
│ --log-level                   TEXT   Log level passed to ffmpeg [default: info]                                                                            │
│ --help                               Show this message and exit.                                                                                           │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

<!-- include end -->

#### Concatenating

Concatenate multiple clips into a single one:

```bash
# Combine specific files
clipsmith forge clip1.mp4 clip2.mp4 combined.mp4

# Combine all clips from a folder
clipsmith forge input_folder/ combined.mp4
```

For any folders passed as inputs, their contents are recursively scanned depth-first to aggregate input videos.

#### Trimming

Trim clips using start and end time offsets:

```bash
# Trim to specific time range
clipsmith forge --trim-start 1.0 --trim-end 5.0 input.mp4 output.mp4

# Trim just the start
clipsmith forge --trim-start 1.0 input.mp4 output.mp4

# Trim just the end
clipsmith forge --trim-end 5.0 input.mp4 output.mp4
```

#### Rescaling

Rescale video duration and resolution:

```bash
# Speed up by factor (e.g. 2x faster)
clipsmith forge --dur-scale 2.0 --no-audio input.mp4 output.mp4

# Slow down by factor (e.g. 2x slower)
clipsmith forge --dur-scale 0.5 --no-audio input.mp4 output.mp4

# Rescale duration to specific value in seconds
clipsmith forge --dur-target 60.0 --no-audio input.mp4 output.mp4

# Rescale resolution by factor
clipsmith forge --res-scale 0.5 input.mp4 output.mp4

# Rescale resolution to specific value as WIDTH:HEIGHT
clipsmith forge --res-target 480:270 input.mp4 output.mp4
```

<!-- TODO:
### Clip playbooks
-->

### Input folder caching

For input folders with many videos, the process of scanning and validating input files can be time-consuming. In such cases, pass `--cache` to cache video metadata per folder. In case of multiple or interrupted invocations, ClipSmith will use this cache to quickly begin its work.

## API

### Context

The `Context` class provides the main interface for working with clips. It implements a task orchestration pattern using `doit`:

```python
from pathlib import Path
from clipsmith import Context

# Create a context
context = Context()

# Forge a new clip from input files
context.forge("output.mp4", [Path("input1.mp4"), Path("input2.mp4")])

# Execute all pending operations
context.doit()
```

### Clips

Clips can be manipulated using operation parameters for duration and resolution. A clip can be "reforged" into another clip, with `doit` managing orchestration of `ffmpeg` invocations.

```python
from clipsmith import (
    Context, 
    OperationParams,
    DurationParams,
    ResolutionParams
)

context = Context()
inputs = [Path("input1.mp4"), Path("input2.mp4")]

# Trimming
clip1 = context.forge(
    "output1.mp4",
    inputs,
    OperationParams(
        duration_params=DurationParams(
            trim_start=1.0,  # Start at 1 second
            trim_end=5.0,  # End at 5 seconds
        ),
    )
)

# Time scaling
clip2 = context.forge(
    "output2.mp4",
    inputs,
    OperationParams(
        duration_params=DurationParams(
            scale=2.0,  # Speed up by 2x
        ),
        audio=False
    )
)

# Resolution scaling
clip3 = context.forge(
    "output3.mp4", 
    inputs,
    OperationParams(
        resolution_params=ResolutionParams(
            target=(480, 270)  # Scale to specific resolution
        )
    )
)

# Chain operations by reforging trimmed clip
clip4 = clip1.reforge(
    "output4.mp4",
    OperationParams(
        resolution_params=ResolutionParams(
            scale=0.5  # Scale resolution by factor
        )
    )
)

# Execute all operations
context.doit()
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/mm21/clipsmith",
    "name": "clipsmith",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<3.14,>=3.11",
    "maintainer_email": null,
    "keywords": null,
    "author": "mm21",
    "author_email": "mm21.dev@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/51/e5/12a104c13457f69a0a6fa2e6bbcfde1e4f6b06fb8c07c409e4b7d5309719/clipsmith-0.1.4.tar.gz",
    "platform": null,
    "description": "<!-- TODO: logo using glyphsynth -->\n\n# ClipSmith\nUtility to work with video clips, especially suited for dashcam footage\n\n[![Python versions](https://img.shields.io/pypi/pyversions/clipsmith.svg)](https://pypi.org/project/clipsmith)\n[![PyPI](https://img.shields.io/pypi/v/clipsmith?color=%2334D058&label=pypi%20package)](https://pypi.org/project/clipsmith)\n[![Tests](./badges/tests.svg?dummy=8484744)]()\n[![Coverage](./badges/cov.svg?dummy=8484744)]()\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n\n- [ClipSmith](#clipsmith)\n  - [Overview](#overview)\n  - [Getting started](#getting-started)\n  - [CLI](#cli)\n    - [Forging clips](#forging-clips)\n      - [Concatenating](#concatenating)\n      - [Trimming](#trimming)\n      - [Rescaling](#rescaling)\n    - [Input folder caching](#input-folder-caching)\n  - [API](#api)\n    - [Context](#context)\n    - [Clips](#clips)\n\n## Overview\n\nThis project leverages [FFmpeg](https://ffmpeg.org/) and [doit](https://pydoit.org/) to provide a user-friendly utility for working with video clips and orchestrating pipelines of video editing operations. Clips can be readily concatenated, trimmed, and/or rescaled in a single command. This is especially useful for working with dashcam footage wherein there are many short video files to manage.\n\n## Getting started\n\nFirst, ensure you have ffmpeg installed:\n\n```bash\n# Ubuntu/Debian\nsudo apt install ffmpeg\n\n# macOS\nbrew install ffmpeg\n\n# Windows\n# Download from https://ffmpeg.org/download.html\n```\n\nThen install using pip:\n\n```bash\npip install clipsmith\n```\n\n## CLI\n\n### Forging clips\n\nThe command `clipsmith forge` is the entry point for creating new clips. Operations for concatenation, duration trimming, duration scaling, resolution scaling, and audio can be specified together in one command.\n\n<!-- include doc/cli/forge.md -->\n```\nUsage: clipsmith forge [OPTIONS] INPUTS... OUTPUT                                                                                                            \n                                                                                                                                                              \n Create a video from one or more videos with specified operations applied                                                                                     \n                                                                                                                                                              \n\u256d\u2500 Arguments \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 *    inputs      INPUTS...  One or more paths to input video(s) or folder(s) of videos [default: None] [required]                                          \u2502\n\u2502 *    output      PATH       Path to output video [default: None] [required]                                                                                \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\u256d\u2500 Options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 --trim-start                  FLOAT  Start offset (seconds) in input file(s) [default: None]                                                               \u2502\n\u2502 --trim-end                    FLOAT  End offset (seconds) in input file(s) [default: None]                                                                 \u2502\n\u2502 --dur-scale                   FLOAT  Scale duration by scale factor [default: None]                                                                        \u2502\n\u2502 --dur-target                  FLOAT  Scale duration to target (seconds) [default: None]                                                                    \u2502\n\u2502 --res-scale                   FLOAT  Scale resolution by scale factor [default: None]                                                                      \u2502\n\u2502 --res-target                  TEXT   Scale resolution to target as WIDTH:HEIGHT [default: None]                                                            \u2502\n\u2502 --audio         --no-audio           Whether to pass through audio to output (not yet supported with time scaling) [default: audio]                        \u2502\n\u2502 --cache         --no-cache           Whether to store a cache of video metadata in input folders [default: no-cache]                                       \u2502\n\u2502 --log-level                   TEXT   Log level passed to ffmpeg [default: info]                                                                            \u2502\n\u2502 --help                               Show this message and exit.                                                                                           \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n```\n\n<!-- include end -->\n\n#### Concatenating\n\nConcatenate multiple clips into a single one:\n\n```bash\n# Combine specific files\nclipsmith forge clip1.mp4 clip2.mp4 combined.mp4\n\n# Combine all clips from a folder\nclipsmith forge input_folder/ combined.mp4\n```\n\nFor any folders passed as inputs, their contents are recursively scanned depth-first to aggregate input videos.\n\n#### Trimming\n\nTrim clips using start and end time offsets:\n\n```bash\n# Trim to specific time range\nclipsmith forge --trim-start 1.0 --trim-end 5.0 input.mp4 output.mp4\n\n# Trim just the start\nclipsmith forge --trim-start 1.0 input.mp4 output.mp4\n\n# Trim just the end\nclipsmith forge --trim-end 5.0 input.mp4 output.mp4\n```\n\n#### Rescaling\n\nRescale video duration and resolution:\n\n```bash\n# Speed up by factor (e.g. 2x faster)\nclipsmith forge --dur-scale 2.0 --no-audio input.mp4 output.mp4\n\n# Slow down by factor (e.g. 2x slower)\nclipsmith forge --dur-scale 0.5 --no-audio input.mp4 output.mp4\n\n# Rescale duration to specific value in seconds\nclipsmith forge --dur-target 60.0 --no-audio input.mp4 output.mp4\n\n# Rescale resolution by factor\nclipsmith forge --res-scale 0.5 input.mp4 output.mp4\n\n# Rescale resolution to specific value as WIDTH:HEIGHT\nclipsmith forge --res-target 480:270 input.mp4 output.mp4\n```\n\n<!-- TODO:\n### Clip playbooks\n-->\n\n### Input folder caching\n\nFor input folders with many videos, the process of scanning and validating input files can be time-consuming. In such cases, pass `--cache` to cache video metadata per folder. In case of multiple or interrupted invocations, ClipSmith will use this cache to quickly begin its work.\n\n## API\n\n### Context\n\nThe `Context` class provides the main interface for working with clips. It implements a task orchestration pattern using `doit`:\n\n```python\nfrom pathlib import Path\nfrom clipsmith import Context\n\n# Create a context\ncontext = Context()\n\n# Forge a new clip from input files\ncontext.forge(\"output.mp4\", [Path(\"input1.mp4\"), Path(\"input2.mp4\")])\n\n# Execute all pending operations\ncontext.doit()\n```\n\n### Clips\n\nClips can be manipulated using operation parameters for duration and resolution. A clip can be \"reforged\" into another clip, with `doit` managing orchestration of `ffmpeg` invocations.\n\n```python\nfrom clipsmith import (\n    Context, \n    OperationParams,\n    DurationParams,\n    ResolutionParams\n)\n\ncontext = Context()\ninputs = [Path(\"input1.mp4\"), Path(\"input2.mp4\")]\n\n# Trimming\nclip1 = context.forge(\n    \"output1.mp4\",\n    inputs,\n    OperationParams(\n        duration_params=DurationParams(\n            trim_start=1.0,  # Start at 1 second\n            trim_end=5.0,  # End at 5 seconds\n        ),\n    )\n)\n\n# Time scaling\nclip2 = context.forge(\n    \"output2.mp4\",\n    inputs,\n    OperationParams(\n        duration_params=DurationParams(\n            scale=2.0,  # Speed up by 2x\n        ),\n        audio=False\n    )\n)\n\n# Resolution scaling\nclip3 = context.forge(\n    \"output3.mp4\", \n    inputs,\n    OperationParams(\n        resolution_params=ResolutionParams(\n            target=(480, 270)  # Scale to specific resolution\n        )\n    )\n)\n\n# Chain operations by reforging trimmed clip\nclip4 = clip1.reforge(\n    \"output4.mp4\",\n    OperationParams(\n        resolution_params=ResolutionParams(\n            scale=0.5  # Scale resolution by factor\n        )\n    )\n)\n\n# Execute all operations\ncontext.doit()\n```\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Utility to work with video clips, especially suited for creating timelapses from dashcam footage",
    "version": "0.1.4",
    "project_urls": {
        "Homepage": "https://github.com/mm21/clipsmith"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "478ea0453078c5b4971232757327fa47279f3cb6678de0ea4803fc3bccee51aa",
                "md5": "0034fcd770e2f5d25952d07305024ba3",
                "sha256": "0e71db31612063d991ef53c06503d2b5a7349a7f3fda5533f70fd59336378ef6"
            },
            "downloads": -1,
            "filename": "clipsmith-0.1.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "0034fcd770e2f5d25952d07305024ba3",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.14,>=3.11",
            "size": 16819,
            "upload_time": "2024-12-17T05:10:30",
            "upload_time_iso_8601": "2024-12-17T05:10:30.138861Z",
            "url": "https://files.pythonhosted.org/packages/47/8e/a0453078c5b4971232757327fa47279f3cb6678de0ea4803fc3bccee51aa/clipsmith-0.1.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "51e512a104c13457f69a0a6fa2e6bbcfde1e4f6b06fb8c07c409e4b7d5309719",
                "md5": "419179d5eec3908536b2ef2b14417064",
                "sha256": "b6e7d7091ceb1bf3d0fe5a9b6dc13133dcb4448c6bd960f080dbab38cc172102"
            },
            "downloads": -1,
            "filename": "clipsmith-0.1.4.tar.gz",
            "has_sig": false,
            "md5_digest": "419179d5eec3908536b2ef2b14417064",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.14,>=3.11",
            "size": 15135,
            "upload_time": "2024-12-17T05:10:33",
            "upload_time_iso_8601": "2024-12-17T05:10:33.471321Z",
            "url": "https://files.pythonhosted.org/packages/51/e5/12a104c13457f69a0a6fa2e6bbcfde1e4f6b06fb8c07c409e4b7d5309719/clipsmith-0.1.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-17 05:10:33",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "mm21",
    "github_project": "clipsmith",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "tox": true,
    "lcname": "clipsmith"
}
        
Elapsed time: 1.32601s