refactor: reduce cyclomatic complexity in fetchWeekAPI

This commit is contained in:
2026-06-11 12:13:22 +02:00
parent 03ccd54c85
commit ca957b5cdc

View File

@@ -389,6 +389,32 @@ func fetchWeekAPI(ctx context.Context, httpClient *http.Client, token string, ye
return WeekSchedule{}, err return WeekSchedule{}, err
} }
payload, err := fetchWeekAPIPayload(ctx, client, token, year, week, location)
if err != nil {
return WeekSchedule{}, err
}
resolvedYear, resolvedWeek := resolveRequestedISOWeek(year, week)
out := WeekSchedule{
Year: resolvedYear,
Week: resolvedWeek,
Days: map[time.Weekday][]Entry{},
}
for _, item := range payload {
entry, ok := weekEntryFromAPI(item, location)
if !ok {
continue
}
out.Days[entry.Weekday] = append(out.Days[entry.Weekday], entry)
}
return out, nil
}
func fetchWeekAPIPayload(ctx context.Context, client *http.Client, token string, year int, week int, location *time.Location) ([]timetableAnimeAPI, error) {
u, _ := url.Parse("https://animeschedule.net/api/v3/timetables/sub") u, _ := url.Parse("https://animeschedule.net/api/v3/timetables/sub")
q := u.Query() q := u.Query()
if year > 0 && week > 0 { if year > 0 && week > 0 {
@@ -400,7 +426,7 @@ func fetchWeekAPI(ctx context.Context, httpClient *http.Client, token string, ye
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil { if err != nil {
return WeekSchedule{}, fmt.Errorf("create api request: %w", err) return nil, fmt.Errorf("create api request: %w", err)
} }
req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/json") req.Header.Set("Accept", "application/json")
@@ -408,13 +434,13 @@ func fetchWeekAPI(ctx context.Context, httpClient *http.Client, token string, ye
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { if err != nil {
return WeekSchedule{}, fmt.Errorf("api request failed: %w", err) return nil, fmt.Errorf("api request failed: %w", err)
} }
defer func() { _ = res.Body.Close() }() defer func() { _ = res.Body.Close() }()
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(res.Body, netutil.Bytes512)) body, _ := io.ReadAll(io.LimitReader(res.Body, netutil.Bytes512))
return WeekSchedule{}, &HTTPStatusError{ return nil, &HTTPStatusError{
StatusCode: res.StatusCode, StatusCode: res.StatusCode,
URL: u.String(), URL: u.String(),
ContentType: strings.TrimSpace(res.Header.Get("Content-Type")), ContentType: strings.TrimSpace(res.Header.Get("Content-Type")),
@@ -424,72 +450,64 @@ func fetchWeekAPI(ctx context.Context, httpClient *http.Client, token string, ye
var payload []timetableAnimeAPI var payload []timetableAnimeAPI
if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
return WeekSchedule{}, fmt.Errorf("decode timetables api: %w", err) return nil, fmt.Errorf("decode timetables api: %w", err)
} }
resolvedYear := year return payload, nil
resolvedWeek := week }
if resolvedYear == 0 || resolvedWeek == 0 {
resolvedYear, resolvedWeek = time.Now().In(time.Local).ISOWeek() func resolveRequestedISOWeek(year int, week int) (int, int) {
if year > 0 && week > 0 {
return year, week
} }
out := WeekSchedule{ return time.Now().In(time.Local).ISOWeek()
Year: resolvedYear, }
Week: resolvedWeek,
Days: map[time.Weekday][]Entry{},
}
for _, item := range payload { func weekEntryFromAPI(item timetableAnimeAPI, location *time.Location) (Entry, bool) {
title := strings.TrimSpace(item.English) title := strings.TrimSpace(item.English)
if title == "" { if title == "" {
title = strings.TrimSpace(item.Title) title = strings.TrimSpace(item.Title)
} }
if title == "" { if title == "" {
continue return Entry{}, false
}
episodeNumber := item.EpisodeNumber
subtracted := item.SubtractedEpisodeNumber
episodeText := ""
switch {
case subtracted > 0 && subtracted < episodeNumber:
episodeText = fmt.Sprintf("Ep %d-%d", subtracted, episodeNumber)
case episodeNumber > 0:
episodeText = fmt.Sprintf("Ep %d", episodeNumber)
default:
episodeText = "Ep ?"
} }
airType := AirType(strings.ToUpper(strings.TrimSpace(item.AirType))) airType := AirType(strings.ToUpper(strings.TrimSpace(item.AirType)))
if airType != AirTypeSUB { if airType != AirTypeSUB {
continue return Entry{}, false
} }
episodeTime := item.EpisodeDate.In(location) episodeTime := item.EpisodeDate.In(location)
weekday := episodeTime.Weekday()
localTime := episodeTime.Format("15:04")
imageURL := "" return Entry{
if strings.TrimSpace(item.ImageVersionRoute) != "" {
imageURL = "https://img.animeschedule.net/production/assets/public/img/" + strings.TrimLeft(strings.TrimSpace(item.ImageVersionRoute), "/")
}
animeURL := ""
if strings.TrimSpace(item.Route) != "" {
animeURL = "https://animeschedule.net/anime/" + strings.TrimLeft(strings.TrimSpace(item.Route), "/")
}
out.Days[weekday] = append(out.Days[weekday], Entry{
Title: title, Title: title,
AnimeURL: animeURL, AnimeURL: joinURLPath("https://animeschedule.net/anime/", item.Route),
ImageURL: imageURL, ImageURL: joinURLPath("https://img.animeschedule.net/production/assets/public/img/", item.ImageVersionRoute),
EpisodeText: episodeText, EpisodeText: formatEpisodeText(item.EpisodeNumber, item.SubtractedEpisodeNumber),
AirType: airType, AirType: airType,
AirsAt: episodeTime, AirsAt: episodeTime,
LocalTime: localTime, LocalTime: episodeTime.Format("15:04"),
Weekday: weekday, Weekday: episodeTime.Weekday(),
}) }, true
}
func formatEpisodeText(episodeNumber int, subtracted int) string {
switch {
case subtracted > 0 && subtracted < episodeNumber:
return fmt.Sprintf("Ep %d-%d", subtracted, episodeNumber)
case episodeNumber > 0:
return fmt.Sprintf("Ep %d", episodeNumber)
default:
return "Ep ?"
}
}
func joinURLPath(base string, path string) string {
trimmed := strings.TrimSpace(path)
if trimmed == "" {
return ""
} }
return out, nil return base + strings.TrimLeft(trimmed, "/")
} }