diff --git a/internal/reader/processor/utils.go b/internal/reader/processor/utils.go index c133fcc9..a57c0f21 100644 --- a/internal/reader/processor/utils.go +++ b/internal/reader/processor/utils.go @@ -6,53 +6,56 @@ package processor // import "miniflux.app/v2/internal/reader/processor" import ( "errors" "fmt" - "regexp" "strconv" + "strings" "time" "github.com/tdewolff/minify/v2" "github.com/tdewolff/minify/v2/html" ) -// TODO: use something less horrible than a regex to parse ISO 8601 durations. - -var ( - iso8601Regex = regexp.MustCompile(`^P((?P\d+)Y)?((?P\d+)M)?((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$`) -) - +// parseISO8601 parses a restricted subset of ISO8601 dates, mainly for youtube video durations func parseISO8601(from string) (time.Duration, error) { - var match []string + after, ok := strings.CutPrefix(from, "PT") + if !ok { + return 0, errors.New("the period doesn't start with PT") + } + var d time.Duration + num := "" - if iso8601Regex.MatchString(from) { - match = iso8601Regex.FindStringSubmatch(from) - } else { - return 0, errors.New("processor: could not parse duration string") - } + for _, char := range after { + var val float64 + var err error - for i, name := range iso8601Regex.SubexpNames() { - part := match[i] - if i == 0 || name == "" || part == "" { - continue - } - - val, err := strconv.ParseInt(part, 10, 64) - if err != nil { - return 0, err - } - - switch name { - case "hour": + switch char { + case 'Y', 'W', 'D': + return 0, fmt.Errorf("the '%c' specifier isn't supported", char) + case 'H': + if val, err = strconv.ParseFloat(num, 64); err != nil { + return 0, err + } d += time.Duration(val) * time.Hour - case "minute": + num = "" + case 'M': + if val, err = strconv.ParseFloat(num, 64); err != nil { + return 0, err + } d += time.Duration(val) * time.Minute - case "second": + num = "" + case 'S': + if val, err = strconv.ParseFloat(num, 64); err != nil { + return 0, err + } d += time.Duration(val) * time.Second + num = "" + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': + num += string(char) + continue default: - return 0, fmt.Errorf("processor: unknown field %s", name) + return 0, errors.New("invalid character in the period") } } - return d, nil }