Export metadata edits (title, composer, source, encrypter) via patchScore

This commit is contained in:
c0dev0id 2026-06-24 17:52:25 +02:00
parent 720a0b6b25
commit 097d4488ef
3 changed files with 54 additions and 3 deletions

View File

@ -47,7 +47,7 @@ export const PaneCP = {
try {
const raw = await fetchScoreText(props.store.credentials);
props.store.rawScoreText = raw;
const patched = patchScore(raw, props.store.scoreModel.instruments, props.store.scoreModel.bars);
const patched = patchScore(raw, props.store.scoreModel.instruments, props.store.scoreModel.bars, props.store.scoreModel.info);
await putScoreText(patched, props.store.credentials);
props.store.synthesisStatus = { frozen: false, currently_rendered_notes: 0, notes_in_total: 0 };
} catch (e) {

View File

@ -141,6 +141,31 @@ export function exportInstrument(instr) {
// Replace dirty instrument blocks and dirty bar _meta blocks.
// Voice note content in bar documents is left verbatim.
const META_KEYS = ['title', 'composer', 'source', 'encrypter'];
function patchMetadata(text, info) {
if (!info) return text;
const lines = text.split('\n');
const replaced = new Set();
const out = lines.map(line => {
for (const key of META_KEYS) {
if (line.startsWith(key + ':') && info[key] != null && info[key] !== '') {
replaced.add(key);
return `${key}: ${info[key]}`;
}
}
return line;
});
const prepend = META_KEYS
.filter(k => !replaced.has(k) && info[k] != null && info[k] !== '')
.map(k => `${k}: ${info[k]}`);
if (prepend.length) out.unshift(...prepend);
return out.join('\n');
}
function patchInstrumentHeader(text, instruments) {
const lines = text.split('\n');
const result = [];
@ -217,11 +242,11 @@ function patchBarMeta(doc, bar) {
return out.join('\n');
}
export function patchScore(rawScoreText, instruments, bars = []) {
export function patchScore(rawScoreText, instruments, bars = [], info = null) {
const SEP = '\n---\n';
const [header, ...barDocs] = rawScoreText.split(SEP);
const patchedHeader = patchInstrumentHeader(header, instruments);
const patchedHeader = patchInstrumentHeader(patchMetadata(header, info), instruments);
if (!barDocs.length) return patchedHeader;

View File

@ -403,6 +403,32 @@ ok('clean bar unchanged', barPatched.includes('beats_per_minute: 100'));
ok('clean bar voice preserved', barPatched.includes('- D4 4'));
ok('document separators preserved', (barPatched.match(/\n---\n/g) ?? []).length === 2);
// ── patchScore metadata ────────────────────────────────────────────────────
section('patchScore metadata');
const META_SCORE = `title: Old Title
composer: Old Composer
source: Some Book
instrument alpha:
character:
A: "1:0,10;1,0"
`;
const updatedInfo = { title: 'New Title', composer: 'New Composer', source: '', encrypter: 'Me' };
const metaPatched = patchScore(META_SCORE, [], [], updatedInfo);
ok('existing title replaced', metaPatched.includes('title: New Title'));
ok('existing composer replaced', metaPatched.includes('composer: New Composer'));
ok('empty value leaves existing line', metaPatched.includes('source: Some Book'));
ok('new key prepended', metaPatched.includes('encrypter: Me'));
ok('instrument block untouched', metaPatched.includes('instrument alpha:'));
const META_SCORE_NO_TITLE = `composer: Bach\n\ninstrument ki:\n character:\n`;
const noTitlePatched = patchScore(META_SCORE_NO_TITLE, [], [], { title: 'Fugue', composer: 'Bach' });
ok('missing title prepended', noTitlePatched.includes('title: Fugue'));
ok('existing composer not duplicated', (noTitlePatched.match(/^composer:/mg) ?? []).length === 1);
const nullInfoPatched = patchScore(META_SCORE, [], [], null);
ok('null info leaves metadata unchanged', nullInfoPatched.includes('title: Old Title'));
// ── Summary ────────────────────────────────────────────────────────────────
console.log(`\n══ ${pass} passed, ${fail} failed ══\n`);
process.exit(fail > 0 ? 1 : 0);