const express = require('express'); const { APP_PORTS } = require('../../constants'); /** * Plex routes factory * @param {Object} deps - Explicit dependencies * @param {Function} deps.fetchT - Timeout-wrapped fetch * @param {Function} deps.asyncHandler - Async route handler wrapper * @param {Function} deps.errorResponse - Error response helper * @param {Object} deps.log - Logger instance * @param {Object} deps.helpers - Arr helpers module * @returns {express.Router} */ module.exports = function(ctx) { const { fetchT, asyncHandler, errorResponse, log, helpers } = ctx; const router = express.Router(); // Plex Libraries endpoint router.get('/plex/libraries', asyncHandler(async (req, res) => { // Get Plex token let plexToken = await helpers.getPlexToken('plex'); if (!plexToken) { plexToken = await ctx.credentialManager.retrieve('arr.plex.token'); } if (!plexToken) { return errorResponse(res, 400, 'No Plex token available. Claim your Plex server first.', { hint: 'Deploy Plex with a claim token or manually configure it.' }); } // Get Plex URL let plexUrl = `http://localhost:${APP_PORTS.plex}`; try { const services = await ctx.servicesStateManager.read(); const svcList = Array.isArray(services) ? services : services.services || []; const plexService = svcList.find(s => s.id === 'plex' || s.appTemplate === 'plex'); if (plexService?.url) { plexUrl = plexService.url.replace('host.docker.internal', 'localhost'); } } catch (e) { /* use default */ } // Fetch libraries const libRes = await fetchT(`${plexUrl}/library/sections`, { headers: { 'X-Plex-Token': plexToken, 'Accept': 'application/json' }, signal: AbortSignal.timeout(10000) }); if (!libRes.ok) { return errorResponse(res, 502, `Plex returned ${libRes.status}`); } const data = await libRes.json(); const libraries = (data.MediaContainer?.Directory || []).map(dir => ({ key: dir.key, title: dir.title, type: dir.type, count: parseInt(dir.count) || 0, scannedAt: dir.scannedAt })); // Get server name let serverName = 'Plex'; let version = null; try { const identityRes = await fetchT(`${plexUrl}/identity`, { headers: { 'X-Plex-Token': plexToken, 'Accept': 'application/json' }, signal: AbortSignal.timeout(5000) }); if (identityRes.ok) { const identity = await identityRes.json(); serverName = identity.MediaContainer?.friendlyName || 'Plex'; version = identity.MediaContainer?.version; } } catch (e) { /* use default */ } // Store token for future use await ctx.credentialManager.store('arr.plex.token', plexToken, { service: 'plex', source: 'local', url: plexUrl, lastVerified: new Date().toISOString() }); res.json({ success: true, serverName, version, libraries }); }, 'plex-libraries')); return router; };