Пример #1
0
    def infopage(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                       'info_page.tmpl'))
        t.admin = ''
        for section, settings in config.getShares():
            if 'type' in settings and settings['type'] == 'admin':
                t.admin += '<a href="/TiVoConnect?Command=Admin&Container=' + section\
                           + '">pyTivo Web Configuration</a><br>'\
                           + '<a href="/TiVoConnect?Command=NPL&Container=' + section\
                           + '">pyTivo ToGo</a><br>'
        if t.admin == '':
            t.admin = '<br><b>No Admin plugin installed in pyTivo.conf</b><br> If you wish to use'\
                      + ' the admin plugin add the following lines to pyTivo.conf<br><br>'\
                      + '[Admin]<br>type=admin'

        t.shares = 'Video shares:<br/>'
        for section, settings in config.getShares():
            if settings.get('type') == 'video':
                t.shares += '<a href="TiVoConnect?Command=QueryContainer&Container=' + section\
                    + '">' +  section + '</a><br/>'
        
        self.wfile.write(t)
        self.end_headers()
Пример #2
0
def setup(in_service=False):
    config.init(sys.argv[1:])
    config.init_logging()
    sys.excepthook = exceptionLogger

    port = config.getPort()

    httpd = httpserver.TivoHTTPServer(('', int(port)),
                                      httpserver.TivoHTTPHandler)

    logger = logging.getLogger('pyTivo')
    logger.info('Last modified: ' + last_date())
    logger.info('Python: ' + platform.python_version())
    logger.info('System: ' + platform.platform())

    for section, settings in config.getShares():
        httpd.add_container(section, settings)

    b = beacon.Beacon()
    b.add_service('TiVoMediaServer:%s/http' % port)
    b.start()
    if 'listen' in config.getBeaconAddresses():
        b.listen()

    httpd.set_beacon(b)
    httpd.set_service_status(in_service)

    logger.info('pyTivo is ready.')
    return httpd
Пример #3
0
 def ToGo(self, handler, query):
     togo_path = config.get_server('togo_path')
     for name, data in config.getShares():
         if togo_path == name:
             togo_path = data.get('path')
     if togo_path:
         tivoIP = query['TiVo'][0]
         tsn = config.tivos_by_ip(tivoIP)
         tivo_mak = config.get_tsn('tivo_mak', tsn)
         urls = query.get('Url', [])
         decode = 'decode' in query
         save = 'save' in query
         for theurl in urls:
             status[theurl] = {'running': False, 'error': '', 'rate': '',
                               'queued': True, 'size': 0, 'finished': False,
                               'decode': decode, 'save': save}
             if tivoIP in queue:
                 queue[tivoIP].append(theurl)
             else:
                 queue[tivoIP] = [theurl]
                 thread.start_new_thread(ToGo.process_queue,
                                         (self, tivoIP, tivo_mak, togo_path))
             logger.info('[%s] Queued "%s" for transfer to %s' %
                         (time.strftime('%d/%b/%Y %H:%M:%S'),
                          unquote(theurl), togo_path))
         urlstring = '<br>'.join([unquote(x) for x in urls])
         message = TRANS_QUEUE % (urlstring, togo_path)
     else:
         message = MISSING
     handler.redir(message, 5)
Пример #4
0
    def SvcDoRun(self): 
        config.init([])
        config.init_logging()

        p = os.path.dirname(__file__)
    
        f = open(os.path.join(p, 'log.txt'), 'w')
        sys.stdout = f
        sys.stderr = f

        port = config.getPort()

        httpd = httpserver.TivoHTTPServer(('', int(port)),
                                          httpserver.TivoHTTPHandler)

        for section, settings in config.getShares():
            httpd.add_container(section, settings)

        b = beacon.Beacon()
        b.add_service('TiVoMediaServer:%s/http' % port)
        b.start()
        if 'listen' in config.getBeaconAddresses():
            b.listen()

        httpd.set_beacon(b)
        
        while 1:
            sys.stdout.flush()
            (rx, tx, er) = select.select((httpd,), (), (), 5)
            for sck in rx:
                sck.handle_request()
            rc = win32event.WaitForSingleObject(self.stop_event, 5)
            if rc == win32event.WAIT_OBJECT_0:
                b.stop()
                break
Пример #5
0
    def infopage(self):
        t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                       'info_page.tmpl'),
                     filter=EncodeUnicode)
        t.admin = ''

        if config.get_server('tivo_mak') and config.get_server('togo_path'):
            t.togo = '<br>Pull from TiVos:<br>'
        else:
            t.togo = ''

        for section, settings in config.getShares():
            plugin_type = settings.get('type')
            if plugin_type == 'settings':
                t.admin += ('<a href="/TiVoConnect?Command=Settings&amp;' +
                            'Container=' + quote(section) +
                            '">Settings</a><br>')
            elif plugin_type == 'togo' and t.togo:
                for tsn in config.tivos:
                    if tsn and 'address' in config.tivos[tsn]:
                        t.togo += ('<a href="/TiVoConnect?' +
                            'Command=NPL&amp;Container=' + quote(section) +  
                            '&amp;TiVo=' + config.tivos[tsn]['address'] +
                            '">' + config.tivos[tsn]['name'] +
                            '</a><br>')

        self.send_html(str(t))
Пример #6
0
    def infopage(self):
        t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                       'info_page.tmpl'),
                     filter=EncodeUnicode)
        t.admin = ''

        if config.get_server('tivo_mak') and config.get_server('togo_path'):
            t.togo = '<br>Pull from TiVos:<br>'
        else:
            t.togo = ''

        for section, settings in config.getShares():
            plugin_type = settings.get('type')
            if plugin_type == 'settings':
                t.admin += ('<a href="/TiVoConnect?Command=Settings&amp;' +
                            'Container=' + quote(section) +
                            '">Settings</a><br>')
            elif plugin_type == 'togo' and t.togo:
                for tsn in config.tivos:
                    if tsn and 'address' in config.tivos[tsn]:
                        t.togo += ('<a href="/TiVoConnect?' +
                                   'Command=NPL&amp;Container=' +
                                   quote(section) + '&amp;TiVo=' +
                                   config.tivos[tsn]['address'] + '">' +
                                   config.tivos[tsn]['name'] + '</a><br>')

        self.send_html(str(t))
