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.
Files changed (72) hide show
  1. asciify_video-0.1.0/.readthedocs.yaml +19 -0
  2. asciify_video-0.1.0/CHANGELOG.md +45 -0
  3. asciify_video-0.1.0/LICENSE +21 -0
  4. asciify_video-0.1.0/MANIFEST.in +5 -0
  5. asciify_video-0.1.0/PKG-INFO +239 -0
  6. asciify_video-0.1.0/README.md +190 -0
  7. asciify_video-0.1.0/docs/Makefile +19 -0
  8. asciify_video-0.1.0/docs/api/cli.rst +4 -0
  9. asciify_video-0.1.0/docs/api/config.rst +4 -0
  10. asciify_video-0.1.0/docs/api/constants.rst +4 -0
  11. asciify_video-0.1.0/docs/api/ffmpeg.rst +4 -0
  12. asciify_video-0.1.0/docs/api/fonts.rst +4 -0
  13. asciify_video-0.1.0/docs/api/index.rst +24 -0
  14. asciify_video-0.1.0/docs/api/keying.rst +4 -0
  15. asciify_video-0.1.0/docs/api/pipeline.rst +4 -0
  16. asciify_video-0.1.0/docs/api/render.rst +4 -0
  17. asciify_video-0.1.0/docs/api/reuse.rst +4 -0
  18. asciify_video-0.1.0/docs/api/state.rst +4 -0
  19. asciify_video-0.1.0/docs/changelog.md +2 -0
  20. asciify_video-0.1.0/docs/conf.py +59 -0
  21. asciify_video-0.1.0/docs/design/architecture.md +100 -0
  22. asciify_video-0.1.0/docs/design/decisions.md +81 -0
  23. asciify_video-0.1.0/docs/design/pipeline.md +101 -0
  24. asciify_video-0.1.0/docs/design/rendering.md +99 -0
  25. asciify_video-0.1.0/docs/design/state-file.md +90 -0
  26. asciify_video-0.1.0/docs/index.rst +51 -0
  27. asciify_video-0.1.0/docs/user/appearance.md +115 -0
  28. asciify_video-0.1.0/docs/user/cli-reference.rst +40 -0
  29. asciify_video-0.1.0/docs/user/installation.md +82 -0
  30. asciify_video-0.1.0/docs/user/python-usage.md +85 -0
  31. asciify_video-0.1.0/docs/user/quickstart.md +103 -0
  32. asciify_video-0.1.0/docs/user/reuse-workflow.md +102 -0
  33. asciify_video-0.1.0/docs/user/troubleshooting.md +93 -0
  34. asciify_video-0.1.0/pyproject.toml +109 -0
  35. asciify_video-0.1.0/setup.cfg +4 -0
  36. asciify_video-0.1.0/src/asciify_video/__init__.py +21 -0
  37. asciify_video-0.1.0/src/asciify_video/__main__.py +10 -0
  38. asciify_video-0.1.0/src/asciify_video/cli.py +456 -0
  39. asciify_video-0.1.0/src/asciify_video/config.py +141 -0
  40. asciify_video-0.1.0/src/asciify_video/constants.py +59 -0
  41. asciify_video-0.1.0/src/asciify_video/ffmpeg.py +322 -0
  42. asciify_video-0.1.0/src/asciify_video/fonts.py +66 -0
  43. asciify_video-0.1.0/src/asciify_video/keying.py +191 -0
  44. asciify_video-0.1.0/src/asciify_video/pipeline.py +518 -0
  45. asciify_video-0.1.0/src/asciify_video/py.typed +0 -0
  46. asciify_video-0.1.0/src/asciify_video/render.py +503 -0
  47. asciify_video-0.1.0/src/asciify_video/reuse.py +144 -0
  48. asciify_video-0.1.0/src/asciify_video/state.py +201 -0
  49. asciify_video-0.1.0/src/asciify_video.egg-info/PKG-INFO +239 -0
  50. asciify_video-0.1.0/src/asciify_video.egg-info/SOURCES.txt +70 -0
  51. asciify_video-0.1.0/src/asciify_video.egg-info/dependency_links.txt +1 -0
  52. asciify_video-0.1.0/src/asciify_video.egg-info/entry_points.txt +2 -0
  53. asciify_video-0.1.0/src/asciify_video.egg-info/requires.txt +22 -0
  54. asciify_video-0.1.0/src/asciify_video.egg-info/top_level.txt +1 -0
  55. asciify_video-0.1.0/tests/__init__.py +0 -0
  56. asciify_video-0.1.0/tests/conftest.py +69 -0
  57. asciify_video-0.1.0/tests/helpers.py +138 -0
  58. asciify_video-0.1.0/tests/integration/__init__.py +0 -0
  59. asciify_video-0.1.0/tests/integration/test_ffmpeg_pipeline.py +90 -0
  60. asciify_video-0.1.0/tests/integration/test_real_artifacts.py +47 -0
  61. asciify_video-0.1.0/tests/unit/__init__.py +0 -0
  62. asciify_video-0.1.0/tests/unit/test_architecture.py +116 -0
  63. asciify_video-0.1.0/tests/unit/test_cli.py +306 -0
  64. asciify_video-0.1.0/tests/unit/test_config.py +39 -0
  65. asciify_video-0.1.0/tests/unit/test_constants.py +17 -0
  66. asciify_video-0.1.0/tests/unit/test_ffmpeg.py +247 -0
  67. asciify_video-0.1.0/tests/unit/test_fonts.py +35 -0
  68. asciify_video-0.1.0/tests/unit/test_keying.py +139 -0
  69. asciify_video-0.1.0/tests/unit/test_pipeline.py +347 -0
  70. asciify_video-0.1.0/tests/unit/test_render.py +236 -0
  71. asciify_video-0.1.0/tests/unit/test_reuse.py +71 -0
  72. 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,5 @@
