Skip to content

Instantly share code, notes, and snippets.

@ascheel
Last active January 5, 2018 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ascheel/b94c1fb5dfb3d8b00f5caa766071564d to your computer and use it in GitHub Desktop.
Save ascheel/b94c1fb5dfb3d8b00f5caa766071564d to your computer and use it in GitHub Desktop.
# 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
#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