Design (LLD) video streaming platform - Machine Coding
Table of contents
Low-level design (LLD) for a video streaming platform:
Video hosting and delivery: The platform will need to store and serve the video content to users. This will likely involve using a cloud storage service such as Amazon S3 to store the videos, and a content delivery network (CDN) to distribute the videos to users.
User accounts and authentication: The platform will need to allow users to create accounts, log in, and log out. We will also need to implement measures to ensure the security of user accounts, such as password hashing and potentially two-factor authentication.
Video metadata and catalog: The platform will need to store information about the videos, such as titles, descriptions, thumbnails, and categories. This will likely involve creating a database or data structure to store this information, as well as functions to search and filter the videos.
Streaming and playback: The platform will need to allow users to stream the videos and control the playback, such as pausing, seeking, and adjusting the volume. This will involve implementing the necessary player functionality and handling any interactions with the server-side logic.
Recommendation and discovery: The platform will need to provide recommendations and suggestions to users based on their viewing history and preferences. This will involve implementing algorithms to analyze the user data and generate recommendations.
User interface: Finally, we will need to design the user interface for the platform, including the layout, navigation, and any necessary graphics or styling. This will involve creating wireframes and mockups, as well as implementing the front-end code to bring the design to life.
Code
import java.util.*;
import java.util.stream.Collectors;
// Factory Pattern for creating key entities
interface VideoStreamingFactory {
User createUser(String id, String name);
Video createVideo(String id, String title, boolean isFree, boolean isPaid, boolean isSubscription, double price);
}
class ConcreteVideoStreamingFactory implements VideoStreamingFactory {
@Override
public User createUser(String id, String name) {
return new User(id, name);
}
@Override
public Video createVideo(String id, String title, boolean isFree, boolean isPaid, boolean isSubscription, double price) {
return new Video(id, title, isFree, isPaid, isSubscription, price);
}
}
// Observer Pattern for Notifications
interface Observer {
void update(String message);
}
class UserObserver implements Observer {
private User user;
public UserObserver(User user) {
this.user = user;
}
@Override
public void update(String message) {
System.out.println("Notification to " + user.getName() + ": " + message);
}
}
// Subject Interface for Notification Service
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
class NotificationService implements Subject {
private List<Observer> observers = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
public void notifyPaymentSuccessful(User user, Video video) {
notifyObservers("Payment for video " + video.getTitle() + " successful for " + user.getName());
}
public void notifySubscriptionSuccessful(User user) {
notifyObservers("Subscription successful for " + user.getName());
}
public void notifySubscriptionCancelled(User user) {
notifyObservers("Subscription cancelled for " + user.getName());
}
public void notifyVideoUploaded(User user, Video video) {
notifyObservers("New video uploaded by " + user.getName() + ": " + video.getTitle());
}
}
// Strategy Pattern for Pricing Models
interface PricingStrategy {
double calculatePrice(double basePrice);
}
class FreePricingStrategy implements PricingStrategy {
@Override
public double calculatePrice(double basePrice) {
return 0; // Free videos have no charge
}
}
class PaidPricingStrategy implements PricingStrategy {
@Override
public double calculatePrice(double basePrice) {
return basePrice; // Charge for individual videos
}
}
class SubscriptionPricingStrategy implements PricingStrategy {
@Override
public double calculatePrice(double basePrice) {
return basePrice * 0.9; // Discounted price for subscription-based videos
}
}
// Caching Mechanism to optimize repeated data access
class UserCache {
private Map<String, User> userCache = new HashMap<>();
public void addUser(User user) {
userCache.put(user.getId(), user);
}
public User getUser(String id) {
return userCache.get(id);
}
public Collection<User> getAllUsers() {
return userCache.values();
}
}
class VideoCache {
private Map<String, Video> videoCache = new HashMap<>();
public void addVideo(Video video) {
videoCache.put(video.getId(), video);
}
public Video getVideo(String id) {
return videoCache.get(id);
}
public Collection<Video> getAllVideos() {
return videoCache.values();
}
}
// Decorator Pattern for Video Enhancements (e.g., subtitles, multiple languages)
abstract class VideoDecorator extends Video {
protected Video decoratedVideo;
public VideoDecorator(Video video) {
super(video.getId(), video.getTitle(), video.isFree(), video.isPaid(), video.isSubscription(), video.getPrice());
this.decoratedVideo = video;
}
@Override
public double getPrice() {
return decoratedVideo.getPrice();
}
}
class SubtitledVideo extends VideoDecorator {
private String subtitleLanguage;
public SubtitledVideo(Video video, String subtitleLanguage) {
super(video);
this.subtitleLanguage = subtitleLanguage;
}
public String getSubtitleLanguage() {
return subtitleLanguage;
}
}
// User class with extended behavior
public class User {
private String id;
private String name;
private Set<Video> watchedVideos = new HashSet<>();
private Set<Video> likedVideos = new HashSet<>();
private Set<Video> dislikedVideos = new HashSet<>();
public User(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void watchVideo(Video video) {
watchedVideos.add(video);
}
public Set<Video> getWatchedVideos() {
return watchedVideos;
}
public void likeVideo(Video video) {
likedVideos.add(video);
}
public void dislikeVideo(Video video) {
dislikedVideos.add(video);
}
public Set<Video> getLikedVideos() {
return likedVideos;
}
public Set<Video> getDislikedVideos() {
return dislikedVideos;
}
}
// Video class with rating and pricing strategies
public class Video {
private String id;
private String title;
private List<Integer> ratings = new ArrayList<>();
private boolean isFree;
private boolean isPaid;
private boolean isSubscription;
private double price;
private PricingStrategy pricingStrategy;
public Video(String id, String title, boolean isFree, boolean isPaid, boolean isSubscription, double price) {
this.id = id;
this.title = title;
this.isFree = isFree;
this.isPaid = isPaid;
this.isSubscription = isSubscription;
this.price = price;
// Set pricing strategy based on video type
if (isFree) {
this.pricingStrategy = new FreePricingStrategy();
} else if (isPaid) {
this.pricingStrategy = new PaidPricingStrategy();
} else if (isSubscription) {
this.pricingStrategy = new SubscriptionPricingStrategy();
}
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public double getPrice() {
return pricingStrategy.calculatePrice(price);
}
public void addRating(int rating) {
ratings.add(rating);
}
public double getAverageRating() {
return ratings.stream().mapToInt(Integer::intValue).average().orElse(0.0);
}
public boolean isFree() {
return isFree;
}
public boolean isPaid() {
return isPaid;
}
public boolean isSubscription() {
return isSubscription;
}
}
// Refined PaymentProcessor with subscription handling
public class PaymentProcessor {
private Map<User, Set<Video>> paidVideosByUser = new HashMap<>();
private Map<User, Boolean> subscriptionsByUser = new HashMap<>();
public void pay(User user, Video video) {
paidVideosByUser.computeIfAbsent(user, k -> new HashSet<>()).add(video);
}
public boolean hasPaid(User user, Video video) {
return paidVideosByUser.getOrDefault(user, Collections.emptySet()).contains(video);
}
public void subscribe(User user) {
subscriptionsByUser.put(user, true);
}
public boolean hasSubscribed(User user) {
return subscriptionsByUser.getOrDefault(user, false);
}
public void cancelSubscription(User user) {
subscriptionsByUser.put(user, false);
}
}
// Main class for Video Streaming Platform
public class VideoStreamingPlatform {
private UserCache userCache;
private VideoCache videoCache;
private PaymentProcessor paymentProcessor;
private RecommendationService recommendationService;
private PlaybackService playbackService;
private NotificationService notificationService;
public VideoStreamingPlatform() {
userCache = new UserCache();
videoCache = new VideoCache();
paymentProcessor = new PaymentProcessor();
recommendationService = new RecommendationService();
playbackService = new PlaybackService();
notificationService = new NotificationService();
}
public void signUpUser(User user) {
userCache.addUser(user);
}
public void uploadVideo(String userId, Video video) {
User user = userCache.getUser(userId);
if (user == null) {
throw new IllegalArgumentException("Invalid user id");
}
videoCache.addVideo(video);
notificationService.notifyVideoUploaded(user, video);
}
public void watchVideo(String userId, String videoId) {
User user = userCache.getUser(userId);
if (user == null) {
throw new IllegalArgumentException("Invalid user id");
}
Video video = videoCache.getVideo(videoId);
if (video == null) {
throw new IllegalArgumentException("Invalid video id");
}
playbackService.playVideo(user, video);
}
}
// PlaybackService handles playback with payment checks
public class PlaybackService {
public void playVideo(User user, Video video) {
if (video.isPaid() && !paymentProcessor.hasPaid(user, video)) {
throw new IllegalArgumentException("Payment required for video: " + video.getTitle());
}
if (video.isSubscription() && !paymentProcessor.hasSubscribed(user)) {
throw new IllegalArgumentException("Subscription required for video: " + video.getTitle());
}
user.watchVideo(video);
}
}
// Recommendation service
public class RecommendationService {
public List<Video> getRecommendations(User user) {
return videoCache.getAllVideos().stream()
.filter(video -> !user.getWatchedVideos().contains(video))
.sorted(Comparator.comparingDouble(Video::getAverageRating).reversed())
.collect(Collectors.toList());
}
}