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,763 @@
1
+ import os
2
+ import json
3
+
4
+ from PySide6.QtWidgets import QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton, QLineEdit, QSizePolicy, QStackedWidget, QProgressBar
5
+ from PySide6.QtCore import Qt, QThread, Signal
6
+
7
+ from subtitld.interface import left_panel
8
+ from subtitld.interface.translation import _
9
+ from subtitld.interface import utils
10
+ from subtitld.modules import session
11
+ from subtitld.modules import utils as modules_utils
12
+
13
+
14
+ def _audio_source_for_transcription():
15
+ """Best available audio path for transcription.
16
+
17
+ Prefers the separated vocals (cleanest input) → falls back to the cached
18
+ full-original FLAC → falls back to the raw video file. Useful when the
19
+ separation thread hasn't finished yet."""
20
+ separation = session.VIDEO.get('music_voice_separation') or {}
21
+ vocals = separation.get('vocals')
22
+ if vocals and os.path.isfile(vocals):
23
+ return vocals
24
+
25
+ video_path = session.VIDEO.get('filepath')
26
+ if video_path:
27
+ key = modules_utils.get_cache_key(video_path)
28
+ if key:
29
+ original_flac = os.path.join(session.PATH_SUBTITLD_DATA_AUDIOSEPARATION, key + '_original.flac')
30
+ if os.path.isfile(original_flac):
31
+ return original_flac
32
+ if os.path.isfile(video_path):
33
+ return video_path
34
+
35
+ return None
36
+
37
+ from vosk import Model, KaldiRecognizer
38
+ import requests
39
+ import zipfile
40
+ import wave
41
+ import subprocess
42
+ import re
43
+ import shutil
44
+ import assemblyai as aai
45
+
46
+ LANGUAGE_DESCRIPTIONS = session.LANGUAGE_DICT_LIST.keys()
47
+
48
+ VOSK_CONFIG = {
49
+ "en": [
50
+ {
51
+ "name": "vosk-model-small-en-us-0.15",
52
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip",
53
+ "size": "40M",
54
+ "license": "Apache 2.0",
55
+ "description": "Small English (US) model. Lightweight, low memory usage, suitable for mobile and embedded/offline applications."
56
+ },
57
+ {
58
+ "name": "vosk-model-en-us-0.22",
59
+ "url": "https://alphacephei.com/vosk/models/vosk-model-en-us-0.22.zip",
60
+ "size": "1.8G",
61
+ "license": "Apache 2.0",
62
+ "description": "Full-size English (US) model. Higher accuracy, recommended for server or desktop environments."
63
+ },
64
+ {
65
+ "name": "vosk-model-en-us-0.22-lgraph",
66
+ "url": "https://alphacephei.com/vosk/models/vosk-model-en-us-0.22-lgraph.zip",
67
+ "size": "128M",
68
+ "license": "Apache 2.0",
69
+ "description": "English (US) model with compact language graph. Balanced size and accuracy."
70
+ },
71
+ {
72
+ "name": "vosk-model-en-us-0.42-gigaspeech",
73
+ "url": "https://alphacephei.com/vosk/models/vosk-model-en-us-0.42-gigaspeech.zip",
74
+ "size": "2.3G",
75
+ "license": "Apache 2.0",
76
+ "description": "Large English model trained on GigaSpeech dataset. Improved accuracy for diverse speech."
77
+ }
78
+ ],
79
+ "pt": [
80
+ {
81
+ "name": "vosk-model-small-pt-0.3",
82
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-pt-0.3.zip",
83
+ "size": "31M",
84
+ "license": "Apache 2.0",
85
+ "description": "Small Portuguese model. Lightweight and suitable for offline/mobile applications."
86
+ },
87
+ {
88
+ "name": "vosk-model-pt-fb-v0.1.1-20220516_2113",
89
+ "url": "https://alphacephei.com/vosk/models/vosk-model-pt-fb-v0.1.1-20220516_2113.zip",
90
+ "size": "1.6G",
91
+ "license": "GPLv3.0",
92
+ "description": "Full-size Portuguese model (Facebook training). Higher accuracy, requires more memory."
93
+ }
94
+ ],
95
+ "es": [
96
+ {
97
+ "name": "vosk-model-small-es-0.42",
98
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-es-0.42.zip",
99
+ "size": "39M",
100
+ "license": "Apache 2.0",
101
+ "description": "Small Spanish model. Lightweight, optimized for embedded and offline usage."
102
+ },
103
+ {
104
+ "name": "vosk-model-es-0.42",
105
+ "url": "https://alphacephei.com/vosk/models/vosk-model-es-0.42.zip",
106
+ "size": "1.4G",
107
+ "license": "Apache 2.0",
108
+ "description": "Full-size Spanish model. Higher recognition accuracy for desktop/server usage."
109
+ }
110
+ ],
111
+ "fr": [
112
+ {
113
+ "name": "vosk-model-small-fr-0.22",
114
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-fr-0.22.zip",
115
+ "size": "41M",
116
+ "license": "Apache 2.0",
117
+ "description": "Small French model. Lightweight and efficient for offline/mobile systems."
118
+ },
119
+ {
120
+ "name": "vosk-model-fr-0.22",
121
+ "url": "https://alphacephei.com/vosk/models/vosk-model-fr-0.22.zip",
122
+ "size": "1.4G",
123
+ "license": "Apache 2.0",
124
+ "description": "Full-size French model. Improved accuracy, recommended for powerful systems."
125
+ }
126
+ ],
127
+ "de": [
128
+ {
129
+ "name": "vosk-model-small-de-0.15",
130
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-de-0.15.zip",
131
+ "size": "45M",
132
+ "license": "Apache 2.0",
133
+ "description": "Small German model. Suitable for low-resource and embedded applications."
134
+ },
135
+ {
136
+ "name": "vosk-model-de-0.21",
137
+ "url": "https://alphacephei.com/vosk/models/vosk-model-de-0.21.zip",
138
+ "size": "1.9G",
139
+ "license": "Apache 2.0",
140
+ "description": "Full-size German model. Higher accuracy for server/desktop environments."
141
+ }
142
+ ],
143
+ "it": [
144
+ {
145
+ "name": "vosk-model-small-it-0.22",
146
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-it-0.22.zip",
147
+ "size": "48M",
148
+ "license": "Apache 2.0",
149
+ "description": "Small Italian model. Lightweight and optimized for offline usage."
150
+ },
151
+ {
152
+ "name": "vosk-model-it-0.22",
153
+ "url": "https://alphacephei.com/vosk/models/vosk-model-it-0.22.zip",
154
+ "size": "1.2G",
155
+ "license": "Apache 2.0",
156
+ "description": "Full-size Italian model. Better accuracy for production/server applications."
157
+ }
158
+ ],
159
+ "ru": [
160
+ {
161
+ "name": "vosk-model-small-ru-0.22",
162
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-ru-0.22.zip",
163
+ "size": "45M",
164
+ "license": "Apache 2.0",
165
+ "description": "Small Russian model. Lightweight and suitable for offline systems."
166
+ },
167
+ {
168
+ "name": "vosk-model-ru-0.42",
169
+ "url": "https://alphacephei.com/vosk/models/vosk-model-ru-0.42.zip",
170
+ "size": "1.8G",
171
+ "license": "Apache 2.0",
172
+ "description": "Full-size Russian model. Higher accuracy for complex speech recognition tasks."
173
+ }
174
+ ],
175
+ "zh": [
176
+ {
177
+ "name": "vosk-model-small-cn-0.22",
178
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-cn-0.22.zip",
179
+ "size": "42M",
180
+ "license": "Apache 2.0",
181
+ "description": "Small Chinese model. Compact and suitable for embedded/offline use."
182
+ },
183
+ {
184
+ "name": "vosk-model-cn-0.22",
185
+ "url": "https://alphacephei.com/vosk/models/vosk-model-cn-0.22.zip",
186
+ "size": "1.3G",
187
+ "license": "Apache 2.0",
188
+ "description": "Full-size Chinese model. Higher recognition accuracy for server environments."
189
+ }
190
+ ],
191
+ "ja": [
192
+ {
193
+ "name": "vosk-model-small-ja-0.22",
194
+ "url": "https://alphacephei.com/vosk/models/vosk-model-small-ja-0.22.zip",
195
+ "size": "48M",
196
+ "license": "Apache 2.0",
197
+ "description": "Small Japanese model. Lightweight for offline/mobile applications."
198
+ },
199
+ {
200
+ "name": "vosk-model-ja-0.22",
201
+ "url": "https://alphacephei.com/vosk/models/vosk-model-ja-0.22.zip",
202
+ "size": "1Gb",
203
+ "license": "Apache 2.0",
204
+ "description": "Full-size Japanese model. Higher accuracy for desktop/server usage."
205
+ }
206
+ ]
207
+ }
208
+
209
+
210
+ class VoskPanel(QWidget):
211
+ transcript_started = Signal()
212
+ transcript_progress = Signal(int)
213
+ transcript_finished = Signal()
214
+ def __init__(widget, parent=None):
215
+ super().__init__(parent=None)
216
+ widget.parent = parent
217
+ widget.setLayout(QVBoxLayout())
218
+ widget.layout().setContentsMargins(0, 0, 0, 0)
219
+ widget.layout().setSpacing(10)
220
+ widget.setProperty('transcription_engine', 'Vosk')
221
+ widget.setProperty('class', 'transparent_panel')
222
+
223
+ widget.selected_model = False
224
+
225
+ widget.model_line = QWidget()
226
+ widget.model_line.setLayout(QHBoxLayout())
227
+ widget.model_line.layout().setContentsMargins(0, 0, 0, 0)
228
+ widget.model_line.layout().setSpacing(5)
229
+ widget.layout().addWidget(widget.model_line)
230
+
231
+ widget.model_combobox = utils.LabeledComboBox()
232
+ widget.model_combobox.setObjectName('global_panel_transcription_vosk_transcription_model_combobox')
233
+ widget.model_combobox.activated.connect(lambda: widget.model_combobox_activated())
234
+ widget.model_line.layout().addWidget(widget.model_combobox, 1)
235
+
236
+ widget.download_model_button = QPushButton()
237
+ widget.download_model_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum)
238
+ widget.download_model_button.setObjectName('global_panel_transcription_vosk_transcription_download_model_button')
239
+ widget.download_model_button.clicked.connect(lambda: widget.download_model_button_clicked())
240
+ widget.model_combobox.bottom_line.addWidget(widget.download_model_button)
241
+
242
+ widget.update_model_button = QPushButton()
243
+ widget.update_model_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum)
244
+ widget.update_model_button.setObjectName('global_panel_transcription_vosk_transcription_update_model_button')
245
+ widget.update_model_button.clicked.connect(lambda: widget.update_model_button_clicked())
246
+ widget.model_combobox.bottom_line.addWidget(widget.update_model_button)
247
+
248
+ widget.remove_model_button = QPushButton()
249
+ widget.remove_model_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum)
250
+ widget.remove_model_button.setObjectName('global_panel_transcription_vosk_transcription_remove_model_button')
251
+ widget.remove_model_button.setProperty('class', 'danger')
252
+ widget.remove_model_button.clicked.connect(lambda: widget.remove_model_button_clicked())
253
+ widget.model_combobox.bottom_line.addWidget(widget.remove_model_button)
254
+
255
+ widget.no_model_available_label = QLabel()
256
+ widget.layout().addWidget(widget.no_model_available_label)
257
+
258
+ widget.details_line = QHBoxLayout()
259
+ widget.details_line.setContentsMargins(0, 0, 0, 0)
260
+ widget.details_line.setSpacing(10)
261
+
262
+ widget.description_label = QLabel()
263
+ widget.description_label.setWordWrap(True)
264
+ widget.details_line.addWidget(widget.description_label, 1, Qt.AlignLeft | Qt.AlignTop)
265
+
266
+ widget.license_label = utils.LabeledLabel()
267
+ widget.details_line.addWidget(widget.license_label, 0, Qt.AlignTop)
268
+
269
+ widget.size_label = utils.LabeledLabel()
270
+ widget.details_line.addWidget(widget.size_label, 0, Qt.AlignTop)
271
+
272
+ widget.layout().addLayout(widget.details_line)
273
+
274
+ widget.layout().addStretch()
275
+
276
+ class download_thread(QThread):
277
+ response = Signal(float)
278
+ url = None
279
+ final_path = None
280
+ def run(self):
281
+ if self.url is not None and self.final_path is not None:
282
+ r = requests.get(self.url, stream=True)
283
+ total_size = int(r.headers.get('content-length', 0))
284
+ size_counter = 0
285
+ with open(self.final_path, 'wb') as f:
286
+ for chunk in r.iter_content(chunk_size = 1024 * 1024):
287
+ if chunk:
288
+ f.write(chunk)
289
+ size_counter += len(chunk)
290
+ self.response.emit(float(size_counter) / float(total_size))
291
+
292
+ with zipfile.ZipFile(self.final_path, 'r') as zip_ref:
293
+ zip_ref.extractall(session.PATH_SUBTITLD_DATA_MODELS)
294
+
295
+ def download_started():
296
+ widget.model_combobox.setEnabled(False)
297
+ widget.size_label.setLabel(_('transcription_panel.downloading'))
298
+
299
+ def download_progress_changed(value):
300
+ widget.size_label.setText(f'{widget.selected_model["size"]} ({int(value * 100)}%)')
301
+
302
+ def download_finished():
303
+ widget.size_label.setLabel(_('transcription_panel.size'))
304
+ widget.model_combobox.setEnabled(True)
305
+ widget.models_update()
306
+
307
+ widget.download_thread = download_thread()
308
+ widget.download_thread.response.connect(lambda value: download_progress_changed(value))
309
+ widget.download_thread.started.connect(lambda: download_started())
310
+ widget.download_thread.finished.connect(lambda: download_finished())
311
+
312
+ class VoskThread(QThread):
313
+ response = Signal(object)
314
+ progress = Signal(int)
315
+ model_path = None
316
+ audio_file = None
317
+
318
+ def run(self):
319
+ if self.model_path and self.audio_file:
320
+ temp_audio_file = os.path.join(session.PATH_TEMP, f'vosk_transcribe_{os.path.basename(self.audio_file)}')
321
+
322
+ self.progress.emit(1)
323
+
324
+ subprocess.Popen(
325
+ [
326
+ session.FFMPEG_EXECUTABLE,
327
+ '-i', self.audio_file,
328
+ '-acodec', 'pcm_s16le',
329
+ '-ar', '16000',
330
+ '-ac', '1',
331
+ '-f', 'wav',
332
+ temp_audio_file
333
+ ],
334
+ stdout=subprocess.DEVNULL,
335
+ stderr=subprocess.DEVNULL,
336
+ startupinfo=session.STARTUPINFO
337
+ ).wait()
338
+
339
+ self.progress.emit(8)
340
+
341
+ model = Model(self.model_path)
342
+
343
+ self.progress.emit(9)
344
+
345
+ wf = wave.open(temp_audio_file, "rb")
346
+
347
+ self.progress.emit(10)
348
+
349
+ rec = KaldiRecognizer(model, wf.getframerate())
350
+ rec.SetWords(True)
351
+
352
+ while True:
353
+ data = wf.readframes(4000)
354
+ if len(data) == 0:
355
+ break
356
+ if rec.AcceptWaveform(data):
357
+ result = rec.Result()
358
+ result = re.sub(r'(\d),(\d)', r'\1.\2', result)
359
+ result = json.loads(result)
360
+ self.progress.emit(10 + int((float(result['result'][0]['start'])/float(session.VIDEO.get('duration', 60.0))) * 90))
361
+ self.response.emit(result)
362
+
363
+ self.response.emit(rec.FinalResult())
364
+
365
+ def translate_thread_response(response):
366
+ if isinstance(response, dict) and all(k in response for k in ('result', 'text')):
367
+ session.SUBTITLE['segments'].append({
368
+ 'start': response['result'][0]['start'],
369
+ 'end': response['result'][-1]['end'],
370
+ 'text': response['text'],
371
+ 'speaker': 'A'
372
+ })
373
+ if not response['result'][0]['speaker'] in session.SPEAKERS:
374
+ session.SPEAKERS[response['result'][0]['speaker']] = {
375
+ 'image': None
376
+ }
377
+ widget.window().timeline_widget.update()
378
+ session.set_unsaved()
379
+
380
+ widget.translate_thread = VoskThread()
381
+ widget.translate_thread.response.connect(translate_thread_response)
382
+ widget.translate_thread.started.connect(lambda: widget.transcript_started.emit())
383
+ widget.translate_thread.progress.connect(lambda value: widget.transcript_progress.emit(value))
384
+ widget.translate_thread.finished.connect(lambda: widget.transcript_finished.emit())
385
+
386
+ widget.update_callback = widget.update
387
+ widget.transcript_callback = widget.transcript
388
+ widget.translate_callback = widget.translate
389
+
390
+ def update(widget):
391
+ widget.model_line.setVisible(bool(session.SUBTITLE.get('language', 'en-us')[:2] in VOSK_CONFIG))
392
+ widget.no_model_available_label.setVisible(not bool(session.SUBTITLE.get('language', 'en-us')[:2] in VOSK_CONFIG))
393
+ widget.description_label.setVisible(bool(session.SUBTITLE.get('language', 'en-us')[:2] in VOSK_CONFIG))
394
+ widget.license_label.setVisible(bool(session.SUBTITLE.get('language', 'en-us')[:2] in VOSK_CONFIG))
395
+ widget.size_label.setVisible(bool(session.SUBTITLE.get('language', 'en-us')[:2] in VOSK_CONFIG))
396
+
397
+ if widget.model_line.isVisible():
398
+ list_of_available_models = [item['name'] for item in VOSK_CONFIG[session.SUBTITLE.get('language', 'en-us')[:2]]]
399
+ widget.model_combobox.clear()
400
+ widget.model_combobox.addItems(list_of_available_models)
401
+
402
+ selected_model_from_config = session.CONFIG['transcription'].get('engine_options', {}).get('Vosk', {}).get('selected_model', False)
403
+ if selected_model_from_config:
404
+ widget.model_combobox.setCurrentText(selected_model_from_config)
405
+
406
+ widget.models_update()
407
+
408
+ def remove_model_button_clicked(widget):
409
+ confirm_dialog = utils.SimpleDialog(widget, title=_('transcription_panel.remove_model_confirm'))
410
+ label = QLabel(_('transcription_panel.remove_model_confirm_text'))
411
+ confirm_dialog.content.layout().addWidget(label)
412
+ confirm_dialog.exec()
413
+ if confirm_dialog.result() == 1:
414
+ widget.selected_model = {}
415
+ for model in VOSK_CONFIG[session.SUBTITLE.get('language', 'en-us')[:2]]:
416
+ if model['name'] == widget.model_combobox.currentText():
417
+ widget.selected_model = model
418
+ break
419
+
420
+ model_path = os.path.join(session.PATH_SUBTITLD_DATA_MODELS, widget.selected_model['name'])
421
+
422
+ if os.path.isdir(model_path):
423
+ shutil.rmtree(model_path)
424
+
425
+ widget.models_update()
426
+
427
+ def models_update(widget):
428
+ if widget.model_line.isVisible():
429
+ widget.selected_model = {}
430
+ for model in VOSK_CONFIG[session.SUBTITLE.get('language', 'en-us')[:2]]:
431
+ if model['name'] == widget.model_combobox.currentText():
432
+ widget.selected_model = model
433
+ break
434
+
435
+ widget.download_model_button.setVisible(not os.path.isdir(os.path.join(session.PATH_SUBTITLD_DATA_MODELS, widget.selected_model['name'])))
436
+ widget.update_model_button.setVisible(os.path.isdir(os.path.join(session.PATH_SUBTITLD_DATA_MODELS, widget.selected_model['name'])))
437
+ widget.remove_model_button.setVisible(os.path.isdir(os.path.join(session.PATH_SUBTITLD_DATA_MODELS, widget.selected_model['name'])))
438
+ widget.description_label.setText(widget.selected_model['description'])
439
+ widget.license_label.setText(widget.selected_model['license'])
440
+ widget.size_label.setText(widget.selected_model['size'])
441
+
442
+ def model_combobox_activated(widget):
443
+ if not 'engine_options' in session.CONFIG['transcription']:
444
+ session.CONFIG['transcription']['engine_options'] = {}
445
+ if not 'Vosk' in session.CONFIG['transcription']['engine_options']:
446
+ session.CONFIG['transcription']['engine_options']['Vosk'] = {}
447
+
448
+ session.CONFIG['transcription']['engine_options']['Vosk']['selected_model'] = widget.model_combobox.currentText()
449
+
450
+ widget.models_update()
451
+
452
+ def download_model_button_clicked(widget):
453
+ widget.selected_model = {}
454
+ for model in VOSK_CONFIG[session.SUBTITLE.get('language', 'en-us')[:2]]:
455
+ if model['name'] == widget.model_combobox.currentText():
456
+ widget.selected_model = model
457
+ break
458
+
459
+ widget.download_thread.url = widget.selected_model['url']
460
+ widget.download_thread.final_path = os.path.join(session.PATH_TEMP, widget.selected_model['name'])
461
+ widget.download_thread.start()
462
+
463
+ def update_model_button_clicked(widget):
464
+ widget.remove_model_button_clicked()
465
+ widget.download_model_button_clicked()
466
+
467
+ def transcript(widget):
468
+ if widget.selected_model:
469
+ audio_file = _audio_source_for_transcription()
470
+ if not audio_file:
471
+ return
472
+ widget.translate_thread.model_path = os.path.join(session.PATH_SUBTITLD_DATA_MODELS, widget.selected_model['name'])
473
+ widget.translate_thread.audio_file = audio_file
474
+ widget.translate_thread.start()
475
+
476
+ def translate(widget):
477
+ widget.model_combobox.setLabel(_('transcription_panel.model'))
478
+ widget.license_label.setLabel(_('transcription_panel.license'))
479
+ widget.size_label.setLabel(_('transcription_panel.size'))
480
+ widget.no_model_available_label.setText(_('transcription_panel.no_model_available'))
481
+
482
+
483
+ class AssemblyAIPanel(QWidget):
484
+ transcript_started = Signal()
485
+ transcript_progress = Signal(int)
486
+ transcript_finished = Signal()
487
+ def __init__(widget, parent=None):
488
+ super().__init__(parent=None)
489
+ widget.parent = parent
490
+ widget.setLayout(QVBoxLayout())
491
+ widget.layout().setContentsMargins(0, 0, 0, 0)
492
+ widget.layout().setSpacing(10)
493
+ widget.setProperty('transcription_engine', 'AssemblyAI')
494
+ widget.setProperty('class', 'transparent_panel')
495
+
496
+ widget.api_key = utils.LabeledLineEdit()
497
+ widget.api_key.setObjectName('global_panel_transcription_assemblyai_transcription_api_key')
498
+ widget.api_key.lineedit.setEchoMode(QLineEdit.Password)
499
+ widget.api_key.editingFinished.connect(lambda value: widget.api_key_activated(value))
500
+ widget.layout().addWidget(widget.api_key, 1)
501
+
502
+ widget.layout().addStretch()
503
+
504
+ class AssemblyAIThread(QThread):
505
+ response = Signal(list)
506
+ response_error = Signal(str)
507
+ progress = Signal(int)
508
+ audio_file = None
509
+
510
+ def run(self):
511
+ api_key = session.CONFIG['transcription'].get('engine_options', {}).get('AssemblyAI', {}).get('api_key', False)
512
+ if api_key and self.audio_file:
513
+ try:
514
+ temp_audio_file = os.path.join(session.PATH_TEMP, f'vosk_transcribe_{os.path.basename(self.audio_file)}.opus')
515
+ subprocess.Popen(
516
+ [
517
+ session.FFMPEG_EXECUTABLE,
518
+ '-i', self.audio_file,
519
+ '-c:a', 'libopus',
520
+ '-b:a', '24k',
521
+ '-vbr', 'on',
522
+ '-compression_level', '10',
523
+ '-application', 'voip',
524
+ '-ac', '1',
525
+ '-ar', '48000',
526
+ temp_audio_file.replace('.wav', '.opus')
527
+ ],
528
+ stdout=subprocess.DEVNULL,
529
+ stderr=subprocess.DEVNULL,
530
+ startupinfo=session.STARTUPINFO
531
+ ).wait()
532
+
533
+ self.progress.emit(25)
534
+
535
+ aai.settings.api_key = api_key
536
+
537
+ assemblyai_config = aai.TranscriptionConfig(
538
+ speaker_labels=True,
539
+ language_code=session.SUBTITLE.get('language', 'en-us')[:2],
540
+ punctuate=True,
541
+ )
542
+
543
+ transcriber = aai.Transcriber()
544
+ transcript = transcriber.transcribe(
545
+ f"{temp_audio_file}",
546
+ config=assemblyai_config
547
+ )
548
+
549
+ segments = []
550
+
551
+ for sentence in transcript.get_sentences():
552
+ segments.append({
553
+ 'start': float(sentence.start / 1000),
554
+ 'end': float(sentence.end / 1000),
555
+ 'text': sentence.text,
556
+ 'speaker': sentence.speaker
557
+ })
558
+
559
+ self.response.emit(segments)
560
+
561
+ self.progress.emit(99)
562
+
563
+ except Exception as e:
564
+ self.response_error.emit(str(e))
565
+
566
+ def translate_thread_response(response):
567
+ if isinstance(response, list):
568
+ session.SUBTITLE['segments'] = response
569
+ for segment in session.SUBTITLE['segments']:
570
+ if not segment.get('speaker', 'A') in session.SPEAKERS:
571
+ session.SPEAKERS[segment.get('speaker', 'A')] = {
572
+ 'image': None
573
+ }
574
+ widget.window().timeline_widget.update()
575
+ session.set_unsaved()
576
+
577
+ def translate_thread_error(response):
578
+ error_dialog = utils.SimpleDialog(widget, title=_('transcription_panel.error'), text=response)
579
+ label = QLabel(response)
580
+ error_dialog.content.layout().addWidget(label)
581
+ error_dialog.reject_button.setVisible(False)
582
+ error_dialog.exec()
583
+
584
+ widget.translate_thread = AssemblyAIThread()
585
+ widget.translate_thread.response.connect(translate_thread_response)
586
+ widget.translate_thread.response_error.connect(lambda: translate_thread_error)
587
+ widget.translate_thread.started.connect(lambda: widget.transcript_started.emit())
588
+ widget.translate_thread.progress.connect(lambda value: widget.transcript_progress.emit(value))
589
+ widget.translate_thread.finished.connect(lambda: widget.transcript_finished.emit())
590
+
591
+ widget.update_callback = widget.update
592
+ widget.transcript_callback = widget.transcript
593
+ widget.translate_callback = widget.translate
594
+
595
+ def update(widget):
596
+ widget.api_key.setText(session.CONFIG['transcription'].get('engine_options', {}).get('AssemblyAI', {}).get('api_key', ''))
597
+
598
+ def api_key_activated(widget, value):
599
+ if not 'engine_options' in session.CONFIG['transcription']:
600
+ session.CONFIG['transcription']['engine_options'] = {}
601
+ if not 'AssemblyAI' in session.CONFIG['transcription']['engine_options']:
602
+ session.CONFIG['transcription']['engine_options']['AssemblyAI'] = {}
603
+
604
+ session.CONFIG['transcription']['engine_options']['AssemblyAI']['api_key'] = value
605
+
606
+
607
+ def transcript(widget):
608
+ if session.CONFIG['transcription'].get('engine_options', {}).get('AssemblyAI', {}).get('api_key', ''):
609
+ audio_file = _audio_source_for_transcription()
610
+ if not audio_file:
611
+ return
612
+ widget.translate_thread.audio_file = audio_file
613
+ widget.translate_thread.start()
614
+ else:
615
+ error_dialog = utils.SimpleDialog(widget, title=_('transcription_panel.error'))
616
+ label = QLabel(_('transcription_panel.no_api_key_error'))
617
+ error_dialog.content.layout().addWidget(label)
618
+ error_dialog.exec()
619
+
620
+ def translate(widget):
621
+ widget.api_key.setLabel(_('transcription_panel.api_key'))
622
+ widget.api_key.lineedit.setPlaceholderText(_('transcription_panel.api_key'))
623
+ widget.api_key.lineedit.setToolTip(_('transcription_panel.api_key'))
624
+
625
+
626
+ def load(self):
627
+ tab_name = 'transcription'
628
+
629
+ left_panel_transcription_panel = left_panel.left_panel(
630
+ parent=self,
631
+ tab_name=tab_name,
632
+ update_callback=update,
633
+ translate_callback=translate
634
+ )
635
+
636
+ self.global_panel_transcription_language_combobox = utils.LabeledComboBox()
637
+ self.global_panel_transcription_language_combobox.addItems(LANGUAGE_DESCRIPTIONS)
638
+ self.global_panel_transcription_language_combobox.activated.connect(lambda: global_panel_transcription_language_combobox_activated(self))
639
+ left_panel_transcription_panel.layout().addWidget(self.global_panel_transcription_language_combobox, 1)
640
+
641
+ self.global_panel_transcription_engine_combobox = utils.LabeledComboBox()
642
+ self.global_panel_transcription_engine_combobox.setProperty('class', 'button')
643
+ self.global_panel_transcription_engine_combobox.addItems(['Vosk', 'AssemblyAI'])
644
+ self.global_panel_transcription_engine_combobox.activated.connect(lambda: global_panel_transcription_engine_combobox_activated(self))
645
+ left_panel_transcription_panel.layout().addWidget(self.global_panel_transcription_engine_combobox)
646
+
647
+ self.global_panel_transcription_tabwidget = QStackedWidget()
648
+
649
+ self.global_panel_transcription_vosk_transcription_widget = VoskPanel()
650
+ self.global_panel_transcription_vosk_transcription_widget.transcript_started.connect(lambda: global_panel_transcription_start_transcription_progress_start(self))
651
+ self.global_panel_transcription_vosk_transcription_widget.transcript_progress.connect(lambda value: global_panel_transcription_start_transcription_progress_update(self, value))
652
+ self.global_panel_transcription_vosk_transcription_widget.transcript_finished.connect(lambda: global_panel_transcription_start_transcription_progress_finish(self))
653
+ self.global_panel_transcription_tabwidget.addWidget(self.global_panel_transcription_vosk_transcription_widget)
654
+
655
+ self.global_panel_transcription_assemblyai_transcription_widget = AssemblyAIPanel()
656
+ self.global_panel_transcription_assemblyai_transcription_widget.transcript_started.connect(lambda: global_panel_transcription_start_transcription_progress_start(self))
657
+ self.global_panel_transcription_assemblyai_transcription_widget.transcript_progress.connect(lambda value: global_panel_transcription_start_transcription_progress_update(self, value))
658
+ self.global_panel_transcription_assemblyai_transcription_widget.transcript_finished.connect(lambda: global_panel_transcription_start_transcription_progress_finish(self))
659
+ self.global_panel_transcription_tabwidget.addWidget(self.global_panel_transcription_assemblyai_transcription_widget)
660
+
661
+ left_panel_transcription_panel.layout().addWidget(self.global_panel_transcription_tabwidget, 1)
662
+
663
+ bottom_line = QHBoxLayout()
664
+ bottom_line.setContentsMargins(0, 0, 0, 0)
665
+ bottom_line.setSpacing(0)
666
+ left_panel_transcription_panel.layout().addLayout(bottom_line)
667
+
668
+ def global_panel_transcription_start_transcription_progress_start(self):
669
+ self.global_panel_transcription_start_transcription_progress.setVisible(True)
670
+ self.global_panel_transcription_start_transcription_progress.setValue(0)
671
+ self.global_panel_transcription_start_transcription_progress.setMaximum(100)
672
+ self.global_panel_transcription_start_transcription_button.setVisible(False)
673
+
674
+ def global_panel_transcription_start_transcription_progress_update(self, value):
675
+ self.global_panel_transcription_start_transcription_progress.setValue(value)
676
+
677
+ def global_panel_transcription_start_transcription_progress_finish(self):
678
+ self.global_panel_transcription_start_transcription_progress.setVisible(False)
679
+ self.global_panel_transcription_start_transcription_button.setVisible(True)
680
+
681
+ self.global_panel_transcription_start_transcription_progress = QProgressBar()
682
+ self.global_panel_transcription_start_transcription_progress.setVisible(False)
683
+ self.global_panel_transcription_start_transcription_progress.setProperty('class', 'secondary')
684
+ bottom_line.addWidget(self.global_panel_transcription_start_transcription_progress)
685
+
686
+ self.global_panel_transcription_start_transcription_button = QPushButton()
687
+ self.global_panel_transcription_start_transcription_button.setProperty('class', 'secondary')
688
+ self.global_panel_transcription_start_transcription_button.clicked.connect(lambda: global_panel_transcription_start_transcription_button_clicked(self))
689
+ bottom_line.addWidget(self.global_panel_transcription_start_transcription_button, 0, Qt.AlignRight)
690
+
691
+ update(self)
692
+
693
+
694
+ def show(self):
695
+ update(self)
696
+
697
+
698
+ def update(self):
699
+ if not session.SUBTITLE.get('language', False):
700
+ session.SUBTITLE['language'] = 'en-us'
701
+ selected_language_name = 'English (United States)'
702
+ for language_name, language_code in session.LANGUAGE_DICT_LIST.items():
703
+ if language_code == session.SUBTITLE['language']:
704
+ selected_language_name = language_name
705
+ break
706
+ self.global_panel_transcription_language_combobox.setCurrentText(selected_language_name)
707
+
708
+ self.global_panel_transcription_engine_combobox.setCurrentText(session.CONFIG['transcription'].get('engine', 'Vosk'))
709
+
710
+ global_panel_transcription_tabwidget_update(self)
711
+
712
+
713
+ def hide(self):
714
+ pass
715
+
716
+
717
+ def global_panel_transcription_language_combobox_activated(self):
718
+ session.SUBTITLE['language'] = session.LANGUAGE_DICT_LIST[self.global_panel_transcription_language_combobox.currentText()]
719
+ global_panel_transcription_tabwidget_update(self)
720
+
721
+
722
+ def global_panel_transcription_engine_combobox_activated(self):
723
+ session.CONFIG['transcription']['engine'] = self.global_panel_transcription_engine_combobox.currentText()
724
+ global_panel_transcription_tabwidget_update(self)
725
+
726
+
727
+ def global_panel_transcription_start_transcription_button_clicked(self):
728
+ confirm_transcript = False
729
+ if session.SUBTITLE['segments']:
730
+ confirm_dialog = utils.SimpleDialog(self, title=_('transcription_panel.start_transcription'))
731
+ label = QLabel(_('transcription_panel.start_transcription_text'))
732
+ confirm_dialog.content.layout().addWidget(label)
733
+ confirm_dialog.exec()
734
+ confirm_transcript = bool(confirm_dialog.result() == 1)
735
+
736
+ if confirm_transcript or not session.SUBTITLE['segments']:
737
+ session.SUBTITLE['segments'] = []
738
+ self.timeline_widget.update()
739
+ for widget in self.global_panel_transcription_tabwidget.findChildren(QWidget):
740
+ if widget.property('transcription_engine') == self.global_panel_transcription_engine_combobox.currentText():
741
+ widget.transcript_callback()
742
+ break
743
+
744
+
745
+ def global_panel_transcription_tabwidget_update(self):
746
+ for widget in self.global_panel_transcription_tabwidget.findChildren(QWidget):
747
+ if widget.property('transcription_engine') == self.global_panel_transcription_engine_combobox.currentText():
748
+ self.global_panel_transcription_tabwidget.setCurrentWidget(widget)
749
+ widget.update_callback()
750
+ break
751
+
752
+
753
+ def translate(self):
754
+ self.global_panel_transcription_language_combobox.setLabel(_('transcription_panel.language'))
755
+ self.global_panel_transcription_start_transcription_button.setText(_('transcription_panel.start_transcription'))
756
+ self.global_panel_transcription_engine_combobox.setLabel(_('transcription_panel.engine'))
757
+ for widget in self.global_panel_transcription_tabwidget.findChildren(QWidget):
758
+ if 'translate_callback' in dir(widget):
759
+ widget.translate_callback()
760
+
761
+
762
+
763
+