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 }