Hug0endob commited on
Commit
4a6e28f
·
verified ·
1 Parent(s): e038d6a

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +53 -17
streamlit_app.py CHANGED
@@ -69,26 +69,49 @@ def _file_sha256(path: Path) -> Optional[str]:
69
 
70
 
71
  def _convert_to_mp4(src: Path) -> Path:
72
- # Use a more robust way to generate the destination name, preserving original stem
73
- dst = src.parent / f"{src.stem}.mp4"
74
- if dst.exists() and dst.stat().st_size > 0: # Check if already converted and not empty
75
- src.unlink(missing_ok=True) # Remove source if conversion already exists
 
 
 
 
 
 
 
 
 
 
 
 
76
  return dst
 
 
77
  try:
78
- ffmpeg.input(str(src)).output(str(dst)).overwrite_output().run(
 
 
79
  capture_stdout=True, capture_stderr=True
80
  )
 
 
81
  except ffmpeg.Error as e:
82
- # Include stderr in the error message for better debugging
 
83
  error_msg = e.stderr.decode()
84
  raise RuntimeError(f"ffmpeg conversion failed for {src.name}: {error_msg}") from e
 
 
 
85
 
 
86
  if dst.exists() and dst.stat().st_size > 0:
87
- src.unlink() # Only unlink if conversion was successful and resulted in a non-empty file
 
 
88
  else:
89
- # If conversion failed silently (no error but no output), raise a specific error
90
- raise RuntimeError(f"ffmpeg conversion for {src.name} produced an empty or missing MP4 file.")
91
- return dst
92
 
93
 
94
  def _compress_video(inp: Path, crf: int = 28, preset: str = "fast") -> Path:
@@ -106,6 +129,10 @@ def _compress_video(inp: Path, crf: int = 28, preset: str = "fast") -> Path:
106
 
107
 
108
  def _maybe_compress(path: Path, limit_mb: int) -> Tuple[Path, bool]:
 
 
 
 
109
  size_mb = path.stat().st_size / (1024 * 1024)
110
  if size_mb <= limit_mb:
111
  return path, False
@@ -140,6 +167,10 @@ def _download_direct(url: str, dst: Path) -> Path:
140
  for chunk in r.iter_content(chunk_size=8192):
141
  if chunk:
142
  f.write(chunk)
 
 
 
 
143
  return out_path
144
 
145
 
@@ -202,9 +233,13 @@ def _download_with_yt_dlp(url: str, dst: Path, password: str = "") -> Path:
202
  if downloaded_file is None:
203
  # yt_dlp might move/rename files, so checking `info['_filename']` is reliable
204
  downloaded_file = Path(info.get('_filename', ''))
205
- # If it's still not an MP4, try to convert it
206
- if downloaded_file.suffix.lower() != ".mp4":
207
- downloaded_file = _convert_to_mp4(downloaded_file)
 
 
 
 
208
 
209
  finally:
210
  bar.empty()
@@ -212,9 +247,8 @@ def _download_with_yt_dlp(url: str, dst: Path, password: str = "") -> Path:
212
 
213
  if downloaded_file and downloaded_file.exists() and downloaded_file.stat().st_size > 0:
214
  # Ensure it's an MP4, even if yt_dlp hook didn't catch final MP4 name
215
- if downloaded_file.suffix.lower() != ".mp4":
216
- return _convert_to_mp4(downloaded_file)
217
- return downloaded_file
218
 
219
  # ---------- Fallback: direct HTTP download ----------
220
  st.warning("yt-dlp failed or did not produce an MP4, attempting direct download.")
@@ -509,7 +543,8 @@ def main() -> None:
509
  raw_path = download_video(
510
  st.session_state["url"], DATA_DIR, st.session_state["video_password"]
511
  )
512
- mp4_path = _convert_to_mp4(Path(raw_path)) # Ensure it's MP4
 
513
  st.session_state["video_path"], was_compressed = _maybe_compress(mp4_path, st.session_state["compress_mb"])
514
  if was_compressed:
515
  st.toast("Video downloaded and compressed.")
@@ -521,6 +556,7 @@ def main() -> None:
521
  RuntimeError,
522
  requests.exceptions.RequestException,
523
  yt_dlp.utils.DownloadError,
 
524
  ) as e:
525
  st.session_state["last_error"] = f"Download failed: {e}"
526
  st.session_state["last_error_detail"] = traceback.format_exc()
 
69
 
70
 
71
  def _convert_to_mp4(src: Path) -> Path:
72
+ # 1. Check if source file exists and is not empty
73
+ if not src.exists():
74
+ raise FileNotFoundError(f"Source file '{src.name}' for MP4 conversion not found.")
75
+ if src.stat().st_size == 0:
76
+ raise ValueError(f"Source file '{src.name}' for MP4 conversion is empty.")
77
+
78
+ # 2. Determine destination path. If source is already MP4, destination is source.
79
+ if src.suffix.lower() == ".mp4":
80
+ dst = src
81
+ else:
82
+ dst = src.parent / f"{src.stem}.mp4"
83
+
84
+ # 3. If destination already exists and is non-empty, assume conversion is done or it was already MP4.
85
+ if dst.exists() and dst.stat().st_size > 0:
86
+ if src != dst: # Only unlink if src is a different, non-MP4 file that was converted
87
+ src.unlink(missing_ok=True)
88
  return dst
