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,496 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2014 Joshua Banton and PyTimeCode developers
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ __version__ = '1.2.1'
25
+
26
+
27
+ class Timecode(object):
28
+ """The main timecode class.
29
+
30
+ Does all the calculation over frames, so the main data it holds is
31
+ frames, then when required it converts the frames to a timecode by
32
+ using the frame rate setting.
33
+
34
+ :param framerate: The frame rate of the Timecode instance. It
35
+ should be one of ['23.976', '23.98', '24', '25', '29.97', '30', '50',
36
+ '59.94', '60', 'NUMERATOR/DENOMINATOR', ms'] where "ms" equals to
37
+ 1000 fps.
38
+ Can not be skipped.
39
+ Setting the framerate will automatically set the :attr:`.drop_frame`
40
+ attribute to correct value.
41
+ :param start_timecode: The start timecode. Use this to be able to
42
+ set the timecode of this Timecode instance. It can be skipped and
43
+ then the frames attribute will define the timecode, and if it is also
44
+ skipped then the start_second attribute will define the start
45
+ timecode, and if start_seconds is also skipped then the default value
46
+ of '00:00:00:00' will be used.
47
+ When using 'ms' frame rate, timecodes like '00:11:01.040' use '.040'
48
+ as frame number. When used with other frame rates, '.040' represents
49
+ a fraction of a second. So '00:00:00.040'@25fps is 1 frame.
50
+ :type framerate: str or int or float or tuple
51
+ :type start_timecode: str or None
52
+ :param start_seconds: A float or integer value showing the seconds.
53
+ :param int frames: Timecode objects can be initialized with an
54
+ integer number showing the total frames.
55
+ """
56
+
57
+ def __init__(self, framerate, start_timecode=None, start_seconds=None,
58
+ frames=None, fractional=False):
59
+ self.drop_frame = False
60
+ self.ms_frame = False
61
+ self.fraction_frame = fractional
62
+ self._int_framerate = None
63
+ self._framerate = None
64
+ self.framerate = framerate
65
+
66
+ self.frames = None
67
+
68
+ # attribute override order
69
+ # start_timecode > frames > start_seconds
70
+ if start_timecode:
71
+ self.frames = self.tc_to_frames(start_timecode)
72
+ else:
73
+ if frames is not None: # because 0==False, and frames can be 0
74
+ self.frames = frames
75
+ elif start_seconds is not None:
76
+ #if start_seconds == 0:
77
+ # start_seconds = 0.001
78
+ self.frames = self.float_to_tc(start_seconds)
79
+ else:
80
+ # use default value of 00:00:00:00
81
+ self.frames = self.tc_to_frames('00:00:00:00')
82
+
83
+ @property
84
+ def framerate(self):
85
+ """getter for _framerate attribute
86
+ """
87
+ return self._framerate
88
+
89
+ @framerate.setter
90
+ def framerate(self, framerate): # lint:ok
91
+ """setter for the framerate attribute
92
+ :param framerate:
93
+ :return:
94
+ """
95
+
96
+ # Convert rational frame rate to float
97
+ numerator = None
98
+ denominator = None
99
+
100
+ try:
101
+ if '/' in framerate:
102
+ numerator, denominator = framerate.split('/')
103
+ except TypeError:
104
+ # not a string
105
+ pass
106
+
107
+ if isinstance(framerate, tuple):
108
+ numerator, denominator = framerate
109
+
110
+ if numerator and denominator:
111
+ framerate = round(float(numerator) / float(denominator), 2)
112
+
113
+ if framerate.is_integer():
114
+ framerate = int(framerate)
115
+
116
+ # check if number is passed and if so convert it to a string
117
+ if isinstance(framerate, (int, float)):
118
+ framerate = str(framerate)
119
+
120
+ # set the int_frame_rate
121
+ if framerate == '29.97':
122
+ self._int_framerate = 30
123
+ self.drop_frame = True
124
+ elif framerate == '59.94':
125
+ self._int_framerate = 60
126
+ self.drop_frame = True
127
+ elif framerate in ['23.976', '23.98']:
128
+ framerate = '24'
129
+ self._int_framerate = 24
130
+ elif framerate in ['ms', '1000']:
131
+ self._int_framerate = 1000
132
+ self.ms_frame = True
133
+ framerate = 1000
134
+ elif framerate == 'frames':
135
+ self._int_framerate = 1
136
+ else:
137
+ self._int_framerate = int(float(framerate))
138
+
139
+ self._framerate = framerate
140
+
141
+ def set_fractional(self, state):
142
+ """Set or unset timecode to be represented with fractional seconds
143
+ :param bool state:
144
+ """
145
+ self.fraction_frame = state
146
+
147
+ def set_timecode(self, timecode):
148
+ """Sets the frames by using the given timecode
149
+ """
150
+ self.frames = self.tc_to_frames(timecode)
151
+
152
+ def float_to_tc(self, seconds):
153
+ """set the frames by using the given seconds
154
+ """
155
+ return int(seconds * self._int_framerate)
156
+
157
+ def tc_to_frames(self, timecode):
158
+ """Converts the given timecode to frames
159
+ """
160
+ hours, minutes, seconds, frames = map(int,
161
+ self.parse_timecode(timecode)
162
+ )
163
+
164
+ if isinstance(timecode, int):
165
+ time_tokens = [hours, minutes, seconds, frames]
166
+ timecode = ':'.join(str(t) for t in time_tokens)
167
+
168
+ if self.drop_frame:
169
+ timecode = ';'.join(timecode.rsplit(':', 1))
170
+
171
+ ffps = float(self._framerate)
172
+
173
+ if self.drop_frame:
174
+ # Number of drop frames is 6% of framerate rounded to nearest
175
+ # integer
176
+ drop_frames = int(round(ffps * .066666))
177
+ else:
178
+ drop_frames = 0
179
+
180
+ # We don't need the exact framerate anymore, we just need it rounded to
181
+ # nearest integer
182
+ ifps = self._int_framerate
183
+
184
+ # Number of frames per hour (non-drop)
185
+ hour_frames = ifps * 60 * 60
186
+
187
+ # Number of frames per minute (non-drop)
188
+ minute_frames = ifps * 60
189
+
190
+ # Total number of minutes
191
+ total_minutes = (60 * hours) + minutes
192
+
193
+ # Handle case where frames are fractions of a second
194
+ if len(timecode.split('.')) == 2 and not self.ms_frame:
195
+ self.fraction_frame = True
196
+ fraction = timecode.rsplit('.', 1)[1]
197
+
198
+ frames = int(round(float('.' + fraction) * ffps))
199
+
200
+ frame_number = \
201
+ ((hour_frames * hours) + (minute_frames * minutes) +
202
+ (ifps * seconds) + frames) - \
203
+ (drop_frames * (total_minutes - (total_minutes // 10)))
204
+
205
+ frames = frame_number + 1
206
+
207
+ return frames
208
+
209
+ def frames_to_tc(self, frames):
210
+ """Converts frames back to timecode
211
+
212
+ :returns str: the string representation of the current time code
213
+ """
214
+ ffps = float(self._framerate)
215
+
216
+ if self.drop_frame:
217
+ # Number of frames to drop on the minute marks is the nearest
218
+ # integer to 6% of the framerate
219
+ drop_frames = int(round(ffps * .066666))
220
+ else:
221
+ drop_frames = 0
222
+
223
+ # Number of frames in an hour
224
+ frames_per_hour = int(round(ffps * 60 * 60))
225
+ # Number of frames in a day - timecode rolls over after 24 hours
226
+ frames_per_24_hours = frames_per_hour * 24
227
+ # Number of frames per ten minutes
228
+ frames_per_10_minutes = int(round(ffps * 60 * 10))
229
+ # Number of frames per minute is the round of the framerate * 60 minus
230
+ # the number of dropped frames
231
+ frames_per_minute = int(round(ffps) * 60) - drop_frames
232
+
233
+ frame_number = frames - 1
234
+
235
+ if frame_number < 0:
236
+ # Negative time. Add 24 hours.
237
+ frame_number += frames_per_24_hours
238
+
239
+ # If frame_number is greater than 24 hrs, next operation will rollover
240
+ # clock
241
+ frame_number %= frames_per_24_hours
242
+
243
+ if self.drop_frame:
244
+ d = frame_number // frames_per_10_minutes
245
+ m = frame_number % frames_per_10_minutes
246
+ if m > drop_frames:
247
+ frame_number += (drop_frames * 9 * d) + \
248
+ drop_frames * ((m - drop_frames) // frames_per_minute)
249
+ else:
250
+ frame_number += drop_frames * 9 * d
251
+
252
+ ifps = self._int_framerate
253
+
254
+ frs = frame_number % ifps
255
+ if self.fraction_frame:
256
+ frs = round(frs / float(ifps), 3)
257
+
258
+ secs = (frame_number // ifps) % 60
259
+ mins = ((frame_number // ifps) // 60) % 60
260
+ hrs = (((frame_number // ifps) // 60) // 60)
261
+
262
+ return hrs, mins, secs, frs
263
+
264
+ def tc_to_string(self, hrs, mins, secs, frs):
265
+ if self.fraction_frame:
266
+ return "{hh:02d}:{mm:02d}:{ss:06.3f}".format(hh=hrs,
267
+ mm=mins,
268
+ ss=secs + frs
269
+ )
270
+
271
+ ff = "%02d"
272
+ if self.ms_frame:
273
+ ff = "%03d"
274
+
275
+ return ("%02d:%02d:%02d%s" + ff) % (hrs,
276
+ mins,
277
+ secs,
278
+ self.frame_delimiter,
279
+ frs)
280
+
281
+ @classmethod
282
+ def parse_timecode(cls, timecode):
283
+ """parses timecode string NDF '00:00:00:00' or DF '00:00:00;00' or
284
+ milliseconds/fractionofseconds '00:00:00.000'
285
+ """
286
+ if isinstance(timecode, int):
287
+ indices = range(2, 10, 2)
288
+ hrs, mins, secs, frs = [hex(timecode)[i:i + 2] for i in indices]
289
+
290
+ else:
291
+ bfr = timecode.replace(';', ':').replace('.', ':').split(':')
292
+ hrs = int(bfr[0])
293
+ mins = int(bfr[1])
294
+ secs = int(bfr[2])
295
+ frs = int(bfr[3])
296
+
297
+ return hrs, mins, secs, frs
298
+
299
+ @property
300
+ def frame_delimiter(self):
301
+ """Return correct symbol based on framerate."""
302
+ if self.drop_frame:
303
+ return ';'
304
+
305
+ elif self.ms_frame or self.fraction_frame:
306
+ return '.'
307
+
308
+ else:
309
+ return ':'
310
+
311
+ def __iter__(self):
312
+ return self
313
+
314
+ def next(self):
315
+ self.add_frames(1)
316
+ return self
317
+
318
+ def back(self):
319
+ self.sub_frames(1)
320
+ return self
321
+
322
+ def add_frames(self, frames):
323
+ """adds or subtracts frames number of frames
324
+ """
325
+ self.frames += frames
326
+
327
+ def sub_frames(self, frames):
328
+ """adds or subtracts frames number of frames
329
+ """
330
+ self.add_frames(-frames)
331
+
332
+ def mult_frames(self, frames):
333
+ """multiply frames
334
+ """
335
+ self.frames *= frames
336
+
337
+ def div_frames(self, frames):
338
+ """adds or subtracts frames number of frames"""
339
+ self.frames = self.frames / frames
340
+
341
+ def __eq__(self, other):
342
+ """the overridden equality operator
343
+ """
344
+ if isinstance(other, Timecode):
345
+ return self._framerate == other._framerate and \
346
+ self.frames == other.frames
347
+ elif isinstance(other, str):
348
+ new_tc = Timecode(self._framerate, other)
349
+ return self.__eq__(new_tc)
350
+ elif isinstance(other, int):
351
+ return self.frames == other
352
+
353
+ def __ge__(self, other):
354
+ """override greater or equal to operator"""
355
+ if isinstance(other, Timecode):
356
+ return self._framerate == other._framerate and \
357
+ self.frames >= other.frames
358
+ elif isinstance(other, str):
359
+ new_tc = Timecode(self._framerate, other)
360
+ return self.frames >= new_tc.frames
361
+ elif isinstance(other, int):
362
+ return self.frames >= other
363
+
364
+ def __gt__(self, other):
365
+ """override greater operator"""
366
+ if isinstance(other, Timecode):
367
+ return self._framerate == other._framerate and \
368
+ self.frames > other.frames
369
+ elif isinstance(other, str):
370
+ new_tc = Timecode(self._framerate, other)
371
+ return self.frames > new_tc.frames
372
+ elif isinstance(other, int):
373
+ return self.frames > other
374
+
375
+ def __le__(self, other):
376
+ """override less or equal to operator"""
377
+ if isinstance(other, Timecode):
378
+ return self._framerate == other._framerate and \
379
+ self.frames <= other.frames
380
+ elif isinstance(other, str):
381
+ new_tc = Timecode(self._framerate, other)
382
+ return self.frames <= new_tc.frames
383
+ elif isinstance(other, int):
384
+ return self.frames <= other
385
+
386
+ def __lt__(self, other):
387
+ """override less operator"""
388
+ if isinstance(other, Timecode):
389
+ return self._framerate == other._framerate and \
390
+ self.frames < other.frames
391
+ elif isinstance(other, str):
392
+ new_tc = Timecode(self._framerate, other)
393
+ return self.frames < new_tc.frames
394
+ elif isinstance(other, int):
395
+ return self.frames < other
396
+
397
+ def __add__(self, other):
398
+ """returns new Timecode instance with the given timecode or frames
399
+ added to this one
400
+ """
401
+ # duplicate current one
402
+ tc = Timecode(self._framerate, frames=self.frames)
403
+
404
+ if isinstance(other, Timecode):
405
+ tc.add_frames(other.frames)
406
+ elif isinstance(other, int):
407
+ tc.add_frames(other)
408
+ else:
409
+ raise TimecodeError(
410
+ 'Type %s not supported for arithmetic.' %
411
+ other.__class__.__name__
412
+ )
413
+
414
+ return tc
415
+
416
+ def __sub__(self, other):
417
+ """returns new Timecode instance with subtracted value"""
418
+ if isinstance(other, Timecode):
419
+ subtracted_frames = self.frames - other.frames
420
+ elif isinstance(other, int):
421
+ subtracted_frames = self.frames - other
422
+ else:
423
+ raise TimecodeError(
424
+ 'Type %s not supported for arithmetic.' %
425
+ other.__class__.__name__
426
+ )
427
+
428
+ return Timecode(self._framerate, frames=subtracted_frames)
429
+
430
+ def __mul__(self, other):
431
+ """returns new Timecode instance with multiplied value"""
432
+ if isinstance(other, Timecode):
433
+ multiplied_frames = self.frames * other.frames
434
+ elif isinstance(other, int):
435
+ multiplied_frames = self.frames * other
436
+ else:
437
+ raise TimecodeError(
438
+ 'Type %s not supported for arithmetic.' %
439
+ other.__class__.__name__
440
+ )
441
+
442
+ return Timecode(self._framerate, frames=multiplied_frames)
443
+
444
+ def __div__(self, other):
445
+ """returns new Timecode instance with divided value"""
446
+ if isinstance(other, Timecode):
447
+ div_frames = self.frames / other.frames
448
+ elif isinstance(other, int):
449
+ div_frames = self.frames / other
450
+ else:
451
+ raise TimecodeError(
452
+ 'Type %s not supported for arithmetic.' %
453
+ other.__class__.__name__
454
+ )
455
+
456
+ return Timecode(self._framerate, frames=div_frames)
457
+
458
+ def __repr__(self):
459
+ return self.tc_to_string(*self.frames_to_tc(self.frames))
460
+
461
+ @property
462
+ def hrs(self):
463
+ hrs, mins, secs, frs = self.frames_to_tc(self.frames)
464
+ return hrs
465
+
466
+ @property
467
+ def mins(self):
468
+ hrs, mins, secs, frs = self.frames_to_tc(self.frames)
469
+ return mins
470
+
471
+ @property
472
+ def secs(self):
473
+ hrs, mins, secs, frs = self.frames_to_tc(self.frames)
474
+ return secs
475
+
476
+ @property
477
+ def frs(self):
478
+ hrs, mins, secs, frs = self.frames_to_tc(self.frames)
479
+ return frs
480
+
481
+ @property
482
+ def frame_number(self):
483
+ """returns the 0 based frame number of the current timecode instance
484
+ """
485
+ return self.frames - 1
486
+
487
+ @property
488
+ def float(self):
489
+ """returns the seconds as float
490
+ """
491
+ return self.frames / float(self.framerate)
492
+
493
+ class TimecodeError(Exception):
494
+ """Raised when an error occurred in timecode calculation
495
+ """
496
+ pass