musica / source /ui /src /lib /ComposeButton.svelte
MMVos's picture
try
4a9e8e5
raw
history blame
No virus
3.22 kB
<script lang="ts">
import { density, composing, style, temperature, notesImage, notesTokens, audioBlob } from '$lib/stores';
import { styles } from './config.json';
const updateMetadata = () => {
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: `${styles[$style]} Composition`,
artist: 'AI Guru Composer',
album: 'Hugging Face',
artwork: [
{
src: 'static/hugging-face-headphones.png',
sizes: '512x512',
type: 'image/png',
},
],
});
}
};
const createTask = async ({
music_style,
density,
temperature,
}: {
music_style: string;
density: string;
temperature: string;
}) => {
const taskResponse = await fetch('task/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
music_style,
density,
temperature,
}),
});
if (!taskResponse.ok || !taskResponse.headers.get('content-type')?.includes('application/json')) {
throw new Error(`Unable to create composition: [${taskResponse.status}] ${await taskResponse.text()}`);
}
const task = await taskResponse.json();
return task;
};
const pollTask = async (task: Record<string, any>) => {
const taskResponse = await fetch(`task/poll?task_id=${task.task_id}`);
if (!taskResponse.ok || !taskResponse.headers.get('content-type')?.includes('application/json')) {
throw new Error(`Unable to create composition: [${taskResponse.status}] ${await taskResponse.text()}`);
}
return await taskResponse.json();
};
const longPollTask = async (task: Record<string, any>, interval = 1_000, max = 100): Promise<Record<string, any>> => {
task = await pollTask(task);
if (task.status === 'completed' || task.status === 'failed' || (max && task.poll_count > max)) {
return task;
}
await new Promise((resolve) => setTimeout(resolve, interval));
return await longPollTask(task, interval, max);
};
const compose = async () => {
$composing = true;
try {
const task = await createTask({
music_style: $style,
density: $density,
temperature: $temperature,
});
const completedTask = await longPollTask(task);
const { audio, image, tokens } = completedTask.output;
$audioBlob = audio;
$notesImage = image;
$notesTokens = tokens;
updateMetadata();
} catch (err) {
console.error(err);
} finally {
$composing = false;
}
};
</script>
<button disabled={$composing} on:click={compose}>
{#if $composing}
Composing...
{:else}
Compose <img src="static/wand.svg" alt="Magic wand" />
{/if}
</button>
<style>
button {
display: block;
font-size: 1.2rem;
font-family: 'Lato', sans-serif;
font-weight: 700;
color: hsl(0 0% 97%);
background: transparent;
border: 3px solid hsl(0 0% 97%);
border-radius: 0.375rem;
padding: 0.5rem 1rem;
cursor: pointer;
margin: 1rem auto 2rem;
}
button[disabled] {
border-color: hsl(0 0% 50%);
color: hsl(0 0% 50%);
cursor: initial;
}
img {
height: 1.2rem;
aspect-ratio: 1 / 1;
vertical-align: bottom;
}
@media (min-width: 900px) {
button {
margin-top: 0;
}
}
</style>