feat: implement episode image fallback for banned youtube icons
This commit is contained in:
@@ -63,6 +63,10 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
for i := range episodes.Data {
|
||||
episodes.Data[i].Images.Jpg.ImageURL = episodes.Data[i].GetFallbackImage(id)
|
||||
}
|
||||
|
||||
sort.Slice(episodes.Data, func(i, j int) bool {
|
||||
return episodes.Data[i].MalID < episodes.Data[j].MalID
|
||||
})
|
||||
|
||||
@@ -3,9 +3,79 @@ package jikan
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const bannedImageURL = "https://myanimelist.net/images/icon-banned-youtube.png"
|
||||
|
||||
var httpClient = &http.Client{Timeout: 10 * time.Second}
|
||||
|
||||
func (e *Episode) GetFallbackImage(animeID int) string {
|
||||
if e.Images == nil || e.Images.Jpg.ImageURL != bannedImageURL {
|
||||
return e.Images.Jpg.ImageURL
|
||||
}
|
||||
|
||||
episodeNum := 1
|
||||
if e.Episode != "" {
|
||||
episodeNum, _ = strconv.Atoi(e.Episode)
|
||||
}
|
||||
|
||||
episodeURL := fmt.Sprintf("https://myanimelist.net/anime/%d/episode/%d", animeID, episodeNum)
|
||||
fallbackURL := scrapeAnimeImageFromEpisodePage(episodeURL, episodeNum)
|
||||
if fallbackURL != "" {
|
||||
return fallbackURL
|
||||
}
|
||||
|
||||
return e.Images.Jpg.ImageURL
|
||||
}
|
||||
|
||||
func scrapeAnimeImageFromEpisodePage(episodeURL string, episodeNum int) string {
|
||||
req, err := http.NewRequest("GET", episodeURL, nil)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
// Setting User-Agent is important for MAL
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return ""
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
html := string(body)
|
||||
|
||||
// Look for the JSON data in MAL.episodeVideo.aroundVideos
|
||||
// We extract the object {} containing "episode_number":X
|
||||
episodeStr := strconv.Itoa(episodeNum)
|
||||
objPattern := regexp.MustCompile(`\{[^{}]*"episode_number":\s*` + episodeStr + `[^{}]*\}`)
|
||||
match := objPattern.FindString(html)
|
||||
if match != "" {
|
||||
thumbRe := regexp.MustCompile(`"thumbnail":\s*"([^"]+)"`)
|
||||
thumbMatch := thumbRe.FindStringSubmatch(match)
|
||||
if len(thumbMatch) > 1 {
|
||||
// Unescape backslashes in URL
|
||||
return strings.ReplaceAll(thumbMatch[1], `\/`, `/`)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Client) GetEpisodes(ctx context.Context, animeID int, page int) (EpisodesResponse, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
|
||||
Reference in New Issue
Block a user