-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathbtsync.py
203 lines (170 loc) · 7.26 KB
/
btsync.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import httplib
import base64
import urllib
import json
import os
import tempfile
# Default path to BTSync executable. Can be overwritten in the init method.
BTSYNC_PATH = r'/Applications/BitTorrent\ Sync.app/Contents/MacOS/BitTorrent\ Sync' #noqa
class BTSync(object):
""" The BTSync class is a light wrapper around the Bittorrent Sync API.
Currently to use the API you will need to apply for a key. You can find out
how to do that, and learn more about the btsync API here:
http://www.bittorrent.com/sync/developers/api
The docstrings of this class' methods were copied from the above site.
"""
def __init__(self, btsync_path=BTSYNC_PATH, host='127.0.0.1', port='8888',
user='admin', pswd='password'):
"""
Parameters
----------
host : str
IP address that the btsync api responds at.
port : str
Port that the btsync api responds at.
user : str
optional username to use if btsync api is protected.
pswd : str
optional password to use if btsync api is protected.
btsync_path : path
Path to BTSync executable.
"""
self.btsync_path = btsync_path
self.conn = httplib.HTTPConnection('{}:{}'.format(host, port))
auth = 'Basic ' + base64.b64encode('{}:{}'.format(user, pswd))
self.headers = {'Authorization': auth}
self.config = {
'use_gui': True,
'webui': {
'listen': '{}:{}'.format(host, port),
'login': user,
'password': pswd,
'api_key': ('2WCXYTARBRSSCL3PMT2NKIMJVBFRXPU72PIVCJ73UHMVILX73'
'UGM5RVLF45ETLCEDFBGEH4P6ACYPCSSNXPQY2LP6BB6YPPVJH'
'VGQDZ3KDVNOQT2IBB5ZKM6XZ4CXA3DUMO3KBY')
}
}
def get_folders(self, secret=None):
"""
Returns an array with folders info. If a secret is specified, will
return info about the folder with this secret.
[
{
"dir": "\\\\?\\D:\\share",
"secret": "A54HDDMPN4T4BTBT7SPBWXDB7JVYZ2K6D",
"size": 23762511569,
"type": "read_write",
"files": 3206,
"error": 0,
"indexing": 0
}
]
http://[address]:[port]/api?method=get_folders[&secret=(secret)]
secret (optional) - if a secret is specified, will return info about
the folder with this secret
"""
params = {'method': 'get_folders'}
if secret is not None:
params['secret'] = secret
return self._request(params)
def add_folder(self, path, secret=None):
"""
Adds a folder to Sync. If a secret is not specified, it will be
generated automatically. The folder will have to pre-exist on the disk
and Sync will add it into a list of syncing folders.
Returns '0' if no errors, error code and error message otherwise.
{ "error": 0 }
http://[address]:[port]/api?method=add_folder&dir=(folderPath)[&secret=(secret)&selective_sync=1] #noqa
dir (required) - specify path to the sync folder
secret (optional) - specify folder secret
selective_sync (optional) - specify sync mode, selective - 1, all files
(default) - 0
"""
params = {'method': 'add_folder', 'dir': path}
if secret is not None:
params['secret'] = secret
return self._request(params)
def remove_folder(self, secret):
"""
Removes folder from Sync while leaving actual folder and files on disk.
It will remove a folder from the Sync list of folders and does not
touch any files or folders on disk. Returns '0' if no error, '1' if
there's no folder with specified secret.
{ "error": 0 }
http://[address]:[port]/api?method=remove_folder&secret=(secret)
secret (required) - specify folder secret
"""
params = {'method': 'remove_folder', 'secret': secret}
return self._request(params)
def get_files(self, secret, path=None):
"""
Returns list of files within the specified directory. If a directory is
not specified, will return list of files and folders within the root
folder. Note that the Selective Sync function is only available in the
API at this time.
[
{
"name": "images",
"state": "created",
"type": "folder"
},
{
"have_pieces": 1,
"name": "index.html",
"size": 2726,
"state": "created",
"total_pieces": 1,
"type": "file",
"download": 1 // only for selective sync folders
}
]
http://[address]:[port]/api?method=get_files&secret=(secret)[&path=(path)] #noqa
secret (required) - must specify folder secret
path (optional) - specify path to a subfolder of the sync folder.
"""
params = {'method': 'get_files', 'secret': secret}
if path is not None:
params['path'] = path
return self._request(params)
def get_secrets(self, secret=None):
"""
Generates read-write, read-only and encryption read-only secrets. If
`secret` parameter is specified, will return secrets available for
sharing under this secret.
The Encryption Secret is new functionality. This is a secret for a
read-only peer with encrypted content (the peer can sync files but can
not see their content). One example use is if a user wanted to backup
files to an untrusted, unsecure, or public location. This is set to
disabled by default for all users but included in the API.
{
"read_only": "ECK2S6MDDD7EOKKJZOQNOWDTJBEEUKGME",
"read_write": "DPFABC4IZX33WBDRXRPPCVYA353WSC3Q6",
"encryption": "G3PNU7KTYM63VNQZFPP3Q3GAMTPRWDEZ"
}
http://[address]:[port]/api?method=get_secrets[&secret=(secret)&type=encryption] #noqa
secret (required) - must specify folder secret
type (optional) - if type=encrypted, generate secret with support of
encrypted peer
"""
params = {'method': 'get_secrets'}
if secret is not None:
params['secret'] = secret
return self._request(params)
def start(self):
""" Start the btsync instance if there isn't already one running,
otherwise do nothing.
"""
if self.get_secrets() is None:
config_file = tempfile.NamedTemporaryFile(delete=False)
config_file.write(json.dumps(self.config))
config_file.close()
os.system('{} --config {}&'.format(self.btsync_path,
config_file.name))
def _request(self, params):
params = urllib.urlencode(params)
self.conn.request('GET', '/api?' + params, '', self.headers)
resp = self.conn.getresponse()
if resp.status == 200:
return json.load(resp)
else:
raise RuntimeError('{}: {}'.format(resp.status, resp.reason))