diff options
Diffstat (limited to 'internal/media/metadata.go')
-rw-r--r-- | internal/media/metadata.go | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/internal/media/metadata.go b/internal/media/metadata.go new file mode 100644 index 000000000..3816b2826 --- /dev/null +++ b/internal/media/metadata.go @@ -0,0 +1,96 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package media + +import ( + "context" + "os" + "strings" + + terminator "codeberg.org/superseriousbusiness/exif-terminator" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/log" +) + +// clearMetadata performs our best-attempt at cleaning metadata from +// input file. Depending on file type this may perform a full EXIF clean, +// or just a clean of global container level metadata records. +func clearMetadata(ctx context.Context, filepath string) error { + var ext, outpath string + + // Generate cleaned output path MAINTAINING extension. + if i := strings.IndexByte(filepath, '.'); i != -1 { + outpath = filepath[:i] + "_cleaned" + filepath[i:] + ext = filepath[i+1:] + } else { + return gtserror.New("input file missing extension") + } + + switch ext { + case "jpeg", "png", "webp": + // For these few file types, we actually support + // cleaning exif data using a native Go library. + log.Debug(ctx, "cleaning with exif-terminator") + err := terminateExif(outpath, filepath, ext) + if err != nil { + return err + } + default: + // For all other types, best-effort clean with ffmpeg. + log.Debug(ctx, "cleaning with ffmpeg -map_metadata -1") + err := ffmpegClearMetadata(ctx, outpath, filepath) + if err != nil { + return err + } + } + + // Move the new output file path to original location. + if err := os.Rename(outpath, filepath); err != nil { + return gtserror.Newf("error renaming %s -> %s: %w", outpath, filepath, err) + } + + return nil +} + +// terminateExif cleans exif data from file at input path, into file +// at output path, exusing given file extension to determine cleaning. +func terminateExif(outpath, inpath string, ext string) error { + // Open input file at given path. + inFile, err := os.Open(inpath) + if err != nil { + return gtserror.Newf("error opening input file %s: %w", inpath, err) + } + + // Open output file at given path. + outFile, err := os.Create(outpath) + if err != nil { + return gtserror.Newf("error opening output file %s: %w", outpath, err) + } + + // Terminate EXIF data from 'inFile' -> 'outFile'. + err = terminator.TerminateInto(outFile, inFile, ext) + if err != nil { + return gtserror.Newf("error terminating exif data: %w", err) + } + + // Done with files. + _ = inFile.Close() + _ = outFile.Close() + + return nil +} |