187 lines
5.4 KiB
Python
187 lines
5.4 KiB
Python
import mimetypes
|
|
import os.path
|
|
|
|
from django.conf import settings
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
from django.core.validators import FileExtensionValidator, MinValueValidator
|
|
from django.db import models
|
|
from django.dispatch import Signal
|
|
from django.urls import reverse
|
|
from django.utils.translation import gettext_lazy as _
|
|
from taggit.managers import TaggableManager
|
|
from wagtail.models import CollectionMember, ReferenceIndex
|
|
from wagtail.search import index
|
|
from wagtail.search.queryset import SearchableQuerySetMixin
|
|
|
|
from wagtailmedia.settings import wagtailmedia_settings
|
|
|
|
|
|
ALLOWED_EXTENSIONS_THUMBNAIL = ["gif", "jpg", "jpeg", "png", "webp"]
|
|
|
|
|
|
class MediaType(models.TextChoices):
|
|
AUDIO = "audio", _("Audio file")
|
|
VIDEO = "video", _("Video file")
|
|
|
|
|
|
class MediaQuerySet(SearchableQuerySetMixin, models.QuerySet):
|
|
pass
|
|
|
|
|
|
class AbstractMedia(CollectionMember, index.Indexed, models.Model):
|
|
title = models.CharField(max_length=255, verbose_name=_("title"))
|
|
file = models.FileField(upload_to="media", verbose_name=_("file"))
|
|
|
|
type = models.CharField(
|
|
choices=MediaType.choices, max_length=255, blank=False, null=False
|
|
)
|
|
duration = models.FloatField(
|
|
blank=True,
|
|
default=0,
|
|
validators=[MinValueValidator(0)],
|
|
verbose_name=_("duration"),
|
|
help_text=_("Duration in seconds"),
|
|
)
|
|
width = models.PositiveIntegerField(null=True, blank=True, verbose_name=_("width"))
|
|
height = models.PositiveIntegerField(
|
|
null=True, blank=True, verbose_name=_("height")
|
|
)
|
|
thumbnail = models.FileField(
|
|
upload_to="media_thumbnails", blank=True, verbose_name=_("thumbnail")
|
|
)
|
|
|
|
created_at = models.DateTimeField(verbose_name=_("created at"), auto_now_add=True)
|
|
uploaded_by_user = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
verbose_name=_("uploaded by user"),
|
|
null=True,
|
|
blank=True,
|
|
editable=False,
|
|
on_delete=models.SET_NULL,
|
|
)
|
|
|
|
tags = TaggableManager(help_text=None, blank=True, verbose_name=_("tags"))
|
|
|
|
objects = MediaQuerySet.as_manager()
|
|
|
|
search_fields = CollectionMember.search_fields + [
|
|
index.SearchField("title", boost=10),
|
|
index.AutocompleteField("title", boost=10),
|
|
index.FilterField("title"),
|
|
index.RelatedFields(
|
|
"tags",
|
|
[
|
|
index.SearchField("name", boost=10),
|
|
index.AutocompleteField("name", boost=10),
|
|
],
|
|
),
|
|
index.FilterField("uploaded_by_user"),
|
|
]
|
|
|
|
admin_form_fields = (
|
|
"title",
|
|
"file",
|
|
"collection",
|
|
"duration",
|
|
"width",
|
|
"height",
|
|
"thumbnail",
|
|
"tags",
|
|
)
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
@property
|
|
def icon(self):
|
|
return f"wagtailmedia-{self.type}"
|
|
|
|
@property
|
|
def filename(self):
|
|
return os.path.basename(self.file.name)
|
|
|
|
@property
|
|
def thumbnail_filename(self) -> str:
|
|
return os.path.basename(self.thumbnail.name) if self.thumbnail else ""
|
|
|
|
@property
|
|
def file_extension(self):
|
|
return os.path.splitext(self.filename)[1][1:]
|
|
|
|
@property
|
|
def url(self):
|
|
return self.file.url
|
|
|
|
@property
|
|
def sources(self):
|
|
return [
|
|
{
|
|
"src": self.url,
|
|
"type": mimetypes.guess_type(self.filename)[0]
|
|
or "application/octet-stream",
|
|
}
|
|
]
|
|
|
|
def get_usage(self):
|
|
return ReferenceIndex.get_references_to(self).group_by_source_object()
|
|
|
|
@property
|
|
def usage_url(self):
|
|
return reverse("wagtailmedia:media_usage", args=(self.id,))
|
|
|
|
def is_editable_by_user(self, user):
|
|
from wagtailmedia.permissions import permission_policy
|
|
|
|
return permission_policy.user_has_permission_for_instance(user, "change", self)
|
|
|
|
def clean(self, *args, **kwargs):
|
|
super().clean(*args, **kwargs)
|
|
if not self.duration:
|
|
self.duration = 0
|
|
|
|
if self.thumbnail:
|
|
validate = FileExtensionValidator(ALLOWED_EXTENSIONS_THUMBNAIL)
|
|
validate(self.thumbnail)
|
|
|
|
if self.type == "audio" and wagtailmedia_settings.AUDIO_EXTENSIONS:
|
|
validate = FileExtensionValidator(wagtailmedia_settings.AUDIO_EXTENSIONS)
|
|
validate(self.file)
|
|
elif self.type == "video" and wagtailmedia_settings.VIDEO_EXTENSIONS:
|
|
validate = FileExtensionValidator(wagtailmedia_settings.VIDEO_EXTENSIONS)
|
|
validate(self.file)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
verbose_name = _("media")
|
|
verbose_name_plural = _("media items")
|
|
|
|
|
|
class Media(AbstractMedia):
|
|
pass
|
|
|
|
|
|
def get_media_model():
|
|
from django.apps import apps
|
|
|
|
from wagtailmedia.settings import wagtailmedia_settings
|
|
|
|
try:
|
|
app_label, model_name = wagtailmedia_settings.MEDIA_MODEL.split(".")
|
|
except AttributeError:
|
|
return Media
|
|
except ValueError as err:
|
|
raise ImproperlyConfigured(
|
|
"WAGTAILMEDIA[\"MEDIA_MODEL\"] must be of the form 'app_label.model_name'"
|
|
) from err
|
|
|
|
media_model = apps.get_model(app_label, model_name)
|
|
if media_model is None:
|
|
raise ImproperlyConfigured(
|
|
f"WAGTAILMEDIA[\"MEDIA_MODEL\"] refers to model '{wagtailmedia_settings.MEDIA_MODEL}' that has not been installed"
|
|
)
|
|
|
|
return media_model
|
|
|
|
|
|
# Provides `request` as an argument
|
|
media_served = Signal()
|