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:
118
script.module.python.koding.aio/lib/koding/__init__.py
Normal file
118
script.module.python.koding.aio/lib/koding/__init__.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# -*- 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 re
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
import inspect
|
||||
try:
|
||||
import simplejson as json
|
||||
except:
|
||||
import json
|
||||
|
||||
from addons import *
|
||||
from android import *
|
||||
from database import *
|
||||
from directory import *
|
||||
from filetools import *
|
||||
from guitools import *
|
||||
from router import *
|
||||
from systemtools import *
|
||||
from tutorials import *
|
||||
from video import *
|
||||
from vartools import *
|
||||
from web import *
|
||||
|
||||
def converthex(url):
|
||||
""" internal command ~"""
|
||||
import binascii
|
||||
return binascii.unhexlify(url)
|
||||
|
||||
try:
|
||||
ADDON_ID = xbmcaddon.Addon().getAddonInfo('id')
|
||||
except:
|
||||
ADDON_ID = Caller()
|
||||
|
||||
AddonVersion = xbmcaddon.Addon(id=ADDON_ID).getAddonInfo('version')
|
||||
ORIG_ID = ADDON_ID
|
||||
|
||||
TestID = ADDON_ID
|
||||
if not ADDON_ID.endswith(converthex('2e74657374')):
|
||||
TestID = ADDON_ID+converthex('2e74657374')
|
||||
|
||||
MODULE_ID = 'script.module.python.koding.aio'
|
||||
ADDON = xbmcaddon.Addon(id=ADDON_ID)
|
||||
THIS_MODULE = xbmcaddon.Addon(id=MODULE_ID)
|
||||
USERDATA = 'special://profile'
|
||||
ADDON_DATA = os.path.join(USERDATA,'addon_data')
|
||||
ADDONS = 'special://home/addons'
|
||||
PACKAGES = os.path.join(ADDONS,'packages')
|
||||
UPDATE_ICON = os.path.join(ADDONS,MODULE_ID,'resources','update.png')
|
||||
DEBUG = Addon_Setting(addon_id=ORIG_ID,setting=converthex('6465627567'))
|
||||
KODI_VER = int(float(xbmc.getInfoLabel("System.BuildVersion")[:2]))
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
dp = xbmcgui.DialogProgress()
|
||||
|
||||
if not xbmcvfs.exists(os.path.join(ADDON_DATA,ORIG_ID,'cookies')):
|
||||
xbmcvfs.mkdirs(os.path.join(ADDON_DATA,ORIG_ID,'cookies'))
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def dolog(string, my_debug=False, line_info=False):
|
||||
"""
|
||||
Print to the Kodi log but only if debugging is enabled in settings.xml
|
||||
|
||||
CODE: koding.dolog(string, [my_debug])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) string - This is your text you want printed to log.
|
||||
|
||||
my_debug - This is optional, if you set this to True you will print
|
||||
to the log regardless of what the debug setting is set at in add-on settings.
|
||||
|
||||
line_info - By default this is set to True and will show the line number where
|
||||
the dolog command was called from along with the filepath it was called from.
|
||||
|
||||
EXAMPLE CODE:
|
||||
koding.dolog(string='Quick test to see if this gets printed to the log', my_debug=True, line_info=True)
|
||||
dialog.ok('[COLOR gold]CHECK LOGFILE 1[/COLOR]','If you check your log file you should be able to see a new test line we printed \
|
||||
and immediately below that should be details of where it was called from.')
|
||||
koding.dolog(string='This one should print without the line and file info', my_debug=True, line_info=False)
|
||||
dialog.ok('[COLOR gold]CHECK LOGFILE 2[/COLOR]','If you check your log file again you should now be able to see a new line printed \
|
||||
but without the file/line details.')
|
||||
~"""
|
||||
import xbmc
|
||||
if DEBUG == 'true' or my_debug:
|
||||
try:
|
||||
xbmc.log('### %s (%s) : %s'%(ADDON_ID,AddonVersion,string), level=xbmc.LOGNOTICE)
|
||||
except:
|
||||
xbmc.log(Last_Error(),level=xbmc.LOGNOTICE)
|
||||
if line_info:
|
||||
try:
|
||||
from inspect import getframeinfo, stack
|
||||
caller = getframeinfo(stack()[1][0])
|
||||
xbmc.log('^ Line No. %s | File: %s'%(caller.lineno,caller.filename),level=xbmc.LOGNOTICE)
|
||||
except:
|
||||
xbmc.log(Last_Error(),level=xbmc.LOGNOTICE)
|
||||
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)
|
||||
#----------------------------------------------------------------
|
||||
93
script.module.python.koding.aio/lib/koding/android.py
Normal file
93
script.module.python.koding.aio/lib/koding/android.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# -*- 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 xbmc
|
||||
import subprocess
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def App_Settings(apk_id):
|
||||
"""
|
||||
Open up the settings for an installed Android app.
|
||||
|
||||
CODE: App_Settings(apk_id)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) apk_id - The id of the app you want to open the settings for.
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_apps = koding.My_Apps()
|
||||
choice = dialog.select('CHOOSE AN APK', my_apps)
|
||||
koding.App_Settings(apk_id=my_apps[choice])
|
||||
~"""
|
||||
xbmc.executebuiltin('StartAndroidActivity("","android.settings.APPLICATION_DETAILS_SETTINGS","","package:%s")' % apk_id)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def My_Apps():
|
||||
"""
|
||||
Return a list of apk id's installed on system
|
||||
|
||||
CODE: My_Apps()
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_apps = koding.My_Apps()
|
||||
choice = dialog.select('CHOOSE AN APK', my_apps)
|
||||
if choice >= 0:
|
||||
koding.App_Settings(apk_id=my_apps[choice])
|
||||
~"""
|
||||
Installed_APK = []
|
||||
if xbmc.getCondVisibility('system.platform.android'):
|
||||
try:
|
||||
Installed_APK = subprocess.Popen(['exec ''/system/bin/pm list packages -3'''], executable='/system/bin/sh', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].rstrip('\n').splitlines()
|
||||
except Exception as e:
|
||||
xbmc.log('Failed to grab installed app details: %s' % e)
|
||||
Installed_APK = []
|
||||
|
||||
for i in range(len(Installed_APK)):
|
||||
Installed_APK[i] = Installed_APK[i].partition(':')[2]
|
||||
|
||||
return Installed_APK
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Start_App(apk_id):
|
||||
"""
|
||||
Open an Android application
|
||||
|
||||
CODE: Start_App(apk_id)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) apk_id - The id of the app you want to open.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('OPEN FACEBOOK','Presuming you have Facebook installed and this is an Android system we will now open that apk')
|
||||
koding.Start_App(apk_id='com.facebook.katana')
|
||||
~"""
|
||||
xbmc.executebuiltin('StartAndroidActivity(%s)' % apk_id)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Uninstall_APK(apk_id):
|
||||
"""
|
||||
Uninstall and Android app
|
||||
|
||||
CODE: Uninstall_APK(apk_id)
|
||||
|
||||
EXAMPLE CODE:
|
||||
if dialog.yesno('UNINSTALL FACEBOOK','Would you like to uninstall the Facebook app from your system?'):
|
||||
koding.Uninstall_APK(apk_id='com.facebook.katana')
|
||||
~"""
|
||||
xbmc.executebuiltin('StartAndroidActivity("","android.intent.action.DELETE","","package:%s")' % apk_id)
|
||||
#----------------------------------------------------------------
|
||||
458
script.module.python.koding.aio/lib/koding/database.py
Normal file
458
script.module.python.koding.aio/lib/koding/database.py
Normal file
@@ -0,0 +1,458 @@
|
||||
# -*- 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 sys
|
||||
try: from sqlite3 import dbapi2 as database
|
||||
except: from pysqlite2 import dbapi2 as database
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
from __init__ import Caller
|
||||
from filetools import Physical_Path
|
||||
|
||||
# Put this in a try statement, when called from a service it will throw an error otherwise
|
||||
try:
|
||||
try:
|
||||
ADDON_ID = xbmcaddon.Addon().getAddonInfo('id')
|
||||
except:
|
||||
ADDON_ID = Caller()
|
||||
|
||||
AddonVersion = xbmcaddon.Addon(id=ADDON_ID).getAddonInfo('version')
|
||||
profile_path = xbmcaddon.Addon(id=ADDON_ID).getAddonInfo('profile')
|
||||
addon_db_path = Physical_Path(os.path.join(profile_path,'database.db'))
|
||||
except:
|
||||
pass
|
||||
|
||||
dbcur, dbcon = None, None
|
||||
dialog = xbmcgui.Dialog()
|
||||
#----------------------------------------------------------------
|
||||
def _connect_to_db():
|
||||
""" internal command ~"""
|
||||
def dict_factory(cursor, row):
|
||||
d = {}
|
||||
for idx, col in enumerate(cursor.description):
|
||||
d[col[0]] = row[idx]
|
||||
return d
|
||||
|
||||
xbmcvfs.mkdirs(profile_path)
|
||||
db_location = os.path.join(profile_path.decode('utf-8'),'database.db')
|
||||
db_location = Physical_Path(db_location)
|
||||
dbcon = database.connect(db_location)
|
||||
dbcon.row_factory = dict_factory
|
||||
dbcur = dbcon.cursor()
|
||||
return (dbcur, dbcon)
|
||||
#----------------------------------------------------------------
|
||||
def _execute_db_string(sql_string, commit = True):
|
||||
""" internal command ~"""
|
||||
global dbcur, dbcon
|
||||
if dbcur is None or dbcon is None:
|
||||
dbcur, dbcon = _connect_to_db()
|
||||
dbcur.execute(sql_string)
|
||||
if commit:
|
||||
dbcon.commit()
|
||||
results = []
|
||||
for result in dbcur:
|
||||
results.append(result)
|
||||
return results
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Add_To_Table(table, spec, abort_on_error=False):
|
||||
"""
|
||||
Add a row to the table in /userdata/addon_data/<your_addon_id>/database.db
|
||||
|
||||
CODE: Add_To_Table(table, spec)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) table - The table name you want to query
|
||||
|
||||
(*) spec - Sent through as a dictionary this is the colums and constraints.
|
||||
|
||||
abort_on_error - Default is set to False but set to True if you want to abort
|
||||
the process when it hits an error.
|
||||
|
||||
EXAMPLE CODE:
|
||||
create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}}
|
||||
koding.Create_Table("test_table", create_specs)
|
||||
add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"}
|
||||
add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"}
|
||||
koding.Add_To_Table("test_table", add_specs1)
|
||||
koding.Add_To_Table("test_table", add_specs2)
|
||||
results = koding.Get_All_From_Table("test_table")
|
||||
final_results = ''
|
||||
for item in results:
|
||||
final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"])
|
||||
koding.Text_Box('DB RESULTS', final_results)
|
||||
koding.Remove_Table('test_table')
|
||||
~"""
|
||||
global dbcon
|
||||
sql_string = "INSERT INTO %s (" % table
|
||||
keys = []
|
||||
values = []
|
||||
if type(spec) != list:
|
||||
spec = [spec]
|
||||
for item in spec:
|
||||
for key in item.keys():
|
||||
keys.append(key)
|
||||
values.append(item[key])
|
||||
for key in keys:
|
||||
sql_string += "%s, " % key
|
||||
sql_string = sql_string[:-2]
|
||||
sql_string += ") Values ("
|
||||
for value in values:
|
||||
sql_string += "\"%s\", " % value
|
||||
sql_string = sql_string[:-2]
|
||||
sql_string += ")"
|
||||
try:
|
||||
_execute_db_string(sql_string, commit=False)
|
||||
except:
|
||||
if abort_on_error:
|
||||
dbcon.rollback()
|
||||
raise Exception()
|
||||
continue
|
||||
dbcon.commit()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Add_Multiple_To_Table(table, keys=[], values=[]):
|
||||
"""
|
||||
This will allow you to add multiple rows to a table in one big (fast) bulk command
|
||||
The db file is: /userdata/addon_data/<your_addon_id>/database.db
|
||||
|
||||
CODE: Add_To_Table(table, spec)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) table - The table name you want to query
|
||||
|
||||
(*) keys - Send through a list of keys you want to add to
|
||||
|
||||
(*) values - A list of values you want to add, this needs to be
|
||||
a list of lists (see example below)
|
||||
|
||||
EXAMPLE CODE:
|
||||
create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}}
|
||||
koding.Create_Table("test_table", create_specs)
|
||||
dialog.ok('ADD TO TABLE','Lets add the details of 3 add-ons to "test_table" in our database.')
|
||||
mykeys = ["name","id"]
|
||||
myvalues = [("YouTube","plugin.video.youtube"), ("vimeo","plugin.video.vimeo"), ("test2","plugin.video.test2")]
|
||||
koding.Add_Multiple_To_Table(table="test_table", keys=mykeys, values=myvalues)
|
||||
results = koding.Get_All_From_Table("test_table")
|
||||
final_results = ''
|
||||
for item in results:
|
||||
final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"])
|
||||
koding.Text_Box('DB RESULTS', 'Below are details of the items pulled from our db:\n\n%s'%final_results)
|
||||
koding.Remove_Table('test_table')
|
||||
~"""
|
||||
dbcur, dbcon = _connect_to_db()
|
||||
sql_string = "INSERT INTO %s (" % table
|
||||
sql_2 = ''
|
||||
if type(keys) != list:
|
||||
keys = [keys]
|
||||
if type(values) != list:
|
||||
values = [values]
|
||||
for item in keys:
|
||||
if not item.startswith('`'):
|
||||
item = r'`'+item
|
||||
if not item.endswith('`'):
|
||||
item = item+r'`'
|
||||
xbmc.log('ITEM: %s'%item,2)
|
||||
sql_string += "%s, " % item
|
||||
sql_2 += "?,"
|
||||
sql_string = "%s) VALUES (%s)"%(sql_string[:-2], sql_2[:-1])
|
||||
dbcur.executemany(sql_string, values)
|
||||
dbcon.commit()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Create_Table(table, spec):
|
||||
"""
|
||||
Create a new table in the database at /userdata/addon_data/<your_addon_id>/database.db
|
||||
|
||||
CODE: Create_Table(table, spec)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) table - The table name you want to query
|
||||
|
||||
(*) spec - Sent through as a dictionary this is the colums and constraints.
|
||||
|
||||
EXAMPLE CODE:
|
||||
create_specs = { "columns":{"name":"TEXT", "id":"TEXT"}, "constraints":{"unique":"id"} }
|
||||
koding.Create_Table("test_table", create_specs)
|
||||
dialog.ok('TABLE CREATED','A new table has been created in your database and the id column has been set as UNIQUE.')
|
||||
my_specs = {"name":"YouTube", "id":"plugin.video.youtube"}
|
||||
try:
|
||||
koding.Add_To_Table("test_table", my_specs)
|
||||
koding.Add_To_Table("test_table", my_specs)
|
||||
except:
|
||||
dialog.ok('FAILED TO ADD','Could not add duplicate items because the the column "id" is set to be UNIQUE')
|
||||
results = koding.Get_All_From_Table("test_table")
|
||||
final_results = ''
|
||||
for item in results:
|
||||
final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"])
|
||||
koding.Text_Box('DB RESULTS', final_results)
|
||||
koding.Remove_Table('test_table')
|
||||
~"""
|
||||
sql_string = "CREATE TABLE IF NOT EXISTS %s (" % table
|
||||
columns = spec.get("columns", {})
|
||||
constraints = spec.get("constraints", {})
|
||||
for key in columns.keys():
|
||||
if not columns[key]:
|
||||
columns[key] = "TEXT"
|
||||
sql_string += "%s %s, " % (key, columns[key])
|
||||
|
||||
for key in constraints.keys():
|
||||
sql_string += "%s(%s), " % (key, constraints[key])
|
||||
sql_string = sql_string[:-2]
|
||||
sql_string += ");"
|
||||
_execute_db_string(sql_string)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def DB_Query(db_path, query, values=''):
|
||||
"""
|
||||
Open a database and either return an array of results with the SELECT SQL command or perform an action such as INSERT, UPDATE, CREATE.
|
||||
|
||||
CODE: DB_Query(db_path, query, [values])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) db_path - the full path to the database file you want to access.
|
||||
|
||||
(*) query - this is the actual db query you want to process, use question marks for values
|
||||
|
||||
values - a list of values, even if there's only one value it must be sent through as a list item.
|
||||
|
||||
IMPORTANT: Directly accessing databases which are outside of your add-ons domain is very much frowned
|
||||
upon. If you need to access a built-in kodi database (as shown in example below) you should always use
|
||||
the JSON-RPC commands where possible.
|
||||
|
||||
EXAMPLE CODE:
|
||||
import filetools
|
||||
dbpath = filetools.DB_Path_Check('addons')
|
||||
db_table = 'addon'
|
||||
kodi_version = int(float(xbmc.getInfoLabel("System.BuildVersion")[:2]))
|
||||
if kodi_version >= 17:
|
||||
db_table = 'addons'
|
||||
db_query = koding.DB_Query(db_path=dbpath, query='SELECT * FROM %s WHERE addonID LIKE ? AND addonID NOT LIKE ?'%db_table, values=['%youtube%','%script.module%'])
|
||||
koding.Text_Box('DB SEARCH RESULTS',str(db_query))
|
||||
~"""
|
||||
db_dict = []
|
||||
db_path = Physical_Path(db_path)
|
||||
con = database.connect(db_path)
|
||||
cur = con.cursor()
|
||||
|
||||
if query.upper().startswith('SELECT'):
|
||||
if values == '':
|
||||
cur.execute(query)
|
||||
else:
|
||||
cur.execute(query, values)
|
||||
|
||||
names = list(map(lambda x: x[0], cur.description))
|
||||
|
||||
for rows in iter(cur.fetchmany, []):
|
||||
for row in rows:
|
||||
temp_dict = {}
|
||||
for idx, col in enumerate(cur.description):
|
||||
temp_dict[col[0]] = row[idx]
|
||||
db_dict.append(temp_dict)
|
||||
return db_dict
|
||||
|
||||
elif query.upper().startswith('CREATE'):
|
||||
cur.execute(query)
|
||||
con.commit()
|
||||
|
||||
# ANY NON SELECT QUERY (UPDATE, INSERT ETC.)
|
||||
else:
|
||||
try:
|
||||
if values == '':
|
||||
cur.executemany(query)
|
||||
con.commit()
|
||||
else:
|
||||
cur.executemany(query, values)
|
||||
con.commit()
|
||||
except:
|
||||
if values == '':
|
||||
cur.execute(query)
|
||||
con.commit()
|
||||
else:
|
||||
cur.execute(query, values)
|
||||
con.commit()
|
||||
|
||||
cur.close()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Get_All_From_Table(table):
|
||||
"""
|
||||
Return a list of all entries from a specific table in /userdata/addon_data/<your_addon_id>/database.db
|
||||
|
||||
CODE: Get_All_From_Table(table)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) table - The table name you want to query
|
||||
|
||||
EXAMPLE CODE:
|
||||
create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}}
|
||||
koding.Create_Table("test_table", create_specs)
|
||||
add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"}
|
||||
add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"}
|
||||
koding.Add_To_Table("test_table", add_specs1)
|
||||
koding.Add_To_Table("test_table", add_specs2)
|
||||
results = koding.Get_All_From_Table("test_table")
|
||||
final_results = ''
|
||||
for item in results:
|
||||
final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"])
|
||||
koding.Text_Box('DB RESULTS', final_results)
|
||||
koding.Remove_Table('test_table')
|
||||
~"""
|
||||
try:
|
||||
return _execute_db_string("SELECT * FROM %s" % table)
|
||||
except:
|
||||
return []
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Get_From_Table(table, spec=None, default_compare_operator="="):
|
||||
"""
|
||||
Return a list of all entries matching a specific criteria from the
|
||||
database stored at: /userdata/addon_data/<your_addon_id>/database.db
|
||||
|
||||
CODE: Get_From_Table(table, spec, compare_operator)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) table - The table name you want to query
|
||||
|
||||
spec - This is the query value, sent through as a dictionary.
|
||||
|
||||
default_compare_operator - By default this is set to '=' but could be any
|
||||
other SQL query string such as 'LIKE', 'NOT LIKE', '!=' etc.
|
||||
|
||||
EXAMPLE CODE:
|
||||
create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}}
|
||||
koding.Create_Table("test_table", create_specs)
|
||||
add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"}
|
||||
add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"}
|
||||
koding.Add_To_Table("test_table", add_specs1)
|
||||
koding.Add_To_Table("test_table", add_specs2)
|
||||
results = koding.Get_From_Table(table="test_table", spec={"name":"%vim%"}, default_compare_operator="LIKE")
|
||||
final_results = ''
|
||||
for item in results:
|
||||
final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"])
|
||||
koding.Text_Box('DB CONTENTS', final_results)
|
||||
koding.Remove_Table('test_table')
|
||||
~"""
|
||||
if spec == None:
|
||||
return Get_All_From_Table()
|
||||
sql_string = "SELECT * FROM %s WHERE " % table
|
||||
for key in spec.keys():
|
||||
if type(spec[key]) == dict:
|
||||
value = spec[key]["value"]
|
||||
column_compare_operator = spec[key].get("compare_operator", default_compare_operator)
|
||||
else:
|
||||
value = spec[key]
|
||||
column_compare_operator = default_compare_operator
|
||||
sql_string += "%s %s \"%s\" AND " % (key, column_compare_operator, value)
|
||||
sql_string = sql_string[:-5]
|
||||
try:
|
||||
return _execute_db_string(sql_string, commit=False)
|
||||
except:
|
||||
return []
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Remove_From_Table(table, spec, default_compare_operator="=", abort_on_error=False):
|
||||
"""
|
||||
Remove entries in the db table at /userdata/addon_data/<your_addon_id>/database.db
|
||||
|
||||
CODE: Remove_From_Table(table, spec, [compare_operator])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) table - The table name you want to query
|
||||
|
||||
spec - This is the query value, sent through as a dictionary.
|
||||
|
||||
default_compare_operator - By default this is set to '=' but could be any
|
||||
other SQL query string such as 'LIKE', 'NOT LIKE', '!=' etc.
|
||||
|
||||
EXAMPLE CODE:
|
||||
create_specs = {"columns":{"name":"TEXT", "id":"TEXT"}}
|
||||
koding.Create_Table(table="test_table", spec=create_specs)
|
||||
add_specs1 = {"name":"YouTube", "id":"plugin.video.youtube"}
|
||||
add_specs2 = {"name":"vimeo","id":"plugin.video.vimeo"}
|
||||
koding.Add_To_Table(table="test_table", spec=add_specs1)
|
||||
koding.Add_To_Table(table="test_table", spec=add_specs2)
|
||||
results = koding.Get_All_From_Table(table="test_table")
|
||||
final_results = ''
|
||||
for item in results:
|
||||
final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"])
|
||||
koding.Text_Box('DB CONTENTS', final_results)
|
||||
dialog.ok('REMOVE ITEM','We will now remove vimeo from the table, lets see if it worked...')
|
||||
koding.Remove_From_Table(table="test_table", spec={"name":"vimeo"})
|
||||
results = koding.Get_All_From_Table(table="test_table")
|
||||
final_results = ''
|
||||
for item in results:
|
||||
final_results += 'ID: %s | Name: %s\n'%(item["id"], item["name"])
|
||||
koding.Text_Box('NEW DB CONTENTS', final_results)
|
||||
koding.Remove_Table('test_table')
|
||||
~"""
|
||||
global dbcon
|
||||
sql_string = "DELETE FROM %s WHERE " % table
|
||||
if type(spec) != list:
|
||||
spec = [spec]
|
||||
for item in spec:
|
||||
for key in item.keys():
|
||||
if type(item[key]) == dict:
|
||||
value = item[key]["value"]
|
||||
column_compare_operator = item[key].get("compare_operator", default_compare_operator)
|
||||
else:
|
||||
value = item[key]
|
||||
column_compare_operator = default_compare_operator
|
||||
sql_string += "%s %s \"%s\" AND " % (key, column_compare_operator, value)
|
||||
sql_string = sql_string[:-4]
|
||||
try:
|
||||
_execute_db_string(sql_string, commit=False)
|
||||
except:
|
||||
if abort_on_error:
|
||||
dbcon.rollback()
|
||||
raise Exception()
|
||||
continue
|
||||
dbcon.commit()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Remove_Table(table):
|
||||
"""
|
||||
Use with caution, this will completely remove a database table and
|
||||
all of it's contents. The only database you can access with this command
|
||||
is your add-ons own db file called database.db
|
||||
|
||||
CODE: Remove_Table(table)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) table - This is the name of the table you want to permanently delete.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('REMOVE TABLE','It\'s a bit pointless doing this as you can\'t physically see what\'s happening so you\'ll just have to take our word it works!')
|
||||
koding.Remove_Table('test_table')
|
||||
~"""
|
||||
sql_string = "DROP TABLE IF EXISTS %s;" % table
|
||||
_execute_db_string(sql_string)
|
||||
#----------------------------------------------------------------
|
||||
def reset_db():
|
||||
global dbcon, dbcur
|
||||
dbcur, dbcon = None, None
|
||||
225
script.module.python.koding.aio/lib/koding/directory.py
Normal file
225
script.module.python.koding.aio/lib/koding/directory.py
Normal file
@@ -0,0 +1,225 @@
|
||||
# -*- 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 sys
|
||||
import urllib
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
mode = ''
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Add_Dir(name, url='', mode='', folder=False, icon='', fanart='', description='', info_labels={}, set_art={}, set_property={}, content_type='', context_items=None, context_override=False, playable=False):
|
||||
"""
|
||||
This allows you to create a list item/folder inside your add-on.
|
||||
Please take a look at your addon default.py comments for more information
|
||||
(presuming you created one at http://totalrevolution.tv)
|
||||
|
||||
TOP TIP: If you want to send multiple variables through to a function just
|
||||
send through as a dictionary encapsulated in quotation marks. In the function
|
||||
you can then use the following code to access them:
|
||||
|
||||
params = eval(url)
|
||||
^ That will then give you a dictionary where you can just pull each variable and value from.
|
||||
|
||||
CODE: Add_Dir(name, url, mode, [folder, icon, fanart, description, info_labels, content_type, context_items, context_override, playable])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) name - This is the name you want to show for the list item
|
||||
|
||||
url - If the route (mode) you're calling requires extra paramaters
|
||||
to be sent through then this is where you add them. If the function is
|
||||
only expecting one item then you can send through as a simple string.
|
||||
Unlike many other Add_Dir functions Python Koding does allow for multiple
|
||||
params to be sent through in the form of a dictionary so let's say your
|
||||
function is expecting the 2 params my_time & my_date. You would send this info
|
||||
through as a dictionary like this:
|
||||
url={'my_time':'10:00', 'my_date':'01.01.1970'}
|
||||
|
||||
If you send through a url starting with plugin:// the item will open up into
|
||||
that plugin path so for example:
|
||||
url='plugin://plugin.video.youtube/play/?video_id=FTI16i7APhU'
|
||||
|
||||
mode - The mode you want to open when this item is clicked, this is set
|
||||
in your master_modes dictionary (see template add-on linked above)
|
||||
|
||||
folder - This is an optional boolean, by default it's set to False.
|
||||
True will open into a folder rather than an executable command
|
||||
|
||||
icon - The path to the thumbnail you want to use for this list item
|
||||
|
||||
fanart - The path to the fanart you want to use for this list item
|
||||
|
||||
description - A description of your list item, it's skin dependant but this
|
||||
usually appears below the thumbnail
|
||||
|
||||
info_labels - You can send through any number of info_labels via this option.
|
||||
For full details on the infolabels available please check the pydocs here:
|
||||
http://mirrors.kodi.tv/docs/python-docs/16.x-jarvis/xbmcgui.html#ListItem-setInfo
|
||||
|
||||
When passing through infolabels you need to use a dictionary in this format:
|
||||
{"genre":"comedy", "title":"test video"}
|
||||
|
||||
set_art - Using the same format as info_labels you can set your artwork via
|
||||
a dictionary here. Full details can be found here:
|
||||
http://mirrors.kodi.tv/docs/python-docs/16.x-jarvis/xbmcgui.html#ListItem-setArt
|
||||
|
||||
set_property - Using the same format as info_labels you can set your artwork via
|
||||
a dictionary here. Full details can be found here:
|
||||
http://kodi.wiki/view/InfoLabels#ListItem
|
||||
|
||||
content_type - By default this will set the content_type for kodi to a blank string
|
||||
which is what Kodi expects for generic category listings. There are plenty of different
|
||||
types though and when set Kodi will perform different actions (such as access the
|
||||
database looking for season/episode information for the list item).
|
||||
|
||||
WARNING: Setting the wrong content type for your listing can cause the system to
|
||||
log thousands of error reports in your log, cause the system to lag and make
|
||||
thousands of unnecessary db calls - sometimes resulting in a crash. You can find
|
||||
details on the content_types available here: http://forum.kodi.tv/showthread.php?tid=299107
|
||||
|
||||
context_items - Add context items to your directory. The params you need to send through
|
||||
need to be in a list format of [(label, action,),] look at the example code below for
|
||||
more details.
|
||||
|
||||
context_override - By default your context items will be added to the global context
|
||||
menu items but you can override this by setting this to True and then only your
|
||||
context menu items will show.
|
||||
|
||||
playable - By default this is set to False but if set to True kodi will just try
|
||||
and play this item natively with no extra fancy functions.
|
||||
|
||||
EXAMPLE:
|
||||
my_context = [('Music','xbmc.executebuiltin("ActivateWindow(music)")'),('Programs','xbmc.executebuiltin("ActivateWindow(programs)")')]
|
||||
# ^ This is our two basic context menu items (music and programs)
|
||||
|
||||
Add_Dir(name='TEST DIRECTORY', url='', mode='test_directory', folder=True, context_items=my_context, context_override=True)
|
||||
# ^ This will add a folder AND a context menu item for when bring up the menu (when focused on this directory).
|
||||
# ^^ The context_override is set to True which means it will override the default Kodi context menu items.
|
||||
|
||||
Add_Dir(name='TEST ITEM', url='', mode='test_item', folder=False, context_items=my_context, context_override=False)
|
||||
# ^ This will add an item to the list AND a context menu item for when bring up the menu (when focused on this item).
|
||||
# ^^ The context_override is set to False which means the new items will appear alongside the default Kodi context menu items.
|
||||
~"""
|
||||
from vartools import Convert_Special, Data_Type
|
||||
|
||||
module_id = 'script.module.python.koding.aio'
|
||||
this_module = xbmcaddon.Addon(id=module_id)
|
||||
|
||||
addon_handle = int(sys.argv[1])
|
||||
# Check we're in an appropriate section for the content type set
|
||||
song_only_modes = ['songs','artist','album','song','music']
|
||||
video_only_modes = ['sets','tvshows','seasons','actors','directors','unknown','video','set','movie','tvshow','season','episode']
|
||||
if xbmc.getInfoLabel('Window.Property(xmlfile)') == 'MyVideoNav.xml' and content_type in song_only_modes:
|
||||
content_type = ''
|
||||
if xbmc.getInfoLabel('Window.Property(xmlfile)') == 'MyMusicNav.xml' and content_type in video_only_modes:
|
||||
content_type = ''
|
||||
|
||||
if description == '':
|
||||
description = this_module.getLocalizedString(30837)
|
||||
|
||||
if Data_Type(url) == 'dict':
|
||||
url = repr(url)
|
||||
|
||||
if Data_Type(info_labels) != 'dict':
|
||||
dialog.ok('WRONG INFO LABELS', 'Please check documentation, these should be sent through as a dictionary.')
|
||||
|
||||
if Data_Type(set_art) != 'dict':
|
||||
dialog.ok('WRONG SET_ART', 'Please check documentation, these should be sent through as a dictionary.')
|
||||
|
||||
if Data_Type(set_property) != 'dict':
|
||||
dialog.ok('WRONG SET_PROPERTY', 'Please check documentation, these should be sent through as a dictionary.')
|
||||
|
||||
# Set the default title, filename and plot if not sent through already via info_labels
|
||||
try:
|
||||
title = info_labels["Title"]
|
||||
if title == '':
|
||||
info_labels["Title"] = name
|
||||
except:
|
||||
info_labels["Title"] = name
|
||||
|
||||
try:
|
||||
filename = info_labels["FileName"]
|
||||
# if filename == '':
|
||||
# info_labels["FileName"] = name
|
||||
except:
|
||||
info_labels["FileName"] = name
|
||||
|
||||
try:
|
||||
plot = info_labels["plot"]
|
||||
if plot == '':
|
||||
info_labels["plot"] = description
|
||||
except:
|
||||
info_labels["plot"] = description
|
||||
# Set default thumbnail image used for listing (if not sent through via set_art)
|
||||
try:
|
||||
set_art["icon"]
|
||||
except:
|
||||
set_art["icon"] = icon
|
||||
|
||||
# Set default Fanart if not already sent through via set_property
|
||||
try:
|
||||
set_property["Fanart_Image"] = fanart
|
||||
except:
|
||||
set_property["Fanart_Image"]
|
||||
|
||||
# Set the main listitem properties
|
||||
liz = xbmcgui.ListItem(label=str(name), iconImage=str(icon), thumbnailImage=str(icon))
|
||||
|
||||
# Set the infolabels
|
||||
liz.setInfo(type=content_type, infoLabels=info_labels)
|
||||
|
||||
# Set the artwork
|
||||
liz.setArt(set_art)
|
||||
|
||||
# Loop through the set_property list and set each item in there
|
||||
for item in set_property.items():
|
||||
liz.setProperty(item[0], item[1])
|
||||
|
||||
# Add a context item (if details for context items are sent through)
|
||||
if context_items:
|
||||
liz.addContextMenuItems(context_items, context_override)
|
||||
|
||||
u = sys.argv[0]
|
||||
u += "?mode=" +str(mode)
|
||||
u += "&url=" +Convert_Special(url,string=True)
|
||||
u += "&name=" +urllib.quote_plus(name)
|
||||
u += "&iconimage=" +urllib.quote_plus(icon)
|
||||
u += "&fanart=" +urllib.quote_plus(fanart)
|
||||
u += "&description=" +urllib.quote_plus(description)
|
||||
|
||||
if url.startswith('plugin://'):
|
||||
xbmcplugin.addDirectoryItem(handle=addon_handle,url=url,listitem=liz,isFolder=True)
|
||||
|
||||
elif folder:
|
||||
xbmcplugin.addDirectoryItem(handle=addon_handle,url=u,listitem=liz,isFolder=True)
|
||||
|
||||
elif playable:
|
||||
liz.setProperty('IsPlayable', 'true')
|
||||
xbmcplugin.addDirectoryItem(handle=addon_handle,url=url,listitem=liz,isFolder=False)
|
||||
|
||||
else:
|
||||
xbmcplugin.addDirectoryItem(handle=addon_handle,url=u,listitem=liz,isFolder=False)
|
||||
#----------------------------------------------------------------
|
||||
def Default_Mode():
|
||||
""" internal command ~"""
|
||||
dialog = xbmcgui.Dialog()
|
||||
dialog.ok('MODE ERROR','You\'ve tried to call Add_Dir() without a valid mode, check you\'ve added the mode into the master_modes dictionary')
|
||||
#----------------------------------------------------------------
|
||||
1076
script.module.python.koding.aio/lib/koding/filetools.py
Normal file
1076
script.module.python.koding.aio/lib/koding/filetools.py
Normal file
File diff suppressed because it is too large
Load Diff
796
script.module.python.koding.aio/lib/koding/guitools.py
Normal file
796
script.module.python.koding.aio/lib/koding/guitools.py
Normal file
@@ -0,0 +1,796 @@
|
||||
# -*- 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 sys
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
from systemtools import Last_Error
|
||||
from filetools import Physical_Path
|
||||
dialog = xbmcgui.Dialog()
|
||||
koding_path = Physical_Path("special://home/addons/script.module.python.koding.aio")
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Browse_To_Folder(header='Select the folder you want to use', path = 'special://home'):
|
||||
"""
|
||||
As the title suggests this will bring up a dialog that allows the user to browse to a folder
|
||||
and the path is then returned.
|
||||
|
||||
IMPORTANT: Do not confuse this with the Browse_To_File function
|
||||
|
||||
CODE: Browse_To_Folder(header, path)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
header - As the name suggests this is a string to be used for the header/title
|
||||
of the window. The default is "Select the folder you want to use".
|
||||
|
||||
path - Optionally you can add a default path for the browse start folder.
|
||||
The default start position is the Kodi HOME folder.
|
||||
|
||||
EXAMPLE CODE:
|
||||
folder = koding.Browse_To_Folder(header='Choose a folder you want to use', path='special://home/userdata')
|
||||
dialog.ok('FOLDER DETAILS','Folder path: [COLOR=dodgerblue]%s[/COLOR]'%folder)
|
||||
~"""
|
||||
text = dialog.browse(type=3, heading=header, shares='files', useThumbs=False, treatAsFolder=False, defaultt=path)
|
||||
return text
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Browse_To_File(header='Select the file you want to use', path='special://home/addons/', extension='', browse_in_archives=False):
|
||||
"""
|
||||
This will allow the user to browse to a specific file and return the path.
|
||||
|
||||
IMPORTANT: Do not confuse this with the Browse_To_Folder function
|
||||
|
||||
CODE: koding.Browse_To_File([header, path, extension, browse_in_archives])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
header - As the name suggests this is a string to be used for the header/title
|
||||
of the window. The default is "Select the file you want to use".
|
||||
|
||||
path - Optionally you can add a default path for the browse start folder.
|
||||
The default start position is the Kodi HOME folder.
|
||||
|
||||
extension - Optionally set extensions to filter by, let's say you only wanted
|
||||
zip and txt files to show you would send through '.zip|.txt'
|
||||
|
||||
browse_in_archives - Set to true if you want to be able to browse inside zips and
|
||||
other archive files. By default this is set to False.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('BROWSE TO FILE 1','We will now browse to your addons folder with browse_in_archives set to [COLOR dodgerblue]False[/COLOR]. Try clicking on a zip file if you can find one (check packages folder).')
|
||||
folder = koding.Browse_To_File(header='Choose a file you want to use', path='special://home/addons')
|
||||
dialog.ok('FOLDER DETAILS','File path: [COLOR=dodgerblue]%s[/COLOR]'%folder)
|
||||
dialog.ok('BROWSE TO FILE 2','We will now browse to your addons folder with browse_in_archives set to [COLOR dodgerblue]True[/COLOR]. Try clicking on a zip file if you can find one (check packages folder).')
|
||||
folder = koding.Browse_To_File(header='Choose a file you want to use', path='special://home/addons', browse_in_archives=True)
|
||||
dialog.ok('FOLDER DETAILS','File path: [COLOR=dodgerblue]%s[/COLOR]'%folder)
|
||||
~"""
|
||||
if not path.endswith(os.sep):
|
||||
path += os.sep
|
||||
try:
|
||||
text = dialog.browse(type=1, heading=header, shares='myprograms', mask=extension, useThumbs=False, treatAsFolder=browse_in_archives, defaultt=path)
|
||||
except:
|
||||
text = dialog.browse(type=1, heading=header, s_shares='myprograms', mask=extension, useThumbs=False,
|
||||
treatAsFolder=browse_in_archives, defaultt=path)
|
||||
return text
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Countdown(title='COUNTDOWN STARTED', message='A quick simple countdown example.', update_msg='Please wait, %s seconds remaining.', wait_time=10, allow_cancel=True, cancel_msg='[COLOR=gold]Sorry, this process cannot be cancelled[/COLOR]'):
|
||||
"""
|
||||
Bring up a countdown timer and return true if waited or false if cancelled.
|
||||
|
||||
CODE: Countdown(title, message, update_msg, wait_time, allow_cancel, cancel_msg):
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
title - The header string in the dialog window, the default is:
|
||||
'COUNTDOWN STARTED'
|
||||
|
||||
message - A short line of info which will show on the first line
|
||||
of the dialog window just below the title. Default is:
|
||||
'A quick simple countdown example.'
|
||||
|
||||
update_msg - The message you want to update during the countdown.
|
||||
This must contain a %s which will be replaced by the current amount
|
||||
of seconds that have passed. The default is:
|
||||
'Please wait, %s seconds remaining.'
|
||||
|
||||
wait_time - This is the amount of seconds you want the countdown to
|
||||
run for. The default is 10.
|
||||
|
||||
allow_cancel - By default this is set to true and the user can cancel
|
||||
which will result in False being returned. If this is set to True
|
||||
they will be unable to cancel.
|
||||
|
||||
cancel_msg - If allow_cancel is set to False you can add a custom
|
||||
message when the user tries to cancel. The default string is:
|
||||
'[COLOR=gold]Sorry, this process cannot be cancelled[/COLOR]'
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('COUNTDOWN EXAMPLE', 'Press OK to bring up a countdown timer', '', 'Try cancelling the process.')
|
||||
my_return = koding.Countdown(title='COUNTDOWN EXAMPLE', message='Quick simple countdown message (cancel enabled).', update_msg='%s seconds remaining', wait_time=5)
|
||||
if my_return:
|
||||
dialog.ok('SUCCESS!','Congratulations you actually waited through the countdown timer without cancelling!')
|
||||
else:
|
||||
dialog.ok('BORED MUCH?','What happened, did you get bored waiting?', '', '[COLOR=dodgerblue]Let\'s set off another countdown you CANNOT cancel...[/COLOR]')
|
||||
koding.Countdown(title='COUNTDOWN EXAMPLE', message='Quick simple countdown message (cancel disabled).', update_msg='%s seconds remaining', wait_time=5, allow_cancel=False, cancel_msg='[COLOR=gold]Sorry, this process cannot be cancelled[/COLOR]')
|
||||
~"""
|
||||
dp = xbmcgui.DialogProgress()
|
||||
current = 0
|
||||
increment = 100 / wait_time
|
||||
cancelled = False
|
||||
|
||||
dp.create(title)
|
||||
while current <= wait_time:
|
||||
if (dp.iscanceled()):
|
||||
if allow_cancel:
|
||||
cancelled = True
|
||||
break
|
||||
else:
|
||||
dp.create(title,cancel_msg)
|
||||
|
||||
if current != 0:
|
||||
xbmc.sleep(1000)
|
||||
|
||||
remaining = wait_time - current
|
||||
if remaining == 0:
|
||||
percent = 100
|
||||
else:
|
||||
percent = increment * current
|
||||
|
||||
remaining_display = update_msg % remaining
|
||||
dp.update(percent, message, remaining_display)
|
||||
|
||||
current += 1
|
||||
|
||||
if cancelled == True:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Custom_Dialog(pos='center', dialog='Text', size='700x500', button_width=200, icon='', fanart='',\
|
||||
header='Disclaimer', main_content='Add some text here', buttons=['Decline','Agree'],\
|
||||
header_color='gold', text_color='white', background='000000', transparency=100,\
|
||||
highlight_color='gold', button_color_focused='4e91cf', button_trans_focused=100,\
|
||||
button_color_nonfocused='586381', button_trans_nonfocused=50):
|
||||
"""
|
||||
A fully customisable dialog where you can have as many buttons as you want.
|
||||
Similar behaviour to the standard Kodi yesno dialog but this allows as many buttons
|
||||
as you want, as much text as you want (with a slider) as well as fully configurable
|
||||
sizing and positioning.
|
||||
|
||||
CODE: Custom_Dialog([pos, dialog, size, button_width, header, main_content, buttons,\
|
||||
header_color, text_color, background, transparency, highlight_color, button_color_focused,\
|
||||
button_trans_focused, button_color_nonfocused, button_trans_nonfocused])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
pos - This is the co-ordinates of where on the screen you want the
|
||||
dialog to appear. This needs to be sent through as a string so for
|
||||
example if you want the dialog top left corner to be 20px in and
|
||||
10px down you would use pos='20x10'. By default this is set to 'center'
|
||||
which will center the dialog on the screen.
|
||||
|
||||
dialog - By default this is set to 'Text'. Currently that is the
|
||||
only custom dialog available but there are plans to improve upon this
|
||||
and allow for image and even video dialogs.
|
||||
|
||||
size - Sent through as a string this is the dimensions you want the
|
||||
dialog to be, by default it's set to '700x500' but you can set to any
|
||||
size you want using that same format. Setting to 'fullscreen' will
|
||||
use 1280x720 (fullscreen).
|
||||
|
||||
button_width - This is sent through as an integer and is the width you
|
||||
want your buttons to be. By default this is set to 200 which is quite large
|
||||
but looks quite nice if using only 2 or 3 buttons.
|
||||
|
||||
icon - If sent through this will be shown in the top right corner of your dialog,
|
||||
make sure your first few lines of text aren't too long or they will overlap on top
|
||||
of the image which is 150x150 pixels.
|
||||
|
||||
fanart - If sent through this will be the background image of your custom dialog.
|
||||
Ideal if you want to only show an image, any text sent through will be overlayed
|
||||
on top of this fanart.
|
||||
|
||||
header - Sent through as a string this is the header shown in the dialog.
|
||||
The default is 'Disclaimer'.
|
||||
|
||||
header_color - Set the text colour, by default it's 'gold'
|
||||
|
||||
text_color - Set the text colour, by default it's 'white'
|
||||
|
||||
main_content - This is sent through as a string and is the main message text
|
||||
you want to show in your dialog. When the ability to add videos, images etc.
|
||||
is added there may well be new options added to this param but it will remain
|
||||
backwards compatible.
|
||||
|
||||
buttons - Sent through as a list (tuple) this is a list of all your buttons.
|
||||
Make sure you do not duplicate any names otherwise it will throw off the
|
||||
formatting of the dialog and you'll get false positives with the results.
|
||||
|
||||
background - Optionally set the background colour (hex colour codes required).
|
||||
The default is '000000' (black).
|
||||
|
||||
transparency - Set the percentage of transparency as an integer. By default
|
||||
it's set to 100 which is a solid colour.
|
||||
|
||||
highlight_color - Set the highlighted text colour, by default it's 'gold'
|
||||
|
||||
button_color_focused - Using the same format as background you can set the
|
||||
colour to use for a button when it's focused.
|
||||
|
||||
button_trans_focused - Using the same format as transparency you can set the
|
||||
transparency amount to use on the button when in focus.
|
||||
|
||||
button_color_nonfocused - Using the same format as background you can set the
|
||||
colour to use for buttons when they are not in focus.
|
||||
|
||||
button_trans_nonfocused - Using the same format as transparency you can set the
|
||||
transparency amount to use on the buttons when not in focus.
|
||||
|
||||
EXAMPLE CODE:
|
||||
main_text = 'This is my main text.\n\nYou can add anything you want in here and the slider will allow you to see all the contents.\n\nThis example shows using a blue background colour and a transparency of 90%.\n\nWe have also changed the highlighted_color to yellow.'
|
||||
my_buttons = ['button 1', 'button 2', 'button 3']
|
||||
my_choice = koding.Custom_Dialog(main_content=main_text,pos='center',buttons=my_buttons,background='213749',transparency=90,highlight_color='yellow')
|
||||
dialog.ok('CUSTOM DIALOG 1','You selected option %s'%my_choice,'The value of this is: [COLOR=dodgerblue]%s[/COLOR]'%my_buttons[my_choice])
|
||||
|
||||
main_text = 'This is example 2 with no fancy colours, just a fullscreen and a working scrollbar.\n\nYou\'ll notice there are also a few more buttons on this one.\n\nline 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\nline 11\nline 12\nline 13\nline 14\nline 15\nline 16\nline 17\nline 18\nline 19\nline 20\n\nYou get the idea we\'ll stop there!'
|
||||
my_buttons = ['button 1', 'button 2', 'button 3','button 4', 'button 5', 'button 6','button 7', 'button 8', 'button 9','button 10', 'button 11', 'button 12', 'button 13','button 14', 'button 15', 'button 16','button 17', 'button 18', 'button 19','button 20']
|
||||
my_choice = koding.Custom_Dialog(main_content=main_text,pos='center',size='fullscreen',buttons=my_buttons)
|
||||
dialog.ok('CUSTOM DIALOG 2','You selected option %s'%my_choice,'The value of this is: [COLOR=dodgerblue]%s[/COLOR]'%my_buttons[my_choice])
|
||||
~"""
|
||||
skin_path = os.path.join(koding_path,"resources","skins","Default","720p")
|
||||
ACTION = -1
|
||||
# Convert the transparency percentage to hex
|
||||
transparency = float(transparency) / 100 * 255
|
||||
transparency = hex(int(transparency)).split('x')[1]
|
||||
button_trans_focused = float(button_trans_focused) / 100 * 255
|
||||
button_trans_focused = hex(int(button_trans_focused)).split('x')[1]
|
||||
button_trans_nonfocused = float(button_trans_nonfocused) / 100 * 255
|
||||
button_trans_nonfocused = hex(int(button_trans_nonfocused)).split('x')[1]
|
||||
|
||||
# Work out the dialog dimensions
|
||||
if size == 'fullscreen':
|
||||
dialog_width = '1280'
|
||||
dialog_height = '720'
|
||||
else:
|
||||
dialog_width, dialog_height = size.split('x')
|
||||
|
||||
# Set the background to black image if not set otherwise remove background/transparency
|
||||
if fanart != '' and xbmcvfs.exists(fanart):
|
||||
background = ''
|
||||
transparency = ''
|
||||
else:
|
||||
fanart = 'DialogBack.png'
|
||||
|
||||
button_count = len(buttons)
|
||||
buttons_per_row = (int(dialog_width)-25) / (button_width+25)
|
||||
if buttons_per_row > button_count:
|
||||
buttons_per_row = button_count
|
||||
|
||||
# work out the number of rows, round up if a float
|
||||
button_rows = int(button_count/buttons_per_row) + (button_count % buttons_per_row > 0)
|
||||
|
||||
# Work out the positioning of the dialog
|
||||
if pos == 'center':
|
||||
posx = str( (1280 - int(dialog_width)) / 2)
|
||||
posy = str( (720 - int(dialog_height)) / 2)
|
||||
else:
|
||||
posx, posy = pos.split(',')
|
||||
|
||||
# Work out the text area size
|
||||
text_width = str( int(dialog_width)-80 )
|
||||
text_height = str( (int(dialog_height)-(50*(button_rows+1)))-70 )
|
||||
scroll_pos = str( int(text_width)+32 )
|
||||
button_max = int(dialog_height)-30
|
||||
iconx = str(int(text_width)-150)
|
||||
|
||||
# Work out the button positions
|
||||
if dialog == 'Text':
|
||||
button_spacing = ( int(dialog_width)-(buttons_per_row*button_width) ) / (buttons_per_row+1)
|
||||
buttons_dict = {}
|
||||
counter = 1
|
||||
row = 1
|
||||
# Create a dictionary of button positioning
|
||||
for button in buttons:
|
||||
if counter > buttons_per_row:
|
||||
counter = 1
|
||||
row += 1
|
||||
# If starting a new line reset the values
|
||||
if counter > buttons_per_row or counter == 1:
|
||||
current_pos = button_spacing
|
||||
counter += 1
|
||||
else:
|
||||
current_pos = current_pos+button_width+button_spacing
|
||||
counter += 1
|
||||
|
||||
buttons_dict[button] = [str(current_pos),row]
|
||||
|
||||
# Set the dialog template name and new temporary "live" XML
|
||||
dialog_type = dialog.capitalize()+'.xml'
|
||||
dialog_new = 'temp.xml'
|
||||
dialog_path = os.path.join(skin_path,dialog_type)
|
||||
temp_path = os.path.join(skin_path,dialog_new)
|
||||
|
||||
button_num = 100
|
||||
counter = 1
|
||||
buttons_code = ''
|
||||
for button in buttons:
|
||||
if buttons_dict[button][1] == 1:
|
||||
onup = 99
|
||||
else:
|
||||
onup = button_num-buttons_per_row
|
||||
|
||||
# If button is on the last row we set down to scrollbar
|
||||
if buttons_dict[button][1] == button_rows:
|
||||
ondown = 99
|
||||
# Otherwise set down to the item on row below
|
||||
elif buttons_dict[button][1] != button_rows:
|
||||
ondown = button_num+buttons_per_row
|
||||
|
||||
# Set the vertical position (y) of the buttons
|
||||
button_y = str( int(text_height)+(buttons_dict[button][1]*50)+40 )
|
||||
if ( int(text_height) < 200 ) or ( int(button_y) > button_max ):
|
||||
if size != 'fullscreen':
|
||||
xbmcgui.Dialog().ok('WE NEED A BIGGER WINDOW!','The amount of buttons sent through do not fit in this window. Either make the button width smaller or make a bigger window')
|
||||
else:
|
||||
xbmcgui.Dialog().ok('SMALLER BUTTONS NEEDED!','The amount of buttons sent through do not fit in this window. Either send through less buttons or decrease their width using the button_width param.')
|
||||
return
|
||||
button_x = str( buttons_dict[button][0] )
|
||||
|
||||
buttons_code += '\
|
||||
<control type="button" id="%s">\n\
|
||||
<posx>%s</posx>\n\
|
||||
<posy>%s</posy>\n\
|
||||
<width>%s</width>\n\
|
||||
<height>40</height>\n\
|
||||
<label>%s</label>\n\
|
||||
<texturefocus colordiffuse="%s%s">DialogBack.png</texturefocus>\n\
|
||||
<texturenofocus colordiffuse="%s%s">DialogBack.png</texturenofocus>\n\
|
||||
<font>font12_title</font>\n\
|
||||
<textcolor>%s</textcolor>\n\
|
||||
<focusedcolor>%s</focusedcolor>\n\
|
||||
<align>center</align>\n\
|
||||
<onleft>%s</onleft>\n\
|
||||
<onright>%s</onright>\n\
|
||||
<onup>%s</onup>\n\
|
||||
<ondown>%s</ondown>\n\
|
||||
</control>\n' % (button_num, button_x, button_y, button_width, buttons[counter-1],\
|
||||
button_trans_focused, button_color_focused, button_trans_nonfocused,\
|
||||
button_color_nonfocused, text_color, highlight_color, button_num-1,\
|
||||
button_num+1, onup, ondown)
|
||||
button_num += 1
|
||||
counter += 1
|
||||
|
||||
# Grab contents of the template and replace with our new values
|
||||
with open(dialog_path, 'r') as content_file:
|
||||
content = content_file.read()
|
||||
content = content.replace('dialog_width',dialog_width)\
|
||||
.replace('dialog_height',dialog_height)\
|
||||
.replace('text_width',text_width)\
|
||||
.replace('text_height',text_height)\
|
||||
.replace('pos_x',posx)\
|
||||
.replace('pos_y',posy)\
|
||||
.replace('PK_Icon',icon)\
|
||||
.replace('PK_I_X',iconx)\
|
||||
.replace('PK_Fanart',fanart)\
|
||||
.replace('PK_Transparency',transparency)\
|
||||
.replace('PK_Color',background)\
|
||||
.replace('PK_Text_Color',text_color)\
|
||||
.replace('PK_Header_Color',header_color)\
|
||||
.replace('<!-- buttons -->',buttons_code)
|
||||
# Create the new temp "live" XML
|
||||
myfile = open(temp_path,'w')
|
||||
myfile.write(content)
|
||||
myfile.close()
|
||||
|
||||
d=MyDisclaimer(dialog_new,koding_path,header=header,main_content=main_content)
|
||||
d.doModal()
|
||||
ACTION = d.ACTION
|
||||
del d
|
||||
return ACTION
|
||||
|
||||
class MyDisclaimer(xbmcgui.WindowXMLDialog):
|
||||
def __init__(self,*args,**kwargs):
|
||||
self.header=kwargs['header']
|
||||
self.main_content=kwargs['main_content']
|
||||
self.WINDOW=xbmcgui.Window( 10000 )
|
||||
self.WINDOW.setProperty( 'PK_Header' , self.header )
|
||||
self.WINDOW.setProperty( 'PK_Main_Text' , self.main_content )
|
||||
self.ACTION=-1
|
||||
def onClick( self, controlID ):
|
||||
if controlID>=100:
|
||||
self.ACTION=(controlID-100)
|
||||
self.close()
|
||||
elif controlID==12:
|
||||
self.close()
|
||||
def onAction( self, action ):
|
||||
if action in [ 5, 6, 7, 9, 10, 92, 117 ] or action.getButtonCode() in [275,257,261]:
|
||||
self.close()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Keyboard(heading='',default='',hidden=False,return_false=False,autoclose=False,kb_type='alphanum'):
|
||||
"""
|
||||
Show an on-screen keyboard and return the string
|
||||
|
||||
CODE: koding.Keyboard([default, heading, hidden, return_false, autoclose, kb_type])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
heading - Optionally enter a heading for the text box.
|
||||
|
||||
default - This is optional, if set this will act as the default text shown in the text box
|
||||
|
||||
hidden - Boolean, if set to True the text will appear as hidden (starred out)
|
||||
|
||||
return_false - By default this is set to False and when escaping out of the keyboard
|
||||
the default text is returned (or an empty string if not set). If set to True then
|
||||
you'll receive a return of False.
|
||||
|
||||
autoclose - By default this is set to False but if you want the keyboard to auto-close
|
||||
after a period of time you can send through an integer. The value sent through needs to
|
||||
be milliseconds, so for example if you want it to close after 3 seconds you'd send through
|
||||
3000. The autoclose function only works with standard alphanumeric keyboard types.
|
||||
|
||||
kb_type - This is the type of keyboard you want to show, by default it's set to alphanum.
|
||||
A list of available values are listed below:
|
||||
|
||||
'alphanum' - A standard on-screen keyboard containing alphanumeric characters.
|
||||
'numeric' - An on-screen numerical pad.
|
||||
'date' - An on-screen numerical pad formatted only for a date.
|
||||
'time' - An on-screen numerical pad formatted only for a time.
|
||||
'ipaddress' - An on-screen numerical pad formatted only for an IP Address.
|
||||
'password' - A standard keyboard but returns value as md5 hash. When typing
|
||||
the text is starred out, once you've entered the password you'll get another
|
||||
keyboard pop up asking you to verify. If the 2 match then your md5 has is returned.
|
||||
|
||||
|
||||
EXAMPLE CODE:
|
||||
mytext = koding.Keyboard(heading='Type in the text you want returned',default='test text')
|
||||
dialog.ok('TEXT RETURNED','You typed in:', '', '[COLOR=dodgerblue]%s[/COLOR]'%mytext)
|
||||
dialog.ok('AUTOCLOSE ENABLED','This following example we\'ve set the autoclose to 3000. That\'s milliseconds which converts to 3 seconds.')
|
||||
mytext = koding.Keyboard(heading='Type in the text you want returned',default='this will close in 3s',autoclose=3000)
|
||||
dialog.ok('TEXT RETURNED','You typed in:', '', '[COLOR=dodgerblue]%s[/COLOR]'%mytext)
|
||||
mytext = koding.Keyboard(heading='Enter a number',kb_type='numeric')
|
||||
dialog.ok('NUMBER RETURNED','You typed in:', '', '[COLOR=dodgerblue]%s[/COLOR]'%mytext)
|
||||
dialog.ok('RETURN FALSE ENABLED','All of the following examples have "return_false" enabled. This means if you escape out of the keyboard the return will be False.')
|
||||
mytext = koding.Keyboard(heading='Enter a date',return_false=True,kb_type='date')
|
||||
dialog.ok('DATE RETURNED','You typed in:', '', '[COLOR=dodgerblue]%s[/COLOR]'%mytext)
|
||||
mytext = koding.Keyboard(heading='Enter a time',return_false=True,kb_type='time')
|
||||
dialog.ok('TIME RETURNED','You typed in:', '', '[COLOR=dodgerblue]%s[/COLOR]'%mytext)
|
||||
mytext = koding.Keyboard(heading='IP Address',return_false=True,kb_type='ipaddress',autoclose=5)
|
||||
dialog.ok('IP RETURNED','You typed in:', '', '[COLOR=dodgerblue]%s[/COLOR]'%mytext)
|
||||
mytext = koding.Keyboard(heading='Password',kb_type='password')
|
||||
dialog.ok('MD5 RETURN','The md5 for this password is:', '', '[COLOR=dodgerblue]%s[/COLOR]'%mytext)
|
||||
~"""
|
||||
from vartools import Decode_String
|
||||
kb_type = eval( 'xbmcgui.INPUT_%s'%kb_type.upper() )
|
||||
if hidden:
|
||||
hidden = eval( 'xbmcgui.%s_HIDE_INPUT'%kb_type.upper() )
|
||||
keyboard = dialog.input(heading,default,kb_type,hidden,autoclose)
|
||||
|
||||
if keyboard != '':
|
||||
return keyboard
|
||||
|
||||
elif not return_false:
|
||||
return Decode_String(default)
|
||||
|
||||
else:
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Notify(title, message, duration=2000, icon='special://home/addons/script.module.python.koding.aio/resources/update.png'):
|
||||
"""
|
||||
Show a short notification for x amount of seconds
|
||||
|
||||
CODE: koding.Notify(title, message, [duration, icon])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) title - A short title to show on top line of notification
|
||||
|
||||
(*) message - A short message to show on the bottom line of notification
|
||||
|
||||
duration - An integer in milliseconds, the default to show the notification for is 2000
|
||||
|
||||
icon - The icon to show in notification bar, default is the update icon from this module.
|
||||
|
||||
EXAMPLE CODE:
|
||||
koding.Notify(title='TEST NOTIFICATION', message='This is a quick 5 second test', duration=5000)
|
||||
~"""
|
||||
xbmc.executebuiltin('XBMC.Notification(%s, %s, %s, %s)' % (title , message , duration, icon))
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def OK_Dialog(title,message):
|
||||
"""
|
||||
This will bring up a short text message in a dialog.ok window.
|
||||
|
||||
CODE: OK_Dialog(title,message)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) title - This is title which appears in the header of the window.
|
||||
|
||||
(*) message - This is the main text you want to appear.
|
||||
|
||||
EXAMPLE CODE:
|
||||
koding.OK_Dialog(title='TEST DIALOG',message='This is a test dialog ok box. Click OK to quit.')
|
||||
~"""
|
||||
dialog.ok(title,message)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Select_Dialog(title,options,key=True):
|
||||
"""
|
||||
This will bring up a selection of options to choose from. The options are
|
||||
sent through as a list and only one can be selected - this is not a multi-select dialog.
|
||||
|
||||
CODE: Select_Dialog(title,options,[key])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) title - This is title which appears in the header of the window.
|
||||
|
||||
(*) options - This is a list of the options you want the user to be able to choose from.
|
||||
|
||||
key - By default this is set to True so you'll get a return of the item number. For example
|
||||
if the user picks "option 2" and that is the second item in the list you'll receive a return of
|
||||
1 (0 would be the first item in list and 1 is the second). If set to False you'll recieve a return
|
||||
of the actual string associated with that key, in this example the return would be "option 2".
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_options = ['Option 1','Option 2','Option 3','Option 4','Option 5']
|
||||
mychoice = koding.Select_Dialog(title='TEST DIALOG',options=my_options,key=False)
|
||||
koding.OK_Dialog(title='SELECTED ITEM',message='You selected: [COLOR=dodgerblue]%s[/COLOR]\nNow let\'s try again - this time we will return a key...'%mychoice)
|
||||
mychoice = koding.Select_Dialog(title='TEST DIALOG',options=my_options,key=True)
|
||||
koding.OK_Dialog(title='SELECTED ITEM',message='The item you selected was position number [COLOR=dodgerblue]%s[/COLOR] in the list'%mychoice)
|
||||
~"""
|
||||
mychoice = dialog.select(title,options)
|
||||
if key:
|
||||
return mychoice
|
||||
else:
|
||||
return options[mychoice]
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Show_Busy(status=True, sleep=0):
|
||||
"""
|
||||
This will show/hide a "working" symbol.
|
||||
|
||||
CODE: Show_Busy([status, sleep])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
status - This optional, by default it's True which means the "working"
|
||||
symbol appears. False will disable.
|
||||
|
||||
sleep - If set the busy symbol will appear for <sleep> amount of
|
||||
milliseconds and then disappear.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('BUSY SYMBOL','Press OK to show a busy dialog which restricts any user interaction. We have added a sleep of 5 seconds at which point it will disable.')
|
||||
koding.Show_Busy(sleep=5000)
|
||||
dialog.ok('BUSY SYMBOL','We will now do the same but with slightly different code')
|
||||
koding.Show_Busy(status=True)
|
||||
xbmc.sleep(5000)
|
||||
koding.Show_Busy(status=False)
|
||||
~"""
|
||||
if status:
|
||||
xbmc.executebuiltin("ActivateWindow(busydialog)")
|
||||
if sleep:
|
||||
xbmc.sleep(sleep)
|
||||
xbmc.executebuiltin("Dialog.Close(busydialog)")
|
||||
else:
|
||||
xbmc.executebuiltin("Dialog.Close(busydialog)")
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Text_Box(header, message):
|
||||
"""
|
||||
This will allow you to open a blank window and fill it with some text.
|
||||
|
||||
CODE: koding.Text_Box(header, message)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) header - As the name suggests this is a string to be used for the header/title of the window
|
||||
|
||||
(*) message - Yes you've probably already gussed it, this is the main message text
|
||||
|
||||
|
||||
EXAMPLE CODE:
|
||||
koding.Text_Box('TEST HEADER','Just some random text... Use kodi tags for new lines, colours etc.')
|
||||
~"""
|
||||
xbmc.executebuiltin("ActivateWindow(10147)")
|
||||
controller = xbmcgui.Window(10147)
|
||||
xbmc.sleep(500)
|
||||
controller.getControl(1).setLabel(header)
|
||||
controller.getControl(5).setText(message)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Reset_Percent(property='update_percent_',window_id=10000):
|
||||
"""
|
||||
If using the Update_Progress function for setting percentages in skinning then this
|
||||
will allow you to reset all the percent properties (1-100)
|
||||
|
||||
CODE: Reset_Percent([property,window_id])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
property - the property name you want reset, this will reset all properties starting
|
||||
with this string from 1-100. For example if you use the default 'update_percent_' this
|
||||
will loop through and reset update_percent_1, update_percent_2 etc. all the way through
|
||||
to update_percent_100.
|
||||
|
||||
window_id - By default this is set to 10000 but you can send any id through you want.
|
||||
|
||||
kwargs - Send through any other params and the respective property will be set.colours etc.')
|
||||
~"""
|
||||
counter = 0
|
||||
while counter <= 100:
|
||||
xbmcgui.Window(10000).clearProperty('update_percent_%s'%counter)
|
||||
counter +=1
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Update_Progress(total_items,current_item,**kwargs):
|
||||
"""
|
||||
This function is designed for skinners but can be used for general Python too. It will
|
||||
work out the current percentage of items that have been processed and update the
|
||||
"update_percent" property accordingly (1-100). You can also send through any properties
|
||||
you want updated and it will loop through updating them with the relevant values.
|
||||
|
||||
To send through properties just send through the property name as the param and assign to a value.
|
||||
Example: Update_Progress( total_items=100,current_item=56, {"myproperty1":"test1","myproperty2":"test2"} )
|
||||
|
||||
|
||||
CODE: Update_Progress(total_items,current_item,[kwargs])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) total_items - Total amount of items in your list you're processing
|
||||
|
||||
(*) current_item - Current item number that's been processed.
|
||||
|
||||
kwargs - Send through any other params and the respective property will be set.colours etc.
|
||||
~"""
|
||||
Reset_Percent()
|
||||
for item in kwargs:
|
||||
if item.endswith('color'):
|
||||
value = '0xFF'+kwargs[item]
|
||||
else:
|
||||
value = kwargs[item]
|
||||
if value == 'false' or value == '' and not item.endswith('color'):
|
||||
xbmcgui.Window(10000).clearProperty(item)
|
||||
elif value:
|
||||
xbmcgui.Window(10000).setProperty(item, value)
|
||||
percent = 100*(current_item/(total_items*1.0))
|
||||
newpercent=int(percent)
|
||||
if (newpercent % 1 == 0) and (newpercent <=100):
|
||||
xbmcgui.Window(10000).setProperty('update_percent',str(newpercent))
|
||||
xbmcgui.Window(10000).setProperty('update_percent_%s'%newpercent,'true')
|
||||
if newpercent == 100:
|
||||
xbmc.executebuiltin('Action(firstpage)')
|
||||
#-----------------------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Update_Screen(disable_quit=False, auto_close=True):
|
||||
"""
|
||||
This will create a full screen overlay showing progress of updates. You'll need to
|
||||
use this in conjunction with the Update_Progress function.
|
||||
|
||||
CODE: Update_Screen([disable_quit, auto_close))
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
disable_quit - By default this is set to False and pressing the parent directory
|
||||
button (generally esc) will allow you to close the window. Setting this to True
|
||||
will mean it's not possible to close the window manually.
|
||||
|
||||
auto_close - By default this is set to true and when the percentage hits 100
|
||||
the window will close. If you intend on then sending through some more commands
|
||||
you might want to consider leaving this window open in which case you'd set this
|
||||
to false. Bare in mind if you go this route the window will stay active until
|
||||
you send through the kill command which is: xbmc.executebuiltin('Action(firstpage)')
|
||||
|
||||
EXAMPLE CODE:
|
||||
mykwargs = {
|
||||
"update_header" : "Downloading latest updates",\
|
||||
"update_main_text" : "Your device is now downloading all the latest updates.\nThis shouldn\'t take too long, "\
|
||||
"depending on your internet speed this could take anything from 2 to 10 minutes.\n\n"\
|
||||
"Once downloaded the system will start to install the updates.",\
|
||||
"update_bar_color" : "4e91cf",\
|
||||
"update_icon" : "special://home/addons/script.module.python.koding.aio/resources/skins/Default/media/update.png",\
|
||||
"update_spinner" : "true"}
|
||||
Update_Screen()
|
||||
counter = 1
|
||||
while counter <= 60:
|
||||
xbmc.sleep(300)
|
||||
Update_Progress(total_items=60,current_item=counter,**mykwargs)
|
||||
if counter == 30:
|
||||
mykwargs = {
|
||||
"update_header" : "Halfway there!",\
|
||||
"update_main_text" : "We just updated the properties to show how you can change things on the fly "\
|
||||
"simply by sending through some different properties. Both the icon and the "\
|
||||
"background images you see here are being pulled from online.",\
|
||||
"update_header_color" : "4e91cf",\
|
||||
"update_percent_color" : "4e91cf",\
|
||||
"update_bar_color" : "4e91cf",\
|
||||
"update_background" : "http://www.planwallpaper.com/static/images/518164-backgrounds.jpg",\
|
||||
"update_icon" : "http://totalrevolution.tv/img/tr_small_black_bg.jpg",\
|
||||
"update_spinner" : "false"}
|
||||
counter += 1
|
||||
~"""
|
||||
import threading
|
||||
update_screen_thread = threading.Thread(target=Show_Update_Screen, args=[disable_quit, auto_close])
|
||||
update_screen_thread.start()
|
||||
xbmc.sleep(2000)
|
||||
|
||||
def Show_Update_Screen(disable_quit=False,auto_close=True):
|
||||
xbmcgui.Window(10000).clearProperty('update_icon')
|
||||
xbmcgui.Window(10000).clearProperty('update_percent')
|
||||
xbmcgui.Window(10000).clearProperty('update_spinner')
|
||||
xbmcgui.Window(10000).clearProperty('update_header')
|
||||
xbmcgui.Window(10000).clearProperty('update_main_text')
|
||||
xbmcgui.Window(10000).setProperty('update_background','whitebg.jpg')
|
||||
xbmcgui.Window(10000).setProperty('update_percent_color','0xFF000000')
|
||||
xbmcgui.Window(10000).setProperty('update_bar_color','0xFF000000')
|
||||
xbmcgui.Window(10000).setProperty('update_main_color','0xFF000000')
|
||||
xbmcgui.Window(10000).setProperty('update_header_color','0xFF000000')
|
||||
# Set a property so we can determine if update screen is active
|
||||
xbmcgui.Window(10000).setProperty('update_screen','active')
|
||||
d=MyUpdateScreen('Loading.xml',koding_path,disable_quit=disable_quit,auto_close=auto_close)
|
||||
d.doModal()
|
||||
del d
|
||||
xbmcgui.Window(10000).clearProperty('update_screen')
|
||||
|
||||
class MyUpdateScreen(xbmcgui.WindowXMLDialog):
|
||||
def __init__(self,*args,**kwargs):
|
||||
self.disable_quit=kwargs['disable_quit']
|
||||
self.auto_close=kwargs['auto_close']
|
||||
self.WINDOW=xbmcgui.Window( 10000 )
|
||||
def onAction( self, action ):
|
||||
if action in [10,7]:
|
||||
if self.disable_quit:
|
||||
xbmc.log("ESC and HOME Disabled",2)
|
||||
else:
|
||||
self.close()
|
||||
if action==159 and self.auto_close:
|
||||
self.close()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def YesNo_Dialog(title,message,yes=None,no=None):
|
||||
"""
|
||||
This will bring up a short text message in a dialog.yesno window. This will
|
||||
return True or False
|
||||
|
||||
CODE: YesNo_Dialog(title,message,[yeslabel,nolabel])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) title - This is title which appears in the header of the window.
|
||||
|
||||
(*) message - This is the main text you want to appear.
|
||||
|
||||
yes - Optionally change the default "YES" to a custom string
|
||||
|
||||
no - Optionally change the default "NO" to a custom string
|
||||
|
||||
EXAMPLE CODE:
|
||||
mychoice = koding.YesNo_Dialog(title='TEST DIALOG',message='This is a yes/no dialog with custom labels.\nDo you want to see an example of a standard yes/no.',yes='Go on then',no='Nooooo!')
|
||||
if mychoice:
|
||||
koding.YesNo_Dialog(title='STANDARD DIALOG',message='This is an example of a standard one without sending custom yes/no params through.')
|
||||
~"""
|
||||
choice = dialog.yesno(title,message,yeslabel=yes,nolabel=no)
|
||||
return choice
|
||||
#----------------------------------------------------------------
|
||||
140
script.module.python.koding.aio/lib/koding/router.py
Normal file
140
script.module.python.koding.aio/lib/koding/router.py
Normal file
@@ -0,0 +1,140 @@
|
||||
# -*- 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 directory
|
||||
import tutorials
|
||||
import video
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
HOME = xbmc.translatePath('special://home')
|
||||
master_modes = {
|
||||
# Required for certain koding functions to work
|
||||
"play_video": {'function': video.Play_Video, 'args': ["url"]},
|
||||
"show_tutorial": {'function': tutorials.Show_Tutorial, 'args': ["url"]},
|
||||
"tutorials": {'function': tutorials.Grab_Tutorials, 'args': []},
|
||||
}
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def route(mode, args=[]):
|
||||
"""
|
||||
Use this to set a function in your master_modes dictionary.
|
||||
This is to be used for Add_Dir() items, see the example below.
|
||||
|
||||
CODE: route(mode, [args])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) mode - This must be set, it needs to be a custom string.
|
||||
This is the string you'd use in your Add_Dir command to call
|
||||
the function.
|
||||
|
||||
args - This is optional but if the function you're calling
|
||||
requires extra paramaters you can add them in here. Just add them
|
||||
as a list of strings. Example: args=['name','artwork','description']
|
||||
|
||||
BELOW IS AN EXAMPLE OF HOW TO CALL THE CODE IN YOUR MAIN ADDON PY FILE:
|
||||
|
||||
@route(mode="test", args=["name","description"])
|
||||
def Test_Function(name,description):
|
||||
dialog.ok('This is a test function', name, description')
|
||||
|
||||
koding.Add_Dir(name='Test Dialog', url={"name":"My Test Function", "description" : "Its ALIVE!!!"}, mode='test')
|
||||
koding.run()
|
||||
~"""
|
||||
if mode not in master_modes:
|
||||
|
||||
def _route(function):
|
||||
master_modes[mode] = {
|
||||
'function': function,
|
||||
'args': args
|
||||
}
|
||||
return function
|
||||
return _route
|
||||
else:
|
||||
dialog.ok('DUPLICATE MODE',
|
||||
'The following mode already exists:',
|
||||
'[COLOR=dodgerblue]%s[/COLOR]' % mode)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Run(default="main"):
|
||||
"""
|
||||
This needs to be called at the bottom of your code in the main default.py
|
||||
|
||||
This checks the modes called in Add_Dir and does all the clever stuff
|
||||
in the background which assigns those modes to functions and sends
|
||||
through the various params.
|
||||
|
||||
Just after this command you need to make
|
||||
sure you set the endOfDirectory (as shown below).
|
||||
|
||||
|
||||
CODE: run([default])
|
||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
default - This is the default mode you want the add-on to open
|
||||
into, it's set as "main" by default. If you have a different mode
|
||||
name you want to open into just edit accordingly.
|
||||
~"""
|
||||
import urllib
|
||||
import urlparse
|
||||
import sys
|
||||
from __init__ import DEBUG
|
||||
from guitools import Text_Box
|
||||
from systemtools import Last_Error
|
||||
|
||||
params = dict(urlparse.parse_qsl(sys.argv[2].replace('?', '')))
|
||||
mode = params.get("mode", default)
|
||||
if mode in master_modes:
|
||||
evaled_args = []
|
||||
|
||||
# Grab the url and split up into a dictionary of args
|
||||
try:
|
||||
main_url = params["url"]
|
||||
# Convert back from special to physical path - useful for community shares
|
||||
if urllib.unquote_plus("special://home/") in main_url:
|
||||
main_url = main_url.replace('special://home/',HOME)
|
||||
except:
|
||||
main_url = ''
|
||||
try:
|
||||
my_args = eval(main_url)
|
||||
except:
|
||||
my_args = {"url":main_url}
|
||||
|
||||
for arg in master_modes[mode]["args"]:
|
||||
try:
|
||||
evaled_args.append(my_args[arg])
|
||||
except:
|
||||
if DEBUG == 'true':
|
||||
dialog.ok('ERROR IN CODE','Your Add_Dir function is expecting the [COLOR=gold][B]%s[/B][/COLOR] paramater to be sent through. This does not exist, please check your Add_Dir function.'%arg)
|
||||
xbmc.log(Last_Error(),2)
|
||||
return
|
||||
try:
|
||||
master_modes[mode]["function"](*evaled_args)
|
||||
except:
|
||||
if DEBUG == 'true':
|
||||
Text_Box('ERROR IN CODE', Last_Error())
|
||||
xbmc.log(Last_Error(),2)
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
dialog.ok('MODE DOES NOT EXIST',
|
||||
'The following mode does not exist in your\
|
||||
master_modes dictionary:',
|
||||
'[COLOR=dodgerblue]%s[/COLOR]' % mode)
|
||||
908
script.module.python.koding.aio/lib/koding/systemtools.py
Normal file
908
script.module.python.koding.aio/lib/koding/systemtools.py
Normal file
@@ -0,0 +1,908 @@
|
||||
# -*- 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
|
||||
try:
|
||||
from vartools import Data_Type
|
||||
except:
|
||||
pass
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Cleanup_Textures(frequency=14,use_count=10):
|
||||
"""
|
||||
This will check for any cached artwork and wipe if it's not been accessed more than 10 times in the past x amount of days.
|
||||
|
||||
CODE: Cleanup_Textures([frequency, use_count])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
frequency - This is an optional integer, be default it checks for any
|
||||
images not accessed in 14 days but you can use any amount of days here.
|
||||
|
||||
use_count - This is an optional integer, be default it checks for any
|
||||
images not accessed more than 10 times. If you want to be more ruthless
|
||||
and remove all images not accessed in the past x amount of days then set this very high.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('Clean Textures','We are going to clear any old cached images not accessed at least 10 times in the past 5 days')
|
||||
koding.Cleanup_Textures(frequency=5)
|
||||
~"""
|
||||
try: from sqlite3 import dbapi2 as database
|
||||
except: from pysqlite2 import dbapi2 as database
|
||||
from filetools import DB_Path_Check
|
||||
|
||||
db = DB_Path_Check('Textures')
|
||||
xbmc.log('### DB_PATH: %s' % db)
|
||||
conn = database.connect(db, timeout = 10, detect_types=database.PARSE_DECLTYPES, check_same_thread = False)
|
||||
conn.row_factory = database.Row
|
||||
c = conn.cursor()
|
||||
|
||||
# Set paramaters to check in db, cull = the datetime (we've set it to 14 days) and useCount is the amount of times the file has been accessed
|
||||
cull = datetime.datetime.today() - datetime.timedelta(days = frequency)
|
||||
|
||||
# Create an array to store paths for images and ids for database
|
||||
ids = []
|
||||
images = []
|
||||
|
||||
c.execute("SELECT idtexture FROM sizes WHERE usecount < ? AND lastusetime < ?", (use_count, str(cull)))
|
||||
|
||||
for row in c:
|
||||
ids.append(row["idtexture"])
|
||||
|
||||
for id in ids:
|
||||
c.execute("SELECT cachedurl FROM texture WHERE id = ?", (id,))
|
||||
for row in c:
|
||||
images.append(row["cachedurl"])
|
||||
|
||||
|
||||
# Clean up database
|
||||
for id in ids:
|
||||
c.execute("DELETE FROM sizes WHERE idtexture = ?", (id,))
|
||||
c.execute("DELETE FROM texture WHERE id = ?", (id,))
|
||||
|
||||
c.execute("VACUUM")
|
||||
conn.commit()
|
||||
c.close()
|
||||
|
||||
xbmc.log("### Automatic Cache Removal: %d Old Textures removed" % len(images))
|
||||
|
||||
# Delete files
|
||||
thumbfolder = 'special://home/userdata/Thumbnails'
|
||||
for image in images:
|
||||
path = os.path.join(thumbfolder, image)
|
||||
try:
|
||||
xbmcvfs.delete(path)
|
||||
except:
|
||||
xbmc.log(Last_Error())
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Current_Profile():
|
||||
"""
|
||||
This will return the current running profile, it's only one line of code but this is for my benefit as much as
|
||||
anyone else's. I use this function quite a lot and keep forgetting the code so figured it would be easier to
|
||||
just write a simple function for it :)
|
||||
|
||||
CODE: Current_Profile()
|
||||
|
||||
EXAMPLE CODE:
|
||||
profile = koding.Current_Profile()
|
||||
dialog.ok('CURRENT PROFILE','Your current running profile is:','[COLOR=dodgerblue]%s[/COLOR]' % profile)
|
||||
~"""
|
||||
|
||||
return xbmc.getInfoLabel('System.ProfileName')
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Force_Close():
|
||||
"""
|
||||
Force close Kodi, should only be used in extreme circumstances.
|
||||
|
||||
CODE: Force_Close()
|
||||
|
||||
EXAMPLE CODE:
|
||||
if dialog.yesno('FORCE CLOSE','Are you sure you want to forcably close Kodi? This could potentially cause corruption if system tasks are taking place in background.'):
|
||||
koding.Force_Close()
|
||||
~"""
|
||||
os._exit(1)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Get_ID(setid=False):
|
||||
"""
|
||||
A simple function to set user id and group id to the current running App
|
||||
for system commands. For example if you're using the subprocess command
|
||||
you could send through the preexec_fn paramater as koding.Get_ID(setid=True).
|
||||
This function will also return the uid and gid in form of a dictionary.
|
||||
|
||||
CODE: Get_ID([setid])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) setid - By default this is set to False but if set to True it
|
||||
will set the ids (to be used for subprocess commands)
|
||||
|
||||
EXAMPLE CODE:
|
||||
ids = Get_ID(setid=False)
|
||||
if ids:
|
||||
uid = ids['uid']
|
||||
gid = ids['gid']
|
||||
dialog.ok('USER & GROUP ID','User ID: %s'%uid, 'Group ID: %s'%gid)
|
||||
else:
|
||||
dialog.ok('USER & GROUP ID','This function is not applicable to your system. We\'ve been sent back a return of False to indicate this function does not exist on your os.')
|
||||
~"""
|
||||
try:
|
||||
uid = os.getuid()
|
||||
gid = os.getgid()
|
||||
if setid:
|
||||
os.setgid(uid)
|
||||
os.setuid(gid)
|
||||
if not setid:
|
||||
return {"uid":uid,"gid":gid}
|
||||
except:
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
def Get_Mac(protocol = 'eth'):
|
||||
cont = False
|
||||
vpn_check = False
|
||||
counter = 0
|
||||
mac = ''
|
||||
while len(mac)!=17 and counter < 5:
|
||||
if sys.platform == 'win32':
|
||||
for line in os.popen("ipconfig /all"):
|
||||
if protocol == 'wifi':
|
||||
if line.startswith('Wireless LAN adapter Wi'):
|
||||
cont = True
|
||||
if line.lstrip().startswith('Physical Address') and cont:
|
||||
mac = line.split(':')[1].strip().replace('-',':').replace(' ','')
|
||||
break
|
||||
else:
|
||||
if line.lstrip().startswith('Description'):
|
||||
if not 'VPN' in line:
|
||||
vpn_check = True
|
||||
if line.lstrip().startswith('Physical Address') and vpn_check:
|
||||
mac = line.split(':')[1].strip().replace('-',':').replace(' ','')
|
||||
vpn_check = False
|
||||
break
|
||||
|
||||
elif sys.platform == 'darwin':
|
||||
if protocol == 'wifi':
|
||||
for line in os.popen("ifconfig en0 | grep ether"):
|
||||
if line.lstrip().startswith('ether'):
|
||||
mac = line.split('ether')[1].strip().replace('-',':').replace(' ','')
|
||||
break
|
||||
else:
|
||||
for line in os.popen("ifconfig en1 | grep ether"):
|
||||
if line.lstrip().startswith('ether'):
|
||||
mac = line.split('ether')[1].strip().replace('-',':').replace(' ','')
|
||||
break
|
||||
|
||||
elif xbmc.getCondVisibility('System.Platform.Android'):
|
||||
try:
|
||||
if protocol == 'wifi':
|
||||
readfile = open('/sys/class/net/wlan0/address', mode='r')
|
||||
if protocol != 'wifi':
|
||||
readfile = open('/sys/class/net/eth0/address', mode='r')
|
||||
mac = readfile.read()
|
||||
readfile.close()
|
||||
mac = mac.strip()
|
||||
mac = mac.replace(' ','')
|
||||
mac = mac[:17]
|
||||
except:
|
||||
mac = ''
|
||||
|
||||
else:
|
||||
mac = ''
|
||||
if protocol == 'wifi':
|
||||
for line in os.popen("/sbin/ifconfig"):
|
||||
if line.find('wlan0') > -1:
|
||||
mac = line.split()[4].strip()
|
||||
break
|
||||
elif line.startswith('en'):
|
||||
if 'Ethernet'in line and 'HWaddr' in line:
|
||||
mac = line.split('HWaddr')[1].strip()
|
||||
break
|
||||
else:
|
||||
for line in os.popen("/sbin/ifconfig"):
|
||||
if line.find('eth0') > -1:
|
||||
mac = line.split()[4].strip()
|
||||
break
|
||||
elif line.startswith('wl'):
|
||||
if 'Ethernet'in line and 'HWaddr' in line:
|
||||
mac = line.split('HWaddr')[1].strip()
|
||||
break
|
||||
counter += 1
|
||||
xbmc.log('attempt no.%s %s mac: %s'%(counter,protocol,mac),2)
|
||||
if len(mac) != 17:
|
||||
counter = 0
|
||||
while counter < 5 and len(mac) != 17:
|
||||
mac = xbmc.getInfoLabel('Network.MacAddress')
|
||||
xbmc.log('MAC (backup): %s'%mac,2)
|
||||
xbmc.sleep(100)
|
||||
counter += 1
|
||||
if len(mac) != 17:
|
||||
return 'Unknown'
|
||||
else:
|
||||
return mac
|
||||
#-----------------------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Grab_Log(log_type = 'std', formatting = 'original', sort_order = 'reverse'):
|
||||
"""
|
||||
This will grab the log file contents, works on all systems even forked kodi.
|
||||
|
||||
CODE: Grab_Log([log_type, formatting, sort_order])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
log_type - This is optional, if not set you will get the current log.
|
||||
If you would prefer the old log set this to 'old'
|
||||
|
||||
formatting - By default you'll just get a default log but you can set
|
||||
this to 'warnings', 'notices', 'errors' to filter by only those error types.
|
||||
Notices will return in blue, warnings in gold and errors in red.
|
||||
You can use as many of the formatting values as you want, just separate by an
|
||||
underscore such as 'warnings_errors'. If using anything other than the
|
||||
default in here your log will returned in order of newest log activity first
|
||||
(reversed order). You can also use 'clean' as an option and that will just
|
||||
return the full log but with clean text formatting and in reverse order.
|
||||
|
||||
sort_order - This will only work if you've sent through an argument other
|
||||
than 'original' for the formatting. By default the log will be shown in
|
||||
'reverse' order but you can set this to 'original' if you prefer ascending
|
||||
timestamp ordering like a normal log.
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_log = koding.Grab_Log()
|
||||
dialog.ok('KODI LOG LOOP','Press OK to see various logging options, every 5 seconds it will show a new log style.')
|
||||
koding.Text_Box('CURRENT LOG FILE (ORIGINAL)',my_log)
|
||||
xbmc.sleep(5000)
|
||||
my_log = koding.Grab_Log(formatting='clean', sort_order='reverse')
|
||||
koding.Text_Box('CURRENT LOG FILE (clean in reverse order)',my_log)
|
||||
xbmc.sleep(5000)
|
||||
my_log = koding.Grab_Log(formatting='errors_warnings', sort_order='reverse')
|
||||
koding.Text_Box('CURRENT LOG FILE (erros & warnings only - reversed)',my_log)
|
||||
xbmc.sleep(5000)
|
||||
old_log = koding.Grab_Log(log_type='old')
|
||||
koding.Text_Box('OLD LOG FILE',old_log)
|
||||
~"""
|
||||
from filetools import Physical_Path, Text_File
|
||||
log_path = Physical_Path('special://logpath/')
|
||||
logfilepath = os.listdir(log_path)
|
||||
finalfile = 0
|
||||
for item in logfilepath:
|
||||
cont = False
|
||||
if item.endswith('.log') and not item.endswith('.old.log') and log_type == 'std':
|
||||
mylog = os.path.join(log_path,item)
|
||||
cont = True
|
||||
elif item.endswith('.old.log') and log_type == 'old':
|
||||
mylog = os.path.join(log_path,item)
|
||||
cont = True
|
||||
if cont:
|
||||
lastmodified = xbmcvfs.Stat(mylog).st_mtime()
|
||||
if lastmodified>finalfile:
|
||||
finalfile = lastmodified
|
||||
logfile = mylog
|
||||
|
||||
logtext = Text_File(logfile, 'r')
|
||||
|
||||
if formatting != 'original':
|
||||
logtext_final = ''
|
||||
|
||||
with open(logfile) as f:
|
||||
log_array = f.readlines()
|
||||
log_array = [line.strip() for line in log_array]
|
||||
|
||||
if sort_order == 'reverse':
|
||||
log_array = reversed(log_array)
|
||||
|
||||
for line in log_array:
|
||||
if ('warnings' in formatting or 'clean' in formatting) and 'WARNING:' in line:
|
||||
logtext_final += line.replace('WARNING:', '[COLOR=gold]WARNING:[/COLOR]')+'\n'
|
||||
if ('errors' in formatting or 'clean' in formatting) and 'ERROR:' in line:
|
||||
logtext_final += line.replace('ERROR:', '[COLOR=red]ERROR:[/COLOR]')+'\n'
|
||||
if ('notices' in formatting or 'clean' in formatting) and 'NOTICE:' in line:
|
||||
logtext_final += line.replace('NOTICE:', '[COLOR=dodgerblue]NOTICE:[/COLOR]')+'\n'
|
||||
|
||||
logtext = logtext_final
|
||||
|
||||
return logtext
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Last_Error():
|
||||
"""
|
||||
Return details of the last error produced, perfect for try/except statements
|
||||
|
||||
CODE: Last_Error()
|
||||
|
||||
EXAMPLE CODE:
|
||||
try:
|
||||
xbmc.log(this_should_error)
|
||||
except:
|
||||
koding.Text_Box('ERROR MESSAGE',Last_Error())
|
||||
~"""
|
||||
|
||||
import traceback
|
||||
error = traceback.format_exc()
|
||||
return error
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Network_Settings():
|
||||
"""
|
||||
Attempt to open the WiFi/network settings for the current running operating system.
|
||||
|
||||
I have no access to any iOS based systems so if anybody wants to add support for
|
||||
that and you know the working code please contact me at info@totalrevolution.tv
|
||||
The Linux one is also currently untested and of course there are many different
|
||||
distros so if you know of any improved code please do pass on. Thank you.
|
||||
|
||||
CODE: Network_Settings()
|
||||
|
||||
EXAMPLE CODE:
|
||||
koding.Network_Settings()
|
||||
~"""
|
||||
content = Grab_Log()
|
||||
if xbmc.getCondVisibility('System.Platform.Android'):
|
||||
xbmc.executebuiltin('StartAndroidActivity(,android.settings.WIFI_SETTINGS)')
|
||||
|
||||
elif xbmc.getCondVisibility('System.Platform.OSX'):
|
||||
os.system('open /System/Library/PreferencePanes/Network.prefPane/')
|
||||
|
||||
elif xbmc.getCondVisibility('System.Platform.Windows'):
|
||||
os.system('ncpa.cpl')
|
||||
|
||||
elif 'Running on OpenELEC' in content or 'Running on LibreELEC' in content:
|
||||
|
||||
if xbmc.getCondVisibility("System.HasAddon(service.openelec.settings)") or xbmc.getCondVisibility("System.HasAddon(service.libreelec.settings)"):
|
||||
if xbmc.getCondVisibility("System.HasAddon(service.openelec.settings)"):
|
||||
xbmcaddon.Addon(id='service.openelec.settings').getAddonInfo('name')
|
||||
xbmc.executebuiltin('RunAddon(service.openelec.settings)')
|
||||
elif xbmc.getCondVisibility("System.HasAddon(service.libreelec.settings)"):
|
||||
xbmcaddon.Addon(id='service.libreelec.settings').getAddonInfo('name')
|
||||
xbmc.executebuiltin('RunAddon(service.libreelec.settings)')
|
||||
xbmc.sleep(1500)
|
||||
xbmc.executebuiltin('Control.SetFocus(1000,2)')
|
||||
xbmc.sleep(500)
|
||||
xbmc.executebuiltin('Control.SetFocus(1200,0)')
|
||||
|
||||
elif xbmc.getCondVisibility('System.Platform.Linux'):
|
||||
os.system('nm-connection-editor')
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Python_Version():
|
||||
"""
|
||||
Return the current version of Python as a string. Very useful if you need
|
||||
to find out whether or not to return https links (Python 2.6 is not SSL friendly).
|
||||
|
||||
CODE: Python_Version()
|
||||
|
||||
EXAMPLE CODE:
|
||||
py_version = koding.Python_Version()
|
||||
dialog.ok('PYTHON VERSION','You are currently running:','Python v.%s'%py_version)
|
||||
~"""
|
||||
py_version = '%s.%s'%(sys.version_info[0],sys.version_info[1])
|
||||
return py_version
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Refresh(r_mode=['addons', 'repos'], profile_name='default'):
|
||||
"""
|
||||
Refresh a number of items in kodi, choose the order they are
|
||||
executed in by putting first in your r_mode. For example if you
|
||||
want to refresh addons then repo and then the profile you would
|
||||
send through a list in the order you want them to be executed.
|
||||
|
||||
CODE: Refresh(r_mode, [profile])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
r_mode - This is the types of "refresh you want to perform",
|
||||
you can send through just one item or a list of items from the
|
||||
list below. If you want a sleep between each action just put a
|
||||
'~' followed by amount of milliseconds after the r_mode. For example
|
||||
r_mode=['addons~3000', 'repos~2000', 'profile']. This would refresh
|
||||
the addons, wait 2 seconds then refresh the repos, wait 3 seconds then
|
||||
reload the profile. The default is set to do a force refresh on
|
||||
addons and repositories - ['addons', 'repos'].
|
||||
|
||||
'addons': This will perform the 'UpdateLocalAddons' command.
|
||||
|
||||
'container': This will refresh the contents of the page.
|
||||
|
||||
'profile': This will refresh the current profile or if
|
||||
the profile_name param is set it will load that.
|
||||
|
||||
'repos': This will perform the 'UpdateAddonRepos' command.
|
||||
|
||||
'skin': This will perform the 'ReloadSkin' command.
|
||||
|
||||
profile_name - If you're sending through the option to refresh
|
||||
a profile it will reload the current running profile by default
|
||||
but you can pass through a profile name here.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('RELOAD SKIN','We will now attempt to update the addons, pause 3s, update repos and pause 2s then reload the skin. Press OK to continue.')
|
||||
koding.Refresh(r_mode=['addons~3000', 'repos~2000', 'skin'])
|
||||
xbmc.sleep(2000)
|
||||
dialog.ok('COMPLETE','Ok that wasn\'t the best test to perform as you can\'t physically see any visible changes other than the skin refreshing so you\'ll just have to trust us that it worked!')
|
||||
~"""
|
||||
from vartools import Data_Type
|
||||
if profile_name == 'default':
|
||||
profile_name = Current_Profile()
|
||||
|
||||
data_type = Data_Type(r_mode)
|
||||
if data_type == 'str':
|
||||
r_mode = [r_mode]
|
||||
|
||||
for item in r_mode:
|
||||
sleeper = 0
|
||||
if '~' in item:
|
||||
item, sleeper = item.split('~')
|
||||
sleeper = int(sleeper)
|
||||
if item =='addons':
|
||||
xbmc.executebuiltin('UpdateLocalAddons')
|
||||
if item =='repos':
|
||||
xbmc.executebuiltin('UpdateAddonRepos')
|
||||
if item =='container':
|
||||
xbmc.executebuiltin('Container.Refresh')
|
||||
if item =='skin':
|
||||
xbmc.executebuiltin('ReloadSkin')
|
||||
if item =='profile':
|
||||
xbmc.executebuiltin('LoadProfile(%s)' % profile_name)
|
||||
if sleeper:
|
||||
xbmc.sleep(sleeper)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Requirements(dependency):
|
||||
"""
|
||||
Return the min and max versions of built-in kodi dependencies required by
|
||||
the running version of Kodi (xbmc.gui, xbmc.python etc.), The return will
|
||||
be a dictionary with the keys 'min' and 'max'.
|
||||
|
||||
CODE: Requirements(dependency)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) dependency - This is the dependency you want to check.
|
||||
You can check any built-in dependency which has backwards-compatibility
|
||||
but the most commonly used are xbmc.gui and xbmc.python.
|
||||
|
||||
EXAMPLE CODE:
|
||||
xbmc_gui = Requirements('xbmc.gui')
|
||||
xbmc_python = Requirements('xbmc.python')
|
||||
dialog.ok('DEPENDENCIES','[COLOR=dodgerblue]xbmc.gui[/COLOR] Min: %s Max: %s'%(xbmc_gui['min'],xbmc_gui['max']),'[COLOR=dodgerblue]xbmc.python[/COLOR] Min: %s Max: %s'%(xbmc_python['min'],xbmc_python['max']))
|
||||
~"""
|
||||
from filetools import Physical_Path,Text_File
|
||||
from vartools import Find_In_Text
|
||||
kodi_ver = xbmc.getInfoLabel("System.BuildVersion")[:2]
|
||||
# Dictionary used for fallback if local file not accessible (AFTV for example)
|
||||
defaults = {'15':{'xbmc.gui':['5.3.0','5.9.0'], 'xbmc.python':['2.1.0','2.20.0']}, '16':{'xbmc.gui':['5.10.0','5.10.0'], 'xbmc.python':['2.1.0','2.24.0']}, '17':{'xbmc.gui':['5.12.0','5.12.0'], 'xbmc.python':['2.1.0','2.25.0']}}
|
||||
root = 'special://xbmc/addons'
|
||||
dep_path = os.path.join(root,dependency,'addon.xml')
|
||||
content = Text_File(dep_path,'r')
|
||||
try:
|
||||
max_ver = Find_In_Text(content=content,start='version="',end='"')[1]
|
||||
min_ver = Find_In_Text(content=content,start='abi="',end='"')[0]
|
||||
except:
|
||||
xbmc.log(repr(defaults[kodi_ver]),2)
|
||||
try:
|
||||
max_ver = defaults[kodi_ver][dependency][1]
|
||||
min_ver = defaults[kodi_ver][dependency][0]
|
||||
except:
|
||||
max_ver = 'unknown'
|
||||
min_ver = 'unknown'
|
||||
xbmc.log('%s min: %s'%(dependency,min_ver),2)
|
||||
xbmc.log('%s max: %s'%(dependency,max_ver),2)
|
||||
return {'min':min_ver,"max":max_ver}
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Running_App():
|
||||
"""
|
||||
Return the Kodi app name you're running, useful for fork compatibility
|
||||
|
||||
CODE: Running_App()
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_kodi = koding.Running_App()
|
||||
kodi_ver = xbmc.getInfoLabel("System.BuildVersion")
|
||||
dialog.ok('KODI VERSION','You are running:','[COLOR=dodgerblue]%s[/COLOR] - v.%s' % (my_kodi, kodi_ver))
|
||||
~"""
|
||||
root_folder = xbmc.translatePath('special://xbmc')
|
||||
xbmc.log(root_folder)
|
||||
if '/cache' in root_folder:
|
||||
root_folder = root_folder.split('/cache')[0]
|
||||
root_folder = root_folder.split('/')
|
||||
if root_folder[len(root_folder)-1] == '':
|
||||
root_folder.pop()
|
||||
finalitem = len(root_folder)-1
|
||||
running = root_folder[finalitem]
|
||||
return running
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Set_Setting(setting, setting_type='kodi_setting', value = 'true'):
|
||||
"""
|
||||
Use this to set built-in kodi settings via JSON or set skin settings.
|
||||
|
||||
CODE: Set_Setting(setting, [setting_type, value])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
setting_type - The type of setting type you want to change. By default
|
||||
it's set to 'kodi_setting', see below for more info.
|
||||
|
||||
AVAILALE VALUES:
|
||||
|
||||
'string' : sets a skin string, requires a value.
|
||||
|
||||
'bool_true' : sets a skin boolean to true, no value required.
|
||||
|
||||
'bool_false' sets a skin boolean to false, no value required.
|
||||
|
||||
'kodi_setting' : sets values found in guisettings.xml. Requires
|
||||
a string of 'true' or 'false' for the value paramater.
|
||||
|
||||
'addon_enable' : enables/disables an addon. Requires a string of
|
||||
'true' (enable) or 'false' (disable) as the value. You will get a
|
||||
return of True/False on whether successul. Depending on your requirements
|
||||
you may prefer to use the Toggle_Addons function.
|
||||
|
||||
'json' : WIP - setitng = method, value = params, see documentation on
|
||||
JSON-RPC API here: http://kodi.wiki/view/JSON-RPC_API)
|
||||
|
||||
setting - This is the name of the setting you want to change, it could be a
|
||||
setting from the kodi settings or a skin based setting. If you're wanting
|
||||
to enable/disable an add-on this is set as the add-on id.
|
||||
|
||||
value: This is the value you want to change the setting to. By default this
|
||||
is set to 'true'.
|
||||
|
||||
|
||||
EXAMPLE CODE:
|
||||
if dialog.yesno('RSS FEEDS','Would you like to enable or disable your RSS feeds?',yeslabel='ENABLE',nolabel='DISABLE'):
|
||||
koding.Set_Setting(setting_type='kodi_setting', setting='lookandfeel.enablerssfeeds', value='true')
|
||||
else:
|
||||
koding.Set_Setting(setting_type='kodi_setting', setting='lookandfeel.enablerssfeeds', value='false')
|
||||
~"""
|
||||
try: import simplejson as json
|
||||
except: import json
|
||||
|
||||
try:
|
||||
|
||||
# If the setting_type is kodi_setting we run the command to set the relevant values in guisettings.xml
|
||||
if setting_type == 'kodi_setting':
|
||||
setting = '"%s"' % setting
|
||||
value = '"%s"' % value
|
||||
|
||||
query = '{"jsonrpc":"2.0", "method":"Settings.SetSettingValue","params":{"setting":%s,"value":%s}, "id":1}' % (setting, value)
|
||||
response = xbmc.executeJSONRPC(query)
|
||||
|
||||
if 'error' in str(response):
|
||||
query = '{"jsonrpc":"2.0", "method":"Settings.SetSettingValue","params":{"setting":%s,"value":%s}, "id":1}' % (setting, value.replace('"',''))
|
||||
response = xbmc.executeJSONRPC(query)
|
||||
if 'error' in str(response):
|
||||
xbmc.log('### Error With Setting: %s' % response, 2)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
# Set a skin string to <value>
|
||||
elif setting_type == 'string':
|
||||
xbmc.executebuiltin('Skin.SetString(%s,%s)' % (setting, value))
|
||||
|
||||
# Set a skin setting to true
|
||||
elif setting_type == 'bool_true':
|
||||
xbmc.executebuiltin('Skin.SetBool(%s)' % setting)
|
||||
|
||||
# Set a skin setting to false
|
||||
elif setting_type == 'bool_false':
|
||||
xbmc.executebuiltin('Skin.Reset(%s)' % setting)
|
||||
|
||||
# If we're enabling/disabling an addon
|
||||
elif setting_type == 'addon_enable':
|
||||
if setting != '':
|
||||
query = '{"jsonrpc":"2.0", "method":"Addons.SetAddonEnabled","params":{"addonid":"%s", "enabled":%s}, "id":1}' % (setting, value)
|
||||
response = xbmc.executeJSONRPC(query)
|
||||
if 'error' in str(response):
|
||||
xbmc.log('### Error in json: %s'%query,2)
|
||||
xbmc.log('^ %s' % response, 2)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# If it's none of the above then it must be a json command so we use the setting_type as the method in json
|
||||
elif setting_type == 'json':
|
||||
query = '{"jsonrpc":"2.0", "method":"%s","params":{%s}, "id":1}' % (setting, value)
|
||||
response = xbmc.executeJSONRPC(query)
|
||||
if 'error' in str(response):
|
||||
xbmc.log('### Error With Setting: %s' % response,2)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
except:
|
||||
xbmc.log(Last_Error())
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Sleep_If_Function_Active(function, args=[], kill_time=30, show_busy=True):
|
||||
"""
|
||||
This will allow you to pause code while a specific function is
|
||||
running in the background.
|
||||
|
||||
CODE: Sleep_If_Function_Active(function, args, kill_time, show_busy)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
function - This is the function you want to run. This does
|
||||
not require brackets, you only need the function name.
|
||||
|
||||
args - These are the arguments you want to send through to
|
||||
the function, these need to be sent through as a list.
|
||||
|
||||
kill_time - By default this is set to 30. This is the maximum
|
||||
time in seconds you want to wait for a response. If the max.
|
||||
time is reached before the function completes you will get
|
||||
a response of False.
|
||||
|
||||
show_busy - By default this is set to True so you'll get a busy
|
||||
working dialog appear while the function is running. Set to
|
||||
false if you'd rather not have this.
|
||||
|
||||
EXAMPLE CODE:
|
||||
def Open_Test_URL(url):
|
||||
koding.Open_URL(url)
|
||||
|
||||
dialog.ok('SLEEP IF FUNCTION ACTIVE','We will now attempt to read a 20MB zip and then give up after 10 seconds.','Press OK to continue.')
|
||||
koding.Sleep_If_Function_Active(function=Open_Test_URL, args=['http://download.thinkbroadband.com/20MB.zip'], kill_time=10, show_busy=True)
|
||||
dialog.ok('FUNCTION COMPLETE','Of course we cannot read that file in just 10 seconds so we\'ve given up!')
|
||||
~"""
|
||||
from guitools import Show_Busy
|
||||
import threading
|
||||
if show_busy:
|
||||
Show_Busy(True)
|
||||
my_thread = threading.Thread(target=function, args=args)
|
||||
my_thread.start()
|
||||
thread_alive = True
|
||||
counter = 0
|
||||
while thread_alive and counter <= kill_time:
|
||||
xbmc.sleep(1000)
|
||||
thread_alive = my_thread.isAlive()
|
||||
counter += 1
|
||||
if show_busy:
|
||||
Show_Busy(False)
|
||||
return thread_alive
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Sleep_If_Window_Active(window_type=10147):
|
||||
"""
|
||||
This will allow you to pause code while a specific window is open.
|
||||
|
||||
CODE: Sleep_If_Window_Active(window_type)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
window_type - This is the window xml name you want to check for, if it's
|
||||
active then the code will sleep until it becomes inactive. By default this
|
||||
is set to the custom text box (10147). You can find a list of window ID's
|
||||
here: http://kodi.wiki/view/Window_IDs
|
||||
|
||||
EXAMPLE CODE:
|
||||
koding.Text_Box('EXAMPLE TEXT','This is just an example, normally a text box would not pause code and the next command would automatically run immediately over the top of this.')
|
||||
koding.Sleep_If_Window_Active(10147) # This is the window id for the text box
|
||||
dialog.ok('WINDOW CLOSED','The window has now been closed so this dialog code has now been initiated')
|
||||
~"""
|
||||
from __init__ import dolog
|
||||
windowactive = False
|
||||
counter = 0
|
||||
|
||||
if window_type == 'yesnodialog' or window_type == 10100:
|
||||
count = 30
|
||||
else:
|
||||
count = 10
|
||||
|
||||
okwindow = False
|
||||
|
||||
# Do not get stuck in an infinite loop. Check x amount of times and if condition isn't met after x amount it quits
|
||||
while not okwindow and counter < count:
|
||||
xbmc.sleep(100)
|
||||
okwindow = xbmc.getCondVisibility('Window.IsActive(%s)' % window_type)
|
||||
counter += 1
|
||||
|
||||
# Window is active
|
||||
while okwindow:
|
||||
okwindow = xbmc.getCondVisibility('Window.IsActive(%s)' % window_type)
|
||||
xbmc.sleep(250)
|
||||
|
||||
return okwindow
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def System(command, function=''):
|
||||
"""
|
||||
This is just a simplified method of grabbing certain Kodi infolabels, paths
|
||||
and booleans as well as performing some basic built in kodi functions.
|
||||
We have a number of regularly used functions added to a dictionary which can
|
||||
quickly be called via this function or you can use this function to easily
|
||||
run a command not currently in the dictionary. Just use one of the
|
||||
many infolabels, builtin commands or conditional visibilities available:
|
||||
|
||||
info: http://kodi.wiki/view/InfoLabels
|
||||
bool: http://kodi.wiki/view/List_of_boolean_conditions
|
||||
|
||||
CODE: System(command, [function])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) command - This is the command you want to perform, below is a list
|
||||
of all the default commands you can choose from, however you can of course
|
||||
send through your own custom command if using the function option (details
|
||||
at bottom of page)
|
||||
|
||||
AVAILABLE VALUES:
|
||||
|
||||
'addonid' : Returns the FOLDER id of the current add-on. Please note could differ from real add-on id.
|
||||
'addonname' : Returns the current name of the add-on
|
||||
'builddate' : Return the build date for the current running version of Kodi
|
||||
'cpu' : Returns the CPU usage as a percentage
|
||||
'cputemp' : Returns the CPU temperature in farenheit or celcius depending on system settings
|
||||
'currentlabel' : Returns the current label of the item in focus
|
||||
'currenticon' : Returns the name of the current icon
|
||||
'currentpos' : Returns the current list position of focused item
|
||||
'currentpath' : Returns the url called by Kodi for the focused item
|
||||
'currentrepo' : Returns the repo of the current focused item
|
||||
'currentskin' : Returns the FOLDER id of the skin. Please note could differ from actual add-on id
|
||||
'date' : Returns the date (Tuesday, April 11, 2017)
|
||||
'debug' : Toggles debug mode on/off
|
||||
'freeram' : Returns the amount of free memory available (in MB)
|
||||
'freespace' : Returns amount of free space on storage in this format: 10848 MB Free
|
||||
'hibernate' : Hibernate system, please note not all systems are capable of waking from hibernation
|
||||
'internetstate' : Returns True or False on whether device is connected to internet
|
||||
'ip' : Return the current LOCAL IP address (not your public IP)
|
||||
'kernel' : Return details of the system kernel
|
||||
'language' : Return the language currently in use
|
||||
'mac' : Return the mac address, will only return the mac currently in use (Wi-Fi OR ethernet, not both)
|
||||
'numitems' : Return the total amount of list items curently in focus
|
||||
'profile' : Return the currently running profile name
|
||||
'quit' : Quit Kodi
|
||||
'reboot' : Reboot the system
|
||||
'restart' : Restart Kodi (Windows/Linux only)
|
||||
'shutdown' : Shutdown the system
|
||||
'sortmethod' : Return the current list sort method
|
||||
'sortorder' : Return the current list sort order
|
||||
'systemname' : Return a clean friendly name for the system
|
||||
'time' : Return the current time in this format: 2:05 PM
|
||||
'usedspace' : Return the amount of used space on the storage in this format: 74982 MB Used
|
||||
'version' : Return the current version of Kodi, this may need cleaning up as it contains full file details
|
||||
'viewmode' : Return the current list viewmode
|
||||
'weatheraddon' : Return the current plugin being used for weather
|
||||
|
||||
|
||||
function - This is optional and default is set to a blank string which will
|
||||
allow you to use the commands listed above but if set you can use your own
|
||||
custom commands by setting this to one of the values below.
|
||||
|
||||
AVAILABLE VALUES:
|
||||
|
||||
'bool' : This will allow you to send through a xbmc.getCondVisibility() command
|
||||
'info' : This will allow you to send through a xbmc.getInfoLabel() command
|
||||
'exec' : This will allow you to send through a xbmc.executebuiltin() command
|
||||
|
||||
EXAMPLE CODE:
|
||||
current_time = koding.System(command='time')
|
||||
current_label = koding.System(command='currentlabel')
|
||||
is_folder = koding.System(command='ListItem.IsFolder', function='bool')
|
||||
dialog.ok('PULLED DETAILS','The current time is %s' % current_time, 'Folder status of list item [COLOR=dodgerblue]%s[/COLOR]: %s' % (current_label, is_folder),'^ A zero means False, as in it\'s not a folder.')
|
||||
~"""
|
||||
params = {
|
||||
'addonid' :'xbmc.getInfoLabel("Container.PluginName")',
|
||||
'addonname' :'xbmc.getInfoLabel("Container.FolderName")',
|
||||
'builddate' :'xbmc.getInfoLabel("System.BuildDate")',
|
||||
'cpu' :'xbmc.getInfoLabel("System.CpuUsage")',
|
||||
'cputemp' :'xbmc.getInfoLabel("System.CPUTemperature")',
|
||||
'currentlabel' :'xbmc.getInfoLabel("System.CurrentControl")',
|
||||
'currenticon' :'xbmc.getInfoLabel("ListItem.Icon")',
|
||||
'currentpos' :'xbmc.getInfoLabel("Container.CurrentItem")',
|
||||
'currentpath' :'xbmc.getInfoLabel("Container.FolderPath")',
|
||||
'currentrepo' :'xbmc.getInfoLabel("Container.Property(reponame)")',
|
||||
'currentskin' :'xbmc.getSkinDir()',
|
||||
'date' :'xbmc.getInfoLabel("System.Date")',
|
||||
'debug' :'xbmc.executebuiltin("ToggleDebug")',
|
||||
'freeram' :'xbmc.getFreeMem()',
|
||||
'freespace' :'xbmc.getInfoLabel("System.FreeSpace")',
|
||||
'hibernate' :'xbmc.executebuiltin("Hibernate")',
|
||||
'internetstate' :'xbmc.getInfoLabel("System.InternetState")',
|
||||
'ip' :'xbmc.getIPAddress()',
|
||||
'kernel' :'xbmc.getInfoLabel("System.KernelVersion")',
|
||||
'language' :'xbmc.getInfoLabel("System.Language")',
|
||||
'mac' :'xbmc.getInfoLabel("Network.MacAddress")',
|
||||
'numitems' :'xbmc.getInfoLabel("Container.NumItems")',
|
||||
'profile' :'xbmc.getInfoLabel("System.ProfileName")',
|
||||
'quit' :'xbmc.executebuiltin("Quit")',
|
||||
'reboot' :'xbmc.executebuiltin("Reboot")',
|
||||
'restart' :'xbmc.restart()', # Windows/Linux only
|
||||
'shutdown' :'xbmc.shutdown()',
|
||||
'sortmethod' :'xbmc.getInfoLabel("Container.SortMethod")',
|
||||
'sortorder' :'xbmc.getInfoLabel("Container.SortOrder")',
|
||||
'systemname' :'xbmc.getInfoLabel("System.FriendlyName")',
|
||||
'time' :'xbmc.getInfoLabel("System.Time")',
|
||||
'usedspace' :'xbmc.getInfoLabel("System.UsedSpace")',
|
||||
'version' :'xbmc.getInfoLabel("System.BuildVersion")',
|
||||
'viewmode' :'xbmc.getInfoLabel("Container.Viewmode")',
|
||||
'weatheraddon' :'xbmc.getInfoLabel("Weather.plugin")',
|
||||
}
|
||||
|
||||
if function == '': newcommand = params[command]
|
||||
elif function == 'info': newcommand = 'xbmc.getInfoLabel("%s")' % command
|
||||
elif function == 'bool': newcommand = 'xbmc.getCondVisibility("%s")' % command
|
||||
elif function == 'exec': newcommand = 'xbmc.getCondVisibility("%s")' % command
|
||||
else:
|
||||
dialog.ok('INCORRECT PARAMS','The following command has been called:','koding.System(%s,[COLOR=dodgerblue]%s[/COLOR])'%(command, function),'^ The wrong function has been sent through, please double check the section highlighted in blue.')
|
||||
|
||||
try:
|
||||
return eval(newcommand)
|
||||
except:
|
||||
return 'error'
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Timestamp(mode = 'integer'):
|
||||
"""
|
||||
This will return the timestamp in various formats. By default it returns as "integer" mode but other options are listed below:
|
||||
|
||||
CODE: Timestamp(mode)
|
||||
mode is optional, by default it's set as integer
|
||||
|
||||
AVAILABLE VALUES:
|
||||
|
||||
'integer' - An integer which is nice and easy to work with in Python (especially for
|
||||
finding out human readable diffs). The format returned is [year][month][day][hour][minutes][seconds].
|
||||
|
||||
'epoch' - Unix Epoch format (calculated in seconds passed since 12:00 1st Jan 1970).
|
||||
|
||||
'clean' - A clean user friendly time format: Tue Jan 13 10:17:09 2009
|
||||
|
||||
'date_time' - A clean interger style date with time at end: 2017-04-07 10:17:09
|
||||
|
||||
EXAMPLE CODE:
|
||||
integer_time = koding.Timestamp('integer')
|
||||
epoch_time = koding.Timestamp('epoch')
|
||||
clean_time = koding.Timestamp('clean')
|
||||
date_time = koding.Timestamp('date_time')
|
||||
import datetime
|
||||
installedtime = str(datetime.datetime.now())[:-7]
|
||||
dialog.ok('CURRENT TIME','Integer: %s' % integer_time, 'Epoch: %s' % epoch_time, 'Clean: %s' % clean_time)
|
||||
~"""
|
||||
import time
|
||||
import datetime
|
||||
|
||||
now = time.time()
|
||||
try:
|
||||
localtime = time.localtime(now)
|
||||
except:
|
||||
localtime = str(datetime.datetime.now())[:-7]
|
||||
localtime = localtime.replace('-','').replace(':','')
|
||||
if mode == 'date_time':
|
||||
return time.strftime('%Y-%m-%d %H:%M:%S', localtime)
|
||||
if mode == 'integer':
|
||||
return time.strftime('%Y%m%d%H%M%S', localtime)
|
||||
|
||||
if mode == 'clean':
|
||||
return time.asctime(localtime)
|
||||
|
||||
if mode == 'epoch':
|
||||
return now
|
||||
#----------------------------------------------------------------
|
||||
169
script.module.python.koding.aio/lib/koding/tutorials.py
Normal file
169
script.module.python.koding.aio/lib/koding/tutorials.py
Normal file
@@ -0,0 +1,169 @@
|
||||
# -*- 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 re
|
||||
import sys
|
||||
import urllib
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
import xbmcvfs
|
||||
|
||||
from directory import Add_Dir
|
||||
from filetools import Text_File
|
||||
from vartools import Find_In_Text
|
||||
from guitools import Text_Box, Show_Busy, Keyboard
|
||||
from systemtools import Sleep_If_Window_Active
|
||||
from video import Play_Video
|
||||
from web import Open_URL
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
py_path = 'special://home/addons/script.module.python.koding.aio/lib/koding'
|
||||
video_base = 'http://totalrevolution.tv/videos/python_koding/'
|
||||
#----------------------------------------------------------------
|
||||
def Grab_Tutorials():
|
||||
""" internal command ~"""
|
||||
import re
|
||||
full_array = []
|
||||
dirs,files = xbmcvfs.listdir(py_path)
|
||||
# Check all the modules for functions with tutorial info
|
||||
for file in files:
|
||||
file_path = os.path.join(py_path,file)
|
||||
if file.endswith('.py') and file != 'tutorials.py':
|
||||
content = Text_File(file_path,'r').replace('\r','')
|
||||
# content_array = re.compile('# TUTORIAL #\ndef (.+?)\(').findall(content)
|
||||
content_array = Find_In_Text(content=content, start='# TUTORIAL #\ndef ', end='\(', show_errors=False)
|
||||
if content_array:
|
||||
for item in content_array:
|
||||
item = item.strip()
|
||||
full_array.append('%s~%s'%(item,file_path))
|
||||
content_array = Find_In_Text(content=content, start='# TUTORIAL #\nclass ', end='\(', show_errors=False)
|
||||
if content_array:
|
||||
for item in content_array:
|
||||
item = item.strip()
|
||||
full_array.append('%s~%s'%(item,file_path))
|
||||
|
||||
# Return a list of tutorials
|
||||
Add_Dir('[COLOR=gold]CREATE YOUR FIRST ADD-ON[/COLOR]',video_base+'Create_Addon.mov','play_video', folder=False, icon='', fanart='', description='How to create your own add-on using the Python Koding framework.')
|
||||
|
||||
for item in sorted(full_array,key=str.lower):
|
||||
name, filepath = item.split('~')
|
||||
filepath = urllib.quote(filepath)
|
||||
Add_Dir(name=name.upper().replace('_',' '), url='%s~%s'%(name,filepath), mode='show_tutorial', folder=False, icon='', fanart='', description='Instructions for how to use the %s function.'%name)
|
||||
#----------------------------------------------------------------
|
||||
def Show_Tutorial(url):
|
||||
""" internal command ~"""
|
||||
name, filepath = url.split('~')
|
||||
filepath = urllib.unquote(filepath)
|
||||
readfile = Text_File(filepath,'r').replace('\r','')
|
||||
try:
|
||||
raw_find = Find_In_Text(content=readfile, start='# TUTORIAL #\ndef %s' % name,end='~"""')[0]
|
||||
except:
|
||||
raw_find = Find_In_Text(content=readfile, start='# TUTORIAL #\nclass %s' % name,end='~"""')[0]
|
||||
# Check if an example code segment exists in the comments
|
||||
if 'EXAMPLE CODE:' in raw_find:
|
||||
code = re.findall(r'(?<=EXAMPLE CODE:)(?s)(.*$)', raw_find)[0]
|
||||
code = code.replace('script.module.python.koding.aio','temp_replace_string')
|
||||
code = code.replace('koding.','').strip()
|
||||
code = code.replace('temp_replace_string','script.module.python.koding.aio')
|
||||
|
||||
else:
|
||||
code = None
|
||||
|
||||
# Check if a video exists in the comments
|
||||
internetstate = xbmc.getInfoLabel('System.InternetState')
|
||||
if internetstate:
|
||||
video_page = Open_URL(video_base)
|
||||
extension = Find_In_Text(video_page, name, '"', False)
|
||||
if extension != '' and extension != None:
|
||||
video = video_base+name+extension[0]
|
||||
else:
|
||||
video = None
|
||||
else:
|
||||
video = None
|
||||
|
||||
counter = 0
|
||||
removal_string = ''
|
||||
final_header = ''
|
||||
newline = ''
|
||||
temp_raw = raw_find.splitlines()
|
||||
for line in temp_raw:
|
||||
if counter == 0:
|
||||
removal_string += line
|
||||
if '[' in line:
|
||||
replace_file = Find_In_Text(content=line,start='\[',end='\]')
|
||||
for item in replace_file:
|
||||
line = line.replace(item,'')
|
||||
if ',' in line:
|
||||
header_extension = line.split(',')
|
||||
for item in header_extension:
|
||||
if '=' in item:
|
||||
item = item.split('=')[0]
|
||||
final_header += item+','
|
||||
final_header = 'koding.'+name+final_header[:-2]+')'
|
||||
else:
|
||||
final_header = 'koding.'+name+line[:-1]
|
||||
else:
|
||||
removal_string += '\n'+line
|
||||
counter += 1
|
||||
if counter == 2:
|
||||
break
|
||||
if final_header.endswith('))'):
|
||||
final_header = final_header[:-1]
|
||||
if final_header.startswith('koding.User_Info'):
|
||||
final_header = 'koding.User_Info()'
|
||||
full_text = raw_find.replace(removal_string,'').strip()
|
||||
|
||||
# Initialise the dialog select
|
||||
dialog_array = ['Documentation']
|
||||
if code:
|
||||
dialog_array.append('Run Example Code')
|
||||
if video:
|
||||
dialog_array.append('Watch Video')
|
||||
|
||||
# If there's more than one item we show a dialog select otherwise we just load up the text window
|
||||
if len(dialog_array) > 1:
|
||||
choice = dialog.select(name, dialog_array)
|
||||
if choice >= 0:
|
||||
choice = dialog_array[choice]
|
||||
if choice == 'Documentation':
|
||||
Text_Box(final_header,full_text
|
||||
.replace('AVAILABLE PARAMS:','[COLOR=dodgerblue]AVAILABLE PARAMS:[/COLOR]')
|
||||
.replace('EXAMPLE CODE:','[COLOR=dodgerblue]EXAMPLE CODE:[/COLOR]')
|
||||
.replace('IMPORTANT:','[COLOR=gold]IMPORTANT:[/COLOR]')
|
||||
.replace('CODE:','[COLOR=dodgerblue]CODE:[/COLOR]')
|
||||
.replace('AVAILABLE VALUES:','[COLOR=dodgerblue]AVAILABLE VALUES:[/COLOR]')
|
||||
.replace('WARNING:','[COLOR=red]WARNING:[/COLOR]'))
|
||||
elif choice == 'Run Example Code':
|
||||
codefile = filepath.split(os.sep)
|
||||
codefile = codefile[len(codefile)-1].replace('.py','')
|
||||
exec('from %s import *' % codefile)
|
||||
# exec('from %s import %s' % (codefile, params["name"]))
|
||||
exec(code)
|
||||
elif choice == 'Watch Video':
|
||||
Play_Video(video)
|
||||
if choice < 0:
|
||||
return
|
||||
else:
|
||||
Text_Box(final_header,full_text
|
||||
.replace('AVAILABLE PARAMS:','[COLOR=dodgerblue]AVAILABLE PARAMS:[/COLOR]')
|
||||
.replace('EXAMPLE CODE:','[COLOR=dodgerblue]EXAMPLE CODE:[/COLOR]')
|
||||
.replace('IMPORTANT:','[COLOR=gold]IMPORTANT:[/COLOR]')
|
||||
.replace('CODE:','[COLOR=dodgerblue]CODE:[/COLOR]')
|
||||
.replace('AVAILABLE VALUES:','[COLOR=dodgerblue]AVAILABLE VALUES:[/COLOR]')
|
||||
.replace('WARNING:','[COLOR=red]WARNING:[/COLOR]'))
|
||||
930
script.module.python.koding.aio/lib/koding/vartools.py
Normal file
930
script.module.python.koding.aio/lib/koding/vartools.py
Normal file
@@ -0,0 +1,930 @@
|
||||
# -*- 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
|
||||
|
||||
from filetools import Physical_Path
|
||||
|
||||
HOME = Physical_Path('special://home')
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def ASCII_Check(sourcefile=HOME, dp=False):
|
||||
"""
|
||||
Return a list of files found containing non ASCII characters in the filename.
|
||||
|
||||
CODE: ASCII_Check([sourcefile, dp])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
sourcefile - The folder you want to scan, by default it's set to the
|
||||
Kodi home folder.
|
||||
|
||||
dp - Optional DialogProgress, by default this is False. If you want
|
||||
to show a dp make sure you initiate an instance of xbmcgui.DialogProgress()
|
||||
and send through as the param.
|
||||
|
||||
EXAMPLE CODE:
|
||||
home = koding.Physical_Path('special://home')
|
||||
progress = xbmcgui.DialogProgress()
|
||||
progress.create('ASCII CHECK')
|
||||
my_return = ASCII_Check(sourcefile=home, dp=progress)
|
||||
if len(my_return) > 0:
|
||||
dialog.select('NON ASCII FILES', my_return)
|
||||
else:
|
||||
dialog.ok('ASCII CHECK CLEAN','Congratulations!','There weren\'t any non-ASCII files found on this system.')
|
||||
~"""
|
||||
rootlen = len(sourcefile)
|
||||
for_progress = []
|
||||
final_array = []
|
||||
ITEM = []
|
||||
|
||||
for base, dirs, files in os.walk(sourcefile):
|
||||
for file in files:
|
||||
ITEM.append(file)
|
||||
N_ITEM =len(ITEM)
|
||||
|
||||
for base, dirs, files in os.walk(sourcefile):
|
||||
dirs[:] = [d for d in dirs]
|
||||
files[:] = [f for f in files]
|
||||
|
||||
for file in files:
|
||||
for_progress.append(file)
|
||||
progress = len(for_progress) / float(N_ITEM) * 100
|
||||
if dp:
|
||||
dp.update(0,"Checking for non ASCII files",'[COLOR yellow]%s[/COLOR]'%d, 'Please Wait')
|
||||
|
||||
try:
|
||||
file.encode('ascii')
|
||||
|
||||
except UnicodeDecodeError:
|
||||
badfile = (str(base)+'/'+str(file)).replace('\\','/').replace(':/',':\\')
|
||||
final_array.append(badfile)
|
||||
return final_array
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Cleanup_String(my_string):
|
||||
"""
|
||||
Clean a string, removes whitespaces and common buggy formatting when pulling from websites
|
||||
|
||||
CODE: Cleanup_String(my_string)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) my_string - This is the main text you want cleaned up.
|
||||
|
||||
EXAMPLE CODE:
|
||||
current_text = '" This is a string of text which should be cleaned up /'
|
||||
dialog.ok('ORIGINAL STRING', '[COLOR dodgerblue]%s[/COLOR]\n\nPress OK to view the cleaned up version.'%current_text)
|
||||
clean_text = koding.Cleanup_String(current_text)
|
||||
dialog.ok('CLEAN STRING', '[COLOR dodgerblue]%s[/COLOR]'%clean_text)
|
||||
~"""
|
||||
import urllib
|
||||
bad_chars = ['/','\\',':',';','"',"'"]
|
||||
|
||||
try:
|
||||
my_string = my_string.encode('utf8')
|
||||
except:
|
||||
pass
|
||||
|
||||
my_string = urllib.unquote_plus(my_string)
|
||||
my_string = my_string.replace('<br>','').replace('<br />','').replace('<br/>','')
|
||||
my_string = my_string.replace('</p>','').replace('</div>','').replace('</class>','')
|
||||
my_string = my_string.replace('&','&')
|
||||
if len(my_string) > 4:
|
||||
if my_string[-4] == '.':
|
||||
my_string = my_string[:-4]
|
||||
|
||||
my_string = my_string.strip()
|
||||
|
||||
while my_string[0] in bad_chars or my_string[-1] in bad_chars:
|
||||
if my_string[-1] in bad_chars:
|
||||
my_string = my_string[:-1]
|
||||
if my_string[0] in bad_chars:
|
||||
my_string = my_string[1:]
|
||||
my_string = my_string.strip()
|
||||
|
||||
return my_string
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Colour_Text(text, colour1='dodgerblue',colour2='white'):
|
||||
"""
|
||||
Capitalize a string and make the first colour of each string blue and the rest of text white
|
||||
That's the default colours but you can change to whatever colours you want.
|
||||
|
||||
CODE: Colour_Text(text, [color1, color2])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) text - This is the main text you want to change
|
||||
|
||||
colour1 - This is optional and is set as dodgerblue by default.
|
||||
This is the first letter of each word in the string
|
||||
|
||||
colour2 - This is optional and is set as white by default.
|
||||
This is the colour of the text
|
||||
|
||||
IMPORTANT: I use the Queens English so please note the word "colour" has a 'u' in it!
|
||||
|
||||
EXAMPLE CODE:
|
||||
current_text = 'This is a string of text which should be changed to dodgerblue and white with every first letter capitalised'
|
||||
mytext = koding.Colour_Text(text=current_text, colour1='dodgerblue', colour2='white')
|
||||
xbmc.log(current_text)
|
||||
xbmc.log(mytext)
|
||||
dialog.ok('CURRENT TEXT', current_text)
|
||||
dialog.ok('NEW TEXT', mytext)
|
||||
~"""
|
||||
if text.startswith('[COLOR') and text.endswith('/COLOR]'):
|
||||
return text
|
||||
|
||||
colour_clean = 0
|
||||
|
||||
if ' ' in text:
|
||||
newname = ''
|
||||
text = text.split(' ')
|
||||
for item in text:
|
||||
if len(item)==1 and item == '&':
|
||||
newname += ' &'
|
||||
if '[/COLOR]' in item:
|
||||
newname += ' '+item
|
||||
elif not item.startswith('[COLOR=') and not colour_clean:
|
||||
if item.startswith('(') or item.startswith('['):
|
||||
newname += '[COLOR=yellow] '+item
|
||||
colour_clean = 1
|
||||
else:
|
||||
if item.isupper():
|
||||
newname += '[COLOR=%s] %s[/COLOR]' % (colour1, item)
|
||||
else:
|
||||
try:
|
||||
newname += '[COLOR=%s] %s[/COLOR][COLOR=%s]%s[/COLOR]' % (colour1, item[0].upper(), colour2, item[1:])
|
||||
except:
|
||||
try:
|
||||
newname += '[COLOR=%s] %s[/COLOR][COLOR=%s][/COLOR]' % (colour1, item[0], colour2, item[1:])
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
elif item.endswith(')') or item.endswith(']'):
|
||||
newname += ' '+item+'[/COLOR]'
|
||||
colour_clean = 0
|
||||
|
||||
else:
|
||||
newname += ' '+item
|
||||
|
||||
else:
|
||||
if text[0] == '(':
|
||||
newname = '[COLOR=%s]%s[/COLOR][COLOR=%s]%s[/COLOR][COLOR=%s]%s[/COLOR]' % (colour2, text[0], colour1, text[1].upper(), colour2, text[2:])
|
||||
else:
|
||||
newname = '[COLOR=%s]%s[/COLOR][COLOR=%s]%s[/COLOR]' % (colour1, text[0], colour2, text[1:])
|
||||
|
||||
success = 0
|
||||
while success != 1:
|
||||
if newname.startswith(' '):
|
||||
newname = newname[1:]
|
||||
success = 1
|
||||
if newname.startswith('[COLOR=%s] ' % colour1):
|
||||
newname = '[COLOR=%s]%s' % (colour1, newname[19:])
|
||||
|
||||
return newname
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Convert_Special(filepath=HOME, string=False, quoted=True):
|
||||
"""
|
||||
Convert physcial paths stored in text files to their special:// equivalent or
|
||||
replace instances of physical paths to special in a string sent through.
|
||||
|
||||
CODE: Convert_Special([filepath, string])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
filepath - This is the path you want to scan, by default it's set to the Kodi HOME directory.
|
||||
|
||||
string - By default this is set to False which means it will convert all instances found of
|
||||
the physical paths to their special equivalent. The scan will convert all instances in all filenames
|
||||
ending in ini, xml, hash, properties. If you set this value to True you will get a return of your
|
||||
'filepath' string and no files will be altered.
|
||||
|
||||
quoted - By default this is set to true, this means the return you get will be converted
|
||||
with urllib.quote_plus(). This is ideal if you need to get a string you can send
|
||||
through as a path for routing.
|
||||
|
||||
EXAMPLE CODE:
|
||||
path = koding.Physical_Path('special://profile')
|
||||
dialog.ok('ORIGINAL PATH','Let\'s convert this path to it\'s special equivalent:\n[COLOR dodgerblue]%s[/COLOR]'%path)
|
||||
path = Convert_Special(filepath=path,string=True,quoted=False)
|
||||
dialog.ok('CONVERTED PATH','This is the converted path:\n[COLOR dodgerblue]%s[/COLOR]'%path)
|
||||
if dialog.yesno('CONVERT PHYSICAL PATHS','We will now run through your Kodi folder converting all physical paths to their special:// equivalent in xml/hash/properties/ini files.\nDo you want to continue?'):
|
||||
koding.Convert_Special()
|
||||
dialog.ok('SUCCESS','Congratulations, all references to your physical paths have been converted to special:// paths.')
|
||||
~"""
|
||||
import urllib
|
||||
from filetools import Text_File
|
||||
if not string:
|
||||
for root, dirs, files in os.walk(filepath):
|
||||
for file in files:
|
||||
if file.endswith(".xml") or file.endswith(".hash") or file.endswith("properies") or file.endswith(".ini"):
|
||||
contents = Text_File(os.path.join(root,file), 'r')
|
||||
encodedpath = urllib.quote(HOME)
|
||||
encodedpath2 = encodedpath.replace('%3A','%3a').replace('%5C','%5c')
|
||||
newfile = contents.replace(HOME, 'special://home/').replace(encodedpath, 'special://home/').replace(encodedpath2, 'special://home/')
|
||||
Text_File(os.path.join(root, file), 'w', newfile)
|
||||
else:
|
||||
encodedpath = urllib.quote(HOME)
|
||||
encodedpath2 = encodedpath.replace('%3A','%3a').replace('%5C','%5c')
|
||||
newstring = filepath.replace(HOME, 'special://home/').replace(encodedpath, 'special://home/').replace(encodedpath2, 'special://home/')
|
||||
if quoted:
|
||||
newstring = urllib.quote_plus(newstring)
|
||||
return newstring
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Data_Type(data):
|
||||
"""
|
||||
This will return whether the item received is a dictionary, list, string, integer etc.
|
||||
|
||||
CODE: Data_Type(data)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) data - This is the variable you want to check.
|
||||
|
||||
RETURN VALUES:
|
||||
list, dict, str, int, float, bool
|
||||
|
||||
EXAMPLE CODE:
|
||||
test1 = ['this','is','a','list']
|
||||
test2 = {"a" : "1", "b" : "2", "c" : 3}
|
||||
test3 = 'this is a test string'
|
||||
test4 = 12
|
||||
test5 = 4.3
|
||||
test6 = True
|
||||
|
||||
my_return = '[COLOR dodgerblue]%s[/COLOR] : %s\n' % (test1, koding.Data_Type(test1))
|
||||
my_return += '[COLOR dodgerblue]%s[/COLOR] : %s\n' % (test2, koding.Data_Type(test2))
|
||||
my_return += '[COLOR dodgerblue]%s[/COLOR] : %s\n' % (test3, koding.Data_Type(test3))
|
||||
my_return += '[COLOR dodgerblue]%s[/COLOR] : %s\n' % (test4, koding.Data_Type(test4))
|
||||
my_return += '[COLOR dodgerblue]%s[/COLOR] : %s\n' % (test5, koding.Data_Type(test5))
|
||||
my_return += '[COLOR dodgerblue]%s[/COLOR] : %s\n' % (test6, koding.Data_Type(test6))
|
||||
|
||||
koding.Text_Box('TEST RESULTS', my_return)
|
||||
~"""
|
||||
data_type = type(data).__name__
|
||||
return data_type
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Decode_String(string):
|
||||
"""
|
||||
This will allow you to send a string which contains a variety of special characters (including
|
||||
non ascii, unicode etc.) and it will convert into a nice clean string which plays nicely
|
||||
with Python and Kodi.
|
||||
|
||||
CODE: Decode_String(string)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) string - This is the string you want to convert
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_string = 'symbols like [COLOR dodgerblue]¥¨˚∆ƒπø“¬∂≈óõřĖė[/COLOR] can cause errors \nnormal chars like [COLOR dodgerblue]asfasdf[/COLOR] are fine'
|
||||
dialog.ok('ORIGINAL TEXT',my_string)
|
||||
my_string = koding.Decode_String(my_string)
|
||||
dialog.ok('DECODED/STRIPPED',my_string)
|
||||
~"""
|
||||
try:
|
||||
string = string.encode('ascii', 'ignore')
|
||||
except:
|
||||
string = string.decode('utf-8').encode('ascii', 'ignore')
|
||||
return string
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Find_In_Text(content, start, end, show_errors = False):
|
||||
"""
|
||||
Regex through some text and return a list of matches.
|
||||
Please note this will return a LIST so even if only one item is found
|
||||
you will still need to access it as a list, see example below.
|
||||
|
||||
CODE: Find_In_Text(content, start, end, [show_errors])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) content - This is the string to search
|
||||
|
||||
(*) start - The start search string
|
||||
|
||||
(*) end - The end search string
|
||||
|
||||
show_errors - Default is False, if set to True the code will show help
|
||||
dialogs for bad code.
|
||||
|
||||
EXAMPLE CODE:
|
||||
textsearch = 'This is some text so lets have a look and see if we can find the words "lets have a look"'
|
||||
dialog.ok('ORIGINAL TEXT','Below is the text we\'re going to use for our search:','[COLOR dodgerblue]%s[/COLOR]'%textsearch)
|
||||
search_result = koding.Find_In_Text(textsearch, 'text so ', ' and see')
|
||||
dialog.ok('SEARCH RESULT','You searched for the start string of "text so " and the end string of " and see".','','Your result is: [COLOR dodgerblue]%s[/COLOR]' % search_result[0])
|
||||
|
||||
# Please note: we know for a fact there is only one result which is why we're only accessing list item zero.
|
||||
# If we were expecting more than one return we would probably do something more useful and loop through in a for loop.
|
||||
~"""
|
||||
import re
|
||||
if content == None or content == False:
|
||||
if show_errors:
|
||||
dialog.ok('ERROR WITH REGEX','No content sent through - there\'s nothing to scrape. Please check the website address is still active (details at bottom of log).')
|
||||
xbmc.log(content)
|
||||
return
|
||||
if end != '':
|
||||
links = re.findall('%s([\s\S]*?)%s' % (start, end), content)
|
||||
if len(links)>0:
|
||||
return links
|
||||
else:
|
||||
if show_errors:
|
||||
xbmc.log(content)
|
||||
dialog.ok('ERROR WITH REGEX','Please check your regex, there was content sent through to search but there are no matches for the regex supplied. The raw content has now been printed to the log')
|
||||
return None
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Fuzzy_Search(search_string, search_list, replace_strings=[]):
|
||||
"""
|
||||
Send through a list of items and try to match against the search string.
|
||||
This will match where the search_string exists in the list or an item in
|
||||
the list exists in the search_string.
|
||||
|
||||
CODE: Fuzzy_Search(search_string, search_list, [strip])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) search_string - This is the string to search for
|
||||
|
||||
(*) search_list - The list of items to search through
|
||||
|
||||
replace_strings - Optionally send through a list of strings you want to
|
||||
replace. For example you may want to search for "West Ham United" but in
|
||||
the list you've sent through they've abbreviated it to "West Ham Utd FC". In
|
||||
this case we might want to send through a replace_strings list of:
|
||||
|
||||
(["united","utd"], ["fc",""])
|
||||
|
||||
This will remove any instances of "FC" from the search and it will replace
|
||||
instances of "united" to "utd". The code will convert everythig to lowercase
|
||||
so it doesn't matter what case you use in these searches.
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_search = 'west ham utd'
|
||||
my_list = ['west ham united', 'west ham utd', 'rangers fc', 'Man City', 'West Ham United FC', 'Fulham FC', 'West Ham f.c']
|
||||
my_replace = (["united","utd"], ["fc",""], ["f.c",""])
|
||||
dialog.ok('FUZZY SEARCH','Let\'s search for matches similar to "west ham utd" in the list:\n\n%s'%my_list)
|
||||
search_result = koding.Fuzzy_Search(my_search, my_list, my_replace)
|
||||
good = ', '.join(search_result)
|
||||
bad = ''
|
||||
for item in my_list:
|
||||
if item not in search_result:
|
||||
bad += item+', '
|
||||
dialog.ok('RESULTS FOUND','[COLOR=dodgerblue]SEARCH:[/COLOR] %s\n[COLOR=lime]GOOD:[/COLOR] %s\n[COLOR=cyan]BAD:[/COLOR] %s'%(my_search,good,bad))
|
||||
~"""
|
||||
final_array = []
|
||||
newsearch = search_string.lower().strip().replace(' ','')
|
||||
for item in replace_strings:
|
||||
newsearch = newsearch.replace(item[0],item[1])
|
||||
xbmc.log('newsearch: %s'%newsearch,2)
|
||||
for item in search_list:
|
||||
newitem = item.lower().strip().replace(' ','')
|
||||
for rep in replace_strings:
|
||||
newitem = newitem.replace(rep[0],rep[1])
|
||||
xbmc.log('list_item: %s'%newitem,2)
|
||||
if (newsearch in newitem) or (newitem in newsearch):
|
||||
final_array.append(item)
|
||||
if len(final_array)>0:
|
||||
return final_array
|
||||
else:
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Highest_Version(content=[],start_point='',end_point=''):
|
||||
"""
|
||||
Send through a list of strings which all have a common naming structure,
|
||||
the one with the highest version number will be returned.
|
||||
|
||||
CODE: Highest_Version(content,[start_point,end_point])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) content - This is the list of filenames you want to check.
|
||||
|
||||
start_point - If your filenames have a common character/string immediately
|
||||
before the version number enter that here. For example if you're looking at
|
||||
online repository/add-on files you would use '-' as the start_point. The version
|
||||
numbers always appear after the final '-' with add-ons residing on repo's.
|
||||
|
||||
end_point - If your version number is followed by a common string (e.g. '.zip')
|
||||
then enter it in here.
|
||||
|
||||
EXAMPLE CODE:
|
||||
mylist = ['plugin.test-1.0.zip','plugin.test-0.7.zip','plugin.test-1.1.zip','plugin.test-0.9.zip']
|
||||
dialog.ok('OUR LIST OF FILES', '[COLOR=dodgerblue]%s[/COLOR]\n[COLOR=powderblue]%s[/COLOR]\n[COLOR=dodgerblue]%s[/COLOR]\n[COLOR=powderblue]%s[/COLOR]'%(mylist[0],mylist[1],mylist[2],mylist[3]))
|
||||
|
||||
highest = Highest_Version(content=mylist,start_point='-',end_point='.zip')
|
||||
dialog.ok('HIGHEST VERSION', 'The highest version number of your files is:','[COLOR=dodgerblue]%s[/COLOR]'%highest)
|
||||
~"""
|
||||
highest = 0
|
||||
highest_ver = ''
|
||||
for item in content:
|
||||
version = item.replace(end_point,'')
|
||||
version = version.split(start_point)
|
||||
version = version[len(version)-1]
|
||||
if version > highest:
|
||||
highest = version
|
||||
highest_ver = item
|
||||
return highest_ver
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def ID_Generator(size=15):
|
||||
"""
|
||||
This will generate a random string made up of uppercase & lowercase ASCII
|
||||
characters and digits - it does not contain special characters.
|
||||
|
||||
CODE: ID_Generator([size])
|
||||
size is an optional paramater.
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
size - just send through an integer, this is the length of the string you'll get returned.
|
||||
So if you want a password generated that's 20 characters long just use ID_Generator(20). The default is 15.
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_password = koding.ID_Generator(20)
|
||||
dialog.ok('ID GENERATOR','Password generated:', '', '[COLOR=dodgerblue]%s[/COLOR]' % my_password)
|
||||
~"""
|
||||
import string
|
||||
import random
|
||||
|
||||
chars=string.ascii_uppercase + string.digits + string.ascii_lowercase
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def List_From_Dict(mydict={},use_key=True):
|
||||
"""
|
||||
Send through a dictionary and return a list of either the keys or values.
|
||||
Please note: The returned list will be sorted in alphabetical order.
|
||||
|
||||
CODE: List_From_Dict(mydict,[use_key])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) mydict - This is the dictionary (original data) you want to traverse through.
|
||||
|
||||
use_key - By default this is set to True and a list of all your dictionary keys
|
||||
will be returned. Set to False if you'd prefer to have a list of the values returned.
|
||||
|
||||
EXAMPLE CODE:
|
||||
raw_data = {'test1':'one','test2':'two','test3':'three','test4':'four','test5':'five'}
|
||||
mylist1 = koding.List_From_Dict(mydict=raw_data)
|
||||
mylist2 = koding.List_From_Dict(mydict=raw_data,use_key=False)
|
||||
koding.Text_Box('LIST_FROM_DICT','Original dictionary: [COLOR dodgerblue]%s[/COLOR][CR][CR]Returned List (use_key=True): [COLOR dodgerblue]%s[/COLOR][CR]Returned List (use_key=False): [COLOR dodgerblue]%s[/COLOR]'%(raw_data,mylist1,mylist2))
|
||||
~"""
|
||||
pos = 1
|
||||
if use_key:
|
||||
pos = 0
|
||||
final_list = []
|
||||
for item in mydict.items():
|
||||
if item[0]!='' and item[1]!='':
|
||||
final_list.append(item[pos])
|
||||
return sorted(final_list)
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def md5_check(src,string=False):
|
||||
"""
|
||||
Return the md5 value of string/file/directory, this will return just one unique value.
|
||||
|
||||
CODE: md5_check(src,[string])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) src - This is the source you want the md5 value of.
|
||||
This can be a string, path of a file or path to a folder.
|
||||
|
||||
string - By default this is set to False but if you want to send
|
||||
through a string rather than a path set this to True.
|
||||
|
||||
EXAMPLE CODE:
|
||||
home = koding.Physical_Path('special://home')
|
||||
home_md5 = koding.md5_check(home)
|
||||
dialog.ok('md5 Check', 'The md5 of your home folder is:', '[COLOR=dodgerblue]%s[/COLOR]'%home_md5)
|
||||
|
||||
guisettings = xbmc.translatePath('special://profile/guisettings.xml')
|
||||
guisettings_md5 = koding.md5_check(guisettings)
|
||||
dialog.ok('md5 Check', 'The md5 of your guisettings.xml:', '[COLOR=dodgerblue]%s[/COLOR]'%guisettings_md5)
|
||||
|
||||
mystring = 'This is just a random text string we\'ll get the md5 value of'
|
||||
myvalue = koding.md5_check(src=mystring,string=True)
|
||||
dialog.ok('md5 String Check', 'String to get md5 value of:', '[COLOR=dodgerblue]%s[/COLOR]'%mystring)
|
||||
dialog.ok('md5 String Check', 'The md5 value of your string:', '[COLOR=dodgerblue]%s[/COLOR]'%myvalue)
|
||||
~"""
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
SHAhash = hashlib.md5()
|
||||
if not os.path.exists(src) and not string:
|
||||
return -1
|
||||
|
||||
# If source is a file
|
||||
if string:
|
||||
return hashlib.md5(src).hexdigest()
|
||||
# If source is a file
|
||||
elif not os.path.isdir(src):
|
||||
return hashlib.md5(open(src,'rb').read()).hexdigest()
|
||||
|
||||
# If source is a directory
|
||||
else:
|
||||
try:
|
||||
for root, dirs, files in os.walk(src):
|
||||
for names in files:
|
||||
filepath = os.path.join(root,names)
|
||||
try:
|
||||
f1 = open(filepath, 'rb')
|
||||
except:
|
||||
f1.close()
|
||||
continue
|
||||
|
||||
while 1:
|
||||
# Read file in as little chunks
|
||||
buf = f1.read(4096)
|
||||
if not buf : break
|
||||
SHAhash.update(hashlib.md5(buf).hexdigest())
|
||||
f1.close()
|
||||
except:
|
||||
return -2
|
||||
|
||||
return SHAhash.hexdigest()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Merge_Dicts(*dict_args):
|
||||
"""
|
||||
Send through any number of dictionaries and get a return of one merged dictionary.
|
||||
Please note: If you have duplicate keys the value will be overwritten by the final
|
||||
dictionary to be checked. So if you send through dicts a-f and the same key exists
|
||||
in dicts a,e,f the final value for that key would be whatever is set in 'f'.
|
||||
|
||||
CODE: Merge_Dicts(*dict_args)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) *dict_args - Enter as many dictionaries as you want, these will be merged
|
||||
into one final dictionary. Please send each dictionary through as a new paramater.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dict1 = {'1':'one','2':'two'}
|
||||
dict2 = {'3':'three','4':'four','5':'five'}
|
||||
dict3 = {'6':'six','7':'seven'}
|
||||
dict4 = {'1':'three','8':'eight'}
|
||||
|
||||
mytext = 'Original Dicts:\ndict1 = %s\ndict2 = %s\ndict3 = %s\ndict4 = %s\n\n'%(repr(dict1),repr(dict2),repr(dict3),repr(dict4))
|
||||
mytext += 'Merged dictionaries (1-3): %s\n\n'%repr(koding.Merge_Dicts(dict1,dict2,dict3))
|
||||
mytext += 'Merged dictionaries (1-4): %s\n\n'%repr(koding.Merge_Dicts(dict1,dict2,dict3,dict4))
|
||||
mytext += "[COLOR = gold]IMPORTANT:[/COLOR]\nNotice how on the last run the key '1'now has a value of three.\nThis is because dict4 also contains that same key."
|
||||
Text_Box('Merge_Dicts',mytext)
|
||||
~"""
|
||||
result = {}
|
||||
for dictionary in dict_args:
|
||||
if Data_Type(dictionary)=='dict':
|
||||
result.update(dictionary)
|
||||
return result
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Parse_XML(source, block, tags):
|
||||
"""
|
||||
Send through the contents of an XML file and pull out a list of matching
|
||||
items in the form of dictionaries. When checking your results you should
|
||||
allow for lists to be returned, by default each tag found in the xml will
|
||||
be returned as a string but if multiple entries of the same tag exists your
|
||||
dictionary item will be a list. Although this can be used for many uses this
|
||||
was predominantly added for support of XML's which contain multiple links to video
|
||||
files using things like <sublink>. When checking to see if a string or list has been
|
||||
returned you can use the Data_Type function from Koding which will return 'str' or 'list'.
|
||||
|
||||
CODE: Parse_XML(source, block, tags)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
source - This is the original source file, this must already be read into
|
||||
memory as a string so made sure you've either used Open_URL or Text_File to
|
||||
read the contents before sending through.
|
||||
|
||||
block - This is the master tag you want to use for creating a dictionary of items.
|
||||
For example if you have an xml which contains multiple tags called <item> and you wanted
|
||||
to create a dictionary of sub items found in each of these you would just use 'item'.
|
||||
|
||||
tags - This is a list of tags you want to return in your dictionary, so lets say each <item>
|
||||
section contains <link>, <title> and <thumb> tags you can return a dictionary of all those
|
||||
items by sending through ['link','title','thumb']
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('DICTIONARY OF ITEMS','We will now attempt to return a list of the source details pulled from the official Kodi repository addon.xml')
|
||||
xml_file = koding.Physical_Path('special://xbmc/addons/repository.xbmc.org/addon.xml')
|
||||
xml_file = koding.Text_File(xml_file,'r')
|
||||
xbmc.log(xml_file,2)
|
||||
repo_details = koding.Parse_XML(source=xml_file, block='extension', tags=['info','checksum','datadir'])
|
||||
counter = 0
|
||||
for item in repo_details:
|
||||
dialog.ok( 'REPO %s'%(counter+1),'info path: [COLOR dodgerblue]%s[/COLOR]\nchecksum path: [COLOR dodgerblue]%s[/COLOR]\ndatadir: [COLOR dodgerblue]%s[/COLOR]' % (repo_details[counter]['info'],repo_details[counter]['checksum'],repo_details[counter]['datadir']) )
|
||||
counter += 1
|
||||
~"""
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
soup = BeautifulSoup(source)
|
||||
my_return = []
|
||||
|
||||
# Grab all the blocks of xml to search
|
||||
for myblock in soup.findAll(block):
|
||||
if myblock:
|
||||
my_dict = {}
|
||||
for tag in tags:
|
||||
newsoup = BeautifulSoup(str(myblock))
|
||||
newtag = newsoup.findAll(tag)
|
||||
if newtag:
|
||||
xbmc.log(repr(newtag),2)
|
||||
|
||||
# If only one instance is found we add to dict as a plain string
|
||||
if len(newtag)==1:
|
||||
newtag = str(newtag).split(r'>')[1]
|
||||
newtag = newtag.split(r'<')[0]
|
||||
|
||||
# Otherwise we add to dict as a list
|
||||
else:
|
||||
tag_array = []
|
||||
for item in newtag:
|
||||
mynewtag = str(item).split(r'>')[1]
|
||||
mynewtag = mynewtag.split(r'<')[0]
|
||||
tag_array.append(mynewtag)
|
||||
newtag = tag_array
|
||||
my_dict[tag] = newtag
|
||||
my_return.append(my_dict)
|
||||
return my_return
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Table_Convert(url, contents={}, table=0):
|
||||
"""
|
||||
Open a web page which a table and pull out the contents of that table
|
||||
into a list of dictionaries with your own custom keys.
|
||||
|
||||
CODE: Table_Convert(url, contents, table)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
url - The url you want to open and pull data from
|
||||
|
||||
contents - Send through a dictionary of the keys you want to assign to
|
||||
each of the cells. The format would be: {my_key : position}
|
||||
You can pull out as many cells as you want, so if your table has 10 columns
|
||||
but you only wanted to pull data for cells 2,4,5,6,8 then you could do so
|
||||
by setting contents to the following params:
|
||||
contents = {"name 1":2, "name 2":4, "name 3":5, "name 4":6, "name 5":8}
|
||||
|
||||
table - By default this is set to zero, this is to be used if there's
|
||||
multiple tables on the page you're accessing. Remeber to start at zero,
|
||||
so if you want to access the 2nd table on the page it will be table=1.
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('TABLE DATA','Let\'s pull some details from the proxy list table found at:\nhttps://free-proxy-list.net.')
|
||||
proxies = koding.Table_Convert(url='https://free-proxy-list.net', contents={"ip":0,"port":1}, table=0)
|
||||
mytext = '[COLOR dodgerblue]Here are some proxies:[/COLOR]\n'
|
||||
for item in proxies:
|
||||
mytext += '\nIP: %s\nPort: %s\n[COLOR steelblue]----------------[/COLOR]'%(item['ip'],item['port'])
|
||||
koding.Text_Box('MASTER PROXY LIST',mytext)
|
||||
~"""
|
||||
from web import Open_URL
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
table_list=[]
|
||||
content = Open_URL(url)
|
||||
if content:
|
||||
rawdata = Parse_XML(content,'table',['td'])
|
||||
|
||||
# Work out the amount of columns in the table
|
||||
soup = BeautifulSoup(content)
|
||||
my_return = []
|
||||
mytable = soup.findAll('table')[table]
|
||||
if mytable:
|
||||
newsoup = BeautifulSoup(str(mytable))
|
||||
newtag = str( newsoup.find('tr') )
|
||||
if '<th' in newtag:
|
||||
count_tag = '<th'
|
||||
else:
|
||||
count_tag = '<td'
|
||||
cells = newtag.count(count_tag)
|
||||
|
||||
rows = [rawdata[table]['td'][x:x+cells] for x in range(0, len(rawdata[0]['td']), cells)]
|
||||
for row in rows:
|
||||
my_dict = {}
|
||||
for cell_name in contents:
|
||||
my_dict[cell_name] = row[contents[cell_name]]
|
||||
table_list.append(my_dict)
|
||||
return table_list
|
||||
else:
|
||||
return {}
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Split_Lines(raw_string, size):
|
||||
"""
|
||||
Splits up a piece of text into a list of lines x amount of chars in length.
|
||||
|
||||
CODE: koding.Split_Lines(raw_string, size)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) raw_string - This is the text you want split up into lines
|
||||
|
||||
(*) size - This is the maximum size you want the line length to be (in characters)
|
||||
|
||||
EXAMPLE CODE:
|
||||
raw_string = 'This is some test code, let\'s take a look and see what happens if we split this up into lines of 20 chars per line'
|
||||
dialog.ok('ORIGINAL TEXT',raw_string)
|
||||
my_list = koding.Split_Lines(raw_string,20)
|
||||
koding.Text_Box('List of lines',str(my_list))
|
||||
~"""
|
||||
final_list=[""]
|
||||
for i in raw_string:
|
||||
length = len(final_list)-1
|
||||
if len(final_list[length]) < size:
|
||||
final_list[length]+=i
|
||||
else:
|
||||
final_list += [i]
|
||||
return final_list
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Split_List(source, split_point, include='all'):
|
||||
"""
|
||||
Send through a list and split it up into multiple lists. You can choose to create
|
||||
lists of every x amount of items or you can split at every nth item and only include
|
||||
specific items in your new list.
|
||||
|
||||
CODE: Split_List(source, split_point, include)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
source - This is the original list you want split
|
||||
|
||||
split_point - This is the postition you want to split your list at. For example:
|
||||
original list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
|
||||
Lets say we want to split it at every 5 items the split point would be 5
|
||||
|
||||
include - You have 3 options here:
|
||||
|
||||
'all' - This will add all items to your lists, so in the example above you will
|
||||
get a return of ([1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15])
|
||||
|
||||
[] - Send through a list of positions you want to include, based on the example
|
||||
above and using include=[0,1,3] you will get a return of ([1,2,4],[6,7,9],[11,12,14])
|
||||
|
||||
int - Send through an integer and it will return everything up to that position,
|
||||
based on the example above and using include=3 you will get a return of
|
||||
([1,2,3],[6,7,8],[11,12,13])
|
||||
|
||||
|
||||
EXAMPLE CODE:
|
||||
my_list = ['one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve','thirteen','fourteen','fifteen']
|
||||
dialog.ok('SPLIT LIST 1','We will now attempt to split the following list into blocks of 5:\n%s'%my_list)
|
||||
newlist = koding.Split_List(source=my_list, split_point=5)
|
||||
dialog.ok('RESULTS','Our returned var:\n%s'%newlist)
|
||||
dialog.ok('SPLIT LIST 2','We will now attempt to split the same list at every 5th item position and only show items [0,1,3]')
|
||||
newlist = koding.Split_List(source=my_list, split_point=5, include=[0,1,3])
|
||||
dialog.ok('RESULTS','Our returned var:\n%s'%newlist)
|
||||
dialog.ok('SPLIT LIST 3','We will now attempt to split the same list at every 5th item position and only show the first 3 items.')
|
||||
newlist = koding.Split_List(source=my_list, split_point=5, include=3)
|
||||
dialog.ok('RESULTS','Our returned var:\n%s'%newlist)
|
||||
~"""
|
||||
if include == 'all':
|
||||
return [source[x:x+split_point] for x in range(0, len(source), split_point)]
|
||||
elif Data_Type(include) == 'list':
|
||||
mylists = [source[x:x+split_point] for x in range(0, len(source), split_point)]
|
||||
return_list = []
|
||||
for item in mylists:
|
||||
mylist = []
|
||||
for keep in include:
|
||||
mylist.append(item[keep])
|
||||
return_list.append(mylist)
|
||||
return return_list
|
||||
elif Data_Type(include) == 'int':
|
||||
return [source[x:x+include] for x in range(0, len(source), split_point)]
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def String(code='', source=''):
|
||||
"""
|
||||
This will return the relevant language skin as set in the
|
||||
resources/language folder for your add-on. By default you'll get
|
||||
the language string returned from your current running add-on
|
||||
but if you send through another add-on id you can grab from
|
||||
any add-on or even the built-in kodi language strings.
|
||||
|
||||
CODE: String(code, [source])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) code - This is the language string code set in your strings.po file.
|
||||
|
||||
source - By default this is set to a blank string and will
|
||||
use your current add-on id. However if you want to pull the string
|
||||
from another add-on just enter the add-on id in here. If you'd prefer
|
||||
to pull from the built-in kodi resources files just set as 'system'.
|
||||
|
||||
EXAMPLE CODE:
|
||||
kodi_string = koding.String(code=10140, source='system')
|
||||
koding_string = koding.String(code=30825, source='script.module.python.koding.aio')
|
||||
dialog.ok('SYSTEM STRING','The string [COLOR=dodgerblue]10140[/COLOR] pulled from the default system language resources is:','[COLOR=gold]%s[/COLOR]' % kodi_string)
|
||||
dialog.ok('PYTHON KODING STRING','The string [COLOR=dodgerblue]30825[/COLOR] pulled from the Python Koding language resources is:','[COLOR=gold]%s[/COLOR]' % koding_string)
|
||||
~"""
|
||||
import xbmcaddon
|
||||
from addons import Caller
|
||||
if source == '':
|
||||
source = Caller()
|
||||
if source != 'system':
|
||||
addon_id = xbmcaddon.Addon(id=source)
|
||||
mystring = addon_id.getLocalizedString(code)
|
||||
else:
|
||||
mystring = xbmc.getLocalizedString(code)
|
||||
return mystring
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Remove_Formatting(string, color=True, bold=True, italic=True, spaces=True, dots=True, dashes=True):
|
||||
"""
|
||||
This will cleanup a Kodi string, it can remove color, bold and italic tags as well as
|
||||
preceding spaces, dots and dashes. Particularly useful if you want to show the names of
|
||||
add-ons in alphabetical order where add-on names have deliberately had certain formatting
|
||||
added to them to get them to always show at the top of lists.
|
||||
|
||||
CODE: Remove_Formatting(string, [color, bold, italic, spaces, dots, dashes])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) string - This is string you want to remove formatting from.
|
||||
|
||||
color - By default this is set to true and all references to the color tag
|
||||
will be removed, set this to false if you don't want color formatting removed.
|
||||
|
||||
bold - By default this is set to true and all references to the bold tag
|
||||
will be removed, set this to false if you don't want bold formatting removed.
|
||||
|
||||
italic - By default this is set to true and all references to the italic tag
|
||||
will be removed, set this to false if you don't want italic formatting removed.
|
||||
|
||||
spaces - By default this is set to true and any spaces at the start of the text
|
||||
will be removed, set this to false if you don't want the spaces removed.
|
||||
|
||||
dots - By default this is set to true and any dots (.) at the start of the text
|
||||
will be removed, set this to false if you don't want the dots removed.
|
||||
|
||||
dashes - By default this is set to true and any dashes (-) at the start of the text
|
||||
will be removed, set this to false if you don't want the dashes removed.
|
||||
|
||||
EXAMPLE CODE:
|
||||
mystring = '...-- [I]This[/I] is the [COLOR dodgerblue]ORIGINAL[/COLOR] [B][COLOR cyan]TEXT[/COLOR][/B]'
|
||||
dialog.ok('ORIGINAL TEXT','Below is the original text we\'re going to try and clean up:[CR]%s'%mystring)
|
||||
dialog.ok('DOTS REMOVED','[COLOR gold]Original:[/COLOR][CR]%s[CR][COLOR gold]This is with only dots set to True:[/COLOR][CR]%s'%(mystring,koding.Remove_Formatting(mystring, color=False, bold=False, italic=False, spaces=False, dots=True, dashes=False)))
|
||||
dialog.ok('DOTS & DASHES REMOVED','[COLOR gold]Original:[/COLOR][CR]%s[CR][COLOR gold]This is with dots & dashes set to True:[/COLOR][CR]%s'%(mystring,koding.Remove_Formatting(mystring, color=False, bold=False, italic=False, spaces=False, dots=True, dashes=True)))
|
||||
dialog.ok('DOTS, DASHES & SPACES REMOVED','[COLOR gold]Original:[/COLOR][CR]%s[CR][COLOR gold]This is with dots, dashes & spaces set to True:[/COLOR][CR]%s'%(mystring,koding.Remove_Formatting(mystring, color=False, bold=False, italic=False, spaces=True, dots=True, dashes=True)))
|
||||
dialog.ok('ALL FORMATTING REMOVED','[COLOR gold]Original:[/COLOR][CR]%s[CR][COLOR gold]This is with all options set to True:[/COLOR][CR]%s'%(mystring,koding.Remove_Formatting(mystring)))
|
||||
~"""
|
||||
import re
|
||||
if color:
|
||||
if '[COLOR' in string:
|
||||
string = string.replace('[/COLOR]','')
|
||||
colorlist = re.compile(r'\[COLOR(.+?)\]').findall(string)
|
||||
for colors in colorlist:
|
||||
string = string.replace('[COLOR%s]'%colors,'')
|
||||
if spaces:
|
||||
string = string.strip()
|
||||
if bold:
|
||||
string = string.replace('[B]','').replace('[/B]','')
|
||||
if spaces:
|
||||
string = string.strip()
|
||||
if italic:
|
||||
string = string.replace('[I]','').replace('[/I]','')
|
||||
if spaces:
|
||||
string = string.strip()
|
||||
if dots:
|
||||
while string.startswith('.'):
|
||||
string = string[1:]
|
||||
if spaces:
|
||||
string = string.strip()
|
||||
if dashes:
|
||||
while string.startswith('-'):
|
||||
string = string[1:]
|
||||
if spaces:
|
||||
string = string.strip()
|
||||
if spaces:
|
||||
string = string.strip()
|
||||
|
||||
return string
|
||||
711
script.module.python.koding.aio/lib/koding/video.py
Normal file
711
script.module.python.koding.aio/lib/koding/video.py
Normal file
@@ -0,0 +1,711 @@
|
||||
# -*- 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 requests
|
||||
import shutil
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
from guitools import Show_Busy
|
||||
from systemtools import Last_Error
|
||||
|
||||
dp = xbmcgui.DialogProgress()
|
||||
check_started = xbmc.translatePath('special://profile/addon_data/script.module.python.koding.aio/temp/playback_in_progress')
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Check_Playback(ignore_dp=False,timeout=10):
|
||||
"""
|
||||
This function will return true or false based on video playback. Simply start a stream
|
||||
(whether via an add-on, direct link to URL or local storage doesn't matter), the code will
|
||||
then work out if playback is successful. This uses a number of checks and should take into
|
||||
account all potential glitches which can occur during playback. The return should happen
|
||||
within a second or two of playback being successful (or not).
|
||||
|
||||
CODE: Check_Playback()
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
ignore_dp - By default this is set to True but if set to False
|
||||
this will ignore the DialogProgress window. If you use a DP while
|
||||
waiting for the stream to start then you'll want to set this True.
|
||||
Please bare in mind the reason this check is in place and enabled
|
||||
by default is because some streams do bring up a DialogProgress
|
||||
when initiated (such as f4m proxy links) and disabling this check
|
||||
in those circumstances can cause false positives.
|
||||
|
||||
timeout - This is the amount of time you want to allow for playback
|
||||
to start before sending back a response of False. Please note if
|
||||
ignore_dp is set to True then it will also add a potential 10s extra
|
||||
to this amount if a DialogProgress window is open. The default setting
|
||||
for this is 10s.
|
||||
|
||||
EXAMPLE CODE:
|
||||
xbmc.Player().play('http://totalrevolution.tv/videos/python_koding/Browse_To_Folder.mov')
|
||||
isplaying = koding.Check_Playback()
|
||||
if isplaying:
|
||||
dialog.ok('PLAYBACK SUCCESSFUL','Congratulations, playback was successful')
|
||||
xbmc.Player().stop()
|
||||
else:
|
||||
dialog.ok('PLAYBACK FAILED','Sorry, playback failed :(')
|
||||
~"""
|
||||
if not ignore_dp:
|
||||
isdialog = True
|
||||
counter = 1
|
||||
|
||||
# Check if the progress window is active and wait for playback
|
||||
while isdialog and counter < 60:
|
||||
if xbmc.getCondVisibility('Window.IsActive(progressdialog)'):
|
||||
try:
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
break
|
||||
except:
|
||||
pass
|
||||
xbmc.log('### Current Window: %s' % xbmc.getInfoLabel('System.CurrentWindow'))
|
||||
xbmc.log('### Current XML: %s' % xbmc.getInfoLabel('Window.Property(xmlfile)'))
|
||||
xbmc.log('### Progress Dialog active, sleeping for %s seconds' % counter)
|
||||
xbmc.sleep(1000)
|
||||
if xbmc.getCondVisibility('Window.IsActive(progressdialog)') or (xbmc.getInfoLabel('Window.Property(xmlfile)') == 'DialogProgress.xml'):
|
||||
isdialog = True
|
||||
else:
|
||||
isdialog = False
|
||||
counter += 1
|
||||
xbmc.log('counter: %s' % counter)
|
||||
|
||||
# Given the DialogProgress 10 seconds to finish and it's still up - time to close it
|
||||
if counter >= 10:
|
||||
try:
|
||||
xbmc.log('attempting to send click to close dp')
|
||||
xbmc.executebuiltin('SendClick()')
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
try:
|
||||
dp.close()
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
xbmc.log('### FAILED TO CLOSE DP')
|
||||
try:
|
||||
dp.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
isplaying = xbmc.Player().isPlaying()
|
||||
counter = 1
|
||||
if xbmc.Player().isPlayingAudio():
|
||||
return True
|
||||
# If xbmc player is not yet active give it some time to initialise
|
||||
while not isplaying and counter < timeout:
|
||||
xbmc.sleep(1000)
|
||||
isplaying = xbmc.Player().isPlaying()
|
||||
xbmc.log('### XBMC Player not yet active, sleeping for %s seconds' % counter)
|
||||
counter += 1
|
||||
|
||||
success = 0
|
||||
counter = 0
|
||||
|
||||
# If it's playing give it time to physically start streaming then attempt to pull some info
|
||||
if isplaying:
|
||||
xbmc.sleep(1000)
|
||||
while not success and counter < 5:
|
||||
try:
|
||||
if xbmc.Player().isPlayingVideo():
|
||||
infotag = xbmc.Player().getVideoInfoTag()
|
||||
vidtime = xbmc.Player().getTime()
|
||||
if vidtime > 0:
|
||||
success = 1
|
||||
|
||||
# If playback doesn't start automatically (buffering) we force it to play
|
||||
else:
|
||||
xbmc.log('### Playback active but time at zero, trying to unpause')
|
||||
xbmc.executebuiltin('PlayerControl(Play)')
|
||||
xbmc.sleep(2000)
|
||||
vidtime = xbmc.Player().getTime()
|
||||
if vidtime > 0:
|
||||
success = 1
|
||||
|
||||
# If no infotag or time could be pulled then we assume playback failed, try and stop the xbmc.player
|
||||
except:
|
||||
counter += 1
|
||||
xbmc.sleep(1000)
|
||||
|
||||
# Check if the busy dialog is still active from previous locked up playback attempt
|
||||
isbusy = xbmc.getCondVisibility('Window.IsActive(busydialog)')
|
||||
counter = 1
|
||||
while isbusy:
|
||||
xbmc.log('### Busy dialog active, sleeping for %ss' % counter)
|
||||
xbmc.sleep(1000)
|
||||
isbusy = xbmc.getCondVisibility('Window.IsActive(busydialog)')
|
||||
counter += 1
|
||||
if counter >= 5:
|
||||
xbmc.executebuiltin('Dialog.Close(busydialog)')
|
||||
|
||||
if not success:
|
||||
xbmc.executebuiltin('PlayerControl(Stop)')
|
||||
xbmc.log('### Failed playback, stopped stream')
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Last_Played():
|
||||
"""
|
||||
Return the link of the last played (or currently playing) video.
|
||||
This differs to the built in getPlayingFile command as that only shows details
|
||||
of the current playing file, these details can differ to the url which was
|
||||
originally sent through to initiate the stream. This Last_Played function
|
||||
directly accesses the database to get the REAL link which was initiated and
|
||||
will even return the plugin path if it's been played through an external add-on.
|
||||
|
||||
CODE: Last_Played()
|
||||
|
||||
EXAMPLE CODE:
|
||||
if koding.Play_Video('http://totalrevolution.tv/videos/python_koding/Browse_To_Folder.mov'):
|
||||
xbmc.sleep(3000)
|
||||
xbmc.Player().stop()
|
||||
last_vid = Last_Played()
|
||||
dialog.ok('VIDEO LINK','The link we just played is:\n\n%s'%last_vid)
|
||||
else:
|
||||
dialog.ok('PLAYBACK FAILED','Sorry this video is no longer available, please try using a different video link.')
|
||||
~"""
|
||||
from database import DB_Query
|
||||
from filetools import DB_Path_Check
|
||||
from vartools import Decode_String
|
||||
db_path = DB_Path_Check('MyVideos')
|
||||
sql = "SELECT files.strFilename as mystring, path.strPath as mybase FROM files JOIN path ON files.idPath=path.idPath ORDER BY files.lastPlayed DESC LIMIT 1"
|
||||
results = DB_Query(db_path, sql)
|
||||
try:
|
||||
if Decode_String(results[0]['mybase']).startswith('plugin://'):
|
||||
return Decode_String(results[0]['mystring'])
|
||||
else:
|
||||
return Decode_String(results[0]['mybase']+results[0]['mystring'])
|
||||
except:
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Link_Tester(video='', local_check=True, proxy_list=None, proxy_url='https://free-proxy-list.net/', ip_col=0, port_col=1, table=0):
|
||||
"""
|
||||
Send through a link and test whether or not it's playable on other devices.
|
||||
Many links include items in the query string which lock the content down to your
|
||||
IP only so what may open fine for you may not open for anyone else!
|
||||
|
||||
This function will attempt to load the page using a proxy. If when trying to access
|
||||
the link via a proxy the header size and content-type match then we assume the
|
||||
link will play on any device. This is not fool proof and could potentially return
|
||||
false positives depending on the security used on the website being accessed.
|
||||
|
||||
The return you'll get is a dictionary of the following items:
|
||||
|
||||
'plugin_path' - This will have the path for a plugin, it means the stream was
|
||||
originally passed through an add-on to get the final link. If this is not set
|
||||
to None then it "should" work on any device so long as that add-on is installed
|
||||
(e.g. YouTube).
|
||||
|
||||
'url' - This is the final resolved url which Kodi was playing, you need to check
|
||||
the status though to find out whether or not that link is locked to your IP only.
|
||||
|
||||
'status' - This will return one of the following status codes:
|
||||
good - The link should work on all IPs.
|
||||
|
||||
bad_link - The link was not valid, won't even play on your current Kodi setup.
|
||||
|
||||
proxy_fail - None of the proxies sent through worked.
|
||||
|
||||
locked - The url only works on this device, if this is the case consider using
|
||||
the plugin_path which should generally work on all devices (although this does
|
||||
depend on how the developer of that add-on coded up their add-on).
|
||||
|
||||
CODE: Link_Tester([proxy_list, url, ip_col, port_col, table])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
video - This is the url of the video you want to check
|
||||
|
||||
local_check - By default this is set to True and this function will first of
|
||||
all attempt to play the video locally with no proxy just to make sure the
|
||||
link is valid in the first place. If you want to skip this step then set
|
||||
this to False.
|
||||
|
||||
proxy_list - If you already have a list of proxies you want to test with
|
||||
send them through in the form of a list of dictionaries. Use the following
|
||||
format: [{"ip":"0.0.0.0","port":"80"},{"ip":"127.0.0.1","port":"8080"}]
|
||||
|
||||
proxy_url - If you want to scrape for online proxies and loop through until a
|
||||
working one has been found you can set the url here. If using this then
|
||||
proxy_list can be left as the default (None). If you open this Link_Tester
|
||||
function with no params the defaults are setup to grab from:
|
||||
free-proxy-list.net but there is no guarantee this will always
|
||||
work, the website may well change it's layout/security over time.
|
||||
|
||||
ip_col - If you've sent through a proxy_url then you'll need to set a column number
|
||||
for where in the table the IP address is stored. The default is 0
|
||||
|
||||
port_col - If you've sent through a proxy_url then you'll need to set a column number
|
||||
for where in the table the port details are stored. The default is 1
|
||||
|
||||
table - If you've sent through a proxy_url then you'll need to set a table number.
|
||||
The default is 0 - this presumes we need to use the first html table found on the
|
||||
page, if you require a different table then alter accordingly - remember zero is the
|
||||
first instance so if you want the 3rd table on the page you would set to 2.
|
||||
|
||||
EXAMPLE CODE:
|
||||
vid_test = Link_Tester(video='http://totalrevolution.tv/videos/python_koding/Browse_To_Folder.mov')
|
||||
if vid_test['status'] == 'bad_link':
|
||||
dialog.ok('BAD LINK','The link you sent through cannot even be played on this device let alone another one!')
|
||||
elif vid_test['status'] == 'proxy_fail':
|
||||
dialog.ok('PROXIES EXHAUSTED','It was not possible to get any working proxies as a result it\'s not possible to fully test whether this link will work on other devices.')
|
||||
elif vid_test['status'] == 'locked':
|
||||
dialog.ok('NOT PLAYABLE','Although you can play this link locally the tester was unable to play it when using a proxy so this is no good.')
|
||||
if vid_test['plugin_path']:
|
||||
dialog.ok('THERE IS SOME GOOD NEWS!','Although the direct link for this video won\'t work on other IPs it "should" be possible to open this using the following path:\n[COLOR dodgerblue]%s[/COLOR]'%vid_test['plugin_path'])
|
||||
else:
|
||||
dialog.ok('WORKING!!!','Congratulations this link can be resolved and added to your playlist.')
|
||||
~"""
|
||||
import random
|
||||
import urllib
|
||||
from guitools import Notify
|
||||
from vartools import Table_Convert
|
||||
from systemtools import System
|
||||
# xbmc.executebuiltin('RunScript(special://home/addons/script.module.python.koding.aio/lib/koding/localproxy.py)')
|
||||
Notify('PLEASE WAIT','Checking Link - Step 1','5000','Video.png')
|
||||
isplaying = xbmc.Player().isPlaying()
|
||||
|
||||
# If video not yet playing try playing it
|
||||
if not isplaying:
|
||||
xbmc.Player().play(video)
|
||||
|
||||
if Check_Playback(True):
|
||||
xbmclink = xbmc.Player().getPlayingFile()
|
||||
active_plugin = System(command='addonid')
|
||||
plugin_path = System(command='currentpath')
|
||||
vid_title = ''
|
||||
title_count = 0
|
||||
|
||||
while vid_title == '' and title_count < 10:
|
||||
vid_title = xbmc.getInfoLabel('Player.Title')
|
||||
xbmc.sleep(100)
|
||||
title_count += 1
|
||||
|
||||
xbmc.Player().stop()
|
||||
video_orig = Last_Played()
|
||||
xbmc.log('VIDEO: %s'%video_orig,2)
|
||||
if video_orig.startswith('plugin://'):
|
||||
video = xbmclink
|
||||
xbmc.log('NEW VIDEO: %s'%video,2)
|
||||
else:
|
||||
video = video_orig
|
||||
r = requests.head(url=video, timeout=5)
|
||||
orig_header = r.headers
|
||||
try:
|
||||
orig_size = orig_header['Content-Length']
|
||||
except:
|
||||
orig_size = 0
|
||||
try:
|
||||
orig_type = orig_header['Content-Type']
|
||||
except:
|
||||
orig_type = ''
|
||||
proxies = Table_Convert(url=proxy_url, contents={"ip":ip_col,"port":port_col}, table=table)
|
||||
myproxies = []
|
||||
used_proxies = []
|
||||
for item in proxies:
|
||||
myproxies.append({'http':'http://%s:%s'%(item['ip'],item['port']),'https':'https://%s:%s'%(item['ip'],item['port'])})
|
||||
success = False
|
||||
if video_orig.startswith('plugin://'):
|
||||
dp.create('[COLOR gold]CHECKING PROXIES[/COLOR]','This video is being parsed through another add-on so using the plugin path should work. Now checking the final resolved link...','')
|
||||
else:
|
||||
dp.create('[COLOR gold]CHECKING PROXIES[/COLOR]','Please wait...','')
|
||||
|
||||
counter = 1
|
||||
while (not success) and (len(myproxies) > 0):
|
||||
dp.update(counter/len(myproxies),'Checking proxy %s'%counter)
|
||||
counter += 1
|
||||
proxychoice = random.choice( range(0,len(myproxies)) )
|
||||
currentproxy = myproxies[proxychoice]
|
||||
|
||||
# Find a working proxy and play the video through it
|
||||
try:
|
||||
xbmc.log(repr(currentproxy),2)
|
||||
r = requests.head(url=video, proxies=currentproxy, timeout=5)
|
||||
headers = r.headers
|
||||
try:
|
||||
new_size = headers['Content-Length']
|
||||
except:
|
||||
new_size = 0
|
||||
try:
|
||||
new_type = headers['Content-Type']
|
||||
except:
|
||||
new_type = ''
|
||||
xbmc.log('orig size: %s'%orig_size,2)
|
||||
xbmc.log('new size: %s'%new_size,2)
|
||||
xbmc.log('orig type: %s'%orig_type,2)
|
||||
xbmc.log('new type: %s'%new_type,2)
|
||||
xbmc.log('VIDEO: %s'%video,2)
|
||||
if orig_size != 0 and (orig_size==new_size) and (orig_type==new_type):
|
||||
dp.close()
|
||||
success = True
|
||||
except:
|
||||
xbmc.log('failed with proxy: %s'%currentproxy,2)
|
||||
|
||||
myproxies.pop(proxychoice)
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
break
|
||||
plugin_path = None
|
||||
if video_orig.startswith('plugin://'):
|
||||
plugin_path = video_orig
|
||||
if len(myproxies)==0 and not success:
|
||||
return {"plugin_path":plugin_path, "url":video, "status":"proxy_fail"}
|
||||
elif not success:
|
||||
return {"plugin_path":plugin_path, "url":video, "status":"locked"}
|
||||
else:
|
||||
return {"plugin_path":plugin_path, "url":video, "status":"good"}
|
||||
else:
|
||||
return {"plugin_path":None, "url":video, "status":"bad_link"}
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def M3U_Selector(url,post_type='get',header='Stream Selection'):
|
||||
"""
|
||||
Send through an m3u/m3u8 playlist and have the contents displayed via a dialog select.
|
||||
The return will be a dictionary of 'name' and 'url'. You can send through either
|
||||
a locally stored filepath or an online URL.
|
||||
|
||||
This function will try it's best to pull out the relevant playlist details even if the
|
||||
web page isn't a correctly formatted m3u playlist (e.g. an m3u playlist embedded into
|
||||
a blog page).
|
||||
|
||||
CODE: M3U_Selector(url, [post_type, header])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
(*) url - The location of your m3u file, this can be local or online
|
||||
|
||||
post_type - If you need to use POST rather than a standard query string
|
||||
in your url set this to 'post', by default it's set to 'get'.
|
||||
|
||||
header - This is the header you want to appear at the top of your dialog
|
||||
selection window, by default it's set to "Stream Selection"
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('M3U SELECTOR','We will now call this function using the following url:','','[COLOR dodgerblue]http://totalrevolution.tv/videos/playlists/youtube.m3u[/COLOR]')
|
||||
|
||||
# This example uses YouTube plugin paths but any playable paths will work
|
||||
vid = koding.M3U_Selector(url='http://totalrevolution.tv/videos/playlists/youtube.m3u')
|
||||
|
||||
|
||||
# Make sure there is a valid link returned
|
||||
if vid:
|
||||
playback = koding.Play_Video(video=vid['url'], showbusy=False)
|
||||
if playback:
|
||||
dialog.ok('SUCCESS!','Congratulations the playback was successful!')
|
||||
xbmc.Player().stop()
|
||||
else:
|
||||
dialog.ok('OOPS!','Looks like something went wrong there, the playback failed. Check the links are still valid.')
|
||||
~"""
|
||||
from web import Open_URL
|
||||
from vartools import Cleanup_String, Find_In_Text
|
||||
from filetools import Text_File
|
||||
success = False
|
||||
if url.startswith('http'):
|
||||
content = Open_URL(url=url, post_type=post_type, timeout=10)
|
||||
else:
|
||||
try:
|
||||
url = xbmc.translatePath(url)
|
||||
except:
|
||||
pass
|
||||
content = Text_File(url,'r')
|
||||
if content:
|
||||
newcontent = content.splitlines()
|
||||
name_array = []
|
||||
url_array = []
|
||||
name = ''
|
||||
for line in newcontent:
|
||||
line = line.strip()
|
||||
# Grab the name of the stream
|
||||
if line.startswith('#EXT'):
|
||||
name = line.split(',')
|
||||
name.pop(0)
|
||||
name = ''.join(name)
|
||||
# Grab the url(s) of the stream
|
||||
if name != '' and line != '' and not line.startswith('#EXT'):
|
||||
name_array.append(Cleanup_String(name))
|
||||
line = line.replace('<br>','').replace('<br />','').replace('<br/>','')
|
||||
line = line.replace('</p>','').replace('</div>','').replace('</class>','')
|
||||
xbmc.log('line: %s'%line)
|
||||
if 'm3u' in line or 'm3u8' in line:
|
||||
line = 'LIST~'+line
|
||||
if 'src="' in line:
|
||||
line = Find_In_Text(content=line, start='src="', end='"')[0]
|
||||
url_array.append(line)
|
||||
name = ''
|
||||
line = ''
|
||||
# If there is only one entry with no names/comments just return as unknown with the link
|
||||
if not '#EXT' in content:
|
||||
return {'name' : 'Unknown', 'url' : line}
|
||||
|
||||
# If there's a list we show a dialog select of the available links
|
||||
if len(name_array) > 0:
|
||||
choice = xbmcgui.Dialog().select(header, name_array)
|
||||
if choice >= 0:
|
||||
|
||||
# If the selection is a final url and not a list of multiple links
|
||||
if not url_array[choice].startswith('LIST~'):
|
||||
success = True
|
||||
return {'name' : name_array[choice], 'url' : url_array[choice]}
|
||||
|
||||
# List of multiple links detected, give option of which link to play
|
||||
else:
|
||||
clean_link = url_array[choice].replace('LIST~','')
|
||||
content = Open_URL(url=clean_link, timeout=10)
|
||||
if content:
|
||||
newcontent = content.splitlines()
|
||||
name_array = []
|
||||
url_array = []
|
||||
name = ''
|
||||
counter = 1
|
||||
for line in newcontent:
|
||||
# Show name as link 1,2,3,4 etc.
|
||||
if line.startswith('#EXT'):
|
||||
name = 'LINK '+str(counter)
|
||||
# Grab the link(s) to the video
|
||||
if name != '' and line != '' and not line.startswith('#EXT'):
|
||||
name_array.append(name)
|
||||
line = line.replace('<br>','').replace('<br />','').replace('<br/>','')
|
||||
line = line.replace('</p>','').replace('</div>','').replace('</class>','')
|
||||
url_array.append(line)
|
||||
name = ''
|
||||
line = ''
|
||||
counter += 1
|
||||
# If there is only one entry with no names/comments just return as unknown with the link
|
||||
if not '#EXT' in content:
|
||||
return {'name' : 'Unknown', 'url' : line}
|
||||
|
||||
# Give option of which link to play in case of multiple links available
|
||||
if len(name_array) > 0:
|
||||
choice = xbmcgui.Dialog().select(header, name_array)
|
||||
if choice >= 0:
|
||||
success = True
|
||||
return {'name' : name_array[choice], 'url' : url_array[choice]}
|
||||
if not success:
|
||||
xbmcgui.Dialog().ok('NO LINKS FOUND','Sorry no valid links could be found for this stream.')
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Play_Video(video,showbusy=True,content='video',ignore_dp=False,timeout=10, item=None, player=xbmc.Player(), resolver=None):
|
||||
"""
|
||||
This will attempt to play a video and return True or False on
|
||||
whether or not playback was successful. This function is similar
|
||||
to Check_Playback but this actually tries a number of methods to
|
||||
play the video whereas Check_Playback does not actually try to
|
||||
play a video - it will just return True/False on whether or not
|
||||
a video is currently playing.
|
||||
|
||||
If you have m3u or m3u8 playlist links please use the M3U_Selector
|
||||
function to get the final resolved url.
|
||||
|
||||
CODE: Play_Video(video, [showbusy, content, ignore_dp, timeout, item])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) video - This is the path to the video, this can be a local
|
||||
path, online path or a channel number from the PVR.
|
||||
|
||||
showbusy - By default this is set to True which means while the
|
||||
function is attempting to playback the video the user will see the
|
||||
busy dialog. Set to False if you prefer this not to appear but do
|
||||
bare in mind a user may navigate to another section and try playing
|
||||
something else if they think this isn't doing anything.
|
||||
|
||||
content - By default this is set to 'video', however if you're
|
||||
passing through audio you may want to set this to 'music' so the
|
||||
system can correctly set the tags for artist, song etc.
|
||||
|
||||
ignore_dp - By default this is set to True but if set to False
|
||||
this will ignore the DialogProgress window. If you use a DP while
|
||||
waiting for the stream to start then you'll want to set this True.
|
||||
Please bare in mind the reason this check is in place and enabled
|
||||
by default is because some streams do bring up a DialogProgress
|
||||
when initiated (such as f4m proxy links) and disabling this check
|
||||
in those circumstances can cause false positives.
|
||||
|
||||
timeout - This is the amount of time you want to allow for playback
|
||||
to start before sending back a response of False. Please note if
|
||||
ignore_dp is set to True then it will also add a potential 10s extra
|
||||
to this amount if a DialogProgress window is open. The default setting
|
||||
for this is 10s.
|
||||
|
||||
item - By default this is set to None and in this case the metadata
|
||||
will be auto-populated from the previous Add_Dir so you'll just get the
|
||||
basics like title, thumb and description. If you want to send through your
|
||||
own metadata in the form of a dictionary you can do so and it will override
|
||||
the auto-generation. If anything else sent through no metadata will be set,
|
||||
you would use this option if you've already set metadata in a previous function.
|
||||
|
||||
player - By default this is set to xbmc.Player() but you can send through
|
||||
a different class/function if required.
|
||||
|
||||
resolver - By default this is set to urlresolver but if you prefer to use
|
||||
your own custom resolver then just send through that class when calling this
|
||||
function and the link sent through will be resolved by your custom resolver.
|
||||
|
||||
EXAMPLE CODE:
|
||||
isplaying = koding.Play_Video('http://totalrevolution.tv/videos/python_koding/Browse_To_Folder.mov')
|
||||
if isplaying:
|
||||
dialog.ok('PLAYBACK SUCCESSFUL','Congratulations, playback was successful')
|
||||
xbmc.Player().stop()
|
||||
else:
|
||||
dialog.ok('PLAYBACK FAILED','Sorry, playback failed :(')
|
||||
~"""
|
||||
|
||||
xbmc.log('### ORIGINAL VIDEO: %s'%video)
|
||||
if not resolver:
|
||||
import urlresolver
|
||||
resolver = urlresolver
|
||||
try: import simplejson as json
|
||||
except: import json
|
||||
|
||||
if not item:
|
||||
meta = {}
|
||||
for i in ['title', 'originaltitle', 'tvshowtitle', 'year', 'season', 'episode', 'genre', 'rating', 'votes',
|
||||
'director', 'writer', 'plot', 'tagline']:
|
||||
try:
|
||||
meta[i] = xbmc.getInfoLabel('listitem.%s' % i)
|
||||
except:
|
||||
pass
|
||||
meta = dict((k, v) for k, v in meta.iteritems() if not v == '')
|
||||
if 'title' not in meta:
|
||||
meta['title'] = xbmc.getInfoLabel('listitem.label')
|
||||
icon = xbmc.getInfoLabel('listitem.icon')
|
||||
item = xbmcgui.ListItem(path=video, iconImage =icon, thumbnailImage=icon)
|
||||
if content == "music":
|
||||
try:
|
||||
meta['artist'] = xbmc.getInfoLabel('listitem.artist')
|
||||
item.setInfo(type='Music', infoLabels={'title': meta['title'], 'artist': meta['artist']})
|
||||
except:
|
||||
item.setInfo(type='Video', infoLabels=meta)
|
||||
else:
|
||||
item.setInfo(type='Video', infoLabels=meta)
|
||||
|
||||
elif type(item).__name__ == 'dict':
|
||||
item.setInfo(type='Video', infoLabels=meta)
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
playback = False
|
||||
if showbusy:
|
||||
Show_Busy()
|
||||
|
||||
|
||||
# if a plugin path is sent we try activate window
|
||||
if video.startswith('plugin://'):
|
||||
try:
|
||||
xbmc.log('Attempting to play via xbmc.Player().play() method')
|
||||
player.play(video)
|
||||
playback = Check_Playback(ignore_dp,timeout)
|
||||
except:
|
||||
xbmc.log(Last_Error())
|
||||
|
||||
# If an XBMC action has been sent through we do an executebuiltin command
|
||||
elif video.startswith('ActivateWindow') or video.startswith('RunAddon') or video.startswith('RunScript') or video.startswith('PlayMedia'):
|
||||
try:
|
||||
xbmc.log('Attempting to play via xbmc.executebuiltin method')
|
||||
xbmc.executebuiltin('%s'%video)
|
||||
playback = Check_Playback(ignore_dp,timeout)
|
||||
except:
|
||||
xbmc.log(Last_Error())
|
||||
|
||||
elif ',' in video:
|
||||
# Standard xbmc.player method (a comma in url seems to throw urlresolver off)
|
||||
try:
|
||||
xbmc.log('Attempting to play via xbmc.Player.play() method')
|
||||
player.play('%s'%video, item)
|
||||
playback = Check_Playback(ignore_dp,timeout)
|
||||
|
||||
# Attempt to resolve via urlresolver
|
||||
except:
|
||||
try:
|
||||
xbmc.log('Attempting to resolve via urlresolver module')
|
||||
xbmc.log('video = %s'%video)
|
||||
hmf = resolver.HostedMediaFile(url=video, include_disabled=False, include_universal=True)
|
||||
if hmf.valid_url() == True:
|
||||
video = hmf.resolve()
|
||||
xbmc.log('### VALID URL, RESOLVED: %s'%video)
|
||||
player.play('%s' % video, item)
|
||||
playback = Check_Playback(ignore_dp,timeout)
|
||||
except:
|
||||
xbmc.log(Last_Error())
|
||||
|
||||
# Play from a db entry - untested
|
||||
elif video.isdigit():
|
||||
xbmc.log('### Video is digit, presuming it\'s a db item')
|
||||
command = ('{"jsonrpc": "2.0", "id":"1", "method": "Player.Open","params":{"item":{"channelid":%s}}}' % url)
|
||||
xbmc.executeJSONRPC(command)
|
||||
playback = Check_Playback(ignore_dp,timeout)
|
||||
|
||||
else:
|
||||
# Attempt to resolve via urlresolver
|
||||
try:
|
||||
xbmc.log('Attempting to resolve via urlresolver module')
|
||||
xbmc.log('video = %s'%video)
|
||||
hmf = resolver.HostedMediaFile(url=video, include_disabled=False, include_universal=True)
|
||||
if hmf.valid_url() == True:
|
||||
video = hmf.resolve()
|
||||
xbmc.log('### VALID URL, RESOLVED: %s'%video)
|
||||
player.play('%s' % video, item)
|
||||
playback = Check_Playback(ignore_dp,timeout)
|
||||
|
||||
# Standard xbmc.player method
|
||||
except:
|
||||
try:
|
||||
xbmc.log('Attempting to play via xbmc.Player.play() method')
|
||||
player.play('%s' % video, item)
|
||||
playback = Check_Playback(ignore_dp,timeout)
|
||||
except:
|
||||
xbmc.log(Last_Error())
|
||||
|
||||
xbmc.log('Playback status: %s' % playback)
|
||||
Show_Busy(False)
|
||||
counter = 1
|
||||
dialogprogress = xbmc.getCondVisibility('Window.IsActive(progressdialog)')
|
||||
if not ignore_dp:
|
||||
while dialogprogress:
|
||||
dp.create('Playback Good','Closing dialog...')
|
||||
xbmc.log('Attempting to close dp #%s'%counter)
|
||||
dp.close()
|
||||
xbmc.sleep(1000)
|
||||
counter += 1
|
||||
dialogprogress = xbmc.getCondVisibility('Window.IsActive(progressdialog)')
|
||||
|
||||
return playback
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Sleep_If_Playback_Active():
|
||||
"""
|
||||
This will allow you to pause code while kodi is playing audio or video
|
||||
|
||||
CODE: Sleep_If_Playback_Active()
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('PLAY A VIDEO','We will now attempt to play a video, once you stop this video you should see a dialog.ok message.')
|
||||
xbmc.Player().play('http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_stereo.avi')
|
||||
xbmc.sleep(3000) # Give kodi enough time to load up the video
|
||||
koding.Sleep_If_Playback_Active()
|
||||
dialog.ok('PLAYBACK FINISHED','The playback has now been finished so this dialog code has now been initiated')
|
||||
~"""
|
||||
isplaying = xbmc.Player().isPlaying()
|
||||
while isplaying:
|
||||
xbmc.sleep(500)
|
||||
isplaying = xbmc.Player().isPlaying()
|
||||
332
script.module.python.koding.aio/lib/koding/web.py
Normal file
332
script.module.python.koding.aio/lib/koding/web.py
Normal file
@@ -0,0 +1,332 @@
|
||||
# -*- 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 time
|
||||
import urllib
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
from systemtools import Python_Version
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Cleanup_URL(url):
|
||||
"""
|
||||
Clean a url, removes whitespaces and common buggy formatting when pulling from websites
|
||||
|
||||
CODE: Cleanup_URL(url)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) url - This is the main url you want cleaned up.
|
||||
|
||||
EXAMPLE CODE:
|
||||
raw_url = '" http://test.com/video/"/'
|
||||
clean_url = koding.Cleanup_URL(raw_url)
|
||||
dialog.ok('CLEANUP URL', 'Orig: %s'%raw_url,'Clean: %s'%clean_url)
|
||||
~"""
|
||||
from HTMLParser import HTMLParser
|
||||
|
||||
bad_chars = ['/','\\',':',';','"',"'"]
|
||||
url = url.strip()
|
||||
|
||||
while url[0] in bad_chars or url[-1] in bad_chars:
|
||||
if url[-1] in bad_chars:
|
||||
url = url[:-1]
|
||||
if url[0] in bad_chars:
|
||||
url = url[1:]
|
||||
url = url.strip()
|
||||
return HTMLParser().unescape(url)
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Delete_Cookies(filename='cookiejar'):
|
||||
"""
|
||||
This will delete your cookies file.
|
||||
|
||||
CODE: Delete_Cookies([filename])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
filename - By default this is set to the filename 'cookiejar'.
|
||||
This is the default cookie filename which is created by the Open_URL
|
||||
function but you can use any name you want and this function will
|
||||
return True or False on whether or not it's successfully been removed.
|
||||
|
||||
EXAMPLE CODE:
|
||||
Open_URL(url='http://google.com',cookiejar='google')
|
||||
dialog.ok('GOOGLE COOKIES CREATED','We have just opened a page to google.com, if you check your addon_data folder for your add-on you should see a cookies folder and in there should be a cookie called "google". When you press OK this will be removed.')
|
||||
koding.Delete_Cookies(filename='google')
|
||||
~"""
|
||||
from addons import Addon_Info
|
||||
Addon_Version = Addon_Info(id='version')
|
||||
Addon_Profile = Addon_Info(id='profile')
|
||||
Cookie_File = os.path.join(Addon_Profile,'cookies',filename)
|
||||
if xbmcvfs.delete(Cookie_File):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Download(url, dest, dp=None, timeout=5):
|
||||
"""
|
||||
This will download a file, currently this has to be a standard download link which doesn't require cookies/login.
|
||||
|
||||
CODE: Download(src,dst,[dp])
|
||||
dp is optional, by default it is set to false
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) src - This is the source file, the URL to your download. If you attempted to download an item but it's not behaving the way you think it should (e.g. a zip file not unzipping) then change the extension of the downloaded file to .txt and open up in a text editor. You'll most likely find it's just a piece of text that was returned from the URL you gave and it should have details explaining why it failed. Could be that's the wrong URL, it requires some kind of login, it only accepts certain user-agents etc.
|
||||
|
||||
(*) dst - This is the destination file, make sure it's a physical path and not "special://...". Also remember you need to add the actual filename to the end of the path, so if we were downloading something to the "downloads" folder and we wanted the file to be called "test.txt" we would use this path: dst = "downloads/test.txt". Of course the downloads folder would actually need to exist otherwise it would fail and based on this poor example the downloads folder would be at root level of your device as we've not specified a path prior to that so it just uses the first level that's accessible.
|
||||
|
||||
dp - This is optional, if you pass through the dp function as a DialogProgress() then you'll get to see the progress of the download. 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.
|
||||
|
||||
timeout - By default this is set to 5. This is the max. amount of time you want to allow for checking whether or
|
||||
not the url is a valid link and can be accessed via the system.
|
||||
|
||||
EXAMPLE CODE:
|
||||
src = 'http://noobsandnerds.com/portal/Bits%20and%20bobs/Documents/user%20guide%20of%20the%20gyro%20remote.pdf'
|
||||
dst = 'special://home/remote.pdf'
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('Downloading File','Please Wait')
|
||||
koding.Download(src,dst,dp)
|
||||
dialog.ok('DOWNLOAD COMPLETE','Your download is complete, please check your home Kodi folder. There should be a new file called remote.pdf.')
|
||||
dialog.ok('DELETE FILE','Click OK to delete the downloaded file.')
|
||||
xbmcvfs.delete(dst)
|
||||
~"""
|
||||
from filetools import Physical_Path
|
||||
dest = Physical_Path(dest)
|
||||
status = Validate_Link(url,timeout)
|
||||
if status >= 200 and status < 400:
|
||||
if Python_Version() < 2.7 and url.startswith('https'):
|
||||
url = url.replace('https','http')
|
||||
start_time=time.time()
|
||||
urllib.urlretrieve(url, dest, lambda nb, bs, fs: Download_Progress(nb, bs, fs, dp, start_time))
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
def Download_Progress(numblocks, blocksize, filesize, dp, start_time):
|
||||
""" internal command ~"""
|
||||
|
||||
try:
|
||||
percent = min(numblocks * blocksize * 100 / filesize, 100)
|
||||
currently_downloaded = float(numblocks) * blocksize / (1024 * 1024)
|
||||
kbps_speed = numblocks * blocksize / (time.time() - start_time)
|
||||
if kbps_speed > 0:
|
||||
eta = (filesize - numblocks * blocksize) / kbps_speed
|
||||
else:
|
||||
eta = 0
|
||||
kbps_speed = kbps_speed / 1024
|
||||
total = float(filesize) / (1024 * 1024)
|
||||
mbs = '%.02f MB of %.02f MB' % (currently_downloaded, total)
|
||||
e = 'Speed: %.02f Kb/s ' % kbps_speed
|
||||
e += 'ETA: %02d:%02d' % divmod(eta, 60)
|
||||
if dp:
|
||||
dp.update(percent, mbs, e)
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
except:
|
||||
percent = 100
|
||||
if dp:
|
||||
dp.update(percent)
|
||||
if dp:
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
dp.close()
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Get_Extension(url):
|
||||
"""
|
||||
Return the extension of a url
|
||||
|
||||
CODE: Get_Extension(url)
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) url - This is the url you want to grab the extension from
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('ONLINE FILE','We will now try and get the extension of the file found at this URL:','','[COLOR=dodgerblue]http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4[/COLOR]')
|
||||
url_extension = koding.Get_Extension('http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4')
|
||||
dialog.ok('FILE EXTENSION','The file extension of this Big Buck Bunny sample is:','','[COLOR=dodgerblue]%s[/COLOR]'%url_extension)
|
||||
~"""
|
||||
|
||||
import os
|
||||
import urlparse
|
||||
|
||||
parsed = urlparse.urlparse(url)
|
||||
root, ext = os.path.splitext(parsed.path)
|
||||
return ext
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Open_URL(url='',post_type='get',payload={},headers={'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3'},cookies=True,auth=None,timeout=None,cookiejar=None,proxies={}):
|
||||
"""
|
||||
If you need to pull the contents of a webpage it's very simple to do so by using this function.
|
||||
This uses the Python Requests module, for more detailed info on how the params work
|
||||
please look at the following link: http://docs.python-requests.org/en/master/user/advanced/
|
||||
|
||||
IMPORTANT: This function will attempt to convert a url with a query string into the
|
||||
correct params for a post or get command but I highly recommend sending through your
|
||||
query string as a dictionary using the payload params. It's much cleaner and is a
|
||||
safer way of doing things, if you send through your url with a query string attached
|
||||
then I take no responsibility if it doesn't work!
|
||||
|
||||
CODE: Open_URL(url,[post_type,payload,headers,cookies,auth,timeout,cookiejar])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
url - This is the main url you want to send through. Send it through
|
||||
as a query string format even if it's a post.
|
||||
|
||||
post_type - By default this is set to 'get' but this can be set to 'post',
|
||||
if set to post the query string will be split up into a post format automatically.
|
||||
|
||||
payload - By default this is not used but if you just want a standard
|
||||
basic Open_URL function you can add a dictionary of params here. If you
|
||||
don't enter anything in here the function will just split up your url
|
||||
accordingly. Make sure you read the important information at the top
|
||||
of this tutorial text.
|
||||
|
||||
headers - Optionally send through headers in form of a dictionary.
|
||||
|
||||
cookies - If set to true your request will send through and store cookies.
|
||||
|
||||
auth - User/pass details
|
||||
|
||||
timeout - Optionally set a timeout for the request.
|
||||
|
||||
cookiejar - An name for the location to store cookies. By default it's
|
||||
set to addon_data/<addon_id>/cookies/cookiejar but if you have multiple
|
||||
websites you access then you may want to use a separate filename for each site.
|
||||
|
||||
proxies - Use a proxy for accessing the link, see requests documentation for full
|
||||
information but essentially you would send through a dictionary like this:
|
||||
proxies = {"http":"http://10.10.1.10:3128","htts":"https://10.10.1.10:3128"}
|
||||
|
||||
EXAMPLE CODE:
|
||||
dialog.ok('OPEN FORUM PAGE','We will attempt to open the noobsandnerds forum page and return the contents. You will now be asked for your forum credentials.')
|
||||
myurl = 'http://noobsandnerds.com/support/index.php'
|
||||
username = koding.Keyboard('ENTER USERNAME')
|
||||
password = koding.Keyboard('ENTER PASSWORD')
|
||||
params = {"username":username,"password":password}
|
||||
xbmc.log(repr(params),2)
|
||||
url_contents = koding.Open_URL(url=myurl, payload=params, post_type='get')
|
||||
koding.Text_Box('CONTENTS OF WEB PAGE',url_contents)
|
||||
~"""
|
||||
import os
|
||||
import pickle
|
||||
import requests
|
||||
import sys
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
|
||||
from __init__ import converthex, dolog, ADDON_ID, KODI_VER
|
||||
from addons import Addon_Info
|
||||
from filetools import Text_File
|
||||
|
||||
Addon_Version = Addon_Info(id='version')
|
||||
Addon_Profile = Addon_Info(id='profile')
|
||||
Cookie_Folder = os.path.join(Addon_Profile,'cookies')
|
||||
xbmc.log(Cookie_Folder,2)
|
||||
if not xbmcvfs.exists(Cookie_Folder):
|
||||
xbmcvfs.mkdirs(Cookie_Folder)
|
||||
|
||||
if cookiejar == None:
|
||||
Cookie_Jar = os.path.join(Cookie_Folder,'cookiejar')
|
||||
else:
|
||||
Cookie_Jar = os.path.join(Cookie_Folder,cookiejar)
|
||||
|
||||
my_cookies = None
|
||||
if cookies:
|
||||
if xbmcvfs.exists(Cookie_Jar):
|
||||
try:
|
||||
openfile = xbmcvfs.File(Cookie_Jar)
|
||||
f = openfile.read()
|
||||
openfile.close()
|
||||
my_cookies = pickle.load(f)
|
||||
except:
|
||||
my_cookies = None
|
||||
|
||||
# If the payload is empty we split the params
|
||||
if len(payload) == 0:
|
||||
|
||||
if '?' in url:
|
||||
url, args = url.split('?')
|
||||
args = args.split('&')
|
||||
for item in args:
|
||||
var, data = item.split('=')
|
||||
payload[var] = data
|
||||
|
||||
dolog('PAYLOAD: %s'%payload)
|
||||
|
||||
try:
|
||||
if Python_Version() < 2.7 and url.startswith('https'):
|
||||
url = url.replace('https','http')
|
||||
|
||||
if post_type == 'post':
|
||||
r = requests.post(url, payload, headers=headers, cookies=my_cookies, auth=auth, timeout=timeout, proxies=proxies)
|
||||
else:
|
||||
r = requests.get(url, payload, headers=headers, cookies=my_cookies, auth=auth, timeout=timeout, proxies=proxies)
|
||||
except:
|
||||
dolog('Failed to pull content for %s'%url)
|
||||
return False
|
||||
dolog('### CODE: %s | REASON: %s' % (r.status_code, r.reason))
|
||||
if r.status_code >= 200 and r.status_code < 400:
|
||||
content = r.text.encode('utf-8')
|
||||
dolog('content: %s'%content)
|
||||
if cookies:
|
||||
openfile = xbmcvfs.File(Cookie_Jar,'wb')
|
||||
pickle.dump(r.cookies, openfile)
|
||||
openfile.close()
|
||||
return content
|
||||
else:
|
||||
dolog('Failed to pull content for %s'%url)
|
||||
return False
|
||||
#----------------------------------------------------------------
|
||||
# TUTORIAL #
|
||||
def Validate_Link(url='',timeout=30):
|
||||
"""
|
||||
Returns the code for a particular link, so for example 200 is a good link and 404 is a URL not found
|
||||
|
||||
CODE: Validate_Link(url,[timeout])
|
||||
|
||||
AVAILABLE PARAMS:
|
||||
|
||||
(*) url - This is url you want to check the header code for
|
||||
|
||||
timeout - An optional timeout integer for checking url (default is 30 seconds)
|
||||
|
||||
EXAMPLE CODE:
|
||||
url_code = koding.Validate_Link('http://totalrevolution.tv')
|
||||
if url_code == 200:
|
||||
dialog.ok('WEBSITE STATUS','The website [COLOR=dodgerblue]totalrevolution.tv[/COLOR] is [COLOR=lime]ONLINE[/COLOR]')
|
||||
else:
|
||||
dialog.ok('WEBSITE STATUS','The website [COLOR=dodgerblue]totalrevolution.tv[/COLOR] is [COLOR=red]OFFLINE[/COLOR]')
|
||||
~"""
|
||||
import requests
|
||||
import xbmc
|
||||
|
||||
if Python_Version() < 2.7 and url.startswith('https'):
|
||||
url = url.replace('https','http')
|
||||
|
||||
try:
|
||||
r = requests.get(url,timeout=timeout)
|
||||
return r.status_code
|
||||
except:
|
||||
return 400
|
||||
#----------------------------------------------------------------
|
||||
Reference in New Issue
Block a user