mirror of
https://github.com/yattee/yattee.git
synced 2026-02-20 01:39:46 +00:00
Yattee v2 rewrite
This commit is contained in:
88
Yattee/Models/Player/QueueSource.swift
Normal file
88
Yattee/Models/Player/QueueSource.swift
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// QueueSource.swift
|
||||
// Yattee
|
||||
//
|
||||
// Tracks the origin of queued videos for continuation loading.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Tracks the origin of queued videos for continuation loading.
|
||||
enum QueueSource: Codable, Equatable, Hashable, Sendable {
|
||||
/// Videos from a channel's video list.
|
||||
case channel(channelID: String, source: ContentSource, continuation: String?)
|
||||
|
||||
/// Videos from a playlist.
|
||||
case playlist(playlistID: String, continuation: String?)
|
||||
|
||||
/// Videos from search results.
|
||||
case search(query: String, continuation: String?)
|
||||
|
||||
/// Videos from subscriptions feed.
|
||||
case subscriptions(continuation: String?)
|
||||
|
||||
/// Manually added individual videos (no continuation).
|
||||
case manual
|
||||
|
||||
/// Videos from a media browser folder (WebDAV/SMB/local folder).
|
||||
/// Folder contents are loaded upfront, so no continuation is needed.
|
||||
case mediaBrowser(sourceID: UUID, folderPath: String)
|
||||
|
||||
/// Whether this source supports loading more items.
|
||||
var supportsContinuation: Bool {
|
||||
switch self {
|
||||
case .channel(_, _, let continuation),
|
||||
.playlist(_, let continuation),
|
||||
.search(_, let continuation),
|
||||
.subscriptions(let continuation):
|
||||
return continuation != nil
|
||||
case .manual, .mediaBrowser:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// The continuation token, if available.
|
||||
var continuation: String? {
|
||||
switch self {
|
||||
case .channel(_, _, let continuation),
|
||||
.playlist(_, let continuation),
|
||||
.search(_, let continuation),
|
||||
.subscriptions(let continuation):
|
||||
return continuation
|
||||
case .manual, .mediaBrowser:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new QueueSource with an updated continuation token.
|
||||
func withContinuation(_ newContinuation: String?) -> QueueSource {
|
||||
switch self {
|
||||
case .channel(let channelID, let source, _):
|
||||
return .channel(channelID: channelID, source: source, continuation: newContinuation)
|
||||
case .playlist(let playlistID, _):
|
||||
return .playlist(playlistID: playlistID, continuation: newContinuation)
|
||||
case .search(let query, _):
|
||||
return .search(query: query, continuation: newContinuation)
|
||||
case .subscriptions:
|
||||
return .subscriptions(continuation: newContinuation)
|
||||
case .manual:
|
||||
return .manual
|
||||
case .mediaBrowser:
|
||||
return self // No continuation for media browser
|
||||
}
|
||||
}
|
||||
|
||||
/// The content source for this queue source, used for instance selection.
|
||||
/// Returns nil for sources that don't use API-based continuation (manual, mediaBrowser).
|
||||
var contentSource: ContentSource? {
|
||||
switch self {
|
||||
case .channel(_, let source, _):
|
||||
return source
|
||||
case .playlist, .search, .subscriptions:
|
||||
// Playlists, search, and subscriptions are YouTube content
|
||||
return .global(provider: ContentSource.youtubeProvider)
|
||||
case .manual, .mediaBrowser:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
93
Yattee/Models/Player/VideoQueueContext.swift
Normal file
93
Yattee/Models/Player/VideoQueueContext.swift
Normal file
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// VideoQueueContext.swift
|
||||
// Yattee
|
||||
//
|
||||
// Context information for video queue when navigating to video details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Closure type for loading more videos via continuation
|
||||
typealias LoadMoreVideosCallback = @Sendable () async throws -> ([Video], String?)
|
||||
|
||||
/// Context information for playing a video with queue support.
|
||||
/// Used when navigating from list views (subscriptions, search, etc.) to video info pages.
|
||||
struct VideoQueueContext {
|
||||
/// The video being viewed
|
||||
let video: Video
|
||||
|
||||
/// Queue source for continuation loading
|
||||
let queueSource: QueueSource?
|
||||
|
||||
/// Display label for the queue source (e.g., "Subscriptions", "Search Results")
|
||||
let sourceLabel: String?
|
||||
|
||||
/// All videos in the current list
|
||||
let videoList: [Video]?
|
||||
|
||||
/// Index of the current video in the list
|
||||
let videoIndex: Int?
|
||||
|
||||
/// Optional start time in seconds
|
||||
let startTime: TimeInterval?
|
||||
|
||||
/// Callback to load more videos when reaching the end of the current list
|
||||
/// Returns new videos and updated continuation token
|
||||
let loadMoreVideos: LoadMoreVideosCallback?
|
||||
|
||||
/// Whether this context has valid queue information
|
||||
var hasQueueInfo: Bool {
|
||||
videoList != nil && videoIndex != nil
|
||||
}
|
||||
|
||||
/// Number of videos that will be queued after the current one
|
||||
var remainingVideosCount: Int {
|
||||
guard let list = videoList, let index = videoIndex else { return 0 }
|
||||
return max(0, list.count - index - 1)
|
||||
}
|
||||
|
||||
/// Whether more videos can be loaded via continuation
|
||||
var canLoadMore: Bool {
|
||||
let hasCallback = loadMoreVideos != nil
|
||||
let supportsContinuation = queueSource?.supportsContinuation == true
|
||||
return hasCallback && supportsContinuation
|
||||
}
|
||||
|
||||
/// Creates a minimal context with just the video (no queue)
|
||||
static func single(_ video: Video, startTime: TimeInterval? = nil) -> VideoQueueContext {
|
||||
VideoQueueContext(
|
||||
video: video,
|
||||
queueSource: nil,
|
||||
sourceLabel: nil,
|
||||
videoList: nil,
|
||||
videoIndex: nil,
|
||||
startTime: startTime,
|
||||
loadMoreVideos: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable & Hashable
|
||||
// Note: Excludes loadMoreVideos callback since closures aren't equatable
|
||||
|
||||
extension VideoQueueContext: Equatable {
|
||||
static func == (lhs: VideoQueueContext, rhs: VideoQueueContext) -> Bool {
|
||||
lhs.video == rhs.video &&
|
||||
lhs.queueSource == rhs.queueSource &&
|
||||
lhs.sourceLabel == rhs.sourceLabel &&
|
||||
lhs.videoList == rhs.videoList &&
|
||||
lhs.videoIndex == rhs.videoIndex &&
|
||||
lhs.startTime == rhs.startTime
|
||||
}
|
||||
}
|
||||
|
||||
extension VideoQueueContext: Hashable {
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(video)
|
||||
hasher.combine(queueSource)
|
||||
hasher.combine(sourceLabel)
|
||||
hasher.combine(videoList)
|
||||
hasher.combine(videoIndex)
|
||||
hasher.combine(startTime)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user