Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: playlists #5

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/interfaces/album.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiArtist } from './artist';
import { ApiPartialTrack } from './track';
import { ApiImage } from './user';
import { ApiImage } from '.';

export interface ApiPartialAlbum {
album_type: 'album' | 'single' | 'compilation';
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/artist.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApiImage } from './user';
import { ApiImage } from './';

export interface ApiPartialArtist {
external_urls: Record<string, string>;
Expand Down
54 changes: 54 additions & 0 deletions src/interfaces/episode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ApiImage } from '.';

export interface ApiEpisode {
audio_preview_url: string | null;
description: string;
html_description: string;
duration_ms: number;
explicit: boolean;
external_urls: Record<string, string>;
href: string;
id: string;
images: ApiImage[];
is_externally_hosted: boolean;
is_playable: boolean;
/**
* @deprecated {@link https://developer.spotify.com/documentation/web-api/reference/get-information-about-the-users-current-playback}
*/
language?: string;
languages: string[];
name: string;
release_date: string;
release_date_precision: 'year' | 'month' | 'day';
resume_point: {
fully_played: boolean;
resume_position_ms: number;
};
type: 'episode';
uri: string;
restrictions: {
reason: 'market' | 'product' | 'explicit'
};
show: {
available_markets: string[];
copyrights: {
text: string;
type: 'C' | 'P'
}
description: string;
html_description: string;
explicit: boolean;
external_urls: Record<string, string>;
href: string;
id: string;
images: ApiImage[];
is_externally_hosted: boolean | null;
languages: string[];
media_type: string;
name: string;
publisher: string;
type: 'show';
uri: string;
total_episodes: number;
};
}
5 changes: 5 additions & 0 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ApiImage {
url: string;
height: number;
width: number;
}
73 changes: 17 additions & 56 deletions src/interfaces/player.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import { ApiTrack } from './track';
import { ApiImage } from './user';
import { ApiEpisode } from './episode';

export enum PlayerContextType {
Artist = 'artist',
Playlist = 'playlist',
Album = 'album',
Show = 'show',
}

export enum CurrentlyPlayingType {
Track = 'track',
Episode = 'episode',
// when not even spotify knows what you're playing, you music taste might suck
Unknown = 'unknown'
}

export interface ApiDevice {
id?: string;
Expand All @@ -17,7 +31,7 @@ export interface ApiPlaybackState {
repeat_state: 'off' | 'track' | 'context';
shuffle_state: boolean;
context?: {
type: 'artist' | 'playlist' | 'album' | 'show';
type: PlayerContextType;
href: string;
external_urls: Record<string, string>;
uri: string;
Expand All @@ -26,7 +40,7 @@ export interface ApiPlaybackState {
progress_ms: number;
is_playing: boolean;
item?: ApiTrack | ApiEpisode;
currently_playing_type: 'track' | 'episode' | 'unknown';
currently_playing_type: CurrentlyPlayingType;
actions: {
interrupting_playback: boolean
pausing: boolean
Expand All @@ -39,57 +53,4 @@ export interface ApiPlaybackState {
toggling_repeat_track: boolean
transferring_playback: boolean
};
}

export interface ApiEpisode {
audio_preview_url: string | null;
description: string;
html_description: string;
duration_ms: number;
explicit: boolean;
external_urls: Record<string, string>;
href: string;
id: string;
images: ApiImage[];
is_externally_hosted: boolean;
is_playable: boolean;
/**
* @deprecated {@link https://developer.spotify.com/documentation/web-api/reference/get-information-about-the-users-current-playback}
*/
language?: string;
languages: string[];
name: string;
release_date: string;
release_date_precision: 'year' | 'month' | 'day';
resume_point: {
fully_played: boolean;
resume_position_ms: number;
};
type: 'episode';
uri: string;
restrictions: {
reason: 'market' | 'product' | 'explicit'
};
show: {
available_markets: string[];
copyrights: {
text: string;
type: 'C' | 'P'
}
description: string;
html_description: string;
explicit: boolean;
external_urls: Record<string, string>;
href: string;
id: string;
images: ApiImage[];
is_externally_hosted: boolean | null;
languages: string[];
media_type: string;
name: string;
publisher: string;
type: 'show';
uri: string;
total_episodes: number;
};
}
61 changes: 61 additions & 0 deletions src/interfaces/playlist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ApiImage } from '.';
import { ApiEpisode } from './episode';
import { ApiTrack } from './track';

export interface ApiUserPlaylists {
href: string;
limit: number;
next: string | null;
offset: number;
previous: string | null;
total: number;
items: ApiPartialPlaylist[];
}

export interface ApiPlaylistOwner {
external_urls: Record<string, string>;
followers: {
href: string;
total: number;
};
href: string;
id: string;
type: 'user';
uri: string;
display_name: string | null;
}

export interface ApiPartialPlaylist {
collaborative: boolean;
description: string | null;
external_urls: Record<string, string>;
href: string;
id: string;
images: ApiImage[];
name: string;
owner: ApiPlaylistOwner;
public: boolean;
snapshot_id: string;
tracks: {
href: string;
total: number;
} | null;
type: string;
uri: string;
}

// no ApiPlaylist for now since only ApiPartialPlaylist is used to get users' playlists.
// export interface ApiPlaylist extends ApiPartialPlaylist {}

export interface ApiPlaylistTrack<T extends ApiTrack | ApiEpisode> {
/*
* Will only be `null` in very old playlists.
*/
added_at: string | null;
/*
* Will only be `null` in very old playlists.
*/
added_by: Omit<ApiPlaylistOwner, 'display_name'> | null;
is_local: boolean;
track: T;
}
8 changes: 2 additions & 6 deletions src/interfaces/user.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ApiImage } from '.';

export interface ApiUser {
country?: string;
display_name: string | null;
Expand All @@ -15,10 +17,4 @@ export interface ApiUser {
id: string;
images: ApiImage[];
product?: 'premium' | 'free' | 'open';
}

export interface ApiImage {
url: string;
height: number;
width: number;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Lunify } from '../..';
import { Player } from '.';
import { Lunify, Player, PlayerDevice } from '../..';
import { ApiDevice } from '../../../interfaces/player';
import { PlayerDevice } from './Device';

export class PlayerDeviceManager {

constructor(
public client: Lunify,
private player: Player,
public player: Player,
) { }

/**
Expand All @@ -24,11 +22,9 @@ export class PlayerDeviceManager {
}
});

const devices: PlayerDevice[] = [];

for (const apiDevice of res.devices) devices.push(new PlayerDevice(this.client, this.player.user, apiDevice));

return devices;
return res.devices.map((device) =>
new PlayerDevice(this.client, this.player, device)
);
}

/**
Expand All @@ -40,23 +36,16 @@ export class PlayerDeviceManager {
* player.devices.transferPlaybackTo(deviceId);
* ```
*/
async transferPlaybackTo(device: string | string[]) {

const finalDevices: string[] = [];

if (typeof device !== 'string') {
for (const d of device) finalDevices.push(d);
}
else {
finalDevices.push(device);
}
async transferPlaybackTo(deviceId: string | string[]) {

await this.client.rest.put('/me/player', {
headers: {
Authorization: await this.player.user.oauth.getAuthorization()
},
body: {
device_ids: finalDevices
device_ids: typeof deviceId === 'string'
? [deviceId]
: deviceId
}
});

Expand Down
1 change: 1 addition & 0 deletions src/lib/managers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './cache';
export * from './credentials';
export * from './devices';
export * from './oauth';
export * from './rest';
export * from './tracks';
Expand Down
9 changes: 3 additions & 6 deletions src/lib/managers/oauth/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { EventEmitter } from 'stream';
import { ApiTokenResponse } from '../../../interfaces/oauth';
import { Lunify, LunifyErrors, RequestDomain, Scopes } from '../..';
import { UserOauth } from '../../structures/user';

export class OauthManager extends EventEmitter {
export class OauthManager {

constructor(public client: Lunify) {
super();
}
constructor(public client: Lunify) {}

/**
* Create a oAuth url for users to authorize
Expand Down Expand Up @@ -95,4 +92,4 @@ export class OauthManager extends EventEmitter {
return new UserOauth(this.client, res);
}

}
}
Loading