asciify-video 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.
- asciify_video-0.1.0/.readthedocs.yaml +19 -0
- asciify_video-0.1.0/CHANGELOG.md +45 -0
- asciify_video-0.1.0/LICENSE +21 -0
- asciify_video-0.1.0/MANIFEST.in +5 -0
- asciify_video-0.1.0/PKG-INFO +239 -0
- asciify_video-0.1.0/README.md +190 -0
- asciify_video-0.1.0/docs/Makefile +19 -0
- asciify_video-0.1.0/docs/api/cli.rst +4 -0
- asciify_video-0.1.0/docs/api/config.rst +4 -0
- asciify_video-0.1.0/docs/api/constants.rst +4 -0
- asciify_video-0.1.0/docs/api/ffmpeg.rst +4 -0
- asciify_video-0.1.0/docs/api/fonts.rst +4 -0
- asciify_video-0.1.0/docs/api/index.rst +24 -0
- asciify_video-0.1.0/docs/api/keying.rst +4 -0
- asciify_video-0.1.0/docs/api/pipeline.rst +4 -0
- asciify_video-0.1.0/docs/api/render.rst +4 -0
- asciify_video-0.1.0/docs/api/reuse.rst +4 -0
- asciify_video-0.1.0/docs/api/state.rst +4 -0
- asciify_video-0.1.0/docs/changelog.md +2 -0
- asciify_video-0.1.0/docs/conf.py +59 -0
- asciify_video-0.1.0/docs/design/architecture.md +100 -0
- asciify_video-0.1.0/docs/design/decisions.md +81 -0
- asciify_video-0.1.0/docs/design/pipeline.md +101 -0
- asciify_video-0.1.0/docs/design/rendering.md +99 -0
- asciify_video-0.1.0/docs/design/state-file.md +90 -0
- asciify_video-0.1.0/docs/index.rst +51 -0
- asciify_video-0.1.0/docs/user/appearance.md +115 -0
- asciify_video-0.1.0/docs/user/cli-reference.rst +40 -0
- asciify_video-0.1.0/docs/user/installation.md +82 -0
- asciify_video-0.1.0/docs/user/python-usage.md +85 -0
- asciify_video-0.1.0/docs/user/quickstart.md +103 -0
- asciify_video-0.1.0/docs/user/reuse-workflow.md +102 -0
- asciify_video-0.1.0/docs/user/troubleshooting.md +93 -0
- asciify_video-0.1.0/pyproject.toml +109 -0
- asciify_video-0.1.0/setup.cfg +4 -0
- asciify_video-0.1.0/src/asciify_video/__init__.py +21 -0
- asciify_video-0.1.0/src/asciify_video/__main__.py +10 -0
- asciify_video-0.1.0/src/asciify_video/cli.py +456 -0
- asciify_video-0.1.0/src/asciify_video/config.py +141 -0
- asciify_video-0.1.0/src/asciify_video/constants.py +59 -0
- asciify_video-0.1.0/src/asciify_video/ffmpeg.py +322 -0
- asciify_video-0.1.0/src/asciify_video/fonts.py +66 -0
- asciify_video-0.1.0/src/asciify_video/keying.py +191 -0
- asciify_video-0.1.0/src/asciify_video/pipeline.py +518 -0
- asciify_video-0.1.0/src/asciify_video/py.typed +0 -0
- asciify_video-0.1.0/src/asciify_video/render.py +503 -0
- asciify_video-0.1.0/src/asciify_video/reuse.py +144 -0
- asciify_video-0.1.0/src/asciify_video/state.py +201 -0
- asciify_video-0.1.0/src/asciify_video.egg-info/PKG-INFO +239 -0
- asciify_video-0.1.0/src/asciify_video.egg-info/SOURCES.txt +70 -0
- asciify_video-0.1.0/src/asciify_video.egg-info/dependency_links.txt +1 -0
- asciify_video-0.1.0/src/asciify_video.egg-info/entry_points.txt +2 -0
- asciify_video-0.1.0/src/asciify_video.egg-info/requires.txt +22 -0
- asciify_video-0.1.0/src/asciify_video.egg-info/top_level.txt +1 -0
- asciify_video-0.1.0/tests/__init__.py +0 -0
- asciify_video-0.1.0/tests/conftest.py +69 -0
- asciify_video-0.1.0/tests/helpers.py +138 -0
- asciify_video-0.1.0/tests/integration/__init__.py +0 -0
- asciify_video-0.1.0/tests/integration/test_ffmpeg_pipeline.py +90 -0
- asciify_video-0.1.0/tests/integration/test_real_artifacts.py +47 -0
- asciify_video-0.1.0/tests/unit/__init__.py +0 -0
- asciify_video-0.1.0/tests/unit/test_architecture.py +116 -0
- asciify_video-0.1.0/tests/unit/test_cli.py +306 -0
- asciify_video-0.1.0/tests/unit/test_config.py +39 -0
- asciify_video-0.1.0/tests/unit/test_constants.py +17 -0
- asciify_video-0.1.0/tests/unit/test_ffmpeg.py +247 -0
- asciify_video-0.1.0/tests/unit/test_fonts.py +35 -0
- asciify_video-0.1.0/tests/unit/test_keying.py +139 -0
- asciify_video-0.1.0/tests/unit/test_pipeline.py +347 -0
- asciify_video-0.1.0/tests/unit/test_render.py +236 -0
- asciify_video-0.1.0/tests/unit/test_reuse.py +71 -0
- asciify_video-0.1.0/tests/unit/test_state.py +94 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Read the Docs build configuration
|
|
2
|
+
# https://docs.readthedocs.io/en/stable/config-file/v2.html
|
|
3
|
+
version: 2
|
|
4
|
+
|
|
5
|
+
build:
|
|
6
|
+
os: ubuntu-24.04
|
|
7
|
+
tools:
|
|
8
|
+
python: "3.12"
|
|
9
|
+
|
|
10
|
+
sphinx:
|
|
11
|
+
configuration: docs/conf.py
|
|
12
|
+
fail_on_warning: true
|
|
13
|
+
|
|
14
|
+
python:
|
|
15
|
+
install:
|
|
16
|
+
- method: pip
|
|
17
|
+
path: .
|
|
18
|
+
extra_requirements:
|
|
19
|
+
- docs
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.0]
|
|
8
|
+
|
|
9
|
+
First packaged release. Earlier versions were a pair of standalone scripts
|
|
10
|
+
(`asciify.py` and `asciify_cli.py`).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Installable package `asciify-video` with an `asciify_video` console command
|
|
15
|
+
(also `python -m asciify_video`), replacing the `./asciify.py` script.
|
|
16
|
+
- Public Python API: `asciify_video.ConversionConfig` and
|
|
17
|
+
`asciify_video.run_conversion`, fully type-annotated (`py.typed`).
|
|
18
|
+
- `--scale-divisor` now takes effect: when `--ascii-width` is not given the
|
|
19
|
+
grid width is the output width divided by the divisor.
|
|
20
|
+
- Sphinx documentation: user guide, design notes and API reference.
|
|
21
|
+
- Continuous integration and tag-driven PyPI releases on GitHub Actions.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **Default grid width** is now output width / 8 instead of a fixed 160
|
|
26
|
+
columns. This is unchanged for 1280-wide output and gives 240 columns for
|
|
27
|
+
1920-wide output. Pass `--ascii-width 160` for the old grid.
|
|
28
|
+
- A `--reuse` run takes the source size and frame rate from the state file
|
|
29
|
+
instead of probing the input again. The frame rate is the one the kept
|
|
30
|
+
frames were extracted at.
|
|
31
|
+
- The directory being reused stays recorded in the state file, so several
|
|
32
|
+
`--reuse` runs can follow one `--keep-temp` run without `--reuse-dir`.
|
|
33
|
+
- Python 3.10 or newer is required.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Transparent (WebM) output failed with "Unrecognized option 'realtime'".
|
|
38
|
+
The VP9 command now uses `-deadline realtime` with `-row-mt 1`.
|
|
39
|
+
- The final encode failed on Windows, whose ffmpeg builds lack
|
|
40
|
+
`-pattern_type glob`. Frames are now read as a numbered sequence.
|
|
41
|
+
- `--reuse` reported ignored flags from `sys.argv` rather than from the
|
|
42
|
+
arguments passed to `main()`.
|
|
43
|
+
- `--reuse` with an incomplete directory printed a traceback instead of an
|
|
44
|
+
error message.
|
|
45
|
+
- Non-positive `--ascii-width` and `--scale-divisor` values are rejected.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mark Bools
|
|
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,239 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: asciify-video
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Convert green-screen video into ASCII-art video
|
|
5
|
+
Author: Mark Bools
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ulenarofmondarth/asciify-video
|
|
8
|
+
Project-URL: Repository, https://github.com/ulenarofmondarth/asciify-video
|
|
9
|
+
Project-URL: Documentation, https://asciify-video.readthedocs.io/
|
|
10
|
+
Project-URL: Issues, https://github.com/ulenarofmondarth/asciify-video/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/ulenarofmondarth/asciify-video/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: ascii,ascii-art,video,chromakey,ffmpeg
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Multimedia :: Video :: Conversion
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: Pillow<13,>=9.2
|
|
30
|
+
Requires-Dist: tqdm<5,>=4.60
|
|
31
|
+
Provides-Extra: test
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0; extra == "test"
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
36
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
37
|
+
Requires-Dist: myst-parser>=3.0; extra == "docs"
|
|
38
|
+
Requires-Dist: sphinx-argparse>=0.5; extra == "docs"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: asciify-video[docs,test]; extra == "dev"
|
|
41
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
43
|
+
Requires-Dist: pip-audit>=2.7; extra == "dev"
|
|
44
|
+
Requires-Dist: pre-commit>=3.5; extra == "dev"
|
|
45
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
46
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
47
|
+
Requires-Dist: types-tqdm; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# asciify-video
|
|
51
|
+
|
|
52
|
+
Convert green-screen video into ASCII-art video, with consistent character geometry, multi-core rendering, optional transparency, and fast iteration through artifact reuse.
|
|
53
|
+
|
|
54
|
+
[](https://github.com/ulenarofmondarth/asciify-video/actions/workflows/ci.yml)
|
|
55
|
+

|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install asciify-video
|
|
62
|
+
asciify_video input.mp4 output.mp4
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`ffmpeg` and `ffprobe` must already be installed and on your `PATH`.
|
|
66
|
+
|
|
67
|
+
With no options this detects the green key colour, keys the subject over black, converts every frame to ASCII, and writes a video with the same size, frame rate and audio as the input, drawn in terminal green.
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
- **Automatic green detection**, or a key colour you supply (`--key-color`); `--no-key` for footage that is already keyed
|
|
72
|
+
- **Consistent geometry**: clips of different source resolutions rendered at one `--target-resolution` have identical character cells
|
|
73
|
+
- **Parallel conversion and rendering** across CPU cores (`--jobs`)
|
|
74
|
+
- **Colour control**: any character colour (`--font-color`), or colours sampled from the source (`--colorized`)
|
|
75
|
+
- **Backgrounds**: any colour, any opacity, including fully transparent WebM for overlays (`--background-color`, `--background-alpha`)
|
|
76
|
+
- **Steady brightness** from a clip-wide histogram stretch (`--hist-stretch`)
|
|
77
|
+
- **Black masking** to leave dark areas empty (`--leave-black-empty`)
|
|
78
|
+
- **Artifact reuse**: keep a run's intermediates and re-render or re-encode without repeating the slow stages (`--keep-temp`, `--reuse`)
|
|
79
|
+
- **Run reporting**: every run is recorded and can be inspected with `--report`
|
|
80
|
+
- **Python API**, fully type-annotated, for use without the command line
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
Requirements:
|
|
85
|
+
|
|
86
|
+
- Python 3.10 or newer
|
|
87
|
+
- FFmpeg (`ffmpeg` and `ffprobe`), with `libx264`; transparent output also needs `libvpx-vp9`
|
|
88
|
+
|
|
89
|
+
Linux is the tested platform. Windows is untested but probably supported.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Debian / Ubuntu
|
|
93
|
+
sudo apt-get install ffmpeg
|
|
94
|
+
|
|
95
|
+
# macOS (Homebrew)
|
|
96
|
+
brew install ffmpeg
|
|
97
|
+
|
|
98
|
+
# Windows
|
|
99
|
+
winget install ffmpeg
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Install from PyPI:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install asciify-video
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Or from source:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
git clone https://github.com/ulenarofmondarth/asciify-video.git
|
|
112
|
+
cd asciify-video
|
|
113
|
+
pip install .
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This provides the `asciify_video` command; `python -m asciify_video` is equivalent.
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
Everything is configured with command-line options; there is no configuration file. The most used options:
|
|
121
|
+
|
|
122
|
+
| Option | Default | Purpose |
|
|
123
|
+
|--------|---------|---------|
|
|
124
|
+
| `--key-color` | detected | Key colour as hex, e.g. `0x00B140` |
|
|
125
|
+
| `--no-key` | off | Skip chromakeying |
|
|
126
|
+
| `--scale-divisor` | 8 | Grid width = output width / divisor |
|
|
127
|
+
| `--ascii-width` | from divisor | Grid width in characters; overrides `--scale-divisor` |
|
|
128
|
+
| `--target-resolution` | source size | Output size, e.g. `1920x1080` |
|
|
129
|
+
| `--font-color` | `#64FF64` | Character colour (not with `--colorized`) |
|
|
130
|
+
| `--colorized` | off | Take character colours from the source |
|
|
131
|
+
| `--background-color` | `#000000` | Background colour |
|
|
132
|
+
| `--background-alpha` | 1 | Background opacity, 0-1; below 1 produces WebM |
|
|
133
|
+
| `--hist-stretch` | off | Clip-wide histogram stretch |
|
|
134
|
+
| `--jobs` | cores - 1 | Worker processes |
|
|
135
|
+
| `--keep-temp` / `--reuse` | off | Keep and reuse intermediate files |
|
|
136
|
+
|
|
137
|
+
`asciify_video --help` lists every option, as does the [CLI reference](https://asciify-video.readthedocs.io/en/latest/user/cli-reference.html).
|
|
138
|
+
|
|
139
|
+
Environment variables:
|
|
140
|
+
|
|
141
|
+
| Variable | Purpose |
|
|
142
|
+
|----------|---------|
|
|
143
|
+
| `ASCII_VIDEO_TEMP` | Directory to reuse with `--reuse` when `--reuse-dir` is not given |
|
|
144
|
+
| `XDG_CACHE_HOME` | Base directory for the run-state file, `$XDG_CACHE_HOME/asciify/state.json` (default `~/.cache`) |
|
|
145
|
+
|
|
146
|
+
## Usage
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Footage that is already keyed
|
|
150
|
+
asciify_video input.mp4 output.mp4 --no-key
|
|
151
|
+
|
|
152
|
+
# Characters in the source colours, with steadier brightness
|
|
153
|
+
asciify_video input.mp4 output.mp4 --colorized --hist-stretch
|
|
154
|
+
|
|
155
|
+
# Amber characters on a dark blue background
|
|
156
|
+
asciify_video input.mp4 output.mp4 --font-color '#FFB000' --background-color '#101820'
|
|
157
|
+
|
|
158
|
+
# Transparent background for overlaying (written as WebM)
|
|
159
|
+
asciify_video input.mp4 overlay.webm --background-alpha 0
|
|
160
|
+
|
|
161
|
+
# Two clips with matching character size
|
|
162
|
+
asciify_video clip_a.mp4 a.mp4 --target-resolution 1920x1080
|
|
163
|
+
asciify_video clip_b.mp4 b.mp4 --target-resolution 1920x1080
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Iterate without repeating the slow stages:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
asciify_video input.mp4 v1.mp4 --keep-temp # full run, keep intermediates
|
|
170
|
+
asciify_video input.mp4 v2.mp4 --reuse ascii --font-color '#FFB000' # re-render only
|
|
171
|
+
asciify_video input.mp4 v3.mp4 --reuse final --fps 30 # re-encode only
|
|
172
|
+
asciify_video --report # describe the last run
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
From Python:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from pathlib import Path
|
|
179
|
+
|
|
180
|
+
from asciify_video import ConversionConfig, run_conversion
|
|
181
|
+
|
|
182
|
+
run_conversion(ConversionConfig(
|
|
183
|
+
input=Path("input.mp4"),
|
|
184
|
+
output=Path("output.mp4"),
|
|
185
|
+
colorized=True,
|
|
186
|
+
))
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The [user guide](https://asciify-video.readthedocs.io/) covers appearance options, transparency, the reuse workflow and troubleshooting in full.
|
|
190
|
+
|
|
191
|
+
> **Changed from the original script.** The default grid is now output width / 8 rather than a fixed 160 columns: still 160 for 1280-wide output, 240 for 1920-wide. Pass `--ascii-width 160` for the old grid. The command is now `asciify_video` rather than `./asciify.py`.
|
|
192
|
+
|
|
193
|
+
## API Reference
|
|
194
|
+
|
|
195
|
+
Full API documentation, generated from the source, is at <https://asciify-video.readthedocs.io/en/latest/api/>. The same site has design notes on the pipeline, rendering and the state file.
|
|
196
|
+
|
|
197
|
+
To build the documentation locally:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
pip install -e ".[docs]"
|
|
201
|
+
make -C docs html
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Testing
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
pip install -e ".[dev]"
|
|
208
|
+
pytest
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The suite fails below 90% coverage. Unit tests fake FFmpeg and need nothing installed; the integration tests convert a generated clip with the real `ffmpeg` and are skipped when it is absent.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
ruff check . # lint, including docstring rules
|
|
215
|
+
mypy # strict type check
|
|
216
|
+
pre-commit run --all-files # everything the commit hooks run
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Contributing
|
|
220
|
+
|
|
221
|
+
Issues and pull requests are welcome at <https://github.com/ulenarofmondarth/asciify-video>.
|
|
222
|
+
|
|
223
|
+
- Branch from `main` and use [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `docs:`, `refactor:`).
|
|
224
|
+
- Code is PEP 8 at 100 columns with type hints and Google-style docstrings on everything public; `ruff` and `mypy` (strict) must pass.
|
|
225
|
+
- Add tests for new behaviour and keep coverage at or above 90%.
|
|
226
|
+
- Keep the layers apart: argument parsing and validation belong in `asciify_video.cli`, video processing everywhere else. Tests enforce this.
|
|
227
|
+
- Run `pre-commit install` once to check each commit locally.
|
|
228
|
+
|
|
229
|
+
Releases are published to PyPI by pushing a `vX.Y.Z` tag that matches the version in `pyproject.toml`.
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT. See [LICENSE](https://github.com/ulenarofmondarth/asciify-video/blob/main/LICENSE).
|
|
234
|
+
|
|
235
|
+
The original concept was inspired by the `jp2a` command-line tool.
|
|
236
|
+
|
|
237
|
+
## Changelog
|
|
238
|
+
|
|
239
|
+
See [CHANGELOG.md](https://github.com/ulenarofmondarth/asciify-video/blob/main/CHANGELOG.md).
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# asciify-video
|
|
2
|
+
|
|
3
|
+
Convert green-screen video into ASCII-art video, with consistent character geometry, multi-core rendering, optional transparency, and fast iteration through artifact reuse.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/ulenarofmondarth/asciify-video/actions/workflows/ci.yml)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install asciify-video
|
|
13
|
+
asciify_video input.mp4 output.mp4
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`ffmpeg` and `ffprobe` must already be installed and on your `PATH`.
|
|
17
|
+
|
|
18
|
+
With no options this detects the green key colour, keys the subject over black, converts every frame to ASCII, and writes a video with the same size, frame rate and audio as the input, drawn in terminal green.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Automatic green detection**, or a key colour you supply (`--key-color`); `--no-key` for footage that is already keyed
|
|
23
|
+
- **Consistent geometry**: clips of different source resolutions rendered at one `--target-resolution` have identical character cells
|
|
24
|
+
- **Parallel conversion and rendering** across CPU cores (`--jobs`)
|
|
25
|
+
- **Colour control**: any character colour (`--font-color`), or colours sampled from the source (`--colorized`)
|
|
26
|
+
- **Backgrounds**: any colour, any opacity, including fully transparent WebM for overlays (`--background-color`, `--background-alpha`)
|
|
27
|
+
- **Steady brightness** from a clip-wide histogram stretch (`--hist-stretch`)
|
|
28
|
+
- **Black masking** to leave dark areas empty (`--leave-black-empty`)
|
|
29
|
+
- **Artifact reuse**: keep a run's intermediates and re-render or re-encode without repeating the slow stages (`--keep-temp`, `--reuse`)
|
|
30
|
+
- **Run reporting**: every run is recorded and can be inspected with `--report`
|
|
31
|
+
- **Python API**, fully type-annotated, for use without the command line
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Requirements:
|
|
36
|
+
|
|
37
|
+
- Python 3.10 or newer
|
|
38
|
+
- FFmpeg (`ffmpeg` and `ffprobe`), with `libx264`; transparent output also needs `libvpx-vp9`
|
|
39
|
+
|
|
40
|
+
Linux is the tested platform. Windows is untested but probably supported.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Debian / Ubuntu
|
|
44
|
+
sudo apt-get install ffmpeg
|
|
45
|
+
|
|
46
|
+
# macOS (Homebrew)
|
|
47
|
+
brew install ffmpeg
|
|
48
|
+
|
|
49
|
+
# Windows
|
|
50
|
+
winget install ffmpeg
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Install from PyPI:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install asciify-video
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or from source:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/ulenarofmondarth/asciify-video.git
|
|
63
|
+
cd asciify-video
|
|
64
|
+
pip install .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This provides the `asciify_video` command; `python -m asciify_video` is equivalent.
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
Everything is configured with command-line options; there is no configuration file. The most used options:
|
|
72
|
+
|
|
73
|
+
| Option | Default | Purpose |
|
|
74
|
+
|--------|---------|---------|
|
|
75
|
+
| `--key-color` | detected | Key colour as hex, e.g. `0x00B140` |
|
|
76
|
+
| `--no-key` | off | Skip chromakeying |
|
|
77
|
+
| `--scale-divisor` | 8 | Grid width = output width / divisor |
|
|
78
|
+
| `--ascii-width` | from divisor | Grid width in characters; overrides `--scale-divisor` |
|
|
79
|
+
| `--target-resolution` | source size | Output size, e.g. `1920x1080` |
|
|
80
|
+
| `--font-color` | `#64FF64` | Character colour (not with `--colorized`) |
|
|
81
|
+
| `--colorized` | off | Take character colours from the source |
|
|
82
|
+
| `--background-color` | `#000000` | Background colour |
|
|
83
|
+
| `--background-alpha` | 1 | Background opacity, 0-1; below 1 produces WebM |
|
|
84
|
+
| `--hist-stretch` | off | Clip-wide histogram stretch |
|
|
85
|
+
| `--jobs` | cores - 1 | Worker processes |
|
|
86
|
+
| `--keep-temp` / `--reuse` | off | Keep and reuse intermediate files |
|
|
87
|
+
|
|
88
|
+
`asciify_video --help` lists every option, as does the [CLI reference](https://asciify-video.readthedocs.io/en/latest/user/cli-reference.html).
|
|
89
|
+
|
|
90
|
+
Environment variables:
|
|
91
|
+
|
|
92
|
+
| Variable | Purpose |
|
|
93
|
+
|----------|---------|
|
|
94
|
+
| `ASCII_VIDEO_TEMP` | Directory to reuse with `--reuse` when `--reuse-dir` is not given |
|
|
95
|
+
| `XDG_CACHE_HOME` | Base directory for the run-state file, `$XDG_CACHE_HOME/asciify/state.json` (default `~/.cache`) |
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Footage that is already keyed
|
|
101
|
+
asciify_video input.mp4 output.mp4 --no-key
|
|
102
|
+
|
|
103
|
+
# Characters in the source colours, with steadier brightness
|
|
104
|
+
asciify_video input.mp4 output.mp4 --colorized --hist-stretch
|
|
105
|
+
|
|
106
|
+
# Amber characters on a dark blue background
|
|
107
|
+
asciify_video input.mp4 output.mp4 --font-color '#FFB000' --background-color '#101820'
|
|
108
|
+
|
|
109
|
+
# Transparent background for overlaying (written as WebM)
|
|
110
|
+
asciify_video input.mp4 overlay.webm --background-alpha 0
|
|
111
|
+
|
|
112
|
+
# Two clips with matching character size
|
|
113
|
+
asciify_video clip_a.mp4 a.mp4 --target-resolution 1920x1080
|
|
114
|
+
asciify_video clip_b.mp4 b.mp4 --target-resolution 1920x1080
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Iterate without repeating the slow stages:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
asciify_video input.mp4 v1.mp4 --keep-temp # full run, keep intermediates
|
|
121
|
+
asciify_video input.mp4 v2.mp4 --reuse ascii --font-color '#FFB000' # re-render only
|
|
122
|
+
asciify_video input.mp4 v3.mp4 --reuse final --fps 30 # re-encode only
|
|
123
|
+
asciify_video --report # describe the last run
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
From Python:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from pathlib import Path
|
|
130
|
+
|
|
131
|
+
from asciify_video import ConversionConfig, run_conversion
|
|
132
|
+
|
|
133
|
+
run_conversion(ConversionConfig(
|
|
134
|
+
input=Path("input.mp4"),
|
|
135
|
+
output=Path("output.mp4"),
|
|
136
|
+
colorized=True,
|
|
137
|
+
))
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The [user guide](https://asciify-video.readthedocs.io/) covers appearance options, transparency, the reuse workflow and troubleshooting in full.
|
|
141
|
+
|
|
142
|
+
> **Changed from the original script.** The default grid is now output width / 8 rather than a fixed 160 columns: still 160 for 1280-wide output, 240 for 1920-wide. Pass `--ascii-width 160` for the old grid. The command is now `asciify_video` rather than `./asciify.py`.
|
|
143
|
+
|
|
144
|
+
## API Reference
|
|
145
|
+
|
|
146
|
+
Full API documentation, generated from the source, is at <https://asciify-video.readthedocs.io/en/latest/api/>. The same site has design notes on the pipeline, rendering and the state file.
|
|
147
|
+
|
|
148
|
+
To build the documentation locally:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pip install -e ".[docs]"
|
|
152
|
+
make -C docs html
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Testing
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pip install -e ".[dev]"
|
|
159
|
+
pytest
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The suite fails below 90% coverage. Unit tests fake FFmpeg and need nothing installed; the integration tests convert a generated clip with the real `ffmpeg` and are skipped when it is absent.
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
ruff check . # lint, including docstring rules
|
|
166
|
+
mypy # strict type check
|
|
167
|
+
pre-commit run --all-files # everything the commit hooks run
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Contributing
|
|
171
|
+
|
|
172
|
+
Issues and pull requests are welcome at <https://github.com/ulenarofmondarth/asciify-video>.
|
|
173
|
+
|
|
174
|
+
- Branch from `main` and use [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `docs:`, `refactor:`).
|
|
175
|
+
- Code is PEP 8 at 100 columns with type hints and Google-style docstrings on everything public; `ruff` and `mypy` (strict) must pass.
|
|
176
|
+
- Add tests for new behaviour and keep coverage at or above 90%.
|
|
177
|
+
- Keep the layers apart: argument parsing and validation belong in `asciify_video.cli`, video processing everywhere else. Tests enforce this.
|
|
178
|
+
- Run `pre-commit install` once to check each commit locally.
|
|
179
|
+
|
|
180
|
+
Releases are published to PyPI by pushing a `vX.Y.Z` tag that matches the version in `pyproject.toml`.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT. See [LICENSE](https://github.com/ulenarofmondarth/asciify-video/blob/main/LICENSE).
|
|
185
|
+
|
|
186
|
+
The original concept was inspired by the `jp2a` command-line tool.
|
|
187
|
+
|
|
188
|
+
## Changelog
|
|
189
|
+
|
|
190
|
+
See [CHANGELOG.md](https://github.com/ulenarofmondarth/asciify-video/blob/main/CHANGELOG.md).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Minimal Sphinx makefile: `make html` builds into _build/html.
|
|
2
|
+
SPHINXOPTS ?= -W --keep-going
|
|
3
|
+
SPHINXBUILD ?= sphinx-build
|
|
4
|
+
SOURCEDIR = .
|
|
5
|
+
BUILDDIR = _build
|
|
6
|
+
|
|
7
|
+
.PHONY: help html clean linkcheck
|
|
8
|
+
|
|
9
|
+
help:
|
|
10
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)
|
|
11
|
+
|
|
12
|
+
html:
|
|
13
|
+
@$(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS)
|
|
14
|
+
|
|
15
|
+
linkcheck:
|
|
16
|
+
@$(SPHINXBUILD) -b linkcheck "$(SOURCEDIR)" "$(BUILDDIR)/linkcheck" $(SPHINXOPTS)
|
|
17
|
+
|
|
18
|
+
clean:
|
|
19
|
+
rm -rf "$(BUILDDIR)"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
API reference
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
The supported programmatic entry points are re-exported from the top-level
|
|
5
|
+
package: build a :class:`~asciify_video.config.ConversionConfig` and pass it
|
|
6
|
+
to :func:`~asciify_video.pipeline.run_conversion`. The remaining modules are
|
|
7
|
+
documented for contributors and for callers who want a single stage.
|
|
8
|
+
|
|
9
|
+
.. automodule:: asciify_video
|
|
10
|
+
:no-members:
|
|
11
|
+
|
|
12
|
+
.. toctree::
|
|
13
|
+
:maxdepth: 1
|
|
14
|
+
|
|
15
|
+
config
|
|
16
|
+
pipeline
|
|
17
|
+
render
|
|
18
|
+
keying
|
|
19
|
+
ffmpeg
|
|
20
|
+
fonts
|
|
21
|
+
reuse
|
|
22
|
+
state
|
|
23
|
+
constants
|
|
24
|
+
cli
|