mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 18:02:26 -06:00 
			
		
		
		
	* [feature] basic video support * fix missing semicolon * replace text shadow with stacked icons Co-authored-by: f0x <f0x@cthu.lu>
		
			
				
	
	
		
			290 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			290 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package mp4
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
type (
 | 
						|
	stringType uint8
 | 
						|
	fieldFlag  uint16
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	stringType_C stringType = iota
 | 
						|
	stringType_C_P
 | 
						|
 | 
						|
	fieldString        fieldFlag = 1 << iota // 0
 | 
						|
	fieldExtend                              // 1
 | 
						|
	fieldDec                                 // 2
 | 
						|
	fieldHex                                 // 3
 | 
						|
	fieldISO639_2                            // 4
 | 
						|
	fieldUUID                                // 5
 | 
						|
	fieldHidden                              // 6
 | 
						|
	fieldOptDynamic                          // 7
 | 
						|
	fieldVarint                              // 8
 | 
						|
	fieldSizeDynamic                         // 9
 | 
						|
	fieldLengthDynamic                       // 10
 | 
						|
)
 | 
						|
 | 
						|
type field struct {
 | 
						|
	children []*field
 | 
						|
	name     string
 | 
						|
	cnst     string
 | 
						|
	order    int
 | 
						|
	optFlag  uint32
 | 
						|
	nOptFlag uint32
 | 
						|
	size     uint
 | 
						|
	length   uint
 | 
						|
	flags    fieldFlag
 | 
						|
	strType  stringType
 | 
						|
	version  uint8
 | 
						|
	nVersion uint8
 | 
						|
}
 | 
						|
 | 
						|
func (f *field) set(flag fieldFlag) {
 | 
						|
	f.flags |= flag
 | 
						|
}
 | 
						|
 | 
						|
func (f *field) is(flag fieldFlag) bool {
 | 
						|
	return f.flags&flag != 0
 | 
						|
}
 | 
						|
 | 
						|
func buildFields(box IImmutableBox) []*field {
 | 
						|
	t := reflect.TypeOf(box).Elem()
 | 
						|
	return buildFieldsStruct(t)
 | 
						|
}
 | 
						|
 | 
						|
