Commit 869634d6 authored by Skia's avatar Skia
Browse files

Add thumbnail generation

parent 71d22e36
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('club', '0004_auto_20160915_1057'),
]
operations = [
migrations.AlterField(
model_name='club',
name='home',
field=models.OneToOneField(related_name='home_of_club', blank=True, on_delete=django.db.models.deletion.SET_NULL, verbose_name='home', null=True, to='core.SithFile'),
),
]
...@@ -36,7 +36,8 @@ class Club(models.Model): ...@@ -36,7 +36,8 @@ class Club(models.Model):
default=settings.SITH_GROUPS['root']['id']) default=settings.SITH_GROUPS['root']['id'])
edit_groups = models.ManyToManyField(Group, related_name="editable_club", blank=True) edit_groups = models.ManyToManyField(Group, related_name="editable_club", blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_club", blank=True) view_groups = models.ManyToManyField(Group, related_name="viewable_club", blank=True)
home = models.OneToOneField(SithFile, related_name='home_of_club', verbose_name=_("home"), null=True, blank=True) home = models.OneToOneField(SithFile, related_name='home_of_club', verbose_name=_("home"), null=True, blank=True,
on_delete=models.SET_NULL)
def check_loop(self): def check_loop(self):
"""Raise a validation error when a loop is found within the parent list""" """Raise a validation error when a loop is found within the parent list"""
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import core.models
class Migration(migrations.Migration):
dependencies = [
('core', '0008_sithfile_asked_for_removal'),
]
operations = [
migrations.AddField(
model_name='sithfile',
name='compressed',
field=models.FileField(upload_to=core.models.get_compressed_directory, null=True, verbose_name='compressed file', blank=True),
),
migrations.AddField(
model_name='sithfile',
name='thumbnail',
field=models.FileField(upload_to=core.models.get_thumbnail_directory, null=True, verbose_name='thumbnail', blank=True),
),
migrations.AlterField(
model_name='user',
name='home',
field=models.OneToOneField(verbose_name='home', related_name='home_of', on_delete=django.db.models.deletion.SET_NULL, null=True, to='core.SithFile', blank=True),
),
]
...@@ -115,7 +115,8 @@ class User(AbstractBaseUser): ...@@ -115,7 +115,8 @@ class User(AbstractBaseUser):
), ),
) )
groups = models.ManyToManyField(RealGroup, related_name='users', blank=True) groups = models.ManyToManyField(RealGroup, related_name='users', blank=True)
home = models.OneToOneField('SithFile', related_name='home_of', verbose_name=_("home"), null=True, blank=True) home = models.OneToOneField('SithFile', related_name='home_of', verbose_name=_("home"), null=True, blank=True,
on_delete=models.SET_NULL)
profile_pict = models.OneToOneField('SithFile', related_name='profile_of', verbose_name=_("profile"), null=True, profile_pict = models.OneToOneField('SithFile', related_name='profile_of', verbose_name=_("profile"), null=True,
blank=True, on_delete=models.SET_NULL) blank=True, on_delete=models.SET_NULL)
avatar_pict = models.OneToOneField('SithFile', related_name='avatar_of', verbose_name=_("avatar"), null=True, avatar_pict = models.OneToOneField('SithFile', related_name='avatar_of', verbose_name=_("avatar"), null=True,
...@@ -491,10 +492,18 @@ class Preferences(models.Model): ...@@ -491,10 +492,18 @@ class Preferences(models.Model):
def get_directory(instance, filename): def get_directory(instance, filename):
return '.{0}/{1}'.format(instance.get_parent_path(), filename) return '.{0}/{1}'.format(instance.get_parent_path(), filename)
def get_compressed_directory(instance, filename):
return '.{0}/compressed/{1}'.format(instance.get_parent_path(), filename)
def get_thumbnail_directory(instance, filename):
return '.{0}/thumbnail/{1}'.format(instance.get_parent_path(), filename)
class SithFile(models.Model): class SithFile(models.Model):
name = models.CharField(_('file name'), max_length=256, blank=False) name = models.CharField(_('file name'), max_length=256, blank=False)
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True) parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True)
file = models.FileField(upload_to=get_directory, verbose_name=_("file"), null=True, blank=True) file = models.FileField(upload_to=get_directory, verbose_name=_("file"), null=True, blank=True)
compressed = models.FileField(upload_to=get_compressed_directory, verbose_name=_("compressed file"), null=True, blank=True)
thumbnail = models.FileField(upload_to=get_thumbnail_directory, verbose_name=_("thumbnail"), null=True, blank=True)
owner = models.ForeignKey(User, related_name="owned_files", verbose_name=_("owner")) owner = models.ForeignKey(User, related_name="owned_files", verbose_name=_("owner"))
edit_groups = models.ManyToManyField(Group, related_name="editable_files", verbose_name=_("edit group"), blank=True) edit_groups = models.ManyToManyField(Group, related_name="editable_files", verbose_name=_("edit group"), blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_files", verbose_name=_("view group"), blank=True) view_groups = models.ManyToManyField(Group, related_name="viewable_files", verbose_name=_("view group"), blank=True)
...@@ -528,6 +537,10 @@ class SithFile(models.Model): ...@@ -528,6 +537,10 @@ class SithFile(models.Model):
for c in self.children.all(): for c in self.children.all():
c.delete() c.delete()
self.file.delete() self.file.delete()
if self.compressed:
self.compressed.delete()
if self.thumbnail:
self.thumbnail.delete()
return super(SithFile, self).delete() return super(SithFile, self).delete()
def clean(self): def clean(self):
...@@ -605,7 +618,7 @@ class SithFile(models.Model): ...@@ -605,7 +618,7 @@ class SithFile(models.Model):
return l return l
def get_parent_path(self): def get_parent_path(self):
return '/' + '/'.join([p.name for p in self.get_parent_list()]) return '/' + '/'.join([p.name for p in self.get_parent_list()[::-1]])
def get_display_name(self): def get_display_name(self):
return self.name return self.name
......
...@@ -13,7 +13,7 @@ def scale_dimension(width, height, long_edge): ...@@ -13,7 +13,7 @@ def scale_dimension(width, height, long_edge):
ratio = long_edge * 1. / height ratio = long_edge * 1. / height
return int(width * ratio), int(height * ratio) return int(width * ratio), int(height * ratio)
def resize_image(im, edge, format): # TODO move that into a utils file def resize_image(im, edge, format):
(w, h) = im.size (w, h) = im.size
(width, height) = scale_dimension(w, h, long_edge=edge) (width, height) = scale_dimension(w, h, long_edge=edge)
content = BytesIO() content = BytesIO()
......
...@@ -19,7 +19,7 @@ import os ...@@ -19,7 +19,7 @@ import os
from core.models import SithFile from core.models import SithFile
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, can_view, not_found from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, can_view, not_found
def send_file(request, file_id, file_class=SithFile): def send_file(request, file_id, file_class=SithFile, file_attr="file"):
""" """
Send a file through Django without loading the whole file into Send a file through Django without loading the whole file into
memory at once. The FileWrapper will turn the file object into an memory at once. The FileWrapper will turn the file object into an
...@@ -35,7 +35,7 @@ def send_file(request, file_id, file_class=SithFile): ...@@ -35,7 +35,7 @@ def send_file(request, file_id, file_class=SithFile):
Counter.objects.filter(token=request.session['counter_token']).exists()) Counter.objects.filter(token=request.session['counter_token']).exists())
): ):
raise PermissionDenied raise PermissionDenied
name = f.file.name name = f.__getattribute__(file_attr).name
with open(settings.MEDIA_ROOT + name, 'rb') as filename: with open(settings.MEDIA_ROOT + name, 'rb') as filename:
wrapper = FileWrapper(filename) wrapper = FileWrapper(filename)
response = HttpResponse(wrapper, content_type=f.mime_type) response = HttpResponse(wrapper, content_type=f.mime_type)
......
...@@ -24,6 +24,12 @@ class Picture(SithFile): ...@@ -24,6 +24,12 @@ class Picture(SithFile):
def get_download_url(self): def get_download_url(self):
return reverse('sas:download', kwargs={'picture_id': self.id}) return reverse('sas:download', kwargs={'picture_id': self.id})
def get_download_compressed_url(self):
return reverse('sas:download_compressed', kwargs={'picture_id': self.id})
def get_download_thumb_url(self):
return reverse('sas:download_thumb', kwargs={'picture_id': self.id})
def get_next(self): def get_next(self):
return self.parent.children.exclude(is_moderated=False, asked_for_removal=True).filter(id__gt=self.id).order_by('id').first() return self.parent.children.exclude(is_moderated=False, asked_for_removal=True).filter(id__gt=self.id).order_by('id').first()
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
{% if p.as_picture.can_be_viewed_by(user) %} {% if p.as_picture.can_be_viewed_by(user) %}
<div style="display: inline-block; border: solid 1px black; width: 9%; margin: 0.1%"> <div style="display: inline-block; border: solid 1px black; width: 9%; margin: 0.1%">
<a href="{{ url("sas:picture", picture_id=p.id) }}#pict"> <a href="{{ url("sas:picture", picture_id=p.id) }}#pict">
<img src="{{ p.as_picture.get_download_url() }}" alt="{{ p.get_display_name() }}" style="max-width: 100%"/> <img src="{{ p.as_picture.get_download_thumb_url() }}" alt="{{ p.get_display_name() }}" style="max-width: 100%"/>
</a> </a>
</div> </div>
{% endif %} {% endif %}
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
{% endif %} {% endif %}
<p> <p>
<a href="{{ url("sas:picture", picture_id=p.id) }}"> <a href="{{ url("sas:picture", picture_id=p.id) }}">
<img src="{{ url('sas:download', picture_id=p.id) }}" alt="{{ p.name }}" style="width: 100px"> <img src="{{ p.get_download_thumb_url() }}" alt="{{ p.name }}" style="width: 100px">
</a><br/> </a><br/>
{% trans %}Full name: {% endtrans %}{{ p.get_parent_path()+'/'+p.name }}<br/> {% trans %}Full name: {% endtrans %}{{ p.get_parent_path()+'/'+p.name }}<br/>
{% trans %}Owner: {% endtrans %}{{ p.owner.get_display_name() }}<br/> {% trans %}Owner: {% endtrans %}{{ p.owner.get_display_name() }}<br/>
......
...@@ -35,21 +35,21 @@ ...@@ -35,21 +35,21 @@
{{ print_path(picture.parent) }} {{ picture.get_display_name() }} {{ print_path(picture.parent) }} {{ picture.get_display_name() }}
<h3>{{ picture.get_display_name() }}</h3> <h3>{{ picture.get_display_name() }}</h3>
<div style="display: inline-block; width: 89%; background: #333;" id="pict"> <div style="display: inline-block; width: 89%; background: #333;" id="pict">
<img src="{{ picture.get_download_url() }}" alt="{{ picture.get_display_name() }}" style="width: 90%; display: block; margin: auto"/> <img src="{{ picture.get_download_compressed_url() }}" alt="{{ picture.get_display_name() }}" style="width: 90%; display: block; margin: auto"/>
</div> </div>
<div style="display: inline-block; width: 10%; vertical-align: top;"> <div style="display: inline-block; width: 10%; vertical-align: top;">
<div> <div>
<div id="prev"> <div id="prev">
{% if picture.get_previous() %} {% if picture.get_previous() %}
<a href="{{ url("sas:picture", picture_id=picture.get_previous().id) }}#pict"> <a href="{{ url("sas:picture", picture_id=picture.get_previous().id) }}#pict">
<img src="{{ picture.get_previous().as_picture.get_download_url() }}" alt="{{ picture.get_previous().get_display_name() }}" /> <img src="{{ picture.get_previous().as_picture.get_download_thumb_url() }}" alt="{{ picture.get_previous().get_display_name() }}" />
</a> </a>
{% endif %} {% endif %}
</div> </div>
<div id="next"> <div id="next">
{% if picture.get_next() %} {% if picture.get_next() %}
<a href="{{ url("sas:picture", picture_id=picture.get_next().id) }}#pict"> <a href="{{ url("sas:picture", picture_id=picture.get_next().id) }}#pict">
<img src="{{ picture.get_next().as_picture.get_download_url() }}" alt="{{ picture.get_next().get_display_name() }}" /> <img src="{{ picture.get_next().as_picture.get_download_thumb_url() }}" alt="{{ picture.get_next().get_display_name() }}" />
</a> </a>
{% endif %} {% endif %}
</div> </div>
...@@ -73,6 +73,9 @@ ...@@ -73,6 +73,9 @@
<p><input type="submit" value="{% trans %}Go{% endtrans %}" /></p> <p><input type="submit" value="{% trans %}Go{% endtrans %}" /></p>
</form> </form>
</div> </div>
<p>
<a href="{{ picture.get_download_url() }}">{% trans %}HD version{% endtrans %}</a>
</p>
<p style="font-size: smaller;"> <p style="font-size: smaller;">
<a href="?ask_removal">{% trans %}Ask for removal{% endtrans %}</a> <a href="?ask_removal">{% trans %}Ask for removal{% endtrans %}</a>
</p> </p>
......
...@@ -8,6 +8,8 @@ urlpatterns = [ ...@@ -8,6 +8,8 @@ urlpatterns = [
url(r'^album/(?P<album_id>[0-9]+)$', AlbumView.as_view(), name='album'), url(r'^album/(?P<album_id>[0-9]+)$', AlbumView.as_view(), name='album'),
url(r'^picture/(?P<picture_id>[0-9]+)$', PictureView.as_view(), name='picture'), url(r'^picture/(?P<picture_id>[0-9]+)$', PictureView.as_view(), name='picture'),
url(r'^picture/(?P<picture_id>[0-9]+)/download$', send_pict, name='download'), url(r'^picture/(?P<picture_id>[0-9]+)/download$', send_pict, name='download'),
url(r'^picture/(?P<picture_id>[0-9]+)/download/compressed$', send_compressed, name='download_compressed'),
url(r'^picture/(?P<picture_id>[0-9]+)/download/thumb$', send_thumb, name='download_thumb'),
# url(r'^album/new$', AlbumCreateView.as_view(), name='album_new'), # url(r'^album/new$', AlbumCreateView.as_view(), name='album_new'),
# url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'), # url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
] ]
......
...@@ -23,6 +23,9 @@ class SASForm(forms.Form): ...@@ -23,6 +23,9 @@ class SASForm(forms.Form):
required=False) required=False)
def process(self, parent, owner, files, automodere=False): def process(self, parent, owner, files, automodere=False):
from core.utils import resize_image
from io import BytesIO
from PIL import Image
try: try:
if self.cleaned_data['album_name'] != "": if self.cleaned_data['album_name'] != "":
album = Album(parent=parent, name=self.cleaned_data['album_name'], owner=owner, is_moderated=automodere) album = Album(parent=parent, name=self.cleaned_data['album_name'], owner=owner, is_moderated=automodere)
...@@ -36,8 +39,15 @@ class SASForm(forms.Form): ...@@ -36,8 +39,15 @@ class SASForm(forms.Form):
is_folder=False, is_moderated=automodere) is_folder=False, is_moderated=automodere)
try: try:
new_file.clean() new_file.clean()
# TODO: generate thumbnail im = Image.open(BytesIO(f.read()))
thumb = resize_image(im, 200, f.content_type.split('/')[-1])
compressed = resize_image(im, 600, f.content_type.split('/')[-1])
new_file.thumbnail = thumb
new_file.thumbnail.name = new_file.name
new_file.compressed = compressed
new_file.compressed.name = new_file.name
new_file.save() new_file.save()
print(new_file.compressed)
except Exception as e: except Exception as e:
self.add_error(None, _("Error uploading file %(file_name)s: %(msg)s") % {'file_name': f, 'msg': repr(e)}) self.add_error(None, _("Error uploading file %(file_name)s: %(msg)s") % {'file_name': f, 'msg': repr(e)})
...@@ -120,6 +130,12 @@ class PictureView(CanViewMixin, DetailView, FormMixin): ...@@ -120,6 +130,12 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
def send_pict(request, picture_id): def send_pict(request, picture_id):
return send_file(request, picture_id, Picture) return send_file(request, picture_id, Picture)
def send_compressed(request, picture_id):
return send_file(request, picture_id, Picture, "compressed")
def send_thumb(request, picture_id):
return send_file(request, picture_id, Picture, "thumbnail")
class AlbumView(CanViewMixin, DetailView, FormMixin): class AlbumView(CanViewMixin, DetailView, FormMixin):
model = Album model = Album
form_class = SASForm form_class = SASForm
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment