Skip to content
Closed
1 change: 1 addition & 0 deletions ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public void Setup()
_personAssetsPool = new TestablePersonAssetsPool(_mockApiCache.Object, _mockImmichApi.Object, _mockAccountSettings.Object);

_mockAccountSettings.SetupGet(s => s.People).Returns(new List<Guid>());
_mockAccountSettings.SetupGet(s => s.ExcludedPeople).Returns(new List<Guid>());
}

private AssetResponseDto CreateAsset(string id, AssetTypeEnum type = AssetTypeEnum.IMAGE) => new AssetResponseDto { Id = id, Type = type };
Expand Down
3 changes: 3 additions & 0 deletions ImmichFrame.Core/Interfaces/IServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public interface IAccountSettings
public List<Guid> ExcludedAlbums { get; }
public List<Guid> People { get; }
public List<string> Tags { get; }
public List<Guid> ExcludedPeople { get; }
public int? Rating { get; }

public void ValidateAndInitialize();
Expand Down Expand Up @@ -59,6 +60,8 @@ public interface IGeneralSettings
public string Style { get; }
public string? BaseFontSize { get; }
public bool ShowWeatherDescription { get; }
public bool ShowTemperatureUnit { get; }
public int TemperatureDecimalDigits { get; }
public string? WeatherIconUrl { get; }
public bool ImageZoom { get; }
public bool ImagePan { get; }
Expand Down
71 changes: 42 additions & 29 deletions ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ImmichFrame.Core.Api;
using ImmichFrame.Core.Helpers;
using ImmichFrame.Core.Interfaces;

namespace ImmichFrame.Core.Logic.Pool;
Expand All @@ -10,41 +11,53 @@ protected override async Task<IEnumerable<AssetResponseDto>> LoadAssets(Cancella
var personAssets = new List<AssetResponseDto>();

var people = accountSettings.People;
if (people == null)
if (people == null || people.Count == 0)
{
return personAssets;
}

foreach (var personId in people)
{
int page = 1;
int batchSize = 1000;
int total;
do
{
var metadataBody = new MetadataSearchDto
{
Page = page,
Size = batchSize,
PersonIds = [personId],
WithExif = true,
WithPeople = true
};

if (!accountSettings.ShowVideos)
{
metadataBody.Type = AssetTypeEnum.IMAGE;
}

var personInfo = await immichApi.SearchAssetsAsync(metadataBody, ct);

total = personInfo.Assets.Total;

personAssets.AddRange(personInfo.Assets.Items);
page++;
} while (total == batchSize);
personAssets.AddRange(await LoadAssetsForPerson(personId, ct));
}

var excludedPersonAssets = new List<AssetResponseDto>();

foreach (var personId in accountSettings.ExcludedPeople)
{
excludedPersonAssets.AddRange(await LoadAssetsForPerson(personId, ct));
}

return personAssets;
return personAssets.WhereExcludes(excludedPersonAssets, t => t.Id);
}

private async Task<List<AssetResponseDto>> LoadAssetsForPerson(Guid personId, CancellationToken ct)
{
var assets = new List<AssetResponseDto>();
int page = 1;
int batchSize = 1000;
int lastPageCount;

do
{
var metadataBody = new MetadataSearchDto
{
Page = page,
Size = batchSize,
PersonIds = [personId],
Type = AssetTypeEnum.IMAGE,
WithExif = true,
WithPeople = true
};

var personInfo = await immichApi.SearchAssetsAsync(metadataBody, ct);

lastPageCount = personInfo.Assets.Items.Count;

assets.AddRange(personInfo.Assets.Items);
page++;
} while (lastPageCount == batchSize);

return assets;
}
}
12 changes: 10 additions & 2 deletions ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void TestLoadConfigV2Yaml()

private void VerifyConfig(IServerSettings serverSettings, bool usePrefix, bool expectNullApiKeyFile)
{
VerifyProperties(serverSettings.GeneralSettings);
VerifyProperties(serverSettings.GeneralSettings, "", expectNullApiKeyFile);
VerifyAccounts(serverSettings.Accounts, usePrefix, expectNullApiKeyFile);
}

