Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions ImmichFrame.Core.Tests/Logic/Pool/AlbumAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public void Setup()

_mockAccountSettings.SetupGet(s => s.Albums).Returns(new List<Guid>());
_mockAccountSettings.SetupGet(s => s.ExcludedAlbums).Returns(new List<Guid>());
_mockAccountSettings.SetupGet(s => s.ShowOnlyAssetsInAlbums).Returns(false);
}

private AssetResponseDto CreateAsset(string id) => new AssetResponseDto { Id = FixtureHelpers.GuidFor(id), Type = AssetTypeEnum.IMAGE };
Expand Down Expand Up @@ -94,6 +95,58 @@ public async Task LoadAssets_NoExcludedAlbums_ReturnsAlbums()
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("A")));
}

[Test]
public async Task LoadAssets_ShowOnlyAssetsInAlbums_LoadsAssetsFromAllAlbums()
{
var album1Id = Guid.NewGuid();
var album2Id = Guid.NewGuid();

_mockAccountSettings.SetupGet(s => s.ShowOnlyAssetsInAlbums).Returns(true);
_mockAccountSettings.SetupGet(s => s.Albums).Returns(new List<Guid>());
_mockImmichApi.Setup(api => api.GetAllAlbumsAsync(null, null, null, null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<AlbumResponseDto>
{
new() { Id = album1Id, AlbumName = "One" },
new() { Id = album2Id, AlbumName = "Two" },
});
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.IsAny<string>(), It.IsAny<string>(), It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(album1Id)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { CreateAsset("A") }, Total = 1 } });
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.IsAny<string>(), It.IsAny<string>(), It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(album2Id)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { CreateAsset("B") }, Total = 1 } });

var result = (await _albumAssetsPool.GetAssets(25)).ToList();

Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("A")));
Assert.That(result.Any(a => a.Id == FixtureHelpers.GuidFor("B")));
_mockImmichApi.Verify(api => api.GetAllAlbumsAsync(null, null, null, null, null, It.IsAny<CancellationToken>()), Times.Once);
}

[Test]
public async Task LoadAssets_ShowOnlyAssetsInAlbums_DeduplicatesAssetsInMultipleAlbums()
{
var album1Id = Guid.NewGuid();
var album2Id = Guid.NewGuid();
var sharedAsset = CreateAsset("shared");

_mockAccountSettings.SetupGet(s => s.ShowOnlyAssetsInAlbums).Returns(true);
_mockImmichApi.Setup(api => api.GetAllAlbumsAsync(null, null, null, null, null, It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<AlbumResponseDto>
{
new() { Id = album1Id, AlbumName = "One" },
new() { Id = album2Id, AlbumName = "Two" },
});
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.IsAny<string>(), It.IsAny<string>(), It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(album1Id)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { sharedAsset }, Total = 1 } });
_mockImmichApi.Setup(api => api.SearchAssetsAsync(It.IsAny<string>(), It.IsAny<string>(), It.Is<MetadataSearchDto>(d => d.AlbumIds.Contains(album2Id)), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SearchResponseDto { Assets = new SearchAssetResponseDto { Items = new List<AssetResponseDto> { sharedAsset }, Total = 1 } });

var result = (await _albumAssetsPool.GetAssets(25)).ToList();

Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result.Single().Id, Is.EqualTo(sharedAsset.Id));
}

