Пример #1
0
    def __init__(self, database_file: str, organizer: Organizer, config: ConfigurationFile):
        self.database_file = database_file
        self.organizer = organizer
        self.config = config

        self._downloader = TorrentDownloader()
        # associates the episodes to the download handle
        self._downloads = {}  # type: dict[Episode: DownloadHandle]
        self._thread = None  # periodically checks for completed episodes
        self._stopped = Event()  # indicates the downloader is stopped
Пример #2
0
class Downloader:
    def __init__(self, database_file: str, organizer: Organizer, config: ConfigurationFile):
        self.database_file = database_file
        self.organizer = organizer
        self.config = config

        self._downloader = TorrentDownloader()
        # associates the episodes to the download handle
        self._downloads = {}  # type: dict[Episode: DownloadHandle]
        self._thread = None  # periodically checks for completed episodes
        self._stopped = Event()  # indicates the downloader is stopped

    def download(self, episode: Episode):
        handle = self._downloader.start_download(episode.link, self.config.cache_dir)
        self._downloads[episode] = handle

    def start(self):
        """
        Starts a new downloader session. The downloader must be started in
        order to be  able to check for completed episodes and organize them
        using the organizer assign to it. Starting the downloader also restarts
        downloading all episodes marked as DOWNLOADING.
        """
        self._stopped.clear()
        database = Database(self.database_file)
        # restart downloading all episodes marked as DOWNLOADING
        for episode in database.episodes(EpisodeState.DOWNLOADING):
            self.download(episode)

        # manage completed episodes periodically
        self._thread = Timer(self.config.download_check_period, self.manage_downloads)
        self._thread.start()

    def stop(self):
        """
        Stops the downloader current session. All downloads are stopped and
        removed from the downloader. The service of checking for completed
        episodes is also stopped.
        """
        self._stopped.set()
        self._downloader.stop()

        if self._thread:
            self._thread.cancel()

    def manage_downloads(self):
        for episode, handle in self._completed():
            # the downloading directory must be obtained before removing the
            # episode from the torrent downloader
            downloading_dir = handle.basename

            self._remove_episode(episode)
            self._moveto_downloaded_dir(downloading_dir, episode)
            self._store_episode(episode)

        if not self._stopped.is_set():
            self._thread = Timer(self.config.download_check_period, self.manage_downloads)
            self._thread.start()

    def downloads(self):
        for episode, handle in self._downloads.items():
            yield Download(episode, handle.progress, handle.download_rate)

    def _completed(self):
        # this method can not be implemented as a generator since it removes
        # episodes from the downloads dictionary
        return [(episode, handle) for episode, handle in self._downloads.items() if handle.is_completed()]

    def _remove_episode(self, episode: Episode):
        """
        Removes an episode from download system. After removing the episode
        the its download is stopped.

        :param episode: episode to remove.
        """
        self._downloader.stop_download(self._downloads[episode])
        del self._downloads[episode]

    def _moveto_downloaded_dir(self, downloading_dir: str, episode: Episode):
        """
        Moves the files in the downloading directory to the downloaded
        directory of the given episode.
        """
        shutil.move(os.path.join(self.config.cache_dir, downloading_dir), self.config.episode_downloaded_dir(episode))

    def _store_episode(self, episode: Episode):
        """
        Stores the given episode through the organizer assign to the downloader.
        Before storing the episode, it is marked as DOWNLOADED.

        :param episode: episode to store.
        """
        database = Database(self.database_file)
        database.set_state(episode, EpisodeState.DOWNLOADED)
        self.organizer.store(episode)