This commit is contained in:
Andrey Kondratev
2025-08-27 18:57:09 +05:00
parent 98787a382e
commit 57f0519a32
13 changed files with 829 additions and 550 deletions

View File

@@ -35,6 +35,6 @@
</div>
</div>
<script src="script.js"></script>
<script src="dist/script.js"></script>
</body>
</html>

View File

@@ -1,34 +1,79 @@
interface TelegramWebApp {
ready(): void;
expand(): void;
sendData(data: string): void;
MainButton: {
show(): void;
hide(): void;
};
initDataUnsafe?: {
user?: {
id: number;
};
};
}
interface Window {
Telegram?: {
WebApp: TelegramWebApp;
};
}
interface VideoResult {
id: string;
title: string;
channel: string;
thumbnail: string;
duration: number;
}
interface SearchResponse {
videos: VideoResult[];
}
interface ConvertResponse {
audioUrl?: string;
title: string;
}
class QuixoticApp {
private tg?: TelegramWebApp;
private searchInput!: HTMLInputElement;
private searchBtn!: HTMLButtonElement;
private loading!: HTMLElement;
private results!: HTMLElement;
private noResults!: HTMLElement;
constructor() {
this.tg = window.Telegram?.WebApp;
this.init();
this.bindEvents();
}
init() {
private init(): void {
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');
this.searchInput = document.getElementById('searchInput') as HTMLInputElement;
this.searchBtn = document.getElementById('searchBtn') as HTMLButtonElement;
this.loading = document.getElementById('loading') as HTMLElement;
this.results = document.getElementById('results') as HTMLElement;
this.noResults = document.getElementById('noResults') as HTMLElement;
}
bindEvents() {
private bindEvents(): void {
this.searchBtn.addEventListener('click', () => this.search());
this.searchInput.addEventListener('keypress', (e) => {
this.searchInput.addEventListener('keypress', (e: KeyboardEvent) => {
if (e.key === 'Enter') {
this.search();
}
});
}
async search() {
private async search(): Promise<void> {
const query = this.searchInput.value.trim();
if (!query) return;
@@ -50,7 +95,7 @@ class QuixoticApp {
throw new Error('Search failed');
}
const data = await response.json();
const data: SearchResponse = await response.json();
this.displayResults(data.videos);
} catch (error) {
console.error('Search error:', error);
@@ -58,19 +103,19 @@ class QuixoticApp {
}
}
showLoading() {
private showLoading(): void {
this.loading.classList.remove('hidden');
this.results.classList.add('hidden');
this.noResults.classList.add('hidden');
this.searchBtn.disabled = true;
}
hideLoading() {
private hideLoading(): void {
this.loading.classList.add('hidden');
this.searchBtn.disabled = false;
}
displayResults(videos) {
private displayResults(videos: VideoResult[]): void {
this.hideLoading();
if (!videos || videos.length === 0) {
@@ -93,16 +138,18 @@ class QuixoticApp {
this.noResults.classList.add('hidden');
}
showNoResults() {
private showNoResults(): void {
this.hideLoading();
this.results.classList.add('hidden');
this.noResults.classList.remove('hidden');
}
async convertVideo(videoId, title) {
public async convertVideo(videoId: string, title: string): Promise<void> {
console.log('Convert video called:', { videoId, title });
const videoElement = event.currentTarget;
videoElement.classList.add('converting');
const videoElement = (event as any)?.currentTarget as HTMLElement;
if (videoElement) {
videoElement.classList.add('converting');
}
try {
console.log('Sending convert request...');
@@ -123,7 +170,7 @@ class QuixoticApp {
throw new Error(`Conversion failed with status: ${response.status}`);
}
const data = await response.json();
const data: ConvertResponse = await response.json();
console.log('Response data:', data);
if (data.audioUrl) {
@@ -152,7 +199,7 @@ class QuixoticApp {
// Should not happen since we removed fallbacks
throw new Error('No audio URL received');
}
} catch (error) {
} catch (error: any) {
console.error('Conversion error:', error);
// Show specific error message
@@ -168,18 +215,20 @@ class QuixoticApp {
this.showMessage(`${errorMsg}`, 'warning');
} finally {
videoElement.classList.remove('converting');
if (videoElement) {
videoElement.classList.remove('converting');
}
}
}
formatDuration(seconds) {
private formatDuration(seconds: number): string {
if (!seconds) return '';
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
showMessage(message, type = 'info') {
private showMessage(message: string, type: string = 'info'): void {
// Remove existing message if any
const existingMessage = document.querySelector('.status-message');
if (existingMessage) {
@@ -193,7 +242,9 @@ class QuixoticApp {
// Add to page
const container = document.querySelector('.container');
container.insertBefore(messageEl, container.firstChild);
if (container) {
container.insertBefore(messageEl, container.firstChild);
}
// Auto-remove after 5 seconds
setTimeout(() => {
@@ -203,11 +254,12 @@ class QuixoticApp {
}, 5000);
}
escapeHtml(text) {
private escapeHtml(text: string): string {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
const app = new QuixoticApp();
const app = new QuixoticApp();
(window as any).app = app;