zoukankan      html  css  js  c++  java
  • 使用GraceNote Web API发展Mac发现音乐信息的应用

    好久没有写博客,最近各种忙,特别忙里忙,今晚难得清闲。写最近完成下一个博客任务的摘要:使用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/"; // 调用网络接口的URL
    NSString * const kClientID  = @"10239232"; // 你申请的应用的Client ID
    NSString * 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为搜索keyword,发起搜索请求
    - (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为搜索keyword。运行album fetch请求,抓取专辑的完整信息
        for (NSString *gnID in _gn_IDs) {
            [self gn_albumFetchWithGNID:gnID];
        }
    }
    
    // 以GN_ID为搜索keyword。运行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的音乐信息查询服务了吧。





    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    经典SQL语句大全
    SQL中With AS
    SQL Server 2008 r2 安装过程图解
    TFS2010中如何添加用户
    SQL中标准函数范例
    关闭子窗体时刷新父窗体
    sharepoint网站备份和还原
    LogHelper类
    TFS2010用户解锁
    ADHelper类
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4643520.html
Copyright © 2011-2022 走看看