percent based resolution
All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m9s

This commit is contained in:
2026-02-09 18:33:21 -05:00
parent cbd5de8cb3
commit e8eb1fe472
2 changed files with 116 additions and 39 deletions

13
app.py
View File

@@ -124,13 +124,12 @@ def process_video():
if width and height:
filters.append(f"crop={width}:{height}:{x}:{y}")
# Add scale filter if resolution specified
resolution = data.get('resolution')
if resolution:
width = resolution.get('width')
height = resolution.get('height')
if width and height:
filters.append(f"scale={width}:{height}")
# Add scale filter if scale percentage specified (not 100%)
scale_percentage = data.get('scale_percentage', 100)
if scale_percentage != 100:
# Scale based on percentage - ffmpeg will apply this after crop
scale_filter = f"scale=iw*{scale_percentage/100}:ih*{scale_percentage/100}"
filters.append(scale_filter)
# Apply filters if any
if filters:

View File

@@ -184,6 +184,63 @@
font-weight: 600;
}
.scale-slider-container {
margin: 15px 0;
}
.scale-slider {
width: 100%;
height: 8px;
border-radius: 4px;
background: #e9ecef;
outline: none;
-webkit-appearance: none;
}
.scale-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
cursor: pointer;
border: 2px solid white;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.scale-slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
cursor: pointer;
border: 2px solid white;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.scale-display {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.scale-value {
font-size: 1.2em;
font-weight: 600;
color: #667eea;
}
.resolution-preview {
background: white;
padding: 10px;
border-radius: 6px;
margin-top: 10px;
font-size: 0.9em;
color: #495057;
}
.timeline-container {
margin: 15px 0;
padding: 20px;
@@ -513,24 +570,23 @@
</div>
</div>
<!-- Resolution Controls -->
<!-- Scale Controls -->
<div class="form-group">
<label>📐 Output Resolution</label>
<div class="preset-resolutions">
<button class="preset-btn" data-width="3840" data-height="2160">4K (3840x2160)</button>
<button class="preset-btn" data-width="1920" data-height="1080">1080p (1920x1080)</button>
<button class="preset-btn" data-width="1280" data-height="720">720p (1280x720)</button>
<button class="preset-btn" data-width="854" data-height="480">480p (854x480)</button>
<button class="preset-btn active" data-width="0" data-height="0">Original</button>
<label>📐 Output Scale</label>
<div class="scale-slider-container">
<div class="scale-display">
<span>Scale: <span class="scale-value" id="scale-value-display">100%</span></span>
<span id="resolution-info" style="color: #6c757d;">Output: <strong id="output-resolution">--</strong></span>
</div>
<div class="form-row">
<div>
<label style="font-weight: normal;">Width</label>
<input type="number" id="width" placeholder="Original" min="0">
<input type="range" class="scale-slider" id="scale-slider" min="10" max="100" value="100" step="5">
<div class="resolution-preview" id="resolution-preview">
<p style="margin: 0;">💡 <strong>How it works:</strong> Scale is applied to the cropped area (or original if no crop). At 50%, a 200x400 crop becomes 100x200.</p>
</div>
<div>
<label style="font-weight: normal;">Height</label>
<input type="number" id="height" placeholder="Original" min="0">
<div class="preset-resolutions" style="margin-top: 15px;">
<button class="preset-btn" data-scale="100">100% (Original)</button>
<button class="preset-btn" data-scale="75">75%</button>
<button class="preset-btn" data-scale="50">50%</button>
<button class="preset-btn" data-scale="25">25%</button>
</div>
</div>
</div>
@@ -595,6 +651,7 @@
let isDraggingEnd = false;
let timelineStartPos = 0;
let timelineEndPos = 100;
let scalePercentage = 100;
// Upload area handling
const uploadArea = document.getElementById('upload-area');
@@ -674,6 +731,9 @@
// Set default values
document.getElementById('end-time').value = videoInfo.duration.toFixed(2);
scalePercentage = 100;
scaleSlider.value = 100;
updateScaleDisplay();
// Setup canvas for cropping
setupCropCanvas();
@@ -915,26 +975,47 @@
updateTimeline();
});
// Resolution presets
// Scale slider
const scaleSlider = document.getElementById('scale-slider');
const scaleValueDisplay = document.getElementById('scale-value-display');
const outputResolution = document.getElementById('output-resolution');
function updateScaleDisplay() {
scalePercentage = parseInt(scaleSlider.value);
scaleValueDisplay.textContent = scalePercentage + '%';
// Calculate output resolution
const cropWidth = parseInt(document.getElementById('crop-width').value) || videoInfo.width;
const cropHeight = parseInt(document.getElementById('crop-height').value) || videoInfo.height;
const outputWidth = Math.round((cropWidth * scalePercentage) / 100);
const outputHeight = Math.round((cropHeight * scalePercentage) / 100);
outputResolution.textContent = `${outputWidth}x${outputHeight}`;
}
scaleSlider.addEventListener('input', updateScaleDisplay);
// Scale presets
document.querySelectorAll('.preset-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.preset-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const width = btn.dataset.width;
const height = btn.dataset.height;
document.getElementById('width').value = width === '0' ? '' : width;
document.getElementById('height').value = height === '0' ? '' : height;
const scale = btn.dataset.scale;
if (scale) {
scaleSlider.value = scale;
updateScaleDisplay();
}
});
});
// Update scale display when crop values change
['crop-width', 'crop-height'].forEach(id => {
document.getElementById(id).addEventListener('input', updateScaleDisplay);
});
// Process video
document.getElementById('process-btn').addEventListener('click', async () => {
const startTime = parseFloat(document.getElementById('start-time').value) || 0;
const endTime = parseFloat(document.getElementById('end-time').value) || videoInfo.duration;
const width = parseInt(document.getElementById('width').value) || 0;
const height = parseInt(document.getElementById('height').value) || 0;
const cropX = parseInt(document.getElementById('crop-x').value) || 0;
const cropY = parseInt(document.getElementById('crop-y').value) || 0;
const cropWidth = parseInt(document.getElementById('crop-width').value) || 0;
@@ -943,13 +1024,10 @@
const payload = {
file_id: currentFileId,
start_time: startTime,
end_time: endTime
end_time: endTime,
scale_percentage: scalePercentage
};
if (width > 0 && height > 0) {
payload.resolution = { width, height };
}
if (cropWidth > 0 && cropHeight > 0) {
payload.crop = {
x: cropX,