Skip to content

Commit

Permalink
Merge pull request #62 from pozitronik/issue_61
Browse files Browse the repository at this point in the history
Issue 61
  • Loading branch information
pozitronik committed Aug 24, 2023
2 parents e234ebd + d6f00df commit beef34a
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 8 deletions.
20 changes: 18 additions & 2 deletions sinner/State.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sinner.Status import Status, Mood
from sinner.handlers.frame.CV2VideoHandler import CV2VideoHandler
from sinner.typing import Frame
from sinner.utilities import is_absolute_path
from sinner.utilities import is_absolute_path, format_sequences
from sinner.validators.AttributeLoader import Rules


Expand All @@ -24,6 +24,7 @@ class State(Status):

final_check_state: bool = True
final_check_empty: bool = True
final_check_integrity: bool = True

def rules(self) -> Rules:
return [
Expand Down Expand Up @@ -147,7 +148,7 @@ def zfill_length(self) -> int:
self._zfill_length = len(str(self.frames_count))
return self._zfill_length

def final_check(self) -> bool:
def final_check(self) -> tuple[bool, List[int]]:
result = True
processed_frames_count = self.processed_frames_count
if self.final_check_state and not self.is_finished:
Expand All @@ -162,4 +163,19 @@ def final_check(self) -> bool:
if zero_sized_files_count > 0:
self.update_status(message=f"There is zero-sized files in {self.path} temp directory ({zero_sized_files_count} of {processed_frames_count}). Check for free disk space and access rights.", mood=Mood.BAD)
result = False
lost_frames = []
if self.final_check_integrity and not self.is_finished:
lost_frames = self.check_integrity()
if lost_frames:
self.update_status(message=f"There is lost frames in the processed sequence: {format_sequences(lost_frames)}", mood=Mood.BAD)
result = False

return result, lost_frames

def check_integrity(self) -> List[int]:
result: List[int] = []
for frame in range(self.frames_count):
f_name = self.get_frame_processed_name(frame)
if not os.path.exists(f_name):
result.append(frame)
return result
12 changes: 11 additions & 1 deletion sinner/processors/frame/BaseFrameProcessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,17 @@ def process(self, desc: str = 'Processing', set_progress: Callable[[int], None]
initial=self.state.processed_frames_count,
) as progress:
self.multi_process_frame(frames_iterator=self.handler, state=self.state, process_frames=self.process_frames, progress=progress)
if not self.state.final_check():
_, lost_frames = self.state.final_check()
if lost_frames:
with tqdm(
total=len(lost_frames),
desc="Processing lost frames", unit='frame',
dynamic_ncols=True,
bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]',
) as progress:
self.multi_process_frame(frames_iterator=lost_frames, state=self.state, process_frames=self.process_frames, progress=progress)
is_ok, _ = self.state.final_check()
if not is_ok:
raise Exception("Something went wrong on processed frames check")

@abstractmethod
Expand Down
18 changes: 18 additions & 0 deletions sinner/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,21 @@ def is_int(value: str) -> bool:
return True
except ValueError:
return False


def format_sequences(sorted_list: List[int]) -> str:
def format_sequence(s_start: int, s_end: int) -> str:
return str(start) if s_start == s_end else f"{start}..{end}"

sequences = []
start = end = sorted_list[0]

for num in sorted_list[1:]:
if num == end + 1:
end = num
else:
sequences.append(format_sequence(start, end))
start = end = num

sequences.append(format_sequence(start, end))
return ", ".join(sequences)
18 changes: 17 additions & 1 deletion tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from sinner.Parameters import Parameters
from sinner.Core import Core
from sinner.utilities import limit_resources, suggest_max_memory, get_file_name
from sinner.processors.frame.DummyProcessor import DummyProcessor
from sinner.utilities import limit_resources, suggest_max_memory, get_file_name, get_app_dir, resolve_relative_path
from sinner.validators.LoaderException import LoadingException
from tests.constants import target_png, source_jpg, target_mp4, source_target_png_result, source_target_mp4_result, state_frames_dir, result_mp4, tmp_dir, result_png, TARGET_FC, images_dir, source_images_result

Expand Down Expand Up @@ -140,3 +141,18 @@ def test_set_execution_provider(capsys) -> None:
captured = capsys.readouterr()
assert "Error Unknown Provider Type" not in captured.out
assert os.path.exists(result_png) is True


def test_reprocess_lost_frames() -> None:
case_temp_dir = resolve_relative_path('temp/DummyProcessor/frames/source.jpg', get_app_dir())
assert os.path.exists(case_temp_dir) is False
params = Parameters(f'--target-path="{state_frames_dir}" --source-path="{source_jpg}" --output-path="{result_mp4}" --execution-treads={threads_count}')
current_processor = DummyProcessor(params.parameters)
current_processor.process()
assert os.path.exists(case_temp_dir) is True
assert len(glob.glob(os.path.join(case_temp_dir, '*.png'))) == 10
os.remove(os.path.join(case_temp_dir, '05.png'))
os.remove(os.path.join(case_temp_dir, '08.png'))
assert len(glob.glob(os.path.join(case_temp_dir, '*.png'))) == 8
current_processor.process()
assert len(glob.glob(os.path.join(case_temp_dir, '*.png'))) == 10
6 changes: 3 additions & 3 deletions tests/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,19 @@ def test_states() -> None:
def test_final_check_ok():
state = State(parameters=Namespace(), target_path=target_mp4, temp_dir=tmp_dir, frames_count=TARGET_FC, processor_name='DummyProcessor')
shutil.copytree(state_frames_dir, state.path, dirs_exist_ok=True)
assert state.final_check() is True
assert state.final_check() == (True, [])


def test_final_check_fail_state():
state = State(parameters=Namespace(), target_path=target_mp4, temp_dir=tmp_dir, frames_count=TARGET_FC, processor_name='DummyProcessor')
shutil.copytree(state_frames_dir, state.path, dirs_exist_ok=True)
os.remove(os.path.join(state.path, '05.png'))
assert state.final_check() is False
assert state.final_check() == (False, [5])


def test_final_check_fail_zero_files():
state = State(parameters=Namespace(), target_path=target_mp4, temp_dir=tmp_dir, frames_count=TARGET_FC, processor_name='DummyProcessor')
shutil.copytree(state_frames_dir, state.path, dirs_exist_ok=True)
with open(os.path.join(state.path, '04.png'), 'r+') as file:
file.truncate(0)
assert state.final_check() is False
assert state.final_check() == (False, [])
7 changes: 6 additions & 1 deletion tests/test_utilities.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from sinner.utilities import get_all_base_names
from sinner.utilities import get_all_base_names, format_sequences


def test_get_all_base_names() -> None:
Expand Down Expand Up @@ -36,3 +36,8 @@ class F(D):
assert 'B' in get_all_base_names(F)
assert 'E' not in get_all_base_names(F)
assert 'C' not in get_all_base_names(F)


def test_find_sequences() -> None:
assert format_sequences([1, 2, 3, 4, 10, 20, 21, 22, 23]) == '1..4, 10, 20..23'
assert format_sequences([100, 3, 2, 3, 4, 5]) == '100, 3, 2..5'

0 comments on commit beef34a

Please sign in to comment.