MDL-49085 block_tag_youtube: Using Youtube data api v3
authorDavid Monllao <davidm@moodle.com>
Wed, 22 Jul 2015 18:29:48 +0000 (20:29 +0200)
committerDavid Monllao <davidm@moodle.com>
Tue, 18 Aug 2015 03:12:55 +0000 (11:12 +0800)
blocks/tag_youtube/block_tag_youtube.php
blocks/tag_youtube/lang/en/block_tag_youtube.php
blocks/tag_youtube/settings.php [new file with mode: 0644]
blocks/tag_youtube/upgrade.txt [new file with mode: 0644]
blocks/tag_youtube/version.php

index a06588e..7ab13be 100644 (file)
@@ -26,14 +26,29 @@ define('DEFAULT_NUMBER_OF_VIDEOS', 5);
 
 class block_tag_youtube extends block_base {
 
+    /**
+     * @var Google_Service_Youtube
+     */
+    protected $service = null;
+
     function init() {
         $this->title = get_string('pluginname','block_tag_youtube');
+        $this->config = new stdClass();
     }
 
     function applicable_formats() {
         return array('tag' => true);
     }
 
+    /**
+     * It can be configured.
+     *
+     * @return bool
+     */
+    public function has_config() {
+        return true;
+    }
+
     function specialization() {
         $this->title = !empty($this->config->title) ? $this->config->title : get_string('pluginname', 'block_tag_youtube');
         // Convert numeric categories (old YouTube API) to
@@ -56,6 +71,14 @@ class block_tag_youtube extends block_base {
             return $this->content;
         }
 
+        $this->content = new stdClass();
+        $this->content->footer = '';
+
+        if (!$this->get_service()) {
+            $this->content->text = $this->get_error_message();
+            return $this->content;
+        }
+
         $text = '';
         if(!empty($this->config->playlist)){
             //videos from a playlist
@@ -72,31 +95,41 @@ class block_tag_youtube extends block_base {
             }
         }
 
-        $this->content = new stdClass;
         $this->content->text = $text;
-        $this->content->footer = '';
 
         return $this->content;
     }
 
     function get_videos_by_playlist(){
 
+        if (!$service = $this->get_service()) {
+            return $this->get_error_message();
+        }
+
         $numberofvideos = DEFAULT_NUMBER_OF_VIDEOS;
         if( !empty($this->config->numberofvideos)) {
             $numberofvideos = $this->config->numberofvideos;
         }
 
-        $request = 'http://gdata.youtube.com/feeds/api/playlists/' .
-                   $this->config->playlist .
-                   '?start-index=1&max-results=' .
-                   $numberofvideos .
-                   '&format=5';
+        try {
+            $response = $service->playlistItems->listPlaylistItems('id,snippet', array(
+                'playlistId' => $this->config->playlist,
+                'maxResults' => $numberofvideos
+            ));
+        } catch (Google_Service_Exception $e) {
+            debugging('Google service exception: ' . $e->getMessage(), DEBUG_DEVELOPER);
+            return $this->get_error_message(get_string('requesterror', 'block_tag_youtube'));
+        }
 
-        return $this->fetch_request($request);
+        return $this->render_items($response);
     }
 
     function get_videos_by_tag(){
 
+        if (!$service = $this->get_service()) {
+            return $this->get_error_message();
+        }
+
         $tagid = optional_param('id', 0, PARAM_INT);   // tag id - for backware compatibility
         $tag = optional_param('tag', '', PARAM_TAG); // tag
 
@@ -117,17 +150,26 @@ class block_tag_youtube extends block_base {
             $numberofvideos = $this->config->numberofvideos;
         }
 
-        $request = 'http://gdata.youtube.com/feeds/api/videos?vq=' .
-                   $querytag .
-                   '&start-index=1&max-results=' .
-                   $numberofvideos .
-                   '&format=5';
+        try {
+            $response = $service->search->listSearch('id,snippet', array(
+                'q' => $querytag,
+                'type' => 'video',
+                'maxResults' => $numberofvideos
+            ));
+        } catch (Google_Service_Exception $e) {
+            debugging('Google service exception: ' . $e->getMessage(), DEBUG_DEVELOPER);
+            return $this->get_error_message(get_string('requesterror', 'block_tag_youtube'));
+        }
 
-        return $this->fetch_request($request);
+        return $this->render_items($response);
     }
 
     function get_videos_by_tag_and_category(){
 
+        if (!$service = $this->get_service()) {
+            return $this->get_error_message();
+        }
+
         $tagid = optional_param('id', 0, PARAM_INT);   // tag id - for backware compatibility
         $tag = optional_param('tag', '', PARAM_TAG); // tag
 
@@ -148,19 +190,32 @@ class block_tag_youtube extends block_base {
             $numberofvideos = $this->config->numberofvideos;
         }
 
-        $request = 'http://gdata.youtube.com/feeds/api/videos?category=' .
-                   $this->config->category .
-                   '&vq=' .
-                   $querytag .
-                   '&start-index=1&max-results=' .
-                   $numberofvideos .
-                   '&format=5';
-
+        try {
+            $response = $service->search->listSearch('id,snippet', array(
+                'q' => $querytag,
+                'type' => 'video',
+                'maxResults' => $numberofvideos,
+                'videoCategoryId' => $this->config->category
+            ));
+        } catch (Google_Service_Exception $e) {
+            debugging('Google service exception: ' . $e->getMessage(), DEBUG_DEVELOPER);
+            return $this->get_error_message(get_string('requesterror', 'block_tag_youtube'));
+        }
 
-        return $this->fetch_request($request);
+        return $this->render_items($response);
     }
 
-    function fetch_request($request){
+    /**
+     * Sends a request to fetch data.
+     *
+     * @see block_tag_youtube::service
+     * @deprecated since Moodle 2.8.8, 2.9.2 and 3.0 MDL-49085 - please do not use this function any more.
+     * @param string $request
+     * @throws coding_exception
+     */
+    public function fetch_request($request) {
+        throw new coding_exception('Sorry, this function has been deprecated in Moodle 2.8.8, 2.9.2 and 3.0. Use block_tag_youtube::get_service instead.');
+
         $c = new curl(array('cache' => true, 'module_cache'=>'tag_youtube'));
         $c->setopt(array('CURLOPT_TIMEOUT' => 3, 'CURLOPT_CONNECTTIMEOUT' => 3));
 
@@ -170,32 +225,118 @@ class block_tag_youtube extends block_base {
         return $this->render_video_list($xml);
     }
 
+    /**
+     * Renders the video list.
+     *
+     * @see block_tag_youtube::render_items
+     * @deprecated since Moodle 2.8.8, 2.9.2 and 3.0 MDL-49085 - please do not use this function any more.
+     * @param SimpleXMLElement $xml
+     * @throws coding_exception
+     */
     function render_video_list(SimpleXMLElement $xml){
+        throw new coding_exception('Sorry, this function has been deprecated in Moodle 2.8.8, 2.9.2 and 3.0. Use block_tag_youtube::render_items instead.');
+    }
 
-        $text = '';
-        $text .= '<ul class="yt-video-entry unlist img-text">';
-
-        foreach($xml->entry as $entry){
-            $media = $entry->children('http://search.yahoo.com/mrss/');
-            $playerattrs = $media->group->player->attributes();
-            $url = s($playerattrs['url']);
-            $thumbattrs = $media->group->thumbnail[0]->attributes();
-            $thumbnail = s($thumbattrs['url']);
-            $title = s($media->group->title);
-            $yt = $media->children('http://gdata.youtube.com/schemas/2007');
-            $secattrs = $yt->duration->attributes();
-            $seconds = $secattrs['seconds'];
-
-            $text .= '<li>';
-            $text .= '<div class="clearfix">';
-            $text .= '<a href="'. $url . '">';
-            $text .= '<img alt="" class="youtube-thumb" src="'. $thumbnail .'" /></a>';
-            $text .= '</div><span><a href="'. $url . '">'. $title .'</a></span>';
-            $text .= '<div>';
-            $text .= format_time($seconds);
-            $text .= "</div></li>\n";
+    /**
+     * Returns an error message.
+     *
+     * Useful when the block is not properly set or something goes wrong.
+     *
+     * @param string $message The message to display.
+     * @return string HTML
+     */
+    protected function get_error_message($message = null) {
+        global $OUTPUT;
+
+        if (empty($message)) {
+            $message = get_string('apierror', 'block_tag_youtube');
+        }
+        return $OUTPUT->notification($message);
+    }
+
+    /**
+     * Gets the youtube service object.
+     *
+     * @return Google_Service_YouTube
+     */
+    protected function get_service() {
+        global $CFG;
+
+        if (!$apikey = get_config('block_tag_youtube', 'apikey')) {
+            return false;
         }
-        $text .= "</ul><div class=\"clearer\"></div>\n";
+
+        // Wrapped in an if in case we call different get_videos_* multiple times.
+        if (!isset($this->service)) {
+            require_once($CFG->libdir . '/google/lib.php');
+            $client = get_google_client();
+            $client->setDeveloperKey($apikey);
+            $client->setScopes(array(Google_Service_YouTube::YOUTUBE_READONLY));
+            $this->service = new Google_Service_YouTube($client);
+        }
+
+        return $this->service;
+    }
+
+    /**
+     * Renders the list of items.
+     *
+     * @param array $videosdata
+     * @return string HTML
+     */
+    protected function render_items($videosdata) {
+
+        if (!$videosdata || empty($videosdata->items)) {
+            if (!empty($videosdata->error)) {
+                debugging('Error fetching data from youtube: ' . $videosdata->error->message, DEBUG_DEVELOPER);
+            }
+            return '';
+        }
+
+        // If we reach that point we already know that the API key is set.
+        $service = $this->get_service();
+
+        $text = html_writer::start_tag('ul', array('class' => 'yt-video-entry unlist img-text'));
+        foreach ($videosdata->items as $video) {
+
+            // Link to the video included in the playlist if listing a playlist.
+            if (!empty($video->snippet->resourceId)) {
+                $id = $video->snippet->resourceId->videoId;
+                $playlist = '&list=' . $video->snippet->playlistId;
+            } else {
+                $id = $video->id->videoId;
+                $playlist = '';
+            }
+
+            $thumbnail = $video->snippet->getThumbnails()->getDefault();
+            $url = 'http://www.youtube.com/watch?v=' . $id . $playlist;
+
+            $videodetails = $service->videos->listVideos('id,contentDetails', array('id' => $id));
+            if ($videodetails && !empty($videodetails->items)) {
+
+                // We fetch by id so we just use the first one.
+                $details = $videodetails->items[0];
+                $start = new DateTime('@0');
+                $start->add(new DateInterval($details->contentDetails->duration));
+                $seconds = $start->format('U');
+            }
+
+            $text .= html_writer::start_tag('li');
+
+            $imgattrs = array('class' => 'youtube-thumb', 'src' => $thumbnail->url, 'alt' => $video->snippet->title);
+            $thumbhtml = html_writer::empty_tag('img', $imgattrs);
+            $link = html_writer::tag('a', $thumbhtml, array('href' => $url));
+            $text .= html_writer::tag('div', $link, array('class' => 'clearfix'));
+
+            $text .= html_writer::tag('span', html_writer::tag('a', $video->snippet->title, array('href' => $url)));
+
+            if (!empty($seconds)) {
+                $text .= html_writer::tag('div', format_time($seconds));
+            }
+            $text .= html_writer::end_tag('li');
+        }
+        $text .= html_writer::end_tag('ul');
+
         return $text;
     }
 
index 2f3cec1..4cdcdf7 100644 (file)
@@ -23,6 +23,9 @@
  */
 
 $string['anycategory'] = 'Any category';
+$string['apierror'] = 'The Youtube API key is not set. Contact your administrator.';
+$string['apikey'] = 'API key';
+$string['apikeyinfo'] = 'Get a <a href="https://developers.google.com/youtube/v3/getting-started">Google API key</a> for your Moodle site.';
 $string['autosvehicles'] = 'Autos &amp; Vehicles';
 $string['category'] = 'Category';
 $string['comedy'] = 'Comedy';
@@ -39,6 +42,7 @@ $string['numberofvideos'] = 'Number of videos';
 $string['peopleblogs'] = 'People &amp; Blogs';
 $string['petsanimals'] = 'Pets &amp; Animals';
 $string['pluginname'] = 'Youtube';
+$string['requesterror'] = 'Data could not be obtained from the server. Contact your administrator if the problem persist.';
 $string['scienceandtech'] = 'Science &amp; Tech';
 $string['sports'] = 'Sports';
 $string['tag_youtube:addinstance'] = 'Add a new youtube block';
diff --git a/blocks/tag_youtube/settings.php b/blocks/tag_youtube/settings.php
new file mode 100644 (file)
index 0000000..ad9f443
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Settings for the RSS client block.
+ *
+ * @package   block_tag_youtube
+ * @copyright 2015 David Monllao
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+    $settings->add(new admin_setting_configtext('block_tag_youtube/apikey', get_string('apikey', 'block_tag_youtube'),
+                       get_string('apikeyinfo', 'block_tag_youtube'), '', PARAM_RAW_TRIMMED, 40));
+}
diff --git a/blocks/tag_youtube/upgrade.txt b/blocks/tag_youtube/upgrade.txt
new file mode 100644 (file)
index 0000000..ae3d80d
--- /dev/null
@@ -0,0 +1,8 @@
+This files describes API changes in the block tag_youtube code.
+
+=== 3.0 ===
+
+* Due to the final YouTube API v2.0 deprecation we needed to adapt the current
+  code to YouTube Data API v3. block_tag_youtube::fetch_request and
+  block_tag_youtube::render_video_list have been deprecated as they can not be
+  used any more.
index 1a5c4de..832b9f1 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015051100;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2015051101;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2015050500;        // Requires this Moodle version
 $plugin->component = 'block_tag_youtube'; // Full name of the plugin (used for diagnostics)