ulduldp commited on
Commit
436736b
·
verified ·
1 Parent(s): cd39461

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +299 -0
app.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+
3
+ from flask import Flask, render_template_string, request, jsonify
4
+ import os
5
+ import uuid
6
+ import subprocess
7
+
8
+ app = Flask(__name__)
9
+
10
+ UPLOAD_FOLDER = "uploads"
11
+ OUTPUT_FOLDER = "static/videos"
12
+
13
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
14
+ os.makedirs(OUTPUT_FOLDER, exist_ok=True)
15
+
16
+ HTML = """
17
+ <!DOCTYPE html>
18
+ <html lang="en">
19
+ <head>
20
+ <meta charset="UTF-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
+
23
+ <title>Photo + Audio To Video</title>
24
+
25
+ <style>
26
+
27
+ *{
28
+ margin:0;
29
+ padding:0;
30
+ box-sizing:border-box;
31
+ font-family:Arial;
32
+ }
33
+
34
+ body{
35
+ background:#0f0f0f;
36
+ color:white;
37
+ min-height:100vh;
38
+ display:flex;
39
+ justify-content:center;
40
+ align-items:center;
41
+ padding:20px;
42
+ }
43
+
44
+ .container{
45
+ width:100%;
46
+ max-width:500px;
47
+ background:#1b1b1b;
48
+ border-radius:20px;
49
+ padding:25px;
50
+ box-shadow:0 0 20px rgba(0,0,0,0.4);
51
+ }
52
+
53
+ h1{
54
+ text-align:center;
55
+ margin-bottom:25px;
56
+ font-size:28px;
57
+ }
58
+
59
+ .upload-box{
60
+ border:2px dashed #444;
61
+ padding:20px;
62
+ border-radius:15px;
63
+ margin-bottom:20px;
64
+ }
65
+
66
+ label{
67
+ display:block;
68
+ margin-bottom:8px;
69
+ color:#ccc;
70
+ }
71
+
72
+ input{
73
+ width:100%;
74
+ padding:12px;
75
+ background:#2a2a2a;
76
+ border:none;
77
+ border-radius:10px;
78
+ color:white;
79
+ margin-bottom:15px;
80
+ }
81
+
82
+ button{
83
+ width:100%;
84
+ padding:15px;
85
+ border:none;
86
+ border-radius:12px;
87
+ background:#00aaff;
88
+ color:white;
89
+ font-size:18px;
90
+ cursor:pointer;
91
+ transition:0.3s;
92
+ }
93
+
94
+ button:hover{
95
+ opacity:0.9;
96
+ }
97
+
98
+ #loading{
99
+ display:none;
100
+ text-align:center;
101
+ margin-top:20px;
102
+ }
103
+
104
+ video{
105
+ width:100%;
106
+ margin-top:20px;
107
+ border-radius:15px;
108
+ display:none;
109
+ }
110
+
111
+ .download-btn{
112
+ display:none;
113
+ margin-top:15px;
114
+ text-align:center;
115
+ }
116
+
117
+ .download-btn a{
118
+ display:inline-block;
119
+ background:#22c55e;
120
+ color:white;
121
+ text-decoration:none;
122
+ padding:12px 20px;
123
+ border-radius:10px;
124
+ }
125
+
126
+ .preview{
127
+ margin-top:15px;
128
+ width:100%;
129
+ border-radius:15px;
130
+ display:none;
131
+ }
132
+
133
+ </style>
134
+ </head>
135
+ <body>
136
+
137
+ <div class="container">
138
+
139
+ <h1>Photo + Audio → Video</h1>
140
+
141
+ <form id="form">
142
+
143
+ <div class="upload-box">
144
+
145
+ <label>Select Photo</label>
146
+ <input type="file" id="image" name="image" accept="image/*" required>
147
+
148
+ <img id="preview" class="preview">
149
+
150
+ <label>Select Audio (mp3/wav)</label>
151
+ <input type="file" name="audio" accept=".mp3,.wav" required>
152
+
153
+ </div>
154
+
155
+ <button type="submit">Generate Video</button>
156
+
157
+ </form>
158
+
159
+ <div id="loading">
160
+ Generating Video...
161
+ </div>
162
+
163
+ <video id="video" controls></video>
164
+
165
+ <div class="download-btn" id="downloadDiv">
166
+ <a id="downloadBtn" download>Download Video</a>
167
+ </div>
168
+
169
+ </div>
170
+
171
+ <script>
172
+
173
+ const form = document.getElementById("form");
174
+ const loading = document.getElementById("loading");
175
+ const video = document.getElementById("video");
176
+ const downloadBtn = document.getElementById("downloadBtn");
177
+ const downloadDiv = document.getElementById("downloadDiv");
178
+ const preview = document.getElementById("preview");
179
+
180
+ document.getElementById("image").addEventListener("change", function(e){
181
+
182
+ const file = e.target.files[0];
183
+
184
+ if(file){
185
+ preview.src = URL.createObjectURL(file);
186
+ preview.style.display = "block";
187
+ }
188
+
189
+ });
190
+
191
+ form.addEventListener("submit", async (e)=>{
192
+
193
+ e.preventDefault();
194
+
195
+ loading.style.display = "block";
196
+ video.style.display = "none";
197
+ downloadDiv.style.display = "none";
198
+
199
+ const formData = new FormData(form);
200
+
201
+ try{
202
+
203
+ const response = await fetch("/generate",{
204
+ method:"POST",
205
+ body:formData
206
+ });
207
+
208
+ const data = await response.json();
209
+
210
+ loading.style.display = "none";
211
+
212
+ if(data.video_url){
213
+
214
+ video.src = data.video_url + "?t=" + new Date().getTime();
215
+ video.style.display = "block";
216
+
217
+ downloadBtn.href = data.video_url;
218
+ downloadDiv.style.display = "block";
219
+
220
+ }else{
221
+ alert(data.error || "Failed");
222
+ }
223
+
224
+ }catch(err){
225
+
226
+ loading.style.display = "none";
227
+ alert("Server Error");
228
+
229
+ }
230
+
231
+ });
232
+
233
+ </script>
234
+
235
+ </body>
236
+ </html>
237
+ """
238
+
239
+ @app.route("/")
240
+ def home():
241
+ return render_template_string(HTML)
242
+
243
+ @app.route("/generate", methods=["POST"])
244
+ def generate():
245
+
246
+ if "image" not in request.files or "audio" not in request.files:
247
+ return jsonify({"error":"Missing files"})
248
+
249
+ image = request.files["image"]
250
+ audio = request.files["audio"]
251
+
252
+ uid = str(uuid.uuid4())
253
+
254
+ image_path = os.path.join(UPLOAD_FOLDER, uid + "_" + image.filename)
255
+ audio_path = os.path.join(UPLOAD_FOLDER, uid + "_" + audio.filename)
256
+
257
+ output_filename = uid + ".mp4"
258
+ output_path = os.path.join(OUTPUT_FOLDER, output_filename)
259
+
260
+ image.save(image_path)
261
+ audio.save(audio_path)
262
+
263
+ cmd = [
264
+ "ffmpeg",
265
+ "-y",
266
+ "-loop", "1",
267
+ "-i", image_path,
268
+ "-i", audio_path,
269
+ "-c:v", "libx264",
270
+ "-tune", "stillimage",
271
+ "-c:a", "aac",
272
+ "-b:a", "192k",
273
+ "-pix_fmt", "yuv420p",
274
+ "-shortest",
275
+ output_path
276
+ ]
277
+
278
+ try:
279
+
280
+ subprocess.run(
281
+ cmd,
282
+ stdout=subprocess.PIPE,
283
+ stderr=subprocess.PIPE,
284
+ check=True
285
+ )
286
+
287
+ return jsonify({
288
+ "video_url": f"/static/videos/{output_filename}"
289
+ })
290
+
291
+ except subprocess.CalledProcessError as e:
292
+
293
+ return jsonify({
294
+ "error":"FFmpeg failed",
295
+ "details": e.stderr.decode()
296
+ })
297
+
298
+ if __name__ == "__main__":
299
+ app.run(host="0.0.0.0", port=7860)