Regular plugin
So you are interested in using Rainlink and want to create your own plugin to extend some of Rainlink's features, so this page can help you somewhat.
Understanding how plugin works
Basically, the plugin in Rainlink will work by overriding the Rainlink
class by adding or overriding variables and functions in it. Hence why it is called a plugin.
Requirements
- Using
rainlink
package. - Extend
RainlinkPlugin
class. - Class named
RainlinkPlugin
(You can useimport { RainlinkPlugin as Plugin } from "rainlink";
to avoid error). - Have
name()
function return the name of that plugin. - Have
type()
function return the type of that plugin usingRainlinkPluginType
enum. In this article, we will usedefault
. - Have
load()
function to load all the modded function of that plugin. - Have
unload()
function to unload all the modded function of that plugin.
Do you know?
- You can check all the requirement on
src/Plugin/RainlinkPlugin.ts
in official repo. default
type is different fromsourceResolver
. I will talk aboutsourceResolver
later.
Example plugin code (YoutubeConverter / Not Release):
index.ts
import { RainlinkPlugin } from "./plugin";
export { RainlinkPlugin as DeezerPlugin }
plguin.ts
import { RainlinkTrack } from "rainlink";
import { RainlinkPluginType } from "rainlink";
import { RainlinkSearchOptions, RainlinkSearchResult, RainlinkSearchResultType } from "rainlink";
import { Rainlink } from "rainlink";
import { RainlinkPlugin as Plugin } from "rainlink";
const YOUTUBE_REGEX = [
/^https?:\/\//,
/(?:https?:\/\/)?(?:www\.)?youtu(?:\.be\/|be.com\/\S*(?:watch|embed)(?:(?:(?=\/[-a-zA-Z0-9_]{11,}(?!\S))\/)|(?:\S*v=|v\/)))([-a-zA-Z0-9_]{11,})/,
/^.*(youtu.be\/|list=)([^#\&\?]*).*/,
];
export type YoutubeConvertOptions = {
sources?: string[];
};
export class RainlinkPlugin extends Plugin {
private options: YoutubeConvertOptions;
private _search?: (query: string, options?: RainlinkSearchOptions) => Promise<RainlinkSearchResult>;
constructor(options?: YoutubeConvertOptions) {
super();
this.options = options ?? { sources: ["scsearch"] };
if (!this.options.sources || this.options.sources.length == 0) this.options.sources = ["scsearch"];
}
/** Name function for getting plugin name */
public name(): string {
return "rainlink-youtubeConvert";
}
/** Type function for diferent type of plugin */
public type(): RainlinkPluginType {
return RainlinkPluginType.Default;
}
/** Load function for make the plugin working */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public load(manager: Rainlink): void {
this._search = manager.search.bind(manager);
manager.search = this.search.bind(this);
}
/** unload function for make the plugin stop working */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public unload(manager: Rainlink): void {
if (!this._search) return;
manager.search = this._search.bind(manager);
this._search = undefined;
}
private async search(query: string, options?: RainlinkSearchOptions): Promise<RainlinkSearchResult> {
// Check if search func avaliable
if (!this._search) return this.buildSearch(undefined, [], RainlinkSearchResultType.SEARCH);
// Check if that's a yt link
const match = YOUTUBE_REGEX.some((match) => {
return match.test(query) == true;
});
if (!match) return await this._search(query, options);
// Get search query
const preRes = await this._search(query, options);
if (preRes.tracks.length == 0) return preRes;
// Remove track encoded to trick rainlink
if (preRes.type == RainlinkSearchResultType.PLAYLIST) {
for (const track of preRes.tracks) {
track.encoded = "";
}
return preRes;
}
const song = preRes.tracks[0];
const searchQuery = [song.author, song.title].filter((x) => !!x).join(" - ");
const res = await this.searchEngine(searchQuery, options);
if (res.tracks.length !== 0) return res;
return preRes;
}
private async searchEngine(query: string, options?: RainlinkSearchOptions): Promise<RainlinkSearchResult> {
if (!this._search) return this.buildSearch(undefined, [], RainlinkSearchResultType.SEARCH);
for (const SearchParams of this.options.sources!) {
const res = await this._search(`directSearch=${SearchParams}:${query}`, options);
if (res.tracks.length !== 0) return res;
}
return this.buildSearch(undefined, [], RainlinkSearchResultType.SEARCH);
}
private buildSearch(
playlistName?: string,
tracks: RainlinkTrack[] = [],
type?: RainlinkSearchResultType
): RainlinkSearchResult {
return {
playlistName,
tracks,
type: type ?? RainlinkSearchResultType.SEARCH,
};
}
}