Updated to support webvideo
authorJason Michalski <armooo@armooo.net>
Sun, 6 Apr 2008 05:54:26 +0000 (6 00:54 -0500)
committerJason Michalski <armooo@armooo.net>
Sun, 6 Apr 2008 05:54:26 +0000 (6 00:54 -0500)
mind.py

diff --git a/mind.py b/mind.py
index 7441373..86a9134 100644 (file)
--- a/mind.py
+++ b/mind.py
@@ -2,31 +2,32 @@ import cookielib
 import urllib2
 import urllib
 import struct
-import httplib 
+import httplib
 import time
 import warnings
 import itertools
+import config
 
 try:
-    import xml.etree.ElementTree as ElementTree 
+    import xml.etree.ElementTree as ElementTree
 except ImportError:
     try:
         import elementtree.ElementTree as ElementTree
     except ImportError:
         warnings.warn('Python 2.5 or higher or elementtree is needed to use the TivoPush')
-        
+
 if 'ElementTree' not in locals():
 
     class Mind:
         def __init__(self, *arg, **karg):
             raise Exception('Python 2.5 or higher or elementtree is needed to use the TivoPush')
-    
+
 else:
 
     class Mind:
         def __init__(self, username, password, debug=False):
             self.__username = username
-            self.__password = password 
+            self.__password = password
 
             self.__debug = debug
 
@@ -38,26 +39,86 @@ else:
             if not self.__pcBodySearch():
                 self.__pcBodyStore('pyTivo', True)
 
-        def pushVideo(self, tsn, url, description='test', duration='40000', size='3000000', title='test', subtitle='test'):
-            
+        def pushVideo(self, tsn, url, description, duration, size, title, subtitle):
+            data = {
+                'bodyId' : 'tsn:' + tsn,
+                'description' : description,
+                'duration' : duration,
+                'encodingType' : 'mpeg2ProgramStream',
+                'partnerId' : 'tivo:pt.3187',
+                'pcBodyId' : pc_body_id,
+                'publishDate' : time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
+                'size' : size,
+                'source' : 'file:/C%3A%2FDocuments%20and%20Settings%2FStephanie%2FDesktop%2FVideo',
+                'state' : 'complete',
+                'subtitle' : subtitle,
+                'title' : title,
+                'url' : url,
+            }
+
             # It looks like tivo only supports one pc per house
             pc_body_id = self.__pcBodySearch()[0]
-            offer_id, content_id = self.__bodyOfferModify(tsn, pc_body_id, description, duration, size, title, subtitle, url)
+            offer_id, content_id = self.__bodyOfferModify(data)
             self.__subscribe(offer_id, content_id, tsn)
 
-        def bodyOfferSchedule(self, pc_body_id):
+        def getDownloadRequests(self):
+            NEEDED_VALUES = [
+                'bodyId',
+                'bodyOfferId',
+                'description',
+                'partnerId',
+                'pcBodyId',
+                'publishDate',
+                'source',
+                'state',
+                'subscriptionId',
+                'subtitle',
+                'title',
+                'url',
+            ]
 
-            data = {'pcBodyId' : pc_body_id,}
-            r = urllib2.Request(
-                '/Steph%27s%20Videos/The%20Fairly%20Odd%20Parents%20-%20Channel%20Chasers.xvid-pyro.avi', 
-                dictcode(data),
-                {'Content-Type' : 'x-tivo/dict-binary'}
-            )
-            result = self.__opener.open(r)
+            # It looks like tivo only supports one pc per house
+            pc_body_id = self.__pcBodySearch()[0]
+
+            requests = []
+            offer_list = self.__bodyOfferSchedule(pc_body_id)
+
+            for offer in offer_list.findall('bodyOffer'):
+                d = {}
+                if offer.findtext('state') != 'scheduled':
+                    continue
+
+                for n in NEEDED_VALUES:
+                    d[n] = offer.findtext(n)
+                requests.append(d)
+
+            return requests
+
+        def completeDownloadRequest(self, request):
+            request['encodingType'] = 'mpeg2ProgramStream'
+            request['state'] = 'complete'
+            request['type'] = 'bodyOfferModify'
+            request['updateDate'] = time.strftime('%Y-%m-%d %H:%M%S', time.gmtime())
+
+            offer_id, content_id = self.__bodyOfferModify(request)
+            self.__subscribe(offer_id, content_id, request['bodyId'][4:])
+
+
+        def getXMPPLoginInfo(self):
+            # It looks like tivo only supports one pc per house
+            pc_body_id = self.__pcBodySearch()[0]
 
-            self.__log('bodyOfferSchedule\n%s\n\n%sg' % (data, result))
+            xml = self.__bodyXmppInfoGet(pc_body_id)
 
-            return result.read()
+            results = {}
+            results['server'] = xml.findtext('server')
+            results['port'] = int(xml.findtext('port'))
+            results['username'] = xml.findtext('xmppId')
+
+            for sendPresence in xml.findall('sendPresence'):
+                results.setdefault('presence_list', []).append(sendPresence.text)
+
+            return results
 
         def __log(self, message):
             if self.__debug:
@@ -75,7 +136,7 @@ else:
             }
 
             r =  urllib2.Request(
-                'https://mind.tivo.com:8181/mind/login', 
+                'https://mind.tivo.com:8181/mind/login',
                 urllib.urlencode(data)
             )
             try:
@@ -85,32 +146,17 @@ else:
 
             self.__log('__login\n%s' % (data))
 
-        def __bodyOfferModify(self, tsn, pc_body_id, description, duration, size, title, subtitle, url):
-
-            data = {
-                'bodyId' : 'tsn:' + tsn,
-                'description' : description,
-                'duration' : duration,
-                'encodingType' : 'mpeg2ProgramStream',
-                'partnerId' : 'tivo:pt.3187',
-                'pcBodyId' : pc_body_id,
-                'publishDate' : time.strftime('%Y-%m-%d %H:%M%S', time.gmtime()),
-                'size' : size,
-                'source' : 'file:/C%3A%2FDocuments%20and%20Settings%2FStephanie%2FDesktop%2FVideo',
-                'state' : 'complete',
-                'subtitle' : subtitle,
-                'title' : title,
-                'url' : url,
-            }
+        def __bodyOfferModify(self, data):
+            """Create an offer"""
             r = urllib2.Request(
-                'https://mind.tivo.com:8181/mind/mind7?type=bodyOfferModify&bodyId=tsn:' + tsn, 
+                'https://mind.tivo.com:8181/mind/mind7?type=bodyOfferModify&bodyId=' + data['bodyId'],
                 dictcode(data),
                 {'Content-Type' : 'x-tivo/dict-binary'}
             )
             result = self.__opener.open(r)
 
             xml = ElementTree.parse(result).find('.')
