update item after playback close

This commit is contained in:
Ethan Pippin 2022-01-11 00:01:37 -07:00
parent 0df99b9899
commit 53048d7d14
10 changed files with 120 additions and 29 deletions

View File

@ -13,6 +13,9 @@ import UIKit
extension BaseItemDto {
func createVideoPlayerViewModel() -> AnyPublisher<VideoPlayerViewModel, Error> {
LogManager.shared.log.debug("Creating video player view model for item: \(id ?? "")")
let builder = DeviceProfileBuilder()
// TODO: fix bitrate settings
builder.setMaxBitrate(bitrate: 60_000_000)

View File

@ -20,5 +20,8 @@ enum SwiftfinNotificationCenter {
static let processDeepLink = Notification.Name("processDeepLink")
static let didPurge = Notification.Name("didPurge")
static let didChangeServerCurrentURI = Notification.Name("didChangeCurrentLoginURI")
// Send with an item id to check if current item for item views
static let didSendStopReport = Notification.Name("didSendStopReport")
}
}

View File

@ -29,10 +29,6 @@ final class EpisodeItemViewModel: ItemViewModel {
return "\(episodeLocator)\n\(item.name ?? "")"
}
override func shouldDisplayRuntime() -> Bool {
false
}
func getEpisodeSeries() {
guard let id = item.seriesId else { return }
UserLibraryAPI.getItem(userId: SessionManager.main.currentLogin.user.id, itemId: id)
@ -44,4 +40,29 @@ final class EpisodeItemViewModel: ItemViewModel {
})
.store(in: &cancellables)
}
override func updateItem() {
ItemsAPI.getItems(userId: SessionManager.main.currentLogin.user.id,
limit: 1,
fields: [
.primaryImageAspectRatio,
.seriesPrimaryImage,
.seasonUserData,
.overview,
.genres,
.people,
.chapters,
],
enableUserData: true,
ids: [item.id ?? ""])
.sink { completion in
self.handleAPIRequestError(completion: completion)
} receiveValue: { response in
if let item = response.items?.first {
self.item = item
self.playButtonItem = item
}
}
.store(in: &cancellables)
}
}

View File

@ -54,9 +54,27 @@ class ItemViewModel: ViewModel {
getSimilarItems()
SwiftfinNotificationCenter.main.addObserver(self,
selector: #selector(receivedStopReport(_:)),
name: SwiftfinNotificationCenter.Keys.didSendStopReport,
object: nil)
refreshItemVideoPlayerViewModel(for: item)
}
@objc
private func receivedStopReport(_ notification: NSNotification) {
guard let itemID = notification.object as? String else { return }
if itemID == item.id {
updateItem()
} else {
// Remove if necessary. Note that this cannot be in deinit as
// holding as an observer won't allow the object to be deinit-ed
SwiftfinNotificationCenter.main.removeObserver(self)
}
}
func refreshItemVideoPlayerViewModel(for item: BaseItemDto) {
item.createVideoPlayerViewModel()
.sink { completion in
@ -139,4 +157,7 @@ class ItemViewModel: ViewModel {
.store(in: &cancellables)
}
}
// Overridden by subclasses
func updateItem() {}
}

View File

@ -10,4 +10,30 @@ import Combine
import Foundation
import JellyfinAPI
final class MovieItemViewModel: ItemViewModel {}
final class MovieItemViewModel: ItemViewModel {
override func updateItem() {
ItemsAPI.getItems(userId: SessionManager.main.currentLogin.user.id,
limit: 1,
fields: [
.primaryImageAspectRatio,
.seriesPrimaryImage,
.seasonUserData,
.overview,
.genres,
.people,
.chapters,
],
enableUserData: true,
ids: [item.id ?? ""])
.sink { completion in
self.handleAPIRequestError(completion: completion)
} receiveValue: { response in
if let item = response.items?.first {
self.item = item
self.playButtonItem = item
}
}
.store(in: &cancellables)
}
}

View File

@ -247,7 +247,7 @@ extension VideoPlayerViewModel {
adjacentTo: item.id,
limit: 3)
.sink(receiveCompletion: { completion in
print(completion)
self.handleAPIRequestError(completion: completion)
}, receiveValue: { response in
// 4 possible states:
@ -510,6 +510,8 @@ extension VideoPlayerViewModel {
self.handleAPIRequestError(completion: completion)
} receiveValue: { _ in
LogManager.shared.log.debug("Stop report sent for item: \(self.item.id ?? "No ID")")
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSendStopReport,
object: self.item.id)
}
.store(in: &cancellables)
}
@ -536,3 +538,13 @@ extension VideoPlayerViewModel {
return newURL.url!
}
}
// MARK: Equatable
extension VideoPlayerViewModel: Equatable {
static func == (lhs: VideoPlayerViewModel, rhs: VideoPlayerViewModel) -> Bool {
lhs.item.id == rhs.item.id &&
lhs.item.userData?.playbackPositionTicks == rhs.item.userData?.playbackPositionTicks
}
}

