You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为OTT平台实现单账号最多3台设备同时播放的限制功能

账号设备同时播放限制实现方案

核心逻辑在DRF侧做设备会话的统一管理、播放权限校验、过期会话自动清理,前后端配合上报状态,整体实现如下:


1. 后端DRF侧核心实现

1.1 新增设备会话模型

新增UserDeviceSession表存储用户登录设备的会话信息:

from django.db import models
from django.contrib.auth import get_user_model
from datetime import timedelta
from django.utils import timezone

User = get_user_model()

class UserDeviceSession(models.Model):
    DEVICE_TYPE_CHOICES = [
        ('web', '网页端'),
        ('ios', 'iOS移动端'),
        ('android', '安卓移动端'),
    ]
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='device_sessions')
    # 设备唯一标识:网页端用localStorage生成的唯一ID+UA摘要,RN端取原生设备ID
    device_id = models.CharField(max_length=255)
    # 登录后生成的会话唯一标识,前后端统一传递
    session_token = models.CharField(max_length=255, unique=True)
    device_type = models.CharField(max_length=10, choices=DEVICE_TYPE_CHOICES)
    last_active = models.DateTimeField(auto_now=True)
    is_playing = models.BooleanField(default=False)

    class Meta:
        # 同一用户同一设备仅生成一条会话,避免重复占用名额
        unique_together = ('user', 'device_id')

1.2 新增播放权限校验类

继承DRF的BasePermission,在用户请求播放资源时触发校验:

from rest_framework.permissions import BasePermission

class PlaybackLimitPermission(BasePermission):
    message = "当前账号同时播放设备数已达上限,请退出其他设备后重试"

    def has_permission(self, request, view):
        user = request.user
        # 仅对音视频播放接口做限制,其他接口直接放行
        if view.__class__.__name__ not in ['VideoPlayView', 'LiveStreamView']:
            return True
        
        # 从请求头取当前会话标识
        session_token = request.META.get('HTTP_X_SESSION_TOKEN')
        # 统计10分钟内有活跃记录、正在播放的有效会话
        active_play_sessions = UserDeviceSession.objects.filter(
            user=user,
            is_playing=True,
            last_active__gte=timezone.now() - timedelta(minutes=10)
        )

        # 达到3台上限时,仅放行当前会话已经在活跃列表的情况(用户自己刷新/切集操作)
        if active_play_sessions.count() >=3:
            return active_play_sessions.filter(session_token=session_token).exists()
        
        # 未达上限,更新当前会话播放状态后放行
        current_session = UserDeviceSession.objects.filter(user=user, session_token=session_token).first()
        if current_session:
            current_session.is_playing = True
            current_session.save()
        return True

把这个权限类加到播放接口的permission_classes列表里即可生效。

1.3 配套接口与定时任务

  • 新增心跳接口:前端每5分钟调用一次,携带session_token,后端更新对应会话的last_active时间
  • 新增会话下线接口:用户暂停/退出播放时调用,把对应会话的is_playing设为False,主动释放名额
  • 定时清理任务:用Celery/Django Q每15分钟跑一次,把超过30分钟无活跃的会话is_playing设为False,避免用户杀App/关页面没上报下线导致的名额占用

2. 前端侧适配实现

2.1 React网页端

  • 用户首次访问时在localStorage生成永久唯一的device_id,登录时传给后端
  • 登录成功后把后端返回的session_token存在本地,所有请求头统一携带X-Session-Token参数
  • 加5分钟定时任务调用心跳接口,用户退出登录/关闭播放页时调用下线接口

2.2 React Native端

  • 用react-native-device-info库获取设备原生唯一ID作为device_id,登录时传给后端
  • 其余逻辑和网页端一致,session_token存在AsyncStorage中,应用退到后台超过10分钟时主动调用下线接口

3. 可选优化功能

如果需要支持用户主动踢掉其他设备,可以加会话列表接口和踢下线接口:

  • 会话列表接口返回当前用户所有活跃的设备会话,展示设备类型、登录时间
  • 踢下线接口接收要删除的session_id,后端直接把对应会话的is_playing设为False,释放名额

内容的提问来源于stack exchange,提问作者Sanjay Sikdar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 03:09:05