sdafd commited on
Commit
87cb710
·
verified ·
1 Parent(s): d87aabb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -60
app.py CHANGED
@@ -4,18 +4,18 @@ import shlex
4
  import tempfile
5
  import uuid
6
  from typing import List
 
 
7
 
8
  from fastapi import FastAPI, File, UploadFile, Form, HTTPException
9
  from fastapi.responses import FileResponse, JSONResponse
10
- import logging
11
 
12
  # Configure logging
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
16
- # --- Define the core ffmpeg function (adapted for API context) ---
17
- # Note: We pass paths directly now, as they are managed within the API call.
18
- # We also return stderr for debugging purposes in case of failure.
19
  def run_ffmpeg_concatenation(input_files: List[str], output_file: str, ffmpeg_executable: str = "ffmpeg"):
20
  """
21
  Runs the FFmpeg concatenation process.
@@ -60,8 +60,6 @@ def run_ffmpeg_concatenation(input_files: List[str], output_file: str, ffmpeg_ex
60
  logger.info("\n--- FFmpeg Execution Start ---")
61
  stderr_output = ""
62
  try:
63
- # Using Popen for potentially better handling of large outputs if needed,
64
- # but run is simpler here. Ensure encoding/errors are handled.
65
  process = subprocess.run(
66
  command,
67
  check=True, # Raises CalledProcessError on non-zero exit
@@ -72,8 +70,6 @@ def run_ffmpeg_concatenation(input_files: List[str], output_file: str, ffmpeg_ex
72
  )
73
  stderr_output = process.stderr
74
  logger.info("--- FFmpeg Execution End ---")
75
- # logger.info("\nSTDOUT:") # Often empty for concat
76
- # logger.info(process.stdout)
77
  logger.info("\nSTDERR (Progress/Info):\n" + stderr_output)
78
  msg = f"Successfully concatenated videos into {os.path.basename(output_file)}"
79
  logger.info(msg)
@@ -93,6 +89,16 @@ def run_ffmpeg_concatenation(input_files: List[str], output_file: str, ffmpeg_ex
93
  logger.exception(err_msg) # Log full traceback
94
  return False, err_msg, stderr_output # Include any captured stderr
95
 
 
 
 
 
 
 
 
 
 
 
96
  # --- FastAPI App Definition ---
97
  app = FastAPI()
98
 
@@ -103,6 +109,7 @@ async def concatenate_videos_api(
103
  ):
104
  """
105
  API endpoint to concatenate uploaded video files using FFmpeg re-encoding.
 
106
  """
107
  if not files:
108
  raise HTTPException(status_code=400, detail="No files were uploaded.")
@@ -111,61 +118,65 @@ async def concatenate_videos_api(
111
 
112
  logger.info(f"Received {len(files)} files for concatenation. Output name: {output_filename}")
113
 
114
- # Create a temporary directory to store uploaded files and the output
115
- with tempfile.TemporaryDirectory() as temp_dir:
116
- input_file_paths = []
117
- original_filenames = []
118
-
119
- try:
120
- # Save uploaded files to the temporary directory
121
- for uploaded_file in files:
122
- original_filenames.append(uploaded_file.filename)
123
- # Create a unique-ish temp filename to avoid clashes, keep extension
124
- _, ext = os.path.splitext(uploaded_file.filename)
125
- temp_input_path = os.path.join(temp_dir, f"{uuid.uuid4()}{ext}")
126
- logger.info(f"Saving uploaded file '{uploaded_file.filename}' to '{temp_input_path}'")
 
 
 
127
  with open(temp_input_path, "wb") as buffer:
128
  buffer.write(await uploaded_file.read())
129
  input_file_paths.append(temp_input_path)
130
- await uploaded_file.close() # Close the file handle
131
-
132
- logger.info(f"Saved files: {original_filenames}")
133
-
134
- # Define the output path within the temporary directory
135
- temp_output_path = os.path.join(temp_dir, output_filename)
136
-
137
- # Run the FFmpeg concatenation logic
138
- success, message, ffmpeg_stderr = run_ffmpeg_concatenation(input_file_paths, temp_output_path)
139
-
140
- if success:
141
- logger.info(f"Concatenation successful. Preparing file response for: {temp_output_path}")
142
- # Return the concatenated file
143
- return FileResponse(
144
- path=temp_output_path,
145
- filename=output_filename, # Send back with the desired name
146
- media_type='video/mp4' # Adjust if needed based on output_filename extension
147
- )
148
- else:
149
- logger.error(f"Concatenation failed: {message}")
150
- # Return a JSON error response including ffmpeg's stderr if available
151
- return JSONResponse(
152
- status_code=500,
153
- content={
154
- "detail": f"Video concatenation failed: {message}",
155
- "ffmpeg_stderr": ffmpeg_stderr,
156
- "input_files": original_filenames
157
- }
158
- )
159
-
160
- except Exception as e:
161
- logger.exception("An unexpected error occurred in the API endpoint.")
162
- raise HTTPException(status_code=500, detail=f"Internal server error: {e}")
 
 
 
 
 
 
163
 
164
  @app.get("/")
165
  async def read_root():
166
- return {"message": "Video Concatenation API is running. Use the /concatenate/ endpoint (POST) to process videos."}
167
-
168
- # Example of how to run locally (Hugging Face Spaces handles this automatically)
169
- # if __name__ == "__main__":
170
- # import uvicorn
171
- # uvicorn.run(app, host="0.0.0.0", port=8000)
 
4
  import tempfile
5
  import uuid
6
  from typing import List
7
+ import shutil # Import shutil for rmtree
8
+ import logging
9
 
10
  from fastapi import FastAPI, File, UploadFile, Form, HTTPException
11
  from fastapi.responses import FileResponse, JSONResponse
12
+ from starlette.background import BackgroundTask # Import BackgroundTask
13
 
14
  # Configure logging
15
  logging.basicConfig(level=logging.INFO)
16
  logger = logging.getLogger(__name__)
17
 
18
+ # --- Define the core ffmpeg function (remains the same) ---
 
 
19
  def run_ffmpeg_concatenation(input_files: List[str], output_file: str, ffmpeg_executable: str = "ffmpeg"):
20
  """
