Kyouka commited on
Commit
4b3aeaa
·
verified ·
1 Parent(s): 4e4eae8

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +85 -23
index.js CHANGED
@@ -250,62 +250,124 @@ app.get('/api/recently-played/json', async (req, res) => {
250
  });
251
 
252
  // 4. Current Playback State endpoint
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  app.get('/api/current-playback', async (req, res) => {
254
- const accessToken = req.cookies.spotify_access;
255
- const userData = req.cookies.spotify_user ? JSON.parse(req.cookies.spotify_user) : null;
 
 
 
 
 
 
 
256
 
257
- if (!accessToken || !userData) {
258
- return res.status(401).json({ error: 'Not authenticated' });
259
- }
 
 
 
 
 
 
 
 
 
 
260
 
261
- try {
262
  const params = {};
263
- if (userData.country) {
264
- params.market = userData.country;
265
  }
266
 
 
267
  const response = await axios.get('https://api.spotify.com/v1/me/player', {
268
  params,
269
- headers: { 'Authorization': `Bearer ${accessToken}` },
 
 
 
270
  });
271
 
 
272
  if (response.status === 204) {
273
- return res.json({ status: 'success', data: null, message: 'No active playback' });
 
 
 
 
274
  }
275
 
 
276
  const playbackData = {
277
- device: response.data.device,
278
- item: {
 
 
 
 
 
 
279
  id: response.data.item.id,
280
  name: response.data.item.name,
281
- artists: response.data.item.artists.map(a => ({ id: a.id, name: a.name })),
 
 
 
 
 
 
282
  album: {
283
  id: response.data.item.album.id,
284
  name: response.data.item.album.name,
285
- images: response.data.item.album.images
286
- },
287
- duration_ms: response.data.item.duration_ms,
288
- progress_ms: response.data.progress_ms
289
  },
290
- is_playing: response.data.is_playing,
 
 
 
 
291
  shuffle_state: response.data.shuffle_state,
292
- repeat_state: response.data.repeat_state
 
293
  };
294
 
295
  res.json({
296
  status: 'success',
297
  user: {
298
- id: userData.id,
299
- display_name: userData.display_name
 
300
  },
301
  data: playbackData
302
  });
 
303
  } catch (error) {
 
 
304
  if (error.response?.status === 401) {
305
- return res.status(401).json({ error: 'Session expired' });
306
  }
 
307
  res.status(500).json({
308
- error: 'Failed to fetch playback state',
309
  details: error.response?.data || error.message
310
  });
311
  }
 
250
  });
251
 
252
  // 4. Current Playback State endpoint
253
+ async function refreshAccessToken(refreshToken) {
254
+ const response = await axios.post('https://accounts.spotify.com/api/token',
255
+ querystring.stringify({
256
+ grant_type: 'refresh_token',
257
+ refresh_token: refreshToken,
258
+ }), {
259
+ headers: {
260
+ 'Content-Type': 'application/x-www-form-urlencoded',
261
+ 'Authorization': 'Basic ' + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'),
262
+ },
263
+ });
264
+ return response.data;
265
+ }
266
+
267
  app.get('/api/current-playback', async (req, res) => {
268
+ try {
269
+ const userId = req.query.user;
270
+
271
+ if (!userId || !userSessions[userId]) {
272
+ return res.status(401).json({
273
+ error: 'Not authenticated',
274
+ solution: 'Please login at /login first'
275
+ });
276
+ }
277
 
278
+ const session = userSessions[userId];
279
+
280
+ // Refresh token if needed
281
+ if (Date.now() >= session.expiresAt) {
282
+ try {
283
+ const newTokens = await refreshAccessToken(session.refreshToken);
284
+ session.accessToken = newTokens.access_token;
285
+ session.expiresAt = Date.now() + newTokens.expires_in * 1000;
286
+ } catch (error) {
287
+ delete userSessions[userId];
288
+ return res.status(401).json({ error: 'Session expired' });
289
+ }
290
+ }
291
 
292
+ // Prepare request parameters
293
  const params = {};
294
+ if (session.country) {
295
+ params.market = session.country;
296
  }
297
 
298
+ // Get current playback state
299
  const response = await axios.get('https://api.spotify.com/v1/me/player', {
300
  params,
301
+ headers: {
302
+ 'Authorization': `Bearer ${session.accessToken}`,
303
+ 'Content-Type': 'application/json'
304
+ },
305
  });
306
 
307
+ // Handle no content response
308
  if (response.status === 204) {
309
+ return res.json({
310
+ status: 'success',
311
+ data: null,
312
+ message: 'No active playback detected'
313
+ });
314
  }
315
 
316
+ // Format playback data
317
  const playbackData = {
318
+ device: {
319
+ id: response.data.device.id,
320
+ name: response.data.device.name,
321
+ type: response.data.device.type,
322
+ is_active: response.data.device.is_active,
323
+ volume_percent: response.data.device.volume_percent
324
+ },
325
+ track: {
326
  id: response.data.item.id,
327
  name: response.data.item.name,
328
+ duration_ms: response.data.item.duration_ms,
329
+ progress_ms: response.data.progress_ms,
330
+ is_playing: response.data.is_playing,
331
+ artists: response.data.item.artists.map(artist => ({
332
+ id: artist.id,
333
+ name: artist.name
334
+ })),
335
  album: {
336
  id: response.data.item.album.id,
337
  name: response.data.item.album.name,
338
+ images: response.data.item.album.images,
339
+ release_date: response.data.item.album.release_date
340
+ }
 
341
  },
342
+ context: response.data.context ? {
343
+ type: response.data.context.type,
344
+ href: response.data.context.href,
345
+ uri: response.data.context.uri
346
+ } : null,
347
  shuffle_state: response.data.shuffle_state,
348
+ repeat_state: response.data.repeat_state,
349
+ timestamp: response.data.timestamp
350
  };
351
 
352
  res.json({
353
  status: 'success',
354
  user: {
355
+ id: userId,
356
+ display_name: session.display_name,
357
+ country: session.country
358
  },
359
  data: playbackData
360
  });
361
+
362
  } catch (error) {
363
+ console.error('Playback error:', error.response?.data || error.message);
364
+
365
  if (error.response?.status === 401) {
366
+ return res.status(401).json({ error: 'Invalid or expired token' });
367
  }
368
+
369
  res.status(500).json({
370
+ error: 'Failed to get playback state',
371
  details: error.response?.data || error.message
372
  });
373
  }