Skip to content

Commit

Permalink
feat: add option to transform video (rotate, mirror, transpose)
Browse files Browse the repository at this point in the history
* support rotate and flip from VLC options

* resources compiled in Ubuntu

* correct tranform reset icon

* fix: avoid crash when switching to hardware SP mode

* refactor: save transformation in playlist

* refactor: add transform action for [ALL]

* refactor: add transform into settings dialog

* chore: replace rotate icon

* chore: rebuild resources

---------

Co-authored-by: LiberiFatali <not_show@email.com>
Co-authored-by: hieunm <hieunm@me.ai>
Co-authored-by: vzhd1701 <vzhd1701@gmail.com>
  • Loading branch information
4 people authored Jun 5, 2023
1 parent a30bbfa commit 236b989
Show file tree
Hide file tree
Showing 21 changed files with 91,739 additions and 91,231 deletions.
17 changes: 17 additions & 0 deletions gridplayer/dialogs/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
VideoAspect,
VideoDriver,
VideoRepeat,
VideoTransform,
)
from gridplayer.settings import Settings
from gridplayer.utils import log_config
Expand Down Expand Up @@ -89,6 +90,7 @@ def __init__(self, parent):
"playlist/disable_click_pause": self.playlistDisableClickPause,
"playlist/disable_wheel_seek": self.playlistDisableWheelSeek,
"video_defaults/aspect": self.videoAspect,
"video_defaults/transform": self.videoTransform,
"video_defaults/repeat": self.repeatMode,
"video_defaults/audio_mode": self.videoAudioMode,
"video_defaults/random_loop": self.videoRandomLoop,
Expand Down Expand Up @@ -153,6 +155,7 @@ def ui_fill(self): # noqa: WPS213
self.fill_playerVideoDriver()
self.fill_gridMode()
self.fill_videoAspect()
self.fill_videoTransform()
self.fill_repeatMode()
self.fill_logLevel()
self.fill_logLevelVLC()
Expand Down Expand Up @@ -285,6 +288,20 @@ def fill_videoAspect(self):

_fill_combo_box(self.videoAspect, aspect_ratios)

def fill_videoTransform(self):
transform_options = {
VideoTransform.ROTATE_90: self.tr("Rotate 90"),
VideoTransform.ROTATE_180: self.tr("Rotate 180"),
VideoTransform.ROTATE_270: self.tr("Rotate 270"),
VideoTransform.HFLIP: self.tr("Flip Horizontally"),
VideoTransform.VFLIP: self.tr("Flip Vertically"),
VideoTransform.TRANSPOSE: self.tr("Transpose"),
VideoTransform.ANTITRANSPOSE: self.tr("Anti-transpose"),
VideoTransform.NONE: self.tr("No Transform"),
}

_fill_combo_box(self.videoTransform, transform_options)

def fill_repeatMode(self):
repeat_modes = {
VideoRepeat.SINGLE_FILE: self.tr("Single File"),
Expand Down
19 changes: 15 additions & 4 deletions gridplayer/dialogs/settings_dialog_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,18 +353,28 @@ def setupUi(self, SettingsDialog):
self.repeatModeLabel = QtWidgets.QLabel(self.page_defaults_video)
self.repeatModeLabel.setObjectName("repeatModeLabel")
self.formLayout_4.setWidget(
1, QtWidgets.QFormLayout.LabelRole, self.repeatModeLabel
2, QtWidgets.QFormLayout.LabelRole, self.repeatModeLabel
)
self.repeatMode = QtWidgets.QComboBox(self.page_defaults_video)
self.repeatMode.setObjectName("repeatMode")
self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.repeatMode)
self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.repeatMode)
self.label_15 = QtWidgets.QLabel(self.page_defaults_video)
self.label_15.setObjectName("label_15")
self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_15)
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_15)
self.videoAudioMode = QtWidgets.QComboBox(self.page_defaults_video)
self.videoAudioMode.setObjectName("videoAudioMode")
self.formLayout_4.setWidget(
2, QtWidgets.QFormLayout.FieldRole, self.videoAudioMode
3, QtWidgets.QFormLayout.FieldRole, self.videoAudioMode
)
self.videoTransformLabel = QtWidgets.QLabel(self.page_defaults_video)
self.videoTransformLabel.setObjectName("videoTransformLabel")
self.formLayout_4.setWidget(
1, QtWidgets.QFormLayout.LabelRole, self.videoTransformLabel
)
self.videoTransform = QtWidgets.QComboBox(self.page_defaults_video)
self.videoTransform.setObjectName("videoTransform")
self.formLayout_4.setWidget(
1, QtWidgets.QFormLayout.FieldRole, self.videoTransform
)
self.lay_page_defaults_video.addLayout(self.formLayout_4)
self.videoRandomLoop = QtWidgets.QCheckBox(self.page_defaults_video)
Expand Down Expand Up @@ -685,6 +695,7 @@ def retranslateUi(self, SettingsDialog):
self.videoAspectLabel.setText(_translate("SettingsDialog", "Aspect mode"))
self.repeatModeLabel.setText(_translate("SettingsDialog", "Repeat mode"))
self.label_15.setText(_translate("SettingsDialog", "Audio mode"))
self.videoTransformLabel.setText(_translate("SettingsDialog", "Transform"))
self.videoRandomLoop.setText(
_translate("SettingsDialog", "Start at random position")
)
Expand Down
8 changes: 7 additions & 1 deletion gridplayer/models/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from pydantic.color import Color