Пример #7
0
    def SvcDoRun(self):

        import sys, os

        p = os.path.dirname(__file__)

        f = open(os.path.join(p, 'log.txt'), 'w')
        sys.stdout = f
        sys.stderr = f

        port = config.getPort()

        httpd = httpserver.TivoHTTPServer(('', int(port)),
                                          httpserver.TivoHTTPHandler)

        for section, settings in config.getShares():
            httpd.add_container(section, settings)

        b = beacon.Beacon()
        b.add_service('TiVoMediaServer:' + str(port) + '/http')
        b.start()

        while 1:
            sys.stdout.flush()
            (rx, tx, er) = select.select((httpd, ), (), (), 5)
            for sck in rx:
                sck.handle_request()
            rc = win32event.WaitForSingleObject(self.stop_event, 5)
            if rc == win32event.WAIT_OBJECT_0:
                b.stop()
                break
Пример #8
0
def setup(in_service=False):
    config.init(sys.argv[1:], in_service)
    config.init_logging()
    sys.excepthook = exceptionLogger

    port = config.getPort()

    httpd = httpserver.TivoHTTPServer(('', int(port)),
                                      httpserver.TivoHTTPHandler)

    logger = logging.getLogger('pyTivo')
    logger.info('Last modified: ' + last_date())
    logger.info('Python: ' + platform.python_version())
    logger.info('System: ' + platform.platform())

    for section, settings in config.getShares():
        httpd.add_container(section, settings)

    b = beacon.Beacon()
    b.add_service('TiVoMediaServer:%s/http' % port)
    b.start()
    if 'listen' in config.getBeaconAddresses():
        b.listen()

    httpd.set_beacon(b)
    httpd.set_service_status(in_service)

    logger.info('pyTivo is ready.')
    return httpd
Пример #9
0
    def push_one_file(self, f):
        file_info = VideoDetails()
        file_info['valid'] = transcode.supported_format(f['path'])

        temp_share = config.get_server('temp_share', '')
        temp_share_path = ''
        if temp_share:
            for name, data in config.getShares():
                if temp_share == name:
                    temp_share_path = data.get('path')

        mime = 'video/mpeg'
        if config.isHDtivo(f['tsn']):
            for m in ['video/mp4', 'video/bif']:
                if transcode.tivo_compatible(f['path'], f['tsn'], m)[0]:
                    mime = m
                    break

            if (mime == 'video/mpeg' and
                transcode.mp4_remuxable(f['path'], f['tsn'])):
                new_path = transcode.mp4_remux(f['path'], f['name'], f['tsn'], temp_share_path)
                if new_path:
                    mime = 'video/mp4'
                    f['name'] = new_path
                    if temp_share_path:
                        ip = config.get_ip()
                        port = config.getPort()
                        container = quote(temp_share) + '/'
                        f['url'] = 'http://%s:%s/%s' % (ip, port, container)

        if file_info['valid']:
            file_info.update(self.metadata_full(f['path'], f['tsn'], mime))

        url = f['url'] + quote(f['name'])

        title = file_info['seriesTitle']
        if not title:
            title = file_info['title']

        source = file_info['seriesId']
        if not source:
            source = title

        subtitle = file_info['episodeTitle']
        try:
            m = mind.getMind(f['tsn'])
            m.pushVideo(
                tsn = f['tsn'],
                url = url,
                description = file_info['description'],
                duration = file_info['duration'] / 1000,
                size = file_info['size'],
                title = title,
                subtitle = subtitle,
                source = source,
                mime = mime,
                tvrating = file_info['tvRating'])
        except Exception, msg:
            logger.error(msg)
Пример #10
0
    def ToGo(self, handler, query):
        subcname = query['Container'][0]
        cname = subcname.split('/')[0]
        for name, data in config.getShares():
            if cname == name:
                if 'tivo_mak' in data:
                    tivo_mak = data['tivo_mak']
                else:
                    tivo_mak = ""
                if 'togo_path' in data:
                    togo_path = data['togo_path']
                else:
                    togo_path = ""
        if tivo_mak != "" and togo_path != "":
            parse_url = urlparse(str(query['Url'][0]))
            theurl = 'http://' + parse_url[1].split(
                ':')[0] + parse_url[2] + "?" + parse_url[4]
            password = tivo_mak  #TiVo MAK
            tivoIP = query['TiVo'][0]
            name = unquote(parse_url[2])[10:300].split('.')
            name.insert(-1,
                        " - " + unquote(parse_url[4]).split("id=")[1] + ".")
            outfile = os.path.join(togo_path, "".join(name))

            status[theurl] = {
                'running': True,
                'error': '',
                'rate': '',
                'finished': False
            }

            thread.start_new_thread(Admin.get_tivo_file,
                                    (self, theurl, password, tivoIP, outfile))

            handler.send_response(200)
            handler.end_headers()
            t = Template(
                file=os.path.join(SCRIPTDIR, 'templates', 'redirect.tmpl'))
            t.container = cname
            t.time = '3'
            t.url = '/' + query['Redirect'][0]
            t.text = '<h3>Transfer Initiated.</h3>  <br>You selected transfer has been initiated.'+\
                     '<br> The <a href="/'+ query['Redirect'][0] +'"> ToGo</a> page will reload in 3 seconds.'
            handler.wfile.write(t)
        else:
            handler.send_response(200)
            handler.end_headers()
            t = Template(
                file=os.path.join(SCRIPTDIR, 'templates', 'redirect.tmpl'))
            t.container = cname
            t.time = '10'
            t.url = '/' + query['Redirect'][0]
            t.text = '<h3>Missing Data.</h3>  <br>You must set both "tivo_mak" and "togo_path" before using this function.'+\
                     '<br> The <a href="/'+ query['Redirect'][0] +'"> ToGo</a> page will reload in 10 seconds.'
            handler.wfile.write(t)
Пример #11
0
 def root_container(self):
     tsn = self.headers.getheader('TiVo_TCD_ID', '')
     tsnshares = config.getShares(tsn)
     tsncontainers = []
     for section, settings in tsnshares:
         try:
             settings['content_type'] = \
                 GetPlugin(settings['type']).CONTENT_TYPE
             tsncontainers.append((section, settings))
         except Exception, msg:
             self.server.logger.error(section + ' - ' + str(msg))
