Spaces:
Build error
Build error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>TranscriptAI | YouTube Analysis Tool</title> | |
| <!-- Bootstrap CSS --> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <!-- Font Awesome --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> | |
| <!-- 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@300;400;500;600;700&family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --primary: #4361ee; | |
| --primary-dark: #3a56d4; | |
| --secondary: #3f37c9; | |
| --accent: #f72585; | |
| --accent-light: #ff8fa3; | |
| --success: #4cc9f0; | |
| --warning: #f8961e; | |
| --danger: #f94144; | |
| --light: #f8f9fa; | |
| --dark: #212529; | |
| --gray-100: #f8f9fa; | |
| --gray-200: #e9ecef; | |
| --gray-300: #dee2e6; | |
| --gray-400: #ced4da; | |
| --gray-500: #adb5bd; | |
| --gray-600: #6c757d; | |
| --gray-700: #495057; | |
| --gray-800: #343a40; | |
| --gray-900: #212529; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #f7f9fc; | |
| color: var(--gray-800); | |
| line-height: 1.6; | |
| } | |
| h1, h2, h3, h4, h5, h6, .display-1, .display-2, .display-3, .display-4, .display-5, .display-6 { | |
| font-family: 'Poppins', sans-serif; | |
| font-weight: 600; | |
| } | |
| .navbar-brand { | |
| font-family: 'Poppins', sans-serif; | |
| font-weight: 700; | |
| } | |
| .card { | |
| border-radius: 12px; | |
| border: none; | |
| box-shadow: 0 2px 15px rgba(0, 0, 0, 0.08); | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| } | |
| .card:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); | |
| } | |
| .card-header { | |
| background-color: #fff; | |
| border-bottom: 1px solid var(--gray-200); | |
| font-weight: 600; | |
| padding: 1.25rem 1.5rem; | |
| } | |
| .card-body { | |
| padding: 1.5rem; | |
| } | |
| .btn { | |
| border-radius: 8px; | |
| font-weight: 500; | |
| padding: 0.6rem 1.2rem; | |
| transition: all 0.2s; | |
| } | |
| .btn-primary { | |
| background-color: var(--primary); | |
| border-color: var(--primary); | |
| } | |
| .btn-primary:hover { | |
| background-color: var(--primary-dark); | |
| border-color: var(--primary-dark); | |
| } | |
| .btn-outline-primary { | |
| color: var(--primary); | |
| border-color: var(--primary); | |
| } | |
| .btn-outline-primary:hover { | |
| background-color: var(--primary); | |
| border-color: var(--primary); | |
| } | |
| .btn-success { | |
| background-color: var(--success); | |
| border-color: var(--success); | |
| } | |
| .form-control { | |
| border-radius: 8px; | |
| padding: 0.75rem 1rem; | |
| border: 1px solid var(--gray-300); | |
| font-size: 1rem; | |
| } | |
| .form-control:focus { | |
| box-shadow: 0 0 0 0.25rem rgba(67, 97, 238, 0.15); | |
| border-color: var(--primary); | |
| } | |
| .form-label { | |
| font-weight: 500; | |
| margin-bottom: 0.5rem; | |
| color: var(--gray-700); | |
| } | |
| .form-text { | |
| color: var(--gray-600); | |
| } | |
| .loading-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(15, 23, 42, 0.8); | |
| backdrop-filter: blur(8px); | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 1050; | |
| color: white; | |
| display: none; | |
| } | |
| .progress-container { | |
| width: 80%; | |
| max-width: 600px; | |
| margin-top: 20px; | |
| } | |
| .progress { | |
| height: 10px; | |
| border-radius: 10px; | |
| background-color: rgba(255, 255, 255, 0.2); | |
| overflow: hidden; | |
| } | |
| .progress-bar { | |
| background-image: linear-gradient(to right, var(--primary), var(--accent)); | |
| height: 100%; | |
| border-radius: 10px; | |
| } | |
| .log-container { | |
| width: 80%; | |
| max-width: 600px; | |
| height: 200px; | |
| overflow-y: auto; | |
| background-color: rgba(15, 23, 42, 0.5); | |
| border-radius: 12px; | |
| padding: 15px; | |
| margin-top: 20px; | |
| font-family: 'Courier New', monospace; | |
| color: #e2e8f0; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .log-container div { | |
| padding: 3px 0; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.05); | |
| } | |
| .log-container div:last-child { | |
| border-bottom: none; | |
| } | |
| .logo { | |
| font-size: 2.5rem; | |
| background: linear-gradient(135deg, #f72585 0%, #4361ee 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| margin-right: 15px; | |
| } | |
| .nav-tabs { | |
| border-bottom: none; | |
| margin-bottom: 20px; | |
| gap: 10px; | |
| } | |
| .nav-tabs .nav-item { | |
| margin-bottom: 0; | |
| } | |
| .nav-tabs .nav-link { | |
| border: none; | |
| border-radius: 8px; | |
| padding: 0.75rem 1.5rem; | |
| color: var(--gray-600); | |
| font-weight: 500; | |
| transition: all 0.2s; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .nav-tabs .nav-link:hover { | |
| color: var(--primary); | |
| background-color: rgba(67, 97, 238, 0.05); | |
| } | |
| .nav-tabs .nav-link.active { | |
| color: var(--primary); | |
| background-color: rgba(67, 97, 238, 0.1); | |
| border-bottom: none; | |
| font-weight: 600; | |
| } | |
| .nav-tabs .nav-link.active i { | |
| color: var(--primary); | |
| } | |
| .tab-content { | |
| background-color: #fff; | |
| border-radius: 12px; | |
| box-shadow: 0 2px 15px rgba(0, 0, 0, 0.08); | |
| padding: 0; | |
| border: none; | |
| } | |
| .recent-items { | |
| max-height: 500px; | |
| overflow-y: auto; | |
| } | |
| #preview-container { | |
| max-height: 700px; | |
| overflow-y: auto; | |
| padding: 25px; | |
| border-radius: 12px; | |
| background-color: #fff; | |
| font-family: 'Inter', sans-serif; | |
| line-height: 1.7; | |
| } | |
| #preview-container h1, #preview-container h2 { | |
| font-weight: 700; | |
| color: var(--gray-800); | |
| margin-top: 1.5em; | |
| margin-bottom: 0.75em; | |
| } | |
| #preview-container h1 { | |
| font-size: 2rem; | |
| border-bottom: 2px solid var(--gray-200); | |
| padding-bottom: 0.5rem; | |
| } | |
| #preview-container h2 { | |
| font-size: 1.5rem; | |
| color: var(--primary); | |
| } | |
| #preview-container p { | |
| margin-bottom: 1rem; | |
| } | |
| #preview-container ul, #preview-container ol { | |
| padding-left: 1.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| #preview-container blockquote { | |
| border-left: 4px solid var(--primary); | |
| padding-left: 1rem; | |
| margin-left: 0; | |
| color: var(--gray-700); | |
| font-style: italic; | |
| } | |
| .table th { | |
| background-color: rgba(67, 97, 238, 0.05); | |
| color: var(--gray-700); | |
| font-weight: 600; | |
| } | |
| .table-hover tbody tr:hover { | |
| background-color: rgba(67, 97, 238, 0.05); | |
| } | |
| .badge { | |
| padding: 0.4em 0.8em; | |
| font-weight: 500; | |
| border-radius: 6px; | |
| } | |
| .badge-hindi { | |
| background-color: rgba(247, 37, 133, 0.1); | |
| color: var(--accent); | |
| } | |
| .badge-english { | |
| background-color: rgba(67, 97, 238, 0.1); | |
| color: var(--primary); | |
| } | |
| .btn-action { | |
| padding: 0.4rem 0.75rem; | |
| border-radius: 6px; | |
| font-size: 0.875rem; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px; | |
| } | |
| .btn-icon { | |
| width: 36px; | |
| height: 36px; | |
| padding: 0; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| border-radius: 50%; | |
| } | |
| .features-icon { | |
| width: 40px; | |
| height: 40px; | |
| background-color: rgba(67, 97, 238, 0.1); | |
| color: var(--primary); | |
| border-radius: 12px; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| margin-right: 15px; | |
| font-size: 1.25rem; | |
| } | |
| .feature-item { | |
| display: flex; | |
| align-items: flex-start; | |
| margin-bottom: 1rem; | |
| } | |
| .feature-content { | |
| flex: 1; | |
| } | |
| .feature-content h5 { | |
| margin-bottom: 0.25rem; | |
| font-weight: 600; | |
| } | |
| .help-card { | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .help-card .card-body { | |
| flex: 1; | |
| } | |
| .banner { | |
| background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); | |
| color: white; | |
| padding: 2rem 0; | |
| border-radius: 16px; | |
| margin-bottom: 2rem; | |
| } | |
| .banner h2 { | |
| font-weight: 700; | |
| margin-bottom: 1rem; | |
| } | |
| .banner p { | |
| opacity: 0.9; | |
| font-weight: 300; | |
| font-size: 1.1rem; | |
| max-width: 600px; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| transform: scale(1); | |
| } | |
| 50% { | |
| transform: scale(1.05); | |
| } | |
| 100% { | |
| transform: scale(1); | |
| } | |
| } | |
| .pulse-icon { | |
| animation: pulse 1.5s infinite; | |
| } | |
| /* For mobile screens */ | |
| @media (max-width: 767.98px) { | |
| .banner { | |
| text-align: center; | |
| padding: 1.5rem; | |
| } | |
| .banner p { | |
| margin-left: auto; | |
| margin-right: auto; | |
| } | |
| .nav-tabs { | |
| overflow-x: auto; | |
| flex-wrap: nowrap; | |
| padding-bottom: 5px; | |
| } | |
| .nav-tabs .nav-link { | |
| white-space: nowrap; | |
| padding: 0.5rem 1rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Navbar --> | |
| <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm"> | |
| <div class="container"> | |
| <a class="navbar-brand d-flex align-items-center" href="/"> | |
| <i class="fab fa-youtube logo"></i> | |
| <span>TranscriptAI</span> | |
| </a> | |
| <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | |
| <span class="navbar-toggler-icon"></span> | |
| </button> | |
| <div class="collapse navbar-collapse" id="navbarNav"> | |
| <ul class="navbar-nav ms-auto"> | |
| <li class="nav-item"> | |
| <a class="nav-link active" href="#"><i class="fas fa-home me-1"></i> Home</a> | |
| </li> | |
| <li class="nav-item"> | |
| <a class="nav-link" href="#"><i class="fas fa-info-circle me-1"></i> About</a> | |
| </li> | |
| <li class="nav-item"> | |
| <a class="nav-link" href="#"><i class="fas fa-question-circle me-1"></i> Help</a> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Loading Overlay --> | |
| <div class="loading-overlay" id="loadingOverlay"> | |
| <div class="text-center mb-4"> | |
| <i class="fas fa-spinner fa-spin fa-3x mb-3 pulse-icon"></i> | |
| <h3 id="statusTitle" class="mb-3">Processing Your Video...</h3> | |
| <p class="text-light opacity-75">This may take a few minutes depending on video length</p> | |
| </div> | |
| <div class="progress-container"> | |
| <div class="progress"> | |
| <div id="progressBar" class="progress-bar" | |
| role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> | |
| </div> | |
| <div class="d-flex justify-content-between mt-2"> | |
| <small class="text-light opacity-75">Processing...</small> | |
| <small id="progressText" class="text-light opacity-75">0%</small> | |
| </div> | |
| </div> | |
| <!-- Success Alert (Hidden by default) --> | |
| <div class="alert alert-success alert-dismissible fade show mt-4 w-75 mx-auto" role="alert" id="successAlert" style="display: none;"> | |
| <div class="d-flex"> | |
| <i class="fas fa-check-circle me-2 mt-1"></i> | |
| <div> | |
| <strong>Success!</strong> <span id="successMessage">The video has been processed successfully.</span> | |
| </div> | |
| </div> | |
| <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | |
| </div> | |
| <!-- Error Alert (Hidden by default) --> | |
| <div class="alert alert-danger alert-dismissible fade show mt-4 w-75 mx-auto" role="alert" id="errorAlert" style="display: none;"> | |
| <div class="d-flex"> | |
| <i class="fas fa-exclamation-circle me-2 mt-1"></i> | |
| <div> | |
| <strong>Error!</strong> <span id="errorMessage">An error occurred during processing.</span> | |
| </div> | |
| </div> | |
| <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | |
| </div> | |
| <div class="log-container" id="logContainer"> | |
| <!-- Log messages will be added here dynamically --> | |
| </div> | |
| </div> | |
| <div class="container py-5"> | |
| <!-- Banner --> | |
| <div class="banner shadow-lg mb-5"> | |
| <div class="container-fluid px-4"> | |
| <div class="row align-items-center"> | |
| <div class="col-md-8"> | |
| <h2 class="display-5">Intelligent YouTube Transcript Analysis</h2> | |
| <p>Transform video content into insightful summaries with AI-powered analysis in English and Hindi. Extract key points, topics, and quotes in seconds.</p> | |
| </div> | |
| <div class="col-md-4 text-md-end text-center mt-3 mt-md-0"> | |
| <i class="fas fa-brain fa-5x opacity-50"></i> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="row"> | |
| <div class="col-12 mb-4"> | |
| <!-- Navigation Tabs --> | |
| <ul class="nav nav-tabs" id="myTab" role="tablist"> | |
| <li class="nav-item" role="presentation"> | |
| <button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home" type="button" role="tab" aria-controls="home" aria-selected="true"> | |
| <i class="fas fa-play-circle"></i> Process Video | |
| </button> | |
| </li> | |
| <li class="nav-item" role="presentation"> | |
| <button class="nav-link" id="results-tab" data-bs-toggle="tab" data-bs-target="#results" type="button" role="tab" aria-controls="results" aria-selected="false"> | |
| <i class="fas fa-list-ul"></i> Recent Results | |
| </button> | |
| </li> | |
| <li class="nav-item" role="presentation"> | |
| <button class="nav-link" id="preview-tab" data-bs-toggle="tab" data-bs-target="#preview" type="button" role="tab" aria-controls="preview" aria-selected="false"> | |
| <i class="fas fa-file-alt"></i> Preview Content | |
| </button> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="tab-content" id="myTabContent"> | |
| <!-- Home Tab (Process Form) --> | |
| <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab"> | |
| <div class="row"> | |
| <div class="col-lg-8"> | |
| <div class="card mb-4"> | |
| <div class="card-header d-flex align-items-center"> | |
| <i class="fas fa-link text-primary me-2"></i> | |
| <span>Enter Video Details</span> | |
| </div> | |
| <div class="card-body"> | |
| <form id="processingForm"> | |
| <div class="mb-4"> | |
| <label for="youtubeUrl" class="form-label">YouTube Video URL</label> | |
| <div class="input-group mb-2"> | |
| <span class="input-group-text"><i class="fab fa-youtube text-danger"></i></span> | |
| <input type="url" class="form-control" id="youtubeUrl" name="youtube_url" | |
| placeholder="https://www.youtube.com/watch?v=..." required> | |
| </div> | |
| <div class="form-text">Enter the full URL of any YouTube video you want to analyze</div> | |
| </div> | |
| <div class="mb-4"> | |
| <label for="apiKey" class="form-label"></label> | |
| <div class="input-group mb-2"> | |
| <span class="input-group-text"><i class="fas fa-key"></i></span> | |
| <input type="password" class="form-control" id="apiKey" name="api_key" | |
| value="{{ api_key }}" required> | |
| </div> | |
| </div> | |
| <div class="d-grid"> | |
| <button type="submit" class="btn btn-primary btn-lg"> | |
| <i class="fas fa-rocket me-2"></i> Process Video | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="col-lg-4"> | |
| <div class="card help-card"> | |
| <div class="card-header d-flex align-items-center"> | |
| <i class="fas fa-info-circle text-primary me-2"></i> | |
| <span>How It Works</span> | |
| </div> | |
| <div class="card-body"> | |
| <div class="feature-item"> | |
| <div class="features-icon"> | |
| <i class="fas fa-download"></i> | |
| </div> | |
| <div class="feature-content"> | |
| <h5>1. Fetch Transcript</h5> | |
| <p class="text-muted">System automatically downloads video subtitles</p> | |
| </div> | |
| </div> | |
| <div class="feature-item"> | |
| <div class="features-icon"> | |
| <i class="fas fa-robot"></i> | |
| </div> | |
| <div class="feature-content"> | |
| <h5>2. AI Processing</h5> | |
| <p class="text-muted"> AI analyzes and extracts key information</p> | |
| </div> | |
| </div> | |
| <div class="feature-item"> | |
| <div class="features-icon"> | |
| <i class="fas fa-language"></i> | |
| </div> | |
| <div class="feature-content"> | |
| <h5>3. Bilingual Output</h5> | |
| <p class="text-muted">Get results in both English and Hindi</p> | |
| </div> | |
| </div> | |
| <div class="feature-item"> | |
| <div class="features-icon"> | |
| <i class="fas fa-file-export"></i> | |
| </div> | |
| <div class="feature-content"> | |
| <h5>4. Export & Share</h5> | |
| <p class="text-muted">Download or preview your analysis results</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- What You Get Section --> | |
| <div class="row mt-4"> | |
| <div class="col-12"> | |
| <div class="card"> | |
| <div class="card-header d-flex align-items-center"> | |
| <i class="fas fa-lightbulb text-primary me-2"></i> | |
| <span>What You'll Get</span> | |
| </div> | |
| <div class="card-body"> | |
| <div class="row g-4"> | |
| <div class="col-md-3 col-sm-6"> | |
| <div class="text-center"> | |
| <div class="mb-3"> | |
| <i class="fas fa-list-ul text-primary fa-2x"></i> | |
| </div> | |
| <h5>Key Points</h5> | |
| <p class="text-muted small">Concise summary of main ideas</p> | |
| </div> | |
| </div> | |
| <div class="col-md-3 col-sm-6"> | |
| <div class="text-center"> | |
| <div class="mb-3"> | |
| <i class="fas fa-sitemap text-primary fa-2x"></i> | |
| </div> | |
| <h5>Topics Covered</h5> | |
| <p class="text-muted small">Major themes and subjects</p> | |
| </div> | |
| </div> | |
| <div class="col-md-3 col-sm-6"> | |
| <div class="text-center"> | |
| <div class="mb-3"> | |
| <i class="fas fa-quote-right text-primary fa-2x"></i> | |
| </div> | |
| <h5>Notable Quotes</h5> | |
| <p class="text-muted small">Important statements highlighted</p> | |
| </div> | |
| </div> | |
| <div class="col-md-3 col-sm-6"> | |
| <div class="text-center"> | |
| <div class="mb-3"> | |
| <i class="fas fa-align-left text-primary fa-2x"></i> | |
| </div> | |
| <h5>Full Transcript</h5> | |
| <p class="text-muted small">Complete formatted content</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Results Tab --> | |
| <div class="tab-pane fade" id="results" role="tabpanel" aria-labelledby="results-tab"> | |
| <div class="card"> | |
| <div class="card-header d-flex justify-content-between align-items-center"> | |
| <div class="d-flex align-items-center"> | |
| <i class="fas fa-history text-primary me-2"></i> | |
| <span>Recent Analyses</span> | |
| </div> | |
| <button id="refreshResultsBtn" class="btn btn-sm btn-outline-primary"> | |
| <i class="fas fa-sync-alt me-1"></i> Refresh | |
| </button> | |
| </div> | |
| <div class="card-body p-0"> | |
| <div class="table-responsive recent-items"> | |
| <table class="table table-hover mb-0"> | |
| <thead> | |
| <tr> | |
| <th class="px-3 py-3">Filename</th> | |
| <th class="px-3 py-3">Language</th> | |
| <th class="px-3 py-3">Created</th> | |
| <th class="px-3 py-3 text-end">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="resultsList"> | |
| <!-- Results will be loaded here dynamically --> | |
| <tr> | |
| <td colspan="4" class="text-center p-4"> | |
| <div class="spinner-border text-primary" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| <p class="mt-2 text-muted">Loading results...</p> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Preview Tab --> | |
| <div class="tab-pane fade" id="preview" role="tabpanel" aria-labelledby="preview-tab"> | |
| <div class="card"> | |
| <div class="card-header d-flex align-items-center mb-0"> | |
| <i class="fas fa-file-alt text-primary me-2"></i> | |
| <h5 id="previewTitle" class="m-0">No content selected</h5> | |
| </div> | |
| <div class="card-body"> | |
| <div id="preview-container" class="markdown-body"> | |
| <div class="text-center p-5 text-muted"> | |
| <i class="far fa-file-alt fa-4x mb-3 opacity-25"></i> | |
| <p>Select a result file from the "Recent Results" tab to preview content here</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="bg-dark text-light py-4 mt-5"> | |
| <div class="container"> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <h5 class="mb-3">TranscriptAI</h5> | |
| </div> | |
| <div class="col-md-6 text-md-end"> | |
| <ul class="list-inline mb-0"> | |
| <li class="list-inline-item"><a href="#" class="text-muted text-decoration-none">Terms</a></li> | |
| <li class="list-inline-item"><a href="#" class="text-muted text-decoration-none">Privacy</a></li> | |
| <li class="list-inline-item"><a href="#" class="text-muted text-decoration-none">Contact</a></li> | |
| </ul> | |
| <p class="mt-2 mb-0 text-muted small">© 2025 TranscriptAI. All rights reserved.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- Bootstrap and jQuery JS | |
| <!-- Bootstrap and jQuery JS --> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <script> | |
| // Set up polling for status updates | |
| let statusInterval; | |
| let isProcessing = false; | |
| // Initialize UI elements | |
| $(document).ready(function() { | |
| // Load results list on page load | |
| loadResultsList(); | |
| // Form submission handler | |
| $('#processingForm').on('submit', function(e) { | |
| e.preventDefault(); | |
| startProcessing(); | |
| }); | |
| // Refresh results button | |
| $('#refreshResultsBtn').on('click', function() { | |
| loadResultsList(); | |
| }); | |
| }); | |
| // Start processing a YouTube URL | |
| function startProcessing() { | |
| const youtubeUrl = $('#youtubeUrl').val().trim(); | |
| const apiKey = $('#apiKey').val().trim(); | |
| if (!youtubeUrl) { | |
| alert('Please enter a valid YouTube URL'); | |
| return; | |
| } | |
| // Show loading overlay | |
| $('#loadingOverlay').fadeIn(); | |
| $('#logContainer').empty(); | |
| $('#progressBar').css('width', '0%'); | |
| $('#progressText').text('0%'); | |
| $('#statusTitle').text('Processing Your Video...'); | |
| $('#successAlert').hide(); | |
| $('#errorAlert').hide(); | |
| // Submit request to server | |
| $.ajax({ | |
| url: '/process', | |
| method: 'POST', | |
| data: { | |
| youtube_url: youtubeUrl, | |
| api_key: apiKey | |
| }, | |
| success: function(response) { | |
| if (response.success) { | |
| // Start polling for status updates | |
| isProcessing = true; | |
| statusInterval = setInterval(checkStatus, 1000); | |
| } else { | |
| // Show error | |
| $('#errorMessage').text(response.error); | |
| $('#errorAlert').fadeIn(); | |
| setTimeout(function() { | |
| $('#loadingOverlay').fadeOut(); | |
| }, 3000); | |
| } | |
| }, | |
| error: function() { | |
| $('#errorMessage').text('Server error. Please try again later.'); | |
| $('#errorAlert').fadeIn(); | |
| setTimeout(function() { | |
| $('#loadingOverlay').fadeOut(); | |
| }, 3000); | |
| } | |
| }); | |
| } | |
| // Check processing status | |
| function checkStatus() { | |
| $.ajax({ | |
| url: '/status', | |
| method: 'GET', | |
| success: function(status) { | |
| // Update progress bar | |
| $('#progressBar').css('width', status.progress + '%'); | |
| $('#progressText').text(status.progress + '%'); | |
| $('#statusTitle').text(status.current_step); | |
| // Update log | |
| updateLog(status.log); | |
| // Check if processing is complete | |
| if (!status.is_processing && isProcessing) { | |
| isProcessing = false; | |
| clearInterval(statusInterval); | |
| // Show success message | |
| $('#successMessage').text('Processing complete! View your results in the "Recent Results" tab.'); | |
| $('#successAlert').fadeIn(); | |
| // Refresh results list | |
| loadResultsList(); | |
| // Switch to results tab after 3 seconds | |
| setTimeout(function() { | |
| $('#results-tab').tab('show'); | |
| $('#loadingOverlay').fadeOut(); | |
| }, 3000); | |
| } | |
| }, | |
| error: function() { | |
| clearInterval(statusInterval); | |
| $('#errorMessage').text('Error checking status. Please try again.'); | |
| $('#errorAlert').fadeIn(); | |
| setTimeout(function() { | |
| $('#loadingOverlay').fadeOut(); | |
| }, 3000); | |
| } | |
| }); | |
| } | |
| // Update log display | |
| function updateLog(logEntries) { | |
| const logContainer = $('#logContainer'); | |
| logContainer.empty(); | |
| logEntries.forEach(function(entry) { | |
| logContainer.append(`<div><span class="text-info">[${entry.time}]</span> ${entry.message}</div>`); | |
| }); | |
| // Auto-scroll to bottom | |
| logContainer.scrollTop(logContainer[0].scrollHeight); | |
| } | |
| // Load results list | |
| function loadResultsList() { | |
| $.ajax({ | |
| url: '/list_results', | |
| method: 'GET', | |
| success: function(files) { | |
| const resultsList = $('#resultsList'); | |
| resultsList.empty(); | |
| if (files.length === 0) { | |
| resultsList.append(` | |
| <tr> | |
| <td colspan="4" class="text-center p-4"> | |
| <i class="far fa-file-alt fa-3x mb-3 text-muted opacity-25"></i> | |
| <p class="text-muted">No results found. Process a video to get started.</p> | |
| </td> | |
| </tr> | |
| `); | |
| return; | |
| } | |
| files.forEach(function(file) { | |
| const date = new Date(file.created * 1000); | |
| const formattedDate = date.toLocaleString(); | |
| const isHindi = file.is_hindi; | |
| resultsList.append(` | |
| <tr> | |
| <td class="px-3 py-3"> | |
| <div class="d-flex align-items-center"> | |
| <i class="far fa-file-alt text-${isHindi ? 'danger' : 'primary'} me-2"></i> | |
| <span>${file.filename}</span> | |
| </div> | |
| </td> | |
| <td class="px-3 py-3"> | |
| <span class="badge ${isHindi ? 'badge-hindi' : 'badge-english'}"> | |
| ${isHindi ? 'Hindi' : 'English'} | |
| </span> | |
| </td> | |
| <td class="px-3 py-3">${formattedDate}</td> | |
| <td class="px-3 py-3 text-end"> | |
| <button class="btn btn-sm btn-outline-primary btn-action view-result" | |
| data-filename="${file.filename}"> | |
| <i class="fas fa-eye"></i> View | |
| </button> | |
| <a href="/results/${file.filename}" class="btn btn-sm btn-outline-success btn-action" download> | |
| <i class="fas fa-download"></i> Download | |
| </a> | |
| </td> | |
| </tr> | |
| `); | |
| }); | |
| // Attach event handlers to view buttons | |
| $('.view-result').on('click', function() { | |
| const filename = $(this).data('filename'); | |
| viewResult(filename); | |
| }); | |
| }, | |
| error: function() { | |
| const resultsList = $('#resultsList'); | |
| resultsList.empty(); | |
| resultsList.append(` | |
| <tr> | |
| <td colspan="4" class="text-center p-4"> | |
| <i class="fas fa-exclamation-circle fa-3x mb-3 text-danger opacity-25"></i> | |
| <p class="text-danger">Error loading results. Please try again.</p> | |
| </td> | |
| </tr> | |
| `); | |
| } | |
| }); | |
| } | |
| // View a result file | |
| function viewResult(filename) { | |
| // Switch to preview tab | |
| $('#preview-tab').tab('show'); | |
| // Set preview title | |
| $('#previewTitle').text(filename); | |
| // Show loading state | |
| $('#preview-container').html(` | |
| <div class="text-center p-5"> | |
| <div class="spinner-border text-primary" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| <p class="mt-3 text-muted">Loading content...</p> | |
| </div> | |
| `); | |
| // Fetch and display the content | |
| $.ajax({ | |
| url: `/results/${filename}`, | |
| method: 'GET', | |
| success: function(content) { | |
| // Convert markdown to HTML | |
| const htmlContent = marked.parse(content); | |
| $('#preview-container').html(htmlContent); | |
| }, | |
| error: function() { | |
| $('#preview-container').html(` | |
| <div class="text-center p-5 text-danger"> | |
| <i class="fas fa-exclamation-circle fa-4x mb-3 opacity-25"></i> | |
| <p>Error loading content. Please try again.</p> | |
| </div> | |
| `); | |
| } | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> |