Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
#import streamlit as st
|
| 2 |
import ffmpeg
|
| 3 |
import os
|
| 4 |
import time
|
|
@@ -17,28 +16,27 @@ import asyncio
|
|
| 17 |
from threading import Thread
|
| 18 |
import nest_asyncio
|
| 19 |
|
| 20 |
-
#
|
| 21 |
nest_asyncio.apply()
|
| 22 |
|
| 23 |
app = FastAPI(title="Video Conversion API")
|
| 24 |
|
| 25 |
-
# Add CORS middleware
|
| 26 |
app.add_middleware(
|
| 27 |
CORSMiddleware,
|
| 28 |
-
allow_origins=["*"],
|
| 29 |
allow_credentials=True,
|
| 30 |
allow_methods=["*"],
|
| 31 |
allow_headers=["*"],
|
| 32 |
)
|
| 33 |
|
| 34 |
# Supported formats
|
| 35 |
-
supported_formats = [
|
| 36 |
-
audio_formats = [
|
| 37 |
gif_formats = ['GIF']
|
| 38 |
-
image_formats = [
|
| 39 |
-
'JPEG', 'JPEG2000', 'MPO', 'MSP', 'PALM', 'PCX', 'PDF', 'PNG', 'PPM', 'SGI', 'SPIDER',
|
| 40 |
-
'TGA', 'TIFF', 'WEBP', 'WMX', 'XBM'])]
|
| 41 |
|
|
|
|
| 42 |
CACHE_DIR = tempfile.mkdtemp()
|
| 43 |
|
| 44 |
# Audio codec mapping
|
|
@@ -79,9 +77,19 @@ VIDEO_CODECS = {
|
|
| 79 |
|
| 80 |
def delete_temp_dir(directory, delay=900):
|
| 81 |
"""Delete temporary directory after delay"""
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
timer.start()
|
| 84 |
|
|
|
|
| 85 |
delete_temp_dir(CACHE_DIR, delay=900)
|
| 86 |
|
| 87 |
def sanitize_filename(filename):
|
|
@@ -239,7 +247,8 @@ def convert_video(video_path, target_format, conversion_type, time_in_seconds=No
|
|
| 239 |
img.save(output_file, format=target_format.upper())
|
| 240 |
|
| 241 |
# Remove temporary PNG file
|
| 242 |
-
os.
|
|
|
|
| 243 |
return output_file
|
| 244 |
|
| 245 |
except Exception as pil_error:
|
|
@@ -382,152 +391,35 @@ async def stcore_host_config():
|
|
| 382 |
}
|
| 383 |
}
|
| 384 |
|
| 385 |
-
@app.get("/health
|
| 386 |
-
async def
|
| 387 |
-
return {"status": "healthy"}
|
| 388 |
-
|
| 389 |
-
@app.get("/health/_stcore/host-config")
|
| 390 |
-
async def health_stcore_host_config():
|
| 391 |
-
return {
|
| 392 |
-
"version": "1.0",
|
| 393 |
-
"config": {
|
| 394 |
-
"enableCors": False,
|
| 395 |
-
"enableXsrfProtection": False
|
| 396 |
-
}
|
| 397 |
-
}
|
| 398 |
-
|
| 399 |
-
@app.get("/convert/_stcore/health")
|
| 400 |
-
async def convert_stcore_health():
|
| 401 |
-
return {"status": "healthy"}
|
| 402 |
-
|
| 403 |
-
@app.get("/convert/_stcore/host-config")
|
| 404 |
-
async def convert_stcore_host_config():
|
| 405 |
-
return {
|
| 406 |
-
"version": "1.0",
|
| 407 |
-
"config": {
|
| 408 |
-
"enableCors": False,
|
| 409 |
-
"enableXsrfProtection": False
|
| 410 |
-
}
|
| 411 |
-
}
|
| 412 |
|
| 413 |
@app.get("/")
|
| 414 |
async def root():
|
| 415 |
-
return {"message": "Video Conversion API is running", "docs": "/docs"}
|
| 416 |
-
|
| 417 |
-
#
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
# # Create two columns
|
| 425 |
-
# col1, col2 = st.columns([1, 1])
|
| 426 |
-
|
| 427 |
-
# with col1:
|
| 428 |
-
# video_file = st.file_uploader("Upload a Video", type=[ext.lower() for ext in supported_formats])
|
| 429 |
-
# if video_file:
|
| 430 |
-
# st.video(video_file)
|
| 431 |
-
|
| 432 |
-
# with col2:
|
| 433 |
-
# if video_file:
|
| 434 |
-
# # Save uploaded video to cache
|
| 435 |
-
# temp_video_path = os.path.join(CACHE_DIR, video_file.name)
|
| 436 |
-
# with open(temp_video_path, "wb") as f:
|
| 437 |
-
# f.write(video_file.getbuffer())
|
| 438 |
-
|
| 439 |
-
# # Get video duration
|
| 440 |
-
# video_duration = get_video_duration(temp_video_path)
|
| 441 |
-
|
| 442 |
-
# # Select conversion type
|
| 443 |
-
# conversion_type = st.selectbox(
|
| 444 |
-
# "Select Conversion Type",
|
| 445 |
-
# ['Video to Video', 'Video to Audio', 'Video to GIF', 'Video to Image']
|
| 446 |
-
# )
|
| 447 |
-
|
| 448 |
-
# # Update format choices
|
| 449 |
-
# def update_format_choices(conversion_type):
|
| 450 |
-
# if conversion_type == 'Video to Video':
|
| 451 |
-
# return supported_formats
|
| 452 |
-
# elif conversion_type == 'Video to Audio':
|
| 453 |
-
# return audio_formats
|
| 454 |
-
# elif conversion_type == 'Video to GIF':
|
| 455 |
-
# return gif_formats
|
| 456 |
-
# elif conversion_type == 'Video to Image':
|
| 457 |
-
# return image_formats
|
| 458 |
-
# return []
|
| 459 |
-
|
| 460 |
-
# target_format_choices = update_format_choices(conversion_type)
|
| 461 |
-
# target_format = st.selectbox("Select Target Format", target_format_choices)
|
| 462 |
-
|
| 463 |
-
# # Show codec info for supported formats
|
| 464 |
-
# if conversion_type == 'Video to Audio' and target_format.upper() in AUDIO_CODECS:
|
| 465 |
-
# codec_info = AUDIO_CODECS[target_format.upper()]
|
| 466 |
-
# st.info(f"🎵 Audio Codec: {codec_info.get('acodec', 'default')}")
|
| 467 |
-
# elif conversion_type == 'Video to Video' and target_format.upper() in VIDEO_CODECS:
|
| 468 |
-
# codec_info = VIDEO_CODECS[target_format.upper()]
|
| 469 |
-
# st.info(f"🎬 Video Codec: {codec_info.get('vcodec', 'default')} | Audio Codec: {codec_info.get('acodec', 'default')}")
|
| 470 |
-
|
| 471 |
-
# if conversion_type == 'Video to Image':
|
| 472 |
-
# time_in_seconds = st.slider(
|
| 473 |
-
# "Time (in seconds) for image extraction",
|
| 474 |
-
# 0, int(video_duration), 0, 1
|
| 475 |
-
# )
|
| 476 |
-
# else:
|
| 477 |
-
# time_in_seconds = None
|
| 478 |
-
|
| 479 |
-
# if st.button("🚀 Convert", type="primary"):
|
| 480 |
-
# with st.spinner("Converting with optimized codecs..."):
|
| 481 |
-
# try:
|
| 482 |
-
# output_file = convert_video(temp_video_path, target_format, conversion_type, time_in_seconds)
|
| 483 |
-
|
| 484 |
-
# st.success("✅ Conversion Successful!")
|
| 485 |
-
|
| 486 |
-
# # Show file size
|
| 487 |
-
# file_size = os.path.getsize(output_file) / (1024 * 1024) # MB
|
| 488 |
-
# st.info(f"📁 File size: {file_size:.2f} MB")
|
| 489 |
-
|
| 490 |
-
# with open(output_file, "rb") as f:
|
| 491 |
-
# st.download_button(
|
| 492 |
-
# "⬇️ Download Converted File",
|
| 493 |
-
# f,
|
| 494 |
-
# file_name=os.path.basename(output_file),
|
| 495 |
-
# type="primary"
|
| 496 |
-
# )
|
| 497 |
-
# except Exception as e:
|
| 498 |
-
# st.error(f"❌ Error: {str(e)}")
|
| 499 |
-
|
| 500 |
-
# API Info & Supported Codecs
|
| 501 |
-
st.sidebar.header("🔧 API Information")
|
| 502 |
-
st.sidebar.info("""
|
| 503 |
-
**API Endpoints:**
|
| 504 |
-
- `POST /api/convert` - Convert video files
|
| 505 |
-
- `GET /api/formats` - Get supported formats
|
| 506 |
-
|
| 507 |
-
**WordPress Integration:**
|
| 508 |
-
Configure the Hugging Face Space URL in WordPress settings.
|
| 509 |
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
""
|
| 513 |
|
| 514 |
-
|
| 515 |
-
with st.sidebar.expander("🎵 Supported Audio Codecs"):
|
| 516 |
-
for format_name, codec_info in AUDIO_CODECS.items():
|
| 517 |
-
st.write(f"**{format_name}:** {codec_info.get('acodec', 'default')}")
|
| 518 |
|
| 519 |
-
with
|
| 520 |
-
|
| 521 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 522 |
|
| 523 |
-
def run_fastapi():
|
| 524 |
-
uvicorn.run(app, host="0.0.0.0", port=7860)
|
| 525 |
-
|
| 526 |
-
# Run both applications
|
| 527 |
if __name__ == "__main__":
|
| 528 |
-
|
| 529 |
-
fastapi_thread = Thread(target=run_fastapi, daemon=True)
|
| 530 |
-
fastapi_thread.start()
|
| 531 |
-
|
| 532 |
-
# # Run Streamlit
|
| 533 |
-
# streamlit_app()
|
|
|
|
|
|
|
| 1 |
import ffmpeg
|
| 2 |
import os
|
| 3 |
import time
|
|
|
|
| 16 |
from threading import Thread
|
| 17 |
import nest_asyncio
|
| 18 |
|
| 19 |
+
# Apply nest_asyncio for concurrent execution
|
| 20 |
nest_asyncio.apply()
|
| 21 |
|
| 22 |
app = FastAPI(title="Video Conversion API")
|
| 23 |
|
| 24 |
+
# Add CORS middleware
|
| 25 |
app.add_middleware(
|
| 26 |
CORSMiddleware,
|
| 27 |
+
allow_origins=["*"],
|
| 28 |
allow_credentials=True,
|
| 29 |
allow_methods=["*"],
|
| 30 |
allow_headers=["*"],
|
| 31 |
)
|
| 32 |
|
| 33 |
# Supported formats
|
| 34 |
+
supported_formats = ['ASF', 'AVI', 'FLV', 'M2TS', 'M4V', 'MKV', 'MOV', 'MP4', 'MPEG', 'MPG', 'MTS', 'TS', 'VOB', 'WEBM', 'WMV']
|
| 35 |
+
audio_formats = ['AAC', 'AIFF', 'ALAC', 'CAF', 'FLAC', 'M4A', 'MP3', 'OGG', 'OPUS', 'SPX', 'TTA', 'WAV', 'WMA', 'WV']
|
| 36 |
gif_formats = ['GIF']
|
| 37 |
+
image_formats = ['BMP', 'DIB', 'EPS', 'GIF', 'ICNS', 'ICO', 'IM', 'JPEG', 'JPEG2000', 'MPO', 'MSP', 'PALM', 'PCX', 'PDF', 'PNG', 'PPM', 'SGI', 'SPIDER', 'TGA', 'TIFF', 'WEBP', 'WMX', 'XBM']
|
|
|
|
|
|
|
| 38 |
|
| 39 |
+
# Create cache directory
|
| 40 |
CACHE_DIR = tempfile.mkdtemp()
|
| 41 |
|
| 42 |
# Audio codec mapping
|
|
|
|
| 77 |
|
| 78 |
def delete_temp_dir(directory, delay=900):
|
| 79 |
"""Delete temporary directory after delay"""
|
| 80 |
+
def cleanup():
|
| 81 |
+
try:
|
| 82 |
+
if os.path.exists(directory):
|
| 83 |
+
shutil.rmtree(directory)
|
| 84 |
+
print(f"Cleaned up temporary directory: {directory}")
|
| 85 |
+
except Exception as e:
|
| 86 |
+
print(f"Error cleaning up directory {directory}: {e}")
|
| 87 |
+
|
| 88 |
+
timer = threading.Timer(delay, cleanup)
|
| 89 |
+
timer.daemon = True
|
| 90 |
timer.start()
|
| 91 |
|
| 92 |
+
# Schedule cleanup
|
| 93 |
delete_temp_dir(CACHE_DIR, delay=900)
|
| 94 |
|
| 95 |
def sanitize_filename(filename):
|
|
|
|
| 247 |
img.save(output_file, format=target_format.upper())
|
| 248 |
|
| 249 |
# Remove temporary PNG file
|
| 250 |
+
if os.path.exists(temp_png_file):
|
| 251 |
+
os.remove(temp_png_file)
|
| 252 |
return output_file
|
| 253 |
|
| 254 |
except Exception as pil_error:
|
|
|
|
| 391 |
}
|
| 392 |
}
|
| 393 |
|
| 394 |
+
@app.get("/health")
|
| 395 |
+
async def health():
|
| 396 |
+
return {"status": "healthy", "message": "Video Conversion API is running"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
|
| 398 |
@app.get("/")
|
| 399 |
async def root():
|
| 400 |
+
return {"message": "Video Conversion API is running", "docs": "/docs", "status": "healthy"}
|
| 401 |
+
|
| 402 |
+
# Main function to run the app
|
| 403 |
+
def main():
|
| 404 |
+
print("Starting Video Conversion API...")
|
| 405 |
+
print(f"Cache directory: {CACHE_DIR}")
|
| 406 |
+
print(f"Supported video formats: {supported_formats}")
|
| 407 |
+
print(f"Supported audio formats: {audio_formats}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
|
| 409 |
+
# Get port from environment variable (Hugging Face Spaces uses specific port)
|
| 410 |
+
port = int(os.environ.get("PORT", 7860))
|
| 411 |
+
host = "0.0.0.0"
|
| 412 |
|
| 413 |
+
print(f"Starting server on {host}:{port}")
|
|
|
|
|
|
|
|
|
|
| 414 |
|
| 415 |
+
# Configure uvicorn with proper settings for Hugging Face Spaces
|
| 416 |
+
uvicorn.run(
|
| 417 |
+
app,
|
| 418 |
+
host=host,
|
| 419 |
+
port=port,
|
| 420 |
+
log_level="info",
|
| 421 |
+
access_log=True
|
| 422 |
+
)
|
| 423 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
if __name__ == "__main__":
|
| 425 |
+
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|