KoD 1.6
- 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)
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
name: Test Suite
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '00 15 * * *'
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
export KODI_INTERACTIVE=0
|
||||
./tests/run.sh
|
||||
|
||||
- name: Commit & Push changes
|
||||
uses: dmnemec/copy_file_to_another_repo_action@v1.0.4
|
||||
env:
|
||||
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
|
||||
with:
|
||||
source_file: 'reports'
|
||||
destination_repo: 'kodiondemand/kodiondemand.github.io'
|
||||
user_email: 'tests@kod.bot'
|
||||
user_name: 'bot'
|
||||
commit_message: 'Test suite'
|
||||
@@ -14,11 +14,13 @@ jobs:
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 2.7
|
||||
python-version: 3.7
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install requests
|
||||
|
||||
- name: Update domains
|
||||
run: |
|
||||
python tools/updateDomains.py
|
||||
run: python tools/updateDomains.py
|
||||
|
||||
- name: Commit & Push changes
|
||||
uses: actions-js/push@master
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<addon id="plugin.video.kod" name="Kodi on Demand" version="1.5.3" provider-name="KoD Team">
|
||||
<addon id="plugin.video.kod" name="Kodi on Demand" version="1.6" provider-name="KoD Team">
|
||||
<requires>
|
||||
<!-- <import addon="script.module.libtorrent" optional="true"/> -->
|
||||
<import addon="metadata.themoviedb.org"/>
|
||||
<import addon="metadata.tvdb.com"/>
|
||||
<import addon="metadata.tvshows.themoviedb.org"/>
|
||||
<!-- <import addon="metadata.tvdb.com"/> -->
|
||||
|
||||
</requires>
|
||||
<extension point="xbmc.python.pluginsource" library="default.py">
|
||||
@@ -26,9 +27,10 @@
|
||||
<screenshot>resources/media/themes/ss/2.png</screenshot>
|
||||
<screenshot>resources/media/themes/ss/3.png</screenshot>
|
||||
</assets>
|
||||
<news>- correzioni di alcuni bug (citiamo ad esempio il crash con il refresh rate e l'impossibilita di entrare nel menu server bloccati)
|
||||
- fix per cambio di struttura a qualche canale/server
|
||||
- migliorie interne</news>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
+13
-13
@@ -1,44 +1,44 @@
|
||||
{
|
||||
"direct": {
|
||||
"altadefinizione01_link": "https://altadefinizione01.fitness",
|
||||
"altadefinizione01_link": "https://altadefinizione01.market",
|
||||
"animealtadefinizione": "https://www.animealtadefinizione.it",
|
||||
"animeforce": "https://www.animeforce.it",
|
||||
"animeleggendari": "https://animeora.com",
|
||||
"animeleggendari": "https://www.animebig.xyz",
|
||||
"animesaturn": "https://www.animesaturn.it",
|
||||
"animestream": "https://www.animeworld.tv",
|
||||
"animesubita": "http://www.animesubita.org",
|
||||
"animetubeita": "http://www.animetubeita.com",
|
||||
"animeunity": "https://www.animeunity.it",
|
||||
"animeuniverse": "https://www.animeuniverse.it/",
|
||||
"animeuniverse": "https://www.animeuniverse.it",
|
||||
"animeworld": "https://www.animeworld.tv",
|
||||
"casacinema": "https://www.casacinema.page",
|
||||
"cb01anime": "https://www.cineblog01.red",
|
||||
"cinemalibero": "https://cinemalibero.store",
|
||||
"cinemalibero": "https://cinemalibero.win",
|
||||
"cinetecadibologna": "http://cinestore.cinetecadibologna.it",
|
||||
"discoveryplus": "https://www.discoveryplus.com",
|
||||
"dreamsub": "https://dreamsub.stream",
|
||||
"dsda": "https://www.dsda.press",
|
||||
"eurostreaming": "https://eurostreaming.chat",
|
||||
"fastsubita": "https://fastsubita.xyz",
|
||||
"eurostreaming": "https://eurostreaming.tube",
|
||||
"filmgratis": "https://www.filmaltadefinizione.me",
|
||||
"filmigratis": "https://filmigratis.org",
|
||||
"filmsenzalimiticc": "https://www.filmsenzalimiti01.online",
|
||||
"filmsenzalimiticc": "https://www.filmsenzalimiti01.work",
|
||||
"filmstreaming01": "https://filmstreaming01.com",
|
||||
"guardaserie_stream": "https://guardaserie.host",
|
||||
"guardaseriecam": "https://guardaserie.cam",
|
||||
"guardaserieclick": "https://www.guardaserie.directory",
|
||||
"guardaserieclick": "https://www.guardaserie.kim",
|
||||
"guardaserieicu": "https://guardaserie.shop",
|
||||
"hd4me": "https://hd4me.net",
|
||||
"ilcorsaronero": "https://ilcorsaronero.link",
|
||||
"ilgeniodellostreaming": "https://ilgeniodellostreaming.cat",
|
||||
"ilgeniodellostreaming": "https://ilgeniodellostreaming.pub",
|
||||
"ilgeniodellostreaming_cam": "https://ilgeniodellostreaming.gold",
|
||||
"italiaserie": "https://italiaserie.top",
|
||||
"italiaserie": "https://italiaserie.bid",
|
||||
"mediasetplay": "https://www.mediasetplay.mediaset.it",
|
||||
"mondoserietv": "https://mondoserietv.fun",
|
||||
"paramount": "https://www.paramountnetwork.it",
|
||||
"piratestreaming": "https://www.piratestreaming.camp",
|
||||
"piratestreaming": "https://www.piratestreaming.live",
|
||||
"polpotv": "https://roma.polpo.tv",
|
||||
"raiplay": "https://www.raiplay.it",
|
||||
"serietvonline": "https://serietvonline.cam",
|
||||
"serietvonline": "https://serietvonline.website",
|
||||
"serietvsubita": "http://serietvsubita.xyz",
|
||||
"serietvu": "https://www.serietvu.link",
|
||||
"streamingcommunity": "https://streamingcommunity.net",
|
||||
@@ -53,7 +53,7 @@
|
||||
"cineblog01": "https://cb01.uno",
|
||||
"film4k": "https://film4k-nuovo.link",
|
||||
"filmpertutti": "https://filmpertutti.nuovo.live",
|
||||
"seriehd": "https://nuovoindirizzo.info/seriehd/",
|
||||
"seriehd": "https://nuovoindirizzo.info/seriehd",
|
||||
"tantifilm": "https://www.tantifilm.wiki"
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ from platformcode import config
|
||||
|
||||
def findhost(url):
|
||||
data = support.httptools.downloadpage(url).data
|
||||
host = support.scrapertools.find_single_match(data, '<div class="elementor-button-wrapper"> <a href="([^"]+)"')
|
||||
host = support.scrapertools.find_single_match(data, '<div class="elementor-button-wrapper">\s*<a href="([^"]+)"')
|
||||
return host
|
||||
|
||||
host = config.get_channel_url(findhost)
|
||||
@@ -72,6 +72,7 @@ def peliculas(item):
|
||||
|
||||
@support.scrape
|
||||
def genres(item):
|
||||
# debugBlock=True
|
||||
action = 'peliculas'
|
||||
patronMenu = r'<li><a href="(?P<url>[^"]+)">(?P<title>[^<]+)<'
|
||||
|
||||
@@ -80,7 +81,7 @@ def genres(item):
|
||||
elif item.args == 'years':
|
||||
patronBlock = r'<ul class="listSubCat" id="Anno">(?P<block>.*)<ul class="listSubCat" id="Qualita">'
|
||||
elif item.args == 'quality':
|
||||
patronBlock = r'<ul class="listSubCat" id="Qualita">(?P<block>.*)</li> </ul> </div> </div> </div> <a'
|
||||
patronBlock = r'<ul class="listSubCat" id="Qualita">(?P<block>.*)</li>\s*?</ul>\s*?</div>\s*?</div>\s*?</div>\s*?<a'
|
||||
elif item.args == 'lucky': # sono i titoli random nella pagina
|
||||
patronBlock = r'<h3 class="titleSidebox dado">FILM RANDOM</h3>(?P<block>.*)</section>'
|
||||
patron = r'<li><a href="(?P<url>[^"]+)">(?P<title>[^<[]+)(?:\[(?P<lang>.+?)\])?<'
|
||||
|
||||
@@ -104,11 +104,11 @@ def peliculas(item):
|
||||
action = 'select'
|
||||
|
||||
if item.args == 'newest':
|
||||
patron = r'<li><a href="(?P<url>[^"]+)"[^=]+="(?P<thumb>[^"]+)"><div>\s+<div[^>]+>(?P<title>[^\(\[<]+)(?:\[(?P<quality1>HD)\])?[ ]?(?:\(|\[)?(?P<lang>Sub-ITA)?(?:\)|\])?[ ]?(?:\[(?P<quality>.+?)\])?[ ]?(?:\((?P<year>\d+)\))?<(?:[^>]+>.+?(?:title="Nuovi episodi">(?P<episode>\d+x\d+)[ ]?(?P<lang2>Sub-Ita)?|title="IMDb">(?P<rating>[^<]+)))?'
|
||||
patron = r'<li><a href="(?P<url>[^"]+)"[^=]+="(?P<thumb>[^"]+)"><div>\s*?<div[^>]+>(?P<title>[^\(\[<]+)(?:\[(?P<quality1>HD)\])?[ ]?(?:\(|\[)?(?P<lang>Sub-ITA)?(?:\)|\])?[ ]?(?:\[(?P<quality>.+?)\])?[ ]?(?:\((?P<year>\d+)\))?<(?:[^>]+>.+?(?:title="Nuovi episodi">(?P<episode>\d+x\d+)[ ]?(?P<lang2>Sub-Ita)?|title="IMDb">(?P<rating>[^<]+)))?'
|
||||
else:
|
||||
patron = r'<li><a href="(?P<url>[^"]+)"[^=]+="(?P<thumb>[^"]+)"><div>\s+<div[^>]+>(?P<title>[^\(\[<]+)(?:\[(?P<quality1>HD)\])?[ ]?(?:\(|\[)?(?P<lang>Sub-ITA)?(?:\)|\])?[ ]?(?:\[(?P<quality>.+?)\])?[ ]?(?:\((?P<year>\d+)\))?'
|
||||
patron = r'<li><a href="(?P<url>[^"]+)"[^=]+="(?P<thumb>[^"]+)"><div>\s*?<div[^>]+>(?P<title>[^\(\[<]+)(?:\[(?P<quality1>HD)\])?\s?(?:[\(\[])?(?P<lang>Sub-ITA)?(?:[\)\]])?\s?(?:\[(?P<quality>.+?)\])?\s?(?:\((?P<year>\d+)\))?<'
|
||||
|
||||
patronNext = r'<a href="([^"]+)" >Pagina'
|
||||
patronNext = r'<a href="([^"]+)"\s*>Pagina'
|
||||
# debug = True
|
||||
|
||||
def itemHook(item):
|
||||
|
||||
+11
-3
@@ -37,7 +37,14 @@ def menu(item):
|
||||
def search(item, texto):
|
||||
support.info(texto)
|
||||
item.url = host + "/?s=" + texto
|
||||
return peliculas(item)
|
||||
try:
|
||||
return peliculas(item)
|
||||
except:
|
||||
import sys
|
||||
for line in sys.exc_info():
|
||||
support.info('search log:', line)
|
||||
return []
|
||||
|
||||
|
||||
def newest(categoria):
|
||||
support.info(categoria)
|
||||
@@ -59,12 +66,13 @@ def newest(categoria):
|
||||
|
||||
@support.scrape
|
||||
def peliculas(item):
|
||||
# debug=True
|
||||
blacklist = Blacklist
|
||||
item.contentType = 'tvshow'
|
||||
if item.args == 'newest':
|
||||
patron = r'<div id="blockvids">\s*<ul>\s*<li>\s*<a href="(?P<url>[^"]+)"[^>]+><img[^>]+src="(?P<thumb>[^"]+)"[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>(?P<title>[^\[]+)\[(?P<lang>[^\]]+)\]'
|
||||
patron = r'<div id="blockvids">\s*<ul>\s*<li>\s*<a href="(?P<url>[^"]+)"[^>]+><img[^>]+src="(?P<thumb>[^"]+)"[^>]*>(?:[^>]+>){4}(?P<title>[^\[]+)\[(?P<lang>[^\]]+)\]'
|
||||
else:
|
||||
patron = r'<div class="span4">\s*<a href="(?P<url>[^"]+)"><img src="(?P<thumb>[^"]+)"[^>]+><\/a>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+> +<h1>(?P<title>[^<\[]+)(?:\[(?P<lang>[^\]]+)\])?</h1></a>.*?-->(?:.*?<br />)?\s*(?P<plot>[^<]+)'
|
||||
patron = r'<div class="span4">\s*<a href="(?P<url>[^"]+)"><img src="(?P<thumb>[^"]+)"[^>]+><\/a>(?:[^>]+>){7}\s*<h1>(?P<title>[^<\[]+)(?:\[(?P<lang>[^\]]+)\])?</h1></a>.*?-->(?:.*?<br />)?\s*(?P<plot>[^<]+)'
|
||||
patronNext = r'<link rel="next" href="([^"]+)"'
|
||||
action = 'check'
|
||||
return locals()
|
||||
|
||||
@@ -78,7 +78,7 @@ def search(item, text):
|
||||
if item.contentType == 'tvshow': item.url = host + '/serietv/'
|
||||
else: item.url = host
|
||||
try:
|
||||
item.url = item.url + "?s=" + text.replace(' ', '+')
|
||||
item.url = item.url + "/search/" + text.replace(' ', '+')
|
||||
return peliculas(item)
|
||||
|
||||
# Continua la ricerca in caso di errore
|
||||
|
||||
@@ -59,7 +59,7 @@ def peliculas(item):
|
||||
patron = r'<a href="(?P<url>(?:https:\/\/.+?\/(?P<title>[^\/]+[a-zA-Z0-9\-]+)(?P<year>\d{4})))/".+?url\((?P<thumb>[^\)]+)\)">'
|
||||
elif item.contentType == 'tvshow':
|
||||
if item.args == 'update':
|
||||
patron = r'<a href="(?P<url>[^"]+)"[^<]+?url\((?P<thumb>.+?)\)">\s+<div class="titolo">(?P<title>.+?)(?: – Serie TV)?(?:\([sSuUbBiItTaA\-]+\))?[ ]?(?P<year>\d{4})?</div>[ ](?:<div class="genere">)?(?:[\w]+?\.?\s?[\s|S]?[\dx\-S]+?\s\(?(?P<lang>[iItTaA]+|[sSuUbBiItTaA\-]+)\)?\s?(?P<quality>[HD]+)?|.+?\(?(?P<lang2>[sSuUbBiItTaA\-]+)?\)?</div>)'
|
||||
patron = r'<a href="(?P<url>[^"]+)"[^<]+?url\((?P<thumb>.+?)\)">\s*?<div class="titolo">(?P<title>.+?)(?: – Serie TV)?(?:\([sSuUbBiItTaA\-]+\))?[ ]?(?P<year>\d{4})?</div>\s*?(?:<div class="genere">)?(?:[\w]+?\.?\s?[\s|S]?[\dx\-S]+?\s\(?(?P<lang>[iItTaA]+|[sSuUbBiItTaA\-]+)\)?\s?(?P<quality>[HD]+)?|.+?\(?(?P<lang2>[sSuUbBiItTaA\-]+)?\)?</div>)'
|
||||
pagination = 25
|
||||
else:
|
||||
patron = r'<a href="(?P<url>[^"]+)"\s*title="(?P<title>[^"\(]+)(?:"|\()(?:(?P<year>\d+)[^"]+)?.*?url\((?P<thumb>[^\)]+)\)(?:.*?<div class="voto">[^>]+>[^>]+>\s*(?P<rating>[^<]+))?.*?<div class="titolo">[^>]+>(?:<div class="genere">[^ ]*(?:\s\d+)?\s*(?:\()?(?P<lang>[^\)< ]+))?'
|
||||
@@ -77,6 +77,8 @@ def peliculas(item):
|
||||
item.title += support.typo(item.lang2, '_ [] color kod')
|
||||
if item.args == 'update':
|
||||
item.title = item.title.replace('-', ' ')
|
||||
if item.args == 'search':
|
||||
item.contentType = 'tvshow' if 'serie-' in item.url else 'movie'
|
||||
|
||||
return item
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "discoveryplus",
|
||||
"name": "Discovery + [free]",
|
||||
"active": true,
|
||||
"language": ["ita"],
|
||||
"thumbnail": "discoveryplus.png",
|
||||
"banner": "discoveryplus.png",
|
||||
"categories": ["tvshow", "documentary", "live"],
|
||||
"not_active": ["include_in_newest"],
|
||||
"default_off": ["include_in_global_search"],
|
||||
"settings": []
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# Canale per Rai Play
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import requests
|
||||
from core import support, jsontools
|
||||
from platformcode import logger, config
|
||||
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()
|
||||
|
||||
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',
|
||||
'Referer': host,
|
||||
'Cookie' : 'st=' + token}
|
||||
|
||||
def Dict(item):
|
||||
global pdict
|
||||
pdict = session.get(api + '/cms/routes/{}?decorators=viewingHistory&include=default'.format(item.args), headers=headers).json()['included']
|
||||
|
||||
|
||||
@support.menu
|
||||
def mainlist(item):
|
||||
top = [('Dirette {bold}', ['', 'live']),
|
||||
('Programmi {bullet bold tv}', ['/programmi', 'peliculas']),
|
||||
('Generi {bullet bold tv}', ['', 'genres'])]
|
||||
|
||||
search = ''
|
||||
|
||||
return locals()
|
||||
|
||||
|
||||
def liveDict():
|
||||
livedict = {}
|
||||
for key in session.get(api + '/cms/routes/home?decorators=viewingHistory&include=default', headers=headers).json()['included']:
|
||||
if key['type'] == 'channel' and key.get('attributes',{}).get('hasLiveStream', '') and 'Free' in key.get('attributes',{}).get('packages', []):
|
||||
title = key['attributes']['name']
|
||||
livedict[title] = {}
|
||||
livedict[title]['plot'] = key['attributes']['description']
|
||||
livedict[title]['url'] = '{}/canali/{}'.format(host, key['attributes']['alternateId'])
|
||||
livedict[title]['id'] = key['id']
|
||||
return livedict
|
||||
|
||||
|
||||
def search(item, text):
|
||||
itemlist = []
|
||||
item.args = 'search'
|
||||
item.text = text
|
||||
try:
|
||||
itemlist = peliculas(item)
|
||||
except:
|
||||
import sys
|
||||
for line in sys.exc_info():
|
||||
support.logger.error("%s" % line)
|
||||
|
||||
return itemlist
|
||||
|
||||
|
||||
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))
|
||||
return support.thumb(itemlist, live=True)
|
||||
|
||||
|
||||
def genres(item):
|
||||
item.action = 'peliculas'
|
||||
itemlist = [
|
||||
item.clone(title='Attualità e inchiesta', url='/genere/attualita-e-inchiesta'),
|
||||
item.clone(title='Beauty and style', url='/genere/beauty-and-style'),
|
||||
item.clone(title='Serie TV', url='/genere/serie-tv'),
|
||||
item.clone(title='Casa', url='/genere/casa'),
|
||||
item.clone(title='Comedy', url='/genere/comedy'),
|
||||
item.clone(title='Crime', url='/genere/crime'),
|
||||
item.clone(title='Documentari', url='/genere/documentari'),
|
||||
item.clone(title='Discovery + Originals', url='/genere/discoveryplus-original'),
|
||||
item.clone(title='Food', url='/genere/food'),
|
||||
item.clone(title='Medical', url='/genere/medical'),
|
||||
item.clone(title='Motori', url='/genere/motori'),
|
||||
item.clone(title='Natura', url='/genere/natura'),
|
||||
item.clone(title='Paranormal', url='/genere/paranormal'),
|
||||
item.clone(title='People', url='/genere/people'),
|
||||
item.clone(title='Real Adventure', url='/genere/real-adventure'),
|
||||
item.clone(title='Real Life', url='/genere/real-life'),
|
||||
item.clone(title='Scienza e Spazio', url='/genere/scienza-e-spazio'),
|
||||
item.clone(title='Sex and love', url='/genere/sex-and-love'),
|
||||
item.clone(title='Sport', url='/genere/sport'),
|
||||
item.clone(title='Talent Show', url='/genere/talent-show'),
|
||||
]
|
||||
return itemlist
|
||||
|
||||
|
||||
def peliculas(item):
|
||||
logger.debug()
|
||||
itemlist =[]
|
||||
if 'search' in item.args:
|
||||
pdict = session.get(api + '/content/shows?include=genres,images,primaryChannel.images,contentPackages&page[size]=12&query=' + item.text, headers=headers).json()['data']
|
||||
else:
|
||||
pdict = session.get(api + '/cms/routes{}?decorators=viewingHistory&include=default'.format(item.url), headers=headers).json()['included']
|
||||
images = list(filter(lambda x: x['type'] == 'image', pdict))
|
||||
|
||||
for key in pdict:
|
||||
if key['type'] == 'show' and 'Free' in str(key.get('relationships',{}).get('contentPackages',{}).get('data',[])):
|
||||
title = key['attributes']['name']
|
||||
plot = key['attributes'].get('description','')
|
||||
url = '{}/programmi/{}'.format(host, key['attributes']['alternateId'])
|
||||
seasons = key['attributes']['seasonNumbers']
|
||||
thumbs = [image['attributes']['src'] for image in images if image['id'] == key['relationships']['images']['data'][0]['id']]
|
||||
thumb = thumbs[0] if thumbs else item.thumbnail
|
||||
fanarts = [image['attributes']['src'] for image in images if len(key['relationships']['images']['data']) > 1 and image['id'] == key['relationships']['images']['data'][1]['id']]
|
||||
fanart = fanarts[0] if fanarts else item.fanart
|
||||
itemlist.append(
|
||||
item.clone(title=typo(title,'bold'),
|
||||
fulltitle=title,
|
||||
plot=plot,
|
||||
url=url,
|
||||
programid=key['attributes']['alternateId'],
|
||||
id=key['id'],
|
||||
seasons=seasons,
|
||||
action='episodios',
|
||||
thumbnail=thumb,
|
||||
fanart=fanart,
|
||||
contentType='tvshow'))
|
||||
|
||||
return itemlist
|
||||
|
||||
|
||||
def episodios(item):
|
||||
logger.debug()
|
||||
itemlist =[]
|
||||
pdict = session.get(api + '/cms/routes/programmi/{}?decorators=viewingHistory&include=default'.format(item.programid), headers=headers).json()['included']
|
||||
|
||||
for key in pdict:
|
||||
if key['type'] == 'collection' and key.get('attributes',{}).get('component',{}).get('id', '') == 'tabbed-content':
|
||||
mandatory = key['attributes']['component']['mandatoryParams']
|
||||
for option in key['attributes']['component']['filters'][0]['options']:
|
||||
url = '{}/cms/collections/{}?decorators=viewingHistory&include=default&{}&{}'.format(api, key['id'], mandatory, option['parameter'])
|
||||
season = session.get(url, headers=headers).json()
|
||||
if season.get('included', {}):
|
||||
for episode in season['included']:
|
||||
if episode['type'] == 'video' and 'Free' in episode['attributes']['packages']:
|
||||
title = '{}x{:02d} - {}'.format(option['id'], episode['attributes']['episodeNumber'], episode['attributes']['name'])
|
||||
plot = episode['attributes']['description']
|
||||
itemlist.append(
|
||||
item.clone(title=typo(title,'bold'),
|
||||
fulltitle=title,
|
||||
plot=plot,
|
||||
id=episode['id'],
|
||||
action='play',
|
||||
contentType='episode',
|
||||
season=option['id'],
|
||||
episode=episode['attributes']['episodeNumber'],
|
||||
forcethumb=True,
|
||||
no_return=True))
|
||||
|
||||
if itemlist: itemlist.sort(key=lambda it: (int(it.season), int(it.episode)))
|
||||
return itemlist
|
||||
|
||||
|
||||
def play(item):
|
||||
if item.livefilter:
|
||||
item.id = liveDict()[item.livefilter]['id']
|
||||
item.fulltitle = item.livefilter
|
||||
item.forcethumb = True
|
||||
item.no_return = True
|
||||
support.thumb(item, live=True)
|
||||
if item.contentType == 'episode': data = session.get('{}/playback/v2/videoPlaybackInfo/{}?usePreAuth=true'.format(api, item.id), headers=headers).json().get('data',{}).get('attributes',{})
|
||||
else: data = session.get('{}/playback/v2/channelPlaybackInfo/{}?usePreAuth=true'.format(api, item.id), headers=headers).json().get('data',{}).get('attributes',{})
|
||||
if data.get('protection', {}).get('drm_enabled',True):
|
||||
url = data['streaming']['dash']['url']
|
||||
item.drm = 'com.widevine.alpha'
|
||||
item.license = data['protection']['schemes']['widevine']['licenseUrl'] + '|PreAuthorization=' + data['protection']['drmToken'] + '|R{SSM}|'
|
||||
else:
|
||||
url = data['streaming']['hls']['url']
|
||||
return support.servertools.find_video_items(item, data=url)
|
||||
@@ -137,7 +137,7 @@ def findvideos(item):
|
||||
if 'sub' in lang.lower():
|
||||
language = 'Sub-' + language
|
||||
quality = url.split('/')[-1].split('?')[0]
|
||||
url += "|User-Agent=" + support.httptools.get_user_agent() + '&Referer=' + item.url
|
||||
url += '|User-Agent=' + support.httptools.get_user_agent() + '&Referer=' + url
|
||||
|
||||
itemlist.append(item.clone(action="play", title=language, url=url, contentLanguage = language, quality = quality, order = quality.replace('p','').zfill(4), server='directo',))
|
||||
|
||||
|
||||
@@ -29,13 +29,14 @@ def mainlist(item):
|
||||
|
||||
@support.scrape
|
||||
def peliculas(item):
|
||||
# debug = True
|
||||
action = 'episodios'
|
||||
if item.args == 'newest':
|
||||
item.contentType = 'episode'
|
||||
patron = r'<span class="serieTitle" style="font-size:20px">(?P<title>[^<]+) –\s*<a href="(?P<url>[^"]+)"[^>]*>\s?(?P<episode>\d+[×x]\d+-\d+|\d+[×x]\d+) (?P<title2>[^<\(]+)\s?\(?(?P<lang>SUB ITA)?\)?</a>'
|
||||
pagination = ''
|
||||
else:
|
||||
patron = r'<div class="post-thumb">.*?\s<img src="(?P<thumb>[^"]+)".*?><a href="(?P<url>[^"]+)"[^>]+>(?P<title>.+?)\s?(?: Serie Tv)?\s?\(?(?P<year>\d{4})?\)?<\/a><\/h2>'
|
||||
patron = r'<div class="post-thumb">.*?<img src="(?P<thumb>[^"]+)".*?><a href="(?P<url>[^"]+)"[^>]+>(?P<title>.+?)\s?(?: Serie Tv)?\s?\(?(?P<year>\d{4})?\)?<\/a><\/h2>'
|
||||
patronNext=r'a class="next page-numbers" href="?([^>"]+)">Avanti »</a>'
|
||||
|
||||
return locals()
|
||||
|
||||
@@ -9,7 +9,7 @@ from platformcode import config
|
||||
|
||||
def findhost(url):
|
||||
page = httptools.downloadpage(url).data
|
||||
url = support.scrapertools.find_single_match(page, 'Il nuovo indirizzo di FILMPERTUTTI è <a href="([^"]+)')
|
||||
url = support.scrapertools.find_single_match(page, 'Il nuovo indirizzo di FILMPERTUTTI è ?<a href="([^"]+)')
|
||||
return url
|
||||
|
||||
host = config.get_channel_url(findhost)
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ def peliculas(item):
|
||||
if item.args == 'alternative':
|
||||
patron = r'<a title="(?P<title>[^\(]+)\(\s*(?P<year>\d+)\)\s\D+(?P<quality>\d+p) ... (?P<lang>[^ ]+).*?[^"]+"\s*href="(?P<url>[^"]+)'
|
||||
else:
|
||||
patron = r'<a href="(?P<url>[^"]+)" rel="?[0-9]+"? title="(?P<title>[^\(]+)(?!\()\s*\((?P<year>\d+)\)\s\D+(?P<quality>\d+p).{3}(?P<lang>[^ ]+).*?<img id="?cov"?.*?src="(?P<thumb>[^"]+)'
|
||||
patron = r'<a href="(?P<url>[^"]+)" (?:rel="?[0-9]+"?)? title="(?P<title>[^\(]+)(?!\()\s*\((?P<year>\d+)\)\s(?:[^\]]+\])?\D+(?P<quality>\d+p).{3}(?P<lang>[^ ]+).*?<img id="?cov"?.*?src="(?P<thumb>[^"]+)'
|
||||
patronNext = r'rel="?next"? href="([^"]+)"'
|
||||
return locals()
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ def mainlist(item):
|
||||
def peliculas(item):
|
||||
info()
|
||||
# debugBlock = True
|
||||
# debug=True
|
||||
|
||||
if item.args == 'search':
|
||||
patronBlock = r'<div class="search-page">(?P<block>.*?)<footer class="main">'
|
||||
@@ -79,13 +80,13 @@ def peliculas(item):
|
||||
action = 'episodios'
|
||||
if item.args == 'update':
|
||||
action = 'findvideos'
|
||||
patron = r'<div class="poster"><img src="(?P<thumb>[^"]+)"[^>]+>[^>]+><a href="(?P<url>[^"]+)">[^>]+>(?P<episode>[\d\-x]+)[^>]+>[^>]+>[^>]+>[^>]+>(?P<title>.+?)(?:\[(?P<lang>Sub-ITA|Sub-ita)\])?<[^>]+>[^>]+>[^>]+>[^>]+>(?P<quality>[HD]+)?(?:.+?)?/span><p class="serie"'
|
||||
patron = r'<div class="poster"><img src="(?P<thumb>[^"]+)"(?:[^>]+>){2}<a href="(?P<url>[^"]+)">[^>]+>(?P<episode>[\d\-x]+)(?:[^>]+>){4}(?P<title>.+?)(?:\[(?P<lang>[SsuUbBiItTaA-]{7})\])?<(?:[^>]+>){4}(?P<quality>[HDWEBRIP-]+)?(?:.+?)?/span><p class="serie"'
|
||||
pagination = 25
|
||||
def itemHook(item):
|
||||
item.contentType = 'episode'
|
||||
return item
|
||||
else:
|
||||
patron = r'<div class="poster">\s?<a href="(?P<url>[^"]+)"><img src="(?P<thumb>[^"]+)" alt="[^"]+"><\/a>[^>]+>[^>]+>[^>]+> (?P<rating>[0-9.]+)<[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>(?P<title>.+?)[ ]?(?:\[(?P<lang>Sub-ITA|Sub-ita)\])?<[^>]+>[^>]+>[^>]+>(?P<year>[0-9]{4})?[^<]*(?:<.*?<div class="texto">(?P<plot>[^<]+))?'
|
||||
patron = r'<div class="poster">\s?<a href="(?P<url>[^"]+)"><img src="(?P<thumb>[^"]+)" alt="[^"]+"><\/a>[^>]+>[^>]+>[^>]+> (?P<rating>[0-9.]+)<[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>(?P<title>.+?)[ ]?(?:\[(?P<lang>Sub-ITA|Sub-ita)\])?<[^>]+>[^>]+>[^>]+>(?P<year>[0-9]{4})?[^<]*(?:<.*?<div class="texto">(?P<plot>[^<]+)?)?'
|
||||
patronNext = '<span class="current">[^<]+<[^>]+><a href=[\'"]([^\'"]+)[\'"]'
|
||||
|
||||
#support.regexDbg(item, patron, headers)
|
||||
@@ -128,13 +129,13 @@ def search(item, text):
|
||||
itemlist = []
|
||||
text = text.replace(' ', '+')
|
||||
item.url = host + "?s=" + text
|
||||
# try:
|
||||
item.args = 'search'
|
||||
return peliculas(item)
|
||||
# except:
|
||||
# import sys
|
||||
# for line in sys.exc_info():
|
||||
# info("%s" % line)
|
||||
try:
|
||||
item.args = 'search'
|
||||
return peliculas(item)
|
||||
except:
|
||||
import sys
|
||||
for line in sys.exc_info():
|
||||
info("%s" % line)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@@ -25,16 +25,18 @@ def mainlist(item):
|
||||
|
||||
@support.scrape
|
||||
def peliculas(item):
|
||||
# debug=True
|
||||
blacklist = ['Aggiornamento Episodi']
|
||||
action = 'episodios'
|
||||
patron = r'<div class="post-thumb">\s*<a href="(?P<url>[^"]+)" title="(?P<title>[^"\[]+)[^>]+>\s*<img src="(?P<thumb>[^"]+)"[^>]+>'
|
||||
|
||||
if item.args == 'update':
|
||||
pagination = ''
|
||||
patron = r'br />(?:[^>]+>)?(?P<title>[^–]+)[^<]+<a href="(?P<url>[^"]+)">(?P<episode>[^ ]+)\s*(?P<title2>[^\(<]+)(?:\((?P<lang>[^\)]+))?'
|
||||
#patron = r'br />(?:[^>]+>)?(?P<title>[^–]+)[^<]+<a href="(?P<url>[^"]+)">(?P<episode>[^ ]+)\s*(?P<title2>[^\(<]+)(?:\((?P<lang>[^\)]+))?'
|
||||
patron = r'br[\s/]*>(?:\s*<[^>]+>)*(?P<title>[^–<]+)[^<]+<a href="(?P<url>[^"]+)"[^>]*>(?:[^,]{0,80}[, ]{2})*(?P<episode>[\S]+)\s*(?P<title2>[^\(<]+)(?:\((?P<lang>[^\)]+))?'
|
||||
action = 'episodios'
|
||||
if item.args == 'top':
|
||||
patron = r'<a href="(?P<url>[^"]+)">(?P<title>[^<]+)</a>[^>]+>[^>]+>[^>]+><img.*?src="(?P<thumb>[^"]+)"[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>:\s*(?P<rating>[^/]+)'
|
||||
patron = r'<a href="(?P<url>[^"]+)">(?P<title>[^<]+)</a>(?:[^>]+>){3}<img.*?src="(?P<thumb>[^"]+)"[^>]+>(?:[^>]+>){5}:\s*(?P<rating>[^/]+)'
|
||||
if item.args =='a-z':
|
||||
pagination = ''
|
||||
patron = r'<li ><a href="(?P<url>[^"]+)" title="(?P<title>[^"]+)"'
|
||||
|
||||
+13
-1
@@ -71,7 +71,14 @@ def replay(item):
|
||||
def search(item, text):
|
||||
item.url = host + '/tutti-i-programmi'
|
||||
item.search = text
|
||||
return peliculas(item)
|
||||
try:
|
||||
return peliculas(item)
|
||||
except:
|
||||
import sys
|
||||
for line in sys.exc_info():
|
||||
support.info('search log:', line)
|
||||
return []
|
||||
|
||||
|
||||
@support.scrape
|
||||
def peliculas(item):
|
||||
@@ -118,6 +125,11 @@ def episodios(item):
|
||||
|
||||
def play(item):
|
||||
support.info()
|
||||
if item.livefilter:
|
||||
for it in live(item):
|
||||
if it.fulltitle == item.livefilter:
|
||||
item = it
|
||||
break
|
||||
data = support.match(item).data
|
||||
match = support.match(data, patron='/content/entry/data/(.*?).mp4').match
|
||||
if match:
|
||||
|
||||
+40
-19
@@ -10,6 +10,7 @@ if sys.version_info[0] >= 3: from urllib.parse import urlencode, quote
|
||||
else: from urllib import urlencode, quote
|
||||
if sys.version_info[0] >= 3: from concurrent import futures
|
||||
else: from concurrent_py2 import futures
|
||||
from collections import OrderedDict
|
||||
|
||||
host = ''
|
||||
DRM = 'com.widevine.alpha'
|
||||
@@ -120,10 +121,9 @@ def menu(item):
|
||||
return itemlist
|
||||
|
||||
|
||||
def live(item):
|
||||
support.info()
|
||||
itemlist = []
|
||||
json = current_session.get(item.url).json()['entries']
|
||||
def liveDict():
|
||||
livedict = OrderedDict({})
|
||||
json = current_session.get('https://feed.entertainment.tv.theplatform.eu/f/PR1GhC/mediaset-prod-all-stations?sort=ShortTitle').json()['entries']
|
||||
for it in json:
|
||||
urls = []
|
||||
if it['tuningInstruction'] and not it['mediasetstation$digitalOnly']:
|
||||
@@ -133,18 +133,25 @@ def live(item):
|
||||
else:
|
||||
for key in it['tuningInstruction']['urn:theplatform:tv:location:any']:
|
||||
urls += key['publicUrls']
|
||||
plot = support.typo(guide['currentListing']['mediasetlisting$epgTitle'],'bold') + '\n' + guide['currentListing']['mediasetlisting$shortDescription'] + '\n' + guide['currentListing']['description'] + '\n\n' + support.typo('A Seguire:' + guide['nextListing']['mediasetlisting$epgTitle'], 'bold')
|
||||
title = it['title']
|
||||
livedict[title] = {}
|
||||
livedict[title]['urls'] = urls
|
||||
livedict[title]['plot'] = support.typo(guide['currentListing']['mediasetlisting$epgTitle'],'bold') + '\n' + guide['currentListing']['mediasetlisting$shortDescription'] + '\n' + guide['currentListing']['description'] + '\n\n' + support.typo('A Seguire:' + guide['nextListing']['mediasetlisting$epgTitle'], 'bold')
|
||||
return livedict
|
||||
|
||||
itemlist.append(item.clone(title=support.typo(it['title'], 'bold'),
|
||||
fulltitle=it['title'],
|
||||
show=it['title'],
|
||||
contentTitle=it['title'],
|
||||
thumbnail=it['thumbnails']['channel_logo-100x100']['url'],
|
||||
forcethumb=True,
|
||||
urls=urls,
|
||||
plot=plot,
|
||||
action='play',
|
||||
no_return=True))
|
||||
def live(item):
|
||||
support.info()
|
||||
itemlist = []
|
||||
for key, value in liveDict().items():
|
||||
itemlist.append(item.clone(title=support.typo(key, 'bold'),
|
||||
fulltitle=key,
|
||||
show=key,
|
||||
contentTitle=key,
|
||||
forcethumb=True,
|
||||
urls=value['urls'],
|
||||
plot=value['plot'],
|
||||
action='play',
|
||||
no_return=True))
|
||||
return support.thumb(itemlist, live=True)
|
||||
|
||||
|
||||
@@ -204,7 +211,9 @@ def peliculas(item):
|
||||
plot=it['longDescription'] if 'longDescription' in it else it['description'] if 'description' in it else '',
|
||||
urls=urls,
|
||||
seriesid = it.get('seriesId',''),
|
||||
url=it['mediasetprogram$pageUrl']))
|
||||
url=it['mediasetprogram$pageUrl'],
|
||||
forcethumb=True,
|
||||
no_return=True))
|
||||
return itemlist
|
||||
|
||||
|
||||
@@ -246,7 +255,7 @@ def episodios(item):
|
||||
urls.append(key['publicUrl'])
|
||||
if urls:
|
||||
title = it['title'].split('-')[-1].strip()
|
||||
if it['tvSeasonNumber'] and it['tvSeasonEpisodeNumber']:
|
||||
if it['tvSeasonNumber'] and it['tvSeasonEpisodeNumber'] and 'puntata del' not in title.lower():
|
||||
item.infoLabels['season'] = it['tvSeasonNumber']
|
||||
item.infoLabels['episode'] = it['tvSeasonEpisodeNumber']
|
||||
episode = '%dx%02d - ' % (it['tvSeasonNumber'], it['tvSeasonEpisodeNumber'])
|
||||
@@ -258,7 +267,10 @@ def episodios(item):
|
||||
fanart=it['thumbnails']['image_keyframe_poster-1280x720']['url'] if 'image_keyframe_poster-1280x720' in it['thumbnails'] else '',
|
||||
plot=it['longDescription'] if 'longDescription' in it else it['description'],
|
||||
urls=urls,
|
||||
url=it['mediasetprogram$pageUrl']))
|
||||
url=it['mediasetprogram$pageUrl'],
|
||||
year=it.get('year',''),
|
||||
forcethumb=True,
|
||||
no_return=True))
|
||||
if episode:
|
||||
itemlist = sorted(itemlist, key=lambda it: it.title)
|
||||
support.videolibrary(itemlist, item)
|
||||
@@ -267,12 +279,17 @@ def episodios(item):
|
||||
|
||||
def findvideos(item):
|
||||
support.info()
|
||||
itemlist = [support.Item(server='directo', title='Direct', url=item.urls, action='play')]
|
||||
itemlist = [support.Item(server='directo', title='Mediaset Play', url=item.urls, action='play')]
|
||||
return support.server(item, itemlist=itemlist, Download=False)
|
||||
|
||||
|
||||
def play(item):
|
||||
support.info()
|
||||
if item.livefilter:
|
||||
d = liveDict()[item.livefilter]
|
||||
# support.dbg()
|
||||
item = item.clone(title=support.typo(item.livefilter, 'bold'), fulltitle=item.livefilter, urls=d['urls'], plot=d['plot'], action='play', forcethumb=True, no_return=True)
|
||||
support.thumb(item, live=True)
|
||||
if not item.urls: urls = item.url
|
||||
else: urls = item.urls
|
||||
data = ''
|
||||
@@ -285,6 +302,10 @@ def play(item):
|
||||
item.drm = DRM
|
||||
item.license = lic_url % support.match(sec_data, patron=r'pid=([^|]+)').match
|
||||
data = support.match(sec_data, patron=r'<video src="([^"]+)').match
|
||||
break
|
||||
else:
|
||||
support.dbg()
|
||||
data = url
|
||||
|
||||
return support.servertools.find_video_items(item, data=data)
|
||||
|
||||
|
||||
@@ -36,9 +36,10 @@ def mainlist(item):
|
||||
|
||||
@support.scrape
|
||||
def peliculas(item):
|
||||
# debug=True
|
||||
action = 'findvideos'
|
||||
patron= r'<img src="[^"]+" alt="(?P<title>[^"]+)" data-echo="(?P<thumb>[^"]+)"[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+><a href="(?P<url>[^"]+)"'
|
||||
patronNext = r'<a href="([^"]+)">»'
|
||||
patron= r'<img src="[^"]+" alt="(?P<title>[^"]+)" data-echo="(?P<thumb>[^"]+)"(?:[^>]+>){7}<a href="(?P<url>[^"]+)"'
|
||||
patronNext = r'<a href="([^"]+)">(?:»|»)'
|
||||
typeContentDict = {'': 'music'}
|
||||
return locals()
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ def peliculas(item):
|
||||
# debug=True
|
||||
if item.args == 'last':
|
||||
patronBlock = r'<table>(?P<block>.*?)</table>'
|
||||
patron = r'<tr><td><a href="(?P<url>[^"]+)">\s*[^>]+>(?P<title>.*?)(?:\s(?P<year>\d{4}))? (?:Streaming|</b>)'
|
||||
patron = r'<tr><td><a href="(?P<url>[^"]+)">\s*[^>]+>(?P<title>.*?)(?:\s(?P<year>\d{4}))?\s*(?:Streaming|</b>)'
|
||||
elif item.args == 'lastep':
|
||||
patronBlock = r'<table>(?P<block>.*?)</table>'
|
||||
patron = r'<td>\s*<a href="[^>]+>(?P<title>.*?)(?:\s(?P<year>\d{4}))?\s(?:(?P<episode>(?:\d+x\d+|\d+)))\s*(?P<title2>[^<]+)(?P<url>.*?)<tr>'
|
||||
|
||||
+23
-10
@@ -5,6 +5,7 @@
|
||||
import inspect
|
||||
from core import support, jsontools
|
||||
from platformcode import autorenumber, logger
|
||||
from collections import OrderedDict
|
||||
|
||||
host = support.config.get_channel_url()
|
||||
headers = [['Referer', host]]
|
||||
@@ -39,24 +40,31 @@ def search(item, text):
|
||||
return []
|
||||
|
||||
|
||||
def live(item):
|
||||
logger.debug()
|
||||
itemlist=[]
|
||||
def liveDict():
|
||||
livedict = OrderedDict({})
|
||||
urls=[]
|
||||
matches = support.match(host, patron=r'(/diretta-tv/[^"]+)"[^>]+>([^ ]+)').matches
|
||||
from datetime import date
|
||||
today = date.today()
|
||||
channels = jsontools.load(support.match(host + '/api/more/tvschedule/' + str(today.year) + str(today.month) + str(today.day)).data)['channels']
|
||||
ch_dict = {}
|
||||
|
||||
for channel in channels:
|
||||
ch_dict[channel['label']] = channel['channelId']
|
||||
title = channel['label']
|
||||
livedict[title] = {}
|
||||
livedict[title]['id'] = channel['channelId']
|
||||
for url, title in matches:
|
||||
if url not in urls:
|
||||
urls.append(url)
|
||||
info = jsontools.load(support.match(host +'/api/on-air?channelId=' + ch_dict[title]).data)
|
||||
support.info(info)
|
||||
plot= '[B]' + info['seriesTitle'] +'[/B]\n' + info['description'] if 'seriesTitle' in info else ''
|
||||
itemlist.append(item.clone(title=support.typo(title,'bold'), contentTitle=title, fulltitle=title, show=title, url=host+url, plot=plot, action='play', forcethumb=True, no_return=True))
|
||||
livedict[title]['url'] = host + url
|
||||
info = jsontools.load(support.match(host +'/api/on-air?channelId=' + livedict[title]['id']).data)
|
||||
livedict[title]['plot']= '[B]' + info['seriesTitle'] +'[/B]\n' + info['description'] if 'seriesTitle' in info else ''
|
||||
return livedict
|
||||
|
||||
def live(item):
|
||||
logger.debug()
|
||||
itemlist=[]
|
||||
for key, value in liveDict().items():
|
||||
itemlist.append(item.clone(title=support.typo(key,'bold'), contentTitle=key, fulltitle=key, show=key, url=value['url'], plot=value['plot'], action='play', forcethumb=True, no_return=True))
|
||||
return support.thumb(itemlist, live=True)
|
||||
|
||||
|
||||
@@ -154,4 +162,9 @@ def findvideos(item):
|
||||
|
||||
def play(item):
|
||||
logger.debug()
|
||||
return support.servertools.find_video_items(item, data=item.url)
|
||||
item.server = 'paramount_server'
|
||||
if item.livefilter:
|
||||
d = liveDict()[item.livefilter]
|
||||
item = item.clone(title=support.typo(item.livefilter, 'bold'), fulltitle=item.livefilter, url=d['url'], plot=d['plot'], action='play', forcethumb=True, no_return=True)
|
||||
support.thumb(item, live=True)
|
||||
return [item]
|
||||
+50
-26
@@ -4,12 +4,14 @@
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import requests, sys, inspect
|
||||
from core import support
|
||||
from platformcode import autorenumber, logger
|
||||
from core import support, channeltools
|
||||
from platformcode import autorenumber, logger, platformtools
|
||||
from collections import OrderedDict
|
||||
if sys.version_info[0] >= 3:
|
||||
from concurrent import futures
|
||||
else:
|
||||
from concurrent_py2 import futures
|
||||
|
||||
current_session = requests.Session()
|
||||
host = support.config.get_channel_url()
|
||||
onair = host + '/palinsesto/onAir.json'
|
||||
@@ -17,19 +19,19 @@ onair = host + '/palinsesto/onAir.json'
|
||||
|
||||
@support.menu
|
||||
def mainlist(item):
|
||||
top = [('Dirette {bold}', ['/dl/RaiPlay/2016/PublishingBlock-9a2ff311-fcf0-4539-8f8f-c4fee2a71d58.html?json', 'live']),
|
||||
top = [('Dirette {bold}', ['', 'live']),
|
||||
('Replay {bold}', ['/dl/RaiPlay/2016/PublishingBlock-9a2ff311-fcf0-4539-8f8f-c4fee2a71d58.html?json', 'replay_menu'])]
|
||||
|
||||
menu = [('Film {bullet bold}', ['/film/index.json', 'menu']),
|
||||
('Serie TV {bullet bold}', ['/serietv/index.json', 'menu']),
|
||||
('Fiction {bullet bold}', ['/fiction/index.json', 'menu']),
|
||||
('Documentari {bullet bold}', ['/documentari/index.json', 'menu']),
|
||||
('Programmi TV{bullet bold}', ['/programmi/index.json', 'menu']),
|
||||
('Programmi per Bambini {bullet bold}', ['/bambini/index.json', 'menu']),
|
||||
('Teen {bullet bold}', ['/teen/index.json', 'learning']),
|
||||
('Learning {bullet bold}', ['/learning/index.json', 'learning']),
|
||||
('Teche Rai {bullet bold storia}', ['/techerai/index.json', 'menu']),
|
||||
('Musica e Teatro {bullet bold}', ['/performing-arts/index.json', 'menu'])
|
||||
menu = [('Film {bullet bold}', ['/tipologia/film/index.json', 'menu']),
|
||||
('Serie TV {bullet bold}', ['/tipologia/serietv/index.json', 'menu']),
|
||||
('Fiction {bullet bold}', ['/tipologia/fiction/index.json', 'menu']),
|
||||
('Documentari {bullet bold}', ['/tipologia/documentari/index.json', 'menu']),
|
||||
('Programmi TV{bullet bold}', ['/tipologia/programmi/index.json', 'menu']),
|
||||
('Programmi per Bambini {bullet bold}', ['/tipologia/bambini/index.json', 'menu']),
|
||||
('Teen {bullet bold}', ['/tipologia/teen/index.json', 'learning']),
|
||||
('Learning {bullet bold}', ['/tipologia/learning/index.json', 'learning']),
|
||||
('Teche Rai {bullet bold storia}', ['/tipologia/techerai/index.json', 'menu']),
|
||||
('Musica e Teatro {bullet bold}', ['/tipologia/musica-e-teatro/index.json', 'menu'])
|
||||
]
|
||||
|
||||
search = ''
|
||||
@@ -157,24 +159,33 @@ def Type(item):
|
||||
return select(item)
|
||||
|
||||
|
||||
def live(item):
|
||||
support.info()
|
||||
itemlist =[]
|
||||
info={}
|
||||
json = current_session.get(item.url).json()['dirette']
|
||||
def liveDict():
|
||||
livedict = OrderedDict({})
|
||||
info = {}
|
||||
url = host + '/dirette.json'
|
||||
json = current_session.get(url).json()['contents']
|
||||
onAir = current_session.get(onair).json()['on_air']
|
||||
support.info(onAir)
|
||||
for key in onAir:
|
||||
channel = key['channel']
|
||||
info[channel] = {}
|
||||
info[channel]['fanart'] = getUrl(key['currentItem']['image'])
|
||||
info[channel]['plot'] = support.typo(key['currentItem']['name'],'bold')+ '\n\n' + key['currentItem']['description']
|
||||
|
||||
for i, key in enumerate(json):
|
||||
for key in json:
|
||||
channel = key['channel']
|
||||
itemlist.append(item.clone(title = support.typo(channel, 'bold'), fulltitle = channel, show = channel, url = key['video']['contentUrl'],
|
||||
thumbnail = key['transparent-icon'].replace("[RESOLUTION]", "256x-"), forcethumb = True , fanart = info[channel]['fanart'],
|
||||
plot = info[channel]['plot'], action = 'play', no_return=True))
|
||||
livedict[channel] = {}
|
||||
livedict[channel]['url'] = key['video']['content_url']
|
||||
livedict[channel]['plot'] = info[channel]['plot']
|
||||
livedict[channel]['fanart'] = info[channel]['fanart']
|
||||
|
||||
return livedict
|
||||
|
||||
|
||||
def live(item):
|
||||
support.info()
|
||||
itemlist =[]
|
||||
for channel, value in liveDict().items():
|
||||
itemlist.append(item.clone(title = support.typo(channel, 'bold'), fulltitle = channel, show = channel, url = value['url'],
|
||||
plot = value['plot'], action = 'play', fanart = value['fanart'], no_return=True))
|
||||
return support.thumb(itemlist, live=True)
|
||||
|
||||
|
||||
@@ -331,7 +342,7 @@ def findvideos(item):
|
||||
else:
|
||||
url = item.url
|
||||
|
||||
itemlist.append(item.clone(server = 'directo', title = support.config.get_localized_string(30137), fanart = item.json, url = getUrl(url), action = 'play' ))
|
||||
itemlist.append(item.clone(server = 'directo', title = 'Rai Play', url = getUrl(url) + '&output=56', action = 'play'))
|
||||
return support.server(item, itemlist=itemlist, Download=False)
|
||||
|
||||
|
||||
@@ -350,7 +361,7 @@ def getUrl(pathId):
|
||||
if url.endswith(".html?json"):
|
||||
url = url.replace(".html?json", ".json")
|
||||
elif url.endswith("/?json"):
|
||||
url = url.replace("/?json","/index.json")
|
||||
url = url.replace("/?json",".json")
|
||||
elif url.endswith("?json"):
|
||||
url = url.replace("?json",".json")
|
||||
|
||||
@@ -407,3 +418,16 @@ def load_episodes(key, item):
|
||||
return itemlist
|
||||
|
||||
|
||||
def play(item):
|
||||
if item.livefilter:
|
||||
d = liveDict()
|
||||
item = item.clone(server='directo', fulltitle=item.livefilter, url=d[item.livefilter]['url'], plot=d[item.livefilter]['plot'], forcethumb=True, no_return=True)
|
||||
support.thumb(item, live=True)
|
||||
if '&output=56' in item.url:
|
||||
match = support.match(item, patron=r'content"><!\[CDATA\[([^\]]+)(?:.*?"WIDEVINE","licenceUrl":"([^"]+))?').match
|
||||
item.url = match[0]
|
||||
if len(match) == 2:
|
||||
item.drm = 'com.widevine.alpha'
|
||||
item.license = match[1] + '|' + host + '|R{SSM}|'
|
||||
logger.debug('PLAY URL', item.url)
|
||||
return [item]
|
||||
+3
-3
@@ -69,13 +69,13 @@ def peliculas(item):
|
||||
|
||||
if item.args == 'last':
|
||||
action = 'findvideos'
|
||||
patron = r'singleUpdate">[^>]+>[^>]+>\s*<img src="(?P<thumb>[^"]+)"[^>]+>[^>]+>[^>]+>\s*<h2>(?P<title>[^<]+)<[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>\s*<a href="(?P<url>[^"]+)">[^>]+>[^>]+>[^>]+>\s*(?P<season>\d+)\D+(?P<episode>\d+)(?:[^\(]*\()?(?P<lang>[^\)]+)?(?:\))?'
|
||||
patron = r'singleUpdate">(?:[^>]+>){2}\s*<img src="(?P<thumb>[^"]+)"(?:[^>]+>){3}\s*<h2>(?P<title>[^<]+)<(?:[^>]+>){14,16}\s*<a href="(?P<url>[^"]+)">(?:[^>]+>){3}\s*(?P<season>\d+)\D+(?P<episode>\d+)(?:[^\(]*\()?(?P<lang>[^\)]+)?(?:\))?'
|
||||
elif item.args == 'best':
|
||||
action='episodios'
|
||||
patron = r'col-md-3">\s*<a href="(?P<url>[^"]+)">[^>]+>\s*<div class="infoVetrina">[^>]+>(?P<year>\d{4})[^>]+>[^>]+>(?P<title>[^<]+)<[^>]+>[^>]+>[^>]+>[^>]+>(?P<rating>[^<]+)[^>]+>[^>]+>[^>]+>[^>]+>\s*<img src="(?P<thumb>[^"]+)"'
|
||||
patron = r'col-md-3">\s*<a href="(?P<url>[^"]+)">[^>]+>\s*<div class="infoVetrina">[^>]+>(?P<year>\d{4})(?:[^>]+>){2}(?P<title>[^<]+)<(?:[^>]+>){4}(?P<rating>[^<]+)(?:[^>]+>){4}\s*<img src="(?P<thumb>[^"]+)"'
|
||||
else:
|
||||
action='episodios'
|
||||
patron = r'<a href="(?P<url>[^"]+)">[^>]+>\s*<div class="infoSeries">\s*<h2>(?P<title>[^<]+)<[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>(?P<rating>[^<]+)?[^>]+>[^>]+>[^>]+>\s*<img src="(?P<thumb>[^"]+)"[^>]+>[^>]+>[^>]+>(?P<quality>[^<]+)<[^>]+>[^>]+>(?P<year>\d{4})'
|
||||
patron = r'<a href="(?P<url>[^"]+)">[^>]+>\s*<div class="infoSeries">\s*<h2>(?P<title>[^<]+)<(?:[^>]+>){5}(?P<rating>[^<]+)?(?:[^>]+>){3}\s*<img src="(?P<thumb>[^"]+)"(?:[^>]+>){3}(?P<quality>[^<]+)<(?:[^>]+>){2}(?P<year>\d{4})'
|
||||
patronNext=r'next page-numbers" href="([^"]+)"'
|
||||
|
||||
return locals()
|
||||
|
||||
@@ -35,7 +35,7 @@ def mainlist(item):
|
||||
|
||||
|
||||
film = ['/ultimi-film-aggiunti/',
|
||||
('Lista', ['/lista-film/', 'peliculas', 'lista'])
|
||||
('A-Z', ['/lista-film/', 'peliculas', 'lista'])
|
||||
]
|
||||
|
||||
tvshow = ['',
|
||||
@@ -65,7 +65,7 @@ def peliculas(item):
|
||||
|
||||
if item.args == 'search':
|
||||
patronBlock = r'>Lista Serie Tv</a></li></ul></div><div id="box_movies">(?P<block>.*?)<div id="paginador">'
|
||||
patron = r'<div class="movie">[^>]+[^>]+>\s?<img src="(?P<thumb>[^"]+)" alt="(?P<title>.+?)\s?(?P<year>[\d\-]+)?"[^>]+>\s?<a href="(?P<url>[^"]+)">'
|
||||
patron = r'<div class="movie">[^>]+[^>]+>\s*<img src="(?P<thumb>[^"]+)" alt="(?P<title>.+?)(?:(?P<year>\d{4})|")[^>]*>\s*<a href="([^"]+)'
|
||||
elif item.contentType == 'episode':
|
||||
pagination = 35
|
||||
action = 'findvideos'
|
||||
@@ -85,7 +85,7 @@ def peliculas(item):
|
||||
pagination = 25
|
||||
|
||||
if item.args == 'lista':
|
||||
patron = r'href="(?P<url>[^"]+)"[^>]+>(?P<title>.*?)(?P<year>\d{4})?<'
|
||||
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>'
|
||||
|
||||
+23
-18
@@ -291,25 +291,30 @@ def newest(categoria):
|
||||
def search(item, texto):
|
||||
info(texto)
|
||||
itemlist = []
|
||||
|
||||
try:
|
||||
patron = r'<li class="cat-item cat-item-\d+"><a href="([^"]+)"\s?>([^<]+)</a>'
|
||||
matches = support.match(item, patron=patron, headers=headers).matches
|
||||
for i, (scrapedurl, scrapedtitle) in enumerate(matches):
|
||||
if texto.upper() in scrapedtitle.upper():
|
||||
scrapedthumbnail = ""
|
||||
scrapedplot = ""
|
||||
title = cleantitle(scrapedtitle)
|
||||
itemlist.append(
|
||||
item.clone(action="episodios",
|
||||
title=title,
|
||||
url=scrapedurl,
|
||||
thumbnail=scrapedthumbnail,
|
||||
fulltitle=title,
|
||||
show=title,
|
||||
plot=scrapedplot,
|
||||
contentType='episode',
|
||||
originalUrl=scrapedurl))
|
||||
tmdb.set_infoLabels_itemlist(itemlist, seekTmdb=True)
|
||||
matches = support.match(item, patron=patron, headers=headers).matches
|
||||
for i, (scrapedurl, scrapedtitle) in enumerate(matches):
|
||||
if texto.upper() in scrapedtitle.upper():
|
||||
scrapedthumbnail = ""
|
||||
scrapedplot = ""
|
||||
title = cleantitle(scrapedtitle)
|
||||
itemlist.append(
|
||||
item.clone(action="episodios",
|
||||
title=title,
|
||||
url=scrapedurl,
|
||||
thumbnail=scrapedthumbnail,
|
||||
fulltitle=title,
|
||||
show=title,
|
||||
plot=scrapedplot,
|
||||
contentType='episode',
|
||||
originalUrl=scrapedurl))
|
||||
tmdb.set_infoLabels_itemlist(itemlist, seekTmdb=True)
|
||||
except:
|
||||
import sys
|
||||
for line in sys.exc_info():
|
||||
support.info('search log:', line)
|
||||
return []
|
||||
|
||||
return itemlist
|
||||
|
||||
|
||||
@@ -33,14 +33,15 @@ def mainlist(item):
|
||||
|
||||
@support.scrape
|
||||
def peliculas(item):
|
||||
# debug=True
|
||||
patronBlock = r'<div class="wrap">\s*<h.>.*?</h.>(?P<block>.*?)<footer>'
|
||||
|
||||
if item.args != 'update':
|
||||
action = 'episodios'
|
||||
patron = r'<div class="item">\s*<a href="(?P<url>[^"]+)" data-original="(?P<thumb>[^"]+)" class="lazy inner">[^>]+>[^>]+>[^>]+>[^>]+>(?P<title>[^<]+)<'
|
||||
patron = r'<div class="item">\s*?<a href="(?P<url>[^"]+)" data-original="(?P<thumb>[^"]+)" class="lazy inner">(?:[^>]+>){4}(?P<title>[^<]+)<'
|
||||
else:
|
||||
action = 'findvideos'
|
||||
patron = r'<div class="item">\s+?<a href="(?P<url>[^"]+)"\s+?data-original="(?P<thumb>[^"]+)"[^>]+>[^>]+>[^>]+>[^>]+>[^>]+>(?P<title>.+?)<[^>]+>\((?P<episode>[\dx\-]+)\s+?(?P<lang>Sub-Ita|[iITtAa]+)\)<'
|
||||
patron = r'<div class="item">\s*?<a href="(?P<url>[^"]+)"\s*?data-original="(?P<thumb>[^"]+)"(?:[^>]+>){5}(?P<title>.+?)<[^>]+>\((?P<episode>[\dx\-]+)\s+?(?P<lang>Sub-Ita|[iITtAa]+)\)<'
|
||||
pagination = 25
|
||||
|
||||
patronNext = r'<li><a href="([^"]+)"\s+?>Pagina successiva'
|
||||
|
||||
+2
-1
@@ -220,7 +220,8 @@ def make_itemlist(itemlist, item, data):
|
||||
for key in data['data']:
|
||||
if search.lower() in encode(key['title']).lower():
|
||||
infoLabels['year'] = key['date_published']
|
||||
infoLabels['title'] = infoLabels['tvshowtitle'] = key['title']
|
||||
infoLabels['title'] = key['title']
|
||||
if item.contentType != 'movie': infoLabels['tvshowtitle'] = key['title']
|
||||
title = encode(key['title'])
|
||||
itemlist.append(
|
||||
item.clone(title = support.typo(title, 'bold'),
|
||||
|
||||
+22
-1
@@ -6,7 +6,28 @@ import sys
|
||||
# Appends the main plugin dir to the PYTHONPATH if an internal package cannot be imported.
|
||||
# Examples: In Plex Media Server all modules are under "Code.*" package, and in Enigma2 under "Plugins.Extensions.*"
|
||||
try:
|
||||
# from core import logger
|
||||
import core
|
||||
except:
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
# Connect to database
|
||||
from . import filetools
|
||||
from platformcode import config
|
||||
from collections import defaultdict
|
||||
from lib.sqlitedict import SqliteDict
|
||||
|
||||
|
||||
class nested_dict_sqlite(defaultdict):
|
||||
'like defaultdict but default_factory receives the key'
|
||||
|
||||
def __missing__(self, key):
|
||||
self[key] = value = self.default_factory(key)
|
||||
return value
|
||||
|
||||
def close(self):
|
||||
for key in self.keys():
|
||||
self[key].close()
|
||||
|
||||
|
||||
db_name = filetools.join(config.get_data_path(), "db.sqlite")
|
||||
db = nested_dict_sqlite(lambda table: SqliteDict(db_name, table, 'c', True))
|
||||
|
||||
+1
-1
@@ -419,7 +419,7 @@ def downloadpage(url, **opt):
|
||||
response['data'] = response['data'].decode('ISO-8859-1')
|
||||
|
||||
if req.headers.get('Server', '').startswith('cloudflare') and response_code in [429, 503, 403]\
|
||||
and not opt.get('CF', False) and 'Please turn JavaScript on and reload the page' in response['data']:
|
||||
and not opt.get('CF', False) and 'Ray ID' in response['data'] and not opt.get('post', None):
|
||||
logger.debug("CF retry... for domain: %s" % domain)
|
||||
from lib import proxytranslate
|
||||
gResp = proxytranslate.process_request_proxy(url)
|
||||
|
||||
+7
-27
@@ -12,16 +12,11 @@ else:
|
||||
|
||||
from lib.requests_toolbelt.adapters import host_header_ssl
|
||||
from lib import doh
|
||||
from platformcode import logger, config
|
||||
from platformcode import logger
|
||||
import requests
|
||||
from core import scrapertools
|
||||
from core import db
|
||||
|
||||
try:
|
||||
import _sqlite3 as sql
|
||||
except:
|
||||
import sqlite3 as sql
|
||||
|
||||
db = os.path.join(config.get_data_path(), 'kod_db.sqlite')
|
||||
if 'PROTOCOL_TLS' in ssl.__dict__:
|
||||
protocol = ssl.PROTOCOL_TLS
|
||||
elif 'PROTOCOL_SSLv23' in ssl.__dict__:
|
||||
@@ -48,8 +43,6 @@ class CustomContext(ssl.SSLContext):
|
||||
class CipherSuiteAdapter(host_header_ssl.HostHeaderSSLAdapter):
|
||||
|
||||
def __init__(self, domain, CF=False, *args, **kwargs):
|
||||
self.conn = sql.connect(db)
|
||||
self.cur = self.conn.cursor()
|
||||
self.ssl_context = CustomContext(protocol, domain)
|
||||
self.CF = CF # if cloudscrape is in action
|
||||
self.cipherSuite = kwargs.pop('cipherSuite', DEFAULT_CIPHERS)
|
||||
@@ -57,18 +50,13 @@ class CipherSuiteAdapter(host_header_ssl.HostHeaderSSLAdapter):
|
||||
super(CipherSuiteAdapter, self).__init__(**kwargs)
|
||||
|
||||
def flushDns(self, request, domain, **kwargs):
|
||||
self.cur.execute('delete from dnscache where domain=?', (domain,))
|
||||
self.conn.commit()
|
||||
del db['dnscache'][domain]
|
||||
return self.send(request, flushedDns=True, **kwargs)
|
||||
|
||||
def getIp(self, domain):
|
||||
ip = None
|
||||
try:
|
||||
self.cur.execute('select ip from dnscache where domain=?', (domain,))
|
||||
ip = self.cur.fetchall()[0][0]
|
||||
logger.info('Cache DNS: ' + domain + ' = ' + str(ip))
|
||||
except:
|
||||
pass
|
||||
ip = db['dnscache'].get(domain, None)
|
||||
logger.info('Cache DNS: ' + domain + ' = ' + str(ip))
|
||||
|
||||
if not ip: # not cached
|
||||
try:
|
||||
ip = doh.query(domain)[0]
|
||||
@@ -81,15 +69,7 @@ class CipherSuiteAdapter(host_header_ssl.HostHeaderSSLAdapter):
|
||||
return ip
|
||||
|
||||
def writeToCache(self, domain, ip):
|
||||
try:
|
||||
self.cur.execute('insert into dnscache values(?,?)', (domain, ip))
|
||||
except:
|
||||
self.cur.execute("""CREATE TABLE IF NOT EXISTS dnscache(
|
||||
"domain" TEXT NOT NULL UNIQUE,
|
||||
"ip" TEXT NOT NULL,
|
||||
PRIMARY KEY("domain")
|
||||
);""")
|
||||
self.conn.commit()
|
||||
db['dnscache'][domain] = ip
|
||||
|
||||
def init_poolmanager(self, *args, **kwargs):
|
||||
kwargs['ssl_context'] = self.ssl_context
|
||||
|
||||
+7
-5
@@ -32,19 +32,21 @@ def find_and_set_infoLabels(item):
|
||||
|
||||
# Get the default Scraper of the configuration according to the content type
|
||||
if item.contentType == "movie":
|
||||
scraper_actual = ['tmdb'][config.get_setting("scraper_movies", "videolibrary")]
|
||||
scraper_actual = 'tmdb'
|
||||
# scraper_actual = ['tmdb'][config.get_setting("scraper_movies", "videolibrary")]
|
||||
tipo_contenido = "movie"
|
||||
title = item.contentTitle
|
||||
# Complete list of options for this type of content
|
||||
list_opciones_cuadro.append(scrapers_disponibles['tmdb'])
|
||||
|
||||
else:
|
||||
scraper_actual = ['tmdb', 'tvdb'][config.get_setting("scraper_tvshows", "videolibrary")]
|
||||
scraper_actual = 'tmdb'
|
||||
# scraper_actual = ['tmdb', 'tvdb'][config.get_setting("scraper_tvshows", "videolibrary")]
|
||||
tipo_contenido = "serie"
|
||||
title = item.contentSerieName
|
||||
# Complete list of options for this type of content
|
||||
list_opciones_cuadro.append(scrapers_disponibles['tmdb'])
|
||||
list_opciones_cuadro.append(scrapers_disponibles['tvdb'])
|
||||
# list_opciones_cuadro.append(scrapers_disponibles['tvdb'])
|
||||
|
||||
# We import the scraper
|
||||
try:
|
||||
@@ -187,7 +189,7 @@ def callback_cuadro_completar(item, dict_values):
|
||||
return False
|
||||
|
||||
|
||||
def get_nfo(item):
|
||||
def get_nfo(item, search_groups=False):
|
||||
"""
|
||||
Returns the information necessary for the result to be scraped into the kodi video library,
|
||||
|
||||
@@ -229,7 +231,7 @@ def get_nfo(item):
|
||||
if item.contentType == "movie": scraper_actual = ['tmdb'][config.get_setting("scraper_movies", "videolibrary")]
|
||||
else: scraper_actual = ['tmdb', 'tvdb'][config.get_setting("scraper_tvshows", "videolibrary")]
|
||||
scraper = __import__('core.%s' % scraper_actual, fromlist=["core.%s" % scraper_actual])
|
||||
return scraper.get_nfo(item)
|
||||
return scraper.get_nfo(item, search_groups)
|
||||
|
||||
|
||||
def sort_episode_list(episodelist):
|
||||
|
||||
+1
-1
@@ -1367,7 +1367,7 @@ def thumb(item_itemlist_string=None, genre=False, live=False):
|
||||
for item in item_itemlist_string:
|
||||
item.thumbnail = "https://raw.githubusercontent.com/kodiondemand/media/master/live/" + item.fulltitle.lower().replace(' ','_') + '.png'
|
||||
else:
|
||||
item_itemlist_string.thumbnail = "https://raw.githubusercontent.com/kodiondemand/media/master/live/" + item.fulltitle.lower().replace(' ','_') + '.png'
|
||||
item_itemlist_string.thumbnail = "https://raw.githubusercontent.com/kodiondemand/media/master/live/" + item_itemlist_string.fulltitle.lower().replace(' ','_') + '.png'
|
||||
return item_itemlist_string
|
||||
|
||||
icon_dict = {'movie':['film', 'movie'],
|
||||
|
||||
+67
-48
@@ -3,7 +3,8 @@
|
||||
# from future import standard_library
|
||||
# standard_library.install_aliases()
|
||||
# from builtins import str
|
||||
import sys
|
||||
import datetime
|
||||
import sys, requests
|
||||
PY3 = False
|
||||
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
|
||||
|
||||
@@ -15,7 +16,7 @@ else:
|
||||
from future.builtins import range
|
||||
from future.builtins import object
|
||||
|
||||
import ast, copy, re, sqlite3, time, xbmcaddon
|
||||
import ast, copy, re, time
|
||||
|
||||
from core import filetools, httptools, jsontools, scrapertools
|
||||
from core.item import InfoLabels
|
||||
@@ -62,27 +63,7 @@ def_lang = info_language[config.get_setting("info_language", "videolibrary")]
|
||||
# ------------------------------------------------- -------------------------------------------------- -----------
|
||||
|
||||
otmdb_global = None
|
||||
fname = filetools.join(config.get_data_path(), "kod_db.sqlite")
|
||||
|
||||
def create_bd():
|
||||
conn = sqlite3.connect(fname)
|
||||
c = conn.cursor()
|
||||
c.execute('CREATE TABLE IF NOT EXISTS tmdb_cache (url TEXT PRIMARY KEY, response TEXT, added TEXT)')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def drop_bd():
|
||||
conn = sqlite3.connect(fname)
|
||||
c = conn.cursor()
|
||||
c.execute('DROP TABLE IF EXISTS tmdb_cache')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
create_bd()
|
||||
from core import db
|
||||
|
||||
|
||||
# The function name is the name of the decorator and receives the function that decorates.
|
||||
@@ -93,17 +74,11 @@ def cache_response(fn):
|
||||
# start_time = time.time()
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
import base64
|
||||
|
||||
def check_expired(ts):
|
||||
import datetime
|
||||
|
||||
def check_expired(saved_date):
|
||||
valided = False
|
||||
|
||||
cache_expire = config.get_setting("tmdb_cache_expire", default=0)
|
||||
|
||||
saved_date = datetime.datetime.fromtimestamp(ts)
|
||||
current_date = datetime.datetime.fromtimestamp(time.time())
|
||||
current_date = datetime.datetime.now()
|
||||
elapsed = current_date - saved_date
|
||||
|
||||
# 1 day
|
||||
@@ -148,29 +123,18 @@ def cache_response(fn):
|
||||
result = fn(*args)
|
||||
else:
|
||||
|
||||
conn = sqlite3.connect(fname, timeout=15)
|
||||
c = conn.cursor()
|
||||
url = re.sub('&year=-', '', args[0])
|
||||
if PY3: url = str.encode(url)
|
||||
url_base64 = base64.b64encode(url)
|
||||
c.execute("SELECT response, added FROM tmdb_cache WHERE url=?", (url_base64,))
|
||||
row = c.fetchone()
|
||||
|
||||
if row and check_expired(float(row[1])):
|
||||
result = eval(base64.b64decode(row[0]))
|
||||
row = db['tmdb_cache'].get(url)
|
||||
|
||||
if row and check_expired(row[1]):
|
||||
result = row[0]
|
||||
|
||||
# si no se ha obtenido información, llamamos a la funcion
|
||||
if not result:
|
||||
result = fn(*args)
|
||||
result = str(result)
|
||||
if PY3: result = str.encode(result)
|
||||
result_base64 = base64.b64encode(result)
|
||||
c.execute("INSERT OR REPLACE INTO tmdb_cache (url, response, added) VALUES (?, ?, ?)",
|
||||
(url_base64, result_base64, time.time()))
|
||||
|
||||
conn.commit()
|
||||
|
||||
conn.close()
|
||||
db['tmdb_cache'][url] = [result, datetime.datetime.now()]
|
||||
|
||||
# elapsed_time = time.time() - start_time
|
||||
# logger.debug("TARDADO %s" % elapsed_time)
|
||||
@@ -550,7 +514,7 @@ def find_and_set_infoLabels(item):
|
||||
return False
|
||||
|
||||
|
||||
def get_nfo(item):
|
||||
def get_nfo(item, search_groups=False):
|
||||
"""
|
||||
Returns the information necessary for the result to be scraped into the kodi video library, for tmdb it works only by passing it the url.
|
||||
@param item: element that contains the data necessary to generate the info
|
||||
@@ -558,6 +522,23 @@ def get_nfo(item):
|
||||
@rtype: str
|
||||
@return:
|
||||
"""
|
||||
|
||||
if search_groups:
|
||||
from platformcode.autorenumber import RENUMBER, GROUP
|
||||
path = filetools.join(config.get_data_path(), "settings_channels", item.channel + "_data.json")
|
||||
if filetools.exists(path):
|
||||
g = jsontools.load(filetools.read(path)).get(RENUMBER,{}).get(item.fulltitle.strip(),{}).get(GROUP,'')
|
||||
if g: return g + '\n'
|
||||
|
||||
groups = get_groups(item)
|
||||
|
||||
if groups:
|
||||
Id = select_group(groups)
|
||||
if Id:
|
||||
info_nfo = 'https://www.themoviedb.org/tv/{}/episode_group/{}\n'.format(item.infoLabels['tmdb_id'], Id)
|
||||
return info_nfo
|
||||
else: return
|
||||
|
||||
if "season" in item.infoLabels and "episode" in item.infoLabels:
|
||||
info_nfo = "https://www.themoviedb.org/tv/%s/season/%s/episode/%s\n" % (item.infoLabels['tmdb_id'], item.contentSeason, item.contentEpisodeNumber)
|
||||
else:
|
||||
@@ -565,6 +546,34 @@ def get_nfo(item):
|
||||
|
||||
return info_nfo
|
||||
|
||||
def get_groups(item):
|
||||
url = 'https://api.themoviedb.org/3/tv/{}/episode_groups?api_key=a1ab8b8669da03637a4b98fa39c39228&language={}'.format(item.infoLabels['tmdb_id'], def_lang)
|
||||
groups = requests.get(url).json().get('results',[])
|
||||
return groups
|
||||
|
||||
def select_group(groups):
|
||||
selected = -1
|
||||
selections = []
|
||||
ids = []
|
||||
for group in groups:
|
||||
name = '[B]{}[/B] Seasons: {} Episodes: {}'.format(group.get('name',''), group.get('group_count',''), group.get('episode_count',''))
|
||||
description = group.get('description','')
|
||||
if description:
|
||||
name = '{}\n{}'.format(name, description)
|
||||
ID = group.get('id','')
|
||||
if ID:
|
||||
selections.append(name)
|
||||
ids.append(ID)
|
||||
if selections and ids:
|
||||
selected = platformtools.dialog_select_group(config.get_localized_string(70831), selections)
|
||||
if selected > -1:
|
||||
return ids[selected]
|
||||
return ''
|
||||
|
||||
def get_group(Id):
|
||||
url = 'https://api.themoviedb.org/3/tv/episode_group/{}?api_key=a1ab8b8669da03637a4b98fa39c39228&language={}'.format(Id, def_lang)
|
||||
group = requests.get(url).json().get('groups',[])
|
||||
return group
|
||||
|
||||
def completar_codigos(item):
|
||||
"""
|
||||
@@ -1009,6 +1018,11 @@ class Tmdb(object):
|
||||
% (buscando, len(results), page, index_results))
|
||||
return 0
|
||||
|
||||
# We sort result based on fuzzy match to detect most similar
|
||||
if len(results) > 1:
|
||||
from lib.fuzzy_match import algorithims
|
||||
results.sort(key=lambda r: algorithims.trigram(text_simple, r['title'] if self.busqueda_tipo == 'movie' else r['name']), reverse=True)
|
||||
|
||||
# We return the number of results of this page
|
||||
self.results = results
|
||||
self.total_results = total_results
|
||||
@@ -1430,6 +1444,11 @@ class Tmdb(object):
|
||||
|
||||
return ret_dic
|
||||
|
||||
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 []
|
||||
|
||||
def get_videos(self):
|
||||
"""
|
||||
:return: Returns an ordered list (language / resolution / type) of Dict objects in which each of its elements corresponds to a trailer, teaser or clip from youtube.
|
||||
|
||||
+1
-1
@@ -298,7 +298,7 @@ def set_infoLabels_item(item):
|
||||
return len(item.infoLabels)
|
||||
|
||||
|
||||
def get_nfo(item):
|
||||
def get_nfo(item, search_groups=False):
|
||||
"""
|
||||
Returns the information necessary for the result to be scraped into the kodi video library,
|
||||
|
||||
|
||||
+32
-27
@@ -14,6 +14,7 @@ from core import filetools, scraper, scrapertools
|
||||
from core.item import Item
|
||||
from lib import generictools
|
||||
from platformcode import config, logger, platformtools
|
||||
from platformcode.autorenumber import RENUMBER
|
||||
|
||||
FOLDER_MOVIES = config.get_setting("folder_movies")
|
||||
FOLDER_TVSHOWS = config.get_setting("folder_tvshows")
|
||||
@@ -234,14 +235,14 @@ def update_renumber_options(item, head_nfo, path):
|
||||
if filetools.isfile(tvshow_path) and item.channel_prefs:
|
||||
for channel in item.channel_prefs:
|
||||
filename = filetools.join(config.get_data_path(), "settings_channels", channel + '_data.json')
|
||||
|
||||
json_file = jsontools.load(filetools.read(filename))
|
||||
if 'TVSHOW_AUTORENUMBER' in json_file:
|
||||
json = json_file['TVSHOW_AUTORENUMBER']
|
||||
if item.fulltitle in json:
|
||||
item.channel_prefs[channel]['TVSHOW_AUTORENUMBER'] = json[item.fulltitle]
|
||||
logger.debug('UPDATED=\n' + str(item.channel_prefs))
|
||||
filetools.write(tvshow_path, head_nfo + item.tojson())
|
||||
if filetools.isfile(filename):
|
||||
json_file = jsontools.load(filetools.read(filename))
|
||||
if RENUMBER in json_file:
|
||||
json = json_file[RENUMBER]
|
||||
if item.fulltitle in json:
|
||||
item.channel_prefs[channel][RENUMBER] = json[item.fulltitle]
|
||||
logger.debug('UPDATED=\n' + str(item.channel_prefs))
|
||||
filetools.write(tvshow_path, head_nfo + item.tojson())
|
||||
|
||||
def add_renumber_options(item, head_nfo, path):
|
||||
from core import jsontools
|
||||
@@ -249,8 +250,8 @@ def add_renumber_options(item, head_nfo, path):
|
||||
ret = None
|
||||
filename = filetools.join(config.get_data_path(), "settings_channels", item.channel + '_data.json')
|
||||
json_file = jsontools.load(filetools.read(filename))
|
||||
if 'TVSHOW_AUTORENUMBER' in json_file:
|
||||
json = json_file['TVSHOW_AUTORENUMBER']
|
||||
if RENUMBER in json_file:
|
||||
json = json_file[RENUMBER]
|
||||
if item.fulltitle in json:
|
||||
ret = json[item.fulltitle]
|
||||
return ret
|
||||
@@ -258,11 +259,11 @@ def add_renumber_options(item, head_nfo, path):
|
||||
def check_renumber_options(item):
|
||||
from platformcode.autorenumber import load, write
|
||||
for key in item.channel_prefs:
|
||||
if 'TVSHOW_AUTORENUMBER' in item.channel_prefs[key]:
|
||||
if RENUMBER in item.channel_prefs[key]:
|
||||
item.channel = key
|
||||
json = load(item)
|
||||
if not json or item.fulltitle not in json:
|
||||
json[item.fulltitle] = item.channel_prefs[key]['TVSHOW_AUTORENUMBER']
|
||||
json[item.fulltitle] = item.channel_prefs[key][RENUMBER]
|
||||
write(item, json)
|
||||
|
||||
# head_nfo, tvshow_item = read_nfo(filetools.join(item.context[0]['nfo']))
|
||||
@@ -275,7 +276,7 @@ def filter_list(episodelist, action=None, path=None):
|
||||
# if xbmc.getCondVisibility('system.platform.windows') > 0: path = path.replace('smb:','').replace('/','\\')
|
||||
channel_prefs = {}
|
||||
lang_sel = quality_sel = show_title = channel =''
|
||||
# from core.support import dbg;dbg()
|
||||
|
||||
if action:
|
||||
tvshow_path = filetools.join(path, "tvshow.nfo")
|
||||
head_nfo, tvshow_item = read_nfo(tvshow_path)
|
||||
@@ -294,7 +295,7 @@ def filter_list(episodelist, action=None, path=None):
|
||||
|
||||
renumber = add_renumber_options(episodelist[0], head_nfo, tvshow_path)
|
||||
if renumber:
|
||||
channel_prefs['TVSHOW_AUTORENUMBER'] = renumber
|
||||
channel_prefs[RENUMBER] = renumber
|
||||
|
||||
if action == 'get_seasons':
|
||||
if 'favourite_language' not in channel_prefs:
|
||||
@@ -499,7 +500,9 @@ def save_tvshow(item, episodelist, silent=False):
|
||||
if not filetools.exists(tvshow_path):
|
||||
# We create tvshow.nfo, if it does not exist, with the head_nfo, series info and watched episode marks
|
||||
logger.debug("Creating tvshow.nfo: " + tvshow_path)
|
||||
head_nfo = scraper.get_nfo(item)
|
||||
head_nfo = scraper.get_nfo(item, search_groups=True)
|
||||
if not head_nfo:
|
||||
return 0, 0, 0, ''
|
||||
item.infoLabels['mediatype'] = "tvshow"
|
||||
item.infoLabels['title'] = item.contentSerieName
|
||||
item_tvshow = Item(title=item.contentSerieName, channel="videolibrary", action="get_seasons",
|
||||
@@ -1004,21 +1007,20 @@ def add_movie(item):
|
||||
# If you do it in "Enter another name", TMDB will automatically search for the new title
|
||||
# If you do it in "Complete Information", it partially changes to the new title, but does not search TMDB. We have to do it
|
||||
# If the second screen is canceled, the variable "scraper_return" will be False. The user does not want to continue
|
||||
|
||||
item = generictools.update_title(item) # We call the method that updates the title with tmdb.find_and_set_infoLabels
|
||||
#if item.tmdb_stat:
|
||||
# del item.tmdb_stat # We clean the status so that it is not recorded in the Video Library
|
||||
# if item:
|
||||
new_item = item.clone(action="findvideos")
|
||||
insertados, sobreescritos, fallidos, path = save_movie(new_item)
|
||||
if item:
|
||||
new_item = item.clone(action="findvideos")
|
||||
insertados, sobreescritos, fallidos, path = save_movie(new_item)
|
||||
|
||||
if fallidos == 0:
|
||||
platformtools.dialog_ok(config.get_localized_string(30131),
|
||||
config.get_localized_string(30135) % new_item.contentTitle) # 'has been added to the video library'
|
||||
else:
|
||||
filetools.rmdirtree(path)
|
||||
platformtools.dialog_ok(config.get_localized_string(30131),
|
||||
config.get_localized_string(60066) % new_item.contentTitle) # "ERROR, the movie has NOT been added to the video library")
|
||||
if fallidos == 0:
|
||||
platformtools.dialog_ok(config.get_localized_string(30131),
|
||||
config.get_localized_string(30135) % new_item.contentTitle) # 'has been added to the video library'
|
||||
else:
|
||||
filetools.rmdirtree(path)
|
||||
platformtools.dialog_ok(config.get_localized_string(30131),
|
||||
config.get_localized_string(60066) % new_item.contentTitle) # "ERROR, the movie has NOT been added to the video library")
|
||||
|
||||
|
||||
def add_tvshow(item, channel=None):
|
||||
@@ -1100,7 +1102,10 @@ def add_tvshow(item, channel=None):
|
||||
magnet_caching = False
|
||||
insertados, sobreescritos, fallidos, path = save_tvshow(item, itemlist)
|
||||
|
||||
if not insertados and not sobreescritos and not fallidos:
|
||||
if not path:
|
||||
pass
|
||||
|
||||
elif not insertados and not sobreescritos and not fallidos:
|
||||
filetools.rmdirtree(path)
|
||||
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60067) % item.show)
|
||||
logger.error("The string %s could not be added to the video library. Could not get any episode" % item.show)
|
||||
|
||||
+2
-3
@@ -27,6 +27,5 @@ from platformcode import launcher
|
||||
|
||||
if sys.argv[2] == "":
|
||||
launcher.start()
|
||||
launcher.run()
|
||||
else:
|
||||
launcher.run()
|
||||
|
||||
launcher.run()
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# __init__.py
|
||||
|
||||
# Version of the fuzzy-match package
|
||||
__version__ = "0.0.1"
|
||||
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
import math
|
||||
from math import floor, ceil
|
||||
import re
|
||||
from collections import Counter
|
||||
# import numpy as np
|
||||
|
||||
|
||||
|
||||
def find_ngrams(string, split_num=3):
|
||||
"""
|
||||
Slice string into ngrams.
|
||||
Returns array of ngrams for the given string.
|
||||
|
||||
Arguments:
|
||||
text: the string to find ngrams for.
|
||||
split_num: the length the ngrams should be. Defaults to 3 (trigrams).
|
||||
"""
|
||||
try:
|
||||
if not string:
|
||||
return set()
|
||||
|
||||
words = [' {} '.format(x) for x in re.split(r'\W+', str(string).lower()) if x.strip()]
|
||||
|
||||
ngrams = set()
|
||||
|
||||
for word in words:
|
||||
for x in range(0, len(word) - split_num + 1):
|
||||
ngrams.add(word[x:x+split_num])
|
||||
|
||||
return ngrams
|
||||
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def trigram(text1, text2, split_num=3):
|
||||
"""
|
||||
Find the similarity between two strings using ngrams.
|
||||
Returns float score value, 0.0 being completely different strings and 1.0 being equal strings.
|
||||
|
||||
Arguments:
|
||||
text1: main string to compare against.
|
||||
text2: second string to compare to text1.
|
||||
split_num: the length the ngrams should be. Defaults to 3 (trigrams).
|
||||
"""
|
||||
try:
|
||||
ngrams1 = find_ngrams(text1, split_num)
|
||||
ngrams2 = find_ngrams(text2, split_num)
|
||||
|
||||
num_unique = len(ngrams1 | ngrams2)
|
||||
num_equal = len(ngrams1 & ngrams2)
|
||||
|
||||
score = round(float(num_equal) / float(num_unique), 6)
|
||||
|
||||
return score
|
||||
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def cosine(text1, text2):
|
||||
"""
|
||||
Find the similarity between two strings using cosine vectors.
|
||||
Returns float score value, 0.0 being completely different strings and 1.0 being equal strings.
|
||||
|
||||
Arguments:
|
||||
text1: main string to compare against.
|
||||
text2: second string to compare to text1.
|
||||
"""
|
||||
try:
|
||||
vec1 = Counter(re.compile(r"\w+").findall(text1))
|
||||
vec2 = Counter(re.compile(r"\w+").findall(text2))
|
||||
intersection = set(vec1.keys()) & set(vec2.keys())
|
||||
numerator = sum([vec1[x] * vec2[x] for x in intersection])
|
||||
|
||||
sum1 = sum([vec1[x] ** 2 for x in list(vec1.keys())])
|
||||
sum2 = sum([vec2[x] ** 2 for x in list(vec2.keys())])
|
||||
denominator = math.sqrt(sum1) * math.sqrt(sum2)
|
||||
|
||||
if not denominator:
|
||||
return 0.0
|
||||
else:
|
||||
return float(numerator) / denominator
|
||||
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def levenshtein(text1, text2):
|
||||
"""
|
||||
Find the similarity between two strings using Levenshtein distance.
|
||||
Returns float score value, 0.0 being completely different strings and 1.0 being equal strings.
|
||||
|
||||
Arguments:
|
||||
text1: main string to compare against.
|
||||
text2: second string to compare to text1.
|
||||
"""
|
||||
try:
|
||||
size_x = len(text1) + 1
|
||||
size_y = len(text2) + 1
|
||||
matrix = np.zeros ((size_x, size_y))
|
||||
for x in range(size_x):
|
||||
matrix [x, 0] = x
|
||||
for y in range(size_y):
|
||||
matrix [0, y] = y
|
||||
|
||||
for x in range(1, size_x):
|
||||
for y in range(1, size_y):
|
||||
if text1[x-1] == text2[y-1]:
|
||||
matrix [x,y] = min(
|
||||
matrix[x-1, y] + 1,
|
||||
matrix[x-1, y-1],
|
||||
matrix[x, y-1] + 1
|
||||
)
|
||||
else:
|
||||
matrix [x,y] = min(
|
||||
matrix[x-1,y] + 1,
|
||||
matrix[x-1,y-1] + 1,
|
||||
matrix[x,y-1] + 1
|
||||
)
|
||||
distance = matrix[size_x - 1, size_y - 1]
|
||||
score = (max(len(text1), len(text2)) - distance) / max(len(text1), len(text2))
|
||||
return float(score)
|
||||
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def jaro_winkler(s1, s2):
|
||||
"""
|
||||
Find the similarity between two strings using Jaro-Winkler distance.
|
||||
Returns float score value, 0.0 being completely different strings and 1.0 being equal strings.
|
||||
|
||||
Arguments:
|
||||
text1: main string to compare against.
|
||||
text2: second string to compare to text1.
|
||||
"""
|
||||
try:
|
||||
if (s1 == s2):
|
||||
return 1.0
|
||||
|
||||
len1 = len(s1)
|
||||
len2 = len(s2)
|
||||
max_dist = floor(max(len1, len2) / 2) - 1
|
||||
match = 0
|
||||
hash_s1 = [0] * len(s1)
|
||||
hash_s2 = [0] * len(s2)
|
||||
|
||||
for i in range(len1):
|
||||
for j in range(max(0, i - max_dist),
|
||||
min(len2, i + max_dist + 1)):
|
||||
|
||||
if (s1[i] == s2[j] and hash_s2[j] == 0):
|
||||
hash_s1[i] = 1
|
||||
hash_s2[j] = 1
|
||||
match += 1
|
||||
break
|
||||
|
||||
if (match == 0):
|
||||
return 0.0
|
||||
|
||||
t = 0
|
||||
point = 0
|
||||
|
||||
for i in range(len1):
|
||||
if (hash_s1[i]):
|
||||
|
||||
while (hash_s2[point] == 0):
|
||||
point += 1
|
||||
|
||||
if (s1[i] != s2[point]):
|
||||
point += 1
|
||||
t += 1
|
||||
t = t//2
|
||||
|
||||
return float(match/ len1 + match / len2 +
|
||||
(match - t + 1) / match)/ 3.0
|
||||
except:
|
||||
return None
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
import heapq
|
||||
from . import algorithims
|
||||
|
||||
|
||||
|
||||
def extract(query, choices, match_type='trigram', score_cutoff=0, limit=5):
|
||||
"""
|
||||
Find the similarity between a query item and a list of choices.
|
||||
Returns a tuple of all choices and their associated similarity score.
|
||||
|
||||
Arguments:
|
||||
query: The string you are wanting to match.
|
||||
choices: An iterable or dictionary-like object containing choices
|
||||
to be matched against the query.
|
||||
score_cutoff: Optional argument for score threshold. If the best
|
||||
match is found, but it is not greater than this number, then
|
||||
return None anyway ("not a good enough match"). Defaults to 0.
|
||||
|
||||
"""
|
||||
try:
|
||||
if match_type == 'trigram':
|
||||
match_type = algorithims.trigram
|
||||
elif match_type == 'levenshtein':
|
||||
match_type = algorithims.levenshtein
|
||||
elif match_type == 'cosine':
|
||||
match_type = algorithims.cosine
|
||||
elif match_type == 'jaro_winkler':
|
||||
match_type = algorithims.jaro_winkler
|
||||
try:
|
||||
if choices is None or len(choices) == 0:
|
||||
return
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
results = []
|
||||
|
||||
for i in choices:
|
||||
score = (match_type(query, i))
|
||||
data = (i, score)
|
||||
if score >= score_cutoff:
|
||||
results.append(data)
|
||||
|
||||
|
||||
return heapq.nlargest(limit, results, key=lambda i: i[1]) if limit is not None else \
|
||||
sorted(results, key=lambda i: i[1], reverse=True)
|
||||
|
||||
# return results
|
||||
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def extractOne(query, choices, match_type='trigram', score_cutoff=0):
|
||||
"""
|
||||
Finds the most similar item to query item from a list of choices.
|
||||
Returns tuple of best choice and its associated similarity score.
|
||||
|
||||
Arguments:
|
||||
query: The string you are wanting to match.
|
||||
choices: An iterable or dictionary-like object containing choices
|
||||
to be matched against the query.
|
||||
score_cutoff: Optional argument for score threshold. If the best
|
||||
match is found, but it is not greater than this number, then
|
||||
return None anyway ("not a good enough match"). Defaults to 0.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
best_list = extract(query, choices, match_type, score_cutoff)
|
||||
|
||||
best = max(best_list, key=lambda i: i[1])
|
||||
|
||||
return best
|
||||
|
||||
except:
|
||||
return None
|
||||
+29
-9
@@ -13,7 +13,10 @@ import re
|
||||
import time
|
||||
|
||||
import requests
|
||||
from platformcode import logger
|
||||
try:
|
||||
from platformcode import logger
|
||||
except ImportError:
|
||||
logger = None
|
||||
|
||||
HEADERS = {
|
||||
'Host': 'translate.google.com',
|
||||
@@ -21,9 +24,11 @@ HEADERS = {
|
||||
}
|
||||
|
||||
MAX_CONECTION_THREAD = 10
|
||||
SL = 'en'
|
||||
TL = 'it'
|
||||
|
||||
BASE_URL_PROXY = 'https://translate.googleusercontent.com'
|
||||
BASE_URL_TRANSLATE = 'https://translate.google.com/translate?hl=it&sl=en&tl=it&u=[TARGET_URL]&sandbox=0' # noqa: E501
|
||||
BASE_URL_TRANSLATE = 'https://translate.google.com/translate?hl=it&sl=' + SL + '&tl=' + TL + '&u=[TARGET_URL]&sandbox=0' # noqa: E501
|
||||
|
||||
|
||||
def checker_url(html, url):
|
||||
@@ -43,7 +48,10 @@ def process_request_proxy(url):
|
||||
target_url = \
|
||||
BASE_URL_TRANSLATE.replace('[TARGET_URL]', request.quote(url))
|
||||
|
||||
logger.debug(target_url)
|
||||
if logger:
|
||||
logger.debug(target_url)
|
||||
else:
|
||||
print(target_url)
|
||||
|
||||
return_html = requests.get(target_url, timeout=20, headers=HEADERS)
|
||||
|
||||
@@ -52,10 +60,13 @@ def process_request_proxy(url):
|
||||
|
||||
url_request = checker_url(
|
||||
return_html.text,
|
||||
BASE_URL_PROXY + '/translate_p?hl=it&sl=en&tl=it&u='
|
||||
BASE_URL_PROXY + '/translate_p?hl=it&sl=' + SL + '&tl=' + TL + '&u='
|
||||
)
|
||||
|
||||
logger.debug(url_request)
|
||||
if logger:
|
||||
logger.debug(url_request)
|
||||
else:
|
||||
print(url_request)
|
||||
|
||||
request_final = requests.get(
|
||||
url_request,
|
||||
@@ -66,7 +77,10 @@ def process_request_proxy(url):
|
||||
url_request_proxy = checker_url(
|
||||
request_final.text, 'translate.google')
|
||||
|
||||
logger.debug(url_request_proxy)
|
||||
if logger:
|
||||
logger.debug(url_request_proxy)
|
||||
else:
|
||||
print(url_request_proxy)
|
||||
|
||||
data = None
|
||||
result = None
|
||||
@@ -80,12 +94,18 @@ def process_request_proxy(url):
|
||||
data = result.content.decode('utf-8', 'ignore')
|
||||
if not PY3:
|
||||
data = data.encode('utf-8')
|
||||
logger.debug()
|
||||
if logger:
|
||||
logger.debug()
|
||||
|
||||
data = re.sub('\s(\w+)=(?!")([^<>\s]+)', r' \1="\2"', data)
|
||||
data = re.sub('https://translate\.googleusercontent\.com/.*?u=(.*?)&usg=[A-Za-z0-9_-]+', '\\1', data)
|
||||
data = re.sub('https?://[a-zA-Z0-9]+--' + domain.replace('.', '-') + '\.translate\.goog(/[a-zA-Z0-9#/-]+)', 'https://' + domain + '\\1', data)
|
||||
data = re.sub('\s+<', '<', data)
|
||||
data = data.replace('&', '&').replace('https://translate.google.com/website?sl=' + SL + '&tl=' + TL + '&u=', '')
|
||||
|
||||
return {'url': url.strip(), 'result': result, 'data': data.replace('&', '&')}
|
||||
return {'url': url.strip(), 'result': result, 'data': data}
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
if logger:
|
||||
logger.error(e)
|
||||
else:
|
||||
print(e)
|
||||
|
||||
@@ -0,0 +1,601 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This code is distributed under the terms and conditions
|
||||
# from the Apache License, Version 2.0
|
||||
#
|
||||
# http://opensource.org/licenses/apache2.0.php
|
||||
#
|
||||
# This code was inspired by:
|
||||
# * http://code.activestate.com/recipes/576638-draft-for-an-sqlite3-based-dbm/
|
||||
# * http://code.activestate.com/recipes/526618/
|
||||
|
||||
"""
|
||||
A lightweight wrapper around Python's sqlite3 database, with a dict-like interface
|
||||
and multi-thread access support::
|
||||
|
||||
>>> mydict = SqliteDict('some.db', autocommit=True) # the mapping will be persisted to file `some.db`
|
||||
>>> mydict['some_key'] = any_picklable_object
|
||||
>>> print mydict['some_key']
|
||||
>>> print len(mydict) # etc... all dict functions work
|
||||
|
||||
Pickle is used internally to serialize the values. Keys are strings.
|
||||
|
||||
If you don't use autocommit (default is no autocommit for performance), then
|
||||
don't forget to call `mydict.commit()` when done with a transaction.
|
||||
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from threading import Thread
|
||||
|
||||
__version__ = '1.7.0.dev0'
|
||||
|
||||
major_version = sys.version_info[0]
|
||||
if major_version < 3: # py <= 2.x
|
||||
if sys.version_info[1] < 5: # py <= 2.4
|
||||
raise ImportError("sqlitedict requires python 2.5 or higher (python 3.3 or higher supported)")
|
||||
|
||||
# necessary to use exec()_ as this would be a SyntaxError in python3.
|
||||
# this is an exact port of six.reraise():
|
||||
def exec_(_code_, _globs_=None, _locs_=None):
|
||||
"""Execute code in a namespace."""
|
||||
if _globs_ is None:
|
||||
frame = sys._getframe(1)
|
||||
_globs_ = frame.f_globals
|
||||
if _locs_ is None:
|
||||
_locs_ = frame.f_locals
|
||||
del frame
|
||||
elif _locs_ is None:
|
||||
_locs_ = _globs_
|
||||
exec("""exec _code_ in _globs_, _locs_""")
|
||||
|
||||
class TimeoutError(OSError):
|
||||
pass
|
||||
|
||||
exec_("def reraise(tp, value, tb=None):\n"
|
||||
" raise tp, value, tb\n")
|
||||
else:
|
||||
def reraise(tp, value, tb=None):
|
||||
if value is None:
|
||||
value = tp()
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
try:
|
||||
from cPickle import dumps, loads, HIGHEST_PROTOCOL as PICKLE_PROTOCOL
|
||||
except ImportError:
|
||||
from pickle import dumps, loads, HIGHEST_PROTOCOL as PICKLE_PROTOCOL
|
||||
|
||||
# some Python 3 vs 2 imports
|
||||
try:
|
||||
from collections import UserDict as DictClass
|
||||
except ImportError:
|
||||
from UserDict import DictMixin as DictClass
|
||||
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def open(*args, **kwargs):
|
||||
"""See documentation of the SqliteDict class."""
|
||||
return SqliteDict(*args, **kwargs)
|
||||
|
||||
|
||||
def encode(obj):
|
||||
"""Serialize an object using pickle to a binary format accepted by SQLite."""
|
||||
return sqlite3.Binary(dumps(obj, protocol=PICKLE_PROTOCOL))
|
||||
|
||||
|
||||
def decode(obj):
|
||||
"""Deserialize objects retrieved from SQLite."""
|
||||
return loads(bytes(obj))
|
||||
|
||||
|
||||
class SqliteDict(DictClass):
|
||||
VALID_FLAGS = ['c', 'r', 'w', 'n']
|
||||
|
||||
def __init__(self, filename=None, tablename='unnamed', flag='c',
|
||||
autocommit=False, journal_mode="DELETE", encode=encode, decode=decode, timeout=5):
|
||||
"""
|
||||
Initialize a thread-safe sqlite-backed dictionary. The dictionary will
|
||||
be a table `tablename` in database file `filename`. A single file (=database)
|
||||
may contain multiple tables.
|
||||
|
||||
If no `filename` is given, a random file in temp will be used (and deleted
|
||||
from temp once the dict is closed/deleted).
|
||||
|
||||
If you enable `autocommit`, changes will be committed after each operation
|
||||
(more inefficient but safer). Otherwise, changes are committed on `self.commit()`,
|
||||
`self.clear()` and `self.close()`.
|
||||
|
||||
Set `journal_mode` to 'OFF' if you're experiencing sqlite I/O problems
|
||||
or if you need performance and don't care about crash-consistency.
|
||||
|
||||
The `flag` parameter. Exactly one of:
|
||||
'c': default mode, open for read/write, creating the db/table if necessary.
|
||||
'w': open for r/w, but drop `tablename` contents first (start with empty table)
|
||||
'r': open as read-only
|
||||
'n': create a new database (erasing any existing tables, not just `tablename`!).
|
||||
|
||||
The `encode` and `decode` parameters are used to customize how the values
|
||||
are serialized and deserialized.
|
||||
The `encode` parameter must be a function that takes a single Python
|
||||
object and returns a serialized representation.
|
||||
The `decode` function must be a function that takes the serialized
|
||||
representation produced by `encode` and returns a deserialized Python
|
||||
object.
|
||||
The default is to use pickle.
|
||||
|
||||
The `timeout` defines the maximum time (in seconds) to wait for initial Thread startup.
|
||||
|
||||
"""
|
||||
self.in_temp = filename is None
|
||||
if self.in_temp:
|
||||
fd, filename = tempfile.mkstemp(prefix='sqldict')
|
||||
os.close(fd)
|
||||
|
||||
if flag not in SqliteDict.VALID_FLAGS:
|
||||
raise RuntimeError("Unrecognized flag: %s" % flag)
|
||||
self.flag = flag
|
||||
|
||||
if flag == 'n':
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
|
||||
dirname = os.path.dirname(filename)
|
||||
if dirname:
|
||||
if not os.path.exists(dirname):
|
||||
raise RuntimeError('Error! The directory does not exist, %s' % dirname)
|
||||
|
||||
self.filename = filename
|
||||
|
||||
# Use standard SQL escaping of double quote characters in identifiers, by doubling them.
|
||||
# See https://github.com/RaRe-Technologies/sqlitedict/pull/113
|
||||
self.tablename = tablename.replace('"', '""')
|
||||
|
||||
self.autocommit = autocommit
|
||||
self.journal_mode = journal_mode
|
||||
self.encode = encode
|
||||
self.decode = decode
|
||||
self.timeout = timeout
|
||||
|
||||
logger.info("opening Sqlite table %r in %r" % (tablename, filename))
|
||||
self.conn = self._new_conn()
|
||||
if self.flag == 'r':
|
||||
if self.tablename not in SqliteDict.get_tablenames(self.filename):
|
||||
msg = 'Refusing to create a new table "%s" in read-only DB mode' % tablename
|
||||
raise RuntimeError(msg)
|
||||
else:
|
||||
MAKE_TABLE = 'CREATE TABLE IF NOT EXISTS "%s" (key TEXT PRIMARY KEY, value BLOB)' % self.tablename
|
||||
self.conn.execute(MAKE_TABLE)
|
||||
self.conn.commit()
|
||||
if flag == 'w':
|
||||
self.clear()
|
||||
|
||||
def _new_conn(self):
|
||||
return SqliteMultithread(self.filename, autocommit=self.autocommit, journal_mode=self.journal_mode,
|
||||
timeout=self.timeout)
|
||||
|
||||
def __enter__(self):
|
||||
if not hasattr(self, 'conn') or self.conn is None:
|
||||
self.conn = self._new_conn()
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
self.close()
|
||||
|
||||
def __str__(self):
|
||||
return "SqliteDict(%s)" % (self.filename)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self) # no need of something complex
|
||||
|
||||
def __len__(self):
|
||||
# `select count (*)` is super slow in sqlite (does a linear scan!!)
|
||||
# As a result, len() is very slow too once the table size grows beyond trivial.
|
||||
# We could keep the total count of rows ourselves, by means of triggers,
|
||||
# but that seems too complicated and would slow down normal operation
|
||||
# (insert/delete etc).
|
||||
GET_LEN = 'SELECT COUNT(*) FROM "%s"' % self.tablename
|
||||
rows = self.conn.select_one(GET_LEN)[0]
|
||||
return rows if rows is not None else 0
|
||||
|
||||
def __bool__(self):
|
||||
# No elements is False, otherwise True
|
||||
GET_MAX = 'SELECT MAX(ROWID) FROM "%s"' % self.tablename
|
||||
m = self.conn.select_one(GET_MAX)[0]
|
||||
# Explicit better than implicit and bla bla
|
||||
return True if m is not None else False
|
||||
|
||||
def iterkeys(self):
|
||||
GET_KEYS = 'SELECT key FROM "%s" ORDER BY rowid' % self.tablename
|
||||
for key in self.conn.select(GET_KEYS):
|
||||
yield key[0]
|
||||
|
||||
def itervalues(self):
|
||||
GET_VALUES = 'SELECT value FROM "%s" ORDER BY rowid' % self.tablename
|
||||
for value in self.conn.select(GET_VALUES):
|
||||
yield self.decode(value[0])
|
||||
|
||||
def iteritems(self):
|
||||
GET_ITEMS = 'SELECT key, value FROM "%s" ORDER BY rowid' % self.tablename
|
||||
for key, value in self.conn.select(GET_ITEMS):
|
||||
yield key, self.decode(value)
|
||||
|
||||
def keys(self):
|
||||
return self.iterkeys() if major_version > 2 else list(self.iterkeys())
|
||||
|
||||
def values(self):
|
||||
return self.itervalues() if major_version > 2 else list(self.itervalues())
|
||||
|
||||
def items(self):
|
||||
return self.iteritems() if major_version > 2 else list(self.iteritems())
|
||||
|
||||
def __contains__(self, key):
|
||||
HAS_ITEM = 'SELECT 1 FROM "%s" WHERE key = ?' % self.tablename
|
||||
return self.conn.select_one(HAS_ITEM, (key,)) is not None
|
||||
|
||||
def __getitem__(self, key):
|
||||
GET_ITEM = 'SELECT value FROM "%s" WHERE key = ?' % self.tablename
|
||||
item = self.conn.select_one(GET_ITEM, (key,))
|
||||
if item is None:
|
||||
raise KeyError(key)
|
||||
return self.decode(item[0])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if self.flag == 'r':
|
||||
raise RuntimeError('Refusing to write to read-only SqliteDict')
|
||||
|
||||
ADD_ITEM = 'REPLACE INTO "%s" (key, value) VALUES (?,?)' % self.tablename
|
||||
self.conn.execute(ADD_ITEM, (key, self.encode(value)))
|
||||
if self.autocommit:
|
||||
self.commit()
|
||||
|
||||
def __delitem__(self, key):
|
||||
if self.flag == 'r':
|
||||
raise RuntimeError('Refusing to delete from read-only SqliteDict')
|
||||
|
||||
if key not in self:
|
||||
raise KeyError(key)
|
||||
DEL_ITEM = 'DELETE FROM "%s" WHERE key = ?' % self.tablename
|
||||
self.conn.execute(DEL_ITEM, (key,))
|
||||
if self.autocommit:
|
||||
self.commit()
|
||||
|
||||
def update(self, items=(), **kwds):
|
||||
if self.flag == 'r':
|
||||
raise RuntimeError('Refusing to update read-only SqliteDict')
|
||||
|
||||
try:
|
||||
items = items.items()
|
||||
except AttributeError:
|
||||
pass
|
||||
items = [(k, self.encode(v)) for k, v in items]
|
||||
|
||||
UPDATE_ITEMS = 'REPLACE INTO "%s" (key, value) VALUES (?, ?)' % self.tablename
|
||||
self.conn.executemany(UPDATE_ITEMS, items)
|
||||
if kwds:
|
||||
self.update(kwds)
|
||||
if self.autocommit:
|
||||
self.commit()
|
||||
|
||||
def __iter__(self):
|
||||
return self.iterkeys()
|
||||
|
||||
def clear(self):
|
||||
if self.flag == 'r':
|
||||
raise RuntimeError('Refusing to clear read-only SqliteDict')
|
||||
|
||||
# avoid VACUUM, as it gives "OperationalError: database schema has changed"
|
||||
CLEAR_ALL = 'DELETE FROM "%s";' % self.tablename
|
||||
self.conn.commit()
|
||||
self.conn.execute(CLEAR_ALL)
|
||||
self.conn.commit()
|
||||
|
||||
@staticmethod
|
||||
def get_tablenames(filename):
|
||||
"""get the names of the tables in an sqlite db as a list"""
|
||||
if not os.path.isfile(filename):
|
||||
raise IOError('file %s does not exist' % (filename))
|
||||
GET_TABLENAMES = 'SELECT name FROM sqlite_master WHERE type="table"'
|
||||
with sqlite3.connect(filename) as conn:
|
||||
cursor = conn.execute(GET_TABLENAMES)
|
||||
res = cursor.fetchall()
|
||||
|
||||
return [name[0] for name in res]
|
||||
|
||||
def commit(self, blocking=True):
|
||||
"""
|
||||
Persist all data to disk.
|
||||
|
||||
When `blocking` is False, the commit command is queued, but the data is
|
||||
not guaranteed persisted (default implication when autocommit=True).
|
||||
"""
|
||||
if self.conn is not None:
|
||||
self.conn.commit(blocking)
|
||||
sync = commit
|
||||
|
||||
def close(self, do_log=True, force=False):
|
||||
if do_log:
|
||||
logger.debug("closing %s" % self)
|
||||
if hasattr(self, 'conn') and self.conn is not None:
|
||||
if self.conn.autocommit and not force:
|
||||
# typically calls to commit are non-blocking when autocommit is
|
||||
# used. However, we need to block on close() to ensure any
|
||||
# awaiting exceptions are handled and that all data is
|
||||
# persisted to disk before returning.
|
||||
self.conn.commit(blocking=True)
|
||||
self.conn.close(force=force)
|
||||
self.conn = None
|
||||
if self.in_temp:
|
||||
try:
|
||||
os.remove(self.filename)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def terminate(self):
|
||||
"""Delete the underlying database file. Use with care."""
|
||||
if self.flag == 'r':
|
||||
raise RuntimeError('Refusing to terminate read-only SqliteDict')
|
||||
|
||||
self.close()
|
||||
|
||||
if self.filename == ':memory:':
|
||||
return
|
||||
|
||||
logger.info("deleting %s" % self.filename)
|
||||
try:
|
||||
if os.path.isfile(self.filename):
|
||||
os.remove(self.filename)
|
||||
except (OSError, IOError):
|
||||
logger.exception("failed to delete %s" % (self.filename))
|
||||
|
||||
def __del__(self):
|
||||
# like close(), but assume globals are gone by now (do not log!)
|
||||
try:
|
||||
self.close(do_log=False, force=True)
|
||||
except Exception:
|
||||
# prevent error log flood in case of multiple SqliteDicts
|
||||
# closed after connection lost (exceptions are always ignored
|
||||
# in __del__ method.
|
||||
pass
|
||||
|
||||
|
||||
# Adding extra methods for python 2 compatibility (at import time)
|
||||
if major_version == 2:
|
||||
SqliteDict.__nonzero__ = SqliteDict.__bool__
|
||||
del SqliteDict.__bool__ # not needed and confusing
|
||||
|
||||
|
||||
class SqliteMultithread(Thread):
|
||||
"""
|
||||
Wrap sqlite connection in a way that allows concurrent requests from multiple threads.
|
||||
|
||||
This is done by internally queueing the requests and processing them sequentially
|
||||
in a separate thread (in the same order they arrived).
|
||||
|
||||
"""
|
||||
def __init__(self, filename, autocommit, journal_mode, timeout):
|
||||
super(SqliteMultithread, self).__init__()
|
||||
self.filename = filename
|
||||
self.autocommit = autocommit
|
||||
self.journal_mode = journal_mode
|
||||
# use request queue of unlimited size
|
||||
self.reqs = Queue()
|
||||
self.setDaemon(True) # python2.5-compatible
|
||||
self.exception = None
|
||||
self._sqlitedict_thread_initialized = None
|
||||
self.timeout = timeout
|
||||
self.log = logging.getLogger('sqlitedict.SqliteMultithread')
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self.autocommit:
|
||||
conn = sqlite3.connect(self.filename, isolation_level=None, check_same_thread=False)
|
||||
else:
|
||||
conn = sqlite3.connect(self.filename, check_same_thread=False)
|
||||
except Exception:
|
||||
self.log.exception("Failed to initialize connection for filename: %s" % self.filename)
|
||||
self.exception = sys.exc_info()
|
||||
raise
|
||||
|
||||
try:
|
||||
conn.execute('PRAGMA journal_mode = %s' % self.journal_mode)
|
||||
conn.text_factory = str
|
||||
cursor = conn.cursor()
|
||||
conn.commit()
|
||||
cursor.execute('PRAGMA synchronous=OFF')
|
||||
except Exception:
|
||||
self.log.exception("Failed to execute PRAGMA statements.")
|
||||
self.exception = sys.exc_info()
|
||||
raise
|
||||
|
||||
self._sqlitedict_thread_initialized = True
|
||||
|
||||
res = None
|
||||
while True:
|
||||
req, arg, res, outer_stack = self.reqs.get()
|
||||
if req == '--close--':
|
||||
assert res, ('--close-- without return queue', res)
|
||||
break
|
||||
elif req == '--commit--':
|
||||
conn.commit()
|
||||
if res:
|
||||
res.put('--no more--')
|
||||
else:
|
||||
try:
|
||||
cursor.execute(req, arg)
|
||||
except Exception:
|
||||
self.exception = (e_type, e_value, e_tb) = sys.exc_info()
|
||||
inner_stack = traceback.extract_stack()
|
||||
|
||||
# An exception occurred in our thread, but we may not
|
||||
# immediately able to throw it in our calling thread, if it has
|
||||
# no return `res` queue: log as level ERROR both the inner and
|
||||
# outer exception immediately.
|
||||
#
|
||||
# Any iteration of res.get() or any next call will detect the
|
||||
# inner exception and re-raise it in the calling Thread; though
|
||||
# it may be confusing to see an exception for an unrelated
|
||||
# statement, an ERROR log statement from the 'sqlitedict.*'
|
||||
# namespace contains the original outer stack location.
|
||||
self.log.error('Inner exception:')
|
||||
for item in traceback.format_list(inner_stack):
|
||||
self.log.error(item)
|
||||
self.log.error('') # deliniate traceback & exception w/blank line
|
||||
for item in traceback.format_exception_only(e_type, e_value):
|
||||
self.log.error(item)
|
||||
|
||||
self.log.error('') # exception & outer stack w/blank line
|
||||
self.log.error('Outer stack:')
|
||||
for item in traceback.format_list(outer_stack):
|
||||
self.log.error(item)
|
||||
self.log.error('Exception will be re-raised at next call.')
|
||||
|
||||
if res:
|
||||
for rec in cursor:
|
||||
res.put(rec)
|
||||
res.put('--no more--')
|
||||
|
||||
if self.autocommit:
|
||||
conn.commit()
|
||||
|
||||
self.log.debug('received: %s, send: --no more--', req)
|
||||
conn.close()
|
||||
res.put('--no more--')
|
||||
|
||||
def check_raise_error(self):
|
||||
"""
|
||||
Check for and raise exception for any previous sqlite query.
|
||||
|
||||
For the `execute*` family of method calls, such calls are non-blocking and any
|
||||
exception raised in the thread cannot be handled by the calling Thread (usually
|
||||
MainThread). This method is called on `close`, and prior to any subsequent
|
||||
calls to the `execute*` methods to check for and raise an exception in a
|
||||
previous call to the MainThread.
|
||||
"""
|
||||
if self.exception:
|
||||
e_type, e_value, e_tb = self.exception
|
||||
|
||||
# clear self.exception, if the caller decides to handle such
|
||||
# exception, we should not repeatedly re-raise it.
|
||||
self.exception = None
|
||||
|
||||
self.log.error('An exception occurred from a previous statement, view '
|
||||
'the logging namespace "sqlitedict" for outer stack.')
|
||||
|
||||
# The third argument to raise is the traceback object, and it is
|
||||
# substituted instead of the current location as the place where
|
||||
# the exception occurred, this is so that when using debuggers such
|
||||
# as `pdb', or simply evaluating the naturally raised traceback, we
|
||||
# retain the original (inner) location of where the exception
|
||||
# occurred.
|
||||
reraise(e_type, e_value, e_tb)
|
||||
|
||||
def execute(self, req, arg=None, res=None):
|
||||
"""
|
||||
`execute` calls are non-blocking: just queue up the request and return immediately.
|
||||
"""
|
||||
self._wait_for_initialization()
|
||||
self.check_raise_error()
|
||||
|
||||
# NOTE: This might be a lot of information to pump into an input
|
||||
# queue, affecting performance. I've also seen earlier versions of
|
||||
# jython take a severe performance impact for throwing exceptions
|
||||
# so often.
|
||||
stack = traceback.extract_stack()[:-1]
|
||||
self.reqs.put((req, arg or tuple(), res, stack))
|
||||
|
||||
def executemany(self, req, items):
|
||||
for item in items:
|
||||
self.execute(req, item)
|
||||
self.check_raise_error()
|
||||
|
||||
def select(self, req, arg=None):
|
||||
"""
|
||||
Unlike sqlite's native select, this select doesn't handle iteration efficiently.
|
||||
|
||||
The result of `select` starts filling up with values as soon as the
|
||||
request is dequeued, and although you can iterate over the result normally
|
||||
(`for res in self.select(): ...`), the entire result will be in memory.
|
||||
"""
|
||||
res = Queue() # results of the select will appear as items in this queue
|
||||
self.execute(req, arg, res)
|
||||
while True:
|
||||
rec = res.get()
|
||||
self.check_raise_error()
|
||||
if rec == '--no more--':
|
||||
break
|
||||
yield rec
|
||||
|
||||
def select_one(self, req, arg=None):
|
||||
"""Return only the first row of the SELECT, or None if there are no matching rows."""
|
||||
try:
|
||||
return next(iter(self.select(req, arg)))
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def commit(self, blocking=True):
|
||||
if blocking:
|
||||
# by default, we await completion of commit() unless
|
||||
# blocking=False. This ensures any available exceptions for any
|
||||
# previous statement are thrown before returning, and that the
|
||||
# data has actually persisted to disk!
|
||||
self.select_one('--commit--')
|
||||
else:
|
||||
# otherwise, we fire and forget as usual.
|
||||
self.execute('--commit--')
|
||||
|
||||
def close(self, force=False):
|
||||
if force:
|
||||
# If a SqliteDict is being killed or garbage-collected, then select_one()
|
||||
# could hang forever because run() might already have exited and therefore
|
||||
# can't process the request. Instead, push the close command to the requests
|
||||
# queue directly. If run() is still alive, it will exit gracefully. If not,
|
||||
# then there's nothing we can do anyway.
|
||||
self.reqs.put(('--close--', None, Queue(), None))
|
||||
else:
|
||||
# we abuse 'select' to "iter" over a "--close--" statement so that we
|
||||
# can confirm the completion of close before joining the thread and
|
||||
# returning (by semaphore '--no more--'
|
||||
self.select_one('--close--')
|
||||
self.join()
|
||||
|
||||
def _wait_for_initialization(self):
|
||||
"""
|
||||
Polls the 'initialized' flag to be set by the started Thread in run().
|
||||
"""
|
||||
# A race condition may occur without waiting for initialization:
|
||||
# __init__() finishes with the start() call, but the Thread needs some time to actually start working.
|
||||
# If opening the database file fails in run(), an exception will occur and self.exception will be set.
|
||||
# But if we run check_raise_error() before run() had a chance to set self.exception, it will report
|
||||
# a false negative: An exception occured and the thread terminates but self.exception is unset.
|
||||
# This leads to a deadlock while waiting for the results of execute().
|
||||
# By waiting for the Thread to set the initialized flag, we can ensure the thread has successfully
|
||||
# opened the file - and possibly set self.exception to be detected by check_raise_error().
|
||||
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < self.timeout:
|
||||
if self._sqlitedict_thread_initialized or self.exception:
|
||||
return
|
||||
time.sleep(0.1)
|
||||
raise TimeoutError("SqliteMultithread failed to flag initialization withing %0.0f seconds." % self.timeout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(__version__)
|
||||
+151
-195
@@ -5,23 +5,22 @@
|
||||
|
||||
|
||||
import xbmc, xbmcgui, re, base64, inspect, sys
|
||||
from core import jsontools, tvdb, scrapertools, filetools
|
||||
from core import jsontools, tmdb, scrapertools, filetools
|
||||
from core.item import Item
|
||||
from core.support import typo, match, dbg, Item
|
||||
from platformcode import config, platformtools, logger
|
||||
PY3 = True if sys.version_info[0] >= 3 else False
|
||||
|
||||
# Json Var
|
||||
TVSHOW_RENUMERATE = "TVSHOW_AUTORENUMBER"
|
||||
ID = "ID"
|
||||
SEASON = "Season"
|
||||
EPISODE = "Episode"
|
||||
SPECIAL = "Special"
|
||||
MODE = "Mode"
|
||||
EPLIST = "EpList"
|
||||
CHECK = "ReCheck"
|
||||
SPLIST = "SpList"
|
||||
TYPE = "Type"
|
||||
RENUMBER = 'TVSHOW_AUTORENUMBER'
|
||||
ID = 'id'
|
||||
SEASONSDICT = 'seasons'
|
||||
SEASON = 'season'
|
||||
EPISODE = 'episode'
|
||||
EPISODES = 'episodes'
|
||||
SPECIALEPISODES = 'specials'
|
||||
MANUALMODE = 'manual'
|
||||
GROUP = 'info'
|
||||
|
||||
# helper Functions
|
||||
def check(item):
|
||||
@@ -41,21 +40,15 @@ def filename(item):
|
||||
|
||||
def load(item):
|
||||
logger.debug()
|
||||
try:
|
||||
json_file = open(filename(item), "r").read()
|
||||
json = jsontools.load(json_file)[TVSHOW_RENUMERATE]
|
||||
|
||||
except:
|
||||
json = {}
|
||||
|
||||
try: json = jsontools.load(open(filename(item), "r").read())[RENUMBER]
|
||||
except: json = {}
|
||||
return json
|
||||
|
||||
|
||||
def write(item, json):
|
||||
logger.debug()
|
||||
json_file = open(filename(item), "r").read()
|
||||
js = jsontools.load(json_file)
|
||||
js[TVSHOW_RENUMERATE] = json
|
||||
js = jsontools.load(open(filename(item), "r").read())
|
||||
js[RENUMBER] = json
|
||||
with open(filename(item), "w") as file:
|
||||
file.write(jsontools.dump(js))
|
||||
file.close()
|
||||
@@ -69,15 +62,6 @@ def b64(json, mode = 'encode'):
|
||||
ret = jsontools.load(base64.b64decode(json))
|
||||
return ret
|
||||
|
||||
def RepresentsInt(s):
|
||||
# Controllo Numro Stagione
|
||||
logger.debug()
|
||||
try:
|
||||
int(s)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def find_episodes(item):
|
||||
logger.debug()
|
||||
ch = __import__('channels.' + item.channel, fromlist=["channels.%s" % item.channel])
|
||||
@@ -88,6 +72,15 @@ def busy(state):
|
||||
if state: xbmc.executebuiltin('ActivateWindow(busydialognocancel)')
|
||||
else: xbmc.executebuiltin('Dialog.Close(busydialognocancel)')
|
||||
|
||||
def RepresentsInt(s):
|
||||
# Controllo Numro Stagione
|
||||
logger.debug()
|
||||
try:
|
||||
int(s)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
# Main
|
||||
def start(itemlist, item=None):
|
||||
if not itemlist: return
|
||||
@@ -106,48 +99,44 @@ class autorenumber():
|
||||
def __init__(self, itemlist, item=None):
|
||||
self.item = item
|
||||
self.itemlist = itemlist
|
||||
self.renumberdict = load(self.itemlist[0]) if self.itemlist else load(item) if item else {}
|
||||
self.selectspecials = False
|
||||
self.manual = False
|
||||
self.auto = False
|
||||
self.dictSeries = load(self.itemlist[0]) if self.itemlist else load(item) if item else {}
|
||||
self.Episodes = {}
|
||||
self.sp = False
|
||||
if self.item:
|
||||
self.auto = config.get_setting('autorenumber', item.channel)
|
||||
self.title = self.item.fulltitle.strip()
|
||||
if match(self.itemlist[0].title, patron=r'[Ss]?(\d+)(?:x|_|\s+)[Ee]?[Pp]?(\d+)').match:
|
||||
item.exit = True
|
||||
return
|
||||
elif self.item.channel in self.item.channel_prefs and TVSHOW_RENUMERATE in self.item.channel_prefs[item.channel] and self.title not in self.dictSeries:
|
||||
elif self.item.channel in self.item.channel_prefs and RENUMBER in self.item.channel_prefs[item.channel] and self.title not in self.renumberdict:
|
||||
from core.videolibrarytools import check_renumber_options
|
||||
from specials.videolibrary import update_videolibrary
|
||||
check_renumber_options(self.item)
|
||||
update_videolibrary(self.item)
|
||||
if self.title in self.dictSeries and ID in self.dictSeries[self.title] and self.dictSeries[self.title][ID] != '0':
|
||||
self.id = self.dictSeries[self.title][ID]
|
||||
self.Episodes = b64(self.dictSeries[self.title][EPISODE], 'decode') if EPISODE in self.dictSeries[self.title] else {}
|
||||
self.Season = self.dictSeries[self.title][SEASON]
|
||||
self.Mode = self.dictSeries[self.title].get(MODE, False)
|
||||
self.Type = self.dictSeries[self.title].get(TYPE, False)
|
||||
if self.item.renumber:
|
||||
self.config()
|
||||
else:
|
||||
self.renumber()
|
||||
self.series = self.renumberdict.get(self.title,{})
|
||||
self.id = self.series.get(ID, 0)
|
||||
self.episodes = self.series.get(EPISODES,{})
|
||||
self.seasonsdict = self.series.get(SEASONSDICT,{})
|
||||
self.season = self.series.get(SEASON, -1)
|
||||
self.episode = self.series.get(EPISODE, -1)
|
||||
self.manual = self.series.get(MANUALMODE, False)
|
||||
self.specials = self.series.get(SPECIALEPISODES, {})
|
||||
if self.id and self.episodes and self.season >= 0 and self.episode >= 0:
|
||||
if self.item.renumber: self.config()
|
||||
else:self.renumber()
|
||||
elif self.auto or self.item.renumber:
|
||||
self.Episodes = {}
|
||||
self.episodes = {}
|
||||
self.config()
|
||||
|
||||
else:
|
||||
for item in self.itemlist:
|
||||
item.context = [{"title": typo(config.get_localized_string(70585), 'bold'),
|
||||
"action": "start",
|
||||
"channel": "autorenumber",
|
||||
"from_channel": item.channel,
|
||||
"from_action": item.action}]
|
||||
"action": "start",
|
||||
"channel": "autorenumber",
|
||||
"from_channel": item.channel,
|
||||
"from_action": item.action}]
|
||||
|
||||
def config(self):
|
||||
self.id = ''
|
||||
if self.title in self.dictSeries:
|
||||
self.id = self.dictSeries[self.title].get(ID,'')
|
||||
|
||||
# Pulizia del Titolo
|
||||
if any( word in self.title.lower() for word in ['specials', 'speciali']):
|
||||
self.title = re.sub(r'\s*specials|\s*speciali', '', self.title.lower())
|
||||
@@ -155,184 +144,149 @@ class autorenumber():
|
||||
self.item.contentSerieName = self.title.rstrip('123456789 ')
|
||||
|
||||
while not self.item.exit:
|
||||
tvdb.find_and_set_infoLabels(self.item)
|
||||
if self.item.infoLabels['tvdb_id']: self.item.exit = True
|
||||
else: self.item = platformtools.dialog_info(self.item, 'tvdb')
|
||||
self.item.infoLabels['tmdb_id'] = ''
|
||||
self.item.infoLabels['year'] = '-'
|
||||
self.item.contentType ='tvshow'
|
||||
tmdb.find_and_set_infoLabels(self.item)
|
||||
if self.item.infoLabels['tmdb_id']: self.item.exit = True
|
||||
else: self.item = platformtools.dialog_info(self.item, 'tmdb')
|
||||
|
||||
# Rinumerazione Automatica
|
||||
if (not self.id and self.auto) or self.item.renumber:
|
||||
self.id = self.item.infoLabels['tvdb_id'] if 'tvdb_id' in self.item.infoLabels else ''
|
||||
self.id = self.item.infoLabels['tmdb_id'] if 'tmdb_id' in self.item.infoLabels else 0
|
||||
if self.id:
|
||||
self.dictRenumber = {ID: self.id}
|
||||
self.dictSeries[self.title] = self.dictRenumber
|
||||
if any(word in self.title.lower() for word in ['specials', 'speciali']): season = '0'
|
||||
elif RepresentsInt(self.title.split()[-1]): season = self.title.split()[-1]
|
||||
else: season = '1'
|
||||
self.Season = self.dictRenumber[SEASON] = season
|
||||
self.series = {ID: self.id}
|
||||
self.renumberdict[self.title] = self.series
|
||||
if any(word in self.title.lower() for word in ['specials', 'speciali']): season = 0
|
||||
elif RepresentsInt(self.title.split()[-1]): season = int(self.title.split()[-1])
|
||||
else: season = 1
|
||||
self.season = self.series[SEASON] = season
|
||||
self.episode = 1
|
||||
self.renumber()
|
||||
|
||||
|
||||
def renumber(self):
|
||||
if not self.item.renumber and self.itemlist:
|
||||
if '|' in self.Season:
|
||||
season = int(self.Season.split('|')[0])
|
||||
addNumber = int(self.Season.split('|')[-1]) - 1
|
||||
else:
|
||||
season = int(self.Season)
|
||||
addNumber = 0
|
||||
for item in self.itemlist:
|
||||
if not match(item.title, patron=r'[Ss]?(\d+)(?:x|_|\s+)[Ee]?[Pp]?(\d+)').match:
|
||||
number = match(item.title, patron=r'(\d+)').match.lstrip('0')
|
||||
if number:
|
||||
if number in self.Episodes:
|
||||
if season > 0: item.title = typo(self.Episodes[number] + ' - ', 'bold') + item.title
|
||||
else: item.title = typo('0x%s - ' % str(int(number) + addNumber), 'bold') + item.title
|
||||
else:
|
||||
self.makelist()
|
||||
if season > 0: item.title = typo(self.Episodes[number] + ' - ', 'bold') + item.title
|
||||
else: item.title = typo('0x%s - ' % str(int(number) + addNumber), 'bold') + item.title
|
||||
if not number in self.episodes: self.makelist()
|
||||
item.title = '{} - {}'.format(typo(self.episodes[number], 'bold'), item.title)
|
||||
else:
|
||||
self.makelist()
|
||||
|
||||
|
||||
def makelist(self):
|
||||
FirstOfSeason= 0
|
||||
self.EpList = b64(self.dictSeries[self.title][EPLIST], 'decode') if EPLIST in self.dictSeries[self.title] else []
|
||||
self.Pages = self.dictSeries[self.title].get(CHECK, [1])
|
||||
self.Mode = self.dictSeries[self.title].get(MODE, False)
|
||||
self.Type = self.dictSeries[self.title].get(TYPE, False)
|
||||
Specials = {}
|
||||
Seasons = {}
|
||||
|
||||
if '|' in self.Season:
|
||||
ep = int(self.Season.split('|')[-1])
|
||||
Season = int(self.Season.split('|')[0])
|
||||
else:
|
||||
Season = int(self.Season)
|
||||
ep = 1
|
||||
|
||||
self.epdict = {}
|
||||
self.group = self.renumberdict[self.title].get(GROUP, None)
|
||||
busy(True)
|
||||
itemlist = find_episodes(self.item)
|
||||
busy(False)
|
||||
|
||||
if self.item.renumber:
|
||||
self.s = Season
|
||||
self.e = 1
|
||||
Season, Episode, self.Mode, Specials, Seasons, Exit = SelectreNumeration(self, itemlist)
|
||||
if Exit: return
|
||||
if ep != 1: self.Season = '%s|%s' % (Season, Episode)
|
||||
else: self.Season = str(Season)
|
||||
|
||||
elif self.Episodes and not self.Mode:
|
||||
self.s = Season
|
||||
self.e = ep
|
||||
self.sp = True
|
||||
Season, Episode, self.Mode, Specials, Seasons, Exit = SelectreNumeration(self, itemlist)
|
||||
|
||||
if self.Mode:
|
||||
if not Seasons:
|
||||
self.s = 1
|
||||
self.e = 1
|
||||
Season, Episode, self.Mode, Specials, Seasons, Exit = SelectreNumeration(self, itemlist, True)
|
||||
self.Episodes = Seasons
|
||||
if self.item.renumber or self.manual:
|
||||
self.item.renumber = False
|
||||
self.season, self.episode, self.manual, self.specials, Manual, Exit = SelectreNumeration(self, itemlist)
|
||||
if Exit:
|
||||
self.item.exit = True
|
||||
return
|
||||
if self.manual:
|
||||
self.episodes = Manual
|
||||
|
||||
else:
|
||||
# Ricava Informazioni da TVDB
|
||||
checkpages = []
|
||||
exist = True
|
||||
Page = self.Pages[-1]
|
||||
Episode = ep
|
||||
if self.group:
|
||||
Id = self.group.split('/')[-1]
|
||||
else:
|
||||
Id = None
|
||||
groups = tmdb.get_groups(self.item)
|
||||
if groups:
|
||||
Id = tmdb.select_group(groups)
|
||||
|
||||
if Id:
|
||||
self.group = 'https://www.themoviedb.org/tv/{}/episode_group/{}'.format(self.item.infoLabels['tmdb_id'], Id)
|
||||
seasons = []
|
||||
groupedSeasons = tmdb.get_group(Id)
|
||||
for groupedSeason in groupedSeasons:
|
||||
seasons.append({'season_number':groupedSeason['order'], 'episode_count':len(groupedSeason['episodes'])})
|
||||
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']
|
||||
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)
|
||||
|
||||
firstep = 0
|
||||
if self.season > 1:
|
||||
for c in range(1, self.season):
|
||||
firstep += self.seasonsdict[str(c)]
|
||||
firstep += self.episode - 1
|
||||
count = 0
|
||||
if self.epdict:
|
||||
for item in itemlist:
|
||||
if not match(re.sub(r'\[[^\]]+\]','',item.title), patron=r'[Ss]?(\d+)(?:x|_|\s+)[Ee]?[Pp]?(\d+)').match:
|
||||
# Otiene Numerazione Episodi
|
||||
scraped_ep = match(re.sub(r'\[[^\]]+\]','',item.title), patron=r'(\d+)').match
|
||||
if scraped_ep:
|
||||
episode = int(scraped_ep)
|
||||
if episode == 0:
|
||||
self.episodes[str(episode)] = '0x01'
|
||||
elif str(episode) in self.specials:
|
||||
self.episodes[str(episode)] = self.specials[str(episode)]
|
||||
count += 1
|
||||
elif episode - count + firstep in self.epdict:
|
||||
self.episodes[str(episode)] = self.epdict[episode - count + firstep]
|
||||
else:
|
||||
self.episodes[str(episode)] = '0x{:02d}'.format(count + 1)
|
||||
count += 1
|
||||
|
||||
if self.episodes: self.renumberdict[self.title][EPISODES] = self.episodes
|
||||
if self.group: self.renumberdict[self.title][GROUP] = self.group
|
||||
self.renumberdict[self.title][MANUALMODE] = self.manual
|
||||
self.renumberdict[self.title][SEASON] = self.season
|
||||
self.renumberdict[self.title][EPISODE] = self.episode
|
||||
self.renumberdict[self.title][SPECIALEPISODES] = self.specials
|
||||
self.renumberdict[self.title][SEASONSDICT] = self.seasonsdict
|
||||
write(self.item, self.renumberdict)
|
||||
# if self.auto: self.renumber()
|
||||
|
||||
while exist:
|
||||
data = tvdb.Tvdb(tvdb_id=self.id).get_list_episodes(self.id, Page)
|
||||
if data:
|
||||
for episode in data['data']:
|
||||
if episode['firstAired'] and [episode['firstAired'], episode['airedSeason'], episode['airedEpisodeNumber']] not in self.EpList:
|
||||
self.EpList.append([episode['firstAired'], episode['airedSeason'], episode['airedEpisodeNumber']])
|
||||
Page += 1
|
||||
else:
|
||||
if Page not in checkpages:
|
||||
checkpages.append(Page -1)
|
||||
exist = False
|
||||
self.Pages = [checkpages[-1]]
|
||||
self.EpList.sort()
|
||||
|
||||
# Crea Dizionari per la Rinumerazione
|
||||
if self.EpList:
|
||||
self.specials = []
|
||||
self.regular = {}
|
||||
self.complete = {}
|
||||
allep = 1
|
||||
specialep = 0
|
||||
|
||||
for episode in self.EpList:
|
||||
self.complete[allep] = [str(episode[1]) + 'x' + str(episode[2]), episode[0]]
|
||||
if episode[1] == 0:
|
||||
self.specials.append(allep)
|
||||
specialep = specialep + 1
|
||||
else:
|
||||
self.regular[ep] = [str(episode[1]) + 'x' + str(episode[2]), str(episode[0]), allep - 1]
|
||||
ep = ep + 1
|
||||
allep = allep + 1
|
||||
|
||||
if Season > 1:
|
||||
for numbers, data in self.regular.items():
|
||||
if data[0] == str(Season) + 'x1':
|
||||
FirstOfSeason = numbers - 1
|
||||
else: FirstOfSeason = Episode - 1
|
||||
|
||||
addiction = 0
|
||||
for item in itemlist:
|
||||
if not match(re.sub(r'\[[^\]]+\]','',item.title), patron=r'[Ss]?(\d+)(?:x|_|\s+)[Ee]?[Pp]?(\d+)').match:
|
||||
# Otiene Numerazione Episodi
|
||||
scraped_ep = match(re.sub(r'\[[^\]]+\]','',item.title), patron=r'(\d+)').match
|
||||
if scraped_ep:
|
||||
episode = int(scraped_ep)
|
||||
number = episode + FirstOfSeason - addiction
|
||||
if episode == 0:
|
||||
self.Episodes[str(episode)] = str(self.complete[self.regular[FirstOfSeason+1][2]][0])
|
||||
elif episode in Specials:
|
||||
self.Episodes[str(episode)] = Specials[episode]
|
||||
addiction += 1
|
||||
elif number <= len(self.regular) and number in self.regular:
|
||||
self.Episodes[str(episode)] = str(self.regular[number][0])
|
||||
else:
|
||||
try: self.Episodes[str(episode)] = str(self.complete[self.regular[number+2][2]][0])
|
||||
except: self.Episodes[str(episode)] = '0x0'
|
||||
|
||||
if self.Episodes: self.dictSeries[self.title][EPISODE] = b64(jsontools.dump(self.Episodes))
|
||||
self.dictSeries[self.title][EPLIST] = b64(jsontools.dump(self.EpList))
|
||||
self.dictSeries[self.title][MODE] = self.Mode
|
||||
self.dictSeries[self.title][SEASON] = self.Season
|
||||
self.dictSeries[self.title][CHECK] = self.Pages
|
||||
write(self.item, self.dictSeries)
|
||||
|
||||
if self.auto: self.renumber()
|
||||
|
||||
|
||||
def SelectreNumeration(opt, itemlist, manual=False):
|
||||
class SelectreNumerationWindow(xbmcgui.WindowXMLDialog):
|
||||
def start(self, opt):
|
||||
self.episodes = opt.Episodes if opt.Episodes else {}
|
||||
self.dictSeries = opt.dictSeries
|
||||
self.episodes = opt.episodes if opt.episodes else {}
|
||||
self.renumberdict = opt.renumberdict
|
||||
self.item = opt.item
|
||||
self.title = opt.title
|
||||
self.season = opt.s
|
||||
self.episode = opt.e
|
||||
self.mode = opt.Mode
|
||||
self.sp = opt.sp
|
||||
self.season = opt.season
|
||||
self.episode = opt.episode
|
||||
self.manual = opt.manual
|
||||
self.sp = opt.selectspecials
|
||||
self.manual = opt.manual
|
||||
self.offset = 0
|
||||
self.Exit = False
|
||||
|
||||
self.itemlist = opt.itemlist
|
||||
self.count = 1
|
||||
self.specials = {}
|
||||
self.specials = opt.specials
|
||||
self.items = []
|
||||
self.selected = []
|
||||
self.seasons = {}
|
||||
self.seasonsdict = opt.seasonsdict
|
||||
|
||||
self.doModal()
|
||||
return self.season, self.episode, self.mode, self.specials, self.seasons, self.Exit
|
||||
return self.season, self.episode, self.manual, self.specials, self.seasons, self.Exit
|
||||
|
||||
def onInit(self):
|
||||
# Compatibility with Kodi 18
|
||||
@@ -349,7 +303,7 @@ def SelectreNumeration(opt, itemlist, manual=False):
|
||||
if fanart: self.getControl(MBACKGROUND).setImage(fanart)
|
||||
self.getControl(INFO).setLabel(typo(config.get_localized_string(70822) + self.title, 'bold'))
|
||||
|
||||
self.mode = True
|
||||
self.manual = True
|
||||
|
||||
se = '1'
|
||||
ep = '1'
|
||||
@@ -434,17 +388,19 @@ def SelectreNumeration(opt, itemlist, manual=False):
|
||||
self.setFocusId(focus - 1)
|
||||
elif action in [UP]:
|
||||
if focus in [S]:
|
||||
s += 1
|
||||
self.getControl(S).setLabel(str(s))
|
||||
if str(s + 1) in self.seasonsdict:
|
||||
s += 1
|
||||
self.getControl(S).setLabel(str(s))
|
||||
elif focus in [E]:
|
||||
e += 1
|
||||
self.getControl(E).setLabel(str(e))
|
||||
if self.seasonsdict[str(s)] > e:
|
||||
e += 1
|
||||
self.getControl(E).setLabel(str(e))
|
||||
elif action in [DOWN]:
|
||||
if focus in [S]:
|
||||
if s > 0: s -= 1
|
||||
if str(s - 1) in self.seasonsdict: s -= 1
|
||||
self.getControl(S).setLabel(str(s))
|
||||
elif focus in [E]:
|
||||
if e > 0: e -= 1
|
||||
if e > 1: e -= 1
|
||||
self.getControl(E).setLabel(str(e))
|
||||
# MANUAL
|
||||
if focus in [MS, ME]:
|
||||
@@ -508,7 +464,7 @@ def SelectreNumeration(opt, itemlist, manual=False):
|
||||
# OPEN MANUAL
|
||||
elif control_id in [M]:
|
||||
self.getControl(INFO).setLabel(typo(config.get_localized_string(70823) + self.title, 'bold'))
|
||||
self.mode = True
|
||||
self.manual = True
|
||||
if self.episodes:
|
||||
items = []
|
||||
se = '1'
|
||||
@@ -539,8 +495,8 @@ def SelectreNumeration(opt, itemlist, manual=False):
|
||||
# DELETE
|
||||
if control_id in [D]:
|
||||
self.Exit = True
|
||||
self.dictSeries.pop(self.title)
|
||||
write(self.item, self.dictSeries)
|
||||
self.renumberdict.pop(self.title)
|
||||
write(self.item, self.renumberdict)
|
||||
self.close()
|
||||
|
||||
## SPECIAL SECTION
|
||||
@@ -548,7 +504,7 @@ def SelectreNumeration(opt, itemlist, manual=False):
|
||||
p1 = self.getControl(SELECTED).getSelectedPosition()
|
||||
if control_id in [LIST]:
|
||||
item = self.getControl(LIST).getSelectedItem()
|
||||
it = xbmcgui.ListItem(str(len(self.selected) + 1))
|
||||
it = xbmcgui.ListItem(str(len(self.selected) + len(self.specials) + 1))
|
||||
it.setProperty('title', item.getLabel())
|
||||
self.selected.append(it)
|
||||
index = self.getControl(SELECTED).getSelectedPosition()
|
||||
|
||||
@@ -41,7 +41,12 @@ def run(item=None):
|
||||
logger.debug()
|
||||
if not item:
|
||||
# Extract item from sys.argv
|
||||
if sys.argv[2]:
|
||||
if sys.argv[2] and 'play' in sys.argv[2]:
|
||||
sp = sys.argv[2].split('/')
|
||||
if len(sp) == 3:
|
||||
item = Item(channel=sp[1], livefilter=sp[2], action='play')
|
||||
# If no item, this is mainlist
|
||||
elif sys.argv[2]:
|
||||
sp = sys.argv[2].split('&')
|
||||
url = sp[0]
|
||||
item = Item().fromurl(url)
|
||||
@@ -49,7 +54,7 @@ def run(item=None):
|
||||
for e in sp[1:]:
|
||||
key, val = e.split('=')
|
||||
item.__setattr__(key, val)
|
||||
# If no item, this is mainlist
|
||||
|
||||
else:
|
||||
item = Item(channel="channelselector", action="getmainlist", viewmode="movie")
|
||||
if not config.get_setting('show_once'):
|
||||
@@ -337,6 +342,10 @@ def run(item=None):
|
||||
else:
|
||||
if platformtools.dialog_yesno(config.get_localized_string(60038), config.get_localized_string(60015)):
|
||||
run(Item(channel="setting", action="report_menu"))
|
||||
finally:
|
||||
if not item.action.startswith('play'):
|
||||
from core import db
|
||||
db.close()
|
||||
|
||||
|
||||
def new_search(item, channel=None):
|
||||
@@ -469,6 +478,7 @@ def play_from_library(item):
|
||||
else:
|
||||
# Pop-up window
|
||||
from specials import videolibrary
|
||||
from core.channeltools import get_channel_parameters
|
||||
p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(20000), config.get_localized_string(60683))
|
||||
p_dialog.update(0, '')
|
||||
item.play_from = 'window'
|
||||
@@ -499,9 +509,10 @@ def play_from_library(item):
|
||||
quality = '[B][' + item.quality + '][/B]' if item.quality else ''
|
||||
if item.server:
|
||||
path = filetools.join(config.get_runtime_path(), 'servers', item.server.lower() + '.json')
|
||||
name = jsontools.load(open(path, "r").read())['name']
|
||||
name = jsontools.load(open(path, "rb").read())['name']
|
||||
if name.startswith('@'): name = config.get_localized_string(int(name.replace('@','')))
|
||||
it = xbmcgui.ListItem('\n[B]%s[/B] %s - %s' % (name, quality, item.contentTitle))
|
||||
logger.debug(item)
|
||||
it = xbmcgui.ListItem('\n[B]%s[/B] %s - %s [%s]' % (name, quality, item.contentTitle, get_channel_parameters(item.contentChannel)['title']))
|
||||
it.setArt({'thumb':item.thumbnail})
|
||||
options.append(it)
|
||||
else:
|
||||
|
||||
+143
-107
@@ -226,6 +226,45 @@ def dialog_info(item, scraper):
|
||||
dialog = TitleOrIDWindow('TitleOrIDWindow.xml', config.get_runtime_path()).Start(item, scraper)
|
||||
return dialog
|
||||
|
||||
def dialog_select_group(heading, _list, preselect=0):
|
||||
class SelectGroup(xbmcgui.WindowXMLDialog):
|
||||
def start(self, heading, _list, preselect):
|
||||
self.selected = preselect
|
||||
self.heading = heading
|
||||
self.list = _list
|
||||
self.doModal()
|
||||
|
||||
return self.selected
|
||||
|
||||
def onInit(self):
|
||||
self.getControl(1).setText(self.heading)
|
||||
itemlist = []
|
||||
for n, text in enumerate(self.list):
|
||||
item = xbmcgui.ListItem(str(n))
|
||||
item.setProperty('title', text)
|
||||
itemlist.append(item)
|
||||
|
||||
self.getControl(2).addItems(itemlist)
|
||||
self.setFocusId(2)
|
||||
self.getControl(2).selectItem(self.selected)
|
||||
|
||||
def onClick(self, control):
|
||||
if control in [100]:
|
||||
self.selected = -1
|
||||
self.close()
|
||||
elif control in [2]:
|
||||
self.selected = self.getControl(2).getSelectedPosition()
|
||||
self.close()
|
||||
|
||||
def onAction(self, action):
|
||||
action = action.getId()
|
||||
if action in [10, 92]:
|
||||
self.selected = -1
|
||||
self.close()
|
||||
|
||||
dialog = SelectGroup('SelectGroup.xml', config.get_runtime_path()).start(heading, _list, preselect)
|
||||
return dialog
|
||||
|
||||
|
||||
def itemlist_refresh():
|
||||
# pos = Item().fromurl(xbmc.getInfoLabel('ListItem.FileNameAndPath')).itemlistPosition
|
||||
@@ -616,63 +655,71 @@ def is_playing():
|
||||
def play_video(item, strm=False, force_direct=False, autoplay=False):
|
||||
logger.debug()
|
||||
logger.debug(item.tostring('\n'))
|
||||
if item.channel == 'downloads':
|
||||
logger.debug("Play local video: %s [%s]" % (item.title, item.url))
|
||||
xlistitem = xbmcgui.ListItem(path=item.url)
|
||||
xlistitem.setArt({"thumb": item.thumbnail})
|
||||
set_infolabels(xlistitem, item, True)
|
||||
set_player(item, xlistitem, item.url, True, None) # Fix Play From Download Section
|
||||
return
|
||||
|
||||
default_action = config.get_setting("default_action")
|
||||
logger.debug("default_action=%s" % default_action)
|
||||
|
||||
# pass referer
|
||||
if item.referer:
|
||||
from core import httptools
|
||||
httptools.default_headers['Referer'] = item.referer
|
||||
|
||||
# Open the selection dialog to see the available options
|
||||
opciones, video_urls, seleccion, salir = get_dialogo_opciones(item, default_action, strm, autoplay)
|
||||
if salir: exit()
|
||||
|
||||
# get default option of addon configuration
|
||||
seleccion = get_seleccion(default_action, opciones, seleccion, video_urls)
|
||||
if seleccion < 0: exit() # Canceled box
|
||||
|
||||
logger.debug("selection=%d" % seleccion)
|
||||
logger.debug("selection=%s" % opciones[seleccion])
|
||||
|
||||
# run the available option, jdwonloader, download, favorites, add to the video library ... IF IT IS NOT PLAY
|
||||
salir = set_opcion(item, seleccion, opciones, video_urls)
|
||||
if salir:
|
||||
return
|
||||
|
||||
# we get the selected video
|
||||
mediaurl, view, mpd = get_video_seleccionado(item, seleccion, video_urls, autoplay)
|
||||
if not mediaurl: return
|
||||
|
||||
# video information is obtained.
|
||||
xlistitem = xbmcgui.ListItem(path=item.url)
|
||||
xlistitem.setArt({"thumb": item.contentThumbnail if item.contentThumbnail else item.thumbnail})
|
||||
set_infolabels(xlistitem, item, True)
|
||||
|
||||
# if it is a video in mpd format, the listitem is configured to play it ith the inpustreamaddon addon implemented in Kodi 17
|
||||
# from core.support import dbg;dbg()
|
||||
if mpd:
|
||||
if not install_inputstream():
|
||||
def play():
|
||||
if item.channel == 'downloads':
|
||||
logger.debug("Play local video: %s [%s]" % (item.title, item.url))
|
||||
xlistitem = xbmcgui.ListItem(path=item.url)
|
||||
xlistitem.setArt({"thumb": item.thumbnail})
|
||||
set_infolabels(xlistitem, item, True)
|
||||
set_player(item, xlistitem, item.url, True, None) # Fix Play From Download Section
|
||||
return
|
||||
xlistitem.setProperty('inputstream' if PY3 else 'inputstreamaddon', 'inputstream.adaptive')
|
||||
xlistitem.setProperty('inputstream.adaptive.manifest_type', 'mpd')
|
||||
if item.drm and item.license:
|
||||
install_widevine()
|
||||
xlistitem.setProperty("inputstream.adaptive.license_type", item.drm)
|
||||
xlistitem.setProperty("inputstream.adaptive.license_key", item.license)
|
||||
xlistitem.setMimeType('application/dash+xml')
|
||||
|
||||
if force_direct: item.play_from = 'window'
|
||||
default_action = config.get_setting("default_action")
|
||||
logger.debug("default_action=%s" % default_action)
|
||||
|
||||
set_player(item, xlistitem, mediaurl, view, strm)
|
||||
# pass referer
|
||||
if item.referer:
|
||||
from core import httptools
|
||||
httptools.default_headers['Referer'] = item.referer
|
||||
|
||||
# Open the selection dialog to see the available options
|
||||
opciones, video_urls, seleccion, salir = get_dialogo_opciones(item, default_action, strm, autoplay)
|
||||
if salir: return
|
||||
|
||||
# get default option of addon configuration
|
||||
seleccion = get_seleccion(default_action, opciones, seleccion, video_urls)
|
||||
if seleccion < 0: return # Canceled box
|
||||
|
||||
logger.debug("selection=%d" % seleccion)
|
||||
logger.debug("selection=%s" % opciones[seleccion])
|
||||
|
||||
# run the available option, jdwonloader, download, favorites, add to the video library ... IF IT IS NOT PLAY
|
||||
salir = set_opcion(item, seleccion, opciones, video_urls)
|
||||
if salir:
|
||||
return
|
||||
|
||||
# we get the selected video
|
||||
mediaurl, view, mpd = get_video_seleccionado(item, seleccion, video_urls, autoplay)
|
||||
if not mediaurl: return
|
||||
|
||||
# video information is obtained.
|
||||
xlistitem = xbmcgui.ListItem(path=item.url)
|
||||
xlistitem.setArt({"thumb": item.contentThumbnail if item.contentThumbnail else item.thumbnail})
|
||||
set_infolabels(xlistitem, item, True)
|
||||
|
||||
# if it is a video in mpd format, the listitem is configured to play it ith the inpustreamaddon addon implemented in Kodi 17
|
||||
# from core.support import dbg;dbg()
|
||||
if mpd:
|
||||
if not install_inputstream():
|
||||
return
|
||||
xlistitem.setProperty('inputstream' if PY3 else 'inputstreamaddon', 'inputstream.adaptive')
|
||||
xlistitem.setProperty('inputstream.adaptive.manifest_type', 'mpd')
|
||||
if item.drm and item.license:
|
||||
install_widevine()
|
||||
xlistitem.setProperty("inputstream.adaptive.license_type", item.drm)
|
||||
xlistitem.setProperty("inputstream.adaptive.license_key", item.license)
|
||||
xlistitem.setMimeType('application/dash+xml')
|
||||
|
||||
if force_direct: item.play_from = 'window'
|
||||
|
||||
set_player(item, xlistitem, mediaurl, view, strm)
|
||||
return True
|
||||
|
||||
if not play():
|
||||
# close db to ensure his thread will stop
|
||||
from core import db
|
||||
db.close()
|
||||
|
||||
|
||||
def stop_video():
|
||||
@@ -1370,71 +1417,60 @@ def get_platform():
|
||||
|
||||
|
||||
def get_played_time(item):
|
||||
import sqlite3
|
||||
from core import filetools
|
||||
db_name = filetools.join(config.get_data_path(), "kod_db.sqlite")
|
||||
ID = item.infoLabels['tmdb_id']
|
||||
conn = sqlite3.connect(db_name, timeout=15)
|
||||
c = conn.cursor()
|
||||
c.execute('CREATE TABLE IF NOT EXISTS viewed (tmdb_id TEXT, season INT, episode INT, played_time REAL)')
|
||||
conn.commit()
|
||||
if ID:
|
||||
if item.contentType == 'movie': c.execute("SELECT played_time FROM viewed WHERE tmdb_id=?", (ID,))
|
||||
elif 'season' in item.infoLabels:
|
||||
S = item.infoLabels['season']
|
||||
E = item.infoLabels['episode']
|
||||
c.execute("SELECT played_time FROM viewed WHERE tmdb_id=? AND season=? AND episode=?", (ID, S, E))
|
||||
elif 'episode' in item.infoLabels:
|
||||
E = item.infoLabels['episode']
|
||||
c.execute("SELECT played_time FROM viewed WHERE tmdb_id=? AND episode=?", (ID, E))
|
||||
result = c.fetchone()
|
||||
if not result: played_time = 0
|
||||
else: played_time = result[0]
|
||||
else: played_time = 0
|
||||
conn.close()
|
||||
logger.debug()
|
||||
from core import db
|
||||
|
||||
if not item.infoLabels:
|
||||
return 0
|
||||
ID = item.infoLabels.get('tmdb_id', '')
|
||||
if not ID:
|
||||
return 0
|
||||
|
||||
S = item.infoLabels.get('season', 0)
|
||||
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))
|
||||
|
||||
if not result: played_time = 0
|
||||
else: played_time = result
|
||||
|
||||
return played_time
|
||||
|
||||
|
||||
def set_played_time(item):
|
||||
import sqlite3
|
||||
from core import filetools
|
||||
ID = item.infoLabels['tmdb_id']
|
||||
logger.debug()
|
||||
from core import db
|
||||
|
||||
played_time = item.played_time
|
||||
db_name = filetools.join(config.get_data_path(), "kod_db.sqlite")
|
||||
conn = sqlite3.connect(db_name, timeout=15)
|
||||
c = conn.cursor()
|
||||
if not item.infoLabels:
|
||||
return
|
||||
|
||||
ID = item.infoLabels.get('tmdb_id', '')
|
||||
if not ID:
|
||||
return
|
||||
|
||||
S = item.infoLabels.get('season', 0)
|
||||
E = item.infoLabels.get('episode')
|
||||
|
||||
|
||||
if item.contentType == 'movie':
|
||||
c.execute("SELECT played_time FROM viewed WHERE tmdb_id=?", (ID,))
|
||||
result = c.fetchone()
|
||||
if result:
|
||||
if played_time > 0: c.execute("UPDATE viewed SET played_time=? WHERE tmdb_id=?", (item.played_time, ID))
|
||||
else: c.execute("DELETE from viewed WHERE tmdb_id=?", (ID,))
|
||||
else: c.execute("INSERT INTO viewed (tmdb_id, played_time) VALUES (?, ?)", (ID, item.played_time))
|
||||
elif 'season' in item.infoLabels:
|
||||
S = item.infoLabels['season']
|
||||
E = item.infoLabels['episode']
|
||||
c.execute("SELECT played_time FROM viewed WHERE tmdb_id=? AND season = ? AND episode=?", (ID, S, E))
|
||||
result = c.fetchone()
|
||||
if result:
|
||||
if played_time > 0: c.execute("UPDATE viewed SET played_time=? WHERE tmdb_id=? AND season=? AND episode=?", (item.played_time, ID, S, E))
|
||||
else: c.execute("DELETE from viewed WHERE tmdb_id=? AND season=? AND episode=?", (ID, S, E))
|
||||
else: c.execute("INSERT INTO viewed (tmdb_id, season, episode, played_time) VALUES (?, ?, ?, ?)", (ID, S, E, item.played_time))
|
||||
elif 'episode' in item.infoLabels:
|
||||
E = item.infoLabels['episode']
|
||||
c.execute("SELECT played_time FROM viewed WHERE tmdb_id=? AND episode=?", (ID, E))
|
||||
result = c.fetchone()
|
||||
if result:
|
||||
if played_time > 0: c.execute("UPDATE viewed SET played_time=? WHERE tmdb_id=? AND episode=?", (item.played_time, ID, E))
|
||||
else: c.execute("DELETE from viewed WHERE tmdb_id=? AND episode=?", (ID, E))
|
||||
else: c.execute("INSERT INTO viewed (tmdb_id, episode, played_time) VALUES (?, ?, ?)", (ID, E, item.played_time))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
db['viewed'][ID] = played_time
|
||||
elif E:
|
||||
newDict = db['viewed'].get(ID, {})
|
||||
newDict[str(S) + 'x' + str(E)] = played_time
|
||||
db['viewed'][ID] = newDict
|
||||
|
||||
|
||||
def prevent_busy(item):
|
||||
logger.debug()
|
||||
if not item.autoplay and not item.window:
|
||||
if item.globalsearch: xbmc.Player().play(os.path.join(config.get_runtime_path(), "resources", "kod.mp4"))
|
||||
else: xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, xbmcgui.ListItem(path=os.path.join(config.get_runtime_path(), "resources", "kod.mp4")))
|
||||
xbmc.sleep(100)
|
||||
xbmc.sleep(200)
|
||||
xbmc.Player().stop()
|
||||
# xbmc.executebuiltin('Action(Stop)')
|
||||
# xbmc.sleep(500)
|
||||
|
||||
@@ -46,6 +46,7 @@ def mark_auto_as_watched(item):
|
||||
ND = next_dialogs[next_ep_type]
|
||||
try: next_episode = next_ep(item)
|
||||
except: next_episode = False
|
||||
logger.debug(next_episode)
|
||||
|
||||
while platformtools.is_playing():
|
||||
actual_time = xbmc.Player().getTime()
|
||||
@@ -108,9 +109,6 @@ def mark_auto_as_watched(item):
|
||||
threading.Thread(target=mark_as_watched_subThread, args=[item]).start()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def sync_trakt_addon(path_folder):
|
||||
"""
|
||||
Updates the values of episodes seen if
|
||||
@@ -351,6 +349,38 @@ def mark_season_as_watched_on_kodi(item, value=1):
|
||||
|
||||
execute_sql_kodi(sql)
|
||||
|
||||
def set_watched_on_kod(data):
|
||||
from specials import videolibrary
|
||||
from core import videolibrarytools
|
||||
data = jsontools.load(data)
|
||||
Type = data.get('item', {}).get('type','')
|
||||
ID = data.get('item', {}).get('id','')
|
||||
if not Type or not ID:
|
||||
return
|
||||
playcount = data.get('playcount',0)
|
||||
for Type in ['movie', 'episode']:
|
||||
sql = 'select strFileName, strPath, uniqueid_value from %s_view where (id%s like "%s")' % (Type, Type.capitalize(), ID)
|
||||
n, records = execute_sql_kodi(sql)
|
||||
if records:
|
||||
for filename, path, uniqueid_value in records:
|
||||
if Type in ['movie']:
|
||||
title = filename.replace('.strm', ' [' + uniqueid_value + ']')
|
||||
filename = title +'.nfo'
|
||||
else:
|
||||
title = filename.replace('.strm', '')
|
||||
filename = 'tvshow.nfo'
|
||||
|
||||
path = filetools.join(path, filename)
|
||||
head_nfo, item = videolibrarytools.read_nfo(path)
|
||||
item.library_playcounts.update({title: playcount})
|
||||
filetools.write(path, head_nfo + item.tojson())
|
||||
|
||||
if item.infoLabels['mediatype'] == "tvshow":
|
||||
for season in item.library_playcounts:
|
||||
if "season" in season:
|
||||
season_num = int(scrapertools.find_single_match(season, r'season (\d+)'))
|
||||
item = videolibrary.check_season_playcount(item, season_num)
|
||||
filetools.write(path, head_nfo + item.tojson())
|
||||
|
||||
def mark_content_as_watched_on_kod(path):
|
||||
from specials import videolibrary
|
||||
@@ -384,6 +414,7 @@ def mark_content_as_watched_on_kod(path):
|
||||
if "\\" in path:
|
||||
path = path.replace("/", "\\")
|
||||
head_nfo, item = videolibrarytools.read_nfo(path) # I read the content .nfo
|
||||
old = item.clone()
|
||||
if not item:
|
||||
logger.error('.NFO not found: ' + path)
|
||||
return
|
||||
@@ -437,8 +468,9 @@ def mark_content_as_watched_on_kod(path):
|
||||
if "season" in season: # we look for the tags "season" inside playCounts
|
||||
season_num = int(scrapertools.find_single_match(season, r'season (\d+)')) # we save the season number
|
||||
item = videolibrary.check_season_playcount(item, season_num) # We call the method that updates Temps. and series
|
||||
|
||||
filetools.write(path, head_nfo + item.tojson())
|
||||
if item.library_playcounts != old.library_playcounts:
|
||||
logger.debug('scrivo')
|
||||
filetools.write(path, head_nfo + item.tojson())
|
||||
|
||||
#logger.debug(item)
|
||||
|
||||
@@ -626,37 +658,14 @@ def set_content(content_type, silent=False, custom=False):
|
||||
xbmc.executebuiltin('Addon.OpenSettings(metadata.universal)', True)
|
||||
|
||||
else: # SERIES
|
||||
scraper = [config.get_localized_string(70098), config.get_localized_string(70093)]
|
||||
scraper = [config.get_localized_string(70093), config.get_localized_string(70098)]
|
||||
if not custom:
|
||||
seleccion = 0 # tvdb
|
||||
seleccion = 0 # tmdb
|
||||
else:
|
||||
seleccion = platformtools.dialog_select(config.get_localized_string(70107), scraper)
|
||||
|
||||
# Instalar The TVDB
|
||||
if seleccion == -1 or seleccion == 0:
|
||||
if not xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'):
|
||||
if not silent:
|
||||
#Ask if we want to install metadata.tvdb.com
|
||||
install = platformtools.dialog_yesno(config.get_localized_string(60048),'')
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Install metadata.tvdb.com
|
||||
xbmc.executebuiltin('InstallAddon(metadata.tvdb.com)', True)
|
||||
logger.debug("The TVDB series Scraper installed ")
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'))
|
||||
if not continuar:
|
||||
msg_text = config.get_localized_string(60049)
|
||||
if continuar:
|
||||
xbmc.executebuiltin('Addon.OpenSettings(metadata.tvdb.com)', True)
|
||||
|
||||
# Instalar The Movie Database
|
||||
elif seleccion == 1:
|
||||
if seleccion == -1 or seleccion == 0:
|
||||
if continuar and not xbmc.getCondVisibility('System.HasAddon(metadata.tvshows.themoviedb.org)'):
|
||||
continuar = False
|
||||
if not silent:
|
||||
@@ -680,6 +689,29 @@ def set_content(content_type, silent=False, custom=False):
|
||||
if continuar:
|
||||
xbmc.executebuiltin('Addon.OpenSettings(metadata.tvshows.themoviedb.org)', True)
|
||||
|
||||
# Instalar The TVDB
|
||||
elif seleccion == 1:
|
||||
if not xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'):
|
||||
if not silent:
|
||||
#Ask if we want to install metadata.tvdb.com
|
||||
install = platformtools.dialog_yesno(config.get_localized_string(60048),'')
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Install metadata.tvdb.com
|
||||
xbmc.executebuiltin('InstallAddon(metadata.tvdb.com)', True)
|
||||
logger.debug("The TVDB series Scraper installed ")
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'))
|
||||
if not continuar:
|
||||
msg_text = config.get_localized_string(60049)
|
||||
if continuar:
|
||||
xbmc.executebuiltin('Addon.OpenSettings(metadata.tvdb.com)', True)
|
||||
|
||||
idPath = 0
|
||||
idParentPath = 0
|
||||
if continuar:
|
||||
@@ -753,11 +785,11 @@ def set_content(content_type, silent=False, custom=False):
|
||||
strContent = 'tvshows'
|
||||
scanRecursive = 0
|
||||
if seleccion == -1 or seleccion == 0:
|
||||
strScraper = 'metadata.tvdb.com'
|
||||
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.tvdb.com/settings.xml")
|
||||
elif seleccion == 1:
|
||||
strScraper = 'metadata.tvshows.themoviedb.org'
|
||||
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.tvshows.themoviedb.org/settings.xml")
|
||||
elif seleccion == 1:
|
||||
strScraper = 'metadata.tvdb.com'
|
||||
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.tvdb.com/settings.xml")
|
||||
if not os.path.exists(path_settings):
|
||||
logger.debug("%s: %s" % (content_type, path_settings + " doesn't exist"))
|
||||
return continuar
|
||||
@@ -961,13 +993,14 @@ def clean(path_list=[]):
|
||||
sql = 'SELECT idPath FROM path where strPath LIKE "%s"' % sql_path
|
||||
logger.debug('sql: ' + sql)
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
idPath = records[0][0]
|
||||
sql = 'DELETE from path WHERE idPath=%s' % idPath
|
||||
logger.debug('sql: ' + sql)
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
sql = 'DELETE from path WHERE idParentPath=%s' % idPath
|
||||
logger.debug('sql: ' + sql)
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if records:
|
||||
idPath = records[0][0]
|
||||
sql = 'DELETE from path WHERE idPath=%s' % idPath
|
||||
logger.debug('sql: ' + sql)
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
sql = 'DELETE from path WHERE idParentPath=%s' % idPath
|
||||
logger.debug('sql: ' + sql)
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
|
||||
from core import videolibrarytools
|
||||
for path, folders, files in filetools.walk(videolibrarytools.MOVIES_PATH):
|
||||
@@ -1236,7 +1269,6 @@ def update_sources(new='', old=''):
|
||||
def ask_set_content(silent=False):
|
||||
logger.debug()
|
||||
logger.debug("videolibrary_kodi %s" % config.get_setting("videolibrary_kodi"))
|
||||
|
||||
def do_config(custom=False):
|
||||
if set_content("movie", True, custom) and set_content("tvshow", True, custom):
|
||||
platformtools.dialog_ok(config.get_localized_string(80026), config.get_localized_string(70104))
|
||||
@@ -1271,11 +1303,11 @@ def ask_set_content(silent=False):
|
||||
config.set_setting("folder_movies", movies_folder)
|
||||
config.set_setting("folder_tvshows", tvshows_folder)
|
||||
config.verify_directories_created()
|
||||
do_config(True)
|
||||
do_config(False)
|
||||
# default path and folders
|
||||
else:
|
||||
platformtools.dialog_ok(config.get_localized_string(80026), config.get_localized_string(80030))
|
||||
do_config(True)
|
||||
do_config(False)
|
||||
# default settings
|
||||
else:
|
||||
platformtools.dialog_ok(config.get_localized_string(80026), config.get_localized_string(80027))
|
||||
@@ -1286,7 +1318,7 @@ def ask_set_content(silent=False):
|
||||
# configuration from the settings menu
|
||||
else:
|
||||
platformtools.dialog_ok(config.get_localized_string(80026), config.get_localized_string(80023))
|
||||
do_config(True)
|
||||
do_config(False)
|
||||
|
||||
|
||||
def next_ep(item):
|
||||
|
||||
Binary file not shown.
@@ -701,7 +701,7 @@ msgid "This website [B]%s[/B] seems to be unavailable, try later. if the web pag
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#60014"
|
||||
msgid "It may be due to a connection problem, the web page of the channel has changed its structure, or an internal error of KoD. If on browser it works, report the issue using [B]Help->Report an issue.[B]"
|
||||
msgid "It may be due to a connection problem, the web page of the channel has changed its structure, or an internal error of KoD. If on browser it works, report the issue using [B]Help->Report an issue.[/B]"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#60015"
|
||||
@@ -6123,6 +6123,9 @@ msgctxt "#70830"
|
||||
msgid "The series / episode number should only be changed if the series has relative numbering."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#70831"
|
||||
msgid "This series has multiple types of numbering, choose the most suitable one "
|
||||
msgstr ""
|
||||
|
||||
# DNS start [ settings and declaration ]
|
||||
msgctxt "#707401"
|
||||
@@ -6370,7 +6373,7 @@ msgid "Configuration of Kodi video library"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#80027"
|
||||
msgid "You will be asked to configure The Movie Database for movies and The TVDB for TV shows"
|
||||
msgid "You will be asked to configure The Movie Database for movies and tvshows"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#80028"
|
||||
|
||||
@@ -700,7 +700,7 @@ msgid "This website [B]%s[/B] seems to be unavailable, try later. if the web pag
|
||||
msgstr "Il sito [B]%s[/B] non sembra essere disponibile, riprova più tardi. Se la pagina web funziona correttamente segnala l'errore qui: https://github.com/kodiondemand/addon/issues"
|
||||
|
||||
msgctxt "#60014"
|
||||
msgid "It may be due to a connection problem, the web page of the channel has changed its structure, or an internal error of KoD. If on browser it works, report the issue using [B]Help->Report an issue.[B]"
|
||||
msgid "It may be due to a connection problem, the web page of the channel has changed its structure, or an internal error of KoD. If on browser it works, report the issue using [B]Help->Report an issue.[/B]"
|
||||
msgstr "Potrebbe essere dovuto a un problema di connessione, la pagina web del canale ha cambiato la sua struttura, oppure un errore interno di KoD. Se sul browser funziona, segnala il problema andando in [B]Aiuto->Segnala un problema[/B]."
|
||||
|
||||
msgctxt "#60015"
|
||||
@@ -6124,6 +6124,10 @@ msgctxt "#70830"
|
||||
msgid "The series / episode number should only be changed if the series has relative numbering."
|
||||
msgstr "Il numero della serie / episodio deve essere modificato solo se la serie ha una numerazione relativa."
|
||||
|
||||
msgctxt "#70831"
|
||||
msgid "This series has multiple types of numbering, choose the most suitable one "
|
||||
msgstr "Questa serie ha più tipi di numerazione, scegli quello più adatto"
|
||||
|
||||
# DNS start [ settings and declaration ]
|
||||
msgctxt "#707401"
|
||||
msgid "Enable DNS check alert"
|
||||
@@ -6370,8 +6374,8 @@ msgid "Configuration of Kodi video library"
|
||||
msgstr "Configurazione della libreria di Kodi"
|
||||
|
||||
msgctxt "#80027"
|
||||
msgid "You will be asked to configure The Movie Database for movies and The TVDB for TV shows"
|
||||
msgstr "Ti verrà chiesto di configurare The Movie Database per i film e The TVDB per le serie TV"
|
||||
msgid "You will be asked to configure The Movie Database for movies and tvshows"
|
||||
msgstr "Ti verrà chiesto di configurare The Movie Database sia per i film che per le serie TV"
|
||||
|
||||
msgctxt "#80028"
|
||||
msgid "The selected folders are already used by the Kodi library. Please change them properly"
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<window>
|
||||
<allowoverlays>false</allowoverlays>
|
||||
<animation type="WindowOpen" reversible="false">
|
||||
<effect type="slide" start="0,200" end="0,0" center="640,225" delay="160" tween="cubic" time="200" />
|
||||
<effect type="fade" delay="160" end="100" time="240" />
|
||||
</animation>
|
||||
<animation type="WindowClose" reversible="false">
|
||||
<effect type="slide" start="0,0" end="0,200" center="640,225" easing="in" tween="cubic" time="200" />
|
||||
<effect type="fade" start="100" end="0" time="240" />
|
||||
</animation>
|
||||
<controls>
|
||||
<control type="group">
|
||||
<description>Container</description>
|
||||
<left>200</left>
|
||||
<top>60</top>
|
||||
<width>860</width>
|
||||
<height>600</height>
|
||||
<control type="image">
|
||||
<description>Background</description>
|
||||
<width>100%</width>
|
||||
<height>100%</height>
|
||||
<texture colordiffuse="FF232323">white.png</texture>
|
||||
</control>
|
||||
<control type="textbox" id="1">
|
||||
<description>Heading</description>
|
||||
<top>0</top>
|
||||
<left>0</left>
|
||||
<height>60</height>
|
||||
<width>100%</width>
|
||||
<font>font13</font>
|
||||
<textcolor>FFFFFFFF</textcolor>
|
||||
<align>center</align>
|
||||
<aligny>center</aligny>
|
||||
<label></label>
|
||||
</control>
|
||||
<control type="group">
|
||||
<left>30</left>
|
||||
<top>70</top>
|
||||
<width>595</width>
|
||||
<height>530</height>
|
||||
<control type="list" id="2">
|
||||
<description>List</description>
|
||||
<left>0</left>
|
||||
<top>0</top>
|
||||
<width>795</width>
|
||||
<height>530</height>
|
||||
<onup>100</onup>
|
||||
<onright>4</onright>
|
||||
<orientation>vertical</orientation>
|
||||
<scrolltime>200</scrolltime>
|
||||
<pagecontrol>4</pagecontrol>
|
||||
<itemlayout height="150" width="795">
|
||||
<control type="textbox">
|
||||
<description>Selected Item</description>
|
||||
<left>20</left>
|
||||
<top>20</top>
|
||||
<width>765</width>
|
||||
<height>110</height>
|
||||
<font>font13</font>
|
||||
<textcolor>FFFFFFFF</textcolor>
|
||||
<label>$INFO[ListItem.Property(title)]</label>
|
||||
<align>center</align>
|
||||
<aligny>center</aligny>
|
||||
</control>
|
||||
</itemlayout>
|
||||
<focusedlayout height="150" width="795">
|
||||
<control type="image">
|
||||
<top>1</top>
|
||||
<width>100%</width>
|
||||
<height>100%</height>
|
||||
<texture colordiffuse="FF0081C2">white.png</texture>
|
||||
</control>
|
||||
<control type="textbox">
|
||||
<description>Selected Item</description>
|
||||
<left>20</left>
|
||||
<top>20</top>
|
||||
<width>765</width>
|
||||
<height>110</height>
|
||||
<font>font13</font>
|
||||
<textcolor>FFFFFFFF</textcolor>
|
||||
<label>$INFO[ListItem.Property(title)]</label>
|
||||
<autoscroll time="3000" delay="3000" repeat="3000">True</autoscroll>
|
||||
<align>center</align>
|
||||
<aligny>center</aligny>
|
||||
</control>
|
||||
</focusedlayout>
|
||||
</control>
|
||||
<control type="scrollbar" id="4">
|
||||
<description>Scrollbar</description>
|
||||
<left>595</left>
|
||||
<top>60</top>
|
||||
<width>5</width>
|
||||
<height>100%</height>
|
||||
<visible>true</visible>
|
||||
<texturesliderbackground colordiffuse="FF232323">white.png</texturesliderbackground>
|
||||
<texturesliderbar colordiffuse="FFFFFFFF">white.png</texturesliderbar>
|
||||
<texturesliderbarfocus colordiffuse="FF0081C2">white.png</texturesliderbarfocus>
|
||||
<textureslidernib colordiffuse="FF232323">white.png</textureslidernib>
|
||||
<textureslidernibfocus colordiffuse="FF232323">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>30</right>
|
||||
<top>10</top>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
<texturefocus colordiffuse="FFFFFFFF">close.png</texturefocus>
|
||||
<texturenofocus colordiffuse="55FFFFFF">close.png</texturenofocus>
|
||||
<ondown>2</ondown>
|
||||
</control>
|
||||
</control>
|
||||
</controls>
|
||||
</window>
|
||||
Binary file not shown.
@@ -18,6 +18,7 @@ def test_video_exists(page_url):
|
||||
|
||||
def get_video_url(page_url, premium=False, user="", password="", video_password=""):
|
||||
logger.debug("url=" + page_url)
|
||||
# from core.support import dbg;dbg()
|
||||
qualities = []
|
||||
video_urls = []
|
||||
mgid = support.match(data, patron=r'uri":"([^"]+)"').match
|
||||
@@ -26,8 +27,9 @@ def get_video_url(page_url, premium=False, user="", password="", video_password=
|
||||
url = jsontools.load(support.match(rootUrl.replace('&device={device}','').format(uri = ID)).data)['package']['video']['item'][0]['rendition'][0]['src']
|
||||
urls = support.match(url, patron=r'RESOLUTION=(\d+x\d+).*?(http[^ ]+)').matches
|
||||
for quality, url in urls:
|
||||
quality = quality.split('x')[0]
|
||||
if quality not in qualities:
|
||||
qualities.append(quality)
|
||||
video_urls.append(["m3u8 {}p [Paramount]".format(quality.split('x')[-1]), url])
|
||||
video_urls.append(["m3u8 {}p [Paramount]".format(quality), url])
|
||||
video_urls.sort(key=lambda url: int(support.match(url[0], patron=r'(\d+)p').match))
|
||||
return video_urls
|
||||
|
||||
+8
-5
@@ -43,11 +43,14 @@ def get_video_url(page_url, premium=False, user="", password="", video_password=
|
||||
addon_dir = xbmc.translatePath( my_addon.getAddonInfo('path') )
|
||||
sys.path.append(filetools.join( addon_dir, 'resources', 'lib' ) )
|
||||
from youtube_resolver import resolve
|
||||
for stream in resolve(page_url):
|
||||
# title = scrapertools.find_single_match(stream['title'], '(\d+p)')
|
||||
if scrapertools.find_single_match(stream['title'], r'(\d+p)'):
|
||||
video_urls.append([re.sub(r'(\[[^\]]+\])', '', stream['title']), stream['url']])
|
||||
video_urls.sort(key=lambda it: int(it[0].split("p", 1)[0]))
|
||||
try:
|
||||
for stream in resolve(page_url):
|
||||
# title = scrapertools.find_single_match(stream['title'], '(\d+p)')
|
||||
if scrapertools.find_single_match(stream['title'], r'(\d+p)'):
|
||||
video_urls.append([re.sub(r'(\[[^\]]+\])', '', stream['title']), stream['url']])
|
||||
video_urls.sort(key=lambda it: int(it[0].split("p", 1)[0]))
|
||||
except:
|
||||
pass
|
||||
|
||||
return video_urls
|
||||
|
||||
|
||||
+61
-1
@@ -24,7 +24,7 @@ sys.path.insert(0, librerias)
|
||||
|
||||
from core import videolibrarytools, filetools, channeltools, httptools, scrapertools
|
||||
from lib import schedule
|
||||
from platformcode import logger, platformtools, updater
|
||||
from platformcode import logger, platformtools, updater, xbmc_videolibrary
|
||||
from specials import videolibrary
|
||||
from servers import torrent
|
||||
|
||||
@@ -398,6 +398,11 @@ class AddonMonitor(xbmc.Monitor):
|
||||
xbmc.executebuiltin('Action(reloadkeymaps)')
|
||||
self.settings_pre = settings_post
|
||||
|
||||
def onNotification(self, sender, method, data):
|
||||
if method == 'VideoLibrary.OnUpdate':
|
||||
xbmc_videolibrary.set_watched_on_kod(data)
|
||||
logger.debug('AGGIORNO')
|
||||
|
||||
def onScreensaverActivated(self):
|
||||
logger.debug('screensaver activated, un-scheduling screen-on jobs')
|
||||
schedule.clear('screenOn')
|
||||
@@ -446,6 +451,61 @@ if __name__ == "__main__":
|
||||
# handling old autoexec method
|
||||
if config.is_autorun_enabled():
|
||||
config.enable_disable_autorun(True)
|
||||
# port old db to new
|
||||
old_db_name = filetools.join(config.get_data_path(), "kod_db.sqlite")
|
||||
if filetools.isfile(old_db_name):
|
||||
try:
|
||||
import sqlite3
|
||||
from core import db
|
||||
|
||||
old_db_conn = sqlite3.connect(old_db_name, timeout=15)
|
||||
old_db = old_db_conn.cursor()
|
||||
old_db.execute('select * from viewed')
|
||||
|
||||
for ris in old_db.fetchall():
|
||||
if ris[1]: # tvshow
|
||||
show = db['viewed'].get(ris[0], {})
|
||||
show[str(ris[1]) + 'x' + str(ris[2])] = ris[3]
|
||||
db['viewed'][ris[0]] = show
|
||||
else: # film
|
||||
db['viewed'][ris[0]] = ris[3]
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
filetools.remove(old_db_name, True, False)
|
||||
|
||||
# replace tvdb to tmdb for series
|
||||
if config.get_setting('videolibrary_kodi') and config.get_setting('show_once'):
|
||||
nun_records, records = xbmc_videolibrary.execute_sql_kodi('select * from path where strPath like "' +
|
||||
filetools.join(config.get_setting('videolibrarypath'), config.get_setting('folder_tvshows')) +
|
||||
'%" and strScraper="metadata.tvdb.com"')
|
||||
if nun_records:
|
||||
import xbmcaddon
|
||||
# change language
|
||||
tvdbLang = xbmcaddon.Addon(id="metadata.tvdb.com").getSetting('language')
|
||||
newLang = tvdbLang + '-' + tvdbLang.upper()
|
||||
xbmcaddon.Addon(id="metadata.tvshows.themoviedb.org").setSetting('language', newLang)
|
||||
updater.refreshLang()
|
||||
|
||||
# prepare to replace strSettings
|
||||
path_settings = xbmc.translatePath(
|
||||
"special://profile/addon_data/metadata.tvshows.themoviedb.org/settings.xml")
|
||||
settings_data = filetools.read(path_settings)
|
||||
strSettings = ' '.join(settings_data.split()).replace("> <", "><")
|
||||
strSettings = strSettings.replace("\"", "\'")
|
||||
|
||||
# update db
|
||||
nun_records, records = xbmc_videolibrary.execute_sql_kodi(
|
||||
'update path set strScraper="metadata.tvshows.themoviedb.org", strSettings="' + strSettings + '" where strPath like "' +
|
||||
filetools.join(config.get_setting('videolibrarypath'), config.get_setting('folder_tvshows')) +
|
||||
'%" and strScraper="metadata.tvdb.com"')
|
||||
|
||||
# scan new info
|
||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||
xbmc.executebuiltin('CleanLibrary(video)')
|
||||
while xbmc.getCondVisibility('Library.IsScanningVideo()'):
|
||||
xbmc.sleep(1000)
|
||||
|
||||
monitor = AddonMonitor()
|
||||
|
||||
# mark as stopped all downloads (if we are here, probably kodi just started)
|
||||
|
||||
@@ -401,6 +401,7 @@ def findvideos(item):
|
||||
json = item.url['links']
|
||||
else:
|
||||
json = item.url
|
||||
|
||||
for option in json:
|
||||
extra = set_extra_values(item, option, item.path)
|
||||
title = item.fulltitle + (' - '+option['title'] if 'title' in option else '')
|
||||
|
||||
@@ -700,7 +700,7 @@ class SearchWindow(xbmcgui.WindowXML):
|
||||
else:
|
||||
self.Focus(SEARCH)
|
||||
self.setFocusId(RESULTS)
|
||||
self.RESULTS.selectItem(self.epos)
|
||||
self.RESULTS.selectItem(self.pos)
|
||||
elif self.EPISODES.isVisible():
|
||||
self.episodes = []
|
||||
self.Focus(SEARCH)
|
||||
|
||||
+5
-5
@@ -107,11 +107,11 @@ def mainlist(item):
|
||||
# itemlist.append(new_item)
|
||||
|
||||
#if list_canales['documentales']:
|
||||
thumbnail = get_thumb("documentary.png")
|
||||
new_item = Item(channel=item.channel, action="novedades", extra="documentales", title=config.get_localized_string(60513),
|
||||
thumbnail=thumbnail)
|
||||
set_category_context(new_item)
|
||||
itemlist.append(new_item)
|
||||
# thumbnail = get_thumb("documentary.png")
|
||||
# new_item = Item(channel=item.channel, action="novedades", extra="documentales", title=config.get_localized_string(60513),
|
||||
# thumbnail=thumbnail)
|
||||
# set_category_context(new_item)
|
||||
# itemlist.append(new_item)
|
||||
thumbnail = get_thumb("setting_0.png")
|
||||
itemlist.append(Item(channel='shortcuts', action="SettingOnPosition", category=7, setting=1,
|
||||
title=typo(config.get_localized_string(70285), 'bold color kod'), thumbnail=thumbnail))
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from core import servertools
|
||||
from core.support import match, info
|
||||
from core.support import match, info, server
|
||||
from core.item import Item
|
||||
from platformcode import config, logger
|
||||
|
||||
@@ -27,10 +27,10 @@ def search(item, text):
|
||||
itemlist = []
|
||||
|
||||
if "server" in item.args:
|
||||
from core.support import server
|
||||
itemlist = server(item, text)
|
||||
elif "direct" in item.args:
|
||||
itemlist.append(Item(channel=item.channel, action="play", url=text, server="directo", title=config.get_localized_string(60092)))
|
||||
itemlist = server(item, itemlist=itemlist)
|
||||
else:
|
||||
data = match(text).data
|
||||
itemlist = servertools.find_video_items(data=data)
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
"label": "@60651",
|
||||
"enabled": false,
|
||||
"default": 0,
|
||||
"visible": false,
|
||||
"lvalues": [
|
||||
"TMDB",
|
||||
"None"
|
||||
@@ -97,6 +98,7 @@
|
||||
"type": "list",
|
||||
"label": "@60652",
|
||||
"default": 0,
|
||||
"visible": false,
|
||||
"lvalues": [
|
||||
"TMDB",
|
||||
"TVDB"
|
||||
|
||||
@@ -7,6 +7,7 @@ PY3 = False
|
||||
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
|
||||
|
||||
import xbmc, os, traceback
|
||||
from time import time
|
||||
|
||||
from core import filetools, scrapertools, videolibrarytools
|
||||
from core.support import typo, thumb
|
||||
@@ -49,6 +50,9 @@ def list_movies(item, silent=False):
|
||||
local= True
|
||||
break
|
||||
|
||||
# from core.support import dbg;dbg()
|
||||
# for movie_path in movies_path:
|
||||
# get_results(movie_path, root, 'movie', local)
|
||||
with futures.ThreadPoolExecutor() as executor:
|
||||
itlist = [executor.submit(get_results, movie_path, root, 'movie', local) for movie_path in movies_path]
|
||||
for res in futures.as_completed(itlist):
|
||||
@@ -67,6 +71,7 @@ def list_tvshows(item):
|
||||
lista = []
|
||||
|
||||
root = videolibrarytools.TVSHOWS_PATH
|
||||
start = time()
|
||||
with futures.ThreadPoolExecutor() as executor:
|
||||
itlist = [executor.submit(get_results, filetools.join(root, folder, "tvshow.nfo"), root, 'tvshow') for folder in filetools.listdir(root)]
|
||||
for res in futures.as_completed(itlist):
|
||||
@@ -75,7 +80,7 @@ def list_tvshows(item):
|
||||
if item_tvshow.library_urls and len(item_tvshow.library_urls) > 0:
|
||||
itemlist += [item_tvshow]
|
||||
lista += [{'title':item_tvshow.contentTitle,'thumbnail':item_tvshow.thumbnail,'fanart':item_tvshow.fanart, 'active': value, 'nfo':item_tvshow.nfo}]
|
||||
|
||||
logger.debug('load list',time() - start)
|
||||
if itemlist:
|
||||
itemlist = sorted(itemlist, key=lambda it: it.title.lower())
|
||||
|
||||
@@ -88,13 +93,8 @@ def list_tvshows(item):
|
||||
|
||||
def get_results(nfo_path, root, Type, local=False):
|
||||
value = 0
|
||||
if Type == 'movie': folder = "folder_movies"
|
||||
else: folder = "folder_tvshows"
|
||||
|
||||
if filetools.exists(nfo_path):
|
||||
# We synchronize the episodes seen from the Kodi video library with that of KoD
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.mark_content_as_watched_on_kod(nfo_path)
|
||||
head_nfo, item = videolibrarytools.read_nfo(nfo_path)
|
||||
|
||||
# If you have not read the .nfo well, we will proceed to the next
|
||||
@@ -107,6 +107,7 @@ def get_results(nfo_path, root, Type, local=False):
|
||||
|
||||
# continue loading the elements of the video library
|
||||
if Type == 'movie':
|
||||
folder = "folder_movies"
|
||||
item.path = filetools.split(nfo_path)[0]
|
||||
item.nfo = nfo_path
|
||||
sep = '/' if '/' in nfo_path else '\\'
|
||||
@@ -118,7 +119,7 @@ def get_results(nfo_path, root, Type, local=False):
|
||||
if not filetools.exists(filetools.join(item.path, filetools.basename(strm_path))) and not local: return Item(), 0
|
||||
|
||||
# Contextual menu: Mark as seen / not seen
|
||||
visto = item.library_playcounts.get(item.path.split(sep)[0], 0)
|
||||
visto = item.library_playcounts.get(strm_path.strip('/').split('/')[0], 0)
|
||||
item.infoLabels["playcount"] = visto
|
||||
if visto > 0:
|
||||
seen_text = config.get_localized_string(60016)
|
||||
@@ -136,7 +137,7 @@ def get_results(nfo_path, root, Type, local=False):
|
||||
item.context = [{"title": seen_text, "action": "mark_content_as_watched", "channel": "videolibrary", "playcount": counter},
|
||||
{"title": delete_text, "action": "delete", "channel": "videolibrary", "multichannel": multichannel}]
|
||||
else:
|
||||
# Sometimes it gives random errors, for not finding the .nfo. Probably timing issues
|
||||
folder = "folder_tvshows"
|
||||
try:
|
||||
item.title = item.contentTitle
|
||||
item.path = filetools.split(nfo_path)[0]
|
||||
@@ -558,6 +559,8 @@ def play(item):
|
||||
else:
|
||||
itemlist = [item.clone(url=item.url, server="local")]
|
||||
|
||||
if not itemlist:
|
||||
return []
|
||||
# For direct links in list format
|
||||
if isinstance(itemlist[0], list):
|
||||
item.video_urls = itemlist
|
||||
@@ -907,7 +910,9 @@ def mark_season_as_watched(item):
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
|
||||
# Get dictionary of marked episodes
|
||||
f = filetools.join(item.path, 'tvshow.nfo')
|
||||
if not item.path: f = item.nfo
|
||||
else: f = filetools.join(item.path, 'tvshow.nfo')
|
||||
|
||||
head_nfo, it = videolibrarytools.read_nfo(f)
|
||||
if not hasattr(it, 'library_playcounts'):
|
||||
it.library_playcounts = {}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ rm tests/home/userdata/addon_data/plugin.video.kod/settings_channels/*.json
|
||||
rm tests/home/userdata/addon_data/plugin.video.kod/settings_servers/*.json
|
||||
rm tests/home/userdata/addon_data/plugin.video.kod/cookies.dat
|
||||
rm tests/home/userdata/addon_data/plugin.video.kod/kod_db.sqlite
|
||||
python -m pip install --upgrade pip
|
||||
python3 -m pip install --upgrade pip
|
||||
pip install sakee
|
||||
pip install html-testRunner
|
||||
pip install parameterized
|
||||
|
||||
+18
-22
@@ -5,9 +5,16 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||
<style>
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h2 class="text-capitalize">{{ title }}</h2>
|
||||
@@ -55,7 +62,9 @@
|
||||
{%- if (test_case.stdout or test_case.err or test_case.err) and test_case.outcome != test_case.SKIP %}
|
||||
<tr style="display:none;">
|
||||
<td class="col-xs-9" colspan="3">
|
||||
{%- if test_case.stdout %}<p style="white-space: pre-line;">{{ test_case.stdout|e }}</p>{% endif %}
|
||||
<textarea rows="40" readonly>
|
||||
{%- if test_case.stdout %}{{ test_case.stdout|e }}{% endif %}
|
||||
</textarea>
|
||||
{%- if test_case.err %}<p style="color:maroon;">{{ test_case.err[0].__name__ }}: {{ test_case.err[1] }}</p>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -63,7 +72,9 @@
|
||||
{%- if (test_case.stdout or test_case.err or test_case.err) and test_case.outcome == test_case.SKIP %}
|
||||
<tr style="display:none;">
|
||||
<td class="col-xs-9" colspan="3">
|
||||
{%- if test_case.stdout %}<p style="white-space: pre-line;">{{ test_case.stdout|e }}</p>{% endif %}
|
||||
<textarea rows="40" readonly>
|
||||
{%- if test_case.stdout %}{{ test_case.stdout|e }}{% endif %}
|
||||
</textarea>
|
||||
{%- if test_case.err %}<p style="color:maroon;">{{ test_case.err }}</p>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -113,8 +124,10 @@
|
||||
{%- if subtest.err or subtest.err %}
|
||||
<tr style="display:none;">
|
||||
<td class="col-xs-9" colspan="3">
|
||||
{%- if subtest.err %}<p style="color:maroon;">{{ subtest.err[0].__name__ }}: {{ subtest.err[1] }}</p>{% endif %}
|
||||
{%- if subtest.err %}<p style="color:maroon;">{{ subtest.test_exception_info }}</p>{% endif %}
|
||||
<textarea rows="40" readonly>
|
||||
{%- if subtest.err %}{{ subtest.err[0].__name__ }}: {{ subtest.err[1] }}{% endif %}
|
||||
</textarea>
|
||||
{%- if subtest.err %}<p style="color:maroon;">{{ subtest.test_exception_info }}</p>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{%- endif %}
|
||||
@@ -126,11 +139,6 @@
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
Total: {{ summaries[test_case_name].total }}, Pass: {{ summaries[test_case_name].success }}{% if summaries[test_case_name].failure %}, Fail: {{ summaries[test_case_name].failure }}{% endif %}{% if summaries[test_case_name].error %}, Error: {{ summaries[test_case_name].error }}{% endif %}{% if summaries[test_case_name].skip %}, Skip: {{ summaries[test_case_name].skip }}{% endif %} -- Duration: {{ summaries[test_case_name].duration }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -171,30 +179,18 @@
|
||||
}
|
||||
$('#showPassed').on('click', function(){
|
||||
$(".success").toggle(this.checked);
|
||||
if (this.checked == false) {
|
||||
$(".success").next('tr').toggle(this.checked);
|
||||
}
|
||||
hideOrShow()
|
||||
});
|
||||
$('#showFailed').on('click', function(){
|
||||
$(".danger").toggle(this.checked);
|
||||
if (this.checked == false) {
|
||||
$(".danger").next('tr').toggle(this.checked);
|
||||
}
|
||||
hideOrShow()
|
||||
});
|
||||
$('#showErrors').on('click', function(){
|
||||
$(".warning").toggle(this.checked);
|
||||
if (this.checked == false) {
|
||||
$(".warning").next('tr').toggle(this.checked);
|
||||
}
|
||||
hideOrShow()
|
||||
});
|
||||
$('#showSkipped').on('click', function(){
|
||||
$(".info").toggle(this.checked);
|
||||
if (this.checked == false) {
|
||||
$(".info").next('tr').toggle(this.checked);
|
||||
}
|
||||
hideOrShow()
|
||||
});
|
||||
});
|
||||
|
||||
+22
-12
@@ -14,19 +14,18 @@ import unittest
|
||||
import xbmc
|
||||
|
||||
if 'KOD_TST_CH' not in os.environ:
|
||||
from sakee import addoninfo
|
||||
# custom paths
|
||||
def add_on_info(*args, **kwargs):
|
||||
return xbmc.AddonData(
|
||||
return addoninfo.AddonData(
|
||||
kodi_home_path=os.path.join(os.getcwd(), 'tests', 'home'),
|
||||
add_on_id='plugin.video.kod',
|
||||
add_on_path=os.getcwd(),
|
||||
kodi_profile_path=os.path.join(os.getcwd(), 'tests', 'home', 'userdata')
|
||||
)
|
||||
|
||||
|
||||
# override
|
||||
xbmc.get_add_on_info_from_calling_script = add_on_info
|
||||
|
||||
addoninfo.get_add_on_info_from_calling_script = add_on_info
|
||||
|
||||
# functions that on kodi 19 moved to xbmcvfs
|
||||
try:
|
||||
@@ -49,10 +48,14 @@ sys.path.insert(0, librerias)
|
||||
from core.support import typo
|
||||
from core.item import Item
|
||||
from core.httptools import downloadpage
|
||||
from core import servertools
|
||||
from core import servertools, httptools
|
||||
import channelselector
|
||||
import re
|
||||
|
||||
|
||||
httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT = 60
|
||||
|
||||
outDir = os.path.join(os.getcwd(), 'reports')
|
||||
validUrlRegex = re.compile(
|
||||
r'^(?:http|ftp)s?://' # http:// or https://
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
|
||||
@@ -144,7 +147,7 @@ channels = []
|
||||
|
||||
channel_list = channelselector.filterchannels("all") if 'KOD_TST_CH' not in os.environ else [Item(channel=os.environ['KOD_TST_CH'], action="mainlist")]
|
||||
logger.info([c.channel for c in channel_list])
|
||||
ret = []
|
||||
results = []
|
||||
|
||||
logger.record = True
|
||||
for chItem in channel_list:
|
||||
@@ -179,8 +182,6 @@ for chItem in channel_list:
|
||||
|
||||
itemlist = getattr(module, it.action)(it)
|
||||
menuItemlist[it.title] = itemlist
|
||||
logMenu[it.title] = logger.recordedLog
|
||||
logger.recordedLog = ''
|
||||
|
||||
# some sites might have no link inside, but if all results are without servers, there's something wrong
|
||||
for resIt in itemlist:
|
||||
@@ -206,9 +207,15 @@ for chItem in channel_list:
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
logMenu[it.title] = logger.recordedLog
|
||||
logger.recordedLog = ''
|
||||
|
||||
logMenu[it.title] = logger.recordedLog
|
||||
logger.recordedLog = ''
|
||||
|
||||
# results.append(
|
||||
# {'ch': ch, 'hasChannelConfig': hasChannelConfig, 'mainlist': [it.title for it in mainlist],
|
||||
# 'menuItemlist': {k: [it.tojson() if type(it) == Item else it for it in menuItemlist[k]] for k in menuItemlist.keys()},
|
||||
# 'serversFound': {k: [it.tojson() if type(it) == Item else it for it in menuItemlist[k]] for k in menuItemlist.keys()},
|
||||
# 'module': str(module), 'logMenu': logMenu, 'error': error})
|
||||
channels.append(
|
||||
{'ch': ch, 'hasChannelConfig': hasChannelConfig, 'mainlist': mainlist, 'menuItemlist': menuItemlist,
|
||||
'serversFound': serversFound, 'module': module, 'logMenu': logMenu, 'error': error})
|
||||
@@ -217,7 +224,10 @@ logger.record = False
|
||||
|
||||
from specials import news
|
||||
dictNewsChannels, any_active = news.get_channels_list()
|
||||
print(channels)
|
||||
# if not os.path.isdir(outDir):
|
||||
# os.mkdir(outDir)
|
||||
# json.dump(results, open(os.path.join(outDir, 'result.json'), 'w'))
|
||||
|
||||
# only 1 server item for single server
|
||||
serverNames = []
|
||||
serversFinal = []
|
||||
@@ -350,6 +360,6 @@ if __name__ == '__main__':
|
||||
unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(report_name='report', add_timestamp=False, combine_reports=True,
|
||||
report_title='KoD Test Suite', template=os.path.join(config.get_runtime_path(), 'tests', 'template.html')), exit=False)
|
||||
import webbrowser
|
||||
webbrowser.open(os.path.join(config.get_runtime_path(), 'reports', 'report.html'))
|
||||
webbrowser.open(os.path.join(outDir, 'report.html'))
|
||||
else:
|
||||
unittest.main()
|
||||
|
||||
+15
-2
@@ -58,9 +58,20 @@ if __name__ == '__main__':
|
||||
# redirect
|
||||
elif str(rslt['code']).startswith('3'):
|
||||
# result[chann] = str(rslt['code']) +' - '+ rslt['redirect'][:-1]
|
||||
if rslt['redirect'].endswith('/'):
|
||||
rslt['redirect'] = rslt['redirect'][:-1]
|
||||
result[chann] = rslt['redirect']
|
||||
# cloudflare...
|
||||
elif rslt['code'] in [429, 503, 403]:
|
||||
from lib import proxytranslate
|
||||
import re
|
||||
|
||||
print('Cloudflare riconosciuto')
|
||||
try:
|
||||
page_data = proxytranslate.process_request_proxy(host).get('data', '')
|
||||
result[chann] = re.search('<base href="([^"]+)', page_data).group(1)
|
||||
rslt['code_new'] = 200
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
# non-existent site
|
||||
elif rslt['code'] == -2:
|
||||
print('Host Sconosciuto - '+ str(rslt['code']) +' - '+ host)
|
||||
@@ -72,6 +83,8 @@ if __name__ == '__main__':
|
||||
print('Errore Sconosciuto - '+str(rslt['code']) +' - '+ host)
|
||||
|
||||
print("check #### FINE #### rslt :%s " % (rslt))
|
||||
if result[chann].endswith('/'):
|
||||
result[chann] = result[chann][:-1]
|
||||
|
||||
result = {'findhost': data['findhost'], 'direct': result}
|
||||
# I write the updated file
|
||||
|
||||
Reference in New Issue
Block a user