Files
addon/core/videolibrarytools.py
2021-05-25 20:00:11 +02:00

1059 lines
48 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Common Library Tools
# ------------------------------------------------------------
#from builtins import str
# from specials import videolibrary
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
from concurrent import futures
else:
from concurrent_py2 import futures
import errno, math, traceback, re, os
from core import filetools, jsontools, scraper, scrapertools, support, db, httptools, tmdb
from core.item import InfoLabels, Item
from lib import generictools
from platformcode import config, logger, platformtools
from platformcode.autorenumber import RENUMBER
from core.videolibrarydb import videolibrarydb
FOLDER_MOVIES = config.get_setting("folder_movies")
FOLDER_TVSHOWS = config.get_setting("folder_tvshows")
VIDEOLIBRARY_PATH = config.get_videolibrary_path()
MOVIES_PATH = filetools.join(VIDEOLIBRARY_PATH, FOLDER_MOVIES)
TVSHOWS_PATH = filetools.join(VIDEOLIBRARY_PATH, FOLDER_TVSHOWS)
if not FOLDER_MOVIES or not FOLDER_TVSHOWS or not VIDEOLIBRARY_PATH or not filetools.exists(MOVIES_PATH) or not filetools.exists(TVSHOWS_PATH):
config.verify_directories_created()
addon_name = "plugin://plugin.video.%s/" % config.PLUGIN_NAME
quality_order = ['4k', '2160p', '2160', '4k2160p', '4k2160', '4k 2160p', '4k 2160', '2k',
'fullhd', 'fullhd 1080', 'fullhd 1080p', 'full hd', 'full hd 1080', 'full hd 1080p', 'hd1080', 'hd1080p', 'hd 1080', 'hd 1080p', '1080', '1080p',
'hd', 'hd720', 'hd720p', 'hd 720', 'hd 720p', '720', '720p', 'hdtv',
'sd', '480p', '480', '360p', '360', '240p', '240']
video_extensions = ['3g2', '3gp', '3gp2', 'asf', 'avi', 'divx', 'flv', 'iso', 'm4v', 'mk2', 'mk3d', 'mka', 'mkv', 'mov', 'mp4', 'mp4a', 'mpeg', 'mpg', 'ogg', 'ogm', 'ogv', 'qt', 'ra', 'ram', 'rm', 'ts', 'vob', 'wav', 'webm', 'wma', 'wmv']
subtitle_extensions = ['srt', 'idx', 'sub', 'ssa', 'ass']
immage_extensions = ['jpg', 'png']
def read_nfo(path_nfo, item=None):
"""
Method to read nfo files.
Nfo files have the following structure: url_scraper | xml + item_json [url_scraper] and [xml] are optional, but only one of them must always exist.
@param path_nfo: absolute path to nfo file
@type path_nfo: str
@param item: If this parameter is passed the returned item will be a copy of it with the values of 'infoLabels', 'library_playcounts' and 'path' read from the nfo
@type: Item
@return: A tuple consisting of the header (head_nfo = 'url_scraper' | 'xml') and the object 'item_json'
@rtype: tuple (str, Item)
"""
head_nfo = ""
it = None
data = filetools.read(path_nfo)
if data:
head_nfo = data.splitlines()[0] + "\n"
data = "\n".join(data.splitlines()[1:])
it_nfo = Item().fromjson(data)
if not it_nfo.library_playcounts: # may be corrupted
it_nfo.library_playcounts = {}
if item:
it = item.clone()
it.infoLabels = it_nfo.infoLabels
if 'library_playcounts' in it_nfo:
it.library_playcounts = it_nfo.library_playcounts
if it_nfo.path:
it.path = it_nfo.path
else:
it = it_nfo
if 'fanart' in it.infoLabels:
it.fanart = it.infoLabels['fanart']
return head_nfo, it
def set_base_name(item, _id):
# set base_name for videolibrary
logger.debug()
if item.contentType == 'movie':
if config.get_setting("original_title_folder", "videolibrary") and item.infoLabels['originaltitle']:
base_name = item.infoLabels['originaltitle']
else:
base_name = item.contentTitle
else:
if config.get_setting("original_title_folder", "videolibrary") and item.infoLabels['originaltitle']:
base_name = item.infoLabels['originaltitle']
elif item.infoLabels['tvshowtitle']:
base_name = item.infoLabels['tvshowtitle']
elif item.infoLabels['title']:
base_name = item.infoLabels['title']
else:
base_name = item.contentSerieName
if not PY3:
base_name = unicode(filetools.validate_path(base_name.replace('/', '-')), "utf8").encode("utf8")
else:
base_name = filetools.validate_path(base_name.replace('/', '-'))
if config.get_setting("lowerize_title", "videolibrary"):
base_name = base_name.lower()
return '{} [{}]'.format(base_name, _id)
def save_movie(item, silent=False):
"""
saves the item element in the movie library, with the values it contains.
@type item: item
@param item: item to be saved.
@rtype inserted: int
@return: the number of elements inserted
@rtype overwritten: int
@return: the number of overwritten elements
@rtype failed: int
@return: the number of failed items or -1 if all failed
"""
logger.debug()
# logger.debug(item.tostring('\n'))
inserted = 0
overwritten = 0
failed = 0
path = ""
# Put the correct title on your site so that scraper can locate it
if not item.contentTitle:
if item.fulltitle: item.contentTitle = item.fulltitle
else: item.contentTitle = re.sub(r'\[\s*[^\]]+\]', '', item.title).strip()
# If at this point we do not have a title, we leave
if not item.contentTitle or not item.channel:
logger.debug("contentTitle NOT FOUND")
return 0, 0, -1, path # Salimos sin guardar
# At this point we can have:
# scraper_return = True: An item with infoLabels with the updated information of the movie
# scraper_return = False: An item without movie information (it has been canceled in the window)
# item.infoLabels['code'] == "" : The required IMDB identifier was not found to continue, we quit
if not item.infoLabels['code']:
logger.debug("NOT FOUND IN SCRAPER OR DO NOT HAVE code")
return 0, 0, -1, path
# Get ID from infoLabels
_id = get_id(item)
if not _id:
logger.debug("NOT FOUND IN SCRAPER OR DO NOT HAVE code")
return 0, 0, -1, path
# get parameters from db
try:
moviedb = videolibrarydb['movie'].get(_id, {})
movie_item = moviedb.get('item', Item())
head_nfo = movie_item.head_nfo
channels = moviedb.get('channels',{})
except:
logger.debug("The film cannot be added to the database")
videolibrarydb.close()
return 0, 0, -1, path
# progress dialog
if not silent: p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(20000), config.get_localized_string(60062))
base_name = set_base_name(item, _id)
path = filetools.join(MOVIES_PATH, base_name)
# check if path already exist
if not filetools.exists(path):
logger.debug("Creating movie directory:" + path)
if not filetools.mkdir(path):
logger.debug("Could not create directory")
videolibrarydb.close()
return 0, 0, -1, path
# try:
# set nfo and strm paths
nfo_path = filetools.join(base_name, "{}.nfo".format(base_name))
strm_path = filetools.join(base_name, "{}.strm".format(base_name))
# check if nfo and strm file exist
nfo_exists = filetools.exists(filetools.join(MOVIES_PATH, nfo_path))
strm_exists = filetools.exists(filetools.join(MOVIES_PATH, strm_path))
if not head_nfo:
head_nfo = scraper.get_nfo(item)
# get extra info from fanart tv
# support.dbg()
extra_info = get_fanart_tv(item)
if not item.infoLabels.get('posters', []): item.infoLabels['posters'] = []
item.infoLabels['posters'] += extra_info['poster']
if not item.infoLabels.get('fanarts', []): item.infoLabels['fanarts'] = []
item.infoLabels['fanarts'] += extra_info['fanart']
if not item.infoLabels.get('clearlogos', []): item.infoLabels['clearlogos'] = []
item.infoLabels['clearlogos'] += extra_info['clearlogo']
if not item.infoLabels.get('cleararts', []): item.infoLabels['cleararts'] = []
item.infoLabels['cleararts'] += extra_info['clearart']
if not item.infoLabels.get('landscapes', []): item.infoLabels['landscapes'] = []
item.infoLabels['landscapes'] += extra_info['landscape']
if not item.infoLabels.get('banners', []): item.infoLabels['banners'] = []
item.infoLabels['banners'] += extra_info['banner']
if not item.infoLabels.get('discs', []): item.infoLabels['discs'] = []
item.infoLabels['discs'] += extra_info['disc']
if 'setid' in item.infoLabels:
collection = videolibrarydb['collection'].get(item.infoLabels['setid'], None)
if not collection:
collection = Item(title=item.infoLabels['set'],
plot=item.infoLabels['setoverview'],
infoLabels={},
thumbnail=item.infoLabels.get('setposters')[0] if item.infoLabels.get('setposters') else item.thumbnail,
fanart=item.infoLabels.get('setfanarts')[0] if item.infoLabels.get('setfanarts') else item.fanart,
videolibrary_id = item.infoLabels['setid'],
set = item.infoLabels['setid'],
channel = "videolibrary",
action='list_movies')
if not collection.infoLabels.get('posters') and item.infoLabels.get('setposters'):
collection.infoLabels['posters'] = item.infoLabels['setposters']
if not collection.infoLabels.get('fanarts') and item.infoLabels.get('fanarts'):
collection.infoLabels['fanarts'] = item.infoLabels['setfanarts']
if not collection.infoLabels.get('clearlogos') and extra_info.get('setclearlogo'):
collection.infoLabels['clearlogos'] = extra_info['setclearlogo']
collection.infoLabels['clearlogo'] = extra_info['setclearlogo'][0]
if not collection.infoLabels.get('cleararts') and extra_info.get('setclearart'):
collection.infoLabels['cleararts'] = extra_info['setclearart']
collection.infoLabels['clearart'] = extra_info['setclearart'][0]
if not collection.infoLabels.get('landscapes') and extra_info.get('setlandscape'):
collection.infoLabels['landscapes'] = extra_info['setlandscape']
collection.infoLabels['landscape'] = extra_info['setlandscape'][0]
if not collection.infoLabels.get('banners') and extra_info.get('setbanner'):
collection.infoLabels['banners'] = extra_info['setbanner']
collection.infoLabels['banner'] = extra_info['setbanner'][0]
if not collection.infoLabels.get('discs') and extra_info.get('setdisc'):
collection.infoLabels['discs'] = extra_info['setdisc']
collection.infoLabels['disc'] = extra_info['setdisc'][0]
videolibrarydb['collection'][item.infoLabels['setid']] = collection
# Make or update Videolibrary Movie Item
movie_item.channel = "videolibrary"
movie_item.action = 'findvideos'
movie_item.infoLabels = item.infoLabels
movie_item.infoLabels['playcount'] = 0
if not movie_item.head_nfo: movie_item.head_nfo = head_nfo
if not movie_item.title: movie_item.title = item.contentTitle
if not movie_item.videolibrary_id: movie_item.videolibrary_id = _id
if not movie_item.strm_path: movie_item.strm_path = strm_path
if not movie_item.nfo_path: movie_item.nfo_path = nfo_path
if not movie_item.base_name: movie_item.base_name = base_name
if not movie_item.thumbnail: movie_item.thumbnail = item.infoLabels['thumbnail']
if not movie_item.fanart: movie_item.fanart = item.infoLabels['fanart']
if not movie_item.infoLabels['landscape'] and item.infoLabels['landscapes']: movie_item.infoLabels['landscape'] = item.infoLabels['landscapes'][0]
if not movie_item.infoLabels['banner'] and item.infoLabels['banners']: movie_item.infoLabels['banner']= item.infoLabels['banners'][0]
if not movie_item.infoLabels['clearart'] and item.infoLabels['cleararts']: movie_item.infoLabels['clearart'] = item.infoLabels['cleararts'][0]
if not movie_item.infoLabels['clearlogo'] and item.infoLabels['clearlogos']: movie_item.infoLabels['clearlogo'] = item.infoLabels['clearlogos'][0]
if not movie_item.infoLabels['disc'] and item.infoLabels['discs']: movie_item.infoLabels['disc'] = item.infoLabels['discs'][0]
if not movie_item.playtime: movie_item.playtime = 0,
if not movie_item.playcounts: movie_item.playcounts = 0,
if not movie_item.prefered_lang: movie_item.prefered_lang = ''
if not movie_item.lang_list: movie_item.lang_list = []
# if not movie_item.info: movie_item.info = extra_info(_id)
if not item.contentLanguage: item.contentLanguage = 'ITA'
if not item.contentLanguage in movie_item.lang_list: movie_item.lang_list.append(item.contentLanguage)
if len(movie_item.lang_list) > 1:
movie_item.prefered_lang = movie_item.lang_list[platformtools.dialog_select(config.get_localized_string(70246), movie_item.lang_list)]
else:
movie_item.prefered_lang = movie_item.lang_list[0]
# create nfo file if it does not exist
# support.dbg()
if not nfo_exists:
# data = dicttoxml(movie_item)
filetools.write(filetools.join(MOVIES_PATH, movie_item.nfo_path), head_nfo)
# create strm file if it does not exist
if not strm_exists:
logger.debug("Creating .strm: " + strm_path)
item_strm = Item(channel='videolibrary', action='play_from_library', strm_path=movie_item.strm_path, contentType='movie', contentTitle=item.contentTitle, videolibraryd_id=item.videolibrary_id)
strm_exists = filetools.write(filetools.join(MOVIES_PATH, movie_item.strm_path), '{}?{}'.format(addon_name, item_strm.tourl()))
# checks if the content already exists
if videolibrarydb['movie'].get(_id, {}):
logger.debug("The file exists. Is overwritten")
overwritten += 1
else:
logger.debug("Creating .nfo: " + nfo_path)
inserted += 1
remove_host(item)
# write on db
if item.channel in channels and item.channel != 'download':
channels_url = [u.url for u in channels[item.channel]]
if item.url not in channels_url:
channels[item.channel].append(item)
else:
del channels[item.channel][channels_url.index(item.url)]
channels[item.channel].append(item)
else:
channels[item.channel] = [item]
moviedb['item'] = movie_item
moviedb['channels'] = channels
videolibrarydb['movie'][_id] = moviedb
# except:
# failed += 1
videolibrarydb.close()
# Only if movie_item and .strm exist we continue
if movie_item and strm_exists:
if not silent:
p_dialog.update(100, item.contentTitle)
p_dialog.close()
# Update Kodi Library
from platformcode.dbconverter import add_video
add_video(movie_item)
# if config.is_xbmc() and config.get_setting("videolibrary_kodi") and not silent and inserted:
# from platformcode.xbmc_videolibrary import update
# update(MOVIES_PATH)
return inserted, overwritten, failed, path
# If we get to this point it is because something has gone wrong
logger.error("Could not save %s in the video library" % item.contentTitle)
if not silent:
p_dialog.update(100, item.contentTitle)
p_dialog.close()
return 0, 0, -1, path
def update_renumber_options(item):
from core import jsontools
filename = filetools.join(config.get_data_path(), "settings_channels", item.channel + '_data.json')
if filetools.isfile(filename):
json_file = jsontools.load(filetools.read(filename))
json = json_file.get(RENUMBER,{}).get(item.fulltitle,{})
if json:
logger.debug('UPDATED=\n' + item.fulltitle)
item.renumber = json
return item
def add_renumber_options(item):
from core import jsontools
# from core.support import dbg;dbg()
ret = None
filename = filetools.join(config.get_data_path(), "settings_channels", item.channel + '_data.json')
json_file = jsontools.load(filetools.read(filename))
if RENUMBER in json_file:
json = json_file[RENUMBER]
if item.fulltitle in json:
ret = json[item.fulltitle]
return ret
def check_renumber_options(item):
from platformcode.autorenumber import load, write
for key in item.channel_prefs:
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][RENUMBER]
write(item, json)
# head_nfo, tvshow_item = read_nfo(filetools.join(item.context[0]['nfo']))
# if tvshow_item['channel_prefs'][item.fullti]
def save_tvshow(item, episodelist, silent=False):
"""
stores in the series library the series with all the chapters included in the episodelist
@type item: item
@param item: item that represents the series to save
@type episodelist: list
@param episodelist: list of items that represent the episodes to be saved.
@rtype inserted: int
@return: the number of episodes inserted
@rtype overwritten: int
@return: the number of overwritten episodes
@rtype failed: int
@return: the number of failed episodes or -1 if the entire series has failed
@rtype path: str
@return: serial directory
"""
inserted = 0
overwritten = 0
failed = 0
path = ""
# support.dbg()
# If at this point we do not have a title or code, we leave
if not (item.contentSerieName or item.infoLabels['code']) or not item.channel:
logger.debug("NOT FOUND contentSerieName or code")
return 0, 0, -1, path # Salimos sin guardar
contentTypeBackup = item.contentType # Fix errors in some channels
item.contentType = contentTypeBackup # Fix errors in some channels
# At this point we can have:
# scraper_return = True: An item with infoLabels with the updated information of the series
# scraper_return = False: An item without movie information (it has been canceled in the window)
# item.infoLabels['code'] == "" :T he required IMDB identifier was not found to continue, we quit
if not item.infoLabels['code']:
logger.debug("NOT FOUND IN SCRAPER OR DO NOT HAVE code")
return 0, 0, -1, path
# Get ID from infoLabels
_id = get_id(item)
if not _id:
logger.debug("NOT FOUND IN SCRAPER OR DO NOT HAVE code")
return 0, 0, -1, path
# get parameters from db
try:
tvshowdb = videolibrarydb['tvshow'].get(_id, {})
tvshow_item = tvshowdb.get('item', Item())
head_nfo = tvshow_item.head_nfo
channels = tvshowdb.get('channels',{})
except:
logger.debug("The tv show cannot be added to the database")
videolibrarydb.close()
return 0, 0, -1, path
# set base name
base_name = set_base_name(item, _id)
path = filetools.join(TVSHOWS_PATH, base_name)
# check if path already exist
if not filetools.exists(path):
logger.debug("Creating tv show directory:" + path)
if not filetools.mkdir(path):
logger.debug("Could not create directory")
return 0, 0, -1, path
nfo_path = filetools.join(base_name, "tvshow.nfo")
nfo_exists = filetools.exists(filetools.join(TVSHOWS_PATH, nfo_path))
# get parameters
if not item.head_nfo:
head_nfo = scraper.get_nfo(item)
if not head_nfo: return 0, 0, -1, ''
# support.dbg()
extra_info = get_fanart_tv(item)
if not item.infoLabels.get('posters'): item.infoLabels['posters'] = []
item.infoLabels['posters'] += extra_info['poster'].get('all',[])
if not item.infoLabels.get('fanarts'): item.infoLabels['fanarts'] = []
item.infoLabels['fanarts'] += extra_info['fanart']
if not item.infoLabels.get('clearlogos'): item.infoLabels['clearlogos'] = []
item.infoLabels['clearlogos'] += extra_info['clearlogo']
if not item.infoLabels.get('cleararts'): item.infoLabels['cleararts'] = []
item.infoLabels['cleararts'] += extra_info['clearart']
if not item.infoLabels.get('landscapes'): item.infoLabels['landscapes'] = []
item.infoLabels['landscapes'] += extra_info['landscape'].get('all',[])
if not item.infoLabels.get('banners'): item.infoLabels['banners'] = []
item.infoLabels['banners'] += extra_info['banner'].get('all',[])
item.infoLabels['mediatype'] = 'tvshow'
item.contentType = 'tvshow'
item.infoLabels['title'] = item.contentSerieName
tvshow_item.infoLabels = item.infoLabels
if not tvshow_item.infoLabels.get('playcount'): tvshow_item.infoLabels['playcount'] = 0
tvshow_item.channel = 'videolibrary'
tvshow_item.action = 'get_seasons'
tvshow_item.nfo_path = nfo_path
if not tvshow_item.head_nfo: tvshow_item.head_nfo = head_nfo
if not tvshow_item.title: tvshow_item.title = item.contentSerieName
if not tvshow_item.videolibrary_id: tvshow_item.videolibrary_id = _id
if not tvshow_item.thumbnail: tvshow_item.thumbnail = item.infoLabels['thumbnail']
if not tvshow_item.fanart: tvshow_item.fanart = item.infoLabels['fanart']
if not tvshow_item.landscape: tvshow_item.landscape = item.infoLabels['landscapes'][0] if item.infoLabels['landscapes'] else item.infoLabels['fanart']
if not tvshow_item.banner and item.infoLabels['banners']: tvshow_item.clearart = item.infoLabels['banners'][0]
if not tvshow_item.clearart and item.infoLabels['cleararts']: tvshow_item.clearart = item.infoLabels['cleararts'][0]
if not tvshow_item.clearlogo and item.infoLabels['clearlogos']: tvshow_item.clearlogo = item.infoLabels['clearlogos'][0]
if not tvshow_item.base_name: tvshow_item.base_name = base_name
if tvshow_item.active == '': tvshow_item.active = True
if not tvshow_item.prefered_lang: tvshow_item.prefered_lang = ''
if not tvshow_item.lang_list: tvshow_item.lang_list = []
remove_host(item)
item.renumber = add_renumber_options(item)
# write on db
if item.channel in channels and item.channel != 'download':
channels_url = [u.url for u in channels[item.channel]]
if item.url not in channels_url:
channels[item.channel].append(item)
else:
del channels[item.channel][channels_url.index(item.url)]
channels[item.channel].append(item)
else:
channels[item.channel] = [item]
tvshowdb['item'] = tvshow_item
tvshowdb['channels'] = channels
videolibrarydb['tvshow'][_id] = tvshowdb
if not nfo_exists:
filetools.write(filetools.join(TVSHOWS_PATH, tvshow_item.nfo_path), head_nfo + ', kod:' + _id)
# support.dbg()
if not episodelist:
# The episode list is empty
return 0, 0, -1, path
# Save the episodes
logger.debug()
inserted, overwritten, failed = save_episodes(tvshow_item, episodelist, extra_info, item.host, silent=silent)
videolibrarydb.close()
# if config.is_xbmc() and config.get_setting("videolibrary_kodi") and not silent and inserted:
# # from platformcode.dbconverter import add_video
# # add_video(tvshow_item)
# from platformcode.xbmc_videolibrary import update
# update(TVSHOWS_PATH, tvshow_item.basename)
return inserted, overwritten, failed, path
def save_episodes(item, episodelist, extra_info, host, silent=False):
"""
saves in the indicated path all the chapters included in the episodelist
@type Item: str
@param path: path to save the episodes
@type episodelist: list
@param episodelist: list of items that represent the episodes to be saved.
@type serie: item
@param serie: series from which to save the episodes
@type silent: bool
@param silent: sets whether notification is displayed
@param overwrite: allows to overwrite existing files
@type overwrite: bool
@rtype inserted: int
@return: the number of episodes inserted
@rtype overwritten: int
@return: the number of overwritten episodes
@rtype failed: int
@return: the number of failed episodes
"""
# support.dbg()
def save_episode(item, episodes, e):
inserted = 0
overwritten = 0
failed = 0
episode = None
season_episode = None
season_episode = scrapertools.get_season_and_episode(e.title)
if season_episode:
strm_path = filetools.join(item.base_name, "{}.strm".format(season_episode))
e.contentSeason = int(season_episode.split('x')[0])
e.contentEpisodeNumber = int(season_episode.split('x')[1])
if item.infoLabels.get('imdb_id'): e.infoLabels['imdb_id'] = item.infoLabels['imdb_id']
if item.infoLabels.get('tmdb_id'): e.infoLabels['tmdb_id'] = item.infoLabels['tmdb_id']
if item.infoLabels.get('tvdb_id'): e.infoLabels['tvdb_id'] = item.infoLabels['tvdb_id']
tmdb.set_infoLabels_item(e)
if not e.infoLabels.get('playcount'): e.infoLabels['playcount'] = 0
head_nfo = scraper.get_nfo(e)
episode_item = Item(action='findvideos',
channel='videolibrary',
strm_path=strm_path,
contentSeason = e.contentSeason,
contentEpisodeNumber = e.contentEpisodeNumber,
contentType = e.contentType,
infoLabels = e.infoLabels,
head_nfo = head_nfo,
videolibrary_id = item.videolibrary_id,
thumbnail = e.infoLabels.get('poster_path') if e.infoLabels.get('poster_path') else item.thumbnail,
fanart = e.infoLabels.get('poster_path') if e.infoLabels.get('poster_path') else item.fanart,
title = '{}. {}'.format(e.contentEpisodeNumber, e.infoLabels['title']))
episode = episodes.get(season_episode, {})
try:
if not episode:
inserted += 1
episode['item'] = episode_item
# else:
epchannels = episode.get('channels',{})
if e.url.startswith(host):
remove_host(e)
e.contentTitle = e.infoLabels['title']
contentType = e.contentType
e.infoLabels = {}
e.contentType = contentType
if e.channel in epchannels and e.channel != 'download':
channels_url = [u.url for u in epchannels[e.channel]]
if e.url not in channels_url:
epchannels[e.channel].append(e)
overwritten += 1
else:
del epchannels[e.channel][channels_url.index(e.url)]
epchannels[e.channel].append(e)
overwritten += 1
else:
epchannels[e.channel] = [e]
overwritten += 1
episode['channels'] = epchannels
if not filetools.exists(filetools.join(TVSHOWS_PATH, strm_path)):
logger.debug("Creating .strm: " + strm_path)
item_strm = Item(channel='videolibrary', action='play_from_library', strm_path=strm_path, contentType='episode', videolibraryd_id=episode_item.videolibrary_id, contentSeason = episode_item.contentSeason, contentEpisodeNumber = episode_item.contentEpisodeNumber,)
filetools.write(filetools.join(TVSHOWS_PATH, strm_path), '{}?{}'.format(addon_name, item_strm.tourl()))
# if not filetools.exists(filetools.join(TVSHOWS_PATH, nfo_path)):
# filetools.write(filetools.join(TVSHOWS_PATH, nfo_path), head_nfo)
except:
logger.error(traceback.format_exc())
failed += 1
return item, episode, season_episode, e.contentLanguage, inserted, overwritten, failed
def save_season(item, seasons, s):
tmdb_info = tmdb.Tmdb(id_Tmdb = item.infoLabels['tmdb_id'], search_type='tv')
seasoninfo = tmdb.get_season_dic(tmdb_info.get_season(s))
infoLabels = {}
if seasoninfo.get('season_posters'): infoLabels['posters'] = seasoninfo.get('season_posters') + extra_info['poster'].get(str(s), [])
if seasoninfo.get('season_fanarts'): infoLabels['fanarts'] = seasoninfo.get('season_fanarts') + extra_info['fanart'].get(str(s), [])
if seasoninfo.get('season_trailer'): infoLabels['trailer'] = seasoninfo.get('season_trailer')
infoLabels['landscapes'] = extra_info['landscape'].get(str(s), [])
infoLabels['banners'] = extra_info['banner'].get(str(s), [])
infoLabels['clearlogos'] = item.infoLabels.get('clearlogos', [])
infoLabels['cleararts'] = item.infoLabels.get('cleararts', [])
if s in seasons: infoLabels['playcount'] = seasons[s].infoLabels.get('playcount', 0)
season_item = Item(action="get_episodes",
channel='videolibrary',
title=seasoninfo.get('season_title'),
thumbnail = seasoninfo.get('season_poster') if seasoninfo.get('season_poster') else item.thumbnail,
fanart = item.fanart,
plot = seasoninfo.get('season_plot') if seasoninfo.get('season_plot') else item.infoLabels.get('plot'),
contentType = 'season',
infoLabels = infoLabels,
contentSeason = s,
videolibrary_id = item.videolibrary_id,
playcount=0)
if infoLabels['clearlogos']: season_item.clearlogo = infoLabels['clearlogos'][0]
if infoLabels['cleararts']: season_item.clearart = infoLabels['cleararts'][0]
if infoLabels['landscapes']: season_item.landscape = infoLabels['landscapes'][0]
if infoLabels['banners']: season_item.banner = infoLabels['banners'][0]
return s, season_item
logger.debug()
# from core import tmdb
# No episode list, nothing to save
if not len(episodelist):
logger.debug("There is no episode list, we go out without creating strm")
return 0, 0, 0
# Silent is to show no progress (for service)
if not silent:
# progress dialog
p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(60064) ,'')
inserted = 0
overwritten = 0
failed = 0
current_seasons = []
seasons = videolibrarydb['season'].get(item.videolibrary_id, {})
episodes = videolibrarydb['episode'].get(item.videolibrary_id, {})
videolibrarydb.close()
try: t = float(100) / len(episodelist)
except: t = 0
# support.dbg()
# for i, e in enumerate(episodelist):
# item, episode, season_episode, lang, I, O, F = save_episode(item, episodes, e)
# inserted += I
# overwritten += O
# failed += F
# if episode:
# episodes[season_episode] = episode
# e = episode['item']
# if not e.contentSeason in current_seasons: current_seasons.append(e.contentSeason)
# if not lang: lang = item.contentLanguage if item.contentLanguage else 'ITA'
# if not lang in item.lang_list: item.lang_list.append(lang)
# if not silent:
# p_dialog.update(int(math.ceil(i * t)), message=e.title)
i = 0
with futures.ThreadPoolExecutor() as executor:
itlist = [executor.submit(save_episode, item, episodes, e) for e in episodelist]
for res in futures.as_completed(itlist):
if res.result():
item, episode, season_episode, lang, I, O, F = res.result()
inserted += I
overwritten += O
failed += F
if episode:
episodes[season_episode] = episode
e = episode['item']
if not e.contentSeason in current_seasons: current_seasons.append(e.contentSeason)
if not lang: lang = item.contentLanguage if item.contentLanguage else 'ITA'
if not lang in item.lang_list: item.lang_list.append(lang)
if not silent:
i += 1
p_dialog.update(int(math.ceil(i * t)), message=e.title)
with futures.ThreadPoolExecutor() as executor:
itlist = [executor.submit(save_season, item, seasons, s) for s in current_seasons]
for res in futures.as_completed(itlist):
if res.result():
s, season_item = res.result()
seasons[s] = season_item
if not silent:
if len(item.lang_list) > 1:
item.prefered_lang = item.lang_list[platformtools.dialog_select(config.get_localized_string(70246), item.lang_list)]
else:
item.prefered_lang = item.lang_list[0]
tvshowdb = videolibrarydb['tvshow'][item.videolibrary_id]
tvshowdb['item'] = item
videolibrarydb['tvshow'][item.videolibrary_id] = tvshowdb
videolibrarydb.close()
videolibrarydb['episode'][item.videolibrary_id] = episodes
videolibrarydb['season'][item.videolibrary_id] = seasons
videolibrarydb.close()
if not silent:
p_dialog.close()
return inserted, overwritten, failed
def config_local_episodes_path(path, item, silent=False):
logger.debug(item)
from platformcode.xbmc_videolibrary import search_local_path
local_episodes_path=search_local_path(item)
if not local_episodes_path:
title = item.contentSerieName if item.contentSerieName else item.fulltitle
if not silent:
silent = platformtools.dialog_yesno(config.get_localized_string(30131), config.get_localized_string(80044) % title)
if silent:
if config.is_xbmc() and not config.get_setting("videolibrary_kodi"):
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(80043))
local_episodes_path = platformtools.dialog_browse(0, config.get_localized_string(80046))
if local_episodes_path == '':
logger.debug("User has canceled the dialog")
return -2, local_episodes_path
elif path in local_episodes_path:
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(80045))
logger.debug("Selected folder is the same of the TV show one")
return -2, local_episodes_path
if local_episodes_path:
# import artwork
artwork_extensions = ['.jpg', '.jpeg', '.png']
files = filetools.listdir(local_episodes_path)
for file in files:
if os.path.splitext(file)[1] in artwork_extensions:
filetools.copy(filetools.join(local_episodes_path, file), filetools.join(path, file))
return 0, local_episodes_path
def process_local_episodes(local_episodes_path, path):
logger.debug()
sub_extensions = ['.srt', '.sub', '.sbv', '.ass', '.idx', '.ssa', '.smi']
artwork_extensions = ['.jpg', '.jpeg', '.png']
extensions = sub_extensions + artwork_extensions
local_episodes_list = []
files_list = []
for root, folders, files in filetools.walk(local_episodes_path):
for file in files:
if os.path.splitext(file)[1] in extensions:
continue
season_episode = scrapertools.get_season_and_episode(file)
if season_episode == "":
continue
local_episodes_list.append(season_episode)
files_list.append(file)
nfo_path = filetools.join(path, "tvshow.nfo")
head_nfo, item_nfo = read_nfo(nfo_path)
# if a local episode has been added, overwrites the strm
for season_episode, file in zip(local_episodes_list, files_list):
if not season_episode in item_nfo.local_episodes_list:
filetools.write(filetools.join(path, season_episode + '.strm'), filetools.join(root, file))
# if a local episode has been removed, deletes the strm
for season_episode in set(item_nfo.local_episodes_list).difference(local_episodes_list):
filetools.remove(filetools.join(path, season_episode + '.strm'))
# updates the local episodes path and list in the nfo
if not local_episodes_list:
item_nfo.local_episodes_path = ''
item_nfo.local_episodes_list = sorted(set(local_episodes_list))
filetools.write(nfo_path, head_nfo + item_nfo.tojson())
def get_local_content(path):
logger.debug()
local_episodelist = []
for root, folders, files in filetools.walk(path):
for file in files:
season_episode = scrapertools.get_season_and_episode(file)
if season_episode == "" or filetools.exists(filetools.join(path, "%s.strm" % season_episode)):
continue
local_episodelist.append(season_episode)
local_episodelist = sorted(set(local_episodelist))
return local_episodelist
def add_movie(item):
"""
Keep a movie at the movie library. The movie can be a link within a channel or a previously downloaded video.
To add locally downloaded episodes, the item must have exclusively:
- contentTitle: title of the movie
- title: title to show next to the list of links -findvideos- ("Play local HD video")
- infoLabels ["tmdb_id"] o infoLabels ["imdb_id"]
- contentType == "movie"
- channel = "downloads"
- url: local path to the video
@type item: item
@param item: item to be saved.
"""
logger.debug()
from platformcode.launcher import set_search_temp; set_search_temp(item)
# To disambiguate titles, TMDB is caused to ask for the really desired title
# The user can select the title among those offered on the first screen
# or you can cancel and enter a new title on the second screen
# 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")
inserted, overwritten, failed, path = save_movie(new_item)
if failed == 0:
platformtools.dialog_notification(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):
"""
Save content in the series library. This content can be one of these two:
- The series with all the chapters included in the episodelist.
- A single chapter previously downloaded locally.
To add locally downloaded episodes, the item must have exclusively:
- contentSerieName (or show): Title of the series
- contentTitle: title of the episode to extract season_and_episode ("1x01 Pilot")
- title: title to show next to the list of links -findvideos- ("Play local video")
- infoLabels ["tmdb_id"] o infoLabels ["imdb_id"]
- contentType != "movie"
- channel = "downloads"
- url: local path to the video
@type item: item
@param item: item that represents the series to save
@type channel: modulo
@param channel: channel from which the series will be saved. By default, item.from_channel or item.channel will be imported.
"""
logger.debug("show=#" + item.show + "#")
from platformcode.launcher import set_search_temp; set_search_temp(item)
if item.channel == "downloads":
itemlist = [item.clone()]
else:
# This mark is because the item has something else apart in the "extra" attribute
# item.action = item.extra if item.extra else item.action
if isinstance(item.extra, str) and "###" in item.extra:
item.action = item.extra.split("###")[0]
item.extra = item.extra.split("###")[1]
if item.from_action:
item.__dict__["action"] = item.__dict__.pop("from_action")
if item.from_channel:
item.__dict__["channel"] = item.__dict__.pop("from_channel")
if not channel:
try:
channel = __import__('channels.%s' % item.channel, fromlist=["channels.%s" % item.channel])
# channel = __import__('specials.%s' % item.channel, fromlist=["specials.%s" % item.channel])
except ImportError:
exec("import channels." + item.channel + " as channel")
# To disambiguate titles, TMDB is caused to ask for the really desired title
# The user can select the title among those offered on the first screen
# or you can cancel and enter a new title on the second screen
# 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 not item: return
#if item.tmdb_stat:
# del item.tmdb_stat # We clean the status so that it is not recorded in the Video Library
# Get the episode list
it = item.clone()
itemlist = getattr(channel, it.action)(it)
item.host = channel.host
if itemlist and not scrapertools.find_single_match(itemlist[0].title, r'[Ss]?(\d+)(?:x|_|\s+)[Ee]?[Pp]?(\d+)'):
from platformcode.autorenumber import start, check
if not check(item):
action = item.action
item.renumber = True
start(item)
item.renumber = False
item.action = action
if not item.exit:
return add_tvshow(item, channel)
itemlist = getattr(channel, item.action)(item)
else:
itemlist = getattr(channel, item.action)(item)
global magnet_caching
magnet_caching = False
inserted, overwritten, failed, path = save_tvshow(item, itemlist)
if not path:
pass
elif not inserted and not overwritten and not failed:
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)
elif failed == -1:
filetools.rmdirtree(path)
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60068) % item.show)
logger.error("The string %s could not be added to the video library" % item.show)
elif failed == -2:
filetools.rmdirtree(path)
elif failed > 0:
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60069) % item.show)
logger.error("Could not add %s episodes of series %s to the video library" % (failed, item.show))
else:
platformtools.dialog_notification(config.get_localized_string(30131), config.get_localized_string(60070) % item.show)
logger.debug("%s episodes of series %s have been added to the video library" % (inserted, item.show))
if config.is_xbmc():
if config.get_setting("sync_trakt_new_tvshow", "videolibrary"):
import xbmc
from platformcode import xbmc_videolibrary
if config.get_setting("sync_trakt_new_tvshow_wait", "videolibrary"):
# Check that you are not looking for content in the Kodi video library
while xbmc.getCondVisibility('Library.IsScanningVideo()'):
xbmc.sleep(1000)
# Synchronization for Kodi video library launched
xbmc_videolibrary.sync_trakt_kodi()
# Synchronization for the addon video library is launched
xbmc_videolibrary.sync_trakt_addon(path)
def remove_host(item):
if PY3: import urllib.parse as urlparse # It is very slow in PY2. In PY3 it is native
else: import urlparse # We use the native of PY2 which is faster
parsed_url = urlparse.urlparse(item.url)
item.url = urlparse.urlunparse(('', '', parsed_url.path, parsed_url.params, parsed_url.query, parsed_url.fragment))
def get_id(item):
_id = ''
for i in item.infoLabels['code']:
if i or i != 'None':
_id = i
break
return _id
def get_local_files(item):
included_files = {}
# search media files in Videolibrary Folder
for root, folder, files in filetools.walk(filetools.join(TVSHOWS_PATH,item.base_name)):
# for folder in folders:
for f in files:
if f.split('.')[-1] in video_extensions:
s, e = scrapertools.find_single_match(f, r'[Ss]?(\d+)(?:x|_|\s+)[Ee]?[Pp]?(\d+)')
included_files['{}x{}'.format(s,e.zfill(2))] = f
if included_files:
return included_files, 1
def get_fanart_tv(item, set='', ret={}):
def set_dict(l):
d = {}
for k in l:
o = d.get(k['season'], [])
o.append(k['url'])
d[k['season']] = o
return d
_id = item.infoLabels.get('tvdb_id', item.infoLabels.get('tmdb_id'))
if _id:
_type = item.contentType.replace('show','').replace('movie','movies')
host = 'http://webservice.fanart.tv/v3/{}/{}?api_key=cab16e262d72fea6a6843d679aa10300'
url = host.format(_type, _id)
res = httptools.downloadpage(url).json
if _type == 'tv':
ret['clearlogo'] = [k.get('url') for k in res.get('hdtvlogo', [])] + [k.get('url') for k in res.get('tvlogo', [])]
ret['clearart'] = [k.get('url') for k in res.get('hdclearart', [])] + [k.get('url') for k in res.get('hdclearart', [])]
ret['fanart'] = set_dict(res.get('showbackground', []))
ret['poster'] = set_dict(res.get('seasonposter', []))
ret['poster']['all'] = [k.get('url') for k in res.get('tvposter', [])]
ret['landscape'] = set_dict(res.get('seasonthumb', []))
ret['landscape']['all'] = [k.get('url') for k in res.get('tvthumb', [])]
ret['banner'] = set_dict(res.get('seasonbanner', []))
ret['banner']['all'] = [k.get('url') for k in res.get('tvbanner', [])]
elif _type == 'movies':
ret[set + 'clearlogo'] = [k.get('url') for k in res.get('hdmovielogo', [])] + [k.get('url') for k in res.get('movielogo', [])]
ret[set + 'poster'] = [k.get('url') for k in res.get('movieposter', [])]
ret[set + 'fanart'] = [k.get('url') for k in res.get('moviebackground', [])]
ret[set + 'clearart'] = [k.get('url') for k in res.get('hdmovieclearart', [])] + [k.get('url') for k in res.get('movieclearart', [])]
ret[set + 'landscape'] = [k.get('url') for k in res.get('moviethumb', [])]
ret[set + 'banner'] = [k.get('url') for k in res.get('moviebanner', [])]
ret[set + 'disc'] = [k.get('url') for k in res.get('moviedisc', [])]
if item.infoLabels.get('setid'):
it = item.clone(infoLabels = {'tmdb_id':item.infoLabels['setid']})
get_fanart_tv(it, 'set', ret)
return ret