Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

spotifly

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

spotifly - npm Package Compare versions

Comparing version
0.1.0
to
0.1.1
+17
dist/base.d.ts
import { SpotifyMyProfile, SpotifyPlaylistContents, SpotifyPlaylistMetadata } from "./types";
export declare class SpotiflyBase {
protected token: string;
protected tokenExpirationTimestampMs: number;
protected cookie: string;
private myProfileId;
constructor(cookie?: string);
protected refreshToken(): Promise<void>;
protected fetch<T>(url: string, optionalHeaders?: {
[index: string]: string;
}): Promise<T>;
protected post<T>(url: string, body: string): Promise<T>;
protected getPlaylistMetadata(id: string, limit?: number): Promise<SpotifyPlaylistMetadata>;
protected getPlaylistContents(id: string, limit?: number): Promise<SpotifyPlaylistContents>;
protected getMyProfile(): Promise<SpotifyMyProfile>;
protected getMyProfileId(): Promise<string>;
}
export class SpotiflyBase {
token = "";
tokenExpirationTimestampMs = -1;
cookie;
myProfileId = "";
constructor(cookie) {
this.cookie = cookie ?? "";
}
async refreshToken() {
if (this.tokenExpirationTimestampMs > Date.now())
return;
const response = await (await fetch("https://open.spotify.com/get_access_token", {
headers: { cookie: this.cookie }
})).json();
this.token = "Bearer " + response.accessToken;
this.tokenExpirationTimestampMs = response.accessTokenExpirationTimestampMs;
}
async fetch(url, optionalHeaders) {
await this.refreshToken();
return (await fetch(url, {
headers: { authorization: this.token, ...optionalHeaders }
})).json();
}
async post(url, body) {
await this.refreshToken();
return (await fetch(url, {
headers: {
authorization: this.token,
accept: "application/json",
"content-type": "application/json"
},
method: "POST",
body: body
})).json();
}
async getPlaylistMetadata(id, limit = 50) {
return this.fetch(`https://api-partner.spotify.com/pathfinder/v1/query?operationName=fetchPlaylistMetadata&variables=%7B%22uri%22%3A%22spotify%3Aplaylist%3A${id}%22%2C%22offset%22%3A0%2C%22limit%22%3A${limit}%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%226f7fef1ef9760ba77aeb68d8153d458eeec2dce3430cef02b5f094a8ef9a465d%22%7D%7D`);
}
async getPlaylistContents(id, limit = 50) {
return this.fetch(`https://api-partner.spotify.com/pathfinder/v1/query?operationName=fetchPlaylistContents&variables=%7B%22uri%22%3A%22spotify%3Aplaylist%3A${id}%22%2C%22offset%22%3A0%2C%22limit%22%3A${limit}%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22c56c706a062f82052d87fdaeeb300a258d2d54153222ef360682a0ee625284d9%22%7D%7D`);
}
async getMyProfile() {
if (!this.cookie)
throw Error("no cookie provided");
return this.fetch("https://api.spotify.com/v1/me");
}
async getMyProfileId() {
return this.myProfileId === "" ? this.myProfileId = (await this.getMyProfile()).id : this.myProfileId;
}
}
import { SpotiflyBase } from "./base.js";
export declare class SpotiflyPlaylist extends SpotiflyBase {
id: string;
constructor(cookie: string);
create(name: string): Promise<{
uri: string;
revision: string;
}>;
rename(newName: string): Promise<unknown>;
changeDescription(newDescription: string): Promise<unknown>;
fetchMetadata(limit?: number): Promise<{
__typename: string;
uri: string;
name: string;
description: string;
ownerV2: {
data: {
__typename: string;
uri: string;
username: string;
name: string;
avatar: {
sources: {
url: string;
width: number;
height: number;
}[];
};
};
};
images: {
items: {
extractedColors: {
colorRaw: {
hex: string;
isFallback: boolean;
};
};
sources: {
url: string;
width: any;
height: any;
}[];
}[];
};
collaborative: boolean;
followers: number;
format: string;
attributes: any[];
sharingInfo: {
shareUrl: string;
};
content: {
__typename: string;
totalCount: number;
pagingInfo: {
limit: number;
};
items: {
itemV2: {
__typename: string;
data: {
__typename: string;
uri: string;
trackDuration: {
totalMilliseconds: number;
};
};
};
}[];
};
}>;
fetchContents(limit?: number): Promise<{
uid: string;
addedAt: {
isoString: string;
};
addedBy: {
data: {
__typename: string;
uri: string;
username: string;
name: string;
avatar: {
sources: {
url: string;
width: number;
height: number;
}[];
};
};
};
attributes: any[];
itemV2: {
__typename: string;
data: {
__typename: string;
uri: string;
name: string;
trackDuration: {
totalMilliseconds: number;
};
playcount: string;
albumOfTrack: {
uri: string;
name: string;
artists: {
items: {
uri: string;
profile: {
name: string;
};
}[];
};
coverArt: {
sources: {
url: string;
width: number;
height: number;
}[];
};
};
artists: {
items: {
uri: string;
profile: {
name: string;
};
}[];
};
discNumber: number;
trackNumber: number;
playability: {
playable: boolean;
reason: string;
};
contentRating: {
label: string;
};
};
};
}[]>;
add(...trackUris: string[]): Promise<unknown>;
remove(...trackUris: string[]): Promise<unknown>;
cloneFrom(id: string, config?: {
name?: string;
description?: string;
limit?: number;
}): Promise<void>;
delete(): Promise<unknown>;
}
import { SpotiflyBase } from "./base.js";
import { Parse } from "./parse.js";
export class SpotiflyPlaylist extends SpotiflyBase {
id = "";
constructor(cookie) {
super(cookie);
}
async create(name) {
const [myProfileId, newPlaylist] = await Promise.all([
this.getMyProfileId(),
this.post("https://spclient.wg.spotify.com/playlist/v2/playlist", `{"ops":[{"kind":6,"updateListAttributes":{"newAttributes":{"values":{"name":"${name}","formatAttributes":[],"pictureSize":[]},"noValue":[]}}}]}`)
]);
await this.post(`https://spclient.wg.spotify.com/playlist/v2/user/${myProfileId}/rootlist/changes`, `{"deltas":[{"ops":[{"kind":2,"add":{"items":[{"uri":"${newPlaylist.uri}","attributes":{"timestamp":"${Date.now()}","formatAttributes":[],"availableSignals":[]}}],"addFirst":true}}],"info":{"source":{"client":5}}}],"wantResultingRevisions":false,"wantSyncResult":false,"nonces":[]}`);
this.id = Parse.uriToId(newPlaylist.uri);
return newPlaylist;
}
async rename(newName) {
return this.post(`https://spclient.wg.spotify.com/playlist/v2/playlist/${this.id}/changes`, `{"deltas":[{"ops":[{"kind":6,"updateListAttributes":{"newAttributes":{"values":{"name":"${newName}","formatAttributes":[],"pictureSize":[]},"noValue":[]}}}],"info":{"source":{"client":5}}}],"wantResultingRevisions":false,"wantSyncResult":false,"nonces":[]}`);
}
async changeDescription(newDescription) {
return this.post(`https://spclient.wg.spotify.com/playlist/v2/playlist/${this.id}/changes`, `{"deltas":[{"ops":[{"kind":6,"updateListAttributes":{"newAttributes":{"values":{"description":"${newDescription}","formatAttributes":[],"pictureSize":[]},"noValue":[]}}}],"info":{"source":{"client":5}}}],"wantResultingRevisions":false,"wantSyncResult":false,"nonces":[]}`);
}
async fetchMetadata(limit = 50) {
return (await this.getPlaylistMetadata(this.id, limit)).data.playlistV2;
}
async fetchContents(limit = 50) {
return (await this.getPlaylistContents(this.id, limit)).data.playlistV2.content.items;
}
async add(...trackUris) {
return this.post("https://api-partner.spotify.com/pathfinder/v1/query", `{"variables":{"uris":${JSON.stringify(trackUris)},"playlistUri":"spotify:playlist:${this.id}","newPosition":{"moveType":"BOTTOM_OF_PLAYLIST","fromUid":null}},"operationName":"addToPlaylist","extensions":{"persistedQuery":{"version":1,"sha256Hash":"200b7618afd05364c4aafb95e2070249ed87ee3f08fc4d2f1d5d04fdf1a516d9"}}}`);
}
async remove(...trackUris) {
const contents = await this.fetchContents();
const uids = [];
contents.forEach(x => { if (trackUris.includes(x.itemV2.data.uri))
uids.push(x.uid); });
return this.post("https://api-partner.spotify.com/pathfinder/v1/query", `{"variables":{"playlistUri":"spotify:playlist:${this.id}","uids":${JSON.stringify(uids)}},"operationName":"removeFromPlaylist","extensions":{"persistedQuery":{"version":1,"sha256Hash":"c0202852f3743f013eb453bfa15637c9da2d52a437c528960f4d10a15f6dfb49"}}}`);
}
async cloneFrom(id, config) {
const metadata = await this.getPlaylistMetadata(id, config?.limit ?? 50);
await this.create(config?.name ?? metadata.data.playlistV2.name);
this.changeDescription(config?.description ?? metadata.data.playlistV2.description);
this.add(...metadata.data.playlistV2.content.items.map(x => x.itemV2.data.uri));
}
async delete() {
const myProfileId = await this.getMyProfileId();
const response = await this.post(`https://spclient.wg.spotify.com/playlist/v2/user/${myProfileId}/rootlist/changes`, `{"deltas":[{"ops":[{"kind":3,"rem":{"items":[{"uri":"spotify:playlist:${this.id}"}],"itemsAsKey":true}}],"info":{"source":{"client":5}}}],"wantResultingRevisions":false,"wantSyncResult":false,"nonces":[]}`);
this.id = "";
return response;
}
}
export type SpotifyColorLyrics = {
lyrics: {
syncType: string;
lines: Array<{
startTimeMs: string;
words: string;
syllables: Array<any>;
endTimeMs: string;
}>;
provider: string;
providerLyricsId: string;
providerDisplayName: string;
syncLyricsUri: string;
isDenseTypeface: boolean;
alternatives: Array<any>;
language: string;
isRtlLanguage: boolean;
fullscreenAction: string;
};
colors: {
background: number;
text: number;
highlightText: number;
};
hasVocalRemoval: boolean;
};
export type SpotifyLikedSongs = {
data: {
me: {
library: {
tracks: {
__typename: string;
pagingInfo: {
offset: number;
limit: number;
};
items: Array<{
__typename: string;
addedAt: {
isoString: string;
};
track: {
_uri: string;
data: {
__typename: string;
name: string;
duration: {
totalMilliseconds: number;
};
albumOfTrack: {
uri: string;
name: string;
artists: {
items: Array<{
uri: string;
profile: {
name: string;
};
}>;
};
coverArt: {
sources: Array<{
url: string;
width: number;
height: number;
}>;
};
};
artists: {
items: Array<{
uri: string;
profile: {
name: string;
};
}>;
};
discNumber: number;
trackNumber: number;
contentRating: {
label: string;
};
playability: {
playable: boolean;
};
};
};
}>;
totalCount: number;
};
};
};
};
extensions: {};
};
export type SpotifyLikedSongsAdd = {
data: {
addLibraryItems: {
__typename: string;
};
};
extensions: {};
};
export type SpotifyLikedSongsRemove = {
data: {
removeLibraryItems: {
__typename: string;
};
};
extensions: {};
};
export type SpotifyMyLibrary = {
data: {
me: {
libraryV2: {
page: {
__typename: string;
availableFilters: Array<{
id: string;
name: string;
}>;
selectedFilters: Array<any>;
availableSortOrders: Array<{
id: string;
name: string;
}>;
selectedSortOrder: {
id: string;
name: string;
};
breadcrumbs: Array<any>;
items: Array<{
addedAt: {
isoString: string;
};
pinnable: boolean;
pinned: boolean;
depth: number;
playedAt?: {
isoString: string;
};
item: {
__typename: string;
_uri: string;
data: {
__typename: string;
uri: string;
name?: string;
count?: number;
image?: {
extractedColors: {
colorDark: {
hex: string;
isFallback: boolean;
};
};
sources: Array<{
url: string;
width: number;
height: number;
}>;
};
collaborative?: boolean;
ownerV2?: {
data: {
__typename: string;
id: string;
name: string;
uri: string;
avatar: {
sources: Array<{
url: string;
height: number;
width: number;
}>;
};
username: string;
};
};
images?: {
items: Array<{
extractedColors: {
colorDark: {
hex: string;
isFallback: boolean;
};
};
sources: Array<{
url: string;
width: any;
height: any;
}>;
}>;
};
profile?: {
name: string;
};
visuals?: {
avatarImage: {
extractedColors: {
colorDark: {
hex: string;
isFallback: boolean;
};
};
sources: Array<{
url: string;
width: number;
height: number;
}>;
};
};
};
};
}>;
pagingInfo: {
offset: number;
limit: number;
};
totalCount: number;
};
};
};
};
extensions: {
cacheControl: {
version: number;
hints: Array<{
path: Array<string>;
maxAge: number;
scope: string;
}>;
};
};
};
export type SpotifyMyProfile = {
birthdate: string;
country: string;
display_name: string;
email: string;
explicit_content: {
filter_enabled: boolean;
filter_locked: boolean;
};
external_urls: {
spotify: string;
};
followers: {
href: string;
total: number;
};
href: string;
id: string;
images: Array<{
height: number;
url: string;
width: number;
}>;
policies: {
opt_in_trial_premium_only_market: boolean;
};
product: string;
type: string;
uri: string;
};
export type SpotifyPlaylistContents = {
data: {
playlistV2: {
__typename: string;
content: {
__typename: string;
totalCount: number;
pagingInfo: {
offset: number;
limit: number;
};
items: Array<{
uid: string;
addedAt: {
isoString: string;
};
addedBy: {
data: {
__typename: string;
uri: string;
username: string;
name: string;
avatar: {
sources: Array<{
url: string;
width: number;
height: number;
}>;
};
};
};
attributes: Array<any>;
itemV2: {
__typename: string;
data: {
__typename: string;
uri: string;
name: string;
trackDuration: {
totalMilliseconds: number;
};
playcount: string;
albumOfTrack: {
uri: string;
name: string;
artists: {
items: Array<{
uri: string;
profile: {
name: string;
};
}>;
};
coverArt: {
sources: Array<{
url: string;
width: number;
height: number;
}>;
};
};
artists: {
items: Array<{
uri: string;
profile: {
name: string;
};
}>;
};
discNumber: number;
trackNumber: number;
playability: {
playable: boolean;
reason: string;
};
contentRating: {
label: string;
};
};
};
}>;
};
};
};
extensions: {
cacheControl: {
version: number;
hints: Array<{
path: [string, number, string];
maxAge: number;
scope: string;
}>;
};
};
};
export type SpotifyPlaylistMetadata = {
data: {
playlistV2: {
__typename: string;
uri: string;
name: string;
description: string;
ownerV2: {
data: {
__typename: string;
uri: string;
username: string;
name: string;
avatar: {
sources: Array<{
url: string;
width: number;
height: number;
}>;
};
};
};
images: {
items: Array<{
extractedColors: {
colorRaw: {
hex: string;
isFallback: boolean;
};
};
sources: Array<{
url: string;
width: any;
height: any;
}>;
}>;
};
collaborative: boolean;
followers: number;
format: string;
attributes: Array<any>;
sharingInfo: {
shareUrl: string;
};
content: {
__typename: string;
totalCount: number;
pagingInfo: {
limit: number;
};
items: Array<{
itemV2: {
__typename: string;
data: {
__typename: string;
uri: string;
trackDuration: {
totalMilliseconds: number;
};
};
};
}>;
};
};
};
extensions: {
cacheControl: {
version: number;
hints: Array<any>;
};
};
};
export type SpotifyProductState = {
ads: string;
catalogue: string;
country: string;
"preferred-locale": string;
"selected-language": string;
product: string;
"on-demand": string;
"multiuserplan-current-size": string;
"multiuserplan-member-type": string;
};
export type SpotifyTrackCredits = {
trackUri: string;
trackTitle: string;
roleCredits: Array<{
roleTitle: string;
artists: Array<{
uri: string;
name: string;
imageUri: string;
subroles: Array<string>;
weight: number;
}>;
}>;
extendedCredits: Array<any>;
sourceNames: Array<string>;
};
+21
-7

