From 305e89044f84eb1d3af91eabaeb74f2f77adf846 Mon Sep 17 00:00:00 2001 From: vzhd1701 Date: Wed, 13 Jul 2022 21:28:11 +0500 Subject: [PATCH] feat: add option to set auto reload timer for live videos --- gridplayer/dialogs/settings.py | 8 ++- gridplayer/dialogs/settings_dialog_ui.py | 62 ++++++++++++++----- gridplayer/models/video.py | 1 + gridplayer/player/managers/actions.py | 13 ++++ gridplayer/player/managers/active_block.py | 6 +- gridplayer/player/managers/menu.py | 2 + gridplayer/player/managers/video_blocks.py | 22 ++++++- gridplayer/settings.py | 1 + gridplayer/widgets/video_block.py | 60 ++++++++++++++++++- resources/ui/settings_dialog.ui | 69 +++++++++++++++++----- 10 files changed, 208 insertions(+), 36 deletions(-) diff --git a/gridplayer/dialogs/settings.py b/gridplayer/dialogs/settings.py index 72fefe2..e9413e1 100644 --- a/gridplayer/dialogs/settings.py +++ b/gridplayer/dialogs/settings.py @@ -89,6 +89,7 @@ def __init__(self, parent): "video_defaults/muted": self.videoMuted, "video_defaults/paused": self.videoPaused, "video_defaults/stream_quality": self.streamQuality, + "video_defaults/auto_reload_timer": self.streamAutoReloadTimer, "misc/overlay_hide": self.timeoutOverlayFlag, "misc/overlay_timeout": self.timeoutOverlay, "misc/mouse_hide": self.timeoutMouseHideFlag, @@ -149,7 +150,7 @@ def ui_fill(self): # noqa: WPS213 self.fill_playlistSeekSyncMode() self.fill_streamingResolverPriority() - def ui_set_limits(self): + def ui_set_limits(self): # noqa: WPS213 self.playerVideoDriverPlayers.setRange(1, MAX_VLC_PROCESSES) self.timeoutOverlay.setRange(1, 60) self.timeoutMouseHide.setRange(1, 60) @@ -160,6 +161,11 @@ def ui_set_limits(self): self.gridSize.setRange(0, 1000) self.gridSize.setSpecialValueText(translate("Grid Size", "Auto")) + self.streamAutoReloadTimer.setRange(0, 1000) + self.streamAutoReloadTimer.setSpecialValueText( + translate("Auto Reload Timer", "Disabled") + ) + def ui_customize_dynamic(self): self.driver_selected(self.playerVideoDriver.currentIndex()) self.timeoutMouseHide.setEnabled(self.timeoutMouseHideFlag.isChecked()) diff --git a/gridplayer/dialogs/settings_dialog_ui.py b/gridplayer/dialogs/settings_dialog_ui.py index f81582e..68285e0 100644 --- a/gridplayer/dialogs/settings_dialog_ui.py +++ b/gridplayer/dialogs/settings_dialog_ui.py @@ -311,34 +311,24 @@ def setupUi(self, SettingsDialog): QtWidgets.QFormLayout.FieldsStayAtSizeHint ) self.formLayout_4.setObjectName("formLayout_4") - self.streamQualityLabel = QtWidgets.QLabel(self.page_defaults_video) - self.streamQualityLabel.setObjectName("streamQualityLabel") - self.formLayout_4.setWidget( - 0, QtWidgets.QFormLayout.LabelRole, self.streamQualityLabel - ) - self.streamQuality = QtWidgets.QComboBox(self.page_defaults_video) - self.streamQuality.setObjectName("streamQuality") - self.formLayout_4.setWidget( - 0, QtWidgets.QFormLayout.FieldRole, self.streamQuality - ) self.videoAspectLabel = QtWidgets.QLabel(self.page_defaults_video) self.videoAspectLabel.setObjectName("videoAspectLabel") self.formLayout_4.setWidget( - 1, QtWidgets.QFormLayout.LabelRole, self.videoAspectLabel + 0, QtWidgets.QFormLayout.LabelRole, self.videoAspectLabel ) self.videoAspect = QtWidgets.QComboBox(self.page_defaults_video) self.videoAspect.setObjectName("videoAspect") self.formLayout_4.setWidget( - 1, QtWidgets.QFormLayout.FieldRole, self.videoAspect + 0, QtWidgets.QFormLayout.FieldRole, self.videoAspect ) self.repeatModeLabel = QtWidgets.QLabel(self.page_defaults_video) self.repeatModeLabel.setObjectName("repeatModeLabel") self.formLayout_4.setWidget( - 2, QtWidgets.QFormLayout.LabelRole, self.repeatModeLabel + 1, QtWidgets.QFormLayout.LabelRole, self.repeatModeLabel ) self.repeatMode = QtWidgets.QComboBox(self.page_defaults_video) self.repeatMode.setObjectName("repeatMode") - self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.repeatMode) + self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.repeatMode) self.lay_page_defaults_video.addLayout(self.formLayout_4) self.videoRandomLoop = QtWidgets.QCheckBox(self.page_defaults_video) self.videoRandomLoop.setObjectName("videoRandomLoop") @@ -349,6 +339,43 @@ def setupUi(self, SettingsDialog): self.videoMuted = QtWidgets.QCheckBox(self.page_defaults_video) self.videoMuted.setObjectName("videoMuted") self.lay_page_defaults_video.addWidget(self.videoMuted) + self.label_12 = QtWidgets.QLabel(self.page_defaults_video) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_12.setFont(font) + self.label_12.setObjectName("label_12") + self.lay_page_defaults_video.addWidget(self.label_12) + self.formLayout_9 = QtWidgets.QFormLayout() + self.formLayout_9.setFieldGrowthPolicy( + QtWidgets.QFormLayout.FieldsStayAtSizeHint + ) + self.formLayout_9.setObjectName("formLayout_9") + self.streamQualityLabel = QtWidgets.QLabel(self.page_defaults_video) + self.streamQualityLabel.setObjectName("streamQualityLabel") + self.formLayout_9.setWidget( + 0, QtWidgets.QFormLayout.LabelRole, self.streamQualityLabel + ) + self.streamQuality = QtWidgets.QComboBox(self.page_defaults_video) + self.streamQuality.setObjectName("streamQuality") + self.formLayout_9.setWidget( + 0, QtWidgets.QFormLayout.FieldRole, self.streamQuality + ) + self.label_13 = QtWidgets.QLabel(self.page_defaults_video) + self.label_13.setObjectName("label_13") + self.formLayout_9.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_13) + self.horizontalLayout_5 = QtWidgets.QHBoxLayout() + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.streamAutoReloadTimer = QtWidgets.QSpinBox(self.page_defaults_video) + self.streamAutoReloadTimer.setObjectName("streamAutoReloadTimer") + self.horizontalLayout_5.addWidget(self.streamAutoReloadTimer) + self.label_14 = QtWidgets.QLabel(self.page_defaults_video) + self.label_14.setObjectName("label_14") + self.horizontalLayout_5.addWidget(self.label_14) + self.formLayout_9.setLayout( + 1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_5 + ) + self.lay_page_defaults_video.addLayout(self.formLayout_9) spacerItem2 = QtWidgets.QSpacerItem( 20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding ) @@ -501,7 +528,7 @@ def setupUi(self, SettingsDialog): self.lay_main.setStretch(0, 1) self.retranslateUi(SettingsDialog) - self.section_page.setCurrentIndex(5) + self.section_page.setCurrentIndex(4) self.buttonBox.accepted.connect(SettingsDialog.accept) # type: ignore self.buttonBox.rejected.connect(SettingsDialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(SettingsDialog) @@ -608,7 +635,6 @@ def retranslateUi(self, SettingsDialog): self.gridSizeLabel.setText(_translate("SettingsDialog", "Grid size")) self.gridFit.setText(_translate("SettingsDialog", "Fit grid cells")) self.gridShuffleOnLoad.setText(_translate("SettingsDialog", "Shuffle on load")) - self.streamQualityLabel.setText(_translate("SettingsDialog", "Stream quality")) self.videoAspectLabel.setText(_translate("SettingsDialog", "Aspect mode")) self.repeatModeLabel.setText(_translate("SettingsDialog", "Repeat mode")) self.videoRandomLoop.setText( @@ -616,6 +642,10 @@ def retranslateUi(self, SettingsDialog): ) self.videoPaused.setText(_translate("SettingsDialog", "Paused")) self.videoMuted.setText(_translate("SettingsDialog", "Muted")) + self.label_12.setText(_translate("SettingsDialog", "Streaming Videos")) + self.streamQualityLabel.setText(_translate("SettingsDialog", "Stream quality")) + self.label_13.setText(_translate("SettingsDialog", "Auto reload time")) + self.label_14.setText(_translate("SettingsDialog", "(min)")) self.playerVideoDriverBox.setTitle( _translate("SettingsDialog", "Video Decoder") ) diff --git a/gridplayer/models/video.py b/gridplayer/models/video.py index d30c267..6cba8cc 100644 --- a/gridplayer/models/video.py +++ b/gridplayer/models/video.py @@ -82,6 +82,7 @@ class Video(BaseModel): # Streamable stream_quality: str = default_field("video_defaults/stream_quality") + auto_reload_timer_min: int = default_field("video_defaults/auto_reload_timer") @property def uri_name(self) -> str: diff --git a/gridplayer/player/managers/actions.py b/gridplayer/player/managers/actions.py index 79bb6fd..835c5a4 100644 --- a/gridplayer/player/managers/actions.py +++ b/gridplayer/player/managers/actions.py @@ -382,6 +382,13 @@ "key": "F5", "func": ("active", "reload"), }, + "Auto Reload: %v": { + "title": "{0}: %v".format(translate("Actions", "Auto Reload")), + "icon": "reload", + "func": ("active", "auto_reload_timer"), + "value_getter": ("active", "get_auto_reload_timer"), + "show_if": "is_active_live", + }, "Close": { "title": translate("Actions", "Close"), "key": "Ctrl+F4", @@ -703,6 +710,12 @@ "key": "Shift+F5", "func": "reload_all", }, + "Auto Reload [ALL]": { + "title": translate("Actions", "Auto Reload"), + "icon": "reload", + "func": "all_set_auto_reload_timer", + "show_if": "is_any_videos_live", + }, # Playlist "Seek Sync (Disabled)": { "title": translate("Actions", "Disabled"), diff --git a/gridplayer/player/managers/active_block.py b/gridplayer/player/managers/active_block.py index 3dd0346..48e9b6b 100644 --- a/gridplayer/player/managers/active_block.py +++ b/gridplayer/player/managers/active_block.py @@ -50,14 +50,14 @@ def is_no_active_block(self): def cmd_active(self, command, *args): if self.is_no_active_block: - return + return None is_inactive_command = command in {"switch_stream_quality", "reload", "close"} if not is_inactive_command and not self.is_active_initialized(): - return + return None - getattr(self._ctx.active_block, command)(*args) + return getattr(self._ctx.active_block, command)(*args) def is_active_initialized(self): if self.is_no_active_block: diff --git a/gridplayer/player/managers/menu.py b/gridplayer/player/managers/menu.py index 0d8d179..fb9c8ee 100644 --- a/gridplayer/player/managers/menu.py +++ b/gridplayer/player/managers/menu.py @@ -121,6 +121,7 @@ "Stream Quality", "Rename", "Reload", + "Auto Reload: %v", "Close", ], "video_all": [ @@ -198,6 +199,7 @@ ), "---", "Reload [ALL]", + "Auto Reload [ALL]", ), ( "Seek Sync", diff --git a/gridplayer/player/managers/video_blocks.py b/gridplayer/player/managers/video_blocks.py index 22f1a92..10664c9 100644 --- a/gridplayer/player/managers/video_blocks.py +++ b/gridplayer/player/managers/video_blocks.py @@ -3,7 +3,7 @@ from PyQt5.QtCore import Qt, pyqtSignal -from gridplayer.dialogs.input_dialog import QCustomSpinboxTimeInput +from gridplayer.dialogs.input_dialog import QCustomSpinboxInput, QCustomSpinboxTimeInput from gridplayer.models.video import Video from gridplayer.params.static import SeekSyncMode, VideoAspect, VideoRepeat from gridplayer.player.managers.base import ManagerBase @@ -122,6 +122,7 @@ class VideoBlocksManager(ManagerBase): all_scale_reset = pyqtSignal() all_set_aspect = pyqtSignal(VideoAspect) + all_set_auto_reload_timer = pyqtSignal(int) def __init__(self, **kwargs): super().__init__(**kwargs) @@ -144,10 +145,12 @@ def commands(self): "all": self.cmd_all, "all_play_pause": self.cmd_all_play_pause, "all_seek_timecode": self.cmd_seek_timecode, + "all_set_auto_reload_timer": self.cmd_set_auto_reload_timer, "is_videos": lambda: bool(self._ctx.video_blocks), "is_any_videos_initialized": self.is_any_videos_initialized, "is_any_videos_seekable": self.is_any_videos_seekable, "is_any_videos_local_file": self.is_any_videos_local_file, + "is_any_videos_live": self.is_any_videos_live, "is_seek_sync_mode_set_to": self.is_seek_sync_mode_set_to, "set_seek_sync_mode": self.set_seek_sync_mode, "reload_all": self.reload_videos, @@ -185,6 +188,19 @@ def cmd_seek_timecode(self): self.all_seek.emit(time_ms) + def cmd_set_auto_reload_timer(self): + time_minutes = QCustomSpinboxInput.get_int( + parent=self.parent(), + title=translate( + "Dialog - Set auto reload timer", "Set auto reload timer", "Header" + ), + special_text=translate("Auto Reload Timer", "Disabled"), + _min=0, + _max=1000, + ) + + self.all_set_auto_reload_timer.emit(time_minutes) + def cmd_shuffle_video_blocks(self): self._ctx.video_blocks.shuffle() self.video_order_changed.emit() @@ -227,6 +243,9 @@ def is_any_videos_initialized(self): def is_any_videos_seekable(self): return any(not vb.is_live for vb in self._ctx.video_blocks.initialized) + def is_any_videos_live(self): + return any(vb.is_live for vb in self._ctx.video_blocks.initialized) + def is_any_videos_local_file(self): return any(vb.is_local_file for vb in self._ctx.video_blocks.initialized) @@ -326,6 +345,7 @@ def _add_video_block(self, video): (self.all_scale_decrease, vb.scale_decrease), (self.all_scale_reset, vb.scale_reset), (self.all_set_aspect, vb.set_aspect), + (self.all_set_auto_reload_timer, vb.set_auto_reload_timer), (self.all_previous_video, vb.previous_video), (self.all_next_video, vb.next_video), (self.hide_overlay, vb.hide_overlay), diff --git a/gridplayer/settings.py b/gridplayer/settings.py index 1ff2212..42dde2e 100644 --- a/gridplayer/settings.py +++ b/gridplayer/settings.py @@ -55,6 +55,7 @@ def default_language(): "video_defaults/muted": True, "video_defaults/paused": False, "video_defaults/stream_quality": "best", + "video_defaults/auto_reload_timer": 0, "misc/overlay_hide": True, "misc/overlay_timeout": 3, "misc/mouse_hide": True, diff --git a/gridplayer/widgets/video_block.py b/gridplayer/widgets/video_block.py index 9160132..3f8186d 100644 --- a/gridplayer/widgets/video_block.py +++ b/gridplayer/widgets/video_block.py @@ -10,7 +10,7 @@ from PyQt5.QtGui import QCursor from PyQt5.QtWidgets import QStackedLayout, QWidget -from gridplayer.dialogs.input_dialog import QCustomSpinboxTimeInput +from gridplayer.dialogs.input_dialog import QCustomSpinboxInput, QCustomSpinboxTimeInput from gridplayer.dialogs.rename_dialog import QVideoRenameDialog from gridplayer.exceptions import PlayerException from gridplayer.models.stream import Streams @@ -80,6 +80,16 @@ def wrapper(*args, **kwargs): return wrapper +def only_live(func): + def wrapper(*args, **kwargs): + self = args[0] # noqa: WPS117 + if not self.is_live: + return None + return func(*args, **kwargs) + + return wrapper + + def only_local_file(func): def wrapper(*args, **kwargs): self = args[0] # noqa: WPS117 @@ -157,6 +167,9 @@ def __init__(self, video_driver, context, **kwargs): self._in_progress_timer.setInterval(IN_PROGRESS_THRESHOLD_MS) self._in_progress_timer.timeout.connect(self.is_in_progress_change) + self._reload_timer = QTimer(self) + self._reload_timer.timeout.connect(self.reload) + self.url_resolver = self.init_url_resolver() self.video_driver = self.init_video_driver() @@ -421,6 +434,48 @@ def seek_timecode(self): self.manual_seek("seek", time_ms) + @only_initialized + @only_live + def auto_reload_timer(self): + time_min = QCustomSpinboxInput.get_int( + parent=self.parent(), + title=translate( + "Dialog - Set auto reload timer", "Set auto reload timer", "Header" + ), + special_text=translate("Auto Reload Timer", "Disabled"), + initial_value=self.video_params.auto_reload_timer_min, + _min=0, + _max=1000, + ) + + self.set_auto_reload_timer(time_min) + + @only_initialized + @only_live + def set_auto_reload_timer(self, time_min): + self.video_params.auto_reload_timer_min = time_min + + time_ms = time_min * 60 * 1000 + + if time_ms == 0: + self._reload_timer.stop() + self._reload_timer.setInterval(0) + return + + self._reload_timer.setInterval(time_ms) + self._reload_timer.start() + + @only_initialized + @only_live + def get_auto_reload_timer(self): + if self.video_params.auto_reload_timer_min == 0: + return translate("Auto Reload Timer", "Disabled") + + return "{0} {1}".format( + self.video_params.auto_reload_timer_min, + translate("Auto Reload Timer", "minute(s)"), + ) + def is_under_cursor(self): return self.rect().contains(self.mapFromGlobal(QCursor.pos())) @@ -568,6 +623,7 @@ def apply_snapshot(self, snapshot: Video): self.set_rate(snapshot.rate, is_silent=True) self.switch_stream_quality(snapshot.stream_quality) + self.set_auto_reload_timer(snapshot.auto_reload_timer_min) self.video_params = snapshot.copy() @@ -653,6 +709,8 @@ def load_video_finish(self): self.set_loop_end_time(self.video_params.loop_end) self.set_rate(self.video_params.rate, is_silent=True) + self.set_auto_reload_timer(self.video_params.auto_reload_timer_min) + self.video_status.hide() self.show_overlay() diff --git a/resources/ui/settings_dialog.ui b/resources/ui/settings_dialog.ui index 4702075..8273c9c 100644 --- a/resources/ui/settings_dialog.ui +++ b/resources/ui/settings_dialog.ui @@ -130,7 +130,7 @@ - 5 + 4 @@ -570,33 +570,23 @@ QFormLayout::FieldsStayAtSizeHint - - - Stream quality - - - - - - - Aspect mode - + - + Repeat mode - + @@ -622,6 +612,57 @@ + + + + + 75 + true + + + + Streaming Videos + + + + + + + QFormLayout::FieldsStayAtSizeHint + + + + + Stream quality + + + + + + + + + + Auto reload time + + + + + + + + + + + + (min) + + + + + + +