Spaces:
Running
Running
Let’s build a podcast commenting page where a podcast plays on the top and users can comment. What do you think about it and it shows up as little tags or dots on the play bar make a prototype - Initial Deployment
af0033d verified | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Podcast with Interactive Comments</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .comment-marker { | |
| position: absolute; | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| transform: translateX(-50%); | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| } | |
| .comment-marker:hover { | |
| transform: translateX(-50%) scale(1.5); | |
| } | |
| .progress-bar-container { | |
| position: relative; | |
| height: 6px; | |
| } | |
| .progress-bar { | |
| height: 100%; | |
| background-color: #e5e7eb; | |
| border-radius: 3px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .progress { | |
| height: 100%; | |
| background-color: #3b82f6; | |
| width: 0%; | |
| border-radius: 3px; | |
| } | |
| .comment-tooltip { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background-color: white; | |
| padding: 8px 12px; | |
| border-radius: 6px; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| z-index: 10; | |
| min-width: 200px; | |
| display: none; | |
| } | |
| .comment-tooltip::after { | |
| content: ''; | |
| position: absolute; | |
| top: 100%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| border-width: 8px; | |
| border-style: solid; | |
| border-color: white transparent transparent transparent; | |
| } | |
| .timestamp-btn { | |
| background-color: #f3f4f6; | |
| border-radius: 4px; | |
| padding: 2px 6px; | |
| font-size: 0.75rem; | |
| cursor: pointer; | |
| } | |
| .comment-input { | |
| transition: all 0.3s ease; | |
| } | |
| .comment-input:focus-within { | |
| box-shadow: 0 0 0 2px #3b82f6; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-4xl"> | |
| <!-- Podcast Header --> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-24 h-24 rounded-lg overflow-hidden shadow-md mr-4"> | |
| <img src="https://images.unsplash.com/photo-1478737270239-2f02b77fc618?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=400&q=80" alt="Podcast Cover" class="w-full h-full object-cover"> | |
| </div> | |
| <div> | |
| <h1 class="text-2xl font-bold text-gray-800">The Future of AI in Everyday Life</h1> | |
| <p class="text-gray-600">Tech Talk Podcast • Episode 42</p> | |
| <div class="flex items-center mt-1"> | |
| <span class="text-sm text-gray-500 mr-3"><i class="fas fa-calendar-alt mr-1"></i> June 15, 2023</span> | |
| <span class="text-sm text-gray-500"><i class="fas fa-clock mr-1"></i> 45 min</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Audio Player --> | |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <div class="progress-bar-container mb-2"> | |
| <div class="progress-bar"> | |
| <div class="progress" id="progress"></div> | |
| <!-- Comment markers will be added here dynamically --> | |
| </div> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center"> | |
| <button id="playBtn" class="w-10 h-10 bg-blue-500 text-white rounded-full flex items-center justify-center mr-4 hover:bg-blue-600 transition"> | |
| <i class="fas fa-play"></i> | |
| </button> | |
| <span id="currentTime" class="text-gray-600">0:00</span> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button class="text-gray-500 hover:text-blue-500 transition"> | |
| <i class="fas fa-step-backward"></i> | |
| </button> | |
| <button class="text-gray-500 hover:text-blue-500 transition"> | |
| <i class="fas fa-step-forward"></i> | |
| </button> | |
| <button class="text-gray-500 hover:text-blue-500 transition"> | |
| <i class="fas fa-volume-up"></i> | |
| </button> | |
| <div class="w-24"> | |
| <input type="range" min="0" max="100" value="80" class="w-full"> | |
| </div> | |
| </div> | |
| </div> | |
| <audio id="audioPlayer" src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"></audio> | |
| </div> | |
| <!-- Comment Input --> | |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8 comment-input"> | |
| <h3 class="text-lg font-semibold text-gray-800 mb-4">Add your comment</h3> | |
| <div class="flex items-start space-x-3"> | |
| <div class="w-10 h-10 rounded-full overflow-hidden bg-gray-200"> | |
| <img src="https://randomuser.me/api/portraits/women/44.jpg" alt="User" class="w-full h-full object-cover"> | |
| </div> | |
| <div class="flex-1"> | |
| <textarea id="commentText" class="w-full border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" rows="3" placeholder="What are your thoughts on this part of the podcast?"></textarea> | |
| <div class="flex justify-between items-center mt-3"> | |
| <button id="addTimestampBtn" class="flex items-center text-sm text-blue-500 hover:text-blue-700"> | |
| <i class="fas fa-clock mr-1"></i> Add at current time | |
| </button> | |
| <button id="submitCommentBtn" class="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition">Post Comment</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Comments Section --> | |
| <div class="bg-white rounded-xl shadow-md p-6"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h3 class="text-lg font-semibold text-gray-800">Comments</h3> | |
| <div class="flex items-center"> | |
| <span class="text-sm text-gray-500 mr-2">Sort by:</span> | |
| <select class="border border-gray-300 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"> | |
| <option>Newest</option> | |
| <option>Oldest</option> | |
| <option>Timeline</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div id="commentsContainer"> | |
| <!-- Sample comments --> | |
| <div class="comment mb-6 pb-6 border-b border-gray-100" data-timestamp="45"> | |
| <div class="flex items-start space-x-3"> | |
| <div class="w-10 h-10 rounded-full overflow-hidden bg-gray-200"> | |
| <img src="https://randomuser.me/api/portraits/men/32.jpg" alt="User" class="w-full h-full object-cover"> | |
| </div> | |
| <div class="flex-1"> | |
| <div class="flex items-center justify-between mb-1"> | |
| <span class="font-semibold text-gray-800">Michael Johnson</span> | |
| <button class="timestamp-btn flex items-center text-xs text-gray-500 hover:text-blue-500"> | |
| <i class="fas fa-clock mr-1"></i> 0:45 | |
| </button> | |
| </div> | |
| <p class="text-gray-700 mb-2">This point about AI in healthcare is fascinating. I work in the field and we're already seeing some of these applications in diagnostic imaging.</p> | |
| <div class="flex items-center text-sm text-gray-500 space-x-4"> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-up mr-1"></i> 12</button> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-down mr-1"></i> 2</button> | |
| <button class="hover:text-blue-500"><i class="far fa-comment mr-1"></i> Reply</button> | |
| <span>2 hours ago</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="comment mb-6 pb-6 border-b border-gray-100" data-timestamp="210"> | |
| <div class="flex items-start space-x-3"> | |
| <div class="w-10 h-10 rounded-full overflow-hidden bg-gray-200"> | |
| <img src="https://randomuser.me/api/portraits/women/65.jpg" alt="User" class="w-full h-full object-cover"> | |
| </div> | |
| <div class="flex-1"> | |
| <div class="flex items-center justify-between mb-1"> | |
| <span class="font-semibold text-gray-800">Sarah Williams</span> | |
| <button class="timestamp-btn flex items-center text-xs text-gray-500 hover:text-blue-500"> | |
| <i class="fas fa-clock mr-1"></i> 3:30 | |
| </button> | |
| </div> | |
| <p class="text-gray-700 mb-2">I disagree with the host's perspective on AI taking creative jobs. Tools are just tools - they enhance human creativity rather than replace it.</p> | |
| <div class="flex items-center text-sm text-gray-500 space-x-4"> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-up mr-1"></i> 8</button> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-down mr-1"></i> 0</button> | |
| <button class="hover:text-blue-500"><i class="far fa-comment mr-1"></i> Reply</button> | |
| <span>5 hours ago</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="comment mb-6" data-timestamp="540"> | |
| <div class="flex items-start space-x-3"> | |
| <div class="w-10 h-10 rounded-full overflow-hidden bg-gray-200"> | |
| <img src="https://randomuser.me/api/portraits/men/75.jpg" alt="User" class="w-full h-full object-cover"> | |
| </div> | |
| <div class="flex-1"> | |
| <div class="flex items-center justify-between mb-1"> | |
| <span class="font-semibold text-gray-800">David Chen</span> | |
| <button class="timestamp-btn flex items-center text-xs text-gray-500 hover:text-blue-500"> | |
| <i class="fas fa-clock mr-1"></i> 9:00 | |
| </button> | |
| </div> | |
| <p class="text-gray-700 mb-2">The ethical considerations mentioned here are crucial. We need more public discourse on how AI systems make decisions that affect people's lives.</p> | |
| <div class="flex items-center text-sm text-gray-500 space-x-4"> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-up mr-1"></i> 15</button> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-down mr-1"></i> 1</button> | |
| <button class="hover:text-blue-500"><i class="far fa-comment mr-1"></i> Reply</button> | |
| <span>1 day ago</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <button class="w-full py-3 text-center text-blue-500 font-medium hover:bg-gray-50 rounded-lg mt-4"> | |
| Load more comments | |
| </button> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const audioPlayer = document.getElementById('audioPlayer'); | |
| const playBtn = document.getElementById('playBtn'); | |
| const progress = document.getElementById('progress'); | |
| const currentTime = document.getElementById('currentTime'); | |
| const commentText = document.getElementById('commentText'); | |
| const addTimestampBtn = document.getElementById('addTimestampBtn'); | |
| const submitCommentBtn = document.getElementById('submitCommentBtn'); | |
| const commentsContainer = document.getElementById('commentsContainer'); | |
| const progressBar = document.querySelector('.progress-bar'); | |
| let isPlaying = false; | |
| let currentTimestamp = 0; | |
| let comments = []; | |
| // Initialize with sample comments | |
| document.querySelectorAll('.comment').forEach(comment => { | |
| const timestamp = parseInt(comment.dataset.timestamp); | |
| comments.push({timestamp}); | |
| addCommentMarker(timestamp); | |
| }); | |
| // Play/Pause functionality | |
| playBtn.addEventListener('click', function() { | |
| if (isPlaying) { | |
| audioPlayer.pause(); | |
| playBtn.innerHTML = '<i class="fas fa-play"></i>'; | |
| } else { | |
| audioPlayer.play(); | |
| playBtn.innerHTML = '<i class="fas fa-pause"></i>'; | |
| } | |
| isPlaying = !isPlaying; | |
| }); | |
| // Update progress bar | |
| audioPlayer.addEventListener('timeupdate', function() { | |
| const duration = audioPlayer.duration; | |
| const currentTimeValue = audioPlayer.currentTime; | |
| const progressPercent = (currentTimeValue / duration) * 100; | |
| progress.style.width = `${progressPercent}%`; | |
| // Update current time display | |
| currentTimestamp = Math.floor(currentTimeValue); | |
| currentTime.textContent = formatTime(currentTimeValue); | |
| }); | |
| // Click on progress bar to seek | |
| progressBar.addEventListener('click', function(e) { | |
| const width = this.clientWidth; | |
| const clickX = e.offsetX; | |
| const duration = audioPlayer.duration; | |
| audioPlayer.currentTime = (clickX / width) * duration; | |
| }); | |
| // Add timestamp button | |
| addTimestampBtn.addEventListener('click', function() { | |
| if (audioPlayer.duration) { | |
| const timestamp = Math.floor(audioPlayer.currentTime); | |
| commentText.value = `${commentText.value} [${formatTime(timestamp)}]`; | |
| } | |
| }); | |
| // Submit comment | |
| submitCommentBtn.addEventListener('click', function() { | |
| if (commentText.value.trim() === '') return; | |
| const comment = { | |
| text: commentText.value, | |
| timestamp: currentTimestamp, | |
| user: { | |
| name: "You", | |
| avatar: "https://randomuser.me/api/portraits/women/44.jpg" | |
| }, | |
| likes: 0, | |
| dislikes: 0, | |
| timeAgo: "Just now" | |
| }; | |
| addComment(comment); | |
| commentText.value = ''; | |
| }); | |
| // Format time from seconds to MM:SS | |
| function formatTime(seconds) { | |
| const mins = Math.floor(seconds / 60); | |
| const secs = Math.floor(seconds % 60); | |
| return `${mins}:${secs < 10 ? '0' : ''}${secs}`; | |
| } | |
| // Add comment to the DOM | |
| function addComment(comment) { | |
| comments.push({timestamp: comment.timestamp}); | |
| addCommentMarker(comment.timestamp); | |
| const commentElement = document.createElement('div'); | |
| commentElement.className = 'comment mb-6 pb-6 border-b border-gray-100'; | |
| commentElement.dataset.timestamp = comment.timestamp; | |
| commentElement.innerHTML = ` | |
| <div class="flex items-start space-x-3"> | |
| <div class="w-10 h-10 rounded-full overflow-hidden bg-gray-200"> | |
| <img src="${comment.user.avatar}" alt="User" class="w-full h-full object-cover"> | |
| </div> | |
| <div class="flex-1"> | |
| <div class="flex items-center justify-between mb-1"> | |
| <span class="font-semibold text-gray-800">${comment.user.name}</span> | |
| <button class="timestamp-btn flex items-center text-xs text-gray-500 hover:text-blue-500"> | |
| <i class="fas fa-clock mr-1"></i> ${formatTime(comment.timestamp)} | |
| </button> | |
| </div> | |
| <p class="text-gray-700 mb-2">${comment.text}</p> | |
| <div class="flex items-center text-sm text-gray-500 space-x-4"> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-up mr-1"></i> ${comment.likes}</button> | |
| <button class="hover:text-blue-500"><i class="far fa-thumbs-down mr-1"></i> ${comment.dislikes}</button> | |
| <button class="hover:text-blue-500"><i class="far fa-comment mr-1"></i> Reply</button> | |
| <span>${comment.timeAgo}</span> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| commentsContainer.prepend(commentElement); | |
| // Add click handler to timestamp button | |
| const timestampBtn = commentElement.querySelector('.timestamp-btn'); | |
| timestampBtn.addEventListener('click', function() { | |
| audioPlayer.currentTime = comment.timestamp; | |
| if (!isPlaying) { | |
| audioPlayer.play(); | |
| playBtn.innerHTML = '<i class="fas fa-pause"></i>'; | |
| isPlaying = true; | |
| } | |
| }); | |
| } | |
| // Add comment marker to progress bar | |
| function addCommentMarker(timestamp) { | |
| const marker = document.createElement('div'); | |
| marker.className = 'comment-marker bg-blue-500'; | |
| marker.dataset.timestamp = timestamp; | |
| // Position marker based on timestamp | |
| const duration = audioPlayer.duration || 540; // Fallback to 9 minutes if duration not loaded | |
| const positionPercent = (timestamp / duration) * 100; | |
| marker.style.left = `${positionPercent}%`; | |
| // Add tooltip | |
| const tooltip = document.createElement('div'); | |
| tooltip.className = 'comment-tooltip'; | |
| tooltip.textContent = `${formatTime(timestamp)} - ${comments.filter(c => c.timestamp === timestamp).length} comment(s)`; | |
| marker.appendChild(tooltip); | |
| // Show tooltip on hover | |
| marker.addEventListener('mouseenter', function() { | |
| tooltip.style.display = 'block'; | |
| }); | |
| marker.addEventListener('mouseleave', function() { | |
| tooltip.style.display = 'none'; | |
| }); | |
| // Seek to timestamp when clicked | |
| marker.addEventListener('click', function() { | |
| audioPlayer.currentTime = timestamp; | |
| if (!isPlaying) { | |
| audioPlayer.play(); | |
| playBtn.innerHTML = '<i class="fas fa-pause"></i>'; | |
| isPlaying = true; | |
| } | |
| }); | |
| progressBar.appendChild(marker); | |
| } | |
| // Add click handlers to existing timestamp buttons | |
| document.querySelectorAll('.timestamp-btn').forEach(btn => { | |
| btn.addEventListener('click', function() { | |
| const comment = this.closest('.comment'); | |
| const timestamp = parseInt(comment.dataset.timestamp); | |
| audioPlayer.currentTime = timestamp; | |
| if (!isPlaying) { | |
| audioPlayer.play(); | |
| playBtn.innerHTML = '<i class="fas fa-pause"></i>'; | |
| isPlaying = true; | |
| } | |
| }); | |
| }); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=send2cloud/podfather" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |