diff --git a/Yattee/Services/Navigation/URLRouter.swift b/Yattee/Services/Navigation/URLRouter.swift index 98c9e45f..f3836d7d 100644 --- a/Yattee/Services/Navigation/URLRouter.swift +++ b/Yattee/Services/Navigation/URLRouter.swift @@ -145,9 +145,11 @@ struct URLRouter: Sendable { let trimmed = raw.trimmingCharacters(in: .whitespaces) guard !trimmed.isEmpty else { return nil } - // Plain numeric value (e.g. "90", "90.5") + // Plain numeric value (e.g. "90", "90.5"). Reject non-finite values + // ("inf"/"infinity"/"nan") that `TimeInterval(_:)` accepts — a `?t=inf` + // link would otherwise seek the player to `Double.infinity`. if let value = TimeInterval(trimmed) { - return value >= 0 ? value : nil + return (value.isFinite && value >= 0) ? value : nil } // Compound form like "1h2m3s", "2m30s", "90s" diff --git a/YatteeTests/NavigationTests.swift b/YatteeTests/NavigationTests.swift index aa46c789..c0bcedb5 100644 --- a/YatteeTests/NavigationTests.swift +++ b/YatteeTests/NavigationTests.swift @@ -123,6 +123,20 @@ struct URLRouterTests { #expect(URLRouter.parseTimestampValue("90.5") == 90.5) } + @Test("Reject non-finite and negative plain timestamp values") + func rejectInvalidPlainTimestampValues() { + // Plain numeric values that are accepted by TimeInterval(_:) but are not + // valid seek targets must return nil rather than poisoning the player. + #expect(URLRouter.parseTimestampValue("inf") == nil) + #expect(URLRouter.parseTimestampValue("infinity") == nil) + #expect(URLRouter.parseTimestampValue("nan") == nil) + #expect(URLRouter.parseTimestampValue("-5") == nil) + // Valid plain seconds still parse. + #expect(URLRouter.parseTimestampValue("0") == 0) + #expect(URLRouter.parseTimestampValue("90") == 90) + #expect(URLRouter.parseTimestampValue("90.5") == 90.5) + } + // MARK: - Share Extension Wrapper Tests @Test("Unwrap yattee://open wrapper to inner URL with timestamp")