From 49f8a82fbd39bbced3bb478aefd7c20ff26403ca Mon Sep 17 00:00:00 2001 From: christian Date: Mon, 26 Oct 2026 12:58:39 -0400 Subject: [PATCH] ProPresenter Look 1.0.0: mock ProPresenter looks API --- .../propresenter-looks/tools/pp-looks-mock.js | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 modules/propresenter-looks/tools/pp-looks-mock.js diff --git a/modules/propresenter-looks/tools/pp-looks-mock.js b/modules/propresenter-looks/tools/pp-looks-mock.js new file mode 100644 index 0000000..a268f03 --- /dev/null +++ b/modules/propresenter-looks/tools/pp-looks-mock.js @@ -0,0 +1,69 @@ +#!/usr/bin/env node +'use strict'; + +/** + * Mock ProPresenter looks API, for developing the ProPresenter Look module + * without the real thing. + * + * node tools/pp-looks-mock.js [port] (default 1601) + * --every how often the live look changes (default 8) + * --password demand ?password= like a locked ProPresenter + * --no-list answer 404 for /v1/looks (an older ProPresenter) + * + * Point the module at host 127.0.0.1 and this port. Every few changes the + * live look is "modified by hand", so it matches no saved look. + */ + +const http = require('http'); +const crypto = require('crypto'); + +const args = process.argv.slice(2); +const flag = (name) => { + const i = args.indexOf(name); + return i >= 0 ? args[i + 1] : undefined; +}; +const port = Number(args.find((a) => /^\d+$/.test(a))) || 1601; +const every = Number(flag('--every')) || 8; +const password = flag('--password') || ''; +const noList = args.includes('--no-list'); + +const screen = (on) => ({ video_input: false, media: on, slide: on, announcements: on, props: on, messages: on, presentation: null, mask: null }); +const saved = ['Normal', 'Lyrics Only', 'Sermon', 'Countdown', 'Video Only'].map((name, index) => ({ + id: { uuid: crypto.randomUUID(), name, index }, + screens: [screen(true), screen(index !== 4)], +})); + +let live = 0; +let modified = false; +let changes = 0; +const liveUuid = crypto.randomUUID(); + +setInterval(() => { + changes += 1; + modified = changes % 4 === 0; + if (!modified) live = (live + 1) % saved.length; + console.log(`live look → ${saved[live].id.name}${modified ? ' (modified)' : ''}`); +}, every * 1000); + +function currentLook() { + return { + id: { uuid: liveUuid, name: saved[live].id.name, index: modified ? saved.length : live }, + screens: saved[live].screens, + }; +} + +http.createServer((req, res) => { + const url = new URL(req.url, 'http://x'); + const send = (code, body) => { + res.writeHead(code, { 'Content-Type': 'application/json' }); + res.end(body === undefined ? '' : JSON.stringify(body)); + }; + if (password && url.searchParams.get('password') !== password) return send(401, { error: 'password' }); + if (url.pathname === '/version') return send(200, { name: 'Mock', platform: 'mac', os_version: '15', host_description: 'ProPresenter 21.0 (mock)' }); + if (url.pathname === '/v1/looks') return noList ? send(404) : send(200, saved); + if (url.pathname === '/v1/look/current') return send(200, currentLook()); + send(404); +}).listen(port, () => { + console.log(`mock ProPresenter looks on http://127.0.0.1:${port} — live look changes every ${every}s`); + console.log(`live look → ${saved[live].id.name}`); +});