Sonos Music API 实施 "Unable to browse music" 错误
Sonos Music API implementation "Unable to browse music" error
我正在尝试实施 SMAPI 服务,但 运行 遇到了一些问题。
我构建了一个 “fake” service,它为这些端点提供硬编码响应:
- getMediaURI
- 获取上次更新
- 获取媒体元数据
- 获取元数据
- 获取扩展元数据
- getSessionId
我已按照说明添加自定义服务,保持所有功能未选中。
我看到我的 Sonos iPhone 应用程序上列出了我的新测试服务,但是当我 select 它时,我看到一个屏幕显示 "Unable to browse music"。
我不太确定如何调试正在发生的事情,所以感觉就像我在黑暗中刺伤一样。我不想推测性地进行可能与问题无关的更改 - 这似乎是浪费大量精力的方法。
有什么方法可以查看 Sonos Controller 应用程序(客户端)出现的错误吗? 我已经查看了 http://[device ip]:1400/support/aggregate
日志,但没有查看任何提及我的服务的域名,或查看任何似乎相关的内容。
我知道(至少一些)请求正在访问我的服务,特别是调用 getLastUpdate
和 getMetadata
。这是日志的输出:
2016-02-23T18:55:24.316373+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.316485+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.431603+00:00 app[web.1]: Processing by SoapController#get_last_update as HTML
2016-02-23T18:55:24.431611+00:00 app[web.1]: Processing by SoapController#get_last_update as HTML
2016-02-23T18:55:24.438452+00:00 app[web.1]: params.inspect: {}
2016-02-23T18:55:24.438458+00:00 app[web.1]: params.inspect: {}
2016-02-23T18:55:24.447855+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (7.6ms)
2016-02-23T18:55:24.447907+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (7.6ms)
2016-02-23T18:55:24.448279+00:00 app[web.1]: Completed 200 OK in 17ms (Views: 9.5ms | ActiveRecord: 0.0ms)
2016-02-23T18:55:24.457801+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.448327+00:00 app[web.1]: Completed 200 OK in 17ms (Views: 9.5ms | ActiveRecord: 0.0ms)
2016-02-23T18:55:24.464365+00:00 app[web.1]: Processing by SoapController#get_metadata as HTML
2016-02-23T18:55:24.457861+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.464428+00:00 app[web.1]: Processing by SoapController#get_metadata as HTML
2016-02-23T18:55:24.465101+00:00 app[web.1]: params.inspect: {"id"=>"root", "index"=>0, "count"=>100}
2016-02-23T18:55:24.465154+00:00 app[web.1]: params.inspect: {"id"=>"root", "index"=>0, "count"=>100}
2016-02-23T18:55:24.472056+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (3.4ms)
2016-02-23T18:55:24.472049+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (3.4ms)
2016-02-23T18:55:24.472248+00:00 app[web.1]: Completed 200 OK in 8ms (Views: 6.9ms | ActiveRecord: 0.0ms)
2016-02-23T18:55:24.472251+00:00 app[web.1]: Completed 200 OK in 8ms (Views: 6.9ms | ActiveRecord: 0.0ms)
任何帮助将不胜感激!
更新
这是使用 SoapUI 对我的虚假服务发出请求的输出:
getLastUpdate
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1">
<soap:Body>
<tns:getLastUpdateResponse>
<getLastUpdateResult>
<catalog>Catalog last updated 2016-02-18 11:52:30</catalog>
<favorites>User: Rob last updated favorites 2016-02-18 12:01:00</favorites>
<pollInterval>30</pollInterval>
</getLastUpdateResult>
</tns:getLastUpdateResponse>
</soap:Body>
</soap:Envelope>
getSessionId
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1">
<soap:Body>
<tns:getSessionIdResponse>
<getSessionIdResult>user0001</getSessionIdResult>
</tns:getSessionIdResponse>
</soap:Body>
</soap:Envelope>
getMetadata
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1">
<soap:Body>
<tns:getMetadataResponse>
<getMetadataResult>
<index>0</index>
<count>2</count>
<total>2</total>
<mediaMetadata>
<id>some_id_1</id>
<title>Title 1</title>
<mimeType>audio/mp4</mimeType>
<itemType>track</itemType>
<displayType>List</displayType>
<summary>Summary text 1. Summary text 1. Summary text 1.</summary>
<trackMetadata>
<artistId/>
<artist/>
<composerId/>
<composer/>
<albumId/>
<album/>
<albumArtURI/>
<albumArtistId/>
<albumArtist/>
<genreId/>
<genre/>
<duration/>
<canPlay/>
<canSkip/>
<canAddToFavorites/>
<rating/>
<trackNumber/>
</trackMetadata>
<streamMetadata></streamMetadata>
</mediaMetadata>
<mediaMetadata>
<id>some_id_2</id>
<title>Title 2</title>
<mimeType>audio/mp4</mimeType>
<itemType>track</itemType>
<displayType>List</displayType>
<summary>Summary text 2. Summary text 2. Summary text 2.</summary>
<trackMetadata>
<artistId/>
<artist/>
<composerId/>
<composer/>
<albumId/>
<album/>
<albumArtURI/>
<albumArtistId/>
<albumArtist/>
<genreId/>
<genre/>
<duration/>
<canPlay/>
<canSkip/>
<canAddToFavorites/>
<rating/>
<trackNumber/>
</trackMetadata>
<streamMetadata></streamMetadata>
</mediaMetadata>
</getMetadataResult>
</tns:getMetadataResponse>
</soap:Body>
</soap:Envelope>
getExtendedMetadata
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1">
<soap:Body>
<tns:getExtendedMetadataResponse>
<getExtendedMetadataResult>
<mediaMetadata>
<id>some_id_1</id>
<title>Title 1</title>
<mimeType>audio/mp4</mimeType>
<itemType>track</itemType>
<displayType>List</displayType>
<summary>Summary text 1. Summary text 1. Summary text 1.</summary>
<trackMetadata>
<artistId/>
<artist/>
<composerId/>
<composer/>
<albumId/>
<album/>
<albumArtURI/>
<albumArtistId/>
<albumArtist/>
<genreId/>
<genre/>
<duration/>
<canPlay/>
<canSkip/>
<canAddToFavorites/>
<rating/>
<trackNumber/>
</trackMetadata>
<streamMetadata></streamMetadata>
</mediaMetadata>
</getExtendedMetadataResult>
</tns:getExtendedMetadataResponse>
</soap:Body>
</soap:Envelope>
getMediaMetadata
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1">
<soap:Body>
<tns:getMediaMetadataResponse>
<getMediaMetadataResult>
<id/>
<title>Fake Track Title</title>
<mimeType>audio/mp4</mimeType>
<itemType>track</itemType>
<displayType/>
<summary/>
<trackMetadata>
<artistId>artist:12345</artistId>
<artist>Bach</artist>
<composerId/>
<composer/>
<albumId/>
<album/>
<albumArtURI>https://sonos.therocketfuel.com/images/istockphoto_1945235_satisfaction.jpg</albumArtURI>
<albumArtistId/>
<albumArtist/>
<genreId/>
<genre>Classical</genre>
<duration>459</duration>
<canPlay>true</canPlay>
<canSkip>true</canSkip>
<canAddToFavorites>false</canAddToFavorites>
<rating/>
<trackNumber/>
</trackMetadata>
<streamMetadata></streamMetadata>
</getMediaMetadataResult>
</tns:getMediaMetadataResponse>
</soap:Body>
</soap:Envelope>
getMediaURI
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1">
<soap:Body>
<tns:getMediaURIResponse>
<getMediaURIResult>https://sonos.therocketfuel.com/audio/960bpm.m4a</getMediaURIResult>
</tns:getMediaURIResponse>
</soap:Body>
</soap:Envelope>
您是否尝试过使用 SoapUI,添加端点并直接点击它以查看返回的内容?
解决方案
我终于弄明白了,基本上我的服务返回有效 XML,但这对于 WSDL 模式来说是不正确的。
我使用 SoapUI 调试如下:
创建一个新的 SOAP 项目
- 项目名称:随意
- 初始 WSDL:指向您的服务提供 WSDL 的位置
- 检查 "Create Requests" 和 "Create TestSuite"
Ctrl-单击(右键单击)TestSteps 文件夹并单击 "Add Step",然后单击 select "SOAP Request"
给它起你喜欢的任何名字(我使用了例如 actionName_validate)
选择要验证的 SOAPoperation/action
确保勾选 "Add Schema Assertion (adds validation that response complies with its schema)" 复选框并单击确定
双击左侧列表中新创建的测试步骤,然后运行它(单击左上角的绿色播放按钮)
如果您的服务响应符合 WSDL,您会在左下方看到一个绿色圆圈,上面写着 "Assertions"。如果没有,它将显示为红色,并会为您提供所有错误回复的完整列表。
一一解决架构中的任何问题,直到它变为绿色
如果您使用的是 Ruby
的额外详细信息
另外,这里有一些更多的细节可以帮助遇到这种情况的人,特别是如果你在 Ruby...
中构建你的服务
Ruby 中围绕 SOAP 服务的工具不如 Java(甚至 PHP)。我使用的是 wash_out gem,它虽然适合全新的 SOAP 服务,但表现力不足以让您定义与官方 Sonos WSDL 匹配的类型映射等。我也无法让它生成正确的 XML 响应(它错过了所需的关键 xmlns
属性,或者不会命名所有元素。)
我通过使用模板手动提供 XML 响应来解决这个问题,因为这起初是最简单的方法。您可以使用 .xml.erb
模板或使用 xml builder
语法和 .xml.builder
模板。不过,实际上,您可以根据需要以任何方式生成 XML,例如to_xml
或其他一些演示者类型库。
对于 WSDL,我转而使用官方 Sonos SMAPI WSDL,只是更改了底部的服务地址,以指向我的服务提供此修改后的 WSDL 的位置。
我正在尝试实施 SMAPI 服务,但 运行 遇到了一些问题。
我构建了一个 “fake” service,它为这些端点提供硬编码响应:
- getMediaURI
- 获取上次更新
- 获取媒体元数据
- 获取元数据
- 获取扩展元数据
- getSessionId
我已按照说明添加自定义服务,保持所有功能未选中。
我看到我的 Sonos iPhone 应用程序上列出了我的新测试服务,但是当我 select 它时,我看到一个屏幕显示 "Unable to browse music"。
我不太确定如何调试正在发生的事情,所以感觉就像我在黑暗中刺伤一样。我不想推测性地进行可能与问题无关的更改 - 这似乎是浪费大量精力的方法。
有什么方法可以查看 Sonos Controller 应用程序(客户端)出现的错误吗? 我已经查看了 http://[device ip]:1400/support/aggregate
日志,但没有查看任何提及我的服务的域名,或查看任何似乎相关的内容。
我知道(至少一些)请求正在访问我的服务,特别是调用 getLastUpdate
和 getMetadata
。这是日志的输出:
2016-02-23T18:55:24.316373+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.316485+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.431603+00:00 app[web.1]: Processing by SoapController#get_last_update as HTML
2016-02-23T18:55:24.431611+00:00 app[web.1]: Processing by SoapController#get_last_update as HTML
2016-02-23T18:55:24.438452+00:00 app[web.1]: params.inspect: {}
2016-02-23T18:55:24.438458+00:00 app[web.1]: params.inspect: {}
2016-02-23T18:55:24.447855+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (7.6ms)
2016-02-23T18:55:24.447907+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (7.6ms)
2016-02-23T18:55:24.448279+00:00 app[web.1]: Completed 200 OK in 17ms (Views: 9.5ms | ActiveRecord: 0.0ms)
2016-02-23T18:55:24.457801+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.448327+00:00 app[web.1]: Completed 200 OK in 17ms (Views: 9.5ms | ActiveRecord: 0.0ms)
2016-02-23T18:55:24.464365+00:00 app[web.1]: Processing by SoapController#get_metadata as HTML
2016-02-23T18:55:24.457861+00:00 app[web.1]: Started POST "/soap/action" for 213.86.218.202 at 2016-02-23 18:55:24 +0000
2016-02-23T18:55:24.464428+00:00 app[web.1]: Processing by SoapController#get_metadata as HTML
2016-02-23T18:55:24.465101+00:00 app[web.1]: params.inspect: {"id"=>"root", "index"=>0, "count"=>100}
2016-02-23T18:55:24.465154+00:00 app[web.1]: params.inspect: {"id"=>"root", "index"=>0, "count"=>100}
2016-02-23T18:55:24.472056+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (3.4ms)
2016-02-23T18:55:24.472049+00:00 app[web.1]: Rendered vendor/bundle/ruby/2.2.0/gems/wash_out-0.10.0/app/views/wash_out/document/response.builder (3.4ms)
2016-02-23T18:55:24.472248+00:00 app[web.1]: Completed 200 OK in 8ms (Views: 6.9ms | ActiveRecord: 0.0ms)
2016-02-23T18:55:24.472251+00:00 app[web.1]: Completed 200 OK in 8ms (Views: 6.9ms | ActiveRecord: 0.0ms)
任何帮助将不胜感激!
更新 这是使用 SoapUI 对我的虚假服务发出请求的输出:
getLastUpdate
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1"> <soap:Body> <tns:getLastUpdateResponse> <getLastUpdateResult> <catalog>Catalog last updated 2016-02-18 11:52:30</catalog> <favorites>User: Rob last updated favorites 2016-02-18 12:01:00</favorites> <pollInterval>30</pollInterval> </getLastUpdateResult> </tns:getLastUpdateResponse> </soap:Body> </soap:Envelope>
getSessionId
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1"> <soap:Body> <tns:getSessionIdResponse> <getSessionIdResult>user0001</getSessionIdResult> </tns:getSessionIdResponse> </soap:Body> </soap:Envelope>
getMetadata
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1"> <soap:Body> <tns:getMetadataResponse> <getMetadataResult> <index>0</index> <count>2</count> <total>2</total> <mediaMetadata> <id>some_id_1</id> <title>Title 1</title> <mimeType>audio/mp4</mimeType> <itemType>track</itemType> <displayType>List</displayType> <summary>Summary text 1. Summary text 1. Summary text 1.</summary> <trackMetadata> <artistId/> <artist/> <composerId/> <composer/> <albumId/> <album/> <albumArtURI/> <albumArtistId/> <albumArtist/> <genreId/> <genre/> <duration/> <canPlay/> <canSkip/> <canAddToFavorites/> <rating/> <trackNumber/> </trackMetadata> <streamMetadata></streamMetadata> </mediaMetadata> <mediaMetadata> <id>some_id_2</id> <title>Title 2</title> <mimeType>audio/mp4</mimeType> <itemType>track</itemType> <displayType>List</displayType> <summary>Summary text 2. Summary text 2. Summary text 2.</summary> <trackMetadata> <artistId/> <artist/> <composerId/> <composer/> <albumId/> <album/> <albumArtURI/> <albumArtistId/> <albumArtist/> <genreId/> <genre/> <duration/> <canPlay/> <canSkip/> <canAddToFavorites/> <rating/> <trackNumber/> </trackMetadata> <streamMetadata></streamMetadata> </mediaMetadata> </getMetadataResult> </tns:getMetadataResponse> </soap:Body> </soap:Envelope>
getExtendedMetadata
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1"> <soap:Body> <tns:getExtendedMetadataResponse> <getExtendedMetadataResult> <mediaMetadata> <id>some_id_1</id> <title>Title 1</title> <mimeType>audio/mp4</mimeType> <itemType>track</itemType> <displayType>List</displayType> <summary>Summary text 1. Summary text 1. Summary text 1.</summary> <trackMetadata> <artistId/> <artist/> <composerId/> <composer/> <albumId/> <album/> <albumArtURI/> <albumArtistId/> <albumArtist/> <genreId/> <genre/> <duration/> <canPlay/> <canSkip/> <canAddToFavorites/> <rating/> <trackNumber/> </trackMetadata> <streamMetadata></streamMetadata> </mediaMetadata> </getExtendedMetadataResult> </tns:getExtendedMetadataResponse> </soap:Body> </soap:Envelope>
getMediaMetadata
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1"> <soap:Body> <tns:getMediaMetadataResponse> <getMediaMetadataResult> <id/> <title>Fake Track Title</title> <mimeType>audio/mp4</mimeType> <itemType>track</itemType> <displayType/> <summary/> <trackMetadata> <artistId>artist:12345</artistId> <artist>Bach</artist> <composerId/> <composer/> <albumId/> <album/> <albumArtURI>https://sonos.therocketfuel.com/images/istockphoto_1945235_satisfaction.jpg</albumArtURI> <albumArtistId/> <albumArtist/> <genreId/> <genre>Classical</genre> <duration>459</duration> <canPlay>true</canPlay> <canSkip>true</canSkip> <canAddToFavorites>false</canAddToFavorites> <rating/> <trackNumber/> </trackMetadata> <streamMetadata></streamMetadata> </getMediaMetadataResult> </tns:getMediaMetadataResponse> </soap:Body> </soap:Envelope>
getMediaURI
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.sonos.com/Services/1.1"> <soap:Body> <tns:getMediaURIResponse> <getMediaURIResult>https://sonos.therocketfuel.com/audio/960bpm.m4a</getMediaURIResult> </tns:getMediaURIResponse> </soap:Body> </soap:Envelope>
您是否尝试过使用 SoapUI,添加端点并直接点击它以查看返回的内容?
解决方案
我终于弄明白了,基本上我的服务返回有效 XML,但这对于 WSDL 模式来说是不正确的。
我使用 SoapUI 调试如下:
创建一个新的 SOAP 项目
- 项目名称:随意
- 初始 WSDL:指向您的服务提供 WSDL 的位置
- 检查 "Create Requests" 和 "Create TestSuite"
Ctrl-单击(右键单击)TestSteps 文件夹并单击 "Add Step",然后单击 select "SOAP Request"
给它起你喜欢的任何名字(我使用了例如 actionName_validate)
选择要验证的 SOAPoperation/action
确保勾选 "Add Schema Assertion (adds validation that response complies with its schema)" 复选框并单击确定
双击左侧列表中新创建的测试步骤,然后运行它(单击左上角的绿色播放按钮)
如果您的服务响应符合 WSDL,您会在左下方看到一个绿色圆圈,上面写着 "Assertions"。如果没有,它将显示为红色,并会为您提供所有错误回复的完整列表。
一一解决架构中的任何问题,直到它变为绿色
如果您使用的是 Ruby
的额外详细信息另外,这里有一些更多的细节可以帮助遇到这种情况的人,特别是如果你在 Ruby...
中构建你的服务Ruby 中围绕 SOAP 服务的工具不如 Java(甚至 PHP)。我使用的是 wash_out gem,它虽然适合全新的 SOAP 服务,但表现力不足以让您定义与官方 Sonos WSDL 匹配的类型映射等。我也无法让它生成正确的 XML 响应(它错过了所需的关键 xmlns
属性,或者不会命名所有元素。)
我通过使用模板手动提供 XML 响应来解决这个问题,因为这起初是最简单的方法。您可以使用 .xml.erb
模板或使用 xml builder
语法和 .xml.builder
模板。不过,实际上,您可以根据需要以任何方式生成 XML,例如to_xml
或其他一些演示者类型库。
对于 WSDL,我转而使用官方 Sonos SMAPI WSDL,只是更改了底部的服务地址,以指向我的服务提供此修改后的 WSDL 的位置。