feat(video): add video format family, resolution control, and marquee pair
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,8 @@ const DEFAULT_ICON_SIZE = 256;
|
|||||||
const ARCHIVE_COMPRESSION_FORMATS = ['zip', 'tar.gz', 'tar.bz2', '7z', 'tar.7z'];
|
const ARCHIVE_COMPRESSION_FORMATS = ['zip', 'tar.gz', 'tar.bz2', '7z', 'tar.7z'];
|
||||||
const AUDIO_BITRATE_FORMATS = ['mp3', 'ogg', 'aac', 'm4a'];
|
const AUDIO_BITRATE_FORMATS = ['mp3', 'ogg', 'aac', 'm4a'];
|
||||||
const AUDIO_BITRATES = [128, 192, 256, 320];
|
const AUDIO_BITRATES = [128, 192, 256, 320];
|
||||||
|
const VIDEO_RESOLUTION_FORMATS = ['mp4', 'webm', 'mov', 'avi', 'mkv'];
|
||||||
|
const VIDEO_RESOLUTIONS = [480, 720, 1080];
|
||||||
|
|
||||||
function rangeProgress(value, min, max) {
|
function rangeProgress(value, min, max) {
|
||||||
return `${((value - min) / (max - min)) * 100}%`;
|
return `${((value - min) / (max - min)) * 100}%`;
|
||||||
@@ -122,6 +124,33 @@ export function FileConfigCard({ item, index, t, onTargetFormatChange, onQuality
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{VIDEO_RESOLUTION_FORMATS.includes(item.targetFormat) && (
|
||||||
|
<div className="chip-field">
|
||||||
|
<span>{t('quality.resolution')}</span>
|
||||||
|
<div className="chip-group" role="group" aria-label={t('quality.resolution')}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`chip${item.quality === null ? ' chip-active' : ''}`}
|
||||||
|
aria-pressed={item.quality === null}
|
||||||
|
onClick={() => onQualityChange(index, null)}
|
||||||
|
>
|
||||||
|
{t('quality.original')}
|
||||||
|
</button>
|
||||||
|
{VIDEO_RESOLUTIONS.map((resolution) => (
|
||||||
|
<button
|
||||||
|
key={resolution}
|
||||||
|
type="button"
|
||||||
|
className={`chip${item.quality === resolution ? ' chip-active' : ''}`}
|
||||||
|
aria-pressed={item.quality === resolution}
|
||||||
|
onClick={() => onQualityChange(index, resolution)}
|
||||||
|
>
|
||||||
|
{resolution}p
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{item.targetFormat === 'ico' && (
|
{item.targetFormat === 'ico' && (
|
||||||
<div className="chip-field">
|
<div className="chip-field">
|
||||||
<span>{t('quality.iconSize')}</span>
|
<span>{t('quality.iconSize')}</span>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const PAIRS = [
|
|||||||
['epub', 'mobi'],
|
['epub', 'mobi'],
|
||||||
['rar', 'zip'],
|
['rar', 'zip'],
|
||||||
['wav', 'mp3'],
|
['wav', 'mp3'],
|
||||||
|
['mov', 'mp4'],
|
||||||
];
|
];
|
||||||
|
|
||||||
function Pill({ from, to }) {
|
function Pill({ from, to }) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Image, FileText, TextAa, BookOpen, Archive, MusicNotes } from '@phosphor-icons/react';
|
import { Image, FileText, TextAa, BookOpen, Archive, MusicNotes, VideoCamera } from '@phosphor-icons/react';
|
||||||
import { FORMAT_FAMILIES } from '../data/formats.js';
|
import { FORMAT_FAMILIES } from '../data/formats.js';
|
||||||
|
|
||||||
const FAMILY_ICONS = {
|
const FAMILY_ICONS = {
|
||||||
@@ -9,6 +9,7 @@ const FAMILY_ICONS = {
|
|||||||
ebooks: BookOpen,
|
ebooks: BookOpen,
|
||||||
archives: Archive,
|
archives: Archive,
|
||||||
audio: MusicNotes,
|
audio: MusicNotes,
|
||||||
|
video: VideoCamera,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FormatsGrid() {
|
export function FormatsGrid() {
|
||||||
|
|||||||
@@ -23,4 +23,8 @@ export const FORMAT_FAMILIES = [
|
|||||||
key: 'audio',
|
key: 'audio',
|
||||||
formats: ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a'],
|
formats: ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'video',
|
||||||
|
formats: ['mp4', 'webm', 'mov', 'avi', 'mkv'],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
--color-family-ebooks: #10b981;
|
--color-family-ebooks: #10b981;
|
||||||
--color-family-archives: #64748b;
|
--color-family-archives: #64748b;
|
||||||
--color-family-audio: #ec4899;
|
--color-family-audio: #ec4899;
|
||||||
|
--color-family-video: #38bdf8;
|
||||||
|
|
||||||
--font-sans: 'Plus Jakarta Sans', system-ui, 'Segoe UI', Roboto, sans-serif;
|
--font-sans: 'Plus Jakarta Sans', system-ui, 'Segoe UI', Roboto, sans-serif;
|
||||||
--font-display: 'Bricolage Grotesque', var(--font-sans);
|
--font-display: 'Bricolage Grotesque', var(--font-sans);
|
||||||
@@ -55,6 +56,7 @@
|
|||||||
--color-family-ebooks: #34d399;
|
--color-family-ebooks: #34d399;
|
||||||
--color-family-archives: #94a3b8;
|
--color-family-archives: #94a3b8;
|
||||||
--color-family-audio: #f472b6;
|
--color-family-audio: #f472b6;
|
||||||
|
--color-family-video: #7dd3fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
*, *::before, *::after {
|
*, *::before, *::after {
|
||||||
|
|||||||
@@ -23,7 +23,9 @@
|
|||||||
"compression": "Compression",
|
"compression": "Compression",
|
||||||
"iconSize": "Icon size",
|
"iconSize": "Icon size",
|
||||||
"compressPdf": "Compress as JPEG",
|
"compressPdf": "Compress as JPEG",
|
||||||
"bitrate": "Bitrate"
|
"bitrate": "Bitrate",
|
||||||
|
"resolution": "Resolution",
|
||||||
|
"original": "Original"
|
||||||
},
|
},
|
||||||
"job": {
|
"job": {
|
||||||
"converting": "Converting...",
|
"converting": "Converting...",
|
||||||
@@ -43,7 +45,8 @@
|
|||||||
"fonts": "Fonts",
|
"fonts": "Fonts",
|
||||||
"ebooks": "Ebooks",
|
"ebooks": "Ebooks",
|
||||||
"archives": "Archives",
|
"archives": "Archives",
|
||||||
"audio": "Audio"
|
"audio": "Audio",
|
||||||
|
"video": "Video"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"rights": "Ombrora Convert — Online file conversion"
|
"rights": "Ombrora Convert — Online file conversion"
|
||||||
|
|||||||
@@ -23,7 +23,9 @@
|
|||||||
"compression": "Compression",
|
"compression": "Compression",
|
||||||
"iconSize": "Taille de l'icône",
|
"iconSize": "Taille de l'icône",
|
||||||
"compressPdf": "Compresser en JPEG",
|
"compressPdf": "Compresser en JPEG",
|
||||||
"bitrate": "Bitrate"
|
"bitrate": "Bitrate",
|
||||||
|
"resolution": "Résolution",
|
||||||
|
"original": "Original"
|
||||||
},
|
},
|
||||||
"job": {
|
"job": {
|
||||||
"converting": "Conversion en cours...",
|
"converting": "Conversion en cours...",
|
||||||
@@ -43,7 +45,8 @@
|
|||||||
"fonts": "Polices",
|
"fonts": "Polices",
|
||||||
"ebooks": "Ebooks",
|
"ebooks": "Ebooks",
|
||||||
"archives": "Archives",
|
"archives": "Archives",
|
||||||
"audio": "Audio"
|
"audio": "Audio",
|
||||||
|
"video": "Vidéo"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"rights": "Ombrora Convert — Conversion de fichiers en ligne"
|
"rights": "Ombrora Convert — Conversion de fichiers en ligne"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Image, FileText, TextAa, BookOpen, Archive, MusicNotes, File as FileIcon } from '@phosphor-icons/react';
|
import { Image, FileText, TextAa, BookOpen, Archive, MusicNotes, VideoCamera, File as FileIcon } from '@phosphor-icons/react';
|
||||||
import { FORMAT_FAMILIES } from '../data/formats.js';
|
import { FORMAT_FAMILIES } from '../data/formats.js';
|
||||||
|
|
||||||
const FAMILY_ICONS = {
|
const FAMILY_ICONS = {
|
||||||
@@ -8,6 +8,7 @@ const FAMILY_ICONS = {
|
|||||||
ebooks: BookOpen,
|
ebooks: BookOpen,
|
||||||
archives: Archive,
|
archives: Archive,
|
||||||
audio: MusicNotes,
|
audio: MusicNotes,
|
||||||
|
video: VideoCamera,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function familyForExtension(extension) {
|
export function familyForExtension(extension) {
|
||||||
|
|||||||
Reference in New Issue
Block a user