BREAKING CHANGE(recorder): Remove FFmpeg-based MP4 conversion; simplify recorder/export to WebM and improve recorder/editor robustness

This commit is contained in:
2025-12-11 19:02:02 +00:00
parent d90df9717b
commit 12c85fa4cb
10 changed files with 32 additions and 522 deletions

View File

@@ -1,6 +1,5 @@
import { DeesElement, customElement, html, css, property, state, type TemplateResult } from '@design.estate/dees-element';
import { RecorderService } from '../services/recorder.service.js';
import { getFFmpegService, type IConversionProgress } from '../services/ffmpeg.service.js';
import type { WccDashboard } from './wcc-dashboard.js';
@customElement('wcc-recording-panel')
@@ -52,15 +51,6 @@ export class WccRecordingPanel extends DeesElement {
@state()
accessor isExporting: boolean = false;
@state()
accessor outputFormat: 'mp4' | 'webm' = 'mp4';
@state()
accessor conversionProgress: IConversionProgress | null = null;
@state()
accessor isConverting: boolean = false;
// Service instance
private recorderService: RecorderService;
@@ -563,94 +553,6 @@ export class WccRecordingPanel extends DeesElement {
to { transform: rotate(360deg); }
}
/* Format Selection Styles */
.format-section {
margin-top: 1.25rem;
padding-top: 1.25rem;
border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.format-section-header {
margin-bottom: 0.75rem;
}
.format-section-title {
font-size: 0.75rem;
font-weight: 500;
color: #888;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.format-buttons {
display: flex;
gap: 0.5rem;
}
.format-btn {
flex: 1;
padding: 0.6rem 0.75rem;
background: var(--input);
border: 1px solid transparent;
border-radius: var(--radius-sm);
color: #999;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.15s ease;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.format-btn:hover {
border-color: var(--primary);
color: #ccc;
}
.format-btn.selected {
background: rgba(59, 130, 246, 0.15);
border-color: var(--primary);
color: var(--primary);
}
.format-icon {
font-size: 1rem;
}
.format-note {
margin-top: 0.5rem;
padding: 0.5rem;
background: rgba(59, 130, 246, 0.1);
border-radius: var(--radius-sm);
font-size: 0.7rem;
color: #888;
display: flex;
align-items: center;
gap: 0.5rem;
}
.note-icon {
font-size: 0.9rem;
}
/* Conversion Progress Styles */
.conversion-progress {
display: flex;
align-items: center;
gap: 0.5rem;
}
.progress-text {
font-size: 0.75rem;
}
.progress-percent {
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.7rem;
color: rgba(255, 255, 255, 0.7);
}
`
];
@@ -806,51 +708,15 @@ export class WccRecordingPanel extends DeesElement {
</div>
</div>
<!-- Format Selection Section -->
<div class="format-section">
<div class="format-section-header">
<span class="format-section-title">Output Format</span>
</div>
<div class="format-buttons">
<button
class="format-btn ${this.outputFormat === 'mp4' ? 'selected' : ''}"
@click=${() => this.outputFormat = 'mp4'}
>
<span class="format-icon">📱</span>
MP4 (Universal)
</button>
<button
class="format-btn ${this.outputFormat === 'webm' ? 'selected' : ''}"
@click=${() => this.outputFormat = 'webm'}
>
<span class="format-icon">🌐</span>
WebM (Web Only)
</button>
</div>
${this.outputFormat === 'mp4' ? html`
<div class="format-note">
<span class="note-icon"></span>
MP4 requires conversion (~31MB download on first use)
</div>
` : null}
</div>
</div>
<div class="preview-modal-actions">
<button class="preview-btn secondary" @click=${() => this.discardRecording()}>Discard</button>
<button
class="preview-btn primary"
?disabled=${this.isExporting || this.isConverting}
?disabled=${this.isExporting}
@click=${() => this.downloadRecording()}
>
${this.isConverting ? html`
<div class="conversion-progress">
<span class="export-spinner"></span>
<span class="progress-text">${this.conversionProgress?.message || 'Converting...'}</span>
${this.conversionProgress?.progress !== undefined ? html`
<span class="progress-percent">${this.conversionProgress.progress}%</span>
` : null}
</div>
` : this.isExporting ? html`<span class="export-spinner"></span>Exporting...` : `Download ${this.outputFormat.toUpperCase()}`}
${this.isExporting ? html`<span class="export-spinner"></span>Exporting...` : 'Download WebM'}
</button>
</div>
</div>
@@ -951,7 +817,7 @@ export class WccRecordingPanel extends DeesElement {
try {
let blobToDownload: Blob;
// Step 1: Handle trimming if needed
// Handle trimming if needed
const needsTrim = this.trimStart > 0.1 || this.trimEnd < this.videoDuration - 0.1;
if (needsTrim) {
@@ -965,43 +831,9 @@ export class WccRecordingPanel extends DeesElement {
blobToDownload = recordedBlob;
}
// Step 2: Convert to MP4 if selected
if (this.outputFormat === 'mp4') {
this.isConverting = true;
this.isExporting = false; // Switch to conversion state
try {
const ffmpegService = getFFmpegService();
blobToDownload = await ffmpegService.convertToMp4({
inputBlob: blobToDownload,
outputFormat: 'mp4',
onProgress: (progress) => {
this.conversionProgress = progress;
}
});
} catch (conversionError) {
console.error('MP4 conversion failed:', conversionError);
// Offer WebM fallback
const useFallback = confirm(
'MP4 conversion failed. Would you like to download as WebM instead?'
);
if (!useFallback) {
this.isConverting = false;
this.conversionProgress = null;
return;
}
// Continue with original WebM blob (already set)
this.outputFormat = 'webm';
}
this.isConverting = false;
this.conversionProgress = null;
}
// Step 3: Trigger download
// Trigger download
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const extension = this.outputFormat;
const filename = `wcctools-recording-${timestamp}.${extension}`;
const filename = `wcctools-recording-${timestamp}.webm`;
const url = URL.createObjectURL(blobToDownload);
const a = document.createElement('a');
@@ -1016,8 +848,6 @@ export class WccRecordingPanel extends DeesElement {
} catch (error) {
console.error('Error exporting video:', error);
this.isExporting = false;
this.isConverting = false;
this.conversionProgress = null;
}
}