Back to projects
SMovie - Comprehensive Online Movie Streaming Platform

SMovie - Comprehensive Online Movie Streaming Platform

October 15, 2024
Backend Engineer, System Architect, Full-Stack Developer
PythonDjangoDjango REST FrameworkWebSocketRedisReactPostgreSQLStreaming

🎬 SMovie - Comprehensive Online Movie Streaming Platform

A modern movie streaming system built with Django REST API architecture, integrating real-time WebSocket, multi-channel authentication, and intelligent content management - delivering a smooth entertainment experience for Vietnamese users.

🧠 Overview

SMovie is an online movie streaming platform developed to provide a comprehensive entertainment experience with thousands of multi-genre films. The project focuses on building a robust backend system, high scalability, and integrating modern features such as:

  • Smooth movie streaming with episode management by seasons
  • Multi-channel authentication system: Email verification, JWT token, Facebook OAuth
  • Real-time comments via WebSocket with Redis Channel Layer
  • Intelligent search using fuzzy matching (thefuzz)
  • Professional content management with extended Django Admin
  • SEO-optimized with video sitemap and structured data

The project serves end users through a responsive web interface while providing RESTful API for mobile applications and third parties.

✨ Key Features

🎯 End Users

  • Diverse movie library: Categorized by genre, country, release year, exclusivity
  • Intelligent search: Fuzzy search supporting search by movie name, actor, director
  • Personal watchlist: Save favorite movies, track viewing history
  • Ratings & comments: Review movies with ratings, real-time comments via WebSocket
  • Flexible authentication: Email registration with verification link, Facebook OAuth login
  • Profile management: Update information, change avatar, modify password

πŸ› οΈ Administrators

  • Statistics dashboard: Detailed reports on views, ratings, comments, trending movies
  • Content management: Full CRUD for movies, episodes, actors, directors, genres
  • Autocomplete fields: Optimized input experience with select2 integration
  • Banner management: Manage advertisements and promotional content
  • Reports module: 6 report types with visual charts (Chart.js)

πŸ”Œ Technical Highlights

  • WebSocket Comments: Real-time messaging with JWT authentication middleware
  • Fuzzy Search: Token set ratio scoring for intelligent search results
  • Pagination: Paginated data loading for optimal performance
  • Video Sitemap: SEO optimization for Google Video Search
  • CORS & CSRF: Cross-origin request security

🧱 Technical Architecture

βš™οΈ System Architecture

Rendering diagram...

πŸ”„ WebSocket Flow - Real-time Comments

Rendering diagram...

πŸ” Authentication Flow

Rendering diagram...

πŸ’» Representative Code Samples

1️⃣ WebSocket Consumer with JWT Authentication

This code demonstrates the ability to build a secure real-time messaging system with JWT authentication:

Python
1# apps/comments/consumers.py
2class ChatConsumer(AsyncWebsocketConsumer):
3    async def connect(self):
4        """Connect WebSocket and join chat room."""
5        self.episode_id = self.scope['url_route']['kwargs']['episode_id']
6        self.room_group_name = f"comments_{self.episode_id}"
7        
8        # Check JWT authentication from middleware
9        self.user = self.scope.get("user")
10        if not self.user or self.user.is_anonymous:
11            await self.close()
12            return
13
14        await self.channel_layer.group_add(
15            self.room_group_name, 
16            self.channel_name
17        )
18        await self.accept()
19
20    async def receive(self, text_data):
21        """Handle message from client."""
22        data = json.loads(text_data)
23        message = data.get("message")
24        
25        # Save comment to database (sync_to_async)
26        await self.save_comment(self.user, self.episode_id, message)
27
28        # Broadcast to all clients in the room
29        await self.channel_layer.group_send(
30            self.room_group_name,
31            {
32                "type": "chat_message",
33                "message": message,
34                "username": self.user.username
35            }
36        )
37
38    @sync_to_async
39    def save_comment(self, user, episode_id, message):
40        Comment.objects.create(
41            user=user, 
42            episode_id=episode_id, 
43            content=message
44        )

Highlights:

  • JWT authentication via custom middleware before accepting connection
  • Uses Redis Channel Layer to broadcast messages
  • Async/await pattern for high performance
  • Save sync data to PostgreSQL with sync_to_async

2️⃣ Fuzzy Search with Token Set Ratio

Intelligent search algorithm allowing users to find movies with inexact keywords:

Python
1# apps/movies/views.py
2def search_movies(request):
3    query = request.GET.get('q', '').strip()
4    
5    # Exact search first
6    exact_matches = Movies.objects.filter(title__iexact=query)
7    if exact_matches.exists():
8        return JsonResponse({"movies": serialize(exact_matches)})
9    
10    # Fuzzy search with thefuzz library
11    movies = Movies.objects.all()
12    movie_data = [
13        (
14            movie.movie_id, 
15            movie.title,
16            ', '.join([genre.genre.name for genre in movie.moviegenres_set.all()]),
17            ', '.join([actor.actor.name for actor in movie.movieactors_set.all()])
18        ) for movie in movies
19    ]
20    
21    # Combine all information into string for searching
22    combined_data = [
23        f"{title} {genres} {actors}" 
24        for _, title, genres, actors in movie_data
25    ]
26    
27    # Fuzzy matching with token_set_ratio
28    fuzzy_results = process.extractBests(
29        query, 
30        combined_data, 
31        scorer=fuzz.token_set_ratio, 
32        score_cutoff=50,
33        limit=20
34    )
35    
36    # Calculate priority score: title score x2 + overall score
37    movie_scores = {}
38    for result in fuzzy_results:
39        idx = result[2]
40        movie_id = movie_data[idx][0]
41        title_score = fuzz.ratio(query.lower(), movie_data[idx][1].lower())
42        overall_score = result[1]
43        
44        combined_score = title_score * 2 + overall_score
45        movie_scores[movie_id] = combined_score
46    
47    # Sort by score
48    sorted_movie_ids = sorted(
49        movie_scores.keys(), 
50        key=lambda x: movie_scores[x], 
51        reverse=True
52    )
53    
54    matched_movies = Movies.objects.filter(
55        movie_id__in=sorted_movie_ids[:10]
56    )
57    
58    return JsonResponse({"movies": serialize(matched_movies)})

Highlights:

  • 3-tier search strategy: exact β†’ fuzzy title β†’ fuzzy full-text
  • Weighted scoring: title match has double weight
  • Multi-dimensional search: title + genres + actors + directors
  • Score cutoff to filter noise results

3️⃣ Email Verification with WebSocket Notification

Modern email verification process with real-time notification:

Python
1# apps/users/views.py
2@csrf_exempt
3def register(request):
4    serializer = RegisterSerializer(data=json.loads(request.body))
5    
6    if serializer.is_valid():
7        user = serializer.save()  # is_active=False
8        
9        # Generate UID and token
10        uid = urlsafe_base64_encode(force_bytes(user.pk))
11        token = default_token_generator.make_token(user)
12        
13        # Send verification email
14        current_site = get_current_site(request)
15        message = render_to_string('acc_active_email.html', {
16            'user': user,
17            'domain': current_site.domain,
18            'uid': uid,
19            'token': token,
20        })
21        
22        email = EmailMultiAlternatives(
23            'Activate your account',
24            "",
25            settings.DEFAULT_FROM_EMAIL,
26            [user.email]
27        )
28        email.attach_alternative(message, "text/html")
29        email.send()
30        
31        # Create JWT tokens for frontend to connect WebSocket
32        refresh = RefreshToken.for_user(user)
33        
34        return JsonResponse({
35            'message': 'Please check your email!',
36            'uid': uid,  # Frontend uses this to connect WebSocket
37            'refresh': str(refresh),
38            'access': str(refresh.access_token)
39        })
40
41def activate_account(request, uidb64, token):
42    try:
43        uid = force_str(urlsafe_base64_decode(uidb64))
44        user = User.objects.get(pk=uid)
45    except:
46        return JsonResponse({'message': 'Invalid link!'}, status=400)
47    
48    if default_token_generator.check_token(user, token):
49        user.is_active = True
50        user.save()
51        
52        # Send notification via WebSocket
53        channel_layer = get_channel_layer()
54        async_to_sync(channel_layer.group_send)(
55            uidb64,  # Room name = uid
56            {
57                "type": "email_verified",
58                "message": "Email verified successfully!"
59            }
60        )
61        
62        return JsonResponse({'message': 'Verification successful!'})

