Show More
@@ -0,0 +1,83 b'' | |||
|
1 | import os | |
|
2 | import re | |
|
3 | ||
|
4 | from django.core.files.uploadedfile import SimpleUploadedFile | |
|
5 | from pytube import YouTube | |
|
6 | import requests | |
|
7 | ||
|
8 | from boards.utils import validate_file_size | |
|
9 | ||
|
10 | YOUTUBE_VIDEO_FORMAT = 'webm' | |
|
11 | ||
|
12 | HTTP_RESULT_OK = 200 | |
|
13 | ||
|
14 | HEADER_CONTENT_LENGTH = 'content-length' | |
|
15 | HEADER_CONTENT_TYPE = 'content-type' | |
|
16 | ||
|
17 | FILE_DOWNLOAD_CHUNK_BYTES = 100000 | |
|
18 | ||
|
19 | YOUTUBE_URL = re.compile(r'https?://www\.youtube\.com/watch\?v=\w+') | |
|
20 | ||
|
21 | ||
|
22 | class Downloader: | |
|
23 | @staticmethod | |
|
24 | def handles(url: str) -> bool: | |
|
25 | return False | |
|
26 | ||
|
27 | @staticmethod | |
|
28 | def download(url: str): | |
|
29 | # Verify content headers | |
|
30 | response_head = requests.head(url, verify=False) | |
|
31 | content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0] | |
|
32 | length_header = response_head.headers.get(HEADER_CONTENT_LENGTH) | |
|
33 | if length_header: | |
|
34 | length = int(length_header) | |
|
35 | validate_file_size(length) | |
|
36 | # Get the actual content into memory | |
|
37 | response = requests.get(url, verify=False, stream=True) | |
|
38 | ||
|
39 | # Download file, stop if the size exceeds limit | |
|
40 | size = 0 | |
|
41 | content = b'' | |
|
42 | for chunk in response.iter_content(FILE_DOWNLOAD_CHUNK_BYTES): | |
|
43 | size += len(chunk) | |
|
44 | validate_file_size(size) | |
|
45 | content += chunk | |
|
46 | ||
|
47 | if response.status_code == HTTP_RESULT_OK and content: | |
|
48 | # Set a dummy file name that will be replaced | |
|
49 | # anyway, just keep the valid extension | |
|
50 | filename = 'file.' + content_type.split('/')[1] | |
|
51 | return SimpleUploadedFile(filename, content, content_type) | |
|
52 | ||
|
53 | ||
|
54 | class YouTubeDownloader(Downloader): | |
|
55 | @staticmethod | |
|
56 | def download(url: str): | |
|
57 | yt = YouTube() | |
|
58 | yt.from_url(url) | |
|
59 | videos = yt.filter(YOUTUBE_VIDEO_FORMAT) | |
|
60 | if len(videos) > 0: | |
|
61 | video = videos[0] | |
|
62 | filename = '{}.{}'.format(video.filename, video.extension) | |
|
63 | try: | |
|
64 | video.download(on_progress=YouTubeDownloader.on_progress) | |
|
65 | ||
|
66 | file = open(filename, 'rb') | |
|
67 | content = file.read() | |
|
68 | file.close() | |
|
69 | ||
|
70 | os.remove(filename) | |
|
71 | return SimpleUploadedFile(filename, content, video.extension) | |
|
72 | except Exception as e: | |
|
73 | if os.path.isfile(filename): | |
|
74 | os.remove(filename) | |
|
75 | raise e | |
|
76 | ||
|
77 | @staticmethod | |
|
78 | def handles(url: str) -> bool: | |
|
79 | return YOUTUBE_URL.match(url) | |
|
80 | ||
|
81 | @staticmethod | |
|
82 | def on_progress(bytes, file_size, start): | |
|
83 | validate_file_size(file_size) |
@@ -8,18 +8,16 b' from django.core.files.uploadedfile impo' | |||
|
8 | 8 | from django.core.exceptions import ObjectDoesNotExist |
|
9 | 9 | from django.forms.util import ErrorList |
|
10 | 10 | from django.utils.translation import ugettext_lazy as _ |
|
11 | import requests | |
|
12 | 11 | |
|
13 | 12 | from boards.mdx_neboard import formatters |
|
13 | from boards.models.attachment.downloaders import Downloader | |
|
14 | 14 | from boards.models.post import TITLE_MAX_LENGTH |
|
15 | 15 | from boards.models import Tag, Post |
|
16 | from boards.utils import validate_file_size | |
|
16 | 17 | from neboard import settings |
|
17 | 18 | import boards.settings as board_settings |
|
18 | 19 | import neboard |
|
19 | 20 | |
|
20 | HEADER_CONTENT_LENGTH = 'content-length' | |
|
21 | HEADER_CONTENT_TYPE = 'content-type' | |
|
22 | ||
|
23 | 21 | REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE) |
|
24 | 22 | |
|
25 | 23 | VETERAN_POSTING_DELAY = 5 |
@@ -41,10 +39,6 b" ERROR_SPEED = _('Please wait %s seconds " | |||
|
41 | 39 | |
|
42 | 40 | TAG_MAX_LENGTH = 20 |
|
43 | 41 | |
|
44 | FILE_DOWNLOAD_CHUNK_BYTES = 100000 | |
|
45 | ||
|
46 | HTTP_RESULT_OK = 200 | |
|
47 | ||
|
48 | 42 | TEXTAREA_ROWS = 4 |
|
49 | 43 | |
|
50 | 44 | |
@@ -182,7 +176,7 b' class PostForm(NeboardForm):' | |||
|
182 | 176 | file = self.cleaned_data['file'] |
|
183 | 177 | |
|
184 | 178 | if file: |
|
185 |
|
|
|
179 | validate_file_size(file.size) | |
|
186 | 180 | |
|
187 | 181 | return file |
|
188 | 182 | |
@@ -196,7 +190,7 b' class PostForm(NeboardForm):' | |||
|
196 | 190 | if not file: |
|
197 | 191 | raise forms.ValidationError(_('Invalid URL')) |
|
198 | 192 | else: |
|
199 |
|
|
|
193 | validate_file_size(file.size) | |
|
200 | 194 | |
|
201 | 195 | return file |
|
202 | 196 | |
@@ -294,13 +288,6 b' class PostForm(NeboardForm):' | |||
|
294 | 288 | if can_post: |
|
295 | 289 | self.session[LAST_POST_TIME] = now |
|
296 | 290 | |
|
297 | def validate_file_size(self, size: int): | |
|
298 | max_size = board_settings.get_int('Forms', 'MaxFileSize') | |
|
299 | if size > max_size: | |
|
300 | raise forms.ValidationError( | |
|
301 | _('File must be less than %s bytes') | |
|
302 | % str(max_size)) | |
|
303 | ||
|
304 | 291 | def _get_file_from_url(self, url: str) -> SimpleUploadedFile: |
|
305 | 292 | """ |
|
306 | 293 | Gets an file file from URL. |
@@ -309,36 +296,18 b' class PostForm(NeboardForm):' | |||
|
309 | 296 | img_temp = None |
|
310 | 297 | |
|
311 | 298 | try: |
|
312 | # Verify content headers | |
|
313 | response_head = requests.head(url, verify=False) | |
|
314 | content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0] | |
|
315 | length_header = response_head.headers.get(HEADER_CONTENT_LENGTH) | |
|
316 |
|
|
|
317 | length = int(length_header) | |
|
318 | self.validate_file_size(length) | |
|
319 | # Get the actual content into memory | |
|
320 | response = requests.get(url, verify=False, stream=True) | |
|
321 | ||
|
322 | # Download file, stop if the size exceeds limit | |
|
323 | size = 0 | |
|
324 | content = b'' | |
|
325 | for chunk in response.iter_content(FILE_DOWNLOAD_CHUNK_BYTES): | |
|
326 | size += len(chunk) | |
|
327 | self.validate_file_size(size) | |
|
328 | content += chunk | |
|
329 | ||
|
330 | if response.status_code == HTTP_RESULT_OK and content: | |
|
331 | # Set a dummy file name that will be replaced | |
|
332 | # anyway, just keep the valid extension | |
|
333 | filename = 'file.' + content_type.split('/')[1] | |
|
334 | img_temp = SimpleUploadedFile(filename, content, | |
|
335 | content_type) | |
|
299 | for downloader in Downloader.__subclasses__(): | |
|
300 | if downloader.handles(url): | |
|
301 | return downloader.download(url) | |
|
302 | # If nobody of the specific downloaders handles this, use generic | |
|
303 | # one | |
|
304 | return Downloader.download(url) | |
|
305 | except forms.ValidationError as e: | |
|
306 | raise e | |
|
336 | 307 | except Exception as e: |
|
337 | 308 | # Just return no file |
|
338 | 309 | pass |
|
339 | 310 | |
|
340 | return img_temp | |
|
341 | ||
|
342 | 311 | |
|
343 | 312 | class ThreadForm(PostForm): |
|
344 | 313 |
@@ -7,8 +7,11 b' import hmac' | |||
|
7 | 7 | |
|
8 | 8 | from django.core.cache import cache |
|
9 | 9 | from django.db.models import Model |
|
10 | from django import forms | |
|
10 | 11 | |
|
11 | 12 | from django.utils import timezone |
|
13 | from django.utils.translation import ugettext_lazy as _ | |
|
14 | import boards | |
|
12 | 15 | |
|
13 | 16 | from neboard import settings |
|
14 | 17 | |
@@ -90,3 +93,11 b' def get_file_hash(file) -> str:' | |||
|
90 | 93 | for chunk in file.chunks(): |
|
91 | 94 | md5.update(chunk) |
|
92 | 95 | return md5.hexdigest() |
|
96 | ||
|
97 | ||
|
98 | def validate_file_size(size: int): | |
|
99 | max_size = boards.settings.get_int('Forms', 'MaxFileSize') | |
|
100 | if size > max_size: | |
|
101 | raise forms.ValidationError( | |
|
102 | _('File must be less than %s bytes') | |
|
103 | % str(max_size)) |
General Comments 0
You need to be logged in to leave comments.
Login now