blob: c36b9897e3c706106f67cf9bb3bd825b5537ab5b [file] [log] [blame]
#
# Copyright 2016 RIFT.IO Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author(s): Varun Prasad
# Creation Date: 09/25/2016
#
import abc
import enum
import os
import uuid
import time
class InvalidDestinationError(Exception):
pass
class DownloaderProtocol:
"""Listener of this class can implement the following method to get a
callback
"""
def on_download_started(self, job):
"""Called when the download starts
Args:
job (DownloadJob): Yang Model
"""
pass
def on_download_progress(self, job):
"""Called after each chunk is downloaded
Args:
job (DownloadJob): Yang Model
"""
pass
def on_download_succeeded(self, job):
"""Called when the download is completed successfully
Args:
job (DownloadJob): Yang Model
"""
pass
def on_download_failed(self, job):
"""Called when the download fails
Args:
job (DownloadJob): Yang Model
"""
pass
def on_download_cancelled(self, job):
"""Called when the download is canceled
Args:
job (DownloadJob): Yang Model
"""
pass
def on_download_finished(self, job):
"""Called when the download finishes regardless of the status of the
download (success, failed or canceled)
Args:
job (DownloadJob): Yang Model
"""
pass
class DownloadStatus(enum.Enum):
STARTED = 1
IN_PROGRESS = 2
COMPLETED = 3
FAILED = 4
CANCELLED = 5
class DownloadMeta:
"""Model data used by the downloader.
"""
def __init__(self, url, dest_file):
self.url = url
self.filepath = dest_file
self.download_id = str(uuid.uuid4())
self.bytes_total = 0
self.progress_percent = 0
self.bytes_downloaded = 0
self.bytes_per_second = 0
self.status = None
self.start_time = 0
self.stop_time = 0
self.detail = ""
@property
def filename(self):
return os.path.basename(self.filepath)
def start_download(self):
self.start_time = time.time()
def end_download(self):
self.end_time = time.time()
def set_state(self, state):
self.status = state
def update_with_data(self, downloaded_chunk):
self.bytes_downloaded += len(downloaded_chunk)
if self.bytes_total != 0:
self.progress_percent = \
int((self.bytes_downloaded / self.bytes_total) * 100)
# compute bps
seconds_elapsed = time.time() - self.start_time
self.bytes_per_second = self.bytes_downloaded // seconds_elapsed
def update_data_with_head(self, headers):
"""Update the model from the header of HEAD request
Args:
headers (dict): headers from HEAD response
"""
if 'Content-Length' in headers:
self.bytes_total = int(headers['Content-Length'])
def as_dict(self):
return self.__dict__
class AbstractDownloader:
def __init__(self):
self._delegate = None
@property
def delegate(self):
return self._delegate
@delegate.setter
def delegate(self, delegate):
self._delegate = delegate
@abc.abstractproperty
def download_id(self):
pass
@abc.abstractmethod
def cancel_download(self):
pass
@abc.abstractmethod
def close(self):
pass
@abc.abstractmethod
def download(self):
pass