| from requests import post, delete | |
| from json import loads | |
| from models import Video | |
| from os import stat | |
| CHUNK_SIZE = 5 * 1024 * 1024 | |
| class UploadSession(object): | |
| def __init__(self, panda, file_name, **kwargs): | |
| self.panda = panda | |
| self.file_name = file_name | |
| self.file_size = stat(file_name).st_size | |
| params = { | |
| "file_size": self.file_size, | |
| "file_name": file_name, | |
| "profiles": "h264" | |
| } | |
| params.update(kwargs) | |
| data = panda.post('/videos/upload.json', params) | |
| self.location = loads(data)["location"] | |
| self.status = "initialized" | |
| self.video = None | |
| self.error_message = None | |
| def _read_chunks(self, file_object): | |
| i = 0 | |
| while True: | |
| data = file_object.read(CHUNK_SIZE) | |
| if not data: | |
| break | |
| yield (data, i) | |
| i = i+1 | |
| def start(self, pos=0): | |
| if self.status == "initialized": | |
| self.status = "uploading" | |
| with open(self.file_name) as f: | |
| f.seek(pos) | |
| try: | |
| for chunk, i in self._read_chunks(f): | |
| index = i * CHUNK_SIZE | |
| res = post(self.location, headers = { | |
| 'Content-Type': 'application/octet-stream', | |
| 'Cache-Control': 'no-cache', | |
| 'Content-Range': "bytes {0}-{1}/{2}".format(pos+index, pos+index+CHUNK_SIZE-1, self.file_size), | |
| 'Content-Length': str(CHUNK_SIZE) | |
| }, data=chunk) | |
| if res.status_code == 200: | |
| self.status = "uploaded" | |
| self.video = Video(self.panda, json_attr=res.json()) | |
| elif res.status_code != 204: | |
| self.status = "error" | |
| break | |
| except Exception as e: | |
| self.status = "error" | |
| self.error_message = str(e) | |
| raise e | |
| except KeyboardInterrupt: | |
| self.status = "error" | |
| self.error_message = "interrupted" | |
| else: | |
| raise KeyError("Already started") | |
| def resume(self): | |
| if self.status != "uploaded": | |
| res = post(self.location, headers = { | |
| 'Content-Type': 'application/octet-stream', | |
| 'Cache-Control': 'no-cache', | |
| 'Content-Range': "bytes */{0}".format(self.file_size), | |
| 'Content-Length': "0" | |
| }) | |
| pos = int(res.headers["Range"].split("-")[1]) | |
| self.status = "initialized" | |
| self.start(pos=pos) | |
| else: | |
| raise KeyError("Already succeed") | |
| def abort(self): | |
| if self.status != "success": | |
| self.status = "aborted" | |
| self.error_message = None | |
| res = delete(self.location) | |
| else: | |
| raise KeyError("Already succeed") | |