from gridplayer.models.video_uri import AbsoluteFilePath, VideoURI, VideoURL
from gridplayer.params.static import AudioChannelMode, VideoAspect, VideoRepeat
from gridplayer.params.static import (
AudioChannelMode,
VideoAspect,
VideoRepeat,
VideoTransform,
)
from gridplayer.settings import default_field

MIN_SCALE = 1.0
Expand Down Expand Up @@ -37,6 +42,7 @@ class Video(BaseModel):
is_paused: bool = default_field("video_defaults/paused")
scale: confloat(ge=MIN_SCALE, le=MAX_SCALE) = 1.0
volume: float = 1.0
transform: VideoTransform = default_field("video_defaults/transform")

# Streamable
stream_quality: str = default_field("video_defaults/stream_quality")
Expand Down
15 changes: 9 additions & 6 deletions gridplayer/multiprocess/process_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,14 @@ def active_instances(self):
active_instances.append(instance) # noqa: WPS220
return active_instances

def get_instance(self):
instance = self._get_available_instance()
def get_instance(self, options):
if options:
instance = None
else:
instance = self._get_available_instance()

if instance is None:
instance = self.create_instance()
instance = self.create_instance(options=options)

with self.instances_lock:
self.instances[instance.id] = instance
Expand All @@ -85,7 +88,7 @@ def get_instance(self):

return instance

def create_instance(self):
def create_instance(self, options):
instance = self._instance_class(
players_per_instance=self._limit, pm_callback_pipe=self._self_pipe
)
Expand All @@ -96,8 +99,8 @@ def create_instance(self):

return instance

def init_player(self, init_data, pipe):
instance = self.get_instance()
def init_player(self, init_data, pipe, options):
instance = self.get_instance(options)
player_id = instance.request_new_player(init_data, pipe)

