mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
feat: export quality UI and source-relative scaling
- Add 4th 'Original' quality tier to export UI - Change non-source export to use source-relative scaling (0.6x, 0.75x, 0.9x) - Update i18n for new quality label - Accept 'high' in project normalization Fixes blurry export and makes quality more intuitive
This commit is contained in:
@@ -813,7 +813,7 @@ export function SettingsPanel({
|
||||
</div>
|
||||
|
||||
{exportFormat === 'mp4' && (
|
||||
<div className="mb-3 bg-white/5 border border-white/5 p-0.5 w-full grid grid-cols-3 h-7 rounded-lg">
|
||||
<div className="mb-3 bg-white/5 border border-white/5 p-0.5 w-full grid grid-cols-4 h-7 rounded-lg">
|
||||
<button
|
||||
onClick={() => onExportQualityChange?.('medium')}
|
||||
className={cn(
|
||||
@@ -832,6 +832,15 @@ export function SettingsPanel({
|
||||
>
|
||||
{tSettings('export.quality.medium')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onExportQualityChange?.('high')}
|
||||
className={cn(
|
||||
"rounded-md transition-all text-[10px] font-medium",
|
||||
exportQuality === 'high' ? "bg-white text-black" : "text-slate-400 hover:text-slate-200"
|
||||
)}
|
||||
>
|
||||
{tSettings('export.quality.high')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onExportQualityChange?.('source')}
|
||||
className={cn(
|
||||
@@ -839,7 +848,7 @@ export function SettingsPanel({
|
||||
exportQuality === 'source' ? "bg-white text-black" : "text-slate-400 hover:text-slate-200"
|
||||
)}
|
||||
>
|
||||
{tSettings('export.quality.high')}
|
||||
{tSettings('export.quality.original')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1525,12 +1525,21 @@ export default function VideoEditor() {
|
||||
bitrate = 80_000_000;
|
||||
}
|
||||
} else {
|
||||
// Use quality-based target resolution
|
||||
const targetHeight = quality === 'medium' ? 720 : 1080;
|
||||
// Use source-relative quality scaling.
|
||||
// "source" is handled above; this branch maps the remaining tiers.
|
||||
const qualityScale =
|
||||
quality === 'medium' ? 0.6 : quality === 'good' ? 0.75 : 0.9;
|
||||
const maxWidth = Math.max(2, Math.floor((sourceWidth * qualityScale) / 2) * 2);
|
||||
const maxHeight = Math.max(2, Math.floor((sourceHeight * qualityScale) / 2) * 2);
|
||||
const maxAspect = maxWidth / maxHeight;
|
||||
|
||||
// Calculate dimensions maintaining aspect ratio
|
||||
exportHeight = Math.floor(targetHeight / 2) * 2;
|
||||
exportWidth = Math.floor((exportHeight * aspectRatioValue) / 2) * 2;
|
||||
if (aspectRatioValue >= maxAspect) {
|
||||
exportWidth = maxWidth;
|
||||
exportHeight = Math.max(2, Math.floor((exportWidth / aspectRatioValue) / 2) * 2);
|
||||
} else {
|
||||
exportHeight = maxHeight;
|
||||
exportWidth = Math.max(2, Math.floor((exportHeight * aspectRatioValue) / 2) * 2);
|
||||
}
|
||||
|
||||
// Adjust bitrate for lower resolutions
|
||||
const totalPixels = exportWidth * exportHeight;
|
||||
|
||||
@@ -358,7 +358,13 @@ export function normalizeProjectEditor(editor: Partial<ProjectEditorState>): Pro
|
||||
(validAspectRatios.has(editor.aspectRatio as AspectRatio) || isCustomAspectRatio(editor.aspectRatio))
|
||||
? (editor.aspectRatio as AspectRatio)
|
||||
: "16:9",
|
||||
exportQuality: editor.exportQuality === "medium" || editor.exportQuality === "source" ? editor.exportQuality : "good",
|
||||
exportQuality:
|
||||
editor.exportQuality === "medium" ||
|
||||
editor.exportQuality === "good" ||
|
||||
editor.exportQuality === "high" ||
|
||||
editor.exportQuality === "source"
|
||||
? editor.exportQuality
|
||||
: "good",
|
||||
exportFormat: editor.exportFormat === "gif" ? "gif" : "mp4",
|
||||
gifFrameRate:
|
||||
editor.gifFrameRate === 15 ||
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
"quality": {
|
||||
"low": "Low",
|
||||
"medium": "Medium",
|
||||
"high": "High"
|
||||
"high": "High",
|
||||
"original": "Original"
|
||||
},
|
||||
"loop": "Loop",
|
||||
"outputDimensions": "Output: {{dimensions}}px",
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
"quality": {
|
||||
"low": "Baja",
|
||||
"medium": "Media",
|
||||
"high": "Alta"
|
||||
"high": "Alta",
|
||||
"original": "Original"
|
||||
},
|
||||
"loop": "Bucle",
|
||||
"outputDimensions": "Salida: {{dimensions}}px",
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
"quality": {
|
||||
"low": "低",
|
||||
"medium": "中",
|
||||
"high": "高"
|
||||
"high": "高",
|
||||
"original": "原始"
|
||||
},
|
||||
"loop": "循环",
|
||||
"outputDimensions": "输出:{{dimensions}}px",
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface VideoFrameData {
|
||||
duration: number; // in microseconds
|
||||
}
|
||||
|
||||
export type ExportQuality = 'medium' | 'good' | 'source';
|
||||
export type ExportQuality = 'medium' | 'good' | 'high' | 'source';
|
||||
|
||||
// GIF Export Types
|
||||
export type ExportFormat = 'mp4' | 'gif';
|
||||
|
||||
Reference in New Issue
Block a user