mirror of https://github.com/miraclx/freyr-js
feat(Apple Music): Automated Authentication (#623)
* apple_music_token * apple_music_token
This commit is contained in:
parent
3a20b68a4b
commit
92228262ba
7
cli.js
7
cli.js
|
|
@ -1537,7 +1537,12 @@ async function init(packageJson, queries, options) {
|
|||
const authQueue = new AsyncQueue('cli:authQueue', 1, async (service, logger) => {
|
||||
async function coreAuth(loginLogger) {
|
||||
if (!Config.opts.attemptAuth) return;
|
||||
const authHandler = service.newAuth();
|
||||
let authHandler;
|
||||
try {
|
||||
authHandler = service.newAuth();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const url = await authHandler.getUrl;
|
||||
if (Config.opts.autoOpenBrowser)
|
||||
await processPromise(open(url), loginLogger, {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@
|
|||
"refreshToken": "AQCbeNs5NiGfHa6He0BlOdoQwEIdo2lwBmefEvpvqVy8WlL2HV7rmGbb30_oaZHMDf9MgXtGjI0gV_QukL33PE8c2bGqJHeMqdHBUjKWhxOW19snJkGUiyxen8UvmDG-OP4"
|
||||
},
|
||||
"apple_music": {
|
||||
"storefront": "us",
|
||||
"developerToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ.eyJpc3MiOiJBTVBXZWJQbGF5IiwiaWF0IjoxNzAyNTAyMjM0LCJleHAiOjE3MDk3NTk4MzQsInJvb3RfaHR0cHNfb3JpZ2luIjpbImFwcGxlLmNvbSJdfQ.zzeMLmez71PLinP9GozYSQnF7NYyCiXHB9tKL3-cyu3LzyeRnYz0ejLj4CrNJs0dlNkFg9_mwKmMLueUAR-KRg"
|
||||
"storefront": "us"
|
||||
},
|
||||
"deezer": {
|
||||
"retries": 2
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import xurl from 'url';
|
||||
import path from 'path';
|
||||
|
||||
import got from 'got';
|
||||
import Promise from 'bluebird';
|
||||
import NodeCache from 'node-cache';
|
||||
import {Client} from '@yujinakayama/apple-music';
|
||||
|
|
@ -22,7 +23,9 @@ export default class AppleMusic {
|
|||
// https://www.debuggex.com/r/Pv_Prjinkz1m2FOB
|
||||
VALID_URL:
|
||||
/(?:(?:(?:(?:https?:\/\/)?(?:www\.)?)(?:(?:music|(?:geo\.itunes))\.apple.com)\/([a-z]{2})\/(song|album|artist|playlist)\/(?:([^/]+)\/)?\w+)|(?:apple_music:(track|album|artist|playlist):([\w.]+)))/,
|
||||
PROP_SCHEMA: {},
|
||||
PROP_SCHEMA: {
|
||||
developerToken: {type: 'string'},
|
||||
},
|
||||
};
|
||||
|
||||
[symbols.meta] = AppleMusic[symbols.meta];
|
||||
|
|
@ -30,6 +33,8 @@ export default class AppleMusic {
|
|||
#store = {
|
||||
cache: new NodeCache(),
|
||||
core: null,
|
||||
axiosInstance: null,
|
||||
expiry: null,
|
||||
defaultStorefront: null,
|
||||
isAuthenticated: false,
|
||||
};
|
||||
|
|
@ -37,23 +42,35 @@ export default class AppleMusic {
|
|||
constructor(config) {
|
||||
if (!config) throw new Error(`[AppleMusic] Please define a configuration object`);
|
||||
if (typeof config !== 'object') throw new Error(`[AppleMusic] Please define a configuration as an object`);
|
||||
if (!config.developerToken)
|
||||
throw new Error(`[AppleMusic] Please define [developerToken] as a property within the configuration`);
|
||||
this.#store.core = new Client({developerToken: config.developerToken});
|
||||
for (let instance of [this.#store.core.albums, this.#store.core.artists, this.#store.core.playlists, this.#store.core.songs])
|
||||
instance.axiosInstance.interceptors.request.use(conf => ((conf.headers.origin = 'https://music.apple.com'), conf));
|
||||
this.#store.core = new Client({});
|
||||
this.#store.axiosInstance = this.#store.core.songs.axiosInstance;
|
||||
for (let instance of [this.#store.core.albums, this.#store.core.artists, this.#store.core.playlists])
|
||||
instance.axiosInstance = this.#store.axiosInstance;
|
||||
this.#store.axiosInstance.defaults.headers['Origin'] = 'https://music.apple.com';
|
||||
this.#store.defaultStorefront = config.storefront;
|
||||
this.#store.isAuthenticated = !!config.developerToken;
|
||||
}
|
||||
|
||||
loadConfig(_config) {}
|
||||
expiresAt(developerToken) {
|
||||
let segments = developerToken.split('.');
|
||||
let payload = Buffer.from(segments[1] || '', 'base64');
|
||||
let parsed = JSON.parse(payload.toString());
|
||||
return parsed.exp * 1000;
|
||||
}
|
||||
|
||||
loadConfig(config) {
|
||||
if (config.developerToken) {
|
||||
this.#store.expiry = this.expiresAt(config.developerToken);
|
||||
this.#store.core.configuration.developerToken = config.developerToken;
|
||||
this.#store.axiosInstance.defaults.headers['Authorization'] = `Bearer ${config.developerToken}`;
|
||||
}
|
||||
}
|
||||
|
||||
hasOnceAuthed() {
|
||||
throw Error('Unimplemented: [AppleMusic:hasOnceAuthed()]');
|
||||
return this.#store.isAuthenticated;
|
||||
}
|
||||
|
||||
isAuthed() {
|
||||
return this.#store.isAuthenticated;
|
||||
return Date.now() < this.#store.expiry;
|
||||
}
|
||||
|
||||
newAuth() {
|
||||
|
|
@ -61,19 +78,32 @@ export default class AppleMusic {
|
|||
}
|
||||
|
||||
canTryLogin() {
|
||||
return !!this.#store.core.configuration.developerToken;
|
||||
return true;
|
||||
}
|
||||
|
||||
hasProps() {
|
||||
return false;
|
||||
return this.#store.isAuthenticated;
|
||||
}
|
||||
|
||||
getProps() {
|
||||
throw Error('Unimplemented: [AppleMusic:getProps()]');
|
||||
return {
|
||||
developerToken: this.#store.core.configuration.developerToken,
|
||||
};
|
||||
}
|
||||
|
||||
async login() {
|
||||
throw Error('Unimplemented: [AppleMusic:login()]');
|
||||
let browsePage = await got('https://music.apple.com/us/browse').text();
|
||||
let scriptUri;
|
||||
if (!(scriptUri = browsePage.match(/assets\/index-[a-z0-9]{8}\.js/)?.[0]))
|
||||
throw new Error('Unable to extract core script from Apple Music');
|
||||
let script = await got(`https://music.apple.com/${scriptUri}`).text();
|
||||
let developerToken;
|
||||
if (!(developerToken = script.match(/eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ[^"]+/)?.[0]))
|
||||
throw new Error('Unable to extract developerToken from Apple Music core script');
|
||||
this.#store.expiry = this.expiresAt(developerToken);
|
||||
this.#store.core.configuration.developerToken = developerToken;
|
||||
this.#store.axiosInstance.defaults.headers['Authorization'] = `Bearer ${developerToken}`;
|
||||
return (this.#store.isAuthenticated = true);
|
||||
}
|
||||
|
||||
validateType(uri) {
|
||||
|
|
@ -94,7 +124,7 @@ export default class AppleMusic {
|
|||
const id = isURI ? match[5] : parsedURL.query.i || path.basename(parsedURL.pathname);
|
||||
const type = isURI ? match[4] : collection_type == 'album' && parsedURL.query.i ? 'track' : collection_type;
|
||||
const scope = collection_type == 'track' || (collection_type == 'album' && parsedURL.query.i) ? 'song' : collection_type;
|
||||
storefront = match[1] || storefront || (#store in this ? this.#store.defaultStorefront : 'us');
|
||||
storefront = match[1] || storefront || (#store in this ? this.#store.defaultStorefront : null) || 'us';
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
|
|
|
|||
Loading…
Reference in New Issue