ソラマメブログ
< 2024年04月 >
S M T W T F S
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        
※カテゴリー別のRSSです
Mail

  
Posted by at

2007年04月23日

マッシュアップ楽天CD検索(2)

rakutenCD links


昨日の楽天CD検索の続き。
ソースコードと考察です。


イチロー!

◆まず考察
lslでは複雑なテキストを処理しづらいのと
受信バイト数2048bytesの制限があることなどから
現状では直接webサービスAPIを叩かないで、
合間に適当なcgiなどをはさんで、サービスを利用したほうが良いように思います。
戻り値を2048バイトに収まり、解析しやすい形のテキストに変更してくれるとベストです。
(そういうメタサービス、探せばありそうですが。)

ところで、メディアテクスチャを使った作例は
気軽にいくつも組めないですね。
手持ちの土地に対して1つまでしかメディアテクスチャを設定できないと思うので。
分譲してグループ保持とかにすればいくつも作れるでしょうか。

◆ソースについて。
テキストの解析に苦労のあとが見えます…。
llHTTPRequestのレスポンスxmlがちょんぎれて
返ってくる不具合対応のため、仕様変更を繰り返したので
自分でもフローが妙になったままだと思うのですが
もはやなおす気力が無いです。
次にllHTTPRequestを使うときはそのあたりを考慮して組もうと思います。

楽天のAPIを利用するにはデベロッパーIDが必要なので
このソースを使ってみる際は、DEV_IDを自分のIDで置き換えてください。

//lsl with Rakuten CD Search  API sample ver 1.00
//by harayoki
key VIDEO_TEXTURE = "38092dd2-625d-dc66-3131-7356bd8d16c1";//harayoki's video texture
integer TEXTURE_SIDE = 2;
string API_BASE = "http://api.rakuten.co.jp/rws/1.7/rest";
string QUERY_BASE = "operation=CDSearch&version=2007-04-11&imageFlag=1&hits=1&sort=random&carrier=0";
string DEV_ID = "**************************";// your rakuten DEV ID!
//string MAX_RESPONCE_BODY_LENGTH = "40960";// not yet supported. only 2048 bytes are available.
string DEFAULT_IMAGE_URL = "http://www.geocities.jp/yokiimage/slmame/rakutenCDSearchTitle.jpg";
string PARSE_FAILED_IMAGE_URL = "http://www.geocities.jp/yokiimage/slmame/parseFailed.jpg";
string LOADING_IMAGE_URL = "http://www.geocities.jp/yokiimage/slmame/loadingImage.gif";
string NO_RESULT_IMAGE_URL = "http://www.geocities.jp/yokiimage/slmame/rakutenNoResult.jpg";
string POWERED_BY_BASE64 = "UG93ZXJlZCBieSDmpb3lpKlDROaknOe0okFQSQ==";
integer CHANNEL = 1;
float SHOW_INTERVAL = 8.0;
string _defaulttexture = "";
integer _bActive = FALSE;
key _requestKey;
integer _pageCount = 0;
integer _showCursor = 1;
string _imageURL = "";
string _itemName = "";
string _linkURL = "";
string _searchWord = "";
//
integer _debug = FALSE;
Init(){
//_debug = TRUE;
SetText("");
_defaulttexture = llGetTexture(TEXTURE_SIDE);
_bActive = TRUE;
SetActive(_bActive);
}
SetText(string s){
llSetText(s,<1,1,1>,1);
}
Trace(string s){
if(_debug) llOwnerSay(s);
}
SetActive(integer active){
SetTexture(active);
if(active){
SetText("say /1 \"searchword\"");
ShowImage(DEFAULT_IMAGE_URL);
}else{
SetText("Search END");
ShowImage(DEFAULT_IMAGE_URL);
llSetTimerEvent(0.0);
}
}
CallAPI(){
string s = _searchWord;
string url = API_BASE+"?"+QUERY_BASE;
url +="&keyword="+llEscapeURL(s);
url += "&developerId="+DEV_ID;
url += "&page="+(string)_showCursor;
list a = [];
a+=[HTTP_METHOD,"GET"];
a+=[HTTP_MIMETYPE,"text/plain;charset=UTF-8"];
//a+=[HTTP_BODY_MAXLENGTH,MAX_RESPONCE_BODY_LENGTH];
_requestKey = llHTTPRequest(url,a,"");
SetText("Searching CD about "+s);
ShowImage(LOADING_IMAGE_URL);
Trace(url);
}
ShowImage(string url){
llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_URL,url]);
llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_PLAY]);
llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_AUTO_ALIGN,TRUE]);
}
SetTexture(integer bOn){
if(bOn){
key texture = llList2Key(llParcelMediaQuery( [PARCEL_MEDIA_COMMAND_TEXTURE]), 0);
if(texture == NULL_KEY){
texture = VIDEO_TEXTURE;
llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_TEXTURE, texture]);
}
llSetTexture(texture,TEXTURE_SIDE);
}else{
// llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_UNLOAD]);
//llSetTexture(_defaulttexture,TEXTURE_SIDE);
}
}
integer XML_PARSE_OK = 0;
integer XML_PARSE_ERROR_NOHITS = 1;
integer XML_PARSE_ERROR_NOPAGECOUNT = 2;
integer XML_PARSE_ERROR_NONAME = 3;
integer XML_PARSE_ERROR_NOIMAGE = 4;
integer ParseXML(string body){
//body = TrimString(body,"<Body>","</Body>");
integer hits = GetHits(body);
_itemName = "";
_imageURL = "";
_linkURL = "";
if(hits==0) return XML_PARSE_ERROR_NOHITS;
_pageCount = GetPageCount(body);
if(_pageCount==0) return XML_PARSE_ERROR_NOPAGECOUNT;
_itemName = GetItemName(body);
if(_itemName==""){
_itemName = "Could not parse item name...";
//return XML_PARSE_ERROR_NONAME;
}
_imageURL = GetImageURL(body);
if(_imageURL==""){
_imageURL = PARSE_FAILED_IMAGE_URL;
return XML_PARSE_ERROR_NOIMAGE;
}
_linkURL = GetLinkURL(body);
return XML_PARSE_OK;
}
string TrimString(string body,string key1,string key2){
integer index1 = llSubStringIndex(body,key1);
integer index2 = llSubStringIndex(body,key2);
if(index1==-1 || index2==-1) return "";
index1 += llStringLength(key1);
index2 -= 1;
string s = llGetSubString(body,index1,index2);
Trace(key1+"~ "+key2 +" trimed -> \""+s+"\"");
return s;
}
integer GetHits(string body){
string key1 = "<hits>";
string key2 = "</hits>";
string s = TrimString(body,key1,key2);
return (integer)s;
}
integer GetPageCount(string body){
string key1 = "<pageCount>";
string key2 = "</pageCount>";
string s = TrimString(body,key1,key2);
return (integer)s;
}
string GetImageURL(string body){
string key1 = "<smallImageUrl>";
string key2 = "?_ex=64x64</smallImageUrl>";
string s = TrimString(body,key1,key2);
return s;
}
string GetItemName(string body){
string key1 = "<itemName>";
string key2 = "</itemName>";
string s = TrimString(body,key1,key2);
return s;
}
string GetLinkURL(string body){
string key1 = "<itemUrl>";
string key2 = "</itemUrl>";
string s = TrimString(body,key1,key2);
return s;
}
NextLoad(){
_showCursor++;
if(_pageCount<_showCursor){
SetActive(FALSE);
}else{
CallAPI();
}
}
ShowInfo(){
integer cur = _showCursor;
string title = (_itemName);
string mes = "";
mes += "Found "+(string)_pageCount+" CD datas.\n";
mes += "#"+(string)cur+" "+title+"\n";
mes += llBase64ToString(POWERED_BY_BASE64);
SetText(mes);
ShowImage(_imageURL);
}
default{
state_entry(){
Init();
llListen(CHANNEL,"",NULL_KEY,"");
}
listen(integer channel, string name, key id, string mes){
if(mes==""){
return;// do something?
}
llSetTimerEvent(0);
Trace("-------------------------------");
_showCursor = 1;
_pageCount = 0;
_searchWord = mes;
CallAPI();
}
on_rez(integer num){
llResetScript();
}
touch_start(integer num){
_bActive = !_bActive;
SetActive(_bActive);
}
http_response(key id, integer status, list meta, string body){
Trace("http_response res:"+(string)status);
if (id == _requestKey){
if(status == 200) {
integer resCheck = ParseXML(body);
if(resCheck==XML_PARSE_ERROR_NOHITS) {
ShowImage(NO_RESULT_IMAGE_URL);
SetText("Search result is Nothing.");
return;
}else if(resCheck==XML_PARSE_ERROR_NOPAGECOUNT){
llSay(0,"XML Parse Error. Could not find pagecount.");
return;
}else if(resCheck==XML_PARSE_ERROR_NOIMAGE){
llSay(0,"XML Parse Error. Could not find image URL.");
//return;
}
//else if(resCheck == XML_PARSE_ERROR_NONAME){
//llSay(0,"XML Parse Error. Can not find Item Name.");
//return;
//}
llSetTimerEvent(SHOW_INTERVAL);
ShowInfo();
}else{
SetText("Some error has occurred . The error code is "+(string)status);
}
}
}
timer(){
NextLoad();
}
touch_start(integer num){
if(_linkURL!="" && _itemName!=""){
llLoadURL(llDetectedKey(0), _itemName, _linkURL);
}
}
}


※コードのハイライト(色分け)にはgoogle-code-prettifyをlsl対応するように
カスタマイズしたものを使っています。
便利そうなので正式に使い方の説明とともに公開したいのですが、
apacheライセンスという事でその詳細がわからないと公開できません…。
apacheライセンスのコードを利用して作ったコードは
必ずapacheライセンスである事とする…だったかなあ。  

Posted by harayoki at 11:51Comments(14)楽天CD検索

2007年04月22日

マッシュアップ楽天CD検索(1)

rakutenCD links


楽天のwebサービスである、楽天CD検索APIと連携するサンプルを作りました。
…燃え尽き…ました。


"/1 アーティスト名"のような形でチャットの発言をすることでCD検索が始まります。
オマケとして、touchすると楽天ページに飛ぶようになってます。
自分のギャラリーにおいてあります。

こちらの検索結果はちょっとヤバメな気がするのでモザイクいり。
(なんのCDだかわかりますか?face03)

webサービスの結果表示された画像の著作権ってどうなんだろう。
他のところがコピーライトかけとバナー張れとか、いろいろかいてあったけど。


参考にしたのはMashup Awardで受賞したというコチラのソースです。
すごいですね。

自分のソースはぐちゃぐちゃですが、次のエントリーで公開するつもりです。
でも、この日記、lslソースを貼り付けると
禁止タグがあるとか怒られちゃいますね。
(ここ、改善を希望します。>ソラマメさん)


…というかですね、そもそもメディアテクスチャーに
映像以外が張れるという事を知りませんでした。
テクスチャには使えないgifフォーマットなども表示できるので、
quickTimeで読み込めるものならいけるという事なのかな。
という事はflash(ver5)もいけるのかなあ…。

それで、なぜ楽天CD検索なのかという所ですが
楽天が好きとかCDが好きとかそういうわけではなく。
現在セカンドライフからまともに利用できるwebサービスはかなり少ないようで、
いろいろ試して使えた物のなかで見栄えがいいのがこれだった、
というだけなのでした。
じゃらんの温泉検索も確か連携できて、ステキだったのですが…
写真無しのテキスト情報なんですよね。惜しい。

APIをたたくだけであればllHTTPRequest一発で簡単です。
しかし、Yahoo画像検索やHotPepper店舗検索のAPIはそもそも
セカンドライフと連携することができず、
エラーコード415という謎のエラーしか返してくれませんでした。
このllHTTPRequestのwikiのページにあるように
妙な(しかし便利そうな)ヘッダ情報を送るのでアクセスを拒否されるのでしょうか。

そして連携できた後も、レスポンスとして受け取れる情報量が
2048バイトまでとかなり少なく、webサービスの返すxmlが途中までしか
読み込めないため、肝心な情報が含まれていなかったりします。
(もし全部のxmlを読み込める方法があればぜひ教えていただきたく…
合間にphpなりperlなりを中継としてはさめば
何とかなるのは確かなんですけど
そうするとSLの製作でなく、サーバサイドの製作になってしまうので
今回はパスしました。そのうちやると思いますけど。)


しょうがないので、今日作ったサンプルでは
データが抽出できない場合、エラー画面を表示するようにしました。
エラー処理ばっかりで嫌なソースコードになりました。


というよりむしろ、9割がたエラーです。
ケイタイ用のxmlを要求して、情報を少なくしているのですが
それでも駄目です。SLはケイタイ以下ですか。
xmlもパーサーなどが関数で用意されて無く(たぶん)、
自前で関数を組み合わせて解析するのでかなり面倒です。

よくわかっていないですが、LSL_XML-RPCあたりを読み漁れば何か解決するでしょうか…。


ただ単に、
Setting HTTP_BODY_MAXLENGTH is not yet supported

これが実装されるだけでいろいろできるんですけどね。
しかし、近いうちに実装される事でしょう。なんとなく。
特に情報ソースなし。


たいていの場合、検索結果として画像無しのタイトルしか表示されませんが
どうやらスタジオジプリ系がけっこうイメージが表示されやすいようです。
他にイメージが出やすい単語を見つけたら教えて下さいませ。


瀬川○子キター!!  

Posted by harayoki at 22:31Comments(6)楽天CD検索