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.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();