1076 lines
48 KiB
Python
1076 lines
48 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# script.module.python.koding.aio
|
|
# Python Koding AIO (c) by TOTALREVOLUTION LTD (support@trmc.freshdesk.com)
|
|
|
|
# Python Koding AIO is licensed under a
|
|
# Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
|
|
# You should have received a copy of the license along with this
|
|
# work. If not, see http://creativecommons.org/licenses/by-nc-nd/4.0.
|
|
|
|
# Please make sure you've read and understood the license, this code can NOT be used commercially
|
|
# and it can NOT be modified and redistributed. If you're found to be in breach of this license
|
|
# then any affected add-ons will be blacklisted and will not be able to work on the same system
|
|
# as any other add-ons which use this code. Thank you for your cooperation.
|
|
|
|
import os
|
|
import shutil
|
|
import os
|
|
import sys
|
|
import xbmc
|
|
import xbmcaddon
|
|
import xbmcgui
|
|
import xbmcvfs
|
|
|
|
from systemtools import Last_Error
|
|
from xml.etree import ElementTree
|
|
|
|
dp = xbmcgui.DialogProgress()
|
|
dialog = xbmcgui.Dialog()
|
|
HOME = 'special://home'
|
|
PROFILE = 'special://profile'
|
|
DATABASE = os.path.join(PROFILE,'Database')
|
|
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
class xml(object):
|
|
"""
|
|
SETTINGS - CREATE CUSTOM ADD-ON SETTINGS:
|
|
All credit goes to OptimusGREEN for this module.
|
|
|
|
This will create a new settings file for your add-on which you can read and write to. This is separate
|
|
to the standard settings.xml and you can call the file whatever you want, however you would presumably
|
|
call it something other than settings.xml as that is already used by Kodi add-ons.
|
|
|
|
CODE: XML(path)
|
|
|
|
AVAILABLE CLASSES:
|
|
|
|
ParseValue - This class will allow you to get the value of an item in these custom settings.
|
|
|
|
SetValue - This class allows you to set a value to the custom settings. If the settings.xml doesn't exist it will be automatically created so long as the path given in XML is writeable.
|
|
|
|
|
|
EXAMPLE CODE:
|
|
myXmlFile = "special://userdata/addon_data/script.module.python.koding.aio/timefile.xml"
|
|
timefile = koding.xml(myXmlFile)
|
|
getSetting = timefile.ParseValue
|
|
setSetting = timefile.SetValue
|
|
dialog.ok('CHECK SETTINGS','If you take a look in the addon_data folder for python koding a new file called timefile.xml will be created when you click OK.')
|
|
setSetting("autorun", "true")
|
|
autoRun = getSetting("autorun")
|
|
dialog.ok('AUTORUN VALUE','The value of autorun in these new settings is [COLOR dodgerblue]%s[/COLOR].[CR][CR]Press OK to delete this file.'%autoRun)
|
|
os.remove(koding.Physical_Path(myXmlFile))
|
|
~"""
|
|
|
|
def __init__(self, xmlFile, masterTag="settings", childTag="setting"):
|
|
self.xmlFile = xmlFile
|
|
self.masterTag = masterTag
|
|
self.childTag = childTag
|
|
self.xmlFile = Physical_Path(self.xmlFile)
|
|
|
|
def ParseValue(self, settingID, settingIDTag="id", settingValueTag="value", addChild=False, formatXML=True):
|
|
if not os.path.exists(self.xmlFile):
|
|
return
|
|
tree = ElementTree.parse(self.xmlFile)
|
|
root = tree.getroot()
|
|
for child in root:
|
|
if child.attrib[settingIDTag] == settingID:
|
|
return child.attrib.get(settingValueTag)
|
|
|
|
def SetValue(self, settingID, newValue, settingIDTag="id", settingValueTag="value", addChild=False, asString=False, formatXML=True):
|
|
if not os.path.exists(self.xmlFile):
|
|
self.CreateXML(settingIDTag=settingIDTag, settingValueTag=settingValueTag, addChild=addChild, formatXML=formatXML)
|
|
tree = ElementTree.parse(self.xmlFile)
|
|
root = tree.getroot()
|
|
targetChild = None
|
|
for child in root:
|
|
if child.attrib[settingIDTag] == settingID:
|
|
targetChild = child
|
|
if targetChild is None:
|
|
self.AppendChild(root, settingID=settingID, newValue=newValue, settingIDTag=settingIDTag, settingValueTag=settingValueTag)
|
|
else:
|
|
for child in root:
|
|
if child.attrib[settingIDTag] == settingID:
|
|
child.attrib['%s' % (settingValueTag)] = '%s' % (newValue)
|
|
tree.write(self.xmlFile)
|
|
if asString:
|
|
readfile = open(self.xmlFile, 'r')
|
|
content = readfile.read()
|
|
readfile.close()
|
|
pretty = self.Prettify(content, asString=True)
|
|
else:
|
|
pretty = self.Prettify(self.xmlFile)
|
|
f = open(self.xmlFile, "w")
|
|
f.write(pretty)
|
|
f.close()
|
|
|
|
def CreateXML(self, settingIDTag="id", settingValueTag="value", addChild=False, formatXML=True):
|
|
root = ElementTree.Element("%s" % self.masterTag)
|
|
if addChild:
|
|
sub = ElementTree.SubElement(root, "%s" % self.childTag)
|
|
sub.set(settingIDTag, "")
|
|
sub.set(settingValueTag, "")
|
|
|
|
tree = ElementTree.ElementTree(root)
|
|
tree.write(self.xmlFile)
|
|
if formatXML:
|
|
pretty = self.Prettify(self.xmlFile)
|
|
f = open(self.xmlFile, "w")
|
|
f.write(pretty)
|
|
f.close()
|
|
|
|
def AppendChild(self, root, settingID, newValue, settingIDTag="id", settingValueTag="value"):
|
|
ElementTree.SubElement(root, self.childTag, attrib={settingIDTag: settingID, settingValueTag: newValue})
|
|
return root
|
|
|
|
def Prettify(self, elem, asString=False):
|
|
import xml.dom.minidom
|
|
if asString:
|
|
xml = xml.dom.minidom.parseString(elem)
|
|
pretty_xml_as_string = '\n'.join([line for line in xml.toprettyxml(indent=' ' * 2).split('\n') if line.strip()])
|
|
else:
|
|
pretty_xml_as_string = '\n'.join([line for line in xml.dom.minidom.parse(open(elem)).toprettyxml(indent=' ' * 2).split('\n') if line.strip()])
|
|
return pretty_xml_as_string
|
|
#----------------------------------------------------------------
|
|
# Legacy code, now use new function Compress
|
|
def Archive_Tree(sourcefile, destfile, exclude_dirs=['temp'], exclude_files=['kodi.log','kodi.old.log','xbmc.log','xbmc.old.log','spmc.log','spmc.old.log'], message_header = 'ARCHIVING', message = 'Creating archive'):
|
|
Compress(src=sourcefile, dst=destfile, exclude_dirs=exclude_dirs, exclude_files=exclude_files)
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Compress(src,dst,compression='zip',parent=False, exclude_dirs=['temp'], exclude_files=['kodi.log','kodi.old.log','xbmc.log','xbmc.old.log','spmc.log','spmc.old.log'], message_header = 'ARCHIVING', message = 'Creating archive'):
|
|
"""
|
|
Compress files in either zip or tar format. This will most likely be replacing
|
|
Archive_Tree longer term as this has better functionality but it's currently
|
|
missing the custom message and exclude files options.
|
|
|
|
IMPORTANT: There was a known bug where some certain compressed tar.gz files can cause the system to hang
|
|
and a bad zipfile will continue to be made until it runs out of space on your storage device. In the unlikely
|
|
event you encounter this issue just add the problematic file(s) to your exclude list. I think this has since
|
|
been fixed since a complete re-code to this function, or at least I've been unable to recreate it. If you
|
|
find this problem is still occuring please let me know on the forum at http://totalrevolution.tv/forum
|
|
(user: trevdev), thankyou.
|
|
|
|
CODE: Compress(src,dst,[compression,parent])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) src - This is the source folder you want to compress
|
|
|
|
(*) dst - This is the destination file you want to create
|
|
|
|
compression - By default this is set to 'zip' but you can also use 'tar'
|
|
|
|
parent - By default this is set to False which means it will compress
|
|
everything inside the path given. If set to True it will do the same but
|
|
it will include the parent folder name - ideal if you want to zip up
|
|
an add-on folder and be able to install via Kodi Settings.
|
|
|
|
exclude_dirs - This is optional, if you have folder names you want to exclude just
|
|
add them here as a list item. By default the folder 'temp' is added to this list so
|
|
if you need to include folders called temp make sure you send through a list, even
|
|
if it's an empty one. The reason for leaving temp out is that's where Kodi logfiles
|
|
and crashlogs are stored on a lot of devices and these are generally not needed in
|
|
backup zips.
|
|
|
|
exclude_files - This is optional, if you have specific file names you want to
|
|
exclude just add them here as a list item. By default the list consists of:
|
|
'kodi.log','kodi.old.log','xbmc.log','xbmc.old.log','spmc.log','spmc.old.log'
|
|
|
|
EXAMPLE CODE:
|
|
koding_path = koding.Physical_Path('special://home/addons/script.module.python.koding.aio')
|
|
zip_dest = koding.Physical_Path('special://home/test_addon.zip')
|
|
zip_dest2 = koding.Physical_Path('special://home/test_addon2.zip')
|
|
tar_dest = koding.Physical_Path('special://home/test_addon.tar')
|
|
tar_dest2 = koding.Physical_Path('special://home/test_addon2.tar')
|
|
koding.Compress(src=koding_path,dst=zip_dest,compression='zip',parent=True)
|
|
koding.Compress(src=koding_path,dst=zip_dest2,compression='zip',parent=False)
|
|
koding.Compress(src=koding_path,dst=tar_dest,compression='tar',parent=True)
|
|
koding.Compress(src=koding_path,dst=tar_dest2,compression='tar',parent=False)
|
|
koding.Text_Box('CHECK HOME FOLDER','If you check your Kodi home folder you should now have 4 different compressed versions of the Python Koding add-on.\n\ntest_addon.zip: This has been zipped up with parent set to True\n\ntest_addon2.zip: This has been zipped up with parent set to False.\n\ntest_addon.tar: This has been compressed using tar format and parent set to True\n\ntest_addon2.tar: This has been compressed using tar format and parent set to False.\n\nFeel free to manually delete these.')
|
|
~"""
|
|
import zipfile
|
|
import tarfile
|
|
directory = os.path.dirname(dst)
|
|
if not os.path.exists(directory):
|
|
try:
|
|
os.makedirs(directory)
|
|
except:
|
|
dialog.ok('ERROR','The destination directory you gave does not exist and it wasn\'t possible to create it.')
|
|
return
|
|
if compression == 'zip':
|
|
zip = zipfile.ZipFile(dst, 'w', compression=zipfile.ZIP_DEFLATED)
|
|
elif compression == 'tar':
|
|
zip = tarfile.open(dst, mode='w')
|
|
module_id = 'script.module.python.koding.aio'
|
|
this_module = xbmcaddon.Addon(id=module_id)
|
|
folder_size = Folder_Size(src,'mb')
|
|
available_space = Free_Space(HOME,'mb')
|
|
if os.path.exists(src):
|
|
choice = True
|
|
if float(available_space) < float(folder_size):
|
|
choice = dialog.yesno(this_module.getLocalizedString(30809), this_module.getLocalizedString(30810), this_module.getLocalizedString(30811) % folder_size, this_module.getLocalizedString(30812) % available_space, yeslabel = this_module.getLocalizedString(30813), nolabel = this_module.getLocalizedString(30814))
|
|
if choice:
|
|
root_len = len(os.path.dirname(os.path.abspath(src)))
|
|
for base, dirs, files in os.walk(src):
|
|
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
|
files[:] = [f for f in files if f not in exclude_files and not 'crashlog' in f and not 'stacktrace' in f]
|
|
archive_root = os.path.abspath(base)[root_len:]
|
|
|
|
for f in files:
|
|
fullpath = os.path.join(base, f)
|
|
if parent:
|
|
archive_name = os.path.join(archive_root, f)
|
|
if compression == 'zip':
|
|
zip.write(fullpath, archive_name, zipfile.ZIP_DEFLATED)
|
|
elif compression == 'tar':
|
|
zip.add(fullpath, archive_name)
|
|
else:
|
|
newpath = fullpath.split(src)[1]
|
|
if compression == 'zip':
|
|
zip.write(fullpath, newpath, zipfile.ZIP_DEFLATED)
|
|
elif compression == 'tar':
|
|
zip.add(fullpath, newpath)
|
|
zip.close()
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Create_Paths(path=''):
|
|
"""
|
|
Send through a path to a file, if the directories required do not exist this will create them.
|
|
|
|
CODE: Create_Paths(path)
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) path - This is the full path including the filename. The path
|
|
sent through will be split up at every instance of '/'
|
|
|
|
EXAMPLE CODE:
|
|
my_path = xbmc.translatePath('special://home/test/testing/readme.txt')
|
|
koding.Create_Paths(path=my_path)
|
|
dialog.ok('PATH CREATED','Check in your Kodi home folder and you should now have sub-folders of /test/testing/.','[COLOR=gold]Press ok to remove these folders.[/COLOR]')
|
|
shutil.rmtree(xbmc.translatePath('special://home/test'))
|
|
~"""
|
|
home_path = Physical_Path('special://home')
|
|
path = path.replace(home_path,'')
|
|
newpath = os.path.join('special://home',path)
|
|
if path != '' and not os.path.exists(Physical_Path(newpath)):
|
|
root_path = path.split(os.sep)
|
|
if root_path[-1] == '':
|
|
root_path.pop()
|
|
root_path.pop()
|
|
final_path = ''
|
|
for item in root_path:
|
|
final_path = os.path.join(final_path,item)
|
|
final_path = os.path.join('special://home',final_path)
|
|
xbmcvfs.mkdirs(final_path)
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def DB_Path_Check(db_path):
|
|
"""
|
|
If you need to find out the current "real" database in use then this is the function for you.
|
|
It will scan for a specific database type (e.g. addons) and return the path to the one which was last updated.
|
|
This is particularly useful if the system has previously updated to a newer version rather than a fresh install
|
|
or if they've installed a "build" which contained old databases.
|
|
|
|
CODE: DB_Path_Check(db_path)
|
|
|
|
AVAILABLE VALUES:
|
|
|
|
(*) db_path - This is the string the database starts with.
|
|
If you want to find the path for the addons*.db you would use "addons"
|
|
as the value, if you wanted to find the path of the MyVideos*.db you would use
|
|
"myvideos" etc. - it is not case sensitive.
|
|
|
|
EXAMPLE CODE:
|
|
dbpath = koding.DB_Path_Check(db_path='addons')
|
|
dialog.ok('ADDONS DB','The path to the current addons database is:',dbpath)
|
|
~"""
|
|
finalfile = 0
|
|
dirs,databasepath = xbmcvfs.listdir(DATABASE)
|
|
for item in databasepath:
|
|
if item.lower().endswith('.db') and item.lower().startswith(db_path.lower()):
|
|
mydb = os.path.join(DATABASE,item)
|
|
lastmodified = xbmcvfs.Stat(mydb).st_mtime()
|
|
if lastmodified>finalfile:
|
|
finalfile = lastmodified
|
|
gooddb = mydb
|
|
return Physical_Path(gooddb)
|
|
#---------------------------------------------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Delete_Crashlogs(extra_paths=[]):
|
|
"""
|
|
Delete all kodi crashlogs. This function will retun the amount of successfully removed crashlogs.
|
|
|
|
CODE: Delete_Crashlogs([extra_paths])
|
|
|
|
AVAILABLE PARAMS:
|
|
extra_paths - By default this will search for crashlogs for xbmc,
|
|
kodi and spmc. If you want to add compatibility for other forks of
|
|
Kodi please send through a list of the files you want deleted. The
|
|
format to use needs to be like example shown below.
|
|
|
|
EXAMPLE CODE:
|
|
# Lets setup some extra crashlog types for tvmc and ftmc kodi forks
|
|
log_path = xbmc.translatePath('special://logpath/')
|
|
tvmc_path = os.path.join(log_path,'tvmc_crashlog*.*')
|
|
ftmc_path = os.path.join(log_path,'ftmc_crashlog*.*')
|
|
|
|
|
|
deleted_files = koding.Delete_Crashlogs(extra_paths=[tvmc_path, ftmc_path])
|
|
if deleted_files > 0:
|
|
dialog.ok('CRASHLOGS DELETED','Congratulations, a total of %s crashlogs have been deleted.')
|
|
else:
|
|
dialog.ok('NO CRASHLOGS','No crashlogs could be found on the system.')
|
|
~"""
|
|
import glob
|
|
log_path = 'special://logpath/'
|
|
xbmc_path = (os.path.join(log_path, 'xbmc_crashlog*.*'))
|
|
kodi_path = (os.path.join(log_path, 'kodi_crashlog*.*'))
|
|
spmc_path = (os.path.join(log_path, 'spmc_crashlog*.*'))
|
|
paths = [xbmc_path, kodi_path, spmc_path]
|
|
total = 0
|
|
for items in paths:
|
|
for file in glob.glob(items):
|
|
try:
|
|
xbmcvfs.delete(file)
|
|
total+=1
|
|
except:
|
|
pass
|
|
return total
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Delete_Files(filepath = HOME, filetype = '*.txt', subdirectories=False):
|
|
"""
|
|
Delete all specific filetypes in a path (including sub-directories)
|
|
|
|
CODE: Delete_Files([filepath, filetype, subdirectories])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) filepath - By default this points to the Kodi HOME folder (special://home).
|
|
The path you send through must be a physical path and not special://
|
|
|
|
(*) filetype - The type of files you want to delete, by default it's set to *.txt
|
|
|
|
subdirectories - By default it will only search the folder given, if set to True
|
|
all filetypes listed above will be deleted in the sub-directories too.
|
|
|
|
WARNING: This is an extremely powerful and dangerous tool! If you wipe your whole system
|
|
by putting in the wrong path then it's your own stupid fault!
|
|
|
|
EXAMPLE CODE:
|
|
delete_path = 'special://profile/addon_data/test'
|
|
xbmcvfs.mkdirs(delete_path)
|
|
test1 = os.path.join(delete_path,'test1.txt')
|
|
test2 = os.path.join(delete_path,'test2.txt')
|
|
koding.Text_File(test1,'w','testing1')
|
|
koding.Text_File(test2,'w','testing2')
|
|
dialog.ok('DELETE FILES','All *.txt files will be deleted from:', '', '/userdata/addon_data/test/')
|
|
koding.Delete_Files(filepath=delete_path, filetype='.txt', subdirectories=True)
|
|
~"""
|
|
filepath = Physical_Path(filepath)
|
|
if filepath == '/' or filepath == '.' or filepath == '' or (filepath[1]==':' and len(filepath)<4):
|
|
dialog.ok('IDTenT ERROR!!!','You are trying to wipe your whole system!!!','Be more careful in future, not everyone puts checks like this in their code!')
|
|
return
|
|
|
|
if os.path.exists(filepath):
|
|
filetype = filetype.replace('*','')
|
|
|
|
if subdirectories:
|
|
for parent, dirnames, filenames in os.walk(filepath):
|
|
for fn in filenames:
|
|
if fn.lower().endswith(filetype):
|
|
xbmcvfs.delete(os.path.join(parent, fn))
|
|
|
|
else:
|
|
for delete_file in xbmcvfs.listdir(filepath):
|
|
delete_path = os.path.join(filepath,delete_file)
|
|
if delete_path.endswith(filetype):
|
|
try:
|
|
xbmcvfs.delete(delete_path)
|
|
except:
|
|
xbmc.log(Last_Error(),2)
|
|
else:
|
|
xbmc.log('### Cannot delete files as directory does not exist: %s' % filepath,2)
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Delete_Folders(filepath='', ignore=[]):
|
|
"""
|
|
Completely delete a folder and all it's sub-folders. With the ability to add
|
|
an ignore list for any folders/files you don't want removed.
|
|
|
|
CODE: Delete_Folders(filepath, [ignore])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) filepath - Use the physical path you want to remove, this must be converted
|
|
to the physical path and will not work with special://
|
|
|
|
ignore - A list of paths you want to ignore. These need to be sent
|
|
through as physical paths so just use koding.Physical_Path() when creating
|
|
your list and these can be folder paths or filepaths.
|
|
|
|
WARNING: This is an extremely powerful and dangerous tool! If you wipe important
|
|
system files from your system by putting in the wrong path then I'm afraid that's
|
|
your own stupid fault! A check has been put in place so you can't accidentally
|
|
wipe the whole root.
|
|
|
|
EXAMPLE CODE:
|
|
delete_path = koding.Physical_Path('special://profile/py_koding_test')
|
|
|
|
# Create new test directory to remove
|
|
if not os.path.exists(delete_path):
|
|
os.makedirs(delete_path)
|
|
|
|
# Fill it with some dummy files
|
|
file1 = os.path.join(delete_path,'file1.txt')
|
|
file2 = os.path.join(delete_path,'file2.txt')
|
|
file3 = os.path.join(delete_path,'file3.txt')
|
|
koding.Dummy_File(dst=file1, size=10, size_format='kb')
|
|
koding.Dummy_File(dst=file2, size=10, size_format='kb')
|
|
koding.Dummy_File(dst=file3, size=10, size_format='kb')
|
|
|
|
dialog.ok('TEST FILE CREATED','If you look in your userdata folder you should now see a new test folder containing 3 dummy files. The folder name is \'py_koding_test\'.')
|
|
if dialog.yesno('[COLOR gold]DELETE FOLDER[/COLOR]','Everything except file1.txt will now be removed from:', '/userdata/py_koding_test/','Do you want to continue?'):
|
|
koding.Delete_Folders(filepath=delete_path, ignore=[file1])
|
|
dialog.ok('DELETE LEFTOVERS','When you press OK we will delete the whole temporary folder we created including it\'s contents')
|
|
koding.Delete_Folders(filepath=delete_path)
|
|
~"""
|
|
exclude_list = ['','/','\\','C:/','storage']
|
|
|
|
# Check you're not trying to wipe root!
|
|
if filepath in exclude_list:
|
|
dialog.ok('FILEPATH REQUIRED','You\'ve attempted to remove files but forgot to pass through a valid filepath. Luckily this failsafe check is in place or you could have wiped your whole system!')
|
|
|
|
# If there's some ignore files we run through deleting everything but those files
|
|
elif len(ignore) > 0:
|
|
for root, dirs, files in os.walk(filepath, topdown=False):
|
|
cont = True
|
|
if not root in ignore:
|
|
for item in ignore:
|
|
if item in root:
|
|
cont=False
|
|
break
|
|
if cont:
|
|
for file in files:
|
|
file_path = os.path.join(root,file)
|
|
if file_path not in ignore:
|
|
try:
|
|
xbmcvfs.delete(file_path)
|
|
except:
|
|
xbmc.log('Failed to delete: %s'%file_path,2)
|
|
if len(os.listdir(root)) == 0:
|
|
try:
|
|
xbmcvfs.rmdir(root)
|
|
except:
|
|
pass
|
|
|
|
# If a simple complete wipe of a directory and all sub-directories is required we use this
|
|
elif os.path.exists(filepath):
|
|
shutil.rmtree(filepath, ignore_errors=True)
|
|
# xbmc.executebuiltin('Container.Refresh')
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Dummy_File(dst= 'special://home/dummy.txt', size='10', size_format='mb'):
|
|
"""
|
|
Create a dummy file in whatever location you want and with the size you want.
|
|
Use very carefully, this is designed for testing purposes only. Accidental
|
|
useage can result in the devices storage becoming completely full in just a
|
|
few seconds. If using a cheap poor quality device (like many android units)
|
|
then you could even end up killing the device as some of them are made
|
|
with very poor components which are liable to irreversable corruption.
|
|
|
|
CODE: koding.Dummy_File(dest, [size, size_format])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
dst - This is the destination folder. This needs to be a FULL path including
|
|
the file extension. By default this is set to special://home/dummy.txt
|
|
|
|
size - This is an optional integer, by default a file of 10 MB will be created.
|
|
|
|
size_format - By default this is set to 'mb' (megabytes) but you can change this to
|
|
'b' (bytes), 'kb' (kilobytes), 'gb' (gigabytes)
|
|
|
|
EXAMPLE CODE:
|
|
dummy = 'special://home/test_dummy.txt'
|
|
koding.Dummy_File(dst=dummy, size=100, size_format='b')
|
|
dialog.ok('DUMMY FILE CREATED','Check your Kodi home folder and you should see a 100 byte test_dummy.txt file.','[COLOR=gold]Press OK to delete this file.[/COLOR]')
|
|
xbmcvfs.delete(dummy)
|
|
~"""
|
|
dst = Physical_Path(dst)
|
|
xbmc.log('dst: %s'%dst,2)
|
|
if size_format == 'kb':
|
|
size = float(size*1024)
|
|
elif size_format == 'mb':
|
|
size = float(size*1024) * 1024
|
|
elif size_format == 'gb':
|
|
size = float(size*1024) * 1024 * 1024
|
|
|
|
xbmc.log('format: %s size: %s'%(size_format, size), 2)
|
|
|
|
f = open(dst,"wb")
|
|
f.seek(size-1)
|
|
f.write("\0")
|
|
f.close()
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def End_Path(path):
|
|
"""
|
|
Split the path at every '/' and return the final file/folder name.
|
|
If your path uses backslashes rather than forward slashes it will use
|
|
that as the separator.
|
|
|
|
CODE: End_Path(path)
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
path - This is the path where you want to grab the end item name.
|
|
|
|
EXAMPLE CODE:
|
|
addons_path = 'special://home/addons'
|
|
file_name = koding.End_Path(path=addons_path)
|
|
dialog.ok('ADDONS FOLDER','Path checked:',addons_path,'Folder Name: [COLOR=dodgerblue]%s[/COLOR]'%file_name)
|
|
file_path = 'special://home/addons/script.module.python.koding.aio/addon.xml'
|
|
file_name = koding.End_Path(path=file_path)
|
|
dialog.ok('FILE NAME','Path checked:',file_path,'File Name: [COLOR=dodgerblue]%s[/COLOR]'%file_name)
|
|
~"""
|
|
if '/' in path:
|
|
path_array = path.split('/')
|
|
if path_array[-1] == '':
|
|
path_array.pop()
|
|
elif '\\' in path:
|
|
path_array = path.split('\\')
|
|
if path_array[-1] == '':
|
|
path_array.pop()
|
|
else:
|
|
return path
|
|
return path_array[-1]
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Extract(_in, _out, dp=None, show_error=False):
|
|
"""
|
|
This function will extract a zip or tar file and return true or false so unlike the
|
|
builtin xbmc function "Extract" this one will pause code until it's completed the action.
|
|
|
|
CODE: koding.Extract(src,dst,[dp])
|
|
dp is optional, by default it is set to false
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) src - This is the source file, the actual zip/tar. Make sure this is a full path to
|
|
your zip file and also make sure you're not using "special://". This extract function
|
|
is only compatible with .zip/.tar/.tar.gz files
|
|
|
|
(*) dst - This is the destination folder, make sure it's a physical path and not
|
|
"special://...". This needs to be a FULL path, if you want it to extract to the same
|
|
location as where the zip is located you still have to enter the full path.
|
|
|
|
dp - This is optional, if you pass through the dp function as a DialogProgress()
|
|
then you'll get to see the status of the extraction process. If you choose not to add
|
|
this paramater then you'll just get a busy spinning circle icon until it's completed.
|
|
See the example below for a dp example.
|
|
|
|
show_error - By default this is set to False, if set to True an error dialog
|
|
will appear showing details of the file which failed to extract.
|
|
|
|
EXAMPLE CODE:
|
|
koding_path = koding.Physical_Path('special://home/addons/script.module.python.koding.aio')
|
|
zip_dest = koding.Physical_Path('special://home/test_addon.zip')
|
|
extract_dest = koding.Physical_Path('special://home/TEST')
|
|
koding.Compress(src=koding_path,dst=zip_dest,compression='zip',parent=True)
|
|
dp = xbmcgui.DialogProgress()
|
|
dp.create('Extracting Zip','Please Wait')
|
|
if koding.Extract(_in=zip_dest,_out=extract_dest,dp=dp,show_error=True):
|
|
dialog.ok('YAY IT WORKED!','We just zipped up your python koding add-on then extracted it to a new folder in your Kodi root directory called TEST. Press OK to delete these files.')
|
|
xbmcvfs.delete(zip_dest)
|
|
shutil.rmtree(extract_dest)
|
|
else:
|
|
dialog.ok('BAD NEWS!','UH OH SOMETHING WENT HORRIBLY WRONG')
|
|
~"""
|
|
import tarfile
|
|
import xbmcaddon
|
|
import zipfile
|
|
|
|
module_id = 'script.module.python.koding.aio'
|
|
this_module = xbmcaddon.Addon(id=module_id)
|
|
nFiles = 0
|
|
count = 0
|
|
|
|
if xbmcvfs.exists(_in):
|
|
if zipfile.is_zipfile(_in):
|
|
zin = zipfile.ZipFile(_in, 'r')
|
|
nFiles = float(len(zin.infolist()))
|
|
contents = zin.infolist()
|
|
|
|
elif tarfile.is_tarfile(_in):
|
|
zin = tarfile.open(_in)
|
|
contents = [tarinfo for tarinfo in zin.getmembers()]
|
|
nFiles = float(len(contents))
|
|
|
|
if nFiles > 0:
|
|
if dp:
|
|
try:
|
|
for item in contents:
|
|
count += 1
|
|
update = count / nFiles * 100
|
|
dp.update(int(update))
|
|
zin.extract(item, _out)
|
|
zin.close()
|
|
return True
|
|
|
|
except:
|
|
xbmc.log(Last_Error(),2)
|
|
return False
|
|
else:
|
|
try:
|
|
zin.extractall(_out)
|
|
return True
|
|
except:
|
|
xbmc.log(Last_Error(),2)
|
|
return False
|
|
|
|
else:
|
|
xbmc.log('NOT A VALID ZIP OR TAR FILE: %s' % _in,2)
|
|
else:
|
|
if show_error:
|
|
dialog.ok(this_module.getLocalizedString(30965),this_module.getLocalizedString(30815) % _in)
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Free_Space(dirname = HOME, filesize = 'b'):
|
|
"""
|
|
Show the amount of available free space in a path, this can be returned in a number of different formats.
|
|
|
|
CODE: Free_Space([dirname, filesize])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
dirname - This optional, by default it will tell you how much space is available in your special://home
|
|
folder. If you require information for another path (such as a different partition or storage device)
|
|
then enter the physical path. This currently only works for local paths and not networked drives.
|
|
|
|
filesize - By default you'll get a return of total bytes, however you can get the value as bytes,
|
|
kilobytes, megabytes, gigabytes and terabytes..
|
|
|
|
VALUES:
|
|
'b' = bytes (integer)
|
|
'kb' = kilobytes (float to 1 decimal place)
|
|
'mb' = kilobytes (float to 2 decimal places)
|
|
'gb' = kilobytes (float to 3 decimal places)
|
|
'tb' = terabytes (float to 4 decimal places)
|
|
|
|
EXAMPLE CODE:
|
|
HOME = Physical_Path('special://home')
|
|
my_space = koding.Free_Space(HOME, 'gb')
|
|
dialog.ok('Free Space','Available space in HOME: %s GB' % my_space)
|
|
~"""
|
|
import ctypes
|
|
dirname = Physical_Path(dirname)
|
|
filesize = filesize.lower()
|
|
if xbmc.getCondVisibility('system.platform.windows'):
|
|
free_bytes = ctypes.c_ulonglong(0)
|
|
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(dirname), None, None, ctypes.pointer(free_bytes))
|
|
finalsize = free_bytes.value
|
|
else:
|
|
st = os.statvfs(dirname)
|
|
finalsize = st.f_bavail * st.f_frsize
|
|
if filesize == 'b':
|
|
return finalsize
|
|
elif filesize == 'kb':
|
|
return "%.1f" % (float(finalsize / 1024))
|
|
elif filesize == 'mb':
|
|
return "%.2f" % (float(finalsize / 1024) / 1024)
|
|
elif filesize == 'gb':
|
|
return "%.3f" % (float(finalsize / 1024) / 1024 / 1024)
|
|
elif filesize == 'tb':
|
|
return "%.4f" % (float(finalsize / 1024) / 1024 / 1024 / 1024)
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Folder_Size(dirname = HOME, filesize = 'b'):
|
|
"""
|
|
Return the size of a folder path including sub-directories,
|
|
this can be returned in a number of different formats.
|
|
|
|
CODE: koding.Folder_Size([dirname, filesize])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
dirname - This optional, by default it will tell you how much space is available in your
|
|
special://home folder. If you require information for another path (such as a different
|
|
partition or storage device) then enter the physical path. This currently only works for
|
|
local paths and not networked drives.
|
|
|
|
filesize - By default you'll get a return of total bytes, however you can get the value as
|
|
bytes, kilobytes, megabytes, gigabytes and terabytes..
|
|
|
|
VALUES:
|
|
'b' = bytes (integer)
|
|
'kb' = kilobytes (float to 1 decimal place)
|
|
'mb' = kilobytes (float to 2 decimal places)
|
|
'gb' = kilobytes (float to 3 decimal places)
|
|
'tb' = terabytes (float to 4 decimal places)
|
|
|
|
EXAMPLE CODE:
|
|
HOME = Physical_Path('special://home')
|
|
home_size = Folder_Size(HOME, 'mb')
|
|
dialog.ok('Folder Size','KODI HOME: %s MB' % home_size)
|
|
~"""
|
|
finalsize = 0
|
|
for dirpath, dirnames, filenames in os.walk(dirname):
|
|
for f in filenames:
|
|
fp = os.path.join(dirpath, f)
|
|
finalsize += os.path.getsize(fp)
|
|
if filesize == 'b':
|
|
return finalsize
|
|
elif filesize == 'kb':
|
|
return "%.1f" % (float(finalsize / 1024))
|
|
elif filesize == 'mb':
|
|
return "%.2f" % (float(finalsize / 1024) / 1024)
|
|
elif filesize == 'gb':
|
|
return "%.3f" % (float(finalsize / 1024) / 1024 / 1024)
|
|
elif filesize == 'tb':
|
|
return "%.4f" % (float(finalsize / 1024) / 1024 / 1024 / 1024)
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Fresh_Install(keep_addons=[],ignore=[],keepdb=True):
|
|
"""
|
|
Attempt to completely wipe your install. You can send through a list
|
|
of addons or paths you want to ignore (leave in the setup) or you can
|
|
leave blank. If left blank and the platform is OpenELEC or LibreELEC
|
|
it will perform a hard reset command followed by a reboot.
|
|
|
|
CODE: Fresh_Install([keep_addons, ignore, keepdb)
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
keep_addons - This is optional, if you have specific add-ons you want to omit
|
|
from the wipe (leave intact) then just enter a list of add-on id's here. The code
|
|
will determine from the addon.xml file which dependencies and sub-dependencies are
|
|
required for that add-on so there's no need to create a huge list, you only need to
|
|
list the master add-on id's. For example if you want to keep the current skin and
|
|
your add-on you would use: keep_addons=['plugin.program.myaddon',System('currentskin')]
|
|
and all addons/dependencies associated with those two add-ons will be added to the ignore
|
|
list.
|
|
|
|
ignore - This is optional, you can send through a list of paths you want to omit from
|
|
the wipe. You can use folder paths to skip the whole folder or you can use individual
|
|
file paths. Please make sure you use the physical path and not special://
|
|
So before creating your list make sure you use xbmc.translatePath()
|
|
|
|
keepdb - By default this is set to True which means the code will keep all the Kodi databases
|
|
intact and perform a profile reload once wipe is complete. This will mean addons, video, music,
|
|
epg, ADSP and viewtypes databases will remain completely untouched and Kodi should be fine to use
|
|
without the need for a restart. If you set keepdb to False nothing will happen once the wipe has
|
|
completed and it's up to you to choose what to do in your main code. I would highly recommend an
|
|
ok dialog followed by xbmc.executebuiltin('Quit'). This will force Kodi to recreate all the relevant
|
|
databases when they re-open. If you try and continue using Kodi without restarting the databases
|
|
will not be recreated and you risk corruption.
|
|
|
|
EXAMPLE CODE:
|
|
if dialog.yesno('[COLOR gold]TOTAL WIPEOUT![/COLOR]','This will attempt give you a totally fresh install of Kodi.','Are you sure you want to continue?'):
|
|
if dialog.yesno('[COLOR gold]FINAL CHANCE!!![/COLOR]','If you click Yes this WILL attempt to wipe your install', '[COLOR=dodgerblue]ARE YOU 100% CERTAIN YOU WANT TO WIPE?[/COLOR]'):
|
|
clean_state = koding.Fresh_Install()
|
|
~"""
|
|
# If it's LE/OE and there are no files to ignore we do a hard reset
|
|
from systemtools import Cleanup_Textures
|
|
if ( len(ignore)==0 ) and ( len(keep_addons)==0 ) and ( xbmc.getCondVisibility("System.HasAddon(service.libreelec.settings)") or xbmc.getCondVisibility("System.HasAddon(service.openelec.settings)") ):
|
|
xbmc.log('OE DETECTED',2)
|
|
resetpath='storage/.cache/reset_oe'
|
|
Text_File(resetpath,'w')
|
|
xbmc.executebuiltin('reboot')
|
|
else:
|
|
from addons import Dependency_Check
|
|
xbmc.log('DOING MAIN WIPE',2)
|
|
skip_array = []
|
|
addonsdb = DB_Path_Check('addons')
|
|
textures = DB_Path_Check('Textures')
|
|
Cleanup_Textures(frequency=1,use_count=999999)
|
|
if len(keep_addons) > 0:
|
|
ignorelist = Dependency_Check(addon_id = keep_addons, recursive = True)
|
|
for item in ignorelist:
|
|
skip_array.append(xbmcaddon.Addon(id=item).getAddonInfo('path'))
|
|
skip_array.append(addonsdb)
|
|
skip_array.append(textures)
|
|
if keepdb:
|
|
try:
|
|
skip_array.append( DB_Path_Check('Epg') )
|
|
except:
|
|
xbmc.log('No EPG DB Found, skipping',2)
|
|
try:
|
|
skip_array.append( DB_Path_Check('MyVideos') )
|
|
except:
|
|
xbmc.log('No MyVideos DB Found, skipping',2)
|
|
try:
|
|
skip_array.append( DB_Path_Check('MyMusic') )
|
|
except:
|
|
xbmc.log('No MyMusic DB Found, skipping',2)
|
|
try:
|
|
skip_array.append( DB_Path_Check('TV') )
|
|
except:
|
|
xbmc.log('No TV DB Found, skipping',2)
|
|
try:
|
|
skip_array.append( DB_Path_Check('ViewModes') )
|
|
except:
|
|
xbmc.log('No ViewModes DB Found, skipping',2)
|
|
try:
|
|
skip_array.append( DB_Path_Check('ADSP') )
|
|
except:
|
|
xbmc.log('No ADSP DB Found, skipping',2)
|
|
for item in ignore:
|
|
skip_array.append(item)
|
|
Delete_Folders(filepath=HOME, ignore=skip_array)
|
|
Refresh()
|
|
if keepdb:
|
|
Refresh('profile')
|
|
|
|
# Good option for wiping android data but not so good if using the app as a launcher!
|
|
# elif xbmc.getCondVisibility('System.Platform.Android'):
|
|
# import subprocess
|
|
# running = Running_App()
|
|
# cleanwipe = subprocess.Popen(['exec ''pm clear '+str(running)+''], executable='/system/bin/sh', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=Get_ID(setid=True)).communicate()[0]
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Get_Contents(path,folders=True,subfolders=False,exclude_list=[],full_path=True,filter=''):
|
|
"""
|
|
Return a list of either files or folders in a given path.
|
|
|
|
CODE: Get_Contents(path, [folders, subfolders, exclude_list, full_path, filter])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) path - This is the path you want to search, no sub-directories are scanned.
|
|
|
|
folders - By default this is set to True and the returned list will only
|
|
show folders. If set to False the returned list will show files only.
|
|
|
|
exclude_list - Optionally you can add a list of items you don't want returned
|
|
|
|
full_path - By default the entries in the returned list will contain the full
|
|
path to the folder/file. If you only want the file/folder name set this to False.
|
|
|
|
subfolders - By default this is set to False but if set to true it will check
|
|
all sub-directories and not just the directory sent through.
|
|
|
|
filter - If you want to only return files ending in a specific string you
|
|
can add details here. For example to only show '.xml' files you would send
|
|
through filter='.xml'.
|
|
|
|
EXAMPLE CODE:
|
|
ADDONS = Physical_Path('special://home/addons')
|
|
addon_folders = koding.Get_Contents(path=ADDONS, folders=True, exclude_list=['packages','temp'], full_path=False)
|
|
results = ''
|
|
for item in addon_folders:
|
|
results += 'FOLDER: [COLOR=dodgerblue]%s[/COLOR]\n'%item
|
|
koding.Text_Box('ADDON FOLDERS','Below is a list of folders found in the addons folder (excluding packages and temp):\n\n%s'%results)
|
|
~"""
|
|
final_list = []
|
|
path = Physical_Path(path)
|
|
# Check all items in the given path
|
|
if not subfolders:
|
|
dirs,files = xbmcvfs.listdir(path)
|
|
if folders:
|
|
active_list = dirs
|
|
else:
|
|
active_list = files
|
|
for item in active_list:
|
|
if item not in exclude_list:
|
|
if full_path:
|
|
final_list.append(os.path.join(path,item))
|
|
else:
|
|
final_list.append(item)
|
|
|
|
# Traverse through all subfolders
|
|
else:
|
|
path = Physical_Path(path)
|
|
for root, dirnames, filenames in os.walk(path):
|
|
if not folders:
|
|
for filename in filenames:
|
|
file_path = os.path.join(root, filename)
|
|
if filter=='':
|
|
if full_path:
|
|
final_list.append(file_path)
|
|
else:
|
|
final_list.append(filename)
|
|
|
|
elif file_path.endswith(filter):
|
|
if full_path:
|
|
final_list.append(file_path)
|
|
else:
|
|
final_list.append(filename)
|
|
else:
|
|
for dirname in dirnames:
|
|
if full_path:
|
|
final_list.append(os.path.join(root, dirname))
|
|
else:
|
|
final_list.append(dirname)
|
|
return final_list
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Move_Tree(src, dst, dp=None):
|
|
"""
|
|
Move a directory including all sub-directories to a new location.
|
|
This will automatically create the new location if it doesn't already
|
|
exist and it wierwrite any existing entries if they exist.
|
|
|
|
CODE: koding.Move_Tree(src, dst)
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) src - This is source directory that you want to copy
|
|
|
|
(*) dst - This is the destination location you want to copy a directory to.
|
|
|
|
dp - This is optional, if you pass through the dp function as a DialogProgress()
|
|
then you'll get to see the status of the move process. See the example below for a dp example.
|
|
|
|
EXAMPLE CODE:
|
|
dp = xbmcgui.DialogProgress()
|
|
source = koding.Physical_Path('special://profile/move_test')
|
|
|
|
# Lets create a 500MB dummy file so we can move and see dialog progress
|
|
dummy = os.path.join(source,'dummy')
|
|
if not os.path.exists(source):
|
|
os.makedirs(source)
|
|
koding.Dummy_File(dst=dummy+'1.txt', size=10, size_format='mb')
|
|
koding.Dummy_File(dst=dummy+'2.txt', size=10, size_format='mb')
|
|
koding.Dummy_File(dst=dummy+'3.txt', size=10, size_format='mb')
|
|
koding.Dummy_File(dst=dummy+'4.txt', size=10, size_format='mb')
|
|
koding.Dummy_File(dst=dummy+'5.txt', size=10, size_format='mb')
|
|
koding.Dummy_File(dst=dummy+'6.txt', size=10, size_format='mb')
|
|
dialog.ok('DUMMY FILE CREATED','If you want to check in your userdata folder you should have a new folder called "move_test" which has 6x 10MB dummy files.')
|
|
|
|
# This is optional but if you want to see a dialog progress then you'll need this
|
|
dp.create('MOVING FILES','Please Wait')
|
|
|
|
destination = koding.Physical_Path('special://home/My MOVED Dummy File')
|
|
koding.Move_Tree(source, destination, dp)
|
|
dialog.ok('CHECK YOUR KODI HOME FOLDER','Please check your Kodi home folder, the dummy file should now have moved in there. When you press OK it will be removed')
|
|
shutil.rmtree(destination)
|
|
~"""
|
|
src = Physical_Path(src)
|
|
dst = Physical_Path(dst)
|
|
if dp:
|
|
totalfiles = 0
|
|
for root, dirs, files in os.walk(src):
|
|
totalfiles += len(files)
|
|
count = 0
|
|
|
|
for src_dir, dirs, files in os.walk(src):
|
|
dst_dir = src_dir.replace(src, dst, 1)
|
|
if os.path.exists(dst_dir) and not os.path.isdir(dst_dir):
|
|
try:
|
|
os.remove(dst_dir)
|
|
except:
|
|
xbmc.log('File with same name as folder exists, need to manually delete:',2)
|
|
xbmc.log(dst_dir,2)
|
|
if not os.path.exists(dst_dir):
|
|
os.makedirs(dst_dir)
|
|
for file_ in files:
|
|
src_file = os.path.join(src_dir, file_)
|
|
dst_file = os.path.join(dst_dir, file_)
|
|
if os.path.exists(dst_file) and dst_file != dst:
|
|
os.remove(dst_file)
|
|
try:
|
|
os.rename(src_file,dst_file)
|
|
except:
|
|
shutil.move(src_file, dst_dir)
|
|
if dp:
|
|
try:
|
|
count += 1
|
|
update = count / totalfiles * 100
|
|
dp.update(int(update))
|
|
except:
|
|
pass
|
|
try:
|
|
shutil.rmtree(src)
|
|
except:
|
|
pass
|
|
|
|
if dp:
|
|
dp.close()
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Physical_Path(path='special://home'):
|
|
"""
|
|
Send through a special:// path and get the real physical path returned.
|
|
This has been written due to the problem where if you're running the Windows Store
|
|
version of Kodi the built-in xbmc.translatePath() function is returning bogus results
|
|
making it impossible to access databases.
|
|
|
|
CODE: koding.Physical_Path([path])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
path - This is the path to the folder/file you want returned. This is optional,
|
|
if you leave this out it will just return the path to the root directory (special://home)
|
|
|
|
EXAMPLE CODE:
|
|
location = 'special://home/addons/script.module.python.koding.aio'
|
|
real_location = koding.Physical_Path(location)
|
|
xbmc.log(real_location,2)
|
|
dialog.ok('PHYSICAL PATH','The real path of special://home/addons/script.module.python.koding.aio is:','[COLOR=dodgerblue]%s[/COLOR]'%real_location)
|
|
~"""
|
|
xbmc_install = xbmc.translatePath('special://xbmc')
|
|
if not "WindowsApps" in xbmc_install:
|
|
clean = xbmc.translatePath(path)
|
|
if sys.platform == 'win32':
|
|
clean = clean.replace('\/','\\')
|
|
else:
|
|
clean = xbmc.translatePath(path)
|
|
if clean.startswith(xbmc_install):
|
|
if sys.platform == 'win32':
|
|
clean = clean.replace('\/','\\')
|
|
else:
|
|
return clean.replace('AppData\\Roaming\\','AppData\\Local\\Packages\\XBMCFoundation.Kodi_4n2hpmxwrvr6p\\LocalCache\\Roaming\\')
|
|
if sys.platform == 'win32':
|
|
clean = clean.replace('\/','\\')
|
|
return clean
|
|
#----------------------------------------------------------------
|
|
# TUTORIAL #
|
|
def Text_File(path, mode, text = ''):
|
|
"""
|
|
Open/create a text file and read/write to it.
|
|
|
|
CODE: koding.Text_File(path, mode, [text])
|
|
|
|
AVAILABLE PARAMS:
|
|
|
|
(*) path - This is the path to the text file
|
|
|
|
(*) mode - This can be 'r' (for reading) or 'w' (for writing)
|
|
|
|
text - This is only required if you're writing to a file, this
|
|
is the text you want to enter. This will completely overwrite any
|
|
text already in the file.
|
|
|
|
EXAMPLE CODE:
|
|
HOME = koding.Physical_Path('special://home')
|
|
koding_test = os.path.join(HOME, 'koding_test.txt')
|
|
koding.Text_File(path=koding_test, mode='w', text='Well done, you\'ve created a text file containing this text!')
|
|
dialog.ok('CREATE TEXT FILE','If you check your home Kodi folder and you should now have a new koding_test.txt file in there.','[COLOR=gold]DO NOT DELETE IT YET![/COLOR]')
|
|
mytext = koding.Text_File(path=koding_test, mode='r')
|
|
dialog.ok('TEXT FILE CONTENTS','The text in the file created is:','[COLOR=dodgerblue]%s[/COLOR]'%mytext,'[COLOR=gold]CLICK OK TO DELETE THE FILE[/COLOR]')
|
|
try:
|
|
os.remove(koding_test)
|
|
except:
|
|
dialog.ok('FAILED TO REMOVE','Could not remove the file, looks like you might have it open in a text editor. Please manually remove yourself')
|
|
~"""
|
|
try:
|
|
textfile = xbmcvfs.File(path, mode)
|
|
|
|
if mode == 'r':
|
|
content = textfile.read()
|
|
textfile.close()
|
|
return content
|
|
|
|
if mode == 'w':
|
|
textfile.write(text)
|
|
textfile.close()
|
|
return True
|
|
|
|
except:
|
|
xbmc.log(Last_Error(),2)
|
|
return False
|
|
#---------------------------------------------------------------- |