@@ -0,10 +1,9 @@

import { SpotiflyBase } from "./base.js";
import { Musixmatch } from "./musixmatch.js";
import { SpotifyAlbum, SpotifyArtist, SpotifyEpisode, SpotifyExtractedColors, SpotifyHome, SpotifyPlaylist, SpotifyPodcast, SpotifyPodcastEpisodes, SpotifyRelatedTrackArtists, SpotifySearchAlbums, SpotifySearchAll, SpotifySearchArtists, SpotifySearchPlaylists, SpotifySearchPodcasts, SpotifySearchTracks, SpotifySearchUsers, SpotifySection, SpotifyTrack, SpotifyUser } from "./types/index";
export declare class Spotifly {
private token;
private tokenExpirationTimestampMs;
private refreshToken;
private fetch;
import { SpotifyAlbum, SpotifyArtist, SpotifyColorLyrics, SpotifyEpisode, SpotifyExtractedColors, SpotifyHome, SpotifyLikedSongs, SpotifyLikedSongsAdd, SpotifyLikedSongsRemove, SpotifyMyLibrary, SpotifyPlaylist, SpotifyPodcast, SpotifyPodcastEpisodes, SpotifyProductState, SpotifyRelatedTrackArtists, SpotifySearchAlbums, SpotifySearchAll, SpotifySearchArtists, SpotifySearchPlaylists, SpotifySearchPodcasts, SpotifySearchTracks, SpotifySearchUsers, SpotifySection, SpotifyTrack, SpotifyTrackCredits, SpotifyUser } from "./types";
declare class SpotiflyMain extends SpotiflyBase {
constructor(cookie?: string);
getHomepage(): Promise<SpotifyHome>;
getTrack(id: string): Promise<SpotifyTrack>;
getTrackCredits(id: string): Promise<SpotifyTrackCredits>;
getRelatedTrackArtists(id: string): Promise<SpotifyRelatedTrackArtists>;

@@ -14,2 +13,4 @@ getArtist(id: string): Promise<SpotifyArtist>;

getPlaylist(id: string, limit?: number): Promise<SpotifyPlaylist>;
getPlaylistMetadata(id: string, limit?: number): Promise<import("./types").SpotifyPlaylistMetadata>;
getPlaylistContents(id: string, limit?: number): Promise<import("./types").SpotifyPlaylistContents>;
getUser(id: string, config?: {

@@ -33,4 +34,17 @@ playlistLimit: number;

extractImageColors(...urls: string[]): Promise<SpotifyExtractedColors>;
getMyProfile(): Promise<import("./types").SpotifyMyProfile>;
getMyLibrary(config?: Partial<{
filter: [] | ["Playlists"] | ["Playlists", "By you"] | ["Artists"];
order: "Recents" | "Recently Added" | "Alphabetical" | "Creator" | "Custom Order";
textFilter: string;
limit: number;
}>): Promise<SpotifyMyLibrary>;
getMyProductState(): Promise<SpotifyProductState>;
getMyLikedSongs(limit?: number): Promise<SpotifyLikedSongs>;
addToLikedSongs(...trackUris: string[]): Promise<SpotifyLikedSongsAdd>;
removeFromLikedSongs(...trackUris: string[]): Promise<SpotifyLikedSongsRemove>;
getTrackColorLyrics(id: string, imgUrl?: string): Promise<SpotifyColorLyrics>;
}
export { Parse } from "./parse.js";
export { Musixmatch };
export { SpotiflyPlaylist } from "./playlist.js";
export { Musixmatch, SpotiflyMain as Spotifly };

@@ -0,16 +1,7 @@

import { SpotiflyBase } from "./base.js";
import { Musixmatch } from "./musixmatch.js";
export class Spotifly {
token = "";
tokenExpirationTimestampMs = -1;
async refreshToken() {
if (this.tokenExpirationTimestampMs > Date.now())
return;
const response = await (await fetch("https://open.spotify.com/get_access_token")).json();
this.token = "Bearer " + response.accessToken;
this.tokenExpirationTimestampMs = response.accessTokenExpirationTimestampMs;
class SpotiflyMain extends SpotiflyBase {
constructor(cookie) {
super(cookie);
}
async fetch(url) {
await this.refreshToken();
return (await fetch(url, { headers: { authorization: this.token } })).json();
}
async getHomepage() {

@@ -22,2 +13,5 @@ return this.fetch(`https://api-partner.spotify.com/pathfinder/v1/query?operationName=home&variables=%7B%22timeZone%22%3A%22${encodeURIComponent(Intl.DateTimeFormat().resolvedOptions().timeZone)}%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22bbc1b1a421216c1299382b076c1aa8d52b91a0dfc91a4ae431a05b0a43a721e0%22%7D%7D`);

}
async getTrackCredits(id) {
return this.fetch(`https://spclient.wg.spotify.com/track-credits-view/v0/experimental/${id}/credits`);
}
async getRelatedTrackArtists(id) {

@@ -35,2 +29,8 @@ return this.fetch(`https://api-partner.spotify.com/pathfinder/v1/query?operationName=getRichTrackArtists&variables=%7B%22uri%22%3A%22spotify%3Atrack%3A${id}%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22b73a738f01c30e4dd90bc7e4c0e59f4d690a74f2b0c48a2eabbfd798a4a7e76a%22%7D%7D`);

}
async getPlaylistMetadata(id, limit = 50) {
return super.getPlaylistMetadata(id, limit);
}
async getPlaylistContents(id, limit = 50) {
return super.getPlaylistContents(id, limit);
}
async getUser(id, config = { playlistLimit: 10, artistLimit: 10, episodeLimit: 10 }) {

@@ -79,4 +79,39 @@ return this.fetch(`https://spclient.wg.spotify.com/user-profile-view/v3/profile/${id}?playlist_limit=${config.playlistLimit}&artist_limit=${config.artistLimit}&episode_limit=${config.episodeLimit}&market=from_token`);

}
/* Cookie Exclusive Functions */
async getMyProfile() {
return super.getMyProfile();
}
async getMyLibrary(config = { filter: [], order: "Recents", textFilter: "", limit: 50 }) {
if (!this.cookie)
throw Error("no cookie provided");
return this.fetch(`https://api-partner.spotify.com/pathfinder/v1/query?operationName=libraryV2&variables=%7B%22filters%22%3A${encodeURIComponent(JSON.stringify(config.filter))}%2C%22order%22%3A%22${config.order}%22%2C%22textFilter%22%3A%22${config.textFilter}%22%2C%22features%22%3A%5B%22LIKED_SONGS%22%2C%22YOUR_EPISODES%22%5D%2C%22limit%22%3A${config.limit}%2C%22offset%22%3A0%2C%22flatten%22%3Atrue%2C%22folderUri%22%3Anull%2C%22includeFoldersWhenFlattening%22%3Atrue%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%22e1f99520ac4e82cba64e9ebdee4ed5532024ee5af6956e8465e99709a8f8348f%22%7D%7D`);
}
async getMyProductState() {
if (!this.cookie)
throw Error("no cookie provided");
return this.fetch("https://spclient.wg.spotify.com/melody/v1/product_state?market=from_token");
}
async getMyLikedSongs(limit = 25) {
if (!this.cookie)
throw Error("no cookie provided");
return this.fetch(`https://api-partner.spotify.com/pathfinder/v1/query?operationName=fetchLibraryTracks&variables=%7B%22offset%22%3A0%2C%22limit%22%3A${limit}%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%228474ec383b530ce3e54611fca2d8e3da57ef5612877838b8dbf00bd9fc692dfb%22%7D%7D`);
}
async addToLikedSongs(...trackUris) {
if (!this.cookie)
throw Error("no cookie provided");
return this.post("https://api-partner.spotify.com/pathfinder/v1/query", `{"variables":{"uris":${JSON.stringify(trackUris)}},"operationName":"addToLibrary","extensions":{"persistedQuery":{"version":1,"sha256Hash":"656c491c3f65d9d08d259be6632f4ef1931540ebcf766488ed17f76bb9156d15"}}}`);
}
async removeFromLikedSongs(...trackUris) {
if (!this.cookie)
throw Error("no cookie provided");
return this.post("https://api-partner.spotify.com/pathfinder/v1/query", `{"variables":{"uris":${JSON.stringify(trackUris)}},"operationName":"removeFromLibrary","extensions":{"persistedQuery":{"version":1,"sha256Hash":"1103bfd4b9d80275950bff95ef6d41a02cec3357e8f7ecd8974528043739677c"}}}`);
}
async getTrackColorLyrics(id, imgUrl) {
if (!this.cookie)
throw Error("no cookie provided");
return this.fetch(`https://spclient.wg.spotify.com/color-lyrics/v2/track/${id}${imgUrl ? `/image/${encodeURIComponent(imgUrl)}` : ""}?format=json&vocalRemoval=false&market=from_token`, { "app-platform": "WebPlayer" });
}
}
export { Parse } from "./parse.js";
export { Musixmatch };
export { SpotiflyPlaylist } from "./playlist.js";
export { Musixmatch, SpotiflyMain as Spotifly };
export declare namespace Parse {
function urlToId(url: string): string;
function uriToId(uri: string): string;
function urlToUri(url: string): string;
function uriToUrl(uri: string): string;
}

@@ -11,2 +11,12 @@ export var Parse;

Parse.uriToId = uriToId;
function urlToUri(url) {
const parts = new URL(url).pathname.split("/");
return `spotify:${parts[1]}:${parts[2]}`;
}
Parse.urlToUri = urlToUri;
function uriToUrl(uri) {
const parts = uri.split(":");
return `https://open.spotify.com/${parts[1]}/${parts[2]}`;
}
Parse.uriToUrl = uriToUrl;
})(Parse || (Parse = {}));

@@ -9,2 +9,4 @@ export { SpotifyAlbum } from "./album";

export { SpotifyPlaylist } from "./playlist";
export { SpotifyPlaylistMetadata } from "./playlistMetadata";
export { SpotifyPlaylistContents } from "./playlistContents";
export { SpotifyPodcast } from "./podcast";

@@ -23,1 +25,7 @@ export { SpotifyPodcastEpisodes } from "./podcastEpisodes";

export { SpotifyUser } from "./user";
export { SpotifyMyProfile } from "./myProfile";
export { SpotifyMyLibrary } from "./myLibrary";
export { SpotifyProductState } from "./productState";
export { SpotifyColorLyrics } from "./colorLyrics";
export { SpotifyTrackCredits } from "./trackCredits";
export { SpotifyLikedSongs, SpotifyLikedSongsAdd, SpotifyLikedSongsRemove } from "./likedSongs";
{
"name": "spotifly",
"version": "0.1.0",
"version": "0.1.1",
"description": "Spotify library in typescript without using the Spotify Web API.",

@@ -40,4 +40,4 @@ "main": "dist/index.js",

"devDependencies": {
"bun-types": "^0.5.8"
"bun-types": "^0.6.12"
}
}
}
+150
-5

@@ -11,2 +11,3 @@ # ![](https://open.spotifycdn.com/cdn/images/favicon32.b64ecc03.png) `spotifly`

- Strongly typed API functions.
- Personalized fetching and automation using cookies.
- Automatic internal token refreshing.

@@ -22,3 +23,3 @@

`node.js (>=17.5.0)` or `bun` runtime.
`node.js (>=17.5.0)`, `bun` or `deno` runtime.

@@ -43,5 +44,8 @@ - ### Installation

Functions marked with an asterisk (*) require your spotify cookies to work. [How to get your Spotify cookies ?](#-how-to-get-your-spotify-cookies-)
- [`Spotifly` module](#spotifly-module)
- [`getHomepage`](#gethomepage-promisespotifyhome)
- [`getTrack`](#gettrackid-string-promisespotifytrack)
- [`getTrackCredits`](#gettrackcreditsid-string-promisespotifytrackcredits)
- [`getRelatedTrackArtists`](#getrelatedtrackartistsid-string-promisespotifyrelatedtrackartists)

@@ -51,2 +55,4 @@ - [`getArtist`](#getartistid-string-promisespotifyartist)

- [`getPlaylist`](#getplaylistid-string-limit-number-promisespotifyplaylist)
- [`getPlaylistMetadata`](#getplaylistmetadataid-string-limit-number-promisespotifyplaylistmetadata)
- [`getPlaylistContents`](#getplaylistcontentsid-string-limit-number-promisespotifyplaylistcontents)
- [`getUser`](#getuserid-string-config---playlistlimit-number-artistlimit-number-episodelimit-number--promisespotifyuser)

@@ -66,2 +72,20 @@ - [`getSection`](#getsectionid-string-promisespotifysection)

- [`extractImageColors`](#extractimagecolorsurls-string-promisespotifyextractedcolors)
- *[`getMyProfile`](#getmyprofile-promisespotifymyprofile)
- *[`getMyLibrary`](#getmylibraryconfig-promisespotifymylibrary)
- *[`getMyProductState`](#getmyproductstate-promisespotifyproductstate)
- *[`getMyLikedSongs`](#getmylikedsongs-promisespotifylikedsongs)
- *[`addToLikedSongs`](#addtolikedsongstrackuris-string-promisespotifylikedsongsadd)
- *[`removeFromLikedSongs`](#removefromlikedsongstrackuris-string-promisespotifylikedsongsremove)
- *[`getTrackColorLyrics`](#gettrackcolorlyricsid-string-imgurl-string-promisespotifycolorlyrics)
- *[`SpotiflyPlaylist` module](#spotiflyplaylist-module)
- [`id`](#id-string)
- [`create`](#createname-string)
- [`rename`](#renamenewname-string)
- [`changeDescription`](#changedescriptionnewdescription-string)
- [`fetchMetadata`](#fetchmetadatalimit-number)
- [`fetchContents`](#fetchcontentslimit-number)
- [`add`](#addtrackuris-string)
- [`remove`](#removetrackuris-string)
- [`cloneFrom`](#clonefromid-string-config--name-string-description-string-limit-number-)
- [`delete`](#delete)
- [`Musixmatch` module](#musixmatch-module)

@@ -74,2 +98,5 @@ - [`search`](#searchterms-string-musixmatchsearch)

- [`uriToId`](#uritoiduri-string-string)
- [`urlToUri`](#urltouriurl-string-string)
- [`uriToUrl`](#uritourluri-string-string)
- [How to get your Spotify cookies ?](#-how-to-get-your-spotify-cookies-)

@@ -80,2 +107,4 @@

### `new Spotifly(cookie?: string)`
The main module containing all the Spotify API functions.

@@ -91,2 +120,6 @@

- ### `getTrackCredits(id: string)`: [*`Promise<SpotifyTrackCredits>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/trackCredits.ts)
Fetch the credits of the provided track id.
- ### `getRelatedTrackArtists(id: string)`: [*`Promise<SpotifyRelatedTrackArtists>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/relatedTrackArtists.ts)

@@ -106,6 +139,14 @@

Fetch the details of the provided playlist id, with optional limit for amount of tracks to fetch.
Fetch all the details of the provided playlist id, with optional limit for amount of tracks to fetch.
- ### `getUser(id: string, config?: { playlistLimit?: number, artistLimit?: number, episodeLimit?: number })`: [*`Promise<SpotifyUser>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/user.ts)
- ### `getPlaylistMetadata(id: string, limit?: number)`: [*`Promise<SpotifyPlaylistMetadata>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/playlistMetadata.ts)
Fetch the metadata only of the provided playlist id, with optional limit for amount of tracks to fetch.
- ### `getPlaylistContents(id: string, limit?: number)`: [*`Promise<SpotifyPlaylistContents>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/playlistContents.ts)
Fetch the contents of the provided playlist id, with optional limit for amount of tracks to fetch.
- ### `getUser(id: string, config?: { playlistLimit?: number, artistLimit?: number, episodeLimit?: number })`: [*`Promise<SpotifyUser>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/user.ts)
Fetch the details of the provided user id, with optional limit for amount of tracks to fetch.

@@ -161,2 +202,4 @@

If you want to fetch lyrics directly from Spotify, see [`getTrackColorLyrics`](#gettrackcolorlyricsid-string-imgurl-string-promisespotifycolorlyrics).
- ### `extractImageColors(...urls: string[])`: [*`Promise<SpotifyExtractedColors>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/extractedColors.ts)

@@ -166,4 +209,87 @@

> The following functions require cookies to work. [How to get your Spotify cookies ?](#-how-to-get-your-spotify-cookies-)
- ### `getMyProfile()`: [*`Promise<SpotifyMyProfile>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/myProfile.ts)
Fetch the details of your Spotify profile.
- ### `getMyLibrary(config?)`: [*`Promise<SpotifyMyLibrary>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/myLibrary.ts)
Fetch your Spotify library.
- config.filter?: `[] | ["Playlists"] | ["Playlists", "By you"] | ["Artists"]`
- config.order?: `"Recents" | "Recently Added" | "Alphabetical" | "Creator" | "Custom Order"`
- config.textFilter?: `string`
- config.limit?: `number`
- ### `getMyProductState()`: [*`Promise<SpotifyProductState>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/productState.ts)
Fetch the details of your Spotify product state like premium plan, etc.
- ### `getMyLikedSongs()`: [*`Promise<SpotifyLikedSongs>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/likedSongs.ts)
Fetch the songs you have liked from your Spotify library.
- ### `addToLikedSongs(...trackUris: string[])`: [*`Promise<SpotifyLikedSongsAdd>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/likedSongs.ts)
Add the tracks to your liked songs library.
- ### `removeFromLikedSongs(...trackUris: string[])`: [*`Promise<SpotifyLikedSongsRemove>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/likedSongs.ts)
Remove the tracks from your liked songs library.
- ### `getTrackColorLyrics(id: string, imgUrl?: string)`: [*`Promise<SpotifyColorLyrics>`*](https://github.com/tr1ckydev/spotifly/blob/main/src/types/likedSongs.ts)
Fetch the track lyrics directly from Spotify's internal Musixmatch API with an optional image url to fetch the colors of that image.
## `SpotiflyPlaylist` module
### `new SpotiflyPlaylist(cookie: string)`
The module containing all the functions to interact with playlists in your Spotify library using the cookies provided. [How to get your Spotify cookies ?](#-how-to-get-your-spotify-cookies-)
- ### `id`: `string`
Property to get or set the playlist id with whom the following functions will be interacting.
- ### `create(name: string)`
Create a new empty playlist with the provided name in your Spotify library and sets the `id` with the newly created one.
- ### `rename(newName: string)`
Change the name of the playlist with the new name provided.
- ### `changeDescription(newDescription: string)`
Change the description of the playlist with the new description provided.
- ### `fetchMetadata(limit?: number)`
Fetch the metadata of the playlist.
- ### `fetchContents(limit?: number)`
Fetch the contents of the playlist.
- ### `add(...trackUris: string[])`
Add tracks to the playlist from the provided track uris.
- ### `remove(...trackUris: string[])`
Remove tracks from the playlist from the provided track uris.
- ### `cloneFrom(id: string, config?: { name?: string, description?: string, limit?: number; })`
Create a new playlist in your Spotify library by cloning from another playlist with optional config to change the data of the created playlist and sets the `id` with the newly created one.
- ### `delete()`
Delete the playlist from your Spotify library.
## `Musixmatch` module

@@ -195,12 +321,31 @@

- ### `uriToId(uri: string)`: *`string`*
Extract the id from a Spotify uri (i.e. `spotify:track:abcdefghijk`).
- ### `urlToUri(url: string)`: `string`
- ### `uriToId(uri: string)`: *`string`*
Convert an `open.spotify.com` url to a Spotify uri (i.e. `spotify:track:abcdefghijk`).
Extract the id from a spotify uri (i.e. `spotify:track:abcdefghijk`).
- ### `uriToUrl(uri: string)`: `string`
Convert a Spotify uri (i.e. `spotify:track:abcdefghijk`) to an `open.spotify.com` url.
## 🍪 How to get your Spotify cookies ?
- Login to your Spotify account in your browser.
- Open *Developer Tools* of your browser and switch to *Network* tab.
- Go to https://open.spotify.com/.
- Find the request with the name `open.spotify.com` and open it.
- From the *Headers* tab, scroll to *Request Headers* section.
- Copy the contents of the `Cookie` header value.
The copied value is your Spotify cookies.
## 📜 License
This repository uses MIT License. See [LICENSE](https://github.com/tr1ckydev/spotifly/blob/main/LICENSE) for full license text.