Skip to content
Snippets Groups Projects
Commit 6566bd97 authored by Stefan Galinski's avatar Stefan Galinski :video_game:
Browse files

Merge branch 'master' into 'feature_Upgrade-to-TYPO3-10'

# Conflicts:
#   composer.json
#   ext_emconf.php
parents e39323bc c96fbedc
No related branches found
No related tags found
1 merge request!2Feature upgrade to typo3 10
......@@ -65,7 +65,7 @@ class YoutubeController extends ActionController {
}
$jsonArray['items'] = $this->youtubeService->mapArray(
$jsonArray['items'], $id, $aspectRatio, $thumbnailType
$jsonArray['items'], $id, $aspectRatio, $thumbnailType, $apiKey
);
} catch (Exception $exception) {
......
......@@ -2,8 +2,6 @@
namespace SGalinski\SgYoutube\Service;
use InvalidArgumentException;
/***************************************************************
* Copyright notice
*
......@@ -28,6 +26,16 @@ use InvalidArgumentException;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use InvalidArgumentException;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
use TYPO3\CMS\Core\Context\LanguageAspect;
use TYPO3\CMS\Core\Http\ServerRequest;
use TYPO3\CMS\Core\Registry;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
/**
* YouTube Helper Service
*/
......@@ -37,6 +45,7 @@ class YoutubeService {
const API_PLAYLIST = 'playlistItems';
const API_VIDEO = 'videos';
const API_PART = 'snippet';
const API_PART_LOCALIZATIONS = 'localizations';
const API_ORDER_BY = 'date';
/**
......@@ -47,11 +56,26 @@ class YoutubeService {
* @param string $youtubeId
* @param string $aspectRatio 16:9 (default) or 4:3 (ONLY used if byAspectRation is set as thumbnail type)
* @param string $thumbnailType maxres, standard, high, medium, default, byAspectRatio (default)
* @param string $apiKey
* @return array
*/
public function mapArray(
$jsonArray = [], $youtubeId = '', $aspectRatio = '16:9', $thumbnailType = 'byAspectRatio'
$jsonArray = [], $youtubeId = '', $aspectRatio = '16:9', $thumbnailType = 'byAspectRatio', $apiKey = ''
): array {
if (count($jsonArray) <= 0) {
return $jsonArray;
}
// Normalize the data to video details.
if (strpos($youtubeId, 'UC') === 0 || strpos($youtubeId, 'PL') === 0) {
$result = $this->getDetailedVideoInformationForJsonArray($jsonArray, $apiKey, self::API_PART);
if (count($result) <= 0 || !isset($result['items'])) {
return $jsonArray;
}
$jsonArray = $result['items'];
}
if (!in_array($thumbnailType, ['maxres', 'standard', 'high', 'medium', 'default', 'byAspectRatio'])) {
$thumbnailType = 'byAspectRatio';
}
......@@ -60,22 +84,26 @@ class YoutubeService {
$aspectRatio = '16:9';
}
$result = [];
foreach ($jsonArray as $field) {
if (strpos($youtubeId, 'UC') === 0) {
$youTubeIdFromArray = $field['id']['videoId'];
// Localization is just available from TYPO3 9.X.X
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '9.0.0', '>=')) {
$context = GeneralUtility::makeInstance(Context::class);
// prevent the channel preview
if ($field['id']['kind'] !== 'youtube#video') {
continue;
}
try {
/** @var LanguageAspect $languageAspect */
$languageAspect = $context->getAspect('language');
$currentLanguageUid = $languageAspect->getId();
} catch (AspectNotFoundException $e) {
// Can't be possible to land here, otherwise the whole frontend would be weird as hell..
$currentLanguageUid = 0;
}
} elseif (strpos($youtubeId, 'PL') === 0) {
$youTubeIdFromArray = $field['snippet']['resourceId']['videoId'];
} else {
$youTubeIdFromArray = $field['id'];
if ($currentLanguageUid > 0 && $youtubeId && $apiKey) {
$jsonArray = $this->addLocalizationData($jsonArray, $apiKey, $currentLanguageUid);
}
}
$result = [];
foreach ($jsonArray as $field) {
$previewImage = [];
$resolutionTypes = ['maxres', 'standard', 'high', 'medium', 'default'];
if ($thumbnailType !== 'byAspectRatio') {
......@@ -105,23 +133,163 @@ class YoutubeService {
'title' => $field['snippet']['title'],
'description' => strip_tags($field['snippet']['description']),
'thumbnail' => $previewImage['url'],
'url' => 'https://www.youtube.com/watch?v=' . $youTubeIdFromArray
'url' => 'https://www.youtube.com/watch?v=' . $field['id']
];
}
return $result;
}
/**
* Adds the localized title and description for each of the given entries in the jsonArray and returns it.
*
* @param array $jsonArray
* @param string $apiKey
* @param int $currentLanguageUid
*
* @return array
*/
protected function addLocalizationData(array $jsonArray, $apiKey, $currentLanguageUid): array {
if (!$apiKey || !$currentLanguageUid || count($jsonArray) <= 0) {
return $jsonArray;
}
$localizationData = $this->getDetailedVideoInformationForJsonArray(
$jsonArray, $apiKey, self::API_PART_LOCALIZATIONS
);
if (!isset($localizationData['items']) || count($localizationData['items']) <= 0) {
return $jsonArray;
}
/** @var ServerRequest $request */
$request = $GLOBALS['TYPO3_REQUEST'];
$attributes = $request->getAttributes();
if (!isset($attributes['site'])) {
return $jsonArray;
}
/** @var Site $site */
$site = $attributes['site'];
$languages = $site->getLanguages();
$currentSiteLanguage = $languages[$currentLanguageUid];
if (!$currentSiteLanguage) {
return $jsonArray;
}
$languageIsoCodes = [
$currentSiteLanguage->getTwoLetterIsoCode()
];
foreach ($currentSiteLanguage->getFallbackLanguageIds() as $languageId) {
$siteLanguage = $languages[$languageId];
if (!$siteLanguage) {
continue;
}
$languageIsoCodes[] = $siteLanguage->getTwoLetterIsoCode();
}
foreach ($localizationData['items'] as $index => $localizationEntry) {
if (!isset($localizationEntry['localizations']) || count($localizationEntry['localizations']) <= 0) {
continue;
}
$title = '';
$description = '';
$localizations = $localizationEntry['localizations'];
foreach ($languageIsoCodes as $languageIsoCode) {
if ($title && $description) {
break;
}
if (!$title && isset($localizations[$languageIsoCode]['title'])) {
$title = $localizations[$languageIsoCode]['title'];
}
if (!$description && isset($localizations[$languageIsoCode]['description'])) {
$description = $localizations[$languageIsoCode]['description'];
}
}
if ($title) {
$jsonArray[$index]['snippet']['title'] = $title;
}
if ($description) {
$jsonArray[$index]['snippet']['description'] = $description;
}
}
return $jsonArray;
}
/**
* Returns the detailed video information for the given json array and returns them as an array.
*
* @param array $jsonArray
* @param string $apiKey
* @param string $part
*
* @return array
*/
public function getDetailedVideoInformationForJsonArray(array $jsonArray, $apiKey, $part): array {
if (!$apiKey || count($jsonArray) <= 0) {
return $jsonArray;
}
$apiUrl = self::API_URL . self::API_VIDEO;
$parameters = [];
$parameters['part'] = $part;
$parameters['key'] = $apiKey;
$query = http_build_query($parameters);
foreach ($jsonArray as $videoData) {
$videoId = 0;
if (isset($videoData['id'])) {
$videoId = $videoData['id']['videoId'] ?? $videoData['id'];
}
if (!$videoId) {
continue;
}
$query .= '&id=' . $videoId;
}
$result = $this->getJsonAsArray('', '10', $apiKey, $apiUrl . '?' . $query);
if (!isset($result['items']) || count($result['items']) <= 0) {
return $jsonArray;
}
return $result;
}
/**
* Returns a JSON array with the video details (title, description, preview image, url)
*
* @param string $youtubeId
* @param string $maxResults
* @param string $apiKey
* @param string $url
* @return array|mixed
*/
public function getJsonAsArray($youtubeId = '', $maxResults = '10', $apiKey = '') {
$url = $this->getApiUrl($youtubeId, $maxResults, $apiKey);
public function getJsonAsArray($youtubeId = '', $maxResults = '10', $apiKey = '', $url = '') {
if (!$url) {
$url = $this->getApiUrl($youtubeId, $maxResults, $apiKey);
}
$registry = GeneralUtility::makeInstance(Registry::class);
$currentDay = date('Y-m-d', $GLOBALS['EXEC_TIME']);
$cacheKey = sha1($url);
$disableYoutubeCache = (bool) GeneralUtility::_GP('disableYoutubeCache');
if (!$disableYoutubeCache) {
$cachedResult = $registry->get('sg_youtube', $cacheKey);
if ($cachedResult) {
if ($cachedResult['CACHE_DATE'] === $currentDay) {
return $cachedResult;
}
$registry->remove('sg_youtube', $cacheKey);
}
}
if (function_exists('curl_init')) {
$ch = curl_init();
......@@ -164,6 +332,11 @@ class YoutubeService {
throw new InvalidArgumentException('No items found.', 403);
}
if (!$disableYoutubeCache) {
$jsonArray['CACHE_DATE'] = $currentDay;
$registry->set('sg_youtube', $cacheKey, $jsonArray);
}
return $jsonArray;
}
......
......@@ -27,3 +27,33 @@ plugin.tx_sgyoutube {
}
}
```
### Registration for more than the free 10.000 quotas per day
It's not 1 quota per 1 api call. Each api has it's own costs, which can be seen in the link below.
Currently at the version 3.2.1 we are using the following apis:
- "search/list" for channel videos
- "playlistItems/list" for videos from a specific playlist
- "videos/list" for getting the details for each video and the localizations, if needed.
The maximum quota costs would be "102" at the moment for rendering the latest videos from a channel with the video
details and translations.
[Quota Calculator](https://developers.google.com/youtube/v3/determine_quota_cost)
[YouTube API Services - Audit and Quota Extension Form](https://support.google.com/youtube/contact/yt_api_form?hl=en)
#### Caching behaviour
Because of the quota costs we implemented a caching for the calls for each day. The response from the apis will be
saved and used for 24 hours. Normally the site cache would do it, but it could be, that the cache will be cleared
multiple times in a row, or that the plugin is on an uncached page. The TYPO3 registry is used as a cache. The cleanup
will is handled on the fly.
If the "?disableYoutubeCache=1" parameter is added to the url, this cache will be ignored as well.
#### Possible way to solve the quota limit, if it's still reached
You can use a different api-key for specific sites. You can implement a TypoScript page uid check and just change the
key from the "TypoScript integration" topic.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment