Address code quality review issues across multiple files
This commit is contained in:
parent
4ff210cdde
commit
ec0d9b5062
4 changed files with 39 additions and 14 deletions
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public struct PlayerState: Hashable {
|
||||
public struct PlayerState: Hashable, Sendable {
|
||||
|
||||
public var itemId: String
|
||||
public var mediaSourceId: String
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@
|
|||
//
|
||||
|
||||
import Adwaita
|
||||
import LuminateCore
|
||||
import LuminateUI
|
||||
import LuminateDI
|
||||
|
||||
public struct PlayerControls: View {
|
||||
|
||||
|
|
@ -106,6 +104,7 @@ public struct PlayerControls: View {
|
|||
// Volume popover — future
|
||||
}
|
||||
.style("flat")
|
||||
.insensitive()
|
||||
|
||||
Button(icon: .default(icon: .viewFullscreen)) {
|
||||
onFullscreen()
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ import LuminateDI
|
|||
import CModules
|
||||
import CAdw
|
||||
|
||||
final class PlayerTasks {
|
||||
var progress: Task<Void, Never>?
|
||||
var controls: Task<Void, Never>?
|
||||
}
|
||||
|
||||
public struct PlayerView: View {
|
||||
|
||||
public var playerState: PlayerState
|
||||
|
|
@ -38,6 +43,9 @@ public struct PlayerView: View {
|
|||
@State private var position: Double = 0
|
||||
@State private var duration: Double = 0
|
||||
@State private var showControls = true
|
||||
@State private var isFullscreen = false
|
||||
@State private var mpvWidget: OpaquePointer?
|
||||
@State private var tasks = PlayerTasks()
|
||||
|
||||
public init(playerState: PlayerState, onClose: @escaping () -> Void) {
|
||||
self.playerState = playerState
|
||||
|
|
@ -49,7 +57,8 @@ public struct PlayerView: View {
|
|||
url: playerState.streamURL.absoluteString,
|
||||
isPlaying: $isPlaying,
|
||||
position: $position,
|
||||
duration: $duration
|
||||
duration: $duration,
|
||||
onWidgetCreated: { ptr in mpvWidget = ptr }
|
||||
)
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
|
|
@ -105,8 +114,8 @@ public struct PlayerView: View {
|
|||
}
|
||||
|
||||
private func stopPlayback() {
|
||||
progressTask?.cancel()
|
||||
controlsTask?.cancel()
|
||||
tasks.progress?.cancel()
|
||||
tasks.controls?.cancel()
|
||||
Task {
|
||||
try? await client.reportPlaybackStopped(
|
||||
info: .init(
|
||||
|
|
@ -120,7 +129,7 @@ public struct PlayerView: View {
|
|||
}
|
||||
|
||||
private func startProgressTimer() {
|
||||
progressTask = Task {
|
||||
tasks.progress = Task {
|
||||
while !Task.isCancelled {
|
||||
try? await Task.sleep(for: .seconds(10))
|
||||
try? await client.reportPlaybackProgress(
|
||||
|
|
@ -141,22 +150,23 @@ public struct PlayerView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@State private var progressTask: Task<Void, Never>?
|
||||
@State private var controlsTask: Task<Void, Never>?
|
||||
|
||||
// MARK: - Seeking
|
||||
|
||||
private func seekBy(_ seconds: Double) {
|
||||
guard let mpvWidget else { return }
|
||||
mpv_widget_seek_relative(mpvWidget, seconds)
|
||||
}
|
||||
|
||||
private func seekTo(_ position: Double) {
|
||||
guard let mpvWidget else { return }
|
||||
mpv_widget_seek_absolute(mpvWidget, position)
|
||||
}
|
||||
|
||||
// MARK: - Controls visibility
|
||||
|
||||
private func startControlsTimer() {
|
||||
controlsTask?.cancel()
|
||||
controlsTask = Task {
|
||||
tasks.controls?.cancel()
|
||||
tasks.controls = Task {
|
||||
try? await Task.sleep(for: .seconds(3))
|
||||
showControls = false
|
||||
}
|
||||
|
|
@ -165,6 +175,15 @@ public struct PlayerView: View {
|
|||
// MARK: - Fullscreen
|
||||
|
||||
private func toggleFullscreen() {
|
||||
guard let widget = mpvWidget else { return }
|
||||
let root = gtk_widget_get_root(widget.cast())
|
||||
guard let root else { return }
|
||||
if isFullscreen {
|
||||
gtk_window_unfullscreen(root.cast())
|
||||
} else {
|
||||
gtk_window_fullscreen(root.cast())
|
||||
}
|
||||
isFullscreen.toggle()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -186,11 +205,14 @@ struct VideoPlayerWidget: Widget {
|
|||
@Binding var position: Double
|
||||
@Binding var duration: Double
|
||||
|
||||
init(url: String?, isPlaying: Binding<Bool>, position: Binding<Double>, duration: Binding<Double>) {
|
||||
var onWidgetCreated: ((OpaquePointer) -> Void)?
|
||||
|
||||
init(url: String?, isPlaying: Binding<Bool>, position: Binding<Double>, duration: Binding<Double>, onWidgetCreated: ((OpaquePointer) -> Void)? = nil) {
|
||||
self.url = url
|
||||
self._isPlaying = isPlaying
|
||||
self._position = position
|
||||
self._duration = duration
|
||||
self.onWidgetCreated = onWidgetCreated
|
||||
}
|
||||
|
||||
func container<Data>(data: WidgetData, type: Data.Type) -> ViewStorage
|
||||
|
|
@ -255,6 +277,10 @@ struct VideoPlayerWidget: Widget {
|
|||
GConnectFlags(rawValue: 1))
|
||||
}
|
||||
|
||||
if let ptr = storage.opaquePointer, let onWidgetCreated {
|
||||
onWidgetCreated(ptr)
|
||||
}
|
||||
|
||||
for function in appearFunctions {
|
||||
function(storage, data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public struct SeekBar: Widget {
|
|||
public func container<Data>(data: WidgetData, type: Data.Type) -> ViewStorage
|
||||
where Data: ViewRenderData {
|
||||
let widgetPtr = gtk_scale_new_with_range(
|
||||
GtkOrientation(rawValue: 0)!,
|
||||
.GTK_ORIENTATION_HORIZONTAL,
|
||||
range.lowerBound,
|
||||
range.upperBound,
|
||||
1.0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue