Spaces:
Sleeping
Sleeping
| // ============================================ | |
| // AUDIO TO TEXT - ЧИСТЫЙ СТАТИК | |
| // ============================================ | |
| const API_URL = 'https://api-inference.huggingface.co/models/openai/whisper-tiny'; | |
| // Элементы интерфейса | |
| const copyBtn = document.getElementById('copyBtn'); | |
| const copyFeedback = document.getElementById('copyFeedback'); | |
| const transcriptText = document.getElementById('transcriptText'); | |
| const dropZone = document.getElementById('dropZone'); | |
| const attachBtn = document.getElementById('attachBtn'); | |
| const transcriptArea = document.getElementById('transcriptArea'); | |
| // ========== УПРАВЛЕНИЕ ТОКЕНОМ ========== | |
| let HF_TOKEN = localStorage.getItem('hf_token'); | |
| function promptForToken() { | |
| const token = prompt('🔑 Введи свой Hugging Face токен:\n(получить: https://huggingface.co/settings/tokens)'); | |
| if (token) { | |
| localStorage.setItem('hf_token', token); | |
| HF_TOKEN = token; | |
| return true; | |
| } | |
| return false; | |
| } | |
| function resetToken() { | |
| localStorage.removeItem('hf_token'); | |
| HF_TOKEN = null; | |
| alert('🔄 Токен сброшен. Обнови страницу для ввода нового.'); | |
| } | |
| // Проверяем токен при загрузке | |
| if (!HF_TOKEN) { | |
| promptForToken(); | |
| } | |
| // ========== КОПИРОВАНИЕ ========== | |
| copyBtn.addEventListener('click', async () => { | |
| if (transcriptText.value) { | |
| await navigator.clipboard.writeText(transcriptText.value); | |
| copyFeedback.classList.add('show'); | |
| setTimeout(() => copyFeedback.classList.remove('show'), 2000); | |
| } | |
| }); | |
| // ========== DRAG & DROP ========== | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, preventDefaults, false); | |
| }); | |
| function preventDefaults(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| } | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, () => { | |
| dropZone.classList.add('drag-over'); | |
| }); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, () => { | |
| dropZone.classList.remove('drag-over'); | |
| }); | |
| }); | |
| dropZone.addEventListener('drop', (e) => { | |
| const files = e.dataTransfer.files; | |
| if (files.length) { | |
| handleFile(files[0]); | |
| } | |
| }); | |
| // ========== ВЫБОР ФАЙЛА ========== | |
| attachBtn.addEventListener('click', () => { | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = 'audio/*'; | |
| input.onchange = (e) => { | |
| if (e.target.files.length) { | |
| handleFile(e.target.files[0]); | |
| } | |
| }; | |
| input.click(); | |
| }); | |
| dropZone.addEventListener('click', (e) => { | |
| if (e.target !== attachBtn && !attachBtn.contains(e.target)) { | |
| attachBtn.click(); | |
| } | |
| }); | |
| // ========== ОСНОВНАЯ ФУНКЦИЯ ========== | |
| async function handleFile(file) { | |
| console.log('🎵 файл:', file.name); | |
| // Проверяем токен | |
| if (!HF_TOKEN) { | |
| if (!promptForToken()) { | |
| transcriptText.value = '❌ Без токена невозможно распознать речь'; | |
| return; | |
| } | |
| } | |
| dropZone.style.display = 'none'; | |
| transcriptArea.classList.add('active'); | |
| transcriptText.value = '🔄 Отправка на распознавание...'; | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| try { | |
| const arrayBuffer = await file.arrayBuffer(); | |
| const response = await fetch(API_URL, { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': `Bearer ${HF_TOKEN}`, | |
| 'Content-Type': file.type || 'audio/mpeg' | |
| }, | |
| body: arrayBuffer | |
| }); | |
| // Обработка ошибок авторизации | |
| if (response.status === 401 || response.status === 403) { | |
| localStorage.removeItem('hf_token'); | |
| HF_TOKEN = null; | |
| transcriptText.value = '❌ Токен недействителен. Обнови страницу и введи новый.'; | |
| return; | |
| } | |
| if (!response.ok) { | |
| throw new Error(`HTTP ${response.status}: ${response.statusText}`); | |
| } | |
| const data = await response.json(); | |
| transcriptText.value = data.text || '⚠️ Пустой ответ от API'; | |
| } catch (error) { | |
| transcriptText.value = `❌ Ошибка: ${error.message}`; | |
| console.error(error); | |
| } | |
| } | |
| // ========== ДОБАВЛЯЕМ КНОПКУ СБРОСА В ФУТЕР ========== | |
| // Этот код добавит ссылку для сброса токена в футер | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const footer = document.querySelector('.footer'); | |
| if (footer) { | |
| const resetLink = document.createElement('span'); | |
| resetLink.innerHTML = ' · <a href="#" onclick="resetToken(); return false;">🔄 сбросить токен</a>'; | |
| footer.appendChild(resetLink); | |
| } | |
| }); | |
| // Делаем функцию глобальной | |
| window.resetToken = resetToken; | |