mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 17:22:26 -05:00 
			
		
		
		
	[feature] support processing of (many) more media types (#3090)
* initial work replacing our media decoding / encoding pipeline with ffprobe + ffmpeg
* specify the video codec to use when generating static image from emoji
* update go-storage library (fixes incompatibility after updating go-iotools)
* maintain image aspect ratio when generating a thumbnail for it
* update readme to show go-ffmpreg
* fix a bunch of media tests, move filesize checking to callers of media manager for more flexibility
* remove extra debug from error message
* fix up incorrect function signatures
* update PutFile to just use regular file copy, as changes are file is on separate partition
* fix remaining tests, remove some unneeded tests now we're working with ffmpeg/ffprobe
* update more tests, add more code comments
* add utilities to generate processed emoji / media outputs
* fix remaining tests
* add test for opus media file, add license header to utility cmds
* limit the number of concurrently available ffmpeg / ffprobe instances
* reduce number of instances
* further reduce number of instances
* fix envparsing test with configuration variables
* update docs and configuration with new media-{local,remote}-max-size variables
	
	
This commit is contained in:
		
					parent
					
						
							
								5bc567196b
							
						
					
				
			
			
				commit
				
					
						cde2fb6244
					
				
			
		
					 376 changed files with 8026 additions and 54091 deletions
				
			
		
							
								
								
									
										237
									
								
								vendor/github.com/dsoprea/go-exif/v3/utility.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										237
									
								
								vendor/github.com/dsoprea/go-exif/v3/utility.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,237 +0,0 @@ | |||
| package exif | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 
 | ||||
| 	"github.com/dsoprea/go-logging" | ||||
| 	"github.com/dsoprea/go-utility/v2/filesystem" | ||||
| 
 | ||||
| 	"github.com/dsoprea/go-exif/v3/common" | ||||
| 	"github.com/dsoprea/go-exif/v3/undefined" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	utilityLogger = log.NewLogger("exif.utility") | ||||
| ) | ||||
| 
 | ||||
| // ExifTag is one simple representation of a tag in a flat list of all of them. | ||||
| type ExifTag struct { | ||||
| 	// IfdPath is the fully-qualified IFD path (even though it is not named as | ||||
| 	// such). | ||||
| 	IfdPath string `json:"ifd_path"` | ||||
| 
 | ||||
| 	// TagId is the tag-ID. | ||||
| 	TagId uint16 `json:"id"` | ||||
| 
 | ||||
| 	// TagName is the tag-name. This is never empty. | ||||
| 	TagName string `json:"name"` | ||||
| 
 | ||||
| 	// UnitCount is the recorded number of units constution of the value. | ||||
| 	UnitCount uint32 `json:"unit_count"` | ||||
| 
 | ||||
| 	// TagTypeId is the type-ID. | ||||
| 	TagTypeId exifcommon.TagTypePrimitive `json:"type_id"` | ||||
| 
 | ||||
| 	// TagTypeName is the type name. | ||||
| 	TagTypeName string `json:"type_name"` | ||||
| 
 | ||||
| 	// Value is the decoded value. | ||||
| 	Value interface{} `json:"value"` | ||||
| 
 | ||||
| 	// ValueBytes is the raw, encoded value. | ||||
| 	ValueBytes []byte `json:"value_bytes"` | ||||
| 
 | ||||
| 	// Formatted is the human representation of the first value (tag values are | ||||
| 	// always an array). | ||||
| 	FormattedFirst string `json:"formatted_first"` | ||||
| 
 | ||||
| 	// Formatted is the human representation of the complete value. | ||||
| 	Formatted string `json:"formatted"` | ||||
| 
 | ||||
| 	// ChildIfdPath is the name of the child IFD this tag represents (if it | ||||
| 	// represents any). Otherwise, this is empty. | ||||
| 	ChildIfdPath string `json:"child_ifd_path"` | ||||
| } | ||||
| 
 | ||||
