| |
|
|
| import random |
| import subprocess |
|
|
| from config import ( |
| CLIPS_DIR, |
| TARGET_WIDTH, |
| TARGET_HEIGHT, |
| TARGET_FPS, |
| MIN_CLIP_DURATION, |
| MAX_CLIP_DURATION, |
| MIN_SOURCE_DURATION, |
| VIDEO_CODEC, |
| PRESET, |
| CRF, |
| VARIANT_RULES, |
| ) |
|
|
| from probe import get_duration |
|
|
|
|
| |
| |
| |
|
|
| VARIANT_NAMES = [ |
| "A", "B", "C", "D", "E", |
| "F", "G", "H", "I", "J" |
| ] |
|
|
|
|
| |
| |
| |
|
|
| def random_clip_duration(): |
|
|
| return round( |
| random.uniform( |
| MIN_CLIP_DURATION, |
| MAX_CLIP_DURATION |
| ), |
| 2 |
| ) |
|
|
|
|
| |
| |
| |
|
|
| def get_variant_count(duration): |
|
|
| for min_dur, max_dur, count in VARIANT_RULES: |
|
|
| if min_dur <= duration < max_dur: |
| return count |
|
|
| return 3 |
|
|
|
|
| |
| |
| |
|
|
| def build_dynamic_zones( |
| duration, |
| count |
| ): |
|
|
| zone_size = duration / count |
|
|
| zones = [] |
|
|
| current = 0 |
|
|
| for _ in range(count): |
|
|
| zones.append( |
| ( |
| current, |
| current + zone_size |
| ) |
| ) |
|
|
| current += zone_size |
|
|
| return zones |
|
|
|
|
| |
| |
| |
|
|
| def random_start_in_zone( |
| zone_start, |
| zone_end, |
| clip_duration |
| ): |
|
|
| available = ( |
| zone_end |
| - zone_start |
| - clip_duration |
| ) |
|
|
| if available <= 0: |
| return zone_start |
|
|
| return round( |
| random.uniform( |
| zone_start, |
| zone_start + available |
| ), |
| 2 |
| ) |
|
|
|
|
| |
| |
| |
|
|
| def render_variant( |
| source_file, |
| output_file, |
| start_time, |
| clip_duration, |
| hflip=False |
| ): |
|
|
| filters = [ |
|
|
| ( |
| f"scale={TARGET_WIDTH}:{TARGET_HEIGHT}:" |
| "force_original_aspect_ratio=increase" |
| ), |
|
|
| ( |
| f"crop={TARGET_WIDTH}:{TARGET_HEIGHT}" |
| ), |
|
|
| f"fps={TARGET_FPS}", |
|
|
| "fade=t=in:st=0:d=0.2", |
|
|
| ( |
| f"fade=t=out:" |
| f"st={max(0, clip_duration - 0.2)}:d=0.2" |
| ), |
| ] |
|
|
| if hflip: |
| filters.append("hflip") |
|
|
| vf = ",".join(filters) |
|
|
| cmd = [ |
|
|
| "ffmpeg", |
| "-y", |
|
|
| "-ss", |
| str(start_time), |
|
|
| "-i", |
| str(source_file), |
|
|
| "-t", |
| str(clip_duration), |
|
|
| "-an", |
|
|
| "-vf", |
| vf, |
|
|
| "-c:v", |
| VIDEO_CODEC, |
|
|
| "-preset", |
| PRESET, |
|
|
| "-crf", |
| str(CRF), |
|
|
| "-pix_fmt", |
| "yuv420p", |
|
|
| str(output_file), |
| ] |
|
|
| subprocess.run( |
| cmd, |
| check=True, |
| capture_output=True |
| ) |
|
|
| return str(output_file) |
|
|
|
|
| |
| |
| |
|
|
| def generate_variants( |
| source_file, |
| source_id |
| ): |
|
|
| duration = get_duration( |
| source_file |
| ) |
|
|
| if duration < MIN_SOURCE_DURATION: |
| return [] |
|
|
| variant_count = get_variant_count( |
| duration |
| ) |
|
|
| zones = build_dynamic_zones( |
| duration, |
| variant_count |
| ) |
|
|
| scene_pool = [] |
|
|
| for idx, zone in enumerate(zones): |
|
|
| variant_name = VARIANT_NAMES[idx] |
|
|
| clip_duration = random_clip_duration() |
|
|
| start_time = random_start_in_zone( |
| zone[0], |
| zone[1], |
| clip_duration |
| ) |
|
|
| |
| |
| |
|
|
| hflip = ( |
| (idx + 1) % 3 == 0 |
| ) |
|
|
| output_file = ( |
| CLIPS_DIR |
| / f"{source_id}_{variant_name}.mp4" |
| ) |
|
|
| render_variant( |
| source_file=source_file, |
| output_file=output_file, |
| start_time=start_time, |
| clip_duration=clip_duration, |
| hflip=hflip |
| ) |
|
|
| scene_pool.append( |
|
|
| { |
| "source_id": source_id, |
|
|
| "family": source_id, |
|
|
| "variant": variant_name, |
|
|
| "path": str(output_file), |
|
|
| "duration": clip_duration, |
| } |
|
|
| ) |
|
|
| return scene_pool |
|
|
|
|
| |
| |
| |
|
|
| def generate_many( |
| source_files |
| ): |
|
|
| all_scenes = [] |
|
|
| for idx, source_file in enumerate( |
| source_files, |
| start=1 |
| ): |
|
|
| try: |
|
|
| scenes = generate_variants( |
| source_file, |
| idx |
| ) |
|
|
| all_scenes.extend( |
| scenes |
| ) |
|
|
| print( |
| f"[Variants] " |
| f"Source {idx} " |
| f"-> {len(scenes)} variants" |
| ) |
|
|
| except Exception as e: |
|
|
| print( |
| f"[Variants] Error:" |
| f" {source_file}" |
| ) |
|
|
| print(e) |
|
|
| return all_scenes |
|
|
|
|
| |
| |
| |
|
|
| if __name__ == "__main__": |
|
|
| scenes = generate_variants( |
| "downloads/source_0001.mp4", |
| 1 |
| ) |
|
|
| print() |
|
|
| print( |
| f"Generated: " |
| f"{len(scenes)} scenes" |
| ) |
|
|
| print() |
|
|
| for scene in scenes: |
|
|
| print(scene) |