github.com/puellanivis/breton@v0.2.16/lib/net/hls/m3u8/attributelist.go (about)

     1  package m3u8
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"reflect"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  )
    13  
    14  func unmarshalAttributeField(field reflect.Value, f reflect.StructField, val []byte) error {
    15  	switch field.Interface().(type) {
    16  	case Resolution:
    17  		return field.Addr().Interface().(*Resolution).TextUnmarshal(val)
    18  
    19  	case time.Time:
    20  		t, err := time.Parse("2006-01-02T15:04:05.999Z07:00", string(val))
    21  		if err != nil {
    22  			return err
    23  		}
    24  		field.Set(reflect.ValueOf(t))
    25  
    26  	case time.Duration:
    27  		s, err := strconv.ParseFloat(string(val), 64)
    28  		if err != nil {
    29  			return err
    30  		}
    31  
    32  		d := time.Duration(s * float64(time.Second))
    33  		field.Set(reflect.ValueOf(d))
    34  
    35  	case bool:
    36  		var b bool
    37  		var err error
    38  
    39  		values := f.Tag.Get("enum")
    40  		switch values {
    41  		case "":
    42  			b, err = strconv.ParseBool(string(val))
    43  
    44  		default:
    45  			e := getEnum(values)
    46  			i, err := e.Index(string(val))
    47  			if err != nil {
    48  				return err
    49  			}
    50  
    51  			if i > 0 {
    52  				b = true
    53  			}
    54  		}
    55  
    56  		if err != nil {
    57  			return err
    58  		}
    59  		field.SetBool(b)
    60  
    61  	case string:
    62  		s := string(val)
    63  
    64  		values := f.Tag.Get("enum")
    65  		switch values {
    66  		case "":
    67  			s, err := strconv.Unquote(s)
    68  			if err != nil {
    69  				return err
    70  			}
    71  
    72  			field.SetString(s)
    73  
    74  		default:
    75  			e := getEnum(values)
    76  			if _, err := e.Test(s); err != nil {
    77  				return err
    78  			}
    79  
    80  			field.SetString(s)
    81  		}
    82  
    83  	case int, int8, int16, int32, int64:
    84  		i, err := strconv.ParseInt(string(val), 10, 0)
    85  		if err != nil {
    86  			return err
    87  		}
    88  
    89  		field.SetInt(i)
    90  
    91  	case uint, uint8, uint16, uint32, uint64:
    92  		u, err := strconv.ParseUint(string(val), 10, 0)
    93  		if err != nil {
    94  			return err
    95  		}
    96  
    97  		field.SetUint(u)
    98  
    99  	case float32:
   100  		f, err := strconv.ParseFloat(string(val), 32)
   101  		if err != nil {
   102  			return err
   103  		}
   104  		field.SetFloat(f)
   105  
   106  	case float64:
   107  		f, err := strconv.ParseFloat(string(val), 64)
   108  		if err != nil {
   109  			return err
   110  		}
   111  		field.SetFloat(f)
   112  
   113  	case []string:
   114  		s, err := strconv.Unquote(string(val))
   115  		if err != nil {
   116  			return err
   117  		}
   118  
   119  		values := strings.Split(s, f.Tag.Get("delim"))
   120  
   121  		field.Set(reflect.AppendSlice(field, reflect.ValueOf(values)))
   122  
   123  	case []int:
   124  		s, err := strconv.Unquote(string(val))
   125  		if err != nil {
   126  			return err
   127  		}
   128  
   129  		values := strings.Split(s, f.Tag.Get("delim"))
   130  
   131  		var ints []int
   132  		for _, value := range values {
   133  			i, err := strconv.Atoi(value)
   134  			if err != nil {
   135  				return err
   136  			}
   137  
   138  			ints = append(ints, i)
   139  		}
   140  		field.Set(reflect.AppendSlice(field, reflect.ValueOf(ints)))
   141  
   142  	case []byte:
   143  		s := string(val)
   144  
   145  		if !strings.HasPrefix(s, "0x") {
   146  			return fmt.Errorf("hexidecimal-sequence does not start with 0x")
   147  		}
   148  
   149  		b, err := hex.DecodeString(s[2:])
   150  		if err != nil {
   151  			return err
   152  		}
   153  
   154  		field.SetBytes(b)
   155  
   156  	default:
   157  		return fmt.Errorf("unknown attribute-list field of type %T", field.Interface())
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  func unmarshalAttributeList(val interface{}, value []byte) error {
   164  	v := reflect.ValueOf(val)
   165  	if v.Kind() != reflect.Ptr || v.IsNil() {
   166  		return fmt.Errorf("m3u8.unmarshalAttributeList on non-pointer: %v", v.Kind())
   167  	}
   168  
   169  	v = v.Elem()
   170  
   171  	if v.Kind() != reflect.Struct {
   172  		return fmt.Errorf("m3u8.unmarshalAttributeList on non-struct pointer: %v", v.Kind())
   173  	}
   174  
   175  	typ := v.Type()
   176  
   177  	var values [][]byte
   178  
   179  	for len(value) > 0 {
   180  		i := bytes.IndexAny(value, "=,")
   181  		if i < 0 {
   182  			values = append(values, value[0:len(value):len(value)])
   183  			break
   184  		}
   185  
   186  		if value[i] == '=' {
   187  			i++
   188  
   189  			var inQuotes bool
   190  			for ; i < len(value); i++ {
   191  				if value[i] == '"' {
   192  					inQuotes = !inQuotes
   193  					continue
   194  				}
   195  
   196  				if !inQuotes && value[i] == ',' {
   197  					break
   198  				}
   199  
   200  				if inQuotes && value[i] == '\\' {
   201  					i++
   202  				}
   203  			}
   204  		}
   205  
   206  		values = append(values, value[0:i:i])
   207  		if i == len(value) {
   208  			value = nil
   209  			continue
   210  		}
   211  		value = value[i+1:]
   212  	}
   213  
   214  	for _, value := range values {
   215  		var wasSet bool
   216  
   217  		for i := 0; i < typ.NumField(); i++ {
   218  			field := v.Field(i)
   219  			if !field.CanSet() {
   220  				continue
   221  			}
   222  
   223  			f := typ.Field(i)
   224  			name := strings.ToUpper(f.Name)
   225  
   226  			if tag := f.Tag.Get("m3u8"); tag != "" {
   227  				fields := strings.Split(tag, ",")
   228  
   229  				if fields[0] != "" {
   230  					name = fields[0]
   231  				}
   232  
   233  				if name == "-" {
   234  					continue
   235  				}
   236  			}
   237  
   238  			if !bytes.HasPrefix(value, []byte(name)) {
   239  				continue
   240  			}
   241  
   242  			value = value[len(name):]
   243  			if value[0] == '=' {
   244  				value = value[1:]
   245  			}
   246  
   247  			if field.Kind() == reflect.Ptr {
   248  				if field.IsNil() {
   249  					p := reflect.New(f.Type.Elem())
   250  					field.Set(p)
   251  				}
   252  
   253  				field = field.Elem()
   254  			}
   255  
   256  			switch field.Interface().(type) {
   257  			case map[string]interface{}:
   258  				if field.IsNil() {
   259  					m := reflect.MakeMap(f.Type)
   260  					field.Set(m)
   261  				}
   262  
   263  				i := bytes.IndexByte(value, '=')
   264  				key := reflect.ValueOf(string(value[:i]))
   265  
   266  				var val interface{}
   267  
   268  				v := string(value[i+1:])
   269  
   270  				if strings.HasPrefix(v, "0x") {
   271  					b, err := hex.DecodeString(v[2:])
   272  					if err != nil {
   273  						return err
   274  					}
   275  					val = b
   276  
   277  				} else if s, err := strconv.Unquote(v); err == nil {
   278  					val = s
   279  
   280  				} else if f, err := strconv.ParseFloat(v, 64); err == nil {
   281  					val = f
   282  
   283  				} else {
   284  					return fmt.Errorf("%s: invalid client-attribute: %s", key, v)
   285  				}
   286  
   287  				field.SetMapIndex(key, reflect.ValueOf(val))
   288  				wasSet = true
   289  				continue
   290  			}
   291  
   292  			err := unmarshalAttributeField(field, f, value)
   293  			if err != nil {
   294  				return fmt.Errorf("%s: %v", name, err)
   295  			}
   296  
   297  			wasSet = true
   298  		}
   299  
   300  		if !wasSet {
   301  			return fmt.Errorf("unknown attribute-list field: %q", value)
   302  		}
   303  	}
   304  
   305  	return nil
   306  }
   307  
   308  func marshalAttributeField(field reflect.Value, f reflect.StructField) (s string, omit bool, err error) {
   309  	switch v := field.Interface().(type) {
   310  	case Resolution:
   311  		omit = (v.Width + v.Height) == 0
   312  		s = v.String()
   313  
   314  	case time.Time:
   315  		omit = v.IsZero()
   316  
   317  		format := f.Tag.Get("format")
   318  		switch format {
   319  		case "":
   320  			s = v.String()
   321  		default:
   322  			s = v.Format(format)
   323  		}
   324  
   325  	case time.Duration:
   326  		f := v.Seconds()
   327  
   328  		omit = v == 0
   329  		s = strconv.FormatFloat(f, 'f', -1, 64)
   330  
   331  	case fmt.Stringer:
   332  		s = v.String()
   333  		omit = s == ""
   334  		s = strconv.Quote(s)
   335  
   336  	case bool:
   337  		omit = !v
   338  
   339  		values := f.Tag.Get("enum")
   340  		switch values {
   341  		case "":
   342  			s = strconv.FormatBool(v)
   343  
   344  		default:
   345  			e := getEnum(values)
   346  			i := 0
   347  			if v {
   348  				i = 1
   349  			}
   350  			s, _ = e.Value(i)
   351  		}
   352  
   353  	case string:
   354  		omit = v == ""
   355  
   356  		values := f.Tag.Get("enum")
   357  		switch values {
   358  		case "":
   359  			s = strconv.Quote(v)
   360  
   361  		default:
   362  			e := getEnum(values)
   363  			s, err = e.Test(v)
   364  		}
   365  
   366  	case int, int8, int16, int32, int64:
   367  		i := field.Int()
   368  
   369  		omit = i == 0
   370  		s = fmt.Sprintf("%d", i)
   371  
   372  	case uint, uint8, uint16, uint32, uint64:
   373  		u := field.Uint()
   374  
   375  		omit = u == 0
   376  		s = fmt.Sprintf("%d", u)
   377  
   378  	case float32:
   379  		omit = v == 0
   380  		s = strconv.FormatFloat(float64(v), 'f', -1, 32)
   381  
   382  	case float64:
   383  		omit = v == 0
   384  		s = strconv.FormatFloat(v, 'f', -1, 64)
   385  
   386  	case []string:
   387  		omit = len(v) == 0
   388  		s = strconv.Quote(strings.Join(v, f.Tag.Get("delim")))
   389  
   390  	case []int:
   391  		omit = len(v) == 0
   392  
   393  		var fields []string
   394  		for _, field := range v {
   395  			fields = append(fields, fmt.Sprint(field))
   396  		}
   397  
   398  		s = strconv.Quote(strings.Join(fields, f.Tag.Get("delim")))
   399  
   400  	case []byte:
   401  		omit = len(v) == 0
   402  		s = hex.EncodeToString(v)
   403  
   404  	default:
   405  		return "", false, fmt.Errorf("unknown attribute-list field of type %T", v)
   406  	}
   407  
   408  	return s, omit, err
   409  }
   410  
   411  func marshalAttributeList(val interface{}) (string, error) {
   412  	v := reflect.ValueOf(val)
   413  	if v.Kind() != reflect.Ptr || v.IsNil() {
   414  		return "", fmt.Errorf("m3u8.unmarshalAttributeList on non-pointer: %v", v.Kind())
   415  	}
   416  
   417  	v = v.Elem()
   418  
   419  	if v.Kind() != reflect.Struct {
   420  		return "", fmt.Errorf("m3u8.unmarshalAttributeList on non-struct pointer: %v", v.Kind())
   421  	}
   422  
   423  	typ := v.Type()
   424  
   425  	var list []string
   426  
   427  	for i := 0; i < typ.NumField(); i++ {
   428  		field := v.Field(i)
   429  		if !field.CanSet() {
   430  			continue
   431  		}
   432  
   433  		f := typ.Field(i)
   434  		name := strings.ToUpper(f.Name)
   435  
   436  		var optional bool
   437  
   438  		if tag := f.Tag.Get("m3u8"); tag != "" {
   439  			fields := strings.Split(tag, ",")
   440  
   441  			if fields[0] != "" {
   442  				name = fields[0]
   443  			}
   444  
   445  			if name == "-" {
   446  				continue
   447  			}
   448  
   449  			for _, field := range fields[1:] {
   450  				switch {
   451  				case field == "optional":
   452  					optional = true
   453  				}
   454  			}
   455  		}
   456  
   457  		if field.Kind() == reflect.Ptr {
   458  			if field.IsNil() {
   459  				p := reflect.New(f.Type.Elem())
   460  				field.Set(p)
   461  			}
   462  
   463  			if _, ok := field.Interface().(fmt.Stringer); !ok {
   464  				field = field.Elem()
   465  			}
   466  		}
   467  
   468  		switch v := field.Interface().(type) {
   469  		case map[string]interface{}:
   470  			if len(v) == 0 {
   471  				continue
   472  			}
   473  
   474  			var keys []string
   475  			for key := range v {
   476  				keys = append(keys, key)
   477  			}
   478  			sort.Strings(keys)
   479  
   480  			for _, key := range keys {
   481  				field := v[key]
   482  				var s string
   483  
   484  				switch v := field.(type) {
   485  				case string:
   486  					s = strconv.Quote(v)
   487  				case int, int8, int16, int32, int64:
   488  					s = fmt.Sprintf("%d", v)
   489  				case uint, uint8, uint16, uint32, uint64:
   490  					s = fmt.Sprintf("%d", v)
   491  				case float32:
   492  					s = strconv.FormatFloat(float64(v), 'f', -1, 32)
   493  				case float64:
   494  					s = strconv.FormatFloat(float64(v), 'f', -1, 64)
   495  				case []byte:
   496  					s = fmt.Sprintf("0x%02X", v)
   497  				default:
   498  					return "", fmt.Errorf("unknown client-attribute field %s of type %T", key, v)
   499  				}
   500  
   501  				list = append(list, fmt.Sprintf("%s%s=%s", name, key, s))
   502  			}
   503  			continue
   504  		}
   505  
   506  		s, omit, err := marshalAttributeField(field, f)
   507  		if err != nil {
   508  			return "", err
   509  		}
   510  
   511  		if omit && optional {
   512  			continue
   513  		}
   514  
   515  		list = append(list, fmt.Sprintf("%s=%s", name, s))
   516  	}
   517  
   518  	return strings.Join(list, ","), nil
   519  }