yattee/Model/Player/PlayerStreams.swift

122 lines
3.8 KiB
Swift
Raw Normal View History

2021-10-16 22:48:58 +00:00
import Foundation
import Siesta
2021-10-18 21:53:02 +00:00
import SwiftUI
2021-10-16 22:48:58 +00:00
extension PlayerModel {
var isLoadingAvailableStreams: Bool {
streamSelection.isNil || availableStreams.isEmpty
}
var isLoadingStream: Bool {
!stream.isNil && stream != streamSelection
}
2021-10-17 21:49:56 +00:00
var availableStreamsSorted: [Stream] {
availableStreams.sorted(by: streamsSorter)
}
2021-10-16 22:48:58 +00:00
func loadAvailableStreams(
_ video: Video,
completionHandler: @escaping ([Stream]) -> Void = { _ in }
) {
availableStreams = []
var instancesWithLoadedStreams = [Instance]()
instances.all.forEach { instance in
2021-10-20 22:21:50 +00:00
fetchStreams(instance.anonymous.video(video.videoID), instance: instance, video: video) { _ in
self.completeIfAllInstancesLoaded(
instance: instance,
streams: self.availableStreams,
instancesWithLoadedStreams: &instancesWithLoadedStreams,
completionHandler: completionHandler
)
2021-10-16 22:48:58 +00:00
}
}
}
2021-10-20 22:21:50 +00:00
private func fetchStreams(
_ resource: Resource,
instance: Instance,
2021-10-16 22:48:58 +00:00
video: Video,
onCompletion: @escaping (ResponseInfo) -> Void = { _ in }
) {
2021-10-20 22:21:50 +00:00
resource
2021-10-16 22:48:58 +00:00
.load()
.onSuccess { response in
if let video: Video = response.typedContent() {
2021-10-20 22:21:50 +00:00
self.availableStreams += self.streamsWithInstance(instance: instance, streams: video.streams)
2021-10-16 22:48:58 +00:00
}
}
.onCompletion(onCompletion)
}
private func completeIfAllInstancesLoaded(
instance: Instance,
streams: [Stream],
instancesWithLoadedStreams: inout [Instance],
completionHandler: @escaping ([Stream]) -> Void
) {
instancesWithLoadedStreams.append(instance)
2021-10-18 21:53:02 +00:00
rebuildStreamsMenu()
2021-10-16 22:48:58 +00:00
if instances.all.count == instancesWithLoadedStreams.count {
completionHandler(streams.sorted { $0.kind < $1.kind })
}
}
2021-10-18 21:53:02 +00:00
#if os(tvOS)
var streamsMenu: UIMenu {
UIMenu(
title: "Streams",
image: UIImage(systemName: "antenna.radiowaves.left.and.right"),
children: streamsMenuActions
)
}
var streamsMenuActions: [UIAction] {
guard !availableStreams.isEmpty else {
return [ // swiftlint:disable:this implicit_return
UIAction(title: "Empty", attributes: .disabled) { _ in }
]
}
return availableStreamsSorted.map { stream in
let state = stream == streamSelection ? UIAction.State.on : .off
return UIAction(title: stream.description, state: state) { _ in
self.streamSelection = stream
self.upgradeToStream(stream)
}
}
}
#endif
func rebuildStreamsMenu() {
#if os(tvOS)
avPlayerViewController?.transportBarCustomMenuItems = [streamsMenu]
#endif
}
2021-10-16 22:48:58 +00:00
func streamsWithInstance(instance: Instance, streams: [Stream]) -> [Stream] {
streams.map { stream in
stream.instance = instance
if instance.app == .invidious {
2021-10-22 15:00:09 +00:00
stream.audioAsset = InvidiousAPI.proxiedAsset(instance: instance, asset: stream.audioAsset)
stream.videoAsset = InvidiousAPI.proxiedAsset(instance: instance, asset: stream.videoAsset)
}
2021-10-22 15:00:09 +00:00
2021-10-16 22:48:58 +00:00
return stream
}
}
2021-10-17 21:49:56 +00:00
func streamsSorter(_ lhs: Stream, _ rhs: Stream) -> Bool {
if lhs.resolution.isNil || rhs.resolution.isNil {
return lhs.kind < rhs.kind
}
return lhs.kind == rhs.kind ? (lhs.resolution.height > rhs.resolution.height) : (lhs.kind < rhs.kind)
2021-10-17 21:49:56 +00:00
}
2021-10-16 22:48:58 +00:00
}