release(camera-ai): publish 0.4.12 catalog portability fix

This commit is contained in:
KINDNICK
2026-08-16 01:05:07 +09:00
parent 8e5e2e8ad0
commit b65e54a7e8
211 changed files with 45990 additions and 45913 deletions
+3 -1
View File
@@ -1,6 +1,8 @@
*.meta -whitespace
CameraAI~/Tools~/CWCameraWorker/** -diff -whitespace
CameraAI~/Tools~/CWCameraWorker/** -text -diff -whitespace
*.exe filter=lfs diff=lfs merge=lfs -text
*.dll filter=lfs diff=lfs merge=lfs -text
*.pyd filter=lfs diff=lfs merge=lfs -text
CameraAI~/RuntimeData~/DatasetExports/**/*.npz filter=lfs diff=lfs merge=lfs -text
CameraAI~/RuntimeData~/DatasetExports/dataset_catalog.json text eol=lf
CameraAI~/RuntimeData~/reports/training_index.json text eol=lf
+11
View File
@@ -1,5 +1,16 @@
# Changelog
## 0.4.12 - 2026-08-16
- Made packaged catalog freshness checks portable across Windows Git
checkouts: an exact raw hash remains authoritative, while a byte-for-byte
equivalent CRLF checkout is accepted only when LF normalization reproduces
the indexed hash. Content changes and Git LFS pointer files still fail
closed.
- Extended `CWCameraWorker doctor` to validate the packaged training index
against its catalog, and pinned the release catalog and training index to LF
line endings. Rebuilt the self-contained Windows worker as 0.1.8.
## 0.4.11 - 2026-08-10
- Integrated the verified YAMO dataset refresh without replacing the packaged
@@ -17,7 +17,7 @@ must remain together.
In Unity Package Manager, choose **Add package from git URL** and enter:
```text
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.17
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.18
```
The initial package download is large because the frozen Windows worker is
@@ -26,7 +26,7 @@ it, remove the package lock entry, and add the package again.
## Reference data
Version 0.1.17 includes a compact, read-only `RuntimeData~` bundle with the
Version 0.1.18 includes a compact, read-only `RuntimeData~` bundle with the
263 prepared reference songs, cut policy, and ranker model. An artist
workstation does not need a separate `CW-AI` checkout or Python installation.
If a newer access-controlled library is available, it remains an optional
+1 -1
View File
@@ -16,7 +16,7 @@ Cinemachine Track이나 기존 카메라 애니메이션은 필요하지 않으
Unity Package Manager의 `Add package from git URL`에는 다음 주소를 사용할 수
있습니다.
`https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.17`
`https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.18`
배포 패키지에는 Windows x64용 `CWCameraWorker` 폴더 전체가 포함됩니다.
Python은 따로 설치하지 않아도 되지만, Git 패키지의 대용량 바이너리를 받으려면
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
{
"schemaVersion": "cw-camera-worker-build-identity-v1",
"workerVersion": "0.1.7",
"createdUtc": "2026-08-09T23:32:08.208961+00:00",
"workerVersion": "0.1.8",
"createdUtc": "2026-08-15T15:56:01.810396+00:00",
"python": "3.12.13",
"sourceRootRelative": "cwai_sources/repository",
"sourceSha256": {
@@ -9,10 +9,10 @@
"MachineLearning/CameraDirector/build_hybrid_preparation_cache.py": "427b3cd8fa6eb444517281ef2e21ebc5d751985f4ad271eee49617fda0fb7b33",
"MachineLearning/CameraDirector/camera_kinematics.py": "e94eefcb56c4ebdccbc57ae8d3650109b2c912e3642892ac13add9409db21a7f",
"MachineLearning/CameraDirector/camera_runtime_data.py": "8ef2e84a9d79b24c3f3232b1f18da49741988765044dffc6075f6e6dd5ff001d",
"MachineLearning/CameraDirector/cw_camera_cli.py": "943870a4cdec7e4b830b9c945d690e700902cf2a37d1e49987bba85bf0b0ea87",
"MachineLearning/CameraDirector/cw_camera_runtime.py": "7530d24a65a55c235726676b7991b7a375a2739d8b6257f53cc0340f6cc7de1c",
"MachineLearning/CameraDirector/cw_camera_cli.py": "b523fdf6820b3b203dcdef80877a6ad9576fba41d7285c790c300e1813789012",
"MachineLearning/CameraDirector/cw_camera_runtime.py": "5cc252ff45b2a80dffbeea14ad37013ba4a428c4f6fe31a617957eec6c823429",
"MachineLearning/CameraDirector/data_driven_cut_planner.py": "36cf86a80577278d2e966cb0ebf7c3110b5b1dda5abd1d8a6b0a76e31d10e32b",
"MachineLearning/CameraDirector/generate_hybrid.py": "b7e0053563065ebb6d563e72fa0221d31a9e36c1c94c37992f13974b25bb5dfd",
"MachineLearning/CameraDirector/generate_hybrid.py": "bd37744b7b6573bf14be4f0ec5202f1465e11bff71ccabec210533e135b546c3",
"MachineLearning/CameraDirector/hybrid_candidate_cache.py": "a0a5b6a8f612f18cb2875394f17ed89950e1e62e38c1226457d9e35848f6e380",
"MachineLearning/CameraDirector/hybrid_cut_reference.py": "306e650dc193fd0d9c42fd52d3f14eef112d1a0da2a27c13b88256d42d8542aa",
"MachineLearning/CameraDirector/hybrid_preparation_cache.py": "e4d062a48cf39e01ea97951d103a37817c860e94f440f49f080c622b4969fb5a",
@@ -23,8 +23,8 @@
"MachineLearning/CameraDirector/trajectory_quality.py": "20cd2ddc1b2349f8c33f6d1460580f37964a521eaa86ceee5b8ae457512ba49d"
},
"preparationLogicIdentifier": "1cb808e207d54854267a1020a658812c79e9bad5a5296f78140228b3a15eeb3b",
"candidateLogicIdentifier": "8dfa557f33c17f4b59849431523cef3650091dc2db3c6c48074888f1f6aef143",
"generationCodeIdentifier": "58a7d5d440e5bea630c060823fea7280d81d533e5c4741259ee974bce27e6eb4",
"candidateLogicIdentifier": "e768283e8496360ff703b5135f85c51520a888787612b15456def131d5ee1d89",
"generationCodeIdentifier": "80e9eeca5aed9e17a9cfb61b235f954ed4eb24308a1a1ca962e424dbb54bea88",
"generationCodeFiles": [
"adjacent_transition.py",
"camera_kinematics.py",
@@ -710,11 +710,12 @@ def dependency_checks() -> list[dict[str, Any]]:
def doctor_payload(runtime_root: Path) -> dict[str, Any]:
runtime_root = Path(runtime_root).resolve()
training_index_path = runtime_root / "reports" / "training_index.json"
resources = (
("datasetRoot", runtime_root / "DatasetExports", "directory"),
(
"trainingIndex",
runtime_root / "reports" / "training_index.json",
training_index_path,
"file",
),
(
@@ -727,6 +728,32 @@ def doctor_payload(runtime_root: Path) -> dict[str, Any]:
for name, path, kind in resources:
ok = path.is_dir() if kind == "directory" else path.is_file()
checks.append({"name": name, "ok": ok, "detail": str(path)})
if training_index_path.is_file():
try:
generator = importlib.import_module("generate_hybrid")
training_index = json.loads(
training_index_path.read_text(encoding="utf-8")
)
catalog_path = generator.validate_training_index_catalog_freshness(
training_index_path,
training_index,
)
except Exception as error:
checks.append(
{
"name": "trainingIndexCatalogFreshness",
"ok": False,
"detail": f"{type(error).__name__}: {error}",
}
)
else:
checks.append(
{
"name": "trainingIndexCatalogFreshness",
"ok": True,
"detail": str(catalog_path),
}
)
if cw_camera_runtime.is_frozen_runtime():
identity = cw_camera_runtime.load_build_identity()
checks.append(
@@ -19,7 +19,7 @@ from typing import Any, Iterable
BUILD_IDENTITY_SCHEMA_VERSION = "cw-camera-worker-build-identity-v1"
BUILD_IDENTITY_FILE = "cw_camera_worker_build_identity.json"
WORKER_VERSION = "0.1.7"
WORKER_VERSION = "0.1.8"
def is_frozen_runtime() -> bool:
@@ -50,6 +50,10 @@ import cw_camera_runtime
import trajectory_quality
GIT_LFS_POINTER_PREFIX = "version https://git-lfs.github.com/spec/v1"
UTF8_BOM = "\ufeff"
HARD_PERSON_SAFE_BOUNDS = {
"wide": (0.1, 0.9, 0.12, 0.86),
"medium": (0.12, 0.88, 0.12, 0.84),
@@ -8675,8 +8679,41 @@ def validate_training_index_catalog_freshness(
raise FileNotFoundError(
f"Training-index catalog was not found. Searched: {searched}"
)
actual_sha256 = sha256_file(resolved_catalog).lower()
if actual_sha256 != expected_sha256:
catalog_bytes = resolved_catalog.read_bytes()
pointer_probe = catalog_bytes
utf8_bom = UTF8_BOM.encode("utf-8")
if pointer_probe.startswith(utf8_bom):
pointer_probe = pointer_probe[len(utf8_bom) :]
if pointer_probe.startswith(GIT_LFS_POINTER_PREFIX.encode("ascii")):
raise ValueError(
"Training-index catalog is still a Git LFS pointer: "
f"{resolved_catalog}. Materialize Git LFS assets and retry."
)
actual_sha256 = hashlib.sha256(catalog_bytes).hexdigest().lower()
catalog_matches = actual_sha256 == expected_sha256
if not catalog_matches:
try:
catalog_bytes.decode("utf-8", errors="strict")
except UnicodeDecodeError:
normalized_sha256 = None
else:
all_cr_bytes_are_crlf = all(
offset + 1 < len(catalog_bytes)
and catalog_bytes[offset + 1] == 0x0A
for offset, value in enumerate(catalog_bytes)
if value == 0x0D
)
if all_cr_bytes_are_crlf:
normalized_bytes = catalog_bytes.replace(b"\r\n", b"\n")
normalized_sha256 = hashlib.sha256(
normalized_bytes
).hexdigest().lower()
else:
normalized_sha256 = None
catalog_matches = normalized_sha256 == expected_sha256
if not catalog_matches:
raise ValueError(
"Training index is stale: catalog SHA-256 mismatch for "
f"{resolved_catalog}. Expected {expected_sha256}, got {actual_sha256}. "
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
../../Scripts/wheel.exe,sha256=1GLKUxJuQuNenSR7B5UFoFKuXyK1UHvT9Dqwy-JntDE,108448
../../Scripts/wheel.exe,sha256=9jTRWXNV9efL3HxLn6B_Q7zW31wwIW3r808tmvMnVhQ,108448
wheel-0.45.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
wheel-0.45.1.dist-info/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107
wheel-0.45.1.dist-info/METADATA,sha256=mKz84H7m7jsxJyzeIcTVORiTb0NPMV39KvOIYhGgmjA,2313
@@ -1,78 +1,78 @@
{
"schemaVersion": "cw-camera-worker-distribution-build-v1",
"createdUtc": "2026-08-09T23:32:56.7289154Z",
"workerVersion": "0.1.7",
"protocolVersion": "1",
"buildIdentitySchemaVersion": "cw-camera-worker-build-identity-v1",
"buildEnvironment": {
"kind": "disposable_venv",
"pythonVersion": "3.12.13",
"isolatedPrefixVerified": true,
"userSiteEnabled": false,
"threadLimits": {
"OMP_NUM_THREADS": "1",
"OPENBLAS_NUM_THREADS": "1",
"MKL_NUM_THREADS": "1",
"NUMEXPR_NUM_THREADS": "1",
"VECLIB_MAXIMUM_THREADS": "1",
"BLIS_NUM_THREADS": "1"
},
"installedDistributions": [
"altgraph==0.17.5",
"audioread==3.1.0",
"certifi==2024.12.14",
"cffi==2.0.0",
"charset-normalizer==3.4.1",
"decorator==5.1.1",
"idna==3.10",
"joblib==1.5.2",
"lazy_loader==0.4",
"librosa==0.11.0",
"llvmlite==0.43.0",
"msgpack==1.1.2",
"numba==0.60.0",
"numpy==1.26.4",
"packaging==26.2",
"pefile==2024.8.26",
"pip==25.0.1",
"platformdirs==4.9.6",
"pooch==1.8.2",
"pycparser==2.21",
"pyinstaller==6.17.0",
"pyinstaller-hooks-contrib==2025.10",
"pywin32-ctypes==0.2.3",
"requests==2.32.3",
"scikit-learn==1.7.2",
"scipy==1.15.3",
"setuptools==78.1.0",
"soundfile==0.13.1",
"soxr==1.0.0",
"threadpoolctl==3.6.0",
"typing_extensions==4.15.0",
"urllib3==2.3.0",
"wheel==0.45.1"
]
},
"requirements": [
{
"path": "MachineLearning/CameraDirector/requirements-worker.txt",
"sha256": "90987782184e15119a6090922e464559e620b6b164b80b5ae4f567e8ae6a7b9d"
},
{
"path": "MachineLearning/CameraDirector/requirements-worker-build.txt",
"sha256": "1ae29e0b81e82bdcbcb14d1817813e5f60e45ae260af0e693a66cc24f3ae6827"
}
],
"payload": {
"fileCount": 646,
"bytes": 267767197,
"executableSha256": "adbc014b8cd85574367c927b2403ddc41ec561d5c655331598f8a4905b2e5b6a",
"buildIdentitySha256": "6243f071b4a05cacd08b94d2c57ba8fad2a6024bda828c7e8e45d176f45fd696",
"sanityRange": {
"minimumFileCount": 500,
"maximumFileCount": 1000,
"minimumBytes": 209715200,
"maximumBytes": 393216000
}
}
"schemaVersion": "cw-camera-worker-distribution-build-v1",
"createdUtc": "2026-08-15T15:57:09.6006631Z",
"workerVersion": "0.1.8",
"protocolVersion": "1",
"buildIdentitySchemaVersion": "cw-camera-worker-build-identity-v1",
"buildEnvironment": {
"kind": "disposable_venv",
"pythonVersion": "3.12.13",
"isolatedPrefixVerified": true,
"userSiteEnabled": false,
"threadLimits": {
"OMP_NUM_THREADS": "1",
"OPENBLAS_NUM_THREADS": "1",
"MKL_NUM_THREADS": "1",
"NUMEXPR_NUM_THREADS": "1",
"VECLIB_MAXIMUM_THREADS": "1",
"BLIS_NUM_THREADS": "1"
},
"installedDistributions": [
"altgraph==0.17.5",
"audioread==3.1.0",
"certifi==2024.12.14",
"cffi==2.0.0",
"charset-normalizer==3.4.1",
"decorator==5.1.1",
"idna==3.10",
"joblib==1.5.2",
"lazy_loader==0.4",
"librosa==0.11.0",
"llvmlite==0.43.0",
"msgpack==1.1.2",
"numba==0.60.0",
"numpy==1.26.4",
"packaging==26.2",
"pefile==2024.8.26",
"pip==25.0.1",
"platformdirs==4.9.6",
"pooch==1.8.2",
"pycparser==2.21",
"pyinstaller-hooks-contrib==2025.10",
"pyinstaller==6.17.0",
"pywin32-ctypes==0.2.3",
"requests==2.32.3",
"scikit-learn==1.7.2",
"scipy==1.15.3",
"setuptools==78.1.0",
"soundfile==0.13.1",
"soxr==1.0.0",
"threadpoolctl==3.6.0",
"typing_extensions==4.15.0",
"urllib3==2.3.0",
"wheel==0.45.1"
]
},
"requirements": [
{
"path": "MachineLearning/CameraDirector/requirements-worker-build.txt",
"sha256": "1ae29e0b81e82bdcbcb14d1817813e5f60e45ae260af0e693a66cc24f3ae6827"
},
{
"path": "MachineLearning/CameraDirector/requirements-worker.txt",
"sha256": "90987782184e15119a6090922e464559e620b6b164b80b5ae4f567e8ae6a7b9d"
}
],
"payload": {
"fileCount": 646,
"bytes": 267778040,
"executableSha256": "559fb67e5dee12cedb0daf1a1f900460fbcf3ff51ece0e826da6ff353506d2ac",
"buildIdentitySha256": "fe1e6e3fd8b22a4874bcf8dc5a93528f6e6aa359b42a6438bc0aba932f18a3bc",
"sanityRange": {
"minimumFileCount": 500,
"maximumFileCount": 1000,
"minimumBytes": 209715200,
"maximumBytes": 393216000
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "com.mingle.cw-ai",
"version": "0.4.11",
"version": "0.4.12",
"displayName": "Mingle Camera Work AI",
"description": "Self-contained high-quality Unity Timeline camera generation with an embedded prepared reference library, per-shot editable clips, and A/B review tools.",
"unity": "6000.0",
+1 -1
View File
@@ -37,7 +37,7 @@ Install **Git and Git LFS before opening Unity**, then use **Add package from
git URL** with:
```text
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.17
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.18
```
The Camera AI package includes the complete Windows x64