Improve translation controls

This commit is contained in:
2025-12-09 10:29:10 +01:00
parent fe915b71f4
commit d92617f82c
11 changed files with 413 additions and 52 deletions

View File

@@ -0,0 +1,60 @@
package com.imgfloat.app.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class SchemaMigration implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(SchemaMigration.class);
private final JdbcTemplate jdbcTemplate;
public SchemaMigration(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void run(ApplicationArguments args) {
ensureChannelCanvasColumns();
}
private void ensureChannelCanvasColumns() {
List<String> columns;
try {
columns = jdbcTemplate.query("PRAGMA table_info(channels)", (rs, rowNum) -> rs.getString("name"));
} catch (DataAccessException ex) {
logger.warn("Unable to inspect channels table for canvas columns", ex);
return;
}
if (columns.isEmpty()) {
// Table does not exist yet; Hibernate will create it with the correct columns.
return;
}
addColumnIfMissing(columns, "canvas_width", "REAL", "1920");
addColumnIfMissing(columns, "canvas_height", "REAL", "1080");
}
private void addColumnIfMissing(List<String> existingColumns, String columnName, String dataType, String defaultValue) {
if (existingColumns.contains(columnName)) {
return;
}
try {
jdbcTemplate.execute("ALTER TABLE channels ADD COLUMN " + columnName + " " + dataType + " DEFAULT " + defaultValue);
logger.info("Added missing column '{}' to channels table", columnName);
} catch (DataAccessException ex) {
logger.warn("Failed to add column '{}' to channels table", columnName, ex);
}
}
}

View File

@@ -2,6 +2,7 @@ package com.imgfloat.app.controller;
import com.imgfloat.app.model.AdminRequest;
import com.imgfloat.app.model.Asset;
import com.imgfloat.app.model.CanvasSettingsRequest;
import com.imgfloat.app.model.TransformRequest;
import com.imgfloat.app.model.VisibilityRequest;
import com.imgfloat.app.service.ChannelDirectoryService;
@@ -85,6 +86,23 @@ public class ChannelApiController {
return channelDirectoryService.getVisibleAssets(broadcaster);
}
@GetMapping("/canvas")
public CanvasSettingsRequest getCanvas(@PathVariable("broadcaster") String broadcaster,
OAuth2AuthenticationToken authentication) {
String login = TwitchUser.from(authentication).login();
ensureAuthorized(broadcaster, login);
return channelDirectoryService.getCanvasSettings(broadcaster);
}
@PutMapping("/canvas")
public CanvasSettingsRequest updateCanvas(@PathVariable("broadcaster") String broadcaster,
@Valid @RequestBody CanvasSettingsRequest request,
OAuth2AuthenticationToken authentication) {
String login = TwitchUser.from(authentication).login();
ensureBroadcaster(broadcaster, login);
return channelDirectoryService.updateCanvasSettings(broadcaster, request);
}
@PostMapping(value = "/assets", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Asset> createAsset(@PathVariable("broadcaster") String broadcaster,
@org.springframework.web.bind.annotation.RequestPart("file") MultipartFile file,

View File

@@ -0,0 +1,35 @@
package com.imgfloat.app.model;
import jakarta.validation.constraints.Positive;
public class CanvasSettingsRequest {
@Positive
private double width;
@Positive
private double height;
public CanvasSettingsRequest() {
}
public CanvasSettingsRequest(double width, double height) {
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}

View File

@@ -28,11 +28,17 @@ public class Channel {
@Column(name = "admin_username")
private Set<String> admins = new HashSet<>();
private double canvasWidth = 1920;
private double canvasHeight = 1080;
public Channel() {
}
public Channel(String broadcaster) {
this.broadcaster = normalize(broadcaster);
this.canvasWidth = 1920;
this.canvasHeight = 1080;
}
public String getBroadcaster() {
@@ -51,6 +57,22 @@ public class Channel {
return admins.remove(normalize(username));
}
public double getCanvasWidth() {
return canvasWidth;
}
public void setCanvasWidth(double canvasWidth) {
this.canvasWidth = canvasWidth;
}
public double getCanvasHeight() {
return canvasHeight;
}
public void setCanvasHeight(double canvasHeight) {
this.canvasHeight = canvasHeight;
}
@PrePersist
@PreUpdate
public void normalizeFields() {
@@ -58,6 +80,12 @@ public class Channel {
this.admins = admins.stream()
.map(Channel::normalize)
.collect(Collectors.toSet());
if (canvasWidth <= 0) {
canvasWidth = 1920;
}
if (canvasHeight <= 0) {
canvasHeight = 1080;
}
}
private static String normalize(String value) {

View File

@@ -3,6 +3,7 @@ package com.imgfloat.app.service;
import com.imgfloat.app.model.Asset;
import com.imgfloat.app.model.AssetEvent;
import com.imgfloat.app.model.Channel;
import com.imgfloat.app.model.CanvasSettingsRequest;
import com.imgfloat.app.model.TransformRequest;
import com.imgfloat.app.model.VisibilityRequest;
import com.imgfloat.app.repository.AssetRepository;
@@ -68,6 +69,19 @@ public class ChannelDirectoryService {
return assetRepository.findByBroadcasterAndHiddenFalse(normalize(broadcaster));
}
public CanvasSettingsRequest getCanvasSettings(String broadcaster) {
Channel channel = getOrCreateChannel(broadcaster);
return new CanvasSettingsRequest(channel.getCanvasWidth(), channel.getCanvasHeight());
}
public CanvasSettingsRequest updateCanvasSettings(String broadcaster, CanvasSettingsRequest request) {
Channel channel = getOrCreateChannel(broadcaster);
channel.setCanvasWidth(request.getWidth());
channel.setCanvasHeight(request.getHeight());
channelRepository.save(channel);
return new CanvasSettingsRequest(channel.getCanvasWidth(), channel.getCanvasHeight());
}
public Optional<Asset> createAsset(String broadcaster, MultipartFile file) throws IOException {
Channel channel = getOrCreateChannel(broadcaster);
byte[] bytes = file.getBytes();