-            
+
             self.__log('__bodyOfferModify\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
 
             if xml.findtext('state') != 'complete':
@@ -123,6 +169,7 @@ else:
 
 
         def __subscribe(self, offer_id, content_id, tsn):
+            """Push the offer to the tivo"""
             data =  {
                 'bodyId' : 'tsn:' + tsn,
                 'idSetSource' : {
@@ -133,9 +180,9 @@ else:
                 'title' : 'pcBodySubscription',
                 'uiType' : 'cds',
             }
-            
+
             r = urllib2.Request(
-                'https://mind.tivo.com:8181/mind/mind7?type=subscribe&bodyId=tsn:' + tsn, 
+                'https://mind.tivo.com:8181/mind/mind7?type=subscribe&bodyId=tsn:' + tsn,
                 dictcode(data),
                 {'Content-Type' : 'x-tivo/dict-binary'}
             )
@@ -147,11 +194,29 @@ else:
 
             return xml
 
+        def __bodyOfferSchedule(self, pc_body_id):
+            """Get pending stuff for this pc"""
+
+            data = {'pcBodyId' : pc_body_id,}
+            r = urllib2.Request(
+                'https://mind.tivo.com:8181/mind/mind7?type=bodyOfferSchedule',
+                dictcode(data),
+                {'Content-Type' : 'x-tivo/dict-binary'}
+            )
+            result = self.__opener.open(r)
+
+            xml = ElementTree.parse(result).find('.')
+
+            self.__log('bodyOfferSchedule\n%s\n\n%sg' % (data, ElementTree.tostring(xml)))
+
+            return xml
+
         def __pcBodySearch(self):
+            """Find PCS"""
 
             data = {}
             r = urllib2.Request(
-                'https://mind.tivo.com:8181/mind/mind7?type=pcBodySearch', 
+                'https://mind.tivo.com:8181/mind/mind7?type=pcBodySearch',
                 dictcode(data),
                 {'Content-Type' : 'x-tivo/dict-binary'}
             )
@@ -165,10 +230,11 @@ else:
             return [id.text for id in xml.findall('pcBody/pcBodyId')]
 
         def __collectionIdSearch(self, url):
+            """Find collection ids"""
 
             data = {'url' : url}
             r = urllib2.Request(
-                'https://mind.tivo.com:8181/mind/mind7?type=collectionIdSearch', 
+                'https://mind.tivo.com:8181/mind/mind7?type=collectionIdSearch',
                 dictcode(data),
                 {'Content-Type' : 'x-tivo/dict-binary'}
             )
@@ -182,15 +248,16 @@ else:
             return collection_id
 
         def __pcBodyStore(self, name, replace=False):
-           
+            """Setup a new PC"""
+
             data = {
                 'name' : name,
                 'replaceExisting' : str(replace).lower(),
             }
 
             r = urllib2.Request(
-                'https://mind.tivo.com:8181/mind/mind7?type=pcBodyStore', 
-                dictcode(data), 
+                'https://mind.tivo.com:8181/mind/mind7?type=pcBodyStore',
+                dictcode(data),
                 {'Content-Type' : 'x-tivo/dict-binary'}
             )
             result = self.__opener.open(r)
@@ -201,6 +268,26 @@ else:
 
             return xml
 
+        def __bodyXmppInfoGet(self, body_id):
+
+            data = {
+                'bodyId' : body_id,
+            }
+
+            r = urllib2.Request(
+                'https://mind.tivo.com:8181/mind/mind7?type=bodyXmppInfoGet&bodyId=' + body_id,
+                dictcode(data),
+                {'Content-Type' : 'x-tivo/dict-binary'}
+            )
+
+            result = self.__opener.open(r)
+
+            xml = ElementTree.parse(result).find('.')
+
+            self.__log('__bodyXmppInfoGe\n%s\n\n%s' % (data, ElementTree.tostring(xml)))
+
+            return xml
+
 
     def dictcode(d):
         """Helper to create x-tivo/dict-binary"""
@@ -220,9 +307,9 @@ else:
                 output.append( dictcode(v) )
 
             else:
-                v = str(v)
+                v = unicode(v).encode('utf-8')
                 output.append( struct.pack('>B', 0x01) )
-                output.append( varint( len(v) ) ) 
+                output.append( varint( len(v) ) )
                 output.append( v )
 
             output.append( struct.pack('>B', 0x00) )
@@ -238,7 +325,7 @@ else:
         while i:
             bits.append(i & 0x01)
             i = i  >> 1
-        
+
         if not bits:
             output = [0]
         else:
@@ -248,7 +335,7 @@ else:
             byte = 0
             mybits = bits[:7]
             del bits[:7]
-           
+
             for bit, p in zip(mybits, itertools.count()):
                 byte += bit * (2 ** p)
 
@@ -257,3 +344,15 @@ else:
         output[-1] = output[-1] | 0x80
         return ''.join([chr(b) for b in output])
 
+
+def getMind():
+    username = config.getTivoUsername()
+    password = config.getTivoPassword()
+
+    if not username or not password:
+       raise Exception("tivo_username and tivo_password required")
+
+    m = Mind(username, password, True)
+
+    return m
+