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

131
src/soundcloud.ts Normal file
View File

@@ -0,0 +1,131 @@
import scdl from 'soundcloud-downloader';
import { Readable } from 'stream';
interface SearchTrack {
id: number;
title: string;
user?: {
username: string;
avatar_url?: string;
};
artwork_url?: string;
duration: number;
permalink_url: string;
streamable: boolean;
downloadable: boolean;
}
interface TrackResult {
id: number;
title: string;
channel: string;
thumbnail: string;
duration: number;
url: string;
streamable: boolean;
downloadable: boolean;
}
interface TrackInfo {
title: string;
author: string;
length: number;
available: boolean;
}
export class SoundCloudService {
constructor() {
console.log('SoundCloud service initialized');
}
async searchTracks(query: string, maxResults: number = 10): Promise<TrackResult[]> {
try {
console.log(`Searching SoundCloud for: ${query}`);
// Search for tracks on SoundCloud
const tracks = await scdl.search({
query: query,
limit: maxResults,
resourceType: 'tracks'
}) as any;
if (!tracks || tracks.length === 0) {
console.log('No tracks found');
return [];
}
const trackResults: TrackResult[] = tracks.map(track => ({
id: track.id,
title: track.title,
channel: track.user?.username || 'Unknown Artist',
thumbnail: track.artwork_url || track.user?.avatar_url || 'https://via.placeholder.com/300x300?text=No+Image',
duration: Math.floor(track.duration / 1000) || 0, // Convert from ms to seconds
url: track.permalink_url,
streamable: track.streamable,
downloadable: track.downloadable
}));
console.log(`Found ${trackResults.length} tracks on SoundCloud`);
return trackResults;
} catch (error: any) {
console.error('SoundCloud search error:', error.message);
return [];
}
}
async getTrackInfo(trackId: string | number): Promise<TrackInfo> {
try {
const track = await scdl.getInfo(String(trackId)) as SearchTrack;
return {
title: track.title,
author: track.user?.username || 'Unknown',
length: Math.floor(track.duration / 1000),
available: track.streamable
};
} catch (error) {
console.error('Error getting track info:', error);
throw error;
}
}
async getAudioStream(trackId: string | number): Promise<Readable> {
try {
console.log(`Getting audio stream for track: ${trackId}`);
// Get track info first
const trackInfo = await scdl.getInfo(String(trackId)) as SearchTrack;
if (!trackInfo.streamable) {
throw new Error('Track is not streamable');
}
console.log(`Track: ${trackInfo.title}`);
console.log(`Artist: ${trackInfo.user?.username || 'Unknown'}`);
console.log(`Duration: ${Math.floor(trackInfo.duration / 1000)}s`);
// Get audio stream
const stream = await scdl.download(String(trackId));
console.log('Audio stream obtained successfully from SoundCloud');
return stream;
} catch (error: any) {
console.error('SoundCloud download failed:', error.message);
// Try alternative approach
try {
console.log('Trying alternative SoundCloud method...');
const trackUrl = `https://soundcloud.com/track/${trackId}`;
const stream = await scdl.download(trackUrl);
console.log('Audio stream obtained with alternative method');
return stream;
} catch (fallbackError: any) {
console.error('Alternative method also failed:', fallbackError.message);
throw new Error(`SoundCloud download failed: ${error.message}`);
}
}
}
}