fix(back-end): img convert
All checks were successful
Build and Deploy / test-backend (push) Successful in 26s
Build and Deploy / test-frontend (push) Successful in 1m1s
Build and Deploy / build-and-push (push) Successful in 39s
Build and Deploy / deploy (push) Successful in 8s

This commit is contained in:
2026-03-09 20:04:21 +01:00
parent cd2666d8e2
commit 77d7bdb265
4 changed files with 103 additions and 3 deletions

View File

@@ -350,7 +350,27 @@ public class AdminMediaControllerService {
} }
String extension = GENERATED_FORMAT_EXTENSIONS.get(format); String extension = GENERATED_FORMAT_EXTENSIONS.get(format);
Path outputFile = generatedDirectory.resolve(preset.name() + "." + extension); Path outputFile = generatedDirectory.resolve(preset.name() + "." + extension);
mediaFfmpegService.generateVariant(sourceFile, outputFile, dimensions.widthPx(), dimensions.heightPx(), format); try {
mediaFfmpegService.generateVariant(
sourceFile,
outputFile,
dimensions.widthPx(),
dimensions.heightPx(),
format
);
} catch (IOException e) {
if (FORMAT_AVIF.equals(format)) {
skippedFormats.add(format);
logger.warn(
"Skipping AVIF variant generation for asset {} preset '{}' because FFmpeg AVIF generation failed: {}",
asset.getId(),
preset.name(),
e.getMessage()
);
continue;
}
throw e;
}
MediaVariant variant = new MediaVariant(); MediaVariant variant = new MediaVariant();
variant.setMediaAsset(asset); variant.setMediaAsset(asset);

View File

@@ -82,8 +82,6 @@ public class MediaFfmpegService {
case "AVIF" -> { case "AVIF" -> {
command.add("-c:v"); command.add("-c:v");
command.add(encoder); command.add(encoder);
command.add("-still-picture");
command.add("1");
command.add("-crf"); command.add("-crf");
command.add("30"); command.add("30");
command.add("-b:v"); command.add("-b:v");

View File

@@ -51,6 +51,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -275,6 +276,31 @@ class AdminMediaControllerServiceTest {
.noneMatch(variant -> "WEBP".equals(variant.getFormat()) || "AVIF".equals(variant.getFormat()))); .noneMatch(variant -> "WEBP".equals(variant.getFormat()) || "AVIF".equals(variant.getFormat())));
} }
@Test
void uploadAsset_whenAvifGenerationFails_shouldKeepAssetReadyAndStoreOtherVariants() throws Exception {
when(mediaImageInspector.inspect(any(Path.class))).thenReturn(
new MediaImageInspector.ImageMetadata("image/png", "png", 1600, 900)
);
doThrow(new java.io.IOException("FFmpeg failed to generate media variant. Unrecognized option 'still-picture'."))
.when(mediaFfmpegService)
.generateVariant(any(Path.class), any(Path.class), anyInt(), anyInt(), org.mockito.ArgumentMatchers.eq("AVIF"));
MockMultipartFile file = new MockMultipartFile(
"file",
"landing-hero.png",
"image/png",
"png-image-content".getBytes(StandardCharsets.UTF_8)
);
AdminMediaAssetDto dto = service.uploadAsset(file, " Landing hero ", " Main headline ", null);
assertEquals("READY", dto.getStatus());
assertEquals(7, dto.getVariants().size());
assertTrue(dto.getVariants().stream().noneMatch(variant -> "AVIF".equals(variant.getFormat())));
assertEquals(3, dto.getVariants().stream().filter(variant -> "JPEG".equals(variant.getFormat())).count());
assertEquals(3, dto.getVariants().stream().filter(variant -> "WEBP".equals(variant.getFormat())).count());
}
@Test @Test
void uploadAsset_withOversizedFile_shouldFailValidationBeforePersistence() { void uploadAsset_withOversizedFile_shouldFailValidationBeforePersistence() {
service = new AdminMediaControllerService( service = new AdminMediaControllerService(

View File

@@ -6,8 +6,11 @@ import org.junit.jupiter.api.io.TempDir;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
class MediaFfmpegServiceTest { class MediaFfmpegServiceTest {
@@ -61,4 +64,57 @@ class MediaFfmpegServiceTest {
assertEquals("Media target file name must not start with '-'.", ex.getMessage()); assertEquals("Media target file name must not start with '-'.", ex.getMessage());
} }
@Test
void generateVariant_avifShouldNotUseStillPictureFlag() throws Exception {
Path fakeFfmpeg = tempDir.resolve("fake-ffmpeg.sh");
Files.writeString(
fakeFfmpeg,
"""
#!/bin/sh
if [ "$1" = "-hide_banner" ] && [ "$2" = "-encoders" ]; then
cat <<'EOF'
V..... mjpeg
V..... libwebp
V..... libaom-av1
EOF
exit 0
fi
for arg in "$@"; do
if [ "$arg" = "-still-picture" ]; then
echo "Unrecognized option 'still-picture'. Error splitting the argument list: Option not found"
exit 1
fi
done
last_arg=""
for arg in "$@"; do
last_arg="$arg"
done
mkdir -p "$(dirname "$last_arg")"
printf 'ok' > "$last_arg"
exit 0
"""
);
Files.setPosixFilePermissions(
fakeFfmpeg,
Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE
)
);
MediaFfmpegService service = new MediaFfmpegService(fakeFfmpeg.toString());
Path source = tempDir.resolve("input.png");
Path target = tempDir.resolve("output.avif");
Files.writeString(source, "image");
service.generateVariant(source, target, 120, 80, "AVIF");
assertTrue(Files.exists(target));
assertEquals("ok", Files.readString(target));
}
} }