21
  Runs the FFmpeg concatenation process.
 
60
  logger.info("\n--- FFmpeg Execution Start ---")
61
  stderr_output = ""
62
  try:
 
 
63
  process = subprocess.run(
64
  command,
65
  check=True, # Raises CalledProcessError on non-zero exit
 
70
  )
71
  stderr_output = process.stderr
72
  logger.info("--- FFmpeg Execution End ---")
 
 
73
  logger.info("\nSTDERR (Progress/Info):\n" + stderr_output)
74
  msg = f"Successfully concatenated videos into {os.path.basename(output_file)}"
75
  logger.info(msg)
 
89
  logger.exception(err_msg) # Log full traceback
90
  return False, err_msg, stderr_output # Include any captured stderr
91
 
92
+ # --- Cleanup Function ---
93
+ def cleanup_temp_dir(temp_dir: str):
94
+ """Removes a temporary directory."""
95
+ try:
96
+ logger.info(f"Cleaning up temporary directory: {temp_dir}")
97
+ shutil.rmtree(temp_dir)
98
+ logger.info(f"Successfully cleaned up temporary directory: {temp_dir}")
99
+ except Exception as e:
100
+ logger.error(f"Error cleaning up temporary directory {temp_dir}: {e}")
101
+
102
  # --- FastAPI App Definition ---
103
  app = FastAPI()
104
 
 
109
  ):
110
  """
111
  API endpoint to concatenate uploaded video files using FFmpeg re-encoding.
112
+ Cleans up temporary files after response is sent.
113
  """
114
  if not files:
115
  raise HTTPException(status_code=400, detail="No files were uploaded.")
 
118
 
119
  logger.info(f"Received {len(files)} files for concatenation. Output name: {output_filename}")
120
 
121
+ # Create a temporary directory manually
122
+ temp_dir = tempfile.mkdtemp()
123
+ logger.info(f"Created temporary directory: {temp_dir}")
124
+
125
+ input_file_paths = []
126
+ original_filenames = []
127
+
128
+ try:
129
+ # Save uploaded files to the temporary directory
130
+ for uploaded_file in files:
131
+ original_filenames.append(uploaded_file.filename)
132
+ _, ext = os.path.splitext(uploaded_file.filename)
133
+ # Ensure unique name within the temp dir
134
+ temp_input_path = os.path.join(temp_dir, f"{uuid.uuid4()}{ext}")
135
+ logger.info(f"Saving uploaded file '{uploaded_file.filename}' to '{temp_input_path}'")
136
+ try:
137
  with open(temp_input_path, "wb") as buffer:
138
  buffer.write(await uploaded_file.read())
139
  input_file_paths.append(temp_input_path)
140
+ finally:
141
+ await uploaded_file.close() # Ensure file handle is closed
142
+
143
+ logger.info(f"Saved files: {original_filenames}")
144
+
145
+ # Define the output path within the temporary directory
146
+ temp_output_path = os.path.join(temp_dir, output_filename)
147
+
148
+ # Run the FFmpeg concatenation logic
149
+ success, message, ffmpeg_stderr = run_ffmpeg_concatenation(input_file_paths, temp_output_path)
150
+
151
+ if success:
152
+ logger.info(f"Concatenation successful. Preparing file response for: {temp_output_path}")
153
+ # Return the concatenated file with a background task for cleanup
154
+ return FileResponse(
155
+ path=temp_output_path,
156
+ filename=output_filename, # Send back with the desired name
157
+ media_type='video/mp4', # Adjust if needed
158
+ background=BackgroundTask(cleanup_temp_dir, temp_dir) # Cleanup AFTER sending
159
+ )
160
+ else:
161
+ logger.error(f"Concatenation failed: {message}")
162
+ # Explicitly clean up now since we are not returning a FileResponse
163
+ cleanup_temp_dir(temp_dir)
164
+ # Return a JSON error response
165
+ return JSONResponse(
166
+ status_code=500,
167
+ content={
168
+ "detail": f"Video concatenation failed: {message}",
169
+ "ffmpeg_stderr": ffmpeg_stderr,
170
+ "input_files": original_filenames
171
+ }
172
+ )
173
+
174
+ except Exception as e:
175
+ logger.exception("An unexpected error occurred in the API endpoint.")
176
+ # Explicitly clean up in case of other errors before returning FileResponse
177
+ cleanup_temp_dir(temp_dir)
178
+ raise HTTPException(status_code=500, detail=f"Internal server error: {e}")
179
 
180
  @app.get("/")
181
  async def read_root():
182
+ return {"message": "Video Concatenation API is running. Use the /concatenate/ endpoint (POST) to process videos."}