[Test]
public async Task LoadAssets_NullAlbums_ReturnsEmpty()
{
Expand Down
1 change: 1 addition & 0 deletions ImmichFrame.Core/Interfaces/IServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public interface IAccountSettings
public bool ShowFavorites { get; }
public bool ShowArchived { get; }
public bool ShowVideos { get; }
public bool ShowOnlyAssetsInAlbums { get; }
public int? ImagesFromDays { get; }
public DateTime? ImagesFromDate { get; }
public DateTime? ImagesUntilDate { get; }
Expand Down
17 changes: 14 additions & 3 deletions ImmichFrame.Core/Logic/Pool/AlbumAssetsPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ protected override async Task<IEnumerable<AssetResponseDto>> LoadAssets(Cancella
{
var albumAssets = new List<AssetResponseDto>();

var albums = accountSettings.Albums;
var albums = await GetAlbumIds(ct);
if (albums != null)
{
foreach (var albumId in albums)
Expand Down Expand Up @@ -37,6 +37,17 @@ protected override async Task<IEnumerable<AssetResponseDto>> LoadAssets(Cancella
}
}

return albumAssets;
return albumAssets.DistinctBy(asset => asset.Id);
}
}

private async Task<IEnumerable<Guid>?> GetAlbumIds(CancellationToken ct)
{
if (!accountSettings.ShowOnlyAssetsInAlbums)
{
return accountSettings.Albums;
}

var albums = await immichApi.GetAllAlbumsAsync(null, null, null, null, null, ct);
return albums.Select(album => album.Id);
}
}
5 changes: 5 additions & 0 deletions ImmichFrame.Core/Logic/PooledImmichFrameLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ private static TimeSpan RefreshInterval(int hours)

private IAssetPool BuildPool(IAccountSettings accountSettings)
{
if (accountSettings.ShowOnlyAssetsInAlbums)
{
return new AlbumAssetsPool(_apiCache, _immichApi, accountSettings);
}

var hasAlbums = accountSettings.Albums?.Any() ?? false;
var hasPeople = accountSettings.People?.Any() ?? false;
var hasTags = accountSettings.Tags?.Any() ?? false;
Expand Down
1 change: 1 addition & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV1.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"ShowFavorites": true,
"ShowArchived": true,
"ShowVideos": true,
"ShowOnlyAssetsInAlbums": true,
"ImagesFromDays": 7,
"ImagesFromDate": "2020-01-02",
"ImagesUntilDate": "2020-01-02",
Expand Down
2 changes: 2 additions & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"ShowFavorites": true,
"ShowArchived": true,
"ShowVideos": true,
"ShowOnlyAssetsInAlbums": true,
"ImagesFromDays": 7,
"ImagesUntilDate": "2020-01-02",
"Rating": 7,
Expand All @@ -72,6 +73,7 @@
"ShowFavorites": true,
"ShowArchived": true,
"ShowVideos": true,
"ShowOnlyAssetsInAlbums": true,
"ImagesFromDays": 7,
"ImagesUntilDate": "2020-01-02",
"Rating": 7,
Expand Down
2 changes: 2 additions & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Accounts:
ShowFavorites: true
ShowArchived: true
ShowVideos: true
ShowOnlyAssetsInAlbums: true
ImagesFromDays: 7
ImagesUntilDate: '2020-01-02'
Rating: 7
Expand All @@ -63,6 +64,7 @@ Accounts:
ShowFavorites: true
ShowArchived: true
ShowVideos: true
ShowOnlyAssetsInAlbums: true
ImagesFromDays: 7
ImagesUntilDate: '2020-01-02'
Rating: 7
Expand Down
4 changes: 3 additions & 1 deletion ImmichFrame.WebApi.Tests/Resources/TestV2_NoGeneral.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"ShowMemories": true,
"ShowFavorites": true,
"ShowArchived": true,
"ShowOnlyAssetsInAlbums": true,
"ImagesFromDays": 7,
"ImagesUntilDate": "2020-01-02",
"Rating": 7,
Expand All @@ -28,6 +29,7 @@
"ShowFavorites": true,
"ShowArchived": true,
"ShowVideos": true,
"ShowOnlyAssetsInAlbums": true,
"ImagesFromDays": 7,
"ImagesUntilDate": "2020-01-02",
"Rating": 7,
Expand All @@ -42,4 +44,4 @@
]
}
]
}
}
2 changes: 2 additions & 0 deletions ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class ServerSettingsV1 : IConfigSettable
public bool ShowFavorites { get; set; } = false;
public bool ShowArchived { get; set; } = false;
public bool ShowVideos { get; set; } = false;
public bool ShowOnlyAssetsInAlbums { get; set; } = false;
public bool DownloadImages { get; set; } = false;
public int RenewImagesDuration { get; set; } = 30;
public int? ImagesFromDays { get; set; }
Expand Down Expand Up @@ -85,6 +86,7 @@ class AccountSettingsV1Adapter(ServerSettingsV1 _delegate) : IAccountSettings
public bool ShowFavorites => _delegate.ShowFavorites;
public bool ShowArchived => _delegate.ShowArchived;
public bool ShowVideos => _delegate.ShowVideos;
public bool ShowOnlyAssetsInAlbums => _delegate.ShowOnlyAssetsInAlbums;
public bool PlayAudio => _delegate.PlayAudio;
public int? ImagesFromDays => _delegate.ImagesFromDays;
public DateTime? ImagesFromDate => _delegate.ImagesFromDate;
Expand Down
1 change: 1 addition & 0 deletions ImmichFrame.WebApi/Models/ServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public class ServerAccountSettings : IAccountSettings, IConfigSettable
public bool ShowFavorites { get; set; } = false;
public bool ShowArchived { get; set; } = false;
public bool ShowVideos { get; set; } = false;
public bool ShowOnlyAssetsInAlbums { get; set; } = false;

