test
This commit is contained in:
560
lib/xbmcutil.py
Normal file
560
lib/xbmcutil.py
Normal file
@@ -0,0 +1,560 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#/*
|
||||
# * Copyright (C) 2011 Libor Zoubek
|
||||
# *
|
||||
# *
|
||||
# * This Program is free software; you can redistribute it and/or modify
|
||||
# * it under the terms of the GNU General Public License as published by
|
||||
# * the Free Software Foundation; either version 2, or (at your option)
|
||||
# * any later version.
|
||||
# *
|
||||
# * This Program is distributed in the hope that it will be useful,
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# * GNU General Public License for more details.
|
||||
# *
|
||||
# * You should have received a copy of the GNU General Public License
|
||||
# * along with this program; see the file COPYING. If not, write to
|
||||
# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
# * http://www.gnu.org/copyleft/gpl.html
|
||||
# *
|
||||
# */
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import traceback
|
||||
import http.cookiejar
|
||||
import time
|
||||
import socket
|
||||
import unicodedata
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcvfs
|
||||
from html.entities import name2codepoint as n2cp
|
||||
import json
|
||||
import util
|
||||
UA = 'Mozilla/6.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.5) Gecko/2008092417 Firefox/3.0.3'
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'usage'))
|
||||
import utmain
|
||||
__addon__ = xbmcaddon.Addon(id='script.module.stream.resolver')
|
||||
__lang__ = __addon__.getLocalizedString
|
||||
##
|
||||
# initializes urllib cookie handler
|
||||
|
||||
|
||||
def init_urllib():
|
||||
cj = http.cookiejar.LWPCookieJar()
|
||||
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
|
||||
urllib.request.install_opener(opener)
|
||||
|
||||
|
||||
def request(url, headers=None):
|
||||
if headers is None:
|
||||
headers = {}
|
||||
debug('request: %s' % url)
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
response = urllib.request.urlopen(req)
|
||||
data = response.read()
|
||||
response.close()
|
||||
debug('len(data) %s' % len(data))
|
||||
return data
|
||||
|
||||
|
||||
def post(url, data):
|
||||
postdata = urllib.parse.urlencode(data)
|
||||
req = urllib.request.Request(url, postdata)
|
||||
req.add_header('User-Agent', UA)
|
||||
response = urllib.request.urlopen(req)
|
||||
data = response.read()
|
||||
response.close()
|
||||
return data
|
||||
|
||||
|
||||
def icon(name):
|
||||
return 'https://github.com/lzoubek/xbmc-doplnky/raw/dharma/icons/' + name
|
||||
|
||||
|
||||
def substr(data, start, end):
|
||||
i1 = data.find(start)
|
||||
i2 = data.find(end, i1)
|
||||
return data[i1:i2]
|
||||
|
||||
|
||||
def add_dir(name, params, logo='', infoLabels={}, menuItems={}):
|
||||
name = decode_html(name)
|
||||
if not 'title' in infoLabels:
|
||||
infoLabels['title'] = ''
|
||||
if logo == None:
|
||||
logo = ''
|
||||
liz = xbmcgui.ListItem(name)
|
||||
liz.setArt({'icon': 'DefaultFolder.png', 'thumb': logo})
|
||||
try:
|
||||
liz.setInfo(type='Video', infoLabels=infoLabels)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
items = []
|
||||
for mi in list(menuItems.keys()):
|
||||
action = menuItems[mi]
|
||||
if not type(action) == type({}):
|
||||
items.append((mi, action))
|
||||
else:
|
||||
if 'action-type' in action:
|
||||
action_type = action['action-type']
|
||||
del action['action-type']
|
||||
if action_type == 'list':
|
||||
items.append((mi, 'Container.Update(%s)' % _create_plugin_url(action)))
|
||||
elif action_type == 'play':
|
||||
items.append((mi, 'PlayMedia(%s)' % _create_plugin_url(action)))
|
||||
else:
|
||||
items.append((mi, 'RunPlugin(%s)' % _create_plugin_url(action)))
|
||||
else:
|
||||
items.append((mi, 'RunPlugin(%s)' % _create_plugin_url(action)))
|
||||
if len(items) > 0:
|
||||
liz.addContextMenuItems(items)
|
||||
return xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=_create_plugin_url(params),
|
||||
listitem=liz, isFolder=True)
|
||||
|
||||
|
||||
def add_local_dir(name, url, logo='', infoLabels={}, menuItems={}):
|
||||
name = decode_html(name)
|
||||
infoLabels['Title'] = name
|
||||
liz = xbmcgui.ListItem(name)
|
||||
liz.setArt({'icon': 'DefaultFolder.png', 'thumb': logo})
|
||||
liz.setInfo(type='Video', infoLabels=infoLabels)
|
||||
items = []
|
||||
for mi in list(menuItems.keys()):
|
||||
items.append((mi, 'RunPlugin(%s)' % _create_plugin_url(menuItems[mi])))
|
||||
if len(items) > 0:
|
||||
liz.addContextMenuItems(items)
|
||||
return xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=liz,
|
||||
isFolder=True)
|
||||
|
||||
|
||||
def add_video(name, params={}, logo='', infoLabels={}, menuItems={}):
|
||||
name = decode_html(name)
|
||||
if not 'Title' in infoLabels:
|
||||
infoLabels['Title'] = name
|
||||
url = _create_plugin_url(params)
|
||||
li = xbmcgui.ListItem(name, path=url)
|
||||
li.setArt({'icon': 'DefaultVideo.png', 'thumb': logo})
|
||||
li.setInfo(type='Video', infoLabels=infoLabels)
|
||||
# remove WARNING: XFILE::CFileFactory::CreateLoader - unsupported
|
||||
# protocol(plugin) in plugin://....
|
||||
li.addStreamInfo('video', {})
|
||||
li.setProperty('IsPlayable', 'true')
|
||||
items = [(xbmc.getLocalizedString(13347), 'Action(Queue)')]
|
||||
for mi in list(menuItems.keys()):
|
||||
action = menuItems[mi]
|
||||
if not type(action) == type({}):
|
||||
items.append((mi, action))
|
||||
else:
|
||||
if 'action-type' in action:
|
||||
action_type = action['action-type']
|
||||
del action['action-type']
|
||||
if action_type == 'list':
|
||||
items.append((mi, 'Container.Update(%s)' % _create_plugin_url(action)))
|
||||
elif action_type == 'play':
|
||||
items.append((mi, 'PlayMedia(%s)' % _create_plugin_url(action)))
|
||||
else:
|
||||
items.append((mi, 'RunPlugin(%s)' % _create_plugin_url(action)))
|
||||
else:
|
||||
items.append((mi, 'RunPlugin(%s)' % _create_plugin_url(action)))
|
||||
|
||||
if len(items) > 0:
|
||||
li.addContextMenuItems(items)
|
||||
return xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=li,
|
||||
isFolder=False)
|
||||
|
||||
|
||||
def _create_plugin_url(params, plugin=sys.argv[0]):
|
||||
url = []
|
||||
for key in list(params.keys()):
|
||||
value = decode_html(params[key])
|
||||
#--value = value.encode('ascii','ignore')
|
||||
value = value.encode('utf-8').hex()
|
||||
url.append(key + '=' + value + '&')
|
||||
return plugin + '?' + ''.join(url)
|
||||
|
||||
|
||||
def reportUsage(addonid, action):
|
||||
host = 'xbmc-doplnky.googlecode.com'
|
||||
tc = 'UA-3971432-4'
|
||||
try:
|
||||
utmain.main({'id': addonid, 'host': host, 'tc': tc, 'action': action})
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def init_usage_reporting(addonid):
|
||||
utmain.main({'do': 'reg', 'id': addonid})
|
||||
|
||||
|
||||
def save_to_file(url, file, headers=None):
|
||||
try:
|
||||
f = open(compat_path(file), 'wb')
|
||||
f.write(request(url, headers))
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
f.close()
|
||||
return True
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def set_subtitles(listItem, url, headers=None):
|
||||
if not (url == '' or url == None):
|
||||
util.info('Downloading subtitles')
|
||||
local = xbmcvfs.translatePath(__addon__.getAddonInfo('path'))
|
||||
c_local = compat_path(local)
|
||||
if not os.path.exists(c_local):
|
||||
os.makedirs(c_local)
|
||||
local = os.path.join(local, 'xbmc_subs' + str(int(time.time())) + '.srt')
|
||||
util.info('Saving subtitles as %s' % local)
|
||||
if not save_to_file(url, local, headers):
|
||||
util.error('Failed to store subtitles!')
|
||||
return
|
||||
util.info('Setting subtitles to playable item')
|
||||
listItem.setSubtitles([local.encode('utf-8')])
|
||||
|
||||
|
||||
def load_subtitles(url, headers=None):
|
||||
util.info('Downloading subtitles and load them to player...')
|
||||
if not (url == '' or url == None):
|
||||
local = xbmcvfs.translatePath(__addon__.getAddonInfo('path')).decode('utf-8')
|
||||
c_local = compat_path(local)
|
||||
if not os.path.exists(c_local):
|
||||
os.makedirs(c_local)
|
||||
local = os.path.join(local, 'xbmc_subs' + str(int(time.time())) + '.srt')
|
||||
util.info('Saving subtitles as %s' % local)
|
||||
if not save_to_file(url, local, headers):
|
||||
util.error('Failed to store subtitles!')
|
||||
return
|
||||
player = xbmc.Player()
|
||||
count = 0
|
||||
max_count = 99
|
||||
while not player.isPlaying() and count < max_count:
|
||||
count += 1
|
||||
xbmc.sleep(200)
|
||||
if count > max_count - 2:
|
||||
util.info("Cannot load subtitles, player timed out")
|
||||
return
|
||||
player.setSubtitles(local.encode('utf-8'))
|
||||
util.info('Subtitles loaded to player')
|
||||
|
||||
|
||||
def _substitute_entity(match):
|
||||
ent = match.group(3)
|
||||
if match.group(1) == '#':
|
||||
# decoding by number
|
||||
if match.group(2) == '':
|
||||
# number is in decimal
|
||||
return chr(int(ent))
|
||||
elif match.group(2) == 'x':
|
||||
# number is in hex
|
||||
return chr(int('0x' + ent, 16))
|
||||
else:
|
||||
# they were using a name
|
||||
cp = n2cp.get(ent)
|
||||
if cp:
|
||||
return chr(cp)
|
||||
else:
|
||||
return match.group()
|
||||
|
||||
|
||||
def decode_html(data):
|
||||
try:
|
||||
if not type(data) == str:
|
||||
data = str(data, 'utf-8', errors='ignore')
|
||||
entity_re = re.compile(r'&(#?)(x?)(\w+);')
|
||||
return entity_re.subn(_substitute_entity, data)[0]
|
||||
except:
|
||||
traceback.print_exc()
|
||||
print([data])
|
||||
return data
|
||||
|
||||
|
||||
def debug(text):
|
||||
xbmc.log(str([text]), xbmc.LOGDEBUG)
|
||||
|
||||
|
||||
def info(text):
|
||||
xbmc.log(str([text]))
|
||||
|
||||
|
||||
def error(text):
|
||||
xbmc.log(str([text]), xbmc.LOGERROR)
|
||||
|
||||
|
||||
def search_remove(cache, search):
|
||||
data = cache.get('search')
|
||||
if data == None or data == '':
|
||||
data = []
|
||||
else:
|
||||
data = eval(data)
|
||||
data.remove(search)
|
||||
cache.set('search', repr(data))
|
||||
|
||||
|
||||
def search_replace(cache, search, replacement):
|
||||
data = cache.get('search')
|
||||
if data == None or data == '':
|
||||
data = []
|
||||
else:
|
||||
data = eval(data)
|
||||
index = data.index(search)
|
||||
if index > -1:
|
||||
data[index] = replacement
|
||||
cache.set('search', repr(data))
|
||||
|
||||
|
||||
def search_add(cache, search, maximum):
|
||||
data = cache.get('search')
|
||||
if data == None or data == '':
|
||||
data = []
|
||||
else:
|
||||
data = eval(data)
|
||||
if search in data:
|
||||
data.remove(search)
|
||||
data.insert(0, search)
|
||||
remove = len(data) - maximum
|
||||
if remove > 0:
|
||||
for i in range(remove):
|
||||
data.pop()
|
||||
cache.set('search', repr(data))
|
||||
|
||||
|
||||
def search_list(cache):
|
||||
data = cache.get('search')
|
||||
if data == None or data == '':
|
||||
data = []
|
||||
else:
|
||||
data = eval(data)
|
||||
return data
|
||||
|
||||
# obsolete functions ..
|
||||
|
||||
|
||||
def get_searches(addon, server):
|
||||
local = xbmcvfs.translatePath(addon.getAddonInfo('profile'))
|
||||
c_local = compat_path(local)
|
||||
if not os.path.exists(c_local):
|
||||
os.makedirs(c_local)
|
||||
c_local = compat_path(os.path.join(local, server))
|
||||
if not os.path.exists(c_local):
|
||||
return []
|
||||
f = open(c_local, 'r')
|
||||
data = f.read()
|
||||
searches = json.loads(str(data.decode('utf-8', 'ignore')))
|
||||
f.close()
|
||||
return searches
|
||||
|
||||
|
||||
def add_search(addon, server, search, maximum):
|
||||
searches = []
|
||||
local = xbmcvfs.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
|
||||
c_local = compat_path(local)
|
||||
if not os.path.exists(c_local):
|
||||
os.makedirs(c_local)
|
||||
c_local = compat_path(os.path.join(local, server))
|
||||
if os.path.exists(c_local):
|
||||
f = open(c_local, 'r')
|
||||
data = f.read()
|
||||
searches = json.loads(str(data.decode('utf-8', 'ignore')))
|
||||
f.close()
|
||||
if search in searches:
|
||||
searches.remove(search)
|
||||
searches.insert(0, search)
|
||||
remove = len(searches) - maximum
|
||||
if remove > 0:
|
||||
for i in range(remove):
|
||||
searches.pop()
|
||||
f = open(c_local, 'w')
|
||||
f.write(json.dumps(searches, ensure_ascii=True))
|
||||
f.close()
|
||||
|
||||
|
||||
def delete_search_history(addon, server):
|
||||
local = xbmcvfs.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
|
||||
c_local = compat_path(local)
|
||||
if not os.path.exists(c_local):
|
||||
return
|
||||
c_local = compat_path(os.path.join(local, server))
|
||||
if os.path.exists(c_local):
|
||||
os.remove(c_local)
|
||||
|
||||
|
||||
def remove_search(addon, server, search):
|
||||
local = xbmcvfs.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
|
||||
c_local = compat_path(local)
|
||||
if not os.path.exists(c_local):
|
||||
return
|
||||
c_local = compat_path(os.path.join(local, server))
|
||||
if os.path.exists(c_local):
|
||||
f = open(c_local, 'r')
|
||||
data = f.read()
|
||||
searches = json.loads(str(data.decode('utf-8', 'ignore')))
|
||||
f.close()
|
||||
searches.remove(search)
|
||||
f = open(c_local, 'w')
|
||||
f.write(json.dumps(searches, ensure_ascii=True))
|
||||
f.close()
|
||||
# obsolete funcionts END
|
||||
|
||||
|
||||
def download(addon, filename, url, local, notifyFinishDialog=True, headers={}):
|
||||
try:
|
||||
util.info('Downloading %s to %s' % (url, local))
|
||||
except:
|
||||
util.info('Downloading ' + url)
|
||||
local = xbmc.makeLegalFilename(local)
|
||||
try:
|
||||
filename = util.replace_diacritic(util.decode_html(filename))
|
||||
except:
|
||||
filename = 'Video soubor'
|
||||
icon = os.path.join(addon.getAddonInfo('path'), 'icon.png')
|
||||
notifyEnabled = addon.getSetting('download-notify') == 'true'
|
||||
notifyEvery = addon.getSetting('download-notify-every')
|
||||
notifyPercent = 1
|
||||
if int(notifyEvery) == 0:
|
||||
notifyPercent = 10
|
||||
if int(notifyEvery) == 1:
|
||||
notifyPercent = 5
|
||||
|
||||
def encode(string):
|
||||
return ' '.join(string).encode('utf-8')
|
||||
|
||||
def notify(title, message, time=3000):
|
||||
try:
|
||||
xbmcgui.Dialog().notification(encode(title), encode(message), time=time, icon=icon,
|
||||
sound=False)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
error('unable to show notification')
|
||||
|
||||
def callback(percent, speed, est, filename):
|
||||
if percent == 0 and speed == 0:
|
||||
notify(xbmc.getLocalizedString(13413), filename)
|
||||
return
|
||||
if notifyEnabled:
|
||||
if percent > 0 and percent % notifyPercent == 0:
|
||||
esTime = '%ss' % est
|
||||
if est > 60:
|
||||
esTime = '%sm' % int(est / 60)
|
||||
message = xbmc.getLocalizedString(
|
||||
24042) % percent + ' - %s KB/s %s' % (speed, esTime)
|
||||
notify(message, filename)
|
||||
|
||||
downloader = Downloader(callback)
|
||||
result = downloader.download(url, local, filename, headers)
|
||||
try:
|
||||
if result == True:
|
||||
if xbmc.Player().isPlaying():
|
||||
notify(xbmc.getLocalizedString(20177), filename)
|
||||
else:
|
||||
if notifyFinishDialog:
|
||||
xbmcgui.Dialog().ok(xbmc.getLocalizedString(20177), filename)
|
||||
else:
|
||||
notify(xbmc.getLocalizedString(20177), filename)
|
||||
else:
|
||||
notify(xbmc.getLocalizedString(257), filename)
|
||||
xbmcgui.Dialog().ok(filename, xbmc.getLocalizedString(257) + ' : ' + result)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
class Downloader(object):
|
||||
|
||||
def __init__(self, callback=None):
|
||||
self.init_time = time.time()
|
||||
self.callback = callback
|
||||
self.gran = 50
|
||||
self.percent = -1
|
||||
|
||||
def download(self, remote, local, filename=None, headers={}):
|
||||
class MyURLopener(urllib.request.FancyURLopener):
|
||||
|
||||
def http_error_default(self, url, fp, errcode, errmsg, headers):
|
||||
self.error_msg = 'Downlad failed, error : ' + str(errcode)
|
||||
|
||||
if not filename:
|
||||
filename = os.path.basename(local)
|
||||
self.filename = filename
|
||||
if self.callback:
|
||||
self.callback(0, 0, 0, filename)
|
||||
socket.setdefaulttimeout(60)
|
||||
opener = MyURLopener()
|
||||
for header in headers:
|
||||
opener.addheader(header, headers[header])
|
||||
try:
|
||||
opener.retrieve(remote, local, reporthook=self.dlProgress)
|
||||
if hasattr(opener, 'error_msg'):
|
||||
return opener.error_msg
|
||||
return True
|
||||
except socket.error:
|
||||
errno, errstr = sys.exc_info()[:2]
|
||||
if errno == socket.timeout:
|
||||
return 'Download failed, connection timeout'
|
||||
except:
|
||||
traceback.print_exc()
|
||||
errno, errstr = sys.exc_info()[:2]
|
||||
return str(errstr)
|
||||
|
||||
def dlProgress(self, count, blockSize, totalSize):
|
||||
if count % self.gran == 0 and not count == 0:
|
||||
percent = int(count * blockSize * 100 / totalSize)
|
||||
newTime = time.time()
|
||||
diff = newTime - self.init_time
|
||||
self.init_time = newTime
|
||||
if diff <= 0:
|
||||
diff = 1
|
||||
speed = int(((1 / diff) * blockSize * self.gran) / 1024)
|
||||
est = int((totalSize - int(count * blockSize)) / 1024 / speed)
|
||||
if self.callback and not self.percent == percent:
|
||||
self.callback(percent, speed, est, self.filename)
|
||||
self.percent = percent
|
||||
|
||||
_diacritic_replace = {'\u00f3': 'o',
|
||||
'\u0213': '-',
|
||||
'\u00e1': 'a',
|
||||
'\u010d': 'c',
|
||||
'\u010c': 'C',
|
||||
'\u010f': 'd',
|
||||
'\u010e': 'D',
|
||||
'\u00e9': 'e',
|
||||
'\u011b': 'e',
|
||||
'\u00ed': 'i',
|
||||
'\u0148': 'n',
|
||||
'\u0159': 'r',
|
||||
'\u0161': 's',
|
||||
'\u0165': 't',
|
||||
'\u016f': 'u',
|
||||
'\u00fd': 'y',
|
||||
'\u017e': 'z'
|
||||
}
|
||||
|
||||
|
||||
def replace_diacritic(string):
|
||||
ret = []
|
||||
for char in string:
|
||||
if char in _diacritic_replace:
|
||||
ret.append(_diacritic_replace[char])
|
||||
else:
|
||||
ret.append(char)
|
||||
return ''.join(ret)
|
||||
|
||||
|
||||
def compat_path(path):
|
||||
return path
|
||||
#no longer needed in python 3
|
||||
#if sys.platform.startswith('win'):
|
||||
# if isinstance(path, str):
|
||||
# path = path.decode('utf-8')
|
||||
#else:
|
||||
# if isinstance(path, str):
|
||||
# path = path.encode('utf-8')
|
||||
#return path
|
||||
Reference in New Issue
Block a user