Raspberry Pi

I was browsing the web today and stumbled onto something quit interesting. As it turns out, someone on Github has made a Python wrapper for the Plex Media Server http/xml API. I’ve been playing with it today and it seems to be something we might want to implement so we have clean and straight forward way to communicate with the server.



The only downside so far is that you have to hard code the ip of your plex server into the script. I’ve tried to get it through a avahi lookup but i’m stuck on installing gobject on the pi. Does anyone have an idea how to fix this?



The original code can be found here: https://github.com/rueckstiess/py-plex



My fork and altered code can be found here: https://github.com/megawubs/py-plex



The name is quit original if i say it myself :stuck_out_tongue:



--------------------- Edit



I just remembered that somewhere in the PyPlex code the ip of the server is available, looking into it tomorrow.

So, forgive me for asking the obvious question, but what’s the difference between this pyplex and the old pyplex?



py-plex is only a plex server API wrapper. PyPlex is the rs-pi plex client that might be usefull to use the API wrapper. The names are a bit confusing.

I’m very interested in this, I have a xios, a boxee box, an apple tv, and a wd-tv live and honestly I can’t stand any of them right now because of their lack of plex. The xios with xbmc-linux is cool, when it doesn’t crash. I decided to order a raspberry pi today, and hope I can help with the development of this, I have no python experience, but i’m a developer, so its all syntax. I’m wondering though the state of the plex-linux on the raspberry pi, and why you guys haven’t just forked that and tweaked it for the pi?



As for the UI (I just read all 14 pages, so it’s all new to me, though you guys probably discussed it months ago). How hard would it be to utilize a browser and plex-web?



Also, is it worth looking at the roku implementation of plex? as the roku box runs off an arm processor as well.



Finally, I read that you transcode everything that is not h.264, I was under the impression you can get a license for mpeg-2, does omx support this? What about decoding xvid? Does the rasberry pi have any hardware to support this?


I'll jump in and answer from my perspective; anyone else is welcome to chime in, of course.

1) Plex Linux; the client is from a previous age, the hard work that's gone in to it isn't really necessary now with most of the work shifted to the server (thank the mobile revolution). It also did not get the kind of tuning that would allow it to flourish with a 700mhz arm CPU and limited ram

2) plex-web in a browser. Honestly, I've never run a browser on the raspberry, but it doesn't feel like a basket to put the eggs in; the CPU is underpowered, and with less ram, the idea of having plex web on top of a browser on top of xwindows on top of an OS is something I'm not sure I'd personally shoot for, let alone start the process of trying to turn that in to a remote-capable client.

3) roku; I'll leave this to more capable people, but will definitely say that plex on the roku gets better with every update (and it's great already)

