Skip to content

Commit

Permalink
Implement authorization bearer token user prompt
Browse files Browse the repository at this point in the history
If there is no saved token, we show the Spotify web site and intercept the user's login to grab the token and store it. Then we continue as usual. If the token is stored, we grab it from the disk cache and get right to business.

This doesn't solve for token expiration, which I will implement later most probably by extracting the token prompt logic to its own file and re-displaying the Spotify web page window so the user can login again and refresh the token.
  • Loading branch information
TomasHubelbauer committed Jun 3, 2024
1 parent e31bdbf commit 2355103
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
bun.lockb
lyrics/*
!lyrics/.gitkeep
token.json
64 changes: 54 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,46 @@ import fs from 'fs';
import askSpotify from './askSpotify.js';

electron.app.on('ready', async () => {
/** @type {string} */
let authorization;

const path = `token.json`;
try {
await fs.promises.access(path);

console.log('Loading bearer token…');
authorization = JSON.parse(await fs.promises.readFile(path));
console.log('Loaded bearer token');
}
catch (error) {
if (error.code !== 'ENOENT') {
console.log(`Failed to load bearer token: ${error}`);
}

console.log('Obtaining bearer token…');
const window = new electron.BrowserWindow({ width: 800, height: 600 });
window.loadURL('https://open.spotify.com/');

authorization = await new Promise((resolve) => {
electron.session.defaultSession.webRequest.onSendHeaders(
{ urls: ['https://gew4-spclient.spotify.com/*'] },
(details) => {
if (authorization) {
return;
}

if (details.requestHeaders['authorization']) {
resolve(details.requestHeaders['authorization']);
}
}
);
});

window.close();
await fs.promises.writeFile(path, JSON.stringify(authorization, null, 2));
console.log('Obtained bearer token');
}

// Hide the Dock icon for the application
electron.app.dock.hide();

Expand Down Expand Up @@ -31,7 +71,7 @@ electron.app.on('ready', async () => {

/** @typedef {{ timeTag: string; words: string; }} Line */

/** @type {{ artist: string; song: string; error: boolean; message?: string; syncType: 'LINE_SYNCED' | 'UNSYNCED'; lines: Line[]; } | undefined} */
/** @type {({ artist: string; song: string; } & ({ error: string; } | { syncType: 'LINE_SYNCED' | 'UNSYNCED'; lines: Line[]; })) | undefined} */
let lyrics;

/** @type {Line | undefined} */
Expand Down Expand Up @@ -70,8 +110,7 @@ electron.app.on('ready', async () => {
}
case 'LINE_SYNCED': {
{
const stamp = `${(~~(position / 60)).toString().padStart('00'.length, '0')}:${(position % 60).toFixed(2).toString().padStart('00.00'.length, '0')}`;
const index = lyrics.lines.findIndex(line => line.timeTag >= stamp);
const index = lyrics.lines.findIndex(line => line.startTimeMs >= position * 1000);
const _line = lyrics.lines[index - 1];
if (_line !== line) {
line = _line;
Expand Down Expand Up @@ -152,18 +191,23 @@ electron.app.on('ready', async () => {
console.log(`Downloading lyrics for ${artist} - ${song} (${id})…`);

// Download LRC (timestamped) lyrics from the unofficial Spotify Lyrics API
// See https://github.com/akashrchandran/spotify-lyrics-api
const apiUrl = `https://spotify-lyric-api.herokuapp.com/?trackid=${id}&format=lrc`;
const apiJson = await fetch(apiUrl, { headers: { 'User-Agent': 'Firefox' } }).then(response => response.json());
// Inspect the `open.spotify.com` developer tools `lyrics` network call to maintain this
const response = await fetch(`https://spclient.wg.spotify.com/color-lyrics/v2/track/${id}?format=json`, { headers: { authorization, 'app-platform': 'WebPlayer' } });
if (response.ok) {
const data = await response.json();

lyrics = { artist, song, ...apiJson };
await fs.promises.writeFile(path, JSON.stringify(lyrics, null, 2));
lyrics = { artist, song, ...data.lyrics };
await fs.promises.writeFile(path, JSON.stringify(lyrics, null, 2));

console.log(`Downloaded lyrics for ${artist} - ${song}`);
console.log(`Downloaded lyrics for ${artist} - ${song} (${id})`);
}
else {
lyrics = { artist, song, error: response.statusText };
}
}

if (lyrics.error) {
console.log(`Lyrics error for ${artist} - ${song}: ${lyrics.message ?? 'unknown error'}`);
console.log(`Lyrics error for ${artist} - ${song} (${id}): ${lyrics.error}`);
}
}

Expand Down

0 comments on commit 2355103

Please sign in to comment.