From ed6f836d4d25337c95bcc8d4d736072ef16197d9 Mon Sep 17 00:00:00 2001 From: AdiosF6F <62105+Adios@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:23:04 +0800 Subject: [PATCH] fix(onsen): Handle numeric delivery_date from API The application crashes with a JSON unmarshaling error when processing data from a paid member session. This is caused by the `delivery_date` field, which is expected to be a "MM/DD" string, sometimes being sent as a number (e.g., 23744) for older episodes accessible to paid members. This commit fixes the crash and correctly handles both data types by implementing a prioritized date-finding strategy, preserving the original logic's structure. 1. The `delivery_date` field in the `nuxt.Content` struct is changed to `interface{}` to accept both strings and numbers, preventing the initial crash. 2. The `Episodes()` method is augmented to be type-aware, following a strict priority for determining an episode's date: - **Priority 1**: If `delivery_date` is a string, the original, preferred `GuessTime` logic is used. - **Last Resort**: If `delivery_date` is a number, a new helper function, `dateFromURL`, is called to parse the full date from the episode's `streaming_url` filename. This is only used when the string date is not present. 3. The initial "anchor-finding" loop is also made type-safe to avoid errors when processing episodes with numeric dates. This resolves the bug for paid sessions by correctly parsing dates from the streaming URL as a fallback, while ensuring the original guessing logic is preserved for all other cases, preventing regressions. --- onsen/nuxt/nuxt.go | 2 +- onsen/onsen.go | 48 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/onsen/nuxt/nuxt.go b/onsen/nuxt/nuxt.go index 45462b3..1f195f6 100644 --- a/onsen/nuxt/nuxt.go +++ b/onsen/nuxt/nuxt.go @@ -63,7 +63,7 @@ type Content struct { MediaType string `json:"media_type"` Premium bool `json:"premium"` ProgramId int `json:"program_id"` - DeliveryDate string `json:"delivery_date"` + DeliveryDate interface{} `json:"delivery_date"` Movie bool `json:"movie"` PosterImageUrl interface{} `json:"poster_image_url"` StreamingUrl *string `json:"streaming_url"` diff --git a/onsen/onsen.go b/onsen/onsen.go index c5ab7c7..03ae230 100644 --- a/onsen/onsen.go +++ b/onsen/onsen.go @@ -251,8 +251,13 @@ func (r Radio) Episodes() []Episode { continue } + var deliveryDateStr string + if s, isString := episode.DeliveryDate.(string); isString { + deliveryDateStr = s + } + reDay := regexp.MustCompile(`^\d{1,2}\/(\d{1,2})$`) - mDay := reDay.FindStringSubmatch(episode.DeliveryDate) + mDay := reDay.FindStringSubmatch(deliveryDateStr) if len(mDay) < 2 { continue } @@ -272,8 +277,15 @@ func (r Radio) Episodes() []Episode { for i := range r.Raw.Contents { e := Episode{Raw: &r.Raw.Contents[i]} - // Guess the current date using the reference time, which is updated after each successful guess. - currentDate, ok := GuessTime(e.Raw.DeliveryDate, ref) + var currentDate time.Time + var ok bool + + if deliveryDateStr, isString := e.Raw.DeliveryDate.(string); isString { + currentDate, ok = GuessTime(deliveryDateStr, ref) + } else if _, isNum := e.Raw.DeliveryDate.(float64); isNum { + currentDate, ok = e.dateFromURL(loc) + } + if ok { e.GuessedDate = currentDate // The new reference for the next (older) episode is the date we just determined. @@ -290,6 +302,36 @@ type Episode struct { GuessedDate time.Time } +var reDateFromURL = regexp.MustCompile(`[a-z]+(\d{2})(\d{2})(\d{2})`) + +// dateFromURL tries to parse a date from the streaming URL. +func (e Episode) dateFromURL(loc *time.Location) (time.Time, bool) { + url, ok := e.Manifest() + if !ok { + return time.Time{}, false + } + + m := reDateFromURL.FindStringSubmatch(url) + if m == nil { + return time.Time{}, false + } + + year, err := strconv.Atoi("20" + m[1]) + if err != nil { + return time.Time{}, false + } + month, err := strconv.Atoi(m[2]) + if err != nil || month < 1 || month > 12 { + return time.Time{}, false + } + day, err := strconv.Atoi(m[3]) + if err != nil || day < 1 || day > 31 { + return time.Time{}, false + } + + return time.Date(year, time.Month(month), day, 0, 0, 0, 0, loc), true +} + func (e Episode) Id() int { return e.Raw.Id }