File size: 4,134 Bytes
65edd82
c187eb7
 
 
 
 
a1a36a1
 
c187eb7
a1a36a1
c187eb7
a1a36a1
c187eb7
 
a1a36a1
c187eb7
a1a36a1
c187eb7
 
a1a36a1
c187eb7
 
a1a36a1
c187eb7
a1a36a1
c187eb7
65edd82
c187eb7
 
a1a36a1
 
c187eb7
65edd82
c187eb7
 
 
 
 
65edd82
c187eb7
 
 
a1a36a1
c187eb7
65edd82
c187eb7
 
 
 
 
 
 
 
 
65edd82
 
 
c187eb7
 
 
a1a36a1
65edd82
a1a36a1
c187eb7
65edd82
c187eb7
65edd82
c187eb7
 
 
 
 
 
a1a36a1
65edd82
c187eb7
 
65edd82
c187eb7
 
65edd82
c187eb7
 
65edd82
a1a36a1
65edd82
c187eb7
 
 
 
 
 
65edd82
 
3c55f88
 
 
a1a36a1
 
65edd82
 
ca80fca
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# 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)