Closed snizzleorg closed 10 years ago
If you don't mind doing a little coding I have a Java API that you might find usefull
well it might be worth a try. I never wrote anything in Java though.
btw. I managed to stop the playing by issuing
http://localhost:3689/ctrl-int/1/pause?session-id=100
i still think there should be the some syntax to do other things (like select speakers but i can't find it anywhere...
If Java is an option I think jkiddo's client is the best bet, but if you're after a Ruby solution I once came across this: https://github.com/jurriaan/ruby-dacpclient
The command for selecting speakers is called setspeakers. A good way to learn the syntax is to set log level to debug, do the stuff you want to implement with Remote, and then grabbing the commands from the log (grep DAAP and grep DACP).
I just read through the Ruby solution at it seems to be a pretty valid solution - and it supports more features than the Java one (guess I'll have to start doing some Ruby to Java porting ... ;) )
I tried the ruby-dacpclient but with no success something seams to wrong with my ruby install or some dependencies...
thats what I get when I run the example /usr/local/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- dbm (LoadError)
I'll ask juriaan about it. thanks anyway for pointing me to dacpclient.
small update:
sessionid=$(curl -s "http://localhost:3689/login?pairing-guid=0x1" | /home/steffen/src/dacp/dacp_sessionid_decode.py)
curl "http://localhost:3689/ctrl-int/1/setspeakers?speaker-id=0xC467B5080CAA,0x1b63235a8b&session-id=$sessionid"
curl "http://localhost:3689/ctrl-int/1/playspec?database-spec='dmap.persistentid:0x1'&container-spec='dmap.persistentid:0x7'&container-item-spec='dmap.containeritemid:0x28A2'&session-id=$sessionid"
this is working so far. the dacp_sessionid_decode.py is a script I adapted from:
#!/usr/bin/python
# simple message decoder for dacp
# released gplv3 by jeffrey sharkey
import sys, struct, re
raw = []
#for c in raw_input(): raw.append(c)
#raw.append('\x0a')
for c in sys.stdin.read(): raw.append(c)
def format(c):
if ord(c) >= 128: return "(byte)0x%02x"%ord(c)
else: return "0x%02x"%ord(c)
print ','.join([ format(c) for c in raw ])
def read(queue, size):
pull = ''.join(queue[0:size])
del queue[0:size]
return pull
group = ['casp','cmst','mlog','agal','mlcl','mshl','mlit','abro','abar','apso','caci','avdb','cmgt','aply','adbs','cmpa']
rebinary = re.compile('[^\x20-\x7e]')
def ashex(s): return ''.join([ "%02x" % ord(c) for c in s ])
def asbyte(s): return struct.unpack('>B', s)[0]
def asint(s): return struct.unpack('>I', s)[0]
def aslong(s): return struct.unpack('>Q', s)[0]
def decode(raw, handle, indent):
while handle >= 8:
# read word data type and length
ptype = read(raw, 4)
plen = asint(read(raw, 4))
handle -= 8 + plen
# recurse into groups
if ptype in group:
print '\t' * indent, ptype, " --+"
decode(raw, plen, indent + 1)
continue
# read and parse data
pdata = read(raw, plen)
nice = '%s' % ashex(pdata)
if plen == 1: nice = '%s == %s' % (ashex(pdata), asbyte(pdata))
if plen == 4: nice = '%s == %s' % (ashex(pdata), asint(pdata))
if plen == 8: nice = '%s == %s' % (ashex(pdata), aslong(pdata))
if rebinary.search(pdata) is None:
nice = pdata
print '\t' * indent, ptype.ljust(6), str(plen).ljust(6), nice
decode(raw, len(raw), 0)
my crude adaptation just prints out the session-id:
#!/usr/bin/python
# simple message decoder for dacp
# released gplv3 by jeffrey sharked
# adapted by universaldilettant to only print the session-id
import sys, struct, re
raw = []
#for c in raw_input(): raw.append(c)
#raw.append('\x0a')
for c in sys.stdin.read(): raw.append(c)
def format(c):
if ord(c) >= 128: return "(byte)0x%02x"%ord(c)
else: return "0x%02x"%ord(c)
#print ','.join([ format(c) for c in raw ])
def read(queue, size):
pull = ''.join(queue[0:size])
del queue[0:size]
return pull
group = ['casp','cmst','mlog','agal','mlcl','mshl','mlit','abro','abar','apso','caci','avdb','cmgt','aply','adbs','cmpa']
rebinary = re.compile('[^\x20-\x7e]')
def ashex(s): return ''.join([ "%02x" % ord(c) for c in s ])
def asbyte(s): return struct.unpack('>B', s)[0]
def asint(s): return struct.unpack('>I', s)[0]
def aslong(s): return struct.unpack('>Q', s)[0]
def decode(raw, handle, indent):
while handle >= 8:
# read word data type and length
ptype = read(raw, 4)
plen = asint(read(raw, 4))
handle -= 8 + plen
# recurse into groups
if ptype in group:
#print '\t' * indent, ptype, " --+"
decode(raw, plen, indent + 1)
continue
# read and parse data
pdata = read(raw, plen)
nice = '%s' % ashex(pdata)
if plen == 1: nice = '%s == %s' % (ashex(pdata), asbyte(pdata))
if plen == 4: nice = '%s == %s' % (ashex(pdata), asint(pdata))
if plen == 8: nice = '%s == %s' % (ashex(pdata), aslong(pdata))
if rebinary.search(pdata) is None:
nice = pdata
if str(ptype.ljust(6)) == "mlid ":
#print '\t' * indent, ptype.ljust(6), str(plen).ljust(6), nice
print asint(pdata)
decode(raw, len(raw), 0)
btw. I found the easiest to get the speaker id's is to call avahi-browse
avahi-browse _raop._tcp.
which puts out a list of airplay devices like this:
+ eth0 IPv4 28E7CFEF7820@beamer (7) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (5) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (3) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (2) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (6) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (4) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (428) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (427) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (397) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (391) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (357) AirTunes Remote Audio local
+ eth0 IPv4 28E7CFEF7820@beamer (331) AirTunes Remote Audio local
+ eth0 IPv4 0011245F6041@gabstar AirTunes Remote Audio local
+ eth0 IPv4 F0D1A90A45C1@living AirTunes Remote Audio local
+ eth0 IPv4 001B63235A8B@kitchentunes AirTunes Remote Audio local
no idea why the beamer (an AppleTV) is showing up so often
Cool! Actually I made a modification a few days ago, so that you in the login can request a session_id. This change is in the pipe branch, which will soon be merged with master. The requested id has to be < 100. The idea is to avoid the need to parse the result from the login, so you can do like this:
curl "http://localhost:3689/login?pairing-guid=0x1&request-session-id=50"
curl "http://localhost:3689/ctrl-int/1/playspec?database-spec='dmap.persistentid:0x1'&container-spec='dmap.persistentid:0x[PLAYLIST-ID]'&container-item-spec='dmap.containeritemid:0x[FILE ID]'&session-id=50"
curl "http://localhost:3689/logout?session-id=50"
Of course, your approach is the proper way to do it. Using the daap parser is probably also going to be useful if you want to do some more sophisticated command line tasks, like for instance getting a list of songs and then selecting one of them.
nice!
only my approach is a dirty hack somehow. But I guess the logout is probably something I'll add anyway. How long is a session valid normally?
right now every command I issue creates a new session - that works but is probably not the best way to do it.
I'll try to see if I can get the decoding and curl(ing) done properly in a python script... That actually would be nice. But now all I wanted was to start the Radio on two speakers in the house in the morning. And that is working perfectly now.
do you have a list of available functions somewhere? can I get this form your source code? I'm basically looking for some function to determine whether forked-daapd is playing. I want to switch on my stereo based on whether forked-daapd is playing and maybe switch it off when the music stops...
You will need to look in httpd_dacp.c (line 1941) and httpd_daap.c (line 2607). I think the call you are looking for is the one in dacp.c called playstatusupdate. Use revision number 1 to get an immediate reply. The reply will be daap encoded, so it has to be parsed (look at line 223 in dacp.c for the tag you are interested in).
If you are more comfortable with looking at Java, have a look at the project called Jolivia (authored by me) - that ought to point out the url's that iRemote calls/uses
hm. I'm not really a programmer. I find my way around more or less with python and a little ansi C but java I never looked into... But thanks anyways.
this not really an issue...but a small request to point me where I can find infos on how to control daapd from the command line. I found this here:
http://www.raspberrypi.org/phpBB3/viewtopic.php?f=66&t=49928
curl "http://localhost:3689/login?pairing-guid=0x1" curl "http://localhost:3689/ctrl-int/1/playspec?database-spec='dmap.persistentid:0x1'&container-spec='dmap.persistentid:0x7'&container-item-spec='dmap.containeritemid:0x10402'&session-id=100"
which is nice to start a stream. But ideally I would like to do a little more like stop it again and select speakers.
Maybe there is even some python or ruby or whatnot library that can do this but I haven't found anything. Any ideas where to look for this?
Thanks