| // String returns a string representation. | ||||
| func (et ExifTag) String() string { | ||||
| 	return fmt.Sprintf( | ||||
| 		"ExifTag<"+ | ||||
| 			"IFD-PATH=[%s] "+ | ||||
| 			"TAG-ID=(0x%02x) "+ | ||||
| 			"TAG-NAME=[%s] "+ | ||||
| 			"TAG-TYPE=[%s] "+ | ||||
| 			"VALUE=[%v] "+ | ||||
| 			"VALUE-BYTES=(%d) "+ | ||||
| 			"CHILD-IFD-PATH=[%s]", | ||||
| 		et.IfdPath, et.TagId, et.TagName, et.TagTypeName, et.FormattedFirst, | ||||
| 		len(et.ValueBytes), et.ChildIfdPath) | ||||
| } | ||||
| 
 | ||||
| // GetFlatExifData returns a simple, flat representation of all tags. | ||||
| func GetFlatExifData(exifData []byte, so *ScanOptions) (exifTags []ExifTag, med *MiscellaneousExifData, err error) { | ||||
| 	defer func() { | ||||
| 		if state := recover(); state != nil { | ||||
| 			err = log.Wrap(state.(error)) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	sb := rifs.NewSeekableBufferWithBytes(exifData) | ||||
| 
 | ||||
| 	exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(sb, so, false) | ||||
| 	log.PanicIf(err) | ||||
| 
 | ||||
| 	return exifTags, med, nil | ||||
| } | ||||
| 
 | ||||
| // RELEASE(dustin): GetFlatExifDataUniversalSearch is a kludge to allow univeral tag searching in a backwards-compatible manner. For the next release, undo this and simply add the flag to GetFlatExifData. | ||||
| 
 | ||||
| // GetFlatExifDataUniversalSearch returns a simple, flat representation of all | ||||
| // tags. | ||||
| func GetFlatExifDataUniversalSearch(exifData []byte, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) { | ||||
| 	defer func() { | ||||
| 		if state := recover(); state != nil { | ||||
| 			err = log.Wrap(state.(error)) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	sb := rifs.NewSeekableBufferWithBytes(exifData) | ||||
| 
 | ||||
| 	exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(sb, so, doUniversalSearch) | ||||
| 	log.PanicIf(err) | ||||
| 
 | ||||
| 	return exifTags, med, nil | ||||
| } | ||||
| 
 | ||||
| // RELEASE(dustin): GetFlatExifDataUniversalSearchWithReadSeeker is a kludge to allow using a ReadSeeker in a backwards-compatible manner. For the next release, drop this and refactor GetFlatExifDataUniversalSearch to take a ReadSeeker. | ||||
| 
 | ||||
| // GetFlatExifDataUniversalSearchWithReadSeeker returns a simple, flat | ||||
| // representation of all tags given a ReadSeeker. | ||||
| func GetFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) { | ||||
| 	defer func() { | ||||
| 		if state := recover(); state != nil { | ||||
| 			err = log.Wrap(state.(error)) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(rs, so, doUniversalSearch) | ||||
| 	log.PanicIf(err) | ||||
| 
 | ||||
| 	return exifTags, med, nil | ||||
| } | ||||
| 
 | ||||
| // getFlatExifDataUniversalSearchWithReadSeeker returns a simple, flat | ||||
| // representation of all tags given a ReadSeeker. | ||||
| func getFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) { | ||||
| 	defer func() { | ||||
| 		if state := recover(); state != nil { | ||||
| 			err = log.Wrap(state.(error)) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	headerData := make([]byte, ExifSignatureLength) | ||||
| 	if _, err = io.ReadFull(rs, headerData); err != nil { | ||||
| 		if err == io.EOF { | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		log.Panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	eh, err := ParseExifHeader(headerData) | ||||
| 	log.PanicIf(err) | ||||
| 
 | ||||
| 	im, err := exifcommon.NewIfdMappingWithStandard() | ||||
| 	log.PanicIf(err) | ||||
| 
 | ||||
| 	ti := NewTagIndex() | ||||
| 
 | ||||
| 	if doUniversalSearch == true { | ||||
| 		ti.SetUniversalSearch(true) | ||||
| 	} | ||||
| 
 | ||||
| 	ebs := NewExifReadSeeker(rs) | ||||
| 	ie := NewIfdEnumerate(im, ti, ebs, eh.ByteOrder) | ||||
| 
 | ||||
| 	exifTags = make([]ExifTag, 0) | ||||
| 
 | ||||
| 	visitor := func(ite *IfdTagEntry) (err error) { | ||||
| 		// This encodes down to base64. Since this an example tool and we do not | ||||
| 		// expect to ever decode the output, we are not worried about | ||||
| 		// specifically base64-encoding it in order to have a measure of | ||||
| 		// control. | ||||
| 		valueBytes, err := ite.GetRawBytes() | ||||
| 		if err != nil { | ||||
| 			if err == exifundefined.ErrUnparseableValue { | ||||
| 				return nil | ||||
| 			} | ||||
| 
 | ||||
| 			log.Panic(err) | ||||
| 		} | ||||
| 
 | ||||
| 		value, err := ite.Value() | ||||
| 		if err != nil { | ||||
| 			if err == exifcommon.ErrUnhandledUndefinedTypedTag { | ||||
| 				value = exifundefined.UnparseableUnknownTagValuePlaceholder | ||||
| 			} else if log.Is(err, exifcommon.ErrParseFail) == true { | ||||
| 				utilityLogger.Warningf(nil, | ||||
| 					"Could not parse value for tag [%s] (%04x) [%s].", | ||||
| 					ite.IfdPath(), ite.TagId(), ite.TagName()) | ||||
| 
 | ||||
| 				return nil | ||||
| 			} else { | ||||
| 				log.Panic(err) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		et := ExifTag{ | ||||
| 			IfdPath:      ite.IfdPath(), | ||||
| 			TagId:        ite.TagId(), | ||||
| 			TagName:      ite.TagName(), | ||||
| 			UnitCount:    ite.UnitCount(), | ||||
| 			TagTypeId:    ite.TagType(), | ||||
| 			TagTypeName:  ite.TagType().String(), | ||||
| 			Value:        value, | ||||
| 			ValueBytes:   valueBytes, | ||||
| 			ChildIfdPath: ite.ChildIfdPath(), | ||||
| 		} | ||||
| 
 | ||||
| 		et.Formatted, err = ite.Format() | ||||
| 		log.PanicIf(err) | ||||
| 
 | ||||
| 		et.FormattedFirst, err = ite.FormatFirst() | ||||
| 		log.PanicIf(err) | ||||
| 
 | ||||
| 		exifTags = append(exifTags, et) | ||||
| 
 | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	med, err = ie.Scan(exifcommon.IfdStandardIfdIdentity, eh.FirstIfdOffset, visitor, nil) | ||||
| 	log.PanicIf(err) | ||||
| 
 | ||||
| 	return exifTags, med, nil | ||||
| } | ||||
| 
 | ||||
| // GpsDegreesEquals returns true if the two `GpsDegrees` are identical. | ||||
| func GpsDegreesEquals(gi1, gi2 GpsDegrees) bool { | ||||
| 	if gi2.Orientation != gi1.Orientation { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	degreesRightBound := math.Nextafter(gi1.Degrees, gi1.Degrees+1) | ||||
| 	minutesRightBound := math.Nextafter(gi1.Minutes, gi1.Minutes+1) | ||||
| 	secondsRightBound := math.Nextafter(gi1.Seconds, gi1.Seconds+1) | ||||
| 
 | ||||
| 	if gi2.Degrees < gi1.Degrees || gi2.Degrees >= degreesRightBound { | ||||
| 		return false | ||||
| 	} else if gi2.Minutes < gi1.Minutes || gi2.Minutes >= minutesRightBound { | ||||
| 		return false | ||||
| 	} else if gi2.Seconds < gi1.Seconds || gi2.Seconds >= secondsRightBound { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue