From b0d7303d16d0d9a22864bfa6d03d66211604eb4e Mon Sep 17 00:00:00 2001 From: Gianluca Pernigotto Date: Tue, 6 Feb 2024 00:59:10 +0100 Subject: [PATCH 1/3] fix ValueError passing N/A string --- CHANGELOG | 6 +++++ TODO | 1 - debian/changelog | 7 ++++++ videomass/vdms_sys/msg_info.py | 4 ++-- videomass/vdms_utils/utils.py | 43 ++++++++++++++++++++-------------- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 87d3a00e..8102e205 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,12 @@ License: GPL3 Change Log: ++------------------------------------+ +Tue, 06 Feb 2024 V.5.0.5 + + * Fixed `ValueError: could not convert string to float: 'N/A'` given by the + `get_milliseconds()` function. + +------------------------------------+ Wed, 24 Jan 2024 V.5.0.4 diff --git a/TODO b/TODO index c382c5ad..230c46a6 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ URGENCY: to do soon -------------------- - -------------- URGENCY: high -------------- diff --git a/debian/changelog b/debian/changelog index a17c6d42..cbd80d6e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +videomass (5.0.5-1) UNRELEASED; urgency=medium + + * Fixed `ValueError: could not convert string to float: 'N/A'` given by the + `get_milliseconds()` function. + + -- Gianluca Pernigotto Tue, 06 Feb 2024 00:55:00 +0200 + videomass (5.0.4-1) UNRELEASED; urgency=high * [Preset Manager] Fixed a serious issue during the automatic update task to diff --git a/videomass/vdms_sys/msg_info.py b/videomass/vdms_sys/msg_info.py index be6cb002..f46d2717 100644 --- a/videomass/vdms_sys/msg_info.py +++ b/videomass/vdms_sys/msg_info.py @@ -36,8 +36,8 @@ def current_release(): """ release_name = 'Videomass' program_name = 'videomass' - version = '5.0.4' - release = 'released' + version = '5.0.5' + release = 'not released' copyr = '2013-2024' website = 'http://jeanslack.github.io/Videomass/' author = ('Gianluca Pernigotto', '(aka jeanslack)') diff --git a/videomass/vdms_utils/utils.py b/videomass/vdms_utils/utils.py index a01ea15e..c9626db8 100644 --- a/videomass/vdms_utils/utils.py +++ b/videomass/vdms_utils/utils.py @@ -6,7 +6,7 @@ Author: Gianluca Pernigotto Copyleft - 2024 Gianluca Pernigotto license: GPL3 -Rev: Gen.22.2024 +Rev: Feb.05.2024 Code checker: flake8, pylint . This file is part of Videomass. @@ -225,28 +225,35 @@ def timehuman(seconds): # ------------------------------------------------------------------------ -def get_milliseconds(timeformat): +def get_milliseconds(timeformat: str = '0') -> int: """ - Convert 24-hour clock unit to milliseconds (duration). - Accepts different forms of time unit string, e.g. + Convert 24-hour time unit to milliseconds (duration). + Accepts only strings representing the following forms + of time units: - '30.5', '00:00:00', '0:00:00', '0:0:0', - '00:00.000', '00:00:00.000. + '00' | '00.00' | '00.0' | '00:00:00' | '0:00:00' + '0:0:0' | '00:00.000' | '00:00:00.000 - The first line adds leading zeros to fill up possibly - non-existing fields. + Any other representation passed as a string argument will + be returned as an integer object equal to 0 (int). + If the argument passed is an object other than the string + object (str) the exception `TypeError` will be raised. Return an int object (milliseconds). - - HACK add 'N/A' (no time) to parser? - - if timeformat == 'N/A': - return int('0') + Return 0 otherwise """ + if not isinstance(timeformat, str): + raise TypeError("Only a string object (str) is expected.") + + # adds leading zeros to fill up possibly non-existing fields. hours, minutes, seconds = (["0", "0"] + timeformat.split(":"))[-3:] - hours = int(hours) - minutes = int(minutes) - seconds = float(seconds) + + try: + hours = int(hours) + minutes = int(minutes) + seconds = float(seconds) + except ValueError: + return 0 return int(hours * 3600000 + minutes * 60000 + seconds * 1000) # ------------------------------------------------------------------------ @@ -437,7 +444,7 @@ def trailing_name_with_prog_digit(destpath, argname) -> str: Returns str(newdirname) """ if not isinstance(destpath, str) or not isinstance(argname, str): - raise TypeError("Expects str object only") + raise TypeError("Only a string object (str) is expected.") name, ext = os.path.splitext(argname) name = re.sub(r"[\'\^\`\~\"\#\'\%\&\*\:\<\>\?\/\\\{\|\}]", '', name) @@ -482,7 +489,7 @@ def leading_name_with_prog_digit(destpath, argname) -> str: Returns str(newdirname) """ if not isinstance(destpath, str) or not isinstance(argname, str): - raise TypeError("Expects str object only") + raise TypeError("Only a string object (str) is expected.") name, ext = os.path.splitext(argname) name = re.sub(r"[\'\^\`\~\"\#\'\%\&\*\:\<\>\?\/\\\{\|\}]", '', name) From 879fec5626daa2af2249934e049b72faf17250b9 Mon Sep 17 00:00:00 2001 From: Gianluca Pernigotto Date: Wed, 7 Feb 2024 11:31:41 +0100 Subject: [PATCH 2/3] improve utils functions --- CHANGELOG | 9 +- debian/changelog | 10 +- .../Unused/time_selector_v4.0.2.py | 16 +- .../vdms_dialogs/filter_colorcorrection.py | 10 +- videomass/vdms_dialogs/filter_crop.py | 10 +- videomass/vdms_dialogs/filter_scale.py | 8 +- videomass/vdms_dialogs/filter_stab.py | 12 +- videomass/vdms_dialogs/filter_transpose.py | 8 +- videomass/vdms_main/main_frame.py | 4 +- videomass/vdms_miniframes/timeline.py | 16 +- videomass/vdms_panels/filedrop.py | 4 +- videomass/vdms_panels/long_processing_task.py | 6 +- videomass/vdms_panels/sequence_to_video.py | 36 +++-- videomass/vdms_utils/utils.py | 144 ++++++++++-------- 14 files changed, 160 insertions(+), 133 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8102e205..95775afb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,10 +7,17 @@ License: GPL3 Change Log: +------------------------------------+ -Tue, 06 Feb 2024 V.5.0.5 +Tue, 07 Feb 2024 V.5.0.5 * Fixed `ValueError: could not convert string to float: 'N/A'` given by the `get_milliseconds()` function. + * The `get_milliseconds` and `get_seconds` functions have been replaced by + the `time_to_integer()` function. + * [Still Image Maker] Improved duration setting, millisecond values will be + rounded to time second values. + * Replace `milliseconds2clock` and `milliseconds2clocksec` to + new `integer_to_time` function. + +------------------------------------+ Wed, 24 Jan 2024 V.5.0.4 diff --git a/debian/changelog b/debian/changelog index cbd80d6e..5c7480f8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,8 +2,14 @@ videomass (5.0.5-1) UNRELEASED; urgency=medium * Fixed `ValueError: could not convert string to float: 'N/A'` given by the `get_milliseconds()` function. - - -- Gianluca Pernigotto Tue, 06 Feb 2024 00:55:00 +0200 + * The `get_milliseconds` and `get_seconds` functions have been replaced by + the `time_to_integer()` function. + * [Still Image Maker] Improved duration setting, millisecond values will be + rounded to time second values. + * Replace `milliseconds2clock` and `milliseconds2clocksec` to + new `integer_to_time` function. + + -- Gianluca Pernigotto Wed, 07 Feb 2024 11:30:00 +0200 videomass (5.0.4-1) UNRELEASED; urgency=high diff --git a/videomass/vdms_dialogs/Unused/time_selector_v4.0.2.py b/videomass/vdms_dialogs/Unused/time_selector_v4.0.2.py index 4f9fd0bc..ec79be0d 100644 --- a/videomass/vdms_dialogs/Unused/time_selector_v4.0.2.py +++ b/videomass/vdms_dialogs/Unused/time_selector_v4.0.2.py @@ -25,8 +25,8 @@ along with Videomass. If not, see . """ import wx -from videomass.vdms_utils.utils import get_milliseconds -from videomass.vdms_utils.utils import milliseconds2clock +from videomass.vdms_utils.utils import time_to_integer +from videomass.vdms_utils.utils import integer_to_time # import wx.lib.masked as masked (not work on macOSX) @@ -42,8 +42,8 @@ def __init__(self, parent, seektxt, cuttxt, milliseconds): in the timeline panel are reproduced exactly here. """ - self.seek_mills = get_milliseconds(seektxt) - self.cut_mills = get_milliseconds(cuttxt) + self.seek_mills = time_to_integer(seektxt) + self.cut_mills = time_to_integer(cuttxt) self.milliseconds = milliseconds # media total duration in ms wx.Dialog.__init__(self, parent, -1, style=wx.DEFAULT_DIALOG_STYLE) sizer_base = wx.BoxSizer(wx.VERTICAL) @@ -213,7 +213,7 @@ def get_duration_values(self): f'{self.duration_sec.GetValue()}.' f'{str(self.duration_mills.GetValue()).zfill(3)}' ) - return duration, get_milliseconds(duration) + return duration, time_to_integer(duration) # ------------------------------------------------------------------# def get_start_values(self): @@ -226,7 +226,7 @@ def get_start_values(self): f'{self.start_sec.GetValue()}.' f'{str(self.start_mills.GetValue()).zfill(3)}' ) - return start, get_milliseconds(start) + return start, time_to_integer(start) # ------------------------------------------------------------------# def enable_start_ctrls(self, enable=True): @@ -269,7 +269,7 @@ def on_duration(self, event): entersum = start[1] + duration[1] if entersum > self.milliseconds: - setmax = milliseconds2clock(self.milliseconds - start[1]) + setmax = integer_to_time(self.milliseconds - start[1]) h, m, s = setmax.split(':') sec, ms = s.split('.') self.duration_hour.SetValue(int(h)) @@ -287,7 +287,7 @@ def on_start(self, event): entersum = start[1] + duration[1] if entersum > self.milliseconds: - setmax = milliseconds2clock(self.milliseconds - duration[1]) + setmax = integer_to_time(self.milliseconds - duration[1]) h, m, s = setmax.split(':') sec, ms = s.split('.') self.start_hour.SetValue(int(h)) diff --git a/videomass/vdms_dialogs/filter_colorcorrection.py b/videomass/vdms_dialogs/filter_colorcorrection.py index 64034105..be4ebe76 100644 --- a/videomass/vdms_dialogs/filter_colorcorrection.py +++ b/videomass/vdms_dialogs/filter_colorcorrection.py @@ -26,8 +26,8 @@ """ import os import wx -from videomass.vdms_utils.utils import get_milliseconds -from videomass.vdms_utils.utils import milliseconds2clocksec +from videomass.vdms_utils.utils import time_to_integer +from videomass.vdms_utils.utils import integer_to_time from videomass.vdms_utils.utils import clockset from videomass.vdms_io.make_filelog import make_log_template from videomass.vdms_threads.generic_task import FFmpegGenericTask @@ -105,7 +105,7 @@ def __init__(self, parent, colorset, iconreset, **kwa): sizertime = wx.StaticBoxSizer(stboxtime, wx.HORIZONTAL) sizerBase.Add(sizertime, 0, wx.ALL | wx.CENTER, 5) self.sld_time = wx.Slider(self, wx.ID_ANY, - get_milliseconds(self.clock), + time_to_integer(self.clock), 0, 1 if not self.mills else self.mills, size=(250, -1), @@ -365,7 +365,7 @@ def on_seek_time(self, event): and sets the label with the converted value. """ seek = self.sld_time.GetValue() - clock = milliseconds2clocksec(seek) # to 24-hour + clock = integer_to_time(seek, False) # to 24-hour self.txttime.SetLabel(clock) # update StaticText if not self.btn_load.IsEnabled(): self.btn_load.Enable() @@ -376,7 +376,7 @@ def on_load_at_time(self, event): Reloads all images frame at a given time clock point """ seek = self.sld_time.GetValue() - self.clock = milliseconds2clocksec(seek) # to 24-hour + self.clock = integer_to_time(seek, False) # to 24-hour error = self.process(self.framesrc) if error: wx.MessageBox(f'{error}', 'ERROR', wx.ICON_ERROR, self) diff --git a/videomass/vdms_dialogs/filter_crop.py b/videomass/vdms_dialogs/filter_crop.py index d6200926..6072a9ce 100644 --- a/videomass/vdms_dialogs/filter_crop.py +++ b/videomass/vdms_dialogs/filter_crop.py @@ -30,8 +30,8 @@ import wx.lib.colourselect as csel from pubsub import pub from videomass.vdms_threads.generic_task import FFmpegGenericTask -from videomass.vdms_utils.utils import get_milliseconds -from videomass.vdms_utils.utils import milliseconds2clocksec +from videomass.vdms_utils.utils import time_to_integer +from videomass.vdms_utils.utils import integer_to_time from videomass.vdms_utils.utils import clockset from videomass.vdms_io.make_filelog import make_log_template @@ -240,7 +240,7 @@ def __init__(self, parent, *args, **kwa): wx.HORIZONTAL) sizerBase.Add(sizer_load, 0, wx.ALL | wx.CENTER, 5) self.sld_time = wx.Slider(self, wx.ID_ANY, - get_milliseconds(self.clock), + time_to_integer(self.clock), 0, 1 if not self.mills else self.mills, size=(250, -1), @@ -384,7 +384,7 @@ def on_Seek(self, event): Slider event on seek time position. """ seek = self.sld_time.GetValue() - clock = milliseconds2clocksec(seek) # to 24-hour + clock = integer_to_time(seek, False) # to 24-hour self.txttime.SetLabel(clock) # update StaticText if not self.btn_load.IsEnabled(): self.btn_load.Enable() @@ -404,7 +404,7 @@ def make_frame_from_file(self, event): sseg = '' else: seek = self.sld_time.GetValue() - self.clock = milliseconds2clocksec(seek) # to 24-HH + self.clock = integer_to_time(seek, False) # to 24-HH sseg = f'-ss {self.clock}' arg = (f'{sseg} -i "{self.filename}" -f image2 ' diff --git a/videomass/vdms_dialogs/filter_scale.py b/videomass/vdms_dialogs/filter_scale.py index 0c79b35a..91186f15 100644 --- a/videomass/vdms_dialogs/filter_scale.py +++ b/videomass/vdms_dialogs/filter_scale.py @@ -30,8 +30,8 @@ from videomass.vdms_io import io_tools from videomass.vdms_dialogs.widget_utils import NormalTransientPopup from videomass.vdms_threads.generic_task import FFmpegGenericTask -from videomass.vdms_utils.utils import get_milliseconds -from videomass.vdms_utils.utils import milliseconds2clocksec +from videomass.vdms_utils.utils import time_to_integer +from videomass.vdms_utils.utils import integer_to_time from videomass.vdms_io.make_filelog import make_log_template @@ -66,7 +66,7 @@ def __init__(self, parent, *args, **kwa): self.filename = kwa['filename'] # selected filename on queued list name = os.path.splitext(os.path.basename(self.filename))[0] self.frame = os.path.join(f'{Scale.TMPSRC}', f'{name}.png') # image - self.mills = get_milliseconds(kwa['duration'].split('.')[0]) + self.mills = time_to_integer(kwa['duration'].split('.')[0]) wx.Dialog.__init__(self, parent, -1, style=wx.DEFAULT_DIALOG_STYLE) sizerBase = wx.BoxSizer(wx.VERTICAL) @@ -346,7 +346,7 @@ def process(self, concat): if not self.mills: sseg = '' else: - stime = milliseconds2clocksec(int(self.mills / 2)) + stime = integer_to_time(int(self.mills / 2), False) sseg = f'-ss {stime}' scale = '' if not concat else f'-vf "{concat}"' arg = (f'{sseg} -i "{self.filename}" -f image2 -update 1 ' diff --git a/videomass/vdms_dialogs/filter_stab.py b/videomass/vdms_dialogs/filter_stab.py index efa49849..778a595f 100644 --- a/videomass/vdms_dialogs/filter_stab.py +++ b/videomass/vdms_dialogs/filter_stab.py @@ -28,8 +28,8 @@ import webbrowser import wx import wx.lib.agw.floatspin as FS -from videomass.vdms_utils.utils import get_milliseconds -from videomass.vdms_utils.utils import milliseconds2clocksec +from videomass.vdms_utils.utils import time_to_integer +from videomass.vdms_utils.utils import integer_to_time from videomass.vdms_utils.utils import clockset from videomass.vdms_io import io_tools from videomass.vdms_threads.generic_task import FFmpegGenericTask @@ -94,7 +94,7 @@ def __init__(self, parent, *args, **kwa): sizertime.Add(boxtime, 0, wx.ALL | wx.CENTER, 5) self.sld_time = wx.Slider(self, wx.ID_ANY, - get_milliseconds(self.clock), + time_to_integer(self.clock), 0, 1 if not self.mills else self.mills, size=(250, -1), @@ -451,7 +451,7 @@ def on_seek_time(self, event): and sets the label with the converted value. """ seek = self.sld_time.GetValue() - clock = milliseconds2clocksec(seek) # to 24-hour + clock = integer_to_time(seek, False) # to 24-hour self.lab_time.SetLabel(clock) # update StaticText if not self.btn_snap.IsEnabled(): self.btn_snap.Enable() @@ -513,8 +513,8 @@ def process(self, infile, outfile=None, args='', mode=None): seek = self.mills - stime if seek < 0: seek, stime = 0, self.mills - duration = milliseconds2clocksec(stime) # to 24-hour - self.clock = milliseconds2clocksec(seek) # to 24-hour + duration = integer_to_time(stime, False) # to 24-hour + self.clock = integer_to_time(seek, False) # to 24-hour sseg = f'-ss {self.clock} -t {duration}' if mode == 'detect': diff --git a/videomass/vdms_dialogs/filter_transpose.py b/videomass/vdms_dialogs/filter_transpose.py index 18e85aff..0ba626b9 100644 --- a/videomass/vdms_dialogs/filter_transpose.py +++ b/videomass/vdms_dialogs/filter_transpose.py @@ -28,8 +28,8 @@ from math import pi as pigreco import wx from videomass.vdms_threads.generic_task import FFmpegGenericTask -from videomass.vdms_utils.utils import get_milliseconds -from videomass.vdms_utils.utils import milliseconds2clocksec +from videomass.vdms_utils.utils import time_to_integer +from videomass.vdms_utils.utils import integer_to_time from videomass.vdms_io.make_filelog import make_log_template @@ -70,7 +70,7 @@ def __init__(self, parent, *args, **kwa): self.frame = os.path.join(Transpose.TMPSRC, f'{name}.png') self.stbitmap = None self.bmp = None - self.mills = get_milliseconds(kwa['duration'].split('.')[0]) + self.mills = time_to_integer(kwa['duration'].split('.')[0]) wx.Dialog.__init__(self, parent, -1, style=wx.DEFAULT_DIALOG_STYLE) self.panelimg = wx.Panel(self, wx.ID_ANY, size=(270, 270),) @@ -161,7 +161,7 @@ def process(self): if not self.mills: sseg = '' else: - stime = milliseconds2clocksec(int(self.mills / 2)) + stime = integer_to_time(int(self.mills / 2), False) sseg = f'-ss {stime}' arg = (f'{sseg} -i "{self.video}" -f image2 ' f'-update 1 -frames:v 1 -y "{self.frame}"') diff --git a/videomass/vdms_main/main_frame.py b/videomass/vdms_main/main_frame.py index 2f785f9a..35d1f982 100644 --- a/videomass/vdms_main/main_frame.py +++ b/videomass/vdms_main/main_frame.py @@ -55,7 +55,7 @@ from videomass.vdms_sys.msg_info import current_release from videomass.vdms_sys.settings_manager import ConfigManager from videomass.vdms_sys.argparser import info_this_platform -from videomass.vdms_utils.utils import get_milliseconds +from videomass.vdms_utils.utils import time_to_integer from videomass.vdms_utils.utils import copydir_recursively @@ -1536,7 +1536,7 @@ def switch_to_processing(self, *args): elif args[0] in ('concat_demuxer', 'sequence_to_video'): dur, seq = args[6], '' elif self.time_seq: - ms = get_milliseconds(self.time_seq.split()[3]) # -t duration + ms = time_to_integer(self.time_seq.split()[3]) # -t duration seq = self.time_seq dur = [ms for n in self.duration] self.statusbar_msg(_('Processing...'), None) diff --git a/videomass/vdms_miniframes/timeline.py b/videomass/vdms_miniframes/timeline.py index 01a931f3..1f39ad6a 100644 --- a/videomass/vdms_miniframes/timeline.py +++ b/videomass/vdms_miniframes/timeline.py @@ -27,8 +27,8 @@ import wx import wx.adv from pubsub import pub -from videomass.vdms_utils.utils import milliseconds2clock -from videomass.vdms_utils.utils import get_milliseconds +from videomass.vdms_utils.utils import integer_to_time +from videomass.vdms_utils.utils import time_to_integer class Time_Selector(wx.Dialog): @@ -137,7 +137,7 @@ def getvalue(self): f'{str(self.box_sec.GetValue()).zfill(2)}.' f'{str(self.box_mills.GetValue()).zfill(3)}' ) - return val, get_milliseconds(val) + return val, time_to_integer(val) class Float_TL(wx.MiniFrame): @@ -318,7 +318,7 @@ def set_values(self, msg): self.milliseconds = self.duration[msg] self.sourcedur = _('Source duration:') - self.overalltime = milliseconds2clock(self.milliseconds) + self.overalltime = integer_to_time(self.milliseconds) self.on_trim_time_reset() # ------------------------------------------------------------------# @@ -327,7 +327,7 @@ def set_time_seq(self, isset=True): Set parent time_seq attr. """ if isset: - dur = milliseconds2clock(self.mills_end - self.mills_start) + dur = integer_to_time(self.mills_end - self.mills_start) self.parent.time_seq = f"-ss {self.clock_start} -t {dur}" msg = _('{0} {1} | Segment Duration: {2}' ).format(self.sourcedur, self.overalltime, dur) @@ -347,13 +347,13 @@ def set_coordinates(self): if self.pointpx[1] > 30: self.bar_w = self.pointpx[0] self.mills_end = int(round(self.bar_w / self.pix)) - self.clock_end = milliseconds2clock(self.mills_end) + self.clock_end = integer_to_time(self.mills_end) self.onRedraw(wx.ClientDC(self.paneltime)) elif self.pointpx[1] < 30: self.bar_x = self.pointpx[0] self.mills_start = int(round(self.bar_x / self.pix)) - self.clock_start = milliseconds2clock(self.mills_start) + self.clock_start = integer_to_time(self.mills_start) self.onRedraw(wx.ClientDC(self.paneltime)) # ------------------------------------------------------------------# @@ -436,7 +436,7 @@ def on_move(self, event): elif self.pointpx[1] < 30: if self.mills_start == 0: return - dur = milliseconds2clock(self.mills_end - self.mills_start) + dur = integer_to_time(self.mills_end - self.mills_start) msg = _('{0} {1} | Segment Duration: {2}' ).format(self.sourcedur, self.overalltime, dur) self.statusbar_msg(f'{msg}', None) diff --git a/videomass/vdms_panels/filedrop.py b/videomass/vdms_panels/filedrop.py index bb6e1ec7..d05b6c7c 100644 --- a/videomass/vdms_panels/filedrop.py +++ b/videomass/vdms_panels/filedrop.py @@ -30,7 +30,7 @@ from pubsub import pub from videomass.vdms_io.io_tools import stream_play from videomass.vdms_threads.ffprobe import ffprobe -from videomass.vdms_utils.utils import get_milliseconds +from videomass.vdms_utils.utils import time_to_integer from videomass.vdms_utils.utils import to_bytes from videomass.vdms_dialogs.renamer import Renamer from videomass.vdms_dialogs.list_warning import ListWarning @@ -173,7 +173,7 @@ def dropUpdate(self, path, newname=None): tdur = f'{tdur[0]}h : {tdur[1]}m : {sec} : {msec}' self.SetItem(self.index, 2, tdur) probe['format']['time'] = probe.get('format').pop('duration') - time = get_milliseconds(probe.get('format')['time']) + time = time_to_integer(probe.get('format')['time']) probe['format']['duration'] = time media = probe['streams'][0]['codec_type'] diff --git a/videomass/vdms_panels/long_processing_task.py b/videomass/vdms_panels/long_processing_task.py index c17eef98..10fd5272 100644 --- a/videomass/vdms_panels/long_processing_task.py +++ b/videomass/vdms_panels/long_processing_task.py @@ -39,7 +39,7 @@ from videomass.vdms_threads.video_stabilization import VidStab from videomass.vdms_threads.concat_demuxer import ConcatDemuxer from videomass.vdms_threads.slideshow import SlideshowMaker -from videomass.vdms_utils.utils import (get_milliseconds, milliseconds2clock) +from videomass.vdms_utils.utils import (time_to_integer, integer_to_time) def delete_file_source(flist, trashdir): @@ -240,7 +240,7 @@ def update_display(self, output, duration, status): if 'time=' in output: # ...in processing i = output.index('time=') + 5 pos = output[i:].split()[0] - msec = get_milliseconds(pos) + msec = time_to_integer(pos) if msec > duration: self.barprog.SetValue(duration) @@ -261,7 +261,7 @@ def update_display(self, output, duration, status): eta = " ETA: N/A" else: # is float rem = (duration - msec) / float(speed) - remaining = milliseconds2clock(round(rem)) + remaining = integer_to_time(round(rem)) eta = f" ETA: {remaining}" else: eta = " ETA: N/A" diff --git a/videomass/vdms_panels/sequence_to_video.py b/videomass/vdms_panels/sequence_to_video.py index 0b761a27..addbeb53 100644 --- a/videomass/vdms_panels/sequence_to_video.py +++ b/videomass/vdms_panels/sequence_to_video.py @@ -34,9 +34,8 @@ from videomass.vdms_dialogs.filter_scale import Scale from videomass.vdms_threads.ffprobe import ffprobe from videomass.vdms_utils.utils import trailing_name_with_prog_digit -from videomass.vdms_utils.utils import get_seconds as getsec -from videomass.vdms_utils.utils import get_milliseconds as getms -from videomass.vdms_utils.utils import milliseconds2clock as clockms +from videomass.vdms_utils.utils import time_to_integer +from videomass.vdms_utils.utils import integer_to_time def check_images_size(flist): @@ -489,19 +488,18 @@ def build_command_slideshow(self, timeline): loop = '' if self.ckbx_audio.IsChecked(): if self.opt["Shortest"][0]: - duration = getms(timeline) * len(self.parent.file_src) - self.opt["Clock"] = clockms(duration) - sec = round(getsec(timeline)) + sec = time_to_integer(timeline, sec=True, rnd=True) + duration = sec * len(self.parent.file_src) + self.opt["Clock"] = integer_to_time(duration * 1000) else: - self.opt["Clock"] = clockms(self.opt["ADuration"]) - sec = round(getsec(timeline)) + self.opt["Clock"] = integer_to_time(self.opt["ADuration"]) + sec = time_to_integer(timeline, sec=True, rnd=True) duration = self.opt["ADuration"] loop = f'-loop 1 -t {self.opt["Clock"]}' - else: - duration = getms(timeline) * len(self.parent.file_src) - self.opt["Clock"] = clockms(duration) - sec = round(getsec(timeline)) + sec = time_to_integer(timeline, sec=True, rnd=True) + duration = sec * len(self.parent.file_src) + self.opt["Clock"] = integer_to_time(duration * 1000) framerate = '-framerate 1/1' if not sec else f'-framerate 1/{sec}' self.opt["Preinput"] = f'{loop} {framerate}' @@ -527,17 +525,17 @@ def build_command_loop_image(self, timeline): """ if self.ckbx_audio.IsChecked(): if self.opt["Shortest"][0]: - duration = getms(timeline) - self.opt["Clock"] = timeline - sec = round(getsec(timeline)) + sec = time_to_integer(timeline, sec=True, rnd=True) + duration = sec + self.opt["Clock"] = integer_to_time(duration * 1000) else: - self.opt["Clock"] = clockms(self.opt["ADuration"]) + self.opt["Clock"] = integer_to_time(self.opt["ADuration"]) duration = self.opt["ADuration"] sec = round(self.opt["ADuration"] / 1000) else: - duration = getms(timeline) - self.opt["Clock"] = timeline - sec = round(duration / 1000) + sec = time_to_integer(timeline, sec=True, rnd=True) + duration = sec + self.opt["Clock"] = integer_to_time(duration * 1000) self.opt["Preinput"] = f'-loop 1 -t {self.opt["Clock"]}' self.opt["Interval"] = sec diff --git a/videomass/vdms_utils/utils.py b/videomass/vdms_utils/utils.py index c9626db8..2ec5c864 100644 --- a/videomass/vdms_utils/utils.py +++ b/videomass/vdms_utils/utils.py @@ -194,100 +194,113 @@ def to_bytes(string, key='ydl'): # ------------------------------------------------------------------------ -def get_seconds(timeformat): +def time_to_integer(timef: str = '0', sec=False, rnd=False) -> int: """ - This is the old implementation to get time human to seconds, - e.g. get_seconds('00:02:00'). Return int(seconds) object. + Converts strings representing the 24-hour format to an + integer equivalent to the time duration in milliseconds + (default). - """ - if timeformat == 'N/A': - return int('0') - - pos = timeformat.split(':') - hours, minutes, seconds = int(pos[0]), int(pos[1]), float(pos[2]) - - return hours * 3600 + minutes * 60 + seconds -# ------------------------------------------------------------------------ - - -def timehuman(seconds): - """ - This is the old implementation to converting seconds to - time format. Accept integear only e.g timehuman(2300). - Useb by youtube-dl downloader, returns a string object - in time format i.e '00:38:20' . + ARGUMENTS: + --------- + timef: Accepts a string representing the 24-hour clock in the + following valid string formats: - """ - minutes, seconds = divmod(seconds, 60) - hours, minutes = divmod(minutes, 60) - # return "%02d:%02d:%02d" % (hours, minutes, seconds) - return f"{hours:02}:{minutes:02}:{seconds:02}" -# ------------------------------------------------------------------------ + '0' | '00' | '00.0' | '00.00' | 00:0 | '00:00:00' + '0:00:00' | '0:0:0' | '00:00.000' | '00:00:00.000 + Default is '0' . + Any other representation passed as a string argument will + be returned as an integer object equal to 0 (int). + If the argument passed is an object other than the string + object (str) the exception `TypeError` will be raised. -def get_milliseconds(timeformat: str = '0') -> int: - """ - Convert 24-hour time unit to milliseconds (duration). - Accepts only strings representing the following forms - of time units: + sec: if `True`, returns the duration in seconds instead + of milliseconds. - '00' | '00.00' | '00.0' | '00:00:00' | '0:00:00' - '0:0:0' | '00:00.000' | '00:00:00.000 + rnd: if `True` rounds the result, i.e. "00:00:02.999" > 3000 + milliseconds instead of 2999 milliseconds or 3 seconds + instead of 2 seconds using `sec=True` argument. - Any other representation passed as a string argument will - be returned as an integer object equal to 0 (int). - If the argument passed is an object other than the string - object (str) the exception `TypeError` will be raised. + Return an int object. + Raise `TypeError` if argument != `str` . - Return an int object (milliseconds). - Return 0 otherwise """ - if not isinstance(timeformat, str): - raise TypeError("Only a string object (str) is expected.") + if not isinstance(timef, str): + raise TypeError("Only a string (str) object is expected.") # adds leading zeros to fill up possibly non-existing fields. - hours, minutes, seconds = (["0", "0"] + timeformat.split(":"))[-3:] + hours, minutes, seconds = (["0", "0"] + timef.split(":"))[-3:] try: hours = int(hours) minutes = int(minutes) seconds = float(seconds) + if rnd: + seconds = round(seconds) except ValueError: return 0 + if sec: + return int(hours * 3600 + minutes * 60 + seconds) return int(hours * 3600000 + minutes * 60000 + seconds * 1000) + # ------------------------------------------------------------------------ -def milliseconds2clock(milliseconds): +def timehuman(seconds): """ - Converts milliseconds to 24-hour clock format + milliseconds, - calculating in sexagesimal format. - Accept an `int` object, such as 2998. Float numbers, such - as 2000.999, must be rounded using `round()` function. - Returns a string object of time format e.g. HOURS:MM:SS.MILLIS, - as in 00:00:00.000 . + This is an old implementation to converting seconds to + time format. Accept integer only e.g timehuman(2300). + Useb by youtube-dl downloader, returns a string object + in time format i.e '00:38:20' . """ - minutes, sec = divmod(milliseconds, 60000) + minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) - seconds = float(sec) / 1000 - # return "%02d:%02d:%06.3f" % (hours, minutes, seconds) - return f"{hours:02}:{minutes:02}:{seconds:06.3f}" + # return "%02d:%02d:%02d" % (hours, minutes, seconds) + return f"{hours:02}:{minutes:02}:{seconds:02}" # ------------------------------------------------------------------------ -def milliseconds2clocksec(milliseconds): +def integer_to_time(integer: int = 0, mills=True, rnd=False) -> str: """ - Like milliseconds2clock but differs in the returned object - which does not have milliseconds. - Returns a string object in 24-hour clock of time units - e.g. HOURS:MM:SS, as in 00:00:00 . + Converts integers to 24-hour clock format calculating in + sexagesimal format. Accept an `int` object, such as 2998. + Float numbers, such as 2000.999, must be rounded using + `round()` function first. + + ARGUMENTS: + --------- + integer: Any int object, default is 0. + + mills: Include milliseconds like "HH:MM:SS.MIL" (default). + Milliseconds will be excluded and ignored if set + to `False`. You could instead use the `rnd` argument + to round them and add the offset to the seconds. + + rnd: If `True` rounds the result. This argument will have + no effect with `mills=True` default argument. + + Returns a string object. + Raise `TypeError` if argument != `int` . """ - minutes, sec = divmod(milliseconds, 60000) + if not isinstance(integer, int): + raise TypeError("An integer (int) object is expected.") + + minutes, sec = divmod(integer, 60000) hours, minutes = divmod(minutes, 60) - seconds = int(sec / 1000) + + if mills: + seconds = float(sec) / 1000 + # return "%02d:%02d:%06.3f" % (hours, minutes, seconds) + return f"{hours:02}:{minutes:02}:{seconds:06.3f}" + + if rnd: + seconds = round(sec / 1000) + else: + seconds = int(sec / 1000) + return f"{hours:02}:{minutes:02}:{seconds:02}" # ------------------------------------------------------------------------ @@ -296,6 +309,7 @@ def copy_missing_data(srcd, destd): """ Copy missing files and directories to a given destination path using the same names as the source path. + """ srclist = os.listdir(srcd) destlist = os.listdir(destd) @@ -313,6 +327,7 @@ def copy_restore(srcfile, destfile): Copy the contents (no metadata) of the file named srcfile to a file named destfile. Please visit doc webpage at + """ try: shutil.copyfile(str(srcfile), str(destfile)) @@ -370,6 +385,7 @@ def copy_on(ext, sourcedir, destdir, overw=True): sourcedir: path to the source directory destdir: path to the destination directory overw: `True`, overwrite file destination + """ destpath = os.listdir(destdir) files = glob.glob(f"{sourcedir}/*.{ext}") @@ -444,7 +460,7 @@ def trailing_name_with_prog_digit(destpath, argname) -> str: Returns str(newdirname) """ if not isinstance(destpath, str) or not isinstance(argname, str): - raise TypeError("Only a string object (str) is expected.") + raise TypeError("Only a string (str) object is expected.") name, ext = os.path.splitext(argname) name = re.sub(r"[\'\^\`\~\"\#\'\%\&\*\:\<\>\?\/\\\{\|\}]", '', name) @@ -489,7 +505,7 @@ def leading_name_with_prog_digit(destpath, argname) -> str: Returns str(newdirname) """ if not isinstance(destpath, str) or not isinstance(argname, str): - raise TypeError("Only a string object (str) is expected.") + raise TypeError("Only a string (str) object is expected.") name, ext = os.path.splitext(argname) name = re.sub(r"[\'\^\`\~\"\#\'\%\&\*\:\<\>\?\/\\\{\|\}]", '', name) @@ -523,14 +539,14 @@ def clockset(duration, fileclock): referenced media file. """ duration = duration.split('.')[0] - millis = get_milliseconds(duration) + millis = time_to_integer(duration) if not millis: clock = {'duration': '00:00:00', 'millis': 0} else: if os.path.exists(fileclock): with open(fileclock, "r", encoding='utf8') as atime: clockread = atime.read().strip() - if get_milliseconds(clockread) <= millis: + if time_to_integer(clockread) <= millis: clock = {'duration': clockread, 'millis': millis} else: clock = {'duration': duration, 'millis': millis} From a11390f49fbf76aba7ed598126334cd092970bf9 Mon Sep 17 00:00:00 2001 From: Gianluca Pernigotto Date: Wed, 7 Feb 2024 16:43:20 +0100 Subject: [PATCH 3/3] test cases --- tests/test_utils.py | 76 ++++++++++++++++++---------- videomass/vdms_utils/utils.py | 15 ------ videomass/vdms_ytdlp/youtubedl_ui.py | 4 +- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 1aab929e..01efa0c3 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,7 @@ # -*- coding: UTF-8 -*- # Porpose: Contains test cases for the utils.py object. -# Rev: April.06.2020 *PEP8 compatible* +# Rev: 07.Feb.2024 import sys import os.path @@ -11,7 +11,11 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) try: - from videomass.vdms_utils import utils + from videomass.vdms_utils.utils import (format_bytes, + to_bytes, + time_to_integer, + integer_to_time, + ) except ImportError as error: sys.exit(error) @@ -20,63 +24,79 @@ class TestFormatBytes(unittest.TestCase): """Test case for the format_bytes function.""" def test_format_bytes_bytes(self): - self.assertEqual(utils.format_bytes(518.00), "518.00B") + self.assertEqual(format_bytes(518.00), "518.00B") def test_format_bytes_kilobytes(self): - self.assertEqual(utils.format_bytes(1024.00), "1.00KiB") + self.assertEqual(format_bytes(1024.00), "1.00KiB") def test_format_bytes_megabytes(self): - self.assertEqual(utils.format_bytes(1048576.00), "1.00MiB") + self.assertEqual(format_bytes(1048576.00), "1.00MiB") def test_format_bytes_gigabytes(self): - self.assertEqual(utils.format_bytes(1073741824.00), "1.00GiB") + self.assertEqual(format_bytes(1073741824.00), "1.00GiB") def test_format_bytes_terabytes(self): - self.assertEqual(utils.format_bytes(1099511627776.00), "1.00TiB") + self.assertEqual(format_bytes(1099511627776.00), "1.00TiB") class TestToBytes(unittest.TestCase): """Test case for the to_bytes function.""" def test_to_bytes_bytes(self): - self.assertEqual(utils.to_bytes("596.00B"), 596.00) - self.assertEqual(utils.to_bytes("133.55B"), 133.55) + self.assertEqual(to_bytes("596.00B"), 596.00) + self.assertEqual(to_bytes("133.55B"), 133.55) def test_to_bytes_kilobytes(self): - self.assertEqual(utils.to_bytes("1.00KiB"), 1024.00) - self.assertEqual(utils.to_bytes("5.55KiB"), 5683.20) + self.assertEqual(to_bytes("1.00KiB"), 1024.00) + self.assertEqual(to_bytes("5.55KiB"), 5683.20) def test_to_bytes_megabytes(self): - self.assertEqual(utils.to_bytes("13.64MiB"), 14302576.64) - self.assertEqual(utils.to_bytes("1.00MiB"), 1048576.00) + self.assertEqual(to_bytes("13.64MiB"), 14302576.64) + self.assertEqual(to_bytes("1.00MiB"), 1048576.00) def test_to_bytes_gigabytes(self): - self.assertEqual(utils.to_bytes("1.00GiB"), 1073741824.00) - self.assertEqual(utils.to_bytes("1.55GiB"), 1664299827.20) + self.assertEqual(to_bytes("1.00GiB"), 1073741824.00) + self.assertEqual(to_bytes("1.55GiB"), 1664299827.20) def test_to_bytes_terabytes(self): - self.assertEqual(utils.to_bytes("1.00TiB"), 1099511627776.00) + self.assertEqual(to_bytes("1.00TiB"), 1099511627776.00) -class TestTimeSeconds(unittest.TestCase): - """ Test case for the time_seconds function""" +class Test_time_to_integer(unittest.TestCase): + """ Test case for the `time_to_integer` function""" - def test_to_seconds(self): - self.assertEqual(utils.get_seconds('00:00:00'), 0.0) - self.assertEqual(utils.get_seconds('00:00:55'), 55.0) - self.assertEqual(utils.get_seconds('00:50:23'), 3023.0) - self.assertEqual(utils.get_seconds('02:30:50'), 9050.0) - self.assertEqual(utils.get_seconds('N/A'), 0) + def test_to_get_seconds(self): + self.assertEqual(time_to_integer('N/A', sec=True), 0) + self.assertEqual(time_to_integer('00:00:00', sec=True), 0) + self.assertEqual(time_to_integer('00:00:55', sec=True), 55) + self.assertEqual(time_to_integer('00:50:23', sec=True), 3023) + self.assertEqual(time_to_integer('02:30:50', sec=True), 9050) + + def test_to_get_milliseconds(self): + self.assertEqual(time_to_integer('any string', sec=False), 0) + self.assertEqual(time_to_integer('00:00:00', sec=False), 0) + self.assertEqual(time_to_integer('00:00:55.777', sec=False), 55777) + self.assertEqual(time_to_integer('00:50:23', sec=False), 3023000) + self.assertEqual(time_to_integer('02:30:50', sec=False), 9050000) + + def test_assertion_errors(self): + with self.assertRaises(TypeError): + # Accepts str only not any other types + time_to_integer(160999) class TestTimeHuman(unittest.TestCase): """ Test case for the time_human function""" def test_to_human(self): - self.assertEqual(utils.timehuman(round(0.0)), '00:00:00') - self.assertEqual(utils.timehuman(round(55.0)), '00:00:55') - self.assertEqual(utils.timehuman(round(3023.0)), '00:50:23') - self.assertEqual(utils.timehuman(round(9050.0)), '02:30:50') + self.assertEqual(integer_to_time(round(0.0 * 1000), + mills=False), '00:00:00') + self.assertEqual(integer_to_time(round(55.0 * 1000), + mills=False), '00:00:55') + self.assertEqual(integer_to_time(round(3023.0 * 1000), + mills=False), '00:50:23') + self.assertEqual(integer_to_time(round(9050.0 * 1000), + mills=False), '02:30:50') def main(): diff --git a/videomass/vdms_utils/utils.py b/videomass/vdms_utils/utils.py index 2ec5c864..7549a5e0 100644 --- a/videomass/vdms_utils/utils.py +++ b/videomass/vdms_utils/utils.py @@ -247,21 +247,6 @@ def time_to_integer(timef: str = '0', sec=False, rnd=False) -> int: # ------------------------------------------------------------------------ -def timehuman(seconds): - """ - This is an old implementation to converting seconds to - time format. Accept integer only e.g timehuman(2300). - Useb by youtube-dl downloader, returns a string object - in time format i.e '00:38:20' . - - """ - minutes, seconds = divmod(seconds, 60) - hours, minutes = divmod(minutes, 60) - # return "%02d:%02d:%02d" % (hours, minutes, seconds) - return f"{hours:02}:{minutes:02}:{seconds:02}" -# ------------------------------------------------------------------------ - - def integer_to_time(integer: int = 0, mills=True, rnd=False) -> str: """ Converts integers to 24-hour clock format calculating in diff --git a/videomass/vdms_ytdlp/youtubedl_ui.py b/videomass/vdms_ytdlp/youtubedl_ui.py index 8126cc48..75d9bbf5 100644 --- a/videomass/vdms_ytdlp/youtubedl_ui.py +++ b/videomass/vdms_ytdlp/youtubedl_ui.py @@ -28,7 +28,7 @@ import wx import wx.lib.scrolledpanel as scrolled from videomass.vdms_io.io_tools import youtubedl_getstatistics -from videomass.vdms_utils.utils import timehuman +from videomass.vdms_utils.utils import integer_to_time as totimesec from videomass.vdms_utils.get_bmpfromsvg import get_bmp from videomass.vdms_ytdlp.playlist_indexing import Indexing from videomass.vdms_ytdlp.subtitles_editor import SubtitleEditor @@ -367,7 +367,7 @@ def get_statistics(self, link): if 'duration' in meta[0]: - ftime = (f"{timehuman(meta[0]['duration'])} " + ftime = (f"{totimesec(round(meta[0]['duration'] * 1000))} " f"({meta[0]['duration']} sec.)") else: ftime = 'N/A'