func buildFieldsStruct(t reflect.Type) []*field {
 | 
						|
	fs := make([]*field, 0, 8)
 | 
						|
	for i := 0; i < t.NumField(); i++ {
 | 
						|
		ft := t.Field(i).Type
 | 
						|
		tag, ok := t.Field(i).Tag.Lookup("mp4")
 | 
						|
		if !ok {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		f := buildField(t.Field(i).Name, tag)
 | 
						|
		f.children = buildFieldsAny(ft)
 | 
						|
		fs = append(fs, f)
 | 
						|
	}
 | 
						|
	sort.SliceStable(fs, func(i, j int) bool {
 | 
						|
		return fs[i].order < fs[j].order
 | 
						|
	})
 | 
						|
	return fs
 | 
						|
}
 | 
						|
 | 
						|
func buildFieldsAny(t reflect.Type) []*field {
 | 
						|
	switch t.Kind() {
 | 
						|
	case reflect.Struct:
 | 
						|
		return buildFieldsStruct(t)
 | 
						|
	case reflect.Ptr, reflect.Array, reflect.Slice:
 | 
						|
		return buildFieldsAny(t.Elem())
 | 
						|
	default:
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func buildField(fieldName string, tag string) *field {
 | 
						|
	f := &field{
 | 
						|
		name: fieldName,
 | 
						|
	}
 | 
						|
	tagMap := parseFieldTag(tag)
 | 
						|
	for key, val := range tagMap {
 | 
						|
		if val != "" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if order, err := strconv.Atoi(key); err == nil {
 | 
						|
			f.order = order
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if val, contained := tagMap["string"]; contained {
 | 
						|
		f.set(fieldString)
 | 
						|
		if val == "c_p" {
 | 
						|
			f.strType = stringType_C_P
 | 
						|
			fmt.Fprint(os.Stderr, "go-mp4: string=c_p tag is deprecated!! See https://github.com/abema/go-mp4/issues/76\n")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if _, contained := tagMap["varint"]; contained {
 | 
						|
		f.set(fieldVarint)
 | 
						|
	}
 | 
						|
 | 
						|
	if val, contained := tagMap["opt"]; contained {
 | 
						|
		if val == "dynamic" {
 | 
						|
			f.set(fieldOptDynamic)
 | 
						|
		} else {
 | 
						|
			base := 10
 | 
						|
			if strings.HasPrefix(val, "0x") {
 | 
						|
				val = val[2:]
 | 
						|
				base = 16
 | 
						|
			}
 | 
						|
			opt, err := strconv.ParseUint(val, base, 32)
 | 
						|
			if err != nil {
 | 
						|
				panic(err)
 | 
						|
			}
 | 
						|
			f.optFlag = uint32(opt)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if val, contained := tagMap["nopt"]; contained {
 | 
						|
		base := 10
 | 
						|
		if strings.HasPrefix(val, "0x") {
 | 
						|
			val = val[2:]
 | 
						|
			base = 16
 | 
						|
		}
 | 
						|
		nopt, err := strconv.ParseUint(val, base, 32)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		f.nOptFlag = uint32(nopt)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, contained := tagMap["extend"]; contained {
 | 
						|
		f.set(fieldExtend)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, contained := tagMap["dec"]; contained {
 | 
						|
		f.set(fieldDec)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, contained := tagMap["hex"]; contained {
 | 
						|
		f.set(fieldHex)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, contained := tagMap["iso639-2"]; contained {
 | 
						|
		f.set(fieldISO639_2)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, contained := tagMap["uuid"]; contained {
 | 
						|
		f.set(fieldUUID)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, contained := tagMap["hidden"]; contained {
 | 
						|
		f.set(fieldHidden)
 | 
						|
	}
 | 
						|
 | 
						|
	if val, contained := tagMap["const"]; contained {
 | 
						|
		f.cnst = val
 | 
						|
	}
 | 
						|
 | 
						|
	f.version = anyVersion
 | 
						|
	if val, contained := tagMap["ver"]; contained {
 | 
						|
		ver, err := strconv.Atoi(val)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		f.version = uint8(ver)
 | 
						|
	}
 | 
						|
 | 
						|
	f.nVersion = anyVersion
 | 
						|
	if val, contained := tagMap["nver"]; contained {
 | 
						|
		ver, err := strconv.Atoi(val)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		f.nVersion = uint8(ver)
 | 
						|
	}
 | 
						|
 | 
						|
	if val, contained := tagMap["size"]; contained {
 | 
						|
		if val == "dynamic" {
 | 
						|
			f.set(fieldSizeDynamic)
 | 
						|
		} else {
 | 
						|
			size, err := strconv.ParseUint(val, 10, 32)
 | 
						|
			if err != nil {
 | 
						|
				panic(err)
 | 
						|
			}
 | 
						|
			f.size = uint(size)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	f.length = LengthUnlimited
 | 
						|
	if val, contained := tagMap["len"]; contained {
 | 
						|
		if val == "dynamic" {
 | 
						|
			f.set(fieldLengthDynamic)
 | 
						|
		} else {
 | 
						|
			l, err := strconv.ParseUint(val, 10, 32)
 | 
						|
			if err != nil {
 | 
						|
				panic(err)
 | 
						|
			}
 | 
						|
			f.length = uint(l)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return f
 | 
						|
}
 | 
						|
 | 
						|
func parseFieldTag(str string) map[string]string {
 | 
						|
	tag := make(map[string]string, 8)
 | 
						|
 | 
						|
	list := strings.Split(str, ",")
 | 
						|
	for _, e := range list {
 | 
						|
		kv := strings.SplitN(e, "=", 2)
 | 
						|
		if len(kv) == 2 {
 | 
						|
			tag[strings.Trim(kv[0], " ")] = strings.Trim(kv[1], " ")
 | 
						|
		} else {
 | 
						|
			tag[strings.Trim(kv[0], " ")] = ""
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return tag
 | 
						|
}
 | 
						|
 | 
						|
type fieldInstance struct {
 | 
						|
	field
 | 
						|
	cfo ICustomFieldObject
 | 
						|
}
 | 
						|
 | 
						|
func resolveFieldInstance(f *field, box IImmutableBox, parent reflect.Value, ctx Context) *fieldInstance {
 | 
						|
	fi := fieldInstance{
 | 
						|
		field: *f,
 | 
						|
	}
 | 
						|
 | 
						|
	cfo, ok := parent.Addr().Interface().(ICustomFieldObject)
 | 
						|
	if ok {
 | 
						|
		fi.cfo = cfo
 | 
						|
	} else {
 | 
						|
		fi.cfo = box
 | 
						|
	}
 | 
						|
 | 
						|
	if fi.is(fieldSizeDynamic) {
 | 
						|
		fi.size = fi.cfo.GetFieldSize(f.name, ctx)
 | 
						|
	}
 | 
						|
 | 
						|
	if fi.is(fieldLengthDynamic) {
 | 
						|
		fi.length = fi.cfo.GetFieldLength(f.name, ctx)
 | 
						|
	}
 | 
						|
 | 
						|
	return &fi
 | 
						|
}
 | 
						|
 | 
						|
func isTargetField(box IImmutableBox, fi *fieldInstance, ctx Context) bool {
 | 
						|
	if box.GetVersion() != anyVersion {
 | 
						|
		if fi.version != anyVersion && box.GetVersion() != fi.version {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		if fi.nVersion != anyVersion && box.GetVersion() == fi.nVersion {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if fi.optFlag != 0 && box.GetFlags()&fi.optFlag == 0 {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	if fi.nOptFlag != 0 && box.GetFlags()&fi.nOptFlag != 0 {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	if fi.is(fieldOptDynamic) && !fi.cfo.IsOptFieldEnabled(fi.name, ctx) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return true
 | 
						|
}
 |