yattee/Model/OpenVideosModel.swift
2024-05-16 19:01:02 +02:00

164 lines
4.9 KiB
Swift

#if canImport(AppKit)
import AppKit
#endif
import Foundation
import Logging
#if canImport(UIKit)
import UIKit
#endif
import SwiftUI
struct OpenVideosModel {
enum PlaybackMode: String, CaseIterable {
case playNow
case shuffleAll
case playNext
case playLast
var description: String {
switch self {
case .playNow:
return "Play Now".localized()
case .shuffleAll:
return "Shuffle All".localized()
case .playNext:
return "Play Next".localized()
case .playLast:
return "Play Last".localized()
}
}
var allowsRemovingQueueItems: Bool {
self == .playNow || self == .shuffleAll
}
var allowedWhenQueueIsEmpty: Bool {
self == .playNow || self == .shuffleAll
}
}
static let shared = Self()
var player: PlayerModel! = .shared
var logger = Logger(label: "stream.yattee.open-videos")
func open(_ url: URL) {
if url.startAccessingSecurityScopedResource() {
let video = Video.local(url)
player.play([video], shuffling: false)
}
}
var urlsFromClipboard: [URL] {
#if os(iOS)
if let pasteboard = UIPasteboard.general.urls {
return pasteboard
}
#elseif os(macOS)
if let pasteboard = NSPasteboard.general.string(forType: .string) {
return urlsFrom(pasteboard)
}
#endif
return []
}
func openURLsFromClipboard(removeQueueItems: Bool = false, playbackMode: Self.PlaybackMode = .playNow) {
if urlsFromClipboard.isEmpty {
NavigationModel.shared.alert = Alert(title: Text("Could not find any links to open in your clipboard".localized()))
if NavigationModel.shared.presentingOpenVideos {
NavigationModel.shared.presentingAlertInOpenVideos = true
} else {
NavigationModel.shared.presentingAlert = true
}
} else {
openURLs(urlsFromClipboard, removeQueueItems: removeQueueItems, playbackMode: playbackMode)
}
}
func openURLs(_ urls: [URL], removeQueueItems: Bool = false, playbackMode: Self.PlaybackMode = .playNow) {
guard !urls.isEmpty else {
return
}
NavigationModel.shared.presentingOpenVideos = false
logger.info("opening \(urls.count) urls")
urls.forEach { logger.info("\($0.absoluteString)") }
if removeQueueItems, playbackMode.allowsRemovingQueueItems {
player.removeQueueItems()
logger.info("removing queue items")
}
switch playbackMode {
case .playNow:
player.playbackMode = .queue
case .shuffleAll:
player.playbackMode = .shuffle
case .playNext:
player.playbackMode = .queue
case .playLast:
player.playbackMode = .queue
}
enqueue(
urls,
prepending: playbackMode == .playNow || playbackMode == .playNext
)
NavigationModel.shared.presentingChannelSheet = false
if playbackMode == .playNow || playbackMode == .shuffleAll {
#if os(iOS)
if player.presentingPlayer {
player.advanceToNextItem()
} else {
player.onPresentPlayer.append { [weak player] in player?.advanceToNextItem() }
}
#else
player.advanceToNextItem()
#endif
player.show()
}
}
func enqueue(_ urls: [URL], prepending: Bool = false) {
var videos = urls.compactMap { url in
var video: Video!
if canOpenVideosByID {
let parser = URLParser(url: url)
if parser.destination == .video, let id = parser.videoID {
video = Video(app: .local, videoID: id)
logger.info("identified remote video: \(id)")
} else {
video = .local(url)
logger.info("identified local video: \(url.absoluteString)")
}
} else {
video = .local(url)
logger.info("identified local video: \(url.absoluteString)")
}
return video
}
if prepending {
videos.reverse()
}
for video in videos {
player.enqueueVideo(video, play: false, prepending: prepending, loadDetails: false)
}
}
func urlsFrom(_ string: String) -> [URL] {
string.split(whereSeparator: \.isNewline).compactMap { URL(string: String($0)) }
}
var canOpenVideosByID: Bool {
guard let app = AccountsModel.shared.current?.app else { return false }
return !AccountsModel.shared.isEmpty && app.supportsOpeningVideosByID
}
}