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

[FEATURE] Add new options to the plugin: thumbnail type, aspect Ratio, debug...

[FEATURE] Add new options to the plugin: thumbnail type, aspect Ratio, debug output & add intelligent thumbnail analyse feature as default & several bugfixes and cleanups
parent e01501e1
No related branches found
No related tags found
No related merge requests found
......@@ -46,14 +46,28 @@ class YoutubeController extends ActionController {
/**
* Renders the Youtube video view
*
* @return void
* @return NULL|string
*/
public function indexAction() {
$id = $this->settings['id'];
$maxResults = (string) $this->settings['maxResults'];
$maxResults = $this->settings['maxResults'];
$apiKey = $this->settings['apiKey'];
$thumbnailType = $this->settings['thumbnailType'];
$aspectRatio = $this->settings['aspectRatio'];
$showApiResult = (boolean) $this->settings['showApiResult'];
$debugOutput = '';
try {
$jsonArray = $this->youtubeService->getJsonAsArray($id, $maxResults, $apiKey);
if ($showApiResult) {
$debugOutput = nl2br($id . "\n\n" . print_r($jsonArray, TRUE));
}
$jsonArray['items'] = $this->youtubeService->mapArray(
$jsonArray['items'], $id, $aspectRatio, $thumbnailType
);
} catch (Exception $exception) {
return '<div style="color: red;">' . $exception->getMessage() . '</div>';
}
......@@ -61,8 +75,11 @@ class YoutubeController extends ActionController {
$this->view->assignMultiple(
[
'feed' => $jsonArray['items'],
'response' => $jsonArray
'response' => $jsonArray,
'debugOutput' => $debugOutput,
]
);
return NULL;
}
}
......@@ -29,7 +29,7 @@ use InvalidArgumentException;
***************************************************************/
/**
* Helper functions
* YouTube Helper Service
*/
class YoutubeService {
const API_URL = 'https://www.googleapis.com/youtube/v3/';
......@@ -40,100 +40,75 @@ class YoutubeService {
const API_ORDER_BY = 'date';
/**
* @param string $youtubeId
* @param string $maxResults
* @param string $apiKey
* @return string
*/
public function getApiUrl($youtubeId = '', $maxResults = '10', $apiKey = ''): string {
$apiUrl = self::API_URL;
$parameters = [];
if (strpos($youtubeId, 'UC') === 0) {
$apiUrl .= self::API_CHANNEL;
$parameters['channelId'] = $youtubeId;
} elseif (strpos($youtubeId, 'PL') === 0) {
$apiUrl .= self::API_PLAYLIST;
$parameters['playlistId'] = $youtubeId;
} else {
$apiUrl .= self::API_VIDEO;
$parameters['id'] = $this->removeIdParameters($youtubeId);
}
$parameters['order'] = self::API_ORDER_BY;
$parameters['part'] = self::API_PART;
$parameters['key'] = $apiKey;
$parameters['maxResults'] = $maxResults;
$query = http_build_query($parameters);
return $apiUrl . '?' . $query;
}
/**
* Maps the json array from the YouTube call to return some unified value. The output from YouTube is pretty
* unsteady. Also we calculate the correct thumbnail sized and so on.
*
* @param array $jsonArray
* @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)
* @return array
*/
public function mapArray($jsonArray = [], $youtubeId = ''): array {
if (strpos($youtubeId, 'UC') === 0) {
return array_map(
static function ($field) {
$previewImage =
$field['snippet']['thumbnails']['maxres'] ?? $field['snippet']['thumbnails']['standard'];
return [
'title' => $field['snippet']['title'],
'description' => strip_tags($field['snippet']['description']),
'thumbnail' => $previewImage['url'],
'url' => 'https://www.youtube.com/watch?v=' . $field['id']['videoId']
];
}, $jsonArray
);
public function mapArray(
$jsonArray = [], $youtubeId = '', $aspectRatio = '16:9', $thumbnailType = 'byAspectRatio'
): array {
if (!in_array($thumbnailType, ['maxres', 'standard', 'high', 'medium', 'default', 'byAspectRatio'])) {
$thumbnailType = 'byAspectRatio';
}
if (strpos($youtubeId, 'PL') === 0) {
return array_map(
static function ($field) {
$previewImage =
$field['snippet']['thumbnails']['maxres'] ?? $field['snippet']['thumbnails']['standard'];
return [
'title' => $field['snippet']['title'],
'description' => strip_tags($field['snippet']['description']),
'thumbnail' => $previewImage['url'],
'url' => 'https://www.youtube.com/watch?v=' . $field['snippet']['resourceId']['videoId']
];
}, $jsonArray
);
if (!in_array($aspectRatio, ['16:9', '4:3'])) {
$aspectRatio = '16:9';
}
return array_map(
static function ($field) {
$previewImage =
$field['snippet']['thumbnails']['maxres'] ?? $field['snippet']['thumbnails']['standard'];
return [
'title' => $field['snippet']['title'],
'description' => strip_tags($field['snippet']['description']),
'thumbnail' => $previewImage['url'],
'url' => 'https://www.youtube.com/watch?v=' . $field['id']
];
}, $jsonArray
);
}
/**
* Removes GET parameters following the ID
*
* @param string $youtubeId
* @return array|string
*/
public function removeIdParameters($youtubeId = '') {
if (strpos($youtubeId, '&')) {
return explode('&', $youtubeId)[0];
$result = [];
foreach ($jsonArray as $field) {
if (strpos($youtubeId, 'UC') === 0) {
$youTubeIdFromArray = $field['id']['videoId'];
// prevent the channel preview
if ($field['id']['kind'] !== 'youtube#video') {
continue;
}
} elseif (strpos($youtubeId, 'PL') === 0) {
$youTubeIdFromArray = $field['snippet']['resourceId']['videoId'];
} else {
$youTubeIdFromArray = $field['id'];
}
$previewImage = [];
$resolutionTypes = ['maxres', 'standard', 'high', 'medium', 'default'];
if ($thumbnailType !== 'byAspectRatio') {
array_unshift($resolutionTypes, $thumbnailType);
}
foreach ($resolutionTypes as $type) {
if (isset($field['snippet']['thumbnails'][$type])) {
$previewImage = $field['snippet']['thumbnails'][$type];
if ($thumbnailType === 'byAspectRatio' && isset($previewImage['height'])) {
$aspectRatioOfImage = $previewImage['width'] / $previewImage['height'];
if ($aspectRatio === '16:9' && $aspectRatioOfImage > 1.7 && $aspectRatioOfImage < 1.9) {
break;
}
if ($aspectRatio === '4:3' && $aspectRatioOfImage > 1.2 && $aspectRatioOfImage < 1.4) {
break;
}
} else {
break;
}
}
}
$result[] = [
'title' => $field['snippet']['title'],
'description' => strip_tags($field['snippet']['description']),
'thumbnail' => $previewImage['url'],
'url' => 'https://www.youtube.com/watch?v=' . $youTubeIdFromArray
];
}
return $youtubeId;
return $result;
}
/**
......@@ -174,7 +149,8 @@ class YoutubeService {
throw new InvalidArgumentException('JSON could\'t be parsed.', 403);
}
// By API documentation provided 'error' key is sent if, probably, URL can not return JSON data or Permission is denied.
// By API documentation provided 'error' key is sent if, probably,
// URL can not return JSON data or Permission is denied.
if (isset($jsonArray['error'])) {
throw new InvalidArgumentException('Message: ' . $jsonArray['error']['message'], 403);
}
......@@ -187,7 +163,52 @@ class YoutubeService {
throw new InvalidArgumentException('No items found.', 403);
}
$jsonArray['items'] = $this->mapArray($jsonArray['items'], $youtubeId);
return $jsonArray;
}
/**
* Returns the YouTube API URL
*
* @param string $youtubeId
* @param string $maxResults
* @param string $apiKey
* @return string
*/
public function getApiUrl($youtubeId = '', $maxResults = '10', $apiKey = ''): string {
$apiUrl = self::API_URL;
$parameters = [];
if (strpos($youtubeId, 'UC') === 0) {
$apiUrl .= self::API_CHANNEL;
$parameters['channelId'] = $youtubeId;
} elseif (strpos($youtubeId, 'PL') === 0) {
$apiUrl .= self::API_PLAYLIST;
$parameters['playlistId'] = $youtubeId;
} else {
$apiUrl .= self::API_VIDEO;
$parameters['id'] = $this->removeIdParameters($youtubeId);
}
$parameters['order'] = self::API_ORDER_BY;
$parameters['part'] = self::API_PART;
$parameters['key'] = $apiKey;
$parameters['maxResults'] = $maxResults;
$query = http_build_query($parameters);
return $apiUrl . '?' . $query;
}
/**
* Removes GET parameters following the ID
*
* @param string $youtubeId
* @return array|string
*/
protected function removeIdParameters($youtubeId = '') {
if (strpos($youtubeId, '&')) {
return explode('&', $youtubeId)[0];
}
return $youtubeId;
}
}
......@@ -42,6 +42,69 @@
</config>
</TCEforms>
</settings.maxResults>
<settings.showApiResult>
<TCEforms>
<exclude>0</exclude>
<label>LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.showApiResult
</label>
<config>
<type>check</type>
<default>0</default>
</config>
</TCEforms>
</settings.showApiResult>
<settings.aspectRatio>
<TCEforms>
<label>LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.aspectRatio</label>
<config>
<type>select</type>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">16:9</numIndex>
<numIndex index="1">16:9</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">4:3</numIndex>
<numIndex index="1">4:3</numIndex>
</numIndex>
</items>
</config>
</TCEforms>
</settings.aspectRatio>
<settings.thumbnailType>
<TCEforms>
<label>LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.thumbnailType</label>
<config>
<type>select</type>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.thumbnailType.byAspectRatio</numIndex>
<numIndex index="1">byAspectRatio</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.thumbnailType.maxres</numIndex>
<numIndex index="1">maxres</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.thumbnailType.standard</numIndex>
<numIndex index="1">standard</numIndex>
</numIndex>
<numIndex index="3" type="array">
<numIndex index="0">LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.thumbnailType.high</numIndex>
<numIndex index="1">high</numIndex>
</numIndex>
<numIndex index="4" type="array">
<numIndex index="0">LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.thumbnailType.medium</numIndex>
<numIndex index="1">medium</numIndex>
</numIndex>
<numIndex index="5" type="array">
<numIndex index="0">LLL:EXT:sg_youtube/Resources/Private/Language/locallang.xlf:flexform.thumbnailType.default</numIndex>
<numIndex index="1">default</numIndex>
</numIndex>
</items>
</config>
</TCEforms>
</settings.thumbnailType>
</el>
</ROOT>
</sDEF>
......
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.0">
<file source-language="en" target-language="de" datatype="plaintext" original="messages" date="2016-02-28T23:20:19Z">
<file source-language="en" target-language="de" datatype="plaintext" original="messages" date="2019-12-07T19:09:30Z">
<header>
<generator>LFEditor</generator>
</header>
<body>
<trans-unit id="flexform.aspectRatio" approved="yes">
<source><![CDATA[Aspect Ratio (only used if thumbnail type is set to check that)]]></source>
<target><![CDATA[Aspect Ratio (nur genutzt, wenn der Thumbnail-Typ dies prüfen soll)]]></target>
</trans-unit>
<trans-unit id="flexform.showApiResult" approved="yes">
<source><![CDATA[Show API Result (Debug Output!)]]></source>
<target><![CDATA[Zeige API-Ergebnis an (Debug-Informationen!)]]></target>
</trans-unit>
<trans-unit id="flexform.thumbnailType" approved="yes">
<source><![CDATA[Thumbnail Type (Resolution, Not all options are available always)]]></source>
<target><![CDATA[Thumbnail-Typ (Auflösung, Nicht immer sind alle Optionen vorhanden)]]></target>
</trans-unit>
<trans-unit id="flexform.thumbnailType.byAspectRatio" approved="yes">
<source><![CDATA[By Aspect Ratio]]></source>
<target><![CDATA[Nach AspectRatio]]></target>
</trans-unit>
<trans-unit id="flexform.thumbnailType.default" approved="yes">
<source><![CDATA[Low Resolution (Default)]]></source>
<target><![CDATA[Niedrige Auflösung (Default)]]></target>
</trans-unit>
<trans-unit id="flexform.thumbnailType.high" approved="yes">
<source><![CDATA[High Resolution]]></source>
<target><![CDATA[Hohe Auflösung]]></target>
</trans-unit>
<trans-unit id="flexform.thumbnailType.maxres" approved="yes">
<source><![CDATA[Maximum Resolution]]></source>
<target><![CDATA[Maximale Auflösung]]></target>
</trans-unit>
<trans-unit id="flexform.thumbnailType.medium" approved="yes">
<source><![CDATA[Medium Resolution]]></source>
<target><![CDATA[Mittlere Auflösung]]></target>
</trans-unit>
<trans-unit id="flexform.thumbnailType.standard" approved="yes">
<source><![CDATA[Standard Resolution]]></source>
<target><![CDATA[Standard-Auflösung]]></target>
</trans-unit>
<trans-unit id="id" approved="yes">
<source>ID of Channel (UC), Playlist (PL) or Single Video (youtube.com/watch?v=%ID%)</source>
<target>ID eines Channels (UC), einer Playlist (PL) oder eines einzelnen Videos (youtube.com/watch?v=%ID%)</target>
<source><![CDATA[ID of Channel (UC), Playlist (PL) or Single Video (youtube.com/watch?v=%ID%)]]></source>
<target><![CDATA[ID eines Channels (UC), einer Playlist (PL) oder eines einzelnen Videos (youtube.com/watch?v=%ID%)]]></target>
</trans-unit>
<trans-unit id="maxResults" approved="yes">
<source>Maximum Amount</source>
<target>Maximale Anzahl</target>
<source><![CDATA[Maximum Amount]]></source>
<target><![CDATA[Maximale Anzahl]]></target>
</trans-unit>
</body>
</file>
......
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.0">
<file source-language="en" datatype="plaintext" original="messages" date="2016-02-28T23:20:19Z">
<file source-language="en" datatype="plaintext" original="messages" date="2019-12-07T19:09:30Z">
<header>
<generator>LFEditor</generator>
</header>
<body>
<trans-unit id="id">
<source>ID of Channel (UC), Playlist (PL) or Single Video (youtube.com/watch?v=%ID%)</source>
</trans-unit>
<trans-unit id="maxResults">
<source>Maximum Amount</source>
</trans-unit>
<trans-unit id="flexform.aspectRatio">
<source><![CDATA[Aspect Ratio (only used if thumbnail type is set to check that)]]></source>
</trans-unit>
<trans-unit id="flexform.showApiResult">
<source><![CDATA[Show API Result (Debug Output!)]]></source>
</trans-unit>
<trans-unit id="flexform.thumbnailType">
<source><![CDATA[Thumbnail Type (Resolution, Not all options are available always)]]></source>
</trans-unit>
<trans-unit id="flexform.thumbnailType.byAspectRatio">
<source><![CDATA[By Aspect Ratio]]></source>
</trans-unit>
<trans-unit id="flexform.thumbnailType.default">
<source><![CDATA[Low Resolution (Default)]]></source>
</trans-unit>
<trans-unit id="flexform.thumbnailType.high">
<source><![CDATA[High Resolution]]></source>
</trans-unit>
<trans-unit id="flexform.thumbnailType.maxres">
<source><![CDATA[Maximum Resolution]]></source>
</trans-unit>
<trans-unit id="flexform.thumbnailType.medium">
<source><![CDATA[Medium Resolution]]></source>
</trans-unit>
<trans-unit id="flexform.thumbnailType.standard">
<source><![CDATA[Standard Resolution]]></source>
</trans-unit>
<trans-unit id="id">
<source><![CDATA[ID of Channel (UC), Playlist (PL) or Single Video (youtube.com/watch?v=%ID%)]]></source>
</trans-unit>
<trans-unit id="maxResults">
<source><![CDATA[Maximum Amount]]></source>
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -2,6 +2,10 @@
<f:section name="main">
<ul class="sg-youtube">
<f:if condition="{debugOutput}">
{debugOutput -> f:format.raw()}
</f:if>
<f:for each="{feed}" as="feedItem" iteration="feedIterator">
<li class="sg-youtube-item-container{f:if(condition: '{feedIterator.total} < 2', then: ' sg-youtube-item-single')}">
<a class="sg-youtube-item sg-card-shadow" href="{feedItem.url}" target="_blank">
......
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