From 0eb76f940305b6030facb3641cebe7c28e9a6b8e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Jun 2021 10:47:25 +0800 Subject: [PATCH] =?UTF-8?q?=E7=82=B9=E6=92=AD=E5=90=8E=E5=8F=B0=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/apps/vod/migrations/0001_initial.py | 58 ++++++++++++++++ server/apps/vod/models.py | 23 +++--- server/apps/vod/serializers.py | 26 +++++++ server/apps/vod/urls.py | 12 ++-- server/apps/vod/views.py | 81 ++++++++++++++++++++-- server/apps/vod/vodclient.py | 37 ++++++++-- 6 files changed, 215 insertions(+), 22 deletions(-) create mode 100644 server/apps/vod/migrations/0001_initial.py create mode 100644 server/apps/vod/serializers.py diff --git a/server/apps/vod/migrations/0001_initial.py b/server/apps/vod/migrations/0001_initial.py new file mode 100644 index 0000000..af501b9 --- /dev/null +++ b/server/apps/vod/migrations/0001_initial.py @@ -0,0 +1,58 @@ +# Generated by Django 3.0.5 on 2021-06-28 01:59 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('system', '0018_auto_20210430_1156'), + ] + + operations = [ + migrations.CreateModel( + name='Video', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('name', models.CharField(max_length=100, verbose_name='视频名称')), + ('description', models.TextField(default='', verbose_name='视频描述')), + ('mediaurl', models.CharField(max_length=200, verbose_name='视频地址')), + ('coverurl', models.CharField(max_length=200, verbose_name='封面地址')), + ('duration', models.IntegerField(default=0, verbose_name='时长(秒)')), + ('views', models.IntegerField(default=0, verbose_name='观看次数')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='system.Dict', verbose_name='视频分类')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='video_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='video_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'verbose_name': '点播视频', + 'verbose_name_plural': '点播视频', + }, + ), + migrations.CreateModel( + name='ViewRecord', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('num', models.IntegerField(default=0, verbose_name='观看次数')), + ('current', models.IntegerField(default=0, verbose_name='当前观看进度')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='viewrecord_user', to=settings.AUTH_USER_MODEL, verbose_name='观看人')), + ('video', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='vod.Video', verbose_name='点播视频')), + ], + options={ + 'verbose_name': '点播观看记录', + 'verbose_name_plural': '点播观看记录', + }, + ), + ] diff --git a/server/apps/vod/models.py b/server/apps/vod/models.py index 3bb9165..10d6e64 100644 --- a/server/apps/vod/models.py +++ b/server/apps/vod/models.py @@ -6,21 +6,28 @@ from apps.system.models import User, CommonAModel, Dict class Video(CommonAModel): name = models.CharField(verbose_name='视频名称', max_length=100) - category = models.ForeignKey(Dict, verbose_name='视频分类') - description = models.TextField(verbose_name='视频描述') + category = models.ForeignKey(Dict, verbose_name='视频分类', on_delete=models.DO_NOTHING) + description = models.TextField(verbose_name='视频描述', default='') + mediaurl = models.CharField(verbose_name='视频地址', max_length=200) + coverurl = models.CharField(verbose_name='封面地址', max_length=200) + duration = models.IntegerField(verbose_name='时长(秒)', default=0) + views = models.IntegerField(verbose_name='观看次数', default=0) + + class Meta: + verbose_name = '点播视频' + verbose_name_plural = verbose_name + + -class WatchRecord(BaseModel): +class ViewRecord(BaseModel): # 观看记录 - user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='观看人') + user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='观看人', related_name='viewrecord_user') num = models.IntegerField(verbose_name='观看次数', default=0) - total = models.IntegerField(verbose_name='观看总时长', default=0) current = models.IntegerField(verbose_name='当前观看进度', default=0) + video = models.ForeignKey(Video, verbose_name='点播视频', on_delete=models.CASCADE) - vod_id = models.CharField(verbose_name='视频id', max_length=100) - vod_title = models.CharField(verbose_name='视频标题', max_length=100) - vod_cover = models.CharField(verbose_name='视频封面地址', max_length=200) class Meta: verbose_name = '点播观看记录' diff --git a/server/apps/vod/serializers.py b/server/apps/vod/serializers.py new file mode 100644 index 0000000..d2dac6e --- /dev/null +++ b/server/apps/vod/serializers.py @@ -0,0 +1,26 @@ +from rest_framework import serializers +from .models import Video, ViewRecord +from apps.system.serializers import UserSimpleSerializer + +class VideoSerializer(serializers.ModelSerializer): + category_name = serializers.StringRelatedField(source='category', read_only=True) + class Meta: + model = Video + fields = '__all__' + +class VideoSimpleSerializer(serializers.ModelSerializer): + class Meta: + model = Video + fields = ['id', 'name', 'duration'] + +class VRecordSerializer(serializers.ModelSerializer): + user_ = UserSimpleSerializer(source='user', read_only=True) + video_ = VideoSimpleSerializer(source='video', read_only=True) + class Meta: + model = ViewRecord + fields = '__all__' + +class VRecordUpdateSerializer(serializers.ModelSerializer): + class Meta: + model = ViewRecord + fields=['num', 'current'] \ No newline at end of file diff --git a/server/apps/vod/urls.py b/server/apps/vod/urls.py index c20f119..c110b3e 100644 --- a/server/apps/vod/urls.py +++ b/server/apps/vod/urls.py @@ -1,9 +1,13 @@ +from django.db.models import base from django.urls import path, include -from .views import ClassView, VideoView +from .views import ClassView, SignatureAPIView, VideoView, VideoViewSet, VRecordViewSet, MyViewRecordAPIView from rest_framework import routers - +router = routers.DefaultRouter() +router.register('video', VideoViewSet, basename="video") +router.register('viewrecord', VRecordViewSet, basename='viewrecord') urlpatterns = [ - path('class/', ClassView.as_view()), - path('video/', VideoView.as_view()) + path('', include(router.urls)), + path('video//myview/', MyViewRecordAPIView.as_view()), + path('signature/', SignatureAPIView.as_view()), ] diff --git a/server/apps/vod/views.py b/server/apps/vod/views.py index 9ee0196..374cf32 100644 --- a/server/apps/vod/views.py +++ b/server/apps/vod/views.py @@ -1,8 +1,16 @@ +from rest_framework.mixins import ListModelMixin +from apps.vod.serializers import VRecordSerializer, VRecordUpdateSerializer, VideoSerializer +from apps.vod.models import Video, ViewRecord from django.shortcuts import render -from .vodclient import getAllClass, searchMedia +from .vodclient import getAllClass, searchMedia, getSignature from rest_framework.views import APIView from rest_framework.response import Response -from rest_framework.viewsets import ModelViewSet +from rest_framework.viewsets import GenericViewSet, ModelViewSet +from utils.pagination import PageOrNot +from apps.system.mixins import CreateUpdateModelAMixin +from rest_framework.decorators import action, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.status import HTTP_400_BAD_REQUEST # Create your views here. class ClassView(APIView): @@ -28,5 +36,70 @@ class VideoView(APIView): params['ClassIds']= request.query_params.getlist('classid') if request.query_params.get('classid') else [] return searchMedia(params) -class RecordViewSet(ModelViewSet): - pass \ No newline at end of file +class VideoViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): + perms_map={'get':'video_view', 'post':'video_create', 'put':'video_update', 'delete':'video_delete'} + queryset = Video.objects.all() + search_fields = ['name', 'category__name', 'description'] + filterset_fields = ['category'] + serializer_class = VideoSerializer + ordering = ['-views', '-create_time'] + + @action(methods=['get'], detail=False, perms_map={'get':'video_view'}) + def myview(self, request, *args, **kwargs): + """ + 个人观看记录 + """ + queryset = ViewRecord.objects.filter(user=request.user) + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + + +class VRecordViewSet(ListModelMixin, GenericViewSet): + perms_map = {'get':'viewrecord_view'} + queryset = ViewRecord.objects.all() + search_fields = ['user__name', 'video__name'] + serializer_class = VRecordSerializer + ordering = ['-update_time'] + + + +class MyViewRecordAPIView(APIView): + + permission_classes = [IsAuthenticated] + def get(self, request, id , format=None): + """ + 该视频的本人观看信息 + """ + try: + video = Video.objects.get(pk=id) + except: + return Response('视频不存在', status=HTTP_400_BAD_REQUEST) + record = ViewRecord.objects.get_or_create(video=video, user=request.user, defaults={'video':video, 'user':request.user}) + serializer = VRecordSerializer(instance=record) + return Response(serializer.data) + + def put(self, request, id, format=None): + """ + 更新该视频本人的观看信息 + """ + try: + video = Video.objects.get(pk=id) + except: + return Response('视频不存在', status=HTTP_400_BAD_REQUEST) + record = ViewRecord.objects.get_or_create(video=video, user=request.user, defaults={'video':video, 'user':request.user}) + serializer = VRecordUpdateSerializer(record, data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data) + +class SignatureAPIView(APIView): + perms_map={'get':'video_create'} + def get(self, request, format=None): + """ + 获取上传签名 + """ + return Response(getSignature(procedure=request.query_params.get('procedure', None))) diff --git a/server/apps/vod/vodclient.py b/server/apps/vod/vodclient.py index dec2c81..f630a80 100644 --- a/server/apps/vod/vodclient.py +++ b/server/apps/vod/vodclient.py @@ -1,11 +1,19 @@ +import base64 +import hashlib +import hmac import json -from tencentcloud.common import credential -from tencentcloud.common.profile.client_profile import ClientProfile -from tencentcloud.common.profile.http_profile import HttpProfile -from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException -from tencentcloud.vod.v20180717 import vod_client, models +import random +import time + from rest_framework.response import Response from rest_framework.status import HTTP_400_BAD_REQUEST +from tencentcloud.common import credential +from tencentcloud.common.exception.tencent_cloud_sdk_exception import \ + TencentCloudSDKException +from tencentcloud.common.profile.client_profile import ClientProfile +from tencentcloud.common.profile.http_profile import HttpProfile +from tencentcloud.vod.v20180717 import models, vod_client + SecretId = 'AKIDhDTaV9WeXAXcQxEH4buwg9LGvJQHr9Au' SecretKey = 'VjfKjrhGYrte8MIxMUMxHL9h39zYqrc6' @@ -45,4 +53,21 @@ def searchMedia(params={}): resp = client.SearchMedia(req) return doResponse(resp) except TencentCloudSDKException as err: - print(err) \ No newline at end of file + print(err) + +def getSignature(procedure=None): + """ + 获取上传签名 + 可指定 + """ + TimeStamp = int(time.time()) + ExpireTime = TimeStamp + 86400 * 365 * 10 + Random = random.randint(0, 999999) + Original = "secretId=" + SecretId + "¤tTimeStamp=" + str(TimeStamp) + "&expireTime=" + str(ExpireTime) + "&random=" + str(Random) + if procedure: + Original = Original + "&procedure="+procedure + Hmac = hmac.new(bytes(SecretKey, 'utf-8'), bytes(Original, 'utf-8'), hashlib.sha1) + Sha1 = Hmac.digest() + Signature = bytes(Sha1) + bytes(Original, 'utf-8') + Signature2 = base64.b64encode(Signature) + return str(Signature2)