Skip to content

updates#655

Open
davayd wants to merge 6 commits into
immichFrame:mainfrom
davayd:dd-edits
Open

updates#655
davayd wants to merge 6 commits into
immichFrame:mainfrom
davayd:dd-edits

Conversation

@davayd

@davayd davayd commented Jun 27, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features
    • Added an authenticated api/Asset/Memories endpoint that returns memory assets in grouped sections.
    • Memory assets are aggregated across configured accounts into nested group results.
  • Bug Fixes
    • Empty memory groups are now omitted from responses.
  • Tests
    • Updated controller tests to use an authorized client helper and refined mocked JSON response content type/encoding.
  • Documentation / Chores
    • Added release-notes guidance, updated NuGet sources, improved Docker image metadata labels, and introduced an ImmichFrame container template.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an authenticated GET api/Asset/Memories endpoint returning memory assets grouped by account and year. Group loading, multi-account delegation, asset-location recording, and authorized controller tests are updated. NuGet configuration, release-writeup instructions, and Docker image metadata are also added.

Changes

Memory Assets Feature

Layer / File(s) Summary
Memory asset contracts
ImmichFrame.Core/Interfaces/IImmichFrameLogic.cs
Adds grouped GetMemoryAssets and RecordAssetLocation contracts.
Grouped asset loading
ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs
Exposes grouped asset loading, filters empty groups, and flattens groups for existing loading.
Pooled logic and selection strategy
ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs, ImmichFrame.Core/Logic/AccountSelection/TotalAccountImagesSelectionStrategy.cs
Wires grouped pool results and delegates asset-location recording.
Multi-account orchestration
ImmichFrame.Core/Logic/MultiImmichFrameLogicDelegate.cs
Fetches account groups concurrently, records locations, applies account scope, and combines grouped assets.
HTTP endpoint and tests
ImmichFrame.WebApi/Controllers/AssetController.cs, ImmichFrame.WebApi.Tests/Controllers/AssetControllerTests.cs
Adds the authenticated Memories endpoint and bearer-authenticated test requests.

Repository Support Files

Layer / File(s) Summary
Release, package, and image metadata
AGENTS.md, NuGet.Config, Dockerfile
Adds release-writeup instructions, the nuget.org v3 source, and OCI image labels.
ImmichFrame container template
templates/immichframe.xml
Adds container metadata and configuration fields for Immich connectivity, media display, weather, clock, and UI settings.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AssetController
  participant MultiImmichFrameLogicDelegate
  participant PooledImmichFrameLogic
  participant MemoryAssetsPool
  participant TotalAccountImagesSelectionStrategy

  Client->>AssetController: GET api/Asset/Memories
  AssetController->>MultiImmichFrameLogicDelegate: GetMemoryAssets()
  MultiImmichFrameLogicDelegate->>PooledImmichFrameLogic: GetMemoryAssets() per account
  PooledImmichFrameLogic->>MemoryAssetsPool: GetAssetGroups()
  MemoryAssetsPool-->>PooledImmichFrameLogic: Grouped assets
  MultiImmichFrameLogicDelegate->>TotalAccountImagesSelectionStrategy: RecordAssetLocation(account, assetId)
  MultiImmichFrameLogicDelegate-->>AssetController: Account-scoped groups
  AssetController-->>Client: List<List<AssetResponseDto>>
Loading

Possibly related PRs

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too generic and does not describe the actual changes in the pull request. Rename it to summarize the main change, such as adding memory assets support and related API updates.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
ImmichFrame.WebApi.Tests/Controllers/AssetControllerTests.cs (1)

195-262: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Please add an integration test for /api/Asset/Memories.

This PR adds a new authenticated route and a nested response shape, but the new coverage still only exercises /api/Asset and /api/Asset/RandomImageAndInfo. A small test with one empty memory group and one populated group would protect the controller wiring and the empty-group filtering in this slice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ImmichFrame.WebApi.Tests/Controllers/AssetControllerTests.cs` around lines
195 - 262, Add an integration test in AssetControllerTests for the authenticated
/api/Asset/Memories route, since current coverage only exercises GetAssets and
GetRandomImageAndInfo. Create a test that mocks the memory response shape with
one empty memory group and one populated group, then call the controller through
CreateAuthorizedClient and assert the nested response is wired correctly and the
empty group is filtered out. Use the existing test patterns in
GetAssets_ReturnsVideoDurationFromImmichMetadata and the AssetController
endpoint setup to locate the right place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs`:
- Around line 10-16: `GetAssetGroups()` in `MemoryAssetsPool` bypasses the
shared cached read path and calls `LoadAssetGroups()` directly, so grouped
requests never benefit from the `CachingApiAssetsPool` cache. Update the
grouped-read flow to go through a single cached fetch path used by both
`GetAssetGroups()` and `LoadAssets()`, or add caching around the grouped result
so `SearchMemoriesAsync` and the asset-info fetches are not repeated on every
request.

In `@ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs`:
- Around line 78-80: GetMemoryAssets currently creates a new MemoryAssetsPool on
every call, which discards any cached state such as DailyApiCache. Update
PooledImmichFrameLogic to hold a dedicated MemoryAssetsPool instance as a field
(initialized once from _immichApi and AccountSettings) and have GetMemoryAssets
reuse that field instead of constructing a fresh pool in the method body.

---

Nitpick comments:
In `@ImmichFrame.WebApi.Tests/Controllers/AssetControllerTests.cs`:
- Around line 195-262: Add an integration test in AssetControllerTests for the
authenticated /api/Asset/Memories route, since current coverage only exercises
GetAssets and GetRandomImageAndInfo. Create a test that mocks the memory
response shape with one empty memory group and one populated group, then call
the controller through CreateAuthorizedClient and assert the nested response is
wired correctly and the empty group is filtered out. Use the existing test
patterns in GetAssets_ReturnsVideoDurationFromImmichMetadata and the
AssetController endpoint setup to locate the right place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 87909de8-e59e-49bd-b967-3f65fce4919e

📥 Commits

Reviewing files that changed from the base of the PR and between efc850c and d8bcf4f.

📒 Files selected for processing (9)
  • AGENTS.md
  • ImmichFrame.Core/Interfaces/IImmichFrameLogic.cs
  • ImmichFrame.Core/Logic/AccountSelection/TotalAccountImagesSelectionStrategy.cs
  • ImmichFrame.Core/Logic/MultiImmichFrameLogicDelegate.cs
  • ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs
  • ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs
  • ImmichFrame.WebApi.Tests/Controllers/AssetControllerTests.cs
  • ImmichFrame.WebApi/Controllers/AssetController.cs
  • NuGet.Config

Comment on lines +10 to +16
public async Task<IEnumerable<IEnumerable<AssetResponseDto>>> GetAssetGroups(CancellationToken ct = default)
=> await LoadAssetGroups(ct);

protected override async Task<IEnumerable<AssetResponseDto>> LoadAssets(CancellationToken ct = default)
=> (await LoadAssetGroups(ct)).SelectMany(assets => assets);

private async Task<List<List<AssetResponseDto>>> LoadAssetGroups(CancellationToken ct = default)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Share the grouped read path with the daily cache.

GetAssetGroups() goes straight to LoadAssetGroups(), so callers never hit CachingApiAssetsPool's cached load path. On a busy /Memories client this will replay SearchMemoriesAsync and the follow-up asset-info fetches on every request; please cache the grouped result too, or make this method read from a single cached fetch path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs` around lines 10 - 16,
`GetAssetGroups()` in `MemoryAssetsPool` bypasses the shared cached read path
and calls `LoadAssetGroups()` directly, so grouped requests never benefit from
the `CachingApiAssetsPool` cache. Update the grouped-read flow to go through a
single cached fetch path used by both `GetAssetGroups()` and `LoadAssets()`, or
add caching around the grouped result so `SearchMemoriesAsync` and the
asset-info fetches are not repeated on every request.

