Skip to content

Instantly share code, notes, and snippets.

@brpowell
Last active March 28, 2023 23:40
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save brpowell/c8b778af7b51f69bdccc to your computer and use it in GitHub Desktop.
Save brpowell/c8b778af7b51f69bdccc to your computer and use it in GitHub Desktop.
Dropbox ROM syncing
# - write token and cursor to .romsync
# * update cursor every delta check
# * config file format
# token=token_string
# cursor=cursor_string
# db_roms=path_to_dropbox_roms
# local_roms=path_to_local_roms
# * upload new config to dropbox
import os
import dropbox
app_key = os.environ['DROPBOX_APP_KEY']
app_secret = os.environ['DROPBOX_APP_SECRET']
DEBUG = 0
class Config(object):
settings = {}
path = ''
def __init__(self, path=os.getenv("HOME") + '/.romsync'):
self.path = path
self.load()
# Return value of setting
def get(self, key):
return self.settings[key]
# Update the cursor
def set_cursor(self, cursor):
self.settings['cursor'] = cursor
# Write settings to config
def write(self):
open(self.path, 'w').close() # Flush config before writing
with open(self.path, 'w') as cfg:
for key in self.settings:
cfg.write(key + '=' + self.settings[key] + '\n')
# Load settings from config
def load(self):
if os.path.isfile(self.path):
with open(self.path, 'r') as cfg:
for line in cfg:
key = line.replace(" ", "").split('=')[0]
value = line.replace(" ", "").split('=')[1].rstrip()
self.settings[key] = value
else:
with open(self.path, 'w') as cfg:
# Begin flow
flow = dropbox.client.DropboxOAuth2FlowNoRedirect(app_key, app_secret)
authorize_url = flow.start()
# Get authorization code
print('1. Go to: ' + authorize_url)
print('2. Click "Allow" (you might have to log in first)')
print('3. Copy the authorization code.')
code = input('Enter the authorization code here: ').strip()
local_path = input('Enter your local roms directory: ')
access_token, user_id = flow.finish(code)
# Write code to config
cfg.write('access_token=' + access_token + '\n')
cfg.write('db_roms=/romsync\n')
cfg.write('local_roms=' + local_path + '\n')
cfg.write('cursor= ' + '\n')
# Download/remove files and directories
# return: dictionary of modified files
def sync(client, delta, config):
dirs = []
files = []
file_change = {'added': [], 'removed': []}
entries = delta['entries']
local_path = config.get('local_roms')
db_path = config.get('db_roms')
for e in entries:
if(e[1] != None):
path = e[1]['path'].replace(db_path, local_path)
if(e[1]['is_dir']):
if(len(path) > 0):
os.makedirs(path)
else:
out = open(path, 'wb')
with client.get_file(e[1]['path']) as rom:
out.write(rom.read())
file_change['added'].append(path.replace(local_path, ''))
else:
path = e[0].replace(db_path, local_path)
try:
if os.path.isdir(path):
shutil.rmtree(path)
for child in os.listdir(path):
file_change['removed'].append(child)
else:
os.remove(path)
file_change['removed'].append(path.replace(local_path, ''))
except:
if(DEBUG):
print(path, ' does not exist')
return file_change
def main():
config = Config()
config.load()
# Get client
client = dropbox.client.DropboxClient(config.get('access_token'))
delta = client.delta(path_prefix=config.get('db_roms'), cursor=config.get('cursor'))
if DEBUG:
print(delta)
if(len(delta['entries']) > 0):
print('Syncing roms...')
file_change = sync(client, delta, config)
added = len(file_change['added'])
removed = len(file_change['removed'])
if(added > 0):
print('\n'+'Fetched %s rom(s)' % str(added))
for entry in file_change['added']:
print(entry)
if(removed > 0):
print('\n'+'Removed %s rom(s)' % str(removed))
for entry in file_change['removed']:
print(entry)
print('')
else:
print('No need to sync')
config.set_cursor(delta['cursor'])
config.write()
if __name__ == "__main__":
main()
@Axeavius
Copy link

Is there a way to configure this to only save the...save files? My Dropbox account is already almost full, and save files (sram and savestate files) are really the only ones I'm concerned with backing up; my ROM folder is on Google Drive since they offer more space for free.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment