diff --git a/Dockerfile b/Dockerfile
index 83abfa9..bb000f6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,7 +7,6 @@ ARG TARGETARCH
WORKDIR /app
COPY . .
-COPY config.example.yaml /app/config/
RUN go mod download
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-X main.version=${VERSION}" -o dist/kiosk .
@@ -21,12 +20,10 @@ ENV TERM=xterm-256color
ENV DEBUG_COLORS=true
ENV COLORTERM=truecolor
-RUN apk add --no-cache tzdata
+RUN apk update && apk add --no-cache tzdata ca-certificates && update-ca-certificates
WORKDIR /
COPY --from=build /app/dist/kiosk .
-EXPOSE 3000
-
ENTRYPOINT ["/kiosk"]
diff --git a/README.md b/README.md
index caaf23a..f54ca9a 100644
--- a/README.md
+++ b/README.md
@@ -246,6 +246,7 @@ kiosk:
| **yaml** | **ENV** | **Value** | **Default** | **Description** |
|-------------------|-------------------------|--------------|-------------|--------------------------------------------------------------------------------------------|
+| port | KIOSK_PORT | int | 3000 | Which port Kiosk should use. NOTE that is port will need to be reflected in your compose file e.g. `KIOSK_PORT:HOST_PORT` |
| password | KIOSK_PASSWORD | string | "" | Please see FAQs for more info. If set, requests MUST contain the password in the GET parameters e.g. `http://192.168.0.123:3000?password=PASSWORD`. |
| cache | KIOSK_CACHE | bool | true | Cache selective Immich api calls to reduce unnecessary calls. |
| prefetch | KIOSK_PREFETCH | bool | true | Pre fetch assets in the background so images load much quicker when refresh timer ends. |
@@ -596,8 +597,9 @@ Then to access Kiosk you MUST add the password param in your URL e.g. http://{UR
- PWA
- monitor config file changes
- make apiCalls more resilient
-- Ken Burns
+- Ken Burns
- splitview horizontal mode
+- docker/immich healthcheck?
------
diff --git a/config.example.yaml b/config.example.yaml
index fe0188d..a979daa 100644
--- a/config.example.yaml
+++ b/config.example.yaml
@@ -14,10 +14,15 @@ disable_screensaver: false # Ask browser to request a lock that prevents device
# Asset sources
show_archived: false # Allow assets marked as archived to be displayed.
-person: # ID(s) of person or people to display
- - ""
-album: # ID(s) of album or albums to display
- - ""
+
+# ID(s) of person or people to display
+person:
+ - "PERSON_ID"
+
+# ID(s) of album or albums to display
+album:
+ - "ALBUM_ID"
+
# UI
disable_ui: false # this is just a shortcut for all ui elements (show_time, show_date, show_image_time, show_image_date)
hide_cursor: false # Hide cursor/mouse via CSS.
diff --git a/config/config.go b/config/config.go
index 326adac..896cbd3 100644
--- a/config/config.go
+++ b/config/config.go
@@ -23,11 +23,15 @@ package config
import (
"encoding/json"
"errors"
+ "os"
"strings"
+ "sync"
+ "time"
"github.com/charmbracelet/log"
"github.com/mcuadros/go-defaults"
"github.com/spf13/viper"
+ "gopkg.in/yaml.v3"
"github.com/labstack/echo/v4"
)
@@ -36,9 +40,13 @@ const (
defaultImmichPort = "2283"
defaultScheme = "http://"
DefaultDateLayout = "02/01/2006"
+ defaultConfigFile = "config.yaml"
)
type KioskSettings struct {
+ // Port which port to use
+ Port int `mapstructure:"port" default:"3000"`
+
// Cache enable/disable api call and image caching
Cache bool `mapstructure:"cache" default:"true"`
@@ -57,6 +65,15 @@ type KioskSettings struct {
}
type Config struct {
+ // v is the viper instance used for configuration management
+ v *viper.Viper
+ // mu is a mutex used to ensure thread-safe access to the configuration
+ mu *sync.Mutex
+ // ReloadTimeStamp timestamp for when the last client reload was called for
+ ReloadTimeStamp string
+ // configLastModTime stores the last modification time of the configuration file
+ configLastModTime time.Time
+
// ImmichApiKey Immich key to access assets
ImmichApiKey string `mapstructure:"immich_api_key" default:""`
// ImmichUrl Immuch base url
@@ -131,6 +148,8 @@ type Config struct {
ShowImageExif bool `mapstructure:"show_image_exif" query:"show_image_exif" form:"show_image_exif" default:"false"`
// ShowImageLocation display image location data
ShowImageLocation bool `mapstructure:"show_image_location" query:"show_image_location" form:"show_image_location" default:"false"`
+ // ShowImageID display image ID
+ ShowImageID bool `mapstructure:"show_image_id" query:"show_image_id" form:"show_image_id" default:"false"`
// Kiosk settings that are unable to be changed via URL queries
Kiosk KioskSettings `mapstructure:"kiosk"`
@@ -141,11 +160,30 @@ type Config struct {
// New returns a new config pointer instance
func New() *Config {
- c := &Config{}
+ c := &Config{
+ v: viper.NewWithOptions(viper.ExperimentalBindStruct()),
+ mu: &sync.Mutex{},
+ ReloadTimeStamp: time.Now().Format(time.RFC3339),
+ }
defaults.SetDefaults(c)
+ info, err := os.Stat(defaultConfigFile)
+ if err == nil {
+ c.configLastModTime = info.ModTime()
+ }
return c
}
+// hasConfigChanged checks if the configuration file has been modified since the last check.
+func (c *Config) hasConfigChanged() bool {
+ info, err := os.Stat(defaultConfigFile)
+ if err != nil {
+ log.Errorf("Checking config file: %v", err)
+ return false
+ }
+
+ return info.ModTime().After(c.configLastModTime)
+}
+
// bindEnvironmentVariables binds specific environment variables to their corresponding
// configuration keys in the Viper instance. This function allows for easy mapping
// between environment variables and configuration settings.
@@ -168,6 +206,7 @@ func bindEnvironmentVariables(v *viper.Viper) error {
configKey string
envVar string
}{
+ {"kiosk.port", "KIOSK_PORT"},
{"kiosk.password", "KIOSK_PASSWORD"},
{"kiosk.cache", "KIOSK_CACHE"},
{"kiosk.prefetch", "KIOSK_PREFETCH"},
@@ -189,6 +228,24 @@ func bindEnvironmentVariables(v *viper.Viper) error {
return nil
}
+// isValidYAML checks if the given file is a valid YAML file.
+func isValidYAML(filename string) bool {
+ content, err := os.ReadFile(filename)
+ if err != nil {
+ log.Errorf("Error reading file: %v", err)
+ return false
+ }
+
+ var data interface{}
+ err = yaml.Unmarshal(content, &data)
+ if err != nil {
+ log.Fatal(err)
+ return false
+ }
+
+ return true
+}
+
// checkUrlScheme checks given url has correct scheme and adds http:// if non if found
func (c *Config) checkUrlScheme() {
@@ -220,9 +277,28 @@ func (c *Config) checkDebuging() {
}
}
+func (c *Config) checkAlbumAndPerson() {
+
+ newAlbum := []string{}
+ for _, album := range c.Album {
+ if album != "" && album != "ALBUM_ID" {
+ newAlbum = append(newAlbum, album)
+ }
+ }
+ c.Album = newAlbum
+
+ newPerson := []string{}
+ for _, person := range c.Person {
+ if person != "" && person != "PERSON_ID" {
+ newPerson = append(newPerson, person)
+ }
+ }
+ c.Person = newPerson
+}
+
// Load loads yaml config file into memory, then loads ENV vars. ENV vars overwrites yaml settings.
func (c *Config) Load() error {
- return c.load("config.yaml")
+ return c.load(defaultConfigFile)
}
// Load loads yaml config file into memory with a custom path, then loads ENV vars. ENV vars overwrites yaml settings.
@@ -230,40 +306,95 @@ func (c *Config) LoadWithConfigLocation(configPath string) error {
return c.load(configPath)
}
+// WatchConfig starts a goroutine that periodically checks for changes in the configuration file
+// and reloads the configuration if changes are detected.
+//
+// This function performs the following actions:
+// 1. Retrieves the initial modification time of the config file.
+// 2. Starts a goroutine that runs indefinitely.
+// 3. Uses a ticker to check for config changes every 5 seconds.
+// 4. If changes are detected, it reloads the configuration and updates the ReloadTimeStamp.
+func (c *Config) WatchConfig() {
+
+ fileInfo, err := os.Stat(defaultConfigFile)
+ if os.IsNotExist(err) {
+ return
+ }
+
+ if fileInfo.IsDir() {
+ log.Errorf("Config file %s is a directory", defaultConfigFile)
+ return
+ }
+
+ info, err := os.Stat(defaultConfigFile)
+ if err != nil {
+ log.Infof("Error getting initial file info: %v", err)
+ } else {
+ c.configLastModTime = info.ModTime()
+ }
+
+ go func() {
+ ticker := time.NewTicker(5 * time.Second)
+ defer ticker.Stop()
+
+ //nolint:gosimple // Using for-select for ticker and potential future cases
+ for {
+ select {
+ case <-ticker.C:
+ if c.hasConfigChanged() {
+ log.Info("Config file changed, reloading config")
+ c.mu.Lock()
+ err := c.Load()
+ if err != nil {
+ log.Errorf("Reloading config: %v", err)
+ } else {
+ c.ReloadTimeStamp = time.Now().Format(time.RFC3339)
+ info, _ := os.Stat(defaultConfigFile)
+ c.configLastModTime = info.ModTime()
+ }
+ c.mu.Unlock()
+ }
+ }
+ }
+ }()
+}
+
// load loads yaml config file into memory, then loads ENV vars. ENV vars overwrites yaml settings.
func (c *Config) load(configFile string) error {
- v := viper.NewWithOptions(viper.ExperimentalBindStruct())
-
- if err := bindEnvironmentVariables(v); err != nil {
+ if err := bindEnvironmentVariables(c.v); err != nil {
log.Errorf("binding environment variables: %v", err)
}
- v.AddConfigPath(".")
+ c.v.AddConfigPath(".")
- v.SetConfigFile(configFile)
+ c.v.SetConfigFile(configFile)
- v.SetEnvPrefix("kiosk")
+ c.v.SetEnvPrefix("kiosk")
- v.AutomaticEnv()
+ c.v.AutomaticEnv()
- err := v.ReadInConfig()
+ err := c.v.ReadInConfig()
if err != nil {
- log.Debug("config.yaml file not being used")
+ if _, err := os.Stat(configFile); os.IsNotExist(err) {
+ log.Infof("Not using %s", configFile)
+ } else if !isValidYAML(configFile) {
+ log.Fatal(err)
+ }
}
- err = v.Unmarshal(&c)
+ err = c.v.Unmarshal(&c)
if err != nil {
log.Error("Environment can't be loaded", "err", err)
return err
}
c.checkRequiredFields()
+ c.checkAlbumAndPerson()
c.checkUrlScheme()
c.checkDebuging()
return nil
-
}
// ConfigWithOverrides overwrites base config with ones supplied via URL queries
diff --git a/config/config_test.go b/config/config_test.go
index 763c903..c00cbfc 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -85,7 +85,7 @@ func TestMalformedURLs(t *testing.T) {
t.Setenv("KIOSK_IMMICH_URL", test.KIOSK_IMMICH_URL)
t.Setenv("KIOSK_IMMICH_API_KEY", "12345")
- var c Config
+ c := New()
err := c.Load()
assert.NoError(t, err, "Config load should not return an error")
@@ -166,3 +166,63 @@ func TestImmichUrlImmichMulitpleAlbum(t *testing.T) {
assert.Contains(t, configWithBaseOnly.Album, "BASE_ALBUM_1", "BASE_ALBUM_1 should be present")
assert.Contains(t, configWithBaseOnly.Album, "BASE_ALBUM_2", "BASE_ALBUM_2 should be present")
}
+
+func TestAlbumAndPerson(t *testing.T) {
+ testCases := []struct {
+ name string
+ inputAlbum []string
+ inputPerson []string
+ expectedAlbum []string
+ expectedPerson []string
+ }{
+ {
+ name: "No empty values",
+ inputAlbum: []string{"album1", "album2"},
+ inputPerson: []string{"person1", "person2"},
+ expectedAlbum: []string{"album1", "album2"},
+ expectedPerson: []string{"person1", "person2"},
+ },
+ {
+ name: "Empty values in album",
+ inputAlbum: []string{"album1", "", "album2", ""},
+ inputPerson: []string{"person1", "person2"},
+ expectedAlbum: []string{"album1", "album2"},
+ expectedPerson: []string{"person1", "person2"},
+ },
+ {
+ name: "Empty values in person",
+ inputAlbum: []string{"album1", "album2"},
+ inputPerson: []string{"", "person1", "", "person2"},
+ expectedAlbum: []string{"album1", "album2"},
+ expectedPerson: []string{"person1", "person2"},
+ },
+ {
+ name: "Empty values in both",
+ inputAlbum: []string{"", "album1", "", "album2"},
+ inputPerson: []string{"person1", "", "", "person2"},
+ expectedAlbum: []string{"album1", "album2"},
+ expectedPerson: []string{"person1", "person2"},
+ },
+ {
+ name: "All empty values",
+ inputAlbum: []string{"", "", ""},
+ inputPerson: []string{"", "", ""},
+ expectedAlbum: []string{},
+ expectedPerson: []string{},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ c := &Config{
+ Album: tc.inputAlbum,
+ Person: tc.inputPerson,
+ }
+
+ c.checkAlbumAndPerson()
+
+ assert.Equal(t, tc.expectedAlbum, c.Album, "Album mismatch")
+ assert.Equal(t, tc.expectedPerson, c.Person, "Person mismatch")
+ })
+ }
+}
diff --git a/custom.example.css b/custom.example.css
index 8ac4079..34c8f19 100644
--- a/custom.example.css
+++ b/custom.example.css
@@ -28,7 +28,8 @@ body{}
.image--metadata--theme-solid{}
.frame--layout-splitview:nth-child(1) .image--metadata--theme-solid{}
.image--metadata .responsive-break{}
-.image--metadata--location:empty{}
+.image--metadata div:empty{}
+.image--metadata div{}
.image--metadata--date{}
.image--metadata--exif{}
.image--metadata--exif--fnumber{}
diff --git a/frontend/public/assets/css/kiosk.css b/frontend/public/assets/css/kiosk.css
index 5716cc8..86af98b 100644
--- a/frontend/public/assets/css/kiosk.css
+++ b/frontend/public/assets/css/kiosk.css
@@ -297,50 +297,29 @@ body {
display: none;
}
.image--metadata:empty,
-.image--metadata--date:empty,
-.image--metadata--exif:empty,
-.image--metadata--location:empty {
+.image--metadata div:empty {
display: none;
padding: 0;
}
+.image--metadata div {
+ z-index: 1;
+}
.image--metadata--date {
font-size: 1.3rem;
- z-index: 1;
}
.image--metadata--exif {
- z-index: 1;
}
.image--metadata--exif--fnumber {
display: inline-block;
font-size: 0.84rem;
font-weight: bold;
transform: translate(0.0625rem, -0.1875rem);
- z-index: 1;
}
.image--metadata--exif--seperator {
opacity: 0.3;
padding: 0 0.5rem;
- z-index: 1;
}
.image--metadata--location {
- z-index: 1;
-}
-@media screen and (max-width: 31.25rem) {
- .image--metadata {
- padding: 0.5rem;
- max-width: 50vw;
- }
- .image--metadata--date,
- .image--metadata--exif,
- .image--metadata--location {
- padding-left: 0.5rem;
- }
- .image--metadata--location span {
- display: none;
- }
- .image--metadata--location .responsive-break {
- display: inline;
- }
}
.frame--layout-splitview:nth-child(1) .image--metadata {
position: absolute;
@@ -352,7 +331,6 @@ body {
left: 0;
right: unset;
max-width: 70vw;
- padding: 1rem 1.375rem 1rem 1rem;
color: #fff;
font-size: 1.1rem;
text-align: left;
@@ -370,6 +348,23 @@ body {
.frame--image-zoom-out img {
animation-name: image-zoom-out;
}
+@media screen and (max-width: 31.25rem) {
+ .image--metadata {
+ padding: 0.5rem !important;
+ max-width: 50vw;
+ }
+ .image--metadata--date,
+ .image--metadata--exif,
+ .image--metadata--location {
+ padding-left: 0.5rem;
+ }
+ .image--metadata--location span {
+ display: none;
+ }
+ .image--metadata--location .responsive-break {
+ display: inline;
+ }
+}
/* src/css/error.css */
.error-container {
diff --git a/frontend/public/assets/js/kiosk.js b/frontend/public/assets/js/kiosk.js
index 9508b91..9a450b8 100644
--- a/frontend/public/assets/js/kiosk.js
+++ b/frontend/public/assets/js/kiosk.js
@@ -3789,6 +3789,9 @@ var kiosk = (() => {
var fullscreenButton = htmx_esm_default.find(
".navigation--fullscreen"
);
+ var fullScreenButtonSeperator = htmx_esm_default.find(
+ ".navigation--fullscreen-separator"
+ );
var kiosk = htmx_esm_default.find("#kiosk");
var menu = htmx_esm_default.find(".navigation");
var menuInteraction = htmx_esm_default.find(
@@ -3806,6 +3809,7 @@ var kiosk = (() => {
}
if (!fullscreenAPI.requestFullscreen) {
fullscreenButton && htmx_esm_default.remove(fullscreenButton);
+ fullScreenButtonSeperator && htmx_esm_default.remove(fullScreenButtonSeperator);
}
if (pollInterval2) {
initPolling(pollInterval2, kiosk, menu, menuPausePlayButton2);
diff --git a/frontend/src/css/image.css b/frontend/src/css/image.css
index eaf83fb..6d47cd3 100644
--- a/frontend/src/css/image.css
+++ b/frontend/src/css/image.css
@@ -93,55 +93,34 @@
}
.image--metadata:empty,
-.image--metadata--date:empty,
-.image--metadata--exif:empty,
-.image--metadata--location:empty {
+.image--metadata div:empty {
display: none;
padding: 0;
}
+.image--metadata div {
+ z-index: 1;
+}
+
.image--metadata--date {
font-size: 1.3rem;
-
- z-index: 1;
}
+
.image--metadata--exif {
- z-index: 1;
}
.image--metadata--exif--fnumber {
display: inline-block;
font-size: 0.84rem;
font-weight: bold;
transform: translate(0.0625rem, -0.1875rem);
- z-index: 1;
}
+
.image--metadata--exif--seperator {
opacity: 0.3;
padding: 0 0.5rem;
- z-index: 1;
-}
-.image--metadata--location {
- z-index: 1;
}
-@media screen and (max-width: 31.25rem) {
- .image--metadata {
- padding: 0.5rem;
- max-width: 50vw;
- }
-
- .image--metadata--date,
- .image--metadata--exif,
- .image--metadata--location {
- padding-left: 0.5rem;
- }
-
- .image--metadata--location span {
- display: none;
- }
- .image--metadata--location .responsive-break {
- display: inline;
- }
+.image--metadata--location {
}
.frame--layout-splitview:nth-child(1) .image--metadata {
@@ -154,7 +133,6 @@
left: 0;
right: unset;
max-width: 70vw;
- padding: 1rem 1.375rem 1rem 1rem;
color: #fff;
font-size: 1.1rem;
text-align: left;
@@ -169,10 +147,29 @@
transition-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
animation-fill-mode: forwards;
}
-
.frame--image-zoom-in img {
animation-name: image-zoom-in;
}
.frame--image-zoom-out img {
animation-name: image-zoom-out;
}
+
+@media screen and (max-width: 31.25rem) {
+ .image--metadata {
+ padding: 0.5rem !important;
+ max-width: 50vw;
+ }
+
+ .image--metadata--date,
+ .image--metadata--exif,
+ .image--metadata--location {
+ padding-left: 0.5rem;
+ }
+
+ .image--metadata--location span {
+ display: none;
+ }
+ .image--metadata--location .responsive-break {
+ display: inline;
+ }
+}
diff --git a/frontend/src/ts/kiosk.ts b/frontend/src/ts/kiosk.ts
index e24fca3..d6a0c04 100644
--- a/frontend/src/ts/kiosk.ts
+++ b/frontend/src/ts/kiosk.ts
@@ -31,6 +31,9 @@ const documentBody = document.body;
const fullscreenButton = htmx.find(
".navigation--fullscreen",
) as HTMLElement | null;
+const fullScreenButtonSeperator = htmx.find(
+ ".navigation--fullscreen-separator",
+) as HTMLElement | null;
const kiosk = htmx.find("#kiosk") as HTMLElement | null;
const menu = htmx.find(".navigation") as HTMLElement | null;
const menuInteraction = htmx.find(
@@ -54,6 +57,7 @@ function init() {
if (!fullscreenAPI.requestFullscreen) {
fullscreenButton && htmx.remove(fullscreenButton);
+ fullScreenButtonSeperator && htmx.remove(fullScreenButtonSeperator);
}
if (pollInterval) {
diff --git a/go.mod b/go.mod
index 117d7ae..c35cd3a 100644
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@ require (
github.com/spf13/viper v1.20.0-alpha.6
github.com/stretchr/testify v1.9.0
golang.org/x/image v0.20.0
+ gopkg.in/yaml.v3 v3.0.1
)
require (
@@ -49,5 +50,4 @@ require (
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/immich/immich.go b/immich/immich.go
index f24229f..ae6dd4b 100644
--- a/immich/immich.go
+++ b/immich/immich.go
@@ -31,7 +31,7 @@ const (
AlbumKeywordAll string = "all"
AlbumKeywordShared string = "shared"
AlbumKeywordFavourites string = "favourites"
- AlbumKeywordFavorites string = "favorites"
+ AlbumKeywordFavorites string = "favorites"
)
var (
@@ -67,7 +67,7 @@ type ExifInfo struct {
FNumber float64 `json:"fNumber"`
FocalLength float64 `json:"focalLength"`
Iso int `json:"iso"`
- ExposureTime string `json:"-"` // `json:"exposureTime"`
+ ExposureTime string `json:"exposureTime"`
Latitude float64 `json:"-"` // `json:"latitude"`
Longitude float64 `json:"-"` // `json:"longitude"`
City string `json:"city"`
diff --git a/immich/immich_helpers.go b/immich/immich_helpers.go
index 0502256..9f95547 100644
--- a/immich/immich_helpers.go
+++ b/immich/immich_helpers.go
@@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/url"
+ "time"
"github.com/charmbracelet/log"
"github.com/patrickmn/go-cache"
@@ -18,18 +19,7 @@ func immichApiFail[T ImmichApiResponse](value T, err error, body []byte, apiUrl
errorUnmarshalErr := json.Unmarshal(body, &immichError)
if errorUnmarshalErr != nil {
log.Error("Couldn't read error", "body", string(body), "url", apiUrl)
- return value, fmt.Errorf(`
- No data or error returned from Immich API.
-
- - Are your data source ID's correct (albumID, personID)?
- - Do those data sources have assets?
- - Is Immich online?
-
-
- Full error:
- %w
-
- `, err)
+ return value, err
}
log.Errorf("%s : %v", immichError.Error, immichError.Message)
return value, fmt.Errorf("%s : %v", immichError.Error, immichError.Message)
@@ -92,7 +82,9 @@ func (i *ImmichAsset) immichApiCall(method, apiUrl string, body io.Reader) ([]by
var responseBody []byte
- client := &http.Client{}
+ client := &http.Client{
+ Timeout: time.Second * 10,
+ }
req, err := http.NewRequest(method, apiUrl, body)
if err != nil {
log.Error(err)
@@ -102,20 +94,35 @@ func (i *ImmichAsset) immichApiCall(method, apiUrl string, body io.Reader) ([]by
req.Header.Add("Accept", "application/json")
req.Header.Add("x-api-key", requestConfig.ImmichApiKey)
- if method == "POST" {
+ if method == "POST" || method == "PUT" || method == "PATCH" {
req.Header.Add("Content-Type", "application/json")
}
- res, err := client.Do(req)
+ var res *http.Response
+ for attempts := 0; attempts < 3; attempts++ {
+ res, err = client.Do(req)
+ if err == nil {
+ break
+ }
+ log.Error("Request failed, retrying", "attempt", attempts, "URL", apiUrl, "err", err)
+ time.Sleep(time.Duration(attempts) * time.Second)
+ }
if err != nil {
- log.Error(err)
+ log.Error("Request failed after retries", "err", err)
return responseBody, err
}
+
defer res.Body.Close()
+ if res.StatusCode < 200 || res.StatusCode >= 300 {
+ err = fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ log.Error(err)
+ return responseBody, err
+ }
+
responseBody, err = io.ReadAll(res.Body)
if err != nil {
- log.Error(err)
+ log.Error("reading response body", "url", apiUrl, "err", err)
return responseBody, err
}
diff --git a/main.go b/main.go
index a53dc8d..23395a5 100644
--- a/main.go
+++ b/main.go
@@ -39,6 +39,8 @@ func init() {
func main() {
+ log.SetTimeFormat("15:04:05")
+
baseConfig := config.New()
err := baseConfig.Load()
@@ -46,8 +48,9 @@ func main() {
log.Error("Failed to load config", "err", err)
}
+ baseConfig.WatchConfig()
+
if baseConfig.Kiosk.Debug {
- log.SetTimeFormat("15:04:05")
log.SetLevel(log.DebugLevel)
if baseConfig.Kiosk.DebugVerbose {
@@ -107,7 +110,9 @@ func main() {
e.GET("/cache/flush", routes.FlushCache)
- err = e.Start(":3000")
+ e.POST("/refresh/check", routes.RefreshCheck(baseConfig))
+
+ err = e.Start(fmt.Sprintf(":%v", baseConfig.Kiosk.Port))
if err != nil {
log.Fatal(err)
}
diff --git a/routes/routes.go b/routes/routes.go
index c5d79c5..4debc90 100644
--- a/routes/routes.go
+++ b/routes/routes.go
@@ -22,7 +22,7 @@ import (
var (
KioskVersion string
- viewDataCache *cache.Cache
+ ViewDataCache *cache.Cache
viewDataCacheMutex sync.Mutex
)
@@ -38,7 +38,7 @@ type RequestData struct {
func init() {
// Setting up Immich api cache
- viewDataCache = cache.New(5*time.Minute, 10*time.Minute)
+ ViewDataCache = cache.New(5*time.Minute, 10*time.Minute)
}
func RenderError(c echo.Context, err error, message string) error {
diff --git a/routes/routes_cache.go b/routes/routes_cache.go
index 2fa0348..a1246f7 100644
--- a/routes/routes_cache.go
+++ b/routes/routes_cache.go
@@ -13,12 +13,12 @@ func FlushCache(c echo.Context) error {
viewDataCacheMutex.Lock()
defer viewDataCacheMutex.Unlock()
- log.Info("Cache before flush", "viewDataCache_items", viewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount())
+ log.Info("Cache before flush", "viewDataCache_items", ViewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount())
- viewDataCache.Flush()
+ ViewDataCache.Flush()
immich.FluchApiCache()
- log.Info("Cache after flush ", "viewDataCache_items", viewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount())
+ log.Info("Cache after flush ", "viewDataCache_items", ViewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount())
c.Response().Header().Set("HX-Refresh", "true")
return c.NoContent(http.StatusOK)
diff --git a/routes/routes_clock.go b/routes/routes_clock.go
index cd320d9..be24ccd 100644
--- a/routes/routes_clock.go
+++ b/routes/routes_clock.go
@@ -2,7 +2,6 @@ package routes
import (
"net/http"
- "time"
"github.com/charmbracelet/log"
"github.com/labstack/echo/v4"
@@ -16,18 +15,11 @@ import (
func Clock(baseConfig *config.Config) echo.HandlerFunc {
return func(c echo.Context) error {
- kioskVersionHeader := c.Request().Header.Get("kiosk-version")
requestID := utils.ColorizeRequestId(c.Response().Header().Get(echo.HeaderXRequestID))
// create a copy of the global config to use with this request
requestConfig := *baseConfig
- // If kiosk version on client and server do not match refresh client.
- if kioskVersionHeader != "" && KioskVersion != kioskVersionHeader {
- c.Response().Header().Set("HX-Refresh", "true")
- return c.NoContent(http.StatusOK)
- }
-
err := requestConfig.ConfigWithOverrides(c)
if err != nil {
log.Error("overriding config", "err", err)
@@ -43,30 +35,6 @@ func Clock(baseConfig *config.Config) echo.HandlerFunc {
"DateFormat", requestConfig.DateFormat,
)
- clockTimeFormat := "15:04"
- if requestConfig.TimeFormat == "12" {
- clockTimeFormat = time.Kitchen
- }
-
- clockDateFormat := utils.DateToLayout(requestConfig.DateFormat)
- if clockDateFormat == "" {
- clockDateFormat = config.DefaultDateLayout
- }
-
- var data views.ClockData
-
- t := time.Now()
-
- switch {
- case (requestConfig.ShowTime && requestConfig.ShowDate):
- data.ClockTime = t.Format(clockTimeFormat)
- data.ClockDate = t.Format(clockDateFormat)
- case requestConfig.ShowTime:
- data.ClockTime = t.Format(clockTimeFormat)
- case requestConfig.ShowDate:
- data.ClockDate = t.Format(clockDateFormat)
- }
-
- return Render(c, http.StatusOK, views.Clock(data))
+ return Render(c, http.StatusOK, views.Clock(requestConfig))
}
}
diff --git a/routes/routes_image_helpers.go b/routes/routes_image_helpers.go
index d5d6dab..b918cff 100644
--- a/routes/routes_image_helpers.go
+++ b/routes/routes_image_helpers.go
@@ -252,13 +252,13 @@ func imagePreFetch(requestConfig config.Config, c echo.Context, kioskDeviceID st
cacheKey := c.Request().URL.String() + kioskDeviceID
- if data, found := viewDataCache.Get(cacheKey); found {
+ if data, found := ViewDataCache.Get(cacheKey); found {
cachedViewData = data.([]views.ViewData)
}
cachedViewData = append(cachedViewData, viewDataToAdd)
- viewDataCache.Set(cacheKey, cachedViewData, cache.DefaultExpiration)
+ ViewDataCache.Set(cacheKey, cachedViewData, cache.DefaultExpiration)
}
@@ -309,12 +309,12 @@ func fromCache(c echo.Context, kioskDeviceID string) []views.ViewData {
defer viewDataCacheMutex.Unlock()
cacheKey := c.Request().URL.String() + kioskDeviceID
- if data, found := viewDataCache.Get(cacheKey); found {
+ if data, found := ViewDataCache.Get(cacheKey); found {
cachedPageData := data.([]views.ViewData)
if len(cachedPageData) > 0 {
return cachedPageData
}
- viewDataCache.Delete(cacheKey)
+ ViewDataCache.Delete(cacheKey)
}
return nil
}
@@ -329,7 +329,7 @@ func renderCachedViewData(c echo.Context, cachedViewData []views.ViewData, reque
cacheKey := c.Request().URL.String() + kioskDeviceID
viewDataToRender := cachedViewData[0]
- viewDataCache.Set(cacheKey, cachedViewData[1:], cache.DefaultExpiration)
+ ViewDataCache.Set(cacheKey, cachedViewData[1:], cache.DefaultExpiration)
// Update history which will be outdated in cache
trimHistory(&requestConfig.History, 10)
diff --git a/routes/routes_refresh.go b/routes/routes_refresh.go
new file mode 100644
index 0000000..2be178c
--- /dev/null
+++ b/routes/routes_refresh.go
@@ -0,0 +1,38 @@
+package routes
+
+import (
+ "net/http"
+
+ "github.com/charmbracelet/log"
+ "github.com/labstack/echo/v4"
+
+ "github.com/damongolding/immich-kiosk/config"
+ "github.com/damongolding/immich-kiosk/utils"
+)
+
+// RefreshCheck endpoint to check if device requires a refresh
+func RefreshCheck(baseConfig *config.Config) echo.HandlerFunc {
+ return func(c echo.Context) error {
+
+ kioskVersionHeader := c.Request().Header.Get("kiosk-version")
+ kioskRefreshTimestampHeader := c.Request().Header.Get("kiosk-reload-timestamp")
+ requestID := utils.ColorizeRequestId(c.Response().Header().Get(echo.HeaderXRequestID))
+
+ // create a copy of the global config to use with this request
+ requestConfig := *baseConfig
+
+ // If kiosk version on client and server do not match refresh client.
+ if KioskVersion != kioskVersionHeader || kioskRefreshTimestampHeader != requestConfig.ReloadTimeStamp {
+ c.Response().Header().Set("HX-Refresh", "true")
+ return c.NoContent(http.StatusOK)
+ }
+
+ log.Debug(
+ requestID,
+ "method", c.Request().Method,
+ "path", c.Request().URL.String(),
+ )
+
+ return c.NoContent(http.StatusOK)
+ }
+}
diff --git a/taskfile.yml b/taskfile.yml
index 5221a7e..a96143b 100644
--- a/taskfile.yml
+++ b/taskfile.yml
@@ -1,12 +1,17 @@
version: "3"
env:
- VERSION: 0.11.1
+ VERSION: 0.11.2
tasks:
default:
deps: [build]
cmds:
- KIOSK_DEBUG=true ./dist/kiosk
+ verbose:
+ deps: [build]
+ cmds:
+ - KIOSK_DEBUG_VERBOSE=true ./dist/kiosk
+
frontend:
deps: [frontend-test, frontend-css, frontend-js]
dir: ./frontend
@@ -53,7 +58,7 @@ tasks:
docker-image:
cmds:
- - docker build --build-arg VERSION={{.VERSION}} --load -t damongolding/immich-kiosk:{{.VERSION}} -t damongolding/immich-kiosk:latest .
+ - docker build --no-cache --build-arg VERSION={{.VERSION}} --load -t damongolding/immich-kiosk:{{.VERSION}} -t damongolding/immich-kiosk:latest .
docker-buildx:
cmds:
diff --git a/views/views_clock.templ b/views/views_clock.templ
index 3873c94..ce77acd 100644
--- a/views/views_clock.templ
+++ b/views/views_clock.templ
@@ -1,17 +1,42 @@
package views
-import "strings"
+import (
+ "github.com/damongolding/immich-kiosk/config"
+ "github.com/damongolding/immich-kiosk/utils"
+ "strings"
+ "time"
+)
+
+func clockDate(c config.Config) string {
+ clockDateFormat := utils.DateToLayout(c.DateFormat)
+
+ if clockDateFormat == "" {
+ clockDateFormat = config.DefaultDateLayout
+ }
+
+ t := time.Now()
+
+ return t.Format(clockDateFormat)
+}
+
+func clockTime(c config.Config) string {
+ clockTimeFormat := "15:04"
+
+ if c.TimeFormat == "12" {
+ clockTimeFormat = time.Kitchen
+ }
+
+ t := time.Now()
+
+ return strings.ToLower(t.Format(clockTimeFormat))
-type ClockData struct {
- ClockTime string
- ClockDate string
}
-templ Clock(data ClockData) {
- if data.ClockDate != "" {
- { data.ClockDate }
+templ Clock(requestConfig config.Config) {
+ if requestConfig.ShowDate {
+ { clockDate(requestConfig) }
}
- if data.ClockTime != "" {
- { strings.ToLower(data.ClockTime) }
+ if requestConfig.ShowTime {
+ { clockTime(requestConfig) }
}
}
diff --git a/views/views_clock_templ.go b/views/views_clock_templ.go
index e13f673..9622de9 100644
--- a/views/views_clock_templ.go
+++ b/views/views_clock_templ.go
@@ -8,14 +8,39 @@ package views
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
-import "strings"
+import (
+ "github.com/damongolding/immich-kiosk/config"
+ "github.com/damongolding/immich-kiosk/utils"
+ "strings"
+ "time"
+)
+
+func clockDate(c config.Config) string {
+ clockDateFormat := utils.DateToLayout(c.DateFormat)
+
+ if clockDateFormat == "" {
+ clockDateFormat = config.DefaultDateLayout
+ }
+
+ t := time.Now()
+
+ return t.Format(clockDateFormat)
+}
+
+func clockTime(c config.Config) string {
+ clockTimeFormat := "15:04"
+
+ if c.TimeFormat == "12" {
+ clockTimeFormat = time.Kitchen
+ }
+
+ t := time.Now()
+
+ return strings.ToLower(t.Format(clockTimeFormat))
-type ClockData struct {
- ClockTime string
- ClockDate string
}
-func Clock(data ClockData) templ.Component {
+func Clock(requestConfig config.Config) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -36,15 +61,15 @@ func Clock(data ClockData) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- if data.ClockDate != "" {
+ if requestConfig.ShowDate {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
- templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.ClockDate)
+ templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(clockDate(requestConfig))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_clock.templ`, Line: 12, Col: 43}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_clock.templ`, Line: 37, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
@@ -55,15 +80,15 @@ func Clock(data ClockData) templ.Component {
return templ_7745c5c3_Err
}
}
- if data.ClockTime != "" {
+ if requestConfig.ShowTime {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strings.ToLower(data.ClockTime))
+ templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(clockTime(requestConfig))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_clock.templ`, Line: 15, Col: 60}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_clock.templ`, Line: 40, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
diff --git a/views/views_error.templ b/views/views_error.templ
index 484c08d..c1a79e8 100644
--- a/views/views_error.templ
+++ b/views/views_error.templ
@@ -18,7 +18,19 @@ templ Error(data ErrorData) {
{ data.Title }
}
- @templ.Raw(data.Message)
+
+ - Are your data source ID's correct (albumID, personID)?
+ - Do those data sources have assets?
+ - Is Immich online?
+
+
+ Full error:
+
+
+
+ @templ.Raw(data.Message)
+
+
diff --git a/views/views_error_templ.go b/views/views_error_templ.go
index 9f465b3..50a0d59 100644
--- a/views/views_error_templ.go
+++ b/views/views_error_templ.go
@@ -57,7 +57,7 @@ func Error(data ErrorData) templ.Component {
return templ_7745c5c3_Err
}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
- Are your data source ID's correct (albumID, personID)?
- Do those data sources have assets?
- Is Immich online?
Full error:
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -65,7 +65,7 @@ func Error(data ErrorData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/views/views_home.templ b/views/views_home.templ
index 34ce1c1..2e18017 100644
--- a/views/views_home.templ
+++ b/views/views_home.templ
@@ -201,7 +201,7 @@ templ paramForm(queries url.Values) {
}
-templ clock(queries url.Values, kioskVersion string, deviceID string, theme string) {
+templ clock(queries url.Values, theme string) {
}
@@ -236,12 +235,20 @@ templ sleepMode(sleepStart, sleepEnd string, queries url.Values) {
}
}
+templ refreshCheckForm(kioskVersion, reloadTimeStamp string) {
+
+}
+
templ Home(viewData ViewData) {
-
+
Immich Kiosk
@@ -288,13 +295,14 @@ templ Home(viewData ViewData) {
if viewData.ShowProgress {
@progressBar()
}
- if !viewData.DisableUi && viewData.ShowTime {
- @clock(viewData.Queries, viewData.KioskVersion, viewData.DeviceID, viewData.Theme)
+ if !viewData.DisableUi && (viewData.ShowTime || viewData.ShowDate) {
+ @clock(viewData.Queries, viewData.Theme)
}
@menu()
@paramForm(viewData.Queries)
@sleepMode(viewData.SleepStart, viewData.SleepEnd, viewData.Queries)
@historyForm()
+ @refreshCheckForm(viewData.KioskVersion, viewData.ReloadTimeStamp)
@offlineIcon()
@kioskData(map[string]any{
"debug": viewData.Kiosk.Debug,
diff --git a/views/views_home_templ.go b/views/views_home_templ.go
index dc09bdd..cbc8bac 100644
--- a/views/views_home_templ.go
+++ b/views/views_home_templ.go
@@ -523,7 +523,7 @@ func paramForm(queries url.Values) templ.Component {
})
}
-func clock(queries url.Values, kioskVersion string, deviceID string, theme string) templ.Component {
+func clock(queries url.Values, theme string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -572,20 +572,7 @@ func clock(queries url.Values, kioskVersion string, deviceID string, theme strin
return templ_7745c5c3_Err
}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" hx-trigger=\"load, every 13s\" hx-swap=\"innerHTML\" hx-headers=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var19 string
- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`{"kiosk-version": "%s", "kiosk-device-id": "%s"}`, kioskVersion, deviceID))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 214, Col: 102}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" hx-trigger=\"load, every 13s\" hx-swap=\"innerHTML\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -609,9 +596,9 @@ func progressBar() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var20 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var20 == nil {
- templ_7745c5c3_Var20 = templ.NopComponent
+ templ_7745c5c3_Var19 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var19 == nil {
+ templ_7745c5c3_Var19 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
@@ -638,9 +625,9 @@ func sleepMode(sleepStart, sleepEnd string, queries url.Values) templ.Component
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var21 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var21 == nil {
- templ_7745c5c3_Var21 = templ.NopComponent
+ templ_7745c5c3_Var20 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var20 == nil {
+ templ_7745c5c3_Var20 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if sleepStart != "" && sleepEnd != "" {
@@ -663,6 +650,48 @@ func sleepMode(sleepStart, sleepEnd string, queries url.Values) templ.Component
})
}
+func refreshCheckForm(kioskVersion, reloadTimeStamp string) templ.Component {
+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
+ return templ_7745c5c3_CtxErr
+ }
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
+ }
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var21 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var21 == nil {
+ templ_7745c5c3_Var21 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
func Home(viewData ViewData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
@@ -679,17 +708,17 @@ func Home(viewData ViewData) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var22 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var22 == nil {
- templ_7745c5c3_Var22 = templ.NopComponent
+ templ_7745c5c3_Var23 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var23 == nil {
+ templ_7745c5c3_Var23 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var23 = []any{baseFontSize(viewData.FontSize)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var23...)
+ var templ_7745c5c3_Var24 = []any{baseFontSize(viewData.FontSize)}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var24...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -697,25 +726,25 @@ func Home(viewData ViewData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var24 string
- templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var23).String())
+ var templ_7745c5c3_Var25 string
+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var24).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 1, Col: 0}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> 0 {
stats.WriteString("|")
}
- stats.WriteString(fmt.Sprintf("%s s", info.ExposureTime))
+ stats.WriteString(fmt.Sprintf("%ss", info.ExposureTime))
}
if info.FocalLength != 0 {
@@ -110,9 +110,34 @@ func ImageDateTime(viewData ViewData, imageIndex int) string {
return imageDate
}
+templ imageMetadata(viewData ViewData, imageIndex int) {
+
+ if viewData.ShowImageDate || viewData.ShowImageTime {
+
+ { ImageDateTime(viewData, imageIndex) }
+
+ }
+ if viewData.ShowImageExif {
+
+ @templ.Raw(ImageExif(viewData.Images[imageIndex].ImmichImage.ExifInfo))
+
+ }
+ if viewData.ShowImageLocation {
+
+ @templ.Raw(ImageLocation(viewData.Images[imageIndex].ImmichImage.ExifInfo))
+
+ }
+ if viewData.ShowImageID {
+
+ { viewData.Images[imageIndex].ImmichImage.ID }
+
+ }
+
+}
+
templ layoutSingleView(viewData ViewData) {
- if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") {
+ if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") && len(viewData.Images[0].ImageBlurData) > 0 {
@@ -132,23 +157,9 @@ templ layoutSingleView(viewData ViewData) {
}
if !viewData.DisableUi {
-
- if viewData.ShowImageDate || viewData.ShowImageTime {
-
- { ImageDateTime(viewData, 0) }
-
- }
- if viewData.ShowImageExif {
-
- @templ.Raw(ImageExif(viewData.Images[0].ImmichImage.ExifInfo))
-
- }
- if viewData.ShowImageLocation {
-
- @templ.Raw(ImageLocation(viewData.Images[0].ImmichImage.ExifInfo))
-
- }
-
+ if !viewData.DisableUi {
+ @imageMetadata(viewData, 0)
+ }
}
}
@@ -157,7 +168,7 @@ templ layoutSplitView(viewData ViewData) {
for imageIndex, imageData := range viewData.Images {
- if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") {
+ if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") && len(imageData.ImageBlurData) > 0 {
@@ -177,23 +188,7 @@ templ layoutSplitView(viewData ViewData) {
}
if !viewData.DisableUi {
-
- if viewData.ShowImageDate || viewData.ShowImageTime {
-
- { ImageDateTime(viewData, imageIndex) }
-
- }
- if viewData.ShowImageExif {
-
- @templ.Raw(ImageExif(imageData.ImmichImage.ExifInfo))
-
- }
- if viewData.ShowImageLocation {
-
- @templ.Raw(ImageLocation(imageData.ImmichImage.ExifInfo))
-
- }
-
+ @imageMetadata(viewData, imageIndex)
}
}
diff --git a/views/views_image_templ.go b/views/views_image_templ.go
index ae8057b..dd2cfd2 100644
--- a/views/views_image_templ.go
+++ b/views/views_image_templ.go
@@ -174,7 +174,7 @@ func ImageExif(info immich.ExifInfo) string {
if stats.Len() > 0 {
stats.WriteString("|")
}
- stats.WriteString(fmt.Sprintf("%s s", info.ExposureTime))
+ stats.WriteString(fmt.Sprintf("%ss", info.ExposureTime))
}
if info.FocalLength != 0 {
@@ -221,7 +221,7 @@ func ImageDateTime(viewData ViewData, imageIndex int) string {
return imageDate
}
-func layoutSingleView(viewData ViewData) templ.Component {
+func imageMetadata(viewData ViewData, imageIndex int) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -242,7 +242,7 @@ func layoutSingleView(viewData ViewData) templ.Component {
templ_7745c5c3_Var7 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- var templ_7745c5c3_Var8 = []any{"frame", templ.KV("frame-black-bg", !viewData.BackgroundBlur)}
+ var templ_7745c5c3_Var8 = []any{"image--metadata", fmt.Sprintf("image--metadata--theme-%s", viewData.Theme)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -264,27 +264,144 @@ func layoutSingleView(viewData ViewData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
- templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(viewData.Images[0].ImageBlurData)
+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(ImageDateTime(viewData, imageIndex))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 117, Col: 47}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 117, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ if viewData.ShowImageExif {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templ.Raw(ImageExif(viewData.Images[imageIndex].ImmichImage.ExifInfo)).Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ if viewData.ShowImageLocation {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templ.Raw(ImageLocation(viewData.Images[imageIndex].ImmichImage.ExifInfo)).Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ if viewData.ShowImageID {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var11 string
+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(viewData.Images[imageIndex].ImmichImage.ID)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 132, Col: 48}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func layoutSingleView(viewData ViewData) templ.Component {
+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
+ return templ_7745c5c3_CtxErr
+ }
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
+ }
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var12 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var12 == nil {
+ templ_7745c5c3_Var12 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ var templ_7745c5c3_Var13 = []any{"frame", templ.KV("frame-black-bg", !viewData.BackgroundBlur)}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var13...)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") && len(viewData.Images[0].ImageBlurData) > 0 {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- var templ_7745c5c3_Var11 = []any{"frame--image", templ.KV("frame--image-zoom", viewData.ImageZoom), animationDuration(viewData.Refresh), zoomInOrOut()}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var11...)
+ var templ_7745c5c3_Var16 = []any{"frame--image", templ.KV("frame--image-zoom", viewData.ImageZoom), animationDuration(viewData.Refresh), zoomInOrOut()}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var16...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -292,12 +409,12 @@ func layoutSingleView(viewData ViewData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var12 string
- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var11).String())
+ var templ_7745c5c3_Var17 string
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var16).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 1, Col: 0}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -334,79 +451,12 @@ func layoutSingleView(viewData ViewData) templ.Component {
return templ_7745c5c3_Err
}
if !viewData.DisableUi {
- var templ_7745c5c3_Var13 = []any{"image--metadata", fmt.Sprintf("image--metadata--theme-%s", viewData.Theme)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var13...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if viewData.ShowImageDate || viewData.ShowImageTime {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var15 string
- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(ImageDateTime(viewData, 0))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 138, Col: 34}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if viewData.ShowImageExif {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(ImageExif(viewData.Images[0].ImmichImage.ExifInfo)).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if viewData.ShowImageLocation {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(ImageLocation(viewData.Images[0].ImmichImage.ExifInfo)).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ if !viewData.DisableUi {
+ templ_7745c5c3_Err = imageMetadata(viewData, 0).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
if templ_7745c5c3_Err != nil {
@@ -432,13 +482,13 @@ func layoutSplitView(viewData ViewData) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var16 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var16 == nil {
- templ_7745c5c3_Var16 = templ.NopComponent
+ templ_7745c5c3_Var18 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var18 == nil {
+ templ_7745c5c3_Var18 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- var templ_7745c5c3_Var17 = []any{"frame", templ.KV("frame-black-bg", !viewData.BackgroundBlur)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...)
+ var templ_7745c5c3_Var19 = []any{"frame", templ.KV("frame-black-bg", !viewData.BackgroundBlur)}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var19...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -446,12 +496,12 @@ func layoutSplitView(viewData ViewData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var18 string
- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var17).String())
+ var templ_7745c5c3_Var20 string
+ templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var19).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 1, Col: 0}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -464,17 +514,17 @@ func layoutSplitView(viewData ViewData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") {
+ if viewData.BackgroundBlur && !strings.EqualFold(viewData.ImageFit, "cover") && len(imageData.ImageBlurData) > 0 {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if viewData.ShowImageDate || viewData.ShowImageTime {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var24 string
- templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(ImageDateTime(viewData, imageIndex))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 183, Col: 45}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if viewData.ShowImageExif {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(ImageExif(imageData.ImmichImage.ExifInfo)).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if viewData.ShowImageLocation {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(ImageLocation(imageData.ImmichImage.ExifInfo)).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
+ templ_7745c5c3_Err = imageMetadata(viewData, imageIndex).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -637,9 +618,9 @@ func Image(viewData ViewData) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var25 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var25 == nil {
- templ_7745c5c3_Var25 = templ.NopComponent
+ templ_7745c5c3_Var24 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var24 == nil {
+ templ_7745c5c3_Var24 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if len(viewData.Images) < 2 {
@@ -662,12 +643,12 @@ func Image(viewData ViewData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var26 string
- templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(historyEntry)
+ var templ_7745c5c3_Var25 string
+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(historyEntry)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 211, Col: 88}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 206, Col: 88}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -681,12 +662,12 @@ func Image(viewData ViewData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var27 string
- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(newHistoryEntry.ImmichImage.ID)
+ var templ_7745c5c3_Var26 string
+ templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(newHistoryEntry.ImmichImage.ID)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 214, Col: 106}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_image.templ`, Line: 209, Col: 106}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/views/views_menu.templ b/views/views_menu.templ
index fdb4fa2..bbab4b2 100644
--- a/views/views_menu.templ
+++ b/views/views_menu.templ
@@ -21,7 +21,7 @@ templ menu() {
-
+