From a4b54aa935f60d392cdaec7c4d315ff274ce73b5 Mon Sep 17 00:00:00 2001 From: tobi Date: Thu, 17 Jul 2025 13:20:01 +0200 Subject: [PATCH] [feature] Add `avif` file support (#4331) # Description > If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements. > > If this is a documentation change, please briefly describe what you've changed and why. This pull request implements support for reading avif images properly. closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4330 ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes. - [x] I/we have run `go fmt ./...` and `golangci-lint run`. Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4331 Co-authored-by: tobi Co-committed-by: tobi --- .../api/client/instance/instancepatch_test.go | 6 ++ internal/media/ffmpeg.go | 15 +++-- internal/media/manager.go | 1 + internal/media/manager_test.go | 56 ++++++++++++++++++ internal/media/test/gotosocial-processed.avif | Bin 0 -> 3815 bytes internal/media/test/gotosocial-thumbnail.webp | Bin 0 -> 4198 bytes internal/media/test/gotosocial.avif | Bin 0 -> 3815 bytes internal/typeutils/internaltofrontend_test.go | 2 + 8 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 internal/media/test/gotosocial-processed.avif create mode 100644 internal/media/test/gotosocial-thumbnail.webp create mode 100644 internal/media/test/gotosocial.avif diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go index dc3eb84b1..f23a44dc8 100644 --- a/internal/api/client/instance/instancepatch_test.go +++ b/internal/api/client/instance/instancepatch_test.go @@ -112,6 +112,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg", @@ -255,6 +256,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg", @@ -398,6 +400,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg", @@ -592,6 +595,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg", @@ -757,6 +761,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg", @@ -941,6 +946,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg", diff --git a/internal/media/ffmpeg.go b/internal/media/ffmpeg.go index 676eba9be..05e8c69a6 100644 --- a/internal/media/ffmpeg.go +++ b/internal/media/ffmpeg.go @@ -342,16 +342,21 @@ func (res *result) GetFileType() (gtsmodel.FileType, string, string) { case "mov,mp4,m4a,3gp,3g2,mj2": switch { case len(res.video) > 0: - if len(res.audio) == 0 && - res.duration <= 30 { + switch { + case res.video[0].framerate == 0 && res.video[0].stream.codec == "av1": + // Looks like an avif image. + return gtsmodel.FileTypeImage, + "image/avif", "avif" + case len(res.audio) == 0 && res.duration <= 30: // Short, soundless // video file aka gifv. return gtsmodel.FileTypeGifv, "video/mp4", "mp4" + default: + // Video file (with or without audio). + return gtsmodel.FileTypeVideo, + "video/mp4", "mp4" } - // Video file (with or without audio). - return gtsmodel.FileTypeVideo, - "video/mp4", "mp4" case len(res.audio) > 0 && res.audio[0].codec == "aac": // m4a only supports [aac] audio. diff --git a/internal/media/manager.go b/internal/media/manager.go index a7bd8948b..2ec222f3c 100644 --- a/internal/media/manager.go +++ b/internal/media/manager.go @@ -38,6 +38,7 @@ var SupportedMIMETypes = []string{ "image/jpeg", // .jpeg "image/gif", // .gif "image/webp", // .webp + "image/avif", // .avif "audio/mp2", // .mp2 "audio/mp3", // .mp3 diff --git a/internal/media/manager_test.go b/internal/media/manager_test.go index 6182babbb..f6914acf0 100644 --- a/internal/media/manager_test.go +++ b/internal/media/manager_test.go @@ -789,6 +789,62 @@ func (suite *ManagerTestSuite) TestPngAlphaChannelProcess() { equalFiles(suite.T(), suite.state.Storage, dbAttachment.Thumbnail.Path, "./test/test-png-alphachannel-thumbnail.jpeg") } +func (suite *ManagerTestSuite) TestAvifProcess() { + ctx := suite.T().Context() + + data := func(_ context.Context) (io.ReadCloser, error) { + // load bytes from a test image + b, err := os.ReadFile("./test/gotosocial.avif") + if err != nil { + panic(err) + } + return io.NopCloser(bytes.NewBuffer(b)), nil + } + + accountID := "01FS1X72SK9ZPW0J1QQ68BD264" + + // process the media with no additional info provided + processing, err := suite.manager.CreateMedia(ctx, + accountID, + data, + media.AdditionalMediaInfo{}, + ) + suite.NoError(err) + suite.NotNil(processing) + + // do a blocking call to fetch the attachment + attachment, err := processing.Load(ctx) + suite.NoError(err) + suite.NotNil(attachment) + + // make sure it's got the stuff set on it that we expect + // the attachment ID and accountID we expect + suite.Equal(processing.ID(), attachment.ID) + suite.Equal(accountID, attachment.AccountID) + + // file meta should be correctly derived from the image + suite.EqualValues(gtsmodel.Original{ + Width: 1038, Height: 980, Size: 1017240, Aspect: 1.0591837, + }, attachment.FileMeta.Original) + suite.EqualValues(gtsmodel.Small{ + Width: 512, Height: 483, Size: 247296, Aspect: 1.0591837, + }, attachment.FileMeta.Small) + suite.Equal("image/avif", attachment.File.ContentType) + suite.Equal("image/webp", attachment.Thumbnail.ContentType) + suite.Equal(3815, attachment.File.FileSize) + suite.Equal(4198, attachment.Thumbnail.FileSize) + suite.Equal("LNQJQ7%M?w-;-pj[bbj[?^ofDiWB", attachment.Blurhash) + + // now make sure the attachment is in the database + dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) + suite.NoError(err) + suite.NotNil(dbAttachment) + + // ensure the files contain the expected data. + equalFiles(suite.T(), suite.state.Storage, dbAttachment.File.Path, "./test/gotosocial-processed.avif") + equalFiles(suite.T(), suite.state.Storage, dbAttachment.Thumbnail.Path, "./test/gotosocial-thumbnail.webp") +} + func (suite *ManagerTestSuite) TestSimpleJpegProcessWithCallback() { ctx := suite.T().Context() diff --git a/internal/media/test/gotosocial-processed.avif b/internal/media/test/gotosocial-processed.avif new file mode 100644 index 0000000000000000000000000000000000000000..f0433b70bdf16751a90ad8d2932241b5ea031afd GIT binary patch literal 3815 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 PbH0Xps7J9An?3&rj->%9 literal 0 HcmV?d00001 diff --git a/internal/media/test/gotosocial-thumbnail.webp b/internal/media/test/gotosocial-thumbnail.webp new file mode 100644 index 0000000000000000000000000000000000000000..370ca9d0b77296bb2f2746b35a27fa3a69883688 GIT binary patch literal 4198 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 PbH0Xps7J9An?3&rj->%9 literal 0 HcmV?d00001 diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index a26ff114e..47ad284e6 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -1773,6 +1773,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg", @@ -1930,6 +1931,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() { "image/jpeg", "image/gif", "image/webp", + "image/avif", "audio/mp2", "audio/mp3", "audio/mpeg",