1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

HME for Python

Discussion in 'Developers Corner' started by wmcbrine, Jan 28, 2008.

  1. wmcbrine

    wmcbrine Ziphead

    10,362
    22
    Aug 2, 2003
    I've started a GitHub page for HME for Python, so that, finally, you can download it without registering here. :)
     
  2. wmcbrine

    wmcbrine Ziphead

    10,362
    22
    Aug 2, 2003
    Direct text keyboard input support.
     
  3. jtseltmann

    jtseltmann New Member

    147
    0
    May 22, 2005
    I'm very new to mac programming and python...well mac in general. How do I go about getting this great code installed and running? I downloaded the zip file...but I don't see how to get it running? Is there install directions for dummies? i'm coming from windows programming background so...be patient!
    Thanks
    J
     
  4. wmcbrine

    wmcbrine Ziphead

    10,362
    22
    Aug 2, 2003
    I already put my best instructions in the README, so unless you have a more specific question, I can't help you. I can tell you that there's no need to "install" it, though. Just unzip it.

    BTW, how do you know it's great code if you haven't got it working yet? :)
     
  5. Feb 1, 2011 #125 of 231
    ttocsmi

    ttocsmi New Member

    19
    0
    Jan 6, 2007
    i'm intrigued by this program - it seems like it's quite handy and i can tell you (and others here) have put a lot of time and effort into its development.

    i downloaded both the zeroconf.py and remote.pwy files, and can view each in IDLE. i also installed python 2.7 on my XP laptop. when i attempt to run each, a shell window briefly appears, disappears, and nothing else happens. if i try to run each file using F5 from within Idle, a "there's an error in your program: invalid syntax" box appears and nothing else happens.

    any ideas as to what i'm doing wrong? thank you very much.

    scott
     
  6. Feb 1, 2011 #126 of 231
    wmcbrine

    wmcbrine Ziphead

    10,362
    22
    Aug 2, 2003
    Well, remote.pyw is over in the other thread... anyway, don't run it from IDLE. You don't need IDLE, and Tk-based apps like remote.pyw won't work from within IDLE. For remote.pyw, just double-click on it from the desktop. Alternatively, you can start it by typing "python remote.pyw" at the command line.
     
  7. Feb 3, 2011 #127 of 231
    ttocsmi

    ttocsmi New Member

    19
    0
    Jan 6, 2007
    thanks again for your assistance. i tried double-clicking it again from my desktop - nothing.

    i also tried launching from the command prompt and got the message:

    file"remote.pyw", line 6
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    SyntaxError: invalid syntax

    very odd.
     
  8. Feb 3, 2011 #128 of 231
    wmcbrine

    wmcbrine Ziphead

    10,362
    22
    Aug 2, 2003
    Yes, that certainly is odd. You've somehow got XHTML in your remote.pyw. I guarantee, it didn't come that way.

    Seeing that you said "i downloaded both the zeroconf.py and remote.pwy files" suggests your error. Download the zip file.

    Anyway, please stop posting about remote.pyw in the HME for Python thread. Post in the appropriate thread, or PM me if you must.
     
  9. Feb 4, 2011 #129 of 231
    ttocsmi

    ttocsmi New Member

    19
    0
    Jan 6, 2007
  10. carlroth

    carlroth New Member

    1
    0
    Oct 3, 2009
    I wrote this simple HME app, and I am finding that the TiVo (or at least, the simulated TiVo) quits the application when the last (non-root) view is removed. Is this expected behavior?
     

    Attached Files:

  11. wmcbrine

    wmcbrine Ziphead

    10,362
    22
    Aug 2, 2003
    It doesn't happen on my real Premiere. Looks like another simulator bug.
     
  12. jbernardis

    jbernardis New Member

    1,072
    0
    Oct 21, 2003
    Princeton NJ
    In another thread, I was saying that I wish that when I requested a video from one of my tivos that pytivo processed this as a push instead of a pull. Then I started thinking that I could write an HME app hased on your module here that does just this.

    I started working on it this pask weekend and have gotten quite far - far enough that I'm now actually thinking about the code for the push itself. The question I have is how do I know the identity of the tivo to which to push the video. I was thinking that I only needed to know the identity of the tivo I was talking to, but then I thought it might be nice if I could push to a different tivo also. However this requires that I discover the identities of the tivos on my network.

    I see that you have the zeroconf.py module as part of your start.py, although you are using it only to advertise the applications. I'm trying to think of the best way to incorporate discovery. I don't think the apps should do it since they only exist for each instantiation and I think it would be expensive to do discovery so often. Also, you already have the zeroconf code intertwined in the start code.

    I guess I could modify the start code to do the discovery, but then I would need to pass this information down into the app - either through a new method that would be stubbed out in the base class but implemented in the apps where necessary, or simply through additional parameters passed into mainloop and then into the startup method.

    Am I missing something here? Can you think of any other way to tackle this problem?

    Thanks
     
  13. orangeboy

    orangeboy yes, I AM orangeboy!

    4,083
    0
    Apr 19, 2004
    East Moline, IL
    Here's a small chunk of python code I use in my TiVoToDo project to get the TiVos on my LAN, and to put the information into a dictionary, with the TiVo name as the key, and the TSN as the item the key refers to:

    TiVoToDo.py:
    Code:
    import config as c
    import tools as t
    
    # Find TiVos on the LAN
    b = t.Beacon()
    b.start()
    b.stop()
    
    tools.py
    Code:
    import config as c
    import Zeroconf as Z
    
    class ZCListener:
        def __init__(self, names):
            self.names = names
    
        def removeService(self, server, type, name):
            if name in self.names:
                self.names.remove(name)
    
        def addService(self, server, type, name):
            self.names.append(name)
    
    class ZCBroadcast:
        def __init__(self):
            self.rz = Z.Zeroconf()
    
        def scan(self):
            """ Look for TiVos using Zeroconf. """
            VIDS = '_tivo-videos._tcp.local.'
            names = []
    
            # Get the names of servers offering TiVo videos.
            browser = Z.ServiceBrowser(self.rz, VIDS, ZCListener(names))
    
            # Give them half a second to respond.
            time.sleep(.5)
    
            # Now get the addresses -- this is the slow part.
            for name in names:
                info = self.rz.getServiceInfo(VIDS, name)
                if info and 'TSN' in info.properties:
                    tsn = info.properties['TSN']
                    name = name.replace('.' + VIDS, '').encode('utf-8')
                    if isHDtivo(tsn):
                        c.tivos[name] = tsn
    
        def shutdown(self):
            self.rz.close()
            time.sleep(.5)
    
    class Beacon:
        UDPSock = socket(AF_INET, SOCK_DGRAM)
        UDPSock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
        services = []
    
        def __init__(self):
            self.log = logging.getLogger("TiVoToDo.tools.Beacon")
            self.log.info('Scanning for TiVos...')
            self.bd = ZCBroadcast()
            self.bd.scan()
    
        def format_services(self):
            return ';'.join(self.services)
    
        def format_beacon(self, conntype, services=True):
            beacon = ['tivoconnect=1',
                      'swversion=1',
                      'method=%s' % conntype,
                      'identity=%s' % c.getGUID(),
                      'machine=%s' % gethostname(),
                      'platform=pc']
    
            if services:
                beacon.append('services=' + self.format_services())
            else:
                beacon.append('services=TiVoMediaServer:0/http')
    
            return '\n'.join(beacon)
    
        def send_beacon(self):
            beacon_ip = c.get_broadcast()
            if not beacon_ip:
                beacon_ip = '255.255.255.255'
            try:
                self.UDPSock.sendto(self.format_beacon('broadcast'),
                                        (beacon_ip, 2190))
            except error, e:
                log.debug(e)
    
        def start(self):
            self.send_beacon()
            self.timer = Timer(60, self.start)
            self.timer.start()
    
        def stop(self):
            self.timer.cancel()
            if self.bd:
                self.bd.shutdown()
    
    This was code modified from pyTivo. Only a couple of lines were changed to get the functionality I needed.

    HTH!

    Edit: You may not need or want the HD model test: "if isHDtivo(tsn):", so you could delete that line and re-indent "c.tivos[name] = tsn" appropriately.
     
  14. jbernardis

    jbernardis New Member

    1,072
    0
    Oct 21, 2003
    Princeton NJ
    Thanks Orangeboy. I was looking through the code, and it seems to me that I could do it simply with zeroconf:
    Code:
    zc = ZeroConf()
    zc.addServiceListener(type, Listener)
    ...wait a few seconds...
    zc.removeServiceListener(Listener)
    zc.close()
    and the listener will have recorded the tivo names.

    The problem is that I don't think I can do this in the app because the main logic already has a listener thread going and I think the two threads would be competing for the same port number (or maybe I don't fully understand this zeroconf stuff - very possible).

    I think instead I will have to adapt the main logic. Right now, it registers the apps inside the Broadcast constructor - but this is also where the Zeroconf constructor is called.

    I was thinking of doing the following:
    1) split the Broadcast constructor into the constructor and a separate method for registering the apps.
    2) adding a method for the above discovery logic (much like the scan method in pytivo)
    3) changing the mainline logic to construct the Broadcast object and to invoke the discovery method before the loop that imports the apps, and then calling the new register method after this loop.

    That would give me the list of Tivos which I would have to pass into the Server constructor so that it could then get passed down into the apps themselves.
     
  15. jbernardis

    jbernardis New Member

    1,072
    0
    Oct 21, 2003
    Princeton NJ
    The code I ended up adding to start.py is the following:
    Code:
    class ZCListener:
    	def __init__(self, names):
    		self.names = names
    	
    	def removeService(self, server, type, name):
    		if name in self.names:
    			self.names.remove(name)
    
    	def addService(self, server, type, name):
    		self.names.append(name)
    
    def doDiscovery(config):
    	""" Look for TiVos using Zeroconf. """
    	VIDS = '_tivo-videos._tcp.local.'
    	names = []
    
    	rz = Zeroconf.Zeroconf()
    	print "Attempting discovery..."
    	# Get the names of servers offering TiVo videos
    	browser = Zeroconf.ServiceBrowser(rz, VIDS, ZCListener(names))
    	
    	config.remove_section(DT_SECTION)
    	
    	tivoCount = 0
    	
    	time.sleep(5)
    	
    	
    	# Now get the addresses -- this is the slow part
    	for name in names:
    		info = rz.getServiceInfo(VIDS, name)
    		if info and 'TSN' in info.properties:
    			tsn = info.properties['TSN']
    			address = socket.inet_ntoa(info.getAddress())
    			name = name.replace('.' + VIDS, '')
    			print "Discovered tivo: " + name + ' ' + address + ' ' + tsn
    			if tivoCount == 0:
    				config.add_section(DT_SECTION)
    			tivoCount = tivoCount + 1
    			config.set(DT_SECTION, 'tivo'+str(tivoCount)+'.name', name)
    			config.set(DT_SECTION, 'tivo'+str(tivoCount)+'.tsn', tsn)
    			config.set(DT_SECTION, 'tivo'+str(tivoCount)+'.address', address)
    
    	browser.cancel()
    	rz.close()
    This was added right before the "main" routine. And then later in the code, I added:

    Code:
    	if have_zc:
    		doDiscovery(config)
    
    This was added immediately after determining if we have zeroconf available.

    Two things to note here: 1) This does not work reliably on windows. It works fine after a reboot, but after that, it only works intermittently. I suspect that windows is not cleaning up the outstanding threads after the process is killed. It works flawlessly on linux. 2) Note that I decided to place the discovery information into the config. This was already available to the application logic, so it was an already-existing way to pass it through.
     
  16. orangeboy

    orangeboy yes, I AM orangeboy!

    4,083
    0
    Apr 19, 2004
    East Moline, IL
    How's this project coming along? I'd be interested in such an app, and wondering if you'll make it available through google code, or some other means?
     
  17. jbernardis

    jbernardis New Member

    1,072
    0
    Oct 21, 2003
    Princeton NJ
    Sure - I'll make it available, but I'm not quite done yet. Also, I'm not crazy about the idea of providing support for the code, so it would most likely be on an as-provided basis.

    I am just about through the user interface - up to the point where I can make the actual push request, but I have also recently decided to add the ability to delete a video file, so I still have to do that too.

    I'm also re-thinking my "need" to do discovery. I need to study the push code in pytivo, but I think the only thing I need is the TSN - not the IP address. If so, I might just statically enter the values in the config file since the TSN isn't ever likely to change.
     
  18. orangeboy

    orangeboy yes, I AM orangeboy!

    4,083
    0
    Apr 19, 2004
    East Moline, IL
    Great! I look forward to seeing the finished product! :up:
    As far as it being "as-is", putting it in google code or some other type of distribution will give the chance for other developers to offer support, and also to make branches/plugins/whatever, as well.

    The delete feature may indeed come in handy.

    Early versions of my TiVoToDo code had the TSNs "hard-coded" in the config file. But yes, I believe only the TSN is required for the TiVo DVRs, but an IP address (or DNS name) and port would still be required for the pyTiVo server.
     
  19. jbernardis

    jbernardis New Member

    1,072
    0
    Oct 21, 2003
    Princeton NJ
    ok - so as of now, the entire user interface is working and the file deletion function is working. All that remains is the actual push. The user can select files to push, and select the tivo to push to, but all I do with the information right now is print it.

    I might need some help here. I had originally thought that I could just send the push request to mind, and pytivo would actually take care of the transfer, and I'm still not certain that won't work. I am having some difficulties right now though.


    I took the mind.py file from pytivo and adapted it for my purposes. Right now, I am able to log in to mind, but I get a 302 back which I think indicates a redirection. The login routine is line for line extracted from the pytivo file, so I don't know if this is an issue or not. Certainly pytivo does nothing with thie return code and just proceeds.

    Next my routine calls pcBodySearch and seems to get back a legitimatebody_id. So the next message sent to mind is via bodyOfferModify. This routine fails though. It tries to invoke the offer_id's replace method, but I get a message that there is no such method. I think the offer_id is getting the value of "None" but I haven't had time to analyze it yet.

    I'll keep going on this, but progress has definitely slowed down.
     
  20. orangeboy

    orangeboy yes, I AM orangeboy!

    4,083
    0
    Apr 19, 2004
    East Moline, IL
    Interesting. So pyTivo will not take any part of the push? I had mistakenly assumed that the request would be sent to pyTivo, and not directly to the mind server. This is what I'm currently doing in batch to initiate a push outside of pyTivo's web interface:

    Code:
    curl.exe -s -d "Command=Push&Container=%Container%&File=!File!&tsn=%tsn%" "http://DL380-Server:%port%/TivoConnect?"
    
    %port% is a variable for me since I have two instances of pyTivo running. All other variables should be self explanatory. It sounds like you're going a bit deeper than just merely passing the request off to pyTivo though...
     

Share This Page