ascii-motion 0.2.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Carlos Andres Huete
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,400 @@
1
+ Metadata-Version: 2.4
2
+ Name: ascii-motion
3
+ Version: 0.2.0
4
+ Summary: Reproduce videos como animaciones ASCII en terminales ANSI.
5
+ Author: Carlos Andres Huete
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/c4rl0s04/ascii-motion
8
+ Project-URL: Repository, https://github.com/c4rl0s04/ascii-motion
9
+ Project-URL: Issues, https://github.com/c4rl0s04/ascii-motion/issues
10
+ Project-URL: Landing, https://c4rl0s04.github.io/ascii-motion/
11
+ Keywords: ascii,terminal,opencv,video,cli
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Multimedia :: Video
21
+ Classifier: Topic :: Terminals
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: numpy>=1.26.0
26
+ Requires-Dist: opencv-python>=4.9.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2.0; extra == "dev"
29
+ Requires-Dist: Pillow>=10.0.0; extra == "dev"
30
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
32
+ Requires-Dist: twine>=5.0.0; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # ascii-motion
36
+
37
+ [![Deploy landing page](https://github.com/c4rl0s04/ascii-motion/actions/workflows/pages.yml/badge.svg)](https://github.com/c4rl0s04/ascii-motion/actions/workflows/pages.yml)
38
+ [![CI](https://github.com/c4rl0s04/ascii-motion/actions/workflows/ci.yml/badge.svg)](https://github.com/c4rl0s04/ascii-motion/actions/workflows/ci.yml)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-8dffb1.svg)](LICENSE)
40
+ [![Landing](https://img.shields.io/badge/site-GitHub%20Pages-6ee7f2.svg)](https://c4rl0s04.github.io/ascii-motion/)
41
+
42
+ `ascii-motion` plays video files as real-time ASCII animations directly inside an ANSI-compatible terminal. The pipeline uses OpenCV for frame capture, NumPy for matrix-based processing, and ANSI escape sequences for flicker-resistant rendering without clearing the whole screen on every frame.
43
+
44
+ ![ascii-motion terminal demo](docs/demo/ascii-motion-demo.gif)
45
+
46
+ ## Landing Page
47
+
48
+ The static promotional site lives in [`site/`](site/) and is published with GitHub Pages:
49
+
50
+ https://c4rl0s04.github.io/ascii-motion/
51
+
52
+ To view it locally:
53
+
54
+ ```bash
55
+ python3 -m http.server 8765 --directory site
56
+ ```
57
+
58
+ Then open `http://127.0.0.1:8765/` in your browser.
59
+
60
+ ![ascii-motion landing desktop](docs/screenshots/landing-desktop.png)
61
+
62
+ Mobile view:
63
+
64
+ ![ascii-motion landing mobile](docs/screenshots/landing-mobile.png)
65
+
66
+ ## Installation
67
+
68
+ PyPI publishing is prepared, but the first publish requires PyPI trusted publisher setup. After the `Publish package` workflow succeeds, install from PyPI:
69
+
70
+ ```bash
71
+ pip install ascii-motion
72
+ ```
73
+
74
+ For isolated CLI usage after PyPI publishing succeeds, `pipx` is recommended:
75
+
76
+ ```bash
77
+ pipx install ascii-motion
78
+ ```
79
+
80
+ Install with Homebrew on macOS:
81
+
82
+ ```bash
83
+ brew install c4rl0s04/ascii-motion/ascii-motion
84
+ ```
85
+
86
+ Or tap first:
87
+
88
+ ```bash
89
+ brew tap c4rl0s04/ascii-motion
90
+ brew install ascii-motion
91
+ ```
92
+
93
+ Local development install:
94
+
95
+ ```bash
96
+ python3 -m venv .venv
97
+ source .venv/bin/activate
98
+ pip install -e ".[dev]"
99
+ ```
100
+
101
+ Local usage-only install:
102
+
103
+ ```bash
104
+ pip install .
105
+ ```
106
+
107
+ ## Basic Usage
108
+
109
+ ```bash
110
+ ascii-motion video.mp4
111
+ ascii-motion video.mp4 --width 120
112
+ ascii-motion video.mp4 --width 160 --fps 60
113
+ ascii-motion video.mp4 --loop
114
+ ascii-motion 0 --width 100
115
+ ```
116
+
117
+ It also works as a Python module:
118
+
119
+ ```bash
120
+ python -m ascii_motion video.mp4
121
+ ```
122
+
123
+ ## Options
124
+
125
+ ### Source And Sizing
126
+
127
+ - `source`: video path or camera index. Use `0` for the default webcam.
128
+ - `-w, --width COLUMNS`: target ASCII width. If omitted, the current terminal width is used.
129
+ - `--height ROWS`: target ASCII height. If omitted, height is calculated from the source aspect ratio.
130
+ - `--fit-terminal`: fit output to the current terminal size. When HUD/progress are visible, rows are reserved for those lines.
131
+
132
+ Terminal characters are usually taller than they are wide, so automatic height uses a `0.5` character aspect correction. With no explicit `--width` or `--height`, vertical videos are also capped to the current terminal height so the default playback fits the visible terminal.
133
+
134
+ Examples:
135
+
136
+ ```bash
137
+ ascii-motion video.mp4 --width 120
138
+ ascii-motion video.mp4 --width 120 --height 40
139
+ ascii-motion video.mp4 --fit-terminal
140
+ ascii-motion 0 --width 100
141
+ ```
142
+
143
+ ### Playback Timing
144
+
145
+ - `--fps FPS`: override source FPS. If omitted, the video FPS is used.
146
+ - `--start SECONDS`: start playback from a timestamp.
147
+ - `--duration SECONDS`: stop playback after the given duration.
148
+ - `--loop`: restart the source when it reaches the end.
149
+ - `--real-time`: explicitly keep wall-clock playback by skipping source frames when processing or terminal output falls behind. This is also the default behavior.
150
+ - `--no-frame-skip`: render every source frame even if playback takes longer than the original video.
151
+
152
+ Examples:
153
+
154
+ ```bash
155
+ ascii-motion video.mp4 --fps 60
156
+ ascii-motion video.mp4 --start 10 --duration 5
157
+ ascii-motion video.mp4 --loop
158
+ ascii-motion video.mp4 --real-time --benchmark
159
+ ascii-motion video.mp4 --no-frame-skip --benchmark
160
+ ```
161
+
162
+ ### Interactive Controls
163
+
164
+ Default controls during interactive playback:
165
+
166
+ ```text
167
+ q quit
168
+ space pause / resume
169
+ left arrow seek backward
170
+ right arrow seek forward
171
+ h seek backward fallback
172
+ l seek forward fallback
173
+ ? show / hide controls
174
+ ```
175
+
176
+ Control options:
177
+
178
+ - `--quit-key KEY`: change the quit key. Default: `q`.
179
+ - `--pause-key KEY`: change the pause/resume key. Default: space.
180
+ - `--backward-key KEY`: change the backward seek fallback key. Default: `h`.
181
+ - `--forward-key KEY`: change the forward seek fallback key. Default: `l`.
182
+ - `--help-key KEY`: change the controls overlay toggle key. Default: `?`.
183
+ - `--seek-seconds SECONDS`: seconds moved by each seek command. Default: `5`.
184
+
185
+ Examples:
186
+
187
+ ```bash
188
+ ascii-motion video.mp4 --quit-key x
189
+ ascii-motion video.mp4 --pause-key p
190
+ ascii-motion video.mp4 --backward-key j --forward-key k --seek-seconds 10
191
+ ```
192
+
193
+ ### HUD, Progress And Terminal Rendering
194
+
195
+ - `--no-hud`: hide the compact playback status line.
196
+ - `--no-progress`: hide the progress bar.
197
+ - `--show-controls`: show the controls line from the start instead of waiting for `?`.
198
+ - `--no-alt-screen`: render in the normal terminal buffer instead of the alternate screen.
199
+ - `--benchmark`: print performance metrics to stderr after playback/export.
200
+
201
+ The renderer uses `\033[H` to return the cursor to the top-left before writing each frame. It does not clear the full screen per frame.
202
+
203
+ Examples:
204
+
205
+ ```bash
206
+ ascii-motion video.mp4 --no-hud --no-progress
207
+ ascii-motion video.mp4 --show-controls
208
+ ascii-motion video.mp4 --no-alt-screen
209
+ ascii-motion video.mp4 --benchmark
210
+ ```
211
+
212
+ ### Character Sets And Visual Modes
213
+
214
+ - `--charset classic|dense|blocks|custom`: choose a built-in character ramp or a custom one.
215
+ - `--chars "..."`: provide a custom dark-to-light character ramp. Use with `--charset custom` or to override another charset.
216
+ - `--invert`: reverse the selected character ramp.
217
+ - `--mode ascii|edges|hybrid`: choose standard luminance ASCII, Sobel edge emphasis, or a luminance/edge blend.
218
+ - `--dither none|ordered`: apply no dithering or ordered Bayer dithering before character mapping.
219
+ - `--list-charsets`: print available character sets and exit.
220
+
221
+ Examples:
222
+
223
+ ```bash
224
+ ascii-motion video.mp4 --charset dense
225
+ ascii-motion video.mp4 --charset blocks
226
+ ascii-motion video.mp4 --charset custom --chars " .oO@"
227
+ ascii-motion video.mp4 --chars " .oO@" --invert
228
+ ascii-motion video.mp4 --mode edges
229
+ ascii-motion video.mp4 --mode hybrid --dither ordered
230
+ ascii-motion --list-charsets
231
+ ```
232
+
233
+ ### Color
234
+
235
+ - `--color none`: emit plain text only. This is the default and fastest mode.
236
+ - `--color truecolor`: emit 24-bit ANSI foreground color.
237
+ - `--color 256`: emit ANSI 256-color foreground color.
238
+ - `--color grayscale`: emit ANSI 256-color grayscale foreground color based on luminance.
239
+
240
+ Examples:
241
+
242
+ ```bash
243
+ ascii-motion video.mp4 --color truecolor
244
+ ascii-motion video.mp4 --color 256
245
+ ascii-motion video.mp4 --color grayscale
246
+ ```
247
+
248
+ ### Preview, Snapshot And Export
249
+
250
+ Non-interactive modes do not use alternate screen or keyboard controls.
251
+
252
+ - `--preview`: print source metadata, selected output dimensions, FPS, charset, mode, dithering, and color settings.
253
+ - `--frame-at SECONDS`: render one frame at a timestamp to stdout.
254
+ - `--export PATH`: export a plain text animation to one file. Frames are separated with form feed `\f`.
255
+ - `--export-ansi PATH`: export an ANSI animation file with cursor-home sequences between frames.
256
+ - `--export-frames DIR`: export each processed frame as a numbered text file.
257
+
258
+ Examples:
259
+
260
+ ```bash
261
+ ascii-motion video.mp4 --preview
262
+ ascii-motion video.mp4 --frame-at 5.0 --width 100 > frame.txt
263
+ ascii-motion video.mp4 --export output.txt --width 100 --duration 5
264
+ ascii-motion video.mp4 --export-ansi output.ans --width 100 --duration 5 --color 256
265
+ ascii-motion video.mp4 --export-frames frames --start 2 --duration 4
266
+ ```
267
+
268
+ Use `--frame-at` when you want exactly one saved frame:
269
+
270
+ ```bash
271
+ ascii-motion video.mp4 --frame-at 12.5 --width 120 > frame.txt
272
+ ```
273
+
274
+ Use `--export-frames` when you want many files, one per processed frame in the selected time range.
275
+
276
+ ### Metadata
277
+
278
+ - `--version`: print the installed package version.
279
+ - `-h, --help`: print CLI help.
280
+
281
+ ## Release Process
282
+
283
+ Package version is defined once in [`ascii_motion/__init__.py`](ascii_motion/__init__.py). `pyproject.toml` reads that value during builds, so releases do not require editing the version in two places.
284
+
285
+ Before tagging a release:
286
+
287
+ ```bash
288
+ python3 -m pytest
289
+ python3 -m ruff check .
290
+ python3 -m compileall ascii_motion tests scripts
291
+ rm -rf dist build ascii_motion.egg-info
292
+ python3 -m build
293
+ python3 -m twine check dist/*
294
+ ```
295
+
296
+ To publish a release, update `__version__`, commit it, then create and push a semantic version tag:
297
+
298
+ ```bash
299
+ git tag v0.2.0
300
+ git push origin v0.2.0
301
+ ```
302
+
303
+ The `Publish package` GitHub Actions workflow builds the package and publishes it to PyPI through trusted publishing. The repository must be configured in PyPI with a trusted publisher for this workflow:
304
+
305
+ ```text
306
+ Owner: c4rl0s04
307
+ Repository: ascii-motion
308
+ Workflow: release.yml
309
+ Environment: pypi
310
+ ```
311
+
312
+ After the release workflow succeeds, verify the published package from a clean environment:
313
+
314
+ ```bash
315
+ pipx install ascii-motion
316
+ ascii-motion --version
317
+ ```
318
+
319
+ ## Technical Pipeline
320
+
321
+ ```text
322
+ VideoCapture -> resize -> luminance/edges/dither -> LUT ASCII -> ANSI render/export
323
+ ```
324
+
325
+ Luminance uses the Rec. 709 formula:
326
+
327
+ ```text
328
+ Y = 0.2126R + 0.7152G + 0.0722B
329
+ ```
330
+
331
+ OpenCV provides frames in BGR order, so the processor reads `R` from channel `2`, `G` from channel `1`, and `B` from channel `0`.
332
+
333
+ ## Engineering Notes
334
+
335
+ `ascii-motion` is structured as a terminal media pipeline rather than a frame-by-frame print script. OpenCV owns source capture and seeking, `FrameProcessor` owns vectorized image transforms, and `TerminalRenderer` owns ANSI terminal state. This keeps future processor backends isolated from playback and output concerns.
336
+
337
+ The core luminance and character mapping path is vectorized with NumPy over full frame matrices. Python does not iterate pixel by pixel; it only assembles the final character rows after the lookup table has produced the ASCII matrix.
338
+
339
+ Rendering avoids full-screen clears during playback. Each frame is emitted with a cursor-home sequence and clear-to-end-of-line suffixes, which prevents scrollback spam and stale HUD text while reducing flicker in modern ANSI terminals.
340
+
341
+ Frame pacing is based on accumulated wall-clock targets instead of sleeping a fixed amount after each frame. When rendering falls behind, default playback skips late source frames to keep the ASCII output close to the original video duration. `--no-frame-skip` switches to completeness over timing.
342
+
343
+ Export modes reuse the same processing path as playback. Snapshot, plain text animation, ANSI animation, and numbered frame exports are non-interactive stdout/file workflows, so they do not enter alternate screen or keyboard-control mode.
344
+
345
+ ## Performance Notes
346
+
347
+ Grayscale-to-character mapping is performed with NumPy over full matrices. There are no nested Python loops walking pixel by pixel. Final text conversion happens by row, which is the practical boundary between matrix processing and terminal output.
348
+
349
+ By default, playback can skip frames when processing or terminal rendering is late. This keeps the ASCII video close to the original duration. Use `--no-frame-skip` when you prefer to render every source frame even if terminal output is too slow.
350
+
351
+ `--mode edges` uses Sobel gradients to emphasize contours. `--mode hybrid` blends luminance and edges. `--dither ordered` applies vectorized Bayer dithering before character mapping.
352
+
353
+ Terminal output can be the main bottleneck at large widths, especially with ANSI color enabled. On modern terminals such as Ghostty, `--width 120` or `--width 160` is a reasonable range for testing 30/60 FPS depending on the video and machine.
354
+
355
+ To measure the pipeline:
356
+
357
+ ```bash
358
+ ascii-motion video.mp4 --width 120 --benchmark
359
+ ```
360
+
361
+ ## Terminal Compatibility
362
+
363
+ Compatibility depends on ANSI support, terminal throughput, font metrics, and keyboard escape handling. Current status:
364
+
365
+ | Terminal | ANSI render | Truecolor | Alternate screen | Keyboard controls | Notes |
366
+ | --- | --- | --- | --- | --- | --- |
367
+ | Ghostty | tested | tested | tested | tested | Best current validation target for high-FPS playback. |
368
+ | iTerm2 | expected | expected | expected | expected | Modern ANSI support; still needs a full local pass. |
369
+ | Terminal.app | expected | expected | expected | expected | Works for standard ANSI paths; high widths may be slower. |
370
+ | Alacritty | expected | expected | expected | expected | Good fit for fast rendering; needs validation. |
371
+ | WezTerm | expected | expected | expected | expected | Good fit for truecolor and alternate screen; needs validation. |
372
+ | VS Code terminal | expected | expected | expected | expected | Useful for development, but terminal throughput may vary. |
373
+ | tmux | needs validation | needs validation | needs validation | needs validation | Depends on tmux terminal-overrides and truecolor config. |
374
+
375
+ ## Recommended Manual Validation
376
+
377
+ ```bash
378
+ ascii-motion sample.mp4 --width 80
379
+ ascii-motion sample.mp4 --width 120
380
+ ascii-motion sample.mp4 --width 160 --benchmark
381
+ ascii-motion sample.mp4 --loop
382
+ ascii-motion sample.mp4 --real-time --benchmark
383
+ ascii-motion sample.mp4 --mode edges
384
+ ascii-motion sample.mp4 --color 256
385
+ ascii-motion sample.mp4 --preview
386
+ ascii-motion sample.mp4 --frame-at 5 --width 100
387
+ ascii-motion sample.mp4 --export output.txt --duration 3
388
+ ascii-motion 0 --width 100
389
+ ```
390
+
391
+ Also test `Ctrl+C` and confirm the cursor is visible and the terminal is restored.
392
+
393
+ ## Limitations
394
+
395
+ - No audio playback.
396
+ - No GUI.
397
+ - No required `curses` dependency.
398
+ - GIF export is not a runtime feature.
399
+ - `blocks` uses Unicode characters; the default `classic` charset is pure ASCII.
400
+ - Very large output sizes increase stdout write cost and can reduce effective FPS.