Compare commits

..

2 Commits

6 changed files with 60 additions and 41 deletions

View File

@ -1,4 +1,5 @@
from flask import Blueprint, render_template, request
from jinja2 import TemplateNotFound
blueprint = Blueprint(
'vue3_neusik',
@ -14,3 +15,14 @@ def index():
'vue3_neusik/index.html',
import_on_load='true' if request.args.get('import') == '1' else 'false',
)
@blueprint.route('/audiowidget', methods=['GET'])
def audiowidget():
try:
return render_template(
'vue3_neusik/audiowidget.tmpl',
result_url=request.args.get('result_url', ''),
errors=request.args.get('errors', ''),
)
except TemplateNotFound:
return '', 204

View File

@ -1,25 +0,0 @@
{# audiowidget.tmpl.stub
Copy to neusician/templates/ and rename to audiowidget.tmpl.
Include from base.tmpl or any score page with:
{% include 'audiowidget.tmpl' %}
Variables expected in context (all provided by /sompyle/status.json
via the sompyler_status_json route):
result_url URL to the rendered audio file (e.g. /sompyle/result.mp3)
errors non-empty string on synthesis failure, else empty/absent
The default JS audio widget in PaneCP renders a plain <audio> element.
Replace or extend this stub to match your site's look and feel.
#}
{% if errors %}
<div class="se-error">{{ errors }}</div>
{% elif result_url %}
<figure class="audio-result">
<figcaption>Rendered result</figcaption>
<audio controls preload="metadata">
<source src="{{ result_url }}" type="audio/mpeg">
<a href="{{ result_url }}">Download MP3</a>
</audio>
</figure>
{% endif %}

View File

@ -44,3 +44,12 @@ export async function fetchStatus(credentials) {
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
return res.json();
}
export async function fetchAudioWidget(resultUrl, errors) {
const params = new URLSearchParams();
if (resultUrl) params.set('result_url', resultUrl);
if (errors) params.set('errors', errors);
const res = await fetch(`${U.audiowidget}?${params}`);
if (!res.ok || res.status === 204) return '';
return res.text();
}

View File

@ -1,5 +1,5 @@
import { h, ref } from 'vue';
import { fetchScoreText, putScoreText, URLS } from '../api.js';
import { h, ref, watch } from 'vue';
import { fetchScoreText, putScoreText, fetchAudioWidget, URLS } from '../api.js';
import { patchScore } from '../exporter.js';
import { StatusPoller } from './StatusPoller.js';
@ -40,6 +40,18 @@ export const PaneCP = {
setup(props) {
const exporting = ref(false);
const exportError = ref('');
const audioWidgetHtml = ref('');
watch(
() => props.store.synthesisStatus,
async (s) => {
if (!s?.frozen) { audioWidgetHtml.value = ''; return; }
audioWidgetHtml.value = await fetchAudioWidget(
s.file_accomplished ? URLS.result : null,
s.errors ?? null,
);
},
);
async function doExport() {
exportError.value = '';
@ -118,18 +130,10 @@ export const PaneCP = {
store.synthesisStatus && !store.synthesisStatus.frozen
? h(StatusPoller, { store }) : null,
// Synthesis error
store.synthesisStatus?.frozen && store.synthesisStatus?.errors
? h('div', { class: 'se-error', style: 'margin-top:0.5rem' },
store.synthesisStatus.errors) : null,
// Audio result
store.synthesisStatus?.file_accomplished
? h('audio', {
controls: true,
src: URLS.result,
style: 'display:block;width:100%;margin-top:0.5rem',
}) : null,
// Audio widget (rendered server-side from audiowidget.tmpl)
audioWidgetHtml.value
? h('div', { innerHTML: audioWidgetHtml.value, style: 'margin-top:0.5rem' })
: null,
]);
};
},

View File

@ -0,0 +1,18 @@
{# audiowidget.tmpl.stub — copy to audiowidget.tmpl in the same directory to activate.
Customise to match your site's look and feel.
Context variables (passed as query params by the score editor):
result_url URL of the rendered MP3, or empty string
errors synthesis error message, or empty string
#}
{% if errors %}
<div class="se-error">{{ errors }}</div>
{% elif result_url %}
<figure class="audio-result">
<figcaption>Rendered result</figcaption>
<audio controls preload="metadata" style="display:block;width:100%">
<source src="{{ result_url }}" type="audio/mpeg">
<a href="{{ result_url }}">Download MP3</a>
</audio>
</figure>
{% endif %}

View File

@ -14,8 +14,9 @@ window.NEUSICIAN_URLS = {
astlog: "{{ url_for('astlog') }}",
score: "{{ url_for('score') }}",
status: "{{ url_for('statusjson') }}",
result: "{{ url_for('send_audio_generated') }}",
submit: "{{ url_for('public-yaml-acceptor') }}"
result: "{{ url_for('send_audio_generated') }}",
submit: "{{ url_for('public-yaml-acceptor') }}",
audiowidget: "{{ url_for('vue3_neusik.audiowidget') }}"
};
</script>
<script type="importmap">