September 2018 Update
Added - Koding AIO to repo as addons depend on it Updated - DefCon now has 2018 talks in
This commit is contained in:
977
script.module.python.koding.aio/lib/koding/addons.py
Normal file
977
script.module.python.koding.aio/lib/koding/addons.py
Normal file
@@ -0,0 +1,977 @@
|
||||
# -*- 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 datetime
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
|
||||
import filetools
|
||||
|
||||
ADDONS = 'special://home/addons'
|
||||
XBMC_PATH = xbmc.translatePath('special://xbmc')
|
||||
kodi_ver = int(float(xbmc.getInfoLabel("System.BuildVersion")[:2]))
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Addon_Genre(genre='adult',custom_url=''):
|
||||
"""
|
||||
Return a dictionary of add-ons which match a specific genre.
|
||||
|
||||
CODE: Addon_Genre([genre, custom_url])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
genre - By default this is set to 'adult' which will return a dictionary of all known
|
||||
adult add-ons. We recommend using the genre labels listed below as they are already in use
|
||||
by some add-on developers, however you can of course create your very own genre keys in
|
||||
your custom genre file if you wish.
|
||||
|
||||
custom_url - If you have your own custom url which returns a dictionary
|
||||
of genres and add-ons you can enter it here. The page must adhere to the format shown below.
|
||||
|
||||
Recommended Genre Keys:
|
||||
adult, anime, audiobooks, comedy, comics, documentary, food, gaming, health, howto, kids,
|
||||
livetv, movies, music, news, podcasts, radio, religion, space, sports, subscription,
|
||||
tech, trailers, tvshows, world
|
||||
|
||||
Correct Genre Dictionary Structure:
|
||||
The dictionary must be a dictionary of genres with each genre dictionary containing keys for
|
||||
each add-on ID and the value being the name you want displayed. See below for an example:
|
||||
{ "movies":{"plugin.video.mymovie":"My movie add-on","plugin.video.trailers":"My Trailer add-on"}, "sports":{"plugin.video.sports":"Sport Clips"} }
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('ADD-ON GENRES','We will now list all known comedy based add-ons. If you have add-ons installed which you feel should be categorised as supplying comedy but they aren\'t then you can help tag them up correctly via the Add-on Portal at NaN.')
|
||||
comedy_addons = koding.Addon_Genre(genre='comedy')
|
||||
if comedy_addons:
|
||||
my_return = 'LIST OF AVAILABLE COMEDY BASED ADD-ONS:\n\n'
|
||||
|
||||
# Convert the dictionary into a list:
|
||||
comedy_addons = comedy_addons.items()
|
||||
for item in comedy_addons:
|
||||
my_return += '[COLOR=gold]Name:[/COLOR] %s | [COLOR=dodgerblue]ID:[/COLOR] %s\n' % (item[0],item[1])
|
||||
koding.Text_Box('[COLOR gold]COMEDY ADD-ONS[/COLOR]',my_return)
|
||||
~"""
|
||||
import binascii
|
||||
from __init__ import converthex
|
||||
from filetools import Text_File
|
||||
from systemtools import Timestamp
|
||||
from vartools import Merge_Dicts
|
||||
from web import Open_URL
|
||||
|
||||
download_new = True
|
||||
local_path = binascii.hexlify('genre_list')
|
||||
cookie_path = "special://profile/addon_data/script.module.python.koding.aio/cookies/"
|
||||
custom_genres= "special://profile/addon_data/script.module.python.koding.aio/genres.txt"
|
||||
final_path = os.path.join(cookie_path,local_path)
|
||||
if not xbmcvfs.exists(cookie_path):
|
||||
xbmcvfs.mkdirs(cookie_path)
|
||||
|
||||
if xbmcvfs.exists(final_path):
|
||||
modified = xbmcvfs.Stat(final_path).st_mtime()
|
||||
old = int(modified)
|
||||
now = int(Timestamp('epoch'))
|
||||
# Add a 24hr wait so we don't kill server
|
||||
if now < (modified+86400):
|
||||
download_new = False
|
||||
|
||||
# Create new file
|
||||
if download_new and custom_url != '':
|
||||
addon_list = Open_URL(custom_url)
|
||||
Text_File(final_path, "w", addon_list)
|
||||
|
||||
# Grab details of the relevant genre
|
||||
if xbmcvfs.exists(final_path):
|
||||
try:
|
||||
addon_list = eval( Text_File(final_path, 'r') )
|
||||
addon_list = addon_list[genre]
|
||||
except:
|
||||
xbmcvfs.delete(final_path)
|
||||
addon_list = {}
|
||||
|
||||
if xbmcvfs.exists(custom_genres):
|
||||
try:
|
||||
custom_list = eval( Text_File(custom_genres, 'r') )
|
||||
custom_list = custom_list[genre]
|
||||
addon_list = Merge_Dicts(addon_list,custom_list)
|
||||
except:
|
||||
pass
|
||||
return addon_list
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Addon_Info(id='',addon_id=''):
|
||||
"""
|
||||
Retrieve details about an add-on, lots of built-in values are available
|
||||
such as path, version, name etc.
|
||||
|
||||
CODE: Addon_Setting(id, [addon_id])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) id - This is the name of the id you want to retrieve.
|
||||
The list of built in id's you can use (current as of 15th April 2017)
|
||||
are: author, changelog, description, disclaimer, fanart, icon, id, name,
|
||||
path, profile, stars, summary, type, version
|
||||
|
||||
addon_id - By default this will use your current add-on id but you
|
||||
can access any add-on you want by entering an id in here.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('ADD-ON INFO','We will now try and pull name and version details for our current running add-on.')
|
||||
version = koding.Addon_Info(id='version')
|
||||
name = koding.Addon_Info(id='name')
|
||||
dialog.ok('NAME AND VERSION','[COLOR=dodgerblue]Add-on Name:[/COLOR] %s' % name,'[COLOR=dodgerblue]Version:[/COLOR] %s' % version)
|
||||
~"""
|
||||
import xbmcaddon
|
||||
if addon_id == '':
|
||||
addon_id = Caller()
|
||||
ADDON = xbmcaddon.Addon(id=addon_id)
|
||||
if id == '':
|
||||
dialog.ok('ENTER A VALID ID','You\'ve called the Addon_Info function but forgot to add an ID. Please correct your code and enter a valid id to pull info on (e.g. "version")')
|
||||
else:
|
||||
return ADDON.getAddonInfo(id=id)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Addon_List(enabled=True, inc_new=False):
|
||||
"""
|
||||
Return a list of enabled or disabled add-ons found in the database.
|
||||
|
||||
CODE: Addon_List([enabled, inc_new])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
enabled - By default this is set to True which means you'll
|
||||
get a list of all the enabled add-ons found in addons*.db but
|
||||
if you want a list of all the disabled ones just set this to
|
||||
False.
|
||||
|
||||
inc_new - This will also add any new add-on folders found on
|
||||
your system that aren't yet in the database (ie ones that have
|
||||
been recently been manually extracted but not scanned in). By
|
||||
default this is set to False.
|
||||
|
||||
EXAMPLE CODE:
|
||||
enabled_list = Addon_List(enabled=True)
|
||||
disabled_list = Addon_List(enabled=False)
|
||||
my_return = ''
|
||||
|
||||
for item in enabled_list:
|
||||
my_return += '[COLOR=lime]ENABLED:[/COLOR] %s\n' % item
|
||||
for item in disabled_list:
|
||||
my_return += '[COLOR=red]DISABLED:[/COLOR] %s\n' % item
|
||||
koding.Text_Box('ADDON STATUS',my_return)
|
||||
~"""
|
||||
from database import DB_Query
|
||||
from guitools import Text_Box
|
||||
from filetools import DB_Path_Check, Get_Contents
|
||||
|
||||
enabled_list = []
|
||||
disabled_list = []
|
||||
addons_db = DB_Path_Check('addons')
|
||||
on_system = DB_Query(addons_db,'SELECT addonID, enabled from installed')
|
||||
|
||||
# Create a list of enabled and disabled add-ons already on system
|
||||
for item in on_system:
|
||||
if item["enabled"]:
|
||||
enabled_list.append(item["addonID"])
|
||||
else:
|
||||
disabled_list.append(item["addonID"])
|
||||
|
||||
if inc_new:
|
||||
my_addons = Get_Contents(path=ADDONS, exclude_list=['packages','temp'])
|
||||
for item in my_addons:
|
||||
addon_id = Get_Addon_ID(item)
|
||||
if not addon_id in enabled_list and not addon_id in disabled_list:
|
||||
disabled_list.append(addon_id)
|
||||
|
||||
if enabled:
|
||||
return enabled_list
|
||||
else:
|
||||
return disabled_list
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Addon_Service(addons='all', mode='list', skip_service=[]):
|
||||
"""
|
||||
Send through an add-on id, list of id's or leave as the default which is "all". This
|
||||
will loop through the list of add-ons and return the ones which are run as services.
|
||||
|
||||
This enable/disable feature will comment out the service lines, and does not stop a running
|
||||
service or start a service. This is designed more for if you've manually extracted a new
|
||||
add-on into your system and it isn't yet enabled. Occasionally if the add-ons have dependencies
|
||||
which are run as services then trying to enable them can cause Kodi to freeze.
|
||||
|
||||
CODE: Addon_Service([addon,disable])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
addons - By default this is set to "all" but if there's a sepcific set of add-ons you
|
||||
want to disable the service for just send through the id's in the form of a list.
|
||||
|
||||
mode - By default this is set to 'list' meaning you'll get a return of add-on folders
|
||||
which contain an instance of service in the add-on.xml. You can set this to "disable" to
|
||||
comment out the instances of service and similarly when you need to re-enable you can use
|
||||
"enable" and that will uncomment out the service item. Please note that by uncommenting
|
||||
the service will not automatically start - you'll need to reload the profile for that.
|
||||
|
||||
skip_service - When running the enable or disable mode you can choose to add a list of
|
||||
add-ons you'd like to skip the process for. Of course you may be thinking why would I send
|
||||
through a list of addons I want the service enabled/disabled for but then I also add them
|
||||
to the skip_service list to say DON'T enable/disable - it makes no sense?! Well you'd be
|
||||
correct that doesn't make any sense as presumably you've already filtered out the add-ons
|
||||
you don't want affected, this command is designed more for those who don't send through a
|
||||
list of add-ons and instead use the default "all" value for the addons paramater. This
|
||||
then makes it very easy to just skip a handful of add-on services and enable all others.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('CHECKING FOR SERVICES','We will now check for all add-ons installed which contain services')
|
||||
service_addons = Addon_Service(mode='list')
|
||||
my_text = 'List of add-ons running as a service:\n\n'
|
||||
for item in service_addons:
|
||||
my_text += item+'\n'
|
||||
koding.Text_Box('[COLOR gold]SERVICE ADDONS[/COLOR]',my_text)
|
||||
~"""
|
||||
from filetools import Get_Contents, Physical_Path, Text_File
|
||||
from vartools import Data_Type
|
||||
from guitools import Text_Box
|
||||
service_addons = []
|
||||
if addons=='all':
|
||||
addons = Get_Contents(path=ADDONS, exclude_list=['packages','temp'],full_path=False)
|
||||
else:
|
||||
if Data_Type(addons) == 'str':
|
||||
addons = [addons]
|
||||
|
||||
if Data_Type(skip_service) == 'str':
|
||||
skip_service = [skip_service]
|
||||
|
||||
service_line = '<extension point="xbmc.service"'
|
||||
|
||||
for item in addons:
|
||||
addon_path = os.path.join(ADDONS,item,'addon.xml')
|
||||
content = Text_File(addon_path,'r')
|
||||
if service_line in content:
|
||||
if item not in service_addons:
|
||||
service_addons.append(item)
|
||||
if mode != 'list':
|
||||
if not item in skip_service:
|
||||
for line in content.splitlines():
|
||||
if service_line in line:
|
||||
if not (line.strip().startswith('<!--')) and (mode == 'disable'):
|
||||
replace_line = '<!--%s-->'%line
|
||||
Text_File(addon_path,'w',content.replace(line,replace_line))
|
||||
break
|
||||
elif line.strip().startswith('<!--') and mode == 'enable':
|
||||
replace_line = line.replace(r'<!--','').replace(r'-->','')
|
||||
Text_File(addon_path,'w',content.replace(line,replace_line))
|
||||
break
|
||||
return service_addons
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Addon_Setting(setting='',value='return_default',addon_id=''):
|
||||
"""
|
||||
Change or retrieve an add-on setting.
|
||||
|
||||
CODE: Addon_Setting(setting, [value, addon_id])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) setting - This is the name of the setting you want to access, by
|
||||
default this function will return the value but if you add the
|
||||
value param shown below it will CHANGE the setting.
|
||||
|
||||
value - If set this will change the setting above to whatever value
|
||||
is in here.
|
||||
|
||||
addon_id - By default this will use your current add-on id but you
|
||||
can access any add-on you want by entering an id in here.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('ADDON SETTING','We will now try and pull the language settings for the YouTube add-on')
|
||||
if os.path.exists(xbmc.translatePath('special://home/addons/plugin.video.youtube')):
|
||||
my_setting = koding.Addon_Setting(setting='youtube.language',addon_id='plugin.video.youtube')
|
||||
dialog.ok('YOUTUBE SETTING','[COLOR=dodgerblue]Setting name:[/COLOR] youtube.language','[COLOR=dodgerblue]Value:[/COLOR] %s' % my_setting)
|
||||
else:
|
||||
dialog.ok('YOUTUBE NOT INSTALLED','Sorry we cannot run this example as you don\'t have YouTube installed.')
|
||||
~"""
|
||||
import xbmcaddon
|
||||
if addon_id == '':
|
||||
addon_id = Caller()
|
||||
ADDON = xbmcaddon.Addon(id=addon_id)
|
||||
if value == 'return_default':
|
||||
mysetting = ADDON.getSetting(setting)
|
||||
return mysetting
|
||||
else:
|
||||
ADDON.setSetting(id=setting, value=value)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Adult_Toggle(adult_list=[], disable=True, update_status=0):
|
||||
"""
|
||||
Remove/Enable a list of add-ons, these are put into a containment area until enabled again.
|
||||
|
||||
CODE: Adult_Toggle(adult_list, [disable, update_status])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) adult_list - A list containing all the add-ons you want to be disabled.
|
||||
|
||||
disable - By default this is set to true so any add-ons in the list sent
|
||||
through will be disabled. Set to False if you want to enable the hidden add-ons.
|
||||
|
||||
update_status - When running this function it needs to disable the
|
||||
auto-update of add-ons by Kodi otherwise it risks crashing. This
|
||||
update_status paramater is the state you want Kodi to revert back to
|
||||
once the toggle of add-ons has completed. By default this is set to 0
|
||||
which is auto-update. You can also choose 1 (notify of updates) or 2
|
||||
(disable auto updates).
|
||||
|
||||
~"""
|
||||
from filetools import End_Path, Move_Tree, Physical_Path
|
||||
|
||||
adult_store = Physical_Path("special://profile/addon_data/script.module.python.koding.aio/adult_store")
|
||||
disable_list = []
|
||||
if not xbmcvfs.exists(adult_store):
|
||||
xbmcvfs.mkdirs(adult_store)
|
||||
my_addons = Installed_Addons()
|
||||
if disable:
|
||||
for item in my_addons:
|
||||
if item != None:
|
||||
item = item["addonid"]
|
||||
if item in adult_list:
|
||||
disable_list.append(item)
|
||||
|
||||
Toggle_Addons(addon=disable_list, enable=False, safe_mode=True, refresh=True, update_status=update_status)
|
||||
for item in disable_list:
|
||||
try:
|
||||
addon_path = xbmcaddon.Addon(id=item).getAddonInfo("path")
|
||||
except:
|
||||
addon_path = Physical_Path(os.path.join(ADDONS,item))
|
||||
path_id = End_Path(addon_path)
|
||||
if os.path.exists(addon_path):
|
||||
Move_Tree(addon_path,os.path.join(adult_store,path_id))
|
||||
else:
|
||||
KODI_VER = int(float(xbmc.getInfoLabel("System.BuildVersion")[:2]))
|
||||
addon_vault = []
|
||||
if os.path.exists(adult_store):
|
||||
for item in os.listdir(adult_store):
|
||||
store_dir = os.path.join(adult_store,item)
|
||||
addon_dir = os.path.join(ADDONS, item)
|
||||
if os.path.exists(store_dir):
|
||||
Move_Tree(store_dir,addon_dir)
|
||||
addon_vault.append(item)
|
||||
|
||||
if KODI_VER >= 16:
|
||||
Toggle_Addons(addon=addon_vault, safe_mode=True, refresh=True, update_status=update_status)
|
||||
else:
|
||||
Refresh(['addons','repos'])
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Caller(my_return='addon'):
|
||||
"""
|
||||
Return the add-on id or path of the script which originally called
|
||||
your function. If it's been called through a number of add-ons/scripts
|
||||
you can grab a list of paths that have been called.
|
||||
|
||||
CODE: Caller(my_return)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
my_return - By default this is set to 'addon', view the options below:
|
||||
|
||||
'addon' : Return the add-on id of the add-on to call this function.
|
||||
|
||||
'addons': Return a list of all add-on id's called to get to this function.
|
||||
|
||||
'path' : Return the full path to the script which called this function.
|
||||
|
||||
'paths' : Return a list of paths which have been called to get to this
|
||||
final function.
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_addon = koding.Caller(my_return='addon')
|
||||
my_addons = koding.Caller(my_return='addons')
|
||||
my_path = koding.Caller(my_return='path')
|
||||
my_paths = koding.Caller(my_return='paths')
|
||||
|
||||
dialog.ok('ADD-ON ID', 'Addon id you called this function from:','[COLOR=dodgerblue]%s[/COLOR]' % my_addon)
|
||||
dialog.ok('SCRIPT PATH', 'Script which called this function:','[COLOR=dodgerblue]%s[/COLOR]' % my_path)
|
||||
|
||||
addon_list = 'Below is a list of add-on id\'s which have been called to get to this final piece of code:\n\n'
|
||||
for item in my_addons:
|
||||
addon_list += item+'\n'
|
||||
koding.Text_Box('ADD-ON LIST', addon_list)
|
||||
koding.Sleep_If_Window_Active(10147)
|
||||
path_list = 'Below is a list of scripts which have been called to get to this final piece of code:\n\n'
|
||||
for item in my_paths:
|
||||
path_list += item+'\n'
|
||||
koding.Text_Box('ADD-ON LIST', path_list)
|
||||
~"""
|
||||
import inspect
|
||||
stack = inspect.stack()
|
||||
last_stack = len(stack)-1
|
||||
stack_array = []
|
||||
addon_array = []
|
||||
for item in stack:
|
||||
last_stack = item[1].replace('<string>','')
|
||||
last_stack = last_stack.strip()
|
||||
stack_array.append(last_stack)
|
||||
try:
|
||||
scrap,addon_id = last_stack.split('addons%s'%os.sep)
|
||||
addon_id = addon_id.split(os.sep)[0]
|
||||
addon_id = Get_Addon_ID(addon_id)
|
||||
if addon_id not in addon_array:
|
||||
addon_array.append(addon_id)
|
||||
except:
|
||||
pass
|
||||
|
||||
if my_return == 'addons':
|
||||
return addon_array
|
||||
if my_return == 'addon':
|
||||
return addon_array[len(addon_array)-1]
|
||||
if my_return == 'path':
|
||||
return stack_array[len(stack_array)-1]
|
||||
if my_return == 'paths':
|
||||
return stack_array
|
||||
#----------------------------------------------------------------
|
||||
def Check_Deps(addon_path, depfiles = []):
|
||||
import re
|
||||
from filetools import Text_File
|
||||
from __init__ import dolog
|
||||
exclude_list = ['xbmc.gui', 'script.module.metahandler', 'metadata.common.allmusic.com',\
|
||||
'kodi.resource','xbmc.core','xbmc.metadata','xbmc.addon','xbmc.json','xbmc.python']
|
||||
file_location = os.path.join(addon_path,'addon.xml')
|
||||
if xbmcvfs.exists(file_location):
|
||||
readxml = Text_File(file_location,'r')
|
||||
dmatch = re.compile('import addon="(.+?)"').findall(readxml)
|
||||
for requires in dmatch:
|
||||
if not requires in exclude_list and not requires in depfiles:
|
||||
depfiles.append(requires)
|
||||
return depfiles
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Check_Repo(repo,show_busy=True,timeout=10):
|
||||
"""
|
||||
This will check the status of repo and return True if the repo is online or False
|
||||
if it contains paths that are no longer accessible online.
|
||||
|
||||
IMPORTANT: If you're running an old version of Kodi which uses the old Python 2.6
|
||||
(OSX and Android lower than Kodi 17 or a linux install with old Python installed on system)
|
||||
you will get a return of False on https links regardless of their real status. This is due
|
||||
to the fact Python 2.6 cannot access secure links. Any still using standard http links
|
||||
will return the correct results.
|
||||
|
||||
CODE: Check_Repo(repo, [show_busy, timeout])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) repo - This is the name of the folder the repository resides in.
|
||||
You can either use the full path or just the folder name which in 99.99%
|
||||
of cases is the add-on id. If only using the folder name DOUBLE check first as
|
||||
there are a handful which have used a different folder name to the actual add-on id!
|
||||
|
||||
show_busy - By default this is set to True and a busy dialog will show during the check
|
||||
|
||||
timeout - By default this is set to 10 (seconds) - this is the maximum each request
|
||||
to the repo url will take before timing out and returning False.
|
||||
|
||||
EXAMPLE CODE:
|
||||
repo_status = Check_Repo('special://xbmc',show_busy=False,timeout=10)
|
||||
if repo_status:
|
||||
dialog.ok('REPO STATUS','The repository modules4all is: [COLOR=lime]ONLINE[/COLOR]')
|
||||
else:
|
||||
dialog.ok('REPO STATUS','The repository modules4all is: [COLOR=red]OFFLINE[/COLOR]')
|
||||
~"""
|
||||
import re
|
||||
|
||||
from __init__ import dolog
|
||||
from filetools import Text_File
|
||||
from guitools import Show_Busy
|
||||
from web import Validate_Link
|
||||
xbmc.log('### CHECKING %s'%repo,2)
|
||||
status = True
|
||||
if show_busy:
|
||||
Show_Busy()
|
||||
if not ADDONS in repo and not XBMC_PATH in repo:
|
||||
repo_path = os.path.join(ADDONS,repo)
|
||||
else:
|
||||
repo_path = repo
|
||||
repo_path = Physical_Path(repo_path)
|
||||
xbmc.log(repo_path,2)
|
||||
repo_path = os.path.join(repo_path,'addon.xml')
|
||||
xbmc.log(repo_path,2)
|
||||
if os.path.exists(repo_path):
|
||||
content = Text_File(repo_path,'r')
|
||||
md5_urls = re.findall(r'<checksum>(.+?)</checksum>', content, re.DOTALL)
|
||||
for item in md5_urls:
|
||||
link_status = Validate_Link(item,timeout)
|
||||
dolog(item)
|
||||
dolog('STATUS: %s'%link_status)
|
||||
if link_status < 200 or link_status >= 400:
|
||||
status = False
|
||||
break
|
||||
if show_busy:
|
||||
Show_Busy(False)
|
||||
return status
|
||||
else:
|
||||
if show_busy:
|
||||
Show_Busy(False)
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Default_Setting(setting='',addon_id='',reset=False):
|
||||
"""
|
||||
This will return the DEFAULT value for a setting (as set in resources/settings.xml)
|
||||
and optionally reset the current value back to this default. If you pass through
|
||||
the setting as blank it will return a dictionary of all default settings.
|
||||
|
||||
CODE: Default_Setting(setting, [addon_id, reset])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
setting - The setting you want to retreive the value for.
|
||||
Leave blank to return a dictionary of all settings
|
||||
|
||||
addon_id - This is optional, if not set it will use the current id.
|
||||
|
||||
reset - By default this is set to False but if set to true and it will
|
||||
reset the current value to the default.
|
||||
|
||||
EXAMPLE CODE:
|
||||
youtube_path = xbmc.translatePath('special://home/addons/plugin.video.youtube')
|
||||
if os.path.exists(youtube_path):
|
||||
my_value = koding.Default_Setting(setting='youtube.region', addon_id='plugin.video.youtube', reset=False)
|
||||
dialog.ok('YOUTUBE SETTING','Below is a default setting for plugin.video.youtube:','Setting: [COLOR=dodgerblue]youtube.region[/COLOR]','Value: [COLOR=dodgerblue]%s[/COLOR]' % my_value)
|
||||
else:
|
||||
dialog.ok('YOUTUBE NOT INSTALLED','We cannot run this example as it uses the YouTube add-on which has not been found on your system.')
|
||||
~"""
|
||||
import re
|
||||
from filetools import Text_File
|
||||
from vartools import Data_Type
|
||||
|
||||
if addon_id == '':
|
||||
addon_id = Caller()
|
||||
values = {}
|
||||
addon_path = Addon_Info(id='path',addon_id=addon_id)
|
||||
settings_path = os.path.join(addon_path,'resources','settings.xml')
|
||||
content = Text_File(settings_path,'r').splitlines()
|
||||
for line in content:
|
||||
if 'id="' in line and 'default="' in line:
|
||||
idx = re.compile('id="(.*?)"').findall(line)
|
||||
idx = idx[0] if (len(idx) > 0) else ''
|
||||
value = re.compile('default="(.*?)"').findall(line)
|
||||
value = value[0] if (len(value) > 0) else ''
|
||||
if setting != '' and idx == setting:
|
||||
values = value
|
||||
break
|
||||
elif idx != '' and value != '' and setting == '':
|
||||
values[idx] = value
|
||||
if reset:
|
||||
if Data_Type(values) == 'dict':
|
||||
for item in values.items():
|
||||
Addon_Setting(addon_id=addon_id,setting=item[0],value=item[1])
|
||||
elif setting != '':
|
||||
Addon_Setting(addon_id=addon_id,setting=setting,value=value)
|
||||
return values
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Dependency_Check(addon_id = 'all', recursive = False):
|
||||
"""
|
||||
This will return a list of all dependencies required by an add-on.
|
||||
This information is grabbed directly from the currently installed addon.xml,
|
||||
an individual add-on id or a list of add-on id's.
|
||||
|
||||
CODE: Dependency_Check([addon_id, recursive])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
addon_id - This is optional, if not set it will return a list of every
|
||||
dependency required from all installed add-ons. If you only want to
|
||||
return results of one particular add-on then send through the id.
|
||||
|
||||
recursive - By default this is set to False but if set to true and you
|
||||
also send through an individual addon_id it will return all dependencies
|
||||
required for that addon id AND the dependencies of the dependencies.
|
||||
|
||||
EXAMPLE CODE:
|
||||
current_id = xbmcaddon.Addon().getAddonInfo('id')
|
||||
dependencies = koding.Dependency_Check(addon_id=current_id, recursive=True)
|
||||
clean_text = ''
|
||||
for item in dependencies:
|
||||
clean_text += item+'\n'
|
||||
koding.Text_Box('Modules required for %s'%current_id,clean_text)
|
||||
~"""
|
||||
import xbmcaddon
|
||||
import re
|
||||
from filetools import Text_File
|
||||
from vartools import Data_Type
|
||||
|
||||
processed = []
|
||||
depfiles = []
|
||||
|
||||
if addon_id == 'all':
|
||||
addon_id = xbmcvfs.listdir(ADDONS)
|
||||
elif Data_Type(addon_id) == 'str':
|
||||
addon_id = [addon_id]
|
||||
|
||||
for name in addon_id:
|
||||
try:
|
||||
addon_path = xbmcaddon.Addon(id=name).getAddonInfo('path')
|
||||
except:
|
||||
addon_path = os.path.join(ADDONS, name)
|
||||
if not name in processed:
|
||||
processed.append(name)
|
||||
|
||||
# Get list of master dependencies
|
||||
depfiles = Check_Deps(addon_path,[name])
|
||||
|
||||
# Recursively check all other dependencies
|
||||
depchecks = depfiles
|
||||
if recursive:
|
||||
while len(depchecks):
|
||||
for depfile in depfiles:
|
||||
if depfile not in processed:
|
||||
try:
|
||||
dep_path = xbmcaddon.Addon(id=depfile).getAddonInfo('path')
|
||||
except:
|
||||
dep_path = os.path.join(ADDONS,depfile)
|
||||
newdepfiles = Check_Deps(dep_path, depfiles)
|
||||
# Pass through the path of sub-dependency and add items to master list and list to check
|
||||
for newdep in newdepfiles:
|
||||
if not (newdep in depchecks) and not (newdep in processed):
|
||||
depchecks.append(newdep)
|
||||
if not newdep in depfiles:
|
||||
depfiles.append(newdep)
|
||||
processed.append(depfile)
|
||||
depchecks.remove(depfile)
|
||||
if name in depchecks:
|
||||
depchecks.remove(name)
|
||||
return processed[1:]
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Get_Addon_ID(folder):
|
||||
"""
|
||||
If you know the folder name of an add-on but want to find out the
|
||||
addon id (it may not necessarily be the same as folder name) then
|
||||
you can use this function. Even if the add-on isn't enabled on the
|
||||
system this will regex out the add-on id.
|
||||
|
||||
CODE: Get_Addon_ID(folder)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
folder - This is folder name of the add-on. Just the name not the path.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('ABOUT','This function allows us to pass through a folder name found in the addons folder and it will return the real id. The vast majority of add-ons use the same folder name as id but there are exceptions. Let\'s check Python Koding...')
|
||||
my_id = koding.Get_Addon_ID(folder='script.module.python.koding.aio')
|
||||
dialog.ok('PYTHON KODING ID','The add-on id found for this folder folder is:','[COLOR=dodgerblue]%s[/COLOR]'%my_id)
|
||||
~"""
|
||||
from filetools import Text_File
|
||||
import re
|
||||
xmlpath = os.path.join(ADDONS, folder, 'addon.xml')
|
||||
if xbmcvfs.exists(xmlpath):
|
||||
contents = Text_File(xmlpath,'r')
|
||||
addon_id = re.compile('id="(.+?)"').findall(contents)
|
||||
addon_id = addon_id[0] if (len(addon_id) > 0) else ''
|
||||
return addon_id
|
||||
else:
|
||||
return folder
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Installed_Addons(types='unknown', content ='unknown', properties = ''):
|
||||
"""
|
||||
This will send back a list of currently installed add-ons on the system.
|
||||
All the three paramaters you can send through to this function are optional,
|
||||
by default (without any params) this function will return a dictionary of all
|
||||
installed add-ons. The dictionary will contain "addonid" and "type" e.g. 'xbmc.python.pluginsource'.
|
||||
|
||||
CODE: Installed_Addons([types, content, properties]):
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
types - If you only want to retrieve details for specific types of add-ons
|
||||
then use this filter. Unfortunately only one type can be filtered at a time,
|
||||
it is not yet possible to filter multiple types all in one go. Please check
|
||||
the official wiki for the add-on types avaialble but here is an example if
|
||||
you only wanted to show installed repositories: koding.Installed_Addons(types='xbmc.addon.repository')
|
||||
|
||||
content - Just as above unfortunately only one content type can be filtered
|
||||
at a time, you can filter by video,audio,image and executable. If you want to
|
||||
only return installed add-ons which appear in the video add-ons section you
|
||||
would use this: koding.Installed_Addons(content='video')
|
||||
|
||||
properties - By default a dictionary containing "addonid" and "type" will be
|
||||
returned for all found add-ons meeting your criteria. However you can add any
|
||||
properties in here available in the add-on xml (check official Wiki for properties
|
||||
available). Unlike the above two options you can choose to add multiple properties
|
||||
to your dictionary, see example below:
|
||||
koding.Installed_Addons(properties='name,thumbnail,description')
|
||||
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_video_plugins = koding.Installed_Addons(types='xbmc.python.pluginsource', content='video', properties='name')
|
||||
final_string = ''
|
||||
for item in my_video_plugins:
|
||||
final_string += 'ID: %s | Name: %s\n'%(item["addonid"], item["name"])
|
||||
koding.Text_Box('LIST OF VIDEO PLUGINS',final_string)
|
||||
~"""
|
||||
try: import simplejson as json
|
||||
except: import json
|
||||
|
||||
addon_dict = []
|
||||
if properties != '':
|
||||
properties = properties.replace(' ','')
|
||||
properties = '"%s"' % properties
|
||||
properties = properties.replace(',','","')
|
||||
|
||||
query = '{"jsonrpc":"2.0", "method":"Addons.GetAddons","params":{"properties":[%s],"enabled":"all","type":"%s","content":"%s"}, "id":1}' % (properties,types,content)
|
||||
response = xbmc.executeJSONRPC(query)
|
||||
data = json.loads(response)
|
||||
if "result" in data:
|
||||
try:
|
||||
addon_dict = data["result"]["addons"]
|
||||
except:
|
||||
pass
|
||||
return addon_dict
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Open_Settings(addon_id='',focus='',click=False,stop_script=True):
|
||||
"""
|
||||
By default this will open the current add-on settings but if you pass through an addon_id it will open the settings for that add-on.
|
||||
|
||||
CODE: Open_Settings([addon_id, focus, click, stop_script])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
addon_id - This optional, it can be any any installed add-on id. If nothing is passed
|
||||
through the current add-on settings will be opened.
|
||||
|
||||
focus - This is optional, if not set the settings will just open to the first item
|
||||
in the list (normal behaviour). However if you want to open to a specific category and
|
||||
setting then enter the number in here separated by a dot. So for example if we want to
|
||||
focus on the 2nd category and 3rd setting in the list we'd send through focus='2.3'
|
||||
|
||||
click - If you want the focused item to automatically be clicked set this to True.
|
||||
|
||||
stop_script - By default this is set to True, as soon as the addon settings are opened
|
||||
the current script will stop running. If you pass through as False then the script will
|
||||
continue running in the background - opening settings does not pause a script, Kodi just
|
||||
see's it as another window being opened.
|
||||
|
||||
EXAMPLE CODE:
|
||||
youtube_path = xbmc.translatePath('special://home/addons/plugin.video.youtube')
|
||||
if os.path.exists(youtube_path):
|
||||
dialog.ok('YOUTUBE SETTINGS','We will now open the YouTube settings.','We will focus on category 2, setting 3 AND send a click.')
|
||||
koding.Open_Settings(addon_id='plugin.video.youtube',focus='2.3',click=True,stop_script=True)
|
||||
else:
|
||||
dialog.ok('YOUTUBE NOT INSTALLED','We cannot run this example as it uses the YouTube add-on which has not been found on your system.')
|
||||
~"""
|
||||
import xbmcaddon
|
||||
if addon_id == '':
|
||||
addon_id = Caller()
|
||||
xbmc.log('ADDON ID: %s'%addon_id,2)
|
||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addon_id)
|
||||
if focus != '':
|
||||
category, setting = focus.split('.')
|
||||
xbmc.executebuiltin('SetFocus(%d)' % (int(category) + 99))
|
||||
xbmc.executebuiltin('SetFocus(%d)' % (int(setting) + 199))
|
||||
if click:
|
||||
xbmc.sleep(500)
|
||||
xbmc.executebuiltin('Action(Select,10140)')
|
||||
if stop_script:
|
||||
try:
|
||||
sys.exit()
|
||||
except:
|
||||
pass
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Toggle_Addons(addon='all', enable=True, safe_mode=True, exclude_list=[], new_only=True, refresh=True, update_status=0):
|
||||
"""
|
||||
Send through either a list of add-on ids or one single add-on id.
|
||||
The add-ons sent through will then be added to the addons*.db
|
||||
and enabled or disabled (depending on state sent through).
|
||||
|
||||
WARNING: If safe_mode is set to False this directly edits the
|
||||
addons*.db rather than using JSON-RPC. Although directly amending
|
||||
the db is a lot quicker there is no guarantee it won't cause
|
||||
severe problems in later versions of Kodi (this was created for v17).
|
||||
DO NOT set safe_mode to False unless you 100% understand the consequences!
|
||||
|
||||
CODE: Toggle_Addons([addon, enable, safe_mode, exclude_list, new_only, refresh])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
(*) addon - This can be a list of addon ids, one single id or
|
||||
'all' to enable/disable all. If enabling all you can still use
|
||||
the exclude_list for any you want excluded from this function.
|
||||
enable - By default this is set to True, if you want to disable
|
||||
the add-on(s) then set this to False.
|
||||
|
||||
safe_mode - By default this is set to True which means the add-ons
|
||||
are enabled/disabled via JSON-RPC which is the method recommended by
|
||||
the XBMC foundation. Setting this to False will result in a much
|
||||
quicker function BUT there is no guarantee this will work on future
|
||||
versions of Kodi and it may even cause corruption in future versions.
|
||||
Setting to False is NOT recommended and you should ONLY use this if
|
||||
you 100% understand the risks that you could break multiple setups.
|
||||
|
||||
exclude_list - Send through a list of any add-on id's you do not
|
||||
want to be included in this command.
|
||||
|
||||
new_only - By default this is set to True so only newly extracted
|
||||
add-on folders will be enabled/disabled. This means that any existing
|
||||
add-ons which have deliberately been disabled by the end user are
|
||||
not affected.
|
||||
|
||||
refresh - By default this is set to True, it will refresh the
|
||||
current container and also force a local update on your add-ons db.
|
||||
|
||||
update_status - When running this function it needs to disable the
|
||||
auto-update of add-ons by Kodi otherwise it risks crashing. This
|
||||
update_status paramater is the state you want Kodi to revert back to
|
||||
once the toggle of add-ons has completed. By default this is set to 0
|
||||
which is auto-update. You can also choose 1 (notify of updates) or 2
|
||||
(disable auto updates).
|
||||
|
||||
EXAMPLE CODE:
|
||||
from systemtools import Refresh
|
||||
xbmc.executebuiltin('ActivateWindow(Videos, addons://sources/video/)')
|
||||
xbmc.sleep(2000)
|
||||
dialog.ok('DISABLE YOUTUBE','We will now disable YouTube (if installed)')
|
||||
koding.Toggle_Addons(addon='plugin.video.youtube', enable=False, safe_mode=True, exclude_list=[], new_only=False)
|
||||
koding.Refresh('container')
|
||||
xbmc.sleep(2000)
|
||||
dialog.ok('ENABLE YOUTUBE','When you click OK we will enable YouTube (if installed)')
|
||||
koding.Toggle_Addons(addon='plugin.video.youtube', enable=True, safe_mode=True, exclude_list=[], new_only=False)
|
||||
koding.Refresh('container')
|
||||
~"""
|
||||
from __init__ import dolog
|
||||
from filetools import DB_Path_Check, Get_Contents
|
||||
from database import DB_Query
|
||||
from systemtools import Last_Error, Refresh, Set_Setting, Sleep_If_Function_Active, Timestamp
|
||||
from vartools import Data_Type
|
||||
|
||||
Set_Setting('general.addonupdates', 'kodi_setting', '2')
|
||||
dolog('disabled auto updates for add-ons')
|
||||
kodi_ver = int(float(xbmc.getInfoLabel("System.BuildVersion")[:2]))
|
||||
addons_db = DB_Path_Check('addons')
|
||||
data_type = Data_Type(addon)
|
||||
state = int(bool(enable))
|
||||
enabled_list = []
|
||||
disabled_list = []
|
||||
if kodi_ver >= 17:
|
||||
on_system = DB_Query(addons_db,'SELECT addonID, enabled from installed')
|
||||
# Create a list of enabled and disabled add-ons already on system
|
||||
enabled_list = Addon_List(enabled=True)
|
||||
disabled_list = Addon_List(enabled=False)
|
||||
|
||||
# If addon has been sent through as a string we add into a list
|
||||
if data_type == 'unicode':
|
||||
addon = addon.encode('utf8')
|
||||
data_type = Data_Type(addon)
|
||||
|
||||
if data_type == 'str' and addon!= 'all':
|
||||
addon = [addon]
|
||||
|
||||
# Grab all the add-on ids from addons folder
|
||||
if addon == 'all':
|
||||
addon = []
|
||||
ADDONS = xbmc.translatePath('special://home/addons')
|
||||
my_addons = Get_Contents(path=ADDONS, exclude_list=['packages','temp'])
|
||||
for item in my_addons:
|
||||
addon_id = Get_Addon_ID(item)
|
||||
addon.append(addon_id)
|
||||
|
||||
# Find out what is and isn't enabled in the addons*.db
|
||||
temp_list = []
|
||||
for addon_id in addon:
|
||||
if not addon_id in exclude_list and addon_id != '':
|
||||
if addon_id in disabled_list and not new_only and enable:
|
||||
temp_list.append(addon_id)
|
||||
elif addon_id not in disabled_list and addon_id not in enabled_list:
|
||||
temp_list.append(addon_id)
|
||||
elif addon_id in enabled_list and not enable:
|
||||
temp_list.append(addon_id)
|
||||
elif addon_id in disabled_list and enable:
|
||||
temp_list.append(addon_id)
|
||||
addon = temp_list
|
||||
|
||||
# If you want to bypass the JSON-RPC mode and directly modify the db (READ WARNING ABOVE!!!)
|
||||
if not safe_mode and kodi_ver >= 17:
|
||||
installedtime = Timestamp('date_time')
|
||||
insert_query = 'INSERT or IGNORE into installed (addonID , enabled, installDate) VALUES (?,?,?)'
|
||||
update_query = 'UPDATE installed SET enabled = ? WHERE addonID = ? '
|
||||
insert_values = [addon, state, installedtime]
|
||||
try:
|
||||
for item in addon:
|
||||
DB_Query(addons_db, insert_query, [item, state, installedtime])
|
||||
DB_Query(addons_db, update_query, [state, item])
|
||||
except:
|
||||
dolog(Last_Error())
|
||||
if refresh:
|
||||
Refresh()
|
||||
|
||||
# Using the safe_mode (JSON-RPC)
|
||||
else:
|
||||
mydeps = []
|
||||
final_enabled = []
|
||||
if state:
|
||||
my_value = 'true'
|
||||
log_value = 'ENABLED'
|
||||
final_addons = []
|
||||
else:
|
||||
my_value = 'false'
|
||||
log_value = 'DISABLED'
|
||||
final_addons = addon
|
||||
|
||||
for my_addon in addon:
|
||||
|
||||
# If enabling the add-on then we also check for dependencies and enable them first
|
||||
if state:
|
||||
dependencies = Dependency_Check(addon_id=my_addon, recursive=True)
|
||||
mydeps.append(dependencies)
|
||||
|
||||
# if enable selected we traverse through the dependencies enabling addons with lowest amount of deps to highest
|
||||
if state:
|
||||
mydeps = sorted(mydeps, key=len)
|
||||
for dep in mydeps:
|
||||
counter = 0
|
||||
for item in dep:
|
||||
enable_dep = True
|
||||
if counter == 0:
|
||||
final_addons.append(item)
|
||||
enable_dep = False
|
||||
elif item in final_enabled:
|
||||
enable_dep = False
|
||||
else:
|
||||
enable_dep = True
|
||||
if enable_dep:
|
||||
if not item in exclude_list and not item in final_enabled and not item in enabled_list:
|
||||
if Set_Setting(setting_type='addon_enable', setting=item, value = 'true'):
|
||||
final_enabled.append(item)
|
||||
counter += 1
|
||||
|
||||
# Now the dependencies are enabled we need to enable the actual main add-ons
|
||||
for my_addon in final_addons:
|
||||
if not my_addon in final_enabled:
|
||||
if Set_Setting(setting_type='addon_enable', setting=my_addon, value = my_value):
|
||||
final_enabled.append(addon)
|
||||
if refresh:
|
||||
Refresh(['addons','container'])
|
||||
Set_Setting('general.addonupdates', 'kodi_setting', '%s'%update_status)
|
||||
#----------------------------------------------------------------
|
||||
Reference in New Issue
Block a user