Fix panic when scraping with unknown field (#6220)

* Fix URL in group scraper causing panic
* Return error instead of panicking on unknown field
This commit is contained in:
WithoutPants 2025-10-31 19:54:35 +11:00 committed by GitHub
parent 336fa3b70e
commit 1b2b4c5221
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 40 deletions

View file

@ -462,6 +462,7 @@ type ScrapedGroup struct {
Date *string `json:"date"` Date *string `json:"date"`
Rating *string `json:"rating"` Rating *string `json:"rating"`
Director *string `json:"director"` Director *string `json:"director"`
URL *string `json:"url"` // included for backward compatibility
URLs []string `json:"urls"` URLs []string `json:"urls"`
Synopsis *string `json:"synopsis"` Synopsis *string `json:"synopsis"`
Studio *ScrapedStudio `json:"studio"` Studio *ScrapedStudio `json:"studio"`

View file

@ -873,50 +873,55 @@ func (r mappedResult) apply(dest interface{}) {
func mapFieldValue(destVal reflect.Value, key string, value interface{}) error { func mapFieldValue(destVal reflect.Value, key string, value interface{}) error {
field := destVal.FieldByName(key) field := destVal.FieldByName(key)
if !field.IsValid() {
return fmt.Errorf("field %s does not exist on %s", key, destVal.Type().Name())
}
if !field.CanSet() {
return fmt.Errorf("field %s cannot be set on %s", key, destVal.Type().Name())
}
fieldType := field.Type() fieldType := field.Type()
if field.IsValid() && field.CanSet() { switch v := value.(type) {
switch v := value.(type) { case string:
case string: // if the field is a pointer to a string, then we need to convert the string to a pointer
// if the field is a pointer to a string, then we need to convert the string to a pointer // if the field is a string slice, then we need to convert the string to a slice
// if the field is a string slice, then we need to convert the string to a slice switch {
switch { case fieldType.Kind() == reflect.String:
case fieldType.Kind() == reflect.String: field.SetString(v)
field.SetString(v) case fieldType.Kind() == reflect.Ptr && fieldType.Elem().Kind() == reflect.String:
case fieldType.Kind() == reflect.Ptr && fieldType.Elem().Kind() == reflect.String: ptr := reflect.New(fieldType.Elem())
ptr := reflect.New(fieldType.Elem()) ptr.Elem().SetString(v)
ptr.Elem().SetString(v) field.Set(ptr)
field.Set(ptr) case fieldType.Kind() == reflect.Slice && fieldType.Elem().Kind() == reflect.String:
case fieldType.Kind() == reflect.Slice && fieldType.Elem().Kind() == reflect.String: field.Set(reflect.ValueOf([]string{v}))
field.Set(reflect.ValueOf([]string{v}))
default:
return fmt.Errorf("cannot convert %T to %s", value, fieldType)
}
case []string:
// expect the field to be a string slice
if fieldType.Kind() == reflect.Slice && fieldType.Elem().Kind() == reflect.String {
field.Set(reflect.ValueOf(v))
} else {
return fmt.Errorf("cannot convert %T to %s", value, fieldType)
}
default: default:
// fallback to reflection return fmt.Errorf("cannot convert %T to %s", value, fieldType)
reflectValue := reflect.ValueOf(value) }
reflectValueType := reflectValue.Type() case []string:
// expect the field to be a string slice
switch { if fieldType.Kind() == reflect.Slice && fieldType.Elem().Kind() == reflect.String {
case reflectValueType.ConvertibleTo(fieldType): field.Set(reflect.ValueOf(v))
field.Set(reflectValue.Convert(fieldType)) } else {
case fieldType.Kind() == reflect.Pointer && reflectValueType.ConvertibleTo(fieldType.Elem()): return fmt.Errorf("cannot convert %T to %s", value, fieldType)
ptr := reflect.New(fieldType.Elem()) }
ptr.Elem().Set(reflectValue.Convert(fieldType.Elem())) default:
field.Set(ptr) // fallback to reflection
default: reflectValue := reflect.ValueOf(value)
return fmt.Errorf("cannot convert %T to %s", value, fieldType) reflectValueType := reflectValue.Type()
}
switch {
case reflectValueType.ConvertibleTo(fieldType):
field.Set(reflectValue.Convert(fieldType))
case fieldType.Kind() == reflect.Pointer && reflectValueType.ConvertibleTo(fieldType.Elem()):
ptr := reflect.New(fieldType.Elem())
ptr.Elem().Set(reflectValue.Convert(fieldType.Elem()))
field.Set(ptr)
default:
return fmt.Errorf("cannot convert %T to %s", value, fieldType)
} }
} else {
return fmt.Errorf("field does not exist or cannot be set")
} }
return nil return nil

View file

@ -143,6 +143,21 @@ func (c Cache) postScrapeMovie(ctx context.Context, m models.ScrapedMovie, exclu
return nil, nil, err return nil, nil, err
} }
// populate URL/URLs
// if URLs are provided, only use those
if len(m.URLs) > 0 {
m.URL = &m.URLs[0]
} else {
urls := []string{}
if m.URL != nil {
urls = append(urls, *m.URL)
}
if len(urls) > 0 {
m.URLs = urls
}
}
// post-process - set the image if applicable // post-process - set the image if applicable
if err := setMovieFrontImage(ctx, c.client, &m, c.globalConfig); err != nil { if err := setMovieFrontImage(ctx, c.client, &m, c.globalConfig); err != nil {
logger.Warnf("could not set front image using URL %s: %v", *m.FrontImage, err) logger.Warnf("could not set front image using URL %s: %v", *m.FrontImage, err)
@ -175,6 +190,21 @@ func (c Cache) postScrapeGroup(ctx context.Context, m models.ScrapedGroup, exclu
return nil, nil, err return nil, nil, err
} }
// populate URL/URLs
// if URLs are provided, only use those
if len(m.URLs) > 0 {
m.URL = &m.URLs[0]
} else {
urls := []string{}
if m.URL != nil {
urls = append(urls, *m.URL)
}
if len(urls) > 0 {
m.URLs = urls
}
}
// post-process - set the image if applicable // post-process - set the image if applicable
if err := setGroupFrontImage(ctx, c.client, &m, c.globalConfig); err != nil { if err := setGroupFrontImage(ctx, c.client, &m, c.globalConfig); err != nil {
logger.Warnf("could not set front image using URL %s: %v", *m.FrontImage, err) logger.Warnf("could not set front image using URL %s: %v", *m.FrontImage, err)