From 56b60dafdcc7aee354467a10751c38ab53d13533 Mon Sep 17 00:00:00 2001 From: jhays Date: Sat, 22 Jan 2022 11:20:22 -0600 Subject: [PATCH] duplicate create view model for livetv --- .../BaseItemDto+VideoPlayerViewModel.swift | 124 ++++++++++++++++++ .../ViewModels/LiveTVChannelsViewModel.swift | 2 +- .../ViewModels/LiveTVProgramsViewModel.swift | 2 +- 3 files changed, 126 insertions(+), 2 deletions(-) diff --git a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift index c658e9e1..d89aac15 100644 --- a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift +++ b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift @@ -166,4 +166,128 @@ extension BaseItemDto { } .eraseToAnyPublisher() } + + func createLiveTVVideoPlayerViewModel() -> AnyPublisher<[VideoPlayerViewModel], Error> { + + LogManager.shared.log.debug("Creating liveTV video player view model for item: \(id ?? "")") + + let builder = DeviceProfileBuilder() + // TODO: fix bitrate settings + let tempOverkillBitrate = 360_000_000 + builder.setMaxBitrate(bitrate: tempOverkillBitrate) + let profile = builder.buildProfile() + + let playbackInfo = PlaybackInfoDto(userId: SessionManager.main.currentLogin.user.id, + maxStreamingBitrate: tempOverkillBitrate, + startTimeTicks: self.userData?.playbackPositionTicks ?? 0, + deviceProfile: profile, + autoOpenLiveStream: true) + + return MediaInfoAPI.getPostedPlaybackInfo(itemId: self.id!, + userId: SessionManager.main.currentLogin.user.id, + maxStreamingBitrate: tempOverkillBitrate, + startTimeTicks: self.userData?.playbackPositionTicks ?? 0, + autoOpenLiveStream: true, + playbackInfoDto: playbackInfo) + .map { response -> [VideoPlayerViewModel] in + let mediaSources = response.mediaSources! + + var viewModels: [VideoPlayerViewModel] = [] + + for currentMediaSource in mediaSources { + let audioStreams = currentMediaSource.mediaStreams?.filter { $0.type == .audio } ?? [] + let subtitleStreams = currentMediaSource.mediaStreams?.filter { $0.type == .subtitle } ?? [] + + let defaultAudioStream = audioStreams.first(where: { $0.index! == currentMediaSource.defaultAudioStreamIndex! }) + + let defaultSubtitleStream = subtitleStreams + .first(where: { $0.index! == currentMediaSource.defaultSubtitleStreamIndex ?? -1 }) + + var directStreamURL: URL + let transcodedStreamURL: URLComponents? + let mediaSourceID: String + let streamType: ServerStreamType + + if let transcodeURL = currentMediaSource.transcodingUrl { + streamType = .transcode + transcodedStreamURL = URLComponents(string: SessionManager.main.currentLogin.server.currentURI + .appending(transcodeURL))! + } else { + streamType = .direct + transcodedStreamURL = nil + } + + if mediaSources.count > 1 { + mediaSourceID = currentMediaSource.id! + } else { + mediaSourceID = self.id! + } + + let requestBuilder = VideosAPI.getVideoStreamWithRequestBuilder(itemId: self.id!, + _static: true, + tag: self.etag, + minSegments: 6, + mediaSourceId: mediaSourceID) + directStreamURL = URL(string: requestBuilder.URLString)! + + // MARK: VidoPlayerViewModel Creation + + var subtitle: String? + + // MARK: Attach media content to self + + var modifiedSelfItem = self + modifiedSelfItem.mediaStreams = currentMediaSource.mediaStreams + + // 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 + + var fileName: String? + if let lastInPath = currentMediaSource.path?.split(separator: "/").last { + fileName = String(lastInPath) + } + + let videoPlayerViewModel = VideoPlayerViewModel(item: modifiedSelfItem, + title: modifiedSelfItem.name ?? "", + subtitle: subtitle, + directStreamURL: directStreamURL, + transcodedStreamURL: transcodedStreamURL?.url, + streamType: streamType, + response: response, + audioStreams: audioStreams, + subtitleStreams: subtitleStreams, + chapters: modifiedSelfItem.chapters ?? [], + selectedAudioStreamIndex: defaultAudioStream?.index ?? -1, + selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1, + subtitlesEnabled: subtitlesEnabled, + autoplayEnabled: autoplayEnabled, + overlayType: overlayType, + shouldShowPlayPreviousItem: shouldShowPlayPreviousItem, + shouldShowPlayNextItem: shouldShowPlayNextItem, + shouldShowAutoPlay: shouldShowAutoPlay, + container: currentMediaSource.container ?? "", + filename: fileName, + versionName: currentMediaSource.name) + + viewModels.append(videoPlayerViewModel) + } + + return viewModels + } + .eraseToAnyPublisher() + } } diff --git a/Shared/ViewModels/LiveTVChannelsViewModel.swift b/Shared/ViewModels/LiveTVChannelsViewModel.swift index 70b95066..e425af55 100644 --- a/Shared/ViewModels/LiveTVChannelsViewModel.swift +++ b/Shared/ViewModels/LiveTVChannelsViewModel.swift @@ -191,7 +191,7 @@ final class LiveTVChannelsViewModel: ViewModel { } func fetchVideoPlayerViewModel(item: BaseItemDto, completion: @escaping (VideoPlayerViewModel) -> Void) { - item.createVideoPlayerViewModel() + item.createLiveTVVideoPlayerViewModel() .sink { completion in self.handleAPIRequestError(completion: completion) } receiveValue: { videoPlayerViewModels in diff --git a/Shared/ViewModels/LiveTVProgramsViewModel.swift b/Shared/ViewModels/LiveTVProgramsViewModel.swift index 51be71cc..8c64a9f6 100644 --- a/Shared/ViewModels/LiveTVProgramsViewModel.swift +++ b/Shared/ViewModels/LiveTVProgramsViewModel.swift @@ -199,7 +199,7 @@ final class LiveTVProgramsViewModel: ViewModel { } func fetchVideoPlayerViewModel(item: BaseItemDto, completion: @escaping (VideoPlayerViewModel) -> Void) { - item.createVideoPlayerViewModel() + item.createLiveTVVideoPlayerViewModel() .sink { completion in self.handleAPIRequestError(completion: completion) } receiveValue: { videoPlayerViewModels in