我正在用C#制作一个.NET Spotify客户端,使用LibspotifyDotNet包装libspotify库,使用libspotify 12.1.51版本。
当我第一次加载会话播放列表容器时(即没有预先存在的设置位置),我会有一些奇怪的行为。该应用程序检查libspotify的功能,以检查播放列表容器是否已加载,返回true,然后据此确定可以安全地获取所有播放列表。当它请求播放列表容器中的播放列表数量时,它会得到0,因此不会加载任何内容。过了一段时间,我收到了一个回调,说播放列表已加载,这确实意味着该应用程序在随后的运行中可以很好地处理播放列表,但在第一次运行时不会。我最好在这个回调中设置一个额外的"isPlaylistReallyLoaded"标志吗?还是这里出了什么问题?我将给出调试器的输出,然后给出一些相关的方法。
调试输出
libspotify> 09:27:28.211 I [user_cache:135] UserCache::initiateGetUsers() will query for 1 users
libspotify> 09:27:28.231 I [ap:1752] Connecting to AP ap.gslb.spotify.com:4070
The thread 'Win32 Thread' (0x172c) has exited with code 0 (0x0).
libspotify> 09:27:28.264 E [c:/Users/spotify-buildagent/BuildAgent/work/1e0ce8a77adfb2dc/client/core/network/proxy_resolver_win32.cpp:215] WinHttpGetProxyForUrl failed
libspotify> 09:27:28.337 I [offline-mgr:2084] Storage has been cleaned
The thread 'Win32 Thread' (0x16f8) has exited with code 0 (0x0).
Itterating over loaded playlists
libspotify> 09:27:44.155 E [ap:1694] AP Socket Error: Undefined Error 0x4E20 (20000)
libspotify> 09:27:45.381 E [ap:3915] Connection error: 117
libspotify> 09:27:54.065 I [ap:1752] Connecting to AP ap.gslb.spotify.com:4070
0 playlists found
libspotify> 09:27:56.290 I [ap:1226] Connected to AP: 78.31.12.21:4070
libspotify> 09:28:03.035 I [user_cache:135] UserCache::initiateGetUsers() will query for 1 users
libspotify> 09:28:03.177 I [user_cache:135] UserCache::initiateGetUsers() will query for 100 users
libspotify> 09:28:03.271 W [core/playlist/playlist.h:45] Adding observer while updating
libspotify> 09:28:03.297 W [core/playlist/playlist.h:45] Adding observer while updating
libspotify> 09:28:03.325 W [core/playlist/playlist.h:45] Adding observer while updating
libspotify> 09:28:03.353 W [core/playlist/playlist.h:45] Adding observer while updating
playlist_added at position 0
playlist_added at position 1
playlist_added at position 2
playlist_added at position 3
playlist_added at position 4
playlist_added at position 5
playlist_added at position 6
playlist_added at position 7
playlist_added at position 8
container_loaded
libspotify> 09:28:03.644 W [core/playlist/playlist.h:45] Adding observer while updating
其中一行写着";在播放列表上循环";是我检查播放列表的加载状态并找到true的地方,我将进入GetAllPlaylists()方法。循环运行后,会输出一行显示找到的0个播放列表,显示在检查sp_playlistcontainer_num_playlists时返回了多少播放列表。声明"container_loaded"的行是对播放列表容器上container_loadd回调的响应。
发挥作用的方法
// this is the method that is called upon login to get the user's playlists
public static List<PlaylistContainer.PlaylistInfo> GetAllSessionPlaylists()
{
waitFor(delegate
{
return PlaylistContainer.GetSessionContainer().IsLoaded
&& PlaylistContainer.GetSessionContainer().PlaylistsAreLoaded;
}, REQUEST_TIMEOUT);
return PlaylistContainer.GetSessionContainer().GetAllPlaylists();
}
在PlaylistContainer模型类中
public static PlaylistContainer GetSessionContainer()
{
if (_sessionContainer == null) {
if (Session.GetSessionPtr() == IntPtr.Zero)
throw new InvalidOperationException("No valid session.");
_sessionContainer = new PlaylistContainer(libspotify.sp_session_playlistcontainer(Session.GetSessionPtr()));
}
return _sessionContainer;
}
public bool IsLoaded {
get {
return libspotify.sp_playlistcontainer_is_loaded(_containerPtr);
}
}
public bool PlaylistsAreLoaded {
get {
if (!this.IsLoaded)
return false;
int count = libspotify.sp_playlistcontainer_num_playlists(_containerPtr);
for (int i = 0; i < count; i++) {
if(libspotify.sp_playlistcontainer_playlist_type(_containerPtr, i) == libspotify.sp_playlist_type.SP_PLAYLIST_TYPE_PLAYLIST) {
using (Playlist p = Playlist.Get(libspotify.sp_playlistcontainer_playlist(_containerPtr, i))) {
if (!p.IsLoaded)
return false;
}
}
}
return true;
}
}
public List<PlaylistInfo> GetAllPlaylists() {
if (!GetSessionContainer().IsLoaded)
throw new InvalidOperationException("Container is not loaded.");
List<PlaylistInfo> playlists = new List<PlaylistInfo>();
Logger.WriteDebug("Itterating over loaded playlists");
int count = libspotify.sp_playlistcontainer_num_playlists(_containerPtr);
for (int i = 0; i < count; i++) {
if (libspotify.sp_playlistcontainer_playlist_type(_containerPtr, i) == libspotify.sp_playlist_type.SP_PLAYLIST_TYPE_PLAYLIST) {
IntPtr playlistPtr = libspotify.sp_playlistcontainer_playlist(_containerPtr, i);
playlists.Add(new PlaylistInfo() {
Pointer = playlistPtr,
PlaylistType = libspotify.sp_playlist_type.SP_PLAYLIST_TYPE_PLAYLIST,
ContainerPtr = _containerPtr,
Name = Functions.PtrToString(libspotify.sp_playlist_name(playlistPtr))
});
}
}
Logger.WriteDebug("{0} playlists found", count);
return playlists;
}
我从Jamcast插件中借用了很多API交互代码(这是唯一的例子),并在发现问题时修复了这些问题。但作为Spotify API的新手,这似乎是一个很好的开始。我正在一点一点地重写它。因此,作为一个额外的问题,我是否值得彻底重写Jamcast的内容,并在发布之前重新开始?
我知道已经有很多了,但如果你需要更多信息,请告诉我。我感谢你能给这个libspotify noob的任何帮助。
几个注意事项:
1) libSpotify实际上不应该以这种方式使用——一次加载所有播放列表是一个非常糟糕的想法。我的账户上有几百个播放列表,其中有10000首曲目。如果我用我的账户登录你的应用程序,你试图像这样将所有播放列表加载到RAM中,你可能很快就会遇到RAM问题,尤其是在移动设备上。
2) 播放列表容器和播放列表本身在加载方面是完全分离的。当容器被加载时,这意味着它知道列表中所有播放列表的文件夹名称和播放列表URI。没什么了。
3) 正如您的日志所示,在触发container_loaded
回调之前,播放列表容器不会完全加载。我认为sp_playlistcontainer_is_loaded
只在容器正在加载时返回false
。因此,一旦您登录,它就会从缓存中读取列表,并将_is_loaded
设置为true。然后,它将开始从缓存的列表中接收增量列表的更新(正如您在日志中看到的,接收那些playlist_added
回调。最后,它将触发container_loaded
回调。
4) libspotify是非常异步的。你之所以看到它们在第二次运行时立即加载,是因为libspotify缓存了一些东西,以便下次快速加载。要在第一次运行时正确接收播放列表,您需要等待,直到收到各种回调,告诉您内容已加载。