class QuixoticApp { constructor() { this.tg = window.Telegram?.WebApp; this.init(); this.bindEvents(); } init() { if (this.tg) { this.tg.ready(); this.tg.expand(); this.tg.MainButton.hide(); } this.searchInput = document.getElementById('searchInput'); this.searchBtn = document.getElementById('searchBtn'); this.loading = document.getElementById('loading'); this.results = document.getElementById('results'); this.noResults = document.getElementById('noResults'); } bindEvents() { this.searchBtn.addEventListener('click', () => this.search()); this.searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { this.search(); } }); } async search() { const query = this.searchInput.value.trim(); if (!query) return; this.showLoading(); try { const response = await fetch('/api/search', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query, userId: this.tg?.initDataUnsafe?.user?.id || 'demo' }) }); if (!response.ok) { throw new Error('Search failed'); } const data = await response.json(); this.displayResults(data.videos); } catch (error) { console.error('Search error:', error); this.showNoResults(); } } showLoading() { this.loading.classList.remove('hidden'); this.results.classList.add('hidden'); this.noResults.classList.add('hidden'); this.searchBtn.disabled = true; } hideLoading() { this.loading.classList.add('hidden'); this.searchBtn.disabled = false; } displayResults(videos) { this.hideLoading(); if (!videos || videos.length === 0) { this.showNoResults(); return; } this.results.innerHTML = videos.map(video => `
${this.escapeHtml(video.title)}
${this.escapeHtml(video.title)}
${this.escapeHtml(video.channel)}
${this.formatDuration(video.duration)}
`).join(''); this.results.classList.remove('hidden'); this.noResults.classList.add('hidden'); } showNoResults() { this.hideLoading(); this.results.classList.add('hidden'); this.noResults.classList.remove('hidden'); } async convertVideo(videoId, title) { console.log('Convert video called:', { videoId, title }); const videoElement = event.currentTarget; videoElement.classList.add('converting'); try { console.log('Sending convert request...'); const response = await fetch('/api/convert', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ videoId, title, userId: this.tg?.initDataUnsafe?.user?.id || 'demo' }) }); console.log('Response status:', response.status); if (!response.ok) { throw new Error(`Conversion failed with status: ${response.status}`); } const data = await response.json(); console.log('Response data:', data); if (data.audioUrl) { // MP3 conversion successful! console.log('MP3 conversion successful:', data.audioUrl); if (this.tg) { // Send to Telegram chat this.tg.sendData(JSON.stringify({ action: 'send_audio', audioUrl: data.audioUrl, title: title })); this.showMessage('✓ MP3 готов! Отправляем в чат...', 'success'); } else { // For testing in browser - download file const link = document.createElement('a'); link.href = data.audioUrl; link.download = `${title}.mp3`; document.body.appendChild(link); link.click(); document.body.removeChild(link); this.showMessage('✓ MP3 скачан!', 'success'); } } else { // Should not happen since we removed fallbacks throw new Error('No audio URL received'); } } catch (error) { console.error('Conversion error:', error); // Show specific error message let errorMsg = 'Ошибка конвертации. Попробуйте другое видео.'; if (error.message.includes('No audio URL')) { errorMsg = 'Не удалось получить аудио файл.'; } else if (error.message.includes('410')) { errorMsg = 'Видео недоступно для скачивания.'; } else if (error.message.includes('403')) { errorMsg = 'Видео заблокировано для скачивания.'; } this.showMessage(`❌ ${errorMsg}`, 'warning'); } finally { videoElement.classList.remove('converting'); } } formatDuration(seconds) { if (!seconds) return ''; const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, '0')}`; } showMessage(message, type = 'info') { // Remove existing message if any const existingMessage = document.querySelector('.status-message'); if (existingMessage) { existingMessage.remove(); } // Create message element const messageEl = document.createElement('div'); messageEl.className = `status-message status-${type}`; messageEl.textContent = message; // Add to page const container = document.querySelector('.container'); container.insertBefore(messageEl, container.firstChild); // Auto-remove after 5 seconds setTimeout(() => { if (messageEl.parentNode) { messageEl.remove(); } }, 5000); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } } const app = new QuixoticApp();