Expand Down Expand Up @@ -120,7 +120,15 @@ private void VerifyProperties(object o, string? prefix = "", bool expectNullApiK
Assert.That(value, Is.EqualTo(true), prop.Name);
break;
case var t when t == typeof(int):
Assert.That(value, Is.EqualTo(7), prop.Name);
// V1 config doesn't support TemperatureDecimalDigits, so it defaults to 1
if (prop.Name.Equals("TemperatureDecimalDigits") && expectNullApiKeyFile)
{
Assert.That(value, Is.EqualTo(1), prop.Name);
}
else
{
Assert.That(value, Is.EqualTo(prop.Name.Equals("TemperatureDecimalDigits") ? 2 : 7), prop.Name);
}
break;
case var t when t == typeof(double):
Assert.That(value, Is.EqualTo(7.7d), prop.Name);
Expand Down
8 changes: 8 additions & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"Style": "Style_TEST",
"BaseFontSize": "BaseFontSize_TEST",
"ShowWeatherDescription": true,
"ShowTemperatureUnit": true,
"TemperatureDecimalDigits": 2,
"WeatherIconUrl": "WeatherIconUrl_TEST",
"ImageZoom": true,
"ImagePan": true,
Expand Down Expand Up @@ -61,6 +63,9 @@
],
"Tags": [
"Account1.Tags_TEST"
],
"ExcludedPeople": [
"00000000-0000-0000-0000-000000000001"
]
},
{
Expand All @@ -86,6 +91,9 @@
],
"Tags": [
"Account2.Tags_TEST"
],
"ExcludedPeople": [
"00000000-0000-0000-0000-000000000001"
]
}
]
Expand Down
6 changes: 6 additions & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ General:
Style: Style_TEST
BaseFontSize: BaseFontSize_TEST
ShowWeatherDescription: true
ShowTemperatureUnit: true
TemperatureDecimalDigits: 2
WeatherIconUrl: WeatherIconUrl_TEST
ImageZoom: true
ImagePan: true
Expand All @@ -55,6 +57,8 @@ Accounts:
- 00000000-0000-0000-0000-000000000001
Tags:
- Account1.Tags_TEST
ExcludedPeople:
- 00000000-0000-0000-0000-000000000001
- ImmichServerUrl: Account2.ImmichServerUrl_TEST
ApiKey: Account2.ApiKey_TEST
ApiKeyFile: Account2.ApiKeyFile_TEST
Expand All @@ -74,3 +78,5 @@ Accounts:
- 00000000-0000-0000-0000-000000000001
Tags:
- Account2.Tags_TEST
ExcludedPeople:
- 00000000-0000-0000-0000-000000000001
14 changes: 13 additions & 1 deletion ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class ServerSettingsV1 : IConfigSettable
public List<Guid> ExcludedAlbums { get; set; } = new List<Guid>();
public List<Guid> People { get; set; } = new List<Guid>();
public List<string> Tags { get; set; } = new List<string>();
public List<Guid> ExcludedPeople { get; set; } = new List<Guid>();
public int? Rating { get; set; }
public List<string> Webcalendars { get; set; } = new List<string>();
public int RefreshAlbumPeopleInterval { get; set; } = 12;
Expand Down Expand Up @@ -93,6 +94,7 @@ class AccountSettingsV1Adapter(ServerSettingsV1 _delegate) : IAccountSettings
public List<Guid> ExcludedAlbums => _delegate.ExcludedAlbums;
public List<Guid> People => _delegate.People;
public List<string> Tags => _delegate.Tags;
public List<Guid> ExcludedPeople => _delegate.ExcludedPeople;
public int? Rating => _delegate.Rating;

public void ValidateAndInitialize() { }
Expand Down Expand Up @@ -128,6 +130,8 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings
public string Style => _delegate.Style;
public string? BaseFontSize => _delegate.BaseFontSize;
public bool ShowWeatherDescription => _delegate.ShowWeatherDescription;
public bool ShowTemperatureUnit => true;
public int TemperatureDecimalDigits => 1;
public string? WeatherIconUrl => _delegate.WeatherIconUrl;
public bool ImageZoom => _delegate.ImageZoom;
public bool ImagePan => _delegate.ImagePan;
Expand All @@ -136,6 +140,14 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings
public string Layout => _delegate.Layout;
public string Language => _delegate.Language;

