mirror of
https://github.com/yattee/yattee.git
synced 2026-08-06 07:11:28 +00:00
Fix timed links resuming at watch position instead of URL timestamp
Timed links arrive wrapped as yattee://open?url=... from the share extension, but the timestamp was parsed from the wrapper URL, where a single-? form like youtu.be/ID?t=N hides t inside the url query item. With no startTime the player fell back to saved watch progress. - Add URLRouter.unwrapped(_:) resolving the wrapper to the inner URL (raw remainder after ?url=, since the share extension does not encode & and URLComponents would drop &t= parts) and use it in handleDeepLink - Thread forceStartTime through openVideo/playPreferringDownloaded/play so explicit link timestamps beat the 90%-watched restart threshold, clamped to just before the end; resume flows keep the old behavior - Carry the parsed timestamp through OpenLinkSheet's play action, which dropped it entirely - Cover timestamp parsing and wrapper unwrapping in NavigationTests
This commit is contained in:
@@ -107,6 +107,63 @@ struct URLRouterTests {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Timestamp Parsing Tests
|
||||
|
||||
@Test("Parse timestamp from youtu.be short URL")
|
||||
func timestampFromShortURL() {
|
||||
let url = URL(string: "https://youtu.be/GBimVR2VBQU?t=17097")!
|
||||
#expect(router.parseTimestamp(url) == 17097)
|
||||
}
|
||||
|
||||
@Test("Parse compound timestamp value")
|
||||
func compoundTimestampValue() {
|
||||
#expect(URLRouter.parseTimestampValue("1h2m3s") == 3723)
|
||||
#expect(URLRouter.parseTimestampValue("2m30s") == 150)
|
||||
#expect(URLRouter.parseTimestampValue("90s") == 90)
|
||||
#expect(URLRouter.parseTimestampValue("90.5") == 90.5)
|
||||
}
|
||||
|
||||
// MARK: - Share Extension Wrapper Tests
|
||||
|
||||
@Test("Unwrap yattee://open wrapper to inner URL with timestamp")
|
||||
func unwrapOpenWrapper() {
|
||||
// Share extension percent-encoding leaves ?, /, : intact - this is the literal form delivered
|
||||
let wrapper = URL(string: "yattee://open?url=https://youtu.be/GBimVR2VBQU?t=17097")!
|
||||
let inner = router.unwrapped(wrapper)
|
||||
|
||||
#expect(inner.absoluteString == "https://youtu.be/GBimVR2VBQU?t=17097")
|
||||
#expect(router.parseTimestamp(inner) == 17097)
|
||||
|
||||
// Unwrapped URL still routes to the right video
|
||||
if case .video(let source, _) = router.route(inner), case .id(let videoID) = source {
|
||||
#expect(videoID.videoID == "GBimVR2VBQU")
|
||||
} else {
|
||||
Issue.record("Expected video destination")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Unwrap wrapper with ampersand timestamp form")
|
||||
func unwrapOpenWrapperAmpersandForm() {
|
||||
let wrapper = URL(string: "yattee://open?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=120")!
|
||||
let inner = router.unwrapped(wrapper)
|
||||
|
||||
#expect(router.parseTimestamp(inner) == 120)
|
||||
if case .video(let source, _) = router.route(inner), case .id(let videoID) = source {
|
||||
#expect(videoID.videoID == "dQw4w9WgXcQ")
|
||||
} else {
|
||||
Issue.record("Expected video destination")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Unwrapped returns non-wrapper URLs unchanged")
|
||||
func unwrapPassthrough() {
|
||||
let plain = URL(string: "https://youtu.be/dQw4w9WgXcQ?t=42")!
|
||||
#expect(router.unwrapped(plain) == plain)
|
||||
|
||||
let scheme = URL(string: "yattee://subscriptions")!
|
||||
#expect(router.unwrapped(scheme) == scheme)
|
||||
}
|
||||
|
||||
// MARK: - PeerTube URL Tests
|
||||
|
||||
@Test("Parse PeerTube /w/ video URL")
|
||||
|
||||
Reference in New Issue
Block a user