test-ffmpeg / ap2p.py
ulduldp's picture
Rename app.py to ap2p.py
3172482 verified
# app.py
from flask import Flask, render_template_string, request, jsonify
import os
import uuid
import subprocess
app = Flask(__name__)
UPLOAD_FOLDER = "uploads"
OUTPUT_FOLDER = "static/videos"
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
HTML = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Photo + Audio To Video</title>
<style>
*{
margin:0;
padding:0;
box-sizing:border-box;
font-family:Arial;
}
body{
background:#0f0f0f;
color:white;
min-height:100vh;
display:flex;
justify-content:center;
align-items:center;
padding:20px;
}
.container{
width:100%;
max-width:500px;
background:#1b1b1b;
border-radius:20px;
padding:25px;
box-shadow:0 0 20px rgba(0,0,0,0.4);
}
h1{
text-align:center;
margin-bottom:25px;
font-size:28px;
}
.upload-box{
border:2px dashed #444;
padding:20px;
border-radius:15px;
margin-bottom:20px;
}
label{
display:block;
margin-bottom:8px;
color:#ccc;
}
input{
width:100%;
padding:12px;
background:#2a2a2a;
border:none;
border-radius:10px;
color:white;
margin-bottom:15px;
}
button{
width:100%;
padding:15px;
border:none;
border-radius:12px;
background:#00aaff;
color:white;
font-size:18px;
cursor:pointer;
transition:0.3s;
}
button:hover{
opacity:0.9;
}
#loading{
display:none;
text-align:center;
margin-top:20px;
}
video{
width:100%;
margin-top:20px;
border-radius:15px;
display:none;
}
.download-btn{
display:none;
margin-top:15px;
text-align:center;
}
.download-btn a{
display:inline-block;
background:#22c55e;
color:white;
text-decoration:none;
padding:12px 20px;
border-radius:10px;
}
.preview{
margin-top:15px;
width:100%;
border-radius:15px;
display:none;
}
</style>
</head>
<body>
<div class="container">
<h1>Photo + Audio → Video</h1>
<form id="form">
<div class="upload-box">
<label>Select Photo</label>
<input type="file" id="image" name="image" accept="image/*" required>
<img id="preview" class="preview">
<label>Select Audio (mp3/wav)</label>
<input type="file" name="audio" required>
</div>
<button type="submit">Generate Video</button>
</form>
<div id="loading">
Generating Video...
</div>
<video id="video" controls></video>
<div class="download-btn" id="downloadDiv">
<a id="downloadBtn" download>Download Video</a>
</div>
</div>
<script>
const form = document.getElementById("form");
const loading = document.getElementById("loading");
const video = document.getElementById("video");
const downloadBtn = document.getElementById("downloadBtn");
const downloadDiv = document.getElementById("downloadDiv");
const preview = document.getElementById("preview");
document.getElementById("image").addEventListener("change", function(e){
const file = e.target.files[0];
if(file){
preview.src = URL.createObjectURL(file);
preview.style.display = "block";
}
});
form.addEventListener("submit", async (e)=>{
e.preventDefault();
loading.style.display = "block";
video.style.display = "none";
downloadDiv.style.display = "none";
const formData = new FormData(form);
try{
const response = await fetch("/generate",{
method:"POST",
body:formData
});
const data = await response.json();
loading.style.display = "none";
if(data.video_url){
video.src = data.video_url + "?t=" + new Date().getTime();
video.style.display = "block";
downloadBtn.href = data.video_url;
downloadDiv.style.display = "block";
}else{
alert(data.error || "Failed");
}
}catch(err){
loading.style.display = "none";
alert("Server Error");
}
});
</script>
</body>
</html>
"""
@app.route("/")
def home():
return render_template_string(HTML)
@app.route("/generate", methods=["POST"])
def generate():
if "image" not in request.files or "audio" not in request.files:
return jsonify({"error":"Missing files"})
image = request.files["image"]
audio = request.files["audio"]
uid = str(uuid.uuid4())
image_path = os.path.join(UPLOAD_FOLDER, uid + "_" + image.filename)
audio_path = os.path.join(UPLOAD_FOLDER, uid + "_" + audio.filename)
output_filename = uid + ".mp4"
output_path = os.path.join(OUTPUT_FOLDER, output_filename)
image.save(image_path)
audio.save(audio_path)
"""cmd = [
"ffmpeg",
"-y",
"-loop", "1",
"-i", image_path,
"-i", audio_path,
"-c:v", "libx264",
"-tune", "stillimage",
"-c:a", "aac",
"-b:a", "192k",
"-pix_fmt", "yuv420p",
"-shortest",
output_path
]"""
cmd = [
"ffmpeg",
"-y",
"-loop", "1",
"-i", image_path,
"-i", audio_path,
"-vf",
"scale=trunc(iw/2)*2:trunc(ih/2)*2",
"-c:v", "libx264",
"-tune", "stillimage",
"-c:a", "aac",
"-b:a", "192k",
"-pix_fmt", "yuv420p",
"-shortest",
output_path
]
try:
subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True
)
return jsonify({
"video_url": f"/static/videos/{output_filename}"
})
except subprocess.CalledProcessError as e:
return jsonify({
"error":"FFmpeg failed",
"details": e.stderr.decode()
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)