subtitld 0.0.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. subtitld/__init__.py +19 -0
  2. subtitld/__main__.py +240 -0
  3. subtitld/ftfy/char_classes.dat +0 -0
  4. subtitld/graphics/Montserrat-Black.ttf +0 -0
  5. subtitld/graphics/Montserrat-BlackItalic.ttf +0 -0
  6. subtitld/graphics/Montserrat-Bold.ttf +0 -0
  7. subtitld/graphics/Montserrat-BoldItalic.ttf +0 -0
  8. subtitld/graphics/Montserrat-ExtraBold.ttf +0 -0
  9. subtitld/graphics/Montserrat-ExtraBoldItalic.ttf +0 -0
  10. subtitld/graphics/Montserrat-ExtraLight.ttf +0 -0
  11. subtitld/graphics/Montserrat-ExtraLightItalic.ttf +0 -0
  12. subtitld/graphics/Montserrat-Italic.ttf +0 -0
  13. subtitld/graphics/Montserrat-Light.ttf +0 -0
  14. subtitld/graphics/Montserrat-LightItalic.ttf +0 -0
  15. subtitld/graphics/Montserrat-Medium.ttf +0 -0
  16. subtitld/graphics/Montserrat-MediumItalic.ttf +0 -0
  17. subtitld/graphics/Montserrat-Regular.ttf +0 -0
  18. subtitld/graphics/Montserrat-SemiBold.ttf +0 -0
  19. subtitld/graphics/Montserrat-SemiBoldItalic.ttf +0 -0
  20. subtitld/graphics/Montserrat-Thin.ttf +0 -0
  21. subtitld/graphics/Montserrat-ThinItalic.ttf +0 -0
  22. subtitld/graphics/Ubuntu-B.ttf +0 -0
  23. subtitld/graphics/Ubuntu-BI.ttf +0 -0
  24. subtitld/graphics/Ubuntu-C.ttf +0 -0
  25. subtitld/graphics/Ubuntu-L.ttf +0 -0
  26. subtitld/graphics/Ubuntu-LI.ttf +0 -0
  27. subtitld/graphics/Ubuntu-M.ttf +0 -0
  28. subtitld/graphics/Ubuntu-MI.ttf +0 -0
  29. subtitld/graphics/Ubuntu-R.ttf +0 -0
  30. subtitld/graphics/Ubuntu-RI.ttf +0 -0
  31. subtitld/graphics/Ubuntu-Th.ttf +0 -0
  32. subtitld/graphics/UbuntuMono-B.ttf +0 -0
  33. subtitld/graphics/UbuntuMono-BI.ttf +0 -0
  34. subtitld/graphics/UbuntuMono-R.ttf +0 -0
  35. subtitld/graphics/UbuntuMono-RI.ttf +0 -0
  36. subtitld/graphics/add_and_play.svg +4 -0
  37. subtitld/graphics/add_from_last.svg +4 -0
  38. subtitld/graphics/add_from_last_checked.svg +4 -0
  39. subtitld/graphics/add_subtitle_icon.svg +4 -0
  40. subtitld/graphics/add_subtitle_to_next_start.svg +4 -0
  41. subtitld/graphics/add_subtitle_to_next_start_checked.svg +4 -0
  42. subtitld/graphics/background_watermark.svg +9 -0
  43. subtitld/graphics/character_count_icon.svg +1 -0
  44. subtitld/graphics/checkbox_checked.svg +8 -0
  45. subtitld/graphics/checkbox_checked_hover.svg +8 -0
  46. subtitld/graphics/checkbox_disabled.svg +4 -0
  47. subtitld/graphics/checkbox_unchecked.svg +5 -0
  48. subtitld/graphics/checkbox_unchecked_hover.svg +5 -0
  49. subtitld/graphics/combobox_down_arrow.svg +4 -0
  50. subtitld/graphics/cps_icon.svg +1 -0
  51. subtitld/graphics/dialog_close_icon.svg +4 -0
  52. subtitld/graphics/dialog_close_icon_hover.svg +4 -0
  53. subtitld/graphics/dubbing_container_collapse_icon.svg +8 -0
  54. subtitld/graphics/dubbing_container_expand_icon.svg +4 -0
  55. subtitld/graphics/file.svg +4 -0
  56. subtitld/graphics/gap_add_icon.svg +4 -0
  57. subtitld/graphics/gap_remove_icon.svg +4 -0
  58. subtitld/graphics/grid_frames_icon.svg +4 -0
  59. subtitld/graphics/grid_frames_icon_checked.svg +4 -0
  60. subtitld/graphics/grid_frames_icon_disabled.svg +4 -0
  61. subtitld/graphics/grid_scenes_icon.svg +4 -0
  62. subtitld/graphics/grid_scenes_icon_checked.svg +4 -0
  63. subtitld/graphics/grid_scenes_icon_disabled.svg +4 -0
  64. subtitld/graphics/grid_seconds_icon.svg +4 -0
  65. subtitld/graphics/grid_seconds_icon_checked.svg +4 -0
  66. subtitld/graphics/grid_seconds_icon_disabled.svg +4 -0
  67. subtitld/graphics/last_end_to_current_position_icon.svg +4 -0
  68. subtitld/graphics/last_start_last_end_to_current_position_button.svg +9 -0
  69. subtitld/graphics/last_start_to_current_position_icon.svg +4 -0
  70. subtitld/graphics/left_panel_autosave.svg +4 -0
  71. subtitld/graphics/left_panel_dubbing.svg +4 -0
  72. subtitld/graphics/left_panel_export.svg +4 -0
  73. subtitld/graphics/left_panel_global.svg +4 -0
  74. subtitld/graphics/left_panel_import.svg +4 -0
  75. subtitld/graphics/left_panel_interface.svg +4 -0
  76. subtitld/graphics/left_panel_keyboard.svg +4 -0
  77. subtitld/graphics/left_panel_metadata.svg +4 -0
  78. subtitld/graphics/left_panel_navigation_background.svg +11 -0
  79. subtitld/graphics/left_panel_qualitycheck.svg +4 -0
  80. subtitld/graphics/left_panel_speakers.svg +4 -0
  81. subtitld/graphics/left_panel_subtitles.svg +1 -0
  82. subtitld/graphics/left_panel_transcription.svg +4 -0
  83. subtitld/graphics/left_panel_translation.svg +4 -0
  84. subtitld/graphics/merge_back_selected_subtitle_icon.svg +4 -0
  85. subtitld/graphics/merge_next_selected_subtitle_icon.svg +4 -0
  86. subtitld/graphics/move_backward_subtitle.svg +4 -0
  87. subtitld/graphics/move_end_back_subtitle.svg +4 -0
  88. subtitld/graphics/move_end_forward_subtitle.svg +4 -0
  89. subtitld/graphics/move_forward_subtitle.svg +4 -0
  90. subtitld/graphics/move_start_back_subtitle.svg +4 -0
  91. subtitld/graphics/move_start_forward_subtitle.svg +4 -0
  92. subtitld/graphics/music_voice_separation_music_icon.svg +4 -0
  93. subtitld/graphics/music_voice_separation_music_icon_disabled.svg +4 -0
  94. subtitld/graphics/music_voice_separation_voice_icon.svg +4 -0
  95. subtitld/graphics/music_voice_separation_voice_icon_disabled.svg +4 -0
  96. subtitld/graphics/new_line_icon.svg +1 -0
  97. subtitld/graphics/next_end_to_current_position_icon.svg +4 -0
  98. subtitld/graphics/next_start_next_end_to_current_position_button.svg +4 -0
  99. subtitld/graphics/next_start_to_current_position_icon.svg +4 -0
  100. subtitld/graphics/pause_icon.svg +12 -0
  101. subtitld/graphics/play_from_last_start_icon.svg +5 -0
  102. subtitld/graphics/play_from_next_start_icon.svg +7 -0
  103. subtitld/graphics/play_icon.svg +12 -0
  104. subtitld/graphics/play_icon_hover.svg +12 -0
  105. subtitld/graphics/play_icon_pressed.svg +12 -0
  106. subtitld/graphics/playback_repeat.svg +4 -0
  107. subtitld/graphics/playback_speed.svg +6 -0
  108. subtitld/graphics/playbackspeed_border.svg +5 -0
  109. subtitld/graphics/playbackspeed_border_checked.svg +5 -0
  110. subtitld/graphics/playbackspeed_border_hover.svg +5 -0
  111. subtitld/graphics/playercontrols_button_first.svg +5 -0
  112. subtitld/graphics/playercontrols_button_first_hover.svg +6 -0
  113. subtitld/graphics/playercontrols_button_first_pressed.svg +6 -0
  114. subtitld/graphics/playercontrols_button_last.svg +6 -0
  115. subtitld/graphics/playercontrols_button_last_hover.svg +6 -0
  116. subtitld/graphics/playercontrols_button_last_pressed.svg +6 -0
  117. subtitld/graphics/playercontrols_widget_center_bottom_line.svg +13 -0
  118. subtitld/graphics/playercontrols_widget_center_top_line.svg +17 -0
  119. subtitld/graphics/plus_icon.svg +4 -0
  120. subtitld/graphics/record_icon.svg +8 -0
  121. subtitld/graphics/record_icon_pressed.svg +5 -0
  122. subtitld/graphics/remove_selected_subtitle_icon.svg +6 -0
  123. subtitld/graphics/repeat_playback_border.svg +5 -0
  124. subtitld/graphics/repeat_playback_border_checked.svg +5 -0
  125. subtitld/graphics/repeat_playback_border_hover.svg +5 -0
  126. subtitld/graphics/save.svg +4 -0
  127. subtitld/graphics/send_text_to_last_subtitle_and_slice_button.svg +8 -0
  128. subtitld/graphics/send_text_to_last_subtitle_icon.svg +8 -0
  129. subtitld/graphics/send_text_to_next_subtitle_and_slice_button.svg +8 -0
  130. subtitld/graphics/send_text_to_next_subtitle_icon.svg +8 -0
  131. subtitld/graphics/slice_selected_subtitle_icon.svg +4 -0
  132. subtitld/graphics/snap_grid_icon.svg +4 -0
  133. subtitld/graphics/snap_grid_icon_checked.svg +4 -0
  134. subtitld/graphics/snap_grid_icon_disabled.svg +4 -0
  135. subtitld/graphics/snap_limits_icon.svg +4 -0
  136. subtitld/graphics/snap_limits_icon_checked.svg +4 -0
  137. subtitld/graphics/snap_limits_icon_disabled.svg +4 -0
  138. subtitld/graphics/snap_move_next.svg +4 -0
  139. subtitld/graphics/snap_move_next_checked.svg +4 -0
  140. subtitld/graphics/snap_move_next_disabled.svg +4 -0
  141. subtitld/graphics/snap_moving_icon.svg +4 -0
  142. subtitld/graphics/snap_moving_icon_checked.svg +4 -0
  143. subtitld/graphics/snap_moving_icon_disabled.svg +4 -0
  144. subtitld/graphics/speaker_list_item_change_color_icon.svg +4 -0
  145. subtitld/graphics/speaker_list_item_change_color_icon_hover.svg +4 -0
  146. subtitld/graphics/speaker_list_item_change_image_icon.svg +4 -0
  147. subtitld/graphics/speaker_list_item_change_image_icon_hover.svg +4 -0
  148. subtitld/graphics/speaker_list_item_export_icon.svg +4 -0
  149. subtitld/graphics/speaker_list_item_export_icon_hover.svg +4 -0
  150. subtitld/graphics/speaker_list_item_remove_icon.svg +4 -0
  151. subtitld/graphics/speaker_list_item_remove_icon_hover.svg +4 -0
  152. subtitld/graphics/speaker_list_item_rename_icon.svg +4 -0
  153. subtitld/graphics/speaker_list_item_rename_icon_hover.svg +4 -0
  154. subtitld/graphics/spinbox_arrow_down.svg +4 -0
  155. subtitld/graphics/spinbox_arrow_up.svg +4 -0
  156. subtitld/graphics/start_screen_top_shadow.svg +23 -0
  157. subtitld/graphics/stop_icon.svg +6 -0
  158. subtitld/graphics/stop_icon_hover.svg +6 -0
  159. subtitld/graphics/stop_icon_pressed.svg +6 -0
  160. subtitld/graphics/stylesheet.qss +1838 -0
  161. subtitld/graphics/subtitld.ico +0 -0
  162. subtitld/graphics/subtitld.png +0 -0
  163. subtitld/graphics/subtitles_panel_simplelist_properties_duration_timing_lock_closed_icon.svg +4 -0
  164. subtitld/graphics/subtitles_panel_simplelist_properties_duration_timing_lock_open_icon.svg +4 -0
  165. subtitld/graphics/subtitles_panel_simplelist_properties_duration_timing_lock_open_icon_hover.svg +4 -0
  166. subtitld/graphics/timeline_bottom_controls_background.svg +25 -0
  167. subtitld/graphics/timeline_cursor_back_frame.svg +4 -0
  168. subtitld/graphics/timeline_cursor_back_frame_hover.svg +4 -0
  169. subtitld/graphics/timeline_cursor_next_frame.svg +4 -0
  170. subtitld/graphics/timeline_cursor_next_frame_hover.svg +4 -0
  171. subtitld/graphics/timeline_show_speaker_color.svg +5 -0
  172. subtitld/graphics/timeline_show_speaker_color_checked.svg +5 -0
  173. subtitld/graphics/timeline_show_speaker_in_tracks.svg +4 -0
  174. subtitld/graphics/timeline_show_speaker_in_tracks_checked.svg +4 -0
  175. subtitld/graphics/timelinescrolling_follow_icon.svg +4 -0
  176. subtitld/graphics/timelinescrolling_follow_icon_checked.svg +4 -0
  177. subtitld/graphics/timelinescrolling_none_icon.svg +6 -0
  178. subtitld/graphics/timelinescrolling_none_icon_checked.svg +6 -0
  179. subtitld/graphics/timelinescrolling_page_icon.svg +4 -0
  180. subtitld/graphics/timelinescrolling_page_icon_checked.svg +4 -0
  181. subtitld/graphics/titleBar_left_container_border.svg +113 -0
  182. subtitld/graphics/unknown_speaker.svg +5 -0
  183. subtitld/graphics/vosk_transcription_download_model_icon.svg +4 -0
  184. subtitld/graphics/vosk_transcription_update_model_icon.svg +4 -0
  185. subtitld/graphics/word_count_icon.svg +1 -0
  186. subtitld/graphics/wpm_icon.svg +1 -0
  187. subtitld/graphics/zoom_in_icon.svg +4 -0
  188. subtitld/graphics/zoom_in_icon_disabled.svg +4 -0
  189. subtitld/graphics/zoom_out_icon.svg +4 -0
  190. subtitld/graphics/zoom_out_icon_disabled.svg +4 -0
  191. subtitld/interface/__init__.py +0 -0
  192. subtitld/interface/actionmanager.py +18 -0
  193. subtitld/interface/bottom_panel.py +43 -0
  194. subtitld/interface/left_panel.py +144 -0
  195. subtitld/interface/left_panel_autosave.py +149 -0
  196. subtitld/interface/left_panel_dubbing.py +469 -0
  197. subtitld/interface/left_panel_export.py +755 -0
  198. subtitld/interface/left_panel_global.py +358 -0
  199. subtitld/interface/left_panel_import.py +82 -0
  200. subtitld/interface/left_panel_interface.py +616 -0
  201. subtitld/interface/left_panel_keyboard.py +261 -0
  202. subtitld/interface/left_panel_metadata.py +74 -0
  203. subtitld/interface/left_panel_qualitycheck.py +262 -0
  204. subtitld/interface/left_panel_speakers.py +737 -0
  205. subtitld/interface/left_panel_subtitleslist.py +791 -0
  206. subtitld/interface/left_panel_transcription.py +763 -0
  207. subtitld/interface/left_panel_translation.py +294 -0
  208. subtitld/interface/playercontrols.py +2348 -0
  209. subtitld/interface/preview_panel.py +407 -0
  210. subtitld/interface/productionscreen.py +65 -0
  211. subtitld/interface/startscreen.py +273 -0
  212. subtitld/interface/timeline.py +1308 -0
  213. subtitld/interface/top_bar.py +329 -0
  214. subtitld/interface/translation.py +68 -0
  215. subtitld/interface/utils.py +286 -0
  216. subtitld/locale/en_US.json +301 -0
  217. subtitld/locale/pt_BR.json +269 -0
  218. subtitld/modules/__init__.py +0 -0
  219. subtitld/modules/audioengine.py +606 -0
  220. subtitld/modules/bounce.py +229 -0
  221. subtitld/modules/config.py +84 -0
  222. subtitld/modules/file_io.py +870 -0
  223. subtitld/modules/history.py +31 -0
  224. subtitld/modules/quality_check.py +53 -0
  225. subtitld/modules/session.py +270 -0
  226. subtitld/modules/shortcuts.py +84 -0
  227. subtitld/modules/subtitles.py +413 -0
  228. subtitld/modules/timecode.py +496 -0
  229. subtitld/modules/usf.py +366 -0
  230. subtitld/modules/utils.py +79 -0
  231. subtitld/modules/waveform.py +53 -0
  232. subtitld-0.0.0.dev0.dist-info/METADATA +97 -0
  233. subtitld-0.0.0.dev0.dist-info/RECORD +237 -0
  234. subtitld-0.0.0.dev0.dist-info/WHEEL +5 -0
  235. subtitld-0.0.0.dev0.dist-info/entry_points.txt +2 -0
  236. subtitld-0.0.0.dev0.dist-info/licenses/LICENSE +674 -0
  237. subtitld-0.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,407 @@
