From 982366560120161d1dd6b81d0f39ac3b7b5e27c7 Mon Sep 17 00:00:00 2001 From: simojenki Date: Thu, 8 Jul 2021 16:48:02 +1000 Subject: [PATCH] Ability to enable/disable reporting of now playing and scrobbling --- README.md | 2 ++ src/app.ts | 49 +++++++++++++++++++++++++++++++++++--------- src/config.ts | 38 +++++++++++++++++++--------------- tests/config.test.ts | 41 ++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 tests/config.test.ts diff --git a/README.md b/README.md index 5993303..d49782b 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ BONOB_SONOS_SERVICE_NAME | bonob | service name for sonos BONOB_SONOS_SERVICE_ID | 246 | service id for sonos BONOB_NAVIDROME_URL | http://$(hostname):4533 | URL for navidrome BONOB_NAVIDROME_CUSTOM_CLIENTS | undefined | Comma delimeted mime types for custom navidrome clients when streaming. ie. "audio/flac,audio/ogg" would use client = 'bonob+audio/flac' for flacs, and 'bonob+audio/ogg' for oggs. +BONOB_SCROBBLE_TRACKS | true | Whether to scrobble the playing of a track if it has been played for >30s +BONOB_REPORT_NOW_PLAYING | true | Whether to report a track as now playing ## Initialising service within sonos app diff --git a/src/app.ts b/src/app.ts index 011da3f..b08c55c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -4,12 +4,13 @@ import { appendMimeTypeToClientFor, DEFAULT, Navidrome } from "./navidrome"; import encryption from "./encryption"; import { InMemoryAccessTokens, sha256 } from "./access_tokens"; import { InMemoryLinkCodes } from "./link_codes"; -import config from "./config"; +import readConfig from "./config"; import sonos, { bonobService } from "./sonos"; +import { MusicService } from "./music_service"; -logger.info( - `Starting bonob with config ${JSON.stringify(config)}` -); +const config = readConfig(); + +logger.info(`Starting bonob with config ${JSON.stringify(config)}`); const bonob = bonobService( config.sonos.serviceName, @@ -20,17 +21,45 @@ const bonob = bonobService( const sonosSystem = sonos(config.sonos.deviceDiscovery, config.sonos.seedHost); -const streamUserAgent = config.navidrome.customClientsFor ? appendMimeTypeToClientFor(config.navidrome.customClientsFor.split(",")) : DEFAULT; +const streamUserAgent = config.navidrome.customClientsFor + ? appendMimeTypeToClientFor(config.navidrome.customClientsFor.split(",")) + : DEFAULT; + +const navidrome = new Navidrome( + config.navidrome.url, + encryption(config.secret), + streamUserAgent +); + +const featureFlagAwareMusicService: MusicService = { + generateToken: navidrome.generateToken, + login: (authToken: string) => + navidrome.login(authToken).then((library) => { + return { + ...library, + scrobble: (id: string) => { + if (config.scrobbleTracks) return library.scrobble(id); + else { + logger.info("Track Scrobbling not enabled") + return Promise.resolve(false); + } + }, + nowPlaying: (id: string) => { + if (config.reportNowPlaying) return library.nowPlaying(id); + else { + logger.info("Reporting track now playing not enabled"); + return Promise.resolve(false); + } + }, + }; + }), +}; const app = server( sonosSystem, bonob, config.webAddress, - new Navidrome( - config.navidrome.url, - encryption(config.secret), - streamUserAgent - ), + featureFlagAwareMusicService, new InMemoryLinkCodes(), new InMemoryAccessTokens(sha256(config.secret)) ); diff --git a/src/config.ts b/src/config.ts index bbe57ea..d8c3ae0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,30 +1,36 @@ import { hostname } from "os"; import logger from "./logger"; -const port = +(process.env["BONOB_PORT"] || 4534); -const webAddress = +export default function () { + const port = +(process.env["BONOB_PORT"] || 4534); + const webAddress = process.env["BONOB_WEB_ADDRESS"] || `http://${hostname()}:${port}`; -if (webAddress.match("localhost")) { - logger.error("BONOB_WEB_ADDRESS containing localhost is almost certainly incorrect, sonos devices will not be able to communicate with bonob using localhost, please specify either public IP or DNS entry"); + if (webAddress.match("localhost")) { + logger.error( + "BONOB_WEB_ADDRESS containing localhost is almost certainly incorrect, sonos devices will not be able to communicate with bonob using localhost, please specify either public IP or DNS entry" + ); process.exit(1); -} + } -export default { + return { port, webAddress, secret: process.env["BONOB_SECRET"] || "bonob", sonos: { - serviceName: process.env["BONOB_SONOS_SERVICE_NAME"] || "bonob", - deviceDiscovery: (process.env["BONOB_SONOS_DEVICE_DISCOVERY"] || "true") == "true", - seedHost: process.env["BONOB_SONOS_SEED_HOST"], - autoRegister: process.env["BONOB_SONOS_AUTO_REGISTER"] == "true", - sid: Number(process.env["BONOS_SONOS_SERVICE_ID"] || "246") + serviceName: process.env["BONOB_SONOS_SERVICE_NAME"] || "bonob", + deviceDiscovery: + (process.env["BONOB_SONOS_DEVICE_DISCOVERY"] || "true") == "true", + seedHost: process.env["BONOB_SONOS_SEED_HOST"], + autoRegister: process.env["BONOB_SONOS_AUTO_REGISTER"] == "true", + sid: Number(process.env["BONOS_SONOS_SERVICE_ID"] || "246"), }, navidrome: { - url: process.env["BONOB_NAVIDROME_URL"] || `http://${hostname()}:4533`, - customClientsFor: process.env["BONOB_NAVIDROME_CUSTOM_CLIENTS"] || undefined, - } + url: process.env["BONOB_NAVIDROME_URL"] || `http://${hostname()}:4533`, + customClientsFor: + process.env["BONOB_NAVIDROME_CUSTOM_CLIENTS"] || undefined, + }, + scrobbleTracks: (process.env["BONOB_SCROBBLE_TRACKS"] || "true") == "true", + reportNowPlaying: (process.env["BONOB_REPORT_NOW_PLAYING"] || "true") == "true", + }; } - - diff --git a/tests/config.test.ts b/tests/config.test.ts new file mode 100644 index 0000000..b807893 --- /dev/null +++ b/tests/config.test.ts @@ -0,0 +1,41 @@ +import config from "../src/config"; + +describe("config", () => { + const OLD_ENV = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...OLD_ENV }; + }); + + afterEach(() => { + process.env = OLD_ENV; + }); + + function describeBooleanConfigValue(name : string, envVar: string) { + describe(name, () => { + function expecting({ + value, + expected, + }: { + value: string; + expected: boolean; + }) { + describe(`when value is ${value}`, () => { + it(`should be ${expected}`, () => { + process.env[envVar] = value; + expect((config() as any)[name]).toEqual(expected); + }); + }); + } + + expecting({ value: "", expected: true }); + expecting({ value: "true", expected: true }); + expecting({ value: "false", expected: false }); + expecting({ value: "foo", expected: false }); + }); + }; + + describeBooleanConfigValue("scrobbleTracks", "BONOB_SCROBBLE_TRACKS"); + describeBooleanConfigValue("reportNowPlaying", "BONOB_REPORT_NOW_PLAYING"); +});