Skip to content

Feature/embedded video player#769

Closed
omsherikar wants to merge 24 commits into
alphaonelabs:mainfrom
omsherikar:feature/embedded-video-player-620
Closed

Feature/embedded video player#769
omsherikar wants to merge 24 commits into
alphaonelabs:mainfrom
omsherikar:feature/embedded-video-player-620

Conversation

@omsherikar
Copy link
Copy Markdown
Contributor

@omsherikar omsherikar commented Oct 23, 2025

Related issues

Fixes #620

Checklist

  • Did you run the pre-commit? (If not, your PR will most likely not pass — please ensure it passes pre-commit)
  • Did you test the change? (Ensure you didn’t just prompt the AI and blindly commit — test the code and confirm it works)
  • Added screenshots to the PR description (if applicable)

Summary by CodeRabbit

  • New Features

    • Added video modal player supporting YouTube and Vimeo with keyboard controls and accessibility features.
    • Added action buttons in teacher message emails for direct navigation to Inbox and Messaging Dashboard.
  • Refactor

    • Improved Docker initialization with enhanced database handling and superuser management.
  • Chores

    • Optimized build dependencies and updated asset references.

omsherikar and others added 24 commits October 11, 2025 00:32
- Add missing system dependencies (pkg-config, default-libmysqlclient-dev, build-essential) for MySQL client compilation
- Fix Poetry package configuration by removing problematic package inclusion
- Reorder superuser creation to occur after test data creation to prevent user clearing
- Set superuser password properly during Docker build process

Resolves MySQL client build failures and Poetry package installation errors
- Replace hardcoded 'adminpassword' with os.environ['DJANGO_SUPERUSER_PASSWORD']
- Add import os to access environment variables
- Ensures password consistency with build ARG/ENV pattern
- Improves security and flexibility of Docker build process
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Remove build-time ARG/ENV for DJANGO_SUPERUSER_* credentials
- Remove RUN commands that bake passwords into image layers
- Add entrypoint script for runtime superuser creation
- Move migrations and test data creation to runtime
- Add proper ENTRYPOINT configuration
- Include documentation for runtime environment variables

Security improvements:
- No credentials visible in docker inspect
- No passwords in image layers
- Flexible credential management per environment
- Works with runtime databases (MySQL, PostgreSQL, etc.)

Fixes security vulnerability identified by CodeRabbit review.
- Add docker/entrypoint.sh with proper shebang and exec handling
- Script includes runtime superuser creation with env vars
- Uses set -euo pipefail for robust error handling
- Ensures proper signal handling with exec "$@"

Resolves CodeRabbit review: entrypoint script was missing from repository
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Add bounded retry loop for database migrations (max 30 attempts)
- Use --noinput flag to prevent interactive prompts
- Add configurable DB_MAX_ATTEMPTS environment variable
- Improve error logging with attempt counters
- Ensure container fails fast on database connection issues

Resolves CodeRabbit review: migrate command robustness and interactivity
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Fix malformed RUN command with trailing backslash and duplicate RUN
- Merge duplicate apt-get commands into single, proper RUN block
- Remove duplicate curl installation and rm commands
- Ensure proper Docker syntax for successful builds

Resolves CodeRabbit critical issue: build failure due to invalid shell syntax
…tifications

- Add inbox_url and messaging_dashboard_url to email context in views.py
- Update both teacher_message.html templates with action buttons
- Add CSS styling for professional-looking buttons
- Include helpful footer text explaining options
- Improve user experience by reducing friction in message access

Changes:
- web/views.py: Add URLs to email context
- web/templates/web/emails/teacher_message.html: Add buttons and styling
- web/templates/emails/teacher_message.html: Add buttons and styling

Users can now click directly to inbox or messaging dashboard from emails.
- Remove duplicate 'from django.urls import reverse' on line 29
- Keep the existing import on line 51: 'from django.urls import NoReverseMatch, reverse, reverse_lazy'
- Fixes flake8 F811 'redefinition of unused name' error
- Remove custom CSS rules for action-buttons, btn, btn-secondary, and footer-note
- Replace with Tailwind classes following project coding guidelines
- Use teal-300 primary color scheme with dark mode variants
- Add focus states and accessibility features
- Maintain professional styling and hover effects

Tailwind classes used:
- Primary button: bg-teal-300 hover:bg-teal-400 with dark variants
- Secondary button: bg-gray-600 hover:bg-gray-700 with dark variants
- Focus states: focus:ring-2 focus:ring-{color}-500
- Layout: my-8 text-center for spacing and alignment
- Typography: text-gray-600 dark:text-gray-300 text-sm
- Add aria-label attributes for better accessibility
- Add {% if %} guards to prevent broken links if URLs are missing
- Update container styling with proper Tailwind classes
- Improve secondary button styling with light/dark variants
- Add space-x-3 for proper button spacing
- Maintain focus states and transitions

