danicor commited on
Commit
b123367
·
verified ·
1 Parent(s): 4448b4a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -0
app.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ffmpeg
2
+ import os
3
+ import tempfile
4
+ import shutil
5
+ import re
6
+ from PIL import Image
7
+ from fastapi import FastAPI, File, Form, UploadFile, HTTPException
8
+ from fastapi.responses import FileResponse
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+
11
+ app = FastAPI(title="Video Conversion API")
12
+
13
+ # CORS middleware
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["*"],
17
+ allow_credentials=True,
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
21
+
22
+ # Supported formats
23
+ supported_formats = [f for f in sorted([
24
+ 'AVI','FLV','M2TS','M4V','MKV','MOV','MP4','MPEG','MPG','MTS','TS','VOB','WEBM','WMV'
25
+ ])]
26
+ audio_formats = [f for f in sorted([
27
+ 'MP3','WAV','AAC','FLAC','OGG','M4A','ALAC','WMA','AIFF','OPUS','CAF','SPX','WV'
28
+ ])]
29
+ image_formats = [f for f in sorted(Image.SAVE.keys() or ['BMP','GIF','JPEG','PNG','TIFF','WEBP'])]
30
+ gif_formats = ['GIF']
31
+
32
+ CACHE_DIR = tempfile.mkdtemp()
33
+
34
+ # Audio & Video codecs
35
+ AUDIO_CODECS = {
36
+ 'MP3': {'acodec':'libmp3lame','audio_bitrate':'192k'},
37
+ 'AAC': {'acodec':'aac','audio_bitrate':'192k'},
38
+ 'WAV': {'acodec':'pcm_s16le'},
39
+ 'FLAC': {'acodec':'flac'},
40
+ 'OGG': {'acodec':'libvorbis','audio_bitrate':'192k'},
41
+ 'M4A': {'acodec':'aac','audio_bitrate':'192k'},
42
+ 'ALAC': {'acodec':'alac'},
43
+ 'WMA': {'acodec':'wmav2','audio_bitrate':'192k'},
44
+ 'AIFF': {'acodec':'pcm_s16be'},
45
+ 'OPUS': {'acodec':'libopus','audio_bitrate':'128k'},
46
+ 'CAF': {'acodec':'alac'},
47
+ 'SPX': {'acodec':'libspeex','audio_bitrate':'32k'},
48
+ 'WV': {'acodec':'wavpack'}
49
+ }
50
+
51
+ VIDEO_CODECS = {
52
+ 'MP4': {'vcodec':'libx264','acodec':'aac','preset':'medium','crf':'23'},
53
+ 'AVI': {'vcodec':'libxvid','acodec':'libmp3lame'},
54
+ 'MOV': {'vcodec':'libx264','acodec':'aac'},
55
+ 'MKV': {'vcodec':'libx264','acodec':'aac'},
56
+ 'WEBM': {'vcodec':'libvpx-vp9','acodec':'libopus'},
57
+ 'FLV': {'vcodec':'libx264','acodec':'aac'},
58
+ 'WMV': {'vcodec':'wmv2','acodec':'wmav2'},
59
+ 'M4V': {'vcodec':'libx264','acodec':'aac'},
60
+ 'MPG': {'vcodec':'mpeg2video','acodec':'mp2'},
61
+ 'MPEG': {'vcodec':'mpeg2video','acodec':'mp2'},
62
+ 'VOB': {'vcodec':'mpeg2video','acodec':'mp2'},
63
+ 'ASF': {'vcodec':'wmv2','acodec':'wmav2'},
64
+ 'TS': {'vcodec':'libx264','acodec':'aac'},
65
+ 'M2TS': {'vcodec':'libx264','acodec':'aac'},
66
+ 'MTS': {'vcodec':'libx264','acodec':'aac'}
67
+ }
68
+
69
+ def sanitize_filename(filename):
70
+ return re.sub(r'[^a-zA-Z0-9_.-]', '_', filename)
71
+
72
+ def convert_video(video_path, target_format, conversion_type, time_in_seconds=None):
73
+ base_name = os.path.splitext(os.path.basename(video_path))[0]
74
+ sanitized_base_name = sanitize_filename(base_name)
75
+ output_dir = os.path.join(CACHE_DIR, "outputs")
76
+ os.makedirs(output_dir, exist_ok=True)
77
+
78
+ if conversion_type == 'Video to Video':
79
+ output_file = os.path.join(output_dir, f"converted_{sanitized_base_name}.{target_format.lower()}")
80
+ codec_settings = VIDEO_CODECS.get(target_format.upper(), {})
81
+ input_stream = ffmpeg.input(video_path)
82
+ ffmpeg.output(input_stream, output_file, **codec_settings).overwrite_output().run(quiet=True)
83
+ return output_file
84
+
85
+ elif conversion_type == 'Video to Audio':
86
+ output_file = os.path.join(output_dir, f"audio_{sanitized_base_name}.{target_format.lower()}")
87
+ codec_settings = AUDIO_CODECS.get(target_format.upper(), {})
88
+ input_stream = ffmpeg.input(video_path)
89
+ ffmpeg.output(input_stream, output_file, vn=None, **codec_settings).overwrite_output().run(quiet=True)
90
+ return output_file
91
+
92
+ elif conversion_type == 'Video to GIF':
93
+ output_file = os.path.join(output_dir, f"gif_{sanitized_base_name}.gif")
94
+ ffmpeg.input(video_path).output(
95
+ output_file,
96
+ vf="fps=15,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff",
97
+ loop=0
98
+ ).overwrite_output().run(quiet=True)
99
+ return output_file
100
+
101
+ elif conversion_type == 'Video to Image':
102
+ if time_in_seconds is None:
103
+ time_in_seconds = 0
104
+ temp_png_file = os.path.join(output_dir, f"temp_{sanitized_base_name}_{time_in_seconds}s.png")
105
+ ffmpeg.input(video_path, ss=time_in_seconds).output(temp_png_file, vframes=1, **{'qscale:v': '1'}).overwrite_output().run(quiet=True)
106
+ if target_format.upper() == 'PNG':
107
+ return temp_png_file
108
+ output_file = os.path.join(output_dir, f"image_{sanitized_base_name}_{time_in_seconds}s.{target_format.lower()}")
109
+ with Image.open(temp_png_file) as img:
110
+ img.convert('RGB').save(output_file, format=target_format.upper())
111
+ os.remove(temp_png_file)
112
+ return output_file
113
+
114
+ @app.post("/api/convert")
115
+ async def api_convert(file: UploadFile = File(...), conversion_type: str = Form("Video to Video"), target_format: str = Form("MP4"), time_in_seconds: int = Form(0)):
116
+ if not file.filename:
117
+ raise HTTPException(status_code=400, detail="No file provided")
118
+ temp_dir = tempfile.mkdtemp()
119
+ file_path = os.path.join(temp_dir, file.filename)
120
+ with open(file_path, "wb") as f:
121
+ f.write(await file.read())
122
+ output_file = convert_video(file_path, target_format, conversion_type, time_in_seconds)
123
+ shutil.rmtree(temp_dir, ignore_errors=True)
124
+ return FileResponse(output_file, filename=os.path.basename(output_file))
125
+
126
+ @app.get("/api/formats")
127
+ async def get_formats():
128
+ return {
129
+ "video_formats": supported_formats,
130
+ "audio_formats": audio_formats,
131
+ "image_formats": image_formats,
132
+ "gif_formats": gif_formats,
133
+ "supported_audio_codecs": list(AUDIO_CODECS.keys()),
134
+ "supported_video_codecs": list(VIDEO_CODECS.keys())
135
+ }
136
+
137
+ @app.get("/")
138
+ async def root():
139
+ return {"message": "Video Conversion API is running", "docs": "/docs"}