##// END OF EJS Templates
Download webm videos from youtube
neko259 -
r1328:3352da82 default
parent child Browse files
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 from django.core.exceptions import ObjectDoesNotExist
8 from django.core.exceptions import ObjectDoesNotExist
9 from django.forms.util import ErrorList
9 from django.forms.util import ErrorList
10 from django.utils.translation import ugettext_lazy as _
10 from django.utils.translation import ugettext_lazy as _
11 import requests
12
11
13 from boards.mdx_neboard import formatters
12 from boards.mdx_neboard import formatters
13 from boards.models.attachment.downloaders import Downloader
14 from boards.models.post import TITLE_MAX_LENGTH
14 from boards.models.post import TITLE_MAX_LENGTH
15 from boards.models import Tag, Post
15 from boards.models import Tag, Post
16 from boards.utils import validate_file_size
16 from neboard import settings
17 from neboard import settings
17 import boards.settings as board_settings
18 import boards.settings as board_settings
18 import neboard
19 import neboard
19
20
20 HEADER_CONTENT_LENGTH = 'content-length'
21 HEADER_CONTENT_TYPE = 'content-type'
22
23 REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE)
21 REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE)
24
22
25 VETERAN_POSTING_DELAY = 5
23 VETERAN_POSTING_DELAY = 5
@@ -41,10 +39,6 b" ERROR_SPEED = _('Please wait %s seconds "
41
39
42 TAG_MAX_LENGTH = 20
40 TAG_MAX_LENGTH = 20
43
41
44 FILE_DOWNLOAD_CHUNK_BYTES = 100000
45
46 HTTP_RESULT_OK = 200
47
48 TEXTAREA_ROWS = 4
42 TEXTAREA_ROWS = 4
49
43
50
44
@@ -182,7 +176,7 b' class PostForm(NeboardForm):'
182 file = self.cleaned_data['file']
176 file = self.cleaned_data['file']
183
177
184 if file:
178 if file:
185 self.validate_file_size(file.size)
179 validate_file_size(file.size)
186
180
187 return file
181 return file
188
182
@@ -196,7 +190,7 b' class PostForm(NeboardForm):'
196 if not file:
190 if not file:
197 raise forms.ValidationError(_('Invalid URL'))
191 raise forms.ValidationError(_('Invalid URL'))
198 else:
192 else:
199 self.validate_file_size(file.size)
193 validate_file_size(file.size)
200
194
201 return file
195 return file
202
196
@@ -294,13 +288,6 b' class PostForm(NeboardForm):'
294 if can_post:
288 if can_post:
295 self.session[LAST_POST_TIME] = now
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 def _get_file_from_url(self, url: str) -> SimpleUploadedFile:
291 def _get_file_from_url(self, url: str) -> SimpleUploadedFile:
305 """
292 """
306 Gets an file file from URL.
293 Gets an file file from URL.
@@ -309,36 +296,18 b' class PostForm(NeboardForm):'
309 img_temp = None
296 img_temp = None
310
297
311 try:
298 try:
312 # Verify content headers
299 for downloader in Downloader.__subclasses__():
313 response_head = requests.head(url, verify=False)
300 if downloader.handles(url):
314 content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0]
301 return downloader.download(url)
315 length_header = response_head.headers.get(HEADER_CONTENT_LENGTH)
302 # If nobody of the specific downloaders handles this, use generic
316 if length_header:
303 # one
317 length = int(length_header)
304 return Downloader.download(url)
318 self.validate_file_size(length)
305 except forms.ValidationError as e:
319 # Get the actual content into memory
306 raise e
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)
336 except Exception as e:
307 except Exception as e:
337 # Just return no file
308 # Just return no file
338 pass
309 pass
339
310
340 return img_temp
341
342
311
343 class ThreadForm(PostForm):
312 class ThreadForm(PostForm):
344
313
@@ -7,8 +7,11 b' import hmac'
7
7
8 from django.core.cache import cache
8 from django.core.cache import cache
9 from django.db.models import Model
9 from django.db.models import Model
10 from django import forms
10
11
11 from django.utils import timezone
12 from django.utils import timezone
13 from django.utils.translation import ugettext_lazy as _
14 import boards
12
15
13 from neboard import settings
16 from neboard import settings
14
17
@@ -90,3 +93,11 b' def get_file_hash(file) -> str:'
90 for chunk in file.chunks():
93 for chunk in file.chunks():
91 md5.update(chunk)
94 md5.update(chunk)
92 return md5.hexdigest()
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))
@@ -1,3 +1,4 b''
1 pytube
1 requests
2 requests
2 adjacent
3 adjacent
3 django-haystack
4 django-haystack
General Comments 0
You need to be logged in to leave comments. Login now