zbyna/plugin.video.sosac.ph

View on GitHub
resources/lib/sutils.py

Summary

Maintainability
A
3 hrs
Test Coverage
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 myPlayer
import json
import buggalo
from urlparse import urlparse
from translatedStrings import *


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 Exception:
            import storageserverdummy as StorageServer
            self.cache = StorageServer.StorageServer("Downloader")

    @staticmethod
    def executeJSON(request):
        # =====================================================================
        # Execute JSON-RPC Command
        # Args:
        # request: Dictionary with JSON-RPC Commands
        # Found code in xbmc-addon-service-watchedlist
        # =====================================================================
        rpccmd = json.dumps(request)    # create string from dict
        json_query = xbmc.executeJSONRPC(rpccmd)
        json_query = unicode(json_query, 'utf-8', errors='ignore')
        json_response = json.loads(json_query)
        return json_response

    @staticmethod
    def adjustGenre(genre):
        result = ''
        for g in genre:
            result = result + g + u' / '
        return result[:-3]

    @staticmethod
    def adjustCast(cast):
        result = []
        for c in cast:
            result.append((c['name'], c['role']))
        return result

    def play(self, item):
        # ======================================================================
        # Override from xbmcprovider
        # ======================================================================
        buggalo.SUBMIT_URL = 'http://sosac.comli.com/submit.php'
        i = 0
        if 'title' in item['info'].keys() and xbmcvfs.exists(item['info']['title']):
            try:
                pomTitle = item['info']['title']
                while True:
                    JSON_req = {"jsonrpc": "2.0",
                                "method": "Files.GetFileDetails",
                                "params": {"file": pomTitle,
                                           "media": "video", },
                                "id": "1"}
                    JSON_result = self.executeJSON(JSON_req)
                    if 'result' in JSON_result.keys() and \
                       'id' in JSON_result["result"]["filedetails"].keys():
                        break
                    else:
                        pomTitle = xbmc.translatePath(item['info']['title'])
                        if pomTitle.startswith('smb://'):
                            pomTitle = xbmc.validatePath(pomTitle)
                        if i > 1:
                            xbmc.log('"film is not in library (playing from Files)"')
                            super(XBMCSosac, self).play(item)
                            return
                        else:
                            i += 1
                pomItemType = JSON_result["result"]["filedetails"]["type"]
                pomItemDBID = JSON_result["result"]["filedetails"]["id"]
            except Exception:
                buggalo.onExceptionRaised({'vstup': '%s' % json.dumps(JSON_req, indent=2),
                                           'vystup': json.dumps(JSON_result, indent=2),
                                           'knihovna': xbmc.translatePath('special://database')})
            if pomItemType == u'episode':
                JSON_req = {"jsonrpc": "2.0",
                            "method": "VideoLibrary.GetEpisodeDetails",
                            "params": {"episodeid": pomItemDBID,
                                       "properties": ["title", "plot", "votes",
                                                      "firstaired", "playcount", "runtime",
                                                      "rating", "director", "userrating",
                                                      "writer", "streamdetails", "cast",
                                                      "productioncode", "season", "episode",
                                                      "originaltitle", "showtitle", "lastplayed",
                                                      "thumbnail", "file", "tvshowid",
                                                      "dateadded", "uniqueid", "art", "fanart"]},
                            "id": "1"}
                JSON_result = self.executeJSON(JSON_req)
                # tvshowtitle in listitem info vs showtitle in database !!!
                JSON_result['result']['episodedetails']['tvshowtitle'] = \
                    JSON_result['result']['episodedetails']['showtitle']
                # for cast list of tuples needed
                JSON_result['result']['episodedetails']['cast'] = self.adjustCast(
                    JSON_result['result']['episodedetails']['cast'])
                item['info'] = JSON_result['result']['episodedetails']
            elif pomItemType == u'movie':
                JSON_req = {"jsonrpc": "2.0",
                            "method": "VideoLibrary.GetMovieDetails",
                            "params": {"movieid": pomItemDBID,
                                       "properties": ["title", "plot", "votes", "rating",
                                                      "studio", "playcount", "runtime", "director",
                                                      "trailer", "tagline", "plotoutline",
                                                      "streamdetails",
                                                      "mpaa", "imdbnumber", "sorttitle", "setid",
                                                      "originaltitle", "lastplayed", "writer",
                                                      "thumbnail", "file",
                                                      "userrating",
                                                      "dateadded", "art", "fanart", "genre",
                                                      "cast"]},
                            "id": "1"}
                JSON_result = self.executeJSON(JSON_req)
                # for cast list of tuples needed
                JSON_result['result']['moviedetails']['cast'] = self.adjustCast(
                    JSON_result['result']['moviedetails']['cast'])
                # for genre one string needed
                JSON_result['result']['moviedetails']['genre'] = self.adjustGenre(
                    JSON_result['result']['moviedetails']['genre'])
                item['info'] = JSON_result['result']['moviedetails']
            pomDict = self.cache.get("resumePoints")
            try:
                pomSlovnik = eval(pomDict)
            except Exception:
                pomSlovnik = {}
            if pomItemDBID in pomSlovnik:
                dialog = xbmcgui.Dialog()
                ret = dialog.select(POKRACOVAT, [OD_MINULE_POZICE, OD_ZACATKU])
                if ret == 1:
                    del pomSlovnik[pomItemDBID]
                    self.cache.set("resumePoints", repr(pomSlovnik))
                del dialog
            pomTitulky = []
            pomTitulky = super(XBMCSosac, self).play(item, pomTitulky)
            if not pomTitulky:
                pomTitulky = None
            mujPlayer = myPlayer.MyPlayer(itemType=pomItemType, itemDBID=pomItemDBID,
                                          slovnik=pomSlovnik, titulky=pomTitulky)
            c = 0
            while not mujPlayer.isPlaying() and c < 2:
                self.sleep(2000)
                c += 1
            while mujPlayer.isPlaying():
                self.sleep(4000)
            self.cache.set("resumePoints", repr(pomSlovnik))
            xbmc.executebuiltin('Container.Refresh')
        elif 'title' in item['info'].keys() and not xbmcvfs.exists(item['info']['title']):
            dialog = xbmcgui.Dialog()
            ret = dialog.select(self.getString(30200),
                                [self.getString(30201), self.getString(30202)],
                                autoclose=5000)
            if ret == 1:
                dialog = xbmcgui.Dialog()
                titulek = self.getString(30203)
                vybranyAdresar = dialog.browseSingle(0, titulek, 'files', '',
                                                     False, False,
                                                     self.getSetting('library-movies'))
                if vybranyAdresar != '':
                    dirs, files = xbmcvfs.listdir(vybranyAdresar)
                    soubory = list()
                    vysl = self.getString(30204) + ':\n'
                    pDialog = xbmcgui.DialogProgress()
                    ret = pDialog.create(self.getString(30204), '...')
                    procenta = 1.0 / len(dirs) * 100
                    i = 1
                    pocetSTRM = 0
                    for di in dirs:
                        pom = os.path.join(vybranyAdresar, di)
                        adr, sou = xbmcvfs.listdir(pom)
                        pDialog.update(int(procenta * i), di)
                        i += 1
                        for s in sou:
                            if 'strm' in s:
                                s = os.path.join(pom, s.decode('utf8'))
                                soubory.append(s)
                                pocetSTRM += 1
                        for aa in adr:
                            pom1 = os.path.join(pom, aa)
                            _, sou1 = xbmcvfs.listdir(pom1)
                            for s1 in sou1:
                                if 'strm' in s1:
                                    s1 = os.path.join(pom1, s1.decode('utf8'))
                                    soubory.append(s1)
                                    pocetSTRM += 1
                    vysl += str(pocetSTRM) + '\n' + '--------------------\n' + \
                        self.getString(30205) + '\n'
                    del dirs
                    del files
                    del adr
                    del sou
                    pocetOK = 0
                    pDialog = xbmcgui.DialogProgress()
                    ret = pDialog.create(self.getString(30207), '...')
                    procenta = 1.0 / len(soubory) * 100
                    i = 1
                    for fi in soubory:
                        pomSoub = xbmcvfs.File(fi, 'rw')
                        pomTxt = pomSoub.read()
                        pomDict = util.params(urlparse(pomTxt).query)
                        pomDict['title'] = fi
                        item_url = xbmcutil._create_plugin_url(
                            pomDict, 'plugin://' + self.addon_id + '/')
                        pomSoub.close()
                        pomSoub = xbmcvfs.File(fi, 'w')
                        if pomSoub.write(item_url):
                            pocetOK += 1
                        else:
                            vysl = vysl + fi + ' Error !!! \n'
                        pomSoub.close()
                        pDialog.update(int(procenta * i), fi)
                        i += 1
                    vysl = vysl + self.getString(30206) + \
                        str(pocetOK)
                    del soubory
                    vysledek = xbmcgui.Dialog()
                    vysledek.textviewer(self.getString(30205), vysl)
                    super(XBMCSosac, self).play(item)
                    xbmc.Player().stop()
                    return
            else:
                super(XBMCSosac, self).play(item)

        else:
            super(XBMCSosac, self).play(item)

    def root(self):
        # ======================================================================
        # Override from xbmcprovider
        # ======================================================================
        addonPom = self.addon
        addonPom.setSetting(
            "library-movies", xbmc.translatePath(addonPom.getSetting("library-movies")))
        addonPom.setSetting(
            "library-tvshows", xbmc.translatePath(addonPom.getSetting("library-tvshows")))
        super(XBMCSosac, self).root()

    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 Exception:
            sleep_time = self.sleep_time

        self.sleep(sleep_time)

        try:
            self.last_run = float(self.cache.get("subscription.last_run"))
        except Exception:
            self.last_run = time.time()
            self.cache.set("subscription.last_run", str(self.last_run))

        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'] == 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': 'add-to-library',
                                'type': 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 getBBDB(self, name):
        name = util.request('http://csfd.bbaron.sk/find.php?' +
                            urllib.urlencode({'sosac': 1, 'name': name}))
        if name != '':
            return self.getTVDB(name, 1)
        return None

    def getTVDB(self, name, level=0):
        data = util.request('http://thetvdb.com/api/GetSeries.php?' +
                            urllib.urlencode({'seriesname': name, 'language': 'cs'}))
        try:
            tvid = re.search('<id>(\d+)</id>', data).group(1)
        except Exception:
            if level == 0:
                tvid = self.getBBDB(name)
            else:
                tvid = None
        return tvid

    def add_item(self, params, addToSubscription=False):
        error = False
        if 'refresh' not in params:
            params['refresh'] = str(self.getSetting("refresh_time"))
        sub = {'name': params['name'], 'refresh': params['refresh'], 'type': params['type']}
        sub['last_run'] = time.time()
        item_dir = self.getSetting('library-movies')
        title_pom = os.path.join(item_dir, self.normalize_filename(sub['name']),
                                 self.normalize_filename(params['name'])) + '.strm'
        arg = {"play": params['url'], 'cp': 'sosac.ph', "title": title_pom}
        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'] == LIBRARY_TYPE_VIDEO:
            item_dir = self.getSetting('library-movies')
            (error, new_items) = self.add_item_to_library(title_pom, item_url, params['type'])
        elif params['type'] == 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')

            if not (params['url'] in subs) and addToSubscription:
                subs.update({params['url']: sub})
                self.set_subs(subs)
                # self.addon.setSetting('tvshows-subs', json.dumps(subs))

            if not xbmcvfs.exists(os.path.join(item_dir, self.normalize_filename(params['name']),
                                               'tvshow.nfo')):
                tvid = self.getTVDB(params['name'])
                if tvid:
                    self.add_item_to_library(
                        os.path.join(item_dir, self.normalize_filename(
                            params['name']), 'tvshow.nfo'),
                        'http://thetvdb.com/index.php?tab=series&id=' + tvid,
                        params['type'])

            episodes = self.provider.list_episodes(params['url'])
            for itm in episodes:
                nfo = re.search('^(?P<season>\d+)x(?P<episode>\d+)',
                                itm['title'], re.IGNORECASE | re.DOTALL)
                title_pom = os.path.join(
                    item_dir, self.normalize_filename(params['name']),
                    'Season ' + nfo.group('season'),
                    "S" + nfo.group('season') +
                    "E" + nfo.group('episode') + '.strm')
                arg = {"play": itm['url'], 'cp': 'sosac.ph',
                       "title": title_pom}
                item_url = xbmcutil._create_plugin_url(arg, 'plugin://' + self.addon_id + '/')
                (err, new) = self.add_item_to_library(title_pom, item_url, params['type'])
                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'], 'New content found')
            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'] == '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 subscription')
                    xbmc.executebuiltin('Container.Refresh')
                return False
            if params['action'] == 'add-to-library':
                if self.settings['add_subscribe'] == '0':
                    pomSubscription = True
                else:
                    pomSubscription = False
                if self.add_item(params, addToSubscription=pomSubscription):
                    xbmc.executebuiltin('Container.Refresh')
                    return True
                xbmc.executebuiltin('Container.Refresh')
                return False
            if params['action'] == 'add-all-to-library':
                self.dialog.create('Adding to library')
                self.dialog.update(0, 'preparing ...')
                if params['title'].decode('utf8') == MOVIES:
                    self.provider.movies_all_to_library()
                elif params['title'].decode('utf8') == MOVIES_RECENTLY_ADDED:
                    self.provider.list_to_library(params['url'])
                elif params['title'].decode('utf8') == TV_SHOWS:
                    self.provider.tvshows_all_to_library()
                elif params['title'].decode('utf8') == MOVIES_BY_GENRES:
                    self.provider.list_to_library(params['url'])
                elif params['title'].decode('utf8') == MOVIES_BY_YEAR:
                    self.provider.generated_list_to_library(params['url'])
                elif params['title'].decode('utf8') == BEST_MOVIES:
                    self.provider.csfd_film_to_library(params['url'])
                elif params['title'].decode('utf8') == BEST_TV_SHOWS:
                    self.provider.csfd_tvshows_to_library(params['url'])
                elif params['title'].decode('utf8') == 'csfd_genres':
                    self.provider.csfd_genres_to_library(params['url'])
                elif params['title'].decode('utf8') == 'csfd_awards_years':
                    self.provider.csfd_awards_years_to_library(params['url'])
                self.dialog.close()
                xbmc.executebuiltin('UpdateLibrary(video)')
                return False
            if params['action'] == 'remove-all-from-subscription':
                self.set_subs({})
                return False
            if params['action'] == 'add-subscription':
                if self.settings['add_subscribe'] == '1':
                    if self.add_item(params, addToSubscription=True):
                        xbmc.executebuiltin('Container.Refresh')
                    return False
                subs = self.get_subs()
                if 'refresh' not in params:
                    params['refresh'] = str(self.getSetting("refresh_time"))
                sub = {'name': params['name'],
                       'refresh': params['refresh'],
                       'type': params['type']}
                sub['last_run'] = time.time()
                subs.update({params['url']: sub})
                self.set_subs(subs)
                xbmc.executebuiltin('Container.Refresh')
                return False
            if params['action'] == 'clear_cache':
                self.cache.delete('%')
                self.showNotification('Common plugin cache info',
                                      'disk cache cleared', 1000)
                if self.provider.cache.execute_sql(
                        'DELETE FROM simplecache WHERE  id  LIKE "sosac%"'):
                    self.provider.cache.win.clearProperties()
                    self.provider.cache.close()
                    self.showNotification('Simple plugin cache info',
                                          'disk and memory cache cleared', 1000)
                return False
            if params['action'] == 'custom_scan':
                util.info('"SOSAC Custom subscription scan"')
                subs = self.get_subs()
                new_items = False
                for url, sub in subs.iteritems():
                    if sub['type'] == LIBRARY_TYPE_TVSHOW:
                        util.debug("SOSAC Refreshing " + url)
                        new_items |= self.run_custom({
                            'action': 'add-to-library',
                            'type': LIBRARY_TYPE_TVSHOW,
                            'url': url,
                            'name': sub['name'],
                            'refresh': sub['refresh']
                        })
                        self.sleep(3000)
                if new_items:
                    xbmc.executebuiltin('UpdateLibrary(video)')

        return False

    def add_item_to_library(self, item_path, item_url, params_type):
        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 as 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 as e:
                    util.error('Failed to create .strm file: ' + item_path + " | " + str(e))
                    error = True
            elif params_type == LIBRARY_TYPE_VIDEO:
                try:
                    file_desc = xbmcvfs.File(item_path, 'w')
                    file_desc.write(item_url)
                    file_desc.close()
                    util.info('"File ' + os.path.basename(item_path) + ' file was updated"')
                except Exception as 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.provider.cache.get('sosa.subscription')
        try:
            if not data:
                return {}
            self.subs = eval(data)
            return self.subs
        except Exception as e:
            util.error(e)
            return {}

    def set_subs(self, subs):
        self.subs = subs
        self.provider.cache.set('sosa.subscription', repr(subs),
                                expiration=datetime.timedelta(days=365))

    @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)