feat: Playtube Stream Dock Plugin 1.0.0 (Ajazz AKP153E) mit Icon-Pack

This commit is contained in:
2026-09-26 21:00:36 +02:00
commit 179a785adb
221 changed files with 10254 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { EventEmitter } = require('node:events');
const { buildInfoSvg, formatTime, svgToDataUri } = require('../src/infoPanel');
const { Controller } = require('../src/controller');
const { PREFIX } = require('../src/actions');
const VIEW = { title: 'Tom & <Live>', subtitle: 'Interpret', playing: true, currentTime: 65, duration: 200, volume: 42.4 };
test('formatTime formatiert Sekunden als m:ss', () => {
assert.equal(formatTime(65), '1:05');
assert.equal(formatTime(0), '0:00');
assert.equal(formatTime(NaN), '0:00');
assert.equal(formatTime(-3), '0:00');
});
test('SVG enthaelt Titel (XML-escaped) und Interpret, quadratisch', () => {
const svg = buildInfoSvg(VIEW);
assert.match(svg, /^<svg /);
assert.ok(svg.includes('Tom &amp;'));
assert.ok(svg.includes('&lt;Live&gt;'));
assert.ok(svg.includes('Interpret'));
assert.ok(!svg.includes('<Live>'));
assert.ok(svg.includes('width="144" height="144"'));
});
test('Cover wird eingebettet, sonst Platzhalter', () => {
assert.ok(buildInfoSvg({ ...VIEW, coverDataUri: 'data:image/jpeg;base64,AAAA' }).includes('href="data:image/jpeg;base64,AAAA"'));
assert.ok(!buildInfoSvg(VIEW).includes('<image'));
});
test('Fortschrittsbalken ist auf 0..100% begrenzt', () => {
assert.ok(buildInfoSvg({ ...VIEW, currentTime: 999 }).includes('width="124.0"'));
assert.ok(buildInfoSvg({ ...VIEW, duration: 0 }).includes('width="0.0"'));
});
test('offline und "nichts spielt" haben eigene Darstellungen', () => {
assert.ok(buildInfoSvg(null).includes('offline'));
assert.ok(buildInfoSvg({ ...VIEW, title: '' }).includes('spielt'));
});
test('svgToDataUri erzeugt eine dekodierbare Data-URI', () => {
const uri = svgToDataUri('<svg/>');
assert.equal(Buffer.from(uri.split(',')[1], 'base64').toString(), '<svg/>');
});
test('Controller: nowplaying auf dem Info-Display sendet ein SVG-Bild', () => {
const calls = [];
const host = new EventEmitter();
host.setTitle = (...a) => calls.push(['setTitle', ...a]);
host.setImage = (...a) => calls.push(['setImage', ...a]);
host.setState = () => {};
const client = new EventEmitter();
client.connected = true;
client.status = async () => ({ activeTab: 'music', tabs: { music: { ...VIEW } } });
const controller = new Controller({
host, client, logger: { info() {}, warn() {}, error() {} }, fetchImage: async () => 'data:image/jpeg;base64,AA',
});
controller.handleEvent({ event: 'willAppear', action: `${PREFIX}nowplaying`, context: 'INFO',
payload: { controller: 'Information', settings: {} } });
controller.state = { activeTab: 'music', tabs: { music: { ...VIEW } } };
controller.renderAll();
const image = calls.filter((c) => c[0] === 'setImage').at(-1);
assert.match(image[2], /^data:image\/svg\+xml;base64,/);
const before = calls.length;
controller.renderAll();
assert.equal(calls.length, before, 'unveraendert sendet nichts erneut');
});