Skip to content

Commit

Permalink
feat: Add move/copy files methods to API
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Mar 14, 2019
1 parent f49c874 commit e1d3994
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
97 changes: 97 additions & 0 deletions src/aria2p/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
This module defines the API class, which makes use of a JSON-RPC client to provide higher-level methods to
interact easily with a remote aria2c process.
"""
import shutil
from base64 import b64encode
from pathlib import Path

from .client import Client, ClientException
from .downloads import Download
Expand Down Expand Up @@ -548,3 +550,98 @@ def get_stats(self):
:class:`~aria2p.stats.Stats` instance: the global stats returned by the remote process.
"""
return Stats(self.client.get_global_stat())

@staticmethod
def move_files(downloads, to_directory, force=False):
"""
Move downloaded files to another directory.
Args:
downloads (list of :class:`~aria2p.downloads.Download`): the list of downloads for which to move files.
to_directory (str/Path): the target directory to move files to.
force (bool): whether to move files even if download is not complete.
Returns:
list of bool: Success or failure of the operation for each given download.
"""
if isinstance(to_directory, str):
to_directory = Path(to_directory)

# raises FileExistsError when target is already a file
to_directory.mkdir(parents=True, exist_ok=True)

results = []
for download in downloads:
if download.is_complete or force:

# don't move leaf after leaf: move entire directories at once
# we build here the list of unique dirs/files
source_directory = Path(download.options.dir)
paths = set()
for file in download.files:
if file.is_metadata:
continue
try:
relative_path = file.path.relative_to(source_directory)
except ValueError:
pass # TODO: log
else:
paths.add(source_directory / relative_path.parts[0])

# actual move operation
for path in paths:
shutil.move(str(path), str(to_directory))

results.append(True)
else:
results.append(False)
return results

@staticmethod
def copy_files(downloads, to_directory, force=False):
"""
Copy downloaded files to another directory.
Args:
downloads (list of :class:`~aria2p.downloads.Download`): the list of downloads for which to move files.
to_directory (str/Path): the target directory to copy files into.
force (bool): whether to move files even if download is not complete.
Returns:
list of bool: Success or failure of the operation for each given download.
"""
if isinstance(to_directory, str):
to_directory = Path(to_directory)

# raises FileExistsError when target is already a file
to_directory.mkdir(parents=True, exist_ok=True)

results = []
for download in downloads:
if download.is_complete or force:

# don't copy leaf after leaf: copy entire directories at once
# we build here the list of unique dirs/files
source_directory = Path(download.options.dir)
paths = set()
for file in download.files:
if file.is_metadata:
continue
try:
relative_path = file.path.relative_to(source_directory)
except ValueError:
pass # TODO: log
else:
paths.add(source_directory / relative_path.parts[0])

# actual copy operation
for path in paths:
if path.is_dir():
shutil.copytree(str(path), str(to_directory / path.name))
elif path.is_file():
shutil.copy(str(path), str(to_directory))

results.append(True)
else:
results.append(False)
return results
10 changes: 10 additions & 0 deletions src/aria2p/downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ def path(self):
"""File path."""
return Path(self._struct.get("path"))

@property
def is_metadata(self):
"""Return True if this file is aria2 metadata and not an actual file."""
return str(self.path).startswith("[METADATA]")

@property
def length(self):
"""File size in bytes."""
Expand Down Expand Up @@ -271,6 +276,11 @@ def is_removed(self):
"""Return True if download was removed."""
return self.status == "removed"

@property
def is_metadata(self):
"""Return True if this download is only composed of metadata, and no actual files."""
return all(_.is_metadata for _ in self.files)

@property
def total_length(self):
"""Total length of the download in bytes."""
Expand Down

0 comments on commit e1d3994

Please sign in to comment.