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;