Пример #12
0
 def root_container(self):
     tsn = self.headers.getheader('TiVo_TCD_ID', '')
     tsnshares = config.getShares(tsn)
     tsncontainers = {}
     for section, settings in tsnshares:
         try:
             settings['content_type'] = \
                 GetPlugin(settings['type']).CONTENT_TYPE
             tsncontainers[section] = settings
         except Exception, msg:
             print section, '-', msg
Пример #13
0
 def do_command(self, query, command, target, tsn):
     for name, container in config.getShares(tsn):
         if target == name:
             plugin = GetPlugin(container['type'])
             if hasattr(plugin, command):
                 self.cname = name
                 self.container = container
                 method = getattr(plugin, command)
                 method(self, query)
                 return True
             else:
                 break
     return False
Пример #14
0
 def root_container(self):
     tsn = self.headers.getheader('TiVo_TCD_ID', '')
     tsnshares = config.getShares(tsn)
     tsncontainers = []
     for section, settings in tsnshares:
         try:
             mime = GetPlugin(settings['type']).CONTENT_TYPE
             if mime.split('/')[1] in ('tivo-videos', 'tivo-music',
                                       'tivo-photos'):
                 settings['content_type'] = mime
                 tsncontainers.append((section, settings))
         except Exception, msg:
             self.server.logger.error(section + ' - ' + str(msg))
Пример #15
0
 def root_container(self):
     tsn = self.headers.getheader('TiVo_TCD_ID', '')
     tsnshares = config.getShares(tsn)
     tsncontainers = []
     for section, settings in tsnshares:
         try:
             mime = GetPlugin(settings['type']).CONTENT_TYPE
             if mime.split('/')[1] in ('tivo-videos', 'tivo-music',
                                       'tivo-photos'):
                 settings['content_type'] = mime
                 tsncontainers.append((section, settings))
         except Exception, msg:
             self.server.logger.error(section + ' - ' + str(msg))
Пример #16
0
 def do_command(self, query, command, target, tsn):
     for name, container in config.getShares(tsn):
         if target == name:
             plugin = GetPlugin(container['type'])
             if hasattr(plugin, command):
                 self.cname = name
                 self.container = container
                 method = getattr(plugin, command)
                 method(self, query)
                 return True
             else:
                 break
     return False
Пример #17
0
    def processDlRequest(self):

        while True:
            data = self.work_queue.get()

            for share_name, settings in config.getShares():
                if settings['type'] == 'webvideo':
                    break
            self.__logger.debug('Processing request: %s' % data)

            path = settings['path']

            file_name = os.path.join(path, '%s-%s' % 
                                     (data['bodyOfferId'].replace(':', '-'),
                                      data['url'].split('/')[-1]))

            status = self.downloadFile(data['url'], file_name)
            mime = 'video/mpeg'

            if status:
                tsn = data['bodyId'][4:]
                file_info = VideoDetails()

                if config.isHDtivo(tsn):
                    for m in ['video/mp4', 'video/bif']:
                        if tivo_compatible(file_name, tsn, m)[0]:
                            mime = m
                            break

                file_info.update(self.metadata_full(file_name, tsn, mime))

                ip = config.get_ip()
                port = config.getPort()

                data['url'] = ('http://%s:%s' % (ip, port) +
                               urllib.quote('/%s/%s' % (share_name,
                                            os.path.basename(file_name))))
                data['duration'] = file_info['duration'] / 1000
                data['size'] = file_info['size']

            self.__logger.debug('Complete request: %s' % data)

            m = mind.getMind()
            m.completeDownloadRequest(data, status, mime)

            self.in_progress_lock.acquire()
            try:
                del self.in_progress[data['bodyOfferId']]
            finally:
                self.in_progress_lock.release()
Пример #18
0
def setup(in_service=False):
    config.init(sys.argv[1:])
    config.init_logging()
    sys.excepthook = exceptionLogger

    port = config.getPort()

    httpd = httpserver.TivoHTTPServer(('', int(port)),
                                      httpserver.TivoHTTPHandler)

    logger = logging.getLogger('pyTivo')
    logger.info('Last modified: ' + last_date())
    logger.info('Python: ' + platform.python_version())
    logger.info('System: ' + platform.platform())

    for section, settings in config.getShares():
        httpd.add_container(section, settings)
        # Precaching of files: does a recursive list of base path
        if settings.get('precache', 'False').lower() == 'true':
            plugin = GetPlugin(settings.get('type'))
            if hasattr(plugin, 'pre_cache'):
                logger.info('Pre-caching the ' + section + ' share.')
                pre_cache_filter = getattr(plugin, 'pre_cache')

                def build_recursive_list(path):
                    try:
                        for f in os.listdir(path):
                            f = os.path.join(path, f)
                            if os.path.isdir(f):
                                build_recursive_list(f)
                            else:
                                pre_cache_filter(f)
                    except:
                        pass

                build_recursive_list(settings.get('path'))

    b = beacon.Beacon()
    b.add_service('TiVoMediaServer:%s/http' % port)
    b.start()
    if 'listen' in config.getBeaconAddresses():
        b.listen()

    httpd.set_beacon(b)
    httpd.set_service_status(in_service)

    logger.info('pyTivo is ready.')
    return httpd
Пример #19
0
def setup(in_service=False):
    config.init(sys.argv[1:])
    config.init_logging()
    sys.excepthook = exceptionLogger

    port = config.getPort()

    httpd = httpserver.TivoHTTPServer(('', int(port)),
        httpserver.TivoHTTPHandler)

    logger = logging.getLogger('pyTivo')
    logger.info('Last modified: ' + last_date())
    logger.info('Python: ' + platform.python_version())
    logger.info('System: ' + platform.platform())

    for section, settings in config.getShares():
        httpd.add_container(section, settings)
        # Precaching of files: does a recursive list of base path
        if settings.get('precache', 'False').lower() == 'true':
            plugin = GetPlugin(settings.get('type'))
            if hasattr(plugin, 'pre_cache'):
                logger.info('Pre-caching the ' + section + ' share.')
                pre_cache_filter = getattr(plugin, 'pre_cache')

                def build_recursive_list(path):
                    try:
                        for f in os.listdir(path):
                            f = os.path.join(path, f)
                            if os.path.isdir(f):
                                build_recursive_list(f)
                            else:
                                pre_cache_filter(f)
                    except:
                        pass

                build_recursive_list(settings.get('path'))

    b = beacon.Beacon()
    b.add_service('TiVoMediaServer:%s/http' % port)
    b.start()
    if 'listen' in config.getBeaconAddresses():
        b.listen()

    httpd.set_beacon(b)
    httpd.set_service_status(in_service)

    logger.info('pyTivo is ready.')
    return httpd
