### Install Moonlink.js Package Source: https://github.com/ecliptia/moonlink.js/wiki/How-should-I-start? Install the moonlink.js package using your preferred package manager. This is a necessary step before you can start using the library in your project. ```bash npm install moonlink.js ``` ```bash yarn add moonlink.js ``` ```bash pnpm install moonlink.js ``` ```bash bun install moonlink.js ``` -------------------------------- ### Player#play Source: https://context7.com/ecliptia/moonlink.js/llms.txt Starts playing the next track from the queue or a specified track. It can also play from an encoded string or at a specific position. ```APIDOC ## Player#play — Start Playback `player.play(options?): Promise` Plays the next track from the queue. Optionally pass a specific `Track` instance, a raw `encoded` base64 string, or a `position` offset in milliseconds. Returns `false` if the queue is empty or voice is unavailable. ### Method `play(options?: { track?: Track, encoded?: string, position?: number }): Promise` ### Description Initiates audio playback. This method can play the next available track in the queue, a specific track object, a track provided as an encoded string, or start playback from a given millisecond offset within a track. It returns a promise that resolves to `true` if playback started successfully, and `false` otherwise. ### Request Example ```typescript // Basic play (next track from queue) const success = await player.play(); // Play a specific track from search results, starting at 30 seconds const result = await manager.search({ query: "lofi hip hop", source: "youtube" }); await player.play({ track: result.tracks[0], position: 30000 }); // Play directly from an encoded string await player.play({ encoded: "QAAAU3QANlJpY2sgQXN0bGV5..." }); ``` ``` -------------------------------- ### Start Track Playback Source: https://context7.com/ecliptia/moonlink.js/llms.txt Plays the next track from the queue. Optionally pass a specific `Track` instance, a raw `encoded` base64 string, or a `position` offset in milliseconds. Returns `false` if the queue is empty or voice is unavailable. ```typescript manager.on("trackStart", (player, track) => { console.log(`Now playing: ${track.title} | Duration: ${track.duration}ms | Requested by: ${track.requester}`); }); manager.on("trackEnd", (player, track, reason) => { console.log(`Track ended: ${track.title}, reason: ${reason}`); // "finished" | "loadFailed" | "stopped" | "replaced" | "cleanup" }); manager.on("queueEnd", (player, lastTrack) => { console.log("Queue is empty."); // player.autoPlay handles auto-queuing if enabled }); // Basic play (next track from queue) const success = await player.play(); // Play a specific track const result = await manager.search({ query: "lofi hip hop", source: "youtube" }); await player.play({ track: result.tracks[0], position: 30000 }); // start 30s in // Play from encoded string directly await player.play({ encoded: "QAAAU3QANlJpY2sgQXN0bGV5..." }); ``` -------------------------------- ### Manager Events Source: https://context7.com/ecliptia/moonlink.js/llms.txt All events emitted by the Manager class, with their respective signatures and example usage. ```APIDOC ## Manager Events This section details all events emitted by the `Manager` class. ### Node Events - `nodeCreate(node: Node)`: Emitted when a new node is created. - `nodeReady(node: Node, payload: any)`: Emitted when a node becomes ready. - `nodeConnected(node: Node)`: Emitted when a node successfully connects. - `nodeDisconnect(node: Node, code: number, reason: string)`: Emitted when a node disconnects. - `nodeReconnecting(node: Node, attempt: number)`: Emitted when a node is attempting to reconnect. - `nodeResume(node: Node)`: Emitted when a node resumes its connection. - `nodeDestroy(identifier: string)`: Emitted when a node is destroyed. - `nodeError(node: Node, error: Error)`: Emitted when an error occurs on a node. - `nodeStateChange(node: Node, oldState, newState)`: Emitted when a node's state changes. - `nodeRaw(node: Node, payload: any)`: Emitted for raw data received from a node. ### Player Lifecycle Events - `playerCreate(player)`: Emitted when a new player is created. - `playerDestroy(player, reason?: string)`: Emitted when a player is destroyed. - `playerUpdate(player, track, payload)`: Emitted for periodic player position updates. - `playerConnected(player, payload?)`: Emitted when a player connects. - `playerDisconnected(player)`: Emitted when a player disconnects. - `playerResuming(player)`: Emitted when a player is resuming playback. - `playerResumed(player)`: Emitted when a player has resumed playback. - `playerSwitchedNode(player, oldNode, newNode)`: Emitted when a player switches to a different node. - `playersMoved(players, oldNode, newNode)`: Emitted for bulk failover of players. - `playersOrphaned(players, deadNode)`: Emitted when players become orphaned. - `playerRecoveryStarted(player)`: Emitted when player recovery begins. - `playerRecoverySuccess(player)`: Emitted when player recovery is successful. - `playerRecoveryFailed(player)`: Emitted when player recovery fails. ### Playback Events - `trackStart(player, track)`: Emitted when a track starts playing. Provides track details like title, author, duration, etc. - `trackEnd(player, track, reason, payload?)`: Emitted when a track finishes. `reason` can be 'finished', 'loadFailed', 'stopped', 'replaced', or 'cleanup'. - `trackStuck(player, track, thresholdMs, payload?)`: Emitted when a track gets stuck. - `trackException(player, track, exception, payload?)`: Emitted when an exception occurs during track playback. - `queueEnd(player, lastTrack?)`: Emitted when the playback queue ends. - `autoPlayed(player, newTrack, previousTrack)`: Emitted when a track is automatically played. - `autoLeaved(player, lastTrack?)`: Emitted when the player automatically leaves. ### Queue Events - `queueAdd(player, tracks)`: Emitted when tracks are added to the queue. - `queueRemove(player, tracks)`: Emitted when tracks are removed from the queue. - `queueMoveRange(player, tracks, from, to)`: Emitted when a range of tracks is moved within the queue. - `queueRemoveRange(player, tracks, start, end)`: Emitted when a range of tracks is removed from the queue. - `queueDuplicate(player, tracks, index)`: Emitted when tracks are duplicated in the queue. ### Player State Changes Events - `playerChangedVolume(player, oldVol, newVol)`: Emitted when the player's volume changes. - `playerChangedLoop(player, oldLoop, loop, oldCount?, newCount?)`: Emitted when the player's loop setting changes. - `filtersUpdate(player, filters)`: Emitted when player filters are updated. - `socketClosed(player, code, reason, byRemote, payload?)`: Emitted when the player's socket connection is closed. ### Debug Events - `debug(message: string)`: Emitted for debug messages. ``` -------------------------------- ### NodeLink Features: Fading Control Source: https://context7.com/ecliptia/moonlink.js/llms.txt Configure and clear audio fading effects for smooth track transitions. Supports customizable track start and end fading with different curves. Requires a NodeLink-compatible server. ```typescript await player.setFading({ enabled: true, trackStart: { duration: 2000, curve: "logarithmic" }, trackEnd: { duration: 3000, curve: "linear" } }); await player.clearFading(); ``` -------------------------------- ### Create and Manage Guild Players with PlayerManager#create Source: https://context7.com/ecliptia/moonlink.js/llms.txt Use manager.players.create to instantiate a player for a guild, optionally providing voice and text channel IDs, volume, and other settings. Existing players can be retrieved with manager.players.get, and all players can be cleared with manager.players.clear. ```typescript const player = manager.players.create({ guildId: "123456789012345678", voiceChannelId: "987654321098765432", textChannelId: "111222333444555666", volume: 75, selfDeaf: true, autoPlay: false, autoLeave: true, loop: "off", }); // Check if a player already exists const existing = manager.players.get("123456789012345678"); if (existing) { console.log(`Already playing: ${existing.current?.title}`); } // Destroy all players (e.g. on bot shutdown) await manager.players.clear(); ``` -------------------------------- ### Initialize Moonlink.js Manager with Discord.js Connector Source: https://context7.com/ecliptia/moonlink.js/llms.txt Instantiate the Manager with node and global options, then use the Discord.js connector to automatically handle gateway events. The manager initializes on clientReady. ```typescript import { Client, GatewayIntentBits } from "discord.js"; import { Manager, Connectors } from "moonlink.js"; const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates], }); const manager = new Manager({ nodes: [ { host: "localhost", port: 2333, password: "youshallnotpass", secure: false, identifier: "main-node", retryAmount: 5, retryDelay: 30000, }, ], options: { resume: true, resumeTimeout: 60000, database: { type: "local" }, defaultPlayer: { volume: 80, autoPlay: true, autoLeave: true, selfDeaf: true, loop: "off", historySize: 20, }, search: { defaultPlatform: "youtube", resultLimit: 10 }, queue: { maxSize: 500, allowDuplicates: false }, trackHandling: { autoSkipOnError: true, skipStuckTracks: true, retryFailedTracks: true, maxRetryAttempts: 3 }, playerDestruction: { autoDestroyOnIdle: true, idleTimeout: 300000 }, node: { selectionStrategy: "leastLoad", avoidUnhealthyNodes: true, autoMovePlayers: true }, spotify: { enabled: true, clientId: "YOUR_CLIENT_ID", clientSecret: "YOUR_CLIENT_SECRET" }, }, }); // Wire Discord gateway events automatically manager.use(new Connectors.DiscordJs(), client); manager.on("nodeReady", (node) => console.log(`Node ${node.identifier} ready`)); manager.on("nodeError", (node, error) => console.error(`Node error: ${error.message}`)); client.login("YOUR_BOT_TOKEN"); // manager.init() is called automatically by the DiscordJs connector on clientReady ``` -------------------------------- ### PlayerManager#create Source: https://context7.com/ecliptia/moonlink.js/llms.txt Creates a new player instance for a guild or retrieves an existing one. Configurable with various playback options. ```APIDOC ## PlayerManager#create — Create a Guild Player `manager.players.create(config: IPlayerConfig): Player` Returns an existing player for the guild or creates a new one on the best available node (selected by the configured `selectionStrategy`). All config fields except `guildId` and `voiceChannelId` are optional and fall back to `defaultPlayer` settings. ### Parameters #### Request Body - **guildId** (string) - Required - The ID of the guild. - **voiceChannelId** (string) - Required - The ID of the voice channel. - **textChannelId** (string) - Optional - The ID of the text channel. - **volume** (number) - Optional - The initial volume (0-1000). - **selfDeaf** (boolean) - Optional - Whether the bot should self-deafen. - **autoPlay** (boolean) - Optional - Whether to enable autoplay. - **autoLeave** (boolean) - Optional - Whether the bot should automatically leave when idle. - **loop** (string) - Optional - The loop mode ('off', 'track', 'queue'). ### Response #### Success Response (Player) - Returns a `Player` object. ### Request Example ```typescript const player = manager.players.create({ guildId: "123456789012345678", voiceChannelId: "987654321098765432", textChannelId: "111222333444555666", volume: 75, selfDeaf: true, autoPlay: false, autoLeave: true, loop: "off", }); // Check if a player already exists const existing = manager.players.get("123456789012345678"); if (existing) { console.log(`Already playing: ${existing.current?.title}`); } // Destroy all players (e.g. on bot shutdown) await manager.players.clear(); ``` ``` -------------------------------- ### NodeLink Features: Next Track Pre-load Source: https://context7.com/ecliptia/moonlink.js/llms.txt Pre-load the next track in the queue to reduce latency when transitioning. This prepares the next track for immediate playback. Requires a NodeLink-compatible server. ```typescript const next = player.queue.first; await player.setNextTrack(next); ``` -------------------------------- ### Queue - Track Management Methods Source: https://context7.com/ecliptia/moonlink.js/llms.txt Provides methods for adding, removing, inserting, moving, shuffling, clearing, finding, and sorting tracks within the player's queue. All mutations persist to the database and emit queue events. ```typescript const q = player.queue; // Add one or many tracks q.add(track); q.add([track1, track2, track3]); // Insert at specific position q.insert(0, urgentTrack); // play next // Remove by index const removed = q.remove(2); // Move track from index 5 to index 1 q.move(5, 1); // Move a range of 3 tracks starting at index 4, to index 0 q.moveRange(4, 0, 3); // Remove a range q.removeRange(2, 5); // remove indices 2–5 inclusive // Jump to a position (removes all tracks before it) q.jump(3); // Dedup, sort, filter q.removeDuplicates(); q.sortByTitle(); q.sortByAuthor(); q.sortByDuration(); const shortTracks = q.filter(t => t.duration < 180000); // Find by title or identifier const found = q.find("never gonna give"); // Inspect console.log(`Queue: ${q.size} tracks, ${q.duration}ms total`); console.log(`Remaining (incl. current): ${q.remainingDuration}ms`); console.log(`Next up: ${q.first?.title}`); // Shuffle only a range q.shuffleRange(1, 5); // shuffle indices 1–5 // Duplicate a track q.duplicate(0, 2); // play first track 3 times total ``` -------------------------------- ### NodeLink Extended Capabilities - Next Track Pre-load Source: https://context7.com/ecliptia/moonlink.js/llms.txt Pre-loads the next track in the queue to reduce latency. ```APIDOC ## NodeLink Next Track Pre-load ### Description Pre-load the next track in the queue for smoother playback. ### Methods - `player.setNextTrack(track)`: Sets the next track to be pre-loaded. ``` -------------------------------- ### Live Node Migration Source: https://context7.com/ecliptia/moonlink.js/llms.txt Migrates a playing player to a different Lavalink node without audible interruption. The current track position is preserved and playback resumes on the target node. ```typescript manager.on("nodeDisconnect", async (node, code, reason) => { console.log(`Node ${node.identifier} disconnected: ${reason}`); // autoMovePlayers: true handles this automatically, but you can also do it manually: const affectedPlayers = manager.players.filter(p => p.node.identifier === node.identifier); const backup = manager.nodes.findNode({ exclude: [node.identifier] }); if (backup) { for (const player of affectedPlayers) { const moved = await player.transferNode(backup); console.log(`Player ${player.guildId} moved: ${moved}`); } } }); ``` -------------------------------- ### NodeLink Extended Capabilities - YouTube Chapters Source: https://context7.com/ecliptia/moonlink.js/llms.txt Loads and retrieves chapter information for YouTube videos. ```APIDOC ## NodeLink YouTube Chapters ### Description Load and access chapter markers for YouTube videos. ### Methods - `player.loadChapters()`: Loads chapters for the current track. Returns data including `title`, `startTime`, `endTime`, and `thumbnails`. ``` -------------------------------- ### Queue — Track Management Source: https://context7.com/ecliptia/moonlink.js/llms.txt Provides methods for managing the track queue, including adding, removing, inserting, moving, shuffling, clearing, finding, and sorting tracks. All queue mutations are persisted to the database and emit corresponding queue events. ```APIDOC ## Queue — Track Management `player.queue.add()`, `.remove()`, `.insert()`, `.move()`, `.shuffle()`, `.clear()`, `.find()`, `.sortBy*()` The `Queue` is a rich array-backed collection attached to each player. All mutations persist to the database and emit queue events. ### Methods - `add(track | Track[])`: Adds one or more tracks to the end of the queue. - `insert(index: number, track: Track)`: Inserts a track at a specific position in the queue. - `remove(index: number): Track`: Removes and returns the track at the specified index. - `removeRange(startIndex: number, endIndex: number)`: Removes a range of tracks (inclusive). - `move(fromIndex: number, toIndex: number)`: Moves a track from one position to another. - `moveRange(fromIndex: number, toIndex: number, count: number)`: Moves a range of tracks. - `jump(index: number)`: Removes all tracks before the specified index, effectively jumping to that track. - `shuffle()`: Shuffles all tracks in the queue. - `shuffleRange(startIndex: number, endIndex: number)`: Shuffles a specific range of tracks. - `clear()`: Removes all tracks from the queue. - `find(query: string): Track | undefined`: Finds a track by its title or identifier. - `sortByTitle()`: Sorts the queue by track title. - `sortByAuthor()`: Sorts the queue by track author. - `sortByDuration()`: Sorts the queue by track duration. - `filter(predicate: (track: Track) => boolean): Track[]`: Filters the queue based on a predicate function. - `duplicate(fromIndex: number, toIndex: number)`: Duplicates a track and inserts it at a specified position. ### Properties - `size`: The number of tracks in the queue. - `duration`: The total duration of all tracks in the queue (in milliseconds). - `remainingDuration`: The duration of tracks remaining in the queue (in milliseconds). - `first`: The first track in the queue (currently playing or next). ### Example ```typescript const q = player.queue; // Add tracks q.add(track); q.add([track1, track2, track3]); // Insert at the beginning q.insert(0, urgentTrack); // Remove track at index 2 const removed = q.remove(2); // Move track from index 5 to index 1 q.move(5, 1); // Remove tracks from index 2 to 5 q.removeRange(2, 5); // Jump to the 3rd track q.jump(3); // Shuffle the entire queue q.shuffle(); // Shuffle tracks from index 1 to 5 q.shuffleRange(1, 5); // Find a track const found = q.find("never gonna give"); // Inspect queue info console.log(`Queue size: ${q.size}, Total duration: ${q.duration}ms`); console.log(`Next track: ${q.first?.title}`); // Sort by title q.sortByTitle(); // Filter for short tracks const shortTracks = q.filter(t => t.duration < 180000); // Duplicate the first track and insert it as the third track q.duplicate(0, 2); ``` ``` -------------------------------- ### Search Tracks and Playlists with Manager#search Source: https://context7.com/ecliptia/moonlink.js/llms.txt Use manager.search to query Lavalink nodes or external sources like YouTube. Attach the requester to tracks for display. Handles different load types for errors, empty results, playlists, and single tracks. ```typescript client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand() || interaction.commandName !== "play") return; if (!interaction.member.voice.channel) { return interaction.reply({ content: "Join a voice channel first!", ephemeral: true }); } await interaction.deferReply(); const result = await manager.search({ query: interaction.options.getString("query", true), source: "youtube", // optional: override default platform requester: interaction.user, // attached to every track for display }); if (result.loadType === "error" || result.loadType === "empty") { return interaction.editReply("Nothing found for that query."); } const player = manager.players.create({ guildId: interaction.guildId, voiceChannelId: interaction.member.voice.channelId, textChannelId: interaction.channelId, autoPlay: true, }); if (!player.connected) await player.connect(); if (result.loadType === "playlist") { player.queue.add(result.tracks); await interaction.editReply(`Added playlist **${result.playlistInfo.name}** (${result.tracks.length} tracks)`); } else { player.queue.add(result.tracks[0]); await interaction.editReply(`Added **${result.tracks[0].title}** to queue`); } if (!player.playing) await player.play(); }); ``` -------------------------------- ### NodeLink Extended Capabilities - Loudness Normalization Source: https://context7.com/ecliptia/moonlink.js/llms.txt Enables loudness normalization for audio playback. ```APIDOC ## NodeLink Loudness Normalization ### Description Apply loudness normalization to the audio output. ### Methods - `player.setLoudnessNormalizer(enabled)`: Enables or disables loudness normalization. `enabled` is a boolean. ``` -------------------------------- ### Player#restart - Resume After Node Reconnect Source: https://context7.com/ecliptia/moonlink.js/llms.txt Reconnects voice and resumes the current track at the last known playback position. Automatically called by the Node after session resume fails, but can be called manually. ```typescript manager.on("playerRecoveryStarted", (player) => { console.log(`Recovering player ${player.guildId}...`); }); manager.on("playerRecoverySuccess", (player) => { console.log(`Player ${player.guildId} recovered successfully`); }); manager.on("playerRecoveryFailed", (player) => { console.error(`Player ${player.guildId} recovery failed`); player.destroy("Recovery failed"); }); // Manual restart (e.g. after your own reconnection logic) const ok = await player.restart(); ``` -------------------------------- ### NodeLink Extended Capabilities - Audio Mix Layers Source: https://context7.com/ecliptia/moonlink.js/llms.txt Allows adding, retrieving, updating, and removing audio mix layers as ambient overlays. ```APIDOC ## NodeLink Audio Mix Layers ### Description Manage ambient audio layers mixed with the main track. ### Methods - `player.addMix(track, options)`: Adds an audio track as a mix layer. `options` can include `volume`. - `player.getMixes()`: Retrieves all active mix layers. - `player.updateMixVolume(mixId, volume)`: Updates the volume of a specific mix layer. - `player.removeMix(mixId)`: Removes a mix layer by its ID. ``` -------------------------------- ### Structure Class Extension/Override Source: https://context7.com/ecliptia/moonlink.js/llms.txt Demonstrates how to extend or override core Moonlink.js classes using Structure.extend and Structure.register. ```APIDOC ## Structure — Class Extension / Override Moonlink.js provides a structure registry allowing for global overriding of core classes. ### `Structure.extend(name, fn)` Extends a core class with new functionality. The provided function receives the base class and should return a new class that extends it. ```typescript import { Structure, Player } from "moonlink.js"; Structure.extend("Player", (Base) => { return class extends Base { constructor(...args: any[]) { super(...args); (this as any).requestCount = 0; } // Add custom methods or properties here }; }); ``` ### `Structure.register(name, class)` Replaces a core class entirely with a custom one. ```typescript import { Structure, Player } from "moonlink.js"; class CustomPlayer extends Player { public djRole: string | null = null; public setDJRole(roleId: string): this { this.djRole = roleId; return this; } public hasDJPermission(member: any): boolean { if (!this.djRole) return true; return member.roles.cache.has(this.djRole); } } // Register before creating the Manager Structure.register("Player", CustomPlayer); ``` **Note**: Customizations should be applied before the `Manager` is initialized to ensure they are used consistently throughout the library. ``` -------------------------------- ### Connect and Disconnect Player to Voice Channel Source: https://context7.com/ecliptia/moonlink.js/llms.txt Connects the bot to the player's voice channel. The `play()` method auto-connects if needed. Pass optional `selfDeaf` and `selfMute` overrides. ```typescript const player = manager.players.create({ guildId: interaction.guildId, voiceChannelId: interaction.member.voice.channelId, }); // Explicit connect (selfDeaf defaults to true from Manager config) await player.connect({ selfDeaf: true, selfMute: false }); // Move to a different voice channel player.setVoiceChannelId(newChannelId); await player.connect(); // Temporarily mute the bot await player.toggleMute(); // player.setVoiceState({ selfMute: true }) // Disconnect (keeps player alive) await player.disconnect(); ``` -------------------------------- ### Player#connect / disconnect Source: https://context7.com/ecliptia/moonlink.js/llms.txt Connects the bot to the player's voice channel and allows for disconnection. The `play()` method can auto-connect if needed. ```APIDOC ## Player#connect / disconnect — Voice Channel Management `player.connect(options?): Promise` / `player.disconnect(): Promise` Connects the bot to the player's `voiceChannelId`. Pass optional `selfDeaf` / `selfMute` overrides. The `play()` method auto-connects if needed. ### Method - `connect(options?: { selfDeaf?: boolean, selfMute?: boolean }): Promise` - `disconnect(): Promise` ### Description Manages the bot's connection to a voice channel. `connect` establishes or re-establishes the connection, optionally setting self-deafened and self-muted states. `disconnect` removes the bot from the voice channel while keeping the player instance alive for potential reconnections. ### Request Example ```typescript // Explicit connect with options await player.connect({ selfDeaf: true, selfMute: false }); // Move to a different voice channel and connect player.setVoiceChannelId(newChannelId); await player.connect(); // Disconnect the bot from the voice channel await player.disconnect(); ``` ``` -------------------------------- ### Player Playback Controls Source: https://context7.com/ecliptia/moonlink.js/llms.txt Provides a comprehensive set of methods for controlling audio playback, including pause, resume, stop, skip, seek, volume adjustment, and loop modes. ```APIDOC ## Player — Playback Controls `player.pause()`, `player.resume()`, `player.stop()`, `player.skip()`, `player.back()`, `player.replay()`, `player.seek(ms)`, `player.forward(ms)`, `player.rewind(ms)`, `player.setVolume(n)`, `player.setLoop(mode)` Full set of transport controls. All methods return `this` (or a boolean for `skip`/`back`) and emit corresponding events. ### Methods - `pause(): Promise` - `resume(options?: { timeout?: number }): Promise` - `stop(): Promise` - `skip(count?: number): Promise` - `back(): Promise` - `replay(): Promise` - `seek(ms: number): Promise` - `forward(ms: number): Promise` - `rewind(ms: number): Promise` - `setVolume(n: number): Player` - `setLoop(mode: "off" | "track" | "queue", count?: number): Player` - `setAutoPlay(enabled: boolean): Player` - `setAutoLeave(enabled: boolean): Player` - `shuffle(): Player` - `destroy(reason?: string): Promise` ### Description Offers a rich API for managing the playback state of the audio player. These methods allow users to pause, resume, stop, skip tracks, navigate through the queue, adjust playback position, control volume, set looping behavior for tracks or the entire queue, and manage autoplay and auto-leave settings. The `destroy` method cleanly shuts down the player and disconnects from voice. ### Request Example ```typescript // Pause and resume playback await player.pause(); await player.resume({ timeout: 5000 }); // Adjust playback position await player.seek(60000); // Jump to 1 minute await player.forward(10000); // Skip forward 10 seconds await player.rewind(5000); // Go back 5 seconds // Skip tracks await player.skip(); // Skip to the next track await player.skip(3); // Skip to the 3rd track in the queue // Go back to the previous track await player.back(); // Replay the current track from the beginning await player.replay(); // Set volume (0-1000, 100 is normal) player.setVolume(50); // Set loop modes player.setLoop("queue"); // Loop the entire queue player.setLoop("track", 3); // Loop the current track 3 times // Enable/disable autoplay and auto-leave player.setAutoPlay(true); player.setAutoLeave(true); // Shuffle the current queue player.shuffle(); // Destroy the player instance await player.destroy("User requested stop"); ``` ``` -------------------------------- ### NodeLink Extended Capabilities - Direct Stream Source: https://context7.com/ecliptia/moonlink.js/llms.txt Provides a direct stream of the audio for external processing. ```APIDOC ## NodeLink Direct Stream ### Description Obtain a direct audio stream for custom processing. ### Methods - `player.getLoadDirectStream(options)`: Returns the audio stream and headers. `options` can include `volume` and `position`. ``` -------------------------------- ### NodeLink Extended Capabilities - Voice Receiver Source: https://context7.com/ecliptia/moonlink.js/llms.txt Creates a voice receiver for processing incoming audio packets from guild members. ```APIDOC ## NodeLink Voice Receiver ### Description Set up a receiver to process real-time audio data from voice channels. ### Methods - `player.createVoiceReceiver(options)`: Creates a voice receiver. `options` can include `mode` (e.g., "opus"). ### Events - `data`: Emitted when audio data packets are received. Includes `userId` and `packet`. ``` -------------------------------- ### Playback Controls Source: https://context7.com/ecliptia/moonlink.js/llms.txt Provides a full set of transport controls including pause, resume, stop, skip, back, replay, seek, volume, and loop settings. All methods return `this` (or a boolean for `skip`/`back`) and emit corresponding events. ```typescript // Pause / resume await player.pause(); await player.resume({ timeout: 5000 }); // optional timeout in ms // Seek, forward, rewind await player.seek(60000); // jump to 1:00 await player.forward(10000); // skip forward 10s await player.rewind(5000); // go back 5s // Skip to next track (or position in queue) await player.skip(); // next track await player.skip(3); // jump to queue index 3 // Go back to previous track await player.back(); // Replay current track from beginning await player.replay(); // Volume (0–1000, 100 = normal) player.setVolume(50); // Loop modes: "off" | "track" | "queue" player.setLoop("queue"); player.setLoop("track", 3); // loop current track exactly 3 times // AutoPlay: continue playing similar tracks when queue ends player.setAutoPlay(true); player.setAutoLeave(true); // destroy player when queue ends // Shuffle the queue player.shuffle(); // Destroy (leaves voice, cleans up) await player.destroy("User requested stop"); ``` -------------------------------- ### Player#transferNode Source: https://context7.com/ecliptia/moonlink.js/llms.txt Migrates a player to a different Lavalink node without interrupting playback, preserving the current track and position. ```APIDOC ## Player#transferNode — Live Node Migration `player.transferNode(node: Node | string): Promise` Migrates a playing player to a different Lavalink node without audible interruption. The current track position is preserved and playback resumes on the target node. ### Method `transferNode(node: Node | string): Promise` ### Description Allows for seamless migration of an active player from its current Lavalink node to a specified alternative node. This is useful for load balancing or in scenarios where a node becomes unavailable. The method ensures that playback continues uninterrupted, maintaining the current track and playback position on the new node. ### Parameters #### Path Parameters - **node** (Node | string) - Required - The target Lavalink node or its identifier to migrate the player to. ### Request Example ```typescript manager.on("nodeDisconnect", async (node, code, reason) => { console.log(`Node ${node.identifier} disconnected: ${reason}`); // If autoMovePlayers is false, manual migration is needed: const affectedPlayers = manager.players.filter(p => p.node.identifier === node.identifier); const backupNode = manager.nodes.findNode({ exclude: [node.identifier] }); if (backupNode) { for (const player of affectedPlayers) { const moved = await player.transferNode(backupNode); console.log(`Player ${player.guildId} moved: ${moved}`); } } }); ``` ``` -------------------------------- ### Extend Moonlink.js Player Class Source: https://context7.com/ecliptia/moonlink.js/llms.txt Extend core Moonlink.js classes like Player to add custom functionality. Use Structure.extend to wrap the base class or Structure.register to provide a completely new class. ```typescript import { Structure, Player } from "moonlink.js"; class CustomPlayer extends Player { public djRole: string | null = null; public setDJRole(roleId: string): this { this.djRole = roleId; return this; } public hasDJPermission(member: any): boolean { if (!this.djRole) return true; return member.roles.cache.has(this.djRole); } } // Override before creating the Manager Structure.extend("Player", (Base) => { return class extends Base { constructor(...args: any[]) { super(...args); (this as any).requestCount = 0; } }; }); // Or register a fully custom class Structure.register("Player", CustomPlayer); ``` -------------------------------- ### NodeLink Features: YouTube Chapters Source: https://context7.com/ecliptia/moonlink.js/llms.txt Load and display YouTube video chapters directly from the player. This feature automatically uses the current track and requires a NodeLink-compatible server. ```typescript const chapters = await player.loadChapters(); // uses player.current automatically console.log(chapters.data); // [{ title, startTime, endTime, thumbnails }] ``` -------------------------------- ### NodeLink Extended Capabilities - Lyrics Source: https://context7.com/ecliptia/moonlink.js/llms.txt Manages lyrics for tracks, including subscribing to real-time updates and loading lyrics directly. ```APIDOC ## NodeLink Lyrics Management ### Description Features exclusive to NodeLink for managing song lyrics. ### Methods - `player.subscribeLyrics(options)`: Subscribes to lyrics for the current track. `options` can include `skipTrackSource` (boolean). - `player.unsubscribeLyrics()`: Unsubscribes from lyrics updates. - `player.node.loadLyrics(track, language)`: Loads lyrics directly for a given track and language. ### Events - `lyricsFound`: Emitted when lyrics are found. Payload contains lyrics data. - `lyricsLine`: Emitted for each line of lyrics. Payload includes the line text. - `lyricsNotFound`: Emitted when lyrics cannot be found for a track. ``` -------------------------------- ### NodeLink Features: Lyrics Subscription and Loading Source: https://context7.com/ecliptia/moonlink.js/llms.txt Utilize NodeLink-specific features to subscribe to real-time lyrics updates or load lyrics directly for a given track. Ensure the node is NodeLink-compatible before using these features. ```typescript // Check before use if (!player.node.isNodeLink) throw new Error("NodeLink required"); // --- Lyrics --- await player.subscribeLyrics({ skipTrackSource: false }); manager.on("lyricsFound", (player, payload) => console.log("Lyrics:", payload)); manager.on("lyricsLine", (player, payload) => console.log("Line:", payload.line?.text)); manager.on("lyricsNotFound", (player, payload) => console.log("No lyrics")); await player.unsubscribeLyrics(); // Load lyrics directly (without subscription) const lyrics = await player.node.loadLyrics(player.current, "en"); ``` -------------------------------- ### Player#restart Source: https://context7.com/ecliptia/moonlink.js/llms.txt Resumes playback after a node reconnect. This method reconnects the voice connection and resumes the current track at its last known position. It is automatically invoked by the Node during session resume failures but can also be called manually. ```APIDOC ## Player#restart — Resume After Node Reconnect `player.restart(): Promise` Reconnects voice and resumes the current track at the last known playback position. Automatically called by the `Node` after session resume fails (disaster recovery), but can be called manually. ### Method `restart` ### Returns - `Promise`: A promise that resolves to `true` if the restart was successful, `false` otherwise. ### Example ```typescript const ok = await player.restart(); ``` ``` -------------------------------- ### Access and Monitor Lavalink Nodes Source: https://context7.com/ecliptia/moonlink.js/llms.txt Retrieve specific nodes by identifier or find the best available node. Monitor node connection status, state, and performance metrics like CPU load and player counts. Use `node.ping()` for round-trip time and `node.latency` for the last measured latency. ```typescript const node = manager.nodes.get("main-node"); // by identifier const best = manager.nodes.findNode(); // by selectionStrategy console.log(node.connected); // boolean console.log(node.state); // NodeState enum: DISCONNECTED | CONNECTING | CONNECTED | READY | DESTROYED console.log(node.stats?.playingPlayers); console.log(node.stats?.cpu.systemLoad); console.log(await node.ping()); // RTT in ms console.log(node.latency); // last measured latency console.log(node.getPenalties()); // composite load score used by selectionStrategy console.log(node.isNodeLink); // true if NodeLink-compatible server console.log([...node.capabilities]); // e.g. ["source:youtube", "filter:timescale"] ``` -------------------------------- ### NodeLink Features: Direct Stream Source: https://context7.com/ecliptia/moonlink.js/llms.txt Obtain a direct audio stream from the player for external processing, such as saving to a file. Allows control over volume and playback position. Requires a NodeLink-compatible server. ```typescript const { stream, headers } = await player.getLoadDirectStream({ volume: 80, position: 5000 }); stream.pipe(fs.createWriteStream("output.pcm")); ``` -------------------------------- ### Forward Discord Voice Gateway Packets with Manager#packetUpdate Source: https://context7.com/ecliptia/moonlink.js/llms.txt Manually forward raw Discord gateway packets to Moonlink.js using manager.packetUpdate. This is necessary for custom connectors that do not use the Discord.js connector, which handles this automatically. ```typescript // Manual connector (without Connectors.DiscordJs): client.on("raw", (packet) => { manager.packetUpdate(packet); }); client.once("ready", () => { manager.init(client.user.id); }); ``` -------------------------------- ### NodeLink Features: Voice Receiver Source: https://context7.com/ecliptia/moonlink.js/llms.txt Create a voice receiver to process incoming audio packets from guild members. This is useful for real-time audio analysis or manipulation. Requires a NodeLink-compatible server. ```typescript const receiver = player.createVoiceReceiver({ mode: "opus" }); receiver.on("data", (userId, packet) => { // Process incoming audio from guild members }); ``` -------------------------------- ### Decode and Encode Lavalink Tracks with Manager#decodeTrack / encodeTrack Source: https://context7.com/ecliptia/moonlink.js/llms.txt Use manager.decodeTrack to convert Lavalink's base64 track strings into readable track info objects, and manager.encodeTrack to create base64 strings from track info objects without node interaction. Useful for persistence or inter-service communication. ```typescript import { Manager } from "moonlink.js"; const player = manager.players.get("1234567890"); const encoded = player.current.encoded; // base64 string from Lavalink // Decode to readable track info const trackInfo = manager.decodeTrack(encoded); console.log(trackInfo.info.title); // "Never Gonna Give You Up" console.log(trackInfo.info.author); // "Rick Astley" console.log(trackInfo.info.length); // 213000 (ms) // Re-encode a custom track info object const reEncoded = manager.encodeTrack({ title: "Custom Track", author: "Someone", length: 180000, identifier: "abc123", isSeekable: true, isStream: false, uri: "https://example.com/audio.mp3", sourceName: "http", position: 0, }); ``` -------------------------------- ### Manager#search Source: https://context7.com/ecliptia/moonlink.js/llms.txt Searches for tracks and playlists against a Lavalink node or external sources like Spotify and Deezer. Supports direct URLs and search prefixes. ```APIDOC ## Manager#search — Track and Playlist Search `manager.search(options: ISearchQuery): Promise` Resolves a query against a Lavalink node (or a native Spotify/Deezer source) and returns a `SearchResult` containing load type, tracks, and playlist metadata. Supports direct URLs or search prefixes. Set `source` to `"youtube"`, `"soundcloud"`, `"spotify"`, `"deezer"`, `"youtubemusic"`, etc. ### Parameters #### Query Parameters - **query** (string) - Required - The search query or URL. - **source** (string) - Optional - The source to use for the search (e.g., "youtube", "spotify"). - **requester** (any) - Optional - The user or entity that requested the track. ### Response #### Success Response (SearchResult) - **loadType** (string) - The type of result loaded ('track', 'playlist', 'search', 'error', 'empty'). - **tracks** (Array) - An array of tracks found. - **playlistInfo** (object) - Information about the playlist if `loadType` is 'playlist'. - **name** (string) - The name of the playlist. - **selectedTrack** (string) - The selected track in the playlist. - **thumbnail** (string) - The thumbnail URL of the playlist. ### Request Example ```typescript const result = await manager.search({ query: interaction.options.getString("query", true), source: "youtube", // optional: override default platform requester: interaction.user, // attached to every track for display }); ``` ### Response Example ```json { "loadType": "track", "tracks": [ { "encoded": "...", "info": { "title": "Example Track Title", "author": "Example Artist", "length": 180000, "identifier": "xyz123", "uri": "https://example.com/track", "sourceName": "youtube" }, "pluginInfo": {} } ], "playlistInfo": {} } ``` ``` -------------------------------- ### Node Lifecycle Events Source: https://context7.com/ecliptia/moonlink.js/llms.txt Listens for various events related to the lifecycle of Lavalink nodes, such as ready, connected, disconnected, and errors. ```APIDOC ## Node Lifecycle Events ### Description Subscribe to events emitted by the NodeManager to react to changes in node status. ### Events - `nodeReady`: Emitted when a node becomes ready. Payload includes the node and session details. - `nodeConnected`: Emitted when a node successfully connects. - `nodeDisconnect`: Emitted when a node disconnects. Includes code and reason. - `nodeReconnecting`: Emitted when a node attempts to reconnect. Includes the attempt number. - `nodeResume`: Emitted when a session is resumed. - `nodeDestroy`: Emitted when a node is destroyed. Includes the node identifier. - `nodeError`: Emitted when an error occurs on a node. Includes the error object. - `nodeStateChange`: Emitted when a node's state changes. Includes old and new states. - `nodeRaw`: Emitted for all raw Lavalink payloads received from the node. ``` -------------------------------- ### Manager#packetUpdate Source: https://context7.com/ecliptia/moonlink.js/llms.txt Forwards Discord voice gateway packets to Moonlink.js for processing voice state and server updates. Essential for custom connectors. ```APIDOC ## Manager#packetUpdate — Discord Voice Gateway Forwarding `manager.packetUpdate(packet: DiscordGatewayPacket): Promise` Must be called with every raw Discord gateway packet so Moonlink.js can process `VOICE_STATE_UPDATE` and `VOICE_SERVER_UPDATE` events that establish voice sessions. The `DiscordJs` connector handles this automatically; only needed when writing a custom connector. ### Parameters #### Request Body - **packet** (DiscordGatewayPacket) - Required - The raw Discord gateway packet. ### Request Example ```typescript // Manual connector (without Connectors.DiscordJs): client.on("raw", (packet) => { manager.packetUpdate(packet); }); client.once("ready", () => { manager.init(client.user.id); }); ``` ``` -------------------------------- ### NodeLink Features: Loudness Normalization Source: https://context7.com/ecliptia/moonlink.js/llms.txt Enable or disable loudness normalization for the audio playback. This feature adjusts audio levels to a consistent perceived loudness. Requires a NodeLink-compatible server. ```typescript await player.setLoudnessNormalizer(true); ``` -------------------------------- ### NodeLink Extended Capabilities - Fading Source: https://context7.com/ecliptia/moonlink.js/llms.txt Configures audio fading effects for track transitions. ```APIDOC ## NodeLink Fading ### Description Set up and manage audio fading effects between tracks. ### Methods - `player.setFading(options)`: Enables and configures fading. `options` include `enabled` (boolean), `trackStart` (with `duration` and `curve`), and `trackEnd` (with `duration` and `curve`). - `player.clearFading()`: Disables and clears all fading configurations. ```