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
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\feeds\Plugin\Type\PluginBase implements FeedsPluginInterface uses DependencyTrait
- class \Drupal\feeds_youtube\Feeds\Fetcher\FeedsYouTubeFetcher implements ClearableInterface, FetcherInterface
- class \Drupal\feeds\Plugin\Type\PluginBase implements FeedsPluginInterface uses DependencyTrait
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of FeedsYouTubeFetcher
File
- src/
Feeds/ Fetcher/ FeedsYouTubeFetcher.php, line 28
Namespace
Drupal\feeds_youtube\Feeds\FetcherView 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
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
DependencyTrait:: |
protected | property | The object's dependencies. | |
DependencyTrait:: |
protected | function | Adds multiple dependencies. | |
DependencyTrait:: |
protected | function | Adds a dependency. | |
FeedsYouTubeFetcher:: |
public | function |
Removes all stored results for a feed. Overrides ClearableInterface:: |
|
FeedsYouTubeFetcher:: |
public | function |
Gets default configuration for this plugin. Overrides PluginBase:: |
|
FeedsYouTubeFetcher:: |
public | function |
Fetch content from a feed and return it. Overrides FetcherInterface:: |
|
FeedsYouTubeFetcher:: |
public | function | Make the API queries to get the data the parser needs. | |
FeedsYouTubeFetcher:: |
public | function | Get access token cache ID. | |
FeedsYouTubeFetcher:: |
public | function | Helper function to get client factory. | |
FeedsYouTubeFetcher:: |
private | function | Parse a YouTube video feed. | |
FeedsYouTubeFetcher:: |
private | function | Perform API request to YouTube API and retrieve search result. | |
FeedsYouTubeFetcher:: |
public | function | Request URL. | |
FeedsYouTubeFetcher:: |
private | function | Convert YouTube video duration to time interval. | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The importer this plugin is working for. | |
PluginBase:: |
protected | property | The link generator. | |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
protected | property | The url generator. | |
PluginBase:: |
public | function |
Calculates dependencies for the configured plugin. Overrides DependentPluginInterface:: |
2 |
PluginBase:: |
private | function | Returns the service container. | |
PluginBase:: |
public | function |
Returns default feed configuration. Overrides FeedsPluginInterface:: |
3 |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets this plugin's configuration. Overrides ConfigurableInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
protected | function | Renders a link to a route given a route name and its parameters. | |
PluginBase:: |
protected | function | Returns the link generator service. | |
PluginBase:: |
public | function | A feed is being deleted. | 3 |
PluginBase:: |
public | function | A feed is being saved. | |
PluginBase:: |
public | function | The feed type is being deleted. | 1 |
PluginBase:: |
public | function | The feed type is being saved. | 1 |
PluginBase:: |
public | function |
Returns the type of plugin. Overrides FeedsPluginInterface:: |
|
PluginBase:: |
public | function |
Sets the configuration for this plugin instance. Overrides ConfigurableInterface:: |
1 |
PluginBase:: |
protected | function | Generates a URL or path for a specific route based on the given parameters. | |
PluginBase:: |
protected | function | Returns the URL generator service. | |
PluginBase:: |
public | function |
Constructs a PluginBase object. Overrides PluginBase:: |
4 |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |