shuttlecheck 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.
- shuttlecheck-0.1.0/LICENSE +43 -0
- shuttlecheck-0.1.0/PKG-INFO +145 -0
- shuttlecheck-0.1.0/PYPI.md +112 -0
- shuttlecheck-0.1.0/README.md +392 -0
- shuttlecheck-0.1.0/pyproject.toml +57 -0
- shuttlecheck-0.1.0/setup.cfg +4 -0
- shuttlecheck-0.1.0/shuttlecheck/__init__.py +39 -0
- shuttlecheck-0.1.0/shuttlecheck/checks.py +156 -0
- shuttlecheck-0.1.0/shuttlecheck/cli.py +240 -0
- shuttlecheck-0.1.0/shuttlecheck/delivery.py +505 -0
- shuttlecheck-0.1.0/shuttlecheck/extensions.py +53 -0
- shuttlecheck-0.1.0/shuttlecheck/infer.py +730 -0
- shuttlecheck-0.1.0/shuttlecheck/patterns.py +350 -0
- shuttlecheck-0.1.0/shuttlecheck/report.py +146 -0
- shuttlecheck-0.1.0/shuttlecheck/schedule.py +215 -0
- shuttlecheck-0.1.0/shuttlecheck/schema.json +1274 -0
- shuttlecheck-0.1.0/shuttlecheck/sidecar.py +330 -0
- shuttlecheck-0.1.0/shuttlecheck/spec.py +241 -0
- shuttlecheck-0.1.0/shuttlecheck/ui.py +332 -0
- shuttlecheck-0.1.0/shuttlecheck/ui_assets/app.css +186 -0
- shuttlecheck-0.1.0/shuttlecheck/ui_assets/app.html +145 -0
- shuttlecheck-0.1.0/shuttlecheck/ui_assets/app.js +396 -0
- shuttlecheck-0.1.0/shuttlecheck/validate.py +335 -0
- shuttlecheck-0.1.0/shuttlecheck/version.py +1 -0
- shuttlecheck-0.1.0/shuttlecheck/walkthrough.py +321 -0
- shuttlecheck-0.1.0/shuttlecheck.egg-info/PKG-INFO +145 -0
- shuttlecheck-0.1.0/shuttlecheck.egg-info/SOURCES.txt +35 -0
- shuttlecheck-0.1.0/shuttlecheck.egg-info/dependency_links.txt +1 -0
- shuttlecheck-0.1.0/shuttlecheck.egg-info/entry_points.txt +2 -0
- shuttlecheck-0.1.0/shuttlecheck.egg-info/requires.txt +8 -0
- shuttlecheck-0.1.0/shuttlecheck.egg-info/top_level.txt +1 -0
- shuttlecheck-0.1.0/tests/test_cli.py +89 -0
- shuttlecheck-0.1.0/tests/test_delivery.py +341 -0
- shuttlecheck-0.1.0/tests/test_infer.py +259 -0
- shuttlecheck-0.1.0/tests/test_ui.py +251 -0
- shuttlecheck-0.1.0/tests/test_validate.py +320 -0
- shuttlecheck-0.1.0/tests/test_verify.py +132 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
ShuttleCheck — Licence
|
|
2
|
+
Copyright (c) 2026 MVRK Systems. All rights reserved.
|
|
3
|
+
|
|
4
|
+
This software is made available free of charge. It is not open source.
|
|
5
|
+
|
|
6
|
+
PERMITTED
|
|
7
|
+
|
|
8
|
+
You may install this software and use it, without charge, for any purpose
|
|
9
|
+
including commercial work, on any number of machines, for as long as you
|
|
10
|
+
like. No account, licence key or registration is required.
|
|
11
|
+
|
|
12
|
+
You may run it against any material you are lawfully in possession of, and
|
|
13
|
+
you may share the reports it produces with anyone.
|
|
14
|
+
|
|
15
|
+
NOT PERMITTED
|
|
16
|
+
|
|
17
|
+
You may not redistribute this software, in whole or in part, in original or
|
|
18
|
+
modified form, whether or not for a fee.
|
|
19
|
+
|
|
20
|
+
You may not modify, adapt, translate, reverse engineer, decompile or create
|
|
21
|
+
derivative works from it, except to the extent that this restriction is
|
|
22
|
+
prohibited by applicable law.
|
|
23
|
+
|
|
24
|
+
You may not remove or alter this notice, or any attribution contained in the
|
|
25
|
+
software.
|
|
26
|
+
|
|
27
|
+
You may not sublicense, rent, lease or sell it, or offer it as a hosted or
|
|
28
|
+
managed service.
|
|
29
|
+
|
|
30
|
+
NO WARRANTY
|
|
31
|
+
|
|
32
|
+
This software is provided "as is", without warranty of any kind, express or
|
|
33
|
+
implied, including but not limited to the warranties of merchantability,
|
|
34
|
+
fitness for a particular purpose and non-infringement.
|
|
35
|
+
|
|
36
|
+
In no event shall MVRK Systems be liable for any claim, damages or other
|
|
37
|
+
liability, whether in an action of contract, tort or otherwise, arising from,
|
|
38
|
+
out of or in connection with the software or its use.
|
|
39
|
+
|
|
40
|
+
This software reports on files. It does not modify them. You remain
|
|
41
|
+
responsible for verifying your own deliverables.
|
|
42
|
+
|
|
43
|
+
Questions, and licences permitting anything above: hello@mvrk.systems
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shuttlecheck
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Check media filenames, folder structure and deliverables against a written convention. Read-only.
|
|
5
|
+
Author-email: MVRK Systems <hello@mvrk.systems>
|
|
6
|
+
License: Proprietary — free to use, no redistribution
|
|
7
|
+
Project-URL: Homepage, https://mvrk.systems/shuttlecheck
|
|
8
|
+
Project-URL: Documentation, https://mvrk.systems/shuttlecheck#checks
|
|
9
|
+
Keywords: broadcast,post-production,dailies,media,naming-convention,file-naming,delivery,conformance,validation,vfx,video
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Environment :: Web Environment
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: Other/Proprietary License
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Multimedia :: Video
|
|
22
|
+
Classifier: Topic :: System :: Archiving
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: PyYAML>=6.0
|
|
28
|
+
Requires-Dist: jsonschema>=4.20
|
|
29
|
+
Requires-Dist: tzdata>=2024.1; sys_platform == "win32"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# ShuttleCheck
|
|
35
|
+
|
|
36
|
+
**Check media filenames, folder structure and deliverables against a written
|
|
37
|
+
convention.** Nothing in this package renames, moves or deletes anything.
|
|
38
|
+
|
|
39
|
+
On a production the naming convention is a PDF or a message in a chat, and
|
|
40
|
+
every vendor on the job breaks it. Nobody finds out until a conform fails in
|
|
41
|
+
post, or a lab rejects a delivery at four in the morning.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install shuttlecheck
|
|
45
|
+
shuttlecheck ui
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Point it at a job you have already finished. It works out the convention you
|
|
49
|
+
are already using, then shows you every file that does not follow it.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## It reads the convention off your own files
|
|
54
|
+
|
|
55
|
+
Nobody can describe their naming convention in the abstract, and nobody should
|
|
56
|
+
have to. Point `init` at a finished job and it recovers the fields, tells you
|
|
57
|
+
how confident it is in each one, and flags anything it had to guess at.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
FIELDS RECOVERED
|
|
61
|
+
{project} constant 100% NRD24
|
|
62
|
+
{day} integer 90% DAY1, DAY2
|
|
63
|
+
{stage} enum 80% HALLA, HALLB
|
|
64
|
+
{session} integer 90% SES001, SES002
|
|
65
|
+
{title} slug 85% opening-remarks, keynote
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
It handles conventions that disagree with themselves — several accepted forms
|
|
69
|
+
per field, optional elements, free-text titles containing the separator —
|
|
70
|
+
because real ones do.
|
|
71
|
+
|
|
72
|
+
## Then it tells you what breaks it
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
SHUTTLECHECK CONFORMANCE REPORT
|
|
76
|
+
files scanned 412
|
|
77
|
+
conforming 398
|
|
78
|
+
errors 9 ingest will fail
|
|
79
|
+
warnings 5 inconsistent, will work
|
|
80
|
+
|
|
81
|
+
ERROR filename-mismatch (6)
|
|
82
|
+
D02/MAIN/MASTERS/AUT26_D02_MAINSTAGE_S114_v01.mov
|
|
83
|
+
the stage field does not match what the spec allows
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Findings name the field at fault, not just the file. Errors mean an ingest will
|
|
87
|
+
reject it; warnings mean it is inconsistent but will work. The two are never
|
|
88
|
+
conflated, because a noisy report does not get read.
|
|
89
|
+
|
|
90
|
+
## What it checks
|
|
91
|
+
|
|
92
|
+
| | |
|
|
93
|
+
|---|---|
|
|
94
|
+
| Names and folders | Against a pattern, with the offending field identified |
|
|
95
|
+
| Codec and container | Read from the file, not from the filename's claim |
|
|
96
|
+
| Resolution, frame rate, scan | Including 29.97 handled as `30000/1001` |
|
|
97
|
+
| Audio | Channel count, layout, sample rate, bit depth |
|
|
98
|
+
| Sidecars | Captions, checksums, metadata a deliverable must carry |
|
|
99
|
+
| Packages | Whether a session is short a master, a proxy, a cutdown |
|
|
100
|
+
| Portability | Illegal characters, reserved names, path length, case collisions |
|
|
101
|
+
| Unicode | Names that compare equal on one platform and not another |
|
|
102
|
+
|
|
103
|
+
Media inspection uses `ffprobe` when it is on your PATH. When it is not, those
|
|
104
|
+
checks are reported as **skipped** — never as passed. A silent skip is how a
|
|
105
|
+
conformance tool starts lying.
|
|
106
|
+
|
|
107
|
+
## For live events, the running order does the work
|
|
108
|
+
|
|
109
|
+
An event agenda is structured data that exists weeks before anyone turns a
|
|
110
|
+
camera on. Joined against time-of-day timecode, every recording names itself —
|
|
111
|
+
stage, session, speaker, sponsor — with no inference of any kind.
|
|
112
|
+
|
|
113
|
+
It will also tell you when a filename is well-formed and still wrong: `S007`
|
|
114
|
+
recorded during `S011` is a valid shape carrying a false claim, and only the
|
|
115
|
+
running order knows.
|
|
116
|
+
|
|
117
|
+
## Checking a drive that arrived
|
|
118
|
+
|
|
119
|
+
If a drive was sealed with a convention on it, one command with no arguments
|
|
120
|
+
checks both that it arrived intact and that the names conform:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
shuttlecheck verify /Volumes/SHUTTLE01
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
No spec to be told about, no account, no connection.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## What this package will not do
|
|
131
|
+
|
|
132
|
+
It will not rename, move or delete anything. That boundary is the point: it is
|
|
133
|
+
the command a stranger will run on a live job, because running it cannot cost
|
|
134
|
+
them anything.
|
|
135
|
+
|
|
136
|
+
Conforming files, the reversible journal, sealing drives and per-vendor specs
|
|
137
|
+
are licensed separately as ShuttleCheck Pro. Asking for one of those commands
|
|
138
|
+
here prints an explanation rather than an error.
|
|
139
|
+
|
|
140
|
+
## Licence
|
|
141
|
+
|
|
142
|
+
Free to install and use. Not open source: no right to redistribute or modify.
|
|
143
|
+
See <https://mvrk.systems/shuttlecheck>.
|
|
144
|
+
|
|
145
|
+
Made by [MVRK Systems](https://mvrk.systems).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# ShuttleCheck
|
|
2
|
+
|
|
3
|
+
**Check media filenames, folder structure and deliverables against a written
|
|
4
|
+
convention.** Nothing in this package renames, moves or deletes anything.
|
|
5
|
+
|
|
6
|
+
On a production the naming convention is a PDF or a message in a chat, and
|
|
7
|
+
every vendor on the job breaks it. Nobody finds out until a conform fails in
|
|
8
|
+
post, or a lab rejects a delivery at four in the morning.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install shuttlecheck
|
|
12
|
+
shuttlecheck ui
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Point it at a job you have already finished. It works out the convention you
|
|
16
|
+
are already using, then shows you every file that does not follow it.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## It reads the convention off your own files
|
|
21
|
+
|
|
22
|
+
Nobody can describe their naming convention in the abstract, and nobody should
|
|
23
|
+
have to. Point `init` at a finished job and it recovers the fields, tells you
|
|
24
|
+
how confident it is in each one, and flags anything it had to guess at.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
FIELDS RECOVERED
|
|
28
|
+
{project} constant 100% NRD24
|
|
29
|
+
{day} integer 90% DAY1, DAY2
|
|
30
|
+
{stage} enum 80% HALLA, HALLB
|
|
31
|
+
{session} integer 90% SES001, SES002
|
|
32
|
+
{title} slug 85% opening-remarks, keynote
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
It handles conventions that disagree with themselves — several accepted forms
|
|
36
|
+
per field, optional elements, free-text titles containing the separator —
|
|
37
|
+
because real ones do.
|
|
38
|
+
|
|
39
|
+
## Then it tells you what breaks it
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
SHUTTLECHECK CONFORMANCE REPORT
|
|
43
|
+
files scanned 412
|
|
44
|
+
conforming 398
|
|
45
|
+
errors 9 ingest will fail
|
|
46
|
+
warnings 5 inconsistent, will work
|
|
47
|
+
|
|
48
|
+
ERROR filename-mismatch (6)
|
|
49
|
+
D02/MAIN/MASTERS/AUT26_D02_MAINSTAGE_S114_v01.mov
|
|
50
|
+
the stage field does not match what the spec allows
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Findings name the field at fault, not just the file. Errors mean an ingest will
|
|
54
|
+
reject it; warnings mean it is inconsistent but will work. The two are never
|
|
55
|
+
conflated, because a noisy report does not get read.
|
|
56
|
+
|
|
57
|
+
## What it checks
|
|
58
|
+
|
|
59
|
+
| | |
|
|
60
|
+
|---|---|
|
|
61
|
+
| Names and folders | Against a pattern, with the offending field identified |
|
|
62
|
+
| Codec and container | Read from the file, not from the filename's claim |
|
|
63
|
+
| Resolution, frame rate, scan | Including 29.97 handled as `30000/1001` |
|
|
64
|
+
| Audio | Channel count, layout, sample rate, bit depth |
|
|
65
|
+
| Sidecars | Captions, checksums, metadata a deliverable must carry |
|
|
66
|
+
| Packages | Whether a session is short a master, a proxy, a cutdown |
|
|
67
|
+
| Portability | Illegal characters, reserved names, path length, case collisions |
|
|
68
|
+
| Unicode | Names that compare equal on one platform and not another |
|
|
69
|
+
|
|
70
|
+
Media inspection uses `ffprobe` when it is on your PATH. When it is not, those
|
|
71
|
+
checks are reported as **skipped** — never as passed. A silent skip is how a
|
|
72
|
+
conformance tool starts lying.
|
|
73
|
+
|
|
74
|
+
## For live events, the running order does the work
|
|
75
|
+
|
|
76
|
+
An event agenda is structured data that exists weeks before anyone turns a
|
|
77
|
+
camera on. Joined against time-of-day timecode, every recording names itself —
|
|
78
|
+
stage, session, speaker, sponsor — with no inference of any kind.
|
|
79
|
+
|
|
80
|
+
It will also tell you when a filename is well-formed and still wrong: `S007`
|
|
81
|
+
recorded during `S011` is a valid shape carrying a false claim, and only the
|
|
82
|
+
running order knows.
|
|
83
|
+
|
|
84
|
+
## Checking a drive that arrived
|
|
85
|
+
|
|
86
|
+
If a drive was sealed with a convention on it, one command with no arguments
|
|
87
|
+
checks both that it arrived intact and that the names conform:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
shuttlecheck verify /Volumes/SHUTTLE01
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
No spec to be told about, no account, no connection.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## What this package will not do
|
|
98
|
+
|
|
99
|
+
It will not rename, move or delete anything. That boundary is the point: it is
|
|
100
|
+
the command a stranger will run on a live job, because running it cannot cost
|
|
101
|
+
them anything.
|
|
102
|
+
|
|
103
|
+
Conforming files, the reversible journal, sealing drives and per-vendor specs
|
|
104
|
+
are licensed separately as ShuttleCheck Pro. Asking for one of those commands
|
|
105
|
+
here prints an explanation rather than an error.
|
|
106
|
+
|
|
107
|
+
## Licence
|
|
108
|
+
|
|
109
|
+
Free to install and use. Not open source: no right to redistribute or modify.
|
|
110
|
+
See <https://mvrk.systems/shuttlecheck>.
|
|
111
|
+
|
|
112
|
+
Made by [MVRK Systems](https://mvrk.systems).
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# ShuttleCheck — validator
|
|
2
|
+
|
|
3
|
+
Naming conformance for a folder tree: infer a spec from it, check it, fix it,
|
|
4
|
+
undo the fix, and send the convention with the media. Installs as `shuttlecheck`;
|
|
5
|
+
the Python package is `shuttlecheck`.
|
|
6
|
+
|
|
7
|
+
`init`, `validate`, `verify` and `conform` (without `--apply`) never write to
|
|
8
|
+
the tree; `seal` writes only inside `_SPEC/`. Only `conform --apply` and
|
|
9
|
+
`revert --apply` move media, and both are journalled with a working undo. The
|
|
10
|
+
validator staying read-only is the point: it is the command a stranger can be
|
|
11
|
+
talked into running on a live job, because running it cannot cost them anything.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
python -m pip install -e .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`tzdata` is a real runtime dependency on Windows — the platform ships no system
|
|
20
|
+
timezone database, so `zoneinfo` cannot resolve the agenda timezone without it.
|
|
21
|
+
|
|
22
|
+
## Use
|
|
23
|
+
|
|
24
|
+
The app is the front door:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
shuttlecheck ui
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That serves a page from the package itself, opens a browser at it, and walks
|
|
31
|
+
you through pointing at a folder, reading the convention off it, seeing what
|
|
32
|
+
does not conform, and fixing what can be derived. No network is used at any
|
|
33
|
+
point. `shuttlecheck ui /Volumes/LAST_JOB` opens straight onto a folder.
|
|
34
|
+
|
|
35
|
+
Everything the app does is also a command, which is what you want in a cron
|
|
36
|
+
job or a delivery hook. Start by reading the convention off a job that already
|
|
37
|
+
exists:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
shuttlecheck init /Volumes/LAST_JOB
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That infers the spec, validates the tree against what it inferred, prints both,
|
|
44
|
+
asks only about what it could not settle, and writes `_SPEC/spec.yaml`. Add
|
|
45
|
+
`--yes` to skip the questions, `--project-code` to override the detected code,
|
|
46
|
+
`--vocabulary film` for a shoot rather than an event.
|
|
47
|
+
|
|
48
|
+
Then check any tree against it:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
shuttlecheck validate /Volumes/SHUTTLE01 --spec EXC26/_SPEC/spec.yaml
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
shuttlecheck validate /Volumes/SHUTTLE01 --spec spec.yaml --json --out report.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
shuttlecheck check-spec --spec examples/event-conference.yaml
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Seal the drive before it ships, and verify it when one arrives:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
shuttlecheck seal /Volumes/SHUTTLE01 --spec spec.yaml --hash
|
|
66
|
+
shuttlecheck verify /Volumes/SHUTTLE01 # no --spec needed
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Fix what can be fixed:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
shuttlecheck conform /Volumes/SHUTTLE01 --spec spec.yaml # plan only
|
|
73
|
+
shuttlecheck conform /Volumes/SHUTTLE01 --spec spec.yaml --apply # execute
|
|
74
|
+
shuttlecheck history /Volumes/SHUTTLE01
|
|
75
|
+
shuttlecheck revert /Volumes/SHUTTLE01 --apply # undo the last run
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Exit codes: `0` clean, `1` findings at error level (or any level with
|
|
79
|
+
`--strict`), `2` the spec or the path could not be read.
|
|
80
|
+
|
|
81
|
+
## What it checks
|
|
82
|
+
|
|
83
|
+
| Rule | Level | What it means |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `filename-mismatch` | error | Name does not parse against the context's pattern |
|
|
86
|
+
| `folder-mismatch` | error | File is not in the folder its context requires |
|
|
87
|
+
| `token-conflict` | error | The same token disagrees between folder and filename |
|
|
88
|
+
| `schedule-mismatch` | error | Named session is not the session the agenda had running |
|
|
89
|
+
| `forbidden-char`, `reserved-name`, `trailing-char` | error | Will not survive a Windows ingest |
|
|
90
|
+
| `path-too-long`, `filename-too-long` | error | Over the platform limit |
|
|
91
|
+
| `case-collision` | error | Siblings differ only by case |
|
|
92
|
+
| `unscheduled-media` | warning | No session was running at that clock time |
|
|
93
|
+
| `loose-file`, `missing-folder` | warning | Structure does not match the spec |
|
|
94
|
+
| `unicode-normalisation` | warning | NFD name; compares unequal across platforms |
|
|
95
|
+
| `unclassified` | info | No context matched; the file was not checked |
|
|
96
|
+
| `spec-*` | error/warning | Problems in the spec itself, not the tree |
|
|
97
|
+
|
|
98
|
+
Delivery rules, for contexts that declare a `deliverable:` profile:
|
|
99
|
+
|
|
100
|
+
| Rule | Level | What it means |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `container-mismatch` | error | Wrapper is not what the spec asks for |
|
|
103
|
+
| `codec-mismatch` | error | Wrong codec or ProRes/DNx variant |
|
|
104
|
+
| `resolution-mismatch`, `framerate-mismatch`, `scan-mismatch` | error | Wrong picture |
|
|
105
|
+
| `video-bitdepth-mismatch` | error | 8-bit where the spec says 10 |
|
|
106
|
+
| `audio-channels-mismatch`, `audio-samplerate-mismatch`, `audio-bitdepth-mismatch`, `audio-track-count-mismatch`, `audio-layout-mismatch` | error | Wrong sound |
|
|
107
|
+
| `duration-out-of-range` | error | Too short or too long |
|
|
108
|
+
| `timecode-missing`, `timecode-start-mismatch` | error | Start timecode wrong or absent |
|
|
109
|
+
| `missing-sidecar` | error | Captions or checksum did not arrive with the file |
|
|
110
|
+
| `no-video-stream`, `no-audio-stream`, `unreadable-media` | error | Not the kind of file it claims to be |
|
|
111
|
+
| `color-*-mismatch` | error | Tagged with the wrong colour space |
|
|
112
|
+
| `color-*-unknown` | warning | Not tagged at all — absence is not proof of wrongness |
|
|
113
|
+
| `incomplete-package` | error | A session or episode is short a deliverable |
|
|
114
|
+
| `spec-unknown-deliverable` | error | A context points at a profile that is not defined |
|
|
115
|
+
|
|
116
|
+
Severities are overridable per rule in the spec's `rules:` block.
|
|
117
|
+
|
|
118
|
+
## The app
|
|
119
|
+
|
|
120
|
+
`shuttlecheck ui` is a local web UI, not Electron and not Qt. The page ships
|
|
121
|
+
inside the wheel and is served by the standard library's HTTP server, so the
|
|
122
|
+
install stays `pip install shuttlecheck`, nothing is downloaded at runtime, and
|
|
123
|
+
it works with no connection at all — which matters, because the machine this
|
|
124
|
+
runs on is often in a truck. No web fonts either; the stylesheet falls back to
|
|
125
|
+
whatever the OS has.
|
|
126
|
+
|
|
127
|
+
Two safety properties are worth knowing, because a local server that renames
|
|
128
|
+
files is a different proposition from one that serves a blog:
|
|
129
|
+
|
|
130
|
+
**Every API call carries a session token.** Any page open in any tab can issue
|
|
131
|
+
requests to `127.0.0.1`. Without a token, a site you happened to visit could
|
|
132
|
+
drive a tool that renames your media. The token is minted at startup, travels
|
|
133
|
+
in the URL the browser is opened with, and never leaves the machine.
|
|
134
|
+
|
|
135
|
+
**Applying a plan requires echoing back the move count.** The page states how
|
|
136
|
+
many moves it believes it is approving. If the folder changed since the plan
|
|
137
|
+
was drawn, the numbers disagree and nothing runs — a stale tab cannot apply a
|
|
138
|
+
plan that no longer matches reality.
|
|
139
|
+
|
|
140
|
+
## How inference works
|
|
141
|
+
|
|
142
|
+
`init` reads the tree rather than asking you to describe it, because nobody can
|
|
143
|
+
describe their own convention in the abstract and most people should not have
|
|
144
|
+
to try.
|
|
145
|
+
|
|
146
|
+
1. **Group** files by the nearest role folder above them (`MASTERS`, `ISO`,
|
|
147
|
+
`PROXY`, `DELIVERABLES`, ...) and by media kind.
|
|
148
|
+
2. **Split** each group's filenames on the separator that divides them most
|
|
149
|
+
consistently. Where a free-text title contains that separator and every file
|
|
150
|
+
has a different field count, anchor from the left and the right while the
|
|
151
|
+
columns still look like fixed codes and treat the remainder as one variable
|
|
152
|
+
field. Names structurally unrelated to the rest are set aside first, and
|
|
153
|
+
reported — they are usually the existing violations.
|
|
154
|
+
3. **Profile** each column: constant, prefixed counter, enum, date, slug, or
|
|
155
|
+
free text, each with a confidence and a plain-English reason.
|
|
156
|
+
4. **Reconcile** value sets across groups, so a column that reads as the
|
|
157
|
+
constant `HALLA` in one group and the enum `{HALLA, HALLB}` in another
|
|
158
|
+
becomes one token rather than two.
|
|
159
|
+
5. **Name** the fields from prefix semantics rather than exact widths — `D01`
|
|
160
|
+
and `DAY1` are both `{day}` — falling back to `{field3}` and flagging it.
|
|
161
|
+
6. **Self-validate**: run the validator over the same tree with the spec just
|
|
162
|
+
inferred, and show the result. That is the thirty-second demonstration.
|
|
163
|
+
|
|
164
|
+
Constants other than the project code become literals in the pattern, so you
|
|
165
|
+
get `{project}-{day}-{stage}-{session}-proxy` rather than a token bound to the
|
|
166
|
+
constant `proxy`.
|
|
167
|
+
|
|
168
|
+
Everything is conservative on purpose. A spec that over-constrains flags every
|
|
169
|
+
file and gets deleted; one that under-constrains still finds the real outliers
|
|
170
|
+
and can be tightened by hand. Low-confidence guesses are listed in the header
|
|
171
|
+
comments of the file it writes.
|
|
172
|
+
|
|
173
|
+
## How the conformer works
|
|
174
|
+
|
|
175
|
+
It renames only what it can *derive*. For each non-conforming file:
|
|
176
|
+
|
|
177
|
+
1. Parse the folder and the filename against the context's patterns.
|
|
178
|
+
2. If both parse but disagree on a token, **skip** — there is no safe way to
|
|
179
|
+
tell which one is the mistake, so it is a question for a human.
|
|
180
|
+
3. If the name fails on a single field, recover that one field: from the folder
|
|
181
|
+
path, by matching a known alias, by re-applying the token's own transforms
|
|
182
|
+
(which fixes casing, spaces and punctuation), or from its declared source.
|
|
183
|
+
4. If a context sets `schedule_check`, cross-check the schedule-bound tokens
|
|
184
|
+
against the agenda and correct them. A name can parse perfectly and still be
|
|
185
|
+
wrong — `S001` recorded during `S005` is a valid shape carrying a false
|
|
186
|
+
claim. An ambiguous clock window corrects nothing rather than guessing.
|
|
187
|
+
5. If the correct name still cannot be derived, **skip and say why**. Inventing
|
|
188
|
+
a session number is worse than leaving a bad filename.
|
|
189
|
+
|
|
190
|
+
Safety, none of it optional:
|
|
191
|
+
|
|
192
|
+
| | |
|
|
193
|
+
|---|---|
|
|
194
|
+
| Dry run | The default. `--apply` is a separate, explicit act. |
|
|
195
|
+
| Originals | `rename: never` contexts are never planned, moved, or renamed. |
|
|
196
|
+
| Overwrite | Never. A destination that exists aborts the whole plan. |
|
|
197
|
+
| Collisions | Two files resolving to one name abort everything — a half-renamed job is worse than an unrenamed one. |
|
|
198
|
+
| Stale plans | Every source is re-checked for size and mtime before the first move. |
|
|
199
|
+
| Case-only renames | Routed through a temporary name, as are swaps and cycles. |
|
|
200
|
+
| Mid-run failure | Every completed move is rolled back, and the run is marked `rolled_back` in the journal. |
|
|
201
|
+
|
|
202
|
+
## The delivery spec
|
|
203
|
+
|
|
204
|
+
Naming is the wedge; this is the business. A file can be named perfectly and
|
|
205
|
+
still be rejected at ingest for being 8-bit instead of 10, 29.97 instead of 25,
|
|
206
|
+
six audio channels where the spec says two, or arriving without its captions.
|
|
207
|
+
|
|
208
|
+
`deliverables:` profiles say what a file must *be*; a context points at one with
|
|
209
|
+
`deliverable:`. `packages:` says what a session or episode owes in total, which
|
|
210
|
+
is the only way to catch the master that was never exported at all.
|
|
211
|
+
|
|
212
|
+
Three things worth knowing:
|
|
213
|
+
|
|
214
|
+
**Frame rates compare with a tolerance.** A spec says `29.97`, a file says
|
|
215
|
+
`30000/1001`. Exact comparison fails every NTSC delivery ever made.
|
|
216
|
+
|
|
217
|
+
**MOV and MP4 cannot be told apart by `format_name`.** They share an ffprobe
|
|
218
|
+
demuxer and both report `mov,mp4,m4a,3gp,3g2,mj2`, so taking the first entry
|
|
219
|
+
marks every MP4 as a MOV and fails every MP4 delivery. The ftyp major brand is
|
|
220
|
+
the real discriminator, with the file extension as a fallback.
|
|
221
|
+
|
|
222
|
+
**Without a media inspector, every media check reports as skipped.** Not passed.
|
|
223
|
+
The report prints a `SOURCES NOT AVAILABLE` block naming exactly what was not
|
|
224
|
+
checked. A delivery report that silently checked nothing is worse than no report
|
|
225
|
+
at all.
|
|
226
|
+
|
|
227
|
+
`ffprobe` is the default inspector and is found on PATH. `Prober` is the seam if
|
|
228
|
+
you ever want a different one.
|
|
229
|
+
|
|
230
|
+
## Vendor packs
|
|
231
|
+
|
|
232
|
+
A convention only works if everyone on the job follows the same one, and a job
|
|
233
|
+
has a captions vendor, a social edit shop and a sponsor agency, none of whom
|
|
234
|
+
share a network with the production or with each other.
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
shuttlecheck pack /Volumes/JOB --spec spec.yaml --vendor captions
|
|
238
|
+
shuttlecheck vendors /Volumes/JOB --spec spec.yaml
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`pack` writes a folder holding a scoped `spec.yaml`, anything it references,
|
|
242
|
+
and a README that states the obligation in plain words:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
WHAT YOU ARE DELIVERING
|
|
246
|
+
Speaker copy
|
|
247
|
+
folder {project}/DELIVERABLES/SPEAKERS/{day}
|
|
248
|
+
filename {project}_{day}_{session}_{speaker}_{title}_{version}
|
|
249
|
+
must be container mp4, codec h264, resolution 1920x1080 or 1280x720,
|
|
250
|
+
25 fps, 2 audio channel(s)
|
|
251
|
+
with Subtitles ({stem}.srt)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
The pack is **derived**, never authored separately — a vendor cannot drift from
|
|
255
|
+
a document they did not write. `vendors` is the other direction: point it at the
|
|
256
|
+
incoming deliveries and it reports who is clear, who has errors, and who has
|
|
257
|
+
sent nothing at all.
|
|
258
|
+
|
|
259
|
+
Two things worth knowing:
|
|
260
|
+
|
|
261
|
+
**Out-of-scope contexts are kept as inert stubs, not deleted.** Contexts are
|
|
262
|
+
first-match-wins, so removing an early one silently re-routes its files into a
|
|
263
|
+
later catch-all — the ISO rushes would come back reported as malformed masters.
|
|
264
|
+
The stubs preserve ordering and say plainly: not yours.
|
|
265
|
+
|
|
266
|
+
**A vendor is never shown a rule they cannot act on.** Packages narrow to the
|
|
267
|
+
requirements they own; a package that ends up empty is dropped rather than
|
|
268
|
+
shown as trivially satisfied. Noise is what stops a report being read.
|
|
269
|
+
|
|
270
|
+
## The journal
|
|
271
|
+
|
|
272
|
+
`_SPEC/journal/`, one append-only JSON Lines file per run, flushed after each
|
|
273
|
+
move so an interrupted run records what actually happened rather than what was
|
|
274
|
+
intended.
|
|
275
|
+
|
|
276
|
+
Its second job matters more than undo. Every file carries an `original_path`,
|
|
277
|
+
recorded the first time it is touched and never rewritten. When a spec is
|
|
278
|
+
edited and the tree re-conformed, names are computed from that identity — never
|
|
279
|
+
from the current name. Without it, tokens compound on every pass: the second
|
|
280
|
+
run re-prefixes what the first already prefixed, and by the third edit the
|
|
281
|
+
names are unrecoverable. That failure is silent and looks like it worked.
|
|
282
|
+
|
|
283
|
+
## The drive sidecar
|
|
284
|
+
|
|
285
|
+
A convention is only worth anything if everyone downstream uses the same one,
|
|
286
|
+
and downstream is a different company on a different network reading a shuttle
|
|
287
|
+
drive out of a Peli case. `seal` writes `_SPEC/` onto the drive itself:
|
|
288
|
+
|
|
289
|
+
| File | What it is |
|
|
290
|
+
|---|---|
|
|
291
|
+
| `spec.yaml` | the convention, plus every file it references (the agenda travels too) |
|
|
292
|
+
| `manifest.jsonl` | one JSON object per line: path, size, mtime, optional hash |
|
|
293
|
+
| `REPORT.txt` | the conformance report as it stood when the drive was sealed |
|
|
294
|
+
| `README.txt` | plain-language instructions for someone who has never heard of this tool |
|
|
295
|
+
| `journal/` | what has been renamed, and by which spec revision |
|
|
296
|
+
|
|
297
|
+
The receiving end runs `shuttlecheck verify .` — no spec to be told about, no
|
|
298
|
+
account, no connectivity. Sealing never touches the media; it only writes
|
|
299
|
+
inside `_SPEC/`.
|
|
300
|
+
|
|
301
|
+
What `verify` reports:
|
|
302
|
+
|
|
303
|
+
| | |
|
|
304
|
+
|---|---|
|
|
305
|
+
| `missing` | error — on the manifest, not on the drive; the copy is incomplete |
|
|
306
|
+
| `size-mismatch` | error — damaged or replaced in transit |
|
|
307
|
+
| `hash-mismatch` | error — same size, different contents (needs `--hash` at both ends) |
|
|
308
|
+
| `added` | warning — on the drive but not the manifest |
|
|
309
|
+
| `mtime-drift` | info — most copy tools do not preserve timestamps |
|
|
310
|
+
| `clock-did-not-survive-copy` | warning — see below |
|
|
311
|
+
|
|
312
|
+
Two judgement calls worth knowing about.
|
|
313
|
+
|
|
314
|
+
**Timestamp drift is not damage.** Most copy tools do not preserve mtime, so
|
|
315
|
+
treating that as corruption would make every genuine transfer look broken.
|
|
316
|
+
*But* if the spec joins the agenda on `file_mtime`, drift silently moves every
|
|
317
|
+
recording to the wrong session and the naming findings become fiction — so that
|
|
318
|
+
specific combination is raised as a warning rather than left to be discovered.
|
|
319
|
+
A spec using embedded timecode does not have the problem.
|
|
320
|
+
|
|
321
|
+
**Hashing is opt-in.** Re-hashing terabytes to restate what an existing MHL
|
|
322
|
+
already asserts wastes hours, so where checksum sidecars are already on the
|
|
323
|
+
drive they are noted and left to do their job. Without `--hash`, sizes and
|
|
324
|
+
timestamps are all that can be checked, and the sidecar says so in writing.
|
|
325
|
+
|
|
326
|
+
## Design notes
|
|
327
|
+
|
|
328
|
+
**It parses, it does not render.** The validator compiles each naming pattern
|
|
329
|
+
into a regex whose groups are constrained by each token's own type, enum values,
|
|
330
|
+
and transforms, then matches the existing name against it. That is why it needs
|
|
331
|
+
no prompting, no schedule, and no media parser to do its core job — and why
|
|
332
|
+
`prompt`-sourced tokens validate fine.
|
|
333
|
+
|
|
334
|
+
**Mismatches name the field.** On a failure it recompiles the pattern with each
|
|
335
|
+
token loosened in turn; whichever loosening makes the name match is the field at
|
|
336
|
+
fault. "The stage field does not match what the spec allows" is a finding
|
|
337
|
+
someone fixes. "Does not match the pattern" is a finding someone ignores.
|
|
338
|
+
|
|
339
|
+
**Missing sources are reported as skipped, never as passed.** Timecode and
|
|
340
|
+
embedded start times need a media parser. Without `ffprobe` on PATH the report
|
|
341
|
+
prints a `SOURCES NOT AVAILABLE` block naming what could not be checked. A
|
|
342
|
+
silent skip is how a conformance tool starts lying.
|
|
343
|
+
|
|
344
|
+
**Original recorder output is never name-checked.** Contexts declaring
|
|
345
|
+
`rename: never` have their folder structure validated and their filenames left
|
|
346
|
+
alone, and the schema will not let such a context propose a name at all.
|
|
347
|
+
|
|
348
|
+
## Tests
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
python -m pytest -q
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
213 tests, across three fixture trees.
|
|
355
|
+
|
|
356
|
+
`tests/make_fixture.py` writes seven conforming files and six deliberate
|
|
357
|
+
violations, plus an agenda whose slots are stamped onto file mtimes so the
|
|
358
|
+
schedule join is exercisable without a media parser:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
python -m tests.make_fixture ./fixture
|
|
362
|
+
shuttlecheck validate ./fixture --spec ./fixture/EXC26/_SPEC/spec.yaml
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
`tests/make_infer_fixture.py` writes a tree using a convention the code has
|
|
366
|
+
never seen — dashes instead of underscores, `DAY1` instead of `D01`, `SES001`
|
|
367
|
+
instead of `S001`, variable-length titles containing the separator, and
|
|
368
|
+
camera-native ISO names. If inference recovers that, it is reading the tree
|
|
369
|
+
rather than recognising something baked into the source:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
python -m tests.make_infer_fixture ./infer-fixture
|
|
373
|
+
shuttlecheck init ./infer-fixture --yes
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The load-bearing test is the round trip: infer a spec from a tree, then run the
|
|
377
|
+
validator over that same tree with that spec, and assert it finds no naming
|
|
378
|
+
errors in the files it learned from.
|
|
379
|
+
|
|
380
|
+
Portability rules are unit-tested against the check functions rather than real
|
|
381
|
+
files, because Windows cannot create a file named `NUL.mov`, `bad<name>.mov`, or
|
|
382
|
+
`trailing..mov.` — which is the point of the check.
|
|
383
|
+
|
|
384
|
+
## Not built yet
|
|
385
|
+
|
|
386
|
+
- Embedded metadata tokens beyond a start time (reel, camera ID, lens, BWF
|
|
387
|
+
scene/take). `MediaProbe` is the seam; it currently reads timecode and
|
|
388
|
+
creation time via `ffprobe` and degrades honestly without it.
|
|
389
|
+
- Agenda formats other than CSV. `ics`, `sched`, `swapcard`, and `cvent` are in
|
|
390
|
+
the schema and raise `ScheduleUnavailable` rather than failing the run.
|
|
391
|
+
- The conformer, its journal, and revert. Deliberately absent — see the
|
|
392
|
+
read-only boundary above.
|