Skip to content

Commit

Permalink
Merge pull request #48 from appujet/main
Browse files Browse the repository at this point in the history
Added Live Lyrics Events and Added JioSaavn in DefaultSources
  • Loading branch information
Tomato6966 authored Sep 16, 2024
2 parents e3dc7ad + 027cea7 commit 0934791
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ testBot/node_modules
testBot/src
.vscode
bun.lockb
package-lock.json
package-lock.json
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@
"node": ">=18.0.0",
"bun": ">=1.0.0"
}
}
}
14 changes: 12 additions & 2 deletions src/structures/LavalinkManagerStatics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ export const DefaultSources: Record<SearchPlatform, LavalinkSearchPlatform | Cli
"http": "http",
"https": "https",
"link": "link",
"uri": "uri"
"uri": "uri",
// jiosaavn
"jiosaavn": "jssearch",
"js": "jssearch",
"jssearch": "jssearch",
"jsrec": "jsrec"
}

/** Lavalink Plugins definiton */
Expand All @@ -75,7 +80,9 @@ export const LavalinkPlugins = {
LavaSrc: "lavasrc-plugin",
GoogleCloudTTS: "tts-plugin",
LavaSearch: "lavasearch-plugin",
LavalinkFilterPlugin: "lavalink-filter-plugin"
Jiosaavn_Plugin: "jiosaavn-plugin",
LavalinkFilterPlugin: "lavalink-filter-plugin",
JavaTimedLyricsPlugin: "java-lyrics-plugin"
}

/** Lavalink Sources regexes for url validations */
Expand Down Expand Up @@ -120,6 +127,9 @@ export const SourceLinksRegexes: Record<SourcesRegex, RegExp> = {

appleMusic: /https?:\/\/?(?:www\.)?music\.apple\.com\/(\S+)/,

/** From jiosaavn-plugin */
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_\/,]+)/,

/** FROM DUNCTE BOT PLUGIN */
tiktok: /https:\/\/www\.tiktok\.com\//,
mixcloud: /https:\/\/www\.mixcloud\.com\//,
Expand Down
157 changes: 155 additions & 2 deletions src/structures/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { NodeSymbol, queueTrackEnd } from "./Utils";
import type { Player } from "./Player";
import type { DestroyReasonsType } from "./Types/Player";
import type { LavalinkTrack, PluginInfo, Track } from "./Types/Track";
import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, LoadTypes, PlayerEvents, PlayerEventType, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session, SponsorBlockChaptersLoaded, SponsorBlockChapterStarted, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, WebSocketClosedEvent } from "./Types/Utils";
import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, LoadTypes, LyricsFoundEvent, LyricsLineEvent, LyricsNotFoundEvent, PlayerEvents, PlayerEventType, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session, SponsorBlockChaptersLoaded, SponsorBlockChapterStarted, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, WebSocketClosedEvent } from "./Types/Utils";
import type { NodeManager } from "./NodeManager";

import type {
BaseNodeStats, LavalinkInfo, LavalinkNodeOptions, ModifyRequest, NodeStats, SponsorBlockSegment
BaseNodeStats, LavalinkInfo, LavalinkNodeOptions, LyricsResult, ModifyRequest, NodeStats, SponsorBlockSegment
} from "./Types/Node";
/**
* Lavalink Node creator class
Expand Down Expand Up @@ -637,6 +637,124 @@ export class LavalinkNode {
}
}

lyrics = {
/**
* Get the lyrics of a track
* @param track the track to get the lyrics for
* @param skipTrackSource wether to skip the track source or not
* @returns the lyrics of the track
* @example
*
* ```ts
* const lyrics = await player.node.lyrics.get(track, true);
* // use it of player instead:
* // const lyrics = await player.getLyrics(track, true);
* ```
*/
get: async (track: Track, skipTrackSource: boolean = false) => {
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");

if(
!this.info.plugins.find(v => v.name === "lavalyrics-plugin")
) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);

if(
!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
!this.info.plugins.find(v => v.name === "java-lyrics-plugin")
) throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);

const url = `/lyrics?track=${track.encoded}&skipTrackSource=${skipTrackSource}`;
return (await this.request(url)) as LyricsResult | null;
},

/**
* Get the lyrics of the current playing track
*
* @param guildId the guild id of the player
* @param skipTrackSource wether to skip the track source or not
* @returns the lyrics of the current playing track
* @example
* ```ts
* const lyrics = await player.node.lyrics.getCurrent(guildId);
* // use it of player instead:
* // const lyrics = await player.getCurrentLyrics();
* ```
*/
getCurrent: async (guildId: string, skipTrackSource: boolean = false) => {
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");

if(
!this.info.plugins.find(v => v.name === "lavalyrics-plugin")
) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);

if(
!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
!this.info.plugins.find(v => v.name === "java-lyrics-plugin")
) throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);

