Skip to content

Commit 0104c03

Browse files
authored
feat: Immich 3.0 support — layout-agnostic plugin detection (#93)
1 parent 7ccdfdc commit 0104c03

5 files changed

Lines changed: 53 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 1.5.24 — 2026-07-03
4+
5+
### Immich 3.0 support
6+
- **Plugin-path detection** now recognizes both the 2.7 layout (`corePlugin/`) and the 3.0 layout (`plugins/immich-plugin-core/`). Immich 3.0 renamed the WASM core-plugin directory, which made the layer-download early-exit never fire on 3.0, so setup downloaded every image layer instead of stopping once the small plugin layer arrived. Setup is fast again on 3.0. The rest of the 3.0 worker (extraction, native Sharp, ffmpeg wrapper, plugin load, worker start) was verified end to end.
7+
- README notes the worker tracks your Immich version, verified with 2.7.x and 3.0.x.
8+
9+
Note: moving your own Docker stack from 2.7 to 3.0 requires Immich's database migration (pgvecto-rs to VectorChord); that is on the Immich side, not the accelerator.
10+
311
## 1.5.23 — 2026-07-03
412

513
### Diagnostics

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Docker (lightweight) Native macOS (compute)
2828
+-------------------------------+
2929
```
3030

31-
The microservices worker is extracted directly from your running Immich Docker image. Always the exact same version, no source builds. The only modification is installing the macOS-native Sharp binary for image processing. Video transcoding is intercepted by a lightweight ffmpeg wrapper that remaps software encoders to VideoToolbox hardware encoders.
31+
The microservices worker is extracted directly from your running Immich Docker image, so it tracks whatever version you run (verified with Immich 2.7.x and 3.0.x). Always the exact same version, no source builds. The only modification is installing the macOS-native Sharp binary for image processing. Video transcoding is intercepted by a lightweight ffmpeg wrapper that remaps software encoders to VideoToolbox hardware encoders.
3232

3333
## What we modify (and how to undo it)
3434

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.5.23
1+
1.5.24

immich_accelerator/__main__.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,22 @@ def _needs_core_plugin(version: str) -> bool:
11861186
return (major, minor) >= (2, 7)
11871187

11881188

1189+
def _build_has_core_plugin(build_data: Path) -> bool:
1190+
"""True once the WASM core plugin has been extracted into build-data.
1191+
1192+
The layout moved between Immich versions, so recognize both:
1193+
2.7.x: build-data/corePlugin/manifest.json
1194+
3.0.x: build-data/plugins/immich-plugin-core/dist/plugin.wasm
1195+
Keying only on the old 2.7 path made the layer-loop early-exit never
1196+
fire on 3.0, so setup downloaded every image layer instead of stopping
1197+
once the (small) plugin layer arrived.
1198+
"""
1199+
if (build_data / "corePlugin" / "manifest.json").exists():
1200+
return True
1201+
plugins = build_data / "plugins"
1202+
return plugins.is_dir() and any(plugins.glob("*/dist/plugin.wasm"))
1203+
1204+
11891205
def _has_everything(
11901206
version: str,
11911207
found_server: bool,
@@ -1293,7 +1309,7 @@ def _get(url, accept=None):
12931309

12941310
for i, layer in sorted_layers:
12951311
size_mb = layer["size"] / 1024 / 1024
1296-
has_core = (build_data / "corePlugin" / "manifest.json").exists()
1312+
has_core = _build_has_core_plugin(build_data)
12971313
if _has_everything(bare_version, found_server, found_build, has_core):
12981314
break
12991315
digest = layer["digest"]

tests/test_accelerator.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,3 +1496,29 @@ def fake_kill(pid, sig):
14961496

14971497
termed = {pgid for pgid, sig in sent if sig == signal.SIGTERM}
14981498
assert termed == {111, 222, 333} # all signalled before any wait
1499+
1500+
1501+
class TestBuildHasCorePlugin:
1502+
"""Plugin detection across Immich layouts (2.7 corePlugin, 3.0 plugins/)."""
1503+
1504+
def test_detects_27_layout(self, tmp_path):
1505+
from immich_accelerator.__main__ import _build_has_core_plugin
1506+
1507+
(tmp_path / "corePlugin").mkdir()
1508+
(tmp_path / "corePlugin" / "manifest.json").write_text("{}")
1509+
assert _build_has_core_plugin(tmp_path) is True
1510+
1511+
def test_detects_30_layout(self, tmp_path):
1512+
from immich_accelerator.__main__ import _build_has_core_plugin
1513+
1514+
wasm = tmp_path / "plugins" / "immich-plugin-core" / "dist" / "plugin.wasm"
1515+
wasm.parent.mkdir(parents=True)
1516+
wasm.write_bytes(b"\x00asm")
1517+
assert _build_has_core_plugin(tmp_path) is True
1518+
1519+
def test_false_when_absent(self, tmp_path):
1520+
from immich_accelerator.__main__ import _build_has_core_plugin
1521+
1522+
assert _build_has_core_plugin(tmp_path) is False
1523+
(tmp_path / "plugins").mkdir() # empty plugins dir is not enough
1524+
assert _build_has_core_plugin(tmp_path) is False

0 commit comments

Comments
 (0)