-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdbx-dl.py
executable file
·113 lines (93 loc) · 3.95 KB
/
dbx-dl.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
#!/usr/bin/env python3
"""Dropbox Downloader
Usage:
dbx-dl.py download-recursive [<path>]
dbx-dl.py du [<path>]
dbx-dl.py ls [<path>]
dbx-dl.py (-h | --help)
dbx-dl.py --version
Options:
-h --help Show this screen.
--version Show version.
"""
import dropbox
import dropbox.exceptions
import os.path
from docopt import docopt
from dropbox.files import FolderMetadata, FileMetadata
from configparser import ConfigParser
from queue import Queue
from dropbox_downloader.DiskUsage import DiskUsage
from dropbox_downloader.Downloader import Downloader
from dropbox_downloader.DownloadWorker import DownloadWorker
class DropboxDownloader:
"""Controlling class for console command."""
def __init__(self):
self._base_path = os.path.dirname(os.path.realpath(__file__))
ini_settings = self._load_config()
self._dbx = dropbox.Dropbox(ini_settings.get('main', 'api_key'))
self._dl_dir = ini_settings.get('main', 'dl_dir')
self._to_dl = str(ini_settings.get('main', 'to_dl')).split(',') or None
def dl(self, path: str = ''):
"""Recursively download all files in given path, or entire dropbox if none given"""
d = Downloader(self._base_path, self._dbx, self._dl_dir, self._to_dl)
queue = Queue()
files_and_folders = d.list_files_and_folders(path)
n_files_and_folders = len(files_and_folders.entries)
n_threads = n_files_and_folders if n_files_and_folders < 8 else 8
# Create 8 ListWorker threads
for x in range(n_threads):
worker = DownloadWorker(d, queue)
# Setting daemon to True will let the main thread exit even though the workers are blocking
worker.daemon = True
worker.start()
for f in files_and_folders.entries:
if isinstance(f, FolderMetadata):
queue.put(f.path_lower)
elif isinstance(f, FileMetadata):
d.download_file(f)
else:
raise RuntimeError(
'Unexpected folder entry: {}\nExpected types: FolderMetadata, FileMetadata'.format(f))
# Causes the main thread to wait for the queue to finish processing all the tasks
queue.join()
print('All files in {} downloaded'.format(path or 'your entire dropbox'))
def du(self, path: str = ''):
"""Get disk usage (size) for path"""
du = DiskUsage(self._dbx)
du.du(path)
def ls(self, path: str = ''):
"""Print contents of a given folder path in text columns"""
files_and_folders = self._dbx.files_list_folder(path)
print('Listing path "{}"...'.format(path))
file_list = [{
'id': f.id,
'name': f.name,
'path_lower': f.path_lower
} for f in files_and_folders.entries]
# get column sizes for formatting
max_len_id = max(len(f['id']) for f in file_list)
max_len_name = max(len(f['name']) for f in file_list)
max_len_path_lower = max(len(f['path_lower']) for f in file_list)
for f in file_list:
print('{:>{}} {:>{}} {:>{}}'.format(
f['id'], max_len_id, f['name'], max_len_name, f['path_lower'], max_len_path_lower))
def _load_config(self) -> ConfigParser:
"""Load `dbx-dl.ini` config file
:return: ConfigParser
"""
# By using `allow_no_value=True` we are allowed to
# write `--force` instead of `--force=true` below.
config = ConfigParser(allow_no_value=True)
with open('{}/dbx-dl.ini'.format(self._base_path)) as f:
config.read_file(f)
return config
if __name__ == '__main__':
arguments = docopt(__doc__, version='Dropbox Downloader')
dd = DropboxDownloader()
if arguments['download-recursive']:
dd.dl(arguments.get('<path>') or '')
elif arguments.get('du'):
dd.du(arguments.get('<path>') or '')
elif arguments.get('ls'):
dd.ls(arguments.get('<path>') or '')