HTML代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>视频帧率处理器</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#10B981',
accent: '#8B5CF6',
dark: '#1E293B',
light: '#F8FAFC'
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.transition-custom {
transition: all 0.3s ease-in-out;
}
.shadow-custom {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.progress-bar {
height: 4px;
background: linear-gradient(90deg, #3B82F6 0%, #8B5CF6 100%);
transform-origin: left;
transform: scaleX(0);
transition: transform 0.3s ease;
}
}
</style>
</head>
<body class="bg-gray-50 min-h-screen flex flex-col">
<!-- 导航栏 -->
<header class="bg-white shadow-md sticky top-0 z-50">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fa fa-film text-primary text-2xl"></i>
<h1 class="text-xl md:text-2xl font-bold text-dark">视频帧率处理器</h1>
</div>
<nav class="hidden md:flex space-x-6">
<a href="#" class="text-gray-700 hover:text-primary transition-custom">首页</a>
<a href="#features" class="text-gray-700 hover:text-primary transition-custom">功能</a>
<a href="#about" class="text-gray-700 hover:text-primary transition-custom">关于</a>
</nav>
<button class="md:hidden text-gray-700 focus:outline-none">
<i class="fa fa-bars text-xl"></i>
</button>
</div>
</header>
<!-- 主内容区 -->
<main class="flex-grow container mx-auto px-4 py-8">
<!-- 介绍部分 -->
<section class="mb-12 text-center max-w-3xl mx-auto">
<h2 class="text-[clamp(1.8rem,3vw,2.5rem)] font-bold text-dark mb-4">轻松调整视频帧率</h2>
<p class="text-gray-600 text-lg mb-8">上传视频文件,选择目标帧率,一键处理并下载优化后的视频</p>
<div class="flex flex-wrap justify-center gap-4">
<div class="flex items-center px-4 py-2 bg-blue-50 rounded-full">
<i class="fa fa-check-circle text-primary mr-2"></i>
<span>高质量处理</span>
</div>
<div class="flex items-center px-4 py-2 bg-blue-50 rounded-full">
<i class="fa fa-check-circle text-primary mr-2"></i>
<span>快速转换</span>
</div>
<div class="flex items-center px-4 py-2 bg-blue-50 rounded-full">
<i class="fa fa-check-circle text-primary mr-2"></i>
<span>多种格式支持</span>
</div>
</div>
</section>
<!-- 视频处理区域 -->
<section class="bg-white rounded-xl shadow-custom p-6 md:p-8 max-w-4xl mx-auto mb-12">
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 items-center">
<!-- 左侧:视频预览 -->
<div class="rounded-lg overflow-hidden border-2 border-gray-200 relative">
<div id="preview-container" class="w-full h-64 md:h-80 bg-gray-100 flex items-center justify-center">
<i class="fa fa-film text-gray-300 text-6xl"></i>
<p class="text-gray-400 mt-4">上传视频后在此预览</p>
</div>
<video id="preview-video" class="w-full h-full object-contain hidden" controls></video>
</div>
<!-- 右侧:处理选项 -->
<div>
<h3 class="text-xl font-semibold text-dark mb-4">视频设置</h3>
<!-- 文件上传 -->
<div class="mb-6">
<label class="block text-gray-700 mb-2">选择视频文件</label>
<div class="relative">
<input type="file" id="video-upload" accept="video/*" class="hidden">
<label for="video-upload" class="block w-full px-4 py-3 bg-primary/10 text-primary rounded-lg border-2 border-dashed border-primary/30 cursor-pointer hover:bg-primary/20 transition-custom text-center">
<i class="fa fa-upload mr-2"></i> 点击上传视频
</label>
</div>
<p id="file-name" class="text-sm text-gray-500 mt-2 hidden"></p>
</div>
<!-- 帧率选择 -->
<div class="mb-6">
<label for="fps" class="block text-gray-700 mb-2">目标帧率 (FPS)</label>
<div class="flex items-center">
<input type="range" id="fps-slider" min="5" max="60" value="30" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<span id="fps-value" class="ml-3 min-w-[3rem] text-center font-medium">30</span>
</div>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>5 FPS</span>
<span>60 FPS</span>
</div>
</div>
<!-- 按钮组 -->
<div class="flex flex-wrap gap-3">
<button id="process-btn" class="flex-1 px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary/90 transition-custom shadow-md hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center">
<i class="fa fa-cog mr-2"></i> 处理视频
</button>
<button id="download-btn" class="flex-1 px-6 py-3 bg-secondary text-white rounded-lg hover:bg-secondary/90 transition-custom shadow-md hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center" disabled>
<i class="fa fa-download mr-2"></i> 下载视频
</button>
</div>
</div>
</div>
<!-- 进度条 -->
<div id="progress-container" class="mt-6 hidden">
<div class="h-4 bg-gray-200 rounded-full overflow-hidden">
<div id="progress-bar" class="progress-bar h-full"></div>
</div>
<p id="progress-text" class="text-sm text-gray-600 mt-2 text-center">处理中...</p>
</div>
</section>
<!-- 功能特点 -->
<section id="features" class="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-5xl mx-auto mb-16">
<div class="bg-white p-6 rounded-xl shadow-custom hover:shadow-lg transition-custom">
<div class="w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-4">
<i class="fa fa-bolt text-primary text-xl"></i>
</div>
<h3 class="text-lg font-semibold text-dark mb-2">快速处理</h3>
<p class="text-gray-600">利用高效算法和优化技术,确保视频处理速度快且质量高。</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-custom hover:shadow-lg transition-custom">
<div class="w-12 h-12 bg-accent/10 rounded-full flex items-center justify-center mb-4">
<i class="fa fa-sliders text-accent text-xl"></i>
</div>
<h3 class="text-lg font-semibold text-dark mb-2">灵活控制</h3>
<p class="text-gray-600">精确调整视频帧率,满足不同场景需求,从慢动作到高速播放。</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-custom hover:shadow-lg transition-custom">
<div class="w-12 h-12 bg-secondary/10 rounded-full flex items-center justify-center mb-4">
<i class="fa fa-shield-alt text-secondary text-xl"></i>
</div>
<h3 class="text-lg font-semibold text-dark mb-2">安全可靠</h3>
<p class="text-gray-600">本地处理视频,不会上传到服务器,确保您的视频内容安全。</p>
</div>
</section>
<!-- 关于部分 -->
<section id="about" class="bg-white rounded-xl shadow-custom p-6 md:p-8 max-w-4xl mx-auto">
<h3 class="text-xl font-semibold text-dark mb-4">关于视频帧率处理</h3>
<p class="text-gray-600 mb-4">
帧率(FPS,Frames Per Second)是视频的重要参数,影响视频的流畅度和文件大小。较高的帧率(如60 FPS)使视频更加流畅,适合动作场景;较低的帧率(如15 FPS)则可减小文件大小,但可能显得不够流畅。
</p>
<p class="text-gray-600">
本工具允许您根据需要调整视频帧率,无论是为了创建特殊效果、优化视频大小,还是适配特定设备。我们使用先进的视频处理技术,确保在转换过程中保持视频质量。
</p>
</section>
</main>
<!-- 页脚 -->
<footer class="bg-dark text-white py-8">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h4 class="text-lg font-semibold mb-4">视频帧率处理器</h4>
<p class="text-gray-400">轻松调整视频帧率,优化您的视频体验。</p>
</div>
<div>
<h4 class="text-lg font-semibold mb-4">联系我们</h4>
<ul class="space-y-2 text-gray-400">
<li class="flex items-center">
<i class="fa fa-envelope mr-2"></i>
<a href="mailto:support@example.com" class="hover:text-white transition-custom">support@example.com</a>
</li>
<li class="flex items-center">
<i class="fa fa-phone mr-2"></i>
<span>+86 123 4567 8901</span>
</li>
</ul>
</div>
<div>
<h4 class="text-lg font-semibold mb-4">关注我们</h4>
<div class="flex space-x-4">
<a href="#" class="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-custom">
<i class="fa fa-weixin"></i>
</a>
<a href="#" class="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-custom">
<i class="fa fa-weibo"></i>
</a>
<a href="#" class="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-custom">
<i class="fa fa-github"></i>
</a>
</div>
</div>
</div>
<div class="border-t border-gray-700 mt-8 pt-6 text-center text-gray-400">
<p>© 2023 视频帧率处理器 | 保留所有权利</p>
</div>
</div>
</footer>
<script>
// DOM 元素
const videoUpload = document.getElementById('video-upload');
const previewContainer = document.getElementById('preview-container');
const previewVideo = document.getElementById('preview-video');
const fileName = document.getElementById('file-name');
const fpsSlider = document.getElementById('fps-slider');
const fpsValue = document.getElementById('fps-value');
const processBtn = document.getElementById('process-btn');
const downloadBtn = document.getElementById('download-btn');
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
let processedVideoUrl = null;
// 监听文件上传
videoUpload.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
// 显示文件名
fileName.textContent = `已选择: ${file.name}`;
fileName.classList.remove('hidden');
// 预览视频
const videoUrl = URL.createObjectURL(file);
previewVideo.src = videoUrl;
previewVideo.classList.remove('hidden');
previewContainer.classList.add('hidden');
// 启用处理按钮
processBtn.disabled = false;
});
// 监听帧率滑块
fpsSlider.addEventListener('input', () => {
fpsValue.textContent = fpsSlider.value;
});
// 处理视频按钮点击
processBtn.addEventListener('click', async () => {
const file = videoUpload.files[0];
if (!file) return;
const targetFps = parseInt(fpsSlider.value);
// 显示进度条
progressContainer.classList.remove('hidden');
progressBar.style.transform = 'scaleX(0)';
progressText.textContent = '准备处理视频...';
// 禁用按钮
processBtn.disabled = true;
downloadBtn.disabled = true;
try {
// 模拟处理进度
await simulateProgress(targetFps);
// 处理完成,启用下载按钮
downloadBtn.disabled = false;
progressText.textContent = '处理完成!';
// 创建一个模拟的处理后视频URL(实际项目中应从服务器获取)
processedVideoUrl = createMockProcessedVideoUrl(file, targetFps);
} catch (error) {
console.error('处理视频时出错:', error);
progressText.textContent = '处理失败,请重试';
}
});
// 下载按钮点击
downloadBtn.addEventListener('click', () => {
if (!processedVideoUrl) return;
// 创建下载链接
const a = document.createElement('a');
a.href = processedVideoUrl;
a.download = `processed_${fileName.textContent.split(': ')[1].split('.')[0]}_${fpsSlider.value}fps.mp4`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
// 模拟处理进度(实际项目中应从服务器获取进度)
async function simulateProgress(targetFps) {
return new Promise((resolve) => {
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
resolve();
}
progressBar.style.transform = `scaleX(${progress / 100})`;
progressText.textContent = `处理中: ${Math.round(progress)}% - 调整至 ${targetFps} FPS`;
}, 200);
});
}
// 创建模拟的处理后视频URL(实际项目中应从服务器获取)
function createMockProcessedVideoUrl(originalFile, targetFps) {
// 在实际项目中,这里应该是从服务器获取的处理后视频URL
// 这里仅作演示,返回原始视频URL
return URL.createObjectURL(originalFile);
}
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
</script>
</body>
</html>
server.py
import os
import subprocess
from flask import Flask, request, jsonify, send_file
from werkzeug.utils import secure_filename
import uuid
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['PROCESSED_FOLDER'] = 'processed'
app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 500 # 500MB
# 创建上传和处理文件夹
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['PROCESSED_FOLDER'], exist_ok=True)
@app.route('/')
def index():
return "视频帧率处理API"
@app.route('/process', methods=['POST'])
def process_video():
# 检查是否有文件上传
if 'video' not in request.files:
return jsonify({"error": "未找到视频文件"}), 400
video_file = request.files['video']
# 检查文件是否有名称
if video_file.filename == '':
return jsonify({"error": "未选择视频文件"}), 400
# 检查文件扩展名是否为视频格式
allowed_extensions = {'mp4', 'avi', 'mov', 'mkv', 'webm'}
file_ext = video_file.filename.rsplit('.', 1)[1].lower()
if file_ext not in allowed_extensions:
return jsonify({"error": "不支持的文件格式"}), 400
# 生成安全的文件名
secure_name = secure_filename(video_file.filename)
unique_id = str(uuid.uuid4())
upload_path = os.path.join(app.config['UPLOAD_FOLDER'], f"{unique_id}_{secure_name}")
video_file.save(upload_path)
# 获取目标帧率
target_fps = request.form.get('fps', 30)
try:
target_fps = int(target_fps)
if target_fps < 5 or target_fps > 120:
target_fps = 30
except ValueError:
target_fps = 30
# 处理视频
processed_filename = f"{unique_id}_processed_{target_fps}fps.mp4"
processed_path = os.path.join(app.config['PROCESSED_FOLDER'], processed_filename)
try:
# 使用FFmpeg处理视频帧率
ffmpeg_cmd = [
'ffmpeg',
'-i', upload_path,
'-r', str(target_fps),
'-c:v', 'libx264',
'-preset', 'medium',
'-crf', '23',
'-c:a', 'aac',
'-strict', 'experimental',
'-b:a', '128k',
processed_path
]
# 执行FFmpeg命令
process = subprocess.run(
ffmpeg_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
if process.returncode != 0:
print("FFmpeg错误:", process.stderr)
return jsonify({"error": "视频处理失败"}), 500
# 处理成功,返回结果
return jsonify({
"success": True,
"original_filename": video_file.filename,
"processed_filename": processed_filename,
"target_fps": target_fps,
"download_url": f"/download/{processed_filename}"
})
except Exception as e:
print("处理异常:", str(e))
return jsonify({"error": "服务器内部错误"}), 500
finally:
# 清理上传的原始文件(可选)
# os.remove(upload_path)
pass
@app.route('/download/<filename>')
def download_processed_file(filename):
processed_path = os.path.join(app.config['PROCESSED_FOLDER'], filename)
if not os.path.exists(processed_path):
return jsonify({"error": "文件不存在"}), 404
return send_file(processed_path, as_attachment=True)
if __name__ == '__main__':
app.run(debug=True)