Comment on lines +78 to +80
public Task<IEnumerable<IEnumerable<AssetResponseDto>>> GetMemoryAssets()
{
return new MemoryAssetsPool(_immichApi, AccountSettings).GetAssetGroups();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Avoid allocating a new MemoryAssetsPool per request.

This throws away DailyApiCache every time GetMemoryAssets() runs, so even a cached pool instance cannot help here. Reuse a dedicated pool field instead of constructing a fresh pool in the method body.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs` around lines 78 - 80,
GetMemoryAssets currently creates a new MemoryAssetsPool on every call, which
discards any cached state such as DailyApiCache. Update PooledImmichFrameLogic
to hold a dedicated MemoryAssetsPool instance as a field (initialized once from
_immichApi and AccountSettings) and have GetMemoryAssets reuse that field
instead of constructing a fresh pool in the method body.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Dockerfile`:
- Line 52: Replace the hardcoded revision in the Docker image metadata with a
build argument: declare the appropriate REVISION argument and use its value for
the org.opencontainers.image.revision label, so each build reports its source
commit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eb12bcf8-4014-4c9a-829f-c12cc9f554b7

📥 Commits

Reviewing files that changed from the base of the PR and between c88801e and a1b0c18.

📒 Files selected for processing (1)
  • Dockerfile

Comment thread Dockerfile
org.opencontainers.image.url="https://github.com/immichFrame/ImmichFrame" \
org.opencontainers.image.licenses="GPL-3.0" \
org.opencontainers.image.version="v${VERSION}" \
org.opencontainers.image.revision="d8bcf4f"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Derive the revision label from the build commit.

d8bcf4f is hardcoded, so every image built from another commit will report an incorrect source revision. Pass the Git SHA through a build argument (for example, ARG REVISION) and use ${REVISION} here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` at line 52, Replace the hardcoded revision in the Docker image
metadata with a build argument: declare the appropriate REVISION argument and
use its value for the org.opencontainers.image.revision label, so each build
reports its source commit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@templates/immichframe.xml`:
- Line 16: Update the TemplateURL value in the immichframe template to reference
the canonical immichFrame/ImmichFrame branch rather than the contributor’s
dd-edits branch, while preserving the existing raw GitHub template path.
- Around line 20-21: Update the ImmichServerUrl and ApiKey Config entries to use
Required="false", allowing the container to be created or updated with only the
file-based /app/Config configuration. Keep their existing defaults,
descriptions, and masking behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9264220d-7776-434a-a3c4-951efa72b05b

📥 Commits

Reviewing files that changed from the base of the PR and between 4c6077c and 63d3293.

📒 Files selected for processing (1)
  • templates/immichframe.xml

Comment thread templates/immichframe.xml
<Description>Configure ImmichFrame with either a Settings.json/Settings.yml file mounted at /app/Config or the variables below. Do not use both methods: a settings file takes precedence.</Description>
<Category>MediaApp:Other</Category>
<WebUI>http://[IP]:[PORT:8080]</WebUI>
<TemplateURL>https://raw.githubusercontent.com/davayd/ImmichFrame/dd-edits/templates/immichframe.xml</TemplateURL>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use the canonical template URL.

TemplateURL points to the contributor’s dd-edits branch, which can be deleted, rebased, or diverge from the released template. Point this at the canonical immichFrame/ImmichFrame branch instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@templates/immichframe.xml` at line 16, Update the TemplateURL value in the
immichframe template to reference the canonical immichFrame/ImmichFrame branch
rather than the contributor’s dd-edits branch, while preserving the existing raw
GitHub template path.

Comment thread templates/immichframe.xml
Comment on lines +20 to +21
<Config Name="ImmichServerUrl" Target="ImmichServerUrl" Default="" Mode="" Description="The URL of your Immich server, e.g. https://photos.yourdomain.com or http://192.168.0.100:2283" Type="Variable" Display="always" Required="true" Mask="false"></Config>
<Config Name="ApiKey" Target="ApiKey" Default="" Mode="" Description="Your Immich API key." Type="Variable" Display="always" Required="true" Mask="true"></Config>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,120p' templates/immichframe.xml

Repository: immichFrame/ImmichFrame

Length of output: 8086


🌐 Web query:

Unraid Docker template Required="true" variable empty default settings file path mount optional config behavior

💡 Result:

In Unraid Docker templates, the behavior of fields marked with Required="true" varies depending on the type of configuration and whether the field is left empty by the user. Environment Variables: When an environment variable is marked Required="true", the Unraid WebGUI typically requires a value to be entered before the container can be created or updated [1]. However, if a user provides an empty string (or leaves it blank in the UI), Unraid may still pass that empty value to the Docker engine [2][3]. This can lead to unexpected behavior if the application inside the container treats an empty string as a valid, non-null override of a default setting [2]. It is a known issue that some templates or deployment tools pass these empty values rather than omitting the variable entirely [2]. Path Mounts (Volumes): For path mappings (defined in the XML as <Config Type="Path"...>), the Required="true" attribute enforces that a host path must be defined [4]. If the host path field is left empty or is not properly populated, the Unraid Docker manager may generate an invalid Docker command (e.g., resulting in "empty section between colons" errors, such as -v '':'/container/path') [3]. This will cause the container creation process to fail because the underlying Docker run command becomes malformed [3]. Best Practices for Optional Configuration: 1. Use Required="false": If a variable or path is truly optional, set Required="false" in the XML template [5]. This signals to the Unraid WebGUI that the field is not mandatory. 2. Template Design: If you are creating a template, avoid hard-coding Required="true" for fields that are intended to be optional [5]. 3. Handling Empty Defaults: If a setting is optional, it is safer to leave the Default attribute empty or provide a sensible fallback value if the application supports it [1][5]. 4. Manual Removal: If a container fails to start due to an empty path or variable, users can often edit the container in "Advanced View" within the Unraid WebGUI to remove the problematic entry or provide a valid path [6][3].

Citations:


🌐 Web query:

Unraid Docker template Required=true environment variable must be entered before create update official docs

💡 Result:

In the context of Unraid Docker templates, setting Required="true" within a configuration entry (e.g., Variable, Port, Path) signifies that the Unraid web interface will treat that field as mandatory [1][2]. When a template developer defines a configuration entry with Required="true", the Unraid web GUI adds a validation requirement to that field [3]. Consequently, when a user attempts to create or update the Docker container via the web GUI, the interface will enforce that the field cannot be left empty [2]. If the field is left blank, the system will typically prevent the container from being created or updated until a value is provided [2][3]. This functionality is primarily used by template authors to ensure that critical configuration parameters—such as API keys, mandatory paths, or specific port mappings—are explicitly set by the end user before the container is launched [4][1]. Key takeaways: - Implementation: Developers define this in the template XML using the attribute Required="true" within a Config node [1]. - User Experience: The Unraid web GUI automatically detects this attribute and marks the field as required, preventing submission of the form if the field is empty [3]. - Purpose: It ensures essential settings are not overlooked during container setup [4]. If you are a user encountering a required field, you must provide a valid value in that field within the Docker container's edit screen before the Apply button will function successfully [5][6]. If you are a template developer, ensure you include this attribute only for parameters that are strictly necessary for the container to function, as overly restrictive requirements can impede user setup [2].

Citations:


Make the ImmichServerUrl and ApiKey variables optional
Required="true" makes Unraid treat these as mandatory fields, so the container can’t be created/updated with only /app/Config set. Set both to Required="false" or document that file-based config still needs placeholder values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@templates/immichframe.xml` around lines 20 - 21, Update the ImmichServerUrl
and ApiKey Config entries to use Required="false", allowing the container to be
created or updated with only the file-based /app/Config configuration. Keep
their existing defaults, descriptions, and masking behavior unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant