discord-player-jiosaavn
Version:
A custom extractor made for discord-player that enables you to extract from JioSaavn.
202 lines (199 loc) • 6.43 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
JiosaavnExtractor: () => JiosaavnExtractor
});
module.exports = __toCommonJS(index_exports);
// src/JiosaavnExtractor.ts
var import_discord_player = require("discord-player");
var JiosaavnExtractor = class extends import_discord_player.BaseExtractor {
constructor() {
super(...arguments);
this.createBridgeQuery = (track) => `${track.title} by ${track.author}`;
}
async activate() {
this.protocols = ["jssearch", "jiosaavn"];
}
async deactivate() {
this.protocols = [];
}
async validate(query) {
const { type } = this.parseURL(query);
return Boolean(type);
}
async handle(query, context) {
const { type, token } = this.parseURL(query);
if (context.protocol === "jssearch" || type === "song") {
let data;
if (type === "song") {
const raw = await this.fetchAPI({
__call: "webapi.get",
token,
type
});
data = raw?.songs[0];
} else {
const raw = await this.fetchAPI({
__call: "search.getResults",
n: 1,
p: 1,
q: query
});
data = raw?.results[0];
}
if (!data) {
return this.createResponse();
}
const track = this.buildTrack(data, context.requestedBy);
return this.createResponse(null, [track]);
} else {
let params = {
__call: "webapi.get",
token,
type: type === "featured" ? "playlist" : type
};
if (type === "artist") {
params = {
...params,
category: "",
n_album: 1,
n_song: 50,
p: 0,
sort_order: "",
sub_type: ""
};
}
if (type === "featured") {
params = {
...params,
n: 50,
p: 1
};
}
const data = await this.fetchAPI(params);
if (!data) {
return this.createResponse();
}
const artistsData = type === "playlist" ? data.more_info.artists : data.more_info.artistMap.primary_artists;
const playlist = new import_discord_player.Playlist(this.context.player, {
author: {
name: type === "artist" ? data.name : artistsData.map((a) => a.name).join(", "),
url: ""
},
description: data.header_desc || "",
id: type === "artist" ? data.artistId : data.id,
rawPlaylist: data,
source: "arbitrary",
thumbnail: data.image?.replace(/\b(50x50|150x150)\b/, "500x500"),
title: type === "artist" ? data.name : data.title,
tracks: [],
type: data.type,
url: type === "artist" ? data.urls.overview : data.perma_url
});
const trackList = type === "artist" ? data.topSongs : data.list || [];
playlist.tracks = trackList.map(
(info) => this.buildTrack(info, context.requestedBy, playlist)
);
return this.createResponse(playlist, playlist.tracks);
}
}
async stream(track) {
const data = await this.fetchAPI({
__call: "song.generateAuthToken",
bitrate: 128,
url: track.raw.more_info.encrypted_media_url
});
if (!data || !data.auth_url) {
return "";
}
const stream = data.auth_url.split("?")[0].replace("ac.cf.saavncdn.com", "aac.saavncdn.com");
return stream;
}
async getRelatedTracks(track) {
const data = await this.fetchAPI({
__call: "reco.getreco",
pid: track.raw.id
});
if (!data) {
return this.createResponse();
}
const tracks = data.map((info) => this.buildTrack(info, track.requestedBy));
return this.createResponse(null, tracks);
}
async bridge(track) {
const query = `${track.author} ${track.source === "youtube" ? track.cleanTitle : track.title}`;
const raw = await this.fetchAPI({
__call: "search.getResults",
n: 1,
p: 1,
q: query
});
const data = raw?.results[0];
if (!data) {
return null;
}
const bridgedTrack = this.buildTrack(data, track.requestedBy);
const stream = await this.stream(bridgedTrack);
return stream;
}
parseURL(link) {
const regex = /https?:\/\/(?:www\.)?jiosaavn\.com\/(featured|artist|song|album)\/([^/?#]+)/;
const match = regex.exec(link);
if (!match) {
return { token: "", type: "" };
}
return { type: match[1], token: match[2] };
}
async fetchAPI(params) {
const apiBase = "https://www.jiosaavn.com/api.php";
const defaultParams = "includeMetaTags=0&ctx=wap6dot0&api_version=4&_format=json&_marker=0";
const url = `${apiBase}?${Object.entries(params).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&")}&${defaultParams}`;
try {
const resp = await fetch(url);
if (!resp.ok) {
return null;
}
return await resp.json();
} catch {
return null;
}
}
buildTrack(data, requestedBy, playlist) {
const track = new import_discord_player.Track(this.context.player, {
author: data.more_info.artistMap.primary_artists.map((a) => a.name).join(", "),
duration: import_discord_player.Util.formatDuration(data.more_info.duration),
playlist,
raw: data,
requestedBy,
source: "arbitrary",
thumbnail: data.image?.replace(/\b(50x50|150x150)\b/, "500x500"),
title: data.title,
url: data.perma_url,
views: data.play_count
});
return track;
}
};
JiosaavnExtractor.identifier = "dp.lpal.jiosaavn";
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
JiosaavnExtractor
});