Skip to content

Immich v3#654

Open
JW-CH wants to merge 3 commits into
mainfrom
immich_v3
Open

Immich v3#654
JW-CH wants to merge 3 commits into
mainfrom
immich_v3

Conversation

@JW-CH

@JW-CH JW-CH commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Refactor

    • Updated asset identification handling across the system for improved consistency.
    • Refined API method signatures for asset retrieval workflows.
    • Enhanced pagination handling with optimized numeric types.
  • Tests

    • Comprehensive updates to asset pool and controller test suites to align with system changes.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Asset identifiers are migrated from string to Guid in IAssetAccountTracker and its implementations. Album and excluded-album asset loading switches from GetAlbumInfoAsync to SearchAssetsAsync with MetadataSearchDto. Several ImmichApi call arities are updated, pagination totals widened to long, and all test fixtures aligned to deterministic GUID generation via a new FixtureHelpers.GuidFor helper.

Changes

Guid ID migration, SearchAssetsAsync adoption, and API alignment

Layer / File(s) Summary
IAssetAccountTracker Guid contract and implementations
ImmichFrame.Core/Logic/AccountSelection/IAssetAccountTracker.cs, ImmichFrame.Core/Logic/AccountSelection/BloomFilterAssetAccountTracker.cs, ImmichFrame.Core/Logic/AccountSelection/TotalAccountImagesSelectionStrategy.cs
Interface methods RecordAssetLocation and ForAsset<T> change from string assetId to Guid assetId. BloomFilterAssetAccountTracker converts Guid to string only for bloom-filter storage. TotalAccountImagesSelectionStrategy forwards the Guid directly instead of calling ToString().
Album asset loading via SearchAssetsAsync
ImmichFrame.Core/Logic/Pool/AlbumAssetsPool.cs, ImmichFrame.Core/Helpers/AssetHelper.cs
AlbumAssetsPool.LoadAssets and AssetHelper.GetExcludedAlbumAssets both replace GetAlbumInfoAsync calls with SearchAssetsAsync using MetadataSearchDto { AlbumIds = [albumId] }, appending searchResponse.Assets.Items.
Pool pagination and API call arity fixes
ImmichFrame.Core/Logic/Pool/TagAssetsPool.cs, ImmichFrame.Core/Logic/Pool/FavoriteAssetsPool.cs, ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs, ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs, ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs, ImmichFrame.WebApi/Controllers/AssetController.cs
TagAssetsPool.seenIds changes to HashSet<Guid> and removes new Guid(tag.Id) conversion. FavoriteAssetsPool and PeopleAssetsPool widen total from int to long. MemoryAssetsPool adds null arguments to SearchMemoriesAsync and GetAssetInfoAsync calls. PooledImmichFrameLogic updates arities for GetAssetInfoAsync, GetAllAlbumsAsync, ViewAssetAsync, and PlayAssetVideoAsync. AssetController removes new Guid(...) wrapping around randomAsset.Id.
FixtureHelpers.GuidFor and pool test alignment
ImmichFrame.Core.Tests/Logic/Pool/FixtureHelpers.cs, ImmichFrame.Core.Tests/Logic/Pool/*AssetsPoolTests.cs, ImmichFrame.Core.Tests/Logic/Pool/*AssetPoolTests.cs
Adds GuidFor(string seed) that derives a deterministic Guid via MD5. All pool test CreateAsset/CreateSampleAssets helpers and assertions across ten test files are updated to use GuidFor-based IDs and the new ImmichApi mock signatures.
AssetControllerTests DTO-based response builder
ImmichFrame.WebApi.Tests/Controllers/AssetControllerTests.cs
Replaces hand-crafted JSON strings for the /search/metadata mock with a SearchResponseDto serialized via System.Text.Json. Adds a BuildAssetResponse helper that constructs schema-valid AssetResponseDto objects with required fields.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • immichFrame/ImmichFrame#499: Introduced GetExcludedAlbumAssets in AssetHelper, which this PR now modifies to use SearchAssetsAsync instead of GetAlbumInfoAsync.
  • immichFrame/ImmichFrame#535: Modified AlbumAssetsPool.LoadAssets null-guards in the same code path that this PR changes to use SearchAssetsAsync.
  • immichFrame/ImmichFrame#537: Introduced TagAssetsPool and tag-based asset selection tests that this PR updates with Guid-based ID handling.

Poem

🐇 A rabbit once muddled with strings in a row,
Swapped them for GUIDs so the tests wouldn't blow.
SearchAssetsAsync now hunts down the lot,
No more GetAlbumInfoAsync, that method's forgot.
With MD5 seeds and deterministic flair,
Each asset gets a proper Guid — perfectly square! 🎲

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.60% 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 'Immich v3' is vague and generic, failing to convey the specific technical changes in this comprehensive refactoring that updates asset ID handling from strings to GUIDs across multiple test files and production code. Use a more descriptive title that captures the main change, such as 'Convert asset identifiers from strings to GUIDs' or 'Update asset ID handling to use deterministic GUID generation'.
✅ 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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch immich_v3

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 and usage tips.

@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.Core/Logic/PooledImmichFrameLogic.cs (1)

78-80: Use named arguments for ImmichApi calls with multiple null parameters.

These calls pass several positional null arguments to generated client methods with optional parameters (key, slug, isOwned, isShared, name, edited, size). Using named arguments would make the intent explicit and guard against parameter order changes during client regeneration.

Affected calls:

  • GetAssetInfoAsync(assetId, null, null)GetAssetInfoAsync(id: assetId, key: null, slug: null) (lines 78, 88)
  • GetAllAlbumsAsync(assetId, null, null, null, null)GetAllAlbumsAsync(assetId: assetId, id: null, isOwned: null, isShared: null, name: null) (line 80)
  • ViewAssetAsync(null, id, string.Empty, AssetMediaSize.Preview, null)ViewAssetAsync(edited: null, id: id, key: string.Empty, size: AssetMediaSize.Preview, slug: null) (line 143)
  • PlayAssetVideoAsync(id, null, null)PlayAssetVideoAsync(id: id, key: null, slug: null) (line 176)
🤖 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,
Replace positional null arguments with named arguments in ImmichApi method calls
to improve clarity and protect against parameter order changes. Update the
GetAssetInfoAsync call on line 78 to use named arguments for key and slug
parameters, the GetAllAlbumsAsync call on line 80 to use named arguments for id,
isOwned, isShared, and name parameters, the ViewAssetAsync call on line 143 to
use named arguments for edited, id, key, size, and slug parameters, and the
PlayAssetVideoAsync call on line 176 to use named arguments for id, key, and
slug parameters. Each call should explicitly name all parameters being passed
instead of relying on positional argument order.
🤖 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/Helpers/AssetHelper.cs`:
- Around line 15-20: The GetExcludedAlbumAssets method in AssetHelper.cs and
LoadAssets method in AlbumAssetsPool.cs only fetch the first page of search
results, causing assets from later pages to be missed. Implement pagination in
both methods by wrapping the search call in a loop that uses Page and Size
parameters set to a batch size of 1000, accumulating results across multiple
pages until a page returns fewer items than the batch size. Follow the same
pagination pattern already implemented in FavoriteAssetsPool, PeopleAssetsPool,
and TagAssetsPool to ensure consistency across the codebase.

In `@ImmichFrame.Core/Logic/Pool/AlbumAssetsPool.cs`:
- Around line 17-19: The albumAssets query in AlbumAssetsPool is not paginated
and only retrieves the first page of results, causing large albums to have
truncated assets. Implement pagination similar to the FavoriteAssetsPool,
PeopleAssetsPool, and TagAssetsPool implementations by wrapping the
immichApi.SearchAssetsAsync call in a loop that increments the Page parameter on
MetadataSearchDto until all pages are retrieved. Apply the same pagination fix
to AssetHelper.cs where the identical issue exists.

---

Nitpick comments:
In `@ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs`:
- Around line 78-80: Replace positional null arguments with named arguments in
ImmichApi method calls to improve clarity and protect against parameter order
changes. Update the GetAssetInfoAsync call on line 78 to use named arguments for
key and slug parameters, the GetAllAlbumsAsync call on line 80 to use named
arguments for id, isOwned, isShared, and name parameters, the ViewAssetAsync
call on line 143 to use named arguments for edited, id, key, size, and slug
parameters, and the PlayAssetVideoAsync call on line 176 to use named arguments
for id, key, and slug parameters. Each call should explicitly name all
parameters being passed instead of relying on positional argument order.
🪄 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: 021382d4-dd64-49fa-b97b-f3c51b252447

📥 Commits

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

📒 Files selected for processing (24)
  • ImmichFrame.Core.Tests/Logic/Pool/AggregatingAssetPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/AlbumAssetsPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/AllAssetsPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/CachingApiAssetsPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/FavoriteAssetsPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/FixtureHelpers.cs
  • ImmichFrame.Core.Tests/Logic/Pool/MemoryAssetsPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/MultiAssetPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/QueuingAssetPoolTests.cs
  • ImmichFrame.Core.Tests/Logic/Pool/TagAssetsPoolTests.cs
  • ImmichFrame.Core/Helpers/AssetHelper.cs
  • ImmichFrame.Core/Logic/AccountSelection/BloomFilterAssetAccountTracker.cs
  • ImmichFrame.Core/Logic/AccountSelection/IAssetAccountTracker.cs
  • ImmichFrame.Core/Logic/AccountSelection/TotalAccountImagesSelectionStrategy.cs
  • ImmichFrame.Core/Logic/Pool/AlbumAssetsPool.cs
  • ImmichFrame.Core/Logic/Pool/FavoriteAssetsPool.cs
  • ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs
  • ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs
  • ImmichFrame.Core/Logic/Pool/TagAssetsPool.cs
  • ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs
  • ImmichFrame.Core/OpenAPIs/immich-openapi-specs.json
  • ImmichFrame.WebApi.Tests/Controllers/AssetControllerTests.cs
  • ImmichFrame.WebApi/Controllers/AssetController.cs

Comment on lines +15 to 20
var metadataBody = new MetadataSearchDto { AlbumIds = [albumId] };
var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
if (searchResponse.Assets != null)
{
excludedAlbumAssets.AddRange(albumInfo.Assets);
excludedAlbumAssets.AddRange(searchResponse.Assets.Items);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Cross-check all exclusion and album search callsites for explicit pagination.
rg -n --type=cs "ExcludedAlbums|AlbumIds" -C2
rg -n --type=cs "SearchAssetsAsync\\(" -C2
rg -n --type=cs "Page\\s*=|Size\\s*="

Repository: immichFrame/ImmichFrame

Length of output: 42490


Excluded-album filtering requires pagination to fetch all assets.

The GetExcludedAlbumAssets method in AssetHelper.cs and LoadAssets method in AlbumAssetsPool.cs both fetch only the first page of search results. Albums with more assets than the default page size will have assets on later pages not excluded/included, allowing them to appear incorrectly in results.

Other search-based pools in the codebase (FavoriteAssetsPool, PeopleAssetsPool, TagAssetsPool) all implement pagination with a loop, Page/Size parameters, and batchSize = 1000, stopping when fewer items are returned than the batch size. The same pattern should be applied here.

Suggested fix
-            var metadataBody = new MetadataSearchDto { AlbumIds = [albumId] };
-            var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
-            if (searchResponse.Assets != null)
-            {
-                excludedAlbumAssets.AddRange(searchResponse.Assets.Items);
-            }
+            const int pageSize = 1000;
+            for (var page = 1; ; page++)
+            {
+                var metadataBody = new MetadataSearchDto
+                {
+                    AlbumIds = [albumId],
+                    Page = page,
+                    Size = pageSize,
+                };
+                var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
+                var items = searchResponse.Assets?.Items ?? [];
+                if (items.Count == 0) break;
+
+                excludedAlbumAssets.AddRange(items);
+                if (items.Count < pageSize) break;
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var metadataBody = new MetadataSearchDto { AlbumIds = [albumId] };
var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
if (searchResponse.Assets != null)
{
excludedAlbumAssets.AddRange(albumInfo.Assets);
excludedAlbumAssets.AddRange(searchResponse.Assets.Items);
}
const int pageSize = 1000;
for (var page = 1; ; page++)
{
var metadataBody = new MetadataSearchDto
{
AlbumIds = [albumId],
Page = page,
Size = pageSize,
};
var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
var items = searchResponse.Assets?.Items ?? [];
if (items.Count == 0) break;
excludedAlbumAssets.AddRange(items);
if (items.Count < pageSize) break;
}
🤖 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/Helpers/AssetHelper.cs` around lines 15 - 20, The
GetExcludedAlbumAssets method in AssetHelper.cs and LoadAssets method in
AlbumAssetsPool.cs only fetch the first page of search results, causing assets
from later pages to be missed. Implement pagination in both methods by wrapping
the search call in a loop that uses Page and Size parameters set to a batch size
of 1000, accumulating results across multiple pages until a page returns fewer
items than the batch size. Follow the same pagination pattern already
implemented in FavoriteAssetsPool, PeopleAssetsPool, and TagAssetsPool to ensure
consistency across the codebase.

Comment on lines +17 to +19
var metadataBody = new MetadataSearchDto { AlbumIds = [albumId] };
var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
albumAssets.AddRange(searchResponse.Assets.Items);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify pagination usage patterns for SearchAssetsAsync in the repo.
rg -n --type=cs "SearchAssetsAsync\\(" -C2
rg -n --type=cs "new MetadataSearchDto" -C2
rg -n --type=cs "Page\\s*=|Size\\s*="

Repository: immichFrame/ImmichFrame

Length of output: 29547


Paginate album search results to avoid silently dropping assets.

At Line 18, each album is queried once without Page/Size, so only the first page is loaded. This is inconsistent with other asset pool implementations (FavoriteAssetsPool, PeopleAssetsPool, TagAssetsPool) which use pagination loops. Large albums will have their assets truncated. The same issue exists in AssetHelper.cs.

Suggested fix
-                var metadataBody = new MetadataSearchDto { AlbumIds = [albumId] };
-                var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
-                albumAssets.AddRange(searchResponse.Assets.Items);
+                const int pageSize = 1000;
+                for (var page = 1; ; page++)
+                {
+                    var metadataBody = new MetadataSearchDto
+                    {
+                        AlbumIds = [albumId],
+                        Page = page,
+                        Size = pageSize,
+                    };
+                    var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
+                    var items = searchResponse.Assets?.Items ?? [];
+                    if (items.Count == 0) break;
+
+                    albumAssets.AddRange(items);
+                    if (items.Count < pageSize) break;
+                }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var metadataBody = new MetadataSearchDto { AlbumIds = [albumId] };
var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
albumAssets.AddRange(searchResponse.Assets.Items);
const int pageSize = 1000;
for (var page = 1; ; page++)
{
var metadataBody = new MetadataSearchDto
{
AlbumIds = [albumId],
Page = page,
Size = pageSize,
};
var searchResponse = await immichApi.SearchAssetsAsync(metadataBody, ct);
var items = searchResponse.Assets?.Items ?? [];
if (items.Count == 0) break;
albumAssets.AddRange(items);
if (items.Count < pageSize) break;
}
🤖 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/AlbumAssetsPool.cs` around lines 17 - 19, The
albumAssets query in AlbumAssetsPool is not paginated and only retrieves the
first page of results, causing large albums to have truncated assets. Implement
pagination similar to the FavoriteAssetsPool, PeopleAssetsPool, and
TagAssetsPool implementations by wrapping the immichApi.SearchAssetsAsync call
in a loop that increments the Page parameter on MetadataSearchDto until all
pages are retrieved. Apply the same pagination fix to AssetHelper.cs where the
identical issue exists.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant