Skip to main content

BaseExtractor

Base class for creating custom extractors. Extractors handle searching, validating, and streaming audio from various sources.

Constructor

new BaseExtractor<T extends object = object>(
  context: ExtractorExecutionContext,
  options?: T
)
context
ExtractorExecutionContext
required
The execution context that instantiated this extractor
options
T
Initialization options for this extractor

Static Properties

identifier
string
default:"com.discord-player.extractor"
Unique identifier for this extractor. Should be overridden in subclasses

Properties

context
ExtractorExecutionContext
The execution context managing this extractor
options
T
Configuration options for this extractor
priority
number
default:1
Priority of this extractor. Higher values execute first (range: any number)
protocols
string[]
default:[]
List of query protocols this extractor supports (e.g., ['spotify:', 'https://spotify.com'])
identifier
string
Instance identifier matching the static identifier property
supportsDemux
boolean
Whether this extractor supports demuxable streams (opus/ogg/webm formats)
createBridgeQuery
(track: Track) => string
Function to create search query for bridging. Defaults to: ${track.title} by ${track.author} official audio

Methods

activate

Called when the extractor is registered and activated.
async activate(): Promise<void>
Example:
class MyExtractor extends BaseExtractor {
  async activate() {
    // Initialize API client, set up event listeners, etc.
    console.log('MyExtractor activated');
  }
}

deactivate

Called when the extractor is unregistered or deactivated.
async deactivate(): Promise<void>
Example:
class MyExtractor extends BaseExtractor {
  async deactivate() {
    // Clean up resources, close connections, etc.
    console.log('MyExtractor deactivated');
  }
}

reconfigure

Reconfigures the extractor with new options.
async reconfigure(options: T): Promise<void>
options
T
required
New options to apply to the extractor
Example:
await myExtractor.reconfigure({
  apiKey: 'new-api-key',
  timeout: 5000
});

validate

Validate if this extractor can handle the given query.
async validate(query: string, type?: SearchQueryType | null): Promise<boolean>
query
string
required
The search query to validate
type
SearchQueryType | null
The type of query being validated
return
Promise<boolean>
true if this extractor can handle the query, false otherwise
Example:
class SpotifyExtractor extends BaseExtractor {
  async validate(query: string) {
    return query.includes('spotify.com') || query.startsWith('spotify:');
  }
}

handle

Handle a search query and return results.
async handle(
  query: string,
  context: ExtractorSearchContext
): Promise<ExtractorInfo>
query
string
required
The search query to handle
context
ExtractorSearchContext
required
Search context containing:
  • type: Query type
  • requestedBy: User who requested the search
  • requestOptions: HTTP request options
  • protocol: Protocol being used
return
Promise<ExtractorInfo>
Object containing playlist and tracks array
Example:
class MyExtractor extends BaseExtractor {
  async handle(query: string, context: ExtractorSearchContext) {
    const results = await this.searchAPI(query);
    const tracks = results.map(r => new Track(this.context.player, {
      title: r.title,
      author: r.artist,
      url: r.url,
      duration: r.duration,
      requestedBy: context.requestedBy
    }));
    
    return this.createResponse(null, tracks);
  }
}

stream

Stream audio for the given track.
async stream(info: Track): Promise<ExtractorStreamable>
info
Track
required
The track to stream
return
Promise<ExtractorStreamable>
Can be:
  • Readable stream
  • String URL to audio
  • Object with { stream: Readable, $fmt: string } where $fmt is the format
Example:
class MyExtractor extends BaseExtractor {
  async stream(track: Track) {
    // Return URL
    return 'https://example.com/audio.mp3';
    
    // Or return stream
    const stream = await getAudioStream(track.url);
    return stream;
    
    // Or return stream with format
    return {
      stream: await getOpusStream(track.url),
      $fmt: 'opus'
    };
  }
}

getRelatedTracks

Get related tracks for autoplay.
async getRelatedTracks(
  track: Track,
  history: GuildQueueHistory
): Promise<ExtractorInfo>
track
Track
required
The source track to find related tracks for
history
GuildQueueHistory
required
Queue history to avoid repeating tracks
return
Promise<ExtractorInfo>
Object containing related tracks
Example:
class MyExtractor extends BaseExtractor {
  async getRelatedTracks(track: Track, history: GuildQueueHistory) {
    const related = await this.api.getRelated(track.url);
    const tracks = related
      .filter(t => !history.tracks.some(h => h.url === t.url))
      .map(t => new Track(this.context.player, t));
    
    return this.createResponse(null, tracks);
  }
}

bridge

Handle stream extraction for another extractor when the source extractor cannot stream.
async bridge(
  track: Track,
  sourceExtractor: BaseExtractor | null
): Promise<ExtractorStreamable | null>
track
Track
required
The track to bridge
sourceExtractor
BaseExtractor | null
required
The extractor that originally found the track
return
Promise<ExtractorStreamable | null>
Stream/URL if bridging is supported, null otherwise
Example:
class YouTubeExtractor extends BaseExtractor {
  async bridge(track: Track, sourceExtractor: BaseExtractor | null) {
    // Search YouTube for the track
    const query = this.createBridgeQuery(track);
    const results = await this.handle(query, {});
    
    if (results.tracks.length === 0) return null;
    
    // Stream the first result
    return this.stream(results.tracks[0]);
  }
}

