This commit is contained in:
Andrey Kondratev
2025-08-30 17:06:00 +05:00
parent 07ed434375
commit 8f1ef3c621
4 changed files with 72 additions and 13 deletions

View File

@@ -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

View File

@@ -254,28 +254,59 @@ export class QuixoticBot {
return this.sendAudioFileInternal(chatId, audioUrl, title, performer, thumbnail); return this.sendAudioFileInternal(chatId, audioUrl, title, performer, thumbnail);
} }
private async sendAudioFileInternal(chatId: number, audioUrl: string, title: string, performer?: string, thumbnail?: string): Promise<void> { private async sendAudioFileInternal(chatId: number, audioUrlOrPath: string, title: string, performer?: string, thumbnail?: string): Promise<void> {
try { try {
console.log(`📤 Sending: ${title} to chat ${chatId}`); 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 { 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, title: title,
performer: performer || 'SoundCloud', performer: performer,
caption: undefined, caption: undefined,
thumbnail: thumbnail, thumbnail: thumbnail,
parse_mode: undefined parse_mode: undefined
}, {
filename: customFilename,
contentType: 'audio/mpeg'
}); });
console.log(`✅ Audio sent: ${title}`); console.log(`✅ Audio sent: ${title}`);
return; return;
} catch { } catch (error: any) {
// Fallback: try as document console.log('Audio send failed, trying as document...', error.message);
// Fallback: try as document with custom filename
try { try {
await this.bot.sendDocument(chatId, audioUrl, { await this.bot.sendDocument(chatId, filePath, {
caption: undefined, caption: undefined,
parse_mode: undefined parse_mode: undefined
}, {
filename: customFilename,
contentType: 'audio/mpeg'
}); });
console.log(`✅ Document sent: ${title}`); console.log(`✅ Document sent: ${title}`);
return; return;
@@ -288,11 +319,13 @@ export class QuixoticBot {
} catch (error: any) { } catch (error: any) {
console.error('❌ Send failed:', error.message); console.error('❌ Send failed:', error.message);
// Send fallback with link // Send fallback with link if it was a URL
try { try {
await this.bot.sendMessage(chatId, const message = audioUrlOrPath.startsWith('http')
`Не удалось отправить файл.\n🎵 ${title}\n🔗 ${audioUrl}` ? `Не удалось отправить файл.\n🎵 ${title}\n🔗 ${audioUrlOrPath}`
); : `Не удалось отправить файл: ${title}`;
await this.bot.sendMessage(chatId, message);
} catch { } catch {
// Silent fail // Silent fail
} }

View File

@@ -99,8 +99,7 @@ app.post('/api/convert', async (req: Request, res: Response) => {
// Generate safe filename // Generate safe filename
const safeTitle = (title || '').replace(/[^\w\s-]/g, '').replace(/\s+/g, '_').substring(0, 50); 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 = `${videoId}_${safeTitle}.mp3`;
const filename = safePerformer ? `${safePerformer} - ${safeTitle}.mp3` : `${safeTitle}.mp3`;
const outputPath = path.join(downloadsDir, filename); const outputPath = path.join(downloadsDir, filename);
// Check if file already exists // Check if file already exists