Spaces:
Sleeping
Sleeping
File size: 5,268 Bytes
51c858c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | // ============================================
// 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;
|