added a "restore.txt" document that can be used to reverse the flow from remote to local.

Added restore mode in settings
This commit is contained in:
robweber 2012-04-21 17:18:41 -05:00
parent 140965733c
commit f520d58c6b
5 changed files with 114 additions and 80 deletions

View File

@ -9,6 +9,8 @@ In the addon settings you can define a remote path for the destination of your x
On the Backup Selection page you can select which items from your user profile folder will be sent to the backup location. By default all are turned on except the Addon Data directory.
To restore your data simply switch the Mode from "backup" to "restore" and the files will be copied from your remote directory to the local path.
What this Addon Will Not Do:
This is not meant as an XBMC file sync solution. If you have multiple frontends you want to keep in sync this addon may work in a "poor man's" sort of way but it is not intended for that.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.xbmcbackup"
name="XBMC Backup" version="0.0.3" provider-name="robweber">
name="XBMC Backup" version="0.0.4" provider-name="robweber">
<requires>
<import addon="xbmc.python" version="2.0"/>
</requires>
@ -8,7 +8,7 @@
<provides>executable</provides>
</extension>
<extension point="xbmc.addon.metadata">
<summary lang="en">Backup your XBMC database and configuration files before an upgrade or in the even of a crash.</summary>
<summary lang="en">Backup abd restore your XBMC database and configuration files in the event of a crash or file corruption.</summary>
<description lang="en">Ever hosed your XBMC configuration and wished you'd had a backup? Now you can with one easy click. You can export your database, playlist, thumbnails, addons and other configuration details to any source writeable by XBMC.</description>
<platform>all</platform>
</extension>

View File

@ -4,31 +4,78 @@ import xbmcgui
import xbmcvfs
import os
class RestoreFile:
class FileManager:
local_path = ''
addonDir = ''
fHandle = None
def __init__(self,addon_dir):
self.local_path = xbmc.translatePath("special://home")
self.addonDir = addon_dir
def createRestoreFile(self):
#create the addon folder if it doesn't exist
if(not os.path.exists(unicode(xbmc.translatePath(self.addonDir),'utf-8'))):
os.makedirs(unicode(xbmc.translatePath(self.addonDir),'utf-8'))
xbmc.log(self.addonDir)
def createFileList(self,Addon):
self.fHandle = open(unicode(xbmc.translatePath(self.addonDir + "restore.txt"),'utf-8'),"w")
#figure out which syncing options to run
if(Addon.getSetting('backup_addons') == 'true'):
self.addFile("-addons")
self.walkTree(self.local_path + "addons/")
self.addFile("-userdata")
if(Addon.getSetting('backup_addon_data') == 'true'):
self.addFile("-userdata/addon_data")
self.walkTree(self.local_path + "userdata/addon_data/")
if(Addon.getSetting('backup_database') == 'true'):
self.addFile("-userdata/Database")
self.walkTree(self.local_path + "userdata/Database")
if(Addon.getSetting("backup_playlists") == 'true'):
self.addFile("-userdata/playlists")
self.walkTree(self.local_path + "userdata/playlists")
if(Addon.getSetting("backup_thumbnails") == "true"):
self.addFile("-userdata/Thumbnails")
self.walkTree(self.local_path + "userdata/Thumbnails")
if(Addon.getSetting("backup_config") == "true"):
#this one is an oddity
configFiles = os.listdir(self.local_path + "userdata/")
for aFile in configFiles:
if(aFile.endswith(".xml")):
self.addFile("userdata/" + aFile)
if(self.fHandle != None):
self.fHandle.close()
def walkTree(self,directory):
for (path, dirs, files) in os.walk(directory):
#get the relative part of this path
path = path[len(self.local_path):]
#create all the subdirs first
for aDir in dirs:
self.addFile("-" + path + os.sep + aDir)
#copy all the files
for aFile in files:
filePath = path + os.sep + aFile
self.addFile(filePath)
def addFile(self,filename):
#write the full remote path name of this file
if(self.fHandle != None):
self.fHandle.write(str(filename) + "\n")
def closeRestoreFile(self,sendDir):
#close out the file and write it to the remote store
if(self.fHandle != None):
self.fHandle.close()
#xbmcvfs.copy(self.addonDir + "restore.txt",sendDir + "restore.txt")
def readFileList(self):
allFiles = open(unicode(xbmc.translatePath(self.addonDir + "restore.txt"),'utf-8'),"r").read().splitlines()
return allFiles
class XbmcBackup:
__addon_id__ = 'script.xbmcbackup'
@ -39,8 +86,10 @@ class XbmcBackup:
#for the progress bar
progressBar = None
dirsLeft = 0
dirsTotal = 0
filesLeft = 0
filesTotal = 0
fileManager = None
def __init__(self):
self.local_path = xbmc.translatePath("special://home")
@ -48,92 +97,73 @@ class XbmcBackup:
if(self.Addon.getSetting('remote_path') != '' and self.Addon.getSetting("backup_name") != ''):
self.remote_path = self.Addon.getSetting("remote_path") + self.Addon.getSetting('backup_name') + "/"
self.restoreFile = RestoreFile(self.Addon.getAddonInfo('profile'))
self.fileManager = FileManager(self.Addon.getAddonInfo('profile'))
self.log("Starting")
self.log('Local Dir: ' + self.local_path)
self.log('Remote Dir: ' + self.remote_path)
def syncFiles(self):
self.restoreFile.createRestoreFile()
def run(self):
#check what mode were are in
if(int(self.Addon.getSetting('addon_mode')) == 0):
self.syncFiles()
else:
self.restoreFiles()
def syncFiles(self):
if(xbmcvfs.exists(self.remote_path)):
#this will fail - need a disclaimer here
self.log("Remote Path exists - may have old files in it!")
#make the remote directory
xbmcvfs.mkdir(self.remote_path)
self.fileManager.createFileList(self.Addon)
allFiles = self.fileManager.readFileList()
#write list from local to remote
self.writeFiles(allFiles,self.local_path,self.remote_path)
#write the restore list
xbmcvfs.copy(self.Addon.getAddonInfo('profile') + "restore.txt",self.remote_path + "restore.txt")
def restoreFiles(self):
#copy the restore file
xbmcvfs.copy(self.remote_path + "restore.txt",self.Addon.getAddonInfo('profile') + "restore.txt")
allFiles = self.fileManager.readFileList()
#write list from remote to local
self.writeFiles(allFiles,self.remote_path,self.local_path)
def writeFiles(self,fileList,source,dest):
self.filesTotal = len(fileList)
self.filesLeft = self.filesTotal
#check if we should use the progress bar
if(self.Addon.getSetting('run_silent') == 'false'):
self.progressBar = xbmcgui.DialogProgress()
self.progressBar.create('XBMC Backup','Running......')
self.updateProgress(1)
#figure out which syncing options to run
if(self.Addon.getSetting('backup_addons') == 'true' and not self.checkCancel()):
xbmcvfs.mkdir(self.remote_path + "addons")
self.walkTree(self.local_path + "addons/")
#write each file from source to destination
for aFile in fileList:
if(not self.checkCancel()):
self.updateProgress(aFile)
if (aFile.startswith("-")):
xbmcvfs.mkdir(dest + aFile[1:])
else:
xbmcvfs.copy(source + aFile,dest + aFile)
xbmcvfs.mkdir(self.remote_path + "userdata")
if(self.Addon.getSetting('backup_addon_data') == 'true' and not self.checkCancel()):
xbmcvfs.mkdir(self.remote_path + "userdata/addon_data")
self.walkTree(self.local_path + "userdata/addon_data/")
if(self.Addon.getSetting('backup_database') == 'true' and not self.checkCancel()):
xbmcvfs.mkdir(self.remote_path + "userdata/Database")
self.walkTree(self.local_path + "userdata/Database")
if(self.Addon.getSetting("backup_playlists") == 'true' and not self.checkCancel()):
xbmcvfs.mkdir(self.remote_path + "userdata/playlists")
self.walkTree(self.local_path + "userdata/playlists")
if(self.Addon.getSetting("backup_thumbnails") == "true" and not self.checkCancel()):
xbmcvfs.mkdir(self.remote_path + "userdata/Thumbnails")
self.walkTree(self.local_path + "userdata/Thumbnails")
if(self.Addon.getSetting("backup_config") == "true" and not self.checkCancel()):
#this one is an oddity
configFiles = os.listdir(self.local_path + "userdata/")
for aFile in configFiles:
if(aFile.endswith(".xml")):
self.log("Copying: " + self.local_path + "userdata/" + aFile)
xbmcvfs.copy(self.local_path + "userdata/" + aFile,self.remote_path + "userdata/" + aFile)
#close out everything
self.restoreFile.closeRestoreFile(self.remote_path)
if(self.Addon.getSetting('run_silent') == 'false'):
self.progressBar.close()
def walkTree(self,directory):
for (path, dirs, files) in os.walk(directory):
if(not self.checkCancel()):
#get the relative part of this path
path = path[len(self.local_path):]
#subtract one from total
self.dirsLeft = self.dirsLeft - 1
self.updateProgress(len(dirs),"Copying: " + path)
#create all the subdirs first
for aDir in dirs:
xbmcvfs.mkdir(self.remote_path + os.sep + path + os.sep + aDir)
self.restoreFile.addFile(path + os.sep + aDir)
#copy all the files
for aFile in files:
filePath = path + os.sep + aFile
self.log("copying: " + self.local_path + filePath)
self.restoreFile.addFile(filePath)
xbmcvfs.copy(self.local_path + filePath,self.remote_path + filePath)
def updateProgress(self,dirCount,message=''):
#add to the total
self.dirsTotal = self.dirsTotal + dirCount
self.dirsLeft = self.dirsLeft + dirCount
def updateProgress(self,message=''):
self.filesLeft = self.filesLeft - 1
#update the progress bar
if(self.progressBar != None):
self.progressBar.update((float(self.dirsTotal - self.dirsLeft)/float(self.dirsTotal)) * 100,message)
self.progressBar.update((float(self.filesTotal - self.filesLeft)/float(self.filesTotal)) * 100,message)
def checkCancel(self):
result = False
@ -153,6 +183,6 @@ class XbmcBackup:
backup = XbmcBackup()
if(backup.isReady()):
backup.syncFiles()
backup.run()
else:
xbmcgui.Dialog().ok('XBMC Backup','Error: Remote path cannot be empty')

View File

@ -2,11 +2,12 @@
<strings>
<string id="30010">XBMC Backup</string>
<string id="30011">General</string>
<string id="30012">Backup Selection</string>
<string id="30012">File Selection</string>
<string id="30020">Remote Path</string>
<string id="30021">Backup Folder Name</string>
<string id="30022">Run Silent</string>
<string id="30023">Mode</string>
<string id="30030">User Addons</string>
<string id="30031">Addon Data</string>

View File

@ -3,6 +3,7 @@
<category id="general" label="30011">
<setting id="remote_path" type="folder" label="30020" />
<setting id="backup_name" type="text" label="30021" default="xbmc_backup"/>
<setting id="addon_mode" type="enum" values="Backup|Restore" default="Backup" label="30023" />
<setting id="run_silent" type="bool" label="30022" default="false" />
</category>
<category id="selection" label="30012">