operations_project/apps/operation/views.py

786 lines
31 KiB
Python
Raw Normal View History

2025-05-13 11:58:17 +08:00
from django.shortcuts import render
import json
import uuid
import logging
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.conf import settings
from django.utils import timezone
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.db.models import Q
import os
from .models import OperatorAccount, PlatformAccount, Video
from .serializers import (
OperatorAccountSerializer, PlatformAccountSerializer, VideoSerializer,
2025-05-20 15:57:10 +08:00
MultiPlatformAccountSerializer
2025-05-13 11:58:17 +08:00
)
from .pagination import CustomPagination
logger = logging.getLogger(__name__)
class OperatorAccountViewSet(viewsets.ModelViewSet):
"""运营账号管理视图集"""
queryset = OperatorAccount.objects.all()
serializer_class = OperatorAccountSerializer
pagination_class = CustomPagination
def list(self, request, *args, **kwargs):
"""获取运营账号列表"""
queryset = self.filter_queryset(self.get_queryset())
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({
"code": 200,
"message": "获取运营账号列表成功",
"data": serializer.data
})
def retrieve(self, request, *args, **kwargs):
"""获取运营账号详情"""
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response({
"code": 200,
"message": "获取运营账号详情成功",
"data": serializer.data
})
def update(self, request, *args, **kwargs):
"""更新运营账号信息"""
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response({
"code": 200,
"message": "更新运营账号信息成功",
"data": serializer.data
})
def partial_update(self, request, *args, **kwargs):
"""部分更新运营账号信息"""
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
2025-05-20 15:57:10 +08:00
"""创建运营账号"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response({
"code": 200,
"message": "运营账号创建成功",
"data": serializer.data
}, status=status.HTTP_201_CREATED)
2025-05-13 11:58:17 +08:00
def destroy(self, request, *args, **kwargs):
2025-05-20 15:57:10 +08:00
"""删除运营账号"""
2025-05-13 11:58:17 +08:00
operator = self.get_object()
operator.is_active = False # 软删除
operator.save()
return Response({
"code": 200,
2025-05-20 15:57:10 +08:00
"message": "运营账号已停用",
2025-05-13 11:58:17 +08:00
"data": None
})
class PlatformAccountViewSet(viewsets.ModelViewSet):
"""平台账号管理视图集"""
queryset = PlatformAccount.objects.all()
serializer_class = PlatformAccountSerializer
pagination_class = CustomPagination
def list(self, request, *args, **kwargs):
"""获取平台账号列表"""
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
2025-05-20 15:57:10 +08:00
# 处理数据结构
response_data = serializer.data
restructured_data = []
for account_data in response_data:
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": account_data.pop("platform_name"),
"account_url": account_data.pop("account_url"),
"account_id": account_data.pop("account_id"),
"account_name": account_data.pop("account_name")
}
# 添加platforms字段作为数组
account_data["platforms"] = [platform_info]
# 保留用户传入的name字段如果没有则使用platform_name
if not account_data.get("name"):
account_data["name"] = platform_info["platform_name"]
restructured_data.append(account_data)
# 使用自定义分页器的响应,但替换数据
return self.get_paginated_response(restructured_data)
2025-05-13 11:58:17 +08:00
serializer = self.get_serializer(queryset, many=True)
2025-05-20 15:57:10 +08:00
# 处理数据结构
response_data = serializer.data
restructured_data = []
for account_data in response_data:
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": account_data.pop("platform_name"),
"account_url": account_data.pop("account_url"),
"account_id": account_data.pop("account_id"),
"account_name": account_data.pop("account_name")
}
# 添加platforms字段作为数组
account_data["platforms"] = [platform_info]
# 保留用户传入的name字段如果没有则使用platform_name
if not account_data.get("name"):
account_data["name"] = platform_info["platform_name"]
restructured_data.append(account_data)
2025-05-13 11:58:17 +08:00
return Response({
"code": 200,
"message": "获取平台账号列表成功",
2025-05-20 15:57:10 +08:00
"data": restructured_data
2025-05-13 11:58:17 +08:00
})
def retrieve(self, request, *args, **kwargs):
"""获取平台账号详情"""
instance = self.get_object()
serializer = self.get_serializer(instance)
2025-05-20 15:57:10 +08:00
# 处理数据结构
account_data = serializer.data
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": account_data.pop("platform_name"),
"account_url": account_data.pop("account_url"),
"account_id": account_data.pop("account_id"),
"account_name": account_data.pop("account_name")
}
# 添加platforms字段
account_data["platforms"] = [platform_info]
# 保留用户传入的name字段如果没有则使用platform_name
if not account_data.get("name"):
account_data["name"] = platform_info["platform_name"]
2025-05-13 11:58:17 +08:00
return Response({
"code": 200,
"message": "获取平台账号详情成功",
2025-05-20 15:57:10 +08:00
"data": account_data
2025-05-13 11:58:17 +08:00
})
def update(self, request, *args, **kwargs):
"""更新平台账号信息"""
partial = kwargs.pop('partial', False)
instance = self.get_object()
2025-05-20 15:57:10 +08:00
# 单独处理platforms字段多平台信息需要特殊处理
data = request.data.copy()
platforms_data = None
if 'platforms' in data and isinstance(data['platforms'], list):
platforms_data = data.pop('platforms')
# 如果有platforms数据先将第一个平台的信息移至顶层保证基本平台信息能正常更新
if platforms_data and len(platforms_data) > 0:
first_platform = platforms_data[0]
if 'platform_name' in first_platform:
data['platform_name'] = first_platform['platform_name']
if 'account_url' in first_platform:
data['account_url'] = first_platform['account_url']
if 'account_id' in first_platform:
data['account_id'] = first_platform['account_id']
if 'account_name' in first_platform:
data['account_name'] = first_platform['account_name']
serializer = self.get_serializer(instance, data=data, partial=partial)
2025-05-13 11:58:17 +08:00
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
2025-05-20 15:57:10 +08:00
# 处理多平台信息
# 这里我们需要实现新的逻辑来处理额外的平台
# 注意:这需要修改模型结构或添加关联模型来支持多平台
# 由于当前模型结构限制,我们暂时只能在此记录其他平台数据
# 用于未来扩展,目前会在日志中记录这些信息
if platforms_data and len(platforms_data) > 1:
logger = logging.getLogger(__name__)
logger.info(f"接收到多平台数据,但当前版本仅支持一个平台。额外平台数据: {platforms_data[1:]}")
# 这里应该添加创建关联平台记录的代码
# 例如:
# for platform_data in platforms_data[1:]:
# RelatedPlatform.objects.create(
# primary_account=instance,
# platform_name=platform_data.get('platform_name', ''),
# account_id=platform_data.get('account_id', ''),
# account_name=platform_data.get('account_name', ''),
# account_url=platform_data.get('account_url', '')
# )
# 处理数据结构
account_data = serializer.data
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": account_data.pop("platform_name"),
"account_url": account_data.pop("account_url"),
"account_id": account_data.pop("account_id"),
"account_name": account_data.pop("account_name")
}
# 添加platforms字段
# 如果有platforms_data使用原始请求中的数据否则使用当前的单平台数据
if platforms_data:
account_data["platforms"] = platforms_data
else:
account_data["platforms"] = [platform_info]
# 保留用户传入的name字段如果没有则使用platform_name
if not account_data.get("name"):
account_data["name"] = platform_info["platform_name"]
2025-05-13 11:58:17 +08:00
return Response({
"code": 200,
"message": "更新平台账号信息成功",
2025-05-20 15:57:10 +08:00
"data": account_data
2025-05-13 11:58:17 +08:00
})
def partial_update(self, request, *args, **kwargs):
"""部分更新平台账号信息"""
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
2025-05-20 15:57:10 +08:00
"""创建平台账号"""
# 传统单平台账号创建流程
print(f"{request.data}")
2025-05-13 11:58:17 +08:00
with transaction.atomic():
# 处理operator字段可能是字符串类型的ID
data = request.data.copy()
if 'operator' in data and isinstance(data['operator'], str):
try:
# 尝试通过ID查找运营账号
operator_id = data['operator']
try:
# 先尝试通过整数ID查找
operator_id_int = int(operator_id)
operator = OperatorAccount.objects.get(id=operator_id_int)
except (ValueError, OperatorAccount.DoesNotExist):
# 如果无法转换为整数或找不到对应账号,尝试通过用户名或真实姓名查找
operator = OperatorAccount.objects.filter(
Q(username=operator_id) | Q(real_name=operator_id)
).first()
if not operator:
return Response({
"code": 404,
"message": f"未找到运营账号: {operator_id}请提供有效的ID、用户名或真实姓名",
"data": None
}, status=status.HTTP_404_NOT_FOUND)
# 更新请求数据中的operator字段为找到的operator的ID
data['operator'] = operator.id
except Exception as e:
return Response({
"code": 400,
"message": f"处理运营账号ID时出错: {str(e)}",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 创建平台账号
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
2025-05-20 15:57:10 +08:00
# 创建平台账号
self.perform_create(serializer)
# 处理响应数据
account_data = serializer.data
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": account_data.pop("platform_name"),
"account_url": account_data.pop("account_url"),
"account_id": account_data.pop("account_id"),
"account_name": account_data.pop("account_name")
}
# 添加platforms字段
account_data["platforms"] = [platform_info]
# 保留用户传入的name字段如果没有则使用platform_name
if not account_data.get("name"):
account_data["name"] = platform_info["platform_name"]
2025-05-13 11:58:17 +08:00
return Response({
"code": 200,
2025-05-20 15:57:10 +08:00
"message": "平台账号创建成功",
"data": account_data
2025-05-13 11:58:17 +08:00
}, status=status.HTTP_201_CREATED)
def destroy(self, request, *args, **kwargs):
2025-05-20 15:57:10 +08:00
"""删除平台账号"""
2025-05-13 11:58:17 +08:00
platform_account = self.get_object()
self.perform_destroy(platform_account)
return Response({
"code": 200,
2025-05-20 15:57:10 +08:00
"message": "平台账号已删除",
2025-05-13 11:58:17 +08:00
"data": None
})
@action(detail=True, methods=['post'])
def update_followers(self, request, pk=None):
2025-05-20 15:57:10 +08:00
"""更新平台账号粉丝数"""
2025-05-13 11:58:17 +08:00
platform_account = self.get_object()
followers_count = request.data.get('followers_count')
if not followers_count:
return Response({
"code": 400,
"message": "粉丝数不能为空",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 更新粉丝数
platform_account.followers_count = followers_count
platform_account.save()
2025-05-20 15:57:10 +08:00
# 准备响应数据,与其他方法保持一致
platform_data = self.get_serializer(platform_account).data
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": platform_data.pop("platform_name"),
"account_url": platform_data.pop("account_url"),
"account_id": platform_data.pop("account_id"),
"account_name": platform_data.pop("account_name")
}
# 添加platforms字段
platform_data["platforms"] = [platform_info]
# 保留用户传入的name字段如果没有则使用platform_name
if not platform_data.get("name"):
platform_data["name"] = platform_info["platform_name"]
2025-05-13 11:58:17 +08:00
return Response({
"code": 200,
"message": "粉丝数更新成功",
2025-05-20 15:57:10 +08:00
"data": platform_data
2025-05-13 11:58:17 +08:00
})
@action(detail=True, methods=['post'])
def update_profile(self, request, pk=None):
"""更新平台账号的头像、标签和最后发布时间"""
platform_account = self.get_object()
# 获取更新的资料数据
profile_data = {}
# 处理标签
if 'tags' in request.data:
2025-05-20 15:57:10 +08:00
# 处理tags支持字符串或数组格式
tags = request.data['tags']
if isinstance(tags, list):
profile_data['tags'] = ','.join(tags)
else:
profile_data['tags'] = tags
2025-05-13 11:58:17 +08:00
# 处理头像
if 'profile_image' in request.data:
profile_data['profile_image'] = request.data['profile_image']
# 处理最后发布时间
if 'last_posting' in request.data:
try:
# 尝试解析时间字符串
from dateutil import parser
last_posting = parser.parse(request.data['last_posting'])
profile_data['last_posting'] = last_posting
except Exception as e:
return Response({
"code": 400,
"message": f"最后发布时间格式错误: {str(e)}",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
2025-05-20 15:57:10 +08:00
# 处理名称
if 'name' in request.data:
profile_data['name'] = request.data['name']
2025-05-13 11:58:17 +08:00
if not profile_data:
return Response({
"code": 400,
"message": "没有提供任何需更新的资料数据",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 更新平台账号资料
for field, value in profile_data.items():
setattr(platform_account, field, value)
platform_account.save()
2025-05-20 15:57:10 +08:00
# 准备响应数据,与其他方法保持一致
platform_data = self.get_serializer(platform_account).data
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": platform_data.pop("platform_name"),
"account_url": platform_data.pop("account_url"),
"account_id": platform_data.pop("account_id"),
"account_name": platform_data.pop("account_name")
}
# 添加platforms字段
platform_data["platforms"] = [platform_info]
# 保留用户传入的name字段如果没有则使用platform_name
if not platform_data.get("name"):
platform_data["name"] = platform_info["platform_name"]
2025-05-13 11:58:17 +08:00
return Response({
"code": 200,
"message": "平台账号资料更新成功",
2025-05-20 15:57:10 +08:00
"data": platform_data
2025-05-13 11:58:17 +08:00
})
class VideoViewSet(viewsets.ModelViewSet):
"""视频管理视图集"""
queryset = Video.objects.all()
serializer_class = VideoSerializer
pagination_class = CustomPagination
def list(self, request, *args, **kwargs):
"""获取视频列表"""
queryset = self.filter_queryset(self.get_queryset())
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({
"code": 200,
"message": "获取视频列表成功",
"data": serializer.data
})
def retrieve(self, request, *args, **kwargs):
"""获取视频详情"""
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response({
"code": 200,
"message": "获取视频详情成功",
"data": serializer.data
})
def update(self, request, *args, **kwargs):
"""更新视频信息"""
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response({
"code": 200,
"message": "更新视频信息成功",
"data": serializer.data
})
def partial_update(self, request, *args, **kwargs):
"""部分更新视频信息"""
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
2025-05-20 15:57:10 +08:00
"""创建视频"""
2025-05-13 11:58:17 +08:00
with transaction.atomic():
# 处理platform_account字段可能是字符串类型的ID
data = request.data.copy()
if 'platform_account' in data and isinstance(data['platform_account'], str):
try:
# 尝试通过ID查找平台账号
platform_id = data['platform_account']
try:
# 先尝试通过整数ID查找
platform_id_int = int(platform_id)
platform = PlatformAccount.objects.get(id=platform_id_int)
except (ValueError, PlatformAccount.DoesNotExist):
# 如果无法转换为整数或找不到对应账号尝试通过账号名称或账号ID查找
platform = PlatformAccount.objects.filter(
Q(account_name=platform_id) | Q(account_id=platform_id)
).first()
if not platform:
return Response({
"code": 404,
"message": f"未找到平台账号: {platform_id}请提供有效的ID、账号名称或账号ID",
"data": None
}, status=status.HTTP_404_NOT_FOUND)
# 更新请求数据中的platform_account字段为找到的platform的ID
data['platform_account'] = platform.id
except Exception as e:
return Response({
"code": 400,
"message": f"处理平台账号ID时出错: {str(e)}",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 创建视频
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
2025-05-20 15:57:10 +08:00
# 创建视频
self.perform_create(serializer)
2025-05-13 11:58:17 +08:00
return Response({
"code": 200,
2025-05-20 15:57:10 +08:00
"message": "视频创建成功",
"data": serializer.data
2025-05-13 11:58:17 +08:00
}, status=status.HTTP_201_CREATED)
def destroy(self, request, *args, **kwargs):
2025-05-20 15:57:10 +08:00
"""删除视频记录"""
2025-05-13 11:58:17 +08:00
video = self.get_object()
self.perform_destroy(video)
return Response({
"code": 200,
2025-05-20 15:57:10 +08:00
"message": "视频记录已删除",
2025-05-13 11:58:17 +08:00
"data": None
})
@action(detail=True, methods=['post'])
def update_stats(self, request, pk=None):
2025-05-20 15:57:10 +08:00
"""更新视频统计数据"""
2025-05-13 11:58:17 +08:00
video = self.get_object()
# 获取更新的统计数据
stats = {}
for field in ['views_count', 'likes_count', 'comments_count', 'shares_count']:
if field in request.data:
stats[field] = request.data[field]
if not stats:
return Response({
"code": 400,
"message": "没有提供任何统计数据",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 更新视频统计数据
for field, value in stats.items():
setattr(video, field, value)
video.save()
return Response({
"code": 200,
"message": "视频统计数据更新成功",
"data": {
"id": video.id,
"title": video.title,
"views_count": video.views_count,
"likes_count": video.likes_count,
"comments_count": video.comments_count,
"shares_count": video.shares_count
}
})
@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
"""发布视频并更新状态"""
video = self.get_object()
# 检查视频状态
if video.status not in ['draft', 'scheduled']:
return Response({
"code": 400,
"message": f"当前视频状态为 {video.get_status_display()},无法发布",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 获取视频URL
video_url = request.data.get('video_url')
if not video_url:
return Response({
"code": 400,
"message": "未提供视频URL",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 更新视频状态和URL
video.video_url = video_url
video.status = 'published'
video.publish_time = timezone.now()
video.save()
return Response({
"code": 200,
"message": "视频已成功发布",
"data": {
"id": video.id,
"title": video.title,
"status": video.status,
"video_url": video.video_url,
"publish_time": video.publish_time
}
})
@action(detail=False, methods=['post'])
def upload_video(self, request):
"""上传视频文件并创建视频记录"""
try:
# 获取上传的视频文件
video_file = request.FILES.get('video_file')
if not video_file:
return Response({
"code": 400,
"message": "未提供视频文件",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 获取平台账号ID
platform_account_id = request.data.get('platform_account')
if not platform_account_id:
return Response({
"code": 400,
"message": "未提供平台账号ID",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
try:
platform_account = PlatformAccount.objects.get(id=platform_account_id)
except PlatformAccount.DoesNotExist:
return Response({
"code": 404,
"message": f"未找到ID为{platform_account_id}的平台账号",
"data": None
}, status=status.HTTP_404_NOT_FOUND)
# 创建保存视频的目录
import os
from django.conf import settings
# 确保文件保存目录存在
media_root = getattr(settings, 'MEDIA_ROOT', os.path.join(settings.BASE_DIR, 'media'))
videos_dir = os.path.join(media_root, 'videos')
account_dir = os.path.join(videos_dir, f"{platform_account.platform_name}_{platform_account.account_name}")
if not os.path.exists(videos_dir):
os.makedirs(videos_dir)
if not os.path.exists(account_dir):
os.makedirs(account_dir)
# 生成唯一的文件名
import time
timestamp = int(time.time())
file_name = f"{timestamp}_{video_file.name}"
file_path = os.path.join(account_dir, file_name)
# 保存视频文件
with open(file_path, 'wb+') as destination:
for chunk in video_file.chunks():
destination.write(chunk)
# 创建视频记录
video_data = {
'platform_account': platform_account,
'title': request.data.get('title', os.path.splitext(video_file.name)[0]),
'description': request.data.get('description', ''),
'local_path': file_path,
'status': 'draft',
'tags': request.data.get('tags', '')
}
# 创建视频记录
video = Video.objects.create(**video_data)
return Response({
"code": 200,
"message": "视频上传成功",
"data": {
"id": video.id,
"title": video.title,
"status": video.get_status_display(),
}
}, status=status.HTTP_201_CREATED)
except Exception as e:
logger.error(f"视频上传失败: {str(e)}")
return Response({
"code": 500,
"message": f"视频上传失败: {str(e)}",
"data": None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['post'])
def manual_publish(self, request, pk=None):
"""手动发布视频"""
video = self.get_object()
# 检查视频状态是否允许发布
if video.status not in ['draft', 'scheduled']:
return Response({
"code": 400,
"message": f"当前视频状态为 {video.get_status_display()},无法发布",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 检查视频文件是否存在
2025-05-20 15:57:10 +08:00
if video.local_path and not os.path.exists(video.local_path):
2025-05-13 11:58:17 +08:00
return Response({
"code": 400,
"message": "视频文件不存在,无法发布",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
try:
2025-05-20 15:57:10 +08:00
# 获取视频URL如果没有提供则创建一个模拟的URL
video_url = request.data.get('video_url')
2025-05-13 11:58:17 +08:00
2025-05-20 15:57:10 +08:00
if not video_url:
# 创建模拟的视频URL和ID
platform_account = video.platform_account
platform_name = platform_account.platform_name
video_url = f"https://example.com/{platform_name}/{video.id}"
2025-05-13 11:58:17 +08:00
# 更新视频状态
video.status = 'published'
video.publish_time = timezone.now()
video.video_url = video_url
2025-05-20 15:57:10 +08:00
video.video_id = f"VID_{video.id}"
2025-05-13 11:58:17 +08:00
video.save()
logger.info(f"视频 {video.id} 已手动发布")
return Response({
"code": 200,
"message": "视频发布成功",
"data": {
"id": video.id,
"title": video.title,
"status": "published",
"video_url": video_url,
"publish_time": video.publish_time.strftime("%Y-%m-%d %H:%M:%S")
}
})
except Exception as e:
logger.error(f"手动发布视频失败: {str(e)}")
return Response({
"code": 500,
"message": f"发布失败: {str(e)}",
"data": None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)