mirror of
https://github.com/robweber/xbmcbackup.git
synced 2025-08-07 07:35:48 +02:00
Merge remote-tracking branch 'remotes/origin/frodo-dev'
Conflicts: addon.xml changelog.txt
This commit is contained in:
2
resources/.gitignore
vendored
Normal file
2
resources/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
*.pyo
|
@@ -15,6 +15,7 @@
|
||||
<string id="30023">Mode</string>
|
||||
<string id="30024">Type Remote Path</string>
|
||||
<string id="30025">Remote Path Type</string>
|
||||
<string id="30026">Backups to keep (0 for all)</string>
|
||||
|
||||
<string id="30030">User Addons</string>
|
||||
<string id="30031">Addon Data</string>
|
||||
@@ -32,6 +33,7 @@
|
||||
<string id="30051">Creating Files List</string>
|
||||
<string id="30052">Writing file</string>
|
||||
<string id="30053">Starting scheduled backup</string>
|
||||
<string id="30054">Removing backup</string>
|
||||
|
||||
<string id="30060">Enable Scheduler</string>
|
||||
<string id="30061">Schedule</string>
|
||||
|
2
resources/lib/.gitignore
vendored
Normal file
2
resources/lib/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
*.pyo
|
@@ -1,6 +1,6 @@
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import vfs as vfs
|
||||
import xbmcvfs
|
||||
import utils as utils
|
||||
import os
|
||||
import time
|
||||
@@ -9,6 +9,7 @@ class FileManager:
|
||||
walk_path = ''
|
||||
fileArray = None
|
||||
verbose_log = False
|
||||
not_dir = ['.zip','.xsp','.rar']
|
||||
|
||||
def __init__(self,path):
|
||||
self.walk_path = path
|
||||
@@ -48,21 +49,28 @@ class FileManager:
|
||||
self.walkTree(self.walk_path + "userdata/peripheral_data")
|
||||
|
||||
#this part is an oddity
|
||||
configFiles = vfs.listdir(self.walk_path + "userdata/",extra_metadata=True)
|
||||
dirs,configFiles = xbmcvfs.listdir(self.walk_path + "userdata/")
|
||||
for aFile in configFiles:
|
||||
if(aFile['file'].endswith(".xml")):
|
||||
self.addFile(aFile['file'][len(self.walk_path):])
|
||||
if(aFile.endswith(".xml")):
|
||||
self.addFile("userdata/" + aFile)
|
||||
|
||||
def walkTree(self,directory):
|
||||
for (path, dirs, files) in vfs.walk(directory):
|
||||
dirs,files = xbmcvfs.listdir(directory)
|
||||
|
||||
#create all the subdirs first
|
||||
for aDir in dirs:
|
||||
dirPath = xbmc.translatePath(directory + "/" + aDir)
|
||||
file_ext = aDir.split('.')[-1]
|
||||
|
||||
self.addFile("-" + dirPath[len(self.walk_path):].decode("UTF-8"))
|
||||
#catch for "non directory" type files
|
||||
if (not any(file_ext in s for s in self.not_dir)):
|
||||
self.walkTree(dirPath)
|
||||
|
||||
#create all the subdirs first
|
||||
for aDir in dirs:
|
||||
self.addFile("-" + aDir[len(self.walk_path):])
|
||||
#copy all the files
|
||||
for aFile in files:
|
||||
filePath = aFile[len(self.walk_path):]
|
||||
self.addFile(filePath)
|
||||
#copy all the files
|
||||
for aFile in files:
|
||||
filePath = xbmc.translatePath(directory + "/" + aFile)
|
||||
self.addFile(filePath[len(self.walk_path):].decode("UTF-8"))
|
||||
|
||||
def addFile(self,filename):
|
||||
#write the full remote path name of this file
|
||||
@@ -78,6 +86,7 @@ class XbmcBackup:
|
||||
Restore = 1
|
||||
|
||||
local_path = ''
|
||||
remote_root = ''
|
||||
remote_path = ''
|
||||
restoreFile = None
|
||||
|
||||
@@ -92,17 +101,17 @@ class XbmcBackup:
|
||||
self.local_path = xbmc.makeLegalFilename(xbmc.translatePath("special://home"),False);
|
||||
|
||||
if(utils.getSetting('remote_selection') == '1'):
|
||||
self.remote_path = utils.getSetting('remote_path_2')
|
||||
self.remote_root = utils.getSetting('remote_path_2')
|
||||
utils.setSetting("remote_path","")
|
||||
elif(utils.getSetting('remote_selection') == '0'):
|
||||
self.remote_path = utils.getSetting("remote_path")
|
||||
self.remote_root = utils.getSetting("remote_path")
|
||||
|
||||
#fix slashes
|
||||
self.remote_path = self.remote_path.replace("\\","/")
|
||||
self.remote_root = self.remote_root.replace("\\","/")
|
||||
|
||||
#check if trailing slash is included
|
||||
if(self.remote_path[-1:] != "/"):
|
||||
self.remote_path = self.remote_path + "/"
|
||||
if(self.remote_root[-1:] != "/"):
|
||||
self.remote_root = self.remote_root + "/"
|
||||
|
||||
utils.log(utils.getString(30046))
|
||||
|
||||
@@ -117,10 +126,10 @@ class XbmcBackup:
|
||||
mode = int(utils.getSetting('addon_mode'))
|
||||
|
||||
#append backup folder name
|
||||
if(mode == self.Backup and self.remote_path != ''):
|
||||
self.remote_path = self.remote_path + time.strftime("%Y%m%d") + "/"
|
||||
elif(mode == self.Restore and utils.getSetting("backup_name") != '' and self.remote_path != ''):
|
||||
self.remote_path = self.remote_path + utils.getSetting("backup_name") + "/"
|
||||
if(mode == self.Backup and self.remote_root != ''):
|
||||
self.remote_path = self.remote_root + time.strftime("%Y%m%d") + "/"
|
||||
elif(mode == self.Restore and utils.getSetting("backup_name") != '' and self.remote_root != ''):
|
||||
self.remote_path = self.remote_root + utils.getSetting("backup_name") + "/"
|
||||
else:
|
||||
self.remote_path = ""
|
||||
|
||||
@@ -133,17 +142,37 @@ class XbmcBackup:
|
||||
self.fileManager = FileManager(self.local_path)
|
||||
|
||||
#for backups check if remote path exists
|
||||
if(vfs.exists(self.remote_path)):
|
||||
if(xbmcvfs.exists(self.remote_path)):
|
||||
#this will fail - need a disclaimer here
|
||||
utils.log(utils.getString(30050))
|
||||
|
||||
self.syncFiles()
|
||||
|
||||
#remove old backups
|
||||
total_backups = int(utils.getSetting('backup_rotation'))
|
||||
if(total_backups > 0):
|
||||
|
||||
dirs,files = xbmcvfs.listdir(self.remote_root)
|
||||
if(len(dirs) > total_backups):
|
||||
#remove backups to equal total wanted
|
||||
dirs.sort()
|
||||
remove_num = len(dirs) - total_backups - 1
|
||||
self.filesTotal = self.filesTotal + remove_num + 1
|
||||
|
||||
#update the progress bar if it is available
|
||||
while(remove_num >= 0 and not self.checkCancel()):
|
||||
self.updateProgress(utils.getString(30054) + " " + dirs[remove_num])
|
||||
utils.log("Removing backup " + dirs[remove_num])
|
||||
xbmcvfs.rmdir(self.remote_root + dirs[remove_num] + "/",True)
|
||||
remove_num = remove_num - 1
|
||||
|
||||
|
||||
else:
|
||||
utils.log(utils.getString(30023) + " - " + utils.getString(30017))
|
||||
self.fileManager = FileManager(self.remote_path)
|
||||
|
||||
#for restores remote path must exist
|
||||
if(vfs.exists(self.remote_path)):
|
||||
if(xbmcvfs.exists(self.remote_path)):
|
||||
self.restoreFiles()
|
||||
else:
|
||||
xbmcgui.Dialog().ok(utils.getString(30010),utils.getString(30045),self.remote_path)
|
||||
@@ -154,7 +183,7 @@ class XbmcBackup:
|
||||
def syncFiles(self):
|
||||
|
||||
#make the remote directory
|
||||
vfs.mkdir(self.remote_path)
|
||||
xbmcvfs.mkdir(self.remote_path)
|
||||
|
||||
utils.log(utils.getString(30051))
|
||||
self.fileManager.createFileList()
|
||||
@@ -187,9 +216,9 @@ class XbmcBackup:
|
||||
utils.log('Writing file: ' + source + aFile,xbmc.LOGDEBUG)
|
||||
self.updateProgress(aFile)
|
||||
if (aFile.startswith("-")):
|
||||
vfs.mkdir(xbmc.makeLegalFilename(dest + aFile[1:],False))
|
||||
xbmcvfs.mkdir(xbmc.makeLegalFilename(dest + aFile[1:],False))
|
||||
else:
|
||||
vfs.copy(xbmc.makeLegalFilename(source + aFile),xbmc.makeLegalFilename(dest + aFile,False))
|
||||
xbmcvfs.copy(xbmc.makeLegalFilename(source + aFile),xbmc.makeLegalFilename(dest + aFile,False))
|
||||
|
||||
def updateProgress(self,message=''):
|
||||
self.filesLeft = self.filesLeft - 1
|
||||
@@ -207,4 +236,4 @@ class XbmcBackup:
|
||||
return result
|
||||
|
||||
def isReady(self):
|
||||
return True if self.remote_path != '' else False
|
||||
return True if self.remote_root != '' else False
|
||||
|
@@ -1,256 +0,0 @@
|
||||
'''
|
||||
Convenience wrappers and extensions for some commonly used VFS functions
|
||||
in XBMC addons. This module exposes all the functionality of xbmcvfs plus
|
||||
some extra functions.
|
||||
|
||||
Copyright (C) 2012 Patrick Carey
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import json
|
||||
import os
|
||||
import xbmc
|
||||
import xbmcvfs
|
||||
import urllib
|
||||
|
||||
def walk(path):
|
||||
|
||||
'''
|
||||
Reimplementation of os.walk using XBMC's jsonrpc API.
|
||||
|
||||
This has the nice added benefits of being able to walk remote
|
||||
directories and inside compressed files such as rars/zips.
|
||||
'''
|
||||
|
||||
dir_tree = [[path]]
|
||||
|
||||
current_depth = 0
|
||||
|
||||
not_dir = ['.zip','.xsp','.rar']
|
||||
|
||||
while True:
|
||||
|
||||
if current_depth > -1:
|
||||
|
||||
try:
|
||||
|
||||
current_path = dir_tree[current_depth].pop(0)
|
||||
current_dirs, current_files = [], []
|
||||
|
||||
for x in listdir(current_path, extra_metadata=True):
|
||||
|
||||
file_ext = x['file'].split('.')[-1]
|
||||
|
||||
if x['filetype'] == 'directory' and not any(file_ext in s for s in not_dir):
|
||||
|
||||
current_dirs.append(urllib.unquote(x['file']))
|
||||
|
||||
else:
|
||||
|
||||
current_files.append(urllib.unquote(x['file']))
|
||||
|
||||
except IndexError:
|
||||
|
||||
current_depth -= 1
|
||||
|
||||
dir_tree.pop()
|
||||
|
||||
else:
|
||||
|
||||
yield (current_path, current_dirs, current_files)
|
||||
|
||||
if current_dirs:
|
||||
|
||||
current_depth += 1
|
||||
|
||||
dir_tree.append(current_dirs)
|
||||
|
||||
else:
|
||||
|
||||
break
|
||||
|
||||
|
||||
def listdir(path, extra_metadata=False):
|
||||
|
||||
'''
|
||||
Reimplementation of os.listdir using XBMC's jsonrpc API.
|
||||
|
||||
Returns a list of file/directory names from the specified path
|
||||
|
||||
Accepts an optional boolean 'extra_metadata' as the second argument
|
||||
which will cause the function to instead return a list of dictionaries
|
||||
containing all of the metadata about each file that was retrieved from
|
||||
XBMC.
|
||||
'''
|
||||
|
||||
fileList = []
|
||||
|
||||
json_response = xbmc.executeJSONRPC('{ "jsonrpc" : "2.0" , "method" : "Files.GetDirectory" , "params" : { "directory" : "%s" , "sort" : { "method" : "file" } } , "id" : 1 }' % path.encode('utf-8').replace('\\', '\\\\'))
|
||||
|
||||
jsonobject = json.loads(json_response)
|
||||
|
||||
if jsonobject.has_key('result') and jsonobject['result']['files']:
|
||||
|
||||
for item in jsonobject['result']['files']:
|
||||
|
||||
if extra_metadata:
|
||||
|
||||
fileList.append(item)
|
||||
|
||||
else:
|
||||
|
||||
fileList.append(item['file'])
|
||||
|
||||
return fileList
|
||||
|
||||
|
||||
def copy(source, destination):
|
||||
|
||||
"""
|
||||
copy(source, destination) -- Copy file to destination, returns true/false.
|
||||
|
||||
source : file to copy.
|
||||
destination : destination file
|
||||
|
||||
example:
|
||||
- success = vfs.copy(source, destination)
|
||||
"""
|
||||
|
||||
return xbmcvfs.copy(source, destination)
|
||||
|
||||
|
||||
def delete(path):
|
||||
|
||||
"""
|
||||
delete(file) -- Delete file
|
||||
|
||||
file : file to delete
|
||||
|
||||
example:
|
||||
- vfs.delete(file)
|
||||
"""
|
||||
|
||||
return xbmcvfs.delete(path)
|
||||
|
||||
|
||||
def exists(path):
|
||||
|
||||
"""
|
||||
exists(path) -- Check if file exists, returns true/false.
|
||||
|
||||
path : file or folder
|
||||
|
||||
example:
|
||||
- success = vfs.exists(path)
|
||||
"""
|
||||
|
||||
return xbmcvfs.exists(path)
|
||||
|
||||
|
||||
def mkdir(path):
|
||||
|
||||
"""
|
||||
mkdir(path) -- Create a folder.
|
||||
|
||||
path : folder
|
||||
|
||||
example:
|
||||
- success = vfs.mkdir(path)
|
||||
"""
|
||||
|
||||
return xbmcvfs.mkdir(path)
|
||||
|
||||
|
||||
def rename(source, target):
|
||||
|
||||
"""
|
||||
rename(file, newFileName) -- Rename file, returns true/false.
|
||||
|
||||
file : file to reaname
|
||||
newFileName : new filename, including the full path
|
||||
|
||||
example:
|
||||
- success = vfs.rename(file, newFileName)
|
||||
"""
|
||||
|
||||
return xbmcvfs.rename(source, target)
|
||||
|
||||
|
||||
def rmdir(path):
|
||||
|
||||
"""
|
||||
rmdir(path) -- Remove a folder.
|
||||
|
||||
path : folder
|
||||
|
||||
example:
|
||||
- success = vfs.rmdir(path)
|
||||
"""
|
||||
|
||||
return xbmcvfs.rmdir(path)
|
||||
|
||||
|
||||
def comparepathlists(list1, list2, fullpath=False):
|
||||
|
||||
"""
|
||||
comparepathlists(list1, list2) -- Compare two lists of paths
|
||||
|
||||
list1 : list, contains paths (local or remote, absolute or relative)
|
||||
list2 : list, contains paths (local or remote, absolute or relative)
|
||||
fullpath : boolean, set True to compare perform straight comparison of lists
|
||||
set False (default) to compare on filename portions of each list
|
||||
|
||||
returns: dictionary:
|
||||
common_items: list, contains paths of items common to both lists
|
||||
list1_items: list, contains paths of items found only in list1
|
||||
list2_items: list, contains paths of items found only in list2
|
||||
|
||||
example:
|
||||
- compare = comparepathlists(list1, list2)
|
||||
"""
|
||||
|
||||
# initialise dict to store results and temp data
|
||||
results = {}
|
||||
temp_data = {}
|
||||
|
||||
if fullpath:
|
||||
|
||||
temp_path = lambda x: x
|
||||
|
||||
else:
|
||||
|
||||
temp_path = lambda x: os.path.split(x)[1]
|
||||
|
||||
for path in list1:
|
||||
|
||||
temp_data['list1'].append(temp_path(path))
|
||||
|
||||
for path in list2:
|
||||
|
||||
temp_data['list2'].append(temp_path(path))
|
||||
|
||||
# get items not in list 2
|
||||
results['list1_items'] = []
|
||||
gen = (i for i, x in enumerate(temp_data['list1']) if not x in temp_data['list2'])
|
||||
for i in gen:
|
||||
results['list1_items'].append(list1[i])
|
||||
|
||||
# get items not in list 1
|
||||
results['list2_items'] = []
|
||||
gen = (i for i, x in enumerate(temp_data['list2']) if not x in temp_data['list1'])
|
||||
for i in gen:
|
||||
results['list2_items'].append(list2[i])
|
||||
|
||||
return results
|
@@ -6,6 +6,7 @@
|
||||
<setting id="remote_path_2" type="text" label="30024" default="" visible="eq(-1,1)" />
|
||||
<setting id="remote_path" type="folder" label="30020" visible="eq(-2,0)" />
|
||||
<setting id="backup_name" type="text" label="30021" default="backup_date" visible="eq(-4,1)"/>
|
||||
<setting id="backup_rotation" type="number" label="30026" default="0" />
|
||||
<setting id="run_silent" type="bool" label="30022" default="false" />
|
||||
</category>
|
||||
<category id="selection" label="30012">
|
||||
|
Reference in New Issue
Block a user