import util import xbmcprovider import xbmcutil import xbmcvfs import xbmcgui import xbmc import unicodedata import os import re import time import string import datetime import urllib import sosac class XBMCSosac(xbmcprovider.XBMCMultiResolverContentProvider): last_run = 0 sleep_time = 1000 * 1 * 60 subs = None def __init__(self, provider, settings, addon): xbmcprovider.XBMCMultiResolverContentProvider.__init__( self, provider, settings, addon) provider.parent = self self.dialog = xbmcgui.DialogProgress() try: import StorageServer self.cache = StorageServer.StorageServer("Downloader") except: import storageserverdummy as StorageServer self.cache = StorageServer.StorageServer("Downloader") def make_name(self, text, lower=True): text = self.normalize_filename( text, "-_.' %s%s" % (string.ascii_letters, string.digits)) word_re = re.compile(r'\b\w+\b') text = ''.join([c for c in text if (c.isalnum() or c == "'" or c == '.' or c == '-' or c.isspace())]) if text else '' text = '-'.join(word_re.findall(text)) return text.lower() if lower else text def normalize_filename(self, name, validChars=None): validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits) if (validChars is not None): validFilenameChars = validChars cleanedFilename = self.encode(name) return ''.join(c for c in cleanedFilename if c in validFilenameChars) def service(self): util.info("SOSAC Service Started") try: sleep_time = int(self.getSetting("start_sleep_time")) * 1000 * 60 * 60 except: sleep_time = self.sleep_time pass self.sleep(sleep_time) try: self.last_run = float(self.cache.get("subscription.last_run")) except: self.last_run = time.time() self.cache.set("subscription.last_run", str(self.last_run)) pass if not xbmc.abortRequested and time.time() > self.last_run: self.evalSchedules() while not xbmc.abortRequested: # evaluate subsciptions every 10 minutes if(time.time() > self.last_run + 600): self.evalSchedules() self.last_run = time.time() self.cache.set("subscription.last_run", str(self.last_run)) self.sleep(self.sleep_time) util.info("SOSAC Shutdown") def showNotification(self, title, message, time=1000): xbmcgui.Dialog().notification(self.encode(title), self.encode(message), time=time, icon=xbmc.translatePath( self.addon_dir() + "/icon.png"), sound=False) def evalSchedules(self): if not self.scanRunning() and not self.isPlaying(): notified = False util.info("SOSAC Loading subscriptions") subs = self.get_subs() new_items = False for url, sub in subs.iteritems(): if xbmc.abortRequested: util.info("SOSAC Exiting") return if sub['type'] == sosac.LIBRARY_TYPE_TVSHOW: if self.scanRunning() or self.isPlaying(): self.cache.delete("subscription.last_run") return refresh = int(sub['refresh']) if refresh > 0: next_check = sub['last_run'] + (refresh * 3600 * 24) if next_check < time.time(): if not notified: self.showNotification( 'Subscription', 'Chcecking') notified = True util.debug("SOSAC Refreshing " + url) new_items |= self.run_custom({ 'action': sosac.LIBRARY_ACTION_ADD, 'type': sosac.LIBRARY_TYPE_TVSHOW, 'update': True, 'url': url, 'name': sub['name'], 'refresh': sub['refresh'] }) self.sleep(3000) else: n = (next_check - time.time()) / 3600 util.debug("SOSAC Skipping " + url + " , next check in %dh" % n) if new_items: xbmc.executebuiltin('UpdateLibrary(video)') notified = False else: util.info("SOSAC Scan skipped") def isPlaying(self): return xbmc.Player().isPlaying() def scanRunning(self): return (xbmc.getCondVisibility('Library.IsScanningVideo') or xbmc.getCondVisibility('Library.IsScanningMusic')) def getTVDB(self, name, id): if id: data = util.request('http://thetvdb.com/api/GetSeriesByRemoteID.php?imdbid=tt' + id + '&language=all') tvid = re.search('<id>(\d+)</id>', data) if tvid: return tvid.group(1) shortname = re.search('(.+) (\(\d{4}\))', name).group(1) urllang = [urllib.urlencode({'seriesname': shortname, 'language': 'cs'}), urllib.urlencode({'seriesname': shortname, 'language': 'all'}), urllib.urlencode({'seriesname': name, 'language': 'cs'}), urllib.urlencode({'seriesname': name, 'language': 'all'})] for iter in urllang: data = util.request('http://thetvdb.com/api/GetSeries.php?' + iter) tvid = re.search('<id>(\d+)</id>', data) if tvid: return tvid.group(1) return None def add_item(self, params): error = False if not 'refresh' in params: params['refresh'] = str(self.getSetting("refresh_time")) sub = {'name': params['name'], 'refresh': params[ 'refresh'], 'type': params['type']} sub['last_run'] = time.time() arg = {"play": params['url'], 'cp': 'sosac.ph', "title": sub['name']} item_url = xbmcutil._create_plugin_url( arg, 'plugin://' + self.addon_id + '/') util.info("item: " + item_url + " | " + str(params)) new_items = False # self.showNotification('Linking', params['name']) if params['type'] == sosac.LIBRARY_TYPE_VIDEO: # movies are not stored in subs database item_dir = self.getSetting('library-movies') nfo_file = os.path.join(item_dir, self.normalize_filename( sub['name']), self.normalize_filename(params['name']) + '.nfo') if not xbmcvfs.exists(nfo_file): metadata = "" if ('imdb' in params and params['imdb'] and not re.match('^$|^[?0]$', params['imdb'])): metadata += "http://www.imdb.com/title/tt{0}/\n".format(params['imdb']) if ('csfd' in params and params['csfd'] and not re.match('^$|^[?0]$', params['csfd'])): metadata += "http://www.csfd.cz/film/{0}\n".format(params['csfd']) if metadata != "": self.add_item_to_library(nfo_file, metadata) (error, new_items) = self.add_item_to_library( os.path.join(item_dir, self.normalize_filename(sub['name']), self.normalize_filename(params['name'])) + '.strm', item_url) elif params['type'] == sosac.LIBRARY_TYPE_TVSHOW: if not ('notify' in params): self.showNotification(sub['name'], 'Checking new content') subs = self.get_subs() item_dir = self.getSetting('library-tvshows') subs.update({params['url']: sub}) self.set_subs(subs) # self.addon.setSetting('tvshows-subs', json.dumps(subs)) nfo_file = os.path.join(item_dir, self.normalize_filename(params['name']), 'tvshow.nfo') if not xbmcvfs.exists(nfo_file): metadata = "" if ('imdb' in params and params['imdb'] and not re.match('^$|^[?0]$', params['imdb'])): metadata += "http://www.imdb.com/title/tt{0}/\n".format(params['imdb']) if ('csfd' in params and params['csfd'] and not re.match('^$|^[?0]$', params['csfd'])): metadata += "http://www.csfd.cz/film/{0}\n".format(params['csfd']) tvid = self.getTVDB(params['name'], params['imdb']) if tvid: metadata += "http://thetvdb.com/index.php?tab=series&id={0}\n".format(tvid) if metadata != "": self.add_item_to_library(nfo_file, metadata) episodes = self.provider.list_episodes(params['url']) for itm in episodes: arg = {"play": itm['url'], 'cp': 'sosac.ph', "title": itm['title']} item_url = xbmcutil._create_plugin_url( arg, 'plugin://' + self.addon_id + '/') dirname = "Season " + str(itm['season']) epname = "S%02dE%02d.strm" % (itm['season'], itm['episode']) filename = os.path.join(item_dir, self.normalize_filename( params['name']), dirname, epname) (err, new) = self.add_item_to_library(filename, item_url) error |= err if new is True and not err: new_items = True if not error and new_items and not ('update' in params) and not ('notify' in params): self.showNotification(params['name'], 'Found new content') xbmc.executebuiltin('UpdateLibrary(video)') elif not error and not ('notify' in params): self.showNotification(params['name'], 'No new content') if error and not ('notify' in params): self.showNotification('Failed, Please check kodi logs', 'Linking') return new_items def run_custom(self, params): if 'action' in params: icon = os.path.join(self.addon.getAddonInfo('path'), 'icon.png') if params['action'] == sosac.LIBRARY_ACTION_REMOVE_SUBSCRIPTION: subs = self.get_subs() if params['url'] in subs.keys(): del subs[params['url']] self.set_subs(subs) self.showNotification( params['name'], 'Removed from subscriptions') xbmc.executebuiltin('Container.Refresh') return False if params['action'] == sosac.LIBRARY_ACTION_ADD: if self.add_item(params): xbmc.executebuiltin('Container.Refresh') return True return False if params['action'] == sosac.LIBRARY_ACTION_REMOVE_ALL: self.set_subs({}) return True if params['action'] == sosac.LIBRARY_ACTION_ADD_ALL: self.dialog.create("Sosac", "Adding All Movies to library") self.dialog.update(0) if params['type'] == sosac.LIBRARY_TYPE_ALL_VIDEOS: self.dialog.create("Sosac", "Adding All Movies to library") self.dialog.update(0) videos = self.provider.library_list_all_videos() for video in videos: if self.dialog.iscanceled(): return if 'progress' in video: self.dialog.update(video['progress']) else: item = video['menu'][sosac.LIBRARY_MENU_ITEM_ADD] item["update"] = True item["notify"] = True item["type"] = sosac.LIBRARY_TYPE_VIDEO self.add_item(item) self.dialog.close() if params['type'] == sosac.LIBRARY_TYPE_RECENT_VIDEOS: self.dialog.create("Sosac", "Adding Recent Movies to library") self.dialog.update(0) videos = self.provider.library_list_recent_videos() for video in videos: if self.dialog.iscanceled(): return if 'progress' in video: self.dialog.update(video['progress']) else: item = video['menu'][sosac.LIBRARY_MENU_ITEM_ADD] item["update"] = True item["notify"] = True item["type"] = sosac.LIBRARY_TYPE_VIDEO self.add_item(item) self.dialog.close() elif params['type'] == sosac.LIBRARY_TYPE_ALL_SHOWS: self.dialog.create( "Sosac", "Adding All TV Shows to library") self.dialog.update(0) shows = self.provider.library_list_all_tvshows() for show in shows: if self.dialog.iscanceled(): return if 'progress' in show: self.dialog.update(show['progress']) else: item = show['menu'][sosac.LIBRARY_MENU_ITEM_ADD] item["update"] = True item["notify"] = True item["type"] = sosac.LIBRARY_TYPE_TVSHOW self.add_item(item) xbmc.executebuiltin('UpdateLibrary(video)') return False def add_item_to_library(self, item_path, item_url): error = False new = False if item_path: item_path = xbmc.translatePath(item_path) dir = os.path.dirname(item_path) if not xbmcvfs.exists(dir): try: xbmcvfs.mkdirs(dir) except Exception, e: error = True util.error('Failed to create directory 1: ' + dir) if not xbmcvfs.exists(item_path): try: file_desc = xbmcvfs.File(item_path, 'w') file_desc.write(item_url) file_desc.close() new = True except Exception, e: util.error('Failed to create .strm file: ' + item_path + " | " + str(e)) error = True else: error = True return (error, new) def get_subs(self): if self.subs is not None: return self.subs data = self.cache.get("subscription-1") try: if data == '': return {} self.subs = eval(data) return self.subs except Exception, e: util.error(e) return {} def set_subs(self, subs): self.subs = subs self.cache.set("subscription-1", repr(subs)) @staticmethod def encode(string): return unicodedata.normalize('NFKD', string.decode('utf-8')).encode('ascii', 'ignore') def addon_dir(self): return self.addon.getAddonInfo('path') def data_dir(self): return self.addon.getAddonInfo('profile') def getSetting(self, name): return self.addon.getSetting(name) def getString(self, string_id): return self.addon.getLocalizedString(string_id) @staticmethod def sleep(sleep_time): while not xbmc.abortRequested and sleep_time > 0: sleep_time -= 1 xbmc.sleep(1)