Highlights:

  • Uses Django's default_token_generator for security
  • WebSocket notification for smooth UX (no need to refresh)
  • JWT tokens returned immediately so frontend can connect WebSocket
  • ASGI sync bridge with async_to_sync to send message from sync view

🎨 Design System

UI Framework & Styling

  • Backend Admin: Django Admin with custom templates, Chart.js for visualizations
  • API Response: RESTful JSON with pagination, filtering, ordering
  • WebSocket Protocol: Standard JSON messaging format

Database Schema Design

Rendering diagram...

Highlights:

  • Normalized design: Avoid data redundancy with junction tables
  • Soft deletes: Use is_active flags instead of hard delete
  • Audit trails: created_at, updated_at for tracking changes
  • Flexible relationships: Many-to-many with extra fields (role in MovieActors)

πŸ’³ External Service Integration

πŸ” Facebook OAuth 2.0

  • Role: Social login for quick onboarding experience
  • Flow: Frontend receives access_token β†’ Backend verifies with Facebook Graph API β†’ Create/get User β†’ Return JWT tokens
  • Edge case handling:
    • Email doesn't exist β†’ Generate fake email face{id}@example.com
    • Token expired β†’ Frontend auto-refresh
    • Save SocialAccount and SocialToken for future reference

πŸ“§ Gmail SMTP

  • Role: Send email verification, password reset
  • Security: Use App Password instead of main password
  • Template: HTML email with Django template engine
  • Error handling: Graceful degradation if SMTP fails (log error, continue registration)

πŸ—ΊοΈ Google Video Sitemap

  • Role: SEO optimization for video content
  • Implementation: Custom view generates XML sitemap per Google standard
  • Structured data:
    XML
    1<video:video>
    2  <video:thumbnail_loc>poster_url</video:thumbnail_loc>
    3  <video:title>movie_title</video:title>
    4  <video:duration>runtime</video:duration>
    5  <video:rating>rating</video:rating>
    6  <video:view_count>views</video:view_count>
    7</video:video>

πŸš€ Performance & Optimization

πŸ”₯ Query Optimization

  • Select Related: Eager loading foreign keys to avoid N+1 queries
    Python
    1Movies.objects.select_related('nation', 'monopoly')
  • Prefetch Related: Optimize many-to-many relationships
    Python
    1movie.prefetch_related('moviegenres_set__genre', 'movieactors_set__actor')
  • Database Indexing: Index on title, release_date, rating, views
  • Query Count: Reduced from ~200 queries/page to ~15 queries

⚑ Caching Strategy

  • Redis Channel Layer: Cache WebSocket connections and messages
  • Django Cache Framework: (Planned) Cache expensive querysets
  • HTTP Caching: ETags and Last-Modified headers for static assets

πŸ“¦ Pagination

  • Django Paginator: Limit 10-20 items/page
  • Lazy Loading: Frontend loads more on scroll
  • Count Optimization: Use Paginator.count cache

🎯 Measured Results

  • API Response Time: ~150ms (average) for list endpoints
  • WebSocket Latency: ~50ms for message delivery
  • Database Connections: Connection pooling with PostgreSQL
  • Concurrent Users: Tested with 100+ simultaneous WebSocket connections

🧠 Challenges & Solutions

Challenge 1: Real-time Comments with JWT Authentication

Problem: WebSocket doesn't support Authorization header like HTTP requests, need to authenticate user when connecting

Solution:

  • Create custom JWTAuthMiddleware to parse JWT from query string
  • Middleware decodes token and attaches user to scope
  • Consumer checks scope['user'] before accepting connection
Python
1class JWTAuthMiddleware(BaseMiddleware):
2    async def __call__(self, scope, receive, send):
3        query_string = parse_qs(scope["query_string"].decode())
4        token = query_string.get("token", [None])[0]
5        
6        try:
7            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
8            user = await sync_to_async(User.objects.get)(id=payload["user_id"])
9            scope["user"] = user
10        except:
11            raise DenyConnection("Unauthorized")
12        
13        return await super().__call__(scope, receive, send)

Challenge 2: Fuzzy Search Performance with Large Dataset

Problem: Fuzzy matching on 10,000+ movies with multiple fields (title, genres, actors) very slow

Solution:

  • Tiered search: Exact match β†’ Title fuzzy β†’ Full-text fuzzy
  • Early exit: Return immediately when exact or high-score matches found
  • Limited candidates: limit=100 instead of searching entire dataset
  • Weighted scoring: Title match has x2 weight to prioritize relevant results
  • Result: Reduced search time from ~3s to ~300ms

Challenge 3: Email Verification UX

Problem: User must open email, click link, then return to login page β†’ fragmented experience

Solution:

  • Frontend maintains WebSocket connection after registration (using uid as room name)
  • User clicks verification link β†’ Backend activates account β†’ Sends WebSocket message
  • Frontend receives notification β†’ Automatically redirects to homepage with authenticated state
  • Result: Seamless UX, user doesn't need additional actions after clicking email

Challenge 4: Facebook OAuth with Missing Email

Problem: ~20% Facebook users don't grant email permission β†’ Registration fails

Solution:

  • Generate synthetic email: face{facebook_id}@example.com
  • Save flag email_verified=False in profile
  • Display prompt requesting real email update on first login
  • Allow user to update email later with verification flow

🧭 Future Enhancements

  • AI Recommendations: Collaborative filtering for personalized movie suggestions
  • Video CDN: Integrate Cloudflare Stream or AWS CloudFront
  • Multi-language: i18n support for subtitles and UI
  • Payment Gateway: Integrate Stripe/VNPay for subscription model
  • Mobile Apps: React Native apps for iOS/Android
  • GraphQL API: Alternative endpoint for flexible queries
  • Elasticsearch: Advanced full-text search replacing fuzzy matching
  • Redis Caching: Cache hot data (trending movies, top reviews)
  • Notification System: Push notifications for new episodes, replies
  • Advanced Analytics: Heatmaps, watch-time tracking, A/B testing

🧰 Tech Stack

Backend Framework:

  • Django 5.0.6 - Web framework
  • Django REST Framework - RESTful API
  • Django Channels + Daphne - WebSocket server

Database & Caching:

  • PostgreSQL - Primary database
  • Redis - Channel layer & caching

Authentication:

  • JWT (Simple JWT) - Token-based auth
  • Django Allauth - Social authentication
  • Facebook OAuth 2.0 - Social login

Real-time:

  • Channels Redis - WebSocket channel layer
  • ASGI - Async server interface

Search & Matching:

  • TheFuzz (FuzzyWuzzy) - Fuzzy string matching

DevOps & Deployment:

  • Gunicorn / Daphne - ASGI/WSGI servers
  • Nginx - Reverse proxy
  • Docker - Containerization (planned)

Development & Testing:

  • Selenium - Browser automation testing
  • Appium - Mobile testing
  • Python Decouple - Environment config

Security:

  • CORS Headers - Cross-origin security
  • CSRF Protection - Form security
  • PyCryptodome - Encryption utilities

Utilities:

  • Pandas - Data processing
  • Pillow - Image handling
  • Python Dotenv - Environment variables

Note: This project is the backend portion of the SMovie system, designed with RESTful API architecture to serve multiple clients (Web, Mobile). The frontend is developed separately with React.js and integrates WebSocket client to receive real-time updates.