1inkusFace commited on
Commit
7ec6c48
·
verified ·
1 Parent(s): eaf1248

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -37
app.py CHANGED
@@ -18,30 +18,42 @@ def run_command(command):
18
  def setup_environment():
19
  """Sets up RIFE, downloads weights, and patches libraries."""
20
  print("--- Starting Environment Setup ---")
 
 
21
  if not os.path.exists(RIFE_DIR):
22
  print("Cloning Practical-RIFE...")
23
  run_command(f"git clone https://github.com/hzwer/Practical-RIFE {RIFE_DIR}")
24
 
 
25
  if not os.path.exists(os.path.join(RIFE_DIR, "HDv3")):
26
  print("Downloading RIFE v4.26 model weights...")
27
  zip_path = os.path.join(RIFE_DIR, "RIFEv4.26_0921.zip")
28
  run_command(f"wget -O {zip_path} {MODEL_URL}")
29
  run_command(f"unzip -o {zip_path} -d {RIFE_DIR}")
30
 
31
- # Fix directory structure
32
  train_log_dir = os.path.join(RIFE_DIR, "train_log")
33
  os.makedirs(train_log_dir, exist_ok=True)
34
  extract_folder = os.path.join(RIFE_DIR, "RIFEv4.26_0921")
35
- shutil.move(os.path.join(extract_folder, "RIFE_HDv3.py"), train_log_dir)
36
- shutil.move(os.path.join(extract_folder, "IFNet_HDv3.py"), train_log_dir)
 
 
 
 
 
37
  with open(os.path.join(train_log_dir, "__init__.py"), 'w') as f: pass
 
38
  hdv3_dir = os.path.join(RIFE_DIR, "HDv3")
39
  os.makedirs(hdv3_dir, exist_ok=True)
40
- shutil.move(os.path.join(extract_folder, "flownet.pkl"), hdv3_dir)
 
 
 
41
  shutil.rmtree(extract_folder)
42
  if os.path.exists(zip_path): os.remove(zip_path)
43
 
44
- # Patch skvideo
45
  try:
46
  spec = importlib.util.find_spec('skvideo.io.abstract')
47
  if spec and spec.origin:
@@ -52,13 +64,14 @@ def setup_environment():
52
  except Exception as e:
53
  print(f"Warning: skvideo patch failed: {e}")
54
 
55
- # Patch RIFE inference for libx264
56
  inference_script = os.path.join(RIFE_DIR, "inference_video.py")
57
  if os.path.exists(inference_script):
58
  with open(inference_script, 'r') as f: content = f.read()
59
  if "libx264" not in content:
60
  new_content = content.replace("-c:v', 'mpeg4', '-qscale:v', '1'", "-c:v', 'libx264', '-preset', 'medium', '-crf', '23'")
61
  with open(inference_script, 'w') as f: f.write(new_content)
 
62
  print("--- Setup Complete ---")
63
 
64
  setup_environment()
@@ -66,24 +79,49 @@ setup_environment()
66
  @spaces.GPU(required=True)
67
  def interpolate_video(input_video_path, multi_factor):
68
  if input_video_path is None: return None
 
69
  factor = str(multi_factor).replace("x", "").strip()
70
  output_path = os.path.join(BASE_DIR, "output_rife.mp4")
71
  final_output_path = os.path.join(BASE_DIR, "final_interpolated.mp4")
72
 
 
73
  if os.path.exists(output_path): os.remove(output_path)
74
  if os.path.exists(final_output_path): os.remove(final_output_path)
75
 
 
 
 
 
 
76
  os.chdir(RIFE_DIR)
77
  try:
 
78
  cmd = ['python3', 'inference_video.py', '--video', input_video_path, '--output', output_path, '--multi', factor, '--model', 'HDv3']
79
  subprocess.run(cmd, check=True)
80
 
 
 
 
 
 
 
81
  if os.path.exists(output_path):
82
- # Re-encode for web compatibility
83
- subprocess.run(['ffmpeg', '-i', output_path, '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', '-y', final_output_path], check=True)
84
- return final_output_path
 
 
 
 
 
 
 
 
 
 
 
85
  except Exception as e:
86
- print(f"Error: {e}")
87
  return None
88
  finally:
89
  os.chdir(BASE_DIR)
@@ -92,13 +130,11 @@ def interpolate_video(input_video_path, multi_factor):
92
  def stitch_videos(video_files, resolution_choice):
93
  if not video_files: return None
94
 
95
- # Parse resolution (e.g., "1024x1024" -> w=1024, h=1024)
96
  try:
97
  target_w, target_h = resolution_choice.split("x")
98
- target_w = target_w.strip()
99
- target_h = target_h.strip()
100
  except:
101
- # Fallback default
102
  target_w, target_h = "1920", "1080"
103
 
104
  print(f"Stitching {len(video_files)} videos into {target_w}x{target_h}...")
@@ -110,30 +146,27 @@ def stitch_videos(video_files, resolution_choice):
110
  if os.path.exists(temp_dir): shutil.rmtree(temp_dir)
111
  os.makedirs(temp_dir)
112
 
113
- # 1. Normalize Step: Convert all inputs to same Codec, FPS (60), and User-Selected Scale
114
  normalized_files = []
115
  for i, vid_path in enumerate(video_files):
116
  temp_filepath = os.path.join(temp_dir, f"norm_{i}.mp4")
117
- print(f"Normalizing clip {i+1}...")
118
 
119
- # FFmpeg Filter Breakdown:
120
- # scale=W:H:force_original_aspect_ratio=decrease -> Resize video to FIT inside the box
121
- # pad=W:H:(ow-iw)/2:(oh-ih)/2 -> Create the box and center the video (adding black bars)
122
  scale_filter = f"scale={target_w}:{target_h}:force_original_aspect_ratio=decrease,pad={target_w}:{target_h}:(ow-iw)/2:(oh-ih)/2"
123
 
124
  cmd = [
125
  'ffmpeg', '-i', vid_path,
126
- '-r', '60', # Force Framerate to 60
127
- '-vf', scale_filter, # Apply Dynamic Scaling
128
  '-c:v', 'libx264', '-crf', '23',
129
- '-c:a', 'aac', # Force Audio Codec
130
- '-ar', '44100', # Force Audio Rate
131
  '-y', temp_filepath
132
  ]
133
  subprocess.run(cmd, check=True)
134
  normalized_files.append(temp_filepath)
135
 
136
- # 2. Create List File
137
  with open(stitch_list_path, 'w') as f:
138
  for path in normalized_files:
139
  f.write(f"file '{path}'\n")
@@ -142,7 +175,6 @@ def stitch_videos(video_files, resolution_choice):
142
  print("Concatenating...")
143
  subprocess.run(['ffmpeg', '-f', 'concat', '-safe', '0', '-i', stitch_list_path, '-c', 'copy', '-y', output_stitched], check=True)
144
 
145
- # Cleanup
146
  shutil.rmtree(temp_dir)
147
  return output_stitched
148
 
@@ -153,11 +185,11 @@ with gr.Blocks() as demo:
153
  with gr.Tabs():
154
  # TAB 1: Interpolate
155
  with gr.TabItem("1. Smooth Video (Interpolate)"):
156
- gr.Markdown("Upload a single video to increase its framerate (make it smoother).")
157
  with gr.Row():
158
  with gr.Column():
159
  video_input = gr.Video(label="Input Video")
160
- multi_select = gr.Dropdown(["2", "4", "8"], value="2", label="Multiplier (2x, 4x, etc.)")
161
  interp_btn = gr.Button("Interpolate", variant="primary")
162
  with gr.Column():
163
  video_output = gr.Video(label="Smoothed Output")
@@ -165,23 +197,15 @@ with gr.Blocks() as demo:
165
 
166
  # TAB 2: Stitch
167
  with gr.TabItem("2. Stitch Videos"):
168
- gr.Markdown("Upload multiple videos. They will be normalized to **60fps** and your selected resolution.")
169
  with gr.Row():
170
  with gr.Column():
171
  stitch_inputs = gr.File(label="Upload Clips", file_count="multiple")
172
-
173
- # New Resolution Dropdown
174
- res_select = gr.Dropdown(
175
- choices=["1920x1080", "1280x1280", "1024x1024"],
176
- value="1920x1080",
177
- label="Output Resolution"
178
- )
179
-
180
  stitch_btn = gr.Button("Stitch Videos", variant="primary")
181
  with gr.Column():
182
  stitch_output = gr.Video(label="Stitched Result")
183
 
184
- # Updated click event passing the resolution choice
185
  stitch_btn.click(stitch_videos, inputs=[stitch_inputs, res_select], outputs=stitch_output)
186
 
187
  demo.launch()
 
18
  def setup_environment():
19
  """Sets up RIFE, downloads weights, and patches libraries."""
20
  print("--- Starting Environment Setup ---")
21
+
22
+ # 1. Clone Repo
23
  if not os.path.exists(RIFE_DIR):
24
  print("Cloning Practical-RIFE...")
25
  run_command(f"git clone https://github.com/hzwer/Practical-RIFE {RIFE_DIR}")
26
 
27
+ # 2. Download Weights
28
  if not os.path.exists(os.path.join(RIFE_DIR, "HDv3")):
29
  print("Downloading RIFE v4.26 model weights...")
30
  zip_path = os.path.join(RIFE_DIR, "RIFEv4.26_0921.zip")
31
  run_command(f"wget -O {zip_path} {MODEL_URL}")
32
  run_command(f"unzip -o {zip_path} -d {RIFE_DIR}")
33
 
34
+ # Fix directory structure (User Fix)
35
  train_log_dir = os.path.join(RIFE_DIR, "train_log")
36
  os.makedirs(train_log_dir, exist_ok=True)
37
  extract_folder = os.path.join(RIFE_DIR, "RIFEv4.26_0921")
38
+
39
+ # Move files safely
40
+ if os.path.exists(os.path.join(extract_folder, "RIFE_HDv3.py")):
41
+ shutil.move(os.path.join(extract_folder, "RIFE_HDv3.py"), train_log_dir)
42
+ if os.path.exists(os.path.join(extract_folder, "IFNet_HDv3.py")):
43
+ shutil.move(os.path.join(extract_folder, "IFNet_HDv3.py"), train_log_dir)
44
+
45
  with open(os.path.join(train_log_dir, "__init__.py"), 'w') as f: pass
46
+
47
  hdv3_dir = os.path.join(RIFE_DIR, "HDv3")
48
  os.makedirs(hdv3_dir, exist_ok=True)
49
+
50
+ if os.path.exists(os.path.join(extract_folder, "flownet.pkl")):
51
+ shutil.move(os.path.join(extract_folder, "flownet.pkl"), hdv3_dir)
52
+
53
  shutil.rmtree(extract_folder)
54
  if os.path.exists(zip_path): os.remove(zip_path)
55
 
56
+ # 3. Patch skvideo (Numpy Fix)
57
  try:
58
  spec = importlib.util.find_spec('skvideo.io.abstract')
59
  if spec and spec.origin:
 
64
  except Exception as e:
65
  print(f"Warning: skvideo patch failed: {e}")
66
 
67
+ # 4. Patch RIFE inference for libx264
68
  inference_script = os.path.join(RIFE_DIR, "inference_video.py")
69
  if os.path.exists(inference_script):
70
  with open(inference_script, 'r') as f: content = f.read()
71
  if "libx264" not in content:
72
  new_content = content.replace("-c:v', 'mpeg4', '-qscale:v', '1'", "-c:v', 'libx264', '-preset', 'medium', '-crf', '23'")
73
  with open(inference_script, 'w') as f: f.write(new_content)
74
+
75
  print("--- Setup Complete ---")
76
 
77
  setup_environment()
 
79
  @spaces.GPU(required=True)
80
  def interpolate_video(input_video_path, multi_factor):
81
  if input_video_path is None: return None
82
+
83
  factor = str(multi_factor).replace("x", "").strip()
84
  output_path = os.path.join(BASE_DIR, "output_rife.mp4")
85
  final_output_path = os.path.join(BASE_DIR, "final_interpolated.mp4")
86
 
87
+ # Clean previous runs
88
  if os.path.exists(output_path): os.remove(output_path)
89
  if os.path.exists(final_output_path): os.remove(final_output_path)
90
 
91
+ # RIFE script creates a specific output name if audio transfer fails
92
+ # It often appends _noaudio, so we must watch for that.
93
+ expected_no_audio = output_path.replace(".mp4", "_noaudio.mp4")
94
+ if os.path.exists(expected_no_audio): os.remove(expected_no_audio)
95
+
96
  os.chdir(RIFE_DIR)
97
  try:
98
+ print(f"Running RIFE with {factor}x on {input_video_path}")
99
  cmd = ['python3', 'inference_video.py', '--video', input_video_path, '--output', output_path, '--multi', factor, '--model', 'HDv3']
100
  subprocess.run(cmd, check=True)
101
 
