- 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:
mac12m99
2021-02-13 16:37:02 +01:00
parent 0ebc744115
commit 748fad7431
64 changed files with 2203 additions and 712 deletions
+33
View File
@@ -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'
+5 -3
View File
@@ -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
+7 -5
View File
@@ -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>
+52 -52
View File
@@ -1,59 +1,59 @@
{
"direct": {
"altadefinizione01_link": "https://altadefinizione01.fitness",
"animealtadefinizione": "https://www.animealtadefinizione.it",
"animeforce": "https://www.animeforce.it",
"animeleggendari": "https://animeora.com",
"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/",
"animeworld": "https://www.animeworld.tv",
"casacinema": "https://www.casacinema.page",
"cb01anime": "https://www.cineblog01.red",
"cinemalibero": "https://cinemalibero.store",
"cinetecadibologna": "http://cinestore.cinetecadibologna.it",
"dreamsub": "https://dreamsub.stream",
"dsda": "https://www.dsda.press",
"eurostreaming": "https://eurostreaming.chat",
"fastsubita": "https://fastsubita.xyz",
"filmgratis": "https://www.filmaltadefinizione.me",
"filmigratis": "https://filmigratis.org",
"filmsenzalimiticc": "https://www.filmsenzalimiti01.online",
"filmstreaming01": "https://filmstreaming01.com",
"guardaserie_stream": "https://guardaserie.host",
"guardaseriecam": "https://guardaserie.cam",
"guardaserieclick": "https://www.guardaserie.directory",
"guardaserieicu": "https://guardaserie.shop",
"hd4me": "https://hd4me.net",
"ilcorsaronero": "https://ilcorsaronero.link",
"ilgeniodellostreaming": "https://ilgeniodellostreaming.cat",
"ilgeniodellostreaming_cam": "https://ilgeniodellostreaming.gold",
"italiaserie": "https://italiaserie.top",
"mediasetplay": "https://www.mediasetplay.mediaset.it",
"mondoserietv": "https://mondoserietv.fun",
"paramount": "https://www.paramountnetwork.it",
"piratestreaming": "https://www.piratestreaming.camp",
"polpotv": "https://roma.polpo.tv",
"raiplay": "https://www.raiplay.it",
"serietvonline": "https://serietvonline.cam",
"serietvsubita": "http://serietvsubita.xyz",
"serietvu": "https://www.serietvu.link",
"streamingcommunity": "https://streamingcommunity.net",
"streamtime": "https://t.me/s/StreamTime",
"toonitalia": "https://toonitalia.pro",
"altadefinizione01_link": "https://altadefinizione01.market",
"animealtadefinizione": "https://www.animealtadefinizione.it",
"animeforce": "https://www.animeforce.it",
"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",
"animeworld": "https://www.animeworld.tv",
"casacinema": "https://www.casacinema.page",
"cb01anime": "https://www.cineblog01.red",
"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.tube",
"filmgratis": "https://www.filmaltadefinizione.me",
"filmigratis": "https://filmigratis.org",
"filmsenzalimiticc": "https://www.filmsenzalimiti01.work",
"filmstreaming01": "https://filmstreaming01.com",
"guardaserie_stream": "https://guardaserie.host",
"guardaseriecam": "https://guardaserie.cam",
"guardaserieclick": "https://www.guardaserie.kim",
"guardaserieicu": "https://guardaserie.shop",
"hd4me": "https://hd4me.net",
"ilcorsaronero": "https://ilcorsaronero.link",
"ilgeniodellostreaming": "https://ilgeniodellostreaming.pub",
"ilgeniodellostreaming_cam": "https://ilgeniodellostreaming.gold",
"italiaserie": "https://italiaserie.bid",
"mediasetplay": "https://www.mediasetplay.mediaset.it",
"mondoserietv": "https://mondoserietv.fun",
"paramount": "https://www.paramountnetwork.it",
"piratestreaming": "https://www.piratestreaming.live",
"polpotv": "https://roma.polpo.tv",
"raiplay": "https://www.raiplay.it",
"serietvonline": "https://serietvonline.website",
"serietvsubita": "http://serietvsubita.xyz",
"serietvu": "https://www.serietvu.link",
"streamingcommunity": "https://streamingcommunity.net",
"streamtime": "https://t.me/s/StreamTime",
"toonitalia": "https://toonitalia.pro",
"vvvvid": "https://www.vvvvid.it"
},
},
"findhost": {
"altadefinizione01": "https://altadefinizione01-nuovo.info",
"altadefinizioneclick": "https://altadefinizione-nuovo.me",
"animealtadefinizione": "https://www.animealtadefinizione.it",
"cineblog01": "https://cb01.uno",
"film4k": "https://film4k-nuovo.link",
"filmpertutti": "https://filmpertutti.nuovo.live",
"seriehd": "https://nuovoindirizzo.info/seriehd/",
"altadefinizione01": "https://altadefinizione01-nuovo.info",
"altadefinizioneclick": "https://altadefinizione-nuovo.me",
"animealtadefinizione": "https://www.animealtadefinizione.it",
"cineblog01": "https://cb01.uno",
"film4k": "https://film4k-nuovo.link",
"filmpertutti": "https://filmpertutti.nuovo.live",
"seriehd": "https://nuovoindirizzo.info/seriehd",
"tantifilm": "https://www.tantifilm.wiki"
}
}
+3 -2
View File
@@ -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>.+?)\])?<'
+3 -3
View File
@@ -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
View File
@@ -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()
+1 -1
View File
@@ -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
+3 -1
View File
@@ -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>.+?)(?: &#8211; 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>.+?)(?: &#8211; 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
+12
View File
@@ -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": []
}
+186
View File
@@ -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)
+1 -1
View File
@@ -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',))
+2 -1
View File
@@ -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 &raquo;</a>'
return locals()
+1 -1
View File
@@ -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
View File
@@ -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()
+10 -9
View File
@@ -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 []
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+3 -2
View File
@@ -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="([^"]+)">&raquo'
patron= r'<img src="[^"]+" alt="(?P<title>[^"]+)" data-echo="(?P<thumb>[^"]+)"(?:[^>]+>){7}<a href="(?P<url>[^"]+)"'
patronNext = r'<a href="([^"]+)">(?:&raquo|»)'
typeContentDict = {'': 'music'}
return locals()
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+3 -3
View File
@@ -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>[^"]+)"(?:|.+?)?>(?:&nbsp;&nbsp;)?[ ]?(?P<title>.*?)[ ]?(?P<quality>HD)?[ ]?(?P<year>\d+)?(?: | HD | Streaming | MD(?: iSTANCE)? )?</a>'
+23 -18
View File
@@ -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
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -27,6 +27,5 @@ from platformcode import launcher
if sys.argv[2] == "":
launcher.start()
launcher.run()
else:
launcher.run()
launcher.run()
+4
View File
@@ -0,0 +1,4 @@
# __init__.py
# Version of the fuzzy-match package
__version__ = "0.0.1"
+181
View File
@@ -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
+78
View File
@@ -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
View File
@@ -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=(.*?)&amp;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('&amp;', '&').replace('https://translate.google.com/website?sl=' + SL + '&tl=' + TL + '&u=', '')
return {'url': url.strip(), 'result': result, 'data': data.replace('&amp;', '&')}
return {'url': url.strip(), 'result': result, 'data': data}
except Exception as e:
logger.error(e)
if logger:
logger.error(e)
else:
print(e)
+601
View File
@@ -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__)
+152 -196
View File
@@ -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()
@@ -754,4 +710,4 @@ DOWN = 4
EXIT = 10
BACKSPACE = 92
path = config.get_runtime_path()
path = config.get_runtime_path()
+15 -4
View File
@@ -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
View File
@@ -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)
+77 -45
View File
@@ -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):
BIN
View File
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.
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1
View File
@@ -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 '')
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+2
View File
@@ -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"
+14 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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