mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
More scrobble fixes
This commit is contained in:
@@ -43,6 +43,25 @@ import { SmapiTokenStore, InMemorySmapiTokenStore } from "./smapi_token_store";
|
|||||||
|
|
||||||
export const BONOB_ACCESS_TOKEN_HEADER = "bat";
|
export const BONOB_ACCESS_TOKEN_HEADER = "bat";
|
||||||
|
|
||||||
|
// Session storage for tracking active streams (for scrobbling)
|
||||||
|
type StreamSession = {
|
||||||
|
serviceToken: string;
|
||||||
|
trackId: string;
|
||||||
|
timestamp: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const streamSessions = new Map<string, StreamSession>();
|
||||||
|
|
||||||
|
// Clean up old sessions (older than 1 hour)
|
||||||
|
setInterval(() => {
|
||||||
|
const oneHourAgo = Date.now() - (60 * 60 * 1000);
|
||||||
|
for (const [sid, session] of streamSessions.entries()) {
|
||||||
|
if (session.timestamp < oneHourAgo) {
|
||||||
|
streamSessions.delete(sid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 5 * 60 * 1000); // Run every 5 minutes
|
||||||
|
|
||||||
interface RangeFilter extends Transform {
|
interface RangeFilter extends Transform {
|
||||||
range: (length: number) => string;
|
range: (length: number) => string;
|
||||||
}
|
}
|
||||||
@@ -401,6 +420,14 @@ function server(
|
|||||||
if (!serviceToken) {
|
if (!serviceToken) {
|
||||||
return res.status(401).send();
|
return res.status(401).send();
|
||||||
} else {
|
} else {
|
||||||
|
// Store session for scrobbling later (when Sonos reports playback)
|
||||||
|
streamSessions.set(id, {
|
||||||
|
serviceToken,
|
||||||
|
trackId: id,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
});
|
||||||
|
logger.debug(`Stored stream session for track ${id}`);
|
||||||
|
|
||||||
return musicService
|
return musicService
|
||||||
.login(serviceToken)
|
.login(serviceToken)
|
||||||
.then((it) =>
|
.then((it) =>
|
||||||
@@ -655,19 +682,22 @@ function server(
|
|||||||
|
|
||||||
// For "final" reports, determine if we should scrobble
|
// For "final" reports, determine if we should scrobble
|
||||||
if (type === "final" && durationPlayedSeconds > 0) {
|
if (type === "final" && durationPlayedSeconds > 0) {
|
||||||
// Extract authentication from request headers or body
|
// Retrieve authentication from stream session storage
|
||||||
// Sonos may send credentials in Authorization header or in the request
|
const session = streamSessions.get(trackId!);
|
||||||
const authHeader = req.headers["authorization"];
|
let serviceToken: string | undefined = session?.serviceToken;
|
||||||
let serviceToken: string | undefined;
|
|
||||||
|
|
||||||
if (authHeader && authHeader.startsWith("Bearer ")) {
|
if (!serviceToken) {
|
||||||
const token = authHeader.substring(7);
|
// Fallback: try to extract from Authorization header (if present)
|
||||||
const smapiToken = serverOpts.smapiTokenStore.get(token);
|
const authHeader = req.headers["authorization"];
|
||||||
if (smapiToken) {
|
if (authHeader && authHeader.startsWith("Bearer ")) {
|
||||||
serviceToken = pipe(
|
const token = authHeader.substring(7);
|
||||||
smapiAuthTokens.verify({ token, key: smapiToken.key }),
|
const smapiToken = serverOpts.smapiTokenStore.get(token);
|
||||||
E.getOrElseW(() => undefined)
|
if (smapiToken) {
|
||||||
);
|
serviceToken = pipe(
|
||||||
|
smapiAuthTokens.verify({ token, key: smapiToken.key }),
|
||||||
|
E.getOrElseW(() => undefined)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user