|
@@
-1,186
+1,192
b''
|
|
1
|
import re
|
|
1
|
import re
|
|
2
|
|
|
2
|
|
|
3
|
from django.contrib.staticfiles import finders
|
|
3
|
from django.contrib.staticfiles import finders
|
|
4
|
from django.contrib.staticfiles.templatetags.staticfiles import static
|
|
4
|
from django.contrib.staticfiles.templatetags.staticfiles import static
|
|
5
|
from django.core.files.images import get_image_dimensions
|
|
5
|
from django.core.files.images import get_image_dimensions
|
|
6
|
from django.template.defaultfilters import filesizeformat
|
|
6
|
from django.template.defaultfilters import filesizeformat
|
|
7
|
|
|
7
|
|
|
8
|
from boards.models.attachment.domains import URL_DOMAINS
|
|
|
|
|
9
|
|
|
8
|
|
|
10
|
REGEX_DOMAIN = re.compile(r'(\w+\.)*(\w+\.\w+)')
|
|
9
|
REGEX_DOMAIN = re.compile(r'(\w+\.)*(\w+\.\w+)')
|
|
11
|
|
|
10
|
|
|
12
|
FILE_STUB_IMAGE = 'images/file.png'
|
|
11
|
FILE_STUB_IMAGE = 'images/file.png'
|
|
13
|
FILE_STUB_URL = 'url'
|
|
12
|
FILE_STUB_URL = 'url'
|
|
14
|
|
|
13
|
|
|
15
|
FILE_TYPES_VIDEO = (
|
|
14
|
FILE_TYPES_VIDEO = (
|
|
16
|
'webm',
|
|
15
|
'webm',
|
|
17
|
'mp4',
|
|
16
|
'mp4',
|
|
18
|
'mpeg',
|
|
17
|
'mpeg',
|
|
19
|
'ogv',
|
|
18
|
'ogv',
|
|
20
|
)
|
|
19
|
)
|
|
21
|
FILE_TYPE_SVG = 'svg'
|
|
20
|
FILE_TYPE_SVG = 'svg'
|
|
22
|
FILE_TYPES_AUDIO = (
|
|
21
|
FILE_TYPES_AUDIO = (
|
|
23
|
'ogg',
|
|
22
|
'ogg',
|
|
24
|
'mp3',
|
|
23
|
'mp3',
|
|
25
|
'opus',
|
|
24
|
'opus',
|
|
26
|
)
|
|
25
|
)
|
|
27
|
FILE_TYPES_IMAGE = (
|
|
26
|
FILE_TYPES_IMAGE = (
|
|
28
|
'jpeg',
|
|
27
|
'jpeg',
|
|
29
|
'jpg',
|
|
28
|
'jpg',
|
|
30
|
'png',
|
|
29
|
'png',
|
|
31
|
'bmp',
|
|
30
|
'bmp',
|
|
32
|
'gif',
|
|
31
|
'gif',
|
|
33
|
)
|
|
32
|
)
|
|
34
|
|
|
33
|
|
|
35
|
PLAIN_FILE_FORMATS = {
|
|
34
|
PLAIN_FILE_FORMATS = {
|
|
36
|
'pdf': 'pdf',
|
|
35
|
'pdf': 'pdf',
|
|
37
|
'djvu': 'djvu',
|
|
36
|
'djvu': 'djvu',
|
|
38
|
'txt': 'txt',
|
|
37
|
'txt': 'txt',
|
|
39
|
'tex': 'tex',
|
|
38
|
'tex': 'tex',
|
|
40
|
'xcf': 'xcf',
|
|
39
|
'xcf': 'xcf',
|
|
41
|
'zip': 'archive',
|
|
40
|
'zip': 'archive',
|
|
42
|
'tar': 'archive',
|
|
41
|
'tar': 'archive',
|
|
43
|
'gz': 'archive',
|
|
42
|
'gz': 'archive',
|
|
44
|
}
|
|
43
|
}
|
|
45
|
|
|
44
|
|
|
46
|
URL_PROTOCOLS = {
|
|
45
|
URL_PROTOCOLS = {
|
|
47
|
'magnet': 'magnet',
|
|
46
|
'magnet': 'magnet',
|
|
48
|
}
|
|
47
|
}
|
|
49
|
|
|
48
|
|
|
50
|
CSS_CLASS_IMAGE = 'image'
|
|
49
|
CSS_CLASS_IMAGE = 'image'
|
|
51
|
CSS_CLASS_THUMB = 'thumb'
|
|
50
|
CSS_CLASS_THUMB = 'thumb'
|
|
52
|
|
|
51
|
|
|
53
|
|
|
52
|
|
|
54
|
def get_viewers():
|
|
53
|
def get_viewers():
|
|
55
|
return AbstractViewer.__subclasses__()
|
|
54
|
return AbstractViewer.__subclasses__()
|
|
56
|
|
|
55
|
|
|
57
|
|
|
56
|
|
|
58
|
def get_static_dimensions(filename):
|
|
57
|
def get_static_dimensions(filename):
|
|
59
|
file_path = finders.find(filename)
|
|
58
|
file_path = finders.find(filename)
|
|
60
|
return get_image_dimensions(file_path)
|
|
59
|
return get_image_dimensions(file_path)
|
|
61
|
|
|
60
|
|
|
62
|
|
|
61
|
|
|
|
|
|
62
|
# TODO Move this to utils
|
|
|
|
|
63
|
def file_exists(filename):
|
|
|
|
|
64
|
return finders.find(filename) is not None
|
|
|
|
|
65
|
|
|
|
|
|
66
|
|
|
63
|
class AbstractViewer:
|
|
67
|
class AbstractViewer:
|
|
64
|
def __init__(self, file, file_type, hash, url):
|
|
68
|
def __init__(self, file, file_type, hash, url):
|
|
65
|
self.file = file
|
|
69
|
self.file = file
|
|
66
|
self.file_type = file_type
|
|
70
|
self.file_type = file_type
|
|
67
|
self.hash = hash
|
|
71
|
self.hash = hash
|
|
68
|
self.url = url
|
|
72
|
self.url = url
|
|
69
|
|
|
73
|
|
|
70
|
@staticmethod
|
|
74
|
@staticmethod
|
|
71
|
def supports(file_type):
|
|
75
|
def supports(file_type):
|
|
72
|
return True
|
|
76
|
return True
|
|
73
|
|
|
77
|
|
|
74
|
def get_view(self):
|
|
78
|
def get_view(self):
|
|
75
|
return '<div class="image">'\
|
|
79
|
return '<div class="image">'\
|
|
76
|
'{}'\
|
|
80
|
'{}'\
|
|
77
|
'<div class="image-metadata"><a href="{}" download >{}, {}</a></div>'\
|
|
81
|
'<div class="image-metadata"><a href="{}" download >{}, {}</a></div>'\
|
|
78
|
'</div>'.format(self.get_format_view(), self.file.url,
|
|
82
|
'</div>'.format(self.get_format_view(), self.file.url,
|
|
79
|
self.file_type, filesizeformat(self.file.size))
|
|
83
|
self.file_type, filesizeformat(self.file.size))
|
|
80
|
|
|
84
|
|
|
81
|
def get_format_view(self):
|
|
85
|
def get_format_view(self):
|
|
82
|
if self.file_type in PLAIN_FILE_FORMATS:
|
|
86
|
if self.file_type in PLAIN_FILE_FORMATS:
|
|
83
|
image = 'images/fileformats/{}.png'.format(
|
|
87
|
image = 'images/fileformats/{}.png'.format(
|
|
84
|
PLAIN_FILE_FORMATS[self.file_type])
|
|
88
|
PLAIN_FILE_FORMATS[self.file_type])
|
|
85
|
else:
|
|
89
|
else:
|
|
86
|
image = FILE_STUB_IMAGE
|
|
90
|
image = FILE_STUB_IMAGE
|
|
87
|
|
|
91
|
|
|
88
|
w, h = get_static_dimensions(image)
|
|
92
|
w, h = get_static_dimensions(image)
|
|
89
|
|
|
93
|
|
|
90
|
return '<a href="{}">'\
|
|
94
|
return '<a href="{}">'\
|
|
91
|
'<img class="url-image" src="{}" width="{}" height="{}"/>'\
|
|
95
|
'<img class="url-image" src="{}" width="{}" height="{}"/>'\
|
|
92
|
'</a>'.format(self.file.url, static(image), w, h)
|
|
96
|
'</a>'.format(self.file.url, static(image), w, h)
|
|
93
|
|
|
97
|
|
|
94
|
|
|
98
|
|
|
95
|
class VideoViewer(AbstractViewer):
|
|
99
|
class VideoViewer(AbstractViewer):
|
|
96
|
@staticmethod
|
|
100
|
@staticmethod
|
|
97
|
def supports(file_type):
|
|
101
|
def supports(file_type):
|
|
98
|
return file_type in FILE_TYPES_VIDEO
|
|
102
|
return file_type in FILE_TYPES_VIDEO
|
|
99
|
|
|
103
|
|
|
100
|
def get_format_view(self):
|
|
104
|
def get_format_view(self):
|
|
101
|
return '<video width="200" height="150" controls src="{}"></video>'\
|
|
105
|
return '<video width="200" height="150" controls src="{}"></video>'\
|
|
102
|
.format(self.file.url)
|
|
106
|
.format(self.file.url)
|
|
103
|
|
|
107
|
|
|
104
|
|
|
108
|
|
|
105
|
class AudioViewer(AbstractViewer):
|
|
109
|
class AudioViewer(AbstractViewer):
|
|
106
|
@staticmethod
|
|
110
|
@staticmethod
|
|
107
|
def supports(file_type):
|
|
111
|
def supports(file_type):
|
|
108
|
return file_type in FILE_TYPES_AUDIO
|
|
112
|
return file_type in FILE_TYPES_AUDIO
|
|
109
|
|
|
113
|
|
|
110
|
def get_format_view(self):
|
|
114
|
def get_format_view(self):
|
|
111
|
return '<audio controls src="{}"></audio>'.format(self.file.url)
|
|
115
|
return '<audio controls src="{}"></audio>'.format(self.file.url)
|
|
112
|
|
|
116
|
|
|
113
|
|
|
117
|
|
|
114
|
class SvgViewer(AbstractViewer):
|
|
118
|
class SvgViewer(AbstractViewer):
|
|
115
|
@staticmethod
|
|
119
|
@staticmethod
|
|
116
|
def supports(file_type):
|
|
120
|
def supports(file_type):
|
|
117
|
return file_type == FILE_TYPE_SVG
|
|
121
|
return file_type == FILE_TYPE_SVG
|
|
118
|
|
|
122
|
|
|
119
|
def get_format_view(self):
|
|
123
|
def get_format_view(self):
|
|
120
|
return '<a class="thumb" href="{}">'\
|
|
124
|
return '<a class="thumb" href="{}">'\
|
|
121
|
'<img class="post-image-preview" width="200" height="150" src="{}" />'\
|
|
125
|
'<img class="post-image-preview" width="200" height="150" src="{}" />'\
|
|
122
|
'</a>'.format(self.file.url, self.file.url)
|
|
126
|
'</a>'.format(self.file.url, self.file.url)
|
|
123
|
|
|
127
|
|
|
124
|
|
|
128
|
|
|
125
|
class ImageViewer(AbstractViewer):
|
|
129
|
class ImageViewer(AbstractViewer):
|
|
126
|
@staticmethod
|
|
130
|
@staticmethod
|
|
127
|
def supports(file_type):
|
|
131
|
def supports(file_type):
|
|
128
|
return file_type in FILE_TYPES_IMAGE
|
|
132
|
return file_type in FILE_TYPES_IMAGE
|
|
129
|
|
|
133
|
|
|
130
|
def get_format_view(self):
|
|
134
|
def get_format_view(self):
|
|
131
|
metadata = '{}, {}'.format(self.file.name.split('.')[-1],
|
|
135
|
metadata = '{}, {}'.format(self.file.name.split('.')[-1],
|
|
132
|
filesizeformat(self.file.size))
|
|
136
|
filesizeformat(self.file.size))
|
|
133
|
width, height = get_image_dimensions(self.file.file)
|
|
137
|
width, height = get_image_dimensions(self.file.file)
|
|
134
|
preview_path = self.file.path.replace('.', '.200x150.')
|
|
138
|
preview_path = self.file.path.replace('.', '.200x150.')
|
|
135
|
pre_width, pre_height = get_image_dimensions(preview_path)
|
|
139
|
pre_width, pre_height = get_image_dimensions(preview_path)
|
|
136
|
|
|
140
|
|
|
137
|
split = self.file.url.rsplit('.', 1)
|
|
141
|
split = self.file.url.rsplit('.', 1)
|
|
138
|
w, h = 200, 150
|
|
142
|
w, h = 200, 150
|
|
139
|
thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
|
|
143
|
thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
|
|
140
|
|
|
144
|
|
|
141
|
return '<a class="{}" href="{full}">' \
|
|
145
|
return '<a class="{}" href="{full}">' \
|
|
142
|
'<img class="post-image-preview"' \
|
|
146
|
'<img class="post-image-preview"' \
|
|
143
|
' src="{}"' \
|
|
147
|
' src="{}"' \
|
|
144
|
' alt="{}"' \
|
|
148
|
' alt="{}"' \
|
|
145
|
' width="{}"' \
|
|
149
|
' width="{}"' \
|
|
146
|
' height="{}"' \
|
|
150
|
' height="{}"' \
|
|
147
|
' data-width="{}"' \
|
|
151
|
' data-width="{}"' \
|
|
148
|
' data-height="{}" />' \
|
|
152
|
' data-height="{}" />' \
|
|
149
|
'</a>' \
|
|
153
|
'</a>' \
|
|
150
|
.format(CSS_CLASS_THUMB,
|
|
154
|
.format(CSS_CLASS_THUMB,
|
|
151
|
thumb_url,
|
|
155
|
thumb_url,
|
|
152
|
self.hash,
|
|
156
|
self.hash,
|
|
153
|
str(pre_width),
|
|
157
|
str(pre_width),
|
|
154
|
str(pre_height), str(width), str(height),
|
|
158
|
str(pre_height), str(width), str(height),
|
|
155
|
full=self.file.url, image_meta=metadata)
|
|
159
|
full=self.file.url, image_meta=metadata)
|
|
156
|
|
|
160
|
|
|
157
|
|
|
161
|
|
|
158
|
class UrlViewer(AbstractViewer):
|
|
162
|
class UrlViewer(AbstractViewer):
|
|
159
|
@staticmethod
|
|
163
|
@staticmethod
|
|
160
|
def supports(file_type):
|
|
164
|
def supports(file_type):
|
|
161
|
return file_type is None
|
|
165
|
return file_type is None
|
|
162
|
|
|
166
|
|
|
163
|
def get_view(self):
|
|
167
|
def get_view(self):
|
|
164
|
return '<div class="image">' \
|
|
168
|
return '<div class="image">' \
|
|
165
|
'{}' \
|
|
169
|
'{}' \
|
|
166
|
'</div>'.format(self.get_format_view())
|
|
170
|
'</div>'.format(self.get_format_view())
|
|
167
|
|
|
171
|
|
|
168
|
def get_format_view(self):
|
|
172
|
def get_format_view(self):
|
|
169
|
protocol = self.url.split('://')[0]
|
|
173
|
protocol = self.url.split('://')[0]
|
|
170
|
full_domain = self.url.split('/')[2]
|
|
174
|
full_domain = self.url.split('/')[2]
|
|
171
|
domain = REGEX_DOMAIN.search(full_domain).group(2)
|
|
175
|
domain = REGEX_DOMAIN.search(full_domain).group(2)
|
|
172
|
|
|
176
|
|
|
173
|
if protocol in URL_PROTOCOLS:
|
|
177
|
if protocol in URL_PROTOCOLS:
|
|
174
|
url_image_name = URL_PROTOCOLS.get(protocol)
|
|
178
|
url_image_name = URL_PROTOCOLS.get(protocol)
|
|
175
|
elif domain in URL_DOMAINS:
|
|
179
|
else:
|
|
176
|
url_image_name = 'domains/' + URL_DOMAINS.get(domain)
|
|
180
|
filename = 'images/domains/{}.png'.format(domain)
|
|
|
|
|
181
|
if file_exists(filename):
|
|
|
|
|
182
|
url_image_name = 'domains/' + domain
|
|
177
|
else:
|
|
183
|
else:
|
|
178
|
url_image_name = FILE_STUB_URL
|
|
184
|
url_image_name = FILE_STUB_URL
|
|
179
|
|
|
185
|
|
|
180
|
image = static('images/' + url_image_name + '.png')
|
|
186
|
image_path = 'images/{}.png'.format(url_image_name)
|
|
181
|
|
|
187
|
image = static(image_path)
|
|
182
|
w, h = get_static_dimensions('images/' + url_image_name + '.png')
|
|
188
|
w, h = get_static_dimensions(image_path)
|
|
183
|
|
|
189
|
|
|
184
|
return '<a href="{}">' \
|
|
190
|
return '<a href="{}">' \
|
|
185
|
'<img class="url-image" src="{}" width="{}" height="{}"/>' \
|
|
191
|
'<img class="url-image" src="{}" width="{}" height="{}"/>' \
|
|
186
|
'</a>'.format(self.url, image, w, h)
|
|
192
|
'</a>'.format(self.url, image, w, h)
|