164 lines
5.5 KiB
Python
164 lines
5.5 KiB
Python
import os
|
|
import uuid
|
|
from django.db import models
|
|
from core.models.base import BaseModel
|
|
from core.models.session import ServiceSession, ProjectSession
|
|
|
|
|
|
def _service_session_video_upload_to(instance: "ServiceSessionVideo", filename: str) -> str:
|
|
"""Upload path for service session videos."""
|
|
base, ext = os.path.splitext(filename)
|
|
ext = ext.lower() or ".mp4"
|
|
sid = instance.service_session_id or "unassigned"
|
|
return f"videos/service_session/{sid}/{uuid.uuid4().hex}{ext}"
|
|
|
|
|
|
def _service_session_video_thumb_upload_to(instance: "ServiceSessionVideo", _filename: str) -> str:
|
|
"""Upload path for service session video thumbnails."""
|
|
sid = instance.service_session_id or "unassigned"
|
|
return f"videos/service_session/{sid}/thumb/{uuid.uuid4().hex}.jpg"
|
|
|
|
|
|
def _project_session_video_upload_to(instance: "ProjectSessionVideo", filename: str) -> str:
|
|
"""Upload path for project session videos."""
|
|
base, ext = os.path.splitext(filename)
|
|
ext = ext.lower() or ".mp4"
|
|
sid = instance.project_session_id or "unassigned"
|
|
return f"videos/project_session/{sid}/{uuid.uuid4().hex}{ext}"
|
|
|
|
|
|
def _project_session_video_thumb_upload_to(instance: "ProjectSessionVideo", _filename: str) -> str:
|
|
"""Upload path for project session video thumbnails."""
|
|
sid = instance.project_session_id or "unassigned"
|
|
return f"videos/project_session/{sid}/thumb/{uuid.uuid4().hex}.jpg"
|
|
|
|
|
|
class Video(BaseModel):
|
|
"""
|
|
Abstract base for video-bearing models.
|
|
|
|
Features:
|
|
- Stores original video file with metadata
|
|
- Optional thumbnail image (can be extracted from video or uploaded separately)
|
|
- Captures dimensions, duration, file size, and content_type
|
|
- Tracks the uploading team profile (optional)
|
|
- Storage-agnostic (respects DEFAULT_FILE_STORAGE)
|
|
"""
|
|
title = models.CharField(max_length=255, blank=True)
|
|
video = models.FileField(upload_to="videos/") # Override in subclasses
|
|
thumbnail = models.ImageField(upload_to="videos/thumbs/", blank=True, null=True)
|
|
content_type = models.CharField(max_length=100, blank=True)
|
|
|
|
# Video-specific metadata
|
|
duration_seconds = models.PositiveIntegerField(
|
|
default=0,
|
|
help_text="Video duration in seconds"
|
|
)
|
|
file_size_bytes = models.PositiveBigIntegerField(
|
|
default=0,
|
|
help_text="File size in bytes"
|
|
)
|
|
width = models.PositiveIntegerField(default=0)
|
|
height = models.PositiveIntegerField(default=0)
|
|
|
|
uploaded_by_team_profile = models.ForeignKey(
|
|
'TeamProfile',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name="%(class)s_videos"
|
|
)
|
|
notes = models.TextField(blank=True)
|
|
internal = models.BooleanField(default=True)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
ordering = ('-created_at',)
|
|
|
|
def __str__(self) -> str:
|
|
return self.title or str(self.id)
|
|
|
|
def save(self, *args, **kwargs):
|
|
"""
|
|
Save and capture file size on creation.
|
|
Video metadata (duration, dimensions) should be set before save
|
|
by the upload handler using video processing utilities.
|
|
"""
|
|
if self._state.adding and self.video and hasattr(self.video, 'size'):
|
|
self.file_size_bytes = self.video.size
|
|
super().save(*args, **kwargs)
|
|
|
|
def delete(self, *args, **kwargs):
|
|
"""
|
|
Delete the model and its associated files from storage.
|
|
"""
|
|
# Store file names before delete
|
|
video_name = self.video.name if self.video else None
|
|
thumbnail_name = self.thumbnail.name if self.thumbnail else None
|
|
|
|
# Delete the model instance
|
|
super().delete(*args, **kwargs)
|
|
|
|
# Delete files from storage
|
|
if video_name:
|
|
try:
|
|
self.video.storage.delete(video_name)
|
|
except Exception:
|
|
pass # File may already be deleted or inaccessible
|
|
|
|
if thumbnail_name:
|
|
try:
|
|
self.thumbnail.storage.delete(thumbnail_name)
|
|
except Exception:
|
|
pass # File may already be deleted or inaccessible
|
|
|
|
|
|
class ServiceSessionVideo(Video):
|
|
"""Video attached to a ServiceSession for documentation."""
|
|
service_session = models.ForeignKey(
|
|
ServiceSession,
|
|
on_delete=models.PROTECT,
|
|
related_name='service_session_videos'
|
|
)
|
|
video = models.FileField(upload_to=_service_session_video_upload_to)
|
|
thumbnail = models.ImageField(
|
|
upload_to=_service_session_video_thumb_upload_to,
|
|
blank=True,
|
|
null=True
|
|
)
|
|
|
|
def __str__(self) -> str:
|
|
return self.title or f"ServiceSessionVideo {self.id}"
|
|
|
|
class Meta:
|
|
ordering = ('-created_at',)
|
|
indexes = [
|
|
models.Index(fields=['service_session', 'created_at']),
|
|
models.Index(fields=['created_at']),
|
|
]
|
|
|
|
|
|
class ProjectSessionVideo(Video):
|
|
"""Video attached to a ProjectSession for documentation."""
|
|
project_session = models.ForeignKey(
|
|
ProjectSession,
|
|
on_delete=models.PROTECT,
|
|
related_name='project_session_videos'
|
|
)
|
|
video = models.FileField(upload_to=_project_session_video_upload_to)
|
|
thumbnail = models.ImageField(
|
|
upload_to=_project_session_video_thumb_upload_to,
|
|
blank=True,
|
|
null=True
|
|
)
|
|
|
|
def __str__(self) -> str:
|
|
return self.title or f"ProjectSessionVideo {self.id}"
|
|
|
|
class Meta:
|
|
ordering = ('-created_at',)
|
|
indexes = [
|
|
models.Index(fields=['project_session', 'created_at']),
|
|
models.Index(fields=['created_at']),
|
|
]
|