102
+ # --- Logic to handle the file finding ---
103
+ # If moviepy fails, RIFE creates 'output_rife.mp4' OR 'output_rife_noaudio.mp4'
104
+ # depending on where it crashed. We check for both.
105
+
106
+ source_to_encode = None
107
+
108
  if os.path.exists(output_path):
109
+ source_to_encode = output_path
110
+ elif os.path.exists(expected_no_audio):
111
+ source_to_encode = expected_no_audio
112
+ print("Audio transfer failed inside RIFE, using no-audio version.")
113
+ else:
114
+ print("Error: Output video file not found after inference.")
115
+ return None
116
+
117
+ # Re-encode for web compatibility
118
+ print(f"Re-encoding {source_to_encode} to {final_output_path}...")
119
+ subprocess.run(['ffmpeg', '-i', source_to_encode, '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', '-y', final_output_path], check=True)
120
+
121
+ return final_output_path
122
+
123
  except Exception as e:
124
+ print(f"Error during interpolation: {e}")
125
  return None
126
  finally:
127
  os.chdir(BASE_DIR)
 
130
  def stitch_videos(video_files, resolution_choice):
131
  if not video_files: return None
132
 
133
+ # Parse resolution
134
  try:
135
  target_w, target_h = resolution_choice.split("x")
136
+ target_w, target_h = target_w.strip(), target_h.strip()
 
137
  except:
 
138
  target_w, target_h = "1920", "1080"
139
 
140
  print(f"Stitching {len(video_files)} videos into {target_w}x{target_h}...")
 
146
  if os.path.exists(temp_dir): shutil.rmtree(temp_dir)
147
  os.makedirs(temp_dir)
148
 
149
+ # 1. Normalize
150
  normalized_files = []
151
  for i, vid_path in enumerate(video_files):
152
  temp_filepath = os.path.join(temp_dir, f"norm_{i}.mp4")
 
153
 
154
+ # Scale and Pad Filter
 
 
155
  scale_filter = f"scale={target_w}:{target_h}:force_original_aspect_ratio=decrease,pad={target_w}:{target_h}:(ow-iw)/2:(oh-ih)/2"
156
 
157
  cmd = [
158
  'ffmpeg', '-i', vid_path,
159
+ '-r', '60',
160
+ '-vf', scale_filter,
161
  '-c:v', 'libx264', '-crf', '23',
162
+ '-c:a', 'aac',
163
+ '-ar', '44100',
164
  '-y', temp_filepath
165
  ]
166
  subprocess.run(cmd, check=True)
167
  normalized_files.append(temp_filepath)
168
 
169
+ # 2. Create List
170
  with open(stitch_list_path, 'w') as f:
171
  for path in normalized_files:
172
  f.write(f"file '{path}'\n")
 
175
  print("Concatenating...")
176
  subprocess.run(['ffmpeg', '-f', 'concat', '-safe', '0', '-i', stitch_list_path, '-c', 'copy', '-y', output_stitched], check=True)
177
 
 
178
  shutil.rmtree(temp_dir)
179
  return output_stitched
180
 
 
185
  with gr.Tabs():
186
  # TAB 1: Interpolate
187
  with gr.TabItem("1. Smooth Video (Interpolate)"):
188
+ gr.Markdown("Upload a single video to increase its framerate.")
189
  with gr.Row():
190
  with gr.Column():
191
  video_input = gr.Video(label="Input Video")
192
+ multi_select = gr.Dropdown(["2", "4", "8"], value="2", label="Multiplier")
193
  interp_btn = gr.Button("Interpolate", variant="primary")
194
  with gr.Column():
195
  video_output = gr.Video(label="Smoothed Output")
 
197
 
198
  # TAB 2: Stitch
199
  with gr.TabItem("2. Stitch Videos"):
200
+ gr.Markdown("Upload multiple videos. They will be normalized to **60fps**.")
201
  with gr.Row():
202
  with gr.Column():
203
  stitch_inputs = gr.File(label="Upload Clips", file_count="multiple")
204
+ res_select = gr.Dropdown(choices=["1920x1080", "1280x1280", "1024x1024"], value="1920x1080", label="Resolution")
 
 
 
 
 
 
 
205
  stitch_btn = gr.Button("Stitch Videos", variant="primary")
206
  with gr.Column():
207
  stitch_output = gr.Video(label="Stitched Result")
208
 
 
209
  stitch_btn.click(stitch_videos, inputs=[stitch_inputs, res_select], outputs=stitch_output)
210
 
211
  demo.launch()