public int? ImagesFromDays { get; set; }
public DateTime? ImagesFromDate { get; set; }
Expand Down
4 changes: 4 additions & 0 deletions docs/docs/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ Accounts:
ShowArchived: false # boolean
# If this is set, video assets are included in the slideshow.
ShowVideos: false # boolean
# If this is set, only assets that are part of any album are displayed.
ShowOnlyAssetsInAlbums: false # boolean
# Show images from the last X days, e.g., 365 -> show images from the last year
ImagesFromDays: null # int
# Show images before date.
Expand Down Expand Up @@ -152,6 +154,8 @@ If this is enabled, the web api required the `Authorization`-Header with `Bearer
### Filtering on Albums or People
You can get the UUIDs from the URL of the album/person. For this URL: `https://demo.immich.app/albums/85c85b29-c95d-4a8b-90f7-c87da1d518ba` this is the UUID: `85c85b29-c95d-4a8b-90f7-c87da1d518ba`

Set `ShowOnlyAssetsInAlbums` to `true` to include assets from all albums without listing each album UUID. When enabled, only album assets are considered and `ExcludedAlbums` still applies.

### Filtering on Tags
For tags, use the full hierarchical path (the `value` field) as it appears in Immich. Tags in Immich support hierarchical structures using forward slashes (e.g., `Parent/Child`). Matching is case-sensitive, and the full path will be automatically resolved to the tag ID.

Expand Down
3 changes: 2 additions & 1 deletion docs/docs/getting-started/configurationV1.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ sidebar_position: 4
| [Filtering](#filtering) | ShowMemories | boolean | false | If this is set, memories are displayed. |
| [Filtering](#filtering) | ShowFavorites | boolean | false | If this is set, favorites are displayed. |
| [Filtering](#filtering) | ShowArchived | boolean | false | If this is set, assets marked archived are displayed. |
| [Filtering](#filtering) | ShowOnlyAssetsInAlbums | boolean | false | If this is set, assets from all albums are displayed and `Albums` is ignored. `ExcludedAlbums` still applies. |
| [Filtering](#filtering) | ImagesFromDays | int | | Show images from the last X days. e.g 365 -> show images from the last year |
| [Filtering](#filtering) | ImagesFromDate | Date | | Show images after date. Overwrites the `ImagesFromDays`-Setting |
| [Filtering](#filtering) | ImagesUntilDate | Date | | Show images before date. |
Expand Down Expand Up @@ -111,4 +112,4 @@ volumes:
```

[openweathermap-url]: https://openweathermap.org/appid
[immich-api-url]: https://immich.app/docs/features/command-line-interface#obtain-the-api-key
[immich-api-url]: https://immich.app/docs/features/command-line-interface#obtain-the-api-key