Code viewer for World: Text To Video Conversion
<!-- Title: Text To Video Conversion by Daniel Marcu & Rory Peng -->
<!-- Description: Convert any prompt you wish into a 3 second video that is generated using the Stable Diffuse AI API -->
<!-- Date: 1/12/23 -->
<!-- API Documentation: https://stablediffusionapi.com/docs/ -->

<!-- HTML Structures and Styles -->
<!-- HTML DOM Document write(): https://www.w3schools.com/jsref/met_doc_write.asp -->

document.write(`
<head>
  <style>
    
    /* General styles for the page */
    body {
      font-family: Arial, sans-serif;
      margin: 0;
      padding: 0;
      display: flex;
      height: 100vh;
    }

    /* Styles for the left container */
    #leftContainer {
      flex: 1;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }

    /* Styles for the right container */
    #rightContainer {
      flex: 1;
      background-color: #2c3e50;
      color: #00ff00;
      overflow-y: scroll;
      max-height: 100vh;
      padding: 20px;
      box-sizing: border-box;
    }

    /* Styles for the console title */
    #consoleTitle {
      text-align: center;
      font-size: 18px;
      color: #ffffff;
      margin-bottom: 10px;
    }

    /* Styles for the console output */
    #console {
      width: 100%;
      padding: 10px;
      font-family: monospace;
      white-space: pre-wrap;
      margin-bottom: 10px;
      overflow: hidden;
    }

    /* Styles for the result container */
    #result {
      border: none;
      padding: 0;
      width: 80%; /* Adjusted width to make the video slightly smaller */
      max-width: 640px;
      margin-bottom: 20px;
    }

    /* Styles for the video element */
    video {
      width: 100%;
      max-width: 100%; /* Ensure video doesn't exceed its container */
      border-radius: 5px;
      box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    }

    /* Styles for buttons and loaders */
    #backButton,
    .extraButtons {
      padding: 10px 20px;
      border: none;
      margin-top: 10px;
      border-radius: 5px;
      cursor: pointer;
      background-color: #007bff;
      color: white;
      transition: background-color 0.3s ease;
    }

    /* Button hover effect */
    #backButton:hover,
    .extraButtons:hover {
      background-color: #0056b3;
    }

    /* Styles for the loop button in green */
    #loopButton.green {
      background-color: #4CAF50;
      color: white;
    }

    /* Styles for hidden elements */
    .hidden { display: none; }

    /* Styles for the loader animation */
    .loader {
      border: 8px solid #f3f3f3;
      border-top: 8px solid #3498db;
      border-radius: 50%;
      width: 50px;
      height: 50px;
      animation: spin 1s linear infinite;
      margin: 20px auto;
    }

    /* Keyframe animation for the loader spin */
    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }

    /* Previous styles - retained for reference */
    #leftPanelTitle {
      font-size: 24px;
      color: #333;
      margin-bottom: 20px;
    }
    
    #apiKeyInput,
    #textInput {
      padding: 10px;
      width: 300px;
      margin-bottom: 10px;
      border-radius: 5px;
      border: 1px solid #ddd;
    }
    
    #sendRequest,
    #loopButton,
    .extraButtons {
      padding: 10px 20px;
      border: none;
      background-color: #007bff;
      color: white;
      border-radius: 5px;
      cursor: pointer;
      transition: background-color 0.3s ease;
    }
    
    #sendRequest:hover,
    #loopButton:hover,
    .extraButtons:hover {
      background-color: #0056b3;
    }
  </style>
</head>
<body>

<!-- Left Container for Prompts and Videos -->
<div id="leftContainer">
  
  <!-- Title for the left panel -->
  <div id="leftPanelTitle">API Request Interface</div>

  <!-- Textbox for API key input -->
  <input type="text" id="apiKeyInput" placeholder="Enter your API key...">

  <!-- Textbox for input -->
  <input type="text" id="textInput" placeholder="Enter prompt here..." onkeydown="if (event.key === 'Enter') sendAPIRequest()">

  <!-- Button to trigger the API request -->
  <button id="sendRequest" onclick="sendAPIRequest()">Send Request</button>
  <br>

  <!-- Loading indicator -->
  <div id="loading" class="hidden">
    <div class="loader"></div>
    <p>Loading...</p>
  </div>

  <!-- Container to display the result or video -->
  <div id="result" class="hidden"></div>
  
  <!-- Back button to return to prompts -->
  <button id="backButton" class="hidden" onclick="showPrompts()">Back to Prompts</button>
</div>

<!-- Right Container for Console Output -->
<div id="rightContainer">
  <div id="consoleTitle">Console</div>
  
  <!-- Console-like output -->
  <div id="console">  > Welcome to the API Request Interface! Enter your API key, provide a prompt, and click "Send Request" to initiate the API request. These usually take between 2-5 minutes.
  </div>
</div>

<script>

<!-- Global variable to store the API key -->
let apiKey;

<!-- Function to get the API key from the input -->
function getApiKey() {
  return document.getElementById('apiKeyInput').value.trim();
}

<!-- Function to log messages to the console-like output -->
function logToConsole(message) {
  const consoleOutput = document.getElementById('console');
  consoleOutput.innerHTML += message + '<br>';
  consoleOutput.scrollTop = consoleOutput.scrollHeight;
}

<!-- Function to show the prompts -->
function showPrompts() {
  document.getElementById('result').classList.add('hidden');
  document.getElementById('backButton').classList.add('hidden');
  document.getElementById('console').innerHTML = '> Welcome to the API Request Interface! Enter your API key, provide a prompt, and click "Send Request" to initiate the API request. These usually take between 2-5 minutes';
  document.getElementById('apiKeyInput').classList.remove('hidden');
  document.getElementById('textInput').classList.remove('hidden');
  document.getElementById('sendRequest').classList.remove('hidden');
}

<!-- Function to hide the prompts -->
function hidePrompts() {
  document.getElementById('apiKeyInput').classList.add('hidden');
  document.getElementById('textInput').classList.add('hidden');
  document.getElementById('sendRequest').classList.add('hidden');
}

<!-- Function to send the API request -->
function sendAPIRequest() {
  apiKey = getApiKey(); // Get the API key from the input
  const textInput = document.getElementById('textInput');
  const promptText = textInput.value; // Get the value from the text input
  const resultContainer = document.getElementById('result');
  const loadingContainer = document.getElementById('loading');
  const backButton = document.getElementById('backButton');

  <!-- Show loading indicator -->
  loadingContainer.classList.remove('hidden');
  resultContainer.innerHTML = ''; // Clear the result container

  logToConsole('> API Request started...');

  const myHeaders = new Headers();
  myHeaders.append("Content-Type", "application/json");

  <!-- JSON stringify source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify -->
  const raw = JSON.stringify({
    "key": apiKey,
    "prompt": promptText,
    "negative_prompt": "Low Quality",
    "scheduler": "UniPCMultistepScheduler",
    "seconds": 3
  });

  const requestOptions = {
    method: 'POST',
    headers: myHeaders,
    body: raw,
    redirect: 'follow'
  };

  <!-- Make the fetch request to the API -->
  fetch("https://stablediffusionapi.com/api/v5/text2video", requestOptions)
    .then(response => response.json())
    .then(result => {
      if (result.status === 'processing') {
        logToConsole(\`  > Please wait! If you don't see any other output in the console in the next 30 seconds, the API is probably down\`);
        setTimeout(() => {
          checkVideoStatus(result.fetch_result);
        }, result.eta * 1000);
      } else if (result.status === 'success') {
        logToConsole('> API Request successful!');
        displayVideo(result.output[0]);
        backButton.classList.remove('hidden');
        hidePrompts();
        addExtraButtons();
      } else {
        logToConsole('  > API Request failed. See F12 console for details.');
        console.error('API Request failed:', result);
      }
    })
    .catch(error => {
      logToConsole('Error: ' + error);
    })
    .finally(() => {
      // Hide loading indicator after displaying the video or in case of an error
      loadingContainer.classList.add('hidden');
    });
}

<!-- Function to display the video -->
function displayVideo(videoUrl) {
  const resultContainer = document.getElementById('result');
  resultContainer.innerHTML = \`<video id="apiVideo" controls><source src="\${videoUrl}" type="video/mp4">Your browser does not support the video tag.</video>\`;
  resultContainer.querySelector('video').load();
  resultContainer.classList.remove('hidden');
}

<!-- Function to check the video status after the estimated time -->
function checkVideoStatus(fetchResultUrl) {
  const myHeaders = new Headers();
  myHeaders.append("Content-Type", "application/json");

  const requestOptions = {
    method: 'POST',
    headers: myHeaders,
    body: JSON.stringify({ "key": apiKey })
  };

  fetch(fetchResultUrl, requestOptions)
    .then(response => {
      if (!response.ok) {
        throw new Error(\`HTTP error! status: \${response.status}\`);
      }
      return response.json();
    })
    .then(result => {
      if (result.status === 'success' && result.output && result.output.length > 0) {
        logToConsole('Video processing complete!');
        displayVideo(result.output[0]);
        document.getElementById('backButton').classList.remove('hidden');
        addExtraButtons();
      } else if (result.status === 'processing') {
        logToConsole('  > Video still processing, will check again in 10 seconds...');
        setTimeout(() => {
          checkVideoStatus(fetchResultUrl);
        }, 10000);
      } else {
        logToConsole('  > Received unexpected result. See console for details.');
        console.error('Received unexpected result:', result);
      }
    })
    .catch(error => {
      logToConsole('Error fetching the result: ' + error.message + '. See console for details.');
      console.error('Error fetching the result:', error);
    });
}

<!-- Function to add extra buttons below the video -->
function addExtraButtons() {
  const resultContainer = document.getElementById('result');

  <!-- Download button -->
  const downloadButton = document.createElement('button');
  downloadButton.className = 'extraButtons';
  downloadButton.textContent = 'Download Video';
  downloadButton.onclick = function () {
    const video = document.getElementById('apiVideo');
    const a = document.createElement('a');
    a.href = video.src;
    a.download = 'video.mp4';
    a.click();
  };

  <!-- Loop button -->
  <!-- Loop button source: https://stackoverflow.com/questions/3455229/control-html5-video-player-loop-with-javascript
  const loopButton = document.createElement('button');
  loopButton.className = 'extraButtons';
  loopButton.textContent = 'Toggle Loop';
  loopButton.onclick = function () {
    const video = document.getElementById('apiVideo');
    video.loop = !video.loop;
    loopButton.classList.toggle('green', video.loop);
  };

  <!-- Fullscreen button -->
  <!-- Fullscreen button source: https://www.w3schools.com/howto/howto_js_fullscreen.asp -->
  
  const fullscreenButton = document.createElement('button');
  fullscreenButton.className = 'extraButtons';
  fullscreenButton.textContent = 'Toggle Fullscreen';
  fullscreenButton.onclick = function () {
    const video = document.getElementById('apiVideo');
    if (video.requestFullscreen) {
      video.requestFullscreen();
    } else if (video.mozRequestFullScreen) { /* Firefox */
      video.mozRequestFullScreen();
    } else if (video.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
      video.webkitRequestFullscreen();
    } else if (video.msRequestFullscreen) { /* IE/Edge */
      video.msRequestFullscreen();
    }
  };

  <!-- Append buttons to the result container -->
  resultContainer.appendChild(downloadButton);
  resultContainer.appendChild(loopButton);
  resultContainer.appendChild(fullscreenButton);
}

</script>

</body>
</html>
`);