Move createVideoPlayerViewModel
This commit is contained in:
parent
c51f11c5bc
commit
467d0d4937
|
@ -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 */,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue