##// END OF EJS Templates
Force validate file size on sync
Bohdan Horbeshko -
r2145:c3a97d51 lite
parent child Browse files
Show More
@@ -1,101 +1,101 b''
1 import re
1 import re
2
2
3 import requests
3 import requests
4 from django.core.files.uploadedfile import TemporaryUploadedFile
4 from django.core.files.uploadedfile import TemporaryUploadedFile
5 from pytube import YouTube
5 from pytube import YouTube
6
6
7 from boards.utils import validate_file_size
7 from boards.utils import validate_file_size
8
8
9 YOUTUBE_VIDEO_FORMAT = 'webm'
9 YOUTUBE_VIDEO_FORMAT = 'webm'
10
10
11 HTTP_RESULT_OK = 200
11 HTTP_RESULT_OK = 200
12
12
13 HEADER_CONTENT_LENGTH = 'content-length'
13 HEADER_CONTENT_LENGTH = 'content-length'
14 HEADER_CONTENT_TYPE = 'content-type'
14 HEADER_CONTENT_TYPE = 'content-type'
15
15
16 FILE_DOWNLOAD_CHUNK_BYTES = 200000
16 FILE_DOWNLOAD_CHUNK_BYTES = 200000
17
17
18 REGEX_YOUTUBE_URL = re.compile(r'https?://((www\.)?youtube\.com/watch\?v=|youtu.be/)[-\w]+')
18 REGEX_YOUTUBE_URL = re.compile(r'https?://((www\.)?youtube\.com/watch\?v=|youtu.be/)[-\w]+')
19 REGEX_MAGNET = re.compile(r'magnet:\?xt=urn:(btih:)?[a-z0-9]{20,50}.*')
19 REGEX_MAGNET = re.compile(r'magnet:\?xt=urn:(btih:)?[a-z0-9]{20,50}.*')
20
20
21 TYPE_URL_ONLY = (
21 TYPE_URL_ONLY = (
22 'application/xhtml+xml',
22 'application/xhtml+xml',
23 'text/html',
23 'text/html',
24 )
24 )
25
25
26
26
27 class Downloader:
27 class Downloader:
28 @staticmethod
28 @staticmethod
29 def handles(url: str) -> bool:
29 def handles(url: str) -> bool:
30 return True
30 return True
31
31
32 @staticmethod
32 @staticmethod
33 def download(url: str, validate):
33 def download(url: str, validate):
34 # Verify content headers
34 # Verify content headers
35 response_head = requests.head(url, verify=False)
35 response_head = requests.head(url, verify=False)
36 content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0]
36 content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0]
37 if validate and content_type in TYPE_URL_ONLY:
37 if validate and content_type in TYPE_URL_ONLY:
38 return None
38 return None
39
39
40 length_header = response_head.headers.get(HEADER_CONTENT_LENGTH)
40 length_header = response_head.headers.get(HEADER_CONTENT_LENGTH)
41 if validate and length_header:
41 if length_header:
42 length = int(length_header)
42 length = int(length_header)
43 validate_file_size(length)
43 validate_file_size(length)
44 # Get the actual content into memory
44 # Get the actual content into memory
45 response = requests.get(url, verify=False, stream=True)
45 response = requests.get(url, verify=False, stream=True)
46
46
47 if response.status_code == HTTP_RESULT_OK:
47 if response.status_code == HTTP_RESULT_OK:
48 # Download file, stop if the size exceeds limit
48 # Download file, stop if the size exceeds limit
49 size = 0
49 size = 0
50
50
51 # Set a dummy file name that will be replaced
51 # Set a dummy file name that will be replaced
52 # anyway, just keep the valid extension
52 # anyway, just keep the valid extension
53 filename = 'file.' + content_type.split('/')[1]
53 filename = 'file.' + content_type.split('/')[1]
54
54
55 file = TemporaryUploadedFile(filename, content_type, 0, None, None)
55 file = TemporaryUploadedFile(filename, content_type, 0, None, None)
56 for chunk in response.iter_content(FILE_DOWNLOAD_CHUNK_BYTES):
56 for chunk in response.iter_content(FILE_DOWNLOAD_CHUNK_BYTES):
57 size += len(chunk)
57 size += len(chunk)
58 validate_file_size(size)
58 validate_file_size(size)
59 file.write(chunk)
59 file.write(chunk)
60
60
61 return file
61 return file
62
62
63
63
64 class YouTubeDownloader(Downloader):
64 class YouTubeDownloader(Downloader):
65 @staticmethod
65 @staticmethod
66 def download(url: str, validate):
66 def download(url: str, validate):
67 yt = YouTube()
67 yt = YouTube()
68 yt.from_url(url)
68 yt.from_url(url)
69 videos = yt.filter(YOUTUBE_VIDEO_FORMAT)
69 videos = yt.filter(YOUTUBE_VIDEO_FORMAT)
70 if len(videos) > 0:
70 if len(videos) > 0:
71 video = videos[0]
71 video = videos[0]
72 return Downloader.download(video.url)
72 return Downloader.download(video.url)
73
73
74 @staticmethod
74 @staticmethod
75 def handles(url: str) -> bool:
75 def handles(url: str) -> bool:
76 return REGEX_YOUTUBE_URL.match(url) is not None
76 return REGEX_YOUTUBE_URL.match(url) is not None
77
77
78
78
79 class NothingDownloader(Downloader):
79 class NothingDownloader(Downloader):
80 @staticmethod
80 @staticmethod
81 def handles(url: str) -> bool:
81 def handles(url: str) -> bool:
82 return REGEX_MAGNET.match(url)
82 return REGEX_MAGNET.match(url)
83
83
84 @staticmethod
84 @staticmethod
85 def download(url: str, validate):
85 def download(url: str, validate):
86 return None
86 return None
87
87
88
88
89 DOWNLOADERS = (
89 DOWNLOADERS = (
90 YouTubeDownloader,
90 YouTubeDownloader,
91 NothingDownloader,
91 NothingDownloader,
92 Downloader,
92 Downloader,
93 )
93 )
94
94
95
95
96 def download(url, validate=True):
96 def download(url, validate=True):
97 for downloader in DOWNLOADERS:
97 for downloader in DOWNLOADERS:
98 if downloader.handles(url):
98 if downloader.handles(url):
99 return downloader.download(url, validate=validate)
99 return downloader.download(url, validate=validate)
100 raise Exception('No downloader supports this URL.')
100 raise Exception('No downloader supports this URL.')
101
101
General Comments 0
You need to be logged in to leave comments. Login now