Пример #20
0
    def handle_query(self, query, tsn):
        mname = False
        if 'Command' in query and len(query['Command']) >= 1:

            command = query['Command'][0]

            # If we are looking at the root container
            if (command == 'QueryContainer' and
                (not 'Container' in query or query['Container'][0] == '/')):
                self.root_container()
                return

            if 'Container' in query:
                # Dispatch to the container plugin
                basepath = query['Container'][0].split('/')[0]
                for name, container in config.getShares(tsn):
                    if basepath == name:
                        plugin = GetPlugin(container['type'])
                        if hasattr(plugin, command):
                            method = getattr(plugin, command)
                            method(self, query)
                            return
                        else:
                            break

            elif (command == 'QueryFormats' and 'SourceFormat' in query and
                  query['SourceFormat'][0].startswith('video')):
                self.send_response(200)
                self.send_header('Content-type', 'text/xml')
                self.end_headers()
                if config.hasTStivo(tsn):
                    self.wfile.write(VIDEO_FORMATS_TS)
                else:
                    self.wfile.write(VIDEO_FORMATS)
                return

            elif command == 'FlushServer':
                # Does nothing -- included for completeness
                self.send_response(200)
                self.end_headers()
                return

        # If we made it here it means we couldn't match the request to
        # anything.
        self.unsupported(query)
Пример #21
0
    def ToGo(self, handler, query):
        subcname = query['Container'][0]
        cname = subcname.split('/')[0]
        for name, data in config.getShares():
            if cname == name:
                if 'tivo_mak' in data:
                    tivo_mak = data['tivo_mak']
                else:
                    tivo_mak = ""
                if 'togo_path' in data:
                    togo_path = data['togo_path']
                else:
                    togo_path = ""
        if tivo_mak != "" and togo_path != "":
            parse_url = urlparse(str(query['Url'][0]))
            theurl = 'http://' + parse_url[1].split(':')[0] + parse_url[2] + "?" + parse_url[4]
            password = tivo_mak #TiVo MAK
            tivoIP = query['TiVo'][0]
            name = unquote(parse_url[2])[10:300].split('.')
            name.insert(-1," - " + unquote(parse_url[4]).split("id=")[1] + ".")
            outfile = os.path.join(togo_path, "".join(name))

            status[theurl] = {'running':True, 'error':'', 'rate':'', 'finished':False}

            thread.start_new_thread(Admin.get_tivo_file, (self, theurl, password, tivoIP, outfile))
            
            handler.send_response(200)
            handler.end_headers()
            t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
            t.container = cname
            t.time = '3'
            t.url = '/'+ query['Redirect'][0]
            t.text = '<h3>Transfer Initiated.</h3>  <br>You selected transfer has been initiated.'+\
                     '<br> The <a href="/'+ query['Redirect'][0] +'"> ToGo</a> page will reload in 3 seconds.'
            handler.wfile.write(t)
        else:
            handler.send_response(200)
            handler.end_headers()
            t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
            t.container = cname
            t.time = '10'
            t.url = '/'+ query['Redirect'][0]
            t.text = '<h3>Missing Data.</h3>  <br>You must set both "tivo_mak" and "togo_path" before using this function.'+\
                     '<br> The <a href="/'+ query['Redirect'][0] +'"> ToGo</a> page will reload in 10 seconds.'
            handler.wfile.write(t)
Пример #22
0
    def infopage(self):
        useragent = self.headers.getheader('User-Agent', '')
        if useragent.lower().find('mobile') > 0:
            t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                       'info_page_mob.tmpl'),
                     filter=EncodeUnicode)
        else:
            t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                       'info_page.tmpl'),
                     filter=EncodeUnicode)
        t.admin = ''

        if config.get_server('tivo_mak') and config.get_server('togo_path'):
            t.togo = '<br>Pull from TiVos:<br>'
        else:
            t.togo = ''

        if (config.get_server('tivo_username') and
            config.get_server('tivo_password')):
            t.shares = '<br>Push from video shares:<br>'
        else:
            t.shares = ''

        for section, settings in config.getShares():
            plugin_type = settings.get('type')
            if plugin_type == 'settings':
                t.admin += ('<a href="/TiVoConnect?Command=Settings&amp;' +
                            'Container=' + quote(section) +
                            '">Settings</a><br>')
            elif plugin_type == 'togo' and t.togo:
                for tsn in config.tivos:
                    if tsn:
                        t.togo += ('<a href="/TiVoConnect?' +
                            'Command=NPL&amp;Container=' + quote(section) +  
                            '&amp;TiVo=' + config.tivos[tsn] + '">' + 
                            escape(config.tivo_names[tsn]) + '</a><br>')
            elif plugin_type and t.shares:
                plugin = GetPlugin(plugin_type)
                if hasattr(plugin, 'Push'):
                    t.shares += ('<a href="/TiVoConnect?Command=' +
                                 'QueryContainer&amp;Container=' +
                                 quote(section) + '&Format=text/html">' +
                                 section + '</a><br>')

        self.send_html(str(t))
Пример #23
0
def setup(in_service=False):
    config.init(sys.argv[1:])
    config.init_logging()
    sys.excepthook = exceptionLogger

    port = config.getPort()

    httpd = httpserver.TivoHTTPServer(("", int(port)), httpserver.TivoHTTPHandler)

    logger = logging.getLogger("pyTivo")

    for section, settings in config.getShares():
        httpd.add_container(section, settings)
        # Precaching of files: does a recursive list of base path
        if settings.get("precache", "False").lower() == "true":
            plugin = GetPlugin(settings.get("type"))
            if hasattr(plugin, "pre_cache"):
                logger.info("Pre-caching the " + section + " share.")
                pre_cache_filter = getattr(plugin, "pre_cache")

                def build_recursive_list(path):
                    try:
                        for f in os.listdir(path):
                            f = os.path.join(path, f)
                            if os.path.isdir(f):
                                build_recursive_list(f)
                            else:
                                pre_cache_filter(f)
                    except:
                        pass

                build_recursive_list(settings.get("path"))

    b = beacon.Beacon()
    b.add_service("TiVoMediaServer:%s/http" % port)
    b.start()
    if "listen" in config.getBeaconAddresses():
        b.listen()

    httpd.set_beacon(b)
    httpd.set_service_status(in_service)

    logger.info("pyTivo is ready.")
    return httpd
Пример #24
0
 def ToGo(self, handler, query):
     togo_path = config.get_server('togo_path')
     for name, data in config.getShares():
         if togo_path == name:
             togo_path = data.get('path')
     if togo_path:
         tivoIP = query['TiVo'][0]
         tsn = config.tivos_by_ip(tivoIP)
         tivo_mak = config.get_tsn('tivo_mak', tsn)
         urls = query.get('Url', [])
         decode = 'decode' in query
         save = 'save' in query
         ts_format = 'ts_format' in query
         for theurl in urls:
             status[theurl] = {
                 'running': False,
                 'error': '',
                 'rate': '',
                 'queued': True,
                 'size': 0,
                 'finished': False,
                 'decode': decode,
                 'save': save,
                 'ts_format': ts_format
             }
             if tivoIP in queue:
                 queue[tivoIP].append(theurl)
             else:
                 queue[tivoIP] = [theurl]
                 thread.start_new_thread(
                     ToGo.process_queue,
                     (self, tivoIP, tivo_mak, togo_path))
             logger.info('[%s] Queued "%s" for transfer to %s' %
                         (time.strftime('%d/%b/%Y %H:%M:%S'),
                          unquote(theurl), togo_path))
         urlstring = '<br>'.join(
             [unicode(unquote(x), 'utf-8') for x in urls])
         message = TRANS_QUEUE % (urlstring, togo_path)
     else:
         message = MISSING
     handler.redir(message, 5)
Пример #25
0
    def infopage(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html; charset=utf-8')
        self.end_headers()
        t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                       'info_page.tmpl'),
                     filter=EncodeUnicode)
        t.admin = ''

        if config.get_server('tivo_mak') and config.get_server('togo_path'):
            t.togo = '<br>Pull from TiVos:<br>'
        else:
            t.togo = ''

        if (config.get_server('tivo_username') and
            config.get_server('tivo_password')):
            t.shares = '<br>Push from video shares:<br>'
        else:
            t.shares = ''

        for section, settings in config.getShares():
            plugin_type = settings.get('type')
            if plugin_type == 'settings':
                t.admin += ('<a href="/TiVoConnect?Command=Settings&amp;' +
                            'Container=' + quote(section) +
                            '">Web Configuration</a><br>')
            elif plugin_type == 'togo' and t.togo:
                for tsn in config.tivos:
                    if tsn:
                        t.togo += ('<a href="/TiVoConnect?' +
                            'Command=NPL&amp;Container=' + quote(section) +  
                            '&amp;TiVo=' + config.tivos[tsn] + '">' + 
                            escape(config.tivo_names[tsn]) + '</a><br>')
            elif plugin_type == 'video' and t.shares:
                t.shares += ('<a href="TiVoConnect?Command=' +
                             'QueryContainer&amp;Container=' +
                             quote(section) + '&Format=text/html">' +
                             section + '</a><br>')

        self.wfile.write(t)
Пример #26
0
    def infopage(self):
        t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                       'info_page.tmpl'),
                     filter=EncodeUnicode)
        t.admin = ''

        if config.get_server('tivo_mak') and config.get_server('togo_path'):
            t.togo = '<br>Pull from TiVos:<br>'
        else:
            t.togo = ''

        if (config.get_server('tivo_username')
                and config.get_server('tivo_password')):
            t.shares = '<br>Push from video shares:<br>'
        else:
            t.shares = ''

        for section, settings in config.getShares():
            plugin_type = settings.get('type')
            if plugin_type == 'settings':
                t.admin += ('<a href="/TiVoConnect?Command=Settings&amp;' +
                            'Container=' + quote(section) +
                            '">Web Configuration</a><br>')
            elif plugin_type == 'togo' and t.togo:
                for tsn in config.tivos:
                    if tsn:
                        t.togo += ('<a href="/TiVoConnect?' +
                                   'Command=NPL&amp;Container=' +
                                   quote(section) + '&amp;TiVo=' +
                                   config.tivos[tsn] + '">' +
                                   escape(config.tivo_names[tsn]) + '</a><br>')
            elif plugin_type and t.shares:
                plugin = GetPlugin(plugin_type)
                if hasattr(plugin, 'Push'):
                    t.shares += ('<a href="/TiVoConnect?Command=' +
                                 'QueryContainer&amp;Container=' +
                                 quote(section) + '&Format=text/html">' +
                                 section + '</a><br>')

        self.send_html(str(t))
Пример #27
0
    def infopage(self):
        t = Template(file=os.path.join(SCRIPTDIR, 'templates', 'info_page.tmpl'))
        t.version = PYTIVO_VERSION
        t.admin = ''

        if config.get_server('tivo_mak') and config.get_togo('path'):
            t.togo = '<br>Pull from TiVos:<br>'
        else:
            t.togo = ''

        for section, settings in config.getShares():
            plugin_type = settings.get('type')
            if plugin_type == 'settings':
                t.admin += ('<a href="/TiVoConnect?Command=Settings&amp;Container={}">Settings</a><br>'
                            .format(quote(section)))
            elif plugin_type == 'togo' and t.togo:
                for tsn in config.tivos:
                    if tsn and 'address' in config.tivos[tsn]:
                        t.togo += ('<a href="/TiVoConnect?Command=NPL&amp;Container={}&amp;TiVo={}">{}</a><br>'
                                   .format(quote(section), config.tivos[tsn]['address'], config.tivos[tsn]['name']))

        self.send_html(str(t))
Пример #28
0
 def root_container(self):
     tsn = self.headers.get('TiVo_TCD_ID', '')
     tsnshares = config.getShares(tsn)
     tsncontainers = []
     for section, settings in tsnshares:
         try:
             mime = GetPlugin(settings['type']).CONTENT_TYPE
             if mime.split('/')[1] in ('tivo-videos', 'tivo-music',
                                       'tivo-photos'):
                 settings['content_type'] = mime
                 tsncontainers.append((section, settings))
         except Exception as msg:
             self.server.logger.error('%s - %s', section, str(msg))
     t = Template(file=os.path.join(SCRIPTDIR, 'templates', 'root_container.tmpl'))
     if self.server.beacon.bd:
         t.renamed = self.server.beacon.bd.renamed
     else:
         t.renamed = {}
     t.containers = tsncontainers
     t.hostname = socket.gethostname()
     t.escape = escape
     t.quote = quote
     self.send_xml(str(t))
Пример #29
0
 def reset(self):
     self.containers.clear()
     for section, settings in config.getShares():
         self.add_container(section, settings)
Пример #30
0
    def ToGo(handler, query):
        """
        HTTP command handler to download a set of recordings from a Tivo.

        If there is already a thread downloading recordings from that Tivo,
        the new recordings will be appended to the existing download task
        list for that Tivo, otherwise a new task list will be created and
        a thread spawned to process it.
        """
        togo_path = config.get_togo('path')
        for name, data in config.getShares():
            if togo_path == name:
                togo_path = data.get('path')
        if togo_path:
            tivoIP = query['TiVo'][0]
            tsn = config.tivos_by_ip(tivoIP)
            tivo_name = config.tivos[tsn].get('name', tivoIP)
            tivo_mak = config.get_tsn('tivo_mak', tsn)
            urls = query.get('Url', [])
            decode = 'decode' in query
            save = 'save' in query
            ts_format = 'ts_format' in query and config.is_ts_capable(tsn)
            sortable = bool(config.get_togo('sortable_names', False))
            for theurl in urls:

                status = {'url': theurl,
                          'running': False,
                          'queued': True,
                          'finished': False,
                          'showinfo': showinfo[theurl], # metadata information about the show
                          'decode': decode,         # decode the downloaded tivo file
                          'save': save,             # save the tivo file's metadata to a .txt file
                          'ts_format': ts_format,   # download using transport stream otherwise program stream
                          'sortable': sortable,     # name saved tivo file in a sortable manner
                          'error': '',
                          'rate': 0,
                          'size': 0,
                          'retry': 0,
                          'download_attempts': [],  # information about each download attempt (used for sync error log)
                          'ts_error_packets': [],   # list of TS packets w/ sync lost as tuples (packet_no, count)
                          'best_attempt_index': None, # index into download_attempts of the attempt w/ fewest errors
                          'best_file': '',
                          'best_error_count': None} # count of TS packets lost (sync byte was wrong) in 'best_file'

                with active_tivos_lock:
                    if tivoIP in active_tivos:
                        with active_tivos[tivoIP]['lock']:
                            active_tivos[tivoIP]['queue'].append(status)
                    else:
                        # we have to add authentication info again because the
                        # download netloc may be different from that used to
                        # retrieve the list of recordings (and in fact the port
                        # is different, 443 to get the NPL and 80 for downloading).
                        auth_handler.add_password('TiVo DVR', urlsplit(theurl).netloc, 'tivo', tivo_mak)
                        logger.debug('ToGo: add password for TiVo DVR netloc: %s', urlsplit(theurl).netloc)

                        active_tivos[tivoIP] = {'tivoIP': tivoIP,
                                                'lock': RLock(),
                                                'thread': None,
                                                'tivo_name': tivo_name,
                                                'mak': tivo_mak,
                                                'dest_path': togo_path,
                                                'fn_format_info': {'episode': config.get_togo('episode_fn'),
                                                                   'movie': config.get_togo('movie_fn')
                                                                  },
                                                'ts_error_mode': config.get_togo('ts_error_mode', 'ignore'),
                                                'ts_max_retries': int(config.get_togo('ts_max_retries', 0)),
                                                'queue': [status]}

                        active_tivos[tivoIP]['thread'] = TivoDownload(tivoIP, active_tivos, active_tivos_lock, tivo_open)
                        active_tivos[tivoIP]['thread'].start()

                logger.info('[%s] Queued "%s" for transfer to %s',
                            time.strftime('%d/%b/%Y %H:%M:%S'),
                            unquote(theurl), togo_path)
            urlstring = '<br>'.join([unquote(x) for x in urls])
            message = TRANS_QUEUE % (urlstring, togo_path)
        else:
            message = MISSING
        handler.redir(message, 5)
Пример #31
0
#!/usr/bin/env python

import beacon, httpserver, os, sys
import config
from plugin import GetPlugin

port = config.getPort()

httpd = httpserver.TivoHTTPServer(('', int(port)), httpserver.TivoHTTPHandler)

for section, settings in config.getShares():
    httpd.add_container(section, settings)
    # Precaching of files: does a recursive list of base path
    if settings.get('precache', 'False').lower() == 'true':
        plugin = GetPlugin(settings.get('type'))
        if hasattr(plugin, 'pre_cache'):
            print 'Pre-caching the', section, 'share.'
            pre_cache_filter = getattr(plugin, 'pre_cache')

            def build_recursive_list(path):
                try:
                    for f in os.listdir(path):
                        f = os.path.join(path, f)
                        if os.path.isdir(f):
                            build_recursive_list(f)
                        else:
                            pre_cache_filter(f)
                except:
                    pass

            build_recursive_list(settings.get('path'))
