@@ -12,85 +12,15 @@ struct PaneVideoViewer: View {
1212 let videoUrl : URL ?
1313
1414 @State private var player : AVPlayer ?
15- @State private var isPlaying : Bool = false
16- @State private var currentTime : Double = 0
17- @State private var duration : Double = 0
18- @State private var volume : Float = 1.0
19- @State private var playbackRate : Float = 1.0
20-
21- // Time observer
22- @State private var timeObserver : Any ?
2315
2416 var body : some View {
2517 Group {
26- if let videoUrl = videoUrl {
27- VStack {
28- // Video Player
29- ScrollView ( [ . horizontal, . vertical] ) {
30- VideoPlayer ( player: player)
31- . containerRelativeFrame ( [ . horizontal, . vertical] )
32- . background ( Color . black)
33- }
34-
35- // Custom Controls
36- VStack {
37- // Time Slider and Labels
38- HStack {
39- Text ( formatTime ( currentTime) )
40- . font ( . caption)
41- . monospacedDigit ( )
42-
43- Slider ( value: Binding (
44- get: { currentTime } ,
45- set: { seek ( to: $0) }
46- ) , in: 0 ... duration)
47-
48- Text ( formatTime ( duration) )
49- . font ( . caption)
50- . monospacedDigit ( )
51- }
52-
53- HStack {
54- // Play/Pause
55- Button ( action: togglePlayPause) {
56- Image ( systemName: isPlaying ? " pause.circle " : " play.circle " )
57- . font ( . title)
58- }
59-
60- // Rewind 10s
61- Button ( action: rewindTenSeconds) {
62- Image ( systemName: " gobackward.10 " )
63- . font ( . title)
64- }
65-
66- // Forward 10s
67- Button ( action: forwardTenSeconds) {
68- Image ( systemName: " goforward.10 " )
69- . font ( . title)
70- }
71-
72- // Volume
73- HStack {
74- Image ( systemName: " speaker.wave.2 " )
75- Slider ( value: $volume, in: 0 ... 1 ) {
76- Text ( " Volume " )
77- }
78- . frame ( width: 100 )
79- }
80-
81- // Playback Speed
82- Menu {
83- Button ( " 0.5x " ) { setPlaybackRate ( 0.5 ) }
84- Button ( " 1.0x " ) { setPlaybackRate ( 1.0 ) }
85- Button ( " 1.5x " ) { setPlaybackRate ( 1.5 ) }
86- Button ( " 2.0x " ) { setPlaybackRate ( 2.0 ) }
87- } label: {
88- Text ( " \( String ( format: " %.1fx " , playbackRate) ) " )
89- }
90- }
91- }
92- . padding ( )
93- }
18+ if videoUrl != nil {
19+ // Video Player with built-in controls
20+ VideoPlayer ( player: player)
21+ . background ( Color . black)
22+ . clipShape ( RoundedRectangle ( cornerRadius: 8 ) )
23+ . padding ( 16 )
9424 } else {
9525 // Empty State
9626 VStack {
@@ -105,12 +35,12 @@ struct PaneVideoViewer: View {
10535 . background ( Color ( NSColor . controlBackgroundColor) )
10636 }
10737 }
38+ . frame ( maxWidth: . infinity, maxHeight: . infinity)
39+ //.background(Color(NSColor.windowBackgroundColor))
40+ . background ( Color ( NSColor . black) )
10841 . onChange ( of: videoUrl) { oldValue, newValue in
10942 loadVideo ( from: newValue)
11043 }
111- . onChange ( of: volume) { oldValue, newValue in
112- player? . volume = volume
113- }
11444 . onAppear {
11545 loadVideo ( from: videoUrl)
11646 }
@@ -127,73 +57,15 @@ struct PaneVideoViewer: View {
12757 let playerItem = AVPlayerItem ( url: url)
12858 let newPlayer = AVPlayer ( playerItem: playerItem)
12959
130- // Set initial volume
131- newPlayer. volume = volume
132-
133- // Observe video duration
134- let durationObserver = playerItem. observe ( \. duration) { item, _ in
135- duration = item. duration. seconds
136- }
137-
138- // Add time observer
139- let interval = CMTime ( seconds: 0.5 , preferredTimescale: CMTimeScale ( NSEC_PER_SEC) )
140- timeObserver = newPlayer. addPeriodicTimeObserver ( forInterval: interval, queue: . main) { time in
141- currentTime = time. seconds
142- }
143-
14460 self . player = newPlayer
14561 }
14662
14763 private func cleanup( ) {
148- if let observer = timeObserver {
149- player? . removeTimeObserver ( observer)
150- }
15164 player? . pause ( )
15265 player = nil
153- isPlaying = false
154- currentTime = 0
155- duration = 0
156- }
157-
158- private func togglePlayPause( ) {
159- isPlaying. toggle ( )
160- if isPlaying {
161- player? . play ( )
162- } else {
163- player? . pause ( )
164- }
165- }
166-
167- private func seek( to time: Double ) {
168- let newTime = CMTime ( seconds: time, preferredTimescale: CMTimeScale ( NSEC_PER_SEC) )
169- player? . seek ( to: newTime)
17066 }
17167
172- private func rewindTenSeconds( ) {
173- seek ( to: max ( currentTime - 10 , 0 ) )
174- }
17568
176- private func forwardTenSeconds( ) {
177- seek ( to: min ( currentTime + 10 , duration) )
178- }
179-
180- private func setPlaybackRate( _ rate: Float ) {
181- playbackRate = rate
182- player? . rate = rate
183- }
184-
185- private func formatTime( _ seconds: Double ) -> String {
186- let time = Int ( seconds)
187- let hours = time / 3600
188- let minutes = ( time % 3600 ) / 60
189- let seconds = time % 60
190-
191- if hours > 0 {
192- return String ( format: " %d:%02d:%02d " , hours, minutes, seconds)
193- } else {
194- return String ( format: " %02d:%02d " , minutes, seconds)
195- }
196- }
19769}
19870
19971#Preview( " No Selection " ) {
0 commit comments