minicut 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.
- minicut-0.1.0/.gitignore +220 -0
- minicut-0.1.0/PKG-INFO +141 -0
- minicut-0.1.0/README.md +130 -0
- minicut-0.1.0/pyproject.toml +36 -0
- minicut-0.1.0/src/minicut/__init__.py +3 -0
- minicut-0.1.0/src/minicut/api/__init__.py +0 -0
- minicut-0.1.0/src/minicut/api/routes_jobs.py +56 -0
- minicut-0.1.0/src/minicut/api/routes_media.py +131 -0
- minicut-0.1.0/src/minicut/api/schemas.py +74 -0
- minicut-0.1.0/src/minicut/app.py +70 -0
- minicut-0.1.0/src/minicut/application/__init__.py +1 -0
- minicut-0.1.0/src/minicut/application/media_query_service.py +195 -0
- minicut-0.1.0/src/minicut/application/process_video_service.py +75 -0
- minicut-0.1.0/src/minicut/cli.py +47 -0
- minicut-0.1.0/src/minicut/config.py +31 -0
- minicut-0.1.0/src/minicut/diagnostics.py +36 -0
- minicut-0.1.0/src/minicut/domain/__init__.py +0 -0
- minicut-0.1.0/src/minicut/domain/models.py +57 -0
- minicut-0.1.0/src/minicut/domain/services.py +34 -0
- minicut-0.1.0/src/minicut/infra/__init__.py +0 -0
- minicut-0.1.0/src/minicut/infra/ffmpeg_adapter.py +46 -0
- minicut-0.1.0/src/minicut/infra/ffprobe_adapter.py +64 -0
- minicut-0.1.0/src/minicut/infra/fs_discovery.py +22 -0
- minicut-0.1.0/src/minicut/infra/output_naming_fs.py +22 -0
- minicut-0.1.0/src/minicut/jobs/__init__.py +0 -0
- minicut-0.1.0/src/minicut/jobs/inproc_queue.py +61 -0
- minicut-0.1.0/src/minicut/static/css/app.css +355 -0
- minicut-0.1.0/src/minicut/static/index.html +135 -0
- minicut-0.1.0/src/minicut/static/js/features/editor/cropStage.js +420 -0
- minicut-0.1.0/src/minicut/static/js/features/editor/editor.js +301 -0
- minicut-0.1.0/src/minicut/static/js/features/editor/processedVideos.js +150 -0
- minicut-0.1.0/src/minicut/static/js/features/editor/stats.js +42 -0
- minicut-0.1.0/src/minicut/static/js/features/editor/utils.js +21 -0
- minicut-0.1.0/src/minicut/static/js/features/jobs/jobs.js +63 -0
- minicut-0.1.0/src/minicut/static/js/features/workspace/list.js +129 -0
- minicut-0.1.0/src/minicut/static/js/main.js +47 -0
- minicut-0.1.0/src/minicut/static/js/shared/api.js +61 -0
minicut-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# UV
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
201
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
203
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
minicut-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: minicut
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local browser app for dataset video trimming and cropping
|
|
5
|
+
Author: Minicut
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: fastapi>=0.116.0
|
|
8
|
+
Requires-Dist: pydantic>=2.9.0
|
|
9
|
+
Requires-Dist: uvicorn>=0.31.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Minicut
|
|
13
|
+
|
|
14
|
+
Minicut is a local-first browser app for dataset preprocessing.
|
|
15
|
+
It trims and crops videos, converts output FPS, and keeps original files unchanged.
|
|
16
|
+
|
|
17
|
+
## What Is Implemented
|
|
18
|
+
|
|
19
|
+
- Workspace video discovery from a single directory (non-recursive).
|
|
20
|
+
- Workspace list with processed-file counts.
|
|
21
|
+
- Workspace list sorting by:
|
|
22
|
+
- name
|
|
23
|
+
- processed count
|
|
24
|
+
- asc or desc order
|
|
25
|
+
- Active/selected source video highlighting in the workspace list.
|
|
26
|
+
- Original video editor with:
|
|
27
|
+
- Photoshop-style crop rectangle (draw, move, edge/corner resize)
|
|
28
|
+
- trim preview controls (preview range, seek begin, seek end)
|
|
29
|
+
- custom play/pause and scrub controls
|
|
30
|
+
- Trim models:
|
|
31
|
+
- begin second + duration in seconds
|
|
32
|
+
- begin second + duration in frames (using output FPS)
|
|
33
|
+
- Output FPS conversion (default: 24).
|
|
34
|
+
- Processed video panel:
|
|
35
|
+
- list all processed variants
|
|
36
|
+
- preview selected output
|
|
37
|
+
- open output directly
|
|
38
|
+
- delete output
|
|
39
|
+
- processed stats table
|
|
40
|
+
- Original and processed stats tables:
|
|
41
|
+
- fps, duration, resolution, codec, format, size, created/modified timestamps
|
|
42
|
+
- Background in-process job execution with job polling/status updates.
|
|
43
|
+
- Automatic workspace refresh on job completion (including a follow-up refresh for count consistency).
|
|
44
|
+
|
|
45
|
+
## Processing Behavior
|
|
46
|
+
|
|
47
|
+
- Output files are written to an output folder under the workspace.
|
|
48
|
+
- Default output folder name: `processed`
|
|
49
|
+
- Source files are never overwritten.
|
|
50
|
+
- Output naming is collision-safe:
|
|
51
|
+
- `<stem>_processed.mp4`
|
|
52
|
+
- `<stem>_processed_1.mp4`
|
|
53
|
+
- `<stem>_processed_2.mp4`
|
|
54
|
+
- ...
|
|
55
|
+
- ffmpeg pipeline currently:
|
|
56
|
+
- trim window (`-ss`, `-to`)
|
|
57
|
+
- video filters: `crop`, `fps`
|
|
58
|
+
- video codec: `libx264`
|
|
59
|
+
- audio codec: `aac`
|
|
60
|
+
|
|
61
|
+
## Requirements
|
|
62
|
+
|
|
63
|
+
- Python 3.11+
|
|
64
|
+
- ffmpeg on PATH
|
|
65
|
+
- ffprobe on PATH
|
|
66
|
+
- uv package manager
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv sync
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Run
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uv run minicut
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
By default this uses:
|
|
81
|
+
|
|
82
|
+
- workspace: current directory
|
|
83
|
+
- output folder: `processed`
|
|
84
|
+
- host: `127.0.0.1`
|
|
85
|
+
- port: `8765`
|
|
86
|
+
|
|
87
|
+
### Common CLI options
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uv run minicut --workspace /path/to/videos
|
|
91
|
+
uv run minicut --workspace /path/to/videos --output-folder processed
|
|
92
|
+
uv run minicut --host 0.0.0.0 --port 8765
|
|
93
|
+
uv run minicut --no-open-browser
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Basic Workflow
|
|
97
|
+
|
|
98
|
+
1. Start Minicut.
|
|
99
|
+
2. Select a source video from Workspace Videos.
|
|
100
|
+
3. Set trim mode, begin, duration, crop rectangle, and output FPS.
|
|
101
|
+
4. Optionally preview trim range in the original player.
|
|
102
|
+
5. Run processing.
|
|
103
|
+
6. Inspect output in Processed Videos and Processed Preview.
|
|
104
|
+
7. Repeat as needed; originals stay intact.
|
|
105
|
+
|
|
106
|
+
## API Summary
|
|
107
|
+
|
|
108
|
+
Media endpoints:
|
|
109
|
+
|
|
110
|
+
- `GET /api/media/videos`
|
|
111
|
+
- `GET /api/media/metadata`
|
|
112
|
+
- `GET /api/media/stats`
|
|
113
|
+
- `GET /api/media/file`
|
|
114
|
+
- `GET /api/media/processed-preview`
|
|
115
|
+
- `GET /api/media/processed-videos`
|
|
116
|
+
- `DELETE /api/media/processed-file`
|
|
117
|
+
|
|
118
|
+
Job endpoints:
|
|
119
|
+
|
|
120
|
+
- `POST /api/jobs/process`
|
|
121
|
+
- `GET /api/jobs/{job_id}`
|
|
122
|
+
|
|
123
|
+
## Safety Constraints
|
|
124
|
+
|
|
125
|
+
- Process source path must be inside the workspace directory.
|
|
126
|
+
- File serving is restricted to workspace or output directory.
|
|
127
|
+
- Delete is restricted to output directory only.
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
Run tests:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
uv run pytest
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Known Limitations
|
|
138
|
+
|
|
139
|
+
- Workspace scan is non-recursive.
|
|
140
|
+
- Job queue is in-memory (no persistence across restart).
|
|
141
|
+
- Processing currently targets H.264/AAC MP4 output.
|
minicut-0.1.0/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Minicut
|
|
2
|
+
|
|
3
|
+
Minicut is a local-first browser app for dataset preprocessing.
|
|
4
|
+
It trims and crops videos, converts output FPS, and keeps original files unchanged.
|
|
5
|
+
|
|
6
|
+
## What Is Implemented
|
|
7
|
+
|
|
8
|
+
- Workspace video discovery from a single directory (non-recursive).
|
|
9
|
+
- Workspace list with processed-file counts.
|
|
10
|
+
- Workspace list sorting by:
|
|
11
|
+
- name
|
|
12
|
+
- processed count
|
|
13
|
+
- asc or desc order
|
|
14
|
+
- Active/selected source video highlighting in the workspace list.
|
|
15
|
+
- Original video editor with:
|
|
16
|
+
- Photoshop-style crop rectangle (draw, move, edge/corner resize)
|
|
17
|
+
- trim preview controls (preview range, seek begin, seek end)
|
|
18
|
+
- custom play/pause and scrub controls
|
|
19
|
+
- Trim models:
|
|
20
|
+
- begin second + duration in seconds
|
|
21
|
+
- begin second + duration in frames (using output FPS)
|
|
22
|
+
- Output FPS conversion (default: 24).
|
|
23
|
+
- Processed video panel:
|
|
24
|
+
- list all processed variants
|
|
25
|
+
- preview selected output
|
|
26
|
+
- open output directly
|
|
27
|
+
- delete output
|
|
28
|
+
- processed stats table
|
|
29
|
+
- Original and processed stats tables:
|
|
30
|
+
- fps, duration, resolution, codec, format, size, created/modified timestamps
|
|
31
|
+
- Background in-process job execution with job polling/status updates.
|
|
32
|
+
- Automatic workspace refresh on job completion (including a follow-up refresh for count consistency).
|
|
33
|
+
|
|
34
|
+
## Processing Behavior
|
|
35
|
+
|
|
36
|
+
- Output files are written to an output folder under the workspace.
|
|
37
|
+
- Default output folder name: `processed`
|
|
38
|
+
- Source files are never overwritten.
|
|
39
|
+
- Output naming is collision-safe:
|
|
40
|
+
- `<stem>_processed.mp4`
|
|
41
|
+
- `<stem>_processed_1.mp4`
|
|
42
|
+
- `<stem>_processed_2.mp4`
|
|
43
|
+
- ...
|
|
44
|
+
- ffmpeg pipeline currently:
|
|
45
|
+
- trim window (`-ss`, `-to`)
|
|
46
|
+
- video filters: `crop`, `fps`
|
|
47
|
+
- video codec: `libx264`
|
|
48
|
+
- audio codec: `aac`
|
|
49
|
+
|
|
50
|
+
## Requirements
|
|
51
|
+
|
|
52
|
+
- Python 3.11+
|
|
53
|
+
- ffmpeg on PATH
|
|
54
|
+
- ffprobe on PATH
|
|
55
|
+
- uv package manager
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv sync
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Run
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv run minicut
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
By default this uses:
|
|
70
|
+
|
|
71
|
+
- workspace: current directory
|
|
72
|
+
- output folder: `processed`
|
|
73
|
+
- host: `127.0.0.1`
|
|
74
|
+
- port: `8765`
|
|
75
|
+
|
|
76
|
+
### Common CLI options
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv run minicut --workspace /path/to/videos
|
|
80
|
+
uv run minicut --workspace /path/to/videos --output-folder processed
|
|
81
|
+
uv run minicut --host 0.0.0.0 --port 8765
|
|
82
|
+
uv run minicut --no-open-browser
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Basic Workflow
|
|
86
|
+
|
|
87
|
+
1. Start Minicut.
|
|
88
|
+
2. Select a source video from Workspace Videos.
|
|
89
|
+
3. Set trim mode, begin, duration, crop rectangle, and output FPS.
|
|
90
|
+
4. Optionally preview trim range in the original player.
|
|
91
|
+
5. Run processing.
|
|
92
|
+
6. Inspect output in Processed Videos and Processed Preview.
|
|
93
|
+
7. Repeat as needed; originals stay intact.
|
|
94
|
+
|
|
95
|
+
## API Summary
|
|
96
|
+
|
|
97
|
+
Media endpoints:
|
|
98
|
+
|
|
99
|
+
- `GET /api/media/videos`
|
|
100
|
+
- `GET /api/media/metadata`
|
|
101
|
+
- `GET /api/media/stats`
|
|
102
|
+
- `GET /api/media/file`
|
|
103
|
+
- `GET /api/media/processed-preview`
|
|
104
|
+
- `GET /api/media/processed-videos`
|
|
105
|
+
- `DELETE /api/media/processed-file`
|
|
106
|
+
|
|
107
|
+
Job endpoints:
|
|
108
|
+
|
|
109
|
+
- `POST /api/jobs/process`
|
|
110
|
+
- `GET /api/jobs/{job_id}`
|
|
111
|
+
|
|
112
|
+
## Safety Constraints
|
|
113
|
+
|
|
114
|
+
- Process source path must be inside the workspace directory.
|
|
115
|
+
- File serving is restricted to workspace or output directory.
|
|
116
|
+
- Delete is restricted to output directory only.
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
Run tests:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
uv run pytest
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Known Limitations
|
|
127
|
+
|
|
128
|
+
- Workspace scan is non-recursive.
|
|
129
|
+
- Job queue is in-memory (no persistence across restart).
|
|
130
|
+
- Processing currently targets H.264/AAC MP4 output.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "minicut"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Local browser app for dataset video trimming and cropping"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Minicut" }
|
|
13
|
+
]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"fastapi>=0.116.0",
|
|
16
|
+
"uvicorn>=0.31.0",
|
|
17
|
+
"pydantic>=2.9.0"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
minicut = "minicut.cli:main"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["src/minicut"]
|
|
25
|
+
|
|
26
|
+
[tool.hatch.build]
|
|
27
|
+
include = [
|
|
28
|
+
"src/minicut/static/**",
|
|
29
|
+
"src/minicut/**/*.py",
|
|
30
|
+
"README.md"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=8.3.0"
|
|
36
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
6
|
+
|
|
7
|
+
from minicut.api.schemas import JobResponse, ProcessVideoRequest
|
|
8
|
+
from minicut.application.process_video_service import ProcessVideoService, normalize_trim, validate_request
|
|
9
|
+
from minicut.app import AppState, get_app_state
|
|
10
|
+
from minicut.domain.models import TrimRange
|
|
11
|
+
|
|
12
|
+
router = APIRouter(prefix="/api/jobs", tags=["jobs"])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.post("/process", response_model=JobResponse)
|
|
16
|
+
def process_video(
|
|
17
|
+
payload: ProcessVideoRequest,
|
|
18
|
+
state: AppState = Depends(get_app_state),
|
|
19
|
+
) -> JobResponse:
|
|
20
|
+
service = ProcessVideoService(
|
|
21
|
+
workspace_dir=state.config.workspace_dir,
|
|
22
|
+
probe=state.probe,
|
|
23
|
+
jobs=state.jobs,
|
|
24
|
+
)
|
|
25
|
+
try:
|
|
26
|
+
job = service.submit(payload)
|
|
27
|
+
except ValueError as exc:
|
|
28
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
29
|
+
return JobResponse(
|
|
30
|
+
id=job.id,
|
|
31
|
+
state=job.state,
|
|
32
|
+
message=job.message,
|
|
33
|
+
result_path=str(job.result_path) if job.result_path else None,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@router.get("/{job_id}", response_model=JobResponse)
|
|
38
|
+
def get_job(job_id: str, state: AppState = Depends(get_app_state)) -> JobResponse:
|
|
39
|
+
job = state.jobs.get(job_id)
|
|
40
|
+
if job is None:
|
|
41
|
+
raise HTTPException(status_code=404, detail="Job not found")
|
|
42
|
+
|
|
43
|
+
return JobResponse(
|
|
44
|
+
id=job.id,
|
|
45
|
+
state=job.state,
|
|
46
|
+
message=job.message,
|
|
47
|
+
result_path=str(job.result_path) if job.result_path else None,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _normalize_trim(payload: ProcessVideoRequest) -> TrimRange:
|
|
52
|
+
return normalize_trim(payload)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _validate_request(trim: TrimRange, payload: ProcessVideoRequest, width: int, height: int, duration: float) -> None:
|
|
56
|
+
validate_request(trim, payload, width, height, duration)
|