Spaces:
Running
Running
Commit ·
eb7dc71
1
Parent(s): 51e9bed
fixed player loading status.
Browse files- app.py +3 -2
- web/score-player.css +32 -0
- web/score-player.js +27 -2
app.py
CHANGED
|
@@ -436,9 +436,10 @@ def build_head ():
|
|
| 436 |
# left under Compose + Logs (robust against Gradio's flex-nesting quirks).
|
| 437 |
os.path.join(WEB_DIR, 'layout-fit.js'),
|
| 438 |
]
|
| 439 |
-
tags = ['<script>window.__LILYSCRIPT_SOUNDFONT_URL=%r;window.__LILYSCRIPT_FLUID_URL=%r;</script>'
|
| 440 |
% (_file_url(os.path.join(WEB_DIR, 'soundfont')) + '/',
|
| 441 |
-
_file_url(os.path.join(WEB_DIR, 'fluid')) + '/'
|
|
|
|
| 442 |
tags.append('<link rel="stylesheet" href="%s">' % _file_url(os.path.join(WEB_DIR, 'score-player.css')))
|
| 443 |
tags.append('<link rel="stylesheet" href="%s">' % _file_url(os.path.join(WEB_DIR, 'lyl-editor.css')))
|
| 444 |
for s in scripts:
|
|
|
|
| 436 |
# left under Compose + Logs (robust against Gradio's flex-nesting quirks).
|
| 437 |
os.path.join(WEB_DIR, 'layout-fit.js'),
|
| 438 |
]
|
| 439 |
+
tags = ['<script>window.__LILYSCRIPT_SOUNDFONT_URL=%r;window.__LILYSCRIPT_FLUID_URL=%r;window.__LILYSCRIPT_LOADING_RENDERER=%s;</script>'
|
| 440 |
% (_file_url(os.path.join(WEB_DIR, 'soundfont')) + '/',
|
| 441 |
+
_file_url(os.path.join(WEB_DIR, 'fluid')) + '/',
|
| 442 |
+
json.dumps(T('loading_renderer')))]
|
| 443 |
tags.append('<link rel="stylesheet" href="%s">' % _file_url(os.path.join(WEB_DIR, 'score-player.css')))
|
| 444 |
tags.append('<link rel="stylesheet" href="%s">' % _file_url(os.path.join(WEB_DIR, 'lyl-editor.css')))
|
| 445 |
for s in scripts:
|
web/score-player.css
CHANGED
|
@@ -223,3 +223,35 @@
|
|
| 223 |
#ls-score .ls-sf.ready .ls-sf-check {
|
| 224 |
display: inline;
|
| 225 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
#ls-score .ls-sf.ready .ls-sf-check {
|
| 224 |
display: inline;
|
| 225 |
}
|
| 226 |
+
|
| 227 |
+
/* In-panel renderer-loading overlay: shown inside the freshly-mounted score panel
|
| 228 |
+
while the 7.6MB verovio bundle is still downloading (score-player.js mounts early,
|
| 229 |
+
before verovio finishes — see app.py head order). Mirrors the static placeholder
|
| 230 |
+
spinner so the loading feedback is continuous across the mount handover. Removed
|
| 231 |
+
(display:none) once verovio is ready or a score has rendered. */
|
| 232 |
+
#ls-score .ls-renderer-loading {
|
| 233 |
+
display: flex;
|
| 234 |
+
flex-direction: column;
|
| 235 |
+
align-items: center;
|
| 236 |
+
justify-content: center;
|
| 237 |
+
min-height: 240px;
|
| 238 |
+
color: #999;
|
| 239 |
+
text-align: center;
|
| 240 |
+
}
|
| 241 |
+
#ls-score .ls-renderer-loading .ls-loading-spinner {
|
| 242 |
+
width: 38px;
|
| 243 |
+
height: 38px;
|
| 244 |
+
margin-bottom: 14px;
|
| 245 |
+
border: 4px solid #e3e3ea;
|
| 246 |
+
border-top-color: #7c5cff;
|
| 247 |
+
border-radius: 50%;
|
| 248 |
+
animation: ls-spin 0.8s linear infinite;
|
| 249 |
+
}
|
| 250 |
+
#ls-score .ls-renderer-loading .ls-loading-text {
|
| 251 |
+
font-size: 14px;
|
| 252 |
+
}
|
| 253 |
+
@media (prefers-reduced-motion: reduce) {
|
| 254 |
+
#ls-score .ls-renderer-loading .ls-loading-spinner {
|
| 255 |
+
animation: ls-spin 2.4s linear infinite;
|
| 256 |
+
}
|
| 257 |
+
}
|
web/score-player.js
CHANGED
|
@@ -20,6 +20,10 @@
|
|
| 20 |
'use strict';
|
| 21 |
|
| 22 |
const SOUNDFONT_URL = window.__LILYSCRIPT_SOUNDFONT_URL || './soundfont/';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
const state = {
|
| 25 |
toolkit: null, // Verovio toolkit
|
|
@@ -178,6 +182,7 @@
|
|
| 178 |
const pages = [];
|
| 179 |
for (let pg = 1; pg <= pageCount; pg++) pages.push(tk.renderToSVG(pg));
|
| 180 |
injectSvg(pages);
|
|
|
|
| 181 |
state.lastCode = code;
|
| 182 |
state.lastMei = mei;
|
| 183 |
setStatus('', '');
|
|
@@ -560,7 +565,15 @@
|
|
| 560 |
const svgBox = document.createElement('div'); svgBox.className = 'ls-svg';
|
| 561 |
const cursor = document.createElement('div'); cursor.className = 'ls-cursor';
|
| 562 |
svgBox.appendChild(cursor);
|
| 563 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
|
| 565 |
// transport bar (above the score)
|
| 566 |
const player = document.createElement('div'); player.className = 'ls-player'; player.style.display = 'none';
|
|
@@ -577,6 +590,7 @@
|
|
| 577 |
root.appendChild(player); root.appendChild(wrap);
|
| 578 |
|
| 579 |
els.root = root; els.svg = svgBox; els.preview = wrap; els.status = status; els.player = player; els.cursor = cursor;
|
|
|
|
| 580 |
els.playBtn = player.querySelector('.ls-play');
|
| 581 |
els.pauseBtn = player.querySelector('.ls-pause');
|
| 582 |
els.stopBtn = player.querySelector('.ls-stop');
|
|
@@ -616,7 +630,10 @@
|
|
| 616 |
if (typeof time === 'number' && !isNaN(time) && time >= 0) seekTo(time);
|
| 617 |
});
|
| 618 |
|
| 619 |
-
|
|
|
|
|
|
|
|
|
|
| 620 |
// Start loading the sound library (FluidSynth GM soundfont, ~40MB) right away
|
| 621 |
// at page mount rather than deferring to the first play — the AudioContext is
|
| 622 |
// created suspended (no autoplay-policy violation) and the fetch/worklet load
|
|
@@ -625,6 +642,14 @@
|
|
| 625 |
initAudio();
|
| 626 |
}
|
| 627 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 628 |
// Public API consumed by app.py's injected glue.
|
| 629 |
window.LilyScore = {
|
| 630 |
mount: mount,
|
|
|
|
| 20 |
'use strict';
|
| 21 |
|
| 22 |
const SOUNDFONT_URL = window.__LILYSCRIPT_SOUNDFONT_URL || './soundfont/';
|
| 23 |
+
// Label for the in-panel renderer-loading spinner. app.py sets the i18n'd string on
|
| 24 |
+
// window.__LILYSCRIPT_LOADING_RENDERER (matches the static placeholder's T('loading_renderer'));
|
| 25 |
+
// fall back to English if it's absent (e.g. score-player.js loaded standalone).
|
| 26 |
+
const LOADING_RENDERER_TEXT = window.__LILYSCRIPT_LOADING_RENDERER || 'Loading score renderer…';
|
| 27 |
|
| 28 |
const state = {
|
| 29 |
toolkit: null, // Verovio toolkit
|
|
|
|
| 182 |
const pages = [];
|
| 183 |
for (let pg = 1; pg <= pageCount; pg++) pages.push(tk.renderToSVG(pg));
|
| 184 |
injectSvg(pages);
|
| 185 |
+
setRendererLoading(false); // score is now visible — ensure the loading spinner is gone
|
| 186 |
state.lastCode = code;
|
| 187 |
state.lastMei = mei;
|
| 188 |
setStatus('', '');
|
|
|
|
| 565 |
const svgBox = document.createElement('div'); svgBox.className = 'ls-svg';
|
| 566 |
const cursor = document.createElement('div'); cursor.className = 'ls-cursor';
|
| 567 |
svgBox.appendChild(cursor);
|
| 568 |
+
// Renderer-loading overlay: a spinner shown INSIDE the freshly-mounted panel while
|
| 569 |
+
// the 7.6MB verovio bundle is still downloading. Since score-player.js now mounts
|
| 570 |
+
// early (before verovio finishes — see app.py head order), without this the panel
|
| 571 |
+
// would sit blank with no feedback during the wait (the static placeholder is gone,
|
| 572 |
+
// replaced by this mount). Removed once verovio is ready (setRendererLoading(false)).
|
| 573 |
+
const loading = document.createElement('div'); loading.className = 'ls-renderer-loading';
|
| 574 |
+
loading.innerHTML = '<div class="ls-loading-spinner" aria-hidden="true"></div>' +
|
| 575 |
+
'<div class="ls-loading-text">' + LOADING_RENDERER_TEXT + '</div>';
|
| 576 |
+
wrap.appendChild(status); wrap.appendChild(svgBox); wrap.appendChild(loading);
|
| 577 |
|
| 578 |
// transport bar (above the score)
|
| 579 |
const player = document.createElement('div'); player.className = 'ls-player'; player.style.display = 'none';
|
|
|
|
| 590 |
root.appendChild(player); root.appendChild(wrap);
|
| 591 |
|
| 592 |
els.root = root; els.svg = svgBox; els.preview = wrap; els.status = status; els.player = player; els.cursor = cursor;
|
| 593 |
+
els.loading = loading;
|
| 594 |
els.playBtn = player.querySelector('.ls-play');
|
| 595 |
els.pauseBtn = player.querySelector('.ls-pause');
|
| 596 |
els.stopBtn = player.querySelector('.ls-stop');
|
|
|
|
| 630 |
if (typeof time === 'number' && !isNaN(time) && time >= 0) seekTo(time);
|
| 631 |
});
|
| 632 |
|
| 633 |
+
// Show the renderer-loading spinner until verovio is ready; initVerovio() resolves
|
| 634 |
+
// when the (possibly still-downloading) verovio bundle has constructed its toolkit.
|
| 635 |
+
setRendererLoading(!state.verovioReady);
|
| 636 |
+
initVerovio().then(function (tk) { setRendererLoading(false); }); // warm up early; clears spinner when ready
|
| 637 |
// Start loading the sound library (FluidSynth GM soundfont, ~40MB) right away
|
| 638 |
// at page mount rather than deferring to the first play — the AudioContext is
|
| 639 |
// created suspended (no autoplay-policy violation) and the fetch/worklet load
|
|
|
|
| 642 |
initAudio();
|
| 643 |
}
|
| 644 |
|
| 645 |
+
// Toggle the in-panel renderer-loading spinner (verovio still downloading). Hidden
|
| 646 |
+
// once a score has actually rendered (an SVG is present) so it never overlaps content.
|
| 647 |
+
function setRendererLoading (flag) {
|
| 648 |
+
if (!els.loading) return;
|
| 649 |
+
var hasSvg = els.svg && els.svg.querySelector('svg');
|
| 650 |
+
els.loading.style.display = (flag && !hasSvg) ? '' : 'none';
|
| 651 |
+
}
|
| 652 |
+
|
| 653 |
// Public API consumed by app.py's injected glue.
|
| 654 |
window.LilyScore = {
|
| 655 |
mount: mount,
|