Last active
January 5, 2018 14:26
-
-
Save ascheel/b94c1fb5dfb3d8b00f5caa766071564d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# SDK docs are at | |
# https://www.dropbox.com/static/developers/dropbox-python-sdk-1.5.1-docs/index.html | |
from __future__ import print_function | |
from dropbox import client, session | |
from oauth.oauth import OAuthToken | |
import os | |
class dropbox_client(object): | |
# Get your own app key and secret from the Dropbox developer website | |
APP_KEY = 'XXXXXXXXXXXXXXX' | |
APP_SECRET = 'XXXXXXXXXXXXXXX' | |
# ACCESS_TYPE should be 'dropbox' or 'app_folder' as configured for your app | |
ACCESS_TYPE = 'dropbox' | |
DATE_FORMAT = "%a, %d %b %Y %H:%M:%S +0000" | |
def __init__(self): | |
self.client = self.get_client() | |
def get_client(self): | |
access_token_file = os.path.join(os.environ["HOME"], ".dropbox-tools-access-token") | |
sess = session.DropboxSession(self.APP_KEY, self.APP_SECRET, self.ACCESS_TYPE) | |
try: | |
with open(access_token_file) as f: | |
access_token = OAuthToken.from_string(f.read()) | |
sess.set_token(access_token.key, access_token.secret) | |
except (IOError, EOFError, KeyError): | |
request_token = sess.obtain_request_token() | |
url = sess.build_authorize_url(request_token) | |
print("Please visit\n\n {}\n\nand press the 'Allow' button, then hit 'Enter' here.".format(url)) | |
raw_input() | |
# This will fail if the user didn't visit the above URL and hit 'Allow' | |
access_token = sess.obtain_access_token(request_token) | |
# dropbox access tokens don't have serialisation methods on them, | |
my_token = OAuthToken(access_token.key, access_token.secret) | |
with open(access_token_file, "w") as f: | |
f.write(my_token.to_string()) | |
conn = client.DropboxClient(sess) | |
print("linked account: {}".format(conn.account_info()["display_name"])) | |
return conn |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#picpi2 | |
from __future__ import print_function | |
from __future__ import division | |
import time | |
import os | |
import sys | |
import dropbox | |
import shutil | |
import signal | |
import pprint | |
import datetime | |
from common import dropbox_client | |
def signal_handler(signal, frame): | |
print('ctrl-c detected.') | |
sys.exit(1) | |
signal.signal(signal.SIGINT, signal_handler) | |
class db(object): | |
def __init__(self): | |
self.db = dropbox_client() | |
self.dbox = self.db.client | |
self.errors = [] | |
self.BLACKLIST = [] | |
#self.BLACKLIST.append('/wallpapers') | |
#self.BLACKLIST.append('/Calibre.Portable') | |
#self.BLACKLIST.append('/arduino') | |
#self.BLACKLIST.append('/Apps') | |
#self.BLACKLIST.append('/programming') | |
#self.BLACKLIST.append('/Media') | |
#self.BLACKLIST.append('/ROM') | |
#self.BLACKLIST.append('/RPG') | |
#self.BLACKLIST.append('/DAKCS') | |
#self.BLACKLIST.append('/software installs') | |
#self.BLACKLIST.append('/raspi') | |
#self.BLACKLIST.append('/Public') | |
#self.BLACKLIST.append('/tmp/ebooks') | |
def restore(self): | |
self.MAX_DAYS=15 | |
self.USE_RESTORE = True | |
try: | |
start_walk = sys.argv[1] | |
except IndexError: | |
start_walk = "/" | |
self.recover_tree(start_walk) | |
def attempt_restore(self, path, rev, num=0): | |
if num == 5: | |
self.errors.append((path,)) | |
return False | |
restore = False | |
try: | |
restore = self.dbox.restore(path, rev) | |
except: | |
time.sleep(2) | |
print(' - Failed. Sleeping for 2 sec.', end='') | |
restore = self.attempt_restore(path, rev, num+1) | |
return restore | |
def recover_tree(self,folder = "/"): | |
# called recursively. We're going to walk the entire Dropbox | |
# file tree, starting at 'folder', files first, and recover anything | |
# deleted in the last 5 days. | |
print(u"walking in {}".format(folder)) | |
if folder in self.BLACKLIST: | |
print('Blacklisted.') | |
return | |
try: | |
meta = self.dbox.metadata(folder, include_deleted=True, file_limit=25000) | |
except dropbox.rest.ErrorResponse, e: | |
self.errors.append((folder, e,)) | |
print(e) # normally "too many files". Dropbox will only list 25000 files in | |
# a folder. THere is probably a way around this, but I haven't needed it yet. | |
return | |
# walk files first, folders later | |
for filedata in filter(lambda f: not f.get("is_dir", False), meta["contents"]): | |
# we only care about deleted files. | |
if not filedata.get("is_deleted", False): | |
continue | |
# this is the date the file was deleted on | |
date = datetime.datetime.strptime(filedata["modified"], self.db.DATE_FORMAT) | |
print(u" {} is deleted".format(filedata["path"]), end='') | |
# fetch file history, and pick the first non-deleted revision. | |
revisions = self.dbox.revisions(filedata["path"], rev_limit=10) | |
alive = filter(lambda r: not r.get("is_deleted", False), revisions)[0] | |
#print('Testing file: {}'.format(filedata['path'])) | |
if self.USE_RESTORE: | |
restore = self.attempt_restore(filedata["path"], alive["rev"]) | |
if restore: | |
print(' ..Restored!') | |
else: | |
print(' NOT RESTORED.') | |
#print(restore) | |
else: | |
# try to download file. | |
# I'm torn here - I could just use the Dropbox API and tell it to | |
# restore the deleted file to the non-deleted version. PRoblem with | |
# that is that it might recover too much. THis approach lets me restore | |
# to a new folder with _just_ the restored files in, and cherry-pick | |
# what I want to copy back into the main dropbox. | |
try: | |
fh = self.dbox.get_file(filedata["path"], rev=alive["rev"]) | |
with open(target+".temp", "w") as oh: | |
oh.write(fh.read()) | |
os.rename(target+'.temp', target) | |
print(" ..recovered") | |
except Exception, e: | |
print("*** RECOVERY FAILED: {}".format(e)) | |
# now loop over the folders and recursively walk into them. Folders can | |
# be deleted too, but don't try to undelete them, we'll rely on them being | |
# implicitly reinflated when their files are restored. | |
for file in filter(lambda f: f.get("is_dir", False), meta["contents"]): | |
self.recover_tree(file["path"]) | |
def print_errors(self): | |
#pp = pp.PrettyPrinter(indent=4) | |
for item in self.errors: | |
print(repr(item)) | |
def main(): | |
dbox = db() | |
dbox.restore() | |
dbox.print_errors() | |
#pp = pprint.PrettyPrinter(indent=4) | |
#pp.pprint(dbox.dbox.account_info()) | |
#pp.pprint(dbox.dbox.metadata('/')) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment