fix: video content fullscreen (borderless window), load actual duration via KVO+async
This commit is contained in:
parent
2a5e3b8ad7
commit
bf4cb64286
@ -61,8 +61,15 @@ final class PlayerBridge: ObservableObject {
|
|||||||
|
|
||||||
private var timeObserver: Any?
|
private var timeObserver: Any?
|
||||||
private var endObserver: Any?
|
private var endObserver: Any?
|
||||||
|
private var itemStatusObserver: NSKeyValueObservation?
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
// 视频全屏
|
||||||
|
private var fullscreenWindow: NSWindow?
|
||||||
|
|
||||||
|
// 缓存时长
|
||||||
|
private var cachedDuration: Double = 0
|
||||||
|
|
||||||
// MARK: - 初始化
|
// MARK: - 初始化
|
||||||
|
|
||||||
func setup() {
|
func setup() {
|
||||||
@ -216,6 +223,18 @@ final class PlayerBridge: ObservableObject {
|
|||||||
player.replaceCurrentItem(with: playerItem)
|
player.replaceCurrentItem(with: playerItem)
|
||||||
player.play()
|
player.play()
|
||||||
|
|
||||||
|
// 重置缓存时长
|
||||||
|
cachedDuration = 0
|
||||||
|
|
||||||
|
// 监听item状态,加载时长
|
||||||
|
itemStatusObserver = playerItem.observe(\.status, options: [.new]) { [weak self] item, _ in
|
||||||
|
if item.status == .readyToPlay {
|
||||||
|
Task { @MainActor in
|
||||||
|
self?.loadDuration(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载音轨信息
|
// 加载音轨信息
|
||||||
loadTrackInfo(playerItem)
|
loadTrackInfo(playerItem)
|
||||||
|
|
||||||
@ -271,12 +290,33 @@ final class PlayerBridge: ObservableObject {
|
|||||||
guard let eng = engine, let item = player.currentItem else { return }
|
guard let eng = engine, let item = player.currentItem else { return }
|
||||||
|
|
||||||
let current = time.seconds
|
let current = time.seconds
|
||||||
let total = item.duration.seconds
|
var total = item.duration.seconds
|
||||||
|
|
||||||
|
// 如果duration还没加载好,尝试缓存值
|
||||||
|
if !total.isFinite || total <= 0 {
|
||||||
|
total = cachedDuration
|
||||||
|
} else {
|
||||||
|
cachedDuration = total
|
||||||
|
}
|
||||||
|
|
||||||
if total.isFinite && total > 0 {
|
if total.isFinite && total > 0 {
|
||||||
eng.store.setValue(id: "time_current", value: formatTime(current))
|
eng.store.setValue(id: "time_current", value: formatTime(current))
|
||||||
eng.store.setValue(id: "time_total", value: formatTime(total))
|
eng.store.setValue(id: "time_total", value: formatTime(total))
|
||||||
eng.store.setValue(id: "progress_slider", value: "\(current)/\(total)")
|
eng.store.setValue(id: "progress_slider", value: "\(current)/\(total)")
|
||||||
|
} else {
|
||||||
|
eng.store.setValue(id: "time_current", value: formatTime(current))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadDuration(_ item: AVPlayerItem) {
|
||||||
|
Task {
|
||||||
|
if let dur = try? await item.asset.load(.duration) {
|
||||||
|
let secs = dur.seconds
|
||||||
|
if secs.isFinite && secs > 0 {
|
||||||
|
cachedDuration = secs
|
||||||
|
engine?.store.setValue(id: "time_total", value: formatTime(secs))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,14 +379,38 @@ final class PlayerBridge: ObservableObject {
|
|||||||
showToast("循环模式: \(repeatMode.rawValue)")
|
showToast("循环模式: \(repeatMode.rawValue)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 全屏
|
// MARK: - 全屏(视频内容全屏,非窗口全屏)
|
||||||
|
|
||||||
func toggleFullscreen() {
|
func toggleFullscreen() {
|
||||||
isFullscreen.toggle()
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
if let window = NSApp.keyWindow {
|
if let fw = fullscreenWindow {
|
||||||
window.toggleFullScreen(nil)
|
fw.close()
|
||||||
|
fullscreenWindow = nil
|
||||||
|
isFullscreen = false
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let screen = NSScreen.main else { return }
|
||||||
|
let win = NSWindow(contentRect: screen.frame, styleMask: .borderless, backing: .buffered, defer: false)
|
||||||
|
win.level = .screenSaver
|
||||||
|
win.backgroundColor = .black
|
||||||
|
win.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
||||||
|
win.hasShadow = false
|
||||||
|
win.ignoresMouseEvents = false
|
||||||
|
|
||||||
|
let hostView = NSHostingView(rootView:
|
||||||
|
VideoPlayerRepresentable(player: player)
|
||||||
|
.background(Color.black)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onTapGesture { [weak self] in self?.toggleFullscreen() }
|
||||||
|
)
|
||||||
|
hostView.frame = screen.frame
|
||||||
|
win.contentView = hostView
|
||||||
|
win.makeKeyAndOrderFront(nil)
|
||||||
|
NSApp.hide(nil)
|
||||||
|
fullscreenWindow = win
|
||||||
|
isFullscreen = true
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,5 +499,6 @@ final class PlayerBridge: ObservableObject {
|
|||||||
if let observer = timeObserver {
|
if let observer = timeObserver {
|
||||||
player.removeTimeObserver(observer)
|
player.removeTimeObserver(observer)
|
||||||
}
|
}
|
||||||
|
itemStatusObserver?.invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user