使用GraceNote Web API开发Mac查询音乐信息应用
来源:互联网 发布:linux 禁止端口访问 编辑:程序博客网 时间:2024/06/04 17:54
好久没写博客了,最近各种忙,大忙特忙,今晚难得有空,写个博客总结下最近完成的一个任务:使用GraceNote的Web API来开发一个查询音乐信息的应用,其实功能和前面的那些GraceNote SDK的博文是一样的,只是这一次不使用任何SDK,单纯的使用Web API,然后开发的平台从iOS转移到了Mac上,于是,我人生中第一个Mac App Demo就出来了。
GraceNote Web API的官方资料:点击打开链接
首先看下基本的查询和响应的数据格式:
可以看到交互的形式是XML。
事实上,任何调用GraceNote的Web API的消息,都是向一个指定的URL POST XML消息,然后对返回的XML消息进行解析并从中提取出我们想要的信息。下面是程序的一些常数:
NSString * const kWebAPIURL = @"https://c10239232.web.cddbp.net/webapi/xml/1.0/"; // 调用网络接口的URLNSString * const kClientID = @"10239232"; // 你申请的应用的Client IDNSString * const kClientTag = @"46B9ABAD30F0F5EB409C7BFAA13EB2EF"; // 你申请的应用的Client Tag
其中kWebAPIURL就是这个固定的发起请求的URL,注意将c后面的数字替换成你的App的Client ID。
kClient ID和kClient Tag可以从在网站中注册的App中找到。
在使用GraceNote的Web API进行查询之前,首先要通过App的Client ID和Client Tag来注册一个User ID,然后在所有后续查询中都要使用这个User ID和之前的Client ID来进行认证,格式如下:
首先看看注册的代码,在注册成功后我们将其保存到本地的NSUserDefaults中:
// 向GraceNote网站注册User ID- (void)gn_registerUserID { NSString *registerString = [NSString stringWithFormat:@"\ <QUERIES>\ <QUERY CMD=\"REGISTER\">\ <CLIENT>%@-%@</CLIENT>\ </QUERY>\ </QUERIES>", kClientID, kClientTag]; // 要POST的字符串,CMD=REGISTER表示注册动作 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kWebAPIURL]]; [request setHTTPMethod:@"POST"]; NSData *data = [registerString dataUsingEncoding:NSUTF8StringEncoding]; [request setHTTPBody:data]; // 建立NSURLSessionDataTask NSURLSession *session = [NSURLSession sharedSession]; __weak AppDelegate *weakSelf = self; // 防止self和block形成retain cycle NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"*** Register ***"); [self showResponseCode:response]; if (data) { NSError *parseError = nil; // 这里使用第三方类库GDataXML解析XML数据,请确保已经安装GDataXML类库 GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data encoding:NSUTF8StringEncoding error:&parseError]; if (parseError) { NSLog(@"Parse Error:%@", [parseError localizedDescription]); weakSelf.app_userID = nil; } else { /** * 返回的XML数据示例: <RESPONSES> <RESPONSE STATUS="OK"> <USER>267493051066226693-31C70A189A61B89C0D45A782DCB7C072</USER> </RESPONSE> </RESPONSES> */ GDataXMLElement *rootElement = [doc rootElement]; NSArray *responses = [rootElement elementsForName:kGNResponse]; GDataXMLElement *resp = responses[0]; if (![self gn_requestSucceed:resp]) { return; } NSString *userID = [[resp elementsForName:kGNUser][0] stringValue]; // 将获取到的user id保存起来 weakSelf.app_userID = userID; // 将user id存储到User Defaults中 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setObject:userID forKey:kUserID]; [userDefaults synchronize]; NSLog(@"User ID = %@", userID); } } if (error) { NSLog(@"error : %@", [error localizedDescription]); } NSLog(@"--- Register Finished ---"); }]; // 最后一定要用resume方法启动任务 [dataTask resume];}
所有的任务都可以通过NSURLSessionDataTask来完成。
然后根据艺术家名,专辑名,歌曲标题,搜索结果的返回范围来发起查询请求(album search):
// 以Artist,Album Title,Track Title为搜索关键字,发起搜索请求- (void)gn_albumSearchWithArtist:(NSString *)anArtist albumTitle:(NSString *)anAlbumTitle trackTitle:(NSString *)aTrackTitle start:(NSUInteger)startIndex end:(NSUInteger)endIndex{ // 首先移除上次残留的查询结果 [_gn_IDs removeAllObjects]; if (startIndex <= 0 || endIndex <= 0 || startIndex > endIndex) { return; } // 设置查询字符串,本次请求属于ALBUM_SEARCH操作 NSString *searchString = [NSString stringWithFormat:@"\ <QUERIES>\ <AUTH>\ <CLIENT>%@-%@</CLIENT>\ <USER>%@</USER>\ </AUTH>\ <QUERY CMD=\"ALBUM_SEARCH\">\ <TEXT TYPE=\"ARTIST\">%@</TEXT>\ <TEXT TYPE=\"ALBUM_TITLE\">%@</TEXT>\ <TEXT TYPE=\"TRACK_TITLE\">%@</TEXT>\ <RANGE>\ <START>%ld</START>\ <END>%ld</END>\ </RANGE>\ </QUERY>\ </QUERIES>", kClientID, kClientTag, _app_userID, anArtist, anAlbumTitle, aTrackTitle, startIndex, endIndex]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kWebAPIURL]]; [request setHTTPMethod:@"POST"]; NSData *data = [searchString dataUsingEncoding:NSUTF8StringEncoding]; [request setHTTPBody:data]; // 建立NSURLSessionDataTask并用resume方法启动任务 NSURLSession *session = [NSURLSession sharedSession]; __weak AppDelegate *weakSelf = self; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"*** Album Search ***"); [self showResponseCode:response]; if (data) { NSError *parseError = nil; GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data encoding:NSUTF8StringEncoding error:&parseError]; if (parseError) { NSLog(@"Parse Error:%@", [parseError localizedDescription]); } else { /** * 请求成功,返回XML结果示例: <RESPONSES> <RESPONSE STATUS="OK"> <RANGE> <COUNT>2</COUNT> <START>1</START> <END>2</END> </RANGE> <ALBUM ORD="1"> <GN_ID>7552265-4E82AF73CE400EDC94DCDA49547C585F</GN_ID> <ARTIST>The Carpenters</ARTIST> <TITLE>Now & Then</TITLE> <PKG_LANG>ENG</PKG_LANG> <DATE>1973</DATE> <GENRE NUM="61365" ID="25333">70's Rock</GENRE> <MATCHED_TRACK_NUM>6</MATCHED_TRACK_NUM> <TRACK_COUNT>15</TRACK_COUNT> <TRACK> <TRACK_NUM>6</TRACK_NUM> <GN_ID>7552271-366ED2D1FEB61E8D720D4941009C91A9</GN_ID> <TITLE>Yesterday Once More</TITLE> </TRACK> </ALBUM> <ALBUM ORD="2"> <GN_ID>19546461-AA0668FE5972459884664A7C3FE9D9C2</GN_ID> <ARTIST>The Carpenters</ARTIST> <TITLE>Now And Then</TITLE> <PKG_LANG>ENG</PKG_LANG> <GENRE NUM="61365" ID="25333">70's Rock</GENRE> <MATCHED_TRACK_NUM>6</MATCHED_TRACK_NUM> <TRACK_COUNT>8</TRACK_COUNT> <TRACK> <TRACK_NUM>6</TRACK_NUM> <GN_ID>19546467-560982E049BFF85016AB89C37513F474</GN_ID> <TITLE>Yesterday Once More</TITLE> </TRACK> </ALBUM> </RESPONSE> </RESPONSES> */ GDataXMLElement *rootElement = [doc rootElement]; NSArray *responses = [rootElement elementsForName:kGNResponse]; if ([responses count]) { GDataXMLElement *resp = [responses firstObject]; if (![self gn_requestSucceed:resp]) { return; } GDataXMLElement *range = [resp elementsForName:kGNRange][0]; if (!range) { // 如果没有返回range元素,那么抓取数据失败 NSLog(@"Fail to search album"); return; } NSUInteger count = (NSUInteger)[[[range elementsForName:kGNCount][0] stringValue] integerValue]; NSUInteger start = (NSUInteger)[[[range elementsForName:kGNStart][0] stringValue] integerValue]; if (count <= 0) { // 没有搜索到结果,直接返回 [self showSearchResultsCountText:0]; return; } p_currentPage = start / 10 + 1; p_allPages = count / 10; NSUInteger i = (count - count / 10 * 10) ? 1 : 0; p_allPages += i; [self updatePagingText]; [self showSearchResultsCountText:count]; NSUInteger searchCount = 0; if (endIndex >= count) { searchCount = count - startIndex; } else { searchCount = endIndex - startIndex; } NSArray *albums = [resp elementsForName:kGNAlbum]; for (NSUInteger i = 0; i <= searchCount; i++) { GDataXMLElement *album = albums[i]; NSString *gn_id = [[album elementsForName:kGNID][0] stringValue]; // 将每一条搜索结果的GN_ID添加到数组gn_IDs中 [weakSelf.gn_IDs addObject:gn_id]; } [_previousPage_button setEnabled:YES]; [_nextPage_button setEnabled:YES]; // 逐个抓取专辑的具体信息 [weakSelf albumFetch]; } } } if (error) { NSLog(@"error : %@", [error localizedDescription]); } NSLog(@"--- Album Search Finished ---"); }]; [dataTask resume];}
将搜索到的gnID(在数据库中标识这个专辑的一个ID)保存进一个数组gn_IDs中,然后根据数组中的每个gn_id发起进一步的抓取专辑完整数据的操作(album fetch):
// 逐个抓取专辑的具体信息- (void)albumFetch { // 首先移除上次搜索的残留数据 [_searchAlbums removeAllObjects]; // 以gn_IDs中的每一个gnID为搜索关键字,执行album fetch请求,抓取专辑的完整信息 for (NSString *gnID in _gn_IDs) { [self gn_albumFetchWithGNID:gnID]; }}// 以GN_ID为搜索关键字,执行album fetch请求,抓取专辑的完整信息- (void)gn_albumFetchWithGNID:(NSString *)aID { // 设置要查询的字符串,本次操作为ALBUM_FETCH操作 NSString *searchString = [NSString stringWithFormat:@"\ <QUERIES>\ <AUTH>\ <CLIENT>%@-%@</CLIENT>\ <USER>%@</USER>\ </AUTH>\ <QUERY CMD=\"ALBUM_FETCH\">\ <MODE>SINGLE_BEST_COVER</MODE>\ <GN_ID>%@</GN_ID>\ <OPTION>\ <PARAMETER>SELECT_EXTENDED</PARAMETER>\ <VALUE>COVER,ARTIST_IMAGE</VALUE>\ </OPTION>\ <OPTION>\ <PARAMETER>COVER_SIZE</PARAMETER>\ <VALUE>THUMBNAIL</VALUE>\ </OPTION>\ </QUERY>\ </QUERIES>", kClientID, kClientTag, _app_userID, aID]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kWebAPIURL]]; [request setHTTPMethod:@"POST"]; NSData *data = [searchString dataUsingEncoding:NSUTF8StringEncoding]; [request setHTTPBody:data]; // 建立NSURLSessionDataTask并用resume方法启动任务 NSURLSession *session = [NSURLSession sharedSession]; __weak AppDelegate *weakSelf = self; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"*** Album Fetch ***"); [self showResponseCode:response]; if (data) {// // 输出返回的xml内容// [self logoutXMLData:data]; // 通过返回的xml二进制数据初始化MFAlbum对象 MFAlbum *album = [[MFAlbum alloc] initWithXMLData:data]; if (album) { // 将查询结果添加到searchAlbums数组中 [weakSelf.searchAlbums addObject:album]; } [weakSelf showResults]; } if (error) { NSLog(@"error : %@", [error localizedDescription]); } NSLog(@"--- Album Fetch Finished ---"); }]; [dataTask resume];}
最后在NSTableView中将数据load出来:
#pragma mark - NSTableViewDataSource- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { return [_searchAlbums count];}- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { NSString *unknown = @"未知"; MFAlbum *album = _searchAlbums[row]; NSString *identifier = tableColumn.identifier; if ([identifier isEqualToString:@"coverArt"]) { NSURL *coverArtURL = [NSURL URLWithString:album.coverArtURLString]; NSImage *image; if (coverArtURL) { image = [[NSImage alloc] initWithContentsOfURL:coverArtURL]; } else { image = [NSImage imageNamed:@"NotFound"]; } return image; } else if ([identifier isEqualToString:@"artistImage"]) { NSURL *artistImageURL = [NSURL URLWithString:album.artistImageURLString]; NSImage *image; if (artistImageURL) { image = [[NSImage alloc] initWithContentsOfURL:artistImageURL]; } else { image = [NSImage imageNamed:@"NotFound"]; } return image; } else if ([identifier isEqualToString:@"trackCount"]) { return [NSString stringWithFormat:@"%ld", album.trackCount] ? [NSString stringWithFormat:@"%ld", album.trackCount] : unknown; } else { NSString *info = [album valueForKey:identifier]; return info ? info : unknown; }}
另外我将专辑元数据抽象成了一个MFAlbum类,可以通过返回的XML响应数据初始化(在这里使用了GDataXML类库进行XML解析),代码如下:
- (instancetype)initWithXMLData:(NSData *)xmlData { self = [super init]; if (self) { NSError *parseError = nil; GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData encoding:NSUTF8StringEncoding error:&parseError]; if (parseError) { NSLog(@"Parse Error:%@", [parseError localizedDescription]); return nil; // 转换出错,直接返回nil } // 逐个解析xml结点,获取专辑对象所需要的所有信息 GDataXMLElement *rootElement = [doc rootElement]; GDataXMLElement *response = [rootElement elementsForName:kGNResponse][0]; if (![self gn_requestSucceed:response]) { return nil; } GDataXMLElement *album = [response elementsForName:kGNAlbum][0]; _gn_id = [[album elementsForName:kGNID][0] stringValue]; _artistName = [[album elementsForName:kGNArtist][0] stringValue]; _albumTitle = [[album elementsForName:kGNTitle][0] stringValue]; _language = [[album elementsForName:kGNLanguage][0] stringValue]; _releaseDate = [[album elementsForName:kGNDate][0] stringValue]; _genre = [[album elementsForName:kGNGenre][0] stringValue]; _trackCount = (NSUInteger)[[[album elementsForName:kGNTrackCount][0] stringValue] integerValue]; _allTracks = [NSMutableArray array]; NSArray *tracks = [album elementsForName:kGNTrack]; for (GDataXMLElement *trackElement in tracks) { NSString *title = [[trackElement elementsForName:kGNTitle][0] stringValue]; [_allTracks addObject:title]; } NSArray *urlElements = [album elementsForName:kGNURL]; if (!urlElements) { return self; } for (GDataXMLElement *element in urlElements) { GDataXMLNode *node = [element attributeForName:kGNType]; NSString *type = [node stringValue]; if ([type isEqualToString:kGNCoverArt]) { _coverArtURLString = [element stringValue]; } else if ([type isEqualToString:kGNArtistImage]) { _artistImageURLString = [element stringValue]; } } } return self;}
主界面部分(MainMenu.xib):
最后上运行结果:
实在好久没写博客,写作水平下降得厉害,加上自己又变懒惰了很多,这篇文章实在写得太烂,只能当做做个记号,证明我有完成了GraceNote的音乐信息查询服务了吧。
- 使用GraceNote Web API开发Mac查询音乐信息应用
- 使用GraceNote Web API开发Mac查询音乐信息应用
- Use GraceNote SDK in iOS(二)获取音乐的完整信息
- web应用开发入门-使用mac版本eclipse搭建tomcat下web应用项目详细步骤
- 新浪微博API开发WEB应用
- Android开发之web应用提交信息
- iPhone开发【十九】XML解析之NSXMLParser(使用Web Services查询火车信息)
- iPhone开发【十九】XML解析之NSXMLParser(使用Web Services查询火车信息)
- iPhone开发【十九】XML解析之NSXMLParser(使用Web Services查询火车信息)
- Java_调用HttpRequest访问淘宝开发API查询IP信息
- 快速学习和使用新浪微博API开发WEB应用
- 使用jQuery、Yahoo API和HTML5的geolocation来开发一个天气预报web应用
- 使用jQuery、Yahoo API和HTML5的geolocation来开发一个天气预报web应用
- 《使用ArcGIS JavaScript API 开发Web 3D应用》学习笔记
- Python-web: Flask 应用、数据库查询数据、API接口
- 使用SWT开发WEB应用
- 使用web-play开发web应用
- js使用第三方API查询IP信息
- java小程序
- 百度笔试题--数组重排
- swift—1
- Eclipse使用Maven创建Web时错误:Could not resolve archetype org.apache.maven.archetypes:maven-archetype-webap
- java程序员职业规划
- 使用GraceNote Web API开发Mac查询音乐信息应用
- 图的深度搜索和广度搜索
- ORACLE数据库监听配置
- SVM入门(一)至(三)Refresh
- POJ 2316 SPIN
- Mahout安装与配置
- SVM入门(四)线性分类器的求解——问题的描述Part1
- 3D打印机
- S2SH框架配置步骤