411 lines
14 KiB
JavaScript
411 lines
14 KiB
JavaScript
/**
|
|
* Utility Functions for Quality of Life Improvements
|
|
*/
|
|
|
|
// ==============================================================================
|
|
// LOCAL STORAGE MANAGEMENT
|
|
// ==============================================================================
|
|
|
|
const Storage = {
|
|
/**
|
|
* Save last used settings
|
|
*/
|
|
saveLastSettings(platform, aspectRatio) {
|
|
try {
|
|
localStorage.setItem('lastPlatform', platform);
|
|
localStorage.setItem('lastAspectRatio', aspectRatio);
|
|
} catch (e) {
|
|
console.warn('Failed to save settings to localStorage:', e);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Load last used settings
|
|
*/
|
|
getLastSettings() {
|
|
try {
|
|
return {
|
|
platform: localStorage.getItem('lastPlatform'),
|
|
aspectRatio: localStorage.getItem('lastAspectRatio')
|
|
};
|
|
} catch (e) {
|
|
console.warn('Failed to load settings from localStorage:', e);
|
|
return { platform: null, aspectRatio: null };
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Clear saved settings
|
|
*/
|
|
clearSettings() {
|
|
try {
|
|
localStorage.removeItem('lastPlatform');
|
|
localStorage.removeItem('lastAspectRatio');
|
|
} catch (e) {
|
|
console.warn('Failed to clear settings from localStorage:', e);
|
|
}
|
|
}
|
|
};
|
|
|
|
// ==============================================================================
|
|
// CLIPBOARD UTILITIES
|
|
// ==============================================================================
|
|
|
|
const Clipboard = {
|
|
/**
|
|
* Copy text to clipboard with fallback
|
|
*/
|
|
async copy(text) {
|
|
try {
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
await navigator.clipboard.writeText(text);
|
|
return true;
|
|
} else {
|
|
// Fallback for older browsers
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = text;
|
|
textArea.style.position = 'fixed';
|
|
textArea.style.left = '-999999px';
|
|
document.body.appendChild(textArea);
|
|
textArea.select();
|
|
const success = document.execCommand('copy');
|
|
document.body.removeChild(textArea);
|
|
return success;
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to copy to clipboard:', error);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Copy video specs to clipboard
|
|
*/
|
|
async copyVideoSpecs(videoInfo, platform, aspectRatio) {
|
|
const specs = `
|
|
Video Specifications:
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
Platform: ${platform || 'N/A'}
|
|
Aspect Ratio: ${aspectRatio || 'N/A'}
|
|
Duration: ${videoInfo.duration ? Math.round(videoInfo.duration) + 's' : 'N/A'}
|
|
Resolution: ${videoInfo.width || '?'}x${videoInfo.height || '?'}
|
|
Bitrate: ${videoInfo.bitrate ? Math.round(videoInfo.bitrate / 1000) + ' kbps' : 'N/A'}
|
|
Codec: ${videoInfo.codec || 'N/A'}
|
|
`.trim();
|
|
|
|
const success = await this.copy(specs);
|
|
if (success) {
|
|
toast.success('Video specs copied to clipboard!');
|
|
} else {
|
|
toast.error('Failed to copy to clipboard');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Copy conversion results to clipboard
|
|
*/
|
|
async copyConversionResults(results) {
|
|
const text = `
|
|
Conversion Results:
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
Input Size: ${formatFileSize(results.input_size)}
|
|
Output Size: ${formatFileSize(results.output_size)}
|
|
Size Reduction: ${results.size_reduction_percent}%
|
|
Space Saved: ${formatFileSize(results.input_size - results.output_size)}
|
|
`.trim();
|
|
|
|
const success = await this.copy(text);
|
|
if (success) {
|
|
toast.success('Conversion results copied to clipboard!');
|
|
} else {
|
|
toast.error('Failed to copy to clipboard');
|
|
}
|
|
}
|
|
};
|
|
|
|
// ==============================================================================
|
|
// KEYBOARD SHORTCUTS
|
|
// ==============================================================================
|
|
|
|
const KeyboardShortcuts = {
|
|
init() {
|
|
document.addEventListener('keydown', (e) => {
|
|
// ESC - Clear/Reset
|
|
if (e.key === 'Escape') {
|
|
this.handleEscape();
|
|
}
|
|
|
|
// Ctrl/Cmd + Enter - Convert (if ready)
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
|
this.handleConvert();
|
|
}
|
|
|
|
// Ctrl/Cmd + K - Open help
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
|
e.preventDefault();
|
|
window.location.href = 'help.html';
|
|
}
|
|
});
|
|
},
|
|
|
|
handleEscape() {
|
|
// Close modals, clear selections, etc.
|
|
const configSection = document.getElementById('configSection');
|
|
const comparisonSection = document.getElementById('comparisonSection');
|
|
const uploadSection = document.getElementById('uploadSection');
|
|
|
|
if (comparisonSection && comparisonSection.style.display !== 'none') {
|
|
// Go back to upload
|
|
if (confirm('Start over with a new video?')) {
|
|
comparisonSection.style.display = 'none';
|
|
if (configSection) configSection.style.display = 'none';
|
|
if (uploadSection) uploadSection.style.display = 'block';
|
|
window.currentFileId = null;
|
|
toast.info('Ready for new upload');
|
|
}
|
|
} else if (configSection && configSection.style.display !== 'none') {
|
|
// Go back to upload
|
|
if (confirm('Cancel and start over?')) {
|
|
configSection.style.display = 'none';
|
|
if (uploadSection) uploadSection.style.display = 'block';
|
|
window.currentFileId = null;
|
|
toast.info('Upload cancelled');
|
|
}
|
|
}
|
|
},
|
|
|
|
handleConvert() {
|
|
const convertBtn = document.getElementById('convertBtn');
|
|
if (convertBtn && !convertBtn.disabled) {
|
|
convertBtn.click();
|
|
toast.info('Starting conversion... (Ctrl+Enter)');
|
|
}
|
|
}
|
|
};
|
|
|
|
// ==============================================================================
|
|
// FILE SIZE FORMATTING
|
|
// ==============================================================================
|
|
|
|
function formatFileSize(bytes) {
|
|
if (bytes === 0) return '0 Bytes';
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
|
}
|
|
|
|
// ==============================================================================
|
|
// TIME ESTIMATION
|
|
// ==============================================================================
|
|
|
|
function estimateConversionTime(fileSize, platform) {
|
|
// Rough estimates based on file size (in MB)
|
|
const sizeInMB = fileSize / (1024 * 1024);
|
|
|
|
// Base processing time: ~2-4 seconds per MB for H264
|
|
const baseTime = sizeInMB * 3;
|
|
|
|
// Add overhead for different platforms
|
|
const overhead = 5; // seconds
|
|
|
|
const totalSeconds = Math.max(10, baseTime + overhead);
|
|
|
|
if (totalSeconds < 60) {
|
|
return `~${Math.round(totalSeconds)} seconds`;
|
|
} else {
|
|
const minutes = Math.floor(totalSeconds / 60);
|
|
const seconds = Math.round(totalSeconds % 60);
|
|
return `~${minutes}m ${seconds}s`;
|
|
}
|
|
}
|
|
|
|
// ==============================================================================
|
|
// DRAG & DROP IMPROVEMENTS
|
|
// ==============================================================================
|
|
|
|
const DragDropEnhancer = {
|
|
init(dropZoneElement) {
|
|
if (!dropZoneElement) return;
|
|
|
|
let dragCounter = 0;
|
|
|
|
dropZoneElement.addEventListener('dragenter', (e) => {
|
|
e.preventDefault();
|
|
dragCounter++;
|
|
if (dragCounter === 1) {
|
|
dropZoneElement.classList.add('drag-over');
|
|
dropZoneElement.style.borderColor = '#FFC407';
|
|
dropZoneElement.style.background = 'rgba(255, 196, 7, 0.1)';
|
|
}
|
|
});
|
|
|
|
dropZoneElement.addEventListener('dragleave', (e) => {
|
|
e.preventDefault();
|
|
dragCounter--;
|
|
if (dragCounter === 0) {
|
|
dropZoneElement.classList.remove('drag-over');
|
|
dropZoneElement.style.borderColor = '';
|
|
dropZoneElement.style.background = '';
|
|
}
|
|
});
|
|
|
|
dropZoneElement.addEventListener('dragover', (e) => {
|
|
e.preventDefault();
|
|
});
|
|
|
|
dropZoneElement.addEventListener('drop', (e) => {
|
|
e.preventDefault();
|
|
dragCounter = 0;
|
|
dropZoneElement.classList.remove('drag-over');
|
|
dropZoneElement.style.borderColor = '';
|
|
dropZoneElement.style.background = '';
|
|
});
|
|
}
|
|
};
|
|
|
|
// ==============================================================================
|
|
// BETTER ERROR MESSAGES
|
|
// ==============================================================================
|
|
|
|
const ErrorHandler = {
|
|
getHelpfulErrorMessage(error) {
|
|
const errorStr = error.toString().toLowerCase();
|
|
|
|
if (errorStr.includes('network') || errorStr.includes('fetch')) {
|
|
return {
|
|
message: 'Network Error',
|
|
details: 'Cannot connect to the server. Please check your internet connection.',
|
|
action: 'Try refreshing the page or contact your administrator.'
|
|
};
|
|
}
|
|
|
|
if (errorStr.includes('timeout')) {
|
|
return {
|
|
message: 'Request Timeout',
|
|
details: 'The operation took too long to complete.',
|
|
action: 'Try with a smaller video file or try again later.'
|
|
};
|
|
}
|
|
|
|
if (errorStr.includes('file size') || errorStr.includes('too large')) {
|
|
return {
|
|
message: 'File Too Large',
|
|
details: 'The video file exceeds the maximum allowed size (500MB).',
|
|
action: 'Compress your video before uploading or use a smaller file.'
|
|
};
|
|
}
|
|
|
|
if (errorStr.includes('unauthorized') || errorStr.includes('403')) {
|
|
return {
|
|
message: 'Access Denied',
|
|
details: 'You do not have permission to perform this action.',
|
|
action: 'Please sign in again or contact your administrator.'
|
|
};
|
|
}
|
|
|
|
if (errorStr.includes('ffmpeg') || errorStr.includes('codec')) {
|
|
return {
|
|
message: 'Conversion Error',
|
|
details: 'The video conversion failed due to encoding issues.',
|
|
action: 'Try a different video format or contact support.'
|
|
};
|
|
}
|
|
|
|
return {
|
|
message: 'Error',
|
|
details: error.toString(),
|
|
action: 'Please try again or contact support if the problem persists.'
|
|
};
|
|
},
|
|
|
|
show(error) {
|
|
const helpful = this.getHelpfulErrorMessage(error);
|
|
toast.error(`${helpful.message}: ${helpful.details}\n${helpful.action}`, 8000);
|
|
}
|
|
};
|
|
|
|
// ==============================================================================
|
|
// LOADING STATE IMPROVEMENTS
|
|
// ==============================================================================
|
|
|
|
const LoadingState = {
|
|
show(message = 'Processing...') {
|
|
let loader = document.getElementById('global-loader');
|
|
|
|
if (!loader) {
|
|
loader = document.createElement('div');
|
|
loader.id = 'global-loader';
|
|
loader.innerHTML = `
|
|
<div style="
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 9999;
|
|
">
|
|
<div style="
|
|
width: 60px;
|
|
height: 60px;
|
|
border: 4px solid #333;
|
|
border-top-color: #FFC407;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
"></div>
|
|
<p id="loader-message" style="
|
|
color: #FFC407;
|
|
font-family: 'Montserrat', sans-serif;
|
|
font-size: 18px;
|
|
margin-top: 20px;
|
|
">${message}</p>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(loader);
|
|
|
|
// Add spin animation
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
} else {
|
|
loader.style.display = 'flex';
|
|
const messageEl = document.getElementById('loader-message');
|
|
if (messageEl) messageEl.textContent = message;
|
|
}
|
|
},
|
|
|
|
hide() {
|
|
const loader = document.getElementById('global-loader');
|
|
if (loader) {
|
|
loader.style.display = 'none';
|
|
}
|
|
},
|
|
|
|
updateMessage(message) {
|
|
const messageEl = document.getElementById('loader-message');
|
|
if (messageEl) messageEl.textContent = message;
|
|
}
|
|
};
|
|
|
|
// ==============================================================================
|
|
// EXPORT UTILITIES
|
|
// ==============================================================================
|
|
|
|
// Make utilities globally available
|
|
window.Storage = Storage;
|
|
window.Clipboard = Clipboard;
|
|
window.KeyboardShortcuts = KeyboardShortcuts;
|
|
window.formatFileSize = formatFileSize;
|
|
window.estimateConversionTime = estimateConversionTime;
|
|
window.DragDropEnhancer = DragDropEnhancer;
|
|
window.ErrorHandler = ErrorHandler;
|
|
window.LoadingState = LoadingState;
|