4) h264; there is the ability to add licenses for both mpeg2 and VC-1, and there is not technical limitation on having omx play these files from plex directly. As for xvid; there is no way to do this on the hardware currently, however, with my own testing, the CPU was able to keep up with decoding SD xvid files (also at this stage it's essentially trivial for the server to transcode them)

Honestly, I think that once you get your hands on the Pi, you'll very quickly understand why this project is taking the direction it is, especially if you think of a Pi, power, an sd card, and an hdmi cable as being all you need to turn a tv in to a plex client :)

Thanks, you pretty much squashed most of my questions, but I’d like to keep number 3 up for discussion. Plex on roku, and Apple TV 2 as well. Those are both arm based architectures with processors slower than the pi, what is keeping that from being ported?



I remember looking at the source code for the roku client on github. It's written in a roku only language (this just pops up in my head, but isnt it possible to install this language on the pi?) I remember it being a lot of files xD but didn't take a good look at how they solved problems etc. etc.
With the implementation of the plex API (which I'm currently working on in the plexAPI branch) communicating with the server becomes all a lot easier. This will also help enormous when we starts working on a GUI.
For example, getting a media object is now not more than 4 lines (including connecting to the server).

#file pyplex/commands/xbmc.pyparsed_path = urlparse(fullpath) # get info to locate the serverip, port = parsed_path.netloc.split(':') # initate the plex API server wrapperserver = Server(ip, port)# Serach for media based on tagmedia = server.getMedia(tag) #Media now contains all kind of information about the file# media.transcodeURL is currentley not workingif(self.omx):      self.Stop()<br />
self.omx = OMXPlayer(media.fileURL, args=self.omxArgs, start_playback=True)


Don't know why he's putting al code on one line....


I did take a look at the py-plex API, and I wasnt impressed. It seemed much more difficult than needed to do some of the tasks.

If you look at the github for PyPlex, you'll notice a plexInterface.py, which is my version (Far more incomplete, but in theory simpler), which does similar things for us.


The implementation might look overkill for just getting one media object, but this object now contains everything we need to know about the file, even the update and scrobble URL. So making a scrobble call would only be server.excequte(media.scrobbleURL). I also belief the code gets a lot cleaner and object orientated with this (aka future proof).
Have to say that I had to make made a lot of changes/additions to the py-plex code to make it the way it works now. You can find it on my GitHub page: github.com/megawubs/plex-api-wrapper

Hey guys!



Seems like you’ve been working on hard on making this come true. I’ve downloaded the PyPlex files and I’m trying to get it to work on my Raspberry Pi now.

I’m running Arch Linux and I’ve installed everything that I could think of in terms of dependencies.



Although, when I try to launch the application I get a ton of errors about print ‘’. I’m guessing that this has to do with Python 3, in which you declare print as a function print(’’).

Just to try, I manually went in and fixed all the lines that printed something so that they are set for Python 3, but now I get these errors:



[jonathan@alarmpi pyplex]$ python pyplex.py<br />
Traceback (most recent call last):<br />
  File "pyplex.py", line 2, in <module><br />
    from pyomxplayer import OMXPlayer<br />
  File "/home/jonathan/Applications/pyplex/pyomxplayer.py", line 3, in <module><br />
    import pexpect<br />
  File "/usr/lib/python3.3/site-packages/pexpect.py", line 82<br />
    except ImportError, e:<br />
                      ^<br />
SyntaxError: invalid syntax



Is the application written in Python 2, and how can I get it running? :/

Thanks in advance,
Jonathan


A common issue with Arch. As with the print function, the error you're seeing is due to another syntax change between python 2 and 3. Try discarding your changes, installing the python2 package and running the following:

python2 pyplex.py


(Also, any python packages required by pyplex will need to installed for python2.)

I have been working the past few days to get the plex-api fully implemented. And it works! Even with Avahi lookup. So no manually inserting the ip of the server at all. I just merged the plexAPI branch with the modules branch. I have tested it all and it seems to work just fine :). Think I’m going to mege the master with the modules branch soon.



The only thing I couldn’t get to work was threading the Avahi server lookup. The lookup is now only capable to find one server (which is the deafault setup for the most users I think) but once it is known it stops looking for one. So when your server goes down or another one comes online it isn’t able to find it. Hope someone has the skills to find a way to make this work as for me I can’t seem to figure or out.



And not to forget, happy new year to you all!! Now it’s time for some beer and fireworks, bye bye sublime text, see you next year :slight_smile:

Just came across this and I’m interested in what you guys have going, though I’m not completely following how you’re controlling the client. When you say that it uses the iOS remote, are you browsing the library on the client and telling it to play on PyPlex?



The reason I ask is because I’ve been trying to find a headless solution to do something like this with Plex on the Raspberry Pi.



So I guess my question is, how does this iOS remote functionality work? Does it tell the server to push data to the client? Does it tell the client to make a request? If I had a custom application that gets input from an RFID reader, will it be able to communicate with PyPlex?



Awesome; considered something like this with movies in RFID-tagged DVD cases.

In fact, the way plex works right now makes this extremely easy to implement, and doubly so with pyplex; all that has to be done is to associate the tag with a plex URL, then pass that to the client via the server

Yeah, it seems like it’d be extremely easy to integrate. Ideally the server could have a custom field that would hold an RFID value, but a secondary application and database that does the job would work too. I’m just having a hard time finding out anything about how clients request content.



Hello, I'll take some time to explain how pyplex knows what to play. You remind me to add more comments to the code ;) will do that later on.

The __main__.py file makes it possible to do python pyplex on a folder, the code inside this file initates pyplex, starts it (if there are no errors) and runs it. Not hard. The module used here is interface, and the class is pyPlex. In the pyPlex class, there happens a lot that is interesting. On initiation the class preforms a few actions.


def __init__(self, arg):<br />
		self.l = pyPlexLogger('PyPlex').logger<br />
		self.canStart = True<br />
		self.l.info("Starting up...")<br />
		self.omxCommand = self.getArg(arg)<br />
		self.hostname = platform.uname()[1]<br />
		self.server = AvahiLookUp("_plexmediasvr._tcp").servers[0]




From top to bottom: start the logger, set canStart to True, log something, set omxCommand (to start omx with), get the hostname, lookup the plex server.

The next method in the pyPlex class is start(), this method does a lot of interesting stuf, lets take a look:

def start(self):<br />
		"""Setting up listners and all other stuff"""<br />
		self.l.info("Setting up listeners")<br />
		self.service = ZeroconfService(name=self.hostname + " PyPlex", port=3000, text=["machineIdentifier=" + self.hostname,"version=2.0"])<br />
		self.service.publish()<br />
		self.duration = 0<br />
		self.queue = Queue.Queue()<br />
		self.xbmcCmmd = xbmcCommands(self.omxCommand, self.server)<br />
		self.udp = udplistener(self.queue)<br />
		self.udp.start()<br />
		self.http = httplistener(self.queue)<br />
		self.http.start()<br />
		if self.udp.error == True or self.http.error == True:<br />
			self.canStart = False




Here are a lot of variables initiated and started. The first being the ZeroconfigService. This service is used to make a Avahi share on the network that'll be recogised as a plex-client. Your server will pick this up and register a new client (you can look at your server's log files when you start pyplex, i'll tell you there is a new client in town) Because there is a new client, all IOS/android devices will now have a tab to choose your player.
The next important thing is the self.queu variable. This is needed to communicate between threads. Next xbmcCmmd is initiated, which is the wrapper class for commands like Play, Pause and PlayMeda. This class actually starts playback. Next is the initiation of two important listeners. The udp listener and the http listener.

To understand these two i'll explain a bit about how mobile devices communicate with it's clients. When you select a item to play on a client (with the "play on ...." tab) the device sends a request url to the client like this


http://ipofyourpi:3000/xbmcCmds/xbmcHttp?command=TheCommand()

the http listener listens on the port 3000 and preforms this code when a get request is made:



 def get(self):<br />
        self.l = pyPlexLogger('httplistener').logger<br />
        string = self.get_argument("command")<br />
        front = string.index("(")<br />
        end = string.rindex(")")<br />
        command = string[:front]<br />
        commandargs = string[front+1:end].split(';')<br />
        self.queue.put((command, commandargs))<br />
        self.write("received")



You can see that the the command and it's arguments are put in the self.queue variable, this is the same one that is initiated in the pyPlex.start() method. The udp listener works quit the same, this one listens to Play, Pause, and seek commands on port 9777 and adds them to the queue.

Now that all listeners are set, and there are no errors, it's time to run pyplex. This is done by the run method in the pyPlex class.

def run(self):<br />
		"""The heart of pyPlex (can you hear it pounding...?)"""<br />
		self.l.info("Running PyPlex")<br />
		if self.canStart == True:<br />
			try:<br />
				while True:<br />
					# check if xmbc is running<br />
					if(self.xbmcCmmd.isRunning()):<br />
						# update position<br />
						self.xbmcCmmd.updatePosition()<br />
					# get the command from the listneners<br />
					command = self.parseCommand()<br />
					if command:<br />
						# read the command and args<br />
						func, args = command<br />
						# excecute the command<br />
						func(*args)<br />
						# check if pyplex has to stop<br />
						if(self.xbmcCmmd.shutDown == True):<br />
							self.stop()<br />
							break<br />
			except Exception, e:<br />
				exc_type, exc_obj, exc_tb = sys.exc_info()<br />
				fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]<br />
				self.l.error("Caught exception")<br />
				self.l.error(exc_type, fname, exc_tb.tb_lineno) <br />
				message = 'There went something wrong in %s'<br />
				if(self.xbmcCmmd):<br />
					self.l.error(message % 'xbmc')<br />
					self.l.error(e) <br />
					self.xbmcCmmd.Stop("")<br />
					self.stop()<br />
					return 0<br />
				if(udp):<br />
					self.l.error(message % 'udp')<br />
					self.l.error(e)<br />
					self.udp.stop()<br />
					self.udp.join()<br />
				if(http):<br />
					self.l.error(message % 'http')<br />
					self.l.error(e)<br />
					self.http.stop()<br />
					self.http.join()<br />
				raise<br />
		else:<br />
			self.l.error("Error while starting PyPlex")<br />
			self.stop()



