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.
Files changed (37) hide show
  1. minicut-0.1.0/.gitignore +220 -0
  2. minicut-0.1.0/PKG-INFO +141 -0
  3. minicut-0.1.0/README.md +130 -0
  4. minicut-0.1.0/pyproject.toml +36 -0
  5. minicut-0.1.0/src/minicut/__init__.py +3 -0
  6. minicut-0.1.0/src/minicut/api/__init__.py +0 -0
  7. minicut-0.1.0/src/minicut/api/routes_jobs.py +56 -0
  8. minicut-0.1.0/src/minicut/api/routes_media.py +131 -0
  9. minicut-0.1.0/src/minicut/api/schemas.py +74 -0
  10. minicut-0.1.0/src/minicut/app.py +70 -0
  11. minicut-0.1.0/src/minicut/application/__init__.py +1 -0
  12. minicut-0.1.0/src/minicut/application/media_query_service.py +195 -0
  13. minicut-0.1.0/src/minicut/application/process_video_service.py +75 -0
  14. minicut-0.1.0/src/minicut/cli.py +47 -0
  15. minicut-0.1.0/src/minicut/config.py +31 -0
  16. minicut-0.1.0/src/minicut/diagnostics.py +36 -0
  17. minicut-0.1.0/src/minicut/domain/__init__.py +0 -0
  18. minicut-0.1.0/src/minicut/domain/models.py +57 -0
  19. minicut-0.1.0/src/minicut/domain/services.py +34 -0
  20. minicut-0.1.0/src/minicut/infra/__init__.py +0 -0
  21. minicut-0.1.0/src/minicut/infra/ffmpeg_adapter.py +46 -0
  22. minicut-0.1.0/src/minicut/infra/ffprobe_adapter.py +64 -0
  23. minicut-0.1.0/src/minicut/infra/fs_discovery.py +22 -0
  24. minicut-0.1.0/src/minicut/infra/output_naming_fs.py +22 -0
  25. minicut-0.1.0/src/minicut/jobs/__init__.py +0 -0
  26. minicut-0.1.0/src/minicut/jobs/inproc_queue.py +61 -0
  27. minicut-0.1.0/src/minicut/static/css/app.css +355 -0
  28. minicut-0.1.0/src/minicut/static/index.html +135 -0
  29. minicut-0.1.0/src/minicut/static/js/features/editor/cropStage.js +420 -0
  30. minicut-0.1.0/src/minicut/static/js/features/editor/editor.js +301 -0
  31. minicut-0.1.0/src/minicut/static/js/features/editor/processedVideos.js +150 -0
  32. minicut-0.1.0/src/minicut/static/js/features/editor/stats.js +42 -0
  33. minicut-0.1.0/src/minicut/static/js/features/editor/utils.js +21 -0
  34. minicut-0.1.0/src/minicut/static/js/features/jobs/jobs.js +63 -0
  35. minicut-0.1.0/src/minicut/static/js/features/workspace/list.js +129 -0
  36. minicut-0.1.0/src/minicut/static/js/main.js +47 -0
  37. minicut-0.1.0/src/minicut/static/js/shared/api.js +61 -0
@@ -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.
@@ -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
+ ]
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "0.1.0"
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)