Accessibility improvements:
- aria-label='View this message in your inbox' for primary button
- aria-label='Open messaging dashboard' for secondary button
- Proper focus ring styling for keyboard navigation

Styling improvements:
- container mx-auto px-4 for proper layout
- space-x-3 for consistent button spacing
- Improved secondary button colors (gray-100/gray-700)
- Consistent focus:ring-2 focus:ring-blue-500 styling
Fix alphaonelabs#621: Add clickable links to teacher message email notifications
- Fix onerror fallback images in features.html template
- Change placeholder.jpg to placeholder.png to match actual file
- Aligns with issue alphaonelabs#666 media file organization changes
- Ensures proper fallback images when feature images fail to load
- Add beautiful video modal with YouTube/Vimeo embed support
- Implement ultra-large responsive modal (95vw width, 70-85vh height)
- Add smooth animations and loading states with gradient backgrounds
- Include full accessibility support with ARIA labels and focus management
- Use semantic HTML5 elements (dialog, header, section) for better structure
- Follow Tailwind CSS best practices with @layer components
- Add keyboard navigation and screen reader support
- Implement mobile-first responsive design with dark mode support
- Replace YouTube redirects with embedded video player
- Maintain CONTRIBUTING.md compliance throughout

Closes alphaonelabs#620
@github-actions github-actions Bot added the f8 label Oct 23, 2025
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @omsherikar, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience by integrating an embedded video player, enabling seamless video consumption directly within the platform. It also improves the application's deployment and startup flexibility through a refactored Docker entrypoint script, which now handles critical setup tasks at runtime. Additionally, email notifications have been made more actionable with direct links to relevant sections of the application.

Highlights

  • Embedded Video Player: Implemented a new modal-based video player for YouTube and Vimeo links, allowing users to watch videos directly within the application without navigating to external sites.
  • Docker Build Process Refinement: Streamlined the Docker build by moving database migrations, test data seeding, and superuser creation to a runtime entrypoint.sh script, improving flexibility and startup management.
  • Enhanced Email Notifications: Added interactive buttons to teacher message emails, providing direct links to the user's inbox or messaging dashboard for improved navigation.
  • Minor Frontend Updates: Corrected image placeholder extensions from .jpg to .png in feature templates and removed an unused package configuration in pyproject.toml.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@omsherikar omsherikar closed this Oct 23, 2025
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a great new feature: an embedded video player that opens in a modal, improving the user experience for watching educational videos. The changes also include significant improvements to the Docker configuration, moving runtime tasks like database migrations and superuser creation into an entrypoint script, which is a best practice. My review focuses on further improving the frontend implementation. I've suggested moving the large blocks of CSS and JavaScript out of the HTML template into separate static files to improve maintainability and caching. I also found a small bug in the video list template due to duplicated code and proposed a fix. Additionally, I've recommended a more robust way to handle data passing from HTML to JavaScript and pointed out a potentially duplicated email template that could be removed to clean up the codebase.

