You are here

class FeedsYouTubeFetcher in Feeds: YouTube Parser 8

Constructs FeedsYouTubeFetcher object.

Plugin annotation


@FeedsFetcher(
  id = "feeds_youtube_fetcher",
  title = @Translation("YouTube"),
  description = @Translation("Fetch videos from a YouTube user"),
  form = {
    "configuration" = "Drupal\feeds_youtube\Feeds\Fetcher\Form\FeedsYouTubeFetcherForm",
    "feed" = "Drupal\feeds_youtube\Feeds\Fetcher\Form\FeedsYouTubeFetcherFeedForm",
  }
)

Hierarchy

Expanded class hierarchy of FeedsYouTubeFetcher

File

src/Feeds/Fetcher/FeedsYouTubeFetcher.php, line 28

Namespace

Drupal\feeds_youtube\Feeds\Fetcher
View source
class FeedsYouTubeFetcher extends PluginBase implements ClearableInterface, FetcherInterface {

  /**
   * {@inheritdoc}
   */
  public function fetch(FeedInterface $feed, StateInterface $state) {
    $result = $this
      ->get($feed
      ->getSource(), $feed
      ->id());
    if ($result !== FALSE) {
      return new RawFetcherResult($result);
    }
    else {
      return new RawFetcherResult('');
    }
  }

  /**
   * Helper function to get client factory.
   *
   * @param string $id
   *   The feed Id.
   *
   * @return Google_Client|null
   */
  public function getClientFactory($id) {
    $config = $this
      ->getConfiguration();
    $cid = $this
      ->getAccessTokenCacheId($id);
    $google_access_token = \Drupal::service('cache.feeds_youtube_tokens')
      ->get($cid);
    if (!empty($config['google_oauth_client_id']) && !empty($config['google_oauth_client_secret']) && !empty($config['google_developer_key'])) {
      $client = new \Google_Client();
      $client
        ->setClientId($config['google_oauth_client_id']);
      $client
        ->setClientSecret($config['google_oauth_client_secret']);
      $client
        ->setApprovalPrompt('force');
      $client
        ->setAccessType('offline');
      $client
        ->setDeveloperKey($config['google_developer_key']);
      $client
        ->setScopes('https://www.googleapis.com/auth/youtube.readonly');
      $current_path = \Drupal::service('path.current')
        ->getPath();
      $path_alias = \Drupal::service('path_alias.manager')
        ->getAliasByPath($current_path);
      $current_url = Url::fromUserInput($path_alias, [
        'absolute' => TRUE,
      ])
        ->toString();
      $redirect = filter_var($current_url, FILTER_SANITIZE_URL);
      $client
        ->setRedirectUri($redirect);
      if (!empty($google_access_token->data)) {
        $client
          ->setAccessToken($google_access_token->data);
        if ($client
          ->isAccessTokenExpired()) {
          $client
            ->refreshToken($client
            ->getRefreshToken());

          // Save refreshed access token.
          $cache_tags = [
            'feeds_youtube:google_access_token',
          ];
          \Drupal::service('cache.feeds_youtube_tokens')
            ->set($cid, $client
            ->getAccessToken(), CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
        }
      }
      return $client;
    }
    else {
      \Drupal::messenger()
        ->addWarning($this
        ->t('Google API access is not configured.'));
    }
  }

  /**
   * Make the API queries to get the data the parser needs.
   *
   * @param string $source
   *   The URL source.
   * @param string $id
   *   The feed Id.
   *
   * @return string
   *   Returns a JSON-encoded array of stdClass objects.
   */
  public function get(string $source, $id) {
    $client = $this
      ->getClientFactory($id);
    $youtube = new \Google_Service_YouTube($client);
    $number_of_pages = $this
      ->getConfiguration('import_video_limit');
    if ($number_of_pages < 1) {
      $number_of_pages = 1;
    }
    $result = [];
    $next_page_token = '';
    for ($i = 0; $i < $number_of_pages; $i++) {
      $api_request_result = $this
        ->requestNextPage($source, $next_page_token, $client, $youtube);
      $next_page_token = !empty($api_request_result['next_page_token']) ? $api_request_result['next_page_token'] : '';
      if (!empty($api_request_result['result'])) {
        $result = array_merge($result, $api_request_result['result']);
      }
    }
    return json_encode($result);
  }

  /**
   * Convert YouTube video duration to time interval.
   *
   * @param string $duration
   *   YouTube video duration.
   *
   * @return string
   */
  private function timeToDuration($duration) {
    $di = new \DateInterval($duration);
    $string = '';
    if ($di->h > 0) {
      $string .= $di->h . ':';
    }
    return $string . $di->i . ':' . $di->s;
  }

  /**
   * Parse a YouTube video feed.
   *
   * @param array $video_items
   *
   * @return array
   */
  private function parseVideoItems($video_items) {
    $items = [];
    foreach ($video_items['items'] as $key => $video) {
      $item = [
        'video_id' => '',
        'video_url' => '',
        'title' => '',
        'description' => '',
        'thumbnail_default' => '',
        'thumbnail_medium' => '',
        'thumbnail_high' => '',
        'thumbnail_standard' => '',
        'thumbnail_maxres' => '',
        'category' => '',
        'tags' => '',
        'duration' => '',
        'duration_raw' => '',
        'published_datetime' => '',
        'published_timestamp' => '',
        'view_count' => '',
        'fav_count' => '',
        'likes' => '',
        'dislikes' => '',
        'favorites' => '',
        'embedded_player' => '',
      ];
      if (isset($video['id']) && is_array($video['id']) && isset($video['id']['videoId'])) {
        $item['video_id'] = $video['id']['videoId'];
      }
      elseif (isset($video['contentDetails']) && isset($video['contentDetails']['videoId'])) {
        $item['video_id'] = $video['contentDetails']['videoId'];
      }
      elseif (isset($video['snippet']) && isset($video['snippet']['resourceId']) && isset($video['snippet']['resourceId']['videoId'])) {
        $item['video_id'] = $video['snippet']['resourceId']['videoId'];
      }
      if (!empty($item['video_id'])) {
        $item['video_url'] = 'https://www.youtube.com/watch?v=' . $item['video_id'];
      }
      if (isset($video['snippet'])) {
        $item['title'] = $video['snippet']['title'];
        $item['description'] = $video['snippet']['description'];
        if (isset($video['snippet']['thumbnails']['default'])) {
          $item['thumbnail_default'] = $video['snippet']['thumbnails']['default']['url'];
        }
        if (isset($video['snippet']['thumbnails']['standard'])) {
          $item['thumbnail_standard'] = $video['snippet']['thumbnails']['standard']['url'];
        }
        if (isset($video['snippet']['thumbnails']['medium'])) {
          $item['thumbnail_medium'] = $video['snippet']['thumbnails']['medium']['url'];
        }
        if (isset($video['snippet']['thumbnails']['high'])) {
          $item['thumbnail_high'] = $video['snippet']['thumbnails']['high']['url'];
        }
        if (isset($video['snippet']['thumbnails']['maxres'])) {
          $item['thumbnail_maxres'] = $video['snippet']['thumbnails']['maxres']['url'];
        }
        if (isset($video['snippet']['categoryId'])) {
          $item['category'] = $video['snippet']['categoryId'];
        }
        if (isset($video['snippet']['tags'])) {
          $item['tags'] = implode(', ', $video['snippet']['tags']);
        }
        $published_timestamp = strtotime($video['snippet']['publishedAt']);
      }
      if (isset($video['contentDetails'])) {
        if (isset($video['contentDetails']['duration'])) {
          $item['duration'] = $this
            ->timeToDuration($video['contentDetails']['duration']);
          $item['duration_raw'] = $video['contentDetails']['duration'];
        }
        if (!isset($published_timestamp) && isset($video['contentDetails']['videoPublishedAt'])) {
          $published_timestamp = strtotime($video['snippet']['publishedAt']);
        }
      }
      if (isset($video['statistics'])) {
        $item['view_count'] = $video['statistics']['viewCount'];
        $item['fav_count'] = $video['statistics']['favoriteCount'];
        $item['likes'] = $video['statistics']['likeCount'];
        $item['dislikes'] = $video['statistics']['dislikeCount'];
        $item['favorites'] = $video['statistics']['favoriteCount'];
      }
      if (isset($video['player'])) {
        $item['embedded_player'] = $video['player']['embedHtml'];
      }
      if (isset($published_timestamp)) {
        $item['published_datetime'] = date('Y-m-d H:i:s', $published_timestamp);
        $item['published_timestamp'] = $published_timestamp;
      }
      $items[] = $item;
    }
    return $items;
  }

  /**
   * Request URL.
   *
   * @param string $url
   *   The URL to request.
   * @param string $page_token
   *   The page token for the request.
   *
   * @return array|null
   */
  public function requestUrl($url, $page_token = '') {
    try {
      $url .= '&maxResults=' . $this
        ->getConfiguration('results_per_page');
      $url .= $page_token != '' ? '&pageToken=' . $page_token : '';
      $api_request_result = \Drupal::httpClient()
        ->get($url);
      $data = (string) $api_request_result
        ->getBody();
      return json_decode($data, TRUE);
    } catch (RequestException $e) {
      \Drupal::messenger()
        ->addError($this
        ->t('Request Error: @message', [
        '@message' => $e
          ->getMessage(),
      ]));
    }
  }

  /**
   * Perform API request to YouTube API and retrieve search result.
   *
   * @param string $url
   *   The URL to request.
   * @param string $page_token
   *   The page token for the request.
   * @param Google_Client $client
   *   A Google client to use.
   * @param Google_Service_YouTube $youtube
   *   A Google YouTube service to use.
   *
   * @return array|null
   */
  private function requestNextPage($url, $page_token = '', $client, $youtube) {
    $result = [];
    $response = $this
      ->requestUrl($url, $page_token);
    if (!empty($response['items'])) {
      $result = $this
        ->parseVideoItems($response);
    }
    else {
      \Drupal::messenger()
        ->addStatus($this
        ->t('No videos found.'));
    }
    return [
      'result' => $result,
      'next_page_token' => isset($search_response['nextPageToken']) ? $search_response['nextPageToken'] : '',
    ];
  }

  /**
   * Get access token cache ID.
   *
   * @param string $id
   *   The feed Id.
   *
   * @return string
   */
  public function getAccessTokenCacheId($id) {
    return 'feeds_youtube:google_access_token:' . $id;
  }

  /**
   * {@inheritdoc}
   */
  public function clear(FeedInterface $feed, StateInterface $state) {
    $this
      ->onFeedDeleteMultiple([
      $feed,
    ]);
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'google_developer_key' => '',
      'google_oauth_client_id' => '',
      'google_oauth_client_secret' => '',
      'import_video_limit' => 50,
      'results_per_page' => 50,
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DependencyTrait::$dependencies protected property The object's dependencies.
DependencyTrait::addDependencies protected function Adds multiple dependencies.
DependencyTrait::addDependency protected function Adds a dependency.
FeedsYouTubeFetcher::clear public function Removes all stored results for a feed. Overrides ClearableInterface::clear
FeedsYouTubeFetcher::defaultConfiguration public function Gets default configuration for this plugin. Overrides PluginBase::defaultConfiguration
FeedsYouTubeFetcher::fetch public function Fetch content from a feed and return it. Overrides FetcherInterface::fetch
FeedsYouTubeFetcher::get public function Make the API queries to get the data the parser needs.
FeedsYouTubeFetcher::getAccessTokenCacheId public function Get access token cache ID.
FeedsYouTubeFetcher::getClientFactory public function Helper function to get client factory.
FeedsYouTubeFetcher::parseVideoItems private function Parse a YouTube video feed.
FeedsYouTubeFetcher::requestNextPage private function Perform API request to YouTube API and retrieve search result.
FeedsYouTubeFetcher::requestUrl public function Request URL.
FeedsYouTubeFetcher::timeToDuration private function Convert YouTube video duration to time interval.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$feedType protected property The importer this plugin is working for.
PluginBase::$linkGenerator protected property The link generator.
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::$urlGenerator protected property The url generator.
PluginBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies 2
PluginBase::container private function Returns the service container.
PluginBase::defaultFeedConfiguration public function Returns default feed configuration. Overrides FeedsPluginInterface::defaultFeedConfiguration 3
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginBase::l protected function Renders a link to a route given a route name and its parameters.
PluginBase::linkGenerator protected function Returns the link generator service.
PluginBase::onFeedDeleteMultiple public function A feed is being deleted. 3
PluginBase::onFeedSave public function A feed is being saved.
PluginBase::onFeedTypeDelete public function The feed type is being deleted. 1
PluginBase::onFeedTypeSave public function The feed type is being saved. 1
PluginBase::pluginType public function Returns the type of plugin. Overrides FeedsPluginInterface::pluginType
PluginBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration 1
PluginBase::url protected function Generates a URL or path for a specific route based on the given parameters.
PluginBase::urlGenerator protected function Returns the URL generator service.
PluginBase::__construct public function Constructs a PluginBase object. Overrides PluginBase::__construct 4
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.