nice clean recipes pear.dunkirk.sh
recipes
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: grab largest image

+72 -10
+72 -10
internal/extract/schema/jsonld.go
··· 25 25 if recipe.Description == "" { 26 26 recipe.Description = findMetaDescription(doc) 27 27 } 28 + if recipe.ImageURL == "" || looksSmall(recipe.ImageURL) { 29 + if ogImg := findMetaImage(doc); ogImg != "" { 30 + recipe.ImageURL = ogImg 31 + } 32 + } 28 33 return recipe, true 29 34 } 30 35 } ··· 179 184 } 180 185 181 186 func extractImage(m map[string]interface{}) string { 182 - img := m["image"] 187 + var urls []string 188 + collectImageURLs(m["image"], &urls) 189 + if best := pickBestImage(urls); best != "" { 190 + return best 191 + } 192 + return "" 193 + } 194 + 195 + func collectImageURLs(img interface{}, urls *[]string) { 183 196 switch v := img.(type) { 184 197 case string: 185 - return v 198 + *urls = append(*urls, v) 186 199 case map[string]interface{}: 187 - return strVal(v, "url") 200 + if u := strVal(v, "url"); u != "" { 201 + *urls = append(*urls, u) 202 + } 203 + if u := strVal(v, "contentUrl"); u != "" { 204 + *urls = append(*urls, u) 205 + } 188 206 case []interface{}: 189 - if len(v) > 0 { 190 - switch first := v[0].(type) { 191 - case string: 192 - return first 193 - case map[string]interface{}: 194 - return strVal(first, "url") 207 + for _, item := range v { 208 + collectImageURLs(item, urls) 209 + } 210 + } 211 + } 212 + 213 + func pickBestImage(urls []string) string { 214 + if len(urls) == 0 { 215 + return "" 216 + } 217 + // Prefer URLs that don't look like thumbnails 218 + for _, u := range urls { 219 + if !looksSmall(u) { 220 + return u 221 + } 222 + } 223 + return urls[0] 224 + } 225 + 226 + var smallImageRe = regexp.MustCompile(`[-_]sm\b|[-_]thumb(?:nail)?\b|[-_]small\b|[-_]\d{2,3}x\d{2,3}\b|[-_]\d{2,3}w\b`) 227 + 228 + func looksSmall(u string) bool { 229 + return smallImageRe.MatchString(u) 230 + } 231 + 232 + func findMetaImage(n *html.Node) string { 233 + var result string 234 + var f func(*html.Node) 235 + f = func(n *html.Node) { 236 + if n.Type == html.ElementNode && n.Data == "meta" { 237 + prop := "" 238 + content := "" 239 + for _, a := range n.Attr { 240 + if a.Key == "property" { 241 + prop = a.Val 242 + } 243 + if a.Key == "content" { 244 + content = a.Val 245 + } 246 + } 247 + if prop == "og:image" && content != "" { 248 + result = content 249 + return 250 + } 251 + } 252 + for c := n.FirstChild; c != nil; c = c.NextSibling { 253 + f(c) 254 + if result != "" { 255 + return 195 256 } 196 257 } 197 258 } 198 - return "" 259 + f(n) 260 + return result 199 261 } 200 262 201 263 func extractIngredients(m map[string]interface{}) []models.Ingredient {