Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
Something went wrong. Try again.
3.7 kB · 88 lines
TypeScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889import type {Express, Request} from 'express';import { childLogger, type Logger } from "@foxxmd/logging";import bodyParser from "body-parser";import cors from 'cors';import path from "path";import { WebhookNotifier } from "../sources/ingressNotifiers/WebhookNotifier.ts";import type ScrobbleSources from "../sources/ScrobbleSources.ts";import { WebScrobblerSource } from "../sources/WebScrobblerSource.ts";import { nonEmptyBody } from "./middleware.ts";import type { createTypedRouter, TypedMiddleware } from "@minisylar/express-typed-router";import { webScrobblePayloadSchema, type WebScrobblerPayload } from '../common/vendor/webscrobbler/interfaces.ts';
const corsOpts: cors.CorsOptions = { methods: ['POST']}
export const setupWebscrobblerRoutes = (app: Express, router: ReturnType<typeof createTypedRouter>, parentLogger: Logger, scrobbleSources: ScrobbleSources) => {
const logger = childLogger(parentLogger, ['Ingress', 'WebScrobbler']);
const webScrobblerJsonParser = bodyParser.json({ type: ['text/*', 'application/json'], // verify: function(req, res, buf, encoding) { // // get rawBody // // @ts-ignore // req.rawBody = buf.toString(); // } }); const webhookIngress = new WebhookNotifier(logger); const rawIngress: TypedMiddleware = (req, res, next) => { webhookIngress.trackIngress(req, true); next(); }; app.options('/api/webscrobbler*path', async (req, res, next) => { webhookIngress.trackIngress(req, true); next(); }, cors(corsOpts));
router.post('/api/webscrobbler{*splat}', { middleware: [rawIngress,cors(corsOpts) as TypedMiddleware,webScrobblerJsonParser,nonEmptyBody(logger, 'WebScrobbler Extension')], bodySchema: webScrobblePayloadSchema, tags: ['WebScrobbler Ingress'], pathExample: '/api/webscrobbler', summary: 'Accepts a Webscrobbler native scrobble', description: 'Use the default path `/api/webscrobbler` for standard configs or append a suffix if using a slug EX `/api/webscrobbler/mySlug`' }, async (req, res) => { webhookIngress.trackIngress(req as Request, false);
res.sendStatus(200);
const parts = path.parse(req.path); const slug = parts.name === 'webscrobbler' ? undefined : parts.name;
// let cleanPath = req.path; // if (cleanPath.charAt(cleanPath.length - 1) === '/') { // cleanPath = cleanPath.slice(0, -1); // } // const splitPath = cleanPath.split('/'); // const slug = splitPath[splitPath.length - 1];
const playerState = WebScrobblerSource.playStateFromRequest(req.body as unknown as WebScrobblerPayload);
const sources = scrobbleSources.getByType('webscrobbler') as WebScrobblerSource[]; if (sources.length === 0) { logger.warn('Received WebScrobbler payload but no WebScrobbler sources are configured'); }
let slugMatched = false; for (const source of sources) { if (source.matchSlug(slug)) { await source.handle(playerState); slugMatched = true; } }
if (!slugMatched) { if (slug === undefined) { logger.warn(`Request URL did not have a slug and no WebScrobbler source was configured without a slug.`); } else { logger.warn(`No WebScrobbler souce had the given slug '${slug}'`); } } });}