diff --git a/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl b/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl index 3ea53dc..f330c2a 100644 Binary files a/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl and b/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl differ diff --git a/.serena/memories/filename_display_fix_complete.md b/.serena/memories/filename_display_fix_complete.md new file mode 100644 index 0000000..1d994fa --- /dev/null +++ b/.serena/memories/filename_display_fix_complete.md @@ -0,0 +1,27 @@ +# Audio File Display Name Fix - Complete + +## Problem Solved +User wanted audio files to show proper "Artist - Title.mp3" names when sent to Telegram, but without changing the saved filename on disk. + +## Solution Implemented +1. **Reverted file storage**: Files are saved with original format `${videoId}_${title}.mp3` +2. **Custom display filename**: Added logic to generate custom filename only for Telegram display +3. **Modified sendAudioFileInternal()**: Now accepts both URLs and file paths, generates custom filename for display + +## Key Changes in src/bot.ts +- Function now detects if input is URL or file path +- Extracts local file path from URL when needed +- Generates custom display filename: `${performer} - ${title}.mp3` +- Uses file options parameter to set custom filename in Telegram +- Maintains backward compatibility with both URL and file path inputs + +## Technical Details +- Uses node-telegram-bot-api file options: `{ filename: customFilename, contentType: 'audio/mpeg' }` +- Sanitizes performer and title for safe filename (removes special chars, limits length) +- Fallback: sends as document if audio fails +- Error handling: sends text message with link if all methods fail + +## Result +- Files stored on disk: `123456_song_title.mp3` (unchanged) +- Files displayed in Telegram: `Artist Name - Song Title.mp3` +- No more "🎵 ..." caption text \ No newline at end of file diff --git a/src/bot.ts b/src/bot.ts index 89be03e..5a6dce7 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -254,28 +254,59 @@ export class QuixoticBot { return this.sendAudioFileInternal(chatId, audioUrl, title, performer, thumbnail); } - private async sendAudioFileInternal(chatId: number, audioUrl: string, title: string, performer?: string, thumbnail?: string): Promise { + private async sendAudioFileInternal(chatId: number, audioUrlOrPath: string, title: string, performer?: string, thumbnail?: string): Promise { try { console.log(`📤 Sending: ${title} to chat ${chatId}`); - // Try sending as audio + // Check if it's a URL or local file path + const isUrl = audioUrlOrPath.startsWith('http'); + let filePath = audioUrlOrPath; + + if (isUrl) { + // Extract filename from URL and construct local path + const urlParts = audioUrlOrPath.split('/'); + const filename = urlParts[urlParts.length - 1]; + filePath = require('path').join(process.cwd(), 'downloads', filename); + } + + // Generate custom filename for display + const safeTitle = (title || '').replace(/[^\w\s-]/g, '').replace(/\s+/g, '_').substring(0, 30); + const safePerformer = (performer || '').replace(/[^\w\s-]/g, '').replace(/\s+/g, '_').substring(0, 20); + const customFilename = safePerformer ? `${safePerformer} - ${safeTitle}` : `${safeTitle}`; + + // Try sending as audio with custom filename try { - await this.bot.sendAudio(chatId, audioUrl, { + const fs = require('fs'); + + // Check if file exists + if (!fs.existsSync(filePath)) { + throw new Error('File not found: ' + filePath); + } + + await this.bot.sendAudio(chatId, filePath, { title: title, - performer: performer || 'SoundCloud', + performer: performer, caption: undefined, thumbnail: thumbnail, parse_mode: undefined + }, { + filename: customFilename, + contentType: 'audio/mpeg' }); console.log(`✅ Audio sent: ${title}`); return; - } catch { - // Fallback: try as document + } catch (error: any) { + console.log('Audio send failed, trying as document...', error.message); + + // Fallback: try as document with custom filename try { - await this.bot.sendDocument(chatId, audioUrl, { + await this.bot.sendDocument(chatId, filePath, { caption: undefined, parse_mode: undefined + }, { + filename: customFilename, + contentType: 'audio/mpeg' }); console.log(`✅ Document sent: ${title}`); return; @@ -288,11 +319,13 @@ export class QuixoticBot { } catch (error: any) { console.error('❌ Send failed:', error.message); - // Send fallback with link + // Send fallback with link if it was a URL try { - await this.bot.sendMessage(chatId, - `❌ Не удалось отправить файл.\n🎵 ${title}\n🔗 ${audioUrl}` - ); + const message = audioUrlOrPath.startsWith('http') + ? `❌ Не удалось отправить файл.\n🎵 ${title}\n🔗 ${audioUrlOrPath}` + : `❌ Не удалось отправить файл: ${title}`; + + await this.bot.sendMessage(chatId, message); } catch { // Silent fail } diff --git a/src/server.ts b/src/server.ts index 04c54ff..23d1ab6 100644 --- a/src/server.ts +++ b/src/server.ts @@ -99,8 +99,7 @@ app.post('/api/convert', async (req: Request, res: Response) => { // Generate safe filename const safeTitle = (title || '').replace(/[^\w\s-]/g, '').replace(/\s+/g, '_').substring(0, 50); - const safePerformer = (performer || '').replace(/[^\w\s-]/g, '').replace(/\s+/g, '_').substring(0, 20); - const filename = safePerformer ? `${safePerformer} - ${safeTitle}.mp3` : `${safeTitle}.mp3`; + const filename = `${videoId}_${safeTitle}.mp3`; const outputPath = path.join(downloadsDir, filename); // Check if file already exists