Use cropped covers/avatars to reduce bandwidth use

This commit is contained in:
Eliot Berriot 2018-07-18 15:37:07 +02:00
commit 979c554b4a
No known key found for this signature in database
GPG key ID: DD6965E2476E5C27
23 changed files with 161 additions and 39 deletions

View file

@ -19,7 +19,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
name = options["script_name"]
if not name:
self.show_help()
return self.show_help()
available_scripts = self.get_scripts()
try:

View file

@ -0,0 +1,6 @@
from . import create_image_variations
from . import django_permissions_to_user_permissions
from . import test
__all__ = ["create_image_variations", "django_permissions_to_user_permissions", "test"]

View file

@ -0,0 +1,30 @@
"""
Compute different sizes of image used for Album covers and User avatars
"""
from versatileimagefield.image_warmer import VersatileImageFieldWarmer
from funkwhale_api.music.models import Album
from funkwhale_api.users.models import User
MODELS = [(Album, "cover", "square"), (User, "avatar", "square")]
def main(command, **kwargs):
for model, attribute, key_set in MODELS:
qs = model.objects.exclude(**{"{}__isnull".format(attribute): True})
qs = qs.exclude(**{attribute: ""})
warmer = VersatileImageFieldWarmer(
instance_or_queryset=qs,
rendition_key_set=key_set,
image_attr=attribute,
verbose=True,
)
command.stdout.write(
"Creating images for {} / {}".format(model.__name__, attribute)
)
num_created, failed_to_create = warmer.warm()
command.stdout.write(
" {} created, {} in error".format(num_created, len(failed_to_create))
)

View file

@ -18,7 +18,11 @@ class TrackFavoriteViewSet(
):
serializer_class = serializers.UserTrackFavoriteSerializer
queryset = models.TrackFavorite.objects.all()
queryset = (
models.TrackFavorite.objects.all()
.select_related("track__artist", "track__album__artist", "user")
.prefetch_related("track__files")
)
permission_classes = [
permissions.ConditionalAuthentication,
permissions.OwnerPermission,

View file

@ -15,7 +15,9 @@ from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone
from taggit.managers import TaggableManager
from versatileimagefield.fields import VersatileImageField
from versatileimagefield.image_warmer import VersatileImageFieldWarmer
from funkwhale_api import downloader, musicbrainz
from funkwhale_api.federation import utils as federation_utils
@ -641,3 +643,13 @@ def update_request_status(sender, instance, created, **kwargs):
# let's mark the request as imported since the import is over
instance.import_request.status = "imported"
return instance.import_request.save(update_fields=["status"])
@receiver(models.signals.post_save, sender=Album)
def warm_album_covers(sender, instance, **kwargs):
if not instance.cover:
return
album_covers_warmer = VersatileImageFieldWarmer(
instance_or_queryset=instance, rendition_key_set="square", image_attr="cover"
)
num_created, failed_to_create = album_covers_warmer.warm()

View file

@ -1,6 +1,7 @@
from django.db.models import Q
from rest_framework import serializers
from taggit.models import Tag
from versatileimagefield.serializers import VersatileImageFieldSerializer
from funkwhale_api.activity import serializers as activity_serializers
from funkwhale_api.users.serializers import UserBasicSerializer
@ -8,8 +9,12 @@ from funkwhale_api.users.serializers import UserBasicSerializer
from . import models, tasks
cover_field = VersatileImageFieldSerializer(allow_null=True, sizes="square")
class ArtistAlbumSerializer(serializers.ModelSerializer):
tracks_count = serializers.SerializerMethodField()
cover = cover_field
class Meta:
model = models.Album
@ -87,6 +92,7 @@ class AlbumTrackSerializer(serializers.ModelSerializer):
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.SerializerMethodField()
artist = ArtistSimpleSerializer(read_only=True)
cover = cover_field
class Meta:
model = models.Album
@ -111,6 +117,7 @@ class AlbumSerializer(serializers.ModelSerializer):
class TrackAlbumSerializer(serializers.ModelSerializer):
artist = ArtistSimpleSerializer(read_only=True)
cover = cover_field
class Meta:
model = models.Album
@ -156,6 +163,8 @@ class TagSerializer(serializers.ModelSerializer):
class SimpleAlbumSerializer(serializers.ModelSerializer):
cover = cover_field
class Meta:
model = models.Album
fields = ("id", "mbid", "title", "release_date", "cover")

View file

@ -107,7 +107,7 @@ class PlaylistSerializer(serializers.ModelSerializer):
covers = []
max_covers = 5
for plt in plts:
url = plt.track.album.cover.url
url = plt.track.album.cover.crop["200x200"].url
if url in covers:
continue
covers.append(url)

View file

@ -11,12 +11,14 @@ import uuid
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from versatileimagefield.fields import VersatileImageField
from versatileimagefield.image_warmer import VersatileImageFieldWarmer
from funkwhale_api.common import fields, preferences
from funkwhale_api.common import utils as common_utils
@ -205,3 +207,13 @@ class Invitation(models.Model):
)
return super().save(**kwargs)
@receiver(models.signals.post_save, sender=User)
def warm_user_avatar(sender, instance, **kwargs):
if not instance.avatar:
return
user_avatar_warmer = VersatileImageFieldWarmer(
instance_or_queryset=instance, rendition_key_set="square", image_attr="avatar"
)
num_created, failed_to_create = user_avatar_warmer.warm()

View file

@ -45,15 +45,7 @@ class UserActivitySerializer(activity_serializers.ModelSerializer):
return "Person"
avatar_field = VersatileImageFieldSerializer(
allow_null=True,
sizes=[
("original", "url"),
("square_crop", "crop__400x400"),
("medium_square_crop", "crop__200x200"),
("small_square_crop", "crop__50x50"),
],
)
avatar_field = VersatileImageFieldSerializer(allow_null=True, sizes="square")
class UserBasicSerializer(serializers.ModelSerializer):