View File

@ -2773,7 +2773,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 66;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = TY84JMYEFE;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
EXCLUDED_ARCHS = "";
@ -2810,7 +2810,7 @@
CURRENT_PROJECT_VERSION = 66;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = TY84JMYEFE;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
EXCLUDED_ARCHS = "";
@ -2841,7 +2841,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 66;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = TY84JMYEFE;
INFOPLIST_FILE = WidgetExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
@ -2868,7 +2868,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 66;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = TY84JMYEFE;
INFOPLIST_FILE = WidgetExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (

View File

@ -54,8 +54,7 @@ struct ItemViewBody: View {
topBarView: {
L10n.seasons.text
.fontWeight(.semibold)
.padding(.bottom)
.padding(.horizontal)
.padding()
}, selectedAction: { season in
itemRouter.route(to: \.item, season)
})
@ -63,14 +62,14 @@ struct ItemViewBody: View {
// MARK: Genres
if let genres = viewModel.item.genreItems {
PillHStackView(title: L10n.genres,
items: genres,
selectedAction: { genre in
itemRouter.route(to: \.library, (viewModel: .init(genre: genre), title: genre.title))
})
.padding(.bottom)
}
if let genres = viewModel.item.genreItems {
PillHStackView(title: L10n.genres,
items: genres,
selectedAction: { genre in
itemRouter.route(to: \.library, (viewModel: .init(genre: genre), title: genre.title))
})
.padding(.bottom)
}
// MARK: Studios
@ -120,7 +119,7 @@ struct ItemViewBody: View {
// MARK: Cast & Crew
if showCastAndCrew {
if let castAndCrew = viewModel.item.people, castAndCrew.count > 0 {
if let castAndCrew = viewModel.item.people, !castAndCrew.isEmpty {
PortraitImageHStackView(items: castAndCrew.filter { BaseItemPerson.DisplayedType.allCasesRaw.contains($0.type ?? "") },
topBarView: {
L10n.castAndCrew.text

View File

@ -14,6 +14,8 @@ struct ItemLandscapeMainView: View {
var itemRouter: ItemCoordinator.Router
@EnvironmentObject
private var viewModel: ItemViewModel
@State
private var playButtonText: String = ""
// MARK: innerBody
@ -72,6 +74,9 @@ struct ItemLandscapeMainView: View {
}
}
}
.onAppear {
playButtonText = viewModel.playButtonText()
}
}
// MARK: body

View File

@ -15,6 +15,8 @@ struct PortraitHeaderOverlayView: View {
var itemRouter: ItemCoordinator.Router
@EnvironmentObject
private var viewModel: ItemViewModel
@State
private var playButtonText: String = ""
var body: some View {
VStack(alignment: .leading) {
@ -37,9 +39,9 @@ struct PortraitHeaderOverlayView: View {
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 10)
if viewModel.item.itemType.showDetails {
// MARK: Runtime
// MARK: Details
HStack {
if viewModel.shouldDisplayRuntime() {
if let runtime = viewModel.item.getItemRuntime() {
Text(runtime)
@ -49,11 +51,7 @@ struct PortraitHeaderOverlayView: View {
.lineLimit(1)
}
}
}
// MARK: Details
HStack {
if let productionYear = viewModel.item.productionYear {
Text(String(productionYear))
.font(.subheadline)
@ -88,7 +86,7 @@ struct PortraitHeaderOverlayView: View {
Image(systemName: "play.fill")
.foregroundColor(viewModel.playButtonItem == nil ? Color(UIColor.secondaryLabel) : Color.white)
.font(.system(size: 20))
Text(viewModel.playButtonText())
Text(playButtonText)
.foregroundColor(viewModel.playButtonItem == nil ? Color(UIColor.secondaryLabel) : Color.white)
.font(.callout)
.fontWeight(.semibold)
@ -137,7 +135,10 @@ struct PortraitHeaderOverlayView: View {
}
}.padding(.top, 8)
}
.padding(.horizontal, 16)
.onAppear {
playButtonText = viewModel.playButtonText()
}
.padding(.horizontal)
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? -189 : -64)
}
}