const url = `/sessions/${this.sessionId}/players/${guildId}/track/lyrics?skipTrackSource=${skipTrackSource}`;
return (await this.request(url)) as LyricsResult | null;
},

/**
* subscribe to lyrics updates for a guild
* @param guildId the guild id of the player
* @returns request data of the request
*
* @example
* ```ts
* await player.node.lyrics.subscribe(guildId);
* // use it of player instead:
* // const lyrics = await player.subscribeLyrics();
* ```
*/

subscribe: async (guildId: string) => {
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");

if(
!this.info.plugins.find(v => v.name === "lavalyrics-plugin")
) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);

if(
!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
!this.info.plugins.find(v => v.name === "java-lyrics-plugin")
) throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);

return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`, (options) => {
options.method = "POST";
}).catch(() => { });
},
/**
* unsubscribe from lyrics updates for a guild
* @param guildId the guild id of the player
* @returns request data of the request
*
* @example
* ```ts
* await player.node.lyrics.unsubscribe(guildId);
* // use it of player instead:
* // const lyrics = await player.unsubscribeLyrics();
* ```
*/
unsubscribe: async (guildId: string) => {
if (!this.sessionId) throw new Error("the Lavalink-Node is either not ready, or not up to date!");

if(
!this.info.plugins.find(v => v.name === "lavalyrics-plugin")
) throw new RangeError(`there is no lavalyrics-plugin available in the lavalink node (required for lyrics): ${this.id}`);

if(
!this.info.plugins.find(v => v.name === "lavasrc-plugin") &&
!this.info.plugins.find(v => v.name === "java-lyrics-plugin")
) throw new RangeError(`there is no lyrics source (via lavasrc-plugin / java-lyrics-plugin) available in the lavalink node (required for lyrics): ${this.id}`);

return await this.request(`/sessions/${this.sessionId}/players/${guildId}/lyrics/unsubscribe`, (options) => {
options.method = "DELETE";
}).catch(() => { });
},
};

/**
* Request Lavalink statistics.
* @returns the lavalink node stats
Expand Down Expand Up @@ -1017,6 +1135,9 @@ export class LavalinkNode {
case "SegmentSkipped": this.SponsorBlockSegmentSkipped(player, player.queue.current as Track, payload); break;
case "ChaptersLoaded": this.SponsorBlockChaptersLoaded(player, player.queue.current as Track, payload); break;
case "ChapterStarted": this.SponsorBlockChapterStarted(player, player.queue.current as Track, payload); break;
case "LyricsLineEvent": this.LyricsLine(player, player.queue.current as Track, payload); break;
case "LyricsFoundEvent": this.LyricsFound(player, player.queue.current as Track, payload); break;
case "LyricsNotFoundEvent": this.LyricsNotFound(player, player.queue.current as Track, payload); break;
default: this.NodeManager.emit("error", this, new Error(`Node#event unknown event '${(payload as PlayerEventType & PlayerEvents).type}'.`), (payload as PlayerEventType & PlayerEvents)); break;
}
return;
Expand Down Expand Up @@ -1369,4 +1490,36 @@ export class LavalinkNode {

return this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
}

/**
* Emitted whenever a line of lyrics gets emitted
* @event
* @param {Player} player The player that emitted the event
* @param {Track} track The track that emitted the event
* @param {LyricsLineEvent} payload The payload of the event
*/
private LyricsLine(player: Player, track: Track, payload: LyricsLineEvent) {
return this.NodeManager.LavalinkManager.emit("LyricsLine", player, track, payload);
}

/**
* Emitted whenever the lyrics for a track got found
* @event
* @param {Player} player The player that emitted the event
* @param {Track} track The track that emitted the event
* @param {LyricsFoundEvent} payload The payload of the event
*/
private LyricsFound(player: Player, track: Track, payload: LyricsFoundEvent) {
return this.NodeManager.LavalinkManager.emit("LyricsFound", player, track, payload);
}
/**
* Emitted whenever the lyrics for a track got not found
* @event
* @param {Player} player The player that emitted the event
* @param {Track} track The track that emitted the event
* @param {LyricsNotFoundEvent} payload The payload of the event
*/
private LyricsNotFound(player: Player, track: Track, payload: LyricsNotFoundEvent) {
return this.NodeManager.LavalinkManager.emit("LyricsNotFound", player, track, payload);
}
}
54 changes: 54 additions & 0 deletions src/structures/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,60 @@ export class Player {
return this;
}

/**
* Get the current lyrics of the track currently playing on the guild
* @param guildId The guild id to get the current lyrics for
* @param skipTrackSource If true, it will not try to get the lyrics from the track source
* @returns The current lyrics
* @example
* ```ts
* const lyrics = await player.getCurrentLyrics();
* ```
*/
public async getCurrentLyrics(skipTrackSource?: boolean) {
return await this.node.lyrics.getCurrent(this.guildId, skipTrackSource);
}

