2010年5月8日土曜日

YouTubeの自分のチャンネルの動画をリストアップするガジェット

昨日、自作ガジェットのコードがキャッシュに阻まれて更新できませんでした。 そのことをチョッと検索。 キーワード「google gadget キャッシュ」で調べてみるとそれっぽい対策情報をいくつか見つけました。 iGoogleにMy gadgetsというガジェットを入れると良いらしい。

で、実際にやってみるとハズレでした。 My gadgetsでキャッシュのチェックを外してからコードを更新しても反映されません。 正確に言うと、ローカルでコードを編集してgoogle gadget editorでUploadというのを繰り返そうとしたら、古いコードしか表示されませんでした。

結局、昨日と同様plalaのウェブサイトスペースにコードをアップロードして、bloggerで動作確認をしました。 面倒くさい。

今日書いたコードは、「YouTubeの自分のチャンネルの動画を再生回数が多い順にリストアップ」というものです。 bloggerの表示スペースを考えて、多い方から5つ表示させてみました。

簡単なコードでできました。 次のサイトに書いてあることを組み合わせるだけです。

まず、YouTubeに動画のリストを問い合わせる方法。 次のurlにアクセスするだけでxmlファイルで回答がもらえます。

http://gdata.youtube.com/feeds/api/videos?author=チャンネル名&orderby=viewCount&max-results=エントリー数

コードがはみ出ているかもしれないけど、気にしない方向でお願いします。 よく見たいときはマウスドラッグとかで選択してテキストエディターに貼り付けてください。

ガジェットAPIでxmlファイルのリクエストを出して受け取る方法はデベロッパーガイドにそのまま書いてます。 あとは必要な要素を取り出してolタグで一覧表示するだけ。

大体のコードは下のようになっています。 cssで怪しいところがあったので、そのままのコードではありません。 コピー&ペーストするときにミスしているかも。 雰囲気はこんな感じって事で。

<?xml version="1.0" encoding="UTF-8"?>
<Module>
    <ModulePrefs title="タイトル">
        <Require feature="dynamic-height"/>
    </ModulePrefs>

    <UserPref name="channel_name"     datatype="hidden" default_value="チャンネル名"/>
    <UserPref name="list_num"         datatype="hidden" default_value="リストアップするエントリー数"/>

    <Content type="html"><![CDATA[
        <div id="user_gadget_div">
            <a target="_top" href="http://www.youtube.com/user/__UP_channel_name__">YouTubeに移動</a>
        </div>
        <script type="text/javascript">
            var prefs = new _IG_Prefs(__MODULE_ID__);
            var channel_name = prefs.getString("channel_name");
            var list_num     = prefs.getInt("list_num");
            
            function makeDOMRequest()
            {
                var params = {};  
                params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM;  
                var url = "http://gdata.youtube.com/feeds/api/videos?author=" + channel_name + "&orderby=viewCount&max-results=" + list_num;  
                gadgets.io.makeRequest(url, response, params);
            };
            
            function response(obj)
            {
                var domdata = obj.data;
                var entryList = domdata.getElementsByTagName("entry");
                
                var text = 'YouTubeのチャンネル<a target="_top" href="http://www.youtube.com/user/' + channel_name + '">' + channel_name + '</a>から再生回数が多いもの' + list_num + 'つを表示しています。<ol>';
                
                for(var i = 0; i < entryList.length; i++)
                {
                    var entryNodeList = entryList.item(i).childNodes;
                    var entry = new Object();
                    entry.title = "タイトル取得失敗";
                    entry.link = "リンク先取得失敗";
                    entry.category = "";
                    entry.viewCount = "再生回数取得失敗";
                    
                    for(var j = 0; j < entryNodeList.length; j++)
                    {
                        var node = entryNodeList.item(j);
                        if(node.nodeName == "title") 
                        {
                            entry.title = node.firstChild.nodeValue;
                        }
                        else if( (node.nodeName == "link") && (node.getAttribute("rel") == "alternate") )
                        {
                            entry.link = node.getAttribute("href");
                        }
                        else if(node.nodeName == "category")
                        {
                            var labelAttr = node.getAttribute("label");
                            if( (labelAttr != null) && (labelAttr != "") )
                            {
                                entry.category += node.getAttribute("term") + "/" + entry.category
                            }
                            else
                            {
                                var keyword = node.getAttribute("term");
                                if(keyword.substring(0, 7) != "http://")
                                {
                                    entry.category += keyword + " ";
                                }
                            }
                        }
                        else if(node.nodeName == "yt:statistics")
                        {
                            entry.viewCount = node.getAttribute("viewCount");
                        }
                    }
                    
                    text += '<li><a target="_top" href="' + entry.link + '" title="' + entry.title + '">' + entry.title + '</a><br/><span title="カテゴリー : ' + entry.category + '">カテゴリー : ' + entry.category + '</span><br/><span title="再生回数 : ' + entry.viewCount + '">再生回数 : ' + entry.viewCount + '回</span></li>';
                }
                
                text += "</ol>";
                document.getElementById('user_gadget_div').innerHTML = text;
                
                gadgets.window.adjustHeight();
            };
            
            gadgets.util.registerOnLoadHandler(makeDOMRequest);
        </script>
    ]]></Content>
</Module>

つまづいたところをいくつかメモしておきます。

  • デベロッパーガイドの通りにやるとUserPrefの値が取り出せなかった。_IG_Prefsを使うと取り出せた。
  • ガジェットにはbloggerのcssは適用されない。スタイルはガジェット専用のものを書く。
  • ガジェットはiframeで表示される。ガジェット内のアンカータグにはtarget="_top"を忘れずに。
  • ieにDOM要素.hasAttributeが実装されていなかった。getAttributeで代用。

上で「cssは怪しい」って書きましたが、その他は大丈夫そうなのでbloggerに追加してしまいました。

その他は大丈夫? そういえば1点だけ、スタイルを適当に調整しながらFireFoxで試していたら、NoScriptに怒られてしまいました。 「xss攻撃の可能性がなんたら」とか。 ブログの管理者がログオンしながら作業すると、ガジェットの右下に設定アイコンが付きます。 それとガジェット内の表示がかぶっていると怒られるようです。

怒られたのに焦ってクリックミス、リポートボタン押してしまいました。 自分で書いたお試しコードの不具合をNoScriptに報告してどうするんだ...

とりあえずガジェットの下の方にパディングでスペースをとってお茶を濁しました。

ん? 管理者以外に設定アイコンは出ないから、放っておいて良かったのかな? まぁ、次回パディングを外して試してみましょう。