import { logger } from 'web-shared/src/logger'; import { Hono } from 'hono'; import { PlexNowListening, RawPlexResponse } from 'web-shared/src/plex'; import { getParameter } from '../../lib/secrets'; const plexUrl = 'https://plex.pfy.ch'; export const plexRouter = new Hono(); plexRouter.get('/listening', async (c) => { try { const plexToken = await getParameter('/web-api/plex-api-token', true); const response = await fetch( `${plexUrl}/status/sessions?X-Plex-Token=${plexToken}`, { method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, }, ); const data = (await response.json()) as RawPlexResponse; const sessions: PlexNowListening[] = [ ...(data.MediaContainer.Metadata || []), ].map((session) => ({ user: session.User.title, device: session.Player.product, state: session.Player.state, progress: session.viewOffset, duration: session.Media[0].duration, albumName: session.parentTitle, albumArtist: session.originalTitle || session.grandparentTitle, albumYear: session.parentYear, trackName: session.title, thumbPath: `/plex/artwork${session.parentThumb}`, })); return c.json({ nowPlaying: sessions, }); } catch (err) { logger.error(err, 'Failed to fetch sessions from Plex'); return c.json({ error: 'Failed to fetch sessions from Plex' }, 500); } }); plexRouter.get('/artwork/:path{.+}', async (c) => { try { const { path } = c.req.param(); const plexToken = await getParameter('/web-api/plex-api-token', true); const response = await fetch( `${plexUrl}/${path}?X-Plex-Token=${plexToken}`, { method: 'GET', }, ); if (!response.ok) { return c.json({ error: 'Artwork not found' }, 404); } const data = await response.arrayBuffer(); const contentType = response.headers.get('Content-Type') || 'image/png'; c.status(200); c.header('Content-Type', contentType); c.header('Cache-Control', 'max-age=604800'); return c.body(data); } catch (err) { logger.error(err, 'Failed to proxy artwork request'); return c.json({ error: 'Failed to proxy artwork request' }, 500); } });