| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |    GoToSocial | 
					
						
							|  |  |  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    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 ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"image" | 
					
						
							|  |  |  | 	"image/gif" | 
					
						
							|  |  |  | 	"image/jpeg" | 
					
						
							|  |  |  | 	"image/png" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/buckket/go-blurhash" | 
					
						
							|  |  |  | 	"github.com/h2non/filetype" | 
					
						
							|  |  |  | 	"github.com/nfnt/resize" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/exifremove/pkg/exifremove" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	// MIMEImage is the mime type for image | 
					
						
							|  |  |  | 	MIMEImage = "image" | 
					
						
							|  |  |  | 	// MIMEJpeg is the jpeg image mime type | 
					
						
							|  |  |  | 	MIMEJpeg = "image/jpeg" | 
					
						
							|  |  |  | 	// MIMEGif is the gif image mime type | 
					
						
							|  |  |  | 	MIMEGif = "image/gif" | 
					
						
							|  |  |  | 	// MIMEPng is the png image mime type | 
					
						
							|  |  |  | 	MIMEPng = "image/png" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// MIMEVideo is the mime type for video | 
					
						
							|  |  |  | 	MIMEVideo = "video" | 
					
						
							|  |  |  | 	// MIMEMp4 is the mp4 video mime type | 
					
						
							|  |  |  | 	MIMEMp4 = "video/mp4" | 
					
						
							|  |  |  | 	// MIMEMpeg is the mpeg video mime type | 
					
						
							|  |  |  | 	MIMEMpeg = "video/mpeg" | 
					
						
							|  |  |  | 	// MIMEWebm is the webm video mime type | 
					
						
							|  |  |  | 	MIMEWebm = "video/webm" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | // parseContentType parses the MIME content type from a file, returning it as a string in the form (eg., "image/jpeg"). | 
					
						
							|  |  |  | // Returns an error if the content type is not something we can process. | 
					
						
							|  |  |  | func parseContentType(content []byte) (string, error) { | 
					
						
							|  |  |  | 	head := make([]byte, 261) | 
					
						
							|  |  |  | 	_, err := bytes.NewReader(content).Read(head) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("could not read first magic bytes of file: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kind, err := filetype.Match(head) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if kind == filetype.Unknown { | 
					
						
							|  |  |  | 		return "", errors.New("filetype unknown") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return kind.MIME.Value, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | // SupportedImageType checks mime type of an image against a slice of accepted types, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | // and returns True if the mime type is accepted. | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | func SupportedImageType(mimeType string) bool { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	acceptedImageTypes := []string{ | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		MIMEJpeg, | 
					
						
							|  |  |  | 		MIMEGif, | 
					
						
							|  |  |  | 		MIMEPng, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	for _, accepted := range acceptedImageTypes { | 
					
						
							|  |  |  | 		if mimeType == accepted { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | // SupportedVideoType checks mime type of a video against a slice of accepted types, | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | // and returns True if the mime type is accepted. | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | func SupportedVideoType(mimeType string) bool { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	acceptedVideoTypes := []string{ | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		MIMEMp4, | 
					
						
							|  |  |  | 		MIMEMpeg, | 
					
						
							|  |  |  | 		MIMEWebm, | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	for _, accepted := range acceptedVideoTypes { | 
					
						
							|  |  |  | 		if mimeType == accepted { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // supportedEmojiType checks that the content type is image/png -- the only type supported for emoji. | 
					
						
							|  |  |  | func supportedEmojiType(mimeType string) bool { | 
					
						
							|  |  |  | 	acceptedEmojiTypes := []string{ | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		MIMEGif, | 
					
						
							|  |  |  | 		MIMEPng, | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	for _, accepted := range acceptedEmojiTypes { | 
					
						
							|  |  |  | 		if mimeType == accepted { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | // purgeExif is a little wrapper for the action of removing exif data from an image. | 
					
						
							|  |  |  | // Only pass pngs or jpegs to this function. | 
					
						
							|  |  |  | func purgeExif(b []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	if len(b) == 0 { | 
					
						
							|  |  |  | 		return nil, errors.New("passed image was not valid") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clean, err := exifremove.Remove(b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("could not purge exif from image: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(clean) == 0 { | 
					
						
							|  |  |  | 		return nil, errors.New("purged image was not valid") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return clean, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | func deriveGif(b []byte, extension string) (*imageAndMeta, error) { | 
					
						
							|  |  |  | 	var g *gif.GIF | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	switch extension { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEGif: | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		g, err = gif.DecodeAll(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("extension %s not recognised", extension) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// use the first frame to get the static characteristics | 
					
						
							|  |  |  | 	width := g.Config.Width | 
					
						
							|  |  |  | 	height := g.Config.Height | 
					
						
							|  |  |  | 	size := width * height | 
					
						
							|  |  |  | 	aspect := float64(width) / float64(height) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &imageAndMeta{ | 
					
						
							| 
									
										
										
										
											2021-09-23 11:13:11 +02:00
										 |  |  | 		image:  b, | 
					
						
							|  |  |  | 		width:  width, | 
					
						
							|  |  |  | 		height: height, | 
					
						
							|  |  |  | 		size:   size, | 
					
						
							|  |  |  | 		aspect: aspect, | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func deriveImage(b []byte, contentType string) (*imageAndMeta, error) { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	var i image.Image | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	switch contentType { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEJpeg: | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		i, err = jpeg.Decode(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEPng: | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		i, err = png.Decode(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		return nil, fmt.Errorf("content type %s not recognised", contentType) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	width := i.Bounds().Size().X | 
					
						
							|  |  |  | 	height := i.Bounds().Size().Y | 
					
						
							|  |  |  | 	size := width * height | 
					
						
							|  |  |  | 	aspect := float64(width) / float64(height) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	return &imageAndMeta{ | 
					
						
							| 
									
										
										
										
											2021-09-23 11:13:11 +02:00
										 |  |  | 		image:  b, | 
					
						
							|  |  |  | 		width:  width, | 
					
						
							|  |  |  | 		height: height, | 
					
						
							|  |  |  | 		size:   size, | 
					
						
							|  |  |  | 		aspect: aspect, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | // deriveThumbnail returns a byte slice and metadata for a thumbnail of width x and height y, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | // of a given jpeg, png, or gif, or an error if something goes wrong. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Note that the aspect ratio of the image will be retained, | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | // so it will not necessarily be a square, even if x and y are set as the same value. | 
					
						
							|  |  |  | func deriveThumbnail(b []byte, contentType string, x uint, y uint) (*imageAndMeta, error) { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	var i image.Image | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	switch contentType { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEJpeg: | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		i, err = jpeg.Decode(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEPng: | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		i, err = png.Decode(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEGif: | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		i, err = gif.Decode(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		return nil, fmt.Errorf("content type %s not recognised", contentType) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	thumb := resize.Thumbnail(x, y, i, resize.NearestNeighbor) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	width := thumb.Bounds().Size().X | 
					
						
							|  |  |  | 	height := thumb.Bounds().Size().Y | 
					
						
							|  |  |  | 	size := width * height | 
					
						
							|  |  |  | 	aspect := float64(width) / float64(height) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-23 11:13:11 +02:00
										 |  |  | 	tiny := resize.Thumbnail(32, 32, thumb, resize.NearestNeighbor) | 
					
						
							|  |  |  | 	bh, err := blurhash.Encode(4, 3, tiny) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	out := &bytes.Buffer{} | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	if err := jpeg.Encode(out, thumb, &jpeg.Options{ | 
					
						
							| 
									
										
										
										
											2021-09-23 11:13:11 +02:00
										 |  |  | 		Quality: 75, | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	}); err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &imageAndMeta{ | 
					
						
							| 
									
										
										
										
											2021-09-23 11:13:11 +02:00
										 |  |  | 		image:    out.Bytes(), | 
					
						
							|  |  |  | 		width:    width, | 
					
						
							|  |  |  | 		height:   height, | 
					
						
							|  |  |  | 		size:     size, | 
					
						
							|  |  |  | 		aspect:   aspect, | 
					
						
							|  |  |  | 		blurhash: bh, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | // deriveStaticEmojji takes a given gif or png of an emoji, decodes it, and re-encodes it as a static png. | 
					
						
							|  |  |  | func deriveStaticEmoji(b []byte, contentType string) (*imageAndMeta, error) { | 
					
						
							|  |  |  | 	var i image.Image | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch contentType { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEPng: | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		i, err = png.Decode(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	case MIMEGif: | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		i, err = gif.Decode(bytes.NewReader(b)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("content type %s not allowed for emoji", contentType) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out := &bytes.Buffer{} | 
					
						
							|  |  |  | 	if err := png.Encode(out, i); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &imageAndMeta{ | 
					
						
							|  |  |  | 		image: out.Bytes(), | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | type imageAndMeta struct { | 
					
						
							|  |  |  | 	image    []byte | 
					
						
							|  |  |  | 	width    int | 
					
						
							|  |  |  | 	height   int | 
					
						
							|  |  |  | 	size     int | 
					
						
							|  |  |  | 	aspect   float64 | 
					
						
							|  |  |  | 	blurhash string | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ParseMediaType converts s to a recognized MediaType, or returns an error if unrecognized | 
					
						
							|  |  |  | func ParseMediaType(s string) (Type, error) { | 
					
						
							|  |  |  | 	switch Type(s) { | 
					
						
							|  |  |  | 	case Attachment: | 
					
						
							|  |  |  | 		return Attachment, nil | 
					
						
							|  |  |  | 	case Header: | 
					
						
							|  |  |  | 		return Header, nil | 
					
						
							|  |  |  | 	case Avatar: | 
					
						
							|  |  |  | 		return Avatar, nil | 
					
						
							|  |  |  | 	case Emoji: | 
					
						
							|  |  |  | 		return Emoji, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "", fmt.Errorf("%s not a recognized MediaType", s) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ParseMediaSize converts s to a recognized MediaSize, or returns an error if unrecognized | 
					
						
							|  |  |  | func ParseMediaSize(s string) (Size, error) { | 
					
						
							|  |  |  | 	switch Size(s) { | 
					
						
							|  |  |  | 	case Small: | 
					
						
							|  |  |  | 		return Small, nil | 
					
						
							|  |  |  | 	case Original: | 
					
						
							|  |  |  | 		return Original, nil | 
					
						
							|  |  |  | 	case Static: | 
					
						
							|  |  |  | 		return Static, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "", fmt.Errorf("%s not a recognized MediaSize", s) | 
					
						
							|  |  |  | } |