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.
- subtitld/__init__.py +19 -0
- subtitld/__main__.py +240 -0
- subtitld/ftfy/char_classes.dat +0 -0
- subtitld/graphics/Montserrat-Black.ttf +0 -0
- subtitld/graphics/Montserrat-BlackItalic.ttf +0 -0
- subtitld/graphics/Montserrat-Bold.ttf +0 -0
- subtitld/graphics/Montserrat-BoldItalic.ttf +0 -0
- subtitld/graphics/Montserrat-ExtraBold.ttf +0 -0
- subtitld/graphics/Montserrat-ExtraBoldItalic.ttf +0 -0
- subtitld/graphics/Montserrat-ExtraLight.ttf +0 -0
- subtitld/graphics/Montserrat-ExtraLightItalic.ttf +0 -0
- subtitld/graphics/Montserrat-Italic.ttf +0 -0
- subtitld/graphics/Montserrat-Light.ttf +0 -0
- subtitld/graphics/Montserrat-LightItalic.ttf +0 -0
- subtitld/graphics/Montserrat-Medium.ttf +0 -0
- subtitld/graphics/Montserrat-MediumItalic.ttf +0 -0
- subtitld/graphics/Montserrat-Regular.ttf +0 -0
- subtitld/graphics/Montserrat-SemiBold.ttf +0 -0
- subtitld/graphics/Montserrat-SemiBoldItalic.ttf +0 -0
- subtitld/graphics/Montserrat-Thin.ttf +0 -0
- subtitld/graphics/Montserrat-ThinItalic.ttf +0 -0
- subtitld/graphics/Ubuntu-B.ttf +0 -0
- subtitld/graphics/Ubuntu-BI.ttf +0 -0
- subtitld/graphics/Ubuntu-C.ttf +0 -0
- subtitld/graphics/Ubuntu-L.ttf +0 -0
- subtitld/graphics/Ubuntu-LI.ttf +0 -0
- subtitld/graphics/Ubuntu-M.ttf +0 -0
- subtitld/graphics/Ubuntu-MI.ttf +0 -0
- subtitld/graphics/Ubuntu-R.ttf +0 -0
- subtitld/graphics/Ubuntu-RI.ttf +0 -0
- subtitld/graphics/Ubuntu-Th.ttf +0 -0
- subtitld/graphics/UbuntuMono-B.ttf +0 -0
- subtitld/graphics/UbuntuMono-BI.ttf +0 -0
- subtitld/graphics/UbuntuMono-R.ttf +0 -0
- subtitld/graphics/UbuntuMono-RI.ttf +0 -0
- subtitld/graphics/add_and_play.svg +4 -0
- subtitld/graphics/add_from_last.svg +4 -0
- subtitld/graphics/add_from_last_checked.svg +4 -0
- subtitld/graphics/add_subtitle_icon.svg +4 -0
- subtitld/graphics/add_subtitle_to_next_start.svg +4 -0
- subtitld/graphics/add_subtitle_to_next_start_checked.svg +4 -0
- subtitld/graphics/background_watermark.svg +9 -0
- subtitld/graphics/character_count_icon.svg +1 -0
- subtitld/graphics/checkbox_checked.svg +8 -0
- subtitld/graphics/checkbox_checked_hover.svg +8 -0
- subtitld/graphics/checkbox_disabled.svg +4 -0
- subtitld/graphics/checkbox_unchecked.svg +5 -0
- subtitld/graphics/checkbox_unchecked_hover.svg +5 -0
- subtitld/graphics/combobox_down_arrow.svg +4 -0
- subtitld/graphics/cps_icon.svg +1 -0
- subtitld/graphics/dialog_close_icon.svg +4 -0
- subtitld/graphics/dialog_close_icon_hover.svg +4 -0
- subtitld/graphics/dubbing_container_collapse_icon.svg +8 -0
- subtitld/graphics/dubbing_container_expand_icon.svg +4 -0
- subtitld/graphics/file.svg +4 -0
- subtitld/graphics/gap_add_icon.svg +4 -0
- subtitld/graphics/gap_remove_icon.svg +4 -0
- subtitld/graphics/grid_frames_icon.svg +4 -0
- subtitld/graphics/grid_frames_icon_checked.svg +4 -0
- subtitld/graphics/grid_frames_icon_disabled.svg +4 -0
- subtitld/graphics/grid_scenes_icon.svg +4 -0
- subtitld/graphics/grid_scenes_icon_checked.svg +4 -0
- subtitld/graphics/grid_scenes_icon_disabled.svg +4 -0
- subtitld/graphics/grid_seconds_icon.svg +4 -0
- subtitld/graphics/grid_seconds_icon_checked.svg +4 -0
- subtitld/graphics/grid_seconds_icon_disabled.svg +4 -0
- subtitld/graphics/last_end_to_current_position_icon.svg +4 -0
- subtitld/graphics/last_start_last_end_to_current_position_button.svg +9 -0
- subtitld/graphics/last_start_to_current_position_icon.svg +4 -0
- subtitld/graphics/left_panel_autosave.svg +4 -0
- subtitld/graphics/left_panel_dubbing.svg +4 -0
- subtitld/graphics/left_panel_export.svg +4 -0
- subtitld/graphics/left_panel_global.svg +4 -0
- subtitld/graphics/left_panel_import.svg +4 -0
- subtitld/graphics/left_panel_interface.svg +4 -0
- subtitld/graphics/left_panel_keyboard.svg +4 -0
- subtitld/graphics/left_panel_metadata.svg +4 -0
- subtitld/graphics/left_panel_navigation_background.svg +11 -0
- subtitld/graphics/left_panel_qualitycheck.svg +4 -0
- subtitld/graphics/left_panel_speakers.svg +4 -0
- subtitld/graphics/left_panel_subtitles.svg +1 -0
- subtitld/graphics/left_panel_transcription.svg +4 -0
- subtitld/graphics/left_panel_translation.svg +4 -0
- subtitld/graphics/merge_back_selected_subtitle_icon.svg +4 -0
- subtitld/graphics/merge_next_selected_subtitle_icon.svg +4 -0
- subtitld/graphics/move_backward_subtitle.svg +4 -0
- subtitld/graphics/move_end_back_subtitle.svg +4 -0
- subtitld/graphics/move_end_forward_subtitle.svg +4 -0
- subtitld/graphics/move_forward_subtitle.svg +4 -0
- subtitld/graphics/move_start_back_subtitle.svg +4 -0
- subtitld/graphics/move_start_forward_subtitle.svg +4 -0
- subtitld/graphics/music_voice_separation_music_icon.svg +4 -0
- subtitld/graphics/music_voice_separation_music_icon_disabled.svg +4 -0
- subtitld/graphics/music_voice_separation_voice_icon.svg +4 -0
- subtitld/graphics/music_voice_separation_voice_icon_disabled.svg +4 -0
- subtitld/graphics/new_line_icon.svg +1 -0
- subtitld/graphics/next_end_to_current_position_icon.svg +4 -0
- subtitld/graphics/next_start_next_end_to_current_position_button.svg +4 -0
- subtitld/graphics/next_start_to_current_position_icon.svg +4 -0
- subtitld/graphics/pause_icon.svg +12 -0
- subtitld/graphics/play_from_last_start_icon.svg +5 -0
- subtitld/graphics/play_from_next_start_icon.svg +7 -0
- subtitld/graphics/play_icon.svg +12 -0
- subtitld/graphics/play_icon_hover.svg +12 -0
- subtitld/graphics/play_icon_pressed.svg +12 -0
- subtitld/graphics/playback_repeat.svg +4 -0
- subtitld/graphics/playback_speed.svg +6 -0
- subtitld/graphics/playbackspeed_border.svg +5 -0
- subtitld/graphics/playbackspeed_border_checked.svg +5 -0
- subtitld/graphics/playbackspeed_border_hover.svg +5 -0
- subtitld/graphics/playercontrols_button_first.svg +5 -0
- subtitld/graphics/playercontrols_button_first_hover.svg +6 -0
- subtitld/graphics/playercontrols_button_first_pressed.svg +6 -0
- subtitld/graphics/playercontrols_button_last.svg +6 -0
- subtitld/graphics/playercontrols_button_last_hover.svg +6 -0
- subtitld/graphics/playercontrols_button_last_pressed.svg +6 -0
- subtitld/graphics/playercontrols_widget_center_bottom_line.svg +13 -0
- subtitld/graphics/playercontrols_widget_center_top_line.svg +17 -0
- subtitld/graphics/plus_icon.svg +4 -0
- subtitld/graphics/record_icon.svg +8 -0
- subtitld/graphics/record_icon_pressed.svg +5 -0
- subtitld/graphics/remove_selected_subtitle_icon.svg +6 -0
- subtitld/graphics/repeat_playback_border.svg +5 -0
- subtitld/graphics/repeat_playback_border_checked.svg +5 -0
- subtitld/graphics/repeat_playback_border_hover.svg +5 -0
- subtitld/graphics/save.svg +4 -0
- subtitld/graphics/send_text_to_last_subtitle_and_slice_button.svg +8 -0
- subtitld/graphics/send_text_to_last_subtitle_icon.svg +8 -0
- subtitld/graphics/send_text_to_next_subtitle_and_slice_button.svg +8 -0
- subtitld/graphics/send_text_to_next_subtitle_icon.svg +8 -0
- subtitld/graphics/slice_selected_subtitle_icon.svg +4 -0
- subtitld/graphics/snap_grid_icon.svg +4 -0
- subtitld/graphics/snap_grid_icon_checked.svg +4 -0
- subtitld/graphics/snap_grid_icon_disabled.svg +4 -0
- subtitld/graphics/snap_limits_icon.svg +4 -0
- subtitld/graphics/snap_limits_icon_checked.svg +4 -0
- subtitld/graphics/snap_limits_icon_disabled.svg +4 -0
- subtitld/graphics/snap_move_next.svg +4 -0
- subtitld/graphics/snap_move_next_checked.svg +4 -0
- subtitld/graphics/snap_move_next_disabled.svg +4 -0
- subtitld/graphics/snap_moving_icon.svg +4 -0
- subtitld/graphics/snap_moving_icon_checked.svg +4 -0
- subtitld/graphics/snap_moving_icon_disabled.svg +4 -0
- subtitld/graphics/speaker_list_item_change_color_icon.svg +4 -0
- subtitld/graphics/speaker_list_item_change_color_icon_hover.svg +4 -0
- subtitld/graphics/speaker_list_item_change_image_icon.svg +4 -0
- subtitld/graphics/speaker_list_item_change_image_icon_hover.svg +4 -0
- subtitld/graphics/speaker_list_item_export_icon.svg +4 -0
- subtitld/graphics/speaker_list_item_export_icon_hover.svg +4 -0
- subtitld/graphics/speaker_list_item_remove_icon.svg +4 -0
- subtitld/graphics/speaker_list_item_remove_icon_hover.svg +4 -0
- subtitld/graphics/speaker_list_item_rename_icon.svg +4 -0
- subtitld/graphics/speaker_list_item_rename_icon_hover.svg +4 -0
- subtitld/graphics/spinbox_arrow_down.svg +4 -0
- subtitld/graphics/spinbox_arrow_up.svg +4 -0
- subtitld/graphics/start_screen_top_shadow.svg +23 -0
- subtitld/graphics/stop_icon.svg +6 -0
- subtitld/graphics/stop_icon_hover.svg +6 -0
- subtitld/graphics/stop_icon_pressed.svg +6 -0
- subtitld/graphics/stylesheet.qss +1838 -0
- subtitld/graphics/subtitld.ico +0 -0
- subtitld/graphics/subtitld.png +0 -0
- subtitld/graphics/subtitles_panel_simplelist_properties_duration_timing_lock_closed_icon.svg +4 -0
- subtitld/graphics/subtitles_panel_simplelist_properties_duration_timing_lock_open_icon.svg +4 -0
- subtitld/graphics/subtitles_panel_simplelist_properties_duration_timing_lock_open_icon_hover.svg +4 -0
- subtitld/graphics/timeline_bottom_controls_background.svg +25 -0
- subtitld/graphics/timeline_cursor_back_frame.svg +4 -0
- subtitld/graphics/timeline_cursor_back_frame_hover.svg +4 -0
- subtitld/graphics/timeline_cursor_next_frame.svg +4 -0
- subtitld/graphics/timeline_cursor_next_frame_hover.svg +4 -0
- subtitld/graphics/timeline_show_speaker_color.svg +5 -0
- subtitld/graphics/timeline_show_speaker_color_checked.svg +5 -0
- subtitld/graphics/timeline_show_speaker_in_tracks.svg +4 -0
- subtitld/graphics/timeline_show_speaker_in_tracks_checked.svg +4 -0
- subtitld/graphics/timelinescrolling_follow_icon.svg +4 -0
- subtitld/graphics/timelinescrolling_follow_icon_checked.svg +4 -0
- subtitld/graphics/timelinescrolling_none_icon.svg +6 -0
- subtitld/graphics/timelinescrolling_none_icon_checked.svg +6 -0
- subtitld/graphics/timelinescrolling_page_icon.svg +4 -0
- subtitld/graphics/timelinescrolling_page_icon_checked.svg +4 -0
- subtitld/graphics/titleBar_left_container_border.svg +113 -0
- subtitld/graphics/unknown_speaker.svg +5 -0
- subtitld/graphics/vosk_transcription_download_model_icon.svg +4 -0
- subtitld/graphics/vosk_transcription_update_model_icon.svg +4 -0
- subtitld/graphics/word_count_icon.svg +1 -0
- subtitld/graphics/wpm_icon.svg +1 -0
- subtitld/graphics/zoom_in_icon.svg +4 -0
- subtitld/graphics/zoom_in_icon_disabled.svg +4 -0
- subtitld/graphics/zoom_out_icon.svg +4 -0
- subtitld/graphics/zoom_out_icon_disabled.svg +4 -0
- subtitld/interface/__init__.py +0 -0
- subtitld/interface/actionmanager.py +18 -0
- subtitld/interface/bottom_panel.py +43 -0
- subtitld/interface/left_panel.py +144 -0
- subtitld/interface/left_panel_autosave.py +149 -0
- subtitld/interface/left_panel_dubbing.py +469 -0
- subtitld/interface/left_panel_export.py +755 -0
- subtitld/interface/left_panel_global.py +358 -0
- subtitld/interface/left_panel_import.py +82 -0
- subtitld/interface/left_panel_interface.py +616 -0
- subtitld/interface/left_panel_keyboard.py +261 -0
- subtitld/interface/left_panel_metadata.py +74 -0
- subtitld/interface/left_panel_qualitycheck.py +262 -0
- subtitld/interface/left_panel_speakers.py +737 -0
- subtitld/interface/left_panel_subtitleslist.py +791 -0
- subtitld/interface/left_panel_transcription.py +763 -0
- subtitld/interface/left_panel_translation.py +294 -0
- subtitld/interface/playercontrols.py +2348 -0
- subtitld/interface/preview_panel.py +407 -0
- subtitld/interface/productionscreen.py +65 -0
- subtitld/interface/startscreen.py +273 -0
- subtitld/interface/timeline.py +1308 -0
- subtitld/interface/top_bar.py +329 -0
- subtitld/interface/translation.py +68 -0
- subtitld/interface/utils.py +286 -0
- subtitld/locale/en_US.json +301 -0
- subtitld/locale/pt_BR.json +269 -0
- subtitld/modules/__init__.py +0 -0
- subtitld/modules/audioengine.py +606 -0
- subtitld/modules/bounce.py +229 -0
- subtitld/modules/config.py +84 -0
- subtitld/modules/file_io.py +870 -0
- subtitld/modules/history.py +31 -0
- subtitld/modules/quality_check.py +53 -0
- subtitld/modules/session.py +270 -0
- subtitld/modules/shortcuts.py +84 -0
- subtitld/modules/subtitles.py +413 -0
- subtitld/modules/timecode.py +496 -0
- subtitld/modules/usf.py +366 -0
- subtitld/modules/utils.py +79 -0
- subtitld/modules/waveform.py +53 -0
- subtitld-0.0.0.dev0.dist-info/METADATA +97 -0
- subtitld-0.0.0.dev0.dist-info/RECORD +237 -0
- subtitld-0.0.0.dev0.dist-info/WHEEL +5 -0
- subtitld-0.0.0.dev0.dist-info/entry_points.txt +2 -0
- subtitld-0.0.0.dev0.dist-info/licenses/LICENSE +674 -0
- 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
|