Files

152 lines
7.6 KiB
JavaScript

// Erzeugt manifest.json, Uebersetzungen (en/de) und alle PNG-Bilder des Plugins.
// Aufruf: npm run assets
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { Canvas } from './raster.mjs';
import { drawGlyph } from './glyphs.mjs';
import { DIM_BG, DIM_GLYPH, WHITE, brandGradient } from './brand.mjs';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const PLUGIN_DIR = path.join(ROOT, 'com.fojadrachi.playtube.sdPlugin');
const PREFIX = 'com.fojadrachi.playtube.';
const VERSION = JSON.parse(readFileSync(path.join(ROOT, 'package.json'), 'utf8')).version;
const KEY_SIZE = 144;
const ICON_SIZE = 80;
const PLUGIN_ICON_SIZE = 256;
/**
* states: Reihenfolge = State-Index im Plugin (siehe actions.js).
* on = Symbol weiss auf Akzentflaeche (aktiv), dim = gedaempfte Farbe (aus).
* titled: Taste zeigt Text (Lautstaerke, Name ...) -> Symbol rutscht nach oben.
*/
const ACTIONS = [
{ id: 'playpause', accent: 'playback', titled: true, states: [{ glyph: 'play' }, { glyph: 'pause' }],
en: ['Play/Pause', 'Toggle playback'], de: ['Wiedergabe/Pause', 'Wiedergabe starten oder pausieren'] },
{ id: 'previous', accent: 'playback', states: [{ glyph: 'previous' }],
en: ['Previous track', 'Go to the previous track'], de: ['Vorheriger Titel', 'Zum vorherigen Titel springen'] },
{ id: 'next', accent: 'playback', states: [{ glyph: 'next' }],
en: ['Next track', 'Skip to the next track'], de: ['Nächster Titel', 'Zum nächsten Titel springen'] },
{ id: 'seekback', accent: 'playback', titled: true, states: [{ glyph: 'rewind' }],
en: ['Rewind', 'Jump back a few seconds'], de: ['Zurückspulen', 'Ein paar Sekunden zurückspringen'] },
{ id: 'seekforward', accent: 'playback', titled: true, states: [{ glyph: 'forward' }],
en: ['Fast forward', 'Jump forward a few seconds'], de: ['Vorspulen', 'Ein paar Sekunden vorspringen'] },
{ id: 'volumeup', accent: 'volume', titled: true, states: [{ glyph: 'volup' }],
en: ['Volume up', 'Louder (hold to keep going)'], de: ['Lauter', 'Lauter (gedrückt halten für Dauer-Anpassung)'] },
{ id: 'volumedown', accent: 'volume', titled: true, states: [{ glyph: 'voldown' }],
en: ['Volume down', 'Quieter (hold to keep going)'], de: ['Leiser', 'Leiser (gedrückt halten für Dauer-Anpassung)'] },
{ id: 'volumeset', accent: 'volume', titled: true, states: [{ glyph: 'vol' }],
en: ['Set volume', 'Jump to a fixed volume'], de: ['Lautstärke setzen', 'Auf eine feste Lautstärke springen'] },
{ id: 'mute', accent: 'volume', titled: true, states: [{ glyph: 'vol' }, { glyph: 'mute', on: true }],
en: ['Mute', 'Mute or unmute'], de: ['Stummschalten', 'Ton aus- oder einschalten'] },
{ id: 'nowplaying', accent: 'playback', titled: true, states: [{ glyph: 'note' }], en: ['Now playing', 'Cover and title, press to play/pause'],
de: ['Aktueller Titel', 'Cover und Titel, Drücken = Wiedergabe/Pause'] },
{ id: 'like', accent: 'like', states: [{ glyph: 'heart' }, { glyph: 'heartfill', on: true }],
en: ['Like', 'Like the current track'], de: ['Gefällt mir', 'Aktuellen Titel mit „Gefällt mir“ markieren'] },
{ id: 'shuffle', accent: 'misc', states: [{ glyph: 'shuffle' }],
en: ['Shuffle', 'Toggle shuffle'], de: ['Zufallswiedergabe', 'Zufallswiedergabe umschalten'] },
{ id: 'repeat', accent: 'misc',
states: [{ glyph: 'repeat', dim: true }, { glyph: 'repeat', on: true }, { glyph: 'repeatone', on: true }],
en: ['Repeat', 'Cycle repeat mode'], de: ['Wiederholen', 'Wiederholungsmodus wechseln'] },
{ id: 'playlist', accent: 'playlist', titled: true, states: [{ glyph: 'playlist' }],
en: ['Play playlist', 'Start a playlist from YouTube Music'],
de: ['Playlist abspielen', 'Eine Playlist aus YouTube Music starten'] },
{ id: 'switchtab', accent: 'misc', titled: true, states: [{ glyph: 'tabs' }],
en: ['Switch tab', 'Switch between YouTube and YouTube Music'],
de: ['Tab wechseln', 'Zwischen YouTube und YouTube Musik wechseln'] },
{ id: 'show', accent: 'misc', states: [{ glyph: 'window' }],
en: ['Show Playtube', 'Bring the Playtube window to the front'],
de: ['Playtube anzeigen', 'Das Playtube-Fenster in den Vordergrund holen'] },
];
function keyImage(action, state) {
const canvas = new Canvas(KEY_SIZE);
const background = state.dim ? DIM_BG : brandGradient(KEY_SIZE);
canvas.roundRect(0, 0, KEY_SIZE, KEY_SIZE, 0, background);
// Symbol immer exakt mittig; bei Tasten mit Text etwas kleiner, damit der Text darunter Platz hat.
const r = action.titled ? 34 : 44;
drawGlyph(canvas, state.glyph, KEY_SIZE / 2, KEY_SIZE / 2, r, state.dim ? DIM_GLYPH : WHITE, background);
return canvas.png();
}
function iconImage(action) {
const canvas = new Canvas(ICON_SIZE);
const background = brandGradient(ICON_SIZE);
canvas.roundRect(0, 0, ICON_SIZE, ICON_SIZE, 18, background);
drawGlyph(canvas, action.states[0].glyph, ICON_SIZE / 2, ICON_SIZE / 2, 26, WHITE, background);
return canvas.png();
}
function pluginIcon() {
const canvas = new Canvas(PLUGIN_ICON_SIZE);
const background = brandGradient(PLUGIN_ICON_SIZE);
canvas.roundRect(0, 0, PLUGIN_ICON_SIZE, PLUGIN_ICON_SIZE, 56, background);
drawGlyph(canvas, 'play', PLUGIN_ICON_SIZE / 2, PLUGIN_ICON_SIZE / 2, 84, WHITE, background);
return canvas.png();
}
function write(relative, content) {
const target = path.join(PLUGIN_DIR, relative);
mkdirSync(path.dirname(target), { recursive: true });
writeFileSync(target, content);
}
function manifest() {
return {
Actions: ACTIONS.map((action) => ({
Icon: `static/icon/${action.id}@2x.png`,
Name: action.en[0],
DisableAutomaticStates: true,
States: action.states.map((_, index) => ({
Image: `static/btn/${action.id}_${index}.png`,
TitleAlignment: 'bottom',
FontSize: '11',
FontStyle: 'Bold',
TitleColor: '#ffffff',
})),
Settings: {},
Controllers: ['Keypad', 'Information'],
UserTitleEnabled: false,
SupportedInMultiActions: true,
Tooltip: action.en[1],
UUID: `${PREFIX}${action.id}`,
PropertyInspectorPath: 'propertyInspector/index.html',
})),
SDKVersion: 1,
Author: 'Playtube',
Name: 'Playtube',
Icon: 'static/icon/[email protected]',
CodePathWin: 'plugin/index.js',
Description: 'Control Playtube (YouTube / YouTube Music) from your Stream Dock',
Category: 'Playtube',
CategoryIcon: 'static/icon/[email protected]',
Version: VERSION,
OS: [{ Platform: 'windows', MinimumVersion: '10' }],
Software: { MinimumVersion: '3.10.188.226' },
Nodejs: { Version: '20' },
};
}
function locale(language, description) {
const entries = Object.fromEntries(ACTIONS.map((action) => {
const [name, tooltip] = action[language];
return [`${PREFIX}${action.id}`, { Name: name, Tooltip: tooltip }];
}));
return { Name: 'Playtube', Description: description, Category: 'Playtube', ...entries };
}
function main() {
write('manifest.json', `${JSON.stringify(manifest(), null, 2)}\n`);
write('en.json', `${JSON.stringify(locale('en', 'Control Playtube (YouTube / YouTube Music)'), null, 2)}\n`);
write('de.json', `${JSON.stringify(locale('de', 'Playtube (YouTube / YouTube Musik) steuern'), null, 2)}\n`);
write('static/icon/[email protected]', pluginIcon());
for (const action of ACTIONS) {
write(`static/icon/${action.id}@2x.png`, iconImage(action));
action.states.forEach((state, index) => write(`static/btn/${action.id}_${index}.png`, keyImage(action, state)));
}
process.stdout.write(`Assets fuer ${ACTIONS.length} Aktionen nach ${PLUGIN_DIR} geschrieben\n`);
}
main();