1
+ import os
2
+
3
+ from PySide6.QtWidgets import QVBoxLayout, QWidget, QVBoxLayout, QGraphicsScene, QGraphicsView, QFrame, QGraphicsOpacityEffect, QSizePolicy, QRubberBand
4
+ from PySide6.QtCore import QPropertyAnimation, QEasingCurve, Qt, QRect, QRectF, QPoint, QSize, QMargins, QMarginsF, Signal, QTimer, QEvent
5
+ from PySide6.QtGui import QPainter, QPen, QColor, QFont, QBrush
6
+ from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer, QMediaMetaData
7
+ from PySide6.QtMultimediaWidgets import QGraphicsVideoItem
8
+
9
+ from subtitld.interface import utils
10
+ from subtitld.interface.translation import _
11
+
12
+ from subtitld.modules import session
13
+ from subtitld.modules import subtitles
14
+ from subtitld.modules import audioengine #AudaspaceAudioDevice
15
+
16
+
17
+ class PlayerWidget(QWidget):
18
+ position_changed_signal = Signal()
19
+ def __init__(widget, parent=None):
20
+ super().__init__(parent)
21
+ widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
22
+ widget.setLayout(QVBoxLayout())
23
+ widget.layout().setContentsMargins(0, 0, 0, 0)
24
+
25
+ widget._media_player = QMediaPlayer()
26
+
27
+ widget._audio_output = QAudioOutput()
28
+ widget._media_player.setAudioOutput(widget._audio_output)
29
+ # widget._media_player.setAudioOutput(QAudioOutput())
30
+
31
+ widget._audio_device = audioengine.SoundDeviceAudioEngine()
32
+
33
+ widget._graphics_scene = QGraphicsScene()
34
+ widget._graphics_view = QGraphicsView(widget._graphics_scene)
35
+ widget._graphics_view.setFrameShadow(QFrame.Plain)
36
+ widget._graphics_view.setFrameShape(QFrame.NoFrame)
37
+ widget._graphics_view.setRenderHints(QPainter.Antialiasing)
38
+ widget._graphics_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
39
+ widget._graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
40
+
41
+ class graphicsvideoitem(QGraphicsVideoItem):
42
+ def __init__(self):
43
+ super().__init__()
44
+
45
+ def paint(self, painter, option, widget):
46
+ super().paint(painter, option, widget)
47
+
48
+ painter.setRenderHint(QPainter.Antialiasing)
49
+
50
+ # Actual video content rect inside the item (letterboxed).
51
+ item_rect = self.boundingRect()
52
+ native = self.nativeSize()
53
+ if native is not None and native.width() > 0 and native.height() > 0:
54
+ scale = min(item_rect.width() / native.width(), item_rect.height() / native.height())
55
+ vw = native.width() * scale
56
+ vh = native.height() * scale
57
+ vx = item_rect.x() + (item_rect.width() - vw) / 2.0
58
+ vy = item_rect.y() + (item_rect.height() - vh) / 2.0
59
+ video_rect = QRectF(vx, vy, vw, vh)
60
+ else:
61
+ video_rect = QRectF(item_rect)
62
+
63
+ painter.setFont(QFont(session.CONFIG.get('videoplayer', {}).get('font_family', 'Montserrat'), session.CONFIG.get('videoplayer', {}).get('font_size', 40)))
64
+
65
+ title_safe_margin_qrect = video_rect - QMarginsF(
66
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_title_x', 10) / 100) * video_rect.width(),
67
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_title_y', 10) / 100) * video_rect.height(),
68
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_title_x', 10) / 100) * video_rect.width(),
69
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_title_y', 10) / 100) * video_rect.height()
70
+ )
71
+
72
+ if session.SUBTITLE.get('current', False):
73
+
74
+ if session.CONFIG.get('videoplayer', {}).get('backgroundbox_enabled', True):
75
+ text_rect = painter.boundingRect(title_safe_margin_qrect, Qt.AlignBottom | Qt.AlignHCenter | Qt.TextWordWrap, session.SUBTITLE['current']['text'])
76
+
77
+ painter.setPen(Qt.NoPen)
78
+ painter.setBrush(QBrush(QColor(session.CONFIG.get('videoplayer', {}).get('backgroundbox_color', '#55000000'))))
79
+
80
+ if session.CONFIG.get('videoplayer', {}).get('backgroundbox_border_radius', 5):
81
+ painter.drawRoundedRect(text_rect.marginsAdded(QMargins(session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10), session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10), session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10), session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10))), session.CONFIG.get('videoplayer', {}).get('backgroundbox_border_radius', 5), session.CONFIG.get('videoplayer', {}).get('backgroundbox_border_radius', 5))
82
+ else:
83
+ painter.drawRect(text_rect.marginsAdded(QMargins(session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10), session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10), session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10), session.CONFIG.get('videoplayer', {}).get('backgroundbox_padding', 10))))
84
+
85
+ painter.setBrush(Qt.NoBrush)
86
+
87
+ if session.CONFIG.get('videoplayer', {}).get('shadow_enabled', True):
88
+ painter.setPen(QPen(session.CONFIG.get('videoplayer', {}).get('shadow_color', '#ff000000')))
89
+ painter.drawText(title_safe_margin_qrect - QMarginsF(session.CONFIG.get('videoplayer', {}).get('shadow_x', 2), session.CONFIG.get('videoplayer', {}).get('shadow_y', 2), -session.CONFIG.get('videoplayer', {}).get('shadow_x', 2), -session.CONFIG.get('videoplayer', {}).get('shadow_y', 2)), Qt.AlignHCenter | Qt.AlignBottom | Qt.TextWordWrap, session.SUBTITLE['current']['text'])
90
+
91
+ painter.setPen(QPen(session.CONFIG.get('videoplayer', {}).get('color', '#ffffffff')))
92
+ painter.drawText(title_safe_margin_qrect, Qt.AlignHCenter | Qt.AlignBottom | Qt.TextWordWrap, session.SUBTITLE['current']['text'])
93
+
94
+ if session.CONFIG.get('videoplayer', {}).get('safe_margin_action_enabled', False):
95
+ action_safe_margin_qrect = video_rect - QMarginsF(
96
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_action_x', 5) / 100) * video_rect.width(),
97
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_action_y', 5) / 100) * video_rect.height(),
98
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_action_x', 5) / 100) * video_rect.width(),
99
+ (session.CONFIG.get('videoplayer', {}).get('safe_margin_action_y', 5) / 100) * video_rect.height()
100
+ )
101
+
102
+ painter.setPen(QPen(QColor(session.CONFIG.get('videoplayer', {}).get('safe_margin_action_color', '#dd67FF4D')), 1, Qt.SolidLine))
103
+ painter.drawRect(action_safe_margin_qrect)
104
+
105
+ center_x = video_rect.center().x()
106
+ center_y = video_rect.center().y()
107
+
108
+ painter.drawLine(
109
+ center_x,
110
+ action_safe_margin_qrect.y(),
111
+ center_x,
112
+ action_safe_margin_qrect.y() + (video_rect.height() * .025)
113
+ )
114
+ painter.drawLine(
115
+ center_x,
116
+ action_safe_margin_qrect.y() + action_safe_margin_qrect.height(),
117
+ center_x,
118
+ action_safe_margin_qrect.y() + action_safe_margin_qrect.height() - (video_rect.height() * .025)
119
+ )
120
+ painter.drawLine(
121
+ action_safe_margin_qrect.x(),
122
+ center_y,
123
+ action_safe_margin_qrect.x() + (video_rect.width() * .025),
124
+ center_y
125
+ )
126
+ painter.drawLine(
127
+ action_safe_margin_qrect.x() + action_safe_margin_qrect.width(),
128
+ center_y,
129
+ action_safe_margin_qrect.x() + action_safe_margin_qrect.width() - (video_rect.width() * .025),
130
+ center_y
131
+ )
132
+
133
+ if session.CONFIG.get('videoplayer', {}).get('safe_margin_title_enabled', False):
134
+ painter.setPen(QPen(QColor(session.CONFIG.get('videoplayer', {}).get('safe_margin_title_color', '#ddff0000')), 1, Qt.SolidLine))
135
+ painter.drawRect(title_safe_margin_qrect)
136
+
137
+ widget._graphics_video_item = graphicsvideoitem()
138
+ widget._graphics_scene.addItem(widget._graphics_video_item)
139
+ widget._media_player.setVideoOutput(widget._graphics_video_item)
140
+ widget._media_player.positionChanged.connect(lambda position: widget.position_changed(position))
141
+
142
+ widget._last_video_frame = None
143
+ widget._graphics_video_item.videoSink().videoFrameChanged.connect(widget._on_video_frame)
144
+
145
+ widget.layout().addWidget(widget._graphics_view, 1)
146
+
147
+ QTimer.singleShot(0, widget._force_resize_update)
148
+
149
+ def position_changed(widget, position):
150
+ session.SUBTITLE['position'] = position / 1000.0
151
+ widget.update_subtitle_layer()
152
+ widget.position_changed_signal.emit()
153
+
154
+ def update_subtitle_layer(widget):
155
+ session.SUBTITLE['current'] = subtitles.subtitle_under_current_position()
156
+ widget.update()
157
+
158
+ def resizeEvent(widget, event):
159
+ widget._graphics_view.setSceneRect(QRect(0, 0, event.size().width(), event.size().height()))
160
+ widget._graphics_video_item.setSize(event.size())
161
+ widget._graphics_view.fitInView(widget._graphics_view.sceneRect(), Qt.KeepAspectRatio)
162
+ event.accept()
163
+
164
+ def loadfile(widget, filepath):
165
+ if os.path.isfile(filepath):
166
+ widget._media_player.setSource(str(filepath))
167
+ # widget._audio_device.load(filepath)
168
+ widget.play()
169
+ widget.pause()
170
+
171
+ def frameStep(widget):
172
+ fps = widget._media_player.metaData().value(QMediaMetaData.VideoFrameRate)
173
+ widget._media_player.setPosition(widget._media_player.position() + int(1000/fps))
174
+
175
+ def frameBackStep(widget):
176
+ fps = widget._media_player.metaData().value(QMediaMetaData.VideoFrameRate)
177
+ widget._media_player.setPosition(widget._media_player.position() - int(1000/fps))
178
+
179
+ def seek(widget, pos=0.0, method='absolute+exact'):
180
+ """Function to seek at some position"""
181
+ widget._media_player.setPosition(int(pos*1000))
182
+ widget._audio_device.seek(pos)
183
+
184
+ def stop(widget):
185
+ """Function to stop playback (fake stop, it is pause + position 0)"""
186
+ widget._media_player.pause()
187
+ widget._audio_device.stop()
188
+ widget.seek(0)
189
+
190
+ def pause(widget):
191
+ """Function to pause playback (fake pause, it just changes actual playback status)"""
192
+ widget._media_player.pause()
193
+ widget._audio_device.pause()
194
+
195
+ def play(widget):
196
+ """Function to play (fake play, it just changes actual playback status)"""
197
+ widget._media_player.play()
198
+ widget._audio_device.play(session.SUBTITLE.get('position', 0))
199
+
200
+ def mute(widget):
201
+ """Function to mute"""
202
+ None
203
+
204
+ def is_paused(widget):
205
+ return widget._media_player.playbackState() == QMediaPlayer.PausedState
206
+
207
+ def volume(widget, vol: int):
208
+ """Function to change volume"""
209
+ None
210
+
211
+ def set_position(widget, pos):
212
+ widget.seek(pos)
213
+
214
+ def get_position(widget):
215
+ return widget._media_player.position()
216
+
217
+ def update_speed(self):
218
+ """Function to change playback speed"""
219
+ self._media_player.setPlaybackRate(session.CONFIG['playback_speed'])
220
+ self._audio_device.set_speed(session.CONFIG['playback_speed'])
221
+ # self._audio_device.change_speed(session.CONFIG['playback_speed'])
222
+
223
+ def _force_resize_update(widget):
224
+ size = widget.size()
225
+ widget._graphics_view.setSceneRect(QRect(0, 0, size.width(), size.height()))
226
+ widget._graphics_video_item.setSize(size)
227
+ widget._graphics_view.fitInView(widget._graphics_view.sceneRect(), Qt.KeepAspectRatio)
228
+ widget.updateGeometry()
229
+
230
+ def start_face_selection(widget, speaker_name):
231
+ """Enter face-selection mode. User drags a 1:1 square on the video;
232
+ on mouse release, the selected region becomes the speaker image.
233
+ Right-click or Esc cancels."""
234
+ widget._face_selection_speaker = speaker_name
235
+ widget._face_selection_origin = None
236
+ if getattr(widget, '_face_rubber_band', None) is None:
237
+ widget._face_rubber_band = QRubberBand(QRubberBand.Rectangle, widget._graphics_view.viewport())
238
+ viewport = widget._graphics_view.viewport()
239
+ widget._face_selection_prev_focus_policy = viewport.focusPolicy()
240
+ viewport.setFocusPolicy(Qt.StrongFocus)
241
+ viewport.setFocus(Qt.OtherFocusReason)
242
+ viewport.setCursor(Qt.CrossCursor)
243
+ viewport.installEventFilter(widget)
244
+
245
+ def _end_face_selection(widget):
246
+ viewport = widget._graphics_view.viewport()
247
+ viewport.removeEventFilter(widget)
248
+ viewport.unsetCursor()
249
+ prev_policy = getattr(widget, '_face_selection_prev_focus_policy', None)
250
+ if prev_policy is not None:
251
+ viewport.setFocusPolicy(prev_policy)
252
+ widget._face_selection_prev_focus_policy = None
253
+ if getattr(widget, '_face_rubber_band', None) is not None:
254
+ widget._face_rubber_band.hide()
255
+ widget._face_selection_speaker = None
256
+ widget._face_selection_origin = None
257
+
258
+ def _on_video_frame(widget, frame):
259
+ if frame.isValid():
260
+ widget._last_video_frame = frame
261
+
262
+ def _video_rect_in_viewport(widget):
263
+ frame = widget._last_video_frame
264
+ if frame is None or not frame.isValid():
265
+ return None
266
+ size = frame.size()
267
+ fw, fh = size.width(), size.height()
268
+ if fw <= 0 or fh <= 0:
269
+ return None
270
+ vp = widget._graphics_view.viewport()
271
+ vw, vh = vp.width(), vp.height()
272
+ scale = min(vw / fw, vh / fh)
273
+ video_w = fw * scale
274
+ video_h = fh * scale
275
+ video_x = (vw - video_w) / 2.0
276
+ video_y = (vh - video_h) / 2.0
277
+ return QRectF(video_x, video_y, video_w, video_h)
278
+
279
+ @staticmethod
280
+ def _clamp_point(pt, rect):
281
+ return QPoint(
282
+ int(max(rect.left(), min(rect.right(), pt.x()))),
283
+ int(max(rect.top(), min(rect.bottom(), pt.y()))),
284
+ )
285
+
286
+ def _square_rect(widget, origin, current):
287
+ video_rect = widget._video_rect_in_viewport()
288
+ if video_rect is not None:
289
+ origin = widget._clamp_point(origin, video_rect)
290
+ current = widget._clamp_point(current, video_rect)
291
+ dx = current.x() - origin.x()
292
+ dy = current.y() - origin.y()
293
+ sx = -1 if dx < 0 else 1
294
+ sy = -1 if dy < 0 else 1
295
+ size = max(abs(dx), abs(dy))
296
+ if video_rect is not None:
297
+ max_x = video_rect.right() - origin.x() if sx > 0 else origin.x() - video_rect.left()
298
+ max_y = video_rect.bottom() - origin.y() if sy > 0 else origin.y() - video_rect.top()
299
+ size = int(min(size, max_x, max_y))
300
+ size = max(0, size)
301
+ end = QPoint(origin.x() + sx * size, origin.y() + sy * size)
302
+ top_left = QPoint(min(origin.x(), end.x()), min(origin.y(), end.y()))
303
+ return QRect(top_left, QSize(size, size))
304
+
305
+ def eventFilter(widget, obj, event):
306
+ if not getattr(widget, '_face_selection_speaker', None):
307
+ return super().eventFilter(obj, event)
308
+ if obj is not widget._graphics_view.viewport():
309
+ return super().eventFilter(obj, event)
310
+
311
+ etype = event.type()
312
+ if etype == QEvent.MouseButtonPress and event.button() == Qt.RightButton:
313
+ widget._end_face_selection()
314
+ return True
315
+ if etype == QEvent.MouseButtonPress and event.button() == Qt.LeftButton:
316
+ widget._face_selection_origin = event.pos()
317
+ widget._face_rubber_band.setGeometry(QRect(event.pos(), QSize()))
318
+ widget._face_rubber_band.show()
319
+ return True
320
+ if etype == QEvent.MouseMove and widget._face_selection_origin is not None:
321
+ widget._face_rubber_band.setGeometry(widget._square_rect(widget._face_selection_origin, event.pos()))
322
+ return True
323
+ if etype == QEvent.MouseButtonRelease and widget._face_selection_origin is not None:
324
+ rect = widget._square_rect(widget._face_selection_origin, event.pos())
325
+ widget._apply_face_selection(rect)
326
+ widget._end_face_selection()
327
+ return True
328
+ if etype == QEvent.KeyPress and event.key() == Qt.Key_Escape:
329
+ widget._end_face_selection()
330
+ return True
331
+ return super().eventFilter(obj, event)
332
+
333
+ def _apply_face_selection(widget, rect):
334
+ frame = widget._last_video_frame
335
+ if frame is None or not frame.isValid():
336
+ return
337
+ video_rect = widget._video_rect_in_viewport()
338
+ if video_rect is None:
339
+ return
340
+
341
+ clipped = rect.intersected(video_rect.toRect())
342
+ if clipped.width() < 4 or clipped.height() < 4:
343
+ return
344
+
345
+ frame_size = frame.size()
346
+ fw, fh = frame_size.width(), frame_size.height()
347
+ scale_x = fw / video_rect.width()
348
+ scale_y = fh / video_rect.height()
349
+ fx = int(round((clipped.x() - video_rect.x()) * scale_x))
350
+ fy = int(round((clipped.y() - video_rect.y()) * scale_y))
351
+ fw_sel = int(round(clipped.width() * scale_x))
352
+ fh_sel = int(round(clipped.height() * scale_y))
353
+ fx = max(0, min(fx, fw - 1))
354
+ fy = max(0, min(fy, fh - 1))
355
+ fw_sel = max(1, min(fw_sel, fw - fx))
356
+ fh_sel = max(1, min(fh_sel, fh - fy))
357
+
358
+ frame_image = frame.toImage()
359
+ cropped = frame_image.copy(QRect(fx, fy, fw_sel, fh_sel))
360
+ if cropped.isNull():
361
+ return
362
+
363
+ speaker_name = widget._face_selection_speaker
364
+ from subtitld.modules import session as _session
365
+ _session.SPEAKERS.setdefault(speaker_name, {})['image'] = cropped
366
+ _session.set_unsaved()
367
+ window = widget.window()
368
+ from subtitld.interface import left_panel_speakers
369
+ left_panel_speakers.update_speakers_list(window)
370
+ widget.update()
371
+
372
+
373
+ def load(self):
374
+ self.preview_panel_container = QWidget()
375
+ self.preview_panel_container.setLayout(QVBoxLayout())
376
+ self.preview_panel_container.layout().setContentsMargins(0, 0, 0, 0)
377
+ self.preview_panel = QWidget(self.preview_panel_container)
378
+ self.preview_panel.setLayout(QVBoxLayout())
379
+ self.preview_panel.setObjectName('preview_panel')
380
+ self.preview_panel.layout().setContentsMargins(0, 0, 0, 0)
381
+ self.preview_panel.opacity = QGraphicsOpacityEffect()
382
+ self.preview_panel.opacity.setOpacity(0)
383
+ self.preview_panel.setGraphicsEffect(self.preview_panel.opacity)
384
+ self.preview_panel.animation = QPropertyAnimation(self.preview_panel, b'pos')
385
+ self.preview_panel.animation.setEasingCurve(QEasingCurve.OutCubic)
386
+
387
+ self.preview_panel_player = PlayerWidget()
388
+ self.preview_panel_player.setObjectName('preview_panel_player')
389
+ self.preview_panel.layout().addWidget(self.preview_panel_player)
390
+
391
+ self.preview_panel_container.layout().addWidget(self.preview_panel)
392
+
393
+ self.main_horizontal_splitter.addWidget(self.preview_panel_container)
394
+
395
+
396
+ def show(self):
397
+ self.preview_panel_player._force_resize_update()
398
+ QTimer().singleShot(100, lambda: self.preview_panel.opacity.setOpacity(1.0))
399
+ utils.animate_element(self.preview_panel.animation, duration=1000, effect='slide_from_right')
400
+
401
+
402
+ def hide(self):
403
+ utils.animate_element(self.preview_panel.animation, duration=200, effect='slide_to_right')
404
+
405
+
406
+ def translate(self):
407
+ pass
@@ -0,0 +1,65 @@
1
+ from PySide6.QtWidgets import QSplitter
2
+ from PySide6.QtCore import Qt
3
+
4
+ from subtitld.modules import file_io
5
+ from subtitld.modules import session
6
+
7
+ from subtitld.interface import left_panel
8
+ from subtitld.interface import preview_panel
9
+ from subtitld.interface import bottom_panel
10
+ from subtitld.interface import top_bar
11
+ from subtitld.interface.translation import _
12
+
13
+
14
+ def load(self):
15
+ self.main_horizontal_splitter = QSplitter(Qt.Horizontal)
16
+ self.main_horizontal_splitter.splitterMoved.connect(lambda pos, index: main_horizontal_splitter_changed(self, pos, index))
17
+
18
+ left_panel.load(self)
19
+
20
+ preview_panel.load(self)
21
+
22
+ self.main_horizontal_splitter.setSizes(session.CONFIG['interface_splitters'].get('main_horizontal', [25, 75]))
23
+
24
+ self.main_vertical_splitter = QSplitter(Qt.Vertical)
25
+ self.main_vertical_splitter.splitterMoved.connect(lambda pos, index: main_vertical_splitter_changed(self, pos, index))
26
+
27
+ self.main_vertical_splitter.addWidget(self.main_horizontal_splitter)
28
+
29
+ bottom_panel.load(self)
30
+
31
+ self.central_widget.layout().addWidget(self.main_vertical_splitter)
32
+
33
+ self.main_vertical_splitter.setSizes(session.CONFIG['interface_splitters'].get('main_vertical', [70, 30]))
34
+
35
+ if session.CONFIG.get('autosave', {}).get('backup_enabled', True):
36
+ self.autosave_backup_timer.start()
37
+
38
+
39
+ def main_horizontal_splitter_changed(self, pos, index):
40
+ session.CONFIG['interface_splitters']['main_horizontal'] = self.main_horizontal_splitter.sizes()
41
+
42
+
43
+ def main_vertical_splitter_changed(self, pos, index):
44
+ session.CONFIG['interface_splitters']['main_vertical'] = self.main_vertical_splitter.sizes()
45
+
46
+
47
+ def show(self):
48
+ self.central_widget.layout().setCurrentWidget(self.main_vertical_splitter)
49
+ left_panel.show(self)
50
+ preview_panel.show(self)
51
+ bottom_panel.show(self)
52
+
53
+
54
+ def hide(self):
55
+ left_panel.hide(self)
56
+ preview_panel.hide(self)
57
+ bottom_panel.hide(self)
58
+
59
+
60
+ def translate(self):
61
+ top_bar.translate(self)
62
+ left_panel.translate(self)
63
+ preview_panel.translate(self)
64
+ bottom_panel.translate(self)
65
+