raju014 commited on
Commit
aa027fd
Β·
verified Β·
1 Parent(s): 9df276c

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +8 -5
  2. README.md +1 -7
  3. app.py +304 -155
  4. requirements.txt +3 -4
Dockerfile CHANGED
@@ -1,18 +1,21 @@
1
- FROM python:3.12-slim
2
 
3
  WORKDIR /app
4
 
5
- # Install system dependencies
6
  RUN apt-get update && apt-get install -y \
7
  ffmpeg \
8
  && rm -rf /var/lib/apt/lists/*
9
 
10
- # Copy requirements and install
11
  COPY requirements.txt .
12
  RUN pip install --no-cache-dir -r requirements.txt
13
 
14
  # Copy app
15
- COPY . .
16
 
17
- # Run the app
 
 
 
18
  CMD ["python", "app.py"]
 
1
+ FROM python:3.11-slim
2
 
3
  WORKDIR /app
4
 
5
+ # Install FFmpeg
6
  RUN apt-get update && apt-get install -y \
7
  ffmpeg \
8
  && rm -rf /var/lib/apt/lists/*
9
 
10
+ # Install Python dependencies
11
  COPY requirements.txt .
12
  RUN pip install --no-cache-dir -r requirements.txt
13
 
14
  # Copy app
15
+ COPY app.py .
16
 
17
+ # Expose port
18
+ EXPOSE 7860
19
+
20
+ # Run
21
  CMD ["python", "app.py"]
README.md CHANGED
@@ -20,13 +20,7 @@ Upload any video and upscale it to:
20
  - High-quality **Lanczos scaling** algorithm
21
  - Multiple quality presets (Fast, Balanced, Best)
22
  - CPU-optimized (works on free HuggingFace Spaces)
23
-
24
- ## Usage:
25
- 1. Upload your video
26
- 2. Select target resolution
27
- 3. Choose quality preset
28
- 4. Click "Upscale Video"
29
- 5. Download the result!
30
 
31
  ## Technical Details:
32
  - Uses FFmpeg with lanczos filter for best upscaling quality
 
20
  - High-quality **Lanczos scaling** algorithm
21
  - Multiple quality presets (Fast, Balanced, Best)
22
  - CPU-optimized (works on free HuggingFace Spaces)
23
+ - Built with FastAPI (fast and reliable)
 
 
 
 
 
 
24
 
25
  ## Technical Details:
26
  - Uses FFmpeg with lanczos filter for best upscaling quality
app.py CHANGED
@@ -1,181 +1,330 @@
1
  """
2
- Video Upscaler - HuggingFace Spaces
3
- Upscale videos to Ultra HD (4K) using FFmpeg with high-quality lanczos scaling.
4
- Optimized for CPU-only environments (2 CPU, 16GB RAM).
5
  """
6
 
7
- import gradio as gr
 
 
8
  import subprocess
9
  import os
10
  import tempfile
11
  import shutil
 
 
 
12
 
13
  # Upscale resolutions
14
  RESOLUTIONS = {
15
- "720p (HD)": "1280:720",
16
- "1080p (Full HD)": "1920:1080",
17
- "1440p (2K)": "2560:1440",
18
- "2160p (4K Ultra HD)": "3840:2160",
19
  }
20
 
21
- def get_video_info(input_path):
22
- """Get video duration and resolution using ffprobe."""
23
- cmd = [
24
- 'ffprobe', '-v', 'error',
25
- '-select_streams', 'v:0',
26
- '-show_entries', 'stream=width,height,duration',
27
- '-show_entries', 'format=duration',
28
- '-of', 'csv=p=0',
29
- input_path
30
- ]
31
- try:
32
- result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
33
- output = result.stdout.strip().split('\n')
34
- # Parse width, height
35
- if output and ',' in output[0]:
36
- parts = output[0].split(',')
37
- width, height = int(parts[0]), int(parts[1])
38
- return width, height
39
- except:
40
- pass
41
- return None, None
42
 
43
- def upscale_video(input_video, target_resolution, quality_preset):
44
- """
45
- Upscale video to target resolution using FFmpeg.
46
-
47
- Args:
48
- input_video: Path to input video file
49
- target_resolution: Target resolution string (e.g., "3840:2160")
50
- quality_preset: Encoding quality preset
51
-
52
- Returns:
53
- Path to upscaled video
54
- """
55
- if input_video is None:
56
- return None, "❌ Please upload a video first!"
57
-
58
- # Get target dimensions
59
- target_dims = RESOLUTIONS.get(target_resolution, "3840:2160")
60
- target_width, target_height = map(int, target_dims.split(':'))
61
-
62
- # Get input video info
63
- input_width, input_height = get_video_info(input_video)
64
-
65
- if input_width and input_height:
66
- if input_width >= target_width and input_height >= target_height:
67
- return None, f"⚠️ Video already {input_width}x{input_height}. No upscaling needed!"
68
-
69
- # Create output path
70
- output_dir = tempfile.mkdtemp()
71
- output_path = os.path.join(output_dir, "upscaled_video.mp4")
72
-
73
- # Quality settings based on preset
74
- quality_settings = {
75
- "Fast (Lower Quality)": ["-crf", "23", "-preset", "fast"],
76
- "Balanced": ["-crf", "18", "-preset", "medium"],
77
- "Best Quality (Slow)": ["-crf", "15", "-preset", "slow"],
78
- }
79
-
80
- crf_preset = quality_settings.get(quality_preset, quality_settings["Balanced"])
81
-
82
- # FFmpeg command for high-quality upscaling
83
- cmd = [
84
- 'ffmpeg', '-y',
85
- '-i', input_video,
86
- # Video scaling with lanczos (highest quality scaling algorithm)
87
- '-vf', f'scale={target_width}:{target_height}:flags=lanczos',
88
- # Video codec settings
89
- '-c:v', 'libx264',
90
- *crf_preset,
91
- # Audio copy (no re-encoding)
92
- '-c:a', 'aac', '-b:a', '192k',
93
- # Output format
94
- '-movflags', '+faststart',
95
- output_path
96
- ]
97
-
98
- try:
99
- # Run FFmpeg
100
- process = subprocess.Popen(
101
- cmd,
102
- stdout=subprocess.PIPE,
103
- stderr=subprocess.PIPE,
104
- text=True
105
- )
106
-
107
- stdout, stderr = process.communicate(timeout=3600) # 1 hour timeout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- if process.returncode == 0 and os.path.exists(output_path):
110
- file_size_mb = os.path.getsize(output_path) / (1024 * 1024)
111
- status = f"βœ… Success! Upscaled to {target_width}x{target_height}\nπŸ“ File size: {file_size_mb:.1f} MB"
112
- return output_path, status
113
- else:
114
- return None, f"❌ FFmpeg error:\n{stderr[-500:]}"
115
 
116
- except subprocess.TimeoutExpired:
117
- process.kill()
118
- return None, "❌ Process timed out (>1 hour). Try a shorter video."
119
- except Exception as e:
120
- return None, f"❌ Error: {str(e)}"
121
-
122
- def create_interface():
123
- """Create Gradio interface."""
124
-
125
- with gr.Blocks(title="🎬 Video Upscaler", theme=gr.themes.Soft()) as app:
126
- gr.Markdown("""
127
- # 🎬 Video Upscaler to Ultra HD (4K)
128
-
129
- Upload a video and upscale it to higher resolution using high-quality lanczos scaling.
 
 
 
 
 
 
 
130
 
131
- **Supported:** MP4, MKV, AVI, MOV, WEBM
132
 
133
- **Note:** Processing time depends on video length. ~2-5 minutes per minute of video.
134
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- with gr.Row():
137
- with gr.Column(scale=1):
138
- input_video = gr.Video(label="πŸ“€ Upload Video")
139
-
140
- target_res = gr.Dropdown(
141
- choices=list(RESOLUTIONS.keys()),
142
- value="2160p (4K Ultra HD)",
143
- label="🎯 Target Resolution"
144
- )
 
 
 
 
 
 
145
 
146
- quality = gr.Dropdown(
147
- choices=["Fast (Lower Quality)", "Balanced", "Best Quality (Slow)"],
148
- value="Balanced",
149
- label="βš™οΈ Quality Preset"
150
- )
151
 
152
- upscale_btn = gr.Button("πŸš€ Upscale Video", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
- with gr.Column(scale=1):
155
- output_video = gr.Video(label="πŸ“₯ Upscaled Video")
156
- status_text = gr.Textbox(label="πŸ“Š Status", lines=3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
- gr.Markdown("""
159
- ---
160
- ### πŸ’‘ Tips:
161
- - **4K (2160p)**: Best for large screens, 4x resolution of 1080p
162
- - **2K (1440p)**: Good balance of quality and file size
163
- - **Best Quality (Slow)**: Use CRF 15, takes longer but looks amazing
164
- - For very long videos, consider processing in parts
165
 
166
- ---
167
- *Powered by FFmpeg with Lanczos scaling algorithm*
168
- """)
 
169
 
170
- # Event handlers
171
- upscale_btn.click(
172
- fn=upscale_video,
173
- inputs=[input_video, target_res, quality],
174
- outputs=[output_video, status_text]
175
- )
176
-
177
- return app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
  if __name__ == "__main__":
180
- app = create_interface()
181
- app.launch(server_name="0.0.0.0", server_port=7860)
 
1
  """
2
+ Video Upscaler - HuggingFace Spaces (FastAPI + HTML)
3
+ Simple, reliable video upscaling using FFmpeg.
4
+ No Gradio - cleaner and fewer dependencies.
5
  """
6
 
7
+ from fastapi import FastAPI, File, UploadFile, Form
8
+ from fastapi.responses import HTMLResponse, FileResponse
9
+ from fastapi.staticfiles import StaticFiles
10
  import subprocess
11
  import os
12
  import tempfile
13
  import shutil
14
+ import uuid
15
+
16
+ app = FastAPI(title="Video Upscaler 4K")
17
 
18
  # Upscale resolutions
19
  RESOLUTIONS = {
20
+ "720p": (1280, 720),
21
+ "1080p": (1920, 1080),
22
+ "1440p": (2560, 1440),
23
+ "2160p": (3840, 2160),
24
  }
25
 
26
+ # Create temp directory for outputs
27
+ OUTPUT_DIR = "/tmp/upscaled"
28
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ HTML_PAGE = """
31
+ <!DOCTYPE html>
32
+ <html lang="en">
33
+ <head>
34
+ <meta charset="UTF-8">
35
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
36
+ <title>🎬 Video Upscaler 4K</title>
37
+ <style>
38
+ * { margin: 0; padding: 0; box-sizing: border-box; }
39
+ body {
40
+ font-family: 'Segoe UI', system-ui, sans-serif;
41
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
42
+ min-height: 100vh;
43
+ color: #fff;
44
+ padding: 20px;
45
+ }
46
+ .container {
47
+ max-width: 600px;
48
+ margin: 0 auto;
49
+ background: rgba(255,255,255,0.1);
50
+ backdrop-filter: blur(10px);
51
+ border-radius: 20px;
52
+ padding: 30px;
53
+ box-shadow: 0 8px 32px rgba(0,0,0,0.3);
54
+ }
55
+ h1 {
56
+ text-align: center;
57
+ margin-bottom: 10px;
58
+ font-size: 2em;
59
+ }
60
+ .subtitle {
61
+ text-align: center;
62
+ color: #aaa;
63
+ margin-bottom: 30px;
64
+ }
65
+ .form-group {
66
+ margin-bottom: 20px;
67
+ }
68
+ label {
69
+ display: block;
70
+ margin-bottom: 8px;
71
+ font-weight: 500;
72
+ }
73
+ input[type="file"] {
74
+ width: 100%;
75
+ padding: 15px;
76
+ background: rgba(255,255,255,0.1);
77
+ border: 2px dashed #666;
78
+ border-radius: 10px;
79
+ color: #fff;
80
+ cursor: pointer;
81
+ }
82
+ input[type="file"]:hover {
83
+ border-color: #00d9ff;
84
+ }
85
+ select {
86
+ width: 100%;
87
+ padding: 12px;
88
+ border-radius: 8px;
89
+ border: none;
90
+ background: rgba(255,255,255,0.2);
91
+ color: #fff;
92
+ font-size: 16px;
93
+ }
94
+ select option {
95
+ background: #1a1a2e;
96
+ color: #fff;
97
+ }
98
+ button {
99
+ width: 100%;
100
+ padding: 15px;
101
+ background: linear-gradient(135deg, #00d9ff, #0066ff);
102
+ border: none;
103
+ border-radius: 10px;
104
+ color: #fff;
105
+ font-size: 18px;
106
+ font-weight: 600;
107
+ cursor: pointer;
108
+ transition: transform 0.2s, box-shadow 0.2s;
109
+ }
110
+ button:hover {
111
+ transform: translateY(-2px);
112
+ box-shadow: 0 5px 20px rgba(0,217,255,0.4);
113
+ }
114
+ button:disabled {
115
+ background: #555;
116
+ cursor: not-allowed;
117
+ transform: none;
118
+ }
119
+ .status {
120
+ margin-top: 20px;
121
+ padding: 15px;
122
+ border-radius: 10px;
123
+ text-align: center;
124
+ display: none;
125
+ }
126
+ .status.show { display: block; }
127
+ .status.processing {
128
+ background: rgba(255,193,7,0.2);
129
+ border: 1px solid #ffc107;
130
+ }
131
+ .status.success {
132
+ background: rgba(40,167,69,0.2);
133
+ border: 1px solid #28a745;
134
+ }
135
+ .status.error {
136
+ background: rgba(220,53,69,0.2);
137
+ border: 1px solid #dc3545;
138
+ }
139
+ .download-link {
140
+ display: inline-block;
141
+ margin-top: 10px;
142
+ padding: 10px 20px;
143
+ background: #28a745;
144
+ color: #fff;
145
+ text-decoration: none;
146
+ border-radius: 8px;
147
+ }
148
+ .tips {
149
+ margin-top: 30px;
150
+ padding: 15px;
151
+ background: rgba(255,255,255,0.05);
152
+ border-radius: 10px;
153
+ font-size: 14px;
154
+ color: #aaa;
155
+ }
156
+ .tips h3 { color: #fff; margin-bottom: 10px; }
157
+ .tips li { margin: 5px 0; }
158
+ </style>
159
+ </head>
160
+ <body>
161
+ <div class="container">
162
+ <h1>🎬 Video Upscaler 4K</h1>
163
+ <p class="subtitle">Upscale your videos to Ultra HD quality</p>
164
 
165
+ <form id="uploadForm" enctype="multipart/form-data">
166
+ <div class="form-group">
167
+ <label>πŸ“€ Upload Video (MP4, MKV, AVI, MOV)</label>
168
+ <input type="file" name="video" id="videoInput" accept="video/*" required>
169
+ </div>
 
170
 
171
+ <div class="form-group">
172
+ <label>🎯 Target Resolution</label>
173
+ <select name="resolution" id="resolution">
174
+ <option value="720p">720p (HD)</option>
175
+ <option value="1080p">1080p (Full HD)</option>
176
+ <option value="1440p">1440p (2K)</option>
177
+ <option value="2160p" selected>2160p (4K Ultra HD)</option>
178
+ </select>
179
+ </div>
180
+
181
+ <div class="form-group">
182
+ <label>βš™οΈ Quality</label>
183
+ <select name="quality" id="quality">
184
+ <option value="fast">Fast (Lower Quality)</option>
185
+ <option value="balanced" selected>Balanced</option>
186
+ <option value="best">Best Quality (Slow)</option>
187
+ </select>
188
+ </div>
189
+
190
+ <button type="submit" id="submitBtn">πŸš€ Upscale Video</button>
191
+ </form>
192
 
193
+ <div id="status" class="status"></div>
194
 
195
+ <div class="tips">
196
+ <h3>πŸ’‘ Tips:</h3>
197
+ <ul>
198
+ <li>4K (2160p) = 3840x2160 pixels</li>
199
+ <li>Processing time: ~2-5 min per minute of video</li>
200
+ <li>Best Quality uses CRF 15 (near-lossless)</li>
201
+ <li>Max file size: 100MB for free tier</li>
202
+ </ul>
203
+ </div>
204
+ </div>
205
+
206
+ <script>
207
+ const form = document.getElementById('uploadForm');
208
+ const statusDiv = document.getElementById('status');
209
+ const submitBtn = document.getElementById('submitBtn');
210
 
211
+ form.addEventListener('submit', async (e) => {
212
+ e.preventDefault();
213
+
214
+ const formData = new FormData(form);
215
+
216
+ submitBtn.disabled = true;
217
+ submitBtn.textContent = '⏳ Processing...';
218
+ statusDiv.className = 'status show processing';
219
+ statusDiv.innerHTML = '⏳ Uploading and processing... This may take a few minutes.';
220
+
221
+ try {
222
+ const response = await fetch('/upscale', {
223
+ method: 'POST',
224
+ body: formData
225
+ });
226
 
227
+ const result = await response.json();
 
 
 
 
228
 
229
+ if (result.success) {
230
+ statusDiv.className = 'status show success';
231
+ statusDiv.innerHTML = `
232
+ βœ… Success! Video upscaled to ${result.resolution}<br>
233
+ πŸ“ Size: ${result.size_mb} MB<br>
234
+ <a href="${result.download_url}" class="download-link" download>πŸ“₯ Download Video</a>
235
+ `;
236
+ } else {
237
+ statusDiv.className = 'status show error';
238
+ statusDiv.innerHTML = '❌ Error: ' + result.error;
239
+ }
240
+ } catch (error) {
241
+ statusDiv.className = 'status show error';
242
+ statusDiv.innerHTML = '❌ Error: ' + error.message;
243
+ }
244
 
245
+ submitBtn.disabled = false;
246
+ submitBtn.textContent = 'πŸš€ Upscale Video';
247
+ });
248
+ </script>
249
+ </body>
250
+ </html>
251
+ """
252
+
253
+ @app.get("/", response_class=HTMLResponse)
254
+ async def home():
255
+ return HTML_PAGE
256
+
257
+ @app.post("/upscale")
258
+ async def upscale_video(
259
+ video: UploadFile = File(...),
260
+ resolution: str = Form("2160p"),
261
+ quality: str = Form("balanced")
262
+ ):
263
+ try:
264
+ # Get target dimensions
265
+ if resolution not in RESOLUTIONS:
266
+ resolution = "2160p"
267
+ width, height = RESOLUTIONS[resolution]
268
 
269
+ # Quality settings
270
+ quality_map = {
271
+ "fast": ("23", "fast"),
272
+ "balanced": ("18", "medium"),
273
+ "best": ("15", "slow")
274
+ }
275
+ crf, preset = quality_map.get(quality, ("18", "medium"))
276
 
277
+ # Save uploaded file
278
+ upload_id = str(uuid.uuid4())[:8]
279
+ input_path = f"/tmp/input_{upload_id}.mp4"
280
+ output_path = f"{OUTPUT_DIR}/upscaled_{upload_id}.mp4"
281
 
282
+ with open(input_path, "wb") as f:
283
+ content = await video.read()
284
+ f.write(content)
285
+
286
+ # FFmpeg command for high-quality upscaling
287
+ cmd = [
288
+ 'ffmpeg', '-y',
289
+ '-i', input_path,
290
+ '-vf', f'scale={width}:{height}:flags=lanczos',
291
+ '-c:v', 'libx264',
292
+ '-crf', crf,
293
+ '-preset', preset,
294
+ '-c:a', 'aac', '-b:a', '192k',
295
+ '-movflags', '+faststart',
296
+ output_path
297
+ ]
298
+
299
+ # Run FFmpeg
300
+ process = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
301
+
302
+ # Clean up input
303
+ os.remove(input_path)
304
+
305
+ if process.returncode == 0 and os.path.exists(output_path):
306
+ size_mb = round(os.path.getsize(output_path) / (1024 * 1024), 1)
307
+ return {
308
+ "success": True,
309
+ "resolution": f"{width}x{height}",
310
+ "size_mb": size_mb,
311
+ "download_url": f"/download/{upload_id}"
312
+ }
313
+ else:
314
+ return {"success": False, "error": "FFmpeg processing failed"}
315
+
316
+ except subprocess.TimeoutExpired:
317
+ return {"success": False, "error": "Processing timeout (>1 hour)"}
318
+ except Exception as e:
319
+ return {"success": False, "error": str(e)}
320
+
321
+ @app.get("/download/{upload_id}")
322
+ async def download_file(upload_id: str):
323
+ file_path = f"{OUTPUT_DIR}/upscaled_{upload_id}.mp4"
324
+ if os.path.exists(file_path):
325
+ return FileResponse(file_path, filename=f"upscaled_{upload_id}.mp4", media_type="video/mp4")
326
+ return {"error": "File not found"}
327
 
328
  if __name__ == "__main__":
329
+ import uvicorn
330
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt CHANGED
@@ -1,4 +1,3 @@
1
- gradio==4.44.1
2
- gradio_client==1.3.0
3
- huggingface_hub==0.25.2
4
- ffmpeg-python
 
1
+ fastapi
2
+ uvicorn
3
+ python-multipart