44from PyQt6 import QtGui
55from PyQt6 .QtCore import QTime , QUrl , Qt , pyqtSignal
66from PyQt6 .QtMultimedia import QAudioOutput , QMediaPlayer
7- from PyQt6 .QtWidgets import QWidget , QSlider , QPushButton , QLabel , QHBoxLayout
7+ from PyQt6 .QtWidgets import QWidget , QSlider , QPushButton , QLabel , QHBoxLayout , QVBoxLayout
88
99from buzz .widgets .icon import PlayIcon , PauseIcon
1010from buzz .settings .settings import Settings
11+ from buzz .transcriber .file_transcriber import is_video_file
1112
1213
1314class AudioPlayer (QWidget ):
@@ -21,17 +22,27 @@ def __init__(self, file_path: str):
2122 self .duration_ms = 0
2223 self .invalid_media = None
2324 self .is_looping = False # Flag to prevent recursive position changes
25+ self .is_slider_dragging = False # Flag to track if use is dragging slider
2426
2527 # Initialize settings
2628 self .settings = Settings ()
2729
30+ self .is_video = is_video_file (file_path )
31+
2832 self .audio_output = QAudioOutput ()
2933 self .audio_output .setVolume (100 )
3034
3135 self .media_player = QMediaPlayer ()
3236 self .media_player .setSource (QUrl .fromLocalFile (file_path ))
3337 self .media_player .setAudioOutput (self .audio_output )
3438
39+ if self .is_video :
40+ from PyQt6 .QtMultimediaWidgets import QVideoWidget
41+ self .video_widget = QVideoWidget (self )
42+ self .media_player .setVideoOutput (self .video_widget )
43+ else :
44+ self .video_widget = None
45+
3546 # Speed control moved to transcription viewer - just set default rate
3647 saved_rate = self .settings .value (Settings .Key .AUDIO_PLAYBACK_RATE , 1.0 , float )
3748 saved_rate = max (0.1 , min (5.0 , saved_rate )) # Ensure valid range
@@ -40,6 +51,11 @@ def __init__(self, file_path: str):
4051 self .scrubber = QSlider (Qt .Orientation .Horizontal )
4152 self .scrubber .setRange (0 , 0 )
4253 self .scrubber .sliderMoved .connect (self .on_slider_moved )
54+ self .scrubber .sliderPressed .connect (self .on_slider_pressed )
55+ self .scrubber .sliderReleased .connect (self .on_slider_released )
56+
57+ # Track if user is dragging the slider
58+ self .is_slider_dragging = False
4359
4460 self .play_icon = PlayIcon (self )
4561 self .pause_icon = PauseIcon (self )
@@ -54,10 +70,23 @@ def __init__(self, file_path: str):
5470 self .time_label .setAlignment (Qt .AlignmentFlag .AlignRight )
5571
5672 # Create main layout - simplified without speed controls
57- main_layout = QHBoxLayout ()
58- main_layout .addWidget (self .play_button , alignment = Qt .AlignmentFlag .AlignVCenter )
59- main_layout .addWidget (self .scrubber , alignment = Qt .AlignmentFlag .AlignVCenter )
60- main_layout .addWidget (self .time_label , alignment = Qt .AlignmentFlag .AlignVCenter )
73+ if self .is_video :
74+ #Vertical layout for video
75+ main_layout = QVBoxLayout ()
76+ main_layout .addWidget (self .video_widget , stretch = 1 ) # As video takes more space
77+
78+ controls_layout = QHBoxLayout ()
79+ controls_layout .addWidget (self .play_button , alignment = Qt .AlignmentFlag .AlignVCenter )
80+ controls_layout .addWidget (self .scrubber , alignment = Qt .AlignmentFlag .AlignVCenter )
81+ controls_layout .addWidget (self .time_label , alignment = Qt .AlignmentFlag .AlignVCenter )
82+
83+ main_layout .addLayout (controls_layout )
84+ else :
85+ # Horizontal layout for audio only
86+ main_layout = QHBoxLayout ()
87+ main_layout .addWidget (self .play_button , alignment = Qt .AlignmentFlag .AlignVCenter )
88+ main_layout .addWidget (self .scrubber , alignment = Qt .AlignmentFlag .AlignVCenter )
89+ main_layout .addWidget (self .time_label , alignment = Qt .AlignmentFlag .AlignVCenter )
6190
6291 self .setLayout (main_layout )
6392
@@ -75,7 +104,12 @@ def on_duration_changed(self, duration_ms: int):
75104 self .update_time_label ()
76105
77106 def on_position_changed (self , position_ms : int ):
78- self .scrubber .setValue (position_ms )
107+ # Don't update slider if user is currently dragging it
108+ if not self .is_slider_dragging :
109+ self .scrubber .blockSignals (True )
110+ self .scrubber .setValue (position_ms )
111+ self .scrubber .blockSignals (False )
112+
79113 self .position_ms = position_ms
80114 self .position_ms_changed .emit (self .position_ms )
81115 self .update_time_label ()
@@ -150,6 +184,16 @@ def on_slider_moved(self, position_ms: int):
150184 if position_ms < (start_range_ms - 2000 ) or position_ms > (end_range_ms + 2000 ):
151185 self .range_ms = None
152186
187+ def on_slider_pressed (self ):
188+ """Called when the user starts dragging the slider"""
189+ self .is_slider_dragging = True
190+
191+ def on_slider_released (self ):
192+ """Called when user releases the slider"""
193+ self .is_slider_dragging = False
194+ # Update the position where user released
195+ self .set_position (self .scrubber .value ())
196+
153197 def set_position (self , position_ms : int ):
154198 self .media_player .setPosition (position_ms )
155199
0 commit comments