Skip to content

Commit 9fddad8

Browse files
committed
Split overlong scenes beyond threshold into equal sub-scenes
- Introduce `split_overlong_scenes` to divide lengthy scenes for better processing. - Add `_SecondsTime` class for lightweight time handling in seconds with mock-like behavior. - Update `process_video` to integrate scene splitting and adjust logging/messages accordingly.
1 parent 773d8af commit 9fddad8

File tree

1 file changed

+64
-10
lines changed

1 file changed

+64
-10
lines changed

shorts.py

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,59 @@ def combine_scenes(scene_list: Sequence[Tuple], config: ProcessingConfig) -> Lis
413413
return combined_scene_list
414414

415415

416+
class _SecondsTime:
417+
"""Lightweight stand-in for scene time objects using seconds.
418+
419+
Provides the minimal API used elsewhere: get_seconds(), get_timecode(), get_frames().
420+
Frames are computed assuming 30 fps to keep behavior consistent with tests.
421+
"""
422+
423+
def __init__(self, seconds: float):
424+
self._seconds = float(seconds)
425+
426+
def get_seconds(self) -> float:
427+
return self._seconds
428+
429+
def get_timecode(self) -> str:
430+
# Keep simple representation similar to tests' MockTime
431+
return str(self._seconds)
432+
433+
def get_frames(self) -> int:
434+
return int(self._seconds * 30)
435+
436+
437+
def split_overlong_scenes(combined_scene_list: List[List], config: ProcessingConfig) -> List[List]:
438+
"""Split scenes longer than 4 * max_short_length into n equal parts.
439+
440+
For each scene with duration D > 4 * max_short_length, compute
441+
n = floor(D / (2 * max_short_length)) and split the scene into n
442+
equal sub-scenes. Scenes not exceeding the threshold are kept as is.
443+
"""
444+
445+
result: List[List] = []
446+
threshold = 4 * config.max_short_length
447+
for scene in combined_scene_list:
448+
start_s = scene[0].get_seconds()
449+
end_s = scene[1].get_seconds()
450+
duration = end_s - start_s
451+
452+
if duration > threshold:
453+
n = int(math.floor(duration / (2 * config.max_short_length)))
454+
if n <= 1:
455+
result.append(scene)
456+
continue
457+
458+
part_len = duration / n
459+
for i in range(n):
460+
part_start = start_s + i * part_len
461+
part_end = start_s + (i + 1) * part_len
462+
result.append([_SecondsTime(part_start), _SecondsTime(part_end)])
463+
else:
464+
result.append(scene)
465+
466+
return result
467+
468+
416469
def process_video(video_file: Path, config: ProcessingConfig, output_dir: Path) -> None:
417470
"""Process a single video file and generate short clips."""
418471

@@ -424,14 +477,15 @@ def process_video(video_file: Path, config: ProcessingConfig, output_dir: Path)
424477
logging.info("Computing audio action profile...")
425478
audio_times, audio_score = compute_audio_action_profile(video_file)
426479

427-
combined_scene_list = combine_scenes(scene_list, config)
480+
processed_scene_list = combine_scenes(scene_list, config)
481+
processed_scene_list = split_overlong_scenes(processed_scene_list, config)
428482

429-
logging.info("Combined scenes list with action scores:")
430-
for i, scene in enumerate(combined_scene_list, start=1):
483+
logging.info("Scenes list with action scores:")
484+
for i, scene in enumerate(processed_scene_list, start=1):
431485
duration = scene[1].get_seconds() - scene[0].get_seconds()
432486
score_val = scene_action_score(scene, audio_times, audio_score)
433487
logging.info(
434-
" Combined Scene %2d: Duration %5.1f s, ActionScore %7.3f,"
488+
" Scene %2d: Duration %5.1f s, ActionScore %7.3f,"
435489
" Start %s / Frame %d, End %s / Frame %d",
436490
i,
437491
duration,
@@ -443,14 +497,14 @@ def process_video(video_file: Path, config: ProcessingConfig, output_dir: Path)
443497
)
444498

445499
# Sort by action score, not by length
446-
sorted_combined_scene_list = sorted(
447-
combined_scene_list,
500+
sorted_processed_scene_list = sorted(
501+
processed_scene_list,
448502
key=lambda s: scene_action_score(s, audio_times, audio_score),
449503
reverse=True,
450504
)
451505

452-
logging.info("Sorted combined scenes list (by action score):")
453-
for i, scene in enumerate(sorted_combined_scene_list, start=1):
506+
logging.info("Sorted scenes list (by action score):")
507+
for i, scene in enumerate(sorted_processed_scene_list, start=1):
454508
duration = scene[1].get_seconds() - scene[0].get_seconds()
455509
score_val = scene_action_score(scene, audio_times, audio_score)
456510
logging.info(
@@ -466,9 +520,9 @@ def process_video(video_file: Path, config: ProcessingConfig, output_dir: Path)
466520
)
467521

468522
video_clip = VideoFileClip(str(video_file))
469-
truncated_list = sorted_combined_scene_list[: config.scene_limit]
523+
truncated_list = sorted_processed_scene_list[: config.scene_limit]
470524

471-
logging.info("Truncated sorted combined scenes list:")
525+
logging.info("Truncated sorted scenes list:")
472526
for i, scene in enumerate(truncated_list, start=1):
473527
logging.info(
474528
" Scene %2d: Duration %d Start %s / Frame %d, End %s / Frame %d",

0 commit comments

Comments
 (0)