Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
<title>Autotutorial.ai - Ionic Demo</title> | |
<link rel="manifest" href="manifest.json"> | |
<meta name="apple-mobile-web-app-capable" content="yes"> | |
<meta name="apple-mobile-web-app-status-bar-style" content="black"> | |
<!-- Ionic CSS --> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@latest/css/ionic.bundle.css"> | |
<!-- Font Awesome CDN --> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" /> | |
<!-- Inter Font from Google Fonts --> | |
<link rel="preconnect" href="https://fonts.googleapis.com"> | |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
<style> | |
:root { | |
--ion-font-family: 'Inter', sans-serif; | |
} | |
body { | |
font-family: var(--ion-font-family); | |
} | |
.video-player-container { | |
border-radius: 8px; | |
overflow: hidden; | |
box-shadow: 0 2px 8px rgba(0,0,0,0.15); | |
position: relative; /* For video overlay */ | |
} | |
.video-overlay-title { | |
position: absolute; | |
bottom: 16px; | |
left: 16px; | |
color: white; | |
font-size: 1.5em; | |
font-weight: 600; | |
text-shadow: 1px 1px 2px rgba(0,0,0,0.8); | |
} | |
.status-box { | |
border-radius: 8px; | |
border: 1px solid var(--ion-color-light); | |
background-color: var(--ion-color-light-contrast); | |
padding: 16px; | |
margin-bottom: 16px; | |
} | |
.status-progress-bar { | |
margin-top: 8px; | |
} | |
</style> | |
</head> | |
<body> | |
<ion-app> | |
<ion-header translucent="true"> | |
<ion-toolbar> | |
<ion-title>Autotutorial.ai</ion-title> | |
</ion-toolbar> | |
</ion-header> | |
<ion-content fullscreen="true"> | |
<ion-card> | |
<ion-card-header> | |
<ion-card-title>Generate Tutorial</ion-card-title> | |
<ion-card-subtitle>Create a new video tutorial</ion-card-subtitle> | |
</ion-card-header> | |
<ion-card-content> | |
<ion-list lines="full"> | |
<ion-item> | |
<ion-label position="floating">Tutorial Topic</ion-label> | |
<ion-input type="text" id="tutorialTopic" value="React State Management Best Practices"></ion-input> | |
</ion-item> | |
<ion-item> | |
<ion-label>Desired Duration</ion-label> | |
<ion-select value="10" id="desiredDuration"> | |
<ion-select-option value="5">5 Minutes</ion-select-option> | |
<ion-select-option value="10">10 Minutes</ion-select-option> | |
<ion-select-option value="15">15 Minutes</ion-select-option> | |
<ion-select-option value="20">20 Minutes</ion-select-option> | |
</ion-select> | |
</ion-item> | |
<ion-item> | |
<ion-label position="floating">OpenAI API Key (Optional)</ion-label> | |
<ion-input type="text" id="openaiApiKey" value="sk-DEMO_OPENAI_KEY" placeholder="For enhanced script generation"></ion-input> | |
</ion-item> | |
<ion-item> | |
<ion-label position="floating">Video Generation API Key (Optional)</ion-label> | |
<ion-input type="text" id="videoGenApiKey" value="vg-DEMO_VIDEO_GEN_KEY" placeholder="For advanced video synthesis"></ion-input> | |
</ion-item> | |
</ion-list> | |
<div style="margin-top: 20px; display: flex; gap: 10px;"> | |
<ion-button id="generateButton" expand="block"><ion-icon slot="start" icon="magic"></ion-icon>Generate Tutorial</ion-button> | |
<ion-button id="resetButton" expand="block" color="secondary" class="ion-hide"><ion-icon slot="start" icon="refresh"></ion-icon>Reset</ion-button> | |
</div> | |
</ion-card-content> | |
</ion-card> | |
<ion-card id="statusSection" class="status-box"> | |
<ion-card-header> | |
<ion-card-title><ion-icon icon="information-circle" style="margin-right: 8px;"></ion-icon>Status Updates</ion-card-title> | |
</ion-card-header> | |
<ion-card-content> | |
<div id="statusMessages"> | |
<p class="ion-text-color-secondary"><ion-icon icon="checkmark-circle" color="success" style="margin-right: 4px;"></ion-icon>Ready to generate. Click "Generate Tutorial".</p> | |
</div> | |
<p id="errorMessage" class="ion-text-color-danger ion-hide" style="margin-top: 8px;"></p> | |
<ion-progress-bar value="0" buffer="0" reversed="false" class="status-progress-bar" id="progressBar"></ion-progress-bar> | |
</ion-card-content> | |
</ion-card> | |
<ion-card id="videoOutputSection" class="ion-hide"> | |
<ion-card-header> | |
<ion-card-title><ion-icon icon="film" style="margin-right: 8px;"></ion-icon>Tutorial Preview</ion-card-title> | |
</ion-card-header> | |
<ion-card-content> | |
<div class="video-player-container"> | |
<video id="tutorialVideo" controls width="100%"> | |
<source src="https://joy.videvo.net/videvo_files/video/free/2016-06/small_watermarked/Drone_Flying_Above_City_Skyline_preview.mp4" type="video/mp4"> | |
Your browser does not support the video tag. | |
</video> | |
<div id="videoTitleOverlay" class="video-overlay-title"></div> | |
</div> | |
<div style="margin-top: 16px; display: flex; justify-content: space-between; align-items: center;"> | |
<div> | |
<p><strong id="videoTitle"></strong></p> | |
<ion-text color="secondary"><p>Generated tutorial preview</p></ion-text> | |
</div> | |
<ion-button id="downloadButton" download="tutorial.mp4"><ion-icon slot="start" icon="download"></ion-icon>Download</ion-button> | |
</div> | |
</ion-card-content> | |
</ion-card> | |
<ion-card id="history-section" class="ion-hide"> | |
<ion-card-header> | |
<ion-card-title><ion-icon icon="time" style="margin-right: 8px;"></ion-icon>Tutorial History</ion-card-title> | |
<ion-card-subtitle>Access past tutorials</ion-card-subtitle> | |
</ion-card-header> | |
<ion-card-content> | |
<ion-list id="historyList"> | |
<ion-item button class="history-item" data-video-title="Mastering JavaScript Promises"> | |
<ion-label> | |
<h3>Mastering JavaScript Promises</h3> | |
<p>Generated: Oct 26, 2023</p> | |
</ion-label> | |
</ion-item> | |
<ion-item button class="history-item" data-video-title="Introduction to Python for Data Science"> | |
<ion-label> | |
<h3>Introduction to Python for Data Science</h3> | |
<p>Generated: Oct 25, 2023</p> | |
</ion-label> | |
</ion-item> | |
<ion-item button class="history-item" data-video-title="Building a REST API with Express.js"> | |
<ion-label> | |
<h3>Building a REST API with Express.js</h3> | |
<p>Generated: Oct 20, 2023</p> | |
</ion-label> | |
</ion-item> | |
</ion-list> | |
<ion-text color="secondary"><p style="margin-top: 16px;">Demo history is simulated.</p></ion-text> | |
</ion-card-content> | |
</ion-card> | |
<ion-card id="settings-section" class="ion-hide"> | |
<ion-card-header> | |
<ion-card-title><ion-icon icon="settings" style="margin-right: 8px;"></ion-icon>Settings</ion-card-title> | |
<ion-card-subtitle>Customize preferences</ion-card-subtitle> | |
</ion-card-header> | |
<ion-card-content> | |
<ion-list lines="full"> | |
<ion-item> | |
<ion-label>Default Duration</ion-label> | |
<ion-select value="10" id="defaultDurationSetting"> | |
<ion-select-option value="5">5 Minutes</ion-select-option> | |
<ion-select-option value="10">10 Minutes</ion-select-option> | |
<ion-select-option value="15">15 Minutes</ion-select-option> | |
</ion-select> | |
</ion-item> | |
<ion-item> | |
<ion-label>Video Quality</ion-label> | |
<ion-select value="720p" id="videoQualitySetting"> | |
<ion-select-option value="720p">720p HD</ion-select-option> | |
<ion-select-option value="1080p">1080p Full HD</ion-select-option> | |
<ion-select-option value="4k">4K Ultra HD (Premium)</ion-select-option> | |
</ion-select> | |
</ion-item> | |
<ion-item> | |
<ion-label>Voiceover Language</ion-label> | |
<ion-select value="en-US" id="voiceoverLanguage"> | |
<ion-select-option value="en-US">English (US)</ion-select-option> | |
<ion-select-option value="en-GB">English (UK)</ion-select-option> | |
<ion-select-option value="es-ES">Spanish</ion-select-option> | |
<ion-select-option value="fr-FR">French</ion-select-option> | |
</ion-select> | |
</ion-item> | |
<ion-item> | |
<ion-label>Output Format</ion-label> | |
<ion-select value="mp4" id="outputFormat"> | |
<ion-select-option value="mp4">MP4 (Recommended)</ion-select-option> | |
<ion-select-option value="mov">MOV</ion-select-option> | |
<ion-select-option value="webm">WebM</ion-select-option> | |
</ion-select> | |
</ion-item> | |
</ion-list> | |
<div style="margin-top: 20px;"> | |
<ion-button id="applySettingsButton" expand="block" color="success"><ion-icon slot="start" icon="checkmark"></ion-icon>Apply Settings</ion-button> | |
</div> | |
<ion-text color="secondary"><p style="margin-top: 16px;">Demo settings only.</p></ion-text> | |
</ion-card-content> | |
</ion-card> | |
<ion-card id="help-section" class="ion-hide"> | |
<ion-card-header> | |
<ion-card-title><ion-icon icon="help-circle" style="margin-right: 8px;"></ion-icon>Help & Support</ion-card-title> | |
<ion-card-subtitle>Guidance and resources</ion-card-subtitle> | |
</ion-card-header> | |
<ion-card-content> | |
<p>Welcome to Autotutorial.ai! Generate tutorials with ease.</p> | |
<p><strong>Getting Started:</strong></p> | |
<ol> | |
<li>Go to "Generate Tutorial" tab.</li> | |
<li>Enter your tutorial topic.</li> | |
<li>Select desired duration.</li> | |
<li>(Optional) Add API keys for enhanced features.</li> | |
<li>Tap "Generate Tutorial" to start.</li> | |
<li>Monitor "Status Updates" for progress.</li> | |
<li>Preview tutorial in "Tutorial Preview" section.</li> | |
</ol> | |
<p><strong>Tutorial History:</strong></p> | |
<p>Access and manage past tutorials in "Tutorial History" (demo feature).</p> | |
<p><strong>Settings:</strong></p> | |
<p>Customize default preferences in "Settings" (basic demo settings).</p> | |
<p>For further assistance, contact support at <a href="mailto:[email protected]">[email protected]</a>.</p> | |
</ion-card-content> | |
</ion-card> | |
</ion-content> | |
<ion-tabs> | |
<ion-tab-bar slot="bottom"> | |
<ion-tab-button tab="generate" data-section="generate" class="tab-button-link active"> | |
<ion-icon icon="magic"></ion-icon> | |
<ion-label>Generate</ion-label> | |
</ion-tab-button> | |
<ion-tab-button tab="history" data-section="history" class="tab-button-link"> | |
<ion-icon icon="time"></ion-icon> | |
<ion-label>History</ion-label> | |
</ion-tab-button> | |
<ion-tab-button tab="settings" data-section="settings" class="tab-button-link"> | |
<ion-icon icon="settings"></ion-icon> | |
<ion-label>Settings</ion-label> | |
</ion-tab-button> | |
<ion-tab-button tab="help" data-section="help" class="tab-button-link"> | |
<ion-icon icon="help-circle"></ion-icon> | |
<ion-label>Help</ion-label> | |
</ion-tab-button> | |
</ion-tab-bar> | |
</ion-tabs> | |
</ion-app> | |
<!-- Ionic Scripts --> | |
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@latest/dist/ionic/ionic.esm.js"></script> | |
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@latest/dist/ionic/ionic.js"></script> | |
<script> | |
document.addEventListener('DOMContentLoaded', () => { | |
// Tab Navigation | |
const tabButtons = document.querySelectorAll('.tab-button-link'); | |
const sections = ['generate-section', 'history-section', 'settings-section', 'help-section']; | |
tabButtons.forEach(button => { | |
button.addEventListener('click', function(event) { | |
event.preventDefault(); | |
const sectionId = this.getAttribute('data-section') + '-section'; | |
tabButtons.forEach(btn => btn.classList.remove('active')); | |
sections.forEach(sec => document.getElementById(sec).classList.add('ion-hide')); | |
this.classList.add('active'); | |
document.getElementById(sectionId).classList.remove('ion-hide'); | |
if (sectionId === 'generate-section') { | |
document.getElementById('videoOutputSection').classList.add('ion-hide'); | |
document.getElementById('videoOutputSection').classList.remove('opacity-0'); | |
} | |
}); | |
}); | |
document.querySelector('.tab-button-link[data-section="generate"]').classList.add('active'); | |
sections.filter(sec => sec !== 'generate-section').forEach(sec => document.getElementById(sec).classList.add('ion-hide')); | |
// Tutorial History Item Click Simulation | |
const historyItems = document.querySelectorAll('.history-item'); | |
const videoTitleSpan = document.getElementById('videoTitle'); | |
const videoTitleOverlay = document.getElementById('videoTitleOverlay'); | |
const videoOutputSection = document.getElementById('videoOutputSection'); | |
historyItems.forEach(item => { | |
item.addEventListener('click', function() { | |
const title = this.getAttribute('data-video-title'); | |
videoTitleSpan.textContent = title + " (Preview)"; | |
videoTitleOverlay.textContent = title; | |
videoOutputSection.classList.remove('ion-hide'); | |
videoOutputSection.classList.remove('opacity-0'); | |
const statusMessagesDiv = document.getElementById('statusMessages'); | |
statusMessagesDiv.innerHTML = `<p class="ion-text-color-secondary"><ion-icon icon="video" color="primary" style="margin-right: 4px;"></ion-icon>Loading: ${title}</p>`; | |
statusMessagesDiv.scrollTop = statusMessagesDiv.scrollHeight; | |
}); | |
}); | |
// Apply Settings Button Simulation | |
document.getElementById('applySettingsButton').addEventListener('click', function() { | |
const statusMessagesDiv = document.getElementById('statusMessages'); | |
statusMessagesDiv.innerHTML = `<p class="ion-text-color-success"><ion-icon icon="checkmark-circle" color="success" style="margin-right: 4px;"></ion-icon>Settings Applied.</p>`; | |
statusMessagesDiv.scrollTop = statusMessagesDiv.scrollHeight; | |
}); | |
}); | |
document.getElementById('generateButton').addEventListener('click', function() { | |
const topic = document.getElementById('tutorialTopic').value; | |
const duration = document.getElementById('desiredDuration').value; | |
const openaiKey = document.getElementById('openaiApiKey').value; | |
const videoGenKey = document.getElementById('videoGenApiKey').value; | |
const statusMessagesDiv = document.getElementById('statusMessages'); | |
const errorMessageDiv = document.getElementById('errorMessage'); | |
const generateButton = document.getElementById('generateButton'); | |
const resetButton = document.getElementById('resetButton'); | |
const statusSection = document.getElementById('statusSection'); | |
const videoOutputSection = document.getElementById('videoOutputSection'); | |
const videoTitleSpan = document.getElementById('videoTitle'); | |
const videoTitleOverlay = document.getElementById('videoTitleOverlay'); | |
const progressBar = document.getElementById('progressBar'); | |
// Reset status and hide video output | |
statusMessagesDiv.innerHTML = ''; | |
errorMessageDiv.classList.add('ion-hide'); | |
videoOutputSection.classList.add('ion-hide'); | |
videoOutputSection.classList.remove('opacity-0'); | |
resetButton.classList.remove('ion-hide'); | |
generateButton.classList.add('ion-hide'); // Hide generate, show reset | |
resetButton.classList.remove('ion-hide'); | |
progressBar.value = 0; | |
const steps = [ | |
{ message: "Analyzing Topic...", delay: 800 }, | |
{ message: "Generating Script...", delay: 1500 }, | |
{ message: "Creating Voiceover...", delay: 2200 }, | |
{ message: "Synthesizing Scenes...", delay: 2000 }, | |
{ message: "Selecting Visuals...", delay: 1800 }, | |
{ message: "Assembling Video...", delay: 2500 }, | |
{ message: "Adding Effects...", delay: 1500 }, | |
{ message: "Rendering Video...", delay: 3000 } | |
]; | |
let currentStepIndex = 0; | |
function processStep() { | |
if (currentStepIndex < steps.length) { | |
simulateStep(statusMessagesDiv, steps[currentStepIndex].message, steps[currentStepIndex].delay, () => { | |
progressBar.value = ((currentStepIndex + 1) / steps.length); | |
currentStepIndex++; | |
processStep(); | |
}, true); | |
} else { | |
progressBar.value = 1; | |
statusMessagesDiv.innerHTML = '<p class="ion-text-color-success"><ion-icon icon="checkmark-circle" color="success" style="margin-right: 4px;"></ion-icon>Generation Complete!</p>'; | |
videoTitleSpan.textContent = topic + " (" + duration + "min Preview)"; | |
videoTitleOverlay.textContent = topic; | |
videoOutputSection.classList.remove('ion-hide'); | |
setTimeout(() => videoOutputSection.classList.remove('opacity-0'), 50); | |
generateButton.classList.remove('ion-hide'); // Show generate, hide reset | |
generateButton.classList.add('ion-hide'); | |
resetButton.classList.remove('ion-hide'); | |
} | |
} | |
processStep(); | |
}); | |
document.getElementById('resetButton').addEventListener('click', function() { | |
const statusMessagesDiv = document.getElementById('statusMessages'); | |
const errorMessageDiv = document.getElementById('errorMessage'); | |
const generateButton = document.getElementById('generateButton'); | |
const resetButton = document.getElementById('resetButton'); | |
const videoOutputSection = document.getElementById('videoOutputSection'); | |
const progressBar = document.getElementById('progressBar'); | |
statusMessagesDiv.innerHTML = '<p class="ion-text-color-secondary"><ion-icon icon="checkmark-circle" color="success" style="margin-right: 4px;"></ion-icon>Ready to generate. Click "Generate Tutorial".</p>'; | |
errorMessageDiv.classList.add('ion-hide'); | |
videoOutputSection.classList.add('ion-hide'); | |
videoOutputSection.classList.remove('opacity-0'); | |
generateButton.classList.remove('ion-hide'); // Show generate, hide reset | |
generateButton.classList.remove('ion-hide'); | |
resetButton.classList.add('ion-hide'); | |
progressBar.value = 0; | |
}); | |
function simulateStep(statusDiv, message, delay, callback, isSuccess = true) { | |
setTimeout(function() { | |
const messageElement = document.createElement('p'); | |
if (isSuccess) { | |
messageElement.classList.add('ion-text-color-secondary'); | |
messageElement.innerHTML = `<ion-icon icon="radio-button-on" color="primary" style="margin-right: 4px;"></ion-icon> ${message}`; | |
} else { | |
messageElement.classList.add('ion-text-color-danger'); | |
messageElement.innerHTML = `<ion-icon icon="close-circle" color="danger" style="margin-right: 4px;"></ion-icon> ${message}`; | |
document.getElementById('errorMessage').textContent = "Error generating tutorial. Check API keys."; | |
document.getElementById('errorMessage').classList.remove('ion-hide'); | |
generateButton.classList.remove('ion-hide'); // Show generate, hide reset | |
generateButton.classList.add('ion-hide'); | |
resetButton.classList.remove('ion-hide'); | |
document.getElementById('progressBar').value = 0; | |
} | |
statusDiv.appendChild(messageElement); | |
statusDiv.scrollTop = statusDiv.scrollHeight; | |
if (callback) callback(); | |
}, delay + Math.random() * 500); | |
} | |
</script> | |
</body> | |
</html> |