forked from mi8882255/3dprinteros-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
downloader.py
114 lines (100 loc) · 4.64 KB
/
downloader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#Copyright (c) 2015 3D Control Systems LTD
#3DPrinterOS client is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#3DPrinterOS client is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU Affero General Public License for more details.
#You should have received a copy of the GNU Affero General Public License
#along with 3DPrinterOS client. If not, see <http://www.gnu.org/licenses/>.
# Author: Vladimir Avdeev <another.vic@yandex.ru>, Alexey Slynko <alex_ey@i.ua>
import os
import time
import logging
import tempfile
import requests
import threading
import log
import config
class Downloader(threading.Thread):
CONNECTION_TIMEOUT = 6
MAX_RETRIES = 15
def __init__(self, base_sender, url):
self.percent = 0
self.url = url
self.base_sender = base_sender
self.cancel_flag = False
self.logger = logging.getLogger('app.' + __name__)
threading.Thread.__init__(self, name="Downloader")
@log.log_exception
def run(self):
self.logger.info('Starting downloading')
downloaded_filename = self.start_download()
if downloaded_filename:
with open(downloaded_filename, 'rb') as f:
gcodes = f.read()
self.base_sender.load_gcodes(gcodes)
self.logger.info('Gcodes loaded to memory, deleting temp file')
try:
os.remove(downloaded_filename)
except:
pass
elif not self.cancel_flag:
config.create_error_report(67, "Can't download gcodes",
self.base_sender.usb_info, self.logger, is_blocking=False)
self.logger.info('Downloading finished')
def get_percent(self):
return self.percent
def start_download(self):
self.logger.info("Downloading from " + self.url)
self.tmp_file = tempfile.NamedTemporaryFile(mode='wb', delete=False, prefix='3dprinteros-', suffix='.gcode')
resume_byte_pos = 0
retry = 0
while retry < self.MAX_RETRIES and not self.base_sender.stop_flag and not self.cancel_flag:
self.logger.info("Connecting to " + self.url)
resume_header = {'Range': 'bytes=%d-' % resume_byte_pos}
try:
request = requests.get(self.url, headers = resume_header, stream=True, timeout = self.CONNECTION_TIMEOUT)
except Exception as e:
request = None
config.create_error_report(65, "Unable to open download link: " + str(e),
self.base_sender.usb_info, self.logger, is_blocking=False)
else:
self.logger.info("Successful connection to " + self.url)
download_length = int(request.headers.get('content-length', 0))
if download_length:
downloaded_size = self.downloading_loop(request, download_length)
resume_byte_pos += downloaded_size
self.logger.info("Downloaded %d bytes" % resume_byte_pos)
if downloaded_size == download_length:
self.tmp_file.close()
return self.tmp_file.name
finally:
if request:
request.close()
retry += 1
self.logger.warning("Download retry/resume N%d" % retry)
time.sleep(1)
self.tmp_file.close()
def downloading_loop(self, request, download_length):
# Taking +1 byte with each chunk to compensate file length tail less than 100 bytes when dividing by 100
percent_length = download_length / 100 + 1
total_size = 0
try:
for chunk in request.iter_content(percent_length):
if self.cancel_flag or self.base_sender.stop_flag:
self.logger.info('Download canceled')
break
self.tmp_file.write(chunk)
self.percent += 1
total_size += len(chunk)
self.logger.info('File downloading : %d%%' % self.percent)
except Exception as e:
config.create_error_report(66, 'Error while downloading: ' + str(e.message),
self.base_sender.usb_info, self.logger, is_blocking=False)
finally:
return total_size
def cancel(self):
self.cancel_flag = True