return PlayerInstance(instance, player_id)
Expand Down
137 changes: 137 additions & 0 deletions gridplayer/params/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
SeekSyncMode,
VideoAspect,
VideoRepeat,
VideoTransform,
)
from gridplayer.utils.command_helpers import AND
from gridplayer.utils.qt import translate
Expand Down Expand Up @@ -361,6 +362,94 @@
"check_if": ("is_active_param_set_to", "aspect_mode", VideoAspect.NONE),
"show_if": "is_active_has_video",
},
"Rotate 90": {
"title": translate("Actions", "Rotate 90"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.ROTATE_90),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.ROTATE_90,
),
"show_if": "is_active_has_video",
},
"Rotate 180": {
"title": translate("Actions", "Rotate 180"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.ROTATE_180),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.ROTATE_180,
),
"show_if": "is_active_has_video",
},
"Rotate 270": {
"title": translate("Actions", "Rotate 270"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.ROTATE_270),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.ROTATE_270,
),
"show_if": "is_active_has_video",
},
"Flip Horizontally": {
"title": translate("Actions", "Flip Horizontally"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.HFLIP),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.HFLIP,
),
"show_if": "is_active_has_video",
},
"Flip Vertically": {
"title": translate("Actions", "Flip Vertically"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.VFLIP),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.VFLIP,
),
"show_if": "is_active_has_video",
},
"Transpose": {
"title": translate("Actions", "Transpose"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.TRANSPOSE),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.TRANSPOSE,
),
"show_if": "is_active_has_video",
},
"Anti-transpose": {
"title": translate("Actions", "Anti-transpose"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.ANTITRANSPOSE),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.ANTITRANSPOSE,
),
"show_if": "is_active_has_video",
},
"No Transform": {
"title": translate("Actions", "No Transform"),
"icon": "empty",
"func": ("active", "set_transform", VideoTransform.NONE),
"check_if": (
"is_active_param_set_to",
"transform",
VideoTransform.NONE,
),
"show_if": "is_active_has_video",
},
"Seek Others (Percent)": {
"title": translate("Actions", "Sync By Percent"),
"icon": "seek-sync-percent",
Expand Down Expand Up @@ -848,6 +937,54 @@
"func": ("all", "set_aspect", VideoAspect.NONE),
"show_if": "is_any_videos_have_video",
},
"Rotate 90 [ALL]": {
"title": translate("Actions", "Rotate 90"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.ROTATE_90),
"show_if": "is_any_videos_have_video",
},
"Rotate 180 [ALL]": {
"title": translate("Actions", "Rotate 180"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.ROTATE_180),
"show_if": "is_any_videos_have_video",
},
"Rotate 270 [ALL]": {
"title": translate("Actions", "Rotate 270"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.ROTATE_270),
"show_if": "is_any_videos_have_video",
},
"Flip Horizontally [ALL]": {
"title": translate("Actions", "Flip Horizontally"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.HFLIP),
"show_if": "is_any_videos_have_video",
},
"Flip Vertically [ALL]": {
"title": translate("Actions", "Flip Vertically"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.VFLIP),
"show_if": "is_any_videos_have_video",
},
"Transpose [ALL]": {
"title": translate("Actions", "Transpose"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.TRANSPOSE),
"show_if": "is_any_videos_have_video",
},
"Anti-transpose [ALL]": {
"title": translate("Actions", "Anti-transpose"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.ANTITRANSPOSE),
"show_if": "is_any_videos_have_video",
},
"No Transform [ALL]": {
"title": translate("Actions", "No Transform"),
"icon": "empty",
"func": ("all", "set_transform", VideoTransform.NONE),
"show_if": "is_any_videos_have_video",
},
"Audio Mode - Original [ALL]": {
"title": translate("Audio Mode", "Original"),
"icon": "empty",
Expand Down
23 changes: 23 additions & 0 deletions gridplayer/params/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"Speed": {"title": translate("Actions", "Speed"), "icon": "speed"},
"Zoom": {"title": translate("Actions", "Zoom"), "icon": "zoom"},
"Aspect": {"title": translate("Actions", "Aspect"), "icon": "aspect"},
"Transform": {"title": translate("Actions", "Transform"), "icon": "rotate"},
"[ALL]": {"title": translate("Actions", "[ALL]"), "icon": "all"},
"Grid": {"title": translate("Actions", "Grid"), "icon": "grid"},
"Seek Sync": {"title": translate("Actions", "Seek Sync"), "icon": "seek-sync"},
Expand Down Expand Up @@ -82,6 +83,17 @@
"Aspect Stretch",
"Aspect None",
),
(
"Transform",
"Rotate 90",
"Rotate 180",
"Rotate 270",
"Flip Horizontally",
"Flip Vertically",
"Transpose",
"Anti-transpose",
"No Transform",
),
),
(
"Playback",
Expand Down Expand Up @@ -193,6 +205,17 @@
"Aspect Stretch [ALL]",
"Aspect None [ALL]",
),
(
"Transform",
"Rotate 90 [ALL]",
"Rotate 180 [ALL]",
"Rotate 270 [ALL]",
"Flip Horizontally [ALL]",
"Flip Vertically [ALL]",
"Transpose [ALL]",
"Anti-transpose [ALL]",
"No Transform [ALL]",
),
),
(
"Playback",
Expand Down
11 changes: 11 additions & 0 deletions gridplayer/params/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ class VideoAspect(AutoName):
NONE = auto()


class VideoTransform(AutoName):
ROTATE_90 = auto()
ROTATE_180 = auto()
ROTATE_270 = auto()
HFLIP = auto()
VFLIP = auto()
TRANSPOSE = auto()
ANTITRANSPOSE = auto()
NONE = auto()


class VideoRepeat(AutoName):
SINGLE_FILE = auto()
DIR = auto()
Expand Down
3 changes: 3 additions & 0 deletions gridplayer/player/managers/video_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
SeekSyncMode,
VideoAspect,
VideoRepeat,
VideoTransform,
)
from gridplayer.player.managers.base import ManagerBase
from gridplayer.settings import Settings
Expand Down Expand Up @@ -127,6 +128,7 @@ class VideoBlocksManager(ManagerBase):
all_scale_reset = pyqtSignal()

all_set_aspect = pyqtSignal(VideoAspect)
all_set_transform = pyqtSignal(VideoTransform)
all_set_auto_reload_timer = pyqtSignal(int)
all_set_audio_channel_mode = pyqtSignal(AudioChannelMode)

Expand Down Expand Up @@ -368,6 +370,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_transform, vb.set_transform),
(self.all_set_auto_reload_timer, vb.set_auto_reload_timer),
(self.all_set_audio_channel_mode, vb.set_audio_channel_mode),
(self.all_volume_increase, vb.volume_increase),
Expand Down
Loading

0 comments on commit 236b989

Please sign in to comment.