mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Normalize settings on save
This commit is contained in:
@@ -2,9 +2,15 @@ package dev.kruhlmann.imgfloat.service;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import dev.kruhlmann.imgfloat.model.AudioAsset;
|
||||||
import dev.kruhlmann.imgfloat.model.Settings;
|
import dev.kruhlmann.imgfloat.model.Settings;
|
||||||
|
import dev.kruhlmann.imgfloat.model.VisualAsset;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.AudioAssetRepository;
|
||||||
import dev.kruhlmann.imgfloat.repository.SettingsRepository;
|
import dev.kruhlmann.imgfloat.repository.SettingsRepository;
|
||||||
|
import dev.kruhlmann.imgfloat.repository.VisualAssetRepository;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -15,10 +21,19 @@ public class SettingsService {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(SettingsService.class);
|
private static final Logger logger = LoggerFactory.getLogger(SettingsService.class);
|
||||||
|
|
||||||
private final SettingsRepository repo;
|
private final SettingsRepository repo;
|
||||||
|
private final VisualAssetRepository visualAssetRepository;
|
||||||
|
private final AudioAssetRepository audioAssetRepository;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public SettingsService(SettingsRepository repo, ObjectMapper objectMapper) {
|
public SettingsService(
|
||||||
|
SettingsRepository repo,
|
||||||
|
VisualAssetRepository visualAssetRepository,
|
||||||
|
AudioAssetRepository audioAssetRepository,
|
||||||
|
ObjectMapper objectMapper
|
||||||
|
) {
|
||||||
this.repo = repo;
|
this.repo = repo;
|
||||||
|
this.visualAssetRepository = visualAssetRepository;
|
||||||
|
this.audioAssetRepository = audioAssetRepository;
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +54,9 @@ public class SettingsService {
|
|||||||
public Settings save(Settings settings) {
|
public Settings save(Settings settings) {
|
||||||
settings.setId(1);
|
settings.setId(1);
|
||||||
logSettings("Saving settings", settings);
|
logSettings("Saving settings", settings);
|
||||||
return repo.save(settings);
|
Settings savedSettings = repo.save(settings);
|
||||||
|
clampAssetsToSettings(savedSettings);
|
||||||
|
return savedSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logSettings(String msg, Settings settings) {
|
public void logSettings(String msg, Settings settings) {
|
||||||
@@ -49,4 +66,78 @@ public class SettingsService {
|
|||||||
logger.error("Failed to serialize settings", e);
|
logger.error("Failed to serialize settings", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clampAssetsToSettings(Settings settings) {
|
||||||
|
double minSpeed = settings.getMinAssetPlaybackSpeedFraction();
|
||||||
|
double maxSpeed = settings.getMaxAssetPlaybackSpeedFraction();
|
||||||
|
double minPitch = settings.getMinAssetAudioPitchFraction();
|
||||||
|
double maxPitch = settings.getMaxAssetAudioPitchFraction();
|
||||||
|
double minVolume = settings.getMinAssetVolumeFraction();
|
||||||
|
double maxVolume = settings.getMaxAssetVolumeFraction();
|
||||||
|
|
||||||
|
List<VisualAsset> visualsToUpdate = new ArrayList<>();
|
||||||
|
for (VisualAsset visual : visualAssetRepository.findAll()) {
|
||||||
|
boolean changed = false;
|
||||||
|
double speed = visual.getSpeed();
|
||||||
|
double clampedSpeed = clamp(speed, minSpeed, maxSpeed);
|
||||||
|
if (Double.compare(speed, clampedSpeed) != 0) {
|
||||||
|
visual.setSpeed(clampedSpeed);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
double volume = visual.getAudioVolume();
|
||||||
|
double clampedVolume = clamp(volume, minVolume, maxVolume);
|
||||||
|
if (Double.compare(volume, clampedVolume) != 0) {
|
||||||
|
visual.setAudioVolume(clampedVolume);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
visualsToUpdate.add(visual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AudioAsset> audioToUpdate = new ArrayList<>();
|
||||||
|
for (AudioAsset audio : audioAssetRepository.findAll()) {
|
||||||
|
boolean changed = false;
|
||||||
|
double speed = audio.getAudioSpeed();
|
||||||
|
double clampedSpeed = clamp(speed, minSpeed, maxSpeed);
|
||||||
|
if (Double.compare(speed, clampedSpeed) != 0) {
|
||||||
|
audio.setAudioSpeed(clampedSpeed);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
double pitch = audio.getAudioPitch();
|
||||||
|
double clampedPitch = clamp(pitch, minPitch, maxPitch);
|
||||||
|
if (Double.compare(pitch, clampedPitch) != 0) {
|
||||||
|
audio.setAudioPitch(clampedPitch);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
double volume = audio.getAudioVolume();
|
||||||
|
double clampedVolume = clamp(volume, minVolume, maxVolume);
|
||||||
|
if (Double.compare(volume, clampedVolume) != 0) {
|
||||||
|
audio.setAudioVolume(clampedVolume);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
audioToUpdate.add(audio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visualsToUpdate.isEmpty()) {
|
||||||
|
visualAssetRepository.saveAll(visualsToUpdate);
|
||||||
|
}
|
||||||
|
if (!audioToUpdate.isEmpty()) {
|
||||||
|
audioAssetRepository.saveAll(audioToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visualsToUpdate.isEmpty() || !audioToUpdate.isEmpty()) {
|
||||||
|
logger.info(
|
||||||
|
"Normalized {} visual assets and {} audio assets to new settings ranges",
|
||||||
|
visualsToUpdate.size(),
|
||||||
|
audioToUpdate.size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double clamp(double value, double min, double max) {
|
||||||
|
return Math.max(min, Math.min(max, value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,6 +318,82 @@ body.has-staging-banner {
|
|||||||
color: #fef3c7;
|
color: #fef3c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 24px;
|
||||||
|
right: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
z-index: 1200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 220px;
|
||||||
|
max-width: 360px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: rgba(15, 23, 42, 0.96);
|
||||||
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.45);
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-size: 14px;
|
||||||
|
animation: toast-enter 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-exit {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(8px);
|
||||||
|
transition:
|
||||||
|
opacity 0.2s ease,
|
||||||
|
transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-indicator {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-message {
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-success {
|
||||||
|
border-color: rgba(34, 197, 94, 0.45);
|
||||||
|
color: #86efac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-error {
|
||||||
|
border-color: rgba(248, 113, 113, 0.45);
|
||||||
|
color: #fecaca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-warning {
|
||||||
|
border-color: rgba(251, 191, 36, 0.5);
|
||||||
|
color: #fde68a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-info {
|
||||||
|
border-color: rgba(56, 189, 248, 0.45);
|
||||||
|
color: #bae6fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes toast-enter {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(8px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.info-card {
|
.info-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -624,6 +700,15 @@ button:disabled:hover {
|
|||||||
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.25);
|
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-input:invalid {
|
||||||
|
border-color: #f87171;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-input:focus:invalid {
|
||||||
|
border-color: #f87171;
|
||||||
|
box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
.text-input:disabled,
|
.text-input:disabled,
|
||||||
.text-input[aria-disabled="true"] {
|
.text-input[aria-disabled="true"] {
|
||||||
background: #020617;
|
background: #020617;
|
||||||
|
|||||||
@@ -121,7 +121,7 @@
|
|||||||
class="text-input"
|
class="text-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
pattern="^(0(\.\d+)?|1(\.0+)?)$"
|
pattern="^(0|[1-9]\d*)(\.\d+)?$"
|
||||||
placeholder="0.5"
|
placeholder="0.5"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
class="text-input"
|
class="text-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
pattern="^(0(\.\d+)?|1(\.0+)?)$"
|
pattern="^(0|[1-9]\d*)(\.\d+)?$"
|
||||||
placeholder="1.0"
|
placeholder="1.0"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
class="text-input"
|
class="text-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
pattern="^(0(\.\d+)?|1(\.0+)?)$"
|
pattern="^(0|[1-9]\d*)(\.\d+)?$"
|
||||||
placeholder="0.8"
|
placeholder="0.8"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -167,7 +167,7 @@
|
|||||||
class="text-input"
|
class="text-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
pattern="^(0(\.\d+)?|1(\.0+)?)$"
|
pattern="^(0|[1-9]\d*)(\.\d+)?$"
|
||||||
placeholder="1.0"
|
placeholder="1.0"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
class="text-input"
|
class="text-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
pattern="^(0(\.\d+)?|1(\.0+)?)$"
|
pattern="^(0|[1-9]\d*)(\.\d+)?$"
|
||||||
placeholder="0.2"
|
placeholder="0.2"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -194,7 +194,7 @@
|
|||||||
class="text-input"
|
class="text-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
pattern="^(0(\.\d+)?|1(\.0+)?)$"
|
pattern="^(0|[1-9]\d*)(\.\d+)?$"
|
||||||
placeholder="1.0"
|
placeholder="1.0"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
class="text-input"
|
class="text-input"
|
||||||
type="text"
|
type="text"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
pattern="^[1-9]\\d*$"
|
pattern="^[1-9]\d*$"
|
||||||
placeholder="60"
|
placeholder="60"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user