Open loretoparisi opened 8 years ago
Hi @loretoparisi
sonos-objc hasn't been updated in a while so some features might be broken. I haven't had time, but I'd love to get back to it.
IIRC the README is outdated, and you should discover devices using
[SonosDiscover discoverControllers:^(NSArray *controllers, NSError *error) {}]
Hello @n1mda ! Thanks for the hint.
So I did a simple fix to make it work again :)
As you suggested now you have to do like this
[SonosDiscover discoverControllers:^(NSArray *controllers, NSError *error) {
if( [controllers count] > 0) {
NSDictionary *controllerInfo = [controllers objectAtIndex:0];
SonosController *controller = [[SonosController alloc] initWithIP:[controllerInfo objectForKey:@"ip"] port:[[controllerInfo objectForKey:@"port"] intValue]];
[controller trackInfo:^(NSString *artist, NSString *title, NSString *album, NSURL *albumArt, NSInteger time, NSInteger duration, NSInteger queueIndex, NSString *trackURI, NSString *protocol, NSError *error){
NSLog(@"Artist: %@", artist);
NSLog(@"Title: %@", title);
NSLog(@"Album: %@", album);
NSLog(@"Album Art: %@", albumArt);
NSLog(@"Time: %d", (int)time);
NSLog(@"Duration: %d", (int)duration);
NSLog(@"Place in queue: %d", (int)queueIndex);
NSLog(@"Track URI: %@", trackURI);
NSLog(@"Protocol: %@", protocol);
}];
}
}];
Then the fix is in the SonosDiscover
class method
+ (void)discoverControllers:(void (^)(NSArray *, NSError *))completion {
is like this very hot hot workaround
NSDictionary *responseDictionary = [XMLReader dictionaryForXMLData:data error:&error];
// NSArray *inputDictionaryArray = responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"][@"ZonePlayer"];
NSDictionary *dictionary = responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"][@"ZonePlayer"];
//for (NSDictionary *dictionary in inputDictionaryArray){
NSString *name = dictionary[@"text"];
NSString *coordinator = dictionary[@"coordinator"];
NSString *uuid = dictionary[@"uuid"];
NSString *group = dictionary[@"group"];
NSString *ip = [[dictionary[@"location"] stringByReplacingOccurrencesOfString:@"http://" withString:@""] stringByReplacingOccurrencesOfString:@"/xml/device_description.xml" withString:@""];
NSArray *location = [ip componentsSeparatedByString:@":"];
SonosController *controllerObject = [[SonosController alloc] initWithIP:[location objectAtIndex:0] port:[[location objectAtIndex:1] intValue]];
[devices addObject:@{@"ip": [location objectAtIndex:0], @"port" : [location objectAtIndex:1], @"name": name, @"coordinator": [NSNumber numberWithBool:[coordinator isEqualToString:@"true"] ? YES : NO], @"uuid": uuid, @"group": group, @"controller": controllerObject}];
//}
since it crashes on the
NSArray *inputDictionaryArray = responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"][@"ZonePlayer"];
Of course this could mean that when you get the
NSDictionary *dictionary = responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"][@"ZonePlayer"];
it could be an array as well when there is more than one Sonos (sorry I have got just one), so you should do a NSArray / NSDictionary check right there. Any ways then it works:
2015-07-09 16:36:38.417 Musixmatch[5927:570986] Artist: Coldplay
2015-07-09 16:36:38.418 Musixmatch[5927:570986] Title: Paradise
2015-07-09 16:36:38.419 Musixmatch[5927:570986] Album: Paradise
2015-07-09 16:36:38.419 Musixmatch[5927:570986] Album Art: http://192.168.2.150:1400http://192.168.2.218:3401/music/image?id=4B00233EB6CC816A
2015-07-09 16:36:38.420 Musixmatch[5927:570986] Time: 0
2015-07-09 16:36:38.420 Musixmatch[5927:570986] Duration: 278
2015-07-09 16:36:38.420 Musixmatch[5927:570986] Place in queue: 1
2015-07-09 16:36:38.421 Musixmatch[5927:570986] Track URI: http://mobile-iPhone-4877ACE2-7AB1-4DD1-93E0-E98BF97F086F.x-udn/music/track.adts?id=4B00233EB6CC816A
2015-07-09 16:36:38.421 Musixmatch[5927:570986] Protocol: http-get:*:audio/mp4:*
and radio protocols as well
2015-07-09 16:44:22.126 Musixmatch[5927:570986] Artist: (null)
2015-07-09 16:44:22.127 Musixmatch[5927:570986] Title: 216.119.144.221:8008
2015-07-09 16:44:22.128 Musixmatch[5927:570986] Album: (null)
2015-07-09 16:44:22.128 Musixmatch[5927:570986] Album Art: http://192.168.2.150:1400(null)
2015-07-09 16:44:22.128 Musixmatch[5927:570986] Time: 6
2015-07-09 16:44:22.129 Musixmatch[5927:570986] Duration: 0
2015-07-09 16:44:22.129 Musixmatch[5927:570986] Place in queue: 1
2015-07-09 16:44:22.130 Musixmatch[5927:570986] Track URI: x-rincon-mp3radio://216.119.144.221:8008
2015-07-09 16:44:22.130 Musixmatch[5927:570986] Protocol: x-rincon-mp3radio:*:*:*
I'm going to check the other features later on!
More info:
(lldb) po responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"]
{
ZonePlayer = {
behindwifiext = 0;
bootseq = 3;
channelfreq = 2437;
coordinator = true;
group = "RINCON_B8E937E150E601400:1";
legacycompatibleversion = "24.0-0000";
location = "http://192.168.2.150:1400/xml/device_description.xml";
mincompatibleversion = "27.0-00000";
text = PostazioneLoreto;
uuid = "RINCON_B8E937E150E601400";
version = "28.1-86173";
wifienabled = 1;
wirelessmode = 0;
};
}
Not sure, but when you have more than one ZonePlayer
, it could become an array at the root level.
So this was my last solution, better than the first, but quick and dirt as well:
NSDictionary *responseDictionary = [XMLReader dictionaryForXMLData:data error:&error];
NSObject *zonePlayers = responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"];
if( [zonePlayers isKindOfClass:[NSDictionary class]] ) { // one player
NSDictionary *dictionary = responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"][@"ZonePlayer"];
NSString *name = dictionary[@"text"];
NSString *coordinator = dictionary[@"coordinator"];
NSString *uuid = dictionary[@"uuid"];
NSString *group = dictionary[@"group"];
NSString *ip = [[dictionary[@"location"] stringByReplacingOccurrencesOfString:@"http://" withString:@""] stringByReplacingOccurrencesOfString:@"/xml/device_description.xml" withString:@""];
NSArray *location = [ip componentsSeparatedByString:@":"];
SonosController *controllerObject = [[SonosController alloc] initWithIP:[location objectAtIndex:0] port:[[location objectAtIndex:1] intValue]];
[devices addObject:@{@"ip": [location objectAtIndex:0], @"port" : [location objectAtIndex:1], @"name": name, @"coordinator": [NSNumber numberWithBool:[coordinator isEqualToString:@"true"] ? YES : NO], @"uuid": uuid, @"group": group, @"controller": controllerObject}];
}
else if( [zonePlayers isKindOfClass:[NSArray class]] ) { // list of players
NSArray *inputDictionaryArray = responseDictionary[@"ZPSupportInfo"][@"ZonePlayers"][@"ZonePlayer"];
for (NSDictionary *dictionary in inputDictionaryArray){
NSString *name = dictionary[@"text"];
NSString *coordinator = dictionary[@"coordinator"];
NSString *uuid = dictionary[@"uuid"];
NSString *group = dictionary[@"group"];
NSString *ip = [[dictionary[@"location"] stringByReplacingOccurrencesOfString:@"http://" withString:@""] stringByReplacingOccurrencesOfString:@"/xml/device_description.xml" withString:@""];
NSArray *location = [ip componentsSeparatedByString:@":"];
SonosController *controllerObject = [[SonosController alloc] initWithIP:[location objectAtIndex:0] port:[[location objectAtIndex:1] intValue]];
[devices addObject:@{@"ip": [location objectAtIndex:0], @"port" : [location objectAtIndex:1], @"name": name, @"coordinator": [NSNumber numberWithBool:[coordinator isEqualToString:@"true"] ? YES : NO], @"uuid": uuid, @"group": group, @"controller": controllerObject}];
}
}
I have found another crash here
- (void)trackInfo:(void (^)(NSString *artist, NSString *title, NSString *album, NSURL *albumArt, NSInteger time, NSInteger duration, NSInteger queueIndex, NSString *trackURI, NSString *protocol, NSError *error))block {
due to the
albumArt Property parsing:
NSURL *albumArt = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d%@", self.ip, self.port, trackMetaData[@"DIDL-Lite"][@"item"][@"upnp:albumArtURI"][@"text"]]];
since now it can be an array of objects having the DLNA
profile for image types:
(lldb) po trackMetaData[@"DIDL-Lite"][@"item"][@"upnp:albumArtURI"]
<__NSArrayM 0x1702460f0>(
{
text = "http://192.168.2.200:10246/MDEServer/C2B26D33-1801-465B-AD1D-B0CF56E689F7/1000.jpg?albumArt=0";
},
{
"dlna:profileID" = "JPEG_TN";
text = "http://192.168.2.200:10246/MDEServer/C2B26D33-1801-465B-AD1D-B0CF56E689F7/1000.jpg?albumArt=0,formatID=00000023-A9AF-4584-84E2-55BFEF0A7D7E,width=160,height=159";
}
)
then we have to take care of this
A solution (that works for sure with my DLNA supported device! ) could be like this
NSURL *albumArt = nil;
NSObject * albumArtObject = trackMetaData[@"DIDL-Lite"][@"item"][@"upnp:albumArtURI"];
if( [albumArtObject isKindOfClass:[NSArray class]] ) {
for( NSDictionary *albumArtElement in (NSArray*)albumArtObject) {
if ( albumArtElement[@"text"] ) {
albumArt = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d%@", self.ip, self.port, albumArtElement[@"text"]]];
}
}
} else if( [albumArtObject isKindOfClass:[NSDictionary class]] ) {
if ( ((NSDictionary*)albumArtObject)[@"text"] ) {
albumArt = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d%@", self.ip, self.port, ((NSDictionary*)albumArtObject)[@"text"]]];
}
}
CRASH 2
NSString *protocol = trackMetaData[@"DIDL-Lite"][@"item"][@"res"][@"protocolInfo"];
since I can have
(lldb) po trackMetaData[@"DIDL-Lite"][@"item"][@"res"]
<__NSArrayM 0x174459860>(
{
bitrate = 23302;
bitsPerSample = 16;
duration = "0:03:26.576";
"microsoft:codec" = "{00000055-0000-0010-8000-00AA00389B71}";
nrAudioChannels = 2;
protocolInfo = "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01700000000000000000000000000000";
sampleFrequency = 44100;
size = 4871876;
text = "http://192.168.2.200:10246/MDEServer/C2B26D33-1801-465B-AD1D-B0CF56E689F7/1000.mp3";
},
{
bitrate = 8000;
duration = "0:03:26.576";
"microsoft:codec" = "{00000161-0000-0010-8000-00AA00389B71}";
nrAudioChannels = 2;
protocolInfo = "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=10;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=01700000000000000000000000000000";
sampleFrequency = 44100;
text = "http://192.168.2.200:10246/MDEServer/C2B26D33-1801-465B-AD1D-B0CF56E689F7/1000.wma?formatID=00000086-A9AF-4584-84E2-55BFEF0A7D7E";
},
{
bitrate = 23302;
bitsPerSample = 16;
duration = "0:03:26.576";
"microsoft:codec" = "{00000055-0000-0010-8000-00AA00389B71}";
nrAudioChannels = 2;
protocolInfo = "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=10;DLNA.ORG_FLAGS=01700000000000000000000000000000";
sampleFrequency = 44100;
text = "http://192.168.2.200:10246/MDEServer/C2B26D33-1801-465B-AD1D-B0CF56E689F7/1000.mp3?formatID=00000008-A9AF-4584-84E2-55BFEF0A7D7E";
}
)
and a fix is
NSString *trackURI = positionInfoResponse[@"TrackURI"][@"text"];
NSObject *itemRes = trackMetaData[@"DIDL-Lite"][@"item"][@"res"];
NSString *protocol = nil;
if ([itemRes isKindOfClass:[NSArray class]]) {
for(NSDictionary *protocolInfo in (NSArray*)itemRes) {
protocol = protocolInfo[@"protocolInfo"];
}
} else if( [itemRes isKindOfClass:[NSDictionary class]] ) {
protocol = ((NSDictionary*)itemRes)[@"protocolInfo"];
}
Sorry no time to merge request / etc. I will update here if I find other issues/crashes...
2015-07-09 19:02:57.316 Musixmatch[6257:601936] Artist: Alanis Morissette
2015-07-09 19:02:57.316 Musixmatch[6257:601936] Title: Perfect
2015-07-09 19:02:57.317 Musixmatch[6257:601936] Album: Jagged Little Pill Acoustic
2015-07-09 19:02:57.317 Musixmatch[6257:601936] Album Art: http://192.168.2.150:1400http://192.168.2.200:10246/MDEServer/C2B26D33-1801-465B-AD1D-B0CF56E689F7/1000.jpg?albumArt=0,formatID=00000023-A9AF-4584-84E2-55BFEF0A7D7E,width=160,height=159
2015-07-09 19:02:57.318 Musixmatch[6257:601936] Time: 9
2015-07-09 19:02:57.318 Musixmatch[6257:601936] Duration: 207
2015-07-09 19:02:57.319 Musixmatch[6257:601936] Place in queue: 1
2015-07-09 19:02:57.319 Musixmatch[6257:601936] Track URI: http://192.168.2.200:10246/MDEServer/C2B26D33-1801-465B-AD1D-B0CF56E689F7/1000.mp3
2015-07-09 19:02:57.320 Musixmatch[6257:601936] Protocol: http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=10;DLNA.ORG_FLAGS=01700000000000000000000000000000
I'm maintaining this project now here: https://github.com/getSenic/sonos-objc/
Here's the fix that tests the players response for an array or for a single dictionary: https://github.com/getSenic/sonos-objc/commit/d73c2290120daa35c697c2537aa6d0736d1fa157
Pull requests welcome!
@larsblumberg thanks for the help!
@loretoparisi You're welcome. @n1mda Thank you for your great work on this library.
I have a Sonos Play:1 on the same WiFi network. I registered the observer and waited for changes, but nothing happened:
Code snippet:
pragma Sonos Delegates
I can see the Sonos PLAY:1 from the Sonos app and from DLNA compatible devices.