Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private static String detectMimeType(File file) throws MediaException {
LOGGER.at(Level.DEBUG).log("MIME type successfully detected");

try {
if (isSupportedVideo(mimeType)) {
if (isSupportedVideo(inputFile, mimeType)) {
if (isVideoCompliant(inputFile)) {
LOGGER.at(Level.INFO).log("The video doesn't need conversion");
return null;
Expand All @@ -114,7 +114,7 @@ private static String detectMimeType(File file) throws MediaException {
return null;
}

if (isSupportedImage(inputFile, mimeType)) {
if (isSupportedImage(mimeType)) {
if (isImageCompliant(inputFile, mimeType)) {
LOGGER.at(Level.INFO).log("The image doesn't need conversion");
return null;
Expand All @@ -133,11 +133,41 @@ private static String detectMimeType(File file) throws MediaException {
/**
* Checks if the MIME type corresponds to one of the supported video formats.
*
* @param file the file to check
* @param mimeType the MIME type to check
* @return {@code true} if the MIME type is supported
*/
private static boolean isSupportedVideo(String mimeType) {
return SUPPORTED_VIDEOS.contains(mimeType);
private static boolean isSupportedVideo(File file, String mimeType) {
return SUPPORTED_VIDEOS.contains(mimeType) || isAnimatedWebp(file, mimeType);
}

/**
* Detects if the file is an animated WebP by checking its file header.
*
* @param file the file to check
* @param mimeType the MIME type to check
* @return {@code true} if the file is an animated WebP
*/
private static boolean isAnimatedWebp(File file, String mimeType) {
if ("image/webp".equals(mimeType)) {
try (var fileInputStream = new FileInputStream(file)) {
var header = fileInputStream.readNBytes(WEBP_HEADER_SIZE);
if (header.length < WEBP_HEADER_SIZE) {
return false;
}

var chunkHeader = new String(header, WEBP_CHUNK_TYPE_OFFSET, WEBP_CHUNK_TYPE_LENGTH, ISO_8859_1);
boolean isExtendedFormat = WEBP_EXTENDED_FILE_FORMAT.equals(chunkHeader);
boolean hasAnimationFlag = (header[WEBP_FLAGS_BYTE_OFFSET] & WEBP_ANIMATION_BIT_MASK) != 0;

return isExtendedFormat && hasAnimationFlag;
} catch (IOException e) {
LOGGER.at(Level.WARN).setCause(e).log("An error occurred checking if the file is an animated WebP");
return false;
}
}

return false;
}

/**
Expand Down Expand Up @@ -311,45 +341,14 @@ private static boolean isAnimationCompliant(@Nullable AnimationDetails animation

/**
* Checks if the MIME type corresponds to one of the supported image formats.
* If the image file is an animated WebP, {@code false} is returned as they are not currently supported.
*
* @param image the image file to check
* @param mimeType the MIME type to check
* @return {@code true} if the MIME type is supported
*/
private static boolean isSupportedImage(File image, String mimeType) {
if ("image/webp".equals(mimeType) && isAnimatedWebp(image)) {
LOGGER.at(Level.INFO).log("The image is an animated WebP");
return false;
}

private static boolean isSupportedImage(String mimeType) {
return mimeType.startsWith("image/");
}

/**
* Detects if a WebP file is animated by checking its file header.
*
* @param file the WebP file to check
* @return {@code true} if the file is an animated WebP
*/
private static boolean isAnimatedWebp(File file) {
try (var fileInputStream = new FileInputStream(file)) {
var header = fileInputStream.readNBytes(WEBP_HEADER_SIZE);
if (header.length < WEBP_HEADER_SIZE) {
return false;
}

var chunkHeader = new String(header, WEBP_CHUNK_TYPE_OFFSET, WEBP_CHUNK_TYPE_LENGTH, ISO_8859_1);
boolean isExtendedFormat = WEBP_EXTENDED_FILE_FORMAT.equals(chunkHeader);
boolean hasAnimationFlag = (header[WEBP_FLAGS_BYTE_OFFSET] & WEBP_ANIMATION_BIT_MASK) != 0;

return isExtendedFormat && hasAnimationFlag;
} catch (IOException e) {
LOGGER.at(Level.WARN).setCause(e).log("An error occurred checking if the file is an animated WebP");
return false;
}
}

/**
* Checks if passed-in image is already compliant with Telegram's requisites.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ public enum Answer {
<tg-thinking>Processing file...</tg-thinking>
"""),
SUPPORTED_FORMATS("""
| Type | Supported formats |
|:---------|:------------------------------------------------|
| images | png, jpg, static webp, tiff, ico, svg, psd |
| videos | gif, mov, avi, mp4, webm, m4v, mkv, live photos |
| stickers | static, video, animated |
| Type | Supported formats |
|:---------|:---------------------------------------------------------------|
| images | png, jpg, static webp, tiff, ico, svg, psd |
| videos | gif, mov, avi, mp4, webm, m4v, mkv, animated webp, live photos |
| stickers | static, video, animated |

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,11 @@ void noVideoConversionNeeded() throws Exception {

@Test
@Tag(Tags.VIDEO)
void resizeAnimatedWebpVideo() {
void resizeAnimatedWebpVideo() throws Exception {
var webpVideo = loadResource("animated.webp");
var result = MediaHelper.convert(webpVideo);

var ex = assertThrows(MediaException.class, () -> MediaHelper.convert(webpVideo));
assertThat(ex.getMessage(), equalTo("The file with image/webp MIME type is not supported"));
assertVideoConsistency(result, 512, 512, 28.583334F, 0.84F);
}

@Test
Expand Down Expand Up @@ -403,6 +403,14 @@ void concurrentGifVideoConversions() {
executeConcurrentConversionsOf(gifVideo);
}

@Test
@Tag(Tags.VIDEO)
@DisplayName("webp videos")
void concurrentWebpVideoConversions() {
var webpVideo = loadResource("animated.webp");
executeConcurrentConversionsOf(webpVideo);
}

@Test
@Tag(Tags.IMAGE)
@DisplayName("webp images")
Expand Down
Loading