##// END OF EJS Templates
Autodetect attachment viewers by getting all abstract viewer subclasses
neko259 -
r1286:7f80fc70 default
parent child Browse files
Show More
@@ -1,77 +1,71 b''
1 import hashlib
1 import hashlib
2 import os
2 import os
3 import time
3 import time
4
4
5 from random import random
5 from random import random
6
6
7 from django.db import models
7 from django.db import models
8
8
9 from boards.models.attachment.viewers import AbstractViewer, WebmViewer, \
9 from boards.models.attachment.viewers import get_viewers, AbstractViewer
10 AudioViewer, SvgViewer
10
11
11
12 FILES_DIRECTORY = 'files/'
12 FILES_DIRECTORY = 'files/'
13 FILE_EXTENSION_DELIMITER = '.'
13 FILE_EXTENSION_DELIMITER = '.'
14
14
15 VIEWERS = (
16 WebmViewer,
17 AudioViewer,
18 SvgViewer,
19 )
20
21
15
22 class AttachmentManager(models.Manager):
16 class AttachmentManager(models.Manager):
23 def create_with_hash(self, file):
17 def create_with_hash(self, file):
24 file_hash = self.get_hash(file)
18 file_hash = self.get_hash(file)
25 existing = self.filter(hash=file_hash)
19 existing = self.filter(hash=file_hash)
26 if len(existing) > 0:
20 if len(existing) > 0:
27 attachment = existing[0]
21 attachment = existing[0]
28 else:
22 else:
29 file_type = file.name.split(FILE_EXTENSION_DELIMITER)[-1].lower()
23 file_type = file.name.split(FILE_EXTENSION_DELIMITER)[-1].lower()
30 attachment = Attachment.objects.create(file=file,
24 attachment = Attachment.objects.create(file=file,
31 mimetype=file_type, hash=file_hash)
25 mimetype=file_type, hash=file_hash)
32
26
33 return attachment
27 return attachment
34
28
35 def get_hash(self, file):
29 def get_hash(self, file):
36 """
30 """
37 Gets hash of an file.
31 Gets hash of an file.
38 """
32 """
39 md5 = hashlib.md5()
33 md5 = hashlib.md5()
40 for chunk in file.chunks():
34 for chunk in file.chunks():
41 md5.update(chunk)
35 md5.update(chunk)
42 return md5.hexdigest()
36 return md5.hexdigest()
43
37
44
38
45 class Attachment(models.Model):
39 class Attachment(models.Model):
46 objects = AttachmentManager()
40 objects = AttachmentManager()
47
41
48 # TODO Dedup the method
42 # TODO Dedup the method
49 def _update_filename(self, filename):
43 def _update_filename(self, filename):
50 """
44 """
51 Gets unique filename
45 Gets unique filename
52 """
46 """
53
47
54 # TODO Use something other than random number in file name
48 # TODO Use something other than random number in file name
55 new_name = '{}{}.{}'.format(
49 new_name = '{}{}.{}'.format(
56 str(int(time.mktime(time.gmtime()))),
50 str(int(time.mktime(time.gmtime()))),
57 str(int(random() * 1000)),
51 str(int(random() * 1000)),
58 filename.split(FILE_EXTENSION_DELIMITER)[-1:][0])
52 filename.split(FILE_EXTENSION_DELIMITER)[-1:][0])
59
53
60 return os.path.join(FILES_DIRECTORY, new_name)
54 return os.path.join(FILES_DIRECTORY, new_name)
61
55
62 file = models.FileField(upload_to=_update_filename)
56 file = models.FileField(upload_to=_update_filename)
63 mimetype = models.CharField(max_length=50)
57 mimetype = models.CharField(max_length=50)
64 hash = models.CharField(max_length=36)
58 hash = models.CharField(max_length=36)
65
59
66 def get_view(self):
60 def get_view(self):
67 file_viewer = None
61 file_viewer = None
68 for viewer in VIEWERS:
62 for viewer in get_viewers():
69 if viewer.supports(self.mimetype):
63 if viewer.supports(self.mimetype):
70 file_viewer = viewer(self.file, self.mimetype)
64 file_viewer = viewer(self.file, self.mimetype)
71 break
65 break
72 if file_viewer is None:
66 if file_viewer is None:
73 file_viewer = AbstractViewer(self.file, self.mimetype)
67 file_viewer = AbstractViewer(self.file, self.mimetype)
74
68
75 return file_viewer.get_view()
69 return file_viewer.get_view()
76
70
77
71
@@ -1,64 +1,68 b''
1 from django.template.defaultfilters import filesizeformat
1 from django.template.defaultfilters import filesizeformat
2 from django.templatetags.static import static
2 from django.templatetags.static import static
3
3
4 FILE_STUB_IMAGE = 'images/file.png'
4 FILE_STUB_IMAGE = 'images/file.png'
5
5
6 FILE_TYPE_WEBM = 'webm'
6 FILE_TYPE_WEBM = 'webm'
7 FILE_TYPE_SVG = 'svg'
7 FILE_TYPE_SVG = 'svg'
8 FILE_TYPES_AUDIO = (
8 FILE_TYPES_AUDIO = (
9 'ogg',
9 'ogg',
10 'mp3',
10 'mp3',
11 )
11 )
12
12
13
13
14 def get_viewers():
15 return AbstractViewer.__subclasses__()
16
17
14 class AbstractViewer:
18 class AbstractViewer:
15 def __init__(self, file, file_type):
19 def __init__(self, file, file_type):
16 self.file = file
20 self.file = file
17 self.file_type = file_type
21 self.file_type = file_type
18
22
19 @staticmethod
23 @staticmethod
20 def supports(file_type):
24 def supports(file_type):
21 return True
25 return True
22
26
23 def get_view(self):
27 def get_view(self):
24 return '<div class="image"><a href="{}">'\
28 return '<div class="image"><a href="{}">'\
25 '<img src="{}" width="200" height="150"/>'\
29 '<img src="{}" width="200" height="150"/>'\
26 '</a>'\
30 '</a>'\
27 '<div class="image-metadata">{}, {}</div>'\
31 '<div class="image-metadata">{}, {}</div>'\
28 '</div>'.format(self.file.url, static(FILE_STUB_IMAGE),
32 '</div>'.format(self.file.url, static(FILE_STUB_IMAGE),
29 self.file_type, filesizeformat(self.file.size))
33 self.file_type, filesizeformat(self.file.size))
30
34
31
35
32 class WebmViewer(AbstractViewer):
36 class WebmViewer(AbstractViewer):
33 @staticmethod
37 @staticmethod
34 def supports(file_type):
38 def supports(file_type):
35 return file_type == FILE_TYPE_WEBM
39 return file_type == FILE_TYPE_WEBM
36
40
37 def get_view(self):
41 def get_view(self):
38 return '<div class="image">'\
42 return '<div class="image">'\
39 '<video width="200" height="150" controls src="{}"/>'\
43 '<video width="200" height="150" controls src="{}"/>'\
40 '</div>'.format(self.file.url)
44 '</div>'.format(self.file.url)
41
45
42
46
43 class AudioViewer(AbstractViewer):
47 class AudioViewer(AbstractViewer):
44 @staticmethod
48 @staticmethod
45 def supports(file_type):
49 def supports(file_type):
46 return file_type in FILE_TYPES_AUDIO
50 return file_type in FILE_TYPES_AUDIO
47
51
48 def get_view(self):
52 def get_view(self):
49 return '<div class="image">' \
53 return '<div class="image">' \
50 '<audio controls src="{}"/>' \
54 '<audio controls src="{}"/>' \
51 '</div>'.format(self.file.url)
55 '</div>'.format(self.file.url)
52
56
53
57
54 class SvgViewer(AbstractViewer):
58 class SvgViewer(AbstractViewer):
55 @staticmethod
59 @staticmethod
56 def supports(file_type):
60 def supports(file_type):
57 return file_type == FILE_TYPE_SVG
61 return file_type == FILE_TYPE_SVG
58
62
59 def get_view(self):
63 def get_view(self):
60 return '<div class="image">'\
64 return '<div class="image">'\
61 '<a class="thumb" href="{}">'\
65 '<a class="thumb" href="{}">'\
62 '<img class="post-image-preview" width="200" height="150" src="{}"/>'\
66 '<img class="post-image-preview" width="200" height="150" src="{}"/>'\
63 '</a>'\
67 '</a>'\
64 '</div>'.format(self.file.url, self.file.url)
68 '</div>'.format(self.file.url, self.file.url)
General Comments 0
You need to be logged in to leave comments. Login now