1
+ # Licensed under the GPL-3.0 License.
2
+ # Created for TagStudio: https://github.com/CyanVoxel/TagStudio
3
+
1
4
import logging
2
- import os
3
- import typing
4
5
5
- # os.environ["QT_MEDIA_BACKEND"] = "ffmpeg"
6
+ from pathlib import Path
7
+ import typing
6
8
7
9
from PySide6 .QtCore import (
8
10
Qt ,
18
20
from PySide6 .QtMultimediaWidgets import QGraphicsVideoItem
19
21
from PySide6 .QtWidgets import QGraphicsView , QGraphicsScene
20
22
from PySide6 .QtGui import (
21
- QInputMethodEvent ,
22
23
QPen ,
23
24
QColor ,
24
25
QBrush ,
29
30
QBitmap ,
30
31
)
31
32
from PySide6 .QtSvgWidgets import QSvgWidget
32
- from PIL import Image
33
33
from src .qt .helpers .file_opener import FileOpenerHelper
34
-
35
- from src .core .constants import VIDEO_TYPES , AUDIO_TYPES
36
34
from PIL import Image , ImageDraw
37
35
from src .core .enums import SettingItems
38
36
41
39
42
40
43
41
class VideoPlayer (QGraphicsView ):
44
- """A simple video player for the TagStudio application ."""
42
+ """A basic video player."""
45
43
46
- resolution = QSize (1280 , 720 )
47
- hover_fix_timer = QTimer ()
48
44
video_preview = None
49
45
play_pause = None
50
46
mute_button = None
51
- content_visible = False
52
- filepath = None
53
47
54
48
def __init__ (self , driver : "QtDriver" ) -> None :
55
- # Set up the base class.
56
49
super ().__init__ ()
57
50
self .driver = driver
51
+ self .resolution = QSize (1280 , 720 )
58
52
self .animation = QVariantAnimation (self )
59
53
self .animation .valueChanged .connect (
60
54
lambda value : self .setTintTransparency (value )
61
55
)
56
+ self .hover_fix_timer = QTimer ()
62
57
self .hover_fix_timer .timeout .connect (lambda : self .checkIfStillHovered ())
63
58
self .hover_fix_timer .setSingleShot (True )
59
+ self .content_visible = False
60
+ self .filepath = None
61
+
64
62
# Set up the video player.
65
63
self .installEventFilter (self )
66
64
self .setScene (QGraphicsScene (self ))
@@ -82,6 +80,7 @@ def __init__(self, driver: "QtDriver") -> None:
82
80
self .setHorizontalScrollBarPolicy (Qt .ScrollBarPolicy .ScrollBarAlwaysOff )
83
81
self .scene ().addItem (self .video_preview )
84
82
self .video_preview .setAcceptedMouseButtons (Qt .MouseButton .LeftButton )
83
+
85
84
# Set up the video tint.
86
85
self .video_tint = self .scene ().addRect (
87
86
0 ,
@@ -91,44 +90,31 @@ def __init__(self, driver: "QtDriver") -> None:
91
90
QPen (QColor (0 , 0 , 0 , 0 )),
92
91
QBrush (QColor (0 , 0 , 0 , 0 )),
93
92
)
94
- # self.video_tint.setParentItem(self.video_preview)
95
- # self.album_art = QGraphicsPixmapItem(self.video_preview)
96
- # self.scene().addItem(self.album_art)
97
- # self.album_art.setPixmap(
98
- # QPixmap("./tagstudio/resources/qt/images/thumb_file_default_512.png")
99
- # )
100
- # self.album_art.setOpacity(0.0)
93
+
101
94
# Set up the buttons.
102
- self .play_pause = QSvgWidget ("./tagstudio/resources/pause.svg" )
95
+ self .play_pause = QSvgWidget ()
103
96
self .play_pause .setAttribute (Qt .WidgetAttribute .WA_TranslucentBackground , True )
104
97
self .play_pause .setMouseTracking (True )
105
98
self .play_pause .installEventFilter (self )
106
99
self .scene ().addWidget (self .play_pause )
107
- self .play_pause .resize (100 , 100 )
100
+ self .play_pause .resize (72 , 72 )
108
101
self .play_pause .move (
109
102
int (self .width () / 2 - self .play_pause .size ().width () / 2 ),
110
103
int (self .height () / 2 - self .play_pause .size ().height () / 2 ),
111
104
)
112
105
self .play_pause .hide ()
113
106
114
- self .mute_button = QSvgWidget ("./tagstudio/resources/volume_muted.svg" )
107
+ self .mute_button = QSvgWidget ()
115
108
self .mute_button .setAttribute (Qt .WidgetAttribute .WA_TranslucentBackground , True )
116
109
self .mute_button .setMouseTracking (True )
117
110
self .mute_button .installEventFilter (self )
118
111
self .scene ().addWidget (self .mute_button )
119
- self .mute_button .resize (40 , 40 )
112
+ self .mute_button .resize (32 , 32 )
120
113
self .mute_button .move (
121
114
int (self .width () - self .mute_button .size ().width () / 2 ),
122
115
int (self .height () - self .mute_button .size ().height () / 2 ),
123
116
)
124
117
self .mute_button .hide ()
125
- # self.fullscreen_button = QSvgWidget('./tagstudio/resources/pause.svg', self)
126
- # self.fullscreen_button.setMouseTracking(True)
127
- # self.fullscreen_button.installEventFilter(self)
128
- # self.scene().addWidget(self.fullscreen_button)
129
- # self.fullscreen_button.resize(40, 40)
130
- # self.fullscreen_button.move(self.fullscreen_button.size().width()/2, self.height() - self.fullscreen_button.size().height()/2)
131
- # self.fullscreen_button.hide()
132
118
133
119
self .setContextMenuPolicy (Qt .ContextMenuPolicy .ActionsContextMenu )
134
120
self .opener = FileOpenerHelper (filepath = self .filepath )
@@ -157,37 +143,32 @@ def toggleAutoplay(self) -> None:
157
143
self .driver .settings .sync ()
158
144
159
145
def checkMediaStatus (self , media_status : QMediaPlayer .MediaStatus ) -> None :
160
- # logging.info(media_status)
161
146
if media_status == QMediaPlayer .MediaStatus .EndOfMedia :
162
- # Switches current video to with video at filepath. Reason for this is because Pyside6 is dumb and can't handle setting a new source and freezes.
147
+ # Switches current video to with video at filepath.
148
+ # Reason for this is because Pyside6 can't handle setting a new source and freezes.
163
149
# Even if I stop the player before switching, it breaks.
164
150
# On the plus side, this adds infinite looping for the video preview.
165
151
self .player .stop ()
166
152
self .player .setSource (QUrl ().fromLocalFile (self .filepath ))
167
- # logging.info(f'Set source to {self.filepath}.')
168
- # self.video_preview.setSize(self.resolution)
169
153
self .player .setPosition (0 )
170
- # logging.info(f'Set muted to true.')
171
154
if self .autoplay .isChecked ():
172
- # logging.info(self.driver.settings.value("autoplay_videos", True, bool))
173
155
self .player .play ()
174
156
else :
175
- # logging.info("Paused")
176
157
self .player .pause ()
177
158
self .opener .set_filepath (self .filepath )
178
159
self .keepControlsInPlace ()
179
160
self .updateControls ()
180
161
181
162
def updateControls (self ) -> None :
182
163
if self .player .audioOutput ().isMuted ():
183
- self .mute_button .load ("./tagstudio/resources/volume_muted.svg" )
164
+ self .mute_button .load (self . driver . rm . volume_mute_icon )
184
165
else :
185
- self .mute_button .load ("./tagstudio/resources/volume_unmuted.svg" )
166
+ self .mute_button .load (self . driver . rm . volume_icon )
186
167
187
168
if self .player .isPlaying ():
188
- self .play_pause .load ("./tagstudio/resources/pause.svg" )
169
+ self .play_pause .load (self . driver . rm . pause_icon )
189
170
else :
190
- self .play_pause .load ("./tagstudio/resources/play.svg" )
171
+ self .play_pause .load (self . driver . rm . play_icon )
191
172
192
173
def wheelEvent (self , event : QWheelEvent ) -> None :
193
174
return
@@ -229,8 +210,10 @@ def eventFilter(self, obj: QObject, event: QEvent) -> bool:
229
210
return super ().eventFilter (obj , event )
230
211
231
212
def checkIfStillHovered (self ) -> None :
232
- # Yet again, Pyside6 is dumb. I don't know why, but the HoverLeave event is not triggered sometimes and does not hide the controls.
233
- # So, this is a workaround. This is called by a QTimer every 10ms to check if the mouse is still in the video preview.
213
+ # I don't know why, but the HoverLeave event is not triggered sometimes
214
+ # and does not hide the controls.
215
+ # So, this is a workaround. This is called by a QTimer every 10ms to check if the mouse
216
+ # is still in the video preview.
234
217
if not self .video_preview .isUnderMouse ():
235
218
self .releaseMouse ()
236
219
else :
@@ -240,55 +223,51 @@ def setTintTransparency(self, value) -> None:
240
223
self .video_tint .setBrush (QBrush (QColor (0 , 0 , 0 , value )))
241
224
242
225
def underMouse (self ) -> bool :
243
- # logging.info("under mouse")
244
226
self .animation .setStartValue (self .video_tint .brush ().color ().alpha ())
245
227
self .animation .setEndValue (100 )
246
- self .animation .setDuration (500 )
228
+ self .animation .setDuration (250 )
247
229
self .animation .start ()
248
230
self .play_pause .show ()
249
231
self .mute_button .show ()
250
- # self.fullscreen_button.show()
251
232
self .keepControlsInPlace ()
252
233
self .updateControls ()
253
- # rcontent = self.contentsRect()
254
- # self.setSceneRect(0, 0, rcontent.width(), rcontent.height())
234
+
255
235
return super ().underMouse ()
256
236
257
237
def releaseMouse (self ) -> None :
258
- # logging.info("release mouse")
259
238
self .animation .setStartValue (self .video_tint .brush ().color ().alpha ())
260
239
self .animation .setEndValue (0 )
261
240
self .animation .setDuration (500 )
262
241
self .animation .start ()
263
242
self .play_pause .hide ()
264
243
self .mute_button .hide ()
265
- # self.fullscreen_button.hide()
244
+
266
245
return super ().releaseMouse ()
267
246
268
247
def resetControlsToDefault (self ) -> None :
269
248
# Resets the video controls to their default state.
270
- self .play_pause .load ("./tagstudio/resources/pause.svg" )
271
- self .mute_button .load ("./tagstudio/resources/volume_muted.svg" )
249
+ self .play_pause .load (self . driver . rm . pause_icon )
250
+ self .mute_button .load (self . driver . rm . volume_mute_icon )
272
251
273
252
def pauseToggle (self ) -> None :
274
253
if self .player .isPlaying ():
275
254
self .player .pause ()
276
- self .play_pause .load ("./tagstudio/resources/play.svg" )
255
+ self .play_pause .load (self . driver . rm . play_icon )
277
256
else :
278
257
self .player .play ()
279
- self .play_pause .load ("./tagstudio/resources/pause.svg" )
258
+ self .play_pause .load (self . driver . rm . pause_icon )
280
259
281
260
def muteToggle (self ) -> None :
282
261
if self .player .audioOutput ().isMuted ():
283
262
self .player .audioOutput ().setMuted (False )
284
- self .mute_button .load ("./tagstudio/resources/volume_unmuted.svg" )
263
+ self .mute_button .load (self . driver . rm . volume_icon )
285
264
else :
286
265
self .player .audioOutput ().setMuted (True )
287
- self .mute_button .load ("./tagstudio/resources/volume_muted.svg" )
266
+ self .mute_button .load (self . driver . rm . volume_mute_icon )
288
267
289
268
def play (self , filepath : str , resolution : QSize ) -> None :
290
- # Sets the filepath and sends the current player position to the very end, so that the new video can be played.
291
- # self.player.audioOutput().setMuted(True)
269
+ # Sets the filepath and sends the current player position to the very end,
270
+ # so that the new video can be played.
292
271
logging .info (f"Playing { filepath } " )
293
272
self .resolution = resolution
294
273
self .filepath = filepath
@@ -297,7 +276,6 @@ def play(self, filepath: str, resolution: QSize) -> None:
297
276
self .player .play ()
298
277
else :
299
278
self .checkMediaStatus (QMediaPlayer .MediaStatus .EndOfMedia )
300
- # logging.info(f"Successfully stopped.")
301
279
302
280
def stop (self ) -> None :
303
281
self .filepath = None
@@ -310,10 +288,10 @@ def resizeVideo(self, new_size: QSize) -> None:
310
288
0 , 0 , self .video_preview .size ().width (), self .video_preview .size ().height ()
311
289
)
312
290
313
- rcontent = self .contentsRect ()
291
+ contents = self .contentsRect ()
314
292
self .centerOn (self .video_preview )
315
293
self .roundCorners ()
316
- self .setSceneRect (0 , 0 , rcontent .width (), rcontent .height ())
294
+ self .setSceneRect (0 , 0 , contents .width (), contents .height ())
317
295
self .keepControlsInPlace ()
318
296
319
297
def roundCorners (self ) -> None :
@@ -346,7 +324,6 @@ def keepControlsInPlace(self) -> None:
346
324
int (self .width () - self .mute_button .size ().width () - 10 ),
347
325
int (self .height () - self .mute_button .size ().height () - 10 ),
348
326
)
349
- # self.fullscreen_button.move(-self.fullscreen_button.size().width()-10, self.height() - self.fullscreen_button.size().height()-10)
350
327
351
328
def resizeEvent (self , event : QResizeEvent ) -> None :
352
329
# Keeps the video preview in the center of the screen.
@@ -358,7 +335,6 @@ def resizeEvent(self, event: QResizeEvent) -> None:
358
335
)
359
336
)
360
337
return
361
- # return super().resizeEvent(event)\
362
338
363
339
364
340
class VideoPreview (QGraphicsVideoItem ):
@@ -367,7 +343,8 @@ def boundingRect(self):
367
343
368
344
def paint (self , painter , option , widget ):
369
345
# painter.brush().setColor(QColor(0, 0, 0, 255))
370
- # You can set any shape you want here. RoundedRect is the standard rectangle with rounded corners
346
+ # You can set any shape you want here.
347
+ # RoundedRect is the standard rectangle with rounded corners.
371
348
# With 2nd and 3rd parameter you can tweak the curve until you get what you expect
372
349
373
350
super ().paint (painter , option , widget )
0 commit comments