89
+
90
+ # 4. Perform conversion if necessary
91
  try:
92
+ # Use a temporary file for output to prevent partial files if conversion fails
93
+ temp_dst = dst.with_suffix(".mp4.tmp")
94
+ ffmpeg.input(str(src)).output(str(temp_dst)).overwrite_output().run(
95
  capture_stdout=True, capture_stderr=True
96
  )
97
+ # If successful, rename temp file to final destination
98
+ temp_dst.rename(dst)
99
  except ffmpeg.Error as e:
100
+ # Ensure temp file is cleaned up on error
101
+ temp_dst.unlink(missing_ok=True)
102
  error_msg = e.stderr.decode()
103
  raise RuntimeError(f"ffmpeg conversion failed for {src.name}: {error_msg}") from e
104
+ except Exception as e: # Catch other potential errors during ffmpeg setup or execution
105
+ temp_dst.unlink(missing_ok=True)
106
+ raise RuntimeError(f"An unexpected error occurred during ffmpeg conversion for {src.name}: {e}") from e
107
 
108
+ # 5. Final check and cleanup
109
  if dst.exists() and dst.stat().st_size > 0:
110
+ if src != dst: # Only unlink the original file if it was converted to a new file
111
+ src.unlink(missing_ok=True)
112
+ return dst
113
  else:
114
+ raise RuntimeError(f"ffmpeg conversion for {src.name} produced an empty or missing MP4 file (final check).")
 
 
115
 
116
 
117
  def _compress_video(inp: Path, crf: int = 28, preset: str = "fast") -> Path:
 
129
 
130
 
131
  def _maybe_compress(path: Path, limit_mb: int) -> Tuple[Path, bool]:
132
+ # Ensure the path exists before trying to stat it, though _convert_to_mp4 should already guarantee this.
133
+ if not path.exists() or path.stat().st_size == 0:
134
+ raise FileNotFoundError(f"Video file for compression not found or is empty: {path.name}")
135
+
136
  size_mb = path.stat().st_size / (1024 * 1024)
137
  if size_mb <= limit_mb:
138
  return path, False
 
167
  for chunk in r.iter_content(chunk_size=8192):
168
  if chunk:
169
  f.write(chunk)
170
+ # Ensure the downloaded file is not empty
171
+ if not out_path.exists() or out_path.stat().st_size == 0:
172
+ out_path.unlink(missing_ok=True)
173
+ raise RuntimeError(f"Direct download of '{url}' resulted in an empty or failed file.")
174
  return out_path
175
 
176
 
 
233
  if downloaded_file is None:
234
  # yt_dlp might move/rename files, so checking `info['_filename']` is reliable
235
  downloaded_file = Path(info.get('_filename', ''))
236
+
237
+ # Ensure the file exists and is not empty before attempting conversion
238
+ if downloaded_file.exists() and downloaded_file.stat().st_size > 0:
239
+ # If it's still not an MP4, convert it. If it is, _convert_to_mp4 will handle it.
240
+ downloaded_file = _convert_to_mp4(downloaded_file)
241
+ else:
242
+ raise RuntimeError(f"yt-dlp download for '{url}' produced an empty or missing file.")
243
 
244
  finally:
245
  bar.empty()
 
247
 
248
  if downloaded_file and downloaded_file.exists() and downloaded_file.stat().st_size > 0:
249
  # Ensure it's an MP4, even if yt_dlp hook didn't catch final MP4 name
250
+ # _convert_to_mp4 is idempotent and handles this already.
251
+ return _convert_to_mp4(downloaded_file)
 
252
 
253
  # ---------- Fallback: direct HTTP download ----------
254
  st.warning("yt-dlp failed or did not produce an MP4, attempting direct download.")
 
543
  raw_path = download_video(
544
  st.session_state["url"], DATA_DIR, st.session_state["video_password"]
545
  )
546
+ # Ensure it's MP4 - _convert_to_mp4 is now idempotent and robust
547
+ mp4_path = _convert_to_mp4(raw_path)
548
  st.session_state["video_path"], was_compressed = _maybe_compress(mp4_path, st.session_state["compress_mb"])
549
  if was_compressed:
550
  st.toast("Video downloaded and compressed.")
 
556
  RuntimeError,
557
  requests.exceptions.RequestException,
558
  yt_dlp.utils.DownloadError,
559
+ FileNotFoundError, # Added to catch explicit FileNotFoundError from _convert_to_mp4 or _maybe_compress
560
  ) as e:
561
  st.session_state["last_error"] = f"Download failed: {e}"
562
  st.session_state["last_error_detail"] = traceback.format_exc()