mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 15:42:25 -05:00 
			
		
		
		
	* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
		
			
				
	
	
		
			222 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package exif
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/dsoprea/go-logging"
 | |
| )
 | |
| 
 | |
| func DumpBytes(data []byte) {
 | |
| 	fmt.Printf("DUMP: ")
 | |
| 	for _, x := range data {
 | |
| 		fmt.Printf("%02x ", x)
 | |
| 	}
 | |
| 
 | |
| 	fmt.Printf("\n")
 | |
| }
 | |
| 
 | |
| func DumpBytesClause(data []byte) {
 | |
| 	fmt.Printf("DUMP: ")
 | |
| 
 | |
| 	fmt.Printf("[]byte { ")
 | |
| 
 | |
| 	for i, x := range data {
 | |
| 		fmt.Printf("0x%02x", x)
 | |
| 
 | |
| 		if i < len(data)-1 {
 | |
| 			fmt.Printf(", ")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fmt.Printf(" }\n")
 | |
| }
 | |
| 
 | |
| func DumpBytesToString(data []byte) string {
 | |
| 	b := new(bytes.Buffer)
 | |
| 
 | |
| 	for i, x := range data {
 | |
| 		_, err := b.WriteString(fmt.Sprintf("%02x", x))
 | |
| 		log.PanicIf(err)
 | |
| 
 | |
| 		if i < len(data)-1 {
 | |
| 			_, err := b.WriteRune(' ')
 | |
| 			log.PanicIf(err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return b.String()
 | |
| }
 | |
| 
 | |
| func DumpBytesClauseToString(data []byte) string {
 | |
| 	b := new(bytes.Buffer)
 | |
| 
 | |
| 	for i, x := range data {
 | |
| 		_, err := b.WriteString(fmt.Sprintf("0x%02x", x))
 | |
| 		log.PanicIf(err)
 | |
| 
 | |
| 		if i < len(data)-1 {
 | |
| 			_, err := b.WriteString(", ")
 | |
| 			log.PanicIf(err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return b.String()
 | |
| }
 | |
| 
 | |
| // ParseExifFullTimestamp parses dates like "2018:11:30 13:01:49" into a UTC
 | |
| // `time.Time` struct.
 | |
| func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, err error) {
 | |
| 	defer func() {
 | |
| 		if state := recover(); state != nil {
 | |
| 			err = log.Wrap(state.(error))
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	parts := strings.Split(fullTimestampPhrase, " ")
 | |
| 	datestampValue, timestampValue := parts[0], parts[1]
 | |
| 
 | |
| 	dateParts := strings.Split(datestampValue, ":")
 | |
| 
 | |
| 	year, err := strconv.ParseUint(dateParts[0], 10, 16)
 | |
| 	if err != nil {
 | |
| 		log.Panicf("could not parse year")
 | |
| 	}
 | |
| 
 | |
| 	month, err := strconv.ParseUint(dateParts[1], 10, 8)
 | |
| 	if err != nil {
 | |
| 		log.Panicf("could not parse month")
 | |
| 	}
 | |
| 
 | |
| 	day, err := strconv.ParseUint(dateParts[2], 10, 8)
 | |
| 	if err != nil {
 | |
| 		log.Panicf("could not parse day")
 | |
| 	}
 | |
| 
 | |
| 	timeParts := strings.Split(timestampValue, ":")
 | |
| 
 | |
| 	hour, err := strconv.ParseUint(timeParts[0], 10, 8)
 | |
| 	if err != nil {
 | |
| 		log.Panicf("could not parse hour")
 | |
| 	}
 | |
| 
 | |
| 	minute, err := strconv.ParseUint(timeParts[1], 10, 8)
 | |
| 	if err != nil {
 | |
| 		log.Panicf("could not parse minute")
 | |
| 	}
 | |
| 
 | |
| 	second, err := strconv.ParseUint(timeParts[2], 10, 8)
 | |
| 	if err != nil {
 | |
| 		log.Panicf("could not parse second")
 | |
| 	}
 | |
| 
 | |
| 	timestamp = time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.UTC)
 | |
| 	return timestamp, nil
 | |
| }
 | |
| 
 | |
| // ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a
 | |
| // `time.Time` struct. It will attempt to convert to UTC first.
 | |
| func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) {
 | |
| 	t = t.UTC()
 | |
| 
 | |
| 	return fmt.Sprintf("%04d:%02d:%02d %02d:%02d:%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
 | |
| }
 | |
| 
 | |
| // ExifTag is one simple representation of a tag in a flat list of all of them.
 | |
| type ExifTag struct {
 | |
| 	IfdPath string `json:"ifd_path"`
 | |
| 
 | |
| 	TagId   uint16 `json:"id"`
 | |
| 	TagName string `json:"name"`
 | |
| 
 | |
| 	TagTypeId   TagTypePrimitive `json:"type_id"`
 | |
| 	TagTypeName string           `json:"type_name"`
 | |
| 	Value       interface{}      `json:"value"`
 | |
| 	ValueBytes  []byte           `json:"value_bytes"`
 | |
| 
 | |
| 	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.Value, len(et.ValueBytes), et.ChildIfdPath)
 | |
| }
 | |
| 
 | |
| // GetFlatExifData returns a simple, flat representation of all tags.
 | |
| func GetFlatExifData(exifData []byte) (exifTags []ExifTag, err error) {
 | |
| 	defer func() {
 | |
| 		if state := recover(); state != nil {
 | |
| 			err = log.Wrap(state.(error))
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	im := NewIfdMappingWithStandard()
 | |
| 	ti := NewTagIndex()
 | |
| 
 | |
| 	_, index, err := Collect(im, ti, exifData)
 | |
| 	log.PanicIf(err)
 | |
| 
 | |
| 	q := []*Ifd{index.RootIfd}
 | |
| 
 | |
| 	exifTags = make([]ExifTag, 0)
 | |
| 
 | |
| 	for len(q) > 0 {
 | |
| 		var ifd *Ifd
 | |
| 		ifd, q = q[0], q[1:]
 | |
| 
 | |
| 		ti := NewTagIndex()
 | |
| 		for _, ite := range ifd.Entries {
 | |
| 			tagName := ""
 | |
| 
 | |
| 			it, err := ti.Get(ifd.IfdPath, ite.TagId)
 | |
| 			if err != nil {
 | |
| 				// If it's a non-standard tag, just leave the name blank.
 | |
| 				if log.Is(err, ErrTagNotFound) != true {
 | |
| 					log.PanicIf(err)
 | |
| 				}
 | |
| 			} else {
 | |
| 				tagName = it.Name
 | |
| 			}
 | |
| 
 | |
| 			value, err := ifd.TagValue(ite)
 | |
| 			if err != nil {
 | |
| 				if err == ErrUnhandledUnknownTypedTag {
 | |
| 					value = UnparseableUnknownTagValuePlaceholder
 | |
| 				} else {
 | |
| 					log.Panic(err)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			valueBytes, err := ifd.TagValueBytes(ite)
 | |
| 			if err != nil && err != ErrUnhandledUnknownTypedTag {
 | |
| 				log.Panic(err)
 | |
| 			}
 | |
| 
 | |
| 			et := ExifTag{
 | |
| 				IfdPath:      ifd.IfdPath,
 | |
| 				TagId:        ite.TagId,
 | |
| 				TagName:      tagName,
 | |
| 				TagTypeId:    ite.TagType,
 | |
| 				TagTypeName:  TypeNames[ite.TagType],
 | |
| 				Value:        value,
 | |
| 				ValueBytes:   valueBytes,
 | |
| 				ChildIfdPath: ite.ChildIfdPath,
 | |
| 			}
 | |
| 
 | |
| 			exifTags = append(exifTags, et)
 | |
| 		}
 | |
| 
 | |
| 		for _, childIfd := range ifd.Children {
 | |
| 			q = append(q, childIfd)
 | |
| 		}
 | |
| 
 | |
| 		if ifd.NextIfd != nil {
 | |
| 			q = append(q, ifd.NextIfd)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return exifTags, nil
 | |
| }
 |