114 lines
4.9 KiB
HTML
114 lines
4.9 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Audio Transcriber — {{ settings.APP_NAME }}{% endblock %}
|
|
{% block content %}
|
|
<div class="page">
|
|
{% if status == "started" or status == "started-mus" %}
|
|
<div id="txStatus" class="form-error" style="background:#fff8e6;color:#8a6d00;border-color:#e6d29a">
|
|
<span class="tx-spinner"></span> <span id="txText">Transcribing…</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<h1 class="greeting">Audio Transcriber</h1>
|
|
<p class="muted" style="max-width:640px">
|
|
Turn an audio or video file into MIDI (via Spotify Basic Pitch). Upload or pick a file from
|
|
your bucket, then the worker produces a MIDI you can download. Video files get their audio extracted automatically.
|
|
</p>
|
|
|
|
<div class="admin-panel">
|
|
<section class="stack">
|
|
<h2 class="section-title">Your audio</h2>
|
|
{% if audio_files %}
|
|
<ul class="file-list">
|
|
{% for k in audio_files %}
|
|
<li>
|
|
<span class="file-name" title="{{ k }}">{{ k }}</span>
|
|
<span style="display:flex;gap:8px;align-items:center">
|
|
<form method="post" action="/transcriber/transcribe" style="display:flex">
|
|
<input type="hidden" name="key" value="{{ k }}">
|
|
<button class="button button-utility" type="submit">MIDI</button>
|
|
</form>
|
|
<form method="post" action="/transcriber/sheetmusic" style="display:flex">
|
|
<input type="hidden" name="key" value="{{ k }}">
|
|
<button class="button button-utility" type="submit" title="MIDI + MusicXML + PDF" style="border-color:#dd5b00;color:#dd5b00">Sheet music</button>
|
|
</form>
|
|
</span>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p class="faint">No audio files yet. Upload one below.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="linkout-card stack">
|
|
<h2 class="section-title">Upload audio / video</h2>
|
|
<form method="post" action="/transcriber/upload" enctype="multipart/form-data">
|
|
<div class="form-field">
|
|
<input class="text-input" type="file" name="file" accept="audio/*,video/*,.wav,.mp3,.flac,.m4a,.mp4,.mov,.webm,.mkv" required>
|
|
</div>
|
|
<button class="button button-primary width-full" type="submit">Upload & transcribe</button>
|
|
</form>
|
|
<p class="faint margin-top" style="font: var(--type-caption)">
|
|
Saved to <strong>{{ bucket }}</strong> under <code>audio/…</code>. <strong>MIDI</strong> = Basic Pitch (fast); <strong>Sheet music</strong> = MuScriptor (MIDI+MusicXML+PDF, slower).
|
|
</p>
|
|
</section>
|
|
</div>
|
|
|
|
<section class="stack" style="margin-top: var(--space-xxl)">
|
|
<h2 class="section-title">Transcriptions</h2>
|
|
{% if transcriptions %}
|
|
<div class="workspace">
|
|
<ul class="file-list">
|
|
{% for t in transcriptions %}
|
|
<li>
|
|
<span class="file-name" title="{{ t }}">{{ t }}</span>
|
|
<span style="display:flex;gap:8px;align-items:center">
|
|
<a class="button button-utility" href="/transcriber/download?key={{ t }}">Download</a>
|
|
<form method="post" action="/transcriber/delete" style="display:flex">
|
|
<input type="hidden" name="key" value="{{ t }}">
|
|
<button class="button button-utility" type="submit" title="Delete" style="border-color:#b3261e;color:#b3261e">🗑</button>
|
|
</form>
|
|
</span>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% else %}
|
|
<p class="faint">Nothing transcribed yet.</p>
|
|
{% endif %}
|
|
</section>
|
|
</div>
|
|
<style>
|
|
.tx-spinner { display:inline-block; width:14px; height:14px; border:2px solid #d7d7d7; border-top-color:#8a6d00; border-radius:50%; animation:txspin .7s linear infinite; vertical-align:-2px; margin-right:6px; }
|
|
@keyframes txspin { to { transform: rotate(360deg); } }
|
|
</style>
|
|
{% if task %}
|
|
<script>
|
|
(function () {
|
|
var task = "{{ task }}";
|
|
var box = document.getElementById('txStatus');
|
|
var txt = document.getElementById('txText');
|
|
if (!box || !task) return;
|
|
function poll() {
|
|
fetch('/api/transcriber/status?task=' + encodeURIComponent(task))
|
|
.then(function (r) { return r.json(); })
|
|
.then(function (d) {
|
|
if (d.state === 'SUCCESS') {
|
|
box.style.background = '#e8f7ec'; box.style.color = '#1aae39'; box.style.borderColor = '#b8e6c7';
|
|
txt.textContent = 'Done ✓ — results below.';
|
|
setTimeout(function () { location.reload(); }, 1200);
|
|
} else if (d.state === 'FAILURE') {
|
|
box.style.background = '#fdecec'; box.style.color = '#b3261e'; box.style.borderColor = '#f5c6c4';
|
|
txt.textContent = 'Failed — check the worker logs.';
|
|
} else {
|
|
txt.textContent = 'Transcribing… (' + d.state.toLowerCase() + ')';
|
|
setTimeout(poll, 3000);
|
|
}
|
|
})
|
|
.catch(function () { setTimeout(poll, 5000); });
|
|
}
|
|
poll();
|
|
})();
|
|
</script>
|
|
{% endif %}
|
|
{% endblock %} |