Пример #32
0
    def push_one_file(self, f):
        file_info = VideoDetails()
        file_info['valid'] = transcode.supported_format(f['path'])

        temp_share = config.get_server('temp_share', '')
        temp_share_path = ''
        remux_path = os.path.dirname(f['path'])
        if temp_share:
            for name, data in config.getShares():
                if temp_share == name:
                    temp_share_path = data.get('path')
                    remux_path = temp_share_path

        mime = 'video/mpeg'
        if config.isHDtivo(f['tsn']):
            for m in ['video/mp4', 'video/bif']:
                if transcode.tivo_compatible(f['path'], f['tsn'], m)[0]:
                    mime = m
                    break

            if (mime == 'video/mpeg' and
                transcode.mp4_remuxable(f['path'], f['tsn'])):
                if config.get_freeSpace(remux_path, f['path']):
                    new_path = transcode.mp4_remux(f['path'], f['name'], f['tsn'], temp_share_path)
                    if new_path:
                        mime = 'video/mp4'
                        f['name'] = new_path
                        if temp_share_path:
                            ip = config.get_ip()
                            port = config.getPort()
                            container = quote(temp_share) + '/'
                            f['url'] = 'http://%s:%s/%s' % (ip, port, container)
                else:
                    logger.warning('Not enough disk space to perform remux, ' +
                                   'transcoding instead.')

        if file_info['valid']:
            file_info.update(self.metadata_full(f['path'], f['tsn'], mime))

        url = f['url'] + quote(f['name'])

        title = file_info['seriesTitle']
        if not title:
            title = file_info['title']

        source = file_info['seriesId']
        if not source:
            source = title

        subtitle = file_info['episodeTitle']
        try:
            m = mind.getMind(f['tsn'])
            m.pushVideo(
                tsn = f['tsn'],
                url = url,
                description = file_info['description'],
                duration = file_info['duration'] / 1000,
                size = file_info['size'],
                title = title,
                subtitle = subtitle,
                source = source,
                mime = mime,
                tvrating = file_info['tvRating'])
        except ValueError, msg:
            if 'usernamePasswordError' in msg:
                if f['name'].endswith('.pyTivo-temp'):
                    fname = os.path.join(remux_path, os.path.basename(f['name']))
                    fname = unicode(fname, 'utf-8')
                    os.remove(fname)
                    logger.debug(fname + ' has been removed')
Пример #33
0
def exceptionLogger(*args):
    sys.excepthook = sys.__excepthook__
    logging.getLogger('pyTivo').error('Exception in pyTivo', exc_info=args)

config.init(sys.argv[1:])
config.init_logging()
sys.excepthook = exceptionLogger

port = config.getPort()

httpd = httpserver.TivoHTTPServer(('', int(port)), httpserver.TivoHTTPHandler)

logger = logging.getLogger('pyTivo')

for section, settings in config.getShares():
    httpd.add_container(section, settings)
    # Precaching of files: does a recursive list of base path
    if settings.get('precache', 'False').lower() == 'true':
        plugin = GetPlugin(settings.get('type'))
        if hasattr(plugin, 'pre_cache'):
            logger.info('Pre-caching the ' + section + ' share.')
            pre_cache_filter = getattr(plugin, 'pre_cache')

            def build_recursive_list(path):
                try:
                    for f in os.listdir(path):
                        f = os.path.join(path, f)
                        if os.path.isdir(f):
                            build_recursive_list(f)
                        else:
Пример #34
0
    def NPL(self, handler, query):
        shows_per_page = 50 #Change this to alter the number of shows returned per page
        subcname = query['Container'][0]
        cname = subcname.split('/')[0]
        folder = ''
        AnchorItem = ''
        AnchorOffset= ''
        for name, data in config.getShares():
            if cname == name:
                if 'tivo_mak' in data:
                    tivo_mak = data['tivo_mak']
                else:
                    tivo_mak = ""
                if 'togo_path' in data:
                    togo_path = data['togo_path']
                else:
                    togo_path = ""

        if 'TiVo' in query:
            tivoIP = query['TiVo'][0]
            theurl = 'https://'+ tivoIP +'/TiVoConnect?Command=QueryContainer&ItemCount='+ str(shows_per_page) +'&Container=/NowPlaying'
            if 'Folder' in query:
                folder += str(query['Folder'][0])
                theurl += '/' + folder
            if 'AnchorItem' in query:
                AnchorItem += str(query['AnchorItem'][0])
                theurl += '&AnchorItem=' + quote(AnchorItem)
            if 'AnchorOffset' in query:
                AnchorOffset += str(query['AnchorOffset'][0])
                theurl += '&AnchorOffset=' + AnchorOffset

            password = tivo_mak #TiVo MAK

            r=urllib2.Request(theurl)
            auth_handler = urllib2.HTTPDigestAuthHandler()
            auth_handler.add_password('TiVo DVR', tivoIP, 'tivo', password)
            opener = urllib2.build_opener(auth_handler)
            urllib2.install_opener(opener)

            if theurl in tivo_cache: #check to see if we have accessed this page before
                if tivo_cache[theurl]['thepage'] == '' or (time.time() - tivo_cache[theurl]['thepage_time']) >= 60: #if page is empty or old then retreive it
                    try:
                        handle = urllib2.urlopen(r)
                    except IOError, e:
                        handler.send_response(200)
                        handler.end_headers()
                        t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
                        t.container = cname
                        t.time = '20'
                        t.url = '/TiVoConnect?Command=NPL&Container=' + cname
                        t.text = '<h3>Unable to Connect to TiVo.</h3>  <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
                                 '<br>This most likely caused by an incorrect Media Access Key.  Please return to the ToGo page and double check your Media Access Key.' +\
                                 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
                        handler.wfile.write(t)
                        return 
                    tivo_cache[theurl]['thepage'] = handle.read()
                    tivo_cache[theurl]['thepage_time'] = time.time()
            else: #not in cache
                try:
                    handle = urllib2.urlopen(r)
                except IOError, e:
                    handler.send_response(200)
                    handler.end_headers()
                    t = Template(file=os.path.join(SCRIPTDIR,'templates', 'redirect.tmpl'))
                    t.container = cname
                    t.time = '20'
                    t.url = '/TiVoConnect?Command=NPL&Container=' + cname
                    t.text = '<h3>Unable to Connect to TiVo.</h3>  <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
                             '<br>This most likely caused by an incorrect Media Access Key.  Please return to the ToGo page and double check your Media Access Key.' +\
                             '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
                    handler.wfile.write(t)
                    return
                tivo_cache[theurl] = {}
                tivo_cache[theurl]['thepage'] = handle.read()
                tivo_cache[theurl]['thepage_time'] = time.time()
Пример #35
0
 def reset(self):
     self.containers.clear()
     for section, settings in config.getShares():
         self.add_container(section, settings)
