mirror of
https://github.com/robweber/xbmcbackup.git
synced 2024-11-14 20:35:48 +01:00
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:
parent
140965733c
commit
f520d58c6b
@ -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.
|
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:
|
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.
|
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.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="script.xbmcbackup"
|
<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>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.0"/>
|
<import addon="xbmc.python" version="2.0"/>
|
||||||
</requires>
|
</requires>
|
||||||
@ -8,7 +8,7 @@
|
|||||||
<provides>executable</provides>
|
<provides>executable</provides>
|
||||||
</extension>
|
</extension>
|
||||||
<extension point="xbmc.addon.metadata">
|
<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>
|
<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>
|
<platform>all</platform>
|
||||||
</extension>
|
</extension>
|
||||||
|
184
default.py
184
default.py
@ -4,31 +4,78 @@ import xbmcgui
|
|||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class RestoreFile:
|
class FileManager:
|
||||||
|
local_path = ''
|
||||||
addonDir = ''
|
addonDir = ''
|
||||||
fHandle = None
|
fHandle = None
|
||||||
|
|
||||||
def __init__(self,addon_dir):
|
def __init__(self,addon_dir):
|
||||||
|
self.local_path = xbmc.translatePath("special://home")
|
||||||
self.addonDir = addon_dir
|
self.addonDir = addon_dir
|
||||||
|
|
||||||
def createRestoreFile(self):
|
|
||||||
#create the addon folder if it doesn't exist
|
#create the addon folder if it doesn't exist
|
||||||
if(not os.path.exists(unicode(xbmc.translatePath(self.addonDir),'utf-8'))):
|
if(not os.path.exists(unicode(xbmc.translatePath(self.addonDir),'utf-8'))):
|
||||||
os.makedirs(unicode(xbmc.translatePath(self.addonDir),'utf-8'))
|
os.makedirs(unicode(xbmc.translatePath(self.addonDir),'utf-8'))
|
||||||
xbmc.log(self.addonDir)
|
|
||||||
self.fHandle = open(unicode(xbmc.translatePath(self.addonDir + "restore.txt"),'utf-8'),"w")
|
|
||||||
|
|
||||||
|
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):
|
def addFile(self,filename):
|
||||||
#write the full remote path name of this file
|
#write the full remote path name of this file
|
||||||
if(self.fHandle != None):
|
if(self.fHandle != None):
|
||||||
self.fHandle.write(str(filename) + "\n")
|
self.fHandle.write(str(filename) + "\n")
|
||||||
|
|
||||||
def closeRestoreFile(self,sendDir):
|
def readFileList(self):
|
||||||
#close out the file and write it to the remote store
|
allFiles = open(unicode(xbmc.translatePath(self.addonDir + "restore.txt"),'utf-8'),"r").read().splitlines()
|
||||||
if(self.fHandle != None):
|
|
||||||
self.fHandle.close()
|
return allFiles
|
||||||
#xbmcvfs.copy(self.addonDir + "restore.txt",sendDir + "restore.txt")
|
|
||||||
|
|
||||||
|
|
||||||
class XbmcBackup:
|
class XbmcBackup:
|
||||||
__addon_id__ = 'script.xbmcbackup'
|
__addon_id__ = 'script.xbmcbackup'
|
||||||
@ -39,8 +86,10 @@ class XbmcBackup:
|
|||||||
|
|
||||||
#for the progress bar
|
#for the progress bar
|
||||||
progressBar = None
|
progressBar = None
|
||||||
dirsLeft = 0
|
filesLeft = 0
|
||||||
dirsTotal = 0
|
filesTotal = 0
|
||||||
|
|
||||||
|
fileManager = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.local_path = xbmc.translatePath("special://home")
|
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") != ''):
|
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.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("Starting")
|
||||||
self.log('Local Dir: ' + self.local_path)
|
self.log('Local Dir: ' + self.local_path)
|
||||||
self.log('Remote Dir: ' + self.remote_path)
|
self.log('Remote Dir: ' + self.remote_path)
|
||||||
|
|
||||||
|
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):
|
def syncFiles(self):
|
||||||
self.restoreFile.createRestoreFile()
|
|
||||||
|
|
||||||
if(xbmcvfs.exists(self.remote_path)):
|
if(xbmcvfs.exists(self.remote_path)):
|
||||||
#this will fail - need a disclaimer here
|
#this will fail - need a disclaimer here
|
||||||
self.log("Remote Path exists - may have old files in it!")
|
self.log("Remote Path exists - may have old files in it!")
|
||||||
|
|
||||||
|
#make the remote directory
|
||||||
xbmcvfs.mkdir(self.remote_path)
|
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'):
|
if(self.Addon.getSetting('run_silent') == 'false'):
|
||||||
self.progressBar = xbmcgui.DialogProgress()
|
self.progressBar = xbmcgui.DialogProgress()
|
||||||
self.progressBar.create('XBMC Backup','Running......')
|
self.progressBar.create('XBMC Backup','Running......')
|
||||||
self.updateProgress(1)
|
|
||||||
|
|
||||||
#figure out which syncing options to run
|
#write each file from source to destination
|
||||||
if(self.Addon.getSetting('backup_addons') == 'true' and not self.checkCancel()):
|
for aFile in fileList:
|
||||||
xbmcvfs.mkdir(self.remote_path + "addons")
|
if(not self.checkCancel()):
|
||||||
self.walkTree(self.local_path + "addons/")
|
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'):
|
if(self.Addon.getSetting('run_silent') == 'false'):
|
||||||
self.progressBar.close()
|
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
|
def updateProgress(self,message=''):
|
||||||
for aDir in dirs:
|
self.filesLeft = self.filesLeft - 1
|
||||||
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
|
|
||||||
|
|
||||||
#update the progress bar
|
#update the progress bar
|
||||||
if(self.progressBar != None):
|
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):
|
def checkCancel(self):
|
||||||
result = False
|
result = False
|
||||||
@ -153,6 +183,6 @@ class XbmcBackup:
|
|||||||
backup = XbmcBackup()
|
backup = XbmcBackup()
|
||||||
|
|
||||||
if(backup.isReady()):
|
if(backup.isReady()):
|
||||||
backup.syncFiles()
|
backup.run()
|
||||||
else:
|
else:
|
||||||
xbmcgui.Dialog().ok('XBMC Backup','Error: Remote path cannot be empty')
|
xbmcgui.Dialog().ok('XBMC Backup','Error: Remote path cannot be empty')
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
<strings>
|
<strings>
|
||||||
<string id="30010">XBMC Backup</string>
|
<string id="30010">XBMC Backup</string>
|
||||||
<string id="30011">General</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="30020">Remote Path</string>
|
||||||
<string id="30021">Backup Folder Name</string>
|
<string id="30021">Backup Folder Name</string>
|
||||||
<string id="30022">Run Silent</string>
|
<string id="30022">Run Silent</string>
|
||||||
|
<string id="30023">Mode</string>
|
||||||
|
|
||||||
<string id="30030">User Addons</string>
|
<string id="30030">User Addons</string>
|
||||||
<string id="30031">Addon Data</string>
|
<string id="30031">Addon Data</string>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<category id="general" label="30011">
|
<category id="general" label="30011">
|
||||||
<setting id="remote_path" type="folder" label="30020" />
|
<setting id="remote_path" type="folder" label="30020" />
|
||||||
<setting id="backup_name" type="text" label="30021" default="xbmc_backup"/>
|
<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" />
|
<setting id="run_silent" type="bool" label="30022" default="false" />
|
||||||
</category>
|
</category>
|
||||||
<category id="selection" label="30012">
|
<category id="selection" label="30012">
|
||||||
|
Loading…
Reference in New Issue
Block a user