2022-11-10 20:47:27 +00:00
|
|
|
#if canImport(AppKit)
|
|
|
|
import AppKit
|
|
|
|
#endif
|
2022-11-10 17:11:28 +00:00
|
|
|
import Foundation
|
|
|
|
import Logging
|
2022-11-10 20:47:27 +00:00
|
|
|
#if canImport(UIKit)
|
|
|
|
import UIKit
|
|
|
|
#endif
|
2022-11-11 17:50:13 +00:00
|
|
|
import SwiftUI
|
2022-11-10 17:11:28 +00:00
|
|
|
|
|
|
|
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 = OpenVideosModel()
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 20:47:27 +00:00
|
|
|
var urlsFromClipboard: [URL] {
|
|
|
|
#if os(iOS)
|
2022-11-11 15:33:28 +00:00
|
|
|
if let pasteboard = UIPasteboard.general.urls {
|
|
|
|
return pasteboard
|
2022-11-10 20:47:27 +00:00
|
|
|
}
|
|
|
|
#elseif os(macOS)
|
|
|
|
if let pasteboard = NSPasteboard.general.string(forType: .string) {
|
|
|
|
return urlsFrom(pasteboard)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2022-11-11 17:50:13 +00:00
|
|
|
func openURLsFromClipboard(removeQueueItems: Bool = false, playbackMode: OpenVideosModel.PlaybackMode = .playNow) {
|
|
|
|
if urlsFromClipboard.isEmpty {
|
2022-11-18 23:06:13 +00:00
|
|
|
NavigationModel.shared.alert = Alert(title: Text("Could not find any links to open in your clipboard".localized()))
|
2022-11-11 17:50:13 +00:00
|
|
|
if NavigationModel.shared.presentingOpenVideos {
|
|
|
|
NavigationModel.shared.presentingAlertInOpenVideos = true
|
|
|
|
} else {
|
|
|
|
NavigationModel.shared.presentingAlert = true
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
openURLs(urlsFromClipboard, removeQueueItems: removeQueueItems, playbackMode: playbackMode)
|
|
|
|
}
|
2022-11-10 20:47:27 +00:00
|
|
|
}
|
|
|
|
|
2022-11-11 17:50:13 +00:00
|
|
|
func openURLs(_ urls: [URL], removeQueueItems: Bool = false, playbackMode: OpenVideosModel.PlaybackMode = .playNow) {
|
2022-11-10 21:20:35 +00:00
|
|
|
guard !urls.isEmpty else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-11 15:19:14 +00:00
|
|
|
NavigationModel.shared.presentingOpenVideos = false
|
|
|
|
|
2022-11-10 17:11:28 +00:00
|
|
|
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
|
|
|
|
)
|
|
|
|
|
2023-04-22 08:56:42 +00:00
|
|
|
NavigationModel.shared.presentingChannelSheet = false
|
2022-12-17 23:08:30 +00:00
|
|
|
|
2022-11-10 17:11:28 +00:00
|
|
|
if playbackMode == .playNow || playbackMode == .shuffleAll {
|
2022-11-11 11:34:55 +00:00
|
|
|
#if os(iOS)
|
|
|
|
if player.presentingPlayer {
|
|
|
|
player.advanceToNextItem()
|
|
|
|
} else {
|
|
|
|
player.onPresentPlayer.append { [weak player] in player?.advanceToNextItem() }
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
player.advanceToNextItem()
|
|
|
|
#endif
|
2022-11-11 17:50:13 +00:00
|
|
|
player.show()
|
2022-11-10 17:11:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2022-12-09 00:15:19 +00:00
|
|
|
video = Video(app: .local, videoID: id)
|
2022-11-10 17:11:28 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
videos.forEach { video in
|
|
|
|
player.enqueueVideo(video, play: false, prepending: prepending, loadDetails: false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 20:47:27 +00:00
|
|
|
func urlsFrom(_ string: String) -> [URL] {
|
|
|
|
string.split(whereSeparator: \.isNewline).compactMap { URL(string: String($0)) }
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:11:28 +00:00
|
|
|
var canOpenVideosByID: Bool {
|
2022-11-24 20:36:05 +00:00
|
|
|
guard let app = AccountsModel.shared.current?.app else { return false }
|
|
|
|
return !AccountsModel.shared.isEmpty && app.supportsOpeningVideosByID
|
2022-11-10 17:11:28 +00:00
|
|
|
}
|
|
|
|
}
|