From 8f288f1689376a8cf6ab7f431e862eb870765342 Mon Sep 17 00:00:00 2001 From: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:58:15 +0000 Subject: [PATCH 1/3] [bugfix] determine mime-type to use during ffprobe evaluation stage, don't bother checking against file extension (#3506) * determine mime-type to use during ffprobe evaluation stage, don't bother rechecking by file extension * set mjpeg content-type * fix up tests expecting differing default values --- go.mod | 1 - go.sum | 2 - .../api/client/instance/instancepatch_test.go | 12 +- internal/media/ffmpeg.go | 75 +- internal/media/manager.go | 2 +- internal/media/manager_test.go | 2 +- internal/media/processingemoji.go | 9 +- internal/media/processingmedia.go | 20 +- internal/media/thumbnail.go | 24 +- internal/media/util.go | 8 - internal/typeutils/internaltofrontend_test.go | 4 +- .../codeberg.org/gruf/go-mimetypes/README.md | 5 - .../gruf/go-mimetypes/get-mime-types.sh | 42 - .../gruf/go-mimetypes/mime.gen.go | 1207 ----------------- vendor/codeberg.org/gruf/go-mimetypes/mime.go | 47 - vendor/modules.txt | 3 - 16 files changed, 85 insertions(+), 1378 deletions(-) delete mode 100644 vendor/codeberg.org/gruf/go-mimetypes/README.md delete mode 100644 vendor/codeberg.org/gruf/go-mimetypes/get-mime-types.sh delete mode 100644 vendor/codeberg.org/gruf/go-mimetypes/mime.gen.go delete mode 100644 vendor/codeberg.org/gruf/go-mimetypes/mime.go diff --git a/go.mod b/go.mod index 01e9291da..2b7ab98fd 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,6 @@ require ( codeberg.org/gruf/go-kv v1.6.5 codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 - codeberg.org/gruf/go-mimetypes v1.2.0 codeberg.org/gruf/go-mutexes v1.5.1 codeberg.org/gruf/go-runners v1.6.3 codeberg.org/gruf/go-sched v1.2.4 diff --git a/go.sum b/go.sum index 2c12373c9..72e252234 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,6 @@ codeberg.org/gruf/go-maps v1.0.4 h1:K+Ww4vvR3TZqm5jqrKVirmguZwa3v1VUvmig2SE8uxY= codeberg.org/gruf/go-maps v1.0.4/go.mod h1:ASX7osM7kFwt5O8GfGflcFjrwYGD8eIuRLl/oMjhEi8= codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 h1:m2/UCRXhjDwAg4vyji6iKCpomKw6P4PmBOUi5DvAMH4= codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760/go.mod h1:E3RcaCFNq4zXpvaJb8lfpPqdUAmSkP5F1VmMiEUYTEk= -codeberg.org/gruf/go-mimetypes v1.2.0 h1:3rZGXY/SkNYbamiddWXs2gETXIBkGIeWYnbWpp2OEbc= -codeberg.org/gruf/go-mimetypes v1.2.0/go.mod h1:YiUWRj/nAdJQc+UFRvcsL6xXZsbc6b6Ic739ycEO8Yg= codeberg.org/gruf/go-mutexes v1.5.1 h1:xICU0WXhWr6wf+Iror4eE3xT+xnXNPrO6o77D/G6QuY= codeberg.org/gruf/go-mutexes v1.5.1/go.mod h1:rPEqQ/y6CmGITaZ3GPTMQVsoZAOzbsAHyIaLsJcOqVE= codeberg.org/gruf/go-runners v1.6.3 h1:To/AX7eTrWuXrTkA3RA01YTP5zha1VZ68LQ+0D4RY7E= diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go index 5c7923adc..504fd7eb9 100644 --- a/internal/api/client/instance/instancepatch_test.go +++ b/internal/api/client/instance/instancepatch_test.go @@ -120,7 +120,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", @@ -261,7 +261,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", @@ -402,7 +402,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", @@ -594,7 +594,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", @@ -757,7 +757,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", @@ -939,7 +939,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", diff --git a/internal/media/ffmpeg.go b/internal/media/ffmpeg.go index 976662e60..1d7b01905 100644 --- a/internal/media/ffmpeg.go +++ b/internal/media/ffmpeg.go @@ -323,14 +323,14 @@ type videoStream struct { // // Note the checks for (len(res.video) > 0) may catch some audio files with embedded // album art as video, but i blame that on the hellscape that is media filetypes. -// -// TODO: we can update this code to also return a mimetype and avoid later parsing! -func (res *result) GetFileType() (gtsmodel.FileType, string) { +func (res *result) GetFileType() (gtsmodel.FileType, string, string) { switch res.format { case "mpeg": - return gtsmodel.FileTypeVideo, "mpeg" + return gtsmodel.FileTypeVideo, + "video/mpeg", "mpeg" case "mjpeg": - return gtsmodel.FileTypeVideo, "mjpeg" + return gtsmodel.FileTypeVideo, + "video/x-motion-jpeg", "mjpeg" case "mov,mp4,m4a,3gp,3g2,mj2": switch { case len(res.video) > 0: @@ -338,55 +338,70 @@ func (res *result) GetFileType() (gtsmodel.FileType, string) { res.duration <= 30 { // Short, soundless // video file aka gifv. - return gtsmodel.FileTypeGifv, "mp4" + return gtsmodel.FileTypeGifv, + "video/mp4", "mp4" } else { // Video file (with or without audio). - return gtsmodel.FileTypeVideo, "mp4" + return gtsmodel.FileTypeVideo, + "video/mp4", "mp4" } case len(res.audio) > 0 && res.audio[0].codec == "aac": // m4a only supports [aac] audio. - return gtsmodel.FileTypeAudio, "m4a" + return gtsmodel.FileTypeAudio, + "audio/mp4", "m4a" } case "apng": - return gtsmodel.FileTypeImage, "apng" + return gtsmodel.FileTypeImage, + "image/apng", "apng" case "png_pipe": - return gtsmodel.FileTypeImage, "png" + return gtsmodel.FileTypeImage, + "image/png", "png" case "image2", "image2pipe", "jpeg_pipe": - return gtsmodel.FileTypeImage, "jpeg" + return gtsmodel.FileTypeImage, + "image/jpeg", "jpeg" case "webp", "webp_pipe": - return gtsmodel.FileTypeImage, "webp" + return gtsmodel.FileTypeImage, + "image/webp", "webp" case "gif": - return gtsmodel.FileTypeImage, "gif" + return gtsmodel.FileTypeImage, + "image/gif", "gif" case "mp3": if len(res.audio) > 0 { switch res.audio[0].codec { case "mp2": - return gtsmodel.FileTypeAudio, "mp2" + return gtsmodel.FileTypeAudio, + "audio/mp2", "mp2" case "mp3": - return gtsmodel.FileTypeAudio, "mp3" + return gtsmodel.FileTypeAudio, + "audio/mp3", "mp3" } } case "asf": switch { case len(res.video) > 0: - return gtsmodel.FileTypeVideo, "wmv" + return gtsmodel.FileTypeVideo, + "video/x-ms-wmv", "wmv" case len(res.audio) > 0: - return gtsmodel.FileTypeAudio, "wma" + return gtsmodel.FileTypeAudio, + "audio/x-ms-wma", "wma" } case "ogg": if len(res.video) > 0 { switch res.video[0].codec { case "theora", "dirac": // daala, tarkin - return gtsmodel.FileTypeVideo, "ogv" + return gtsmodel.FileTypeVideo, + "video/ogg", "ogv" } } if len(res.audio) > 0 { switch res.audio[0].codec { case "opus", "libopus": - return gtsmodel.FileTypeAudio, "opus" + return gtsmodel.FileTypeAudio, + "audio/opus", "opus" default: - return gtsmodel.FileTypeAudio, "ogg" + return gtsmodel.FileTypeAudio, + "audio/ogg", "ogg" } } case "matroska,webm": @@ -411,21 +426,27 @@ func (res *result) GetFileType() (gtsmodel.FileType, string) { } if isWebm { - // Check for valid webm codec config. - return gtsmodel.FileTypeVideo, "webm" + // Check valid webm codec config. + return gtsmodel.FileTypeVideo, + "video/webm", "webm" } // All else falls under generic mkv. - return gtsmodel.FileTypeVideo, "mkv" + return gtsmodel.FileTypeVideo, + "video/x-matroska", "mkv" case len(res.audio) > 0: - return gtsmodel.FileTypeAudio, "mka" + return gtsmodel.FileTypeAudio, + "audio/x-matroska", "mka" } case "avi": - return gtsmodel.FileTypeVideo, "avi" + return gtsmodel.FileTypeVideo, + "video/x-msvideo", "avi" case "flac": - return gtsmodel.FileTypeAudio, "flac" + return gtsmodel.FileTypeAudio, + "audio/flac", "flac" } - return gtsmodel.FileTypeUnknown, res.format + return gtsmodel.FileTypeUnknown, + "", res.format } // ImageMeta extracts image metadata contained within ffprobe'd media result streams. diff --git a/internal/media/manager.go b/internal/media/manager.go index 22afd0028..2807848bd 100644 --- a/internal/media/manager.go +++ b/internal/media/manager.go @@ -56,7 +56,7 @@ "video/ogg", // .ogv // mpeg4 types - "audio/x-m4a", // .m4a + "audio/mp4", // .m4a "video/mp4", // .mp4 "video/quicktime", // .mov diff --git a/internal/media/manager_test.go b/internal/media/manager_test.go index c281b1000..e175369f5 100644 --- a/internal/media/manager_test.go +++ b/internal/media/manager_test.go @@ -664,7 +664,7 @@ func (suite *ManagerTestSuite) TestOpusProcess() { Duration: util.Ptr(float32(122.10006)), Bitrate: util.Ptr(uint64(116426)), }, attachment.FileMeta.Original) - suite.Equal("audio/ogg", attachment.File.ContentType) + suite.Equal("audio/opus", attachment.File.ContentType) suite.Equal(1776956, attachment.File.FileSize) suite.Empty(attachment.Blurhash) diff --git a/internal/media/processingemoji.go b/internal/media/processingemoji.go index 750144296..95c224cda 100644 --- a/internal/media/processingemoji.go +++ b/internal/media/processingemoji.go @@ -163,9 +163,10 @@ func (p *ProcessingEmoji) store(ctx context.Context) error { } var ext string + var fileType gtsmodel.FileType - // Get type from ffprobe format data. - fileType, ext := result.GetFileType() + // Get abstract file type, mimetype and ext from ffprobe data. + fileType, p.emoji.ImageContentType, ext = result.GetFileType() if fileType != gtsmodel.FileTypeImage { return gtserror.Newf("unsupported emoji filetype: %s (%s)", fileType, ext) } @@ -216,10 +217,6 @@ func (p *ProcessingEmoji) store(ctx context.Context) error { "png", ) - // Get mimetype for the file container - // type, falling back to generic data. - p.emoji.ImageContentType = getMimeType(ext) - // Set the known emoji static content type. p.emoji.ImageStaticContentType = "image/png" diff --git a/internal/media/processingmedia.go b/internal/media/processingmedia.go index 4c6670b06..894975a1b 100644 --- a/internal/media/processingmedia.go +++ b/internal/media/processingmedia.go @@ -186,8 +186,8 @@ func (p *ProcessingMedia) store(ctx context.Context) error { p.media.FileMeta.Original.Duration = util.PtrIf(float32(result.duration)) p.media.FileMeta.Original.Bitrate = util.PtrIf(result.bitrate) - // Set media type from ffprobe format data. - p.media.Type, ext = result.GetFileType() + // Set generic media type and mimetype from ffprobe format data. + p.media.Type, p.media.File.ContentType, ext = result.GetFileType() // Add file extension to path. newpath := temppath + "." + ext @@ -236,10 +236,10 @@ func (p *ProcessingMedia) store(ctx context.Context) error { // Determine if blurhash needs generating. needBlurhash := (p.media.Blurhash == "") - var newBlurhash string + var newBlurhash, mimeType string - // Generate thumbnail, and new blurhash if need from media. - thumbpath, newBlurhash, err = generateThumb(ctx, temppath, + // Generate thumbnail, and new blurhash if needed from temp media. + thumbpath, mimeType, newBlurhash, err = generateThumb(ctx, temppath, thumbWidth, thumbHeight, result.orientation, @@ -250,6 +250,9 @@ func (p *ProcessingMedia) store(ctx context.Context) error { return gtserror.Newf("error generating image thumb: %w", err) } + // Set generated thumbnail's mimetype. + p.media.Thumbnail.ContentType = mimeType + if needBlurhash { // Set newly determined blurhash. p.media.Blurhash = newBlurhash @@ -265,10 +268,6 @@ func (p *ProcessingMedia) store(ctx context.Context) error { ext, ) - // Get mimetype for the file container - // type, falling back to generic data. - p.media.File.ContentType = getMimeType(ext) - // Copy temporary file into storage at path. filesz, err := p.mgr.state.Storage.PutFile(ctx, p.media.File.Path, @@ -295,9 +294,6 @@ func (p *ProcessingMedia) store(ctx context.Context) error { thumbExt, ) - // Determine thumbnail content-type from thumb ext. - p.media.Thumbnail.ContentType = getMimeType(thumbExt) - // Copy thumbnail file into storage at path. thumbsz, err := p.mgr.state.Storage.PutFile(ctx, p.media.Thumbnail.Path, diff --git a/internal/media/thumbnail.go b/internal/media/thumbnail.go index 322af8d7e..f1c7c678e 100644 --- a/internal/media/thumbnail.go +++ b/internal/media/thumbnail.go @@ -84,17 +84,21 @@ func generateThumb( needBlurhash bool, ) ( outpath string, + mimeType string, blurhash string, err error, ) { var ext string + // Default type is webp. + mimeType = "image/webp" + // Generate thumb output path REPLACING extension. if i := strings.IndexByte(filepath, '.'); i != -1 { outpath = filepath[:i] + "_thumb.webp" ext = filepath[i+1:] // old extension } else { - return "", "", gtserror.New("input file missing extension") + return "", "", "", gtserror.New("input file missing extension") } // Check for the few media types we @@ -106,6 +110,7 @@ func generateThumb( // Replace the "webp" with "jpeg", as we'll // use our native Go thumbnailing generation. outpath = outpath[:len(outpath)-4] + "jpeg" + mimeType = "image/jpeg" log.Debug(ctx, "generating thumb from jpeg") blurhash, err := generateNativeThumb( @@ -117,7 +122,7 @@ func generateThumb( jpeg.Decode, needBlurhash, ) - return outpath, blurhash, err + return outpath, mimeType, blurhash, err // We specifically only allow generating native // thumbnails from gif IF it doesn't contain an @@ -128,6 +133,7 @@ func generateThumb( // Replace the "webp" with "jpeg", as we'll // use our native Go thumbnailing generation. outpath = outpath[:len(outpath)-4] + "jpeg" + mimeType = "image/jpeg" log.Debug(ctx, "generating thumb from gif") blurhash, err := generateNativeThumb( @@ -139,7 +145,7 @@ func generateThumb( gif.Decode, needBlurhash, ) - return outpath, blurhash, err + return outpath, mimeType, blurhash, err // We specifically only allow generating native // thumbnails from png IF it doesn't contain an @@ -150,6 +156,7 @@ func generateThumb( // Replace the "webp" with "jpeg", as we'll // use our native Go thumbnailing generation. outpath = outpath[:len(outpath)-4] + "jpeg" + mimeType = "image/jpeg" log.Debug(ctx, "generating thumb from png") blurhash, err := generateNativeThumb( @@ -161,7 +168,7 @@ func generateThumb( png.Decode, needBlurhash, ) - return outpath, blurhash, err + return outpath, mimeType, blurhash, err // We specifically only allow generating native // thumbnails from webp IF it doesn't contain an @@ -172,6 +179,7 @@ func generateThumb( // Replace the "webp" with "jpeg", as we'll // use our native Go thumbnailing generation. outpath = outpath[:len(outpath)-4] + "jpeg" + mimeType = "image/jpeg" log.Debug(ctx, "generating thumb from webp") blurhash, err := generateNativeThumb( @@ -183,7 +191,7 @@ func generateThumb( webp.Decode, needBlurhash, ) - return outpath, blurhash, err + return outpath, mimeType, blurhash, err } // The fallback for thumbnail generation, which @@ -196,18 +204,18 @@ func generateThumb( height, pixfmt, ); err != nil { - return outpath, "", err + return outpath, "", "", err } if needBlurhash { // Generate new blurhash from webp output thumb. blurhash, err = generateWebpBlurhash(outpath) if err != nil { - return outpath, "", gtserror.Newf("error generating blurhash: %w", err) + return outpath, "", "", gtserror.Newf("error generating blurhash: %w", err) } } - return outpath, blurhash, err + return outpath, mimeType, blurhash, nil } // generateNativeThumb generates a thumbnail diff --git a/internal/media/util.go b/internal/media/util.go index f6bf06260..4e47955ad 100644 --- a/internal/media/util.go +++ b/internal/media/util.go @@ -18,7 +18,6 @@ package media import ( - "cmp" "errors" "fmt" "io" @@ -28,7 +27,6 @@ "codeberg.org/gruf/go-bytesize" "codeberg.org/gruf/go-iotools" - "codeberg.org/gruf/go-mimetypes" "github.com/superseriousbusiness/gotosocial/internal/gtserror" ) @@ -87,12 +85,6 @@ func getExtension(path string) string { return "" } -// getMimeType returns a suitable mimetype for file extension. -func getMimeType(ext string) string { - const defaultType = "application/octet-stream" - return cmp.Or(mimetypes.MimeTypes[ext], defaultType) -} - // drainToTmp drains data from given reader into a new temp file // and closes it, returning the path of the resulting temp file. // diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index a01060ebc..cf11655ca 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -1958,7 +1958,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", @@ -2103,7 +2103,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() { "image/apng", "audio/ogg", "video/ogg", - "audio/x-m4a", + "audio/mp4", "video/mp4", "video/quicktime", "audio/x-ms-wma", diff --git a/vendor/codeberg.org/gruf/go-mimetypes/README.md b/vendor/codeberg.org/gruf/go-mimetypes/README.md deleted file mode 100644 index faf0fe4bb..000000000 --- a/vendor/codeberg.org/gruf/go-mimetypes/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# go-mimetypes - -A generated lookup map of file extensions to mimetypes, from data provided at: https://raw.githubusercontent.com/micnic/mime.json/master/index.json - -This allows determining mimetype without relying on OS mimetype lookups. \ No newline at end of file diff --git a/vendor/codeberg.org/gruf/go-mimetypes/get-mime-types.sh b/vendor/codeberg.org/gruf/go-mimetypes/get-mime-types.sh deleted file mode 100644 index 0ee088c73..000000000 --- a/vendor/codeberg.org/gruf/go-mimetypes/get-mime-types.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh - -# Mime types JSON source -URL='https://raw.githubusercontent.com/micnic/mime.json/master/index.json' - -# Define intro to file -FILE=' -// This is an automatically generated file, do not edit -package mimetypes - - -// MimeTypes is a map of file extensions to mime types. -var MimeTypes = map[string]string{ -' - -# Set break on new-line -IFS=' -' - -for line in $(curl -fL "$URL" | grep -E '".+"\s*:\s*".+"'); do - # Trim final whitespace - line=$(echo "$line" | sed -e 's|\s*$||') - - # Ensure it ends in a comma - [ "${line%,}" = "$line" ] && line="${line}," - - # Add to file - FILE="${FILE}${line} -" -done - -# Add final statement to file -FILE="${FILE} -} - -" - -# Write to file -echo "$FILE" > 'mime.gen.go' - -# Check for valid go -gofumpt -w 'mime.gen.go' \ No newline at end of file diff --git a/vendor/codeberg.org/gruf/go-mimetypes/mime.gen.go b/vendor/codeberg.org/gruf/go-mimetypes/mime.gen.go deleted file mode 100644 index 54e5b2249..000000000 --- a/vendor/codeberg.org/gruf/go-mimetypes/mime.gen.go +++ /dev/null @@ -1,1207 +0,0 @@ -// This is an automatically generated file, do not edit -package mimetypes - -// MimeTypes is a map of file extensions to mime types. -var MimeTypes = map[string]string{ - "123": "application/vnd.lotus-1-2-3", - "1km": "application/vnd.1000minds.decision-model+xml", - "3dml": "text/vnd.in3d.3dml", - "3ds": "image/x-3ds", - "3g2": "video/3gpp2", - "3gp": "video/3gpp", - "3gpp": "video/3gpp", - "3mf": "model/3mf", - "7z": "application/x-7z-compressed", - "aab": "application/x-authorware-bin", - "aac": "audio/x-aac", - "aam": "application/x-authorware-map", - "aas": "application/x-authorware-seg", - "abw": "application/x-abiword", - "ac": "application/vnd.nokia.n-gage.ac+xml", - "acc": "application/vnd.americandynamics.acc", - "ace": "application/x-ace-compressed", - "acu": "application/vnd.acucobol", - "acutc": "application/vnd.acucorp", - "adp": "audio/adpcm", - "adts": "audio/aac", - "aep": "application/vnd.audiograph", - "afm": "application/x-font-type1", - "afp": "application/vnd.ibm.modcap", - "age": "application/vnd.age", - "ahead": "application/vnd.ahead.space", - "ai": "application/postscript", - "aif": "audio/x-aiff", - "aifc": "audio/x-aiff", - "aiff": "audio/x-aiff", - "air": "application/vnd.adobe.air-application-installer-package+zip", - "ait": "application/vnd.dvb.ait", - "ami": "application/vnd.amiga.ami", - "aml": "application/automationml-aml+xml", - "amlx": "application/automationml-amlx+zip", - "amr": "audio/amr", - "apk": "application/vnd.android.package-archive", - "apng": "image/apng", - "appcache": "text/cache-manifest", - "appinstaller": "application/appinstaller", - "application": "application/x-ms-application", - "appx": "application/appx", - "appxbundle": "application/appxbundle", - "apr": "application/vnd.lotus-approach", - "arc": "application/x-freearc", - "arj": "application/x-arj", - "asc": "application/pgp-signature", - "asf": "video/x-ms-asf", - "asm": "text/x-asm", - "aso": "application/vnd.accpac.simply.aso", - "asx": "video/x-ms-asf", - "atc": "application/vnd.acucorp", - "atom": "application/atom+xml", - "atomcat": "application/atomcat+xml", - "atomdeleted": "application/atomdeleted+xml", - "atomsvc": "application/atomsvc+xml", - "atx": "application/vnd.antix.game-component", - "au": "audio/basic", - "avci": "image/avci", - "avcs": "image/avcs", - "avi": "video/x-msvideo", - "avif": "image/avif", - "aw": "application/applixware", - "azf": "application/vnd.airzip.filesecure.azf", - "azs": "application/vnd.airzip.filesecure.azs", - "azv": "image/vnd.airzip.accelerator.azv", - "azw": "application/vnd.amazon.ebook", - "b16": "image/vnd.pco.b16", - "bat": "application/x-msdownload", - "bcpio": "application/x-bcpio", - "bdf": "application/x-font-bdf", - "bdm": "application/vnd.syncml.dm+wbxml", - "bdoc": "application/x-bdoc", - "bed": "application/vnd.realvnc.bed", - "bh2": "application/vnd.fujitsu.oasysprs", - "bin": "application/octet-stream", - "blb": "application/x-blorb", - "blorb": "application/x-blorb", - "bmi": "application/vnd.bmi", - "bmml": "application/vnd.balsamiq.bmml+xml", - "bmp": "image/x-ms-bmp", - "book": "application/vnd.framemaker", - "box": "application/vnd.previewsystems.box", - "boz": "application/x-bzip2", - "bpk": "application/octet-stream", - "bsp": "model/vnd.valve.source.compiled-map", - "btf": "image/prs.btif", - "btif": "image/prs.btif", - "buffer": "application/octet-stream", - "bz": "application/x-bzip", - "bz2": "application/x-bzip2", - "c": "text/x-c", - "c11amc": "application/vnd.cluetrust.cartomobile-config", - "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg", - "c4d": "application/vnd.clonk.c4group", - "c4f": "application/vnd.clonk.c4group", - "c4g": "application/vnd.clonk.c4group", - "c4p": "application/vnd.clonk.c4group", - "c4u": "application/vnd.clonk.c4group", - "cab": "application/vnd.ms-cab-compressed", - "caf": "audio/x-caf", - "cap": "application/vnd.tcpdump.pcap", - "car": "application/vnd.curl.car", - "cat": "application/vnd.ms-pki.seccat", - "cb7": "application/x-cbr", - "cba": "application/x-cbr", - "cbr": "application/x-cbr", - "cbt": "application/x-cbr", - "cbz": "application/x-cbr", - "cc": "text/x-c", - "cco": "application/x-cocoa", - "cct": "application/x-director", - "ccxml": "application/ccxml+xml", - "cdbcmsg": "application/vnd.contact.cmsg", - "cdf": "application/x-netcdf", - "cdfx": "application/cdfx+xml", - "cdkey": "application/vnd.mediastation.cdkey", - "cdmia": "application/cdmi-capability", - "cdmic": "application/cdmi-container", - "cdmid": "application/cdmi-domain", - "cdmio": "application/cdmi-object", - "cdmiq": "application/cdmi-queue", - "cdx": "chemical/x-cdx", - "cdxml": "application/vnd.chemdraw+xml", - "cdy": "application/vnd.cinderella", - "cer": "application/pkix-cert", - "cfs": "application/x-cfs-compressed", - "cgm": "image/cgm", - "chat": "application/x-chat", - "chm": "application/vnd.ms-htmlhelp", - "chrt": "application/vnd.kde.kchart", - "cif": "chemical/x-cif", - "cii": "application/vnd.anser-web-certificate-issue-initiation", - "cil": "application/vnd.ms-artgalry", - "cjs": "application/node", - "cla": "application/vnd.claymore", - "class": "application/java-vm", - "cld": "model/vnd.cld", - "clkk": "application/vnd.crick.clicker.keyboard", - "clkp": "application/vnd.crick.clicker.palette", - "clkt": "application/vnd.crick.clicker.template", - "clkw": "application/vnd.crick.clicker.wordbank", - "clkx": "application/vnd.crick.clicker", - "clp": "application/x-msclip", - "cmc": "application/vnd.cosmocaller", - "cmdf": "chemical/x-cmdf", - "cml": "chemical/x-cml", - "cmp": "application/vnd.yellowriver-custom-menu", - "cmx": "image/x-cmx", - "cod": "application/vnd.rim.cod", - "coffee": "text/coffeescript", - "com": "application/x-msdownload", - "conf": "text/plain", - "cpio": "application/x-cpio", - "cpl": "application/cpl+xml", - "cpp": "text/x-c", - "cpt": "application/mac-compactpro", - "crd": "application/x-mscardfile", - "crl": "application/pkix-crl", - "crt": "application/x-x509-ca-cert", - "crx": "application/x-chrome-extension", - "cryptonote": "application/vnd.rig.cryptonote", - "csh": "application/x-csh", - "csl": "application/vnd.citationstyles.style+xml", - "csml": "chemical/x-csml", - "csp": "application/vnd.commonspace", - "css": "text/css", - "cst": "application/x-director", - "csv": "text/csv", - "cu": "application/cu-seeme", - "curl": "text/vnd.curl", - "cwl": "application/cwl", - "cww": "application/prs.cww", - "cxt": "application/x-director", - "cxx": "text/x-c", - "dae": "model/vnd.collada+xml", - "daf": "application/vnd.mobius.daf", - "dart": "application/vnd.dart", - "dataless": "application/vnd.fdsn.seed", - "davmount": "application/davmount+xml", - "dbf": "application/vnd.dbf", - "dbk": "application/docbook+xml", - "dcr": "application/x-director", - "dcurl": "text/vnd.curl.dcurl", - "dd2": "application/vnd.oma.dd2+xml", - "ddd": "application/vnd.fujixerox.ddd", - "ddf": "application/vnd.syncml.dmddf+xml", - "dds": "image/vnd.ms-dds", - "deb": "application/x-debian-package", - "def": "text/plain", - "deploy": "application/octet-stream", - "der": "application/x-x509-ca-cert", - "dfac": "application/vnd.dreamfactory", - "dgc": "application/x-dgc-compressed", - "dib": "image/bmp", - "dic": "text/x-c", - "dir": "application/x-director", - "dis": "application/vnd.mobius.dis", - "disposition-notification": "message/disposition-notification", - "dist": "application/octet-stream", - "distz": "application/octet-stream", - "djv": "image/vnd.djvu", - "djvu": "image/vnd.djvu", - "dll": "application/x-msdownload", - "dmg": "application/x-apple-diskimage", - "dmp": "application/vnd.tcpdump.pcap", - "dms": "application/octet-stream", - "dna": "application/vnd.dna", - "doc": "application/msword", - "docm": "application/vnd.ms-word.document.macroenabled.12", - "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "dot": "application/msword", - "dotm": "application/vnd.ms-word.template.macroenabled.12", - "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", - "dp": "application/vnd.osgi.dp", - "dpg": "application/vnd.dpgraph", - "dpx": "image/dpx", - "dra": "audio/vnd.dra", - "drle": "image/dicom-rle", - "dsc": "text/prs.lines.tag", - "dssc": "application/dssc+der", - "dtb": "application/x-dtbook+xml", - "dtd": "application/xml-dtd", - "dts": "audio/vnd.dts", - "dtshd": "audio/vnd.dts.hd", - "dump": "application/octet-stream", - "dvb": "video/vnd.dvb.file", - "dvi": "application/x-dvi", - "dwd": "application/atsc-dwd+xml", - "dwf": "model/vnd.dwf", - "dwg": "image/vnd.dwg", - "dxf": "image/vnd.dxf", - "dxp": "application/vnd.spotfire.dxp", - "dxr": "application/x-director", - "ear": "application/java-archive", - "ecelp4800": "audio/vnd.nuera.ecelp4800", - "ecelp7470": "audio/vnd.nuera.ecelp7470", - "ecelp9600": "audio/vnd.nuera.ecelp9600", - "ecma": "application/ecmascript", - "edm": "application/vnd.novadigm.edm", - "edx": "application/vnd.novadigm.edx", - "efif": "application/vnd.picsel", - "ei6": "application/vnd.pg.osasli", - "elc": "application/octet-stream", - "emf": "image/emf", - "eml": "message/rfc822", - "emma": "application/emma+xml", - "emotionml": "application/emotionml+xml", - "emz": "application/x-msmetafile", - "eol": "audio/vnd.digital-winds", - "eot": "application/vnd.ms-fontobject", - "eps": "application/postscript", - "epub": "application/epub+zip", - "es3": "application/vnd.eszigno3+xml", - "esa": "application/vnd.osgi.subsystem", - "esf": "application/vnd.epson.esf", - "et3": "application/vnd.eszigno3+xml", - "etx": "text/x-setext", - "eva": "application/x-eva", - "evy": "application/x-envoy", - "exe": "application/x-msdownload", - "exi": "application/exi", - "exp": "application/express", - "exr": "image/aces", - "ext": "application/vnd.novadigm.ext", - "ez": "application/andrew-inset", - "ez2": "application/vnd.ezpix-album", - "ez3": "application/vnd.ezpix-package", - "f": "text/x-fortran", - "f4v": "video/x-f4v", - "f77": "text/x-fortran", - "f90": "text/x-fortran", - "fbs": "image/vnd.fastbidsheet", - "fcdt": "application/vnd.adobe.formscentral.fcdt", - "fcs": "application/vnd.isac.fcs", - "fdf": "application/vnd.fdf", - "fdt": "application/fdt+xml", - "fe_launch": "application/vnd.denovo.fcselayout-link", - "fg5": "application/vnd.fujitsu.oasysgp", - "fgd": "application/x-director", - "fh": "image/x-freehand", - "fh4": "image/x-freehand", - "fh5": "image/x-freehand", - "fh7": "image/x-freehand", - "fhc": "image/x-freehand", - "fig": "application/x-xfig", - "fits": "image/fits", - "flac": "audio/x-flac", - "fli": "video/x-fli", - "flo": "application/vnd.micrografx.flo", - "flv": "video/x-flv", - "flw": "application/vnd.kde.kivio", - "flx": "text/vnd.fmi.flexstor", - "fly": "text/vnd.fly", - "fm": "application/vnd.framemaker", - "fnc": "application/vnd.frogans.fnc", - "fo": "application/vnd.software602.filler.form+xml", - "for": "text/x-fortran", - "fpx": "image/vnd.fpx", - "frame": "application/vnd.framemaker", - "fsc": "application/vnd.fsc.weblaunch", - "fst": "image/vnd.fst", - "ftc": "application/vnd.fluxtime.clip", - "fti": "application/vnd.anser-web-funds-transfer-initiation", - "fvt": "video/vnd.fvt", - "fxp": "application/vnd.adobe.fxp", - "fxpl": "application/vnd.adobe.fxp", - "fzs": "application/vnd.fuzzysheet", - "g2w": "application/vnd.geoplan", - "g3": "image/g3fax", - "g3w": "application/vnd.geospace", - "gac": "application/vnd.groove-account", - "gam": "application/x-tads", - "gbr": "application/rpki-ghostbusters", - "gca": "application/x-gca-compressed", - "gdl": "model/vnd.gdl", - "gdoc": "application/vnd.google-apps.document", - "ged": "text/vnd.familysearch.gedcom", - "geo": "application/vnd.dynageo", - "geojson": "application/geo+json", - "gex": "application/vnd.geometry-explorer", - "ggb": "application/vnd.geogebra.file", - "ggt": "application/vnd.geogebra.tool", - "ghf": "application/vnd.groove-help", - "gif": "image/gif", - "gim": "application/vnd.groove-identity-message", - "glb": "model/gltf-binary", - "gltf": "model/gltf+json", - "gml": "application/gml+xml", - "gmx": "application/vnd.gmx", - "gnumeric": "application/x-gnumeric", - "gph": "application/vnd.flographit", - "gpx": "application/gpx+xml", - "gqf": "application/vnd.grafeq", - "gqs": "application/vnd.grafeq", - "gram": "application/srgs", - "gramps": "application/x-gramps-xml", - "gre": "application/vnd.geometry-explorer", - "grv": "application/vnd.groove-injector", - "grxml": "application/srgs+xml", - "gsf": "application/x-font-ghostscript", - "gsheet": "application/vnd.google-apps.spreadsheet", - "gslides": "application/vnd.google-apps.presentation", - "gtar": "application/x-gtar", - "gtm": "application/vnd.groove-tool-message", - "gtw": "model/vnd.gtw", - "gv": "text/vnd.graphviz", - "gxf": "application/gxf", - "gxt": "application/vnd.geonext", - "gz": "application/gzip", - "h": "text/x-c", - "h261": "video/h261", - "h263": "video/h263", - "h264": "video/h264", - "hal": "application/vnd.hal+xml", - "hbci": "application/vnd.hbci", - "hbs": "text/x-handlebars-template", - "hdd": "application/x-virtualbox-hdd", - "hdf": "application/x-hdf", - "heic": "image/heic", - "heics": "image/heic-sequence", - "heif": "image/heif", - "heifs": "image/heif-sequence", - "hej2": "image/hej2k", - "held": "application/atsc-held+xml", - "hh": "text/x-c", - "hjson": "application/hjson", - "hlp": "application/winhlp", - "hpgl": "application/vnd.hp-hpgl", - "hpid": "application/vnd.hp-hpid", - "hps": "application/vnd.hp-hps", - "hqx": "application/mac-binhex40", - "hsj2": "image/hsj2", - "htc": "text/x-component", - "htke": "application/vnd.kenameaapp", - "htm": "text/html", - "html": "text/html", - "hvd": "application/vnd.yamaha.hv-dic", - "hvp": "application/vnd.yamaha.hv-voice", - "hvs": "application/vnd.yamaha.hv-script", - "i2g": "application/vnd.intergeo", - "icc": "application/vnd.iccprofile", - "ice": "x-conference/x-cooltalk", - "icm": "application/vnd.iccprofile", - "ico": "image/x-icon", - "ics": "text/calendar", - "ief": "image/ief", - "ifb": "text/calendar", - "ifm": "application/vnd.shana.informed.formdata", - "iges": "model/iges", - "igl": "application/vnd.igloader", - "igm": "application/vnd.insors.igm", - "igs": "model/iges", - "igx": "application/vnd.micrografx.igx", - "iif": "application/vnd.shana.informed.interchange", - "img": "application/octet-stream", - "imp": "application/vnd.accpac.simply.imp", - "ims": "application/vnd.ms-ims", - "in": "text/plain", - "ini": "text/plain", - "ink": "application/inkml+xml", - "inkml": "application/inkml+xml", - "install": "application/x-install-instructions", - "iota": "application/vnd.astraea-software.iota", - "ipfix": "application/ipfix", - "ipk": "application/vnd.shana.informed.package", - "irm": "application/vnd.ibm.rights-management", - "irp": "application/vnd.irepository.package+xml", - "iso": "application/x-iso9660-image", - "itp": "application/vnd.shana.informed.formtemplate", - "its": "application/its+xml", - "ivp": "application/vnd.immervision-ivp", - "ivu": "application/vnd.immervision-ivu", - "jad": "text/vnd.sun.j2me.app-descriptor", - "jade": "text/jade", - "jam": "application/vnd.jam", - "jar": "application/java-archive", - "jardiff": "application/x-java-archive-diff", - "java": "text/x-java-source", - "jhc": "image/jphc", - "jisp": "application/vnd.jisp", - "jls": "image/jls", - "jlt": "application/vnd.hp-jlyt", - "jng": "image/x-jng", - "jnlp": "application/x-java-jnlp-file", - "joda": "application/vnd.joost.joda-archive", - "jp2": "image/jp2", - "jpe": "image/jpeg", - "jpeg": "image/jpeg", - "jpf": "image/jpx", - "jpg": "image/jpeg", - "jpg2": "image/jp2", - "jpgm": "video/jpm", - "jpgv": "video/jpeg", - "jph": "image/jph", - "jpm": "video/jpm", - "jpx": "image/jpx", - "js": "text/javascript", - "json": "application/json", - "json5": "application/json5", - "jsonld": "application/ld+json", - "jsonml": "application/jsonml+json", - "jsx": "text/jsx", - "jt": "model/jt", - "jxr": "image/jxr", - "jxra": "image/jxra", - "jxrs": "image/jxrs", - "jxs": "image/jxs", - "jxsc": "image/jxsc", - "jxsi": "image/jxsi", - "jxss": "image/jxss", - "kar": "audio/midi", - "karbon": "application/vnd.kde.karbon", - "kdbx": "application/x-keepass2", - "key": "application/x-iwork-keynote-sffkey", - "kfo": "application/vnd.kde.kformula", - "kia": "application/vnd.kidspiration", - "kml": "application/vnd.google-earth.kml+xml", - "kmz": "application/vnd.google-earth.kmz", - "kne": "application/vnd.kinar", - "knp": "application/vnd.kinar", - "kon": "application/vnd.kde.kontour", - "kpr": "application/vnd.kde.kpresenter", - "kpt": "application/vnd.kde.kpresenter", - "kpxx": "application/vnd.ds-keypoint", - "ksp": "application/vnd.kde.kspread", - "ktr": "application/vnd.kahootz", - "ktx": "image/ktx", - "ktx2": "image/ktx2", - "ktz": "application/vnd.kahootz", - "kwd": "application/vnd.kde.kword", - "kwt": "application/vnd.kde.kword", - "lasxml": "application/vnd.las.las+xml", - "latex": "application/x-latex", - "lbd": "application/vnd.llamagraphics.life-balance.desktop", - "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml", - "les": "application/vnd.hhe.lesson-player", - "less": "text/less", - "lgr": "application/lgr+xml", - "lha": "application/x-lzh-compressed", - "link66": "application/vnd.route66.link66+xml", - "list": "text/plain", - "list3820": "application/vnd.ibm.modcap", - "listafp": "application/vnd.ibm.modcap", - "litcoffee": "text/coffeescript", - "lnk": "application/x-ms-shortcut", - "log": "text/plain", - "lostxml": "application/lost+xml", - "lrf": "application/octet-stream", - "lrm": "application/vnd.ms-lrm", - "ltf": "application/vnd.frogans.ltf", - "lua": "text/x-lua", - "luac": "application/x-lua-bytecode", - "lvp": "audio/vnd.lucent.voice", - "lwp": "application/vnd.lotus-wordpro", - "lzh": "application/x-lzh-compressed", - "m13": "application/x-msmediaview", - "m14": "application/x-msmediaview", - "m1v": "video/mpeg", - "m21": "application/mp21", - "m2a": "audio/mpeg", - "m2v": "video/mpeg", - "m3a": "audio/mpeg", - "m3u": "audio/x-mpegurl", - "m3u8": "application/vnd.apple.mpegurl", - "m4a": "audio/x-m4a", - "m4p": "application/mp4", - "m4s": "video/iso.segment", - "m4u": "video/vnd.mpegurl", - "m4v": "video/x-m4v", - "ma": "application/mathematica", - "mads": "application/mads+xml", - "maei": "application/mmt-aei+xml", - "mag": "application/vnd.ecowin.chart", - "maker": "application/vnd.framemaker", - "man": "text/troff", - "manifest": "text/cache-manifest", - "map": "application/json", - "mar": "application/octet-stream", - "markdown": "text/markdown", - "mathml": "application/mathml+xml", - "mb": "application/mathematica", - "mbk": "application/vnd.mobius.mbk", - "mbox": "application/mbox", - "mc1": "application/vnd.medcalcdata", - "mcd": "application/vnd.mcd", - "mcurl": "text/vnd.curl.mcurl", - "md": "text/markdown", - "mdb": "application/x-msaccess", - "mdi": "image/vnd.ms-modi", - "mdx": "text/mdx", - "me": "text/troff", - "mesh": "model/mesh", - "meta4": "application/metalink4+xml", - "metalink": "application/metalink+xml", - "mets": "application/mets+xml", - "mfm": "application/vnd.mfmp", - "mft": "application/rpki-manifest", - "mgp": "application/vnd.osgeo.mapguide.package", - "mgz": "application/vnd.proteus.magazine", - "mid": "audio/midi", - "midi": "audio/midi", - "mie": "application/x-mie", - "mif": "application/vnd.mif", - "mime": "message/rfc822", - "mj2": "video/mj2", - "mjp2": "video/mj2", - "mjs": "text/javascript", - "mk3d": "video/x-matroska", - "mka": "audio/x-matroska", - "mkd": "text/x-markdown", - "mks": "video/x-matroska", - "mkv": "video/x-matroska", - "mlp": "application/vnd.dolby.mlp", - "mmd": "application/vnd.chipnuts.karaoke-mmd", - "mmf": "application/vnd.smaf", - "mml": "text/mathml", - "mmr": "image/vnd.fujixerox.edmics-mmr", - "mng": "video/x-mng", - "mny": "application/x-msmoney", - "mobi": "application/x-mobipocket-ebook", - "mods": "application/mods+xml", - "mov": "video/quicktime", - "movie": "video/x-sgi-movie", - "mp2": "audio/mpeg", - "mp21": "application/mp21", - "mp2a": "audio/mpeg", - "mp3": "audio/mpeg", - "mp4": "video/mp4", - "mp4a": "audio/mp4", - "mp4s": "application/mp4", - "mp4v": "video/mp4", - "mpc": "application/vnd.mophun.certificate", - "mpd": "application/dash+xml", - "mpe": "video/mpeg", - "mpeg": "video/mpeg", - "mpf": "application/media-policy-dataset+xml", - "mpg": "video/mpeg", - "mpg4": "video/mp4", - "mpga": "audio/mpeg", - "mpkg": "application/vnd.apple.installer+xml", - "mpm": "application/vnd.blueice.multipass", - "mpn": "application/vnd.mophun.application", - "mpp": "application/vnd.ms-project", - "mpt": "application/vnd.ms-project", - "mpy": "application/vnd.ibm.minipay", - "mqy": "application/vnd.mobius.mqy", - "mrc": "application/marc", - "mrcx": "application/marcxml+xml", - "ms": "text/troff", - "mscml": "application/mediaservercontrol+xml", - "mseed": "application/vnd.fdsn.mseed", - "mseq": "application/vnd.mseq", - "msf": "application/vnd.epson.msf", - "msg": "application/vnd.ms-outlook", - "msh": "model/mesh", - "msi": "application/x-msdownload", - "msix": "application/msix", - "msixbundle": "application/msixbundle", - "msl": "application/vnd.mobius.msl", - "msm": "application/octet-stream", - "msp": "application/octet-stream", - "msty": "application/vnd.muvee.style", - "mtl": "model/mtl", - "mts": "model/vnd.mts", - "mus": "application/vnd.musician", - "musd": "application/mmt-usd+xml", - "musicxml": "application/vnd.recordare.musicxml+xml", - "mvb": "application/x-msmediaview", - "mvt": "application/vnd.mapbox-vector-tile", - "mwf": "application/vnd.mfer", - "mxf": "application/mxf", - "mxl": "application/vnd.recordare.musicxml", - "mxmf": "audio/mobile-xmf", - "mxml": "application/xv+xml", - "mxs": "application/vnd.triscape.mxs", - "mxu": "video/vnd.mpegurl", - "n-gage": "application/vnd.nokia.n-gage.symbian.install", - "n3": "text/n3", - "nb": "application/mathematica", - "nbp": "application/vnd.wolfram.player", - "nc": "application/x-netcdf", - "ncx": "application/x-dtbncx+xml", - "nfo": "text/x-nfo", - "ngdat": "application/vnd.nokia.n-gage.data", - "nitf": "application/vnd.nitf", - "nlu": "application/vnd.neurolanguage.nlu", - "nml": "application/vnd.enliven", - "nnd": "application/vnd.noblenet-directory", - "nns": "application/vnd.noblenet-sealer", - "nnw": "application/vnd.noblenet-web", - "npx": "image/vnd.net-fpx", - "nq": "application/n-quads", - "nsc": "application/x-conference", - "nsf": "application/vnd.lotus-notes", - "nt": "application/n-triples", - "ntf": "application/vnd.nitf", - "numbers": "application/x-iwork-numbers-sffnumbers", - "nzb": "application/x-nzb", - "oa2": "application/vnd.fujitsu.oasys2", - "oa3": "application/vnd.fujitsu.oasys3", - "oas": "application/vnd.fujitsu.oasys", - "obd": "application/x-msbinder", - "obgx": "application/vnd.openblox.game+xml", - "obj": "model/obj", - "oda": "application/oda", - "odb": "application/vnd.oasis.opendocument.database", - "odc": "application/vnd.oasis.opendocument.chart", - "odf": "application/vnd.oasis.opendocument.formula", - "odft": "application/vnd.oasis.opendocument.formula-template", - "odg": "application/vnd.oasis.opendocument.graphics", - "odi": "application/vnd.oasis.opendocument.image", - "odm": "application/vnd.oasis.opendocument.text-master", - "odp": "application/vnd.oasis.opendocument.presentation", - "ods": "application/vnd.oasis.opendocument.spreadsheet", - "odt": "application/vnd.oasis.opendocument.text", - "oga": "audio/ogg", - "ogex": "model/vnd.opengex", - "ogg": "audio/ogg", - "ogv": "video/ogg", - "ogx": "application/ogg", - "omdoc": "application/omdoc+xml", - "onepkg": "application/onenote", - "onetmp": "application/onenote", - "onetoc": "application/onenote", - "onetoc2": "application/onenote", - "opf": "application/oebps-package+xml", - "opml": "text/x-opml", - "oprc": "application/vnd.palm", - "opus": "audio/ogg", - "org": "text/x-org", - "osf": "application/vnd.yamaha.openscoreformat", - "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml", - "osm": "application/vnd.openstreetmap.data+xml", - "otc": "application/vnd.oasis.opendocument.chart-template", - "otf": "font/otf", - "otg": "application/vnd.oasis.opendocument.graphics-template", - "oth": "application/vnd.oasis.opendocument.text-web", - "oti": "application/vnd.oasis.opendocument.image-template", - "otp": "application/vnd.oasis.opendocument.presentation-template", - "ots": "application/vnd.oasis.opendocument.spreadsheet-template", - "ott": "application/vnd.oasis.opendocument.text-template", - "ova": "application/x-virtualbox-ova", - "ovf": "application/x-virtualbox-ovf", - "owl": "application/rdf+xml", - "oxps": "application/oxps", - "oxt": "application/vnd.openofficeorg.extension", - "p": "text/x-pascal", - "p10": "application/pkcs10", - "p12": "application/x-pkcs12", - "p7b": "application/x-pkcs7-certificates", - "p7c": "application/pkcs7-mime", - "p7m": "application/pkcs7-mime", - "p7r": "application/x-pkcs7-certreqresp", - "p7s": "application/pkcs7-signature", - "p8": "application/pkcs8", - "pac": "application/x-ns-proxy-autoconfig", - "pages": "application/x-iwork-pages-sffpages", - "pas": "text/x-pascal", - "paw": "application/vnd.pawaafile", - "pbd": "application/vnd.powerbuilder6", - "pbm": "image/x-portable-bitmap", - "pcap": "application/vnd.tcpdump.pcap", - "pcf": "application/x-font-pcf", - "pcl": "application/vnd.hp-pcl", - "pclxl": "application/vnd.hp-pclxl", - "pct": "image/x-pict", - "pcurl": "application/vnd.curl.pcurl", - "pcx": "image/x-pcx", - "pdb": "application/x-pilot", - "pde": "text/x-processing", - "pdf": "application/pdf", - "pem": "application/x-x509-ca-cert", - "pfa": "application/x-font-type1", - "pfb": "application/x-font-type1", - "pfm": "application/x-font-type1", - "pfr": "application/font-tdpfr", - "pfx": "application/x-pkcs12", - "pgm": "image/x-portable-graymap", - "pgn": "application/x-chess-pgn", - "pgp": "application/pgp-encrypted", - "php": "application/x-httpd-php", - "pic": "image/x-pict", - "pkg": "application/octet-stream", - "pki": "application/pkixcmp", - "pkipath": "application/pkix-pkipath", - "pkpass": "application/vnd.apple.pkpass", - "pl": "application/x-perl", - "plb": "application/vnd.3gpp.pic-bw-large", - "plc": "application/vnd.mobius.plc", - "plf": "application/vnd.pocketlearn", - "pls": "application/pls+xml", - "pm": "application/x-perl", - "pml": "application/vnd.ctc-posml", - "png": "image/png", - "pnm": "image/x-portable-anymap", - "portpkg": "application/vnd.macports.portpkg", - "pot": "application/vnd.ms-powerpoint", - "potm": "application/vnd.ms-powerpoint.template.macroenabled.12", - "potx": "application/vnd.openxmlformats-officedocument.presentationml.template", - "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12", - "ppd": "application/vnd.cups-ppd", - "ppm": "image/x-portable-pixmap", - "pps": "application/vnd.ms-powerpoint", - "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12", - "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", - "ppt": "application/vnd.ms-powerpoint", - "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12", - "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "pqa": "application/vnd.palm", - "prc": "model/prc", - "pre": "application/vnd.lotus-freelance", - "prf": "application/pics-rules", - "provx": "application/provenance+xml", - "ps": "application/postscript", - "psb": "application/vnd.3gpp.pic-bw-small", - "psd": "image/vnd.adobe.photoshop", - "psf": "application/x-font-linux-psf", - "pskcxml": "application/pskc+xml", - "pti": "image/prs.pti", - "ptid": "application/vnd.pvi.ptid1", - "pub": "application/x-mspublisher", - "pvb": "application/vnd.3gpp.pic-bw-var", - "pwn": "application/vnd.3m.post-it-notes", - "pya": "audio/vnd.ms-playready.media.pya", - "pyo": "model/vnd.pytha.pyox", - "pyox": "model/vnd.pytha.pyox", - "pyv": "video/vnd.ms-playready.media.pyv", - "qam": "application/vnd.epson.quickanime", - "qbo": "application/vnd.intu.qbo", - "qfx": "application/vnd.intu.qfx", - "qps": "application/vnd.publishare-delta-tree", - "qt": "video/quicktime", - "qwd": "application/vnd.quark.quarkxpress", - "qwt": "application/vnd.quark.quarkxpress", - "qxb": "application/vnd.quark.quarkxpress", - "qxd": "application/vnd.quark.quarkxpress", - "qxl": "application/vnd.quark.quarkxpress", - "qxt": "application/vnd.quark.quarkxpress", - "ra": "audio/x-realaudio", - "ram": "audio/x-pn-realaudio", - "raml": "application/raml+yaml", - "rapd": "application/route-apd+xml", - "rar": "application/x-rar-compressed", - "ras": "image/x-cmu-raster", - "rcprofile": "application/vnd.ipunplugged.rcprofile", - "rdf": "application/rdf+xml", - "rdz": "application/vnd.data-vision.rdz", - "relo": "application/p2p-overlay+xml", - "rep": "application/vnd.businessobjects", - "res": "application/x-dtbresource+xml", - "rgb": "image/x-rgb", - "rif": "application/reginfo+xml", - "rip": "audio/vnd.rip", - "ris": "application/x-research-info-systems", - "rl": "application/resource-lists+xml", - "rlc": "image/vnd.fujixerox.edmics-rlc", - "rld": "application/resource-lists-diff+xml", - "rm": "application/vnd.rn-realmedia", - "rmi": "audio/midi", - "rmp": "audio/x-pn-realaudio-plugin", - "rms": "application/vnd.jcp.javame.midlet-rms", - "rmvb": "application/vnd.rn-realmedia-vbr", - "rnc": "application/relax-ng-compact-syntax", - "rng": "application/xml", - "roa": "application/rpki-roa", - "roff": "text/troff", - "rp9": "application/vnd.cloanto.rp9", - "rpm": "application/x-redhat-package-manager", - "rpss": "application/vnd.nokia.radio-presets", - "rpst": "application/vnd.nokia.radio-preset", - "rq": "application/sparql-query", - "rs": "application/rls-services+xml", - "rsat": "application/atsc-rsat+xml", - "rsd": "application/rsd+xml", - "rsheet": "application/urc-ressheet+xml", - "rss": "application/rss+xml", - "rtf": "text/rtf", - "rtx": "text/richtext", - "run": "application/x-makeself", - "rusd": "application/route-usd+xml", - "s": "text/x-asm", - "s3m": "audio/s3m", - "saf": "application/vnd.yamaha.smaf-audio", - "sass": "text/x-sass", - "sbml": "application/sbml+xml", - "sc": "application/vnd.ibm.secure-container", - "scd": "application/x-msschedule", - "scm": "application/vnd.lotus-screencam", - "scq": "application/scvp-cv-request", - "scs": "application/scvp-cv-response", - "scss": "text/x-scss", - "scurl": "text/vnd.curl.scurl", - "sda": "application/vnd.stardivision.draw", - "sdc": "application/vnd.stardivision.calc", - "sdd": "application/vnd.stardivision.impress", - "sdkd": "application/vnd.solent.sdkm+xml", - "sdkm": "application/vnd.solent.sdkm+xml", - "sdp": "application/sdp", - "sdw": "application/vnd.stardivision.writer", - "sea": "application/x-sea", - "see": "application/vnd.seemail", - "seed": "application/vnd.fdsn.seed", - "sema": "application/vnd.sema", - "semd": "application/vnd.semd", - "semf": "application/vnd.semf", - "senmlx": "application/senml+xml", - "sensmlx": "application/sensml+xml", - "ser": "application/java-serialized-object", - "setpay": "application/set-payment-initiation", - "setreg": "application/set-registration-initiation", - "sfd-hdstx": "application/vnd.hydrostatix.sof-data", - "sfs": "application/vnd.spotfire.sfs", - "sfv": "text/x-sfv", - "sgi": "image/sgi", - "sgl": "application/vnd.stardivision.writer-global", - "sgm": "text/sgml", - "sgml": "text/sgml", - "sh": "application/x-sh", - "shar": "application/x-shar", - "shex": "text/shex", - "shf": "application/shf+xml", - "shtml": "text/html", - "sid": "image/x-mrsid-image", - "sieve": "application/sieve", - "sig": "application/pgp-signature", - "sil": "audio/silk", - "silo": "model/mesh", - "sis": "application/vnd.symbian.install", - "sisx": "application/vnd.symbian.install", - "sit": "application/x-stuffit", - "sitx": "application/x-stuffitx", - "siv": "application/sieve", - "skd": "application/vnd.koan", - "skm": "application/vnd.koan", - "skp": "application/vnd.koan", - "skt": "application/vnd.koan", - "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12", - "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", - "slim": "text/slim", - "slm": "text/slim", - "sls": "application/route-s-tsid+xml", - "slt": "application/vnd.epson.salt", - "sm": "application/vnd.stepmania.stepchart", - "smf": "application/vnd.stardivision.math", - "smi": "application/smil+xml", - "smil": "application/smil+xml", - "smv": "video/x-smv", - "smzip": "application/vnd.stepmania.package", - "snd": "audio/basic", - "snf": "application/x-font-snf", - "so": "application/octet-stream", - "spc": "application/x-pkcs7-certificates", - "spdx": "text/spdx", - "spf": "application/vnd.yamaha.smaf-phrase", - "spl": "application/x-futuresplash", - "spot": "text/vnd.in3d.spot", - "spp": "application/scvp-vp-response", - "spq": "application/scvp-vp-request", - "spx": "audio/ogg", - "sql": "application/x-sql", - "src": "application/x-wais-source", - "srt": "application/x-subrip", - "sru": "application/sru+xml", - "srx": "application/sparql-results+xml", - "ssdl": "application/ssdl+xml", - "sse": "application/vnd.kodak-descriptor", - "ssf": "application/vnd.epson.ssf", - "ssml": "application/ssml+xml", - "st": "application/vnd.sailingtracker.track", - "stc": "application/vnd.sun.xml.calc.template", - "std": "application/vnd.sun.xml.draw.template", - "stf": "application/vnd.wt.stf", - "sti": "application/vnd.sun.xml.impress.template", - "stk": "application/hyperstudio", - "stl": "model/stl", - "stpx": "model/step+xml", - "stpxz": "model/step-xml+zip", - "stpz": "model/step+zip", - "str": "application/vnd.pg.format", - "stw": "application/vnd.sun.xml.writer.template", - "styl": "text/stylus", - "stylus": "text/stylus", - "sub": "text/vnd.dvb.subtitle", - "sus": "application/vnd.sus-calendar", - "susp": "application/vnd.sus-calendar", - "sv4cpio": "application/x-sv4cpio", - "sv4crc": "application/x-sv4crc", - "svc": "application/vnd.dvb.service", - "svd": "application/vnd.svd", - "svg": "image/svg+xml", - "svgz": "image/svg+xml", - "swa": "application/x-director", - "swf": "application/x-shockwave-flash", - "swi": "application/vnd.aristanetworks.swi", - "swidtag": "application/swid+xml", - "sxc": "application/vnd.sun.xml.calc", - "sxd": "application/vnd.sun.xml.draw", - "sxg": "application/vnd.sun.xml.writer.global", - "sxi": "application/vnd.sun.xml.impress", - "sxm": "application/vnd.sun.xml.math", - "sxw": "application/vnd.sun.xml.writer", - "t": "text/troff", - "t3": "application/x-t3vm-image", - "t38": "image/t38", - "taglet": "application/vnd.mynfc", - "tao": "application/vnd.tao.intent-module-archive", - "tap": "image/vnd.tencent.tap", - "tar": "application/x-tar", - "tcap": "application/vnd.3gpp2.tcap", - "tcl": "application/x-tcl", - "td": "application/urc-targetdesc+xml", - "teacher": "application/vnd.smart.teacher", - "tei": "application/tei+xml", - "teicorpus": "application/tei+xml", - "tex": "application/x-tex", - "texi": "application/x-texinfo", - "texinfo": "application/x-texinfo", - "text": "text/plain", - "tfi": "application/thraud+xml", - "tfm": "application/x-tex-tfm", - "tfx": "image/tiff-fx", - "tga": "image/x-tga", - "thmx": "application/vnd.ms-officetheme", - "tif": "image/tiff", - "tiff": "image/tiff", - "tk": "application/x-tcl", - "tmo": "application/vnd.tmobile-livetv", - "toml": "application/toml", - "torrent": "application/x-bittorrent", - "tpl": "application/vnd.groove-tool-template", - "tpt": "application/vnd.trid.tpt", - "tr": "text/troff", - "tra": "application/vnd.trueapp", - "trig": "application/trig", - "trm": "application/x-msterminal", - "ts": "video/mp2t", - "tsd": "application/timestamped-data", - "tsv": "text/tab-separated-values", - "ttc": "font/collection", - "ttf": "font/ttf", - "ttl": "text/turtle", - "ttml": "application/ttml+xml", - "twd": "application/vnd.simtech-mindmapper", - "twds": "application/vnd.simtech-mindmapper", - "txd": "application/vnd.genomatix.tuxedo", - "txf": "application/vnd.mobius.txf", - "txt": "text/plain", - "u32": "application/x-authorware-bin", - "u3d": "model/u3d", - "u8dsn": "message/global-delivery-status", - "u8hdr": "message/global-headers", - "u8mdn": "message/global-disposition-notification", - "u8msg": "message/global", - "ubj": "application/ubjson", - "udeb": "application/x-debian-package", - "ufd": "application/vnd.ufdl", - "ufdl": "application/vnd.ufdl", - "ulx": "application/x-glulx", - "umj": "application/vnd.umajin", - "unityweb": "application/vnd.unity", - "uo": "application/vnd.uoml+xml", - "uoml": "application/vnd.uoml+xml", - "uri": "text/uri-list", - "uris": "text/uri-list", - "urls": "text/uri-list", - "usda": "model/vnd.usda", - "usdz": "model/vnd.usdz+zip", - "ustar": "application/x-ustar", - "utz": "application/vnd.uiq.theme", - "uu": "text/x-uuencode", - "uva": "audio/vnd.dece.audio", - "uvd": "application/vnd.dece.data", - "uvf": "application/vnd.dece.data", - "uvg": "image/vnd.dece.graphic", - "uvh": "video/vnd.dece.hd", - "uvi": "image/vnd.dece.graphic", - "uvm": "video/vnd.dece.mobile", - "uvp": "video/vnd.dece.pd", - "uvs": "video/vnd.dece.sd", - "uvt": "application/vnd.dece.ttml+xml", - "uvu": "video/vnd.uvvu.mp4", - "uvv": "video/vnd.dece.video", - "uvva": "audio/vnd.dece.audio", - "uvvd": "application/vnd.dece.data", - "uvvf": "application/vnd.dece.data", - "uvvg": "image/vnd.dece.graphic", - "uvvh": "video/vnd.dece.hd", - "uvvi": "image/vnd.dece.graphic", - "uvvm": "video/vnd.dece.mobile", - "uvvp": "video/vnd.dece.pd", - "uvvs": "video/vnd.dece.sd", - "uvvt": "application/vnd.dece.ttml+xml", - "uvvu": "video/vnd.uvvu.mp4", - "uvvv": "video/vnd.dece.video", - "uvvx": "application/vnd.dece.unspecified", - "uvvz": "application/vnd.dece.zip", - "uvx": "application/vnd.dece.unspecified", - "uvz": "application/vnd.dece.zip", - "vbox": "application/x-virtualbox-vbox", - "vbox-extpack": "application/x-virtualbox-vbox-extpack", - "vcard": "text/vcard", - "vcd": "application/x-cdlink", - "vcf": "text/x-vcard", - "vcg": "application/vnd.groove-vcard", - "vcs": "text/x-vcalendar", - "vcx": "application/vnd.vcx", - "vdi": "application/x-virtualbox-vdi", - "vds": "model/vnd.sap.vds", - "vhd": "application/x-virtualbox-vhd", - "vis": "application/vnd.visionary", - "viv": "video/vnd.vivo", - "vmdk": "application/x-virtualbox-vmdk", - "vob": "video/x-ms-vob", - "vor": "application/vnd.stardivision.writer", - "vox": "application/x-authorware-bin", - "vrml": "model/vrml", - "vsd": "application/vnd.visio", - "vsf": "application/vnd.vsf", - "vss": "application/vnd.visio", - "vst": "application/vnd.visio", - "vsw": "application/vnd.visio", - "vtf": "image/vnd.valve.source.texture", - "vtt": "text/vtt", - "vtu": "model/vnd.vtu", - "vxml": "application/voicexml+xml", - "w3d": "application/x-director", - "wad": "application/x-doom", - "wadl": "application/vnd.sun.wadl+xml", - "war": "application/java-archive", - "wasm": "application/wasm", - "wav": "audio/x-wav", - "wax": "audio/x-ms-wax", - "wbmp": "image/vnd.wap.wbmp", - "wbs": "application/vnd.criticaltools.wbs+xml", - "wbxml": "application/vnd.wap.wbxml", - "wcm": "application/vnd.ms-works", - "wdb": "application/vnd.ms-works", - "wdp": "image/vnd.ms-photo", - "weba": "audio/webm", - "webapp": "application/x-web-app-manifest+json", - "webm": "video/webm", - "webmanifest": "application/manifest+json", - "webp": "image/webp", - "wg": "application/vnd.pmi.widget", - "wgsl": "text/wgsl", - "wgt": "application/widget", - "wif": "application/watcherinfo+xml", - "wks": "application/vnd.ms-works", - "wm": "video/x-ms-wm", - "wma": "audio/x-ms-wma", - "wmd": "application/x-ms-wmd", - "wmf": "image/wmf", - "wml": "text/vnd.wap.wml", - "wmlc": "application/vnd.wap.wmlc", - "wmls": "text/vnd.wap.wmlscript", - "wmlsc": "application/vnd.wap.wmlscriptc", - "wmv": "video/x-ms-wmv", - "wmx": "video/x-ms-wmx", - "wmz": "application/x-msmetafile", - "woff": "font/woff", - "woff2": "font/woff2", - "wpd": "application/vnd.wordperfect", - "wpl": "application/vnd.ms-wpl", - "wps": "application/vnd.ms-works", - "wqd": "application/vnd.wqd", - "wri": "application/x-mswrite", - "wrl": "model/vrml", - "wsc": "message/vnd.wfa.wsc", - "wsdl": "application/wsdl+xml", - "wspolicy": "application/wspolicy+xml", - "wtb": "application/vnd.webturbo", - "wvx": "video/x-ms-wvx", - "x32": "application/x-authorware-bin", - "x3d": "model/x3d+xml", - "x3db": "model/x3d+fastinfoset", - "x3dbz": "model/x3d+binary", - "x3dv": "model/x3d-vrml", - "x3dvz": "model/x3d+vrml", - "x3dz": "model/x3d+xml", - "x_b": "model/vnd.parasolid.transmit.binary", - "x_t": "model/vnd.parasolid.transmit.text", - "xaml": "application/xaml+xml", - "xap": "application/x-silverlight-app", - "xar": "application/vnd.xara", - "xav": "application/xcap-att+xml", - "xbap": "application/x-ms-xbap", - "xbd": "application/vnd.fujixerox.docuworks.binder", - "xbm": "image/x-xbitmap", - "xca": "application/xcap-caps+xml", - "xcs": "application/calendar+xml", - "xdf": "application/xcap-diff+xml", - "xdm": "application/vnd.syncml.dm+xml", - "xdp": "application/vnd.adobe.xdp+xml", - "xdssc": "application/dssc+xml", - "xdw": "application/vnd.fujixerox.docuworks", - "xel": "application/xcap-el+xml", - "xenc": "application/xenc+xml", - "xer": "application/patch-ops-error+xml", - "xfdf": "application/xfdf", - "xfdl": "application/vnd.xfdl", - "xht": "application/xhtml+xml", - "xhtm": "application/vnd.pwg-xhtml-print+xml", - "xhtml": "application/xhtml+xml", - "xhvml": "application/xv+xml", - "xif": "image/vnd.xiff", - "xla": "application/vnd.ms-excel", - "xlam": "application/vnd.ms-excel.addin.macroenabled.12", - "xlc": "application/vnd.ms-excel", - "xlf": "application/xliff+xml", - "xlm": "application/vnd.ms-excel", - "xls": "application/vnd.ms-excel", - "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12", - "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", - "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "xlt": "application/vnd.ms-excel", - "xltm": "application/vnd.ms-excel.template.macroenabled.12", - "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", - "xlw": "application/vnd.ms-excel", - "xm": "audio/xm", - "xml": "text/xml", - "xns": "application/xcap-ns+xml", - "xo": "application/vnd.olpc-sugar", - "xop": "application/xop+xml", - "xpi": "application/x-xpinstall", - "xpl": "application/xproc+xml", - "xpm": "image/x-xpixmap", - "xpr": "application/vnd.is-xpr", - "xps": "application/vnd.ms-xpsdocument", - "xpw": "application/vnd.intercon.formnet", - "xpx": "application/vnd.intercon.formnet", - "xsd": "application/xml", - "xsf": "application/prs.xsf+xml", - "xsl": "application/xslt+xml", - "xslt": "application/xslt+xml", - "xsm": "application/vnd.syncml+xml", - "xspf": "application/xspf+xml", - "xul": "application/vnd.mozilla.xul+xml", - "xvm": "application/xv+xml", - "xvml": "application/xv+xml", - "xwd": "image/x-xwindowdump", - "xyz": "chemical/x-xyz", - "xz": "application/x-xz", - "yaml": "text/yaml", - "yang": "application/yang", - "yin": "application/yin+xml", - "yml": "text/yaml", - "ymp": "text/x-suse-ymp", - "z1": "application/x-zmachine", - "z2": "application/x-zmachine", - "z3": "application/x-zmachine", - "z4": "application/x-zmachine", - "z5": "application/x-zmachine", - "z6": "application/x-zmachine", - "z7": "application/x-zmachine", - "z8": "application/x-zmachine", - "zaz": "application/vnd.zzazz.deck+xml", - "zip": "application/zip", - "zir": "application/vnd.zul", - "zirz": "application/vnd.zul", - "zmm": "application/vnd.handheld-entertainment+xml", -} diff --git a/vendor/codeberg.org/gruf/go-mimetypes/mime.go b/vendor/codeberg.org/gruf/go-mimetypes/mime.go deleted file mode 100644 index 028092da6..000000000 --- a/vendor/codeberg.org/gruf/go-mimetypes/mime.go +++ /dev/null @@ -1,47 +0,0 @@ -package mimetypes - -import "path" - -// PreferredExts defines preferred file -// extensions for input mime types (as there -// can be multiple extensions per mime type). -var PreferredExts = map[string]string{ - MimeTypes["mp3"]: "mp3", // audio/mpeg - MimeTypes["mpeg"]: "mpeg", // video/mpeg -} - -// GetForFilename returns mimetype for given filename. -func GetForFilename(filename string) (string, bool) { - ext := path.Ext(filename) - if len(ext) < 1 { - return "", false - } - mime, ok := MimeTypes[ext[1:]] - return mime, ok -} - -// GetFileExt returns the file extension to use for mimetype. Relying first upon -// the 'PreferredExts' map. It simply returns the first match there may multiple. -func GetFileExt(mimeType string) (string, bool) { - ext, ok := PreferredExts[mimeType] - if ok { - return ext, true - } - for ext, mime := range MimeTypes { - if mime == mimeType { - return ext, true - } - } - return "", false -} - -// GetFileExts returns known file extensions used for mimetype. -func GetFileExts(mimeType string) []string { - var exts []string - for ext, mime := range MimeTypes { - if mime == mimeType { - exts = append(exts, ext) - } - } - return exts -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5f07936db..10c1e595c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -48,9 +48,6 @@ codeberg.org/gruf/go-maps # codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 ## explicit; go 1.22.2 codeberg.org/gruf/go-mempool -# codeberg.org/gruf/go-mimetypes v1.2.0 -## explicit; go 1.17 -codeberg.org/gruf/go-mimetypes # codeberg.org/gruf/go-mutexes v1.5.1 ## explicit; go 1.22.2 codeberg.org/gruf/go-mutexes From f3b2eca8b86c797170d198d2aeb16a4cadd71e41 Mon Sep 17 00:00:00 2001 From: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:00:10 +0000 Subject: [PATCH 2/3] [feature] add support for hinting via api/v_/instance preferred image / video max sizes (#3505) * add support for hinting via api/v_/instance endpoints a preferred image / video size limit * fix tests expecting old default values --- docs/configuration/media.md | 18 +++++ example/config.yaml | 18 +++++ .../api/client/instance/instancepatch_test.go | 36 +++++----- internal/config/config.go | 2 + internal/config/helpers.gen.go | 50 ++++++++++++++ internal/typeutils/internaltofrontend.go | 65 +++++++++++++------ internal/typeutils/internaltofrontend_test.go | 12 ++-- test/envparsing.sh | 4 ++ 8 files changed, 161 insertions(+), 44 deletions(-) diff --git a/docs/configuration/media.md b/docs/configuration/media.md index 4e222c6c7..e49b59dd3 100644 --- a/docs/configuration/media.md +++ b/docs/configuration/media.md @@ -18,6 +18,24 @@ # Default: 40MiB (41943040 bytes) media-local-max-size: 40MiB +# Size. Size in bytes of max image size referred to on /api/v_/instance endpoints, +# used by applications like Tusky to automatically scale locally uploaded media. +# +# Leaving this unset will default to media-local-max-size. +# +# Examples: [64, 500, 5MiB, 5MB, 50M] +# Default: unset +media-image-size-hint: 5MiB + +# Size. Size in bytes of max video size referred to on /api/v_/instance endpoints, +# used by applications like Tusky to automatically scale locally uploaded media. +# +# Leaving this unset will default to media-local-max-size. +# +# Examples: [64, 4096, 4MiB, 4MB, 40M] +# Default: unset +media-video-size-hint: 40MiB + # Size. Max size in bytes of media to download from other instances. # # Lowering this limit may cause your instance not to fetch post media. diff --git a/example/config.yaml b/example/config.yaml index d3de40791..644b51575 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -471,6 +471,24 @@ accounts-custom-css-length: 10000 # Default: 40MiB (41943040 bytes) media-local-max-size: 40MiB +# Size. Size in bytes of max image size referred to on /api/v_/instance endpoints, +# used by applications like Tusky to automatically scale locally uploaded media. +# +# Leaving this unset will default to media-local-max-size. +# +# Examples: [64, 500, 5MiB, 5MB, 50M] +# Default: unset +media-image-size-hint: 5MiB + +# Size. Size in bytes of max video size referred to on /api/v_/instance endpoints, +# used by applications like Tusky to automatically scale locally uploaded media. +# +# Leaving this unset will default to media-local-max-size. +# +# Examples: [64, 4096, 4MiB, 4MB, 40M] +# Default: unset +media-video-size-hint: 40MiB + # Size. Max size in bytes of media to download from other instances. # # Lowering this limit may cause your instance not to fetch post media. diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go index 504fd7eb9..3a362f27c 100644 --- a/internal/api/client/instance/instancepatch_test.go +++ b/internal/api/client/instance/instancepatch_test.go @@ -130,10 +130,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, @@ -271,10 +271,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, @@ -412,10 +412,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, @@ -604,10 +604,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, @@ -767,10 +767,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, @@ -949,10 +949,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, diff --git a/internal/config/config.go b/internal/config/config.go index 4a40e9c13..9001b61d0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -98,6 +98,8 @@ type Configuration struct { MediaRemoteCacheDays int `name:"media-remote-cache-days" usage:"Number of days to locally cache media from remote instances. If set to 0, remote media will be kept indefinitely."` MediaEmojiLocalMaxSize bytesize.Size `name:"media-emoji-local-max-size" usage:"Max size in bytes of emojis uploaded to this instance via the admin API."` MediaEmojiRemoteMaxSize bytesize.Size `name:"media-emoji-remote-max-size" usage:"Max size in bytes of emojis to download from other instances."` + MediaImageSizeHint bytesize.Size `name:"media-image-size-hint" usage:"Size in bytes of max image size referred to on /api/v_/instance endpoints (else, local max size)"` + MediaVideoSizeHint bytesize.Size `name:"media-video-size-hint" usage:"Size in bytes of max video size referred to on /api/v_/instance endpoints (else, local max size)"` MediaLocalMaxSize bytesize.Size `name:"media-local-max-size" usage:"Max size in bytes of media uploaded to this instance via API"` MediaRemoteMaxSize bytesize.Size `name:"media-remote-max-size" usage:"Max size in bytes of media to download from other instances"` MediaCleanupFrom string `name:"media-cleanup-from" usage:"Time of day from which to start running media cleanup/prune jobs. Should be in the format 'hh:mm:ss', eg., '15:04:05'."` diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go index d25d6cca8..2a7e5b6ad 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go @@ -1225,6 +1225,56 @@ func GetMediaEmojiRemoteMaxSize() bytesize.Size { return global.GetMediaEmojiRem // SetMediaEmojiRemoteMaxSize safely sets the value for global configuration 'MediaEmojiRemoteMaxSize' field func SetMediaEmojiRemoteMaxSize(v bytesize.Size) { global.SetMediaEmojiRemoteMaxSize(v) } +// GetMediaImageSizeHint safely fetches the Configuration value for state's 'MediaImageSizeHint' field +func (st *ConfigState) GetMediaImageSizeHint() (v bytesize.Size) { + st.mutex.RLock() + v = st.config.MediaImageSizeHint + st.mutex.RUnlock() + return +} + +// SetMediaImageSizeHint safely sets the Configuration value for state's 'MediaImageSizeHint' field +func (st *ConfigState) SetMediaImageSizeHint(v bytesize.Size) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.MediaImageSizeHint = v + st.reloadToViper() +} + +// MediaImageSizeHintFlag returns the flag name for the 'MediaImageSizeHint' field +func MediaImageSizeHintFlag() string { return "media-image-size-hint" } + +// GetMediaImageSizeHint safely fetches the value for global configuration 'MediaImageSizeHint' field +func GetMediaImageSizeHint() bytesize.Size { return global.GetMediaImageSizeHint() } + +// SetMediaImageSizeHint safely sets the value for global configuration 'MediaImageSizeHint' field +func SetMediaImageSizeHint(v bytesize.Size) { global.SetMediaImageSizeHint(v) } + +// GetMediaVideoSizeHint safely fetches the Configuration value for state's 'MediaVideoSizeHint' field +func (st *ConfigState) GetMediaVideoSizeHint() (v bytesize.Size) { + st.mutex.RLock() + v = st.config.MediaVideoSizeHint + st.mutex.RUnlock() + return +} + +// SetMediaVideoSizeHint safely sets the Configuration value for state's 'MediaVideoSizeHint' field +func (st *ConfigState) SetMediaVideoSizeHint(v bytesize.Size) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.MediaVideoSizeHint = v + st.reloadToViper() +} + +// MediaVideoSizeHintFlag returns the flag name for the 'MediaVideoSizeHint' field +func MediaVideoSizeHintFlag() string { return "media-video-size-hint" } + +// GetMediaVideoSizeHint safely fetches the value for global configuration 'MediaVideoSizeHint' field +func GetMediaVideoSizeHint() bytesize.Size { return global.GetMediaVideoSizeHint() } + +// SetMediaVideoSizeHint safely sets the value for global configuration 'MediaVideoSizeHint' field +func SetMediaVideoSizeHint(v bytesize.Size) { global.SetMediaVideoSizeHint(v) } + // GetMediaLocalMaxSize safely fetches the Configuration value for state's 'MediaLocalMaxSize' field func (st *ConfigState) GetMediaLocalMaxSize() (v bytesize.Size) { st.mutex.RLock() diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 03b24fc9c..2a6d495d7 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -18,9 +18,11 @@ package typeutils import ( + "cmp" "context" "errors" "fmt" + "math" "slices" "strconv" "strings" @@ -42,16 +44,13 @@ ) const ( - instanceStatusesCharactersReservedPerURL = 25 - instanceMediaAttachmentsImageMatrixLimit = 16777216 // width * height - instanceMediaAttachmentsVideoMatrixLimit = 16777216 // width * height - instanceMediaAttachmentsVideoFrameRateLimit = 60 - instancePollsMinExpiration = 300 // seconds - instancePollsMaxExpiration = 2629746 // seconds - instanceAccountsMaxFeaturedTags = 10 - instanceAccountsMaxProfileFields = 6 // FIXME: https://github.com/superseriousbusiness/gotosocial/issues/1876 - instanceSourceURL = "https://github.com/superseriousbusiness/gotosocial" - instanceMastodonVersion = "3.5.3" + instanceStatusesCharactersReservedPerURL = 25 + instancePollsMinExpiration = 300 // seconds + instancePollsMaxExpiration = 2629746 // seconds + instanceAccountsMaxFeaturedTags = 10 + instanceAccountsMaxProfileFields = 6 // FIXME: https://github.com/superseriousbusiness/gotosocial/issues/1876 + instanceSourceURL = "https://github.com/superseriousbusiness/gotosocial" + instanceMastodonVersion = "3.5.3" ) var instanceStatusesSupportedMimeTypes = []string{ @@ -1563,11 +1562,24 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes - instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated. - instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit - instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated. - instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit - instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit + + // NOTE: we use the local max sizes here + // as it hints to apps like Tusky for image + // compression of locally uploaded media. + // + // TODO: return local / remote depending + // on authorized endpoint user (if any)? + localMax := config.GetMediaLocalMaxSize() + imageSz := cmp.Or(config.GetMediaImageSizeHint(), localMax) + videoSz := cmp.Or(config.GetMediaVideoSizeHint(), localMax) + instance.Configuration.MediaAttachments.ImageSizeLimit = int(imageSz) // #nosec G115 -- Already validated. + instance.Configuration.MediaAttachments.VideoSizeLimit = int(videoSz) // #nosec G115 -- Already validated. + + // we don't actually set any limits on these. set to max possible. + instance.Configuration.MediaAttachments.ImageMatrixLimit = math.MaxInt + instance.Configuration.MediaAttachments.VideoFrameRateLimit = math.MaxInt + instance.Configuration.MediaAttachments.VideoMatrixLimit = math.MaxInt + instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions() instance.Configuration.Polls.MaxCharactersPerOption = config.GetStatusesPollOptionMaxChars() instance.Configuration.Polls.MinExpiration = instancePollsMinExpiration @@ -1713,11 +1725,24 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes - instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated. - instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit - instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated. - instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit - instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit + + // NOTE: we use the local max sizes here + // as it hints to apps like Tusky for image + // compression of locally uploaded media. + // + // TODO: return local / remote depending + // on authorized endpoint user (if any)? + localMax := config.GetMediaLocalMaxSize() + imageSz := cmp.Or(config.GetMediaImageSizeHint(), localMax) + videoSz := cmp.Or(config.GetMediaVideoSizeHint(), localMax) + instance.Configuration.MediaAttachments.ImageSizeLimit = int(imageSz) // #nosec G115 -- Already validated. + instance.Configuration.MediaAttachments.VideoSizeLimit = int(videoSz) // #nosec G115 -- Already validated. + + // we don't actually set any limits on these. set to max possible. + instance.Configuration.MediaAttachments.ImageMatrixLimit = math.MaxInt + instance.Configuration.MediaAttachments.VideoFrameRateLimit = math.MaxInt + instance.Configuration.MediaAttachments.VideoMatrixLimit = math.MaxInt + instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions() instance.Configuration.Polls.MaxCharactersPerOption = config.GetStatusesPollOptionMaxChars() instance.Configuration.Polls.MinExpiration = instancePollsMinExpiration diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index cf11655ca..6c318e851 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -1968,10 +1968,10 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, @@ -2113,10 +2113,10 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() { "video/x-matroska" ], "image_size_limit": 41943040, - "image_matrix_limit": 16777216, + "image_matrix_limit": 9223372036854775807, "video_size_limit": 41943040, - "video_frame_rate_limit": 60, - "video_matrix_limit": 16777216 + "video_frame_rate_limit": 9223372036854775807, + "video_matrix_limit": 9223372036854775807 }, "polls": { "max_options": 6, diff --git a/test/envparsing.sh b/test/envparsing.sh index ac6c2edc0..503bdd817 100755 --- a/test/envparsing.sh +++ b/test/envparsing.sh @@ -129,9 +129,11 @@ EXPECT=$(cat << "EOF" "media-emoji-local-max-size": 420, "media-emoji-remote-max-size": 420, "media-ffmpeg-pool-size": 8, + "media-image-size-hint": 5242880, "media-local-max-size": 420, "media-remote-cache-days": 30, "media-remote-max-size": 420, + "media-video-size-hint": 41943040, "metrics-auth-enabled": false, "metrics-auth-password": "", "metrics-auth-username": "", @@ -244,12 +246,14 @@ GTS_ACCOUNTS_REGISTRATION_OPEN=true \ GTS_ACCOUNTS_REASON_REQUIRED=false \ GTS_MEDIA_DESCRIPTION_MIN_CHARS=69 \ GTS_MEDIA_DESCRIPTION_MAX_CHARS=5000 \ +GTS_MEDIA_IMAGE_SIZE_HINT='5MiB' \ GTS_MEDIA_LOCAL_MAX_SIZE=420 \ GTS_MEDIA_REMOTE_MAX_SIZE=420 \ GTS_MEDIA_REMOTE_CACHE_DAYS=30 \ GTS_MEDIA_EMOJI_LOCAL_MAX_SIZE=420 \ GTS_MEDIA_EMOJI_REMOTE_MAX_SIZE=420 \ GTS_MEDIA_FFMPEG_POOL_SIZE=8 \ +GTS_MEDIA_VIDEO_SIZE_HINT='40MiB' \ GTS_METRICS_AUTH_ENABLED=false \ GTS_METRICS_ENABLED=false \ GTS_STORAGE_BACKEND='local' \ From 53aaeb18d420df4a15ac395e0138e4d369d0a2a5 Mon Sep 17 00:00:00 2001 From: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:50:56 +0000 Subject: [PATCH 3/3] previously we were using the ffmpeg runner for ffprobe :facepalm: (#3512) --- internal/media/ffmpeg/ffprobe.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/media/ffmpeg/ffprobe.go b/internal/media/ffmpeg/ffprobe.go index 99d3d0563..ccd5072dd 100644 --- a/internal/media/ffmpeg/ffprobe.go +++ b/internal/media/ffmpeg/ffprobe.go @@ -40,7 +40,7 @@ func InitFfprobe(ctx context.Context, max int) error { // Ffprobe runs the given arguments with an instance of ffprobe. func Ffprobe(ctx context.Context, args Args) (uint32, error) { - return ffmpegRunner.Run(ctx, func() (uint32, error) { + return ffprobeRunner.Run(ctx, func() (uint32, error) { return wasm.Run(ctx, runtime, ffprobe, args) }) }