fb-dl / app.py
devusman's picture
tweak
3c55f88
# ffmpeg_service.py
from flask import Flask, request, Response, stream_with_context
import requests
import subprocess
import tempfile
import os
import shutil
import time
ffmpeg_app = Flask(__name__)
@ffmpeg_app.route('/convert', methods=['GET'])
def convert_media():
"""
Downloads the raw media file from the provided URL and converts it using FFmpeg.
"""
media_url = request.args.get('url')
target_format = request.args.get('format') # e.g., 'mp3', 'm4a', 'wav'
if not media_url or not target_format:
return "Missing 'url' or 'format' parameter", 400
temp_dir = tempfile.mkdtemp()
# Use the original format's extension for the input file to help FFmpeg
input_ext = media_url.split('.')[-1].split('?')[0]
input_file_path = os.path.join(temp_dir, f"input.{input_ext}")
output_file_path = os.path.join(temp_dir, f"output.{target_format}")
try:
start_time = time.time()
print(f"[{time.strftime('%H:%M:%S')}] Starting conversion for {target_format} from URL: {media_url[:100]}...")
# 1. Download the raw media file to a temporary file
with requests.get(media_url, stream=True, timeout=120) as r:
r.raise_for_status()
content_length = int(r.headers.get('content-length', 0))
print(f"[{time.strftime('%H:%M:%S')}] Raw media size: {content_length/1024/1024:.2f} MB")
with open(input_file_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
download_time = time.time()
print(f"[{time.strftime('%H:%M:%S')}] Download complete. Time taken: {download_time - start_time:.2f} seconds.")
# 2. Run FFmpeg conversion
command = [
'ffmpeg',
'-y', # Overwrite output files without asking
'-i', input_file_path,
# Audio-specific options for quality and codec
'-q:a', '0', # Variable bitrate, highest quality
'-map', 'a', # Only include audio streams
# Codec selection based on format
'-c:a', 'libmp3lame' if target_format == 'mp3' else
('pcm_s16le' if target_format == 'wav' else 'aac'),
'-f', target_format,
output_file_path
]
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=180) # 3-minute timeout
conversion_time = time.time()
print(f"[{time.strftime('%H:%M:%S')}] FFmpeg conversion complete. Time taken: {conversion_time - download_time:.2f} seconds.")
# 3. Stream the converted file back
def stream_output_file():
with open(output_file_path, 'rb') as f:
chunk = True
while chunk:
chunk = f.read(8192)
yield chunk
mime_type = f'audio/{target_format}' if target_format != 'm4a' else 'audio/mp4'
return Response(stream_with_context(stream_output_file()),
mimetype=mime_type)
except subprocess.CalledProcessError as e:
print(f"[{time.strftime('%H:%M:%S')}] FFmpeg command failed with error: {e.stderr.decode()}")
return f"Conversion failed: {e.stderr.decode()}", 500
except requests.exceptions.Timeout:
return "Media download or conversion timed out.", 504
except Exception as e:
print(f"[{time.strftime('%H:%M:%S')}] An error occurred in the FFmpeg service: {e}")
return f"Internal Server Error: {e}", 500
finally:
# 4. Cleanup temporary files
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
print(f"[{time.strftime('%H:%M:%S')}] Cleaned up temp directory: {temp_dir}")
@ffmpeg_app.route('/', methods=['GET'])
def index():
return "FFmpeg Conversion Service is running."
if __name__ == '__main__':
# For local development/testing of the FFmpeg service
# NOTE: When deployed via Docker/Gunicorn on Hugging Face, this block is ignored.
ffmpeg_app.run(host='0.0.0.0', port=7860, debug=True)