Пример #36
0
    def ToGo(self, handler, query):
        togo_path = config.get_server('togo_path')
        for name, data in config.getShares():
            if togo_path == name:
                togo_path = data.get('path')
        if togo_path:
            tivoIP = query['TiVo'][0]
            tsn = config.tivos_by_ip(tivoIP)
            tivo_mak = config.get_tsn('tivo_mak', tsn)
            urls = query.get('Url', [])
            decode = 'decode' in query
            save = 'save' in query

            if 'postprocess' in query:
                postprocess = query['postprocess'][0]
            else:
                postprocess = config.get_server('vrd_post_processing')

            if 'postprocess_profile' in query:
                postprocess_profile = query['postprocess_profile'][0]
            else:
                postprocess_profile = config.get_server('vrd_profile', '')

            postprocess_decrypt = 'postprocess_decrypt' in query
            if not postprocess_decrypt:
                try:
                    postprocess_decrypt = config.config.getboolean(
                        'Server', 'vrd_decrypt_qsf')
                except:
                    postprocess_decrypt = False

            postprocess_delete = 'postprocess_delete' in query
            if not postprocess_delete:
                try:
                    postprocess_delete = config.config.getboolean(
                        'Server', 'vrd_delete_on_success')
                except:
                    postprocess_delete = False

            ts_format = 'ts_format' in query and config.is_ts_capable(tsn)
            for theurl in urls:
                if theurl in status:
                    del status[theurl]

                status[theurl] = {
                    'running':
                    False,
                    'status':
                    '',
                    'error':
                    '',
                    'rate':
                    0,
                    'percent':
                    0,
                    'queued':
                    True,
                    'size':
                    0,
                    'postprocessing':
                    False,
                    'finished':
                    False,
                    'decode':
                    decode,
                    'save':
                    save,
                    'ts_format':
                    ts_format,
                    'postprocess':
                    postprocess,
                    'postprocess_profile':
                    postprocess_profile,
                    'postprocess_decrypt':
                    postprocess_decrypt,
                    'postprocess_delete':
                    postprocess_delete,
                    'retry':
                    0,
                    'ts_max_retries':
                    int(config.get_server('togo_ts_max_retries', 0)),
                    'ts_error_count':
                    0,
                    'best_file':
                    '',
                    'best_error_count':
                    0
                }
                if tivoIP in queue:
                    queue[tivoIP].append(theurl)
                else:
                    queue[tivoIP] = [theurl]
                    thread.start_new_thread(
                        ToGo.process_queue,
                        (self, tivoIP, tivo_mak, togo_path))
                logger.info('[%s] Queued "%s" for transfer to %s' %
                            (time.strftime('%d/%b/%Y %H:%M:%S'),
                             unquote(theurl), togo_path))
            urlstring = '<br>'.join(
                [unicode(unquote(x), 'utf-8') for x in urls])
            message = TRANS_QUEUE % (urlstring, togo_path)
        else:
            message = MISSING
        handler.redir(message, 5)
Пример #37
0
    def NPL(self, handler, query):
        shows_per_page = 50  #Change this to alter the number of shows returned per page
        subcname = query['Container'][0]
        cname = subcname.split('/')[0]
        folder = ''
        AnchorItem = ''
        AnchorOffset = ''
        for name, data in config.getShares():
            if cname == name:
                if 'tivo_mak' in data:
                    tivo_mak = data['tivo_mak']
                else:
                    tivo_mak = ""
                if 'togo_path' in data:
                    togo_path = data['togo_path']
                else:
                    togo_path = ""

        if 'TiVo' in query:
            tivoIP = query['TiVo'][0]
            theurl = 'https://' + tivoIP + '/TiVoConnect?Command=QueryContainer&ItemCount=' + str(
                shows_per_page) + '&Container=/NowPlaying'
            if 'Folder' in query:
                folder += str(query['Folder'][0])
                theurl += '/' + folder
            if 'AnchorItem' in query:
                AnchorItem += str(query['AnchorItem'][0])
                theurl += '&AnchorItem=' + quote(AnchorItem)
            if 'AnchorOffset' in query:
                AnchorOffset += str(query['AnchorOffset'][0])
                theurl += '&AnchorOffset=' + AnchorOffset

            password = tivo_mak  #TiVo MAK

            r = urllib2.Request(theurl)
            auth_handler = urllib2.HTTPDigestAuthHandler()
            auth_handler.add_password('TiVo DVR', tivoIP, 'tivo', password)
            opener = urllib2.build_opener(auth_handler)
            urllib2.install_opener(opener)

            if theurl in tivo_cache:  #check to see if we have accessed this page before
                if tivo_cache[theurl]['thepage'] == '' or (
                        time.time() - tivo_cache[theurl]['thepage_time']
                ) >= 60:  #if page is empty or old then retreive it
                    try:
                        handle = urllib2.urlopen(r)
                    except IOError, e:
                        handler.send_response(200)
                        handler.end_headers()
                        t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                                       'redirect.tmpl'))
                        t.container = cname
                        t.time = '20'
                        t.url = '/TiVoConnect?Command=NPL&Container=' + cname
                        t.text = '<h3>Unable to Connect to TiVo.</h3>  <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
                                 '<br>This most likely caused by an incorrect Media Access Key.  Please return to the ToGo page and double check your Media Access Key.' +\
                                 '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
                        handler.wfile.write(t)
                        return
                    tivo_cache[theurl]['thepage'] = handle.read()
                    tivo_cache[theurl]['thepage_time'] = time.time()
            else:  #not in cache
                try:
                    handle = urllib2.urlopen(r)
                except IOError, e:
                    handler.send_response(200)
                    handler.end_headers()
                    t = Template(file=os.path.join(SCRIPTDIR, 'templates',
                                                   'redirect.tmpl'))
                    t.container = cname
                    t.time = '20'
                    t.url = '/TiVoConnect?Command=NPL&Container=' + cname
                    t.text = '<h3>Unable to Connect to TiVo.</h3>  <br>pyTivo was unable to connect to the TiVo at ' + tivoIP +\
                             '<br>This most likely caused by an incorrect Media Access Key.  Please return to the ToGo page and double check your Media Access Key.' +\
                             '<br> The <a href="/TiVoConnect?Command=NPL&Container='+ cname + '"> ToGo</a> page will reload in 20 seconds.'
                    handler.wfile.write(t)
                    return
                tivo_cache[theurl] = {}
                tivo_cache[theurl]['thepage'] = handle.read()
                tivo_cache[theurl]['thepage_time'] = time.time()