handlePostStream

Middleware to process streams before passing to the player.
handlePostStream(stream: Readable, next: NextFunction): void
stream
Readable
required
The incoming audio stream
next
NextFunction
required
Callback function: (error?: Error | null, stream?: Readable) => void
Example:
class MyExtractor extends BaseExtractor {
  handlePostStream(stream: Readable, next: NextFunction) {
    // Transform the stream
    const transformed = stream.pipe(someTransform());
    
    // Pass to next middleware
    next(null, transformed);
    
    // Or handle errors
    transformed.on('error', (err) => next(err));
  }
}

createResponse

Create standardized extractor response.
createResponse(
  playlist?: Playlist | null,
  tracks?: Track[]
): ExtractorInfo
playlist
Playlist | null
Playlist object if results are from a playlist
tracks
Track[]
Array of tracks. Defaults to playlist tracks if not provided
return
ExtractorInfo
Standardized response object with playlist and tracks properties
Example:
// Single track
return this.createResponse(null, [track]);

// Multiple tracks without playlist
return this.createResponse(null, tracks);

// Playlist with tracks
return this.createResponse(playlist, tracks);

emit

Dispatch an event to the player.
emit<K extends keyof PlayerEvents>(
  event: K,
  ...args: Parameters<PlayerEvents[K]>
): boolean
event
keyof PlayerEvents
required
The event name to emit
args
any[]
required
Event arguments
Example:
this.emit('error', queue, error);
this.emit('debug', 'Fetching audio stream...');

debug

Write a debug message.
debug(message: string): void
message
string
required
The debug message to log
Example:
this.debug('Searching for tracks...');
this.debug(`Found ${results.length} results`);

Types

ExtractorStreamable

Valid return types for the stream() method.
type ExtractorStreamable =
  | Readable
  | string
  | { $fmt: string; stream: Readable };

ExtractorInfo

Standard extractor response format.
interface ExtractorInfo {
  playlist: Playlist | null;
  tracks: Track[];
}

ExtractorSearchContext

Context provided to the handle() method.
interface ExtractorSearchContext {
  type?: SearchQueryType | null;
  requestedBy?: User | null;
  requestOptions?: RequestOptions;
  protocol?: string | null;
}

NextFunction

Callback for stream middleware.
type NextFunction = (error?: Error | null, stream?: Readable) => void;

Complete Example

import { BaseExtractor, ExtractorInfo, ExtractorSearchContext } from 'discord-player';
import { Track } from 'discord-player';

interface MyExtractorOptions {
  apiKey: string;
  timeout?: number;
}

class MyExtractor extends BaseExtractor<MyExtractorOptions> {
  static identifier = 'com.example.my-extractor';
  
  public priority = 10; // Higher priority
  public protocols = ['myservice:', 'https://myservice.com'];
  
  async activate() {
    this.debug('MyExtractor activated with API key');
    // Initialize API client
  }
  
  async deactivate() {
    this.debug('MyExtractor deactivated');
    // Clean up resources
  }
  
  async validate(query: string) {
    return query.includes('myservice.com') || query.startsWith('myservice:');
  }
  
  async handle(query: string, context: ExtractorSearchContext): Promise<ExtractorInfo> {
    this.debug(`Searching for: ${query}`);
    
    // Search your service
    const results = await this.searchAPI(query);
    
    // Convert to Track objects
    const tracks = results.map(result => new Track(this.context.player, {
      title: result.title,
      author: result.artist,
      url: result.url,
      duration: result.duration,
      thumbnail: result.thumbnail,
      requestedBy: context.requestedBy
    }));
    
    return this.createResponse(null, tracks);
  }
  
  async stream(track: Track) {
    this.debug(`Streaming: ${track.title}`);
    
    // Get stream URL from your service
    const streamUrl = await this.getStreamUrl(track.url);
    
    return streamUrl; // Or return a Readable stream
  }
  
  async getRelatedTracks(track: Track, history: GuildQueueHistory): Promise<ExtractorInfo> {
    const related = await this.api.getRelated(track.url);
    const tracks = related
      .filter(t => !history.tracks.some(h => h.url === t.url))
      .slice(0, 5)
      .map(t => new Track(this.context.player, t));
    
    return this.createResponse(null, tracks);
  }
  
  private async searchAPI(query: string) {
    // Your API implementation
    return [];
  }
  
  private async getStreamUrl(url: string) {
    // Your streaming implementation
    return 'https://example.com/stream.mp3';
  }
}

// Register the extractor
await player.extractors.register(MyExtractor, {
  apiKey: 'your-api-key',
  timeout: 5000
});

Build docs developers (and LLMs) love