/**
* Get the lyrics of a specific track
* @param track The track to get the lyrics for
* @param skipTrackSource If true, it will not try to get the lyrics from the track source
* @returns The lyrics of the track
* @example
* ```ts
* const lyrics = await player.getLyrics(player.queue.tracks[0], true);
* ```
*/
public async getLyrics(track: Track, skipTrackSource?: boolean) {
return await this.node.lyrics.get(track, skipTrackSource);
}

/**
* Subscribe to the lyrics event on a specific guild to active live lyrics events
* @param guildId The guild id to subscribe to
* @returns The unsubscribe function
* @example
* ```ts
* const lyrics = await player.subscribeLyrics();
* ```
*/
public subscribeLyrics() {
return this.node.lyrics.subscribe(this.guildId);
}

/**
* Unsubscribe from the lyrics event on a specific guild to disable live lyrics events
* @param guildId The guild id to unsubscribe from
* @returns The unsubscribe function
* @example
* ```ts
* const lyrics = await player.unsubscribeLyrics();
* ```
*/
public unsubscribeLyrics(guildId: string) {
return this.node.lyrics.unsubscribe(guildId);
}

/**
* Move the player on a different Audio-Node
* @param newNode New Node / New Node Id
Expand Down
25 changes: 23 additions & 2 deletions src/structures/Types/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { LavalinkNodeOptions } from "./Node";
import type { DestroyReasonsType, PlayerJson } from "./Player";
import type { ManagerQueueOptions } from "./Queue";
import type { Track, UnresolvedTrack } from "./Track";
import type { GuildShardPayload, SearchPlatform, SponsorBlockChaptersLoaded, SponsorBlockChapterStarted, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, TrackExceptionEvent, TrackEndEvent, TrackStuckEvent, WebSocketClosedEvent, TrackStartEvent } from "./Utils";
import type { GuildShardPayload, SearchPlatform, SponsorBlockChaptersLoaded, SponsorBlockChapterStarted, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, TrackExceptionEvent, TrackEndEvent, TrackStuckEvent, WebSocketClosedEvent, TrackStartEvent, LyricsFoundEvent, LyricsNotFoundEvent, LyricsLineEvent } from "./Utils";

/**
* The events from the lavalink Manager
Expand Down Expand Up @@ -117,7 +117,28 @@ export interface LavalinkManagerEvents {
*
* @event Manager#debug
*/
"debug": (eventKey: DebugEvents, eventData: { message: string, state: "log" | "warn" | "error", error?: Error|string, functionLayer: string }) => void;
"debug": (eventKey: DebugEvents, eventData: { message: string, state: "log" | "warn" | "error", error?: Error | string, functionLayer: string }) => void;

/**
* Emitted when a Lyrics line is received
* @link https://github.com/topi314/LavaLyrics
* @event Manager#LyricsLine
*/
"LyricsLine": (player: Player, track: Track | UnresolvedTrack | null, payload: LyricsLineEvent) => void;

/**
* Emitted when a Lyrics is found
* @link https://github.com/topi314/LavaLyrics
* @event Manager#LyricsFound
*/
"LyricsFound": (player: Player, track: Track | UnresolvedTrack | null, payload: LyricsFoundEvent) => void;

/**
* Emitted when a Lyrics is not found
* @link https://github.com/topi314/LavaLyrics
* @event Manager#LyricsNotFound
*/
"LyricsNotFound": (player: Player, track: Track | UnresolvedTrack | null, payload: LyricsNotFoundEvent) => void;
}
/**
* The Bot client Options needed for the manager
Expand Down
24 changes: 24 additions & 0 deletions src/structures/Types/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type internal from "stream";
import type { LavalinkNode } from "../Node";
import type { DestroyReasonsType } from "./Player";
import type { InvalidLavalinkRestRequest, LavalinkPlayer } from "./Utils";
import { PluginInfo } from "./Track";

/** Ability to manipulate fetch requests */
export type ModifyRequest = (options: RequestInit & { path: string, extraQueryUrlParams?: URLSearchParams }) => void;
Expand Down Expand Up @@ -166,6 +167,29 @@ export interface PluginObject {
version: string;
}

export interface LyricsResult {
/**The name of the source */
sourceName: string;
/**The name of the provider */
provider: string;
/**The result text */
text: string | null;
/**The lyrics lines */
lines: LyricsLine[];
/**Information about the plugin */
plugin: PluginInfo;
}

export interface LyricsLine {
/**The millisecond timestamp */
timestamp: number;
/**The line duration in milliseconds */
duration: number | null;
/**The line text */
line: string;
/**Information about the plugin */
plugin: PluginInfo;
}
export type LavalinkNodeIdentifier = string;

export interface NodeManagerEvents {
Expand Down
Loading

0 comments on commit 0934791

Please sign in to comment.