126 lines
7.4 KiB
Swift
126 lines
7.4 KiB
Swift
//
|
|
/*
|
|
* SwiftFin is subject to the terms of the Mozilla Public
|
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
|
*/
|
|
|
|
import Combine
|
|
import Defaults
|
|
import JellyfinAPI
|
|
import UIKit
|
|
|
|
extension BaseItemDto {
|
|
func createVideoPlayerViewModel() -> AnyPublisher<VideoPlayerViewModel, Error> {
|
|
let builder = DeviceProfileBuilder()
|
|
// TODO: fix bitrate settings
|
|
builder.setMaxBitrate(bitrate: 60000000)
|
|
let profile = builder.buildProfile()
|
|
|
|
let playbackInfo = PlaybackInfoDto(userId: SessionManager.main.currentLogin.user.id,
|
|
maxStreamingBitrate: 60000000,
|
|
startTimeTicks: self.userData?.playbackPositionTicks ?? 0,
|
|
deviceProfile: profile,
|
|
autoOpenLiveStream: true)
|
|
|
|
return MediaInfoAPI.getPostedPlaybackInfo(itemId: self.id!,
|
|
userId: SessionManager.main.currentLogin.user.id,
|
|
maxStreamingBitrate: 60000000,
|
|
startTimeTicks: self.userData?.playbackPositionTicks ?? 0,
|
|
autoOpenLiveStream: true,
|
|
playbackInfoDto: playbackInfo)
|
|
.map({ response -> VideoPlayerViewModel in
|
|
let mediaSource = response.mediaSources!.first!
|
|
|
|
let audioStreams = mediaSource.mediaStreams?.filter({ $0.type == .audio }) ?? []
|
|
let subtitleStreams = mediaSource.mediaStreams?.filter({ $0.type == .subtitle }) ?? []
|
|
|
|
let defaultAudioStream = audioStreams.first(where: { $0.index! == mediaSource.defaultAudioStreamIndex! })
|
|
|
|
let defaultSubtitleStream = subtitleStreams.first(where: { $0.index! == mediaSource.defaultSubtitleStreamIndex ?? -1 })
|
|
|
|
let videoStream = mediaSource.mediaStreams!.first(where: { $0.type! == MediaStreamType.video })
|
|
|
|
let audioCodecs = mediaSource.mediaStreams!.filter({ $0.type! == MediaStreamType.audio }).map({ $0.codec! })
|
|
|
|
// MARK: basic stream
|
|
var streamURL = URLComponents(string: SessionManager.main.currentLogin.server.currentURI)!
|
|
streamURL.path = "/Videos/\(self.id!)/stream"
|
|
|
|
streamURL.addQueryItem(name: "Static", value: "true")
|
|
streamURL.addQueryItem(name: "MediaSourceId", value: self.id!)
|
|
streamURL.addQueryItem(name: "Tag", value: self.etag)
|
|
streamURL.addQueryItem(name: "MinSegments", value: "6")
|
|
|
|
// MARK: hls stream
|
|
var hlsURL = URLComponents(string: SessionManager.main.currentLogin.server.currentURI)!
|
|
hlsURL.path = "/videos/\(self.id!)/master.m3u8"
|
|
|
|
hlsURL.addQueryItem(name: "DeviceId", value: UIDevice.vendorUUIDString)
|
|
hlsURL.addQueryItem(name: "MediaSourceId", value: self.id!)
|
|
hlsURL.addQueryItem(name: "VideoCodec", value: videoStream?.codec!)
|
|
hlsURL.addQueryItem(name: "AudioCodec", value: audioCodecs.joined(separator: ","))
|
|
hlsURL.addQueryItem(name: "AudioStreamIndex", value: "\(defaultAudioStream!.index!)")
|
|
hlsURL.addQueryItem(name: "VideoBitrate", value: "\(videoStream!.bitRate!)")
|
|
hlsURL.addQueryItem(name: "AudioBitrate", value: "\(defaultAudioStream!.bitRate!)")
|
|
hlsURL.addQueryItem(name: "PlaySessionId", value: response.playSessionId!)
|
|
hlsURL.addQueryItem(name: "TranscodingMaxAudioChannels", value: "6")
|
|
hlsURL.addQueryItem(name: "RequireAvc", value: "false")
|
|
hlsURL.addQueryItem(name: "Tag", value: mediaSource.eTag!)
|
|
hlsURL.addQueryItem(name: "SegmentContainer", value: "ts")
|
|
hlsURL.addQueryItem(name: "MinSegments", value: "2")
|
|
hlsURL.addQueryItem(name: "BreakOnNonKeyFrames", value: "true")
|
|
hlsURL.addQueryItem(name: "TranscodeReasons", value: "VideoCodecNotSupported,AudioCodecNotSupported")
|
|
hlsURL.addQueryItem(name: "api_key", value: SessionManager.main.currentLogin.user.accessToken)
|
|
|
|
if defaultSubtitleStream?.index != nil {
|
|
hlsURL.addQueryItem(name: "SubtitleMethod", value: "Encode")
|
|
hlsURL.addQueryItem(name: "SubtitleStreamIndex", value: "\(defaultSubtitleStream!.index!)")
|
|
}
|
|
|
|
// MARK: VidoPlayerViewModel Creation
|
|
|
|
var subtitle: String? = nil
|
|
|
|
// TODO: other forms of media subtitle
|
|
if self.itemType == .episode {
|
|
if let seriesName = self.seriesName, let episodeLocator = self.getEpisodeLocator() {
|
|
subtitle = "\(seriesName) - \(episodeLocator)"
|
|
}
|
|
}
|
|
|
|
let subtitlesEnabled = defaultSubtitleStream != nil
|
|
|
|
let shouldShowAutoPlay = Defaults[.shouldShowAutoPlay] && itemType == .episode
|
|
let autoplayEnabled = Defaults[.autoplayEnabled] && shouldShowAutoPlay
|
|
|
|
let overlayType = Defaults[.overlayType]
|
|
|
|
let shouldShowPlayPreviousItem = Defaults[.shouldShowPlayPreviousItem] && itemType == .episode
|
|
let shouldShowPlayNextItem = Defaults[.shouldShowPlayNextItem] && itemType == .episode
|
|
|
|
let videoPlayerViewModel = VideoPlayerViewModel(item: self,
|
|
title: self.name ?? "",
|
|
subtitle: subtitle,
|
|
streamURL: streamURL.url!,
|
|
hlsURL: hlsURL.url!,
|
|
response: response,
|
|
audioStreams: audioStreams,
|
|
subtitleStreams: subtitleStreams,
|
|
selectedAudioStreamIndex: defaultAudioStream?.index ?? -1,
|
|
selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1,
|
|
subtitlesEnabled: subtitlesEnabled,
|
|
autoplayEnabled: autoplayEnabled,
|
|
overlayType: overlayType,
|
|
shouldShowPlayPreviousItem: shouldShowPlayPreviousItem,
|
|
shouldShowPlayNextItem: shouldShowPlayNextItem,
|
|
shouldShowAutoPlay: shouldShowAutoPlay)
|
|
|
|
return videoPlayerViewModel
|
|
})
|
|
.eraseToAnyPublisher()
|
|
}
|
|
}
|