Move createVideoPlayerViewModel

This commit is contained in:
Ethan Pippin 2021-12-28 14:48:43 -07:00
parent c51f11c5bc
commit 467d0d4937
3 changed files with 111 additions and 93 deletions

View File

@ -237,6 +237,8 @@
E10EAA4F277BBCC4000269ED /* CGSizeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */; };
E10EAA50277BBCC4000269ED /* CGSizeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */; };
E10EAA51277BBCC4000269ED /* CGSizeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */; };
E10EAA53277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA52277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift */; };
E10EAA54277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA52277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift */; };
E11B1B6C2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
E11B1B6E2718CDBA006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
@ -564,6 +566,7 @@
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayButtonRowView.swift; sourceTree = "<group>"; };
E10EAA49277BB6F5000269ED /* VideoPlayerOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerOverlay.swift; sourceTree = "<group>"; };
E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSizeExtensions.swift; sourceTree = "<group>"; };
E10EAA52277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseItemDto+VideoPlayerViewModel.swift"; sourceTree = "<group>"; };
E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinAPIError.swift; sourceTree = "<group>"; };
E11D224127378428003F9CB3 /* ServerDetailCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailCoordinator.swift; sourceTree = "<group>"; };
E1267D3D271A1F46003C492E /* PreferenceUIHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceUIHostingController.swift; sourceTree = "<group>"; };
@ -1340,6 +1343,7 @@
children = (
E18845F426DD631E00B0C5B7 /* BaseItemDto+Stackable.swift */,
E1AD104C26D96CE3003E4A08 /* BaseItemDtoExtensions.swift */,
E10EAA52277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift */,
5364F454266CA0DC0026ECBA /* BaseItemPersonExtensions.swift */,
E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */,
E1AD105E26D9ADDD003E4A08 /* NameGUIDPairExtensions.swift */,
@ -1868,6 +1872,7 @@
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
62E1DCC4273CE19800C9AE76 /* URLExtensions.swift in Sources */,
5398514726B64E4100101B49 /* SearchBarView.swift in Sources */,
E10EAA54277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */,
091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */,
E1D4BF882719D27100A11E64 /* Bitrates.swift in Sources */,
E193D5432719407E00900D82 /* tvOSMainCoordinator.swift in Sources */,
@ -2029,6 +2034,7 @@
5D64683D277B1649009E09AE /* PreferenceUIHostingSwizzling.swift in Sources */,
C40CD922271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift in Sources */,
E13DD3C827164B1E009D4DAF /* UIDeviceExtensions.swift in Sources */,
E10EAA53277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */,
E1AD104D26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */,
E13DD3BF27163DD7009D4DAF /* AppDelegate.swift in Sources */,
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,

View File

@ -0,0 +1,104 @@
//
/*
* 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 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!)")
}
let videoPlayerViewModel = VideoPlayerViewModel(item: self,
title: self.name!,
subtitle: self.seriesName,
streamURL: streamURL.url!,
hlsURL: hlsURL.url!,
response: response,
audioStreams: audioStreams,
subtitleStreams: subtitleStreams,
defaultAudioStreamIndex: defaultAudioStream?.index ?? -1,
defaultSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1,
playerState: .playing,
shouldShowGoogleCast: false,
shouldShowAirplay: false,
subtitlesEnabled: defaultAudioStream?.index != nil,
sliderPercentage: (self.userData?.playedPercentage ?? 0) / 100,
selectedAudioStreamIndex: defaultAudioStream?.index ?? -1,
selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1)
return videoPlayerViewModel
})
.eraseToAnyPublisher()
}
}

View File

@ -36,7 +36,7 @@ class ItemViewModel: ViewModel {
getSimilarItems()
self.createVideoPlayerViewModel(item: item)
item.createVideoPlayerViewModel()
.sink { completion in
self.handleAPIRequestError(completion: completion)
} receiveValue: { videoPlayerViewModel in
@ -111,96 +111,4 @@ class ItemViewModel: ViewModel {
.store(in: &cancellables)
}
}
func createVideoPlayerViewModel(item: BaseItemDto) -> 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: item.userData?.playbackPositionTicks ?? 0,
deviceProfile: profile,
autoOpenLiveStream: true)
return MediaInfoAPI.getPostedPlaybackInfo(itemId: item.id!,
userId: SessionManager.main.currentLogin.user.id,
maxStreamingBitrate: 60000000,
startTimeTicks: item.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/\(item.id!)/stream"
streamURL.addQueryItem(name: "Static", value: "true")
streamURL.addQueryItem(name: "MediaSourceId", value: item.id!)
streamURL.addQueryItem(name: "Tag", value: item.etag)
streamURL.addQueryItem(name: "MinSegments", value: "6")
// MARK: hls stream
var hlsURL = URLComponents(string: SessionManager.main.currentLogin.server.currentURI)!
hlsURL.path = "/videos/\(item.id!)/master.m3u8"
hlsURL.addQueryItem(name: "DeviceId", value: UIDevice.vendorUUIDString)
hlsURL.addQueryItem(name: "MediaSourceId", value: item.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!)")
}
// startURL.queryItems?.append(URLQueryItem(name: "SubtitleCodec", value: "\(defaultSubtitleStream!.codec!)"))
let videoPlayerViewModel = VideoPlayerViewModel(item: item,
title: item.name!,
subtitle: item.seriesName,
streamURL: streamURL.url!,
hlsURL: hlsURL.url!,
response: response,
audioStreams: audioStreams,
subtitleStreams: subtitleStreams,
defaultAudioStreamIndex: defaultAudioStream?.index ?? -1,
defaultSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1,
playerState: .playing,
shouldShowGoogleCast: false,
shouldShowAirplay: false,
subtitlesEnabled: defaultAudioStream?.index != nil,
sliderPercentage: (item.userData?.playedPercentage ?? 0) / 100,
selectedAudioStreamIndex: defaultAudioStream?.index ?? -1,
selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1)
return videoPlayerViewModel
})
.eraseToAnyPublisher()
}
}