test
This commit is contained in:
BIN
lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc
Normal file
BIN
lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc
Normal file
Binary file not shown.
Binary file not shown.
230
lib/contentprovider/provider.py
Normal file
230
lib/contentprovider/provider.py
Normal file
@@ -0,0 +1,230 @@
|
||||
# * Copyright (C) 2012 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
|
||||
# *
|
||||
# */
|
||||
from collections import defaultdict
|
||||
|
||||
import util
|
||||
import resolver
|
||||
|
||||
|
||||
try:
|
||||
import StorageServer
|
||||
except ImportError:
|
||||
print('Using dummy storage server')
|
||||
import storageserverdummy as StorageServer
|
||||
|
||||
|
||||
class ResolveException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ContentProvider(object):
|
||||
"""
|
||||
ContentProvider class provides an internet content. It should NOT have any xbmc-related imports
|
||||
and must be testable without XBMC runtime. This is a basic/dummy implementation.
|
||||
"""
|
||||
|
||||
def __init__(self, name='dummy', base_url='/', username=None, password=None, filter=None, tmp_dir='.'):
|
||||
"""
|
||||
ContentProvider constructor
|
||||
Args:
|
||||
name (str): name of provider
|
||||
base_url (str): base url of site being accessed
|
||||
username (str): login username
|
||||
password (str): login password
|
||||
filter (func{item}): function to filter results returned by search or list methods
|
||||
tmp_dir (str): temporary dir where provider can store/cache files
|
||||
"""
|
||||
self.name = name
|
||||
self.username = username
|
||||
self.password = password
|
||||
if not base_url[-1] == '/':
|
||||
base_url += '/'
|
||||
self.base_url = base_url
|
||||
self.filter = filter
|
||||
self.tmp_dir = tmp_dir
|
||||
self.cache = StorageServer.StorageServer(self.name, 24)
|
||||
self.lang = 'cs' # initialize, current language could be set by XBMContentProvider
|
||||
|
||||
def __str__(self):
|
||||
return 'ContentProvider' + self.name
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
This function gets called by XbmcContentProvider after it initializes itself
|
||||
and sets eveything up (especially 'lang' property of ContentProvider which gets detected
|
||||
from kodi)
|
||||
"""
|
||||
pass
|
||||
|
||||
def capabilities(self):
|
||||
"""
|
||||
This way class defines which capabilities it provides ['login','search','resolve','categories']
|
||||
It may also contain '!download' when provider does not support downloading
|
||||
"""
|
||||
return []
|
||||
|
||||
def video_item(self, url='', img='', quality='???'):
|
||||
"""
|
||||
Returns empty video item - contains all required fields
|
||||
"""
|
||||
return {'type': 'video', 'title': '', 'rating': 0, 'year': 0, 'size': '0MB', 'url': url, 'img': img,
|
||||
'length': '', 'quality': quality, 'subs': '', 'surl': '', 'lang': ''}
|
||||
|
||||
def dir_item(self, title='', url='', type='dir'):
|
||||
"""
|
||||
Returns empty directory item
|
||||
"""
|
||||
return {'type': type, 'title': title, 'size': '0', 'url': url}
|
||||
|
||||
def login(self):
|
||||
"""
|
||||
A login method returns True on successfull login, False otherwise
|
||||
"""
|
||||
return False
|
||||
|
||||
def search(self, keyword):
|
||||
"""
|
||||
Search for a keyword on a site
|
||||
Args:
|
||||
keyword (str)
|
||||
|
||||
returns:
|
||||
array of video or directory items
|
||||
"""
|
||||
return []
|
||||
|
||||
def list(self, url):
|
||||
"""
|
||||
Lists content on given url
|
||||
Args:
|
||||
url (str): either relative or absolute provider URL
|
||||
|
||||
Returns:
|
||||
array of video or directory items
|
||||
|
||||
"""
|
||||
return []
|
||||
|
||||
def categories(self):
|
||||
"""
|
||||
Lists categories on provided site
|
||||
|
||||
Returns:
|
||||
array of video or directory items
|
||||
"""
|
||||
return []
|
||||
|
||||
def findstreams(self, data, regexes=None):
|
||||
"""
|
||||
Finds streams in given data (see resovler.findstreams for more details)
|
||||
|
||||
:param data: A string (piece of HTML, for example) or an array of URLs
|
||||
:param regexes: An array of regexes to be used for extracting URLs from
|
||||
'data' of type 'string'
|
||||
:returns: An array of video items
|
||||
"""
|
||||
resolved = resolver.findstreams(data, regexes)
|
||||
if resolved is None:
|
||||
raise ResolveException(
|
||||
'Nelze ziskat video link [CR]zkontrolujte jestli video nebylo odstraneno')
|
||||
elif isinstance(resolved, list) and not resolved:
|
||||
raise ResolveException('Video je na serveru, ktery neni podporovan')
|
||||
elif not resolved:
|
||||
raise ResolveException(
|
||||
'Nebyl nalezen zadny video embed [CR]zkontrolujte stranku pripadne nahlaste chybu pluginu')
|
||||
result = []
|
||||
for j in resolved:
|
||||
i = defaultdict(lambda: '', j)
|
||||
item = self.video_item()
|
||||
item['title'] = i['name']
|
||||
item['url'] = i['url']
|
||||
item['quality'] = i['quality']
|
||||
item['surl'] = i['surl']
|
||||
item['subs'] = i['subs']
|
||||
item['headers'] = i['headers']
|
||||
item['lang'] = i['lang']
|
||||
item['info'] = i['info']
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
def resolve(self, item, captcha_cb=None, select_cb=None, wait_cb=None):
|
||||
"""
|
||||
Resolves given video item to a downloable/playable file/stream URL
|
||||
|
||||
Args:
|
||||
url (str): relative or absolute URL to be resolved
|
||||
captcha_cb(func{obj}): callback function when user input is required (captcha, one-time passwords etc).
|
||||
function implementation must be Provider-specific
|
||||
select_cb(func{array}): callback function for cases when given url resolves to multiple streams,
|
||||
provider class may call this function and require user interaction
|
||||
wait_cb(func{obj}): callback function for cases when url resolves to stream which becomes available
|
||||
somewhere in future (typically in several seconds). Provider may call this and require waiting.
|
||||
Returns:
|
||||
None - if ``url`` was not resolved. Video item with 'url' key pointing to resolved target
|
||||
"""
|
||||
return None
|
||||
|
||||
def _url(self, url):
|
||||
"""
|
||||
Transforms relative to absolute url based on ``base_url`` class property
|
||||
"""
|
||||
if url.startswith('http'):
|
||||
return url
|
||||
return self.base_url + url.lstrip('./')
|
||||
|
||||
def _filter(self, result, item):
|
||||
"""
|
||||
Applies filter, if filter passes `item` is appended to `result`
|
||||
|
||||
Args:
|
||||
result (array) : target array
|
||||
item (obj) : item that is being applied filter on
|
||||
"""
|
||||
if self.filter:
|
||||
if self.filter(item):
|
||||
result.append(item)
|
||||
else:
|
||||
result.append(item)
|
||||
|
||||
def info(self, msg):
|
||||
util.info('[%s] %s' % (self.name, msg))
|
||||
|
||||
def error(self, msg):
|
||||
util.error('[%s] %s' % (self.name, msg))
|
||||
|
||||
|
||||
class cached(object):
|
||||
"""
|
||||
A method decorator that can be used on any ContentProvider method
|
||||
Having this decorator means that results of such method are going
|
||||
to be cached for 24hours by default. You can pass number argument
|
||||
to decorator, for example @cached(1) would cache for 1 hour.
|
||||
"""
|
||||
|
||||
def __init__(self, ttl=24):
|
||||
self.ttl = ttl
|
||||
|
||||
def __call__(self, f):
|
||||
def wrap(*args):
|
||||
provider = args[0]
|
||||
cache = StorageServer.StorageServer(provider.name + str(self.ttl), self.ttl)
|
||||
return cache.cacheFunction(f, *args)
|
||||
|
||||
return wrap
|
||||
464
lib/contentprovider/xbmcprovider.py
Normal file
464
lib/contentprovider/xbmcprovider.py
Normal file
@@ -0,0 +1,464 @@
|
||||
# * Copyright (C) 2012 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 sys
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
import util
|
||||
import xbmcutil
|
||||
import resolver
|
||||
import time
|
||||
import xbmcplugin
|
||||
import xbmc
|
||||
import xbmcvfs
|
||||
import xbmcgui
|
||||
import urllib.parse
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
from collections import defaultdict
|
||||
from provider import ResolveException
|
||||
|
||||
|
||||
class XBMContentProvider(object):
|
||||
'''
|
||||
ContentProvider class provides an internet content. It should NOT have any xbmc-related imports
|
||||
and must be testable without XBMC runtime. This is a basic/dummy implementation.
|
||||
'''
|
||||
|
||||
def __init__(self, provider, settings, addon):
|
||||
'''
|
||||
XBMContentProvider constructor
|
||||
Args:
|
||||
name (str): name of provider
|
||||
'''
|
||||
self.provider = provider
|
||||
# inject current user language
|
||||
try: # not fully supported on Frodo
|
||||
provider.lang = xbmc.getLanguage(xbmc.ISO_639_1)
|
||||
except:
|
||||
provider.lang = None
|
||||
pass
|
||||
self.settings = settings
|
||||
# lang setting is optional for plugins
|
||||
if not 'lang' in self.settings:
|
||||
self.settings['lang'] = '0'
|
||||
|
||||
util.info('Initializing provider %s with settings %s' % (provider.name, settings))
|
||||
self.addon = addon
|
||||
self.addon_id = addon.getAddonInfo('id')
|
||||
if '!download' not in self.provider.capabilities():
|
||||
self.check_setting_keys(['downloads'])
|
||||
self.cache = provider.cache
|
||||
provider.on_init()
|
||||
|
||||
def check_setting_keys(self, keys):
|
||||
for key in keys:
|
||||
if not key in list(self.settings.keys()):
|
||||
raise Exception('Invalid settings passed - [' + key + '] setting is required')
|
||||
|
||||
def params(self):
|
||||
return {'cp': self.provider.name}
|
||||
|
||||
def run(self, params):
|
||||
if params == {} or params == self.params():
|
||||
return self.root()
|
||||
if 'list' in list(params.keys()):
|
||||
self.list(self.provider.list(params['list']))
|
||||
return xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
if 'down' in list(params.keys()):
|
||||
return self.download({'url': params['down'], 'title': params['title']})
|
||||
if 'play' in list(params.keys()):
|
||||
return self.play({'url': params['play'], 'info': params})
|
||||
if 'search-list' in list(params.keys()):
|
||||
return self.search_list()
|
||||
if 'search' in list(params.keys()):
|
||||
return self.do_search(params['search'])
|
||||
if 'search-remove' in list(params.keys()):
|
||||
return self.search_remove(params['search-remove'])
|
||||
if 'search-edit' in list(params.keys()):
|
||||
return self.search_edit(params['search-edit'])
|
||||
if self.run_custom:
|
||||
return self.run_custom(params)
|
||||
|
||||
def search_list(self):
|
||||
params = self.params()
|
||||
params.update({'search': '#'})
|
||||
menu1 = self.params()
|
||||
menu2 = self.params()
|
||||
xbmcutil.add_dir(xbmcutil.__lang__(30004), params, xbmcutil.icon('search.png'))
|
||||
for what in xbmcutil.search_list(self.cache):
|
||||
params['search'] = what
|
||||
menu1['search-remove'] = what
|
||||
menu2['search-edit'] = what
|
||||
xbmcutil.add_dir(what, params, menuItems={xbmcutil.__lang__(
|
||||
30016): menu2, xbmc.getLocalizedString(117): menu1})
|
||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
def search_remove(self, what):
|
||||
xbmcutil.search_remove(self.cache, what)
|
||||
xbmc.executebuiltin('Container.Refresh')
|
||||
|
||||
def search_edit(self, what):
|
||||
kb = xbmc.Keyboard(what, xbmcutil.__lang__(30003), False)
|
||||
kb.doModal()
|
||||
if kb.isConfirmed():
|
||||
replacement = kb.getText()
|
||||
xbmcutil.search_replace(self.cache, what, replacement)
|
||||
params = self.params()
|
||||
params.update({'search': replacement})
|
||||
action = xbmcutil._create_plugin_url(params)
|
||||
xbmc.executebuiltin('Container.Update(%s)' % action)
|
||||
|
||||
def do_search(self, what):
|
||||
if what == '' or what == '#':
|
||||
kb = xbmc.Keyboard('', xbmcutil.__lang__(30003), False)
|
||||
kb.doModal()
|
||||
if kb.isConfirmed():
|
||||
what = kb.getText()
|
||||
if not what == '':
|
||||
maximum = 20
|
||||
try:
|
||||
maximum = int(self.settings['keep-searches'])
|
||||
except:
|
||||
util.error('Unable to parse convert addon setting to number')
|
||||
pass
|
||||
xbmcutil.search_add(self.cache, what, maximum)
|
||||
self.search(what)
|
||||
|
||||
def root(self):
|
||||
searches = xbmcutil.get_searches(self.addon, self.provider.name)
|
||||
if len(searches) > 0:
|
||||
self.provider.info('Upgrading to new saved search storage...')
|
||||
for s in searches:
|
||||
self.provider.info('Moving item %s' % s)
|
||||
xbmcutil.search_add(self.cache, s, 9999999)
|
||||
xbmcutil.delete_search_history(self.addon, self.provider.name)
|
||||
|
||||
if 'search' in self.provider.capabilities():
|
||||
params = self.params()
|
||||
params.update({'search-list': '#'})
|
||||
xbmcutil.add_dir(xbmcutil.__lang__(30003), params, xbmcutil.icon('search.png'))
|
||||
if not '!download' in self.provider.capabilities():
|
||||
xbmcutil.add_local_dir(xbmcutil.__lang__(30006), self.settings[
|
||||
'downloads'], xbmcutil.icon('download.png'))
|
||||
self.list(self.provider.categories())
|
||||
return xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
def download(self, item):
|
||||
downloads = self.settings['downloads']
|
||||
if '' == downloads:
|
||||
xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30009))
|
||||
return
|
||||
stream = self.resolve(item['url'])
|
||||
if stream:
|
||||
if not 'headers' in list(stream.keys()):
|
||||
stream['headers'] = {}
|
||||
xbmcutil.reportUsage(self.addon_id, self.addon_id + '/download')
|
||||
# clean up \ and /
|
||||
name = item['title'].replace('/', '_').replace('\\', '_')
|
||||
if not stream['subs'] == '':
|
||||
xbmcutil.save_to_file(stream['subs'], os.path.join(
|
||||
downloads, name + '.srt'), stream['headers'])
|
||||
dot = name.find('.')
|
||||
if dot <= 0:
|
||||
# name does not contain extension, append some
|
||||
name += '.mp4'
|
||||
xbmcutil.download(self.addon, name, self.provider._url(
|
||||
stream['url']), os.path.join(downloads, name), headers=stream['headers'])
|
||||
|
||||
def play(self, item):
|
||||
stream = self.resolve(item['url'])
|
||||
if stream:
|
||||
xbmcutil.reportUsage(self.addon_id, self.addon_id + '/play')
|
||||
if 'headers' in list(stream.keys()):
|
||||
headerStr = '|' + urllib.parse.urlencode(stream['headers'])
|
||||
if len(headerStr) > 1:
|
||||
stream['url'] += headerStr.encode('utf-8')
|
||||
print('Sending %s to player' % stream['url'])
|
||||
li = xbmcgui.ListItem(path=stream['url'])
|
||||
li.setArt({'icon': 'DefaulVideo.png'})
|
||||
il = self._extract_infolabels(item['info'])
|
||||
if len(il) > 0: # only set when something was extracted
|
||||
li.setInfo('video', il)
|
||||
try:
|
||||
local_subs = xbmcutil.set_subtitles(li, stream['subs'], stream.get('headers'))
|
||||
except:
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, li)
|
||||
xbmcutil.load_subtitles(stream['subs'], stream.get('headers'))
|
||||
else:
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, li)
|
||||
|
||||
def _handle_exc(self, e):
|
||||
msg = e.message
|
||||
if msg.find('$') == 0:
|
||||
try:
|
||||
msg = self.addon.getLocalizedString(int(msg[1:]))
|
||||
except:
|
||||
try:
|
||||
msg = xbmcutil.__lang__(int(msg[1:]))
|
||||
except:
|
||||
pass
|
||||
xbmcgui.Dialog().ok(self.provider.name, msg)
|
||||
|
||||
def resolve(self, url):
|
||||
item = self.provider.video_item()
|
||||
item.update({'url': url})
|
||||
try:
|
||||
return self.provider.resolve(item)
|
||||
except ResolveException as e:
|
||||
self._handle_exc(e)
|
||||
|
||||
def search(self, keyword):
|
||||
self.list(self.provider.search(keyword))
|
||||
return xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
def list(self, items):
|
||||
params = self.params()
|
||||
for item in items:
|
||||
if item['type'] == 'dir':
|
||||
self.render_dir(item)
|
||||
elif item['type'] == 'next':
|
||||
params.update({'list': item['url']})
|
||||
xbmcutil.add_dir(xbmcutil.__lang__(30007), params, xbmcutil.icon('next.png'))
|
||||
elif item['type'] == 'prev':
|
||||
params.update({'list': item['url']})
|
||||
xbmcutil.add_dir(xbmcutil.__lang__(30008), params, xbmcutil.icon('prev.png'))
|
||||
elif item['type'] == 'new':
|
||||
params.update({'list': item['url']})
|
||||
xbmcutil.add_dir(xbmcutil.__lang__(30012), params, xbmcutil.icon('new.png'))
|
||||
elif item['type'] == 'top':
|
||||
params.update({'list': item['url']})
|
||||
xbmcutil.add_dir(xbmcutil.__lang__(30013), params, xbmcutil.icon('top.png'))
|
||||
elif item['type'] == 'video':
|
||||
self.render_video(item)
|
||||
else:
|
||||
self.render_default(item)
|
||||
|
||||
def render_default(self, item):
|
||||
raise Exception("Unable to render item " + str(item))
|
||||
|
||||
def render_dir(self, item):
|
||||
params = self.params()
|
||||
params.update({'list': item['url']})
|
||||
title = item['title']
|
||||
img = None
|
||||
if 'img' in list(item.keys()):
|
||||
img = item['img']
|
||||
if title.find('$') == 0:
|
||||
try:
|
||||
title = self.addon.getLocalizedString(int(title[1:]))
|
||||
except:
|
||||
pass
|
||||
menuItems = {}
|
||||
if 'menu' in list(item.keys()):
|
||||
for ctxtitle, value in item['menu'].items():
|
||||
if ctxtitle.find('$') == 0:
|
||||
try:
|
||||
ctxtitle = self.addon.getLocalizedString(int(ctxtitle[1:]))
|
||||
except:
|
||||
pass
|
||||
menuItems[ctxtitle] = value
|
||||
xbmcutil.add_dir(title, params, img, infoLabels=self._extract_infolabels(
|
||||
item), menuItems=menuItems)
|
||||
|
||||
def _extract_infolabels(self, item):
|
||||
infoLabels = {}
|
||||
for label in ['title', 'plot', 'year', 'genre', 'rating', 'director',
|
||||
'votes', 'cast', 'trailer', 'tvshowtitle', 'season',
|
||||
'episode', 'duration']:
|
||||
if label in list(item.keys()):
|
||||
infoLabels[label] = util.decode_html(item[label])
|
||||
return infoLabels
|
||||
|
||||
def render_video(self, item):
|
||||
params = self.params()
|
||||
params.update({'play': item['url']})
|
||||
downparams = self.params()
|
||||
downparams.update({'title': item['title'], 'down': item['url']})
|
||||
def_item = self.provider.video_item()
|
||||
if item['size'] == def_item['size']:
|
||||
item['size'] = ''
|
||||
else:
|
||||
item['size'] = ' (%s)' % item['size']
|
||||
title = '%s%s' % (item['title'], item['size'])
|
||||
menuItems = {}
|
||||
if "!download" not in self.provider.capabilities():
|
||||
menuItems[xbmc.getLocalizedString(33003)] = downparams
|
||||
if 'menu' in list(item.keys()):
|
||||
for ctxtitle, value in item['menu'].items():
|
||||
if ctxtitle.find('$') == 0:
|
||||
try:
|
||||
ctxtitle = self.addon.getLocalizedString(int(ctxtitle[1:]))
|
||||
except:
|
||||
pass
|
||||
menuItems[ctxtitle] = value
|
||||
xbmcutil.add_video(title,
|
||||
params,
|
||||
item['img'],
|
||||
infoLabels=self._extract_infolabels(item),
|
||||
menuItems=menuItems
|
||||
)
|
||||
|
||||
def categories(self):
|
||||
self.list(self.provider.categories(keyword))
|
||||
return xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
|
||||
class XBMCMultiResolverContentProvider(XBMContentProvider):
|
||||
|
||||
def __init__(self, provider, settings, addon):
|
||||
XBMContentProvider.__init__(self, provider, settings, addon)
|
||||
self.check_setting_keys(['quality'])
|
||||
|
||||
def resolve(self, url):
|
||||
item = self.provider.video_item()
|
||||
item.update({'url': url})
|
||||
|
||||
def select_cb(resolved):
|
||||
|
||||
quality = self.settings['quality'] or '0'
|
||||
filtered = resolver.filter_by_quality(resolved, quality)
|
||||
lang = self.settings['lang'] or '0'
|
||||
filtered = resolver.filter_by_language(filtered, lang)
|
||||
# if user requested something but 'ask me' or filtered result is exactly 1
|
||||
if len(filtered) == 1 or (int(quality) > 0 and int(lang) == 0):
|
||||
return filtered[0]
|
||||
# if user requested particular language and we have it
|
||||
if len(filtered) > 0 and int(lang) > 0:
|
||||
return filtered[0]
|
||||
dialog = xbmcgui.Dialog()
|
||||
opts = []
|
||||
for r in resolved:
|
||||
d = defaultdict(lambda: '', r)
|
||||
opts.append('%s [%s] %s' % (d['title'], d['quality'], d['lang']))
|
||||
ret = dialog.select(xbmcutil.__lang__(30005), opts)
|
||||
if ret >= 0:
|
||||
return resolved[ret]
|
||||
try:
|
||||
return self.provider.resolve(item, select_cb=select_cb)
|
||||
except ResolveException as e:
|
||||
self._handle_exc(e)
|
||||
|
||||
|
||||
class XBMCLoginRequiredContentProvider(XBMContentProvider):
|
||||
|
||||
def root(self):
|
||||
if not self.provider.login():
|
||||
xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011))
|
||||
else:
|
||||
return XBMContentProvider.root(self)
|
||||
|
||||
|
||||
class XBMCLoginOptionalContentProvider(XBMContentProvider):
|
||||
|
||||
def __init__(self, provider, settings, addon):
|
||||
XBMContentProvider.__init__(self, provider, settings, addon)
|
||||
self.check_setting_keys(['vip'])
|
||||
|
||||
def ask_for_captcha(self, params):
|
||||
img = os.path.join(str(xbmcvfs.translatePath(
|
||||
self.addon.getAddonInfo('profile'))), 'captcha.png')
|
||||
util.save_to_file(params['img'], img)
|
||||
cd = CaptchaDialog('captcha-dialog.xml',
|
||||
xbmcutil.__addon__.getAddonInfo('path'), 'default', '0')
|
||||
cd.image = img
|
||||
xbmc.sleep(3000)
|
||||
cd.doModal()
|
||||
del cd
|
||||
kb = xbmc.Keyboard('', self.addon.getLocalizedString(200), False)
|
||||
kb.doModal()
|
||||
if kb.isConfirmed():
|
||||
print('got code ' + kb.getText())
|
||||
return kb.getText()
|
||||
|
||||
def ask_for_account_type(self):
|
||||
if len(self.provider.username) == 0:
|
||||
util.info('Username is not set, NOT using VIP account')
|
||||
return False
|
||||
if self.settings['vip'] == '0':
|
||||
util.info('Asking user whether to use VIP account')
|
||||
ret = xbmcgui.Dialog().yesno(self.provider.name, xbmcutil.__lang__(30010))
|
||||
return ret == 1
|
||||
return self.settings['vip'] == '1'
|
||||
|
||||
def resolve(self, url):
|
||||
item = self.provider.video_item()
|
||||
item.update({'url': url})
|
||||
if not self.ask_for_account_type():
|
||||
# set user/pass to null - user does not want to use VIP at this time
|
||||
self.provider.username = None
|
||||
self.provider.password = None
|
||||
else:
|
||||
if not self.provider.login():
|
||||
xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011))
|
||||
return
|
||||
try:
|
||||
return self.provider.resolve(item, captcha_cb=self.ask_for_captcha)
|
||||
except ResolveException as e:
|
||||
self._handle_exc(e)
|
||||
|
||||
|
||||
class XBMCLoginOptionalDelayedContentProvider(XBMCLoginOptionalContentProvider):
|
||||
|
||||
def wait_cb(self, wait):
|
||||
left = wait
|
||||
msg = xbmcutil.__lang__(30014).encode('utf-8')
|
||||
while left > 0:
|
||||
xbmc.executebuiltin("XBMC.Notification(%s,%s,1000,%s)" %
|
||||
(self.provider.name, msg % str(left), ''))
|
||||
left -= 1
|
||||
time.sleep(1)
|
||||
|
||||
def resolve(self, url):
|
||||
item = self.video_item()
|
||||
item.update({'url': url})
|
||||
if not self.ask_for_account_type():
|
||||
# set user/pass to null - user does not want to use VIP at this time
|
||||
self.provider.username = None
|
||||
self.provider.password = None
|
||||
else:
|
||||
if not self.provider.login():
|
||||
xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011))
|
||||
return
|
||||
try:
|
||||
return self.provider.resolve(item, captcha_cb=self.ask_for_captcha, wait_cb=self.wait_cb)
|
||||
except ResolveException as e:
|
||||
self._handle_exc(e)
|
||||
|
||||
|
||||
class CaptchaDialog (xbmcgui.WindowXMLDialog):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(xbmcgui.WindowXMLDialog, self).__init__(args, kwargs)
|
||||
self.image = None
|
||||
|
||||
def onFocus(self, controlId):
|
||||
self.controlId = controlId
|
||||
|
||||
def onInit(self):
|
||||
self.getControl(101).setImage(self.image)
|
||||
|
||||
def onAction(self, action):
|
||||
if action.getId() in [9, 10]:
|
||||
self.close()
|
||||
|
||||
def onClick(self, controlId):
|
||||
if controlId == 102:
|
||||
self.close()
|
||||
Reference in New Issue
Block a user