KoD 1.6.1
-Migliorata l'efficacia del riconoscimento dei contenuti in ricerca film/serie - corretti alcuni bug e fatti alcuni fix per i soliti cambi di struttura
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<addon id="plugin.video.kod" name="Kodi on Demand" version="1.6" provider-name="KoD Team">
|
||||
<addon id="plugin.video.kod" name="Kodi on Demand" version="1.6.1" provider-name="KoD Team">
|
||||
<requires>
|
||||
<!-- <import addon="script.module.libtorrent" optional="true"/> -->
|
||||
<import addon="metadata.themoviedb.org"/>
|
||||
@@ -27,10 +27,8 @@
|
||||
<screenshot>resources/media/themes/ss/2.png</screenshot>
|
||||
<screenshot>resources/media/themes/ss/3.png</screenshot>
|
||||
</assets>
|
||||
<news>- rimosso supporto a TVDB (l'accesso alle API diventerà a pagamento)
|
||||
- aggiunto canale Discovery+
|
||||
- aggiunta possibilità di scegliere numerazioni alternative per le serie tv
|
||||
- migliorie interne di vario tipo (tra cui un migliore riconoscimento dei contenuti nel caso siano scritti male)</news>
|
||||
<news>-Migliorata l'efficacia del riconoscimento dei contenuti in ricerca film/serie
|
||||
- corretti alcuni bug e fatti alcuni fix per i soliti cambi di struttura</news>
|
||||
<description lang="it">Naviga velocemente sul web e guarda i contenuti presenti</description>
|
||||
<disclaimer>[COLOR red]The owners and submitters to this addon do not host or distribute any of the content displayed by these addons nor do they have any affiliation with the content providers.[/COLOR]
|
||||
[COLOR yellow]Kodi © is a registered trademark of the XBMC Foundation. We are not connected to or in any other way affiliated with Kodi, Team Kodi, or the XBMC Foundation. Furthermore, any software, addons, or products offered by us will receive no support in official Kodi channels, including the Kodi forums and various social networks.[/COLOR]</disclaimer>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"eurostreaming": "https://eurostreaming.team",
|
||||
"filmgratis": "https://www.filmaltadefinizione.me",
|
||||
"filmigratis": "https://filmigratis.org",
|
||||
"filmsenzalimiticc": "https://www.filmsenzalimiti01.surf",
|
||||
"filmsenzalimiticc": "https://www.filmsenzalimiti01.xyz",
|
||||
"filmstreaming01": "https://filmstreaming01.com",
|
||||
"guardaserie_stream": "https://guardaserie.yoga",
|
||||
"guardaseriecam": "https://guardaserie.cam",
|
||||
|
||||
@@ -10,14 +10,7 @@ typo = support.typo
|
||||
session = requests.Session()
|
||||
host = support.config.get_channel_url()
|
||||
|
||||
def getToken():
|
||||
token = config.get_setting('token', 'discoveryplus', None)
|
||||
if not token:
|
||||
token = session.get('https://disco-api.discoveryplus.it/token?realm=dplayit').json()['data']['attributes']['token']
|
||||
config.set_setting('token', token, 'discoveryplus')
|
||||
return token
|
||||
|
||||
token = getToken()
|
||||
token = session.get('https://disco-api.discoveryplus.it/token?realm=dplayit').json()['data']['attributes']['token']
|
||||
|
||||
api = "https://disco-api.discoveryplus.it"
|
||||
headers = {'User-Agent': 'Mozilla/50.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0',
|
||||
@@ -70,7 +63,7 @@ def live(item):
|
||||
logger.debug()
|
||||
itemlist =[]
|
||||
for name, values in liveDict().items():
|
||||
itemlist.append(item.clone(title=typo(name), fulltitle=name, plot=values['plot'], url=values['url'], id=values['id'], action='play', forcethumb=True, no_return=True))
|
||||
itemlist.append(item.clone(title=typo(name,'bold'), fulltitle=name, plot=values['plot'], url=values['url'], id=values['id'], action='play', forcethumb=True, no_return=True))
|
||||
return support.thumb(itemlist, live=True)
|
||||
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ def play(item):
|
||||
data = support.match(sec_data, patron=r'<video src="([^"]+)').match
|
||||
break
|
||||
else:
|
||||
support.dbg()
|
||||
# support.dbg()
|
||||
data = url
|
||||
|
||||
return support.servertools.find_video_items(item, data=data)
|
||||
|
||||
@@ -88,7 +88,7 @@ def peliculas(item):
|
||||
patron = r'href="(?P<url>[^"]+)"[^>]+>(?P<title>.+?)(?:\s(?P<year>\d{4})|<)'
|
||||
patronBlock = r'Lista dei film disponibili in streaming e anche in download\.</p>(?P<block>.*?)<div class="footer_c">'
|
||||
else:
|
||||
patron = r'<tr><td><a href="(?P<url>[^"]+)"(?:|.+?)?>(?: )?[ ]?(?P<title>.*?)[ ]?(?P<quality>HD)?[ ]?(?P<year>\d+)?(?: | HD | Streaming | MD(?: iSTANCE)? )?</a>'
|
||||
patron = r'<tr><td><a href="(?P<url>[^"]+)"(?:|.+?)?>(?: )?[ ]?(?P<title>.*?)[ ]?(?P<quality>HD)?[ ]?(?P<year>\d+)?(?: | HD[^<]*| Streaming[^<]*| MD(?: iSTANCE)? [^<]*)?</a>'
|
||||
|
||||
def itemHook(item):
|
||||
if 'film' in item.url:
|
||||
|
||||
@@ -5,17 +5,11 @@
|
||||
|
||||
from core import support
|
||||
import sys
|
||||
if sys.version_info[0] >= 3: from concurrent import futures
|
||||
else: from concurrent_py2 import futures
|
||||
|
||||
host = support.config.get_channel_url()
|
||||
|
||||
headers = [['Referer', host]]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@support.menu
|
||||
def mainlist(item):
|
||||
|
||||
@@ -33,22 +27,10 @@ def search(item, text):
|
||||
support.info(text)
|
||||
# item.args='search'
|
||||
item.text = text
|
||||
itemlist = []
|
||||
itlist = []
|
||||
item.url = item.url + '/?%73=' + text.replace(' ', '+')
|
||||
|
||||
try:
|
||||
# item.url = host + '/lista-serie-tv/'
|
||||
# item.contentType = 'tvshow'
|
||||
# itemlist += peliculas(item)
|
||||
with futures.ThreadPoolExecutor() as executor:
|
||||
for par in [['/serie-tv/', 'tvshow', ''],['/anime/', 'tvshow', ''], ['/-anime-sub-ita/', 'tvshow', 'sub'], ['/film-animazione/', 'movie', '']]:
|
||||
item.url = host + par[0]
|
||||
item.contentType = par[1]
|
||||
item.args = par[2]
|
||||
itlist.append(executor.submit(peliculas, item))
|
||||
for res in futures.as_completed(itlist):
|
||||
itemlist += res.result()
|
||||
return itemlist
|
||||
return peliculas(item)
|
||||
# Continua la ricerca in caso di errore
|
||||
except:
|
||||
import sys
|
||||
@@ -75,21 +57,26 @@ def newest(categoria):
|
||||
|
||||
@support.scrape
|
||||
def peliculas(item):
|
||||
search = item.text
|
||||
# debugBlock = True
|
||||
# debug = True
|
||||
# search = item.text
|
||||
if item.contentType != 'movie': anime = True
|
||||
action = 'findvideos' if item.contentType == 'movie' else 'episodios'
|
||||
blacklist = ['-Film Animazione disponibili in attesa di recensione ']
|
||||
|
||||
if search:
|
||||
if item.action == 'search':
|
||||
pagination = ''
|
||||
patronBlock = '"lcp_catlist"[^>]+>(?P<block>.*)</ul>'
|
||||
patron = r'href="(?P<url>[^"]+)" title="(?P<title>[^"]+)"'
|
||||
#patronBlock = '"lcp_catlist"[^>]+>(?P<block>.*)</ul>'
|
||||
patronBlock = '<main[^>]+>(?P<block>.*?)</ma'
|
||||
#patron = r'href="(?P<url>[^"]+)" title="(?P<title>[^"]+)"'
|
||||
patron = r'<a href="(?P<url>[^"]+)"[^>]*>(?P<title>[^<]+)<[^>]+>[^>]+><div'
|
||||
elif item.args == 'last':
|
||||
patronBlock = 'Aggiornamenti</h2>(?P<block>.*)</ul>'
|
||||
patron = r'<a href="(?P<url>[^"]+)">\s*<img[^>]+src(?:set)?="(?P<thumbnail>[^ ]+)[^>]+>\s*<span[^>]+>(?P<title>[^<]+)'
|
||||
patron = r'<a href="(?P<url>[^"]+)">\s*<img[^>]+src[set]{0,3}="(?P<thumbnail>[^ ]+)[^>]+>\s*<span[^>]+>(?P<title>[^<]+)'
|
||||
else:
|
||||
patronBlock = '<main[^>]+>(?P<block>.*)</main>'
|
||||
patron = r'<a href="(?P<url>[^"]+)" rel="bookmark">(?P<title>[^<]+)</a>[^>]+>[^>]+>[^>]+><img.*?src="(?P<thumb>[^"]+)".*?<p>(?P<plot>[^<]+)</p>.*?<span class="cat-links">Pubblicato in.*?.*?(?P<type>(?:[Ff]ilm|</artic))[^>]+>'
|
||||
# patron = r'<a href="(?P<url>[^"]+)" rel="bookmark">(?P<title>[^<]+)</a>[^>]+>[^>]+>[^>]+><img.*?src="(?P<thumb>[^"]+)".*?<p>(?P<plot>[^<]+)</p>.*?<span class="cat-links">Pubblicato in.*?.*?(?P<type>(?:[Ff]ilm|</artic))[^>]+>'
|
||||
patron = r'<a href="(?P<url>[^"]+)"[^>]+>(?P<title>[^<]+)</a>[^>]+>[^>]+>[^>]+><img.*?src="(?P<thumb>[^"]+)".*?<p>(?P<plot>[^<]+)</p>.*?tag">.*?(?P<type>(?:[Ff]ilm|</art|Serie Tv))'
|
||||
typeContentDict={'movie':['film']}
|
||||
typeActionDict={'findvideos':['film']}
|
||||
patronNext = '<a class="next page-numbers" href="([^"]+)">'
|
||||
|
||||
@@ -350,7 +350,7 @@ class Item(object):
|
||||
def fromurl(self, url):
|
||||
"""
|
||||
Generate an item from a text string. The string can be created by the tourl () function or have
|
||||
the old format: plugin: //plugin.video.kod/? channel = ... (+ other parameters)
|
||||
the old format: plugin: //plugin.video.kod/? channel = ... (+ other parameters)
|
||||
Use: item.fromurl("string")
|
||||
|
||||
@param url: url
|
||||
|
||||
36
core/tmdb.py
36
core/tmdb.py
@@ -66,6 +66,10 @@ otmdb_global = None
|
||||
from core import db
|
||||
|
||||
|
||||
def clean_cache():
|
||||
db['tmdb_cache'].clear()
|
||||
|
||||
|
||||
# The function name is the name of the decorator and receives the function that decorates.
|
||||
def cache_response(fn):
|
||||
logger.debug()
|
||||
@@ -180,7 +184,8 @@ def set_infoLabels_itemlist(item_list, seekTmdb=False, idioma_busqueda=def_lang,
|
||||
"""
|
||||
Concurrently, it gets the data of the items included in the item_list.
|
||||
|
||||
The API has a limit of 40 requests per IP every 10 '' and that is why the list should not have more than 30 items to ensure the proper functioning of this function.
|
||||
The API in the past had a limit of 40 requests per IP every 10 '', now there's no limit (https://developers.themoviedb.org/3/getting-started/request-rate-limiting)
|
||||
If a limit will be re-added uncomment "tmdb_threads" and related code
|
||||
|
||||
@param item_list: list of Item objects that represent movies, series or chapters. The infoLabels attribute of each Item object will be modified including the extra localized data.
|
||||
@type item_list: list
|
||||
@@ -197,18 +202,18 @@ def set_infoLabels_itemlist(item_list, seekTmdb=False, idioma_busqueda=def_lang,
|
||||
return
|
||||
import threading
|
||||
|
||||
threads_num = config.get_setting("tmdb_threads", default=20)
|
||||
semaforo = threading.Semaphore(threads_num)
|
||||
# threads_num = config.get_setting("tmdb_threads", default=20)
|
||||
# semaforo = threading.Semaphore(threads_num)
|
||||
lock = threading.Lock()
|
||||
r_list = list()
|
||||
i = 0
|
||||
l_hilo = list()
|
||||
|
||||
def sub_thread(_item, _i, _seekTmdb):
|
||||
semaforo.acquire()
|
||||
# semaforo.acquire()
|
||||
ret = set_infoLabels_item(_item, _seekTmdb, idioma_busqueda, lock)
|
||||
# logger.debug(str(ret) + "item: " + _item.tostring())
|
||||
semaforo.release()
|
||||
# semaforo.release()
|
||||
r_list.append((_i, _item, ret))
|
||||
|
||||
for item in item_list:
|
||||
@@ -560,16 +565,14 @@ def select_group(groups, item):
|
||||
selected = -1
|
||||
url = 'https://api.themoviedb.org/3/tv/{}?api_key=a1ab8b8669da03637a4b98fa39c39228&language={}'.format(item.infoLabels['tmdb_id'], def_lang)
|
||||
res = requests.get(url).json()
|
||||
selections = ['[B]Original[/B] Seasons: {} Episodes: {}'.format(res.get('number_of_seasons',0), res.get('number_of_episodes',0))]
|
||||
selections = [['Original',res.get('number_of_seasons',0), res.get('number_of_episodes',0), '', item.thumbnail]]
|
||||
ids = ['original']
|
||||
for group in groups:
|
||||
name = '[B]{}[/B] Seasons: {} Episodes: {}'.format(group.get('name',''), group.get('group_count',0), group.get('episode_count',0))
|
||||
description = group.get('description','')
|
||||
if description:
|
||||
name = '{}\n{}'.format(name, description)
|
||||
# name = '{} Seasons: {} Episodes: {}'.format(group.get('name',''), group.get('group_count',0), group.get('episode_count',0))
|
||||
# description = group.get('description','')
|
||||
ID = group.get('id','')
|
||||
if ID:
|
||||
selections.append(name)
|
||||
selections.append([group.get('name',''), group.get('group_count',0), group.get('episode_count',0), group.get('description',''), item.thumbnail])
|
||||
ids.append(ID)
|
||||
if selections and ids:
|
||||
selected = platformtools.dialog_select_group(config.get_localized_string(70831), selections)
|
||||
@@ -1454,7 +1457,14 @@ class Tmdb(object):
|
||||
def get_list_episodes(self):
|
||||
url = 'https://api.themoviedb.org/3/tv/{id}?api_key=a1ab8b8669da03637a4b98fa39c39228&language={lang}'.format(id=self.busqueda_id, lang=self.busqueda_idioma)
|
||||
results = requests.get(url).json().get('seasons', [])
|
||||
return results if 'Error' not in results else []
|
||||
seasons = []
|
||||
if results and 'Error' not in results:
|
||||
for season in results:
|
||||
url = 'https://api.themoviedb.org/3/tv/{id}/season/{season}?api_key=a1ab8b8669da03637a4b98fa39c39228&language={lang}'.format(id=self.busqueda_id, season=season['season_number'], lang=self.busqueda_idioma)
|
||||
try: start_from = requests.get(url).json()['episodes'][0]['episode_number']
|
||||
except: start_from = 1
|
||||
seasons.append({'season_number':season['season_number'], 'episode_count':season['episode_count'], 'start_from':start_from})
|
||||
return seasons
|
||||
|
||||
def get_videos(self):
|
||||
"""
|
||||
@@ -1617,7 +1627,7 @@ class Tmdb(object):
|
||||
elif k == 'credits_cast' or k == 'temporada_cast' or k == 'episodio_guest_stars':
|
||||
dic_aux = dict((name, character) for (name, character) in l_castandrole)
|
||||
l_castandrole.extend([(p['name'], p.get('character', '') or p.get('character_name', '')) \
|
||||
for p in v if p['name'] not in list(dic_aux.keys())])
|
||||
for p in v if 'name' in p and p['name'] not in list(dic_aux.keys())])
|
||||
|
||||
elif k == 'videos':
|
||||
if not isinstance(v, list):
|
||||
|
||||
@@ -33,7 +33,7 @@ def country(config, common_words):
|
||||
return CountryFinder(allowed_countries, common_words).find(string)
|
||||
|
||||
rebulk.functional(find_countries,
|
||||
# Prefer language and any other property over country if not US or GB.
|
||||
# Prefer language and any other property over country if not US or GB.
|
||||
conflict_solver=lambda match, other: match
|
||||
if other.name != 'language' or match.value not in (babelfish.Country('US'),
|
||||
babelfish.Country('GB'))
|
||||
|
||||
@@ -1370,13 +1370,13 @@
|
||||
video_codec: Xvid
|
||||
release_group: Etc-Group
|
||||
type: movie
|
||||
# Fallback to movie type because we can't tell it's a series ...
|
||||
# Fallback to movie type because we can't tell it's a series ...
|
||||
|
||||
? Show.Name.Part.1.and.Part.2.Blah-Group
|
||||
: part: [1, 2]
|
||||
title: Show Name
|
||||
type: movie
|
||||
# Fallback to movie type because we can't tell it's a series ...
|
||||
# Fallback to movie type because we can't tell it's a series ...
|
||||
|
||||
? Show Name - 01 - Ep Name
|
||||
: episode: 1
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
episode:
|
||||
- 1
|
||||
- 7
|
||||
episode_title: FooBar-Group # Make sure it doesn't conflict with uuid
|
||||
episode_title: FooBar-Group # Make sure it doesn't conflict with uuid
|
||||
season: 1
|
||||
title: Test
|
||||
type: episode
|
||||
|
||||
@@ -191,20 +191,20 @@ class autorenumber():
|
||||
seasons =[]
|
||||
groupedSeasons = tmdb.get_group(self.group.replace('\n','').split('/')[-1])
|
||||
for groupedSeason in groupedSeasons:
|
||||
seasons.append({'season_number':groupedSeason['order'], 'episode_count':len(groupedSeason['episodes'])})
|
||||
seasons.append({'season_number':groupedSeason['order'], 'episode_count':len(groupedSeason['episodes']), 'start_from':groupedSeason['episodes'][0]['episode_number']})
|
||||
else:
|
||||
seasons = tmdb.Tmdb(id_Tmdb=self.id).get_list_episodes()
|
||||
|
||||
count = 0
|
||||
|
||||
for season in seasons:
|
||||
s = season['season_number']
|
||||
c = season['episode_count']
|
||||
fe = season['start_from']
|
||||
self.seasonsdict[str(s)] = c
|
||||
if s > 0:
|
||||
for e in range(1, c + 1):
|
||||
count += 1
|
||||
self.epdict[count] = '{}x{:02d}'.format(s,e)
|
||||
self.epdict[count] = '{}x{:02d}'.format(s, e + fe - 1)
|
||||
|
||||
if self.item.renumber or self.manual:
|
||||
self.item.renumber = False
|
||||
|
||||
@@ -170,13 +170,13 @@ def enable_disable_autorun(is_enabled):
|
||||
set_setting('autostart', True)
|
||||
return True
|
||||
|
||||
|
||||
def get_all_settings_addon():
|
||||
# Read the settings.xml file and return a dictionary with {id: value}
|
||||
from core import scrapertools
|
||||
|
||||
infile = open(os.path.join(get_data_path(), "settings.xml"), "r")
|
||||
data = infile.read()
|
||||
infile.close()
|
||||
with open(os.path.join(get_data_path(), "settings.xml"), "rb") as infile:
|
||||
data = infile.read().decode('utf-8')
|
||||
|
||||
ret = {}
|
||||
matches = scrapertools.find_multiple_matches(data, '<setting id=\"([^\"]+)\"[^>]*>([^<]*)</setting>')
|
||||
|
||||
@@ -134,8 +134,8 @@ def run(item=None):
|
||||
|
||||
elif item.action == "script":
|
||||
from core import tmdb
|
||||
if tmdb.drop_bd():
|
||||
platformtools.dialog_notification(config.get_localized_string(20000), config.get_localized_string(60011), time=2000, sound=False)
|
||||
tmdb.clean_cache()
|
||||
platformtools.dialog_notification(config.get_localized_string(20000), config.get_localized_string(60011), time=2000, sound=False)
|
||||
elif item.action == "itemInfo":
|
||||
platformtools.dialog_textviewer('Item info', item.parent)
|
||||
elif item.action == "open_browser":
|
||||
@@ -332,7 +332,7 @@ def run(item=None):
|
||||
platformtools.dialog_ok(config.get_localized_string(60087) % Channel, config.get_localized_string(60014))
|
||||
else:
|
||||
if platformtools.dialog_yesno(config.get_localized_string(60038), config.get_localized_string(60015)):
|
||||
run(Item(channel="setting", action="report_menu"))
|
||||
platformtools.itemlist_update(Item(channel="setting", action="report_menu"), True)
|
||||
finally:
|
||||
# db need to be closed when not used, it will cause freezes
|
||||
from core import db
|
||||
|
||||
@@ -239,9 +239,14 @@ def dialog_select_group(heading, _list, preselect=0):
|
||||
def onInit(self):
|
||||
self.getControl(1).setText(self.heading)
|
||||
itemlist = []
|
||||
for n, text in enumerate(self.list):
|
||||
for n, it in enumerate(self.list):
|
||||
logger.debug(it)
|
||||
item = xbmcgui.ListItem(str(n))
|
||||
item.setProperty('title', text)
|
||||
item.setProperty('title', it[0])
|
||||
item.setProperty('seasons', str(it[1]))
|
||||
item.setProperty('episodes', str(it[2]))
|
||||
item.setProperty('description', '\n' + it[3])
|
||||
item.setProperty('thumb', it[4])
|
||||
itemlist.append(item)
|
||||
|
||||
self.getControl(2).addItems(itemlist)
|
||||
@@ -441,13 +446,13 @@ def set_view_mode(item, parent_item):
|
||||
def set_infolabels(listitem, item, player=False):
|
||||
"""
|
||||
Method to pass the information to the listitem (see tmdb.set_InfoLabels())
|
||||
item.infoLabels is a dictionary with the key / value pairs described in:
|
||||
http://mirrors.xbmc.org/docs/python-docs/14.x-helix/xbmcgui.html#ListItem-setInfo
|
||||
https://kodi.wiki/view/InfoLabels
|
||||
@param listitem: xbmcgui.ListItem object
|
||||
@type listitem: xbmcgui.ListItem
|
||||
@param item: Item object that represents a movie, series or chapter
|
||||
@type item: item
|
||||
item.infoLabels is a dictionary with the key / value pairs described in:
|
||||
http://mirrors.xbmc.org/docs/python-docs/14.x-helix/xbmcgui.html#ListItem-setInfo
|
||||
https://kodi.wiki/view/InfoLabels
|
||||
@param listitem: xbmcgui.ListItem object
|
||||
@type listitem: xbmcgui.ListItem
|
||||
@param item: Item object that represents a movie, series or chapter
|
||||
@type item: item
|
||||
"""
|
||||
|
||||
infoLabels_dict = {'aired': 'aired', 'album': 'album', 'artist': 'artist', 'cast': 'cast',
|
||||
@@ -482,31 +487,30 @@ def set_infolabels(listitem, item, player=False):
|
||||
def set_context_commands(item, item_url, parent_item, **kwargs):
|
||||
"""
|
||||
Function to generate context menus.
|
||||
1. Based on the data in item.context
|
||||
a. Old method item.context type str separating options by "|" (example: item.context = "1 | 2 | 3")
|
||||
(only predefined)
|
||||
b. List method: item.context is a list with the different menu options:
|
||||
- Predefined: A predefined option will be loaded with a name.
|
||||
item.context = ["1", "2", "3"]
|
||||
1. Based on the data in item.context
|
||||
a. Old method item.context type str separating options by "|" (example: item.context = "1 | 2 | 3")
|
||||
(only predefined)
|
||||
b. List method: item.context is a list with the different menu options:
|
||||
- Predefined: A predefined option will be loaded with a name.
|
||||
item.context = ["1", "2", "3"]
|
||||
|
||||
- dict (): The current item will be loaded modifying the fields included in the dict () in case of
|
||||
modify the channel and action fields these will be saved in from_channel and from_action.
|
||||
item.context = [{"title": "Name of the menu", "action": "action of the menu", "channel": "menu channel"}, {...}]
|
||||
- dict (): The current item will be loaded modifying the fields included in the dict () in case of
|
||||
modify the channel and action fields these will be saved in from_channel and from_action.
|
||||
item.context = [{"title": "Name of the menu", "action": "action of the menu", "channel": "menu channel"}, {...}]
|
||||
|
||||
2. Adding options according to criteria
|
||||
Options can be added to the context menu to items that meet certain conditions.
|
||||
2. Adding options according to criteria
|
||||
Options can be added to the context menu to items that meet certain conditions.
|
||||
|
||||
3. Adding options to all items
|
||||
Options can be added to the context menu for all items
|
||||
|
||||
3. Adding options to all items
|
||||
Options can be added to the context menu for all items
|
||||
4. You can disable the context menu options by adding a command 'no_context' to the item.context.
|
||||
The options that Kodi, the skin or another added add to the contextual menu cannot be disabled.
|
||||
|
||||
4. You can disable the context menu options by adding a command 'no_context' to the item.context.
|
||||
The options that Kodi, the skin or another added add to the contextual menu cannot be disabled.
|
||||
|
||||
@param item: element that contains the contextual menus
|
||||
@type item: item
|
||||
@param parent_item:
|
||||
@type parent_item: item
|
||||
@param item: element that contains the contextual menus
|
||||
@type item: item
|
||||
@param parent_item:
|
||||
@type parent_item: item
|
||||
"""
|
||||
context_commands = []
|
||||
# num_version_xbmc = config.get_platform(True)['num_version']
|
||||
@@ -821,9 +825,9 @@ def show_channel_settings(**kwargs):
|
||||
def show_video_info(*args, **kwargs):
|
||||
"""
|
||||
It shows a window with the info of the video.
|
||||
The parameters passed to it can be seen in the method that is called
|
||||
The parameters passed to it can be seen in the method that is called
|
||||
|
||||
@return: returns the window with the elements
|
||||
@return: returns the window with the elements
|
||||
@rtype: InfoWindow
|
||||
"""
|
||||
|
||||
@@ -1420,6 +1424,7 @@ def get_played_time(item):
|
||||
logger.debug()
|
||||
from core import db
|
||||
|
||||
played_time = 0
|
||||
if not item.infoLabels:
|
||||
return 0
|
||||
ID = item.infoLabels.get('tmdb_id', '')
|
||||
@@ -1430,13 +1435,18 @@ def get_played_time(item):
|
||||
E = item.infoLabels.get('episode')
|
||||
result = None
|
||||
|
||||
if item.contentType == 'movie':
|
||||
result = db['viewed'].get(ID)
|
||||
elif S and E:
|
||||
result = db['viewed'].get(ID, {}).get(str(S)+'x'+str(E))
|
||||
try:
|
||||
if item.contentType == 'movie':
|
||||
result = db['viewed'].get(ID)
|
||||
elif S and E:
|
||||
result = db['viewed'].get(ID, {}).get(str(S)+'x'+str(E))
|
||||
|
||||
if not result: played_time = 0
|
||||
else: played_time = result
|
||||
if result:
|
||||
played_time = result
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
del db['viewed'][ID]
|
||||
|
||||
return played_time
|
||||
|
||||
@@ -1456,13 +1466,17 @@ def set_played_time(item):
|
||||
S = item.infoLabels.get('season', 0)
|
||||
E = item.infoLabels.get('episode')
|
||||
|
||||
|
||||
if item.contentType == 'movie':
|
||||
db['viewed'][ID] = played_time
|
||||
elif E:
|
||||
newDict = db['viewed'].get(ID, {})
|
||||
newDict[str(S) + 'x' + str(E)] = played_time
|
||||
db['viewed'][ID] = newDict
|
||||
try:
|
||||
if item.contentType == 'movie':
|
||||
db['viewed'][ID] = played_time
|
||||
elif E:
|
||||
newDict = db['viewed'].get(ID, {})
|
||||
newDict[str(S) + 'x' + str(E)] = played_time
|
||||
db['viewed'][ID] = newDict
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
del db['viewed'][ID]
|
||||
|
||||
|
||||
def prevent_busy(item):
|
||||
|
||||
@@ -132,7 +132,7 @@ def SettingOnPosition(item):
|
||||
|
||||
|
||||
def select(item):
|
||||
from core.support import dbg;dbg()
|
||||
# from core.support import dbg;dbg()
|
||||
from platformcode import config, platformtools
|
||||
# item.id = setting ID
|
||||
# item.type = labels or values
|
||||
|
||||
@@ -104,7 +104,7 @@ def check(background=False):
|
||||
patch_url = commitJson['html_url'] + '.patch'
|
||||
logger.info('applicando ' + patch_url)
|
||||
from lib import patch
|
||||
patch.fromurl(patch_url).apply(root=addonDir)
|
||||
patchOk = patch.fromurl(patch_url).apply(root=addonDir)
|
||||
|
||||
for file in commitJson['files']:
|
||||
if file["filename"] == trackingFile: # il file di tracking non si modifica
|
||||
@@ -115,7 +115,7 @@ def check(background=False):
|
||||
poFilesChanged = True
|
||||
if 'service.py' in file["filename"]:
|
||||
serviceChanged = True
|
||||
if (file['status'] == 'modified' and 'patch' not in file) or file['status'] == 'added':
|
||||
if (file['status'] == 'modified' and 'patch' not in file) or file['status'] == 'added' or (file['status'] == 'modified' and not patchOk):
|
||||
# è un file NON testuale che è stato modificato, oppure è un file nuovo (la libreria non supporta la creazione di un nuovo file)
|
||||
# lo devo scaricare
|
||||
filename = os.path.join(addonDir, file['filename'])
|
||||
|
||||
@@ -2017,7 +2017,7 @@ msgid "Yes, the option to display merged or split results by channels can be fou
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#60467"
|
||||
msgid "To report a problem on'https://t.me/kodiondemand' you need to:|the version you're using of Alpha.|The version you're using of Kodi, mediaserver, etc.|the version and name of the operating system you're using.|The name of the skin (in case you're using Kodi) and whether using the default skin has solved the problem.|Description of the problem and any test cases.To activate the log in detailed mode, go to:|Configuration.|Preferences.|In the General tab - Check the option: Generate detailed log. The detailed log file can be found in the following path: \n\n%s"
|
||||
msgid "To report a problem on'https://t.me/kodiondemand' you need to:|the version you're using of Alpha.|The version you're using of Kodi, mediaserver, etc.|the version and name of the operating system you're using.|The name of the skin (in case you're using Kodi) and whether using the default skin has solved the problem.|Description of the problem and any test cases.To activate the log in detailed mode, go to:|Configuration.|Preferences.|In the General tab - Check the option: Generate detailed log. The detailed log file can be found in the following path: \n\n%s"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#60468"
|
||||
|
||||
@@ -2016,7 +2016,7 @@ msgid "Yes, the option to display merged or split results by channels can be fou
|
||||
msgstr "Sì, l'opzione per mostrare i risultati uniti o divisi per canali si trova in 'Impostazioni>Impostazioni ricerca globale>Altre impostazioni'. Vuoi accedere a queste impostazioni?"
|
||||
|
||||
msgctxt "#60467"
|
||||
msgid "To report a problem on'https://t.me/kodiondemand' you need to:|the version you're using of Alpha.|The version you're using of Kodi, mediaserver, etc.|the version and name of the operating system you're using.|The name of the skin (in case you're using Kodi) and whether using the default skin has solved the problem.|Description of the problem and any test cases.To activate the log in detailed mode, go to:|Configuration.|Preferences.|In the General tab - Check the option: Generate detailed log. The detailed log file can be found in the following path: \n\n%s"
|
||||
msgid "To report a problem on'https://t.me/kodiondemand' you need to:|the version you're using of Alpha.|The version you're using of Kodi, mediaserver, etc.|the version and name of the operating system you're using.|The name of the skin (in case you're using Kodi) and whether using the default skin has solved the problem.|Description of the problem and any test cases.To activate the log in detailed mode, go to:|Configuration.|Preferences.|In the General tab - Check the option: Generate detailed log. The detailed log file can be found in the following path: \n\n%s"
|
||||
msgstr "Per segnalare un problema su 'https://t.me/kodiondemand' è necessario:|la versione che si sta usando di Kodi on Demand.|La versione che si sta usando di Kodi, mediaserver, ecc.|la versione e il nome del sistema operativo che si sta usando.|Il nome della skin (nel caso in cui si stia usando Kodi) e se l'utilizzo della skin predefinita ha risolto il problema.|La descrizione del problema e tutti i casi di test.Per attivare il log in modalità dettagliata, andare su:|Configurazione.|Preferenze.|Nella scheda Generale - Selezionare l'opzione: Genera log dettagliato Il file di log dettagliato si trova nel seguente percorso: \n\n%s"
|
||||
|
||||
msgctxt "#60468"
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<setting id="search_channels" type="action" label="59994" action="RunPlugin(plugin://plugin.video.kod/?ew0KICAgICJhY3Rpb24iOiJzZXR0aW5nX2NoYW5uZWxfbmV3IiwNCiAgICAiY2hhbm5lbCI6InNlYXJjaCINCn0=)"/>
|
||||
<setting label="70154" type="lsep"/>
|
||||
<setting id="tmdb_active" default="true" visible="false"/>
|
||||
<setting id="tmdb_threads" type="slider" option="int" range="5,5,30" label="70155" default="20"/>
|
||||
<!-- <setting id="tmdb_threads" type="slider" option="int" range="5,5,30" label="70155" default="20"/>-->
|
||||
<setting id="tmdb_plus_info" type="bool" label="70156" default="false"/>
|
||||
<setting id="tmdb_cache" type="bool" label="70157" default="true"/>
|
||||
<setting id="tmdb_cache_expire" type="select" lvalues="70158|70159|70160|70161|70170" label="70162" enable="eq(-1,true)" default="4"/>
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<controls>
|
||||
<control type="group">
|
||||
<description>Container</description>
|
||||
<left>200</left>
|
||||
<left>40</left>
|
||||
<top>60</top>
|
||||
<width>860</width>
|
||||
<width>1200</width>
|
||||
<height>600</height>
|
||||
<control type="image">
|
||||
<description>Background</description>
|
||||
@@ -22,49 +22,56 @@
|
||||
<height>100%</height>
|
||||
<texture colordiffuse="FF232323">white.png</texture>
|
||||
</control>
|
||||
<control type="image">
|
||||
<description>Poster</description>
|
||||
<top>0</top>
|
||||
<left>0</left>
|
||||
<height>600</height>
|
||||
<width>400</width>
|
||||
<texture>$INFO[Container(2).ListItem.Property(thumb)]</texture>
|
||||
</control>
|
||||
<control type="textbox" id="1">
|
||||
<description>Heading</description>
|
||||
<top>0</top>
|
||||
<left>0</left>
|
||||
<height>60</height>
|
||||
<width>100%</width>
|
||||
<left>460</left>
|
||||
<height>80</height>
|
||||
<width>680</width>
|
||||
<font>font13</font>
|
||||
<textcolor>FFFFFFFF</textcolor>
|
||||
<align>center</align>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<label></label>
|
||||
</control>
|
||||
<control type="group">
|
||||
<left>30</left>
|
||||
<top>70</top>
|
||||
<width>795</width>
|
||||
<height>530</height>
|
||||
<left>440</left>
|
||||
<top>80</top>
|
||||
<width>720</width>
|
||||
<height>480</height>
|
||||
<control type="list" id="2">
|
||||
<description>List</description>
|
||||
<left>0</left>
|
||||
<top>0</top>
|
||||
<width>795</width>
|
||||
<width>720</width>
|
||||
<height>530</height>
|
||||
<onup>100</onup>
|
||||
<onright>4</onright>
|
||||
<orientation>vertical</orientation>
|
||||
<scrolltime>200</scrolltime>
|
||||
<pagecontrol>4</pagecontrol>
|
||||
<itemlayout height="150" width="795">
|
||||
<itemlayout height="60" width="720">
|
||||
<control type="textbox">
|
||||
<description>Selected Item</description>
|
||||
<left>20</left>
|
||||
<top>20</top>
|
||||
<width>765</width>
|
||||
<height>110</height>
|
||||
<top>0</top>
|
||||
<width>680</width>
|
||||
<height>60</height>
|
||||
<font>font13</font>
|
||||
<textcolor>FFFFFFFF</textcolor>
|
||||
<label>$INFO[ListItem.Property(title)]</label>
|
||||
<align>center</align>
|
||||
<label>[B]$INFO[ListItem.Property(title)][/B] $ADDON[plugin.video.kod 30140]: $INFO[ListItem.Property(seasons)] $ADDON[plugin.video.kod 70362]: $INFO[ListItem.Property(episodes)]</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
</control>
|
||||
</itemlayout>
|
||||
<focusedlayout height="150" width="795">
|
||||
<focusedlayout height="200" width="720">
|
||||
<control type="image">
|
||||
<top>1</top>
|
||||
<width>100%</width>
|
||||
@@ -75,39 +82,22 @@
|
||||
<description>Selected Item</description>
|
||||
<left>20</left>
|
||||
<top>20</top>
|
||||
<width>765</width>
|
||||
<height>110</height>
|
||||
<width>680</width>
|
||||
<height>160</height>
|
||||
<font>font13</font>
|
||||
<textcolor>FFFFFFFF</textcolor>
|
||||
<label>$INFO[ListItem.Property(title)]</label>
|
||||
<label>[B]$INFO[ListItem.Property(title)][/B] $ADDON[plugin.video.kod 30140]: $INFO[ListItem.Property(seasons)] $ADDON[plugin.video.kod 70362]: $INFO[ListItem.Property(episodes)]$INFO[ListItem.Property(description)]</label>
|
||||
<autoscroll time="3000" delay="3000" repeat="3000">True</autoscroll>
|
||||
<align>center</align>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
</control>
|
||||
</focusedlayout>
|
||||
</control>
|
||||
<control type="scrollbar" id="4">
|
||||
<description>Scrollbar</description>
|
||||
<left>800</left>
|
||||
<top>0</top>
|
||||
<width>5</width>
|
||||
<height>470</height>
|
||||
<visible>true</visible>
|
||||
<texturesliderbackground colordiffuse="FF232323">white.png</texturesliderbackground>
|
||||
<texturesliderbar colordiffuse="FFFFFFFF">white.png</texturesliderbar>
|
||||
<texturesliderbarfocus colordiffuse="FF0081C2">white.png</texturesliderbarfocus>
|
||||
<textureslidernib colordiffuse="FFFFFFFF">white.png</textureslidernib>
|
||||
<textureslidernibfocus colordiffuse="FFFFFFFF">white.png</textureslidernibfocus>
|
||||
<orientation>vertical</orientation>
|
||||
<showonepage>false</showonepage>
|
||||
<onleft>2</onleft>
|
||||
<onright>3</onright>
|
||||
</control>
|
||||
</control>
|
||||
<control type="button" id="100">
|
||||
<description>Close Button</description>
|
||||
<right>20</right>
|
||||
<top>10</top>
|
||||
<top>20</top>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
<texturefocus colordiffuse="FFFFFFFF">close.png</texturefocus>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"active": true,
|
||||
"find_videos": {
|
||||
"ignore_urls": [],
|
||||
"patterns": [
|
||||
{
|
||||
"pattern": "ninjastream.to/(?:watch/)?([0-9a-zA-Z]+)",
|
||||
"pattern": "ninjastream\\.to/(?:watch|download)/([0-9a-zA-Z]+)",
|
||||
"url": "https://ninjastream.to/watch/\\1"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -20,11 +20,17 @@ def get_video_url(page_url, premium=False, user="", password="", video_password=
|
||||
logger.debug("URL", page_url)
|
||||
video_urls = []
|
||||
|
||||
h = json.loads(support.match(data, patron='stream="([^"]+)"').match.replace('"','"').replace('\\',''))
|
||||
baseurl = h['host'] + h['hash']
|
||||
h = json.loads(support.match(data, patron='stream="([^"]+)"').match.replace('"','"'))
|
||||
baseurl = decode(h['host']) + h['hash']
|
||||
matches = support.match(baseurl + '/index.m3u8', patron=r'RESOLUTION=\d+x(\d+)\s*([^\s]+)').matches
|
||||
|
||||
for quality, url in matches:
|
||||
video_urls.append(["{} {}p [NinjaStream]".format(url.split('.')[-1], quality), '{}/{}'.format(baseurl, url)])
|
||||
|
||||
return video_urls
|
||||
return video_urls
|
||||
|
||||
def decode(host):
|
||||
Host = ''
|
||||
for n in range(len(host)):
|
||||
Host += chr(ord(host[n]) ^ ord('2'))
|
||||
return Host
|
||||
@@ -12,10 +12,10 @@ def test_video_exists(page_url):
|
||||
logger.debug("(page_url='%s')" % page_url)
|
||||
global data
|
||||
data = httptools.downloadpage(page_url, cookies=False).data
|
||||
if 'Video embed restricted for this domain'in data:
|
||||
headers = {'Referer':''}
|
||||
if 'Video embed restricted for this domain' in data:
|
||||
headers = {'Referer': ''}
|
||||
data = httptools.downloadpage(page_url, headers=headers, cookies=False).data
|
||||
if 'File is no longer available as it expired or has been deleted' in data:
|
||||
if 'File is no longer available as it expired or has been deleted' in data or 'fake-' in data:
|
||||
return False, config.get_localized_string(70449) % "SuperVideo"
|
||||
|
||||
return True, ""
|
||||
|
||||
@@ -18,5 +18,8 @@ def get_video_url(page_url, premium=False, user="", password="", video_password=
|
||||
logger.debug("url=" + page_url)
|
||||
global data
|
||||
video_urls = support.get_jwplayer_mediaurl(data, 'Vidmoly')
|
||||
for url in video_urls:
|
||||
logger.debug(url)
|
||||
url[-1] = url[-1].replace(',','').replace('.urlset','').replace('/hls','')
|
||||
|
||||
return video_urls
|
||||
|
||||
@@ -477,15 +477,15 @@ def get_server_position(server):
|
||||
def get_match_list(data, match_list, order_list=None, only_ascii=False, ignorecase=False):
|
||||
"""
|
||||
Search for matches in a text string, with a dictionary of "ID" / "List of search strings":
|
||||
{"ID1": ["String 1", "String 2", "String 3"],
|
||||
"ID2": ["String 4", "String 5", "String 6"]
|
||||
}
|
||||
{"ID1": ["String 1", "String 2", "String 3"],
|
||||
"ID2": ["String 4", "String 5", "String 6"]
|
||||
}
|
||||
|
||||
The dictionary could not contain the same search string in several IDs.
|
||||
The search is performed in order of search string size (from longest to shortest) if a string matches,
|
||||
it is removed from the search string for the following, so that two categories are not detected if one string is part of another:
|
||||
for example: "Spanish Language" and "Spanish" if the first appears in the string "Pablo knows how to speak the Spanish Language"
|
||||
It will match "Spanish Language" but not "Spanish" since the longest match has priority.
|
||||
The dictionary could not contain the same search string in several IDs.
|
||||
The search is performed in order of search string size (from longest to shortest) if a string matches,
|
||||
it is removed from the search string for the following, so that two categories are not detected if one string is part of another:
|
||||
for example: "Spanish Language" and "Spanish" if the first appears in the string "Pablo knows how to speak the Spanish Language"
|
||||
It will match "Spanish Language" but not "Spanish" since the longest match has priority.
|
||||
|
||||
"""
|
||||
match_dict = dict()
|
||||
|
||||
@@ -235,23 +235,25 @@ def new_search(item):
|
||||
|
||||
def live(item):
|
||||
import sys
|
||||
import channelselector
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
from concurrent import futures
|
||||
else:
|
||||
from concurrent_py2 import futures
|
||||
itemlist = []
|
||||
channels_dict = {}
|
||||
channels = ['raiplay', 'mediasetplay', 'la7', 'paramount']
|
||||
channels = channelselector.filterchannels('live')
|
||||
|
||||
with futures.ThreadPoolExecutor() as executor:
|
||||
itlist = [executor.submit(load_live, channel) for channel in channels]
|
||||
itlist = [executor.submit(load_live, ch.channel) for ch in channels]
|
||||
for res in futures.as_completed(itlist):
|
||||
if res.result():
|
||||
channel_name, itlist = res.result()
|
||||
channels_dict[channel_name] = itlist
|
||||
|
||||
for channel in channels:
|
||||
itemlist += channels_dict[channel]
|
||||
for ch in channels:
|
||||
itemlist += channels_dict[ch.channel]
|
||||
return itemlist
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import xbmc, xbmcgui, sys, channelselector, time, os
|
||||
from core import support
|
||||
from core.support import dbg, tmdb
|
||||
from core.item import Item
|
||||
from core import channeltools, servertools, scrapertools
|
||||
@@ -8,8 +9,12 @@ 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
|
||||
if sys.version_info[0] >= 3:
|
||||
PY3 = True
|
||||
from concurrent import futures
|
||||
else:
|
||||
PY3 = False
|
||||
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")]
|
||||
@@ -75,17 +80,14 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
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.pos = 0
|
||||
selfeppos = 0
|
||||
self.items = []
|
||||
|
||||
if not searchActions:
|
||||
@@ -116,6 +118,7 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
from specials.search import save_search
|
||||
save_search(self.item.text)
|
||||
|
||||
|
||||
def getActions(self):
|
||||
logger.debug()
|
||||
count = 0
|
||||
@@ -144,9 +147,12 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
if self.item.mode == 'movie':
|
||||
title = result['title']
|
||||
result['mode'] = 'movie'
|
||||
else:
|
||||
elif self.item.mode == 'tvshow':
|
||||
title = result['name']
|
||||
result['mode'] = 'tvshow'
|
||||
else:
|
||||
title = result.get('title', '')
|
||||
result['mode'] = result['media_type'].replace('tv', 'tvshow')
|
||||
|
||||
thumbnail = result.get('thumbnail', '')
|
||||
noThumb = 'Infoplus/' + result['mode'].replace('show','') + '.png'
|
||||
@@ -304,16 +310,12 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
logger.debug('end search for:', searchAction.channel)
|
||||
|
||||
def get_channel_results(self, searchAction):
|
||||
logger.debug()
|
||||
channel = searchAction.channel
|
||||
results = []
|
||||
valid = []
|
||||
other = []
|
||||
|
||||
try:
|
||||
results = self.moduleDict[channel].search(searchAction, self.item.text)
|
||||
def search(text):
|
||||
valid = []
|
||||
other = []
|
||||
results = self.moduleDict[channel].search(searchAction, text)
|
||||
if len(results) == 1:
|
||||
if not results[0].action or config.get_localized_string(70006).lower() in results[0].title.lower():
|
||||
if not results[0].action or results[0].nextPage:
|
||||
results = []
|
||||
|
||||
if self.item.mode != 'all':
|
||||
@@ -324,6 +326,23 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
valid.append(elem)
|
||||
else:
|
||||
other.append(elem)
|
||||
return results, valid, other
|
||||
|
||||
logger.debug()
|
||||
channel = searchAction.channel
|
||||
results = []
|
||||
valid = []
|
||||
other = []
|
||||
|
||||
try:
|
||||
results, valid, other = search(self.item.text)
|
||||
|
||||
# if we are on movie search but no valid results is found, and there's a lot of results (more pages), try
|
||||
# to add year to search text for better filtering
|
||||
if self.item.contentType == 'movie' and not valid and other and other[-1].nextPage \
|
||||
and self.item.infoLabels['year']:
|
||||
logger.debug('retring adding year on channel ' + channel)
|
||||
dummy, valid, dummy = search(self.item.text + " " + str(self.item.infoLabels['year']))
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -424,9 +443,15 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
self.channels = []
|
||||
self.moduleDict = {}
|
||||
self.searchActions = []
|
||||
if percent == 100 and not self.results:
|
||||
self.PROGRESS.setVisible(False)
|
||||
self.NORESULTS.setVisible(True)
|
||||
|
||||
# if no results
|
||||
total = 0
|
||||
for num in self.results.values():
|
||||
total += num
|
||||
if not total:
|
||||
self.PROGRESS.setVisible(False)
|
||||
self.NORESULTS.setVisible(True)
|
||||
self.setFocusId(CLOSE)
|
||||
|
||||
def onInit(self):
|
||||
self.time = time.time()
|
||||
@@ -453,7 +478,9 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
if self.type:
|
||||
self.type = None
|
||||
if self.item.mode in ['all', 'search']:
|
||||
if self.item.type: self.item.mode = self.item.type
|
||||
if self.item.type:
|
||||
self.item.mode = self.item.type
|
||||
self.item.text = title_unify(self.item.text)
|
||||
self.thread = Thread(target=self.search)
|
||||
self.thread.start()
|
||||
elif self.item.mode in ['movie', 'tvshow', 'person_']:
|
||||
@@ -507,9 +534,12 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
elif self.EPISODES.isVisible(): self.setFocusId(EPISODESLIST)
|
||||
elif self.RESULTS.isVisible(): self.setFocusId(RESULTS)
|
||||
|
||||
elif focus in [RESULTS] and self.item.mode == 'all':
|
||||
elif focus in [RESULTS]:
|
||||
pos = self.RESULTS.getSelectedPosition()
|
||||
self.CHANNELS.getSelectedItem().setProperty('position', str(pos))
|
||||
try:
|
||||
self.CHANNELS.getSelectedItem().setProperty('position', str(pos))
|
||||
except:
|
||||
pass
|
||||
|
||||
elif action == ENTER and focus in [CHANNELS]:
|
||||
self.setFocusId(RESULTS)
|
||||
@@ -559,25 +589,14 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
self.actors()
|
||||
elif search == 'persons':
|
||||
item = self.item.clone(mode='person_', discovery=self.persons[pos])
|
||||
# self.close()
|
||||
Search(item, self.moduleDict, self.searchActions)
|
||||
if close_action:
|
||||
self.close
|
||||
self.close()
|
||||
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)
|
||||
|
||||
# self.close()
|
||||
Search(item, self.moduleDict, self.searchActions)
|
||||
if close_action:
|
||||
self.close()
|
||||
@@ -589,7 +608,6 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
self.pos = self.RESULTS.getSelectedPosition()
|
||||
item = Item().fromurl(self.RESULTS.getSelectedItem().getProperty('item'))
|
||||
else:
|
||||
self.eppos = self.EPISODESLIST.getSelectedPosition()
|
||||
item_url = self.EPISODESLIST.getSelectedItem().getProperty('item')
|
||||
if item_url:
|
||||
item = Item().fromurl(item_url)
|
||||
@@ -706,8 +724,6 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
self.Focus(SEARCH)
|
||||
self.setFocusId(RESULTS)
|
||||
self.RESULTS.selectItem(self.pos)
|
||||
elif self.item.mode in ['person']:
|
||||
self.actors()
|
||||
else:
|
||||
self.Close()
|
||||
|
||||
@@ -746,3 +762,26 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
server.globalsearch = True
|
||||
return run(server)
|
||||
|
||||
|
||||
def title_unify(title):
|
||||
import unicodedata
|
||||
|
||||
u_title = ''
|
||||
if type(title) == str: title = u'' + title
|
||||
for c in unicodedata.normalize('NFD', title):
|
||||
cat = unicodedata.category(c)
|
||||
if cat != 'Mn':
|
||||
if cat == 'Pd':
|
||||
c_new = '-'
|
||||
elif cat in ['Ll', 'Lu'] or c == ':':
|
||||
c_new = c
|
||||
else:
|
||||
c_new = ' '
|
||||
u_title += c_new
|
||||
|
||||
if (u_title.count(':') + u_title.count('-')) == 1:
|
||||
# subtitle, split but only if there's one, it might be part of title
|
||||
spl = u_title.replace(':', '-').split('-')
|
||||
u_title = spl[0] if len(spl[0]) > 5 else spl[1]
|
||||
|
||||
return u_title.strip()
|
||||
|
||||
@@ -13,7 +13,6 @@ from core import filetools, scrapertools, videolibrarytools
|
||||
from core.support import typo, thumb
|
||||
from core.item import Item
|
||||
from platformcode import config, logger, platformtools
|
||||
from distutils import dir_util
|
||||
if PY3:
|
||||
from concurrent import futures
|
||||
else:
|
||||
@@ -202,14 +201,14 @@ def configure_update_videolibrary(item):
|
||||
|
||||
# Select Dialog
|
||||
ret = platformtools.dialog_multiselect(config.get_localized_string(60601), lista, preselect=preselect, useDetails=True)
|
||||
if ret < 0:
|
||||
if ret is None:
|
||||
return False # order cancel
|
||||
seleccionados = [ids[i] for i in ret]
|
||||
selection = [ids[i] for i in ret]
|
||||
|
||||
for tvshow in ids:
|
||||
if tvshow not in seleccionados:
|
||||
if tvshow not in selection:
|
||||
tvshow.active = 0
|
||||
elif tvshow in seleccionados:
|
||||
elif tvshow in selection:
|
||||
tvshow.active = 1
|
||||
mark_tvshow_as_updatable(tvshow, silent=True)
|
||||
|
||||
@@ -416,124 +415,129 @@ def findvideos(item):
|
||||
itemlist = []
|
||||
|
||||
all_videolibrary = []
|
||||
for nom_canal, json_path in list(list_canales.items()):
|
||||
if filtro_canal and filtro_canal != nom_canal.capitalize():
|
||||
continue
|
||||
|
||||
item_canal = Item()
|
||||
|
||||
# We import the channel of the selected part
|
||||
try:
|
||||
if nom_canal == 'community':
|
||||
channel = __import__('specials.%s' % nom_canal, fromlist=["channels.%s" % nom_canal])
|
||||
else:
|
||||
channel = __import__('channels.%s' % nom_canal, fromlist=["channels.%s" % nom_canal])
|
||||
except ImportError:
|
||||
exec("import channels." + nom_canal + " as channel")
|
||||
except:
|
||||
dead_list = []
|
||||
zombie_list = []
|
||||
|
||||
if nom_canal not in dead_list and nom_canal not in zombie_list: confirm = platformtools.dialog_yesno(config.get_localized_string(30131), config.get_localized_string(30132) % nom_canal.upper() + '\n' + config.get_localized_string(30133))
|
||||
elif nom_canal in zombie_list: confirm = False
|
||||
else: confirm = True
|
||||
|
||||
if confirm:
|
||||
# delete the channel from all movie and tvshow
|
||||
from past.utils import old_div
|
||||
num_enlaces = 0
|
||||
dialog = platformtools.dialog_progress(config.get_localized_string(30131), config.get_localized_string(60005) % nom_canal)
|
||||
if not all_videolibrary:
|
||||
all_videolibrary = list_movies(Item()) + list_tvshows(Item())
|
||||
for n, it in enumerate(all_videolibrary):
|
||||
if nom_canal in it.library_urls:
|
||||
dead_item = Item(multichannel=len(it.library_urls) > 1,
|
||||
contentType=it.contentType,
|
||||
dead=nom_canal,
|
||||
path=filetools.split(it.nfo)[0],
|
||||
nfo=it.nfo,
|
||||
library_urls=it.library_urls,
|
||||
infoLabels={'title': it.contentTitle})
|
||||
num_enlaces += delete(dead_item)
|
||||
dialog.update(old_div(100*n, len(all_videolibrary)))
|
||||
|
||||
dialog.close()
|
||||
msg_txt = config.get_localized_string(70087) % (num_enlaces, nom_canal)
|
||||
logger.info(msg_txt)
|
||||
platformtools.dialog_notification(config.get_localized_string(30131), msg_txt)
|
||||
platformtools.itemlist_refresh()
|
||||
|
||||
if nom_canal not in dead_list:
|
||||
dead_list.append(nom_canal)
|
||||
ch_results = []
|
||||
with futures.ThreadPoolExecutor() as executor:
|
||||
for nom_canal, json_path in list(list_canales.items()):
|
||||
if filtro_canal and filtro_canal != nom_canal.capitalize():
|
||||
continue
|
||||
else:
|
||||
if nom_canal not in zombie_list:
|
||||
zombie_list.append(nom_canal)
|
||||
|
||||
if len(dead_list) > 0:
|
||||
for nom_canal in dead_list:
|
||||
if nom_canal in item.library_urls:
|
||||
del item.library_urls[nom_canal]
|
||||
# We import the channel of the selected part
|
||||
try:
|
||||
if nom_canal == 'community':
|
||||
channel = __import__('specials.%s' % nom_canal, fromlist=["channels.%s" % nom_canal])
|
||||
else:
|
||||
channel = __import__('channels.%s' % nom_canal, fromlist=["channels.%s" % nom_canal])
|
||||
except ImportError:
|
||||
exec("import channels." + nom_canal + " as channel")
|
||||
except:
|
||||
dead_list = []
|
||||
zombie_list = []
|
||||
|
||||
item_json = Item().fromjson(filetools.read(json_path))
|
||||
list_servers = []
|
||||
if nom_canal not in dead_list and nom_canal not in zombie_list: confirm = platformtools.dialog_yesno(config.get_localized_string(30131), config.get_localized_string(30132) % nom_canal.upper() + '\n' + config.get_localized_string(30133))
|
||||
elif nom_canal in zombie_list: confirm = False
|
||||
else: confirm = True
|
||||
|
||||
try:
|
||||
# FILTERTOOLS
|
||||
# if the channel has a filter, the name it has saved is passed to it so that it filters correctly.
|
||||
if "list_language" in item_json:
|
||||
# if it comes from the addon video library
|
||||
if "library_filter_show" in item:
|
||||
item_json.show = item.library_filter_show.get(nom_canal, "")
|
||||
if confirm:
|
||||
# delete the channel from all movie and tvshow
|
||||
from past.utils import old_div
|
||||
num_enlaces = 0
|
||||
dialog = platformtools.dialog_progress(config.get_localized_string(30131), config.get_localized_string(60005) % nom_canal)
|
||||
if not all_videolibrary:
|
||||
all_videolibrary = list_movies(Item()) + list_tvshows(Item())
|
||||
for n, it in enumerate(all_videolibrary):
|
||||
if nom_canal in it.library_urls:
|
||||
dead_item = Item(multichannel=len(it.library_urls) > 1,
|
||||
contentType=it.contentType,
|
||||
dead=nom_canal,
|
||||
path=filetools.split(it.nfo)[0],
|
||||
nfo=it.nfo,
|
||||
library_urls=it.library_urls,
|
||||
infoLabels={'title': it.contentTitle})
|
||||
num_enlaces += delete(dead_item)
|
||||
dialog.update(old_div(100*n, len(all_videolibrary)))
|
||||
|
||||
# We run find_videos, from the channel or common
|
||||
item_json.contentChannel = 'videolibrary'
|
||||
item_json.play_from = item.play_from
|
||||
item_json.nfo = item.nfo
|
||||
item_json.strm_path = item.strm_path
|
||||
if hasattr(channel, 'findvideos'):
|
||||
from core import servertools
|
||||
if item_json.videolibray_emergency_urls:
|
||||
del item_json.videolibray_emergency_urls
|
||||
list_servers = getattr(channel, 'findvideos')(item_json)
|
||||
elif item_json.action == 'play':
|
||||
from platformcode import platformtools
|
||||
# autoplay.set_status(True)
|
||||
item_json.contentChannel = item_json.channel
|
||||
item_json.channel = "videolibrary"
|
||||
platformtools.play_video(item_json)
|
||||
return ''
|
||||
else:
|
||||
from core import servertools
|
||||
list_servers = servertools.find_video_items(item_json)
|
||||
except Exception as ex:
|
||||
logger.error("The findvideos function for the channel %s failed" % nom_canal)
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex).__name__, ex.args)
|
||||
logger.error(message)
|
||||
logger.error(traceback.format_exc())
|
||||
dialog.close()
|
||||
msg_txt = config.get_localized_string(70087) % (num_enlaces, nom_canal)
|
||||
logger.info(msg_txt)
|
||||
platformtools.dialog_notification(config.get_localized_string(30131), msg_txt)
|
||||
platformtools.itemlist_refresh()
|
||||
|
||||
# Change the title to the servers adding the name of the channel in front and the infoLabels and the images of the item if the server does not have
|
||||
for server in list_servers:
|
||||
server.contentChannel = server.channel
|
||||
server.channel = "videolibrary"
|
||||
server.nfo = item.nfo
|
||||
server.strm_path = item.strm_path
|
||||
server.play_from = item.play_from
|
||||
if nom_canal not in dead_list:
|
||||
dead_list.append(nom_canal)
|
||||
continue
|
||||
else:
|
||||
if nom_canal not in zombie_list:
|
||||
zombie_list.append(nom_canal)
|
||||
|
||||
# Kodi 18 Compatibility - Prevents wheel from spinning around in Direct Links
|
||||
if server.action == 'play':
|
||||
server.folder = False
|
||||
if len(dead_list) > 0:
|
||||
for nom_canal in dead_list:
|
||||
if nom_canal in item.library_urls:
|
||||
del item.library_urls[nom_canal]
|
||||
|
||||
# Channel name is added if desired
|
||||
if config.get_setting("quit_channel_name", "videolibrary") == 0:
|
||||
server.title = "%s: %s" % (nom_canal.capitalize(), server.title)
|
||||
item_json = Item().fromjson(filetools.read(json_path))
|
||||
list_servers = []
|
||||
|
||||
if not server.thumbnail:
|
||||
server.thumbnail = item.thumbnail
|
||||
try:
|
||||
# FILTERTOOLS
|
||||
# if the channel has a filter, the name it has saved is passed to it so that it filters correctly.
|
||||
if "list_language" in item_json:
|
||||
# if it comes from the addon video library
|
||||
if "library_filter_show" in item:
|
||||
item_json.show = item.library_filter_show.get(nom_canal, "")
|
||||
|
||||
# logger.debug("server:\n%s" % server.tostring('\n'))
|
||||
itemlist.append(server)
|
||||
# We run find_videos, from the channel or common
|
||||
item_json.contentChannel = 'videolibrary'
|
||||
item_json.play_from = item.play_from
|
||||
item_json.nfo = item.nfo
|
||||
item_json.strm_path = item.strm_path
|
||||
if hasattr(channel, 'findvideos'):
|
||||
from core import servertools
|
||||
if item_json.videolibray_emergency_urls:
|
||||
del item_json.videolibray_emergency_urls
|
||||
ch_results.append(executor.submit(getattr(channel, 'findvideos'), item_json))
|
||||
elif item_json.action == 'play':
|
||||
from platformcode import platformtools
|
||||
# autoplay.set_status(True)
|
||||
item_json.contentChannel = item_json.channel
|
||||
item_json.channel = "videolibrary"
|
||||
platformtools.play_video(item_json)
|
||||
return ''
|
||||
else:
|
||||
from core import servertools
|
||||
ch_results.append(executor.submit(servertools.find_video_items, item_json))
|
||||
|
||||
except Exception as ex:
|
||||
logger.error("The findvideos function for the channel %s failed" % nom_canal)
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex).__name__, ex.args)
|
||||
logger.error(message)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
for ris in futures.as_completed(ch_results):
|
||||
list_servers.extend(ris.result())
|
||||
|
||||
|
||||
# Change the title to the servers adding the name of the channel in front and the infoLabels and the images of the item if the server does not have
|
||||
for server in list_servers:
|
||||
server.contentChannel = server.channel
|
||||
server.channel = "videolibrary"
|
||||
server.nfo = item.nfo
|
||||
server.strm_path = item.strm_path
|
||||
server.play_from = item.play_from
|
||||
|
||||
# Kodi 18 Compatibility - Prevents wheel from spinning around in Direct Links
|
||||
if server.action == 'play':
|
||||
server.folder = False
|
||||
|
||||
# Channel name is added if desired
|
||||
if config.get_setting("quit_channel_name", "videolibrary") == 0:
|
||||
server.title = "%s: %s" % (server.contentChannel.capitalize(), server.title)
|
||||
|
||||
if not server.thumbnail:
|
||||
server.thumbnail = item.thumbnail
|
||||
|
||||
# logger.debug("server:\n%s" % server.tostring('\n'))
|
||||
itemlist.append(server)
|
||||
|
||||
if autoplay.play_multi_channel(item, itemlist): # hideserver
|
||||
return []
|
||||
@@ -605,6 +609,8 @@ def update_videolibrary(item=''):
|
||||
|
||||
|
||||
def move_videolibrary(current_path, new_path, current_movies_folder, new_movies_folder, current_tvshows_folder, new_tvshows_folder):
|
||||
from distutils import dir_util
|
||||
|
||||
logger.debug()
|
||||
|
||||
backup_current_path = current_path
|
||||
|
||||
Reference in New Issue
Block a user