This one is quit hard to understand. First there is a check if pyplex actually can start. If it can it'll start a loop that continues to check for new commands and executes them. I'll also put the self.parseCommand() method up here, cause it's quit necessary.


def parseCommand(self):<br />
		"""Get commands from the queue"""<br />
		try:<br />
			command, args = self.queue.get(True, 2)<br />
			self.l.info("Got command: %s, args: %s" %(command, args))<br />
			if not hasattr(self.xbmcCmmd, command):<br />
				self.l.error("Command %s not implemented yet" % command)<br />
			else:<br />
				func = getattr(self.xbmcCmmd, command)<br />
				# Retun the function + it's arguments<br />
				return [func, args]<br />
		except Queue.Empty:<br />
			pass



This method get's the command and it's arguments from the queue, checks if the command is a attribute of self.xmbcCmmnd (the command module) if it is, it returns a list of the function object and it's arguments. The next thing that happens in the pyPlex.run() method is the actual command call. This keeps repeating till pyPlex.stop() is ran, which stoppes all the listeners and breaks the loop in pyPlex.run() method.

I hope this gives you an idea how pyplex works and how commands are passed to it.

Ahh that’s fantastic! That’s exactly what I needed to know! I could simply have another process that starts with the listeners that will translate RFID tags into Plex commands. The ideal way for that to be implemented, I’m not sure, but I can at least get a prototype working, if only with one or two RFID tags. Thanks for the help. Time to invest in the hardware!



Happy to help! A new listener would indeed be the way to go.

This is really impressive.

Thinking about getting a second raspberry pi to try this out! But out of curiosity why add a gui to the raspberry pi? Wouldn’t it be better to build a webui that can be used from a computer or mobile if the user doesn’t have any iOS/Android device?



Since tornado already is installed it shouln’t take a huge amount of work to make a web ui with just some CSS.

Or what do you think.



This is also really interesting, the gui is more of a test so see if it's possible, and i want to start small with the gui. I was thinking of a slideshow of media art when idle and showing some info when getting a PlayMedia request, the time between sending the request and getting feedback is way too long now because of the transcode url. A progress bar would also be nice. atm I wasn't thinking about a gui that's capable of browsing the media.

(mobile) Web interface would be awesome as well.