在我的專案中,我有一個視頻部分,我想在其中計算用戶觀看時間的百分比。通過以下網址可以訪問視頻的詳細資訊
網址:視頻/video_id 輸出:
"video": {
"id": "84e7288c-dc09-44aa-850c-08546a98ffde",
"deleted": null,
"datetime_created": "02/04/2022 06:56 AM",
"datetime_updated": "02/04/2022 06:56 AM",
"video_name": "video name3",
"description": "description about video",
"duration": "00:33:20",
"create_date": "02/04/2022 06:56 AM",
"video_type": "micro",
"file_url": "https://vimeo.com/216763352",
"general_status": "high",
"review_status": "draft",
"video_number": "VD6129",
"created_by": null
},
"duration": "00:33:20" 是視頻的總時長。如果以總秒數傳遞時間,如何計算用戶正在觀看的視頻時間百分比
{
"time":200
}
uj5u.com熱心網友回復:
好吧,我最近做了類似的事情,我的任務是計算視頻觀看時間并相應地給予分數。我還被要求不要給出觀看視頻時間的百分比和分數。我使用合并演算法來合并重疊間隔。您可以根據您的任務要求自定義代碼,因為您可能不會像我一樣被要求給用戶積分并進行獨特的 100% 準確計算,這是我所做的:
模型.py
from django.db import models
from django.apps import apps
from django.db.models import Sum, F, Subquery
from django.db.models.functions import Coalesce
from django.utils.translation import ugettext_lazy as _
from django.dispatch import receiver
from django.db.models.signals import post_save
from common.models import BaseUser
from helpers.models import BaseModel
class LessonTypeChoices(models.TextChoices):
video = "video", _("Video")
task = "task", _("Task")
exam = "exam", _("Exam")
book = "book", _("Book")
audiobook = "audiobook", _("Audio book")
class Lesson(BaseModel):
course = models.ForeignKey(
"courses.Course",
on_delete=models.CASCADE,
related_name="lessons",
verbose_name=_("course"),
)
type = models.CharField(_("type"), max_length=32, choices=LessonTypeChoices.choices)
title = models.CharField(_("title"), max_length=256)
description = models.TextField(_("description"))
points = models.IntegerField(_("points"), default=0)
order = models.IntegerField(_("order"), default=0)
def __str__(self):
return f"{self.title} - {self.type}"
class Meta:
db_table = "lesson"
verbose_name = _("lesson")
verbose_name_plural = _("lessons")
@receiver(post_save, sender=Lesson)
def add_course_point(sender, instance, created, **kwargs):
"""
Increment course points by lesson points when lesson is created
"""
Course = apps.get_model("courses.Course")
Course.objects.filter(id=instance.course_id).update(
points=Subquery(
Course.objects.filter(id=instance.course_id)
.annotate(sum_points=Sum("lessons__points"))
.values("sum_points")
)
)
class LessonProgress(BaseModel):
lesson = models.ForeignKey(
Lesson,
on_delete=models.CASCADE,
related_name="lesson_progress",
verbose_name=_("lesson"),
)
user = models.ForeignKey(
BaseUser,
on_delete=models.CASCADE,
related_name="lesson_progress",
verbose_name=_("user"),
)
type = models.CharField(_("type"), max_length=32, choices=LessonTypeChoices.choices)
percentage = models.FloatField(_("progress percentage"), default=0)
points = models.FloatField(_("points"), default=0)
is_locked = models.BooleanField(_("lesson is locked"), default=True)
is_completed = models.BooleanField(_("lesson is completed"), default=False)
last_progress = models.IntegerField(_("last progress"), default=0)
class Meta:
unique_together = ("lesson", "user")
db_table = "lesson_progress"
verbose_name = _("lesson progress")
verbose_name_plural = _("lesson progress")
def set_locked(self, instance, user):
"""
Update lessons' locked status
"""
percentage = self.get_percentage(instance, user)
for index, lesson in enumerate(instance.lessons.all().order_by("order")):
if index in [0, 1] or percentage / ((index 1) - 2) >= 80:
lesson.lesson_progress.update(is_locked=False)
else:
lesson.lesson_progress.update(is_locked=True)
@staticmethod
def get_percentage(instance, user):
"""
Calculate average percentage of completed lessons
"""
percentage = (
instance.lessons.all()
.aggregate(
percentage=Coalesce(
Sum(
"lesson_progress__percentage",
filter=models.Q(lesson_progress__user=user),
),
0,
)
)
.get("percentage")
)
return percentage
@staticmethod
def is_complete(percentage):
"""
decide whether lesson is completed or not
"""
if percentage >= 90:
return True
return False
@staticmethod
def calculate_percentage(length, progress):
"""
Calculate the watched video or submitted exam answer percentage
"""
return progress / length * 100
@staticmethod
def calculate_points(
instance,
percentage,
):
"""
Calculate earned points by lesson progress percentage
"""
return instance.points / 100 * percentage
@staticmethod
def user_points(user, points, is_expired):
"""
Add points to user
"""
if not is_expired:
user.score = points
user.save()
@staticmethod
def task_progress_points(instance, percentage, is_completed, points, is_expired):
"""
Update task lesson progress -> percentage, is_completed, points
"""
instance.percentage = percentage
instance.is_completed = is_completed
if not is_expired:
instance.points = points
instance.save()
@staticmethod
def exam_video_progress_points(
instance, percentage, last_progress, is_completed, points, is_expired
):
"""
Update exam video lesson progress -> percentage, last_progress, is_completed, points
"""
instance.percentage = percentage
instance.last_progress = last_progress
instance.is_completed = is_completed
if not is_expired:
instance.points = points
instance.save()
@staticmethod
def merge(intervals, progress_before):
"""
merge queryset with each other, remove overlapping intervals and create
"""
bulk_list = []
for progress in intervals:
# merge overlapping intervals
if bulk_list and progress.start_progress <= bulk_list[-1].end_progress:
bulk_list[-1].end_progress = max(
bulk_list[-1].end_progress, progress.end_progress
)
bulk_list[-1].progress = (
max(bulk_list[-1].end_progress, progress.end_progress)
- bulk_list[0].start_progress
)
bulk_list[-1].video_length = max(
bulk_list[-1].video_length, progress.video_length
)
else:
# add merged interval object to bulk_list
bulk_list.append(
LessonVideoProgress(
lesson_progress_id=progress.lesson_progress_id,
video_length=progress.video_length,
start_progress=progress.start_progress,
end_progress=progress.end_progress,
progress=progress.end_progress - progress.start_progress,
)
)
# delete all interval objects
intervals.delete()
# create intervals from bulk_list
LessonVideoProgress.objects.bulk_create(bulk_list)
# calculate the duration seconds of new intervals
progress_after = intervals.aggregate(progress_sum=Sum("progress")).get(
"progress_sum"
)
# get the watched duration seconds -> subtracting old progress seconds by new progress seconds
return float(progress_after - progress_before)
@staticmethod
def progress_before_merge(queryset):
# calculate the progress duration seconds sum before creating new video progress instance
return queryset.aggregate(progress_sum=Coalesce(Sum("progress"), 0)).get(
"progress_sum"
)
@staticmethod
def create_lesson_video_progress(
progress_id, video_length, start_progress, end_progress
):
# create new video progress instance
LessonVideoProgress.objects.create(
lesson_progress_id=progress_id,
video_length=video_length,
start_progress=start_progress,
end_progress=end_progress,
)
@staticmethod
def round_percentage(percentage, progress_percentage):
# round the given percentages to 100 in case it exceeds it
return percentage - ((progress_percentage percentage) - 100)
@receiver(post_save, sender=LessonProgress)
def run_lessons(sender, instance, **kwargs):
if instance.pk:
instance.set_locked(instance.lesson.course, instance.user)
class LessonVideoProgress(BaseModel):
lesson_progress = models.ForeignKey(
LessonProgress, on_delete=models.CASCADE, related_name="lesson_video_progress"
)
video_length = models.DecimalField(
_("video length"), max_digits=19, decimal_places=3
)
progress = models.DecimalField(
_("progress duration"), default=0, max_digits=19, decimal_places=3
)
start_progress = models.DecimalField(
_("start progress"), max_digits=19, decimal_places=3
)
end_progress = models.DecimalField(
_("end progress"), max_digits=19, decimal_places=3
)
class Meta:
db_table = "lesson_video_progress"
verbose_name = _("lesson video progress")
verbose_name_plural = _("lesson video progress")
def __str__(self):
return f"{self.progress}"
視圖.py
class LessonVideoExamSubmitView(generics.GenericAPIView):
queryset = LessonProgress.objects.all()
serializer_class = serializers.LessonVideoExamSubmitSerializer
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(
data=request.data, context={"request": request}
)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
序列化程式.py
class LessonVideoExamSubmitSerializer(serializers.Serializer):
lesson_id = serializers.IntegerField()
video_length = serializers.FloatField()
start_progress = serializers.FloatField()
end_progress = serializers.FloatField()
is_expired = serializers.BooleanField(default=False)
def create(self, validated_data):
# get lesson progress for given lesson id
progress = LessonProgress.objects.get(
lesson_id=validated_data.get("lesson_id"), user=self.context["request"].user
)
# get the current video progress before merging with other progresses
progress_before_merge = progress.progress_before_merge(
progress.lesson_video_progress
)
# create new video progress for given intervals
progress.create_lesson_video_progress(
progress.id,
validated_data.get("video_length"),
validated_data.get("start_progress"),
validated_data.get("end_progress"),
)
# merge created video progress objects with existing objects in the database
progress_merge = progress.merge(
progress.lesson_video_progress.all().order_by("start_progress"),
progress_before_merge,
)
# pass video length and merged objects duration in seconds and get watched video percentage
percentage = progress.calculate_percentage(
validated_data.get("video_length"), progress_merge
)
if progress.percentage percentage >= 100:
"""
add progress percentage and calculated percentage, round the sum to 100
"""
percentage = progress.round_percentage(percentage, progress.percentage)
# get points to the lesson by passing the percentage
points = progress.calculate_points(progress.lesson, percentage)
# add points to user
progress.user_points(progress.user, points, validated_data.get("is_expired"))
# get completed status by sum of progress percentages
is_completed = progress.is_complete(progress.percentage percentage)
# update lesson progress object
progress.exam_video_progress_points(
progress,
percentage,
validated_data.get("end_progress"),
is_completed,
points,
validated_data.get("is_expired"),
)
return validated_data
請注意,您可以為自己的視頻計算邏輯優化和洗掉不必要的部分。這也可以用于音頻計算邏輯。我通常不分享源代碼,但只是因為我真的很想幫忙,我做到了,干杯)
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/438680.html