public void Validate() { }
public void Validate()
{
if (TemperatureDecimalDigits < 0 || TemperatureDecimalDigits > 2)
{
throw new ArgumentOutOfRangeException(nameof(TemperatureDecimalDigits),
TemperatureDecimalDigits,
"TemperatureDecimalDigits must be between 0 and 2.");
}
}
}
}
4 changes: 4 additions & 0 deletions ImmichFrame.WebApi/Models/ClientSettingsDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class ClientSettingsDto
public string Style { get; set; }
public string? BaseFontSize { get; set; }
public bool ShowWeatherDescription { get; set; }
public bool ShowTemperatureUnit { get; set; }
public int TemperatureDecimalDigits { get; set; }
public string? WeatherIconUrl { get; set; }
public bool ImageZoom { get; set; }
public bool ImagePan { get; set; }
Expand Down Expand Up @@ -57,6 +59,8 @@ public static ClientSettingsDto FromGeneralSettings(IGeneralSettings generalSett
dto.Style = generalSettings.Style;
dto.BaseFontSize = generalSettings.BaseFontSize;
dto.ShowWeatherDescription = generalSettings.ShowWeatherDescription;
dto.ShowTemperatureUnit = generalSettings.ShowTemperatureUnit;
dto.TemperatureDecimalDigits = generalSettings.TemperatureDecimalDigits;
dto.WeatherIconUrl = generalSettings.WeatherIconUrl;
dto.ImageZoom = generalSettings.ImageZoom;
dto.ImagePan = generalSettings.ImagePan;
Expand Down
13 changes: 12 additions & 1 deletion ImmichFrame.WebApi/Models/ServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable
public string Style { get; set; } = "none";
public string? BaseFontSize { get; set; }
public bool ShowWeatherDescription { get; set; } = true;
public bool ShowTemperatureUnit { get; set; } = true;
public int TemperatureDecimalDigits { get; set; } = 1;
public string? WeatherIconUrl { get; set; } = "https://openweathermap.org/img/wn/{IconId}.png";
public bool ImageZoom { get; set; } = true;
public bool ImagePan { get; set; } = false;
Expand All @@ -73,7 +75,15 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable
public string? Webhook { get; set; }
public string? AuthenticationSecret { get; set; }

public void Validate() { }
public void Validate()
{
if (TemperatureDecimalDigits < 0 || TemperatureDecimalDigits > 2)
{
throw new ArgumentOutOfRangeException(nameof(TemperatureDecimalDigits),
TemperatureDecimalDigits,
"TemperatureDecimalDigits must be between 0 and 2.");
}
}
}

public class ServerAccountSettings : IAccountSettings, IConfigSettable
Expand All @@ -93,6 +103,7 @@ public class ServerAccountSettings : IAccountSettings, IConfigSettable
public List<Guid> ExcludedAlbums { get; set; } = new();
public List<Guid> People { get; set; } = new();
public List<string> Tags { get; set; } = new();
public List<Guid> ExcludedPeople { get; set; } = new();
public int? Rating { get; set; }

public void ValidateAndInitialize()
Expand Down
3 changes: 3 additions & 0 deletions docker/Settings.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"Tags": [
"Vacation",
"Travel/Europe"
],
"ExcludedPeople": [
"UUID"
]
}
]
Expand Down
2 changes: 2 additions & 0 deletions docker/Settings.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,5 @@ Accounts:
Tags:
- Vacation
- Travel/Europe
ExcludedPeople:
- UUID
1 change: 1 addition & 0 deletions docker/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ ApiKey=KEY
# Albums=ALBUM1,ALBUM2
# ExcludedAlbums=ALBUM3,ALBUM4
# People=PERSON1,PERSON2
# ExcludedPeople=PERSON3,PERSON4
# Webcalendars=https://calendar.google.com/calendar/ical/XXXXXX/public/basic.ics,https://user:pass@calendar.immichframe.dev/dav/calendars/basic.ics
# RefreshAlbumPeopleInterval=12
# ShowClock=true
Expand Down
14 changes: 7 additions & 7 deletions immichFrame.Web/src/lib/components/elements/clock.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@
class="text-xl sm:text-xl md:text-2xl lg:text-3xl font-semibold text-shadow-sm weather-info"
>
{#if $configStore.weatherIconUrl && primaryIconId()}
<img
src="{$configStore.weatherIconUrl.replace('{IconId}', encodeURIComponent(primaryIconId()!))}"
class="icon-weather"
<img
src="{$configStore.weatherIconUrl.replace('{IconId}', encodeURIComponent(primaryIconId()!))}"
class="icon-weather"
alt="{weather.description}"
>
{/if}

<div class="weather-location">{weather.location},</div>
<div class="weather-temperature">{weather.temperature?.toFixed(1)}</div>
<div class="weather-unit">{weather.unit}</div>
<div class="weather-temperature">{(weather.temperature ?? 0).toFixed($configStore.temperatureDecimalDigits ?? 1)}</div>
<div class="weather-unit">{$configStore.showTemperatureUnit === false ? '°' : (weather.unit ?? '°')}</div>
</div>

{#if $configStore.showWeatherDescription}
<p id="clockweatherdesc" class="text-sm sm:text-sm md:text-md lg:text-xl text-shadow-sm">
{weather.description}
Expand Down
2 changes: 2 additions & 0 deletions immichFrame.Web/src/lib/immichFrameApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ export type ClientSettingsDto = {
style?: string | null;
baseFontSize?: string | null;
showWeatherDescription?: boolean;
showTemperatureUnit?: boolean;
temperatureDecimalDigits?: number;
weatherIconUrl?: string | null;
imageZoom?: boolean;
imagePan?: boolean;
Expand Down