Spaces:
Running
Running
File size: 9,333 Bytes
f7598da |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
import os
import json
import shutil
import zipfile
from .utils import json_to_srt, get_video_dims
from .face_detection import detect_faces_jit
from .rendering import render_segmented_overlays
from .xml_generator import create_premiere_xml
def export_pack(project_path, segment_index, output_format="premiere"):
"""
Generates a ZIP Pack for the segment.
"""
print(f"Starting Export Pack for Project: {os.path.basename(project_path)}, Segment: {segment_index}")
# Paths
proj_name = os.path.basename(project_path)
cut_dir = os.path.join(project_path, "cuts")
# 1. IDENTIFY VIDEO FILE
video_file = None
original_scale_file = None
if os.path.exists(cut_dir):
files = os.listdir(cut_dir)
# Search for {index}_..._original_scale.mp4 or similar
prefix_idx = f"{segment_index:03d}_"
for f in files:
if f.startswith(prefix_idx) and (f.endswith(".mp4") or f.endswith(".mov")):
video_file = os.path.join(cut_dir, f)
break
if not video_file:
print(f"Error: No video file found for segment {segment_index} in {cut_dir}")
return
print(f"Selected Video: {video_file}")
# 2. IDENTIFY SUBTITLE FILES
subs_dir = os.path.join(project_path, "subs_ass")
ass_file = None
if os.path.exists(subs_dir):
sub_files = os.listdir(subs_dir)
prefix_idx = f"{segment_index:03d}_"
# Prioritize Clean Processed > Processed > Any
patterns = [
(lambda f: f.endswith(".ass") and f.startswith(prefix_idx) and "processed" in f and "original" not in f),
(lambda f: f.endswith(".ass") and f.startswith(prefix_idx) and "processed" in f),
(lambda f: f.endswith(".ass") and f.startswith(prefix_idx))
]
for p in patterns:
if ass_file: break
for f in sub_files:
if p(f):
ass_file = os.path.join(subs_dir, f)
break
# JSON in 'subs' usually
subs_json_dir = os.path.join(project_path, "subs")
json_file = None
if os.path.exists(subs_json_dir):
sub_files = os.listdir(subs_json_dir)
prefix_idx = f"{segment_index:03d}_"
# Same pattern priority
json_patterns = [
(lambda f: f.endswith(".json") and f.startswith(prefix_idx) and "processed" in f),
(lambda f: f.endswith(".json") and f.startswith(prefix_idx))
]
for p in json_patterns:
if json_file: break
for f in sub_files:
if p(f):
json_file = os.path.join(subs_json_dir, f)
break
# 2.1 IDENTIFY FACE COORDS
final_dir = os.path.join(project_path, "final")
face_data = None
if os.path.exists(final_dir):
final_files = os.listdir(final_dir)
prefix_idx = f"{segment_index:03d}_"
for f in final_files:
if f.startswith(prefix_idx) and f.endswith("_coords.json"):
try:
with open(os.path.join(final_dir, f), 'r') as fd:
face_data = json.load(fd)
print(f"Found Face Coordinates: {f}")
except Exception as e:
print(f"Face coords load error: {e}")
break
if face_data is None:
print("No pre-computed face data found. Attempting JIT detection...")
face_data = detect_faces_jit(video_file)
# 3. PREPARE STAGING
export_name = f"export_{proj_name}_seg{segment_index}"
stage_dir = os.path.join(project_path, export_name)
if os.path.exists(stage_dir):
try:
shutil.rmtree(stage_dir)
except Exception:
import random
stage_dir += f"_{random.randint(1000,9999)}"
os.makedirs(stage_dir, exist_ok=True)
# 4. COPY VIDEO (Prefer Original Scale for XML editing)
source_video_to_copy = video_file
dest_filename = "video_cut.mp4"
# Try to find original scale version in 'cuts' folder
# video_file is usually in 'cuts', lets check there
try:
cuts_dir = os.path.dirname(video_file)
# Attempt 1: Direct suffix replacement
original_scale_candidate = video_file.replace(".mp4", "_original_scale.mp4")
if not os.path.exists(original_scale_candidate):
# Attempt 2: Search by prefix
prefix_idx = f"{segment_index:03d}_"
if os.path.exists(cuts_dir):
for f in os.listdir(cuts_dir):
if f.startswith(prefix_idx) and "original_scale" in f and f.endswith(".mp4"):
original_scale_candidate = os.path.join(cuts_dir, f)
break
if os.path.exists(original_scale_candidate):
print(f"Using Original Scale Source for Export: {original_scale_candidate}")
source_video_to_copy = original_scale_candidate
dest_filename = "video_source.mp4" # Distinct name
except Exception as e:
print(f"Error checking for original scale video: {e}")
dest_video = os.path.join(stage_dir, dest_filename)
shutil.copy2(source_video_to_copy, dest_video)
# 5. RENDER OVERLAYS (SEGMENTED)
overlay_segments = []
if ass_file and json_file:
try:
with open(json_file, 'r', encoding='utf-8') as f:
jdata = json.load(f)
# Extract segment list
jdata_segs = []
if isinstance(jdata, dict) and "segments" in jdata:
jdata_segs = jdata["segments"]
elif isinstance(jdata, list):
jdata_segs = jdata
if jdata_segs:
# Create 'captions' subfolder for organization
captions_dir = os.path.join(stage_dir, "captions")
os.makedirs(captions_dir, exist_ok=True)
# Render into subfolder
overlay_segments = render_segmented_overlays(ass_file, jdata_segs, video_file, captions_dir)
except Exception as e:
print(f"Error preparing overlay segments: {e}")
else:
print("Missing ASS or JSON for subtitles. Skipping overlays.")
# 6. GENERATE SRT (Standard)
dest_srt = os.path.join(stage_dir, f"{proj_name}_Seg{segment_index}.srt")
if json_file:
try:
with open(json_file, 'r', encoding='utf-8') as f:
jdata_srt = json.load(f)
if isinstance(jdata_srt, dict) and "segments" in jdata_srt:
jdata_srt = jdata_srt["segments"]
srt_content = json_to_srt(jdata_srt)
with open(dest_srt, 'w', encoding='utf-8') as f:
f.write(srt_content)
except Exception: pass
# 7. GENERATE XML
width_src, height_src, frames, fps = get_video_dims(dest_video)
# Validation for resolution mismatch (same as before)
if face_data:
max_x = 0
for entry in face_data:
for f in entry.get('faces', []):
if len(f) >= 3 and f[2] > max_x: max_x = f[2]
if max_x > width_src:
print(f"Correction: Detecting 4K source based on face coords ({max_x} > {width_src})")
width_src = 3840
height_src = 2160
# 6. XML GENERATION
width, height, duration, fps = get_video_dims(video_file)
print(f"DEBUG: Passing face_data to XML: {len(face_data) if face_data else 'None'}")
# Logic to Determine Sequence Resolution
# Default 1080p Vertical
seq_w = 1080
seq_h = 1920
# If source is 4K (Width > 2000 or Height > 2000), upgrade to 4K Vertical
# Note: width_src from 'get_video_dims' usually returns width.
# Normal 4K is 3840x2160.
if width_src > 3000 or height_src > 3000:
print("Detected 4K Source Content. Setting Sequence to 4K Vertical (2160x3840).")
seq_w = 2160
seq_h = 3840
else:
print("Source is 1080p or lower. Setting Sequence to 1080p Vertical (1080x1920).")
xml_content = create_premiere_xml(
project_name=proj_name,
video_path=dest_video,
overlay_segments=overlay_segments,
duration_frames=duration,
width=seq_w,
height=seq_h,
timebase=int(fps),
scale_value=100.0,
face_data=face_data,
source_width=width_src,
source_height=height_src
)
xml_output = os.path.join(stage_dir, "timeline.xml")
with open(xml_output, "w", encoding="utf-8") as f:
f.write(xml_content)
print("Generated Custom Premiere XML (Opus-Style Segments).")
# 8. ZIP IT
zip_path = f"{stage_dir}.zip"
shutil.make_archive(stage_dir, 'zip', stage_dir)
print(f"SUCCESS: Export Pack created at {zip_path}")
# Cleanup
try:
# shutil.rmtree(stage_dir)
pass
except: pass
return zip_path
|