Skip to content

Commit

Permalink
Merge pull request #94 from damongolding/image-weighting
Browse files Browse the repository at this point in the history
Image weighting
  • Loading branch information
damongolding authored Sep 18, 2024
2 parents 904fb15 + 1e9b36d commit ec0572d
Show file tree
Hide file tree
Showing 20 changed files with 585 additions and 157 deletions.
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
## Table of Contents
- [What is Immich Kiosk?](#what-is-immich-kiosk)
- [Example 1: Raspberry Pi](#example-2)
- [Key features](#key-features)
- [Example 1: Raspberry Pi](#example-1)
- [Installation](#installation)
- [Docker Compose](#docker-compose)
- [Configuration](#configuration)
Expand All @@ -65,14 +66,22 @@
## What is Immich Kiosk?
Immich Kiosk is a lightweight slideshow for running on kiosk devices and browsers that uses [Immich][immich-github-url] as a data source.

## Key features
- Simple installation and updates via Docker.
- Lightweight, responsive frontend for smooth performance.
- Display random images from your Immich collection, or curate specific albums and people.
- Fully customizable appearance with flexible transitions.
- Add a live clock with adjustable formats.
- Define default settings for all devices through environment variables or YAML config files.
- Configure device-specific settings using URL parameters.

![preview 1](/assets/demo_1.jpg)
**Image shot by Damon Golding**

![preview 2](/assets/demo_2.jpg)
**[Image shot by @insungpandora](https://unsplash.com/@insungpandora)**


### Example 1
## Example 1
You have a two spare Raspberry Pi's laying around. One hooked up to a LCD screen and the other you connect to your TV. You install a fullscreen browser OS or service on them (I use [DietPi][dietpi-url]).

You want the pi connected to the LCD screen to only show images from your recent holiday, which are stored in a album on Immich. It's an older pi so you want to disable CSS transitions, also we don't want to display the time of the image.
Expand Down Expand Up @@ -112,6 +121,11 @@ services:
```
### When using environment variables
> [!NOTE]
> You do not need to specifiy all of these.
> If you want the default behaviour/value you can omit it from you compose file.
```yaml
services:
immich-kiosk:
Expand Down Expand Up @@ -157,6 +171,7 @@ services:
KIOSK_PASSWORD: ""
KIOSK_CACHE: TRUE
KIOSK_PRE_FETCH: TRUE
KIOSK_ASSET_WEIGHTING: TRUE
ports:
- 3000:3000
restart: on-failure
Expand Down Expand Up @@ -218,6 +233,7 @@ kiosk:
| 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. |
| pre_fetch | KIOSK_PRE_FETCH | bool | true | Pre fetch assets in the background so images load much quicker when refresh timer ends. |
| asset_weighting | KIOSK_ASSET_WEIGHTING | bool | true | Balances asset selection when multiple sources are used e.g. multiple people and albums. When enabled, sources with fewer assets will show less often. |


------
Expand Down Expand Up @@ -415,6 +431,21 @@ While I did not create Kiosk with [Home Assistant](https://www.home-assistant.io

## FAQ

**Q: What is the difference between ImmichFrame and ImmichKiosk?**\
**A**:The main differences between ImmichFrame and ImmichKiosk are in how they are set up and how they interact with Immich:

- **ImmichFrame**: For individual devices
- Installed on each device you want to use.
- The device connects directly to Immich.
- Data is processed on the device itself.

- **ImmichKiosk**: For multiple devices
- Installed once on a central server.
- Devices connect to it via a web browser, and it connects to Immich.
- Data is processed by the Kiosk server.

In short, ImmichFrame is a 'one device, one installation, direct connection' setup, while ImmichKiosk is 'one installation, multiple devices, indirect connection.'"

![no-wifi icon](/assets/offline.svg)\
**Q: What is the no wifi icon?**\
**A**: This icon shows when the front end can't connect to the back end .
Expand Down Expand Up @@ -457,7 +488,6 @@ Then to access Kiosk you MUST add the password param in your URL e.g. http://{UR
------
## TODO / Roadmap
- Exclude video thumbnails from being displayed
- Clock/timestamp shadow redesign
- Whitelist for people and albums
- Exclude list
Expand Down
1 change: 1 addition & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ kiosk:
password: ""
cache: true # cache select api calls
pre_fetch: true # fetch assets in the background
asset_weighting: true # use weighting when picking assets
34 changes: 25 additions & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ type KioskSettings struct {
// Cache enable/disable api call caching
Cache bool `mapstructure:"cache" default:"true"`

// PreFetch get and cache an image in the background
// PreFetch fetch and cache an image in the background
PreFetch bool `mapstructure:"pre_fetch" default:"true"`

// Password the password used to add authentication to the frontend
Password string `mapstructure:"password" default:""`

// AssetWeighting use weighting when picking assets
AssetWeighting bool `mapstructure:"asset_weighting" default:"true"`

// debug modes
Debug bool `mapstructure:"debug" default:"false"`
DebugVerbose bool `mapstructure:"debug_verbose" default:"false"`
Expand Down Expand Up @@ -137,26 +140,38 @@ func (c *Config) checkRequiredFields() {
}
}

func (c *Config) checkDebuging() {
if c.Kiosk.DebugVerbose {
c.Kiosk.Debug = true
}
}

// Load loads yaml config file into memory, then loads ENV vars. ENV vars overwrites yaml settings.
// If configFile is provided, it will use that file instead of the default "config.yaml".
func (c *Config) Load(configFile ...string) error {
func (c *Config) Load() error {
return c.load("config.yaml")
}

// Load loads yaml config file into memory with a custom path, then loads ENV vars. ENV vars overwrites yaml settings.
func (c *Config) LoadWithConfigLocation(configPath string) error {
return c.load(configPath)
}

// 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())

v.BindEnv("kiosk.password", "KIOSK_PASSWORD")
v.BindEnv("kiosk.cache", "KIOSK_CACHE")
v.BindEnv("kiosk.pre_fetch", "KIOSK_PRE_FETCH")
v.BindEnv("kiosk.asset_weighting", "KIOSK_ASSET_WEIGHTING")

v.BindEnv("kiosk.debug", "KIOSK_DEBUG")
v.BindEnv("kiosk.debug_verbose", "KIOSK_DEBUG_VERBOSE")

v.AddConfigPath(".")
// Use the provided config file if one is given, otherwise use the default
if len(configFile) > 0 && configFile[0] != "" {
v.SetConfigFile(configFile[0])
} else {
v.SetConfigFile("config.yaml")
}

v.SetConfigFile(configFile)

v.SetEnvPrefix("kiosk")

Expand All @@ -175,6 +190,7 @@ func (c *Config) Load(configFile ...string) error {

c.checkRequiredFields()
c.checkUrlScheme()
c.checkDebuging()

return nil

Expand Down
34 changes: 18 additions & 16 deletions frontend/public/assets/css/kiosk.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ body {
Arial,
Helvetica,
sans-serif;
letter-spacing: 1px;
letter-spacing: 0.0625rem;
background-color: #000;
}
@media screen and (max-width: 500px) {
@media screen and (max-width: 31.25rem) {
html,
body {
font-size: 80%;
Expand Down Expand Up @@ -204,7 +204,7 @@ body {
color: #fff;
font-size: 1.1rem;
text-align: right;
text-shadow: 0 0 20px black;
text-shadow: 0 0 1.25rem black;
background-color: rgba(0, 0, 0, 0.3);
background: rgb(0, 0, 0);
background: -moz-linear-gradient(270deg, rgba(0, 0, 0, 0.500437675070028) 0%, rgba(0, 0, 0, 0) 100%);
Expand Down Expand Up @@ -237,7 +237,7 @@ body {
display: inline-block;
font-size: 0.84rem;
font-weight: bold;
transform: translate(1px, -3px);
transform: translate(0.0625rem, -0.1875rem);
}
.image--metadata--exif--seperator {
opacity: 0.3;
Expand All @@ -246,7 +246,7 @@ body {
.image--metadata--location {
padding-left: 3rem;
}
@media screen and (max-width: 500px) {
@media screen and (max-width: 31.25rem) {
.image--metadata {
padding: 0.5rem;
max-width: 50vw;
Expand Down Expand Up @@ -276,12 +276,12 @@ body {
}
.error {
background-color: rgba(204, 0, 0, 0.4);
border: 2px solid rgb(204, 0, 0);
max-width: 560px;
border: 0.125rem solid rgb(204, 0, 0);
max-width: 35rem;
width: 100%;
color: #fff;
padding: 2rem;
border-radius: 12px;
border-radius: 0.75rem;
}
.error--title {
font-size: 2.5rem;
Expand All @@ -302,14 +302,15 @@ body {
position: absolute;
top: 0;
width: 100%;
height: 3px;
height: 0.18rem;
}
.progress--bar {
position: absolute;
width: 0;
height: 100%;
top: 0;
left: 0;
border-radius: 0 1rem 1rem 0;
background-color: #1e83f7;
transition: width 0.03s linear;
will-change: width;
Expand All @@ -325,7 +326,7 @@ body {
display: none;
}
#offline svg {
filter: drop-shadow(0px 0px 1rem black);
filter: drop-shadow(0rem 0rem 1rem black);
}
.offline {
display: block !important;
Expand All @@ -339,7 +340,7 @@ body {
color: #fff;
font-size: 3rem;
padding: 1rem;
text-shadow: 0 0 20px black;
text-shadow: 0 0 1.25rem black;
background-color: rgba(0, 0, 0, 0.3);
background: rgb(0, 0, 0);
background: -moz-linear-gradient(90deg, rgba(0, 0, 0, 0.500437675070028) 0%, rgba(0, 0, 0, 0) 100%);
Expand All @@ -356,7 +357,7 @@ body {
display: none;
padding: 0;
}
@media screen and (max-width: 500px) {
@media screen and (max-width: 31.25rem) {
#clock {
padding: 0.5rem;
}
Expand Down Expand Up @@ -384,7 +385,7 @@ body {
visibility: visible;
transition: opacity 0.5s ease;
will-change: opacity;
box-shadow: rgba(17, 17, 26, 0.1) 0px 4px 16px, rgba(17, 17, 26, 0.05) 0px 8px 32px;
box-shadow: rgba(17, 17, 26, 0.1) 0rem 0.25rem 1rem, rgba(17, 17, 26, 0.05) 0rem 0.5rem 2rem;
}
.navigation--item {
width: 3.62rem;
Expand All @@ -395,16 +396,17 @@ body {
cursor: pointer;
}
.navigation--item--separator {
border: 1px solid rgba(255, 255, 255, 0.2);
border: 0.0625rem solid rgba(255, 255, 255, 0.2);
}
.navigation-hidden {
transition-duration: 0s;
opacity: 0;
visibility: hidden;
}
.navigation svg {
fill: white;
max-width: 2rem;
max-height: 2rem;
width: 2rem;
height: 2rem;
}
.navigation--fullscreen--exit {
display: none;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/css/clock.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
color: #fff;
font-size: 3rem;
padding: 1rem;
text-shadow: 0 0 20px black;
text-shadow: 0 0 1.25rem black;
background-color: rgba(0, 0, 0, 0.3);
background: rgb(0, 0, 0);
background: -moz-linear-gradient(
Expand Down Expand Up @@ -34,7 +34,7 @@
padding: 0;
}

@media screen and (max-width: 500px) {
@media screen and (max-width: 31.25rem) {
#clock {
padding: 0.5rem;
}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/css/error.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

.error {
background-color: rgba(204, 0, 0, 0.4);
border: 2px solid rgb(204, 0, 0);
max-width: 560px;
border: 0.125rem solid rgb(204, 0, 0);
max-width: 35rem;
width: 100%;
color: #fff;
padding: 2rem;
border-radius: 12px;
border-radius: 0.75rem;
}

.error--title {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/css/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ body {
margin: 0;
font-size: 100%; /* 16px */
font-family: "Barlow", Arial, Helvetica, sans-serif;
letter-spacing: 1px;
letter-spacing: 0.0625rem;
background-color: #000;
}

@media screen and (max-width: 500px) {
@media screen and (max-width: 31.25rem) {
html,
body {
font-size: 80%; /* 16px */
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/css/image.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
color: #fff;
font-size: 1.1rem;
text-align: right;
text-shadow: 0 0 20px black;
text-shadow: 0 0 1.25rem black;
background-color: rgba(0, 0, 0, 0.3);
background: rgb(0, 0, 0);
background: -moz-linear-gradient(
Expand Down Expand Up @@ -56,7 +56,7 @@
display: inline-block;
font-size: 0.84rem;
font-weight: bold;
transform: translate(1px, -3px);
transform: translate(0.0625rem, -0.1875rem);
}
.image--metadata--exif--seperator {
opacity: 0.3;
Expand All @@ -66,7 +66,7 @@
padding-left: 3rem;
}

@media screen and (max-width: 500px) {
@media screen and (max-width: 31.25rem) {
.image--metadata {
padding: 0.5rem;
max-width: 50vw;
Expand Down
Loading

0 comments on commit ec0572d

Please sign in to comment.