I'm trying to use the Spotify SDK in an Angular app. I can import the script from the CDN in index.html but can't figure out how I'm supposed to actually use it at the component level. I feel like I'm missing something basic here, like importing it somehow but I can't seem to get anything to work.
I know that's not much info to work off of, but really I just need to know how to use the imported script at a component level.
Error message on trying to use the SDK in the component level:
Property 'onSpotifyWebPlaybackSDKReady' does not exist on type 'Window & typeof globalThis'
I also found this source code I thought I might try as an example to see how this all fits together. I don't understand how it all works, but in that project's package.json
this line was included: "@types/spotify-web-playback-sdk": "0.1.9",
CodePudding user response:
Turns out @Konrad Linkowski's comment and simply declaring a variable for the required objects seems to fix this and allows full use of the library. There might be a more correct way of handling things, but this works without issue.
declare global {
interface Window { onSpotifyWebPlaybackSDKReady: any; }
}
declare let Spotify: any;
window.onSpotifyWebPlaybackSDKReady = window.onSpotifyWebPlaybackSDKReady || {};
CodePudding user response:
You can put this code into a file with the suffix .d.ts
anywhere in your src
folder. I took the types from here: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/spotify-web-playback-sdk/index.d.ts and converted them into a module.
spotify.d.ts
export {};
declare global {
interface Window {
onSpotifyWebPlaybackSDKReady(): void;
Spotify: typeof Spotify;
}
namespace Spotify {
interface Entity {
name: string;
uri: string;
url: string;
}
interface Album {
name: string;
uri: string;
images: Image[];
}
interface Error {
message: string;
}
type ErrorTypes =
| 'account_error'
| 'authentication_error'
| 'initialization_error'
| 'playback_error';
interface Image {
height?: number | null | undefined;
url: string;
size?: string | null | undefined;
width?: number | null | undefined;
}
interface PlaybackContextTrack extends Entity {
artists: Entity[];
content_type: string;
estimated_duration: number;
group: Entity;
images: Image[];
uid: string;
}
interface PlaybackContextRestrictions {
pause: string[];
resume: string[];
seek: string[];
skip_next: string[];
skip_prev: string[];
toggle_repeat_context: string[];
toggle_repeat_track: string[];
toggle_shuffle: string[];
peek_next: string[];
peek_prev: string[];
}
interface PlaybackContextMetadata extends Entity {
current_item: PlaybackContextTrack;
next_items: PlaybackContextTrack[];
previous_items: PlaybackContextTrack[];
restrictions: PlaybackContextRestrictions;
options: {
repeat_mode: string;
shuffled: boolean;
};
}
interface PlaybackContext {
metadata: PlaybackContextMetadata | null;
uri: string | null;
}
interface PlaybackDisallows {
pausing?: boolean;
peeking_next?: boolean;
peeking_prev?: boolean;
resuming?: boolean;
seeking?: boolean;
skipping_next?: boolean;
skipping_prev?: boolean;
toggling_repeat_context?: boolean;
toggling_repeat_track?: boolean;
toggling_shuffle?: boolean;
}
interface PlaybackRestrictions {
disallow_pausing_reasons?: string[];
disallow_peeking_next_reasons?: string[];
disallow_peeking_prev_reasons?: string[];
disallow_resuming_reasons?: string[];
disallow_seeking_reasons?: string[];
disallow_skipping_next_reasons?: string[];
disallow_skipping_prev_reasons?: string[];
disallow_toggling_repeat_context_reasons?: string[];
disallow_toggling_repeat_track_reasons?: string[];
disallow_toggling_shuffle_reasons?: string[];
}
interface PlaybackState {
context: PlaybackContext;
disallows: PlaybackDisallows;
duration: number;
paused: boolean;
position: number;
loading: boolean;
timestamp: number;
/**
* 0: NO_REPEAT
* 1: ONCE_REPEAT
* 2: FULL_REPEAT
*/
repeat_mode: 0 | 1 | 2;
shuffle: boolean;
restrictions: PlaybackRestrictions;
track_window: PlaybackTrackWindow;
playback_id: string;
playback_quality: string;
playback_features: {
hifi_status: string;
};
}
interface PlaybackTrackWindow {
current_track: Track;
previous_tracks: Track[];
next_tracks: Track[];
}
interface PlayerInit {
name: string;
getOAuthToken(cb: (token: string) => void): void;
volume?: number | undefined;
}
type ErrorListener = (err: Error) => void;
type PlaybackInstanceListener = (inst: WebPlaybackInstance) => void;
type PlaybackStateListener = (s: PlaybackState) => void;
type EmptyListener = () => void;
type AddListenerFn = ((
event: 'ready' | 'not_ready',
cb: PlaybackInstanceListener
) => void) &
((event: 'autoplay_failed', cb: EmptyListener) => void) &
((event: 'player_state_changed', cb: PlaybackStateListener) => void) &
((event: ErrorTypes, cb: ErrorListener) => void);
class Player {
readonly _options: PlayerInit & { id: string };
constructor(options: PlayerInit);
connect(): Promise<boolean>;
disconnect(): void;
getCurrentState(): Promise<PlaybackState | null>;
getVolume(): Promise<number>;
nextTrack(): Promise<void>;
addListener: AddListenerFn;
on: AddListenerFn;
removeListener(
event: 'ready' | 'not_ready' | 'player_state_changed' | ErrorTypes,
cb?: ErrorListener | PlaybackInstanceListener | PlaybackStateListener
): void;
pause(): Promise<void>;
previousTrack(): Promise<void>;
resume(): Promise<void>;
seek(pos_ms: number): Promise<void>;
setName(name: string): Promise<void>;
setVolume(volume: number): Promise<void>;
togglePlay(): Promise<void>;
activateElement(): Promise<void>;
}
interface Track {
album: Album;
artists: Entity[];
duration_ms: number;
id: string | null;
is_playable: boolean;
name: string;
uid: string;
uri: string;
media_type: 'audio' | 'video';
type: 'track' | 'episode' | 'ad';
track_type: 'audio' | 'video';
linked_from: {
uri: string | null;
id: string | null;
};
}
interface WebPlaybackInstance {
device_id: string;
}
}
}
Then to use it, you can create the global window function, and add the script programatically afterwards.
export class AppComponent {
ngOnInit() {
window.onSpotifyWebPlaybackSDKReady = () => {
console.log('spotify script loaded');
const player = new Spotify.Player({
name: 'nameHere',
getOAuthToken: () => {
'functionHere';
},
});
player.connect().then((success) => console.log('Connected:', success));
};
const script = document.createElement('script');
script.src = 'https://sdk.scdn.co/spotify-player.js';
document.body.appendChild(script);
}
}
Alternatively you can install this package: https://www.npmjs.com/package/@types/spotify-web-playback-sdk
npm i @types/spotify-web-playback-sdk
But it isn't set up as a module so you will need to reference it manually like so
/// <reference types="@types/spotify-web-playback-sdk"/>
import { Component } from '@angular/core';
@Component({ ... })
export class AppComponent { ... }
That's a special typescript syntax for referencing types: https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-