Spaces:
Running
Running
| // ShortForge AI – App logic | |
| class ShortForgeApp { | |
| constructor() { | |
| this.currentTemplate = 'narration'; | |
| this.generated = false; | |
| this.voices = { | |
| 'fr-lea': { name: 'Lea', lang: 'fr-FR', gender: 'female' }, | |
| 'fr-remy': { name: 'Rémy', lang: 'fr-FR', gender: 'male' }, | |
| 'en-bella': { name: 'Bella', lang: 'en-US', gender: 'female' }, | |
| 'en-mat': { name: 'Mat', lang: 'en-US', gender: 'male' } | |
| }; | |
| this.init(); | |
| } | |
| init() { | |
| this.bindEvents(); | |
| this.updateTemplateButtons(); | |
| } | |
| bindEvents() { | |
| // Template buttons | |
| document.querySelectorAll('.template-btn').forEach(btn => { | |
| btn.addEventListener('click', e => { | |
| document.querySelectorAll('.template-btn').forEach(b => b.classList.remove('active')); | |
| e.target.classList.add('active'); | |
| this.currentTemplate = e.target.dataset.template; | |
| this.updateTemplatePreview(); | |
| }); | |
| }); | |
| // Generate | |
| document.getElementById('voiceSelect').addEventListener('change', () => this.updateVoicePreview()); | |
| document.getElementById('generateBtn').addEventListener('click', () => this.generate()); | |
| // Download | |
| document.getElementById('downloadBtn').addEventListener('click', () => this.download()); | |
| // File upload | |
| document.getElementById('loadTxt').addEventListener('click', () => this.loadTxt()); | |
| document.getElementById('loadReddit').addEventListener('click', () => this.loadReddit()); | |
| } | |
| updateTemplateButtons() { | |
| document.querySelectorAll('.template-btn').forEach(btn => { | |
| btn.classList.toggle('active', btn.dataset.template === this.currentTemplate); | |
| }); | |
| } | |
| updateTemplatePreview() { | |
| const templates = { | |
| narration: 'Narration douce avec transitions lentes.', | |
| reddit: 'Reddit story rythmé pour maximiser le retention.', | |
| educatif: 'Format éducatif avec captions synchronisés.', | |
| top5: 'Top 5 dynamique avec effets punchy.' | |
| }; | |
| const desc = templates[this.currentTemplate]; | |
| const preview = document.createElement('div'); | |
| preview.className = 'mt-2 text-xs text-gray-400'; | |
| preview.textContent = desc; | |
| const existing = document.querySelector('#templateDesc'); | |
| if (existing) existing.remove(); | |
| preview.id = 'templateDesc'; | |
| document.querySelector('.template-btn.active').parentElement.appendChild(preview); | |
| } | |
| updateVoicePreview() { | |
| const select = document.getElementById('voiceSelect'); | |
| const voiceId = select.value; | |
| const voice = this.voices[voiceId]; | |
| const preview = document.createElement('div'); | |
| preview.className = 'mt-2 text-xs text-gray-400'; | |
| preview.textContent = `${voice.name} – ${voice.lang} (${voice.gender})`; | |
| const existing = document.querySelector('#voiceDesc'); | |
| if (existing) existing.remove(); | |
| preview.id = 'voiceDesc'; | |
| select.parentElement.appendChild(preview); | |
| } | |
| async loadTxt() { | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = '.txt'; | |
| input.onchange = e => { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = evt => { | |
| document.getElementById('scriptInput').value = evt.target.result; | |
| }; | |
| reader.readAsText(file); | |
| }; | |
| input.click(); | |
| } | |
| loadReddit() { | |
| const url = prompt('Collez l’URL Reddit du post :'); | |
| if (!url) return; | |
| // Mock fetch | |
| document.getElementById('scriptInput').value = 'Mock Reddit story récupéré !'; | |
| } | |
| async generate() { | |
| const script = document.getElementById('scriptInput').value.trim(); | |
| if (!script) return alert('Veuillez entrer un script.'); | |
| const btn = document.getElementById('generateBtn'); | |
| const original = btn.innerHTML; | |
| btn.disabled = true; | |
| btn.innerHTML = '<i data-feather="loader" class="inline w-5 h-5 mr-2 animate-spin"></i>Génération…'; | |
| feather.replace(); | |
| // Simulate API | |
| await new Promise(r => setTimeout(r, 3000)); | |
| // Fake video | |
| const video = document.getElementById('previewVideo'); | |
| video.src = 'https://sample-videos.com/shorts/9-16/mp4/20.mp4'; | |
| document.getElementById('previewPlayer').classList.remove('hidden'); | |
| document.getElementById('previewPlaceholder').classList.add('hidden'); | |
| document.getElementById('downloadBtn').classList.remove('hidden'); | |
| // Save to localStorage | |
| const history = JSON.parse(localStorage.getItem('sf-history') || '[]'); | |
| history.unshift({ script, template: this.currentTemplate, timestamp: Date.now() }); | |
| localStorage.setItem('sf-history', JSON.stringify(history.slice(0, 10))); | |
| this.generated = true; | |
| btn.disabled = false; | |
| btn.innerHTML = original; | |
| feather.replace(); | |
| } | |
| download() { | |
| if (!this.generated) return; | |
| const link = document.createElement('a'); | |
| link.href = 'https://sample-videos.com/shorts/9-16/mp4/20.mp4'; | |
| link.download = 'shortforge-video.mp4'; | |
| link.click(); | |
| } | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| new ShortForgeApp(); | |
| }); | |
| // History quick-load | |
| window.addEventListener('load', () => { | |
| const history = JSON.parse(localStorage.getItem('sf-history') || '[]'); | |
| if (history.length > 0) { | |
| const last = history[0]; | |
| document.getElementById('scriptInput').placeholder = `Dernier script: "${last.script.substring(0, 50)}…"`; | |
| } | |
| }); | |