Resolve URL shorteners and prompt for ambiguous description links

Tapping bit.ly/tinyurl/t.co/etc. in a description or comment previously
opened Safari even when the destination was a playable YouTube URL.
Added an opt-in "Resolve Short Links" toggle under YouTube Enhancements
(off by default) that follows the redirect on tap: if the target is a
YouTube/PeerTube/direct-media URL, open it in-app; otherwise prompt the
user before falling back to yt-dlp extraction or the browser.

Also added a confirmation dialog for non-shortener links that only
matched the loose .externalVideo yt-dlp fallback, so arbitrary web
pages in descriptions no longer silently kick off extraction.

Prompts live on NavigationCoordinator and are dual-hosted by YatteeApp
and ExpandedPlayerSheet so they remain visible whether or not the
expanded player is covering the main view.
This commit is contained in:
Arkadiusz Fal
2026-04-23 07:29:57 +02:00
parent d38b781858
commit 5a839da1bd
13 changed files with 511 additions and 2 deletions

View File

@@ -11,4 +11,15 @@ extension Notification.Name {
static let showSettings = Notification.Name("showSettings")
static let showOpenLinkSheet = Notification.Name("showOpenLinkSheet")
static let openDescriptionLink = Notification.Name("openDescriptionLink")
/// Posted when a URL shortener (bit.ly, etc.) has been resolved to an
/// ambiguous destination the app isn't certain it can play it, so the
/// user is prompted whether to try opening it in Yattee or in the browser.
/// `object` is the resolved `URL`.
static let promptResolvedShortLink = Notification.Name("promptResolvedShortLink")
/// Posted when a tapped link is not confidently a video (no YouTube /
/// PeerTube / direct-media match) but could potentially be extracted via
/// the Yattee server / yt-dlp. User is prompted whether to try extracting
/// or open it in the system browser instead.
/// `object` is the candidate `URL`.
static let promptAmbiguousExternalLink = Notification.Name("promptAmbiguousExternalLink")
}

View File

@@ -41,6 +41,9 @@ enum SettingsKey: String, CaseIterable {
case deArrowAPIURL
case deArrowThumbnailAPIURL
// Short link resolution
case resolveShortLinksEnabled
// Platform-specific
case macPlayerMode
case playerSheetAutoResize

View File

@@ -87,4 +87,21 @@ extension SettingsManager {
set(newValue, for: .deArrowThumbnailAPIURL)
}
}
// MARK: - Short Link Resolution
/// When enabled, taps on known URL shorteners (bit.ly, tinyurl, t.co, ) in
/// descriptions and comments follow the redirect and, if the destination is a
/// supported YouTube/PeerTube URL, open it in-app. Off by default because it
/// performs a network request to the shortener host on tap.
var resolveShortLinksEnabled: Bool {
get {
if let cached = _resolveShortLinksEnabled { return cached }
return bool(for: .resolveShortLinksEnabled, default: false)
}
set {
_resolveShortLinksEnabled = newValue
set(newValue, for: .resolveShortLinksEnabled)
}
}
}

View File

@@ -54,6 +54,9 @@ final class SettingsManager {
var _deArrowAPIURL: String?
var _deArrowThumbnailAPIURL: String?
// Short link resolution
var _resolveShortLinksEnabled: Bool?
// User Agent
var _customUserAgent: String?
var _randomizeUserAgentPerRequest: Bool?
@@ -419,6 +422,7 @@ final class SettingsManager {
_deArrowReplaceThumbnails = nil
_deArrowAPIURL = nil
_deArrowThumbnailAPIURL = nil
_resolveShortLinksEnabled = nil
_customUserAgent = nil
_randomizeUserAgentPerRequest = nil
_feedCacheValidityMinutes = nil