気楽なソフト工房

プログラミングについていろいろな記事を書いています。



mykonos2008

Author:mykonos2008
システムエンジニアとして働いている30代の会社員です。
仕事や趣味でプログラムを書いている方の役に立つ記事を書いていきたいと思っています。
ご意見、ご感想はこちらまで
If you are an english speaker,Please visit my english blog.

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
動画を再生するフォームにYoutubeのコメントを表示するようにしました。

(画像をクリックすると拡大表示します。)

取得の仕方は動画を検索するのとほぼ変わらず意外と簡単にできました。
YouTube APIに接続して検索などを行う専用のクラス[YouTubeProxy」にGetComments()を追加しました。
クエリに指定するURLは[YouTubeEntry]クラスの「Comments.FeedLink.Href」から取得できます。

   public CommentsFeed GetComments(String url, int startIndex)
     {
         //APIへのプロキシ
         YouTubeService service = new YouTubeService(_applicationName);

         //クエリオブジェクト
         YouTubeQuery query = new YouTubeQuery();
         query.Uri = new Uri(url);
         query.StartIndex = startIndex;
         query.NumberToRetrieve = _retrieveCount;

         return service.Query(query) as CommentsFeed;
     }

そして、CommentsFeedからCommentEntryを取り出して、コメントの情報を取得します。
CommentsFeedの中には他のコメントの返信コメントも含めて指定した動画に関連するコメントが格納されています。
CommentEntryのRepliesが設定されているコメントは、他のコメントに対する返信コメントになります。Repliesから返信先のURLを取得し、関連付けを行うことができます。

前々回で、動画の再生はFlashのActiveXをフォームに貼り付ける方法で行っていましたが、この方法だとキャッシュがされないことと、自動的に再生が始まらないという問題があったため、WebBrowserコントロールに「ここ」に載っている方法でPlayerを貼り付けることにしました。
スポンサーサイト
大体、調査が出来たのでいよいよ本格的にビューワーの開発を始めました。今日までで YouTubeからフィードを取得して、情報を画面に表示し、それがクリックされると動画を再生する ところまでいきました。
まだかなり物足りないですが、画面イメージはこんな感じです。

(画像をクリックすると拡大表示します。)

まず、YouTube APIに接続して検索などを行う専用のクラス[YouTubeProxy」を作成しました。 主なメソッドは検索を行うための[Search]です。複合キーワードによる検索など もう少し詳細な処理を追加したいのですが、今のところはこんな感じ。

   /// 
   /// YouTubeからFeedを検索する
   /// 
   /// 検索ワード
   /// 
   public YouTubeFeed Search(String searchWord,int startIndex)
   {
       //APIへのプロキシ
       YouTubeService service = new YouTubeService(_applicationName);

       //クエリオブジェクト
       YouTubeQuery query = new YouTubeQuery();
       query.StartIndex = startIndex;
       query.NumberToRetrieve = _retrieveCount;
       query.Uri = new Uri(String.Format("http://gdata.youtube.com/feeds/api/videos?vq={0}", HttpUtility.UrlEncode(searchWord)));
       query.Formats.Add(YouTubeQuery.VideoFormat.Embeddable); //検索対象を埋め込みPlayerに限定

       //検索を行い、フィードを返却する
       return service.Query(query);
  }


次はフィードのエントリを表示するためのUserControlを作成しました。
今のところ、サムネイルとタイトル、あと説明を表示しています。 サムネイルはPictureBoxを使用して表示しています。ImageLocationにフィードから 取得したサムネイルのURLを設定するのみでした。

   /// 
   /// コンストラクタ
   /// 
   /// 
   public EntryControl(YouTubeEntry entry)
   {
       InitializeComponent();

       _entry = entry;

       MediaGroup group = _entry.Media;

       //Titleを設定
       _title.Text = group.Title.Value;

       //Descを設定
       _desc.Text = group.Description.Value;

       //サムネイル画像を設定
       if (group.Thumbnails.Count != 0)
       {
         SortedList attributes = group.Thumbnails[0].Attributes;
         _thumbnail.ImageLocation = attributes["url"].ToString();
       }

       //動画URLを設定
       foreach (MediaContent content in group.Contents)
       {
         SortedList attributes = content.Attributes;

         //埋め込み型プレーヤーへのURLの場合
         if (attributes["format"].ToString().Equals("5"))
         {
             _movieUrl = attributes["url"].ToString();
             break;
         }
       }
   }


そして、メインフォームの方で検索ボタンクリックのイベントを処理し、 検索を行い、ユーザコントロールを作成し、Panelに貼り付けています。

最初はTableLayoutPanelを使っていたのですが、何故か縦スクロールバーが表示されるタイミングで 横スクロールバーが不必要に表示されてしまうため、Panelに切り替えました。

YouTube APIに接続している間、画面が固まってしまうのが嫌だったので、 その部分は別スレッドに処理させるようにしました。

   private void _searchBtn_Click(object sender, EventArgs e)
   {
       //検索ワードが入力されている場合
       if (searchWord.Text.Length != 0)
       {
         //別スレッドを起動して、非同期で検索処理を行う
         Thread thread = new Thread(new ThreadStart(ExecuteSearch));
         thread.Start();
       }
   }
   
   /// 
   /// 検索を行うスレッドが実行する処理メソッド
   /// 
   private void ExecuteSearch()
   {
       try
       {
         //検索ワード
         String word = null;
   
         Invoke((MethodInvoker)delegate()
         {
             _status.Text = "検索中...";
             word = searchWord.Text;
         });
   
         //YouTube APIに接続して検索を行う
         YouTubeProxy proxy = new YouTubeProxy();
         YouTubeFeed feed = proxy.Search(word,0);
   
         Invoke((MethodInvoker)delegate()
         {
             _status.Text = "結果を取得中...";
   
             _resultPanel.Controls.Clear();
   
             int y = 0;
   
             //エントリ数分ループ
             foreach (YouTubeEntry entry in feed.Entries)
             {
               //エントリを表示するコントロールを生成し、パネルに貼り付ける
               EntryControl entryCtl = new EntryControl(entry);
               entryCtl.Location = new Point(entryCtl.Margin.Left, y);
               y = y + entryCtl.Height + entryCtl.Margin.Top;
   
               _resultPanel.Controls.Add(entryCtl);
   
             }
   
             _status.Text = "";
   
         });
   
       }
       catch
       {
            MessageBox.Show("検索中にエラーが発生しました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
       }
   }


まだまだ先は長いですが、がんばります。

『ライブラリ』にここまでの成果をソリューション毎置きました。(.Netクライアントライブラリを除く)

ご参考にしてください。
何とかYouTubeからフィードを検索できたので、次は動画を再生する部分。
Flash PlayerのActiveXコントロールを使います。といってもこれはとても簡単でした。

C# Expressのメニュー「ツール」→「ツールボックスアイテムの選択」でCOMタブを選んで
「Shockwave Flash Object」を追加します。するとツールボックスに「Shockwave Flash Object」が
出てくるのでこれをフォームに貼り付けるだけです。
(FlashのActiveXを利用するためにはFlash Player6以上がインストールされている必要があります)

あとは前々回の記事で取得した動画のURLをLoadMovieメソッドに渡してあげれば動画を再生できます。

 player.LoadMovie(0, "http://www.youtube.com/v/KLbL3i9p9EE&f=gdata_videos");

YouTubeのサイトだとページが表示されると自動的に再生が始まるのですが、
上のメソッドだけでは再生が始まらず、画面の真ん中に三角のボタンが表示
された状態でとまっています。続けてplayer.Play()としても結果は同じ。

多分ですが、FlashにはJavaScriptからActionScriptの関数を呼び出す機能があり、
それを使って、自動再生を開始しているのかなと思います。

なので、今回のアプリからロードした瞬間に実行できるようにすることは
不可能ではないと思うのですが、(WebBrowserコントロールを使うなりして)
難しそうです。

これでひとまず調査は完了にしてアプリを作ってみたいと思います。
前回、ライブラリを使用してYouTubeからフィードを取得することが出来たのですが、
検索ワードに日本語を指定すると、明らかに結果がおかしい。。
英語だと問題ないのだが。

よくよく調べてみると原因が判明しました。

フィードを検索する際にこんな感じで記述しているのですが、

YouTubeQuery query = new YouTubeQuery();
query.Uri = new Uri(
                   String.Format("http://gdata.youtube.com/feeds/api/videos?vq={0}", 
                   HttpUtility.UrlEncode(args[0]))
                 );
YouTubeFeed feed = service.Query(query);

このUriクラスがくせものでした。.NETのドキュメントによると、このクラスのUriプロパティは
「127 より大きい Unicode 値の文字はすべて、それと等価の 16 進数値に置換されます」と書かれています。
つまり、日本語などは勝手にUnicodeを表す16進数にエンコードされてしまうのです。

Google Data APIではURIはUTF-8でエンコードする必要があるため、ちゃんとした結果が返ってこないのでした。

さて、どうしようかと悩み、結局、ライブラリのソースコードもZIPに含まれていたので一部ライブラリを修正することにしました。

WebRequestを生成する部分を以下のように修正(Core Clientプロジェクトのrequest.cs)

//this.webRequest = WebRequest.Create(this.targetUri);	//521行目
this.webRequest = WebRequest.Create(Uri.UnescapeDataString(targetUri.AbsoluteUri));

Uriクラスが持つ文字列をデコードして元のURI文字列を取得し、そいつを渡して WebRequestクラスのインスタンスを生成するようにしました。

これでビルドして、実行してみたところ、上手く取得できました!!!
他にもっと良い方法があるかもしれませんが、とりあえず解決。

次は、Flash PlayerをWindowsフォームから利用するところをやりたいと思います。
Google Data APIとFlash PlayerのActiveXコントローラを使って、簡単なYouTubeビューワーを作ってみたいと思います。

今日はまず、youtubeに登録されている動画エントリを検索して取得してくるところをやってみたいと思います。開発環境はフリーの「Visual C# Express 2008」を使います。

Google Data APIはAtomをベースにしているので、生でHTTP接続して必要なデータをもってくることもできると思うのですが、それっとちょっと面倒くさいです。探してみると ここ に.NET用のクライアントライブラリがありました!

ZIP形式でダウンロードして解凍します。参照設定で、「Google.GData.Extensions.dll」、「Google.GData.YouTube.dll」を追加して準備完了。

ビューワーの画面にはYoutubeのWebサイトと同じようにタイトルと、説明、あとサムネイルの画像を表示してみたいので、それらをAPIを使って取得します。もちろん動画のURLでもですね。まずはAPIの動きを確認したいので、コンソールアプリケーションで、取得した情報を表示してみます。 まずは、以下のusing句を追加

 using Google.GData.Extensions.MediaRss; 
 using Google.GData.YouTube;

次に検索してFeedを取得する部分。

   //サービスを作成
   YouTubeService service = new YouTubeService("example"); //引数はなんでもいい。

   //クエリを生成
   YouTubeQuery query = new YouTubeQuery();
   query.StartIndex = 0; //取得するレコードの開始位置
   query.NumberToRetrieve = 10; //取得件数

   //args[0]の部分に検索文字列をいれる
   query.Uri = new Uri(
                        String.Format("http://gdata.youtube.com/feeds/api/videos?vq={0}",
                        HttpUtility.UrlEncode(args[0]))
                    );

   //クエリーを発行
   YouTubeFeed feed = service.Query(query);



そして、取得したFeedから情報を取り出します。youtubeのFeedはこんな感じになってます。
  ← YouTubeFeedクラス
   ← YouTubeEntryクラス
      ← MediaGroupクラス ここにタイトルと説明がある 
         ← MediaContentクラス 動画のURLがある
         ← MediaThumbnailクラス ← サムネイルのURLがある


対応する各クラスから情報を取得します。

   //エントリ数分ループ
   foreach (YouTubeEntry entry in feed.Entries)
   {
       MediaGroup group = entry.Media;

       //タイトルと説明
       Console.WriteLine("Title / {0}", group.Title.Value);
       Console.WriteLine("Desc / {0}", group.Description.Value);

       //コンテンツ数分ループ
       foreach (MediaContent content in group.Contents)
       {
           SortedList attributes = content.Attributes;

           //属性数分ループ
           foreach (DictionaryEntry attribute in attributes)
           {
              if (attribute.Key.Equals("url"))
              { 
                  //動画のURL
                  Console.WriteLine("URL / {0}", attribute.Value);
              }
           }           
       }

       //サムネイル数分ループ
       foreach (MediaThumbnail thumbnail in group.Thumbnails)
       {
           SortedList attributes = thumbnail.Attributes;

           //属性数分ループ
           foreach (DictionaryEntry attribute in attributes)
           {
              if (attribute.Key.Equals("url"))
              {
                  //サムネイルのURL
                  Console.WriteLine("Thumbnail key / {0}", attribute.Value);
              }
           }
       }
   }



とりあえずデータは取れているのですが、検索条件に日本語を入れた場合の挙動があやしい。 もう少し調べてみます。
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。