1
+ include CHANGELOG.md
2
+ include .readthedocs.yaml
3
+ recursive-include docs *.py *.rst *.md Makefile
4
+ recursive-include tests *.py
5
+ prune docs/_build
@@ -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
+ [![CI](https://github.com/ulenarofmondarth/asciify-video/actions/workflows/ci.yml/badge.svg)](https://github.com/ulenarofmondarth/asciify-video/actions/workflows/ci.yml)
55
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)
56
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
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
+ [![CI](https://github.com/ulenarofmondarth/asciify-video/actions/workflows/ci.yml/badge.svg)](https://github.com/ulenarofmondarth/asciify-video/actions/workflows/ci.yml)
6
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)
7
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
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,4 @@
1
+ asciify_video.cli
2
+ =================
3
+
4
+ .. automodule:: asciify_video.cli
@@ -0,0 +1,4 @@
1
+ asciify_video.config
2
+ ====================
3
+
4
+ .. automodule:: asciify_video.config
@@ -0,0 +1,4 @@
1
+ asciify_video.constants
2
+ =======================
3
+
4
+ .. automodule:: asciify_video.constants
@@ -0,0 +1,4 @@
1
+ asciify_video.ffmpeg
2
+ ====================
3
+
4
+ .. automodule:: asciify_video.ffmpeg
@@ -0,0 +1,4 @@
1
+ asciify_video.fonts
2
+ ===================
3
+
4
+ .. automodule:: asciify_video.fonts
@@ -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
@@ -0,0 +1,4 @@
1
+ asciify_video.keying
2
+ ====================
3
+
4
+ .. automodule:: asciify_video.keying
@@ -0,0 +1,4 @@
1
+ asciify_video.pipeline
2
+ ======================
3
+
4
+ .. automodule:: asciify_video.pipeline
@@ -0,0 +1,4 @@
1
+ asciify_video.render
2
+ ====================
3
+
4
+ .. automodule:: asciify_video.render
@@ -0,0 +1,4 @@
1
+ asciify_video.reuse
2
+ ===================
3
+
4
+ .. automodule:: asciify_video.reuse
@@ -0,0 +1,4 @@
1
+ asciify_video.state
2
+ ===================
3
+
4
+ .. automodule:: asciify_video.state
@@ -0,0 +1,2 @@
1
+ ```{include} ../CHANGELOG.md
2
+ ```