import axios, { type AxiosInstance } from "axios"; import { ampApiUrl, appleMusicHomepageUrl, licenseApiUrl, webplaybackApiUrl } from "../constants/urls.js"; import type { GetSongResponse } from "./types/appleMusic/responses.js"; import type { SongAttributesExtensionTypes } from "./types/appleMusic/extensions.js"; import { getToken } from "./token.js"; import { config } from "../config.js"; export default class AppleMusicApi { private storefront: string; private http: AxiosInstance; public constructor( storefront: string, language: string, mediaUserToken: string ) { this.storefront = storefront; this.http = axios.create({ baseURL: ampApiUrl }); this.http.defaults.headers.common["Origin"] = appleMusicHomepageUrl; this.http.defaults.headers.common["Media-User-Token"] = mediaUserToken; // yeah dude. awesome // https://stackoverflow.com/a/54636780 this.http.defaults.params = {}; this.http.defaults.params["l"] = language; } public async login(): Promise { this.http.defaults.headers.common["Authorization"] = `Bearer ${await getToken(appleMusicHomepageUrl)}`; } async getSong< T extends SongAttributesExtensionTypes = ["extendedAssetUrls"] > ( id: string, extend: T = ["extendedAssetUrls"] as T ): Promise> { return (await this.http.get>(`/v1/catalog/${this.storefront}/songs/${id}`, { params: { extend: extend.join(",") } })).data; } async getWebplayback( trackId: string ): Promise { return (await this.http.post(webplaybackApiUrl, { salableAdamId: trackId, language: config.downloader.api.language })).data; } async getWidevineLicense( trackId: string, trackUri: string, challenge: string ): Promise { return (await this.http.post(licenseApiUrl, { challenge: challenge, "key-system": "com.widevine.alpha", uri: trackUri, adamId: trackId, isLibrary: false, "user-initiated": true }, { headers: { // do these do anything. "x-apple-music-user-token": this.http.defaults.headers.common["Media-User-Token"], "x-apple-renewal": true }})).data; } } // these are super special types // i'm not putting this in the ./types folder. export type WebplaybackResponse = { songList: { assets: { flavor: string, URL: string }[], songId: string }[] }; export type WidevineLicenseResponse = { license: string | undefined };