Comment on lines +26 to 234
/* Video Modal - Following Tailwind Best Practices with Enhanced Styling */
@layer components {
.video-modal {
@apply hidden fixed inset-0 z-50 bg-gradient-to-br from-black via-gray-900 to-black bg-opacity-95 backdrop-blur-md opacity-0 transition-all duration-300 ease-out;
}

.video-modal.show {
@apply flex items-center justify-center opacity-100;
animation: modalFadeIn 0.3s ease-out;
}

@keyframes modalFadeIn {
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}

.video-modal-content {
@apply relative bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 dark:from-gray-800 dark:via-gray-700 dark:to-gray-800 rounded-3xl overflow-hidden max-w-[95vw] w-full mx-2 shadow-2xl border border-white border-opacity-20 transform scale-95 transition-all duration-300 ease-out;
box-shadow:
0 32px 64px -12px rgba(0, 0, 0, 0.6),
0 0 0 1px rgba(255, 255, 255, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.video-modal.show .video-modal-content {
@apply scale-100;
}

.video-modal-header {
@apply flex justify-between items-center px-8 py-6 bg-gradient-to-r from-gray-800 via-gray-700 to-gray-800 dark:from-gray-700 dark:via-gray-600 dark:to-gray-700 border-b border-gray-600 relative;
background: linear-gradient(135deg, #2a2a2a, #1a1a1a);
}

.video-modal-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
}

.video-modal-title {
@apply text-white text-2xl font-bold m-0 tracking-tight;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
letter-spacing: -0.025em;
}

.video-modal-close {
@apply bg-white bg-opacity-10 border border-white border-opacity-20 text-white text-xl cursor-pointer p-3 rounded-xl transition-all duration-200 ease-out w-12 h-12 flex items-center justify-center hover:bg-opacity-20 hover:border-opacity-30 hover:scale-105 active:scale-95 focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 focus:outline-none;
backdrop-filter: blur(10px);
}

.video-modal-close:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.video-modal-body {
@apply relative;
}

.video-modal-iframe {
@apply w-full h-[70vh] sm:h-[75vh] lg:h-[80vh] xl:h-[85vh] border-0 rounded-b-3xl;
background: linear-gradient(45deg, #1a1a1a, #2a2a2a);
}

.video-card {
@apply transition-all duration-300 ease-out relative overflow-hidden;
}

.video-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
z-index: 1;
}

.video-card:hover {
@apply -translate-y-2 shadow-2xl;
box-shadow:
0 20px 40px -12px rgba(0, 0, 0, 0.15),
0 0 0 1px rgba(255, 255, 255, 0.1);
}

.video-card:hover::before {
opacity: 1;
}

.video-card .video-link {
position: relative;
z-index: 2;
}

.video-card .video-link i.fa-play {
@apply transition-all duration-300 ease-out;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}

.video-card:hover .video-link i.fa-play {
@apply scale-110;
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.7);
}

.sr-only {
@apply absolute w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap border-0;
clip: rect(0, 0, 0, 0);
}
}

/* Enhanced Responsive Design - Mobile First */
@media (max-width: 768px) {
.video-modal-content {
@apply mx-1 rounded-2xl;
max-width: 98vw;
}

.video-modal-iframe {
@apply h-[65vh];
}

.video-modal-header {
@apply px-6 py-5;
}

.video-modal-title {
@apply text-xl;
}

.video-modal-close {
@apply w-10 h-10 p-2;
}
}

@media (max-width: 480px) {
.video-modal-content {
@apply mx-0.5 rounded-xl;
max-width: 99vw;
}

.video-modal-iframe {
@apply h-[60vh];
}

.video-modal-header {
@apply px-4 py-4;
}

.video-modal-title {
@apply text-lg;
}

.video-modal-close {
@apply w-9 h-9 p-1.5;
}
}

/* Dark mode enhancements */
@media (prefers-color-scheme: dark) {
.video-modal {
background: linear-gradient(135deg, rgba(0, 0, 0, 0.95), rgba(10, 10, 20, 0.98));
}

.video-card:hover {
box-shadow:
0 20px 40px -12px rgba(0, 0, 0, 0.4),
0 0 0 1px rgba(255, 255, 255, 0.1);
}
}

/* Loading animation for iframe */
.video-modal-iframe::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 40px;
height: 40px;
margin: -20px 0 0 -20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid #ffffff;
border-radius: 50%;
animation: spin 1s linear infinite;
z-index: 1;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

/* Hide loading spinner when video loads */
.video-modal-iframe.loaded::before {
display: none;
}
</style>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This is a very large block of CSS. It's generally better to keep CSS in separate .css files for better maintainability, separation of concerns, and browser caching. Please move this CSS to a new static file (e.g., static/css/video-modal.css) and include it in the template using a <link> tag in the extra_head block.

This will make the template file much cleaner and easier to read.

Comment on lines +539 to 689
<script>
document.addEventListener('DOMContentLoaded', function() {
const videoModal = document.getElementById('videoModal');
const videoModalTitle = document.getElementById('videoModalTitle');
const videoModalIframe = document.getElementById('videoModalIframe');
const videoModalClose = document.getElementById('videoModalClose');

// Function to extract YouTube video ID from URL
function getYouTubeVideoId(url) {
const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
const match = url.match(regExp);
return (match && match[2].length === 11) ? match[2] : null;
}

// Function to extract Vimeo video ID from URL
function getVimeoVideoId(url) {
const regExp = /vimeo\.com\/(?:.*#|.*\/videos\/)?([0-9]+)/;
const match = url.match(regExp);
return match ? match[1] : null;
}

// Function to open video modal
function openVideoModal(videoUrl, videoTitle) {
let embedUrl = '';
const youtubeId = getYouTubeVideoId(videoUrl);
const vimeoId = getVimeoVideoId(videoUrl);

if (youtubeId) {
embedUrl = `https://www.youtube.com/embed/${youtubeId}?autoplay=1&rel=0&modestbranding=1&enablejsapi=1`;
} else if (vimeoId) {
embedUrl = `https://player.vimeo.com/video/${vimeoId}?autoplay=1&title=0&byline=0&portrait=0`;
} else {
// Fallback to original URL for unsupported platforms
window.open(videoUrl, '_blank');
return;
}

// Set title and accessibility attributes with smooth animation
videoModalTitle.textContent = videoTitle;
videoModalTitle.style.opacity = '0';
videoModalTitle.style.transform = 'translateY(10px)';
videoModalIframe.title = `Video player for ${videoTitle}`;

// Show modal with beautiful animation
videoModal.classList.add('show');
videoModal.showModal(); // Use native dialog API
document.body.style.overflow = 'hidden';

// Animate title in
setTimeout(() => {
videoModalTitle.style.transition = 'all 0.3s ease-out';
videoModalTitle.style.opacity = '1';
videoModalTitle.style.transform = 'translateY(0)';
}, 100);

// Set iframe source with loading state
videoModalIframe.classList.remove('loaded');
videoModalIframe.src = embedUrl;

// Handle video loading
videoModalIframe.onload = function() {
setTimeout(() => {
videoModalIframe.classList.add('loaded');
videoModalIframe.style.transition = 'opacity 0.3s ease-out';
videoModalIframe.style.opacity = '1';
}, 200);
};

// Focus management for accessibility
videoModalClose.focus();
}

// Function to close video modal
function closeVideoModal() {
// Animate out with smooth transition
videoModal.style.opacity = '0';
videoModal.querySelector('.video-modal-content').style.transform = 'scale(0.9) translateY(20px)';

setTimeout(() => {
// Close modal with proper accessibility
videoModal.classList.remove('show');
videoModal.close(); // Use native dialog API
videoModalIframe.src = '';
document.body.style.overflow = '';

// Reset styles for next opening
videoModal.style.opacity = '';
videoModal.querySelector('.video-modal-content').style.transform = '';
videoModalTitle.style.opacity = '';
videoModalTitle.style.transform = '';
videoModalIframe.style.opacity = '';
videoModalIframe.classList.remove('loaded');

// Return focus to the video link that opened the modal
if (document.activeElement && document.activeElement.classList.contains('video-link')) {
document.activeElement.focus();
}
}, 300);
}

// Add click event listeners to all video links
document.querySelectorAll('a.video-link').forEach(function(link) {
link.addEventListener('click', function(e) {
e.preventDefault();
const videoUrl = this.href;
const videoTitle = this.closest('.video-card')?.querySelector('h3 a')?.textContent ||
this.closest('.bg-white')?.querySelector('h3 a')?.textContent ||
'Educational Video';
openVideoModal(videoUrl, videoTitle);
});
});

// Close modal events
videoModalClose.addEventListener('click', closeVideoModal);

videoModal.addEventListener('click', function(e) {
if (e.target === videoModal) {
closeVideoModal();
}
});

// Enhanced keyboard navigation for accessibility
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && videoModal.classList.contains('show')) {
closeVideoModal();
}

// Trap focus within modal when open
if (videoModal.classList.contains('show')) {
const focusableElements = videoModal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];

if (e.key === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus();
e.preventDefault();
}
} else {
if (document.activeElement === lastElement) {
firstElement.focus();
e.preventDefault();
}
}
}
}
});
});
</script>
{% endblock %}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This is a large block of JavaScript code. For better code organization, maintainability, and to leverage browser caching, it's recommended to move this script into its own static file (e.g., static/js/video-modal.js) and include it at the end of the extra_js block using <script src="{% static 'js/video-modal.js' %}"></script>.

Comment on lines +644 to +646
const videoTitle = this.closest('.video-card')?.querySelector('h3 a')?.textContent ||
this.closest('.bg-white')?.querySelector('h3 a')?.textContent ||
'Educational Video';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Getting the video title with this.closest('.video-card')?.querySelector('h3 a')?.textContent is a bit fragile as it tightly couples the JavaScript to the specific HTML structure. A more robust approach is to use a data-* attribute to hold the video title directly on the link element.

For example, you could modify the video links (e.g., on lines 310, 320, and 334) like this:

<a href="{{ video.video_url }}"
   class="hover:text-orange-500 transition-colors video-link"
   data-video-title="{{ video.title|escapejs }}">
    {{ video.title }}
</a>

Note the use of |escapejs to ensure the title is properly escaped for use in JavaScript.

Then, in your JavaScript, you can retrieve the title much more simply and reliably. This decouples your script from the DOM structure, making future template changes safer.

            const videoTitle = this.dataset.videoTitle || 'Educational Video';

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 23, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This pull request restructures the Docker build process to defer runtime operations (migrations, superuser creation, test data seeding) to an entrypoint script, implements a video modal for in-platform playback with YouTube/Vimeo support, and enhances email templates with contextual action buttons.

Changes

Cohort / File(s) Summary
Docker & Entrypoint Setup
Dockerfile, docker/entrypoint.sh
Enhanced Docker build with optimized install flags (--no-install-recommends), added build dependencies (pkg-config, default-libmysqlclient-dev, build-essential), and --no-root for poetry install. Introduced new entrypoint script handling database migrations with configurable retry logic, optional test data seeding, and Django superuser creation/updates. Removed build-time migrations, test data, and superuser env declarations. Added static directories creation.
Email Template & Context
web/templates/emails/teacher_message.html, web/templates/web/emails/teacher_message.html, web/views.py
Added conditional action buttons (View in Inbox, Messaging Dashboard) to teacher message emails with Tailwind styling. Updated web/views.py to inject inbox_url and messaging_dashboard_url into email template context. Replaced static reply guidance with dynamic button-driven user actions.
Video Modal Implementation
web/templates/videos/list.html
Introduced semantic HTML5 video modal dialog with full-screen overlay and iframe embedding. Added JavaScript workflow to detect and embed YouTube/Vimeo URLs with autoplay, fallback to external tab for unsupported platforms, and keyboard/focus accessibility features (Escape-to-close, focus trapping). Enhanced video links with video-link class and video-card styling.
Minor Updates
web/templates/features.html, pyproject.toml
Updated image fallback paths from .jpg to .png; removed explicit "web" package declaration from pyproject.toml.

Sequence Diagram(s)

sequenceDiagram
    participant Container as Docker Container
    participant Entrypoint as entrypoint.sh
    participant DB as Database
    participant Django as Django App
    participant App as Application Server

    Container->>Entrypoint: Execute entrypoint.sh
    Entrypoint->>DB: Attempt migrations (with retry)
    alt DB Ready
        DB-->>Entrypoint: Success
    else DB Not Ready
        Entrypoint->>Entrypoint: Sleep & Retry (up to DB_MAX_ATTEMPTS)
        Entrypoint->>DB: Retry migrations
    end
    
    alt CREATE_TEST_DATA == 1
        Entrypoint->>Django: Run create_test_data command
        Django-->>Entrypoint: Test data seeded
    end
    
    alt DJANGO_SUPERUSER_* provided
        Entrypoint->>Django: Create/update superuser via shell
        Django-->>Entrypoint: Superuser ready
    end
    
    Entrypoint->>App: exec "$@" (run server)
    App-->>Container: Server running
Loading
sequenceDiagram
    participant User as User
    participant Page as Videos List Page
    participant Modal as Video Modal
    participant Embed as Embed Service

    User->>Page: Click video thumbnail
    Page->>Page: Detect video-link click
    Page->>Modal: Show modal dialog
    Modal->>Modal: Parse video URL
    alt YouTube URL
        Modal->>Embed: Generate embed URL (youtube.com/embed/...)
    else Vimeo URL
        Modal->>Embed: Generate embed URL (vimeo.com/video/...)
    else Other
        Modal->>Page: Open in new tab
    end
    
    Modal->>Modal: Load iframe with autoplay
    Modal->>User: Display video with controls
    User->>Modal: Press Escape or click close
    Modal->>Modal: Trap focus & close
    Modal-->>Page: Modal dismissed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Rationale: Multiple heterogeneous changes span DevOps infrastructure (Docker/entrypoint with conditional logic and retry mechanisms), frontend interactivity (video modal with JavaScript event handling and accessibility features), Django context modifications, and template enhancements. The video modal implementation requires careful review of JavaScript correctness, keyboard/focus accessibility compliance, and the noted duplicate markup. Docker entrypoint introduces multi-step orchestration logic requiring verification of database retry semantics and conditional command execution order.

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad91f49 and 86d6e98.

📒 Files selected for processing (8)
  • Dockerfile (3 hunks)
  • docker/entrypoint.sh (1 hunks)
  • pyproject.toml (0 hunks)
  • web/templates/emails/teacher_message.html (2 hunks)
  • web/templates/features.html (3 hunks)
  • web/templates/videos/list.html (6 hunks)
  • web/templates/web/emails/teacher_message.html (2 hunks)
  • web/views.py (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Open Educational Videos Directly on the Platform (Instead of Redirecting to YouTube)

2 participants