mirror of
https://github.com/yattee/yattee.git
synced 2026-10-25 14:12:53 +00:00
Open more YouTube link types and playlists from links
Route YouTube search results links to search and nocookie embeds to the video, match YouTube hosts exactly, and report YouTube pages without a video as unsupported instead of sending them to yt-dlp. A watch link inside a playlist shows both the video and the playlist when loaded, and plays the video with the playlist queued around it. URL scheme play, queue and bookmark actions accept playlist links.
This commit is contained in:
@@ -37,11 +37,25 @@ final class OpenedLinksViewModel {
|
||||
private static let maxConcurrentResolutions = 4
|
||||
|
||||
init(urls: [URL]) {
|
||||
// A watch link inside a playlist (`watch?v=X&list=Y`) shows both the
|
||||
// video and the playlist
|
||||
let router = URLRouter()
|
||||
let expanded = urls.flatMap { url -> [URL] in
|
||||
guard let listID = router.youTubePlaylistContext(url),
|
||||
let playlistURL = Self.youTubePlaylistURL(listID) else { return [url] }
|
||||
return [url, playlistURL]
|
||||
}
|
||||
var seen = Set<String>()
|
||||
let unique = urls.filter { seen.insert($0.absoluteString).inserted }
|
||||
let unique = expanded.filter { seen.insert($0.absoluteString).inserted }
|
||||
links = unique.enumerated().map { OpenedLink(id: $0.offset, url: $0.element) }
|
||||
}
|
||||
|
||||
private static func youTubePlaylistURL(_ listID: String) -> URL? {
|
||||
var components = URLComponents(string: "https://www.youtube.com/playlist")
|
||||
components?.queryItems = [URLQueryItem(name: "list", value: listID)]
|
||||
return components?.url
|
||||
}
|
||||
|
||||
/// Resolved videos, in link order.
|
||||
var videos: [Video] {
|
||||
links.compactMap { link in
|
||||
@@ -111,13 +125,9 @@ final class OpenedLinksViewModel {
|
||||
case .video(.loaded(let video), _):
|
||||
return .video(video)
|
||||
|
||||
case .playlist(.remote(let playlistID, let instance, _, _)):
|
||||
guard let instance = instance
|
||||
?? playlistID.source.flatMap({ appEnvironment.instancesManager.playlistInstance(for: $0) }) else {
|
||||
return .unresolved(destination, message: String(localized: "openLink.noInstance"))
|
||||
}
|
||||
case .playlist(let source):
|
||||
do {
|
||||
let playlist = try await appEnvironment.contentService.playlist(id: playlistID.playlistID, instance: instance)
|
||||
let playlist = try await LinkVideoResolver(appEnvironment: appEnvironment).playlist(for: source)
|
||||
return .playlist(playlist, destination: destination)
|
||||
} catch {
|
||||
return .unresolved(destination, message: error.localizedDescription)
|
||||
|
||||
Reference in New Issue
Block a user