VideoSpeed 1.0.5__tar.gz → 1.0.9__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 (45) hide show
  1. videospeed-1.0.9/PKG-INFO +203 -0
  2. videospeed-1.0.9/README.md +186 -0
  3. {videospeed-1.0.5 → videospeed-1.0.9}/pyproject.toml +4 -4
  4. videospeed-1.0.9/src/VideoSpeed/__init__.py +6 -0
  5. {videospeed-1.0.5 → videospeed-1.0.9}/src/VideoSpeed/__main__.py +3 -1
  6. videospeed-1.0.9/src/VideoSpeed/about_page.py +201 -0
  7. videospeed-1.0.9/src/VideoSpeed/check_system.py +200 -0
  8. videospeed-1.0.9/src/VideoSpeed/cli.py +557 -0
  9. videospeed-1.0.9/src/VideoSpeed/edit_walk.py +428 -0
  10. videospeed-1.0.9/src/VideoSpeed/encoder.py +324 -0
  11. videospeed-1.0.9/src/VideoSpeed/file_stage.py +73 -0
  12. videospeed-1.0.9/src/VideoSpeed/media_info.py +67 -0
  13. videospeed-1.0.9/src/VideoSpeed/menu_model.py +211 -0
  14. videospeed-1.0.9/src/VideoSpeed/menu_painter.py +269 -0
  15. videospeed-1.0.9/src/VideoSpeed/menu_session.py +75 -0
  16. videospeed-1.0.9/src/VideoSpeed/run_output.py +110 -0
  17. videospeed-1.0.9/src/VideoSpeed/tui.py +387 -0
  18. videospeed-1.0.9/src/VideoSpeed.egg-info/PKG-INFO +203 -0
  19. videospeed-1.0.9/src/VideoSpeed.egg-info/SOURCES.txt +36 -0
  20. videospeed-1.0.9/src/VideoSpeed.egg-info/requires.txt +2 -0
  21. videospeed-1.0.9/tests/test_about.py +260 -0
  22. videospeed-1.0.9/tests/test_build.py +252 -0
  23. videospeed-1.0.9/tests/test_cli.py +452 -0
  24. videospeed-1.0.9/tests/test_dependencies.py +89 -0
  25. videospeed-1.0.9/tests/test_docs.py +65 -0
  26. videospeed-1.0.9/tests/test_errors.py +49 -0
  27. videospeed-1.0.9/tests/test_fs.py +173 -0
  28. videospeed-1.0.9/tests/test_json.py +132 -0
  29. videospeed-1.0.9/tests/test_logging.py +286 -0
  30. videospeed-1.0.9/tests/test_package.py +54 -0
  31. videospeed-1.0.9/tests/test_prereq.py +63 -0
  32. videospeed-1.0.9/tests/test_tui.py +678 -0
  33. videospeed-1.0.5/PKG-INFO +0 -150
  34. videospeed-1.0.5/README.md +0 -133
  35. videospeed-1.0.5/src/VideoSpeed/__init__.py +0 -3
  36. videospeed-1.0.5/src/VideoSpeed/cli.py +0 -489
  37. videospeed-1.0.5/src/VideoSpeed.egg-info/PKG-INFO +0 -150
  38. videospeed-1.0.5/src/VideoSpeed.egg-info/SOURCES.txt +0 -13
  39. videospeed-1.0.5/src/VideoSpeed.egg-info/requires.txt +0 -2
  40. {videospeed-1.0.5 → videospeed-1.0.9}/LICENSE.md +0 -0
  41. {videospeed-1.0.5 → videospeed-1.0.9}/setup.cfg +0 -0
  42. {videospeed-1.0.5 → videospeed-1.0.9}/src/VideoSpeed/cli.bootstrap-old.py +0 -0
  43. {videospeed-1.0.5 → videospeed-1.0.9}/src/VideoSpeed.egg-info/dependency_links.txt +0 -0
  44. {videospeed-1.0.5 → videospeed-1.0.9}/src/VideoSpeed.egg-info/entry_points.txt +0 -0
  45. {videospeed-1.0.5 → videospeed-1.0.9}/src/VideoSpeed.egg-info/top_level.txt +0 -0
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: VideoSpeed
3
+ Version: 1.0.9
4
+ Summary: A command-line video editing tool built in Python for cutting, speeding up, and applying a boomerang effect to MP4 videos.
5
+ Author-email: Wilgat Wong <wilgat.wong@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Wilgat/VideoSpeed
8
+ Project-URL: Repository, https://github.com/Wilgat/VideoSpeed
9
+ Project-URL: Issues, https://github.com/Wilgat/VideoSpeed/issues
10
+ Keywords: Video Editing,Multimeida
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE.md
14
+ Requires-Dist: opencv-python-headless>=5.0.0.93
15
+ Requires-Dist: ChronicleLogger>=1.3.1
16
+ Dynamic: license-file
17
+
18
+ # VideoSpeed - Cut, speed, and boomerang MP4 clips from the CLI
19
+
20
+ ![Version](https://img.shields.io/badge/Version-1.0.9-blue?style=flat-square)
21
+ ![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)
22
+ [![CIAO](https://img.shields.io/badge/Philosophy-CIAO%20(Caution%20%E2%80%A2%20Intentional%20%E2%80%A2%20Anti--fragile%20%E2%80%A2%20Over--engineered)-purple.svg)](https://github.com/cloudgen/ciao)
23
+ [![Stars](https://img.shields.io/github/stars/Wilgat/VideoSpeed?style=flat-square)](https://github.com/Wilgat/VideoSpeed)
24
+ [![Python](https://img.shields.io/badge/Python-3.11%2B-blue?style=flat-square)]()
25
+ [![PyPI](https://img.shields.io/pypi/v/VideoSpeed?style=flat-square)](https://pypi.org/project/VideoSpeed/)
26
+
27
+ VideoSpeed cuts a time range from an MP4, changes that clip’s length (percent), and can append a reverse pass (boomerang). On a terminal, no arguments opens a text menu (edit, hello, about, Exit). Choosing edit walks through the prompts. Scripts pass `--file` / `--start` / `--end`. Encoding uses **FFmpeg**; duration probing uses **OpenCV**. The menu screen uses the default text-menu style: aligned number, verb, and note, and a rounded box along the bottom.
28
+
29
+ | You | The other role | Not this |
30
+ |-----|----------------|----------|
31
+ | Editor at a terminal or a script | FFmpeg on `PATH` (does the encode) | A website, installer, or root/sudo tool |
32
+
33
+ Package version SSOT: `src/VideoSpeed/__init__.py` (`MAJOR_VERSION`, `MINOR_VERSION`, `PATCH_VERSION` → **1.0.9**). `pyproject.toml` copies that string. Console entrypoint: `video-speed`.
34
+
35
+ ## Features
36
+
37
+ - Recursive **MP4** discovery under a chosen folder
38
+ - Interactive **cut** (start/end seconds) with invalid-range re-prompt
39
+ - **Length/speed** change (**20–200%**) with A/V tempo kept in sync
40
+ - Optional **boomerang** (forward then reverse)
41
+ - **USB-safe** intermediate files (staged next to the output) and final publish via `shutil.move`
42
+ - **Text menu** on a terminal (`edit`, `hello`, `about`, `Exit`): columns line up, and a three-row rounded input box sits on the bottom with a status line under it
43
+ - **Product verbs** `help`, `about`, `hello`, `edit`, and `list-mp4`. `help` prints the same usage as `--help`. `about` and `hello` print a page and do not ask for a folder or a file. `edit` on a terminal asks for the folder, then the file. `list-mp4` lists MP4 files and does not encode. `Exit` stays on the menu. `./build.sh` verbs such as `setup` are not `video-speed` verbs
44
+ - **`--help`** / **`--version`**; no arguments in a terminal opens that menu
45
+ - Non-interactive job: `--file`, `--start`, `--end`, optional `--percent` / `--boomerang`, with or without `edit`. Any of those selectors with no product verb skips the menu
46
+ - Fail-closed when **FFmpeg** is missing from `PATH`, when prompts are needed without a terminal, or when `--percent` or `--boomerang` is passed without a job
47
+
48
+ ## Quick Installation
49
+
50
+ ### Prerequisites (system)
51
+
52
+ - **FFmpeg** on `PATH` (`ffmpeg -version`)
53
+ - A supported **Python** environment (see `requires-python` in `pyproject.toml`)
54
+
55
+ ### From PyPI (registry channel)
56
+
57
+ Package name SSOT: **`VideoSpeed`** (`pyproject.toml` `[project].name`). Maintainer verbs are on `./build.sh`: `help`, `version`, `setup`, `clean`, `build`, `test`, `test-install`, `upload`, `git`, `tag`, `release` (`all` is the same chain as `release`). `test` runs `tests/run.sh`. `test-install` reads `[project].name`, removes that pip install when present, and installs this checkout. Empty `./build.sh` prints help and does not upload.
58
+
59
+ ```bash
60
+ pip install VideoSpeed
61
+ ```
62
+
63
+ If the index is behind a local release, install from a checkout or wheel instead (below). Live index version is shown by the PyPI badge above.
64
+
65
+ ### From a git checkout (development)
66
+
67
+ ```bash
68
+ git clone git@github.com:Wilgat/VideoSpeed.git
69
+ cd VideoSpeed
70
+ pip install -e .
71
+ ```
72
+
73
+ This installs the console script **`video-speed`** and pulls Python deps (`opencv-python-headless>=5.0.0.93`, `ChronicleLogger>=1.3.1`).
74
+
75
+ ### Local pyenv install (testing)
76
+
77
+ ```sh
78
+ ./setup.sh
79
+ pyenv shell 3.14
80
+ video-speed --version
81
+ ```
82
+
83
+ `setup.sh` runs `pyenv shell 3.14` and installs this checkout with pip into that interpreter. Install CPython 3.14 first when it is missing (`pyenv install 3.14`). Run `./setup.sh` again after source changes. In the shell where you want the command:
84
+
85
+ ```sh
86
+ pyenv shell 3.14
87
+ video-speed --help
88
+ ```
89
+
90
+ The console script is installed on the pyenv 3.14 prefix. `./setup.sh` changes that interpreter only. It does not install FFmpeg and it does not use root.
91
+
92
+ ### From a local wheel / sdist
93
+
94
+ ```bash
95
+ # after: python -m build (or ./build.sh build)
96
+ pip install dist/VideoSpeed-1.0.9-py3-none-any.whl
97
+ # or
98
+ pip install dist/VideoSpeed-1.0.9.tar.gz
99
+ ```
100
+
101
+ ### Verify
102
+
103
+ ```bash
104
+ video-speed --version
105
+ # or
106
+ python -m VideoSpeed --version
107
+ ```
108
+
109
+ ## Usage
110
+
111
+ Text menu (no arguments, in a terminal):
112
+
113
+ ```bash
114
+ video-speed
115
+ # or
116
+ python -m VideoSpeed
117
+ ```
118
+
119
+ The screen shows a numbered menu and a rounded input box along the bottom, as wide as the terminal, with one line to type on and a status line under the box. **edit** continues the prompts. **hello** (7) shows `Hello.`. **about** shows the product. **Exit** leaves.
120
+
121
+ The same actions are verbs. `edit` starts at the folder question. `list-mp4` asks for the folder when it is omitted, prints the numbered list, and stops.
122
+
123
+ ```bash
124
+ video-speed help
125
+ video-speed about
126
+ video-speed hello
127
+ video-speed edit
128
+ video-speed edit --folder ./clips
129
+ video-speed edit --file clip.mp4 --start 1 --end 5 --percent 100
130
+ video-speed list-mp4
131
+ video-speed list-mp4 --folder ./clips
132
+ ```
133
+
134
+ 1. Folder containing MP4s (Enter = current directory)
135
+ 2. Choose a video by number
136
+ 3. Cut start/end seconds
137
+ 4. New length percent (default 100; allowed 20–200)
138
+ 5. Optional boomerang (y/n)
139
+ 6. Output is written **next to the source** with a descriptive name
140
+
141
+ Non-interactive job (scripts / CI):
142
+
143
+ ```bash
144
+ video-speed --file clip.mp4 --start 1 --end 5 --percent 100
145
+ video-speed --file clip.mp4 --start 1 --end 5 --percent 50 --boomerang
146
+ ```
147
+
148
+ Other:
149
+
150
+ ```bash
151
+ video-speed --help
152
+ video-speed --version
153
+ ```
154
+
155
+ Without a terminal and without a job, the program exits with an error instead of hanging on prompts. `--percent` or `--boomerang` alone does the same and names `--file`, `--start`, and `--end`. `--folder` checks the directory. It does not pick a file.
156
+
157
+ `--json` keeps that same choice, closes the text menu, and prints one JSON object on standard output. On a terminal with no job flags, the questions are written to the error stream. Progress is not mixed into the object.
158
+
159
+ ## Examples
160
+
161
+ ```bash
162
+ # Install editable, then run interactive session in a folder of clips
163
+ cd /path/to/videos
164
+ video-speed
165
+
166
+ # Check identity without starting prompts
167
+ video-speed --version
168
+
169
+ # One-shot job (no prompts)
170
+ video-speed --file clip.mp4 --start 1.0 --end 5.0 --percent 100
171
+ ```
172
+
173
+ Example output name shape:
174
+
175
+ ```text
176
+ clip_cut1.0-5.0s_100pct.mp4
177
+ clip_cut1.0-5.0s_50pct_BOOMERANG.mp4
178
+ ```
179
+
180
+ ## Platform Compatibility
181
+
182
+ | Platform | Status |
183
+ |----------|--------|
184
+ | Linux | Primary (tested in development) |
185
+ | macOS / Windows | Expected to work when Python, FFmpeg, and OpenCV are available |
186
+ | Filesystems | Local disk and removable/USB volumes supported for output (temps staged beside final path) |
187
+
188
+ ## Related Projects
189
+
190
+ - [VideoSpeed on GitHub](https://github.com/Wilgat/VideoSpeed)
191
+ - [VideoSpeed on PyPI](https://pypi.org/project/VideoSpeed/)
192
+
193
+ ## Contributing
194
+
195
+ Fork the repository, use a checkout install (`pip install -e .` or `./setup.sh`), and open a pull request. Prefer small, tested changes. Do not strip defensive temp/publish behavior (cross-filesystem `shutil.move`) without an explicit redesign.
196
+
197
+ ## License
198
+
199
+ MIT License — see [`LICENSE.md`](LICENSE.md).
200
+
201
+ ## Last Update
202
+
203
+ 2026-10-01 — **1.0.9**: a non-TUI, non-JSON run shows `debug mode` when `DEBUG` is already on, and each class logs `instantiated` on the one ChronicleLogger. `def main` stays in `src/VideoSpeed/cli.py`.
@@ -0,0 +1,186 @@
1
+ # VideoSpeed - Cut, speed, and boomerang MP4 clips from the CLI
2
+
3
+ ![Version](https://img.shields.io/badge/Version-1.0.9-blue?style=flat-square)
4
+ ![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)
5
+ [![CIAO](https://img.shields.io/badge/Philosophy-CIAO%20(Caution%20%E2%80%A2%20Intentional%20%E2%80%A2%20Anti--fragile%20%E2%80%A2%20Over--engineered)-purple.svg)](https://github.com/cloudgen/ciao)
6
+ [![Stars](https://img.shields.io/github/stars/Wilgat/VideoSpeed?style=flat-square)](https://github.com/Wilgat/VideoSpeed)
7
+ [![Python](https://img.shields.io/badge/Python-3.11%2B-blue?style=flat-square)]()
8
+ [![PyPI](https://img.shields.io/pypi/v/VideoSpeed?style=flat-square)](https://pypi.org/project/VideoSpeed/)
9
+
10
+ VideoSpeed cuts a time range from an MP4, changes that clip’s length (percent), and can append a reverse pass (boomerang). On a terminal, no arguments opens a text menu (edit, hello, about, Exit). Choosing edit walks through the prompts. Scripts pass `--file` / `--start` / `--end`. Encoding uses **FFmpeg**; duration probing uses **OpenCV**. The menu screen uses the default text-menu style: aligned number, verb, and note, and a rounded box along the bottom.
11
+
12
+ | You | The other role | Not this |
13
+ |-----|----------------|----------|
14
+ | Editor at a terminal or a script | FFmpeg on `PATH` (does the encode) | A website, installer, or root/sudo tool |
15
+
16
+ Package version SSOT: `src/VideoSpeed/__init__.py` (`MAJOR_VERSION`, `MINOR_VERSION`, `PATCH_VERSION` → **1.0.9**). `pyproject.toml` copies that string. Console entrypoint: `video-speed`.
17
+
18
+ ## Features
19
+
20
+ - Recursive **MP4** discovery under a chosen folder
21
+ - Interactive **cut** (start/end seconds) with invalid-range re-prompt
22
+ - **Length/speed** change (**20–200%**) with A/V tempo kept in sync
23
+ - Optional **boomerang** (forward then reverse)
24
+ - **USB-safe** intermediate files (staged next to the output) and final publish via `shutil.move`
25
+ - **Text menu** on a terminal (`edit`, `hello`, `about`, `Exit`): columns line up, and a three-row rounded input box sits on the bottom with a status line under it
26
+ - **Product verbs** `help`, `about`, `hello`, `edit`, and `list-mp4`. `help` prints the same usage as `--help`. `about` and `hello` print a page and do not ask for a folder or a file. `edit` on a terminal asks for the folder, then the file. `list-mp4` lists MP4 files and does not encode. `Exit` stays on the menu. `./build.sh` verbs such as `setup` are not `video-speed` verbs
27
+ - **`--help`** / **`--version`**; no arguments in a terminal opens that menu
28
+ - Non-interactive job: `--file`, `--start`, `--end`, optional `--percent` / `--boomerang`, with or without `edit`. Any of those selectors with no product verb skips the menu
29
+ - Fail-closed when **FFmpeg** is missing from `PATH`, when prompts are needed without a terminal, or when `--percent` or `--boomerang` is passed without a job
30
+
31
+ ## Quick Installation
32
+
33
+ ### Prerequisites (system)
34
+
35
+ - **FFmpeg** on `PATH` (`ffmpeg -version`)
36
+ - A supported **Python** environment (see `requires-python` in `pyproject.toml`)
37
+
38
+ ### From PyPI (registry channel)
39
+
40
+ Package name SSOT: **`VideoSpeed`** (`pyproject.toml` `[project].name`). Maintainer verbs are on `./build.sh`: `help`, `version`, `setup`, `clean`, `build`, `test`, `test-install`, `upload`, `git`, `tag`, `release` (`all` is the same chain as `release`). `test` runs `tests/run.sh`. `test-install` reads `[project].name`, removes that pip install when present, and installs this checkout. Empty `./build.sh` prints help and does not upload.
41
+
42
+ ```bash
43
+ pip install VideoSpeed
44
+ ```
45
+
46
+ If the index is behind a local release, install from a checkout or wheel instead (below). Live index version is shown by the PyPI badge above.
47
+
48
+ ### From a git checkout (development)
49
+
50
+ ```bash
51
+ git clone git@github.com:Wilgat/VideoSpeed.git
52
+ cd VideoSpeed
53
+ pip install -e .
54
+ ```
55
+
56
+ This installs the console script **`video-speed`** and pulls Python deps (`opencv-python-headless>=5.0.0.93`, `ChronicleLogger>=1.3.1`).
57
+
58
+ ### Local pyenv install (testing)
59
+
60
+ ```sh
61
+ ./setup.sh
62
+ pyenv shell 3.14
63
+ video-speed --version
64
+ ```
65
+
66
+ `setup.sh` runs `pyenv shell 3.14` and installs this checkout with pip into that interpreter. Install CPython 3.14 first when it is missing (`pyenv install 3.14`). Run `./setup.sh` again after source changes. In the shell where you want the command:
67
+
68
+ ```sh
69
+ pyenv shell 3.14
70
+ video-speed --help
71
+ ```
72
+
73
+ The console script is installed on the pyenv 3.14 prefix. `./setup.sh` changes that interpreter only. It does not install FFmpeg and it does not use root.
74
+
75
+ ### From a local wheel / sdist
76
+
77
+ ```bash
78
+ # after: python -m build (or ./build.sh build)
79
+ pip install dist/VideoSpeed-1.0.9-py3-none-any.whl
80
+ # or
81
+ pip install dist/VideoSpeed-1.0.9.tar.gz
82
+ ```
83
+
84
+ ### Verify
85
+
86
+ ```bash
87
+ video-speed --version
88
+ # or
89
+ python -m VideoSpeed --version
90
+ ```
91
+
92
+ ## Usage
93
+
94
+ Text menu (no arguments, in a terminal):
95
+
96
+ ```bash
97
+ video-speed
98
+ # or
99
+ python -m VideoSpeed
100
+ ```
101
+
102
+ The screen shows a numbered menu and a rounded input box along the bottom, as wide as the terminal, with one line to type on and a status line under the box. **edit** continues the prompts. **hello** (7) shows `Hello.`. **about** shows the product. **Exit** leaves.
103
+
104
+ The same actions are verbs. `edit` starts at the folder question. `list-mp4` asks for the folder when it is omitted, prints the numbered list, and stops.
105
+
106
+ ```bash
107
+ video-speed help
108
+ video-speed about
109
+ video-speed hello
110
+ video-speed edit
111
+ video-speed edit --folder ./clips
112
+ video-speed edit --file clip.mp4 --start 1 --end 5 --percent 100
113
+ video-speed list-mp4
114
+ video-speed list-mp4 --folder ./clips
115
+ ```
116
+
117
+ 1. Folder containing MP4s (Enter = current directory)
118
+ 2. Choose a video by number
119
+ 3. Cut start/end seconds
120
+ 4. New length percent (default 100; allowed 20–200)
121
+ 5. Optional boomerang (y/n)
122
+ 6. Output is written **next to the source** with a descriptive name
123
+
124
+ Non-interactive job (scripts / CI):
125
+
126
+ ```bash
127
+ video-speed --file clip.mp4 --start 1 --end 5 --percent 100
128
+ video-speed --file clip.mp4 --start 1 --end 5 --percent 50 --boomerang
129
+ ```
130
+
131
+ Other:
132
+
133
+ ```bash
134
+ video-speed --help
135
+ video-speed --version
136
+ ```
137
+
138
+ Without a terminal and without a job, the program exits with an error instead of hanging on prompts. `--percent` or `--boomerang` alone does the same and names `--file`, `--start`, and `--end`. `--folder` checks the directory. It does not pick a file.
139
+
140
+ `--json` keeps that same choice, closes the text menu, and prints one JSON object on standard output. On a terminal with no job flags, the questions are written to the error stream. Progress is not mixed into the object.
141
+
142
+ ## Examples
143
+
144
+ ```bash
145
+ # Install editable, then run interactive session in a folder of clips
146
+ cd /path/to/videos
147
+ video-speed
148
+
149
+ # Check identity without starting prompts
150
+ video-speed --version
151
+
152
+ # One-shot job (no prompts)
153
+ video-speed --file clip.mp4 --start 1.0 --end 5.0 --percent 100
154
+ ```
155
+
156
+ Example output name shape:
157
+
158
+ ```text
159
+ clip_cut1.0-5.0s_100pct.mp4
160
+ clip_cut1.0-5.0s_50pct_BOOMERANG.mp4
161
+ ```
162
+
163
+ ## Platform Compatibility
164
+
165
+ | Platform | Status |
166
+ |----------|--------|
167
+ | Linux | Primary (tested in development) |
168
+ | macOS / Windows | Expected to work when Python, FFmpeg, and OpenCV are available |
169
+ | Filesystems | Local disk and removable/USB volumes supported for output (temps staged beside final path) |
170
+
171
+ ## Related Projects
172
+
173
+ - [VideoSpeed on GitHub](https://github.com/Wilgat/VideoSpeed)
174
+ - [VideoSpeed on PyPI](https://pypi.org/project/VideoSpeed/)
175
+
176
+ ## Contributing
177
+
178
+ Fork the repository, use a checkout install (`pip install -e .` or `./setup.sh`), and open a pull request. Prefer small, tested changes. Do not strip defensive temp/publish behavior (cross-filesystem `shutil.move`) without an explicit redesign.
179
+
180
+ ## License
181
+
182
+ MIT License — see [`LICENSE.md`](LICENSE.md).
183
+
184
+ ## Last Update
185
+
186
+ 2026-10-01 — **1.0.9**: a non-TUI, non-JSON run shows `debug mode` when `DEBUG` is already on, and each class logs `instantiated` on the one ChronicleLogger. `def main` stays in `src/VideoSpeed/cli.py`.
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "VideoSpeed"
7
- version = "1.0.5"
7
+ version = "1.0.9"
8
8
  description = "A command-line video editing tool built in Python for cutting, speeding up, and applying a boomerang effect to MP4 videos."
9
9
  readme = "README.md"
10
- requires-python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
10
+ requires-python = ">=3.11"
11
11
  license = {text = "MIT"}
12
12
  authors = [
13
13
  {name = "Wilgat Wong", email = "wilgat.wong@gmail.com"}
@@ -15,8 +15,8 @@ authors = [
15
15
  keywords = ["Video Editing", "Multimeida"]
16
16
 
17
17
  dependencies = [
18
- "ChronicleLogger>=1.2.3",
19
- "opencv-python"
18
+ "opencv-python-headless>=5.0.0.93",
19
+ "ChronicleLogger>=1.3.1",
20
20
  ]
21
21
 
22
22
  # Official project links (this is what shows on PyPI!)
@@ -0,0 +1,6 @@
1
+ # Version SSOT (requirement-python-version). Do not import the CLI from here.
2
+ MAJOR_VERSION = 1
3
+ MINOR_VERSION = 0
4
+ PATCH_VERSION = 9
5
+ __version__ = "{0}.{1}.{2}".format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION)
6
+ __all__ = ["MAJOR_VERSION", "MINOR_VERSION", "PATCH_VERSION", "__version__"]
@@ -1,4 +1,6 @@
1
+ import sys
2
+
1
3
  from .cli import main
2
4
 
3
5
  if __name__ == "__main__":
4
- main()
6
+ sys.exit(main() or 0)
@@ -0,0 +1,201 @@
1
+ # =============================================================================
2
+ # About page composer.
3
+ # requirement-python-oop — class AboutPage. Line text stays on requirement-python-about.
4
+ # =============================================================================
5
+ from __future__ import print_function, unicode_literals
6
+
7
+ import os
8
+
9
+ from .check_system import CheckSystem
10
+ from .run_output import log_instantiated
11
+
12
+
13
+ class AboutPage:
14
+ """Identity lines, install sentence, and the star box. One class, one module.
15
+
16
+ The host check is CheckSystem. This class calls that object.
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ check,
22
+ app_name,
23
+ version,
24
+ major,
25
+ minor,
26
+ patch,
27
+ author,
28
+ updated,
29
+ homepage,
30
+ download_url,
31
+ usage,
32
+ console_name,
33
+ logger=None,
34
+ ):
35
+ self.logger = logger
36
+ log_instantiated(logger, "AboutPage")
37
+ self.check = check if check is not None else CheckSystem(logger=logger)
38
+ self.app_name = app_name
39
+ self.version = version
40
+ self.major = major
41
+ self.minor = minor
42
+ self.patch = patch
43
+ self.author = author
44
+ self.updated = updated
45
+ self.homepage = homepage
46
+ self.download_url = download_url
47
+ self.usage = usage
48
+ self.console_name = console_name
49
+
50
+ def _is_source_checkout(self, path):
51
+ """True when path is this package inside a source tree that has pyproject.toml."""
52
+ marker = "{}src{}VideoSpeed{}".format(os.sep, os.sep, os.sep)
53
+ if marker not in path:
54
+ return False
55
+ cursor = os.path.dirname(path)
56
+ for _ in range(8):
57
+ if os.path.isfile(os.path.join(cursor, "pyproject.toml")):
58
+ return True
59
+ parent = os.path.dirname(cursor)
60
+ if parent == cursor:
61
+ return False
62
+ cursor = parent
63
+ return False
64
+
65
+ def install_kind(self, path):
66
+ """
67
+ General Purpose: global, local, or uninstalled for an about location.
68
+ A source checkout is uninstalled. A copy under the home directory is local.
69
+ """
70
+ path = os.path.realpath(path)
71
+ parts = path.split(os.sep)
72
+ packaged = "site-packages" in parts or "dist-packages" in parts
73
+ if not packaged and self._is_source_checkout(path):
74
+ return "uninstalled"
75
+ home = os.path.realpath(os.path.expanduser("~"))
76
+ in_home = path == home or path.startswith(home + os.sep)
77
+ base = os.path.basename(path)
78
+ if in_home and (packaged or base in (self.console_name, self.app_name)):
79
+ return "local"
80
+ if path.startswith("/usr/") or path.startswith("/opt/") or path.startswith("/bin/"):
81
+ return "global"
82
+ if packaged and not in_home:
83
+ return "global"
84
+ return "uninstalled"
85
+
86
+ def install_sentence(self, kind):
87
+ """General Purpose: The location sentence inside the about box."""
88
+ if kind == "global":
89
+ return "You are using the GLOBAL INSTALLED version, location:"
90
+ if kind == "local":
91
+ return "You are using the LOCAL INSTALLED version, location:"
92
+ return "You are using an UNINSTALLED version, location:"
93
+
94
+ def about_box_lines(
95
+ self,
96
+ usage=None,
97
+ location=None,
98
+ kind=None,
99
+ homepage=None,
100
+ download_url=None,
101
+ author=None,
102
+ updated=None,
103
+ ):
104
+ """
105
+ General Purpose: Star-bordered about box.
106
+ An empty download URL omits the install line.
107
+ """
108
+ if usage is None:
109
+ usage = self.usage
110
+ if location is None:
111
+ location = self.check.self_location()
112
+ if kind is None:
113
+ kind = self.install_kind(location)
114
+ if homepage is None:
115
+ homepage = self.homepage
116
+ if download_url is None:
117
+ download_url = self.download_url
118
+ if author is None:
119
+ author = self.author
120
+ if updated is None:
121
+ updated = self.updated
122
+ app = self.install_sentence(kind)
123
+ python_exe = self.check.python_executable_name() or "python3"
124
+ msg1 = "{} ({}.{}.{}) by {} on {}".format(
125
+ self.app_name, self.major, self.minor, self.patch, author, updated
126
+ )
127
+ if download_url == "":
128
+ msg = [
129
+ msg1,
130
+ "",
131
+ app,
132
+ " {}".format(location),
133
+ "",
134
+ "Basic Usage:",
135
+ " {}".format(usage),
136
+ "",
137
+ "Please visit our homepage: ",
138
+ ' "{}"'.format(homepage),
139
+ "",
140
+ "",
141
+ "",
142
+ "",
143
+ ]
144
+ else:
145
+ msg = [
146
+ msg1,
147
+ "",
148
+ app,
149
+ " {}".format(location),
150
+ "",
151
+ "Basic Usage:",
152
+ " {}".format(usage),
153
+ "",
154
+ "Please visit our homepage: ",
155
+ ' "{}"'.format(homepage),
156
+ "",
157
+ "Installation command:",
158
+ " curl -fsSL {} | {}".format(download_url, python_exe),
159
+ "",
160
+ ]
161
+ if download_url == "":
162
+ if homepage == "":
163
+ max_line = len(msg) - 3
164
+ else:
165
+ max_line = len(msg) - 2
166
+ else:
167
+ max_line = len(msg) - 1
168
+ if max_line < 1:
169
+ max_line = len(msg)
170
+ max_len = 0
171
+ for index in range(0, max_line):
172
+ if len(msg[index]) > max_len:
173
+ max_len = len(msg[index])
174
+ boxed = []
175
+ for index in range(0, max_line):
176
+ boxed.append(msg[index] + (" " * (max_len - len(msg[index]))))
177
+ star = "*" * (max_len + 4)
178
+ blank = " " * max_len
179
+ lines = [star, "* {} *".format(blank)]
180
+ for row in boxed:
181
+ lines.append("* {} *".format(row))
182
+ lines.append("* {} *".format(blank))
183
+ lines.append(star)
184
+ return lines
185
+
186
+ def framework_about(self):
187
+ """
188
+ General Purpose: About text for the menu result page.
189
+ requirement-python-about: identity, host check, and the install box.
190
+ """
191
+ lines = [
192
+ "{} {}".format(self.app_name, self.version),
193
+ "Domain: Cut → speed/length → optional boomerang for MP4",
194
+ "Runtime tools: FFmpeg (encode), OpenCV (duration probe)",
195
+ "Entry points: video-speed, python -m VideoSpeed",
196
+ "",
197
+ ]
198
+ lines.extend(self.check.check_system_lines())
199
+ lines.append("")
200
+ lines.extend(self.about_box_lines())
201
+ return "\n".join(lines)