// Cloned by Niall Ryan on 24 Nov 2023 from World "Calling APIs" by Niall Ryan
// Please leave this clone trail here.
// Cloned by Niall Ryan on 22 Nov 2023 from World "Chat with GPT model" by Starter user
// Please leave this clone trail here.
// Sample prompt for a song
let prompt = "";
let key = "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";
// METHOD FOR CALLING AI APIs
async function fetchData(apiUrl, data) {
// MH edit
console.log ( "fetchData: " + apiUrl );
console.log ( data );
const response = await fetch(apiUrl, {
headers: {
Authorization: `Bearer ${key}`
},
method: "POST",
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP Error! status: ${response.status}`);
}
return response;
}
// Function handles the query to the image generating AI API
async function queryImage(data) {
// Add a 'loader' to the image container whilst awaiting the API
let loader = document.createElement('div');
loader.className = 'loader';
document.getElementById('image').appendChild(loader);
try {
const response = await fetchData("https://api-inference.huggingface.co/models/runwayml/stable-diffusion-v1-5", data);
const imageBlob = await response.blob();
updateUI('image', imageBlob, true);
} catch (error) {
handleError('image', error.message);
}
}
// General function for querying sound API (small & medium sized models)
async function querySound(data, apiUrl, containerId) {
// Add a 'loader' to the image container whilst awaiting the API
let loader = document.createElement('div');
loader.className = 'loader';
document.getElementById(containerId).appendChild(loader);
try {
const response = await fetchData(apiUrl, data);
if (response.headers.get("content-type") === "audio/flac") {
const audioBlob = await response.blob();
updateUI(containerId, audioBlob, false);
} else {
const result = await response.json();
return result;
}
} catch (error) {
handleError(containerId, error.message);
}
}
// Function updates the UI with new content or error messages
function updateUI(containerId, content, isImage) {
const container = document.getElementById(containerId);
container.innerHTML = ''; // Clear previous content or loaders
if (isImage) {
const image = document.createElement('img');
image.src = URL.createObjectURL(content);
container.appendChild(image);
} else {
const audio = new Audio(URL.createObjectURL(content));
audio.controls = true;
container.appendChild(audio);
audio.addEventListener('canplaythrough', () => audio.play());
}
}
function clearUI() {
document.getElementById('image').innerHTML = '';
document.getElementById('audioSmall').innerHTML = '';
document.getElementById('audioMedium').innerHTML = '';
}
// Error handler for UI updates
function handleError(containerId, status) {
const container = document.getElementById(containerId);
container.innerHTML = ''; // Clear loaders
const errorElement = document.createElement('p');
errorElement.className = 'errorText';
errorElement.textContent = `ERROR: ${containerId} returned ${status}`;
container.appendChild(errorElement);
console.error(`HTTP Error! status: ${status}`);
}
// Function that handles the user's custom prompt input
async function handleUserInput() {
// Clear any existing inference results
clearUI()
// Get the user prompt
const userInput = document.getElementById('userPrompt').value;
const prompt = userInput; // Use the user input as the new prompt
// Update the displayed prompt
document.getElementById('prompt').innerHTML = `<b>Prompt:</b> ${prompt}`;
// Execute the API calls with the new prompt
const imagePromise = queryImage({ inputs: `Album cover art for a song described as "${prompt}"` });
const soundSmallPromise = querySound({ inputs: prompt }, "https://api-inference.huggingface.co/models/facebook/musicgen-small", 'audioSmall');
const soundMediumPromise = querySound({ inputs: prompt }, "https://api-inference.huggingface.co/models/facebook/musicgen-medium", 'audioMedium');
// Execute all the API calls at once
await Promise.all([imagePromise, soundSmallPromise, soundMediumPromise]);
}
// The actual document HTML
document.write(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Song Generator</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"/>
<style>
body {
font-family: 'Roboto', sans-serif;
text-align: center;
margin: 0;
color: #fff;
}
.background {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
background-color: #333;
}
.container {
background-color: #282828;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(4, 170, 109,0.2);
margin: auto;
padding: 20px;
max-width: 800px;
margin-top: 40px;
}
img, audio {
max-width: 100%;
border-radius: 5px;
margin-top: 20px;
}
.input-field {
width: 80%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 3px solid #04AA6D;
border-radius: 6px;
box-sizing: border-box;
background-color: #f0f0f0;
color: #333;
font-size: 16px;
transition: border 0.3s;
}
.input-field:focus {
border-color: #026D4D;
outline: none;
}
.input-field::placeholder {
color: #7a7a7a;
}
.input-field:hover {
border-color: #013D2D;
}
.generate-button {
padding: 10px 20px;
border-radius: 5px;
border: none;
background-color: #04AA6D;
color: #D6EEE2;
color: white;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.generate-button:hover {
background-color: #D6EEE2;
color: #04AA6D;
}
.loader {
border: 5px solid #D6EEE2;
border-top: 5px solid #04AA6D;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 2s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.errorText {
color: #e74c3c;
padding: 10px;
font-weight: 900;
}
.prompt-display {
background-color: #333;
border-left: 5px solid #35634C;
padding: 10px;
margin: 20px 0;
display: block;
text-align: left;
}
.prompt-display b {
color: #D6EEE2
}
.title {
color: #04AA6D;
margin-bottom: 5px;
margin: 0 0 20px 0;
}
footer {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: #282828;
padding: 10px 0;
}
.footer-content {
display: flex;
justify-content: center;
align-items: center;
}
.green-bar {
color: 00ff00;
font-weight: 900;
margin: 0 10px 0 20px;
}
</style>
</head>
<body>
<div class="background">
<div class="container">
<h1 class="title">AI Song Generator</h1>
<input type="text" id="userPrompt" class="input-field" placeholder="Describe your fictional song..." value="${prompt}">
<button onclick="handleUserInput()" class="generate-button">Generate</button>
<span class="prompt-display" id="prompt"><b>Prompt:</b> ${prompt}</span>
<div id="image"></div>
<div id="audioSmall" class="pad"></div>
<div id="audioMedium" class="pad"></div>
</div>
</div>
<footer>
<div class="footer-content">
<span class="green-bar">|</span>
Audio APIs are unreliable due to their popularity.
<span class="green-bar">|</span>
Audio APIs take ~30s to perform inference and return info.
</div>
</footer>
</body>
</html>
`);