vidio-cli 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 (35) hide show
  1. vidio_cli-0.1.0/.gitattributes +1 -0
  2. vidio_cli-0.1.0/.github/workflows/ci.yml +65 -0
  3. vidio_cli-0.1.0/.github/workflows/publish.yml +72 -0
  4. vidio_cli-0.1.0/.gitignore +55 -0
  5. vidio_cli-0.1.0/AGENTS.md +143 -0
  6. vidio_cli-0.1.0/CHANGELOG.md +20 -0
  7. vidio_cli-0.1.0/LICENSE +21 -0
  8. vidio_cli-0.1.0/PKG-INFO +238 -0
  9. vidio_cli-0.1.0/README.md +210 -0
  10. vidio_cli-0.1.0/pyproject.toml +53 -0
  11. vidio_cli-0.1.0/src/vidio_cli/__init__.py +3 -0
  12. vidio_cli-0.1.0/src/vidio_cli/cli.py +62 -0
  13. vidio_cli-0.1.0/src/vidio_cli/commands/__init__.py +36 -0
  14. vidio_cli-0.1.0/src/vidio_cli/commands/concat.py +103 -0
  15. vidio_cli-0.1.0/src/vidio_cli/commands/crop.py +391 -0
  16. vidio_cli-0.1.0/src/vidio_cli/commands/grid.py +258 -0
  17. vidio_cli-0.1.0/src/vidio_cli/commands/info.py +244 -0
  18. vidio_cli-0.1.0/src/vidio_cli/commands/list.py +316 -0
  19. vidio_cli-0.1.0/src/vidio_cli/commands/resize.py +220 -0
  20. vidio_cli-0.1.0/src/vidio_cli/commands/to_gif.py +351 -0
  21. vidio_cli-0.1.0/src/vidio_cli/commands/trim.py +133 -0
  22. vidio_cli-0.1.0/src/vidio_cli/config.py +25 -0
  23. vidio_cli-0.1.0/src/vidio_cli/ffmpeg_utils.py +197 -0
  24. vidio_cli-0.1.0/tests/assets/sample.mp4 +3 -0
  25. vidio_cli-0.1.0/tests/conftest.py +18 -0
  26. vidio_cli-0.1.0/tests/test_cli.py +31 -0
  27. vidio_cli-0.1.0/tests/test_concat.py +65 -0
  28. vidio_cli-0.1.0/tests/test_crop.py +664 -0
  29. vidio_cli-0.1.0/tests/test_grid.py +108 -0
  30. vidio_cli-0.1.0/tests/test_info.py +56 -0
  31. vidio_cli-0.1.0/tests/test_list.py +112 -0
  32. vidio_cli-0.1.0/tests/test_resize.py +146 -0
  33. vidio_cli-0.1.0/tests/test_to_gif.py +192 -0
  34. vidio_cli-0.1.0/tests/test_trim.py +110 -0
  35. vidio_cli-0.1.0/uv.lock +378 -0
@@ -0,0 +1 @@
1
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
@@ -0,0 +1,65 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ fast-tests:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.10", "3.11", "3.12"]
13
+
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Setup Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v5
25
+ with:
26
+ enable-cache: true
27
+
28
+ - name: Sync dependencies
29
+ run: uv sync --group dev
30
+
31
+ - name: Lint
32
+ run: uv run ruff check .
33
+
34
+ - name: Fast tests (non-integration)
35
+ run: uv run pytest -q -m "not integration"
36
+
37
+ integration-tests:
38
+ runs-on: ubuntu-latest
39
+
40
+ steps:
41
+ - name: Checkout
42
+ uses: actions/checkout@v4
43
+ with:
44
+ lfs: true
45
+
46
+ - name: Setup Python
47
+ uses: actions/setup-python@v5
48
+ with:
49
+ python-version: "3.11"
50
+
51
+ - name: Install uv
52
+ uses: astral-sh/setup-uv@v5
53
+ with:
54
+ enable-cache: true
55
+
56
+ - name: Install ffmpeg
57
+ run: |
58
+ sudo apt-get update
59
+ sudo apt-get install -y ffmpeg
60
+
61
+ - name: Sync dependencies
62
+ run: uv sync --group dev
63
+
64
+ - name: Integration tests
65
+ run: uv run pytest -q -m "integration"
@@ -0,0 +1,72 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ repository:
9
+ description: "Package repository target"
10
+ required: true
11
+ default: testpypi
12
+ type: choice
13
+ options:
14
+ - testpypi
15
+ - pypi
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Checkout
22
+ uses: actions/checkout@v4
23
+
24
+ - name: Setup Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+
29
+ - name: Install uv
30
+ uses: astral-sh/setup-uv@v5
31
+ with:
32
+ enable-cache: true
33
+
34
+ - name: Build distributions
35
+ run: uv build
36
+
37
+ - name: Validate distributions
38
+ run: |
39
+ python -m pip install --upgrade pip twine
40
+ python -m twine check dist/*
41
+
42
+ - name: Upload dist artifacts
43
+ uses: actions/upload-artifact@v4
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+
48
+ publish:
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ permissions:
52
+ id-token: write
53
+ environment:
54
+ name: pypi
55
+ env:
56
+ TARGET_REPOSITORY: ${{ github.event_name == 'release' && 'pypi' || github.event.inputs.repository }}
57
+ steps:
58
+ - name: Download dist artifacts
59
+ uses: actions/download-artifact@v4
60
+ with:
61
+ name: dist
62
+ path: dist/
63
+
64
+ - name: Publish to TestPyPI
65
+ if: env.TARGET_REPOSITORY == 'testpypi'
66
+ uses: pypa/gh-action-pypi-publish@release/v1
67
+ with:
68
+ repository-url: https://test.pypi.org/legacy/
69
+
70
+ - name: Publish to PyPI
71
+ if: env.TARGET_REPOSITORY == 'pypi'
72
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,55 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ .python-version
23
+
24
+ # IDE - PyCharm
25
+ .idea/
26
+ *.iws
27
+ *.iml
28
+ *.ipr
29
+
30
+ # IDE - VSCode
31
+ .vscode/
32
+ *.code-workspace
33
+
34
+ # Virtual environments
35
+ venv/
36
+ env/
37
+ ENV/
38
+ .venv/
39
+ .env/
40
+
41
+ # Testing
42
+ .coverage
43
+ htmlcov/
44
+ .pytest_cache/
45
+ .tox/
46
+ .nox/
47
+
48
+ # OS specific
49
+ .DS_Store
50
+ Thumbs.db
51
+
52
+ # Temporary files
53
+ tmp/
54
+
55
+ tasks.md
@@ -0,0 +1,143 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ `vidio-cli` is a Python-based CLI tool that provides a simple, user-friendly wrapper around ffmpeg for common video operations. It uses Typer for CLI framework and Rich for terminal output formatting.
8
+
9
+ ## Development Setup
10
+
11
+ This project uses `uv` for package management:
12
+
13
+ ```bash
14
+ # Install dependencies
15
+ uv sync
16
+
17
+ # Install with dev dependencies
18
+ uv sync --group dev
19
+
20
+ # Install in development mode (editable install)
21
+ uv pip install -e .
22
+ ```
23
+
24
+ ## Running Commands
25
+
26
+ ```bash
27
+ # Run the CLI during development
28
+ uv run vidio <command>
29
+
30
+ # Or after installing in dev mode
31
+ vidio <command>
32
+
33
+ # Run tests
34
+ uv run pytest
35
+
36
+ # Run tests with verbose output
37
+ uv run pytest -v
38
+
39
+ # Run specific test file
40
+ uv run pytest tests/test_concat.py
41
+
42
+ # Lint with ruff
43
+ uv run ruff check .
44
+
45
+ # Format with ruff
46
+ uv run ruff format .
47
+ ```
48
+
49
+ ## Architecture
50
+
51
+ ### Command Registration System
52
+
53
+ Commands are **dynamically discovered and registered** at startup via `src/vidio_cli/commands/__init__.py`:
54
+
55
+ 1. The `get_commands()` function scans the `commands/` directory for Python modules
56
+ 2. Each module with a `register()` function is treated as a command
57
+ 3. Module names (with underscores) become command names (with hyphens), e.g., `to_gif.py` → `to-gif`
58
+ 4. Commands are registered with the main Typer app in `src/vidio_cli/cli.py`
59
+
60
+ ### Command Module Pattern
61
+
62
+ Each command module follows this structure:
63
+
64
+ ```python
65
+ def register(app: typer.Typer) -> None:
66
+ """Register the command with the Typer app."""
67
+ app.command(no_args_is_help=True)(command_function)
68
+
69
+ def command_function(
70
+ ctx: typer.Context,
71
+ # ... command arguments and options
72
+ ) -> None:
73
+ """Command implementation."""
74
+ verbose = ctx.obj.get("VERBOSE", False) if ctx.obj else False
75
+ # ... command logic using ffmpeg_utils
76
+ ```
77
+
78
+ ### Shared Utilities
79
+
80
+ - **`ffmpeg_utils.py`**: Core utilities for running ffmpeg/ffprobe commands
81
+ - `ensure_ffmpeg()`: Verifies ffmpeg is installed
82
+ - `run_ffmpeg()`: Executes ffmpeg commands with error handling
83
+ - `get_video_info()`: Retrieves video metadata using ffprobe
84
+ - `check_output_file()`: Handles output file overwrite checks
85
+
86
+ - **`config.py`**: Default settings for video encoding (codec, quality, preset)
87
+
88
+ ### Global Context
89
+
90
+ The verbose flag (`--verbose` / `-v`) is stored in the Typer context object and accessed by commands via `ctx.obj.get("VERBOSE", False)`.
91
+
92
+ ## Adding a New Command
93
+
94
+ 1. Create `src/vidio_cli/commands/<command_name>.py`
95
+ 2. Implement the `register(app: typer.Typer)` function
96
+ 3. Implement the command function with appropriate arguments/options
97
+ 4. Use `ffmpeg_utils` functions for running ffmpeg operations
98
+ 5. Access verbose flag from context: `verbose = ctx.obj.get("VERBOSE", False)`
99
+ 6. Create corresponding test file: `tests/test_<command_name>.py`
100
+ 7. The command will be automatically discovered and registered (no manual registration needed)
101
+
102
+ ## Testing
103
+
104
+ Tests use pytest and should cover:
105
+ - Command registration
106
+ - Argument/option validation
107
+ - ffmpeg command generation (can mock `run_ffmpeg`)
108
+ - Error handling
109
+
110
+ Test videos can be placed in `tests/assets/` if needed.
111
+
112
+ ## Dependencies
113
+
114
+ - **typer**: CLI framework with type annotations
115
+ - **rich**: Terminal formatting and colors
116
+ - **ffmpeg**: External dependency (must be in PATH)
117
+
118
+ ## PyPI Release Workflow
119
+
120
+ Use this release flow for `vidio-cli`:
121
+
122
+ 1. Update version in `pyproject.toml`.
123
+ 2. Update `CHANGELOG.md` with the release date and notable changes.
124
+ 3. Run quality checks locally:
125
+ - `uv run ruff check .`
126
+ - `uv run pytest -q`
127
+ 4. Build and validate the distribution:
128
+ - `uv build`
129
+ - `python -m twine check dist/*`
130
+ 5. Commit and tag:
131
+ - `git tag v<version>`
132
+ - `git push origin main --tags`
133
+ 6. Publish:
134
+ - Preferred: create a GitHub Release from the version tag; this triggers `.github/workflows/publish.yml` and publishes to PyPI.
135
+ - Optional: run the publish workflow manually with `workflow_dispatch` to publish to TestPyPI first.
136
+
137
+ ### Trusted Publishing Setup
138
+
139
+ The project should use PyPI Trusted Publishing with GitHub Actions:
140
+
141
+ - In PyPI/TestPyPI project settings, add a trusted publisher for this repository.
142
+ - Point it to workflow file `.github/workflows/publish.yml`.
143
+ - Restrict to the `main` branch and/or release tags as needed.
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## 0.1.0 - 2026-02-06
6
+
7
+ First public release of `vidio-cli`.
8
+
9
+ - Added core commands for everyday video workflows:
10
+ - `list` / `ls`
11
+ - `info`
12
+ - `trim`
13
+ - `resize`
14
+ - `crop`
15
+ - `concat`
16
+ - `grid`
17
+ - `to-gif`
18
+ - Added dynamic command discovery and registration.
19
+ - Added test coverage for command behavior and output handling.
20
+ - Added distribution build support for wheel and source tarball.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matan Ben-Yosef
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,238 @@
1
+ Metadata-Version: 2.4
2
+ Name: vidio-cli
3
+ Version: 0.1.0
4
+ Summary: A simple and easy-to-use ffmpeg wrapper for common video operations
5
+ Project-URL: Homepage, https://github.com/matanb/vidio-cli
6
+ Project-URL: Repository, https://github.com/matanb/vidio-cli
7
+ Project-URL: Issues, https://github.com/matanb/vidio-cli/issues
8
+ Author-email: Matan Ben-Yosef <matan.ben.yosef@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: cli,ffmpeg,gif,media,video
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
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: Topic :: Multimedia :: Video
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: rich>=13.5.0
26
+ Requires-Dist: typer>=0.9.0
27
+ Description-Content-Type: text/markdown
28
+
29
+ # vidio-cli
30
+
31
+ A simple and easy-to-use ffmpeg wrapper for common video operations.
32
+
33
+ ## Features
34
+
35
+ - **Stupidly Easy:** Simple CLI commands for common video operations.
36
+ - **Powerful Defaults:** Sensible defaults make it work great out of the box.
37
+ - **Focused v1 Scope:** Core commands people use repeatedly, without feature bloat.
38
+
39
+ ## Requirements
40
+
41
+ - Python 3.10+
42
+ - ffmpeg (must be installed and in your PATH)
43
+
44
+ ## v1 Command Scope
45
+
46
+ `vidio-cli` 0.1.0 intentionally focuses on practical, high-frequency tasks:
47
+
48
+ - `list` / `ls`: find video files quickly
49
+ - `info`: inspect metadata and streams
50
+ - `trim`: cut clips by time range
51
+ - `resize`: scale for delivery targets
52
+ - `crop`: convert to target aspect ratios
53
+ - `concat`: place videos side-by-side or stacked
54
+ - `grid`: build multi-video collages
55
+ - `to-gif`: convert clips to GIF with good defaults
56
+
57
+ Commands that are less consistently useful (for example, niche one-off transforms) are deferred to later releases.
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ # Using pip
63
+ pip install vidio-cli
64
+
65
+ # Using uv
66
+ uv pip install vidio-cli
67
+ ```
68
+
69
+ ## Usage
70
+
71
+ ```bash
72
+ # Get help
73
+ vidio --help
74
+
75
+ # Get help for a specific command
76
+ vidio concat --help
77
+ ```
78
+
79
+ ### Concatenate Videos
80
+
81
+ ```bash
82
+ # Concatenate videos horizontally
83
+ vidio concat video1.mp4 video2.mp4 output.mp4
84
+
85
+ # Concatenate videos vertically
86
+ vidio concat video1.mp4 video2.mp4 output.mp4 --vertical
87
+ ```
88
+
89
+ ### List Video Files
90
+
91
+ ```bash
92
+ # List videos in current directory (ls-style output)
93
+ vidio list
94
+ # or use the shorter alias
95
+ vidio ls
96
+
97
+ # List with detailed information (duration, resolution, codec)
98
+ vidio ls --list
99
+
100
+ # Search recursively in subdirectories
101
+ vidio ls --recursive
102
+
103
+ # Use table format instead of ls-style
104
+ vidio ls --table
105
+
106
+ # Get JSON output for scripting
107
+ vidio ls --json
108
+ ```
109
+
110
+ ### Video Information
111
+
112
+ ```bash
113
+ # Display detailed metadata about a video file
114
+ vidio info video.mp4
115
+
116
+ # Get machine-readable JSON output
117
+ vidio info video.mp4 --json
118
+
119
+ # Calculate exact frame count (slower but accurate)
120
+ vidio info video.mp4 --exact-frames
121
+ ```
122
+
123
+ ### Create Video Grids
124
+
125
+ ```bash
126
+ # Arrange videos in a 2x2 grid (auto-calculated)
127
+ vidio grid video1.mp4 video2.mp4 video3.mp4 video4.mp4 output.mp4
128
+
129
+ # Specify grid dimensions
130
+ vidio grid video1.mp4 video2.mp4 video3.mp4 video4.mp4 output.mp4 --rows 2 --cols 2
131
+
132
+ # Control cell size and add padding
133
+ vidio grid video1.mp4 video2.mp4 video3.mp4 video4.mp4 output.mp4 --width 640 --height 360 --padding 10
134
+
135
+ # Change background color
136
+ vidio grid video1.mp4 video2.mp4 video3.mp4 video4.mp4 output.mp4 --background white
137
+ ```
138
+
139
+ ### Trim Videos
140
+
141
+ ```bash
142
+ # Trim from 30 seconds to 90 seconds
143
+ vidio trim input.mp4 output.mp4 --start 30 --end 90
144
+
145
+ # Trim from 1:30 for 45 seconds duration
146
+ vidio trim input.mp4 output.mp4 --start 1:30 --duration 45
147
+
148
+ # Trim from beginning to 2:15
149
+ vidio trim input.mp4 output.mp4 --end 2:15
150
+
151
+ # Trim using different time formats
152
+ vidio trim input.mp4 output.mp4 --start 0:01:30 --end 0:02:45
153
+ ```
154
+
155
+ ### Resize Videos
156
+
157
+ ```bash
158
+ # Resize to specific dimensions (maintains aspect ratio)
159
+ vidio resize input.mp4 output.mp4 --width 1920 --height 1080
160
+
161
+ # Scale by percentage
162
+ vidio resize input.mp4 output.mp4 --scale 0.5 # 50% of original size
163
+
164
+ # Resize width only (height auto-calculated)
165
+ vidio resize input.mp4 output.mp4 --width 1280
166
+
167
+ # Resize height only (width auto-calculated)
168
+ vidio resize input.mp4 output.mp4 --height 720
169
+
170
+ # Force exact dimensions (may distort)
171
+ vidio resize input.mp4 output.mp4 -w 1920 -h 1080 --force-aspect
172
+ ```
173
+
174
+ ### Convert to GIF
175
+
176
+ ```bash
177
+ # Basic conversion with optimized palette
178
+ vidio to-gif video.mp4 output.gif
179
+
180
+ # High quality with custom frame rate
181
+ vidio to-gif video.mp4 output.gif --fps 15 --quality high
182
+
183
+ # Small file size for web
184
+ vidio to-gif video.mp4 output.gif --scale 0.3 --fps 8 --quality low
185
+
186
+ # Convert specific time range
187
+ vidio to-gif video.mp4 output.gif --start 10 --duration 5
188
+
189
+ # Custom width with different dithering
190
+ vidio to-gif video.mp4 output.gif --width 800 --dither bayer
191
+
192
+ # Fast conversion (skip optimization)
193
+ vidio to-gif video.mp4 output.gif --no-optimize
194
+ ```
195
+
196
+ ### Crop Videos
197
+
198
+ ```bash
199
+ # Crop to center square (perfect for Instagram)
200
+ vidio crop input.mp4 output.mp4 --preset center-square
201
+
202
+ # Crop to 16:9 aspect ratio
203
+ vidio crop input.mp4 output.mp4 --preset 16:9
204
+
205
+ # Crop to 9:16 aspect ratio (vertical/portrait)
206
+ vidio crop input.mp4 output.mp4 --preset 9:16
207
+
208
+ # Crop to 4:3 aspect ratio
209
+ vidio crop input.mp4 output.mp4 --preset 4:3
210
+
211
+ # Custom crop region (centered)
212
+ vidio crop input.mp4 output.mp4 --width 1280 --height 720
213
+
214
+ # Custom crop with specific position
215
+ vidio crop input.mp4 output.mp4 -w 1280 -h 720 --x 100 --y 50
216
+ ```
217
+
218
+
219
+ ## Development
220
+
221
+ ```bash
222
+ # Clone the repo
223
+ git clone https://github.com/matanb/vidio-cli.git
224
+ cd vidio-cli
225
+
226
+ # Install dependencies
227
+ uv sync --group dev
228
+
229
+ # Run tests
230
+ uv run pytest -q
231
+
232
+ # Run lint
233
+ uv run ruff check .
234
+ ```
235
+
236
+ ## License
237
+
238
+ MIT