Files
addon/platformcode/globalsearch.py
2020-12-11 17:16:59 +01:00

713 lines
28 KiB
Python

# -*- coding: utf-8 -*-
import xbmc, xbmcgui, sys, channelselector, time, gc
from core.support import dbg, typo, tmdb
from core.item import Item
from core import channeltools, servertools, scrapertools
from platformcode import platformtools, config, logger
from platformcode.launcher import run
from threading import Thread
if sys.version_info[0] >= 3: from concurrent import futures
else: from concurrent_py2 import futures
info_language = ["de", "en", "es", "fr", "it", "pt"] # from videolibrary.json
def_lang = info_language[config.get_setting("info_language", "videolibrary")]
def busy(state):
if state: xbmc.executebuiltin('ActivateWindow(busydialognocancel)')
else: xbmc.executebuiltin('Dialog.Close(busydialognocancel)')
def set_workers():
workers = config.get_setting('thread_number') if config.get_setting('thread_number') > 0 else None
return workers
def Search(*args):
xbmc.executebuiltin('Dialog.Close(all)')
w = SearchWindow('GlobalSearch.xml', config.get_runtime_path())
w.start(*args)
del w
gc.collect()
# xbmc.sleep(600)
# Actions
LEFT = 1
RIGHT = 2
UP = 3
DOWN = 4
ENTER = 7
EXIT = 10
BACKSPACE = 92
SWIPEUP = 531
CONTEXT = 117
MOUSEMOVE = 107
# Container
SEARCH = 1
EPISODES = 2
SERVERS = 3
NORESULTS = 4
LOADING = 5
# Search
MAINTITLE = 100
CHANNELS = 101
RESULTS = 102
PROGRESS = 500
COUNT = 501
MENU = 502
BACK = 503
CLOSE = 504
QUALITYTAG = 505
# Servers
EPISODESLIST = 200
SERVERLIST = 300
class SearchWindow(xbmcgui.WindowXML):
def start(self, item, moduleDict={}, searchActions=[]):
logger.debug()
self.exit = False
self.item = item
self.type = self.item.mode
self.channels = []
self.persons = []
self.episodes = []
self.servers = []
self.results = {}
self.focus = SEARCH
self.process = True
self.page = 1
self.moduleDict = moduleDict
self.searchActions = searchActions
self.thread = None
self.selected = False
self.items = []
if not searchActions:
self.thActions = Thread(target=self.getActions)
self.thActions.start()
else:
self.thActions = None
self.lastSearch()
if not self.item.text: return
self.doModal()
def lastSearch(self):
logger.debug()
if not self.item.text:
if config.get_setting('last_search'): last_search = channeltools.get_channel_setting('Last_searched', 'search', '')
else: last_search = ''
if not self.item.text: self.item.text = platformtools.dialog_input(default=last_search, heading='')
if self.item.text:
channeltools.set_channel_setting('Last_searched', self.item.text, 'search')
from specials.search import save_search
save_search(self.item.text)
def getActions(self):
logger.debug()
count = 0
self.channelsList = self.get_channels()
for channel in self.channelsList:
self.getModule(channel)
count += 1
percent = (float(count) / len(self.channelsList)) * 100
if self.thread or self.selected: # window started
self.PROGRESS.setPercent(percent)
self.COUNT.setText('%s/%s' % (count, len(self.channelsList)))
def select(self):
logger.debug()
self.PROGRESS.setVisible(False)
self.items = []
if self.persons:
tmdb_info = tmdb.discovery(self.item, dict_=self.item.discovery)
results = tmdb_info.results.get('cast',[])
else:
tmdb_info = tmdb.Tmdb(texto_buscado=self.item.text, tipo=self.item.mode.replace('show', ''))
results = tmdb_info.results
for result in results:
logger.info(result)
result = tmdb_info.get_infoLabels(result, origen=result)
if self.item.mode == 'movie':
title = result['title']
result['mode'] = 'movie'
else:
title = result['name']
result['mode'] = 'tvshow'
thumbnail = result.get('thumbnail', '')
noThumb = 'Infoplus/' + result['mode'].replace('show','') + '.png'
fanart = result.get('fanart', '')
year = result.get('release_date', '')
rating = str(result.get('vote_average', ''))
new_item = Item(channel='search',
action=True,
title=title,
thumbnail=thumbnail,
fanart=fanart,
mode='search',
type=result['mode'],
contentType=result['mode'],
text=title,
infoLabels=result)
if self.item.mode == 'movie':
new_item.contentTitle = result['title']
else:
new_item.contentSerieName = result['name']
it = xbmcgui.ListItem(title)
it.setProperties({'thumb': result.get('thumbnail', noThumb), 'fanart': result.get('fanart', ''), 'rating': ' [' + rating + ']' if rating else '',
'plot': result.get('overview', ''), 'search': 'search', 'release_date': '', 'item': new_item.tourl(),
'year': ' [' + year.split('/')[-1] + ']' if year else ' [' + result.get('first_air_date','').split('-')[0] + ']'})
self.items.append(it)
if self.items:
self.RESULTS.reset()
self.RESULTS.addItems(self.items)
self.setFocusId(RESULTS)
else:
self.NORESULTS.setVisible(True)
self.setFocusId(CLOSE)
def actors(self):
logger.debug()
self.PROGRESS.setVisible(False)
items = []
dict_ = {'url': 'search/person', 'language': def_lang, 'query': self.item.text, 'page':self.page}
prof = {'Acting': 'Actor', 'Directing': 'Director', 'Production': 'Productor'}
plot = ''
self.item.search_type = 'person'
tmdb_inf = tmdb.discovery(self.item, dict_=dict_)
results = tmdb_inf.results
for elem in results:
name = elem.get('name', '')
if not name: continue
rol = elem.get('known_for_department', '')
rol = prof.get(rol, rol)
know_for = elem.get('known_for', '')
cast_id = elem.get('id', '')
if know_for:
t_k = know_for[0].get('title', '')
if t_k: plot = '%s in %s' % (rol, t_k)
t = elem.get('profile_path', '')
if t: thumb = 'https://image.tmdb.org/t/p/original' + t
else : thumb = 'Infoplus/no_photo.png'
discovery = {'url': 'person/%s/combined_credits' % cast_id, 'page': '1', 'sort_by': 'primary_release_date.desc', 'language': def_lang}
self.persons.append(discovery)
new_item = Item(channel='search',
action=True,
title=name,
thumbnail=thumb,
mode='search')
it = xbmcgui.ListItem(name)
it.setProperties({'thumb': thumb, 'plot': plot, 'search': 'persons', 'item': new_item.tourl()})
items.append(it)
if len(results) > 19:
it = xbmcgui.ListItem(config.get_localized_string(70006))
it.setProperty('thumb', 'Infoplus/next_focus.png')
it.setProperty('search','next')
items.append(it)
if self.page > 1:
it = xbmcgui.ListItem(config.get_localized_string(70005))
it.setProperty('thumb', 'Infoplus/previous_focus.png')
it.setProperty('search','previous')
items.insert(0, it)
if items:
self.RESULTS.reset()
self.RESULTS.addItems(items)
self.setFocusId(RESULTS)
else:
self.NORESULTS.setVisible(True)
def get_channels(self):
logger.debug()
channels_list = []
all_channels = channelselector.filterchannels('all')
for ch in all_channels:
channel = ch.channel
ch_param = channeltools.get_channel_parameters(channel)
if not ch_param.get("active", False):
continue
list_cat = ch_param.get("categories", [])
if not ch_param.get("include_in_global_search", False):
continue
if 'anime' in list_cat:
n = list_cat.index('anime')
list_cat[n] = 'tvshow'
if self.item.mode == 'all' or self.item.mode in list_cat or self.item.type in list_cat:
if config.get_setting("include_in_global_search", channel) and ch_param.get("active", False):
channels_list.append(channel)
logger.debug('search in channels:',channels_list)
return channels_list
def getModule(self, channel):
logger.debug()
try:
module = __import__('channels.%s' % channel, fromlist=["channels.%s" % channel])
mainlist = getattr(module, 'mainlist')(Item(channel=channel, global_search=True))
action = [elem for elem in mainlist if elem.action == "search" and (self.item.mode == 'all' or elem.contentType in [self.item.mode, 'undefined'])]
self.moduleDict[channel] = module
self.searchActions += action
except:
import traceback
logger.error('error importing/getting search items of ' + channel)
logger.error(traceback.format_exc())
def search(self):
self.count = 0
self.LOADING.setVisible(True)
if self.thActions:
self.thActions.join()
logger.debug()
with futures.ThreadPoolExecutor(max_workers=set_workers()) as executor:
for searchAction in self.searchActions:
if self.exit: return
executor.submit(self.get_channel_results, self.item, self.moduleDict, searchAction)
def get_channel_results(self, item, module_dict, search_action):
logger.debug()
channel = search_action.channel
results = []
valid = []
other = []
module = module_dict[channel]
searched_id = item.infoLabels['tmdb_id']
try:
results.extend(module.search(search_action, item.text))
if len(results) == 1:
if not results[0].action or config.get_localized_string(70006).lower() in results[0].title.lower():
results = []
if self.item.mode != 'all':
for elem in results:
if elem.infoLabels['tmdb_id'] == searched_id:
elem.from_channel = channel
elem.verified = 1
valid.append(elem)
else:
other.append(elem)
except:
pass
self.count += 1
self.update(channel, valid, other if other else results)
def makeItem(self, url):
item = Item().fromurl(url)
logger.debug(item)
channelParams = channeltools.get_channel_parameters(item.channel)
thumb = item.thumbnail if item.thumbnail else 'Infoplus/' + item.contentType.replace('show', '') + '.png'
logger.info('THUMB', thumb)
it = xbmcgui.ListItem(item.title)
year = str(item.year if item.year else item.infoLabels.get('year', ''))
rating = str(item.infoLabels.get('rating', ''))
it.setProperties({'thumb': thumb, 'fanart': item.fanart, 'plot': item.plot,
'year': ' [' + year + ']' if year else '', 'rating':' [' + rating + ']' if rating else '',
'item': url, 'verified': item.verified, 'channel':channelParams['title'], 'channelthumb': channelParams['thumbnail'] if item.verified else ''})
if item.server:
color = scrapertools.find_single_match(item.alive, r'(FF[^\]]+)')
it.setProperties({'channel': channeltools.get_channel_parameters(item.channel).get('title', ''),
'thumb': "https://raw.githubusercontent.com/kodiondemand/media/master/resources/servers/%s.png" % item.server.lower(),
'servername': servertools.get_server_parameters(item.server.lower()).get('name', item.server),
'color': color if color else 'FF0082C2'})
return it
def update(self, channel, valid, results):
if self.exit:
return
logger.debug('Search on channel', channel)
if self.item.mode != 'all' and 'valid' not in self.results:
self.results['valid'] = 0
item = xbmcgui.ListItem('valid')
item.setProperties({'thumb': 'valid.png',
'position': '0',
'results': '0'})
self.channels.append(item)
pos = self.CHANNELS.getSelectedPosition()
self.CHANNELS.reset()
self.CHANNELS.addItems(self.channels)
self.CHANNELS.selectItem(pos)
self.setFocusId(CHANNELS)
if valid:
item = self.CHANNELS.getListItem(0)
resultsList = item.getProperty('items')
for result in valid:
resultsList += result.tourl() + '|'
item.setProperty('items',resultsList)
self.channels[0].setProperty('results', str(len(resultsList.split('|')) - 1))
if self.CHANNELS.getSelectedPosition() == 0:
channelResults = self.CHANNELS.getListItem(0).getProperty('items').split('|')
items = []
for result in channelResults:
if result: items.append(self.makeItem(result))
pos = self.RESULTS.getSelectedPosition()
self.RESULTS.reset()
self.RESULTS.addItems(items)
self.RESULTS.selectItem(pos)
if results:
resultsList = ''
channelParams = channeltools.get_channel_parameters(channel)
name = channelParams['title']
if name not in self.results:
self.results[name] = len(self.results)
item = xbmcgui.ListItem(name)
item.setProperties({'thumb': channelParams['thumbnail'],
'position': '0',
'results': str(len(results))
})
for result in results:
resultsList += result.tourl() + '|'
item.setProperty('items',resultsList)
self.channels.append(item)
else:
item = self.CHANNELS.getListItem(self.results[name])
resultsList = item.getProperty('items')
for result in results:
resultsList += result.tourl() + '|'
item.setProperty('items',resultsList)
self.channels[int(self.results[name])].setProperty('results', str(len(results)))
pos = self.CHANNELS.getSelectedPosition()
self.CHANNELS.reset()
self.CHANNELS.addItems(self.channels)
self.CHANNELS.selectItem(pos)
if len(self.channels) == 1:
self.setFocusId(CHANNELS)
channelResults = self.CHANNELS.getListItem(self.results[name]).getProperty('items').split('|')
items = []
for result in channelResults:
if result: items.append(self.makeItem(result))
self.RESULTS.reset()
self.RESULTS.addItems(items)
percent = (float(self.count) / len(self.searchActions)) * 100
self.LOADING.setVisible(False)
self.PROGRESS.setPercent(percent)
self.COUNT.setText('%s/%s [%s"]' % (self.count, len(self.searchActions), int(time.time() - self.time) ))
if percent == 100:
self.channels = []
self.moduleDict = {}
self.searchActions = []
if percent == 100 and not self.results:
self.PROGRESS.setVisible(False)
self.NORESULTS.setVisible(True)
def onInit(self):
self.time = time.time()
# collect controls
self.CHANNELS = self.getControl(CHANNELS)
self.RESULTS = self.getControl(RESULTS)
self.PROGRESS = self.getControl(PROGRESS)
self.COUNT = self.getControl(COUNT)
self.MAINTITLE = self.getControl(MAINTITLE)
self.MAINTITLE.setText(typo(config.get_localized_string(30993).replace('...','') % '"%s"' % self.item.text, 'bold'))
self.SEARCH = self.getControl(SEARCH)
self.EPISODES = self.getControl(EPISODES)
self.EPISODESLIST = self.getControl(EPISODESLIST)
self.SERVERS = self.getControl(SERVERS)
self.SERVERLIST = self.getControl(SERVERLIST)
self.NORESULTS = self.getControl(NORESULTS)
self.NORESULTS.setVisible(False)
self.LOADING = self.getControl(LOADING)
self.LOADING.setVisible(False)
self.Focus(self.focus)
if self.type:
self.type = None
if self.item.mode in ['all', 'search']:
if self.item.type: self.item.mode = self.item.type
self.thread = Thread(target=self.search)
self.thread.start()
elif self.item.mode in ['movie', 'tvshow']:
self.select()
elif self.item.mode in ['person']:
self.actors()
def Focus(self, focusid):
if focusid in [SEARCH]:
self.focus = CHANNELS
self.SEARCH.setVisible(True)
self.EPISODES.setVisible(False)
self.SERVERS.setVisible(False)
if focusid in [EPISODES]:
self.focus = focusid
self.SEARCH.setVisible(False)
self.EPISODES.setVisible(True)
self.SERVERS.setVisible(False)
if focusid in [SERVERS]:
self.focus = SERVERLIST
self.SEARCH.setVisible(False)
self.EPISODES.setVisible(False)
self.SERVERS.setVisible(True)
def onAction(self, action):
action = action.getId()
focus = self.getFocusId()
if action in [CONTEXT] and focus in [RESULTS, EPISODESLIST, SERVERLIST]:
self.context()
elif action in [SWIPEUP] and self.CHANNELS.isVisible():
self.setFocusId(CHANNELS)
pos = self.CHANNELS.getSelectedPosition()
self.CHANNELS.selectItem(pos)
elif action in [LEFT, RIGHT, MOUSEMOVE] and focus in [CHANNELS] and self.CHANNELS.isVisible():
items = []
name = self.CHANNELS.getSelectedItem().getLabel()
subpos = int(self.CHANNELS.getSelectedItem().getProperty('position'))
channelResults = self.CHANNELS.getListItem(self.results[name]).getProperty('items').split('|')
for result in channelResults:
if result: items.append(self.makeItem(result))
self.RESULTS.reset()
self.RESULTS.addItems(items)
self.RESULTS.selectItem(subpos)
elif action in [DOWN] and focus in [BACK, CLOSE, MENU]:
if self.SERVERS.isVisible(): self.setFocusId(SERVERLIST)
elif self.EPISODES.isVisible(): self.setFocusId(EPISODESLIST)
else: self.setFocusId(RESULTS)
elif focus in [RESULTS] and self.item.mode == 'all':
pos = self.RESULTS.getSelectedPosition()
self.CHANNELS.getSelectedItem().setProperty('position', str(pos))
elif action == ENTER and focus in [CHANNELS]:
self.setFocusId(RESULTS)
if action in [BACKSPACE]:
self.Back()
elif action in [EXIT]:
self.Close()
def onClick(self, control_id):
if self.RESULTS.getSelectedItem(): search = self.RESULTS.getSelectedItem().getProperty('search')
else: search = None
if control_id in [CHANNELS]:
items = []
name = self.CHANNELS.getSelectedItem().getLabel()
subpos = int(self.CHANNELS.getSelectedItem().getProperty('position'))
channelResults = self.CHANNELS.getListItem(self.results[name]).getProperty('items').split('|')
for result in channelResults:
if result: items.append(self.makeItem(result))
self.RESULTS.reset()
self.RESULTS.addItems(items)
self.RESULTS.selectItem(subpos)
self.CHANNELS.getSelectedItem().setProperty('position', str(subpos))
elif control_id in [BACK]:
self.Back()
elif control_id in [CLOSE]:
self.Close()
elif control_id in [MENU]:
self.context()
elif search:
pos = self.RESULTS.getSelectedPosition()
if search == 'next':
self.page += 1
self.actors()
elif search == 'previous':
self.page -= 1
self.actors()
elif search == 'persons':
self.item.discovery = self.persons[pos]
self.select()
else:
item = Item().fromurl(self.RESULTS.getSelectedItem().getProperty('item'))
if self.item.mode == 'movie': item.contentTitle = self.RESULTS.getSelectedItem().getLabel()
else: item.contentSerieName = self.RESULTS.getSelectedItem().getLabel()
self.RESULTS.reset()
self.RESULTS.setVisible(False)
self.PROGRESS.setVisible(True)
self.selected = True
self.thActions.join()
self.RESULTS.addItems(self.items)
self.RESULTS.setVisible(True)
self.PROGRESS.setVisible(False)
return Search(item, self.moduleDict, self.searchActions)
elif control_id in [RESULTS, EPISODESLIST]:
busy(True)
if control_id in [RESULTS]:
name = self.CHANNELS.getSelectedItem().getLabel()
self.pos = self.RESULTS.getSelectedPosition()
item = Item().fromurl(self.RESULTS.getSelectedItem().getProperty('item'))
else:
self.pos = self.EPISODESLIST.getSelectedPosition()
item_url = self.EPISODESLIST.getSelectedItem().getProperty('item')
if item_url:
item = Item().fromurl(item_url)
else: # no results item
busy(False)
return
if item.action not in ['findvideos', 'episodios']: # special items (add to videolibrary, download ecc.)
xbmc.executebuiltin("RunPlugin(plugin://plugin.video.kod/?" + item_url)
busy(False)
return
try:
self.channel = __import__('channels.%s' % item.channel, fromlist=["channels.%s" % item.channel])
self.itemsResult = getattr(self.channel, item.action)(item)
except:
import traceback
logger.error('error importing/getting search items of ' + item.channel)
logger.error(traceback.format_exc())
self.itemsResult = []
if self.itemsResult and self.itemsResult[0].action in ['play', '']:
if config.get_setting('checklinks') and not config.get_setting('autoplay'):
self.itemsResult = servertools.check_list_links(self.itemsResult, config.get_setting('checklinks_number'))
servers = self.itemsResult if self.itemsResult else []
self.itemsResult = []
uhd = []
fhd = []
hd = []
sd = []
unknown = []
for i, item in enumerate(servers):
if item.server:
it = self.makeItem(item.tourl())
it.setProperty('index', str(i))
if item.quality in ['4k', '2160p', '2160', '4k2160p', '4k2160', '4k 2160p', '4k 2160', '2k']:
it.setProperty('quality', 'uhd.png')
uhd.append(it)
elif item.quality in ['fullhd', 'fullhd 1080', 'fullhd 1080p', 'full hd', 'full hd 1080', 'full hd 1080p', 'hd1080', 'hd1080p', 'hd 1080', 'hd 1080p', '1080', '1080p']:
it.setProperty('quality', 'Fhd.png')
fhd.append(it)
elif item.quality in ['hd', 'hd720', 'hd720p', 'hd 720', 'hd 720p', '720', '720p', 'hdtv']:
it.setProperty('quality', 'hd.png')
hd.append(it)
elif item.quality in ['sd', '480p', '480', '360p', '360', '240p', '240']:
it.setProperty('quality', 'sd.png')
sd.append(it)
else:
it.setProperty('quality', '')
unknown.append(it)
elif not item.action:
self.getControl(QUALITYTAG).setText(item.fulltitle)
uhd.sort(key=lambda it: it.getProperty('index'))
fhd.sort(key=lambda it: it.getProperty('index'))
hd.sort(key=lambda it: it.getProperty('index'))
sd.sort(key=lambda it: it.getProperty('index'))
unknown.sort(key=lambda it: it.getProperty('index'))
serverlist = uhd + fhd + hd + sd + unknown
if not serverlist:
serverlist = [xbmcgui.ListItem(config.get_localized_string(60347))]
serverlist[0].setProperty('thumb', channelselector.get_thumb('nofolder.png'))
self.Focus(SERVERS)
self.SERVERLIST.reset()
self.SERVERLIST.addItems(serverlist)
self.setFocusId(SERVERLIST)
else:
episodes = self.itemsResult if self.itemsResult else []
self.itemsResult = []
ep = []
for item in episodes:
# if item.action == 'findvideos':
it = xbmcgui.ListItem(item.title)
it.setProperty('item', item.tourl())
ep.append(it)
if not ep:
ep = [xbmcgui.ListItem(config.get_localized_string(60347))]
ep[0].setProperty('thumb', channelselector.get_thumb('nofolder.png'))
self.Focus(EPISODES)
self.EPISODESLIST.reset()
self.EPISODESLIST.addItems(ep)
self.setFocusId(EPISODESLIST)
busy(False)
elif control_id in [SERVERLIST]:
server = Item().fromurl(self.getControl(control_id).getSelectedItem().getProperty('item'))
server.globalsearch = True
return run(server)
def Back(self):
self.getControl(QUALITYTAG).setText('')
if self.SERVERS.isVisible():
if self.episodes:
self.Focus(EPISODES)
self.setFocusId(EPISODESLIST)
else:
self.Focus(SEARCH)
self.setFocusId(RESULTS)
self.RESULTS.selectItem(self.pos)
elif self.EPISODES.isVisible():
self.episodes = []
self.Focus(SEARCH)
self.setFocusId(RESULTS)
self.RESULTS.selectItem(self.pos)
elif self.item.mode in ['person']:
self.actors()
else:
self.Close()
def Close(self):
self.exit = True
if self.thread:
busy(True)
self.thread.join()
busy(False)
self.close()
del self
def context(self):
focus = self.getFocusId()
if focus == EPISODESLIST: # context on episode
item_url = self.EPISODESLIST.getSelectedItem().getProperty('item')
parent = Item().fromurl(self.RESULTS.getSelectedItem().getProperty('item'))
elif focus == SERVERLIST:
item_url = self.SERVERLIST.getSelectedItem().getProperty('item')
parent = Item().fromurl(self.RESULTS.getSelectedItem().getProperty('item'))
else:
item_url = self.RESULTS.getSelectedItem().getProperty('item')
parent = self.item
item = Item().fromurl(item_url)
parent.noMainMenu = True
commands = platformtools.set_context_commands(item, item_url, parent)
context = [c[0] for c in commands]
context_commands = [c[1].replace('Container.Refresh', 'RunPlugin').replace('Container.Update', 'RunPlugin') for c in commands]
index = xbmcgui.Dialog().contextmenu(context)
if index > 0: xbmc.executebuiltin(context_commands[index])