Commit 1a4be063 authored by Stefan Galinski's avatar Stefan Galinski 🎮
Browse files

[FEATURE] Properly cache the youtube thumbnail and deliver that as a picture...

[FEATURE] Properly cache the youtube thumbnail and deliver that as a picture tag (heavy performance improvement)
parent 8b0774c8
......@@ -21,6 +21,7 @@ namespace SGalinski\SgYoutube\Controller;
***************************************************************/
use Exception;
use SGalinski\SgYoutube\Service\CachedImageService;
use SGalinski\SgYoutube\Service\YoutubeService;
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\FileReference;
......@@ -78,6 +79,13 @@ class YoutubeController extends ActionController {
return '<div style="color: red;">' . $exception->getMessage() . '</div>';
}
$cachedImageService = GeneralUtility::makeInstance(CachedImageService::class, 'youtube');
foreach ($jsonArray['items'] as $key => $item) {
if (isset($item['thumbnail'])) {
$jsonArray['items'][$key]['thumbnail'] = $cachedImageService->getImage($item['thumbnail']);
}
}
$this->view->assignMultiple(
[
'feed' => $jsonArray['items'],
......@@ -125,7 +133,7 @@ class YoutubeController extends ActionController {
$cropVariantCollection = CropVariantCollection::create((string) $cropString);
$cropArea = $cropVariantCollection->getCropArea('default');
$processingInstructions = [
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($fileObject),
'crop' => $cropArea->isEmpty() ? NULL : $cropArea->makeAbsoluteBasedOnFile($fileObject),
];
$imageService = GeneralUtility::makeInstance(ImageService::class);
$processedImage = $imageService->applyProcessingInstructions($fileObject, $processingInstructions);
......
<?php
/**
*
* Copyright notice
*
* (c) sgalinski Internet Services (https://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project 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.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script 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.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
namespace SGalinski\SgYoutube\Service;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/***************************************************************
* Copyright notice
*
* (c) sgalinski Internet Services (https://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project 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.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script 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.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
* Handles the caching of images that are hosted externally
*
* NOTE: PURE COPY FROM PROJECT_BASE
*/
class CachedImageService {
public const CACHED_IMAGES_DIRECTORY = 'fileadmin/Cache/';
/**
* @var string
*/
protected $cacheDirectory = '';
/**
* Creates a new instance that caches images in the $cacheDirectory sub-folder
*
* CachedImageService constructor.
*
* @param string $cacheDirectory
*/
public function __construct($cacheDirectory) {
$this->cacheDirectory = ltrim($cacheDirectory, '/');
}
/**
* Returns the currently set subdirectory of the cache folder this service should write to
*
* @return string
*/
public function getCacheDirectory(): string {
return $this->cacheDirectory;
}
/**
* Sets the subdirectory of the cache folder this service should write to
*
* @param string $cacheDirectory
* @return CachedImageService
*/
public function setCacheDirectory($cacheDirectory): CachedImageService {
$this->cacheDirectory = $cacheDirectory;
return $this;
}
/**
* Takes a URL to an image and returns the locally cached counterpart
* If the image is not yet in the cache, it will be downloaded.
*
* @param string $url
* @return string
*/
public function getImage($url): string {
$cachedImageFolderPath = GeneralUtility::getFileAbsFileName(
self::CACHED_IMAGES_DIRECTORY . $this->cacheDirectory . '/'
);
// Create a hash based on the URL to identify the image
$imageHash = \md5($url);
// check if any kind of image with this hash is already in the cache-directory
$cachedImage = \glob($cachedImageFolderPath . $imageHash . '.*');
if (count($cachedImage) <= 0) {
// if the cache-directory has not been created yet, do so
GeneralUtility::mkdir_deep($cachedImageFolderPath);
// copy without file extension for now
\copy($url, $cachedImageFolderPath . $imageHash);
$imageType = \exif_imagetype($cachedImageFolderPath . $imageHash);
// figure out the file extension based on the image's mime-type
$cachedFileName = $imageHash . \image_type_to_extension($imageType);
$cachedFilePath = $cachedImageFolderPath . $cachedFileName;
// correct name
\rename($cachedImageFolderPath . $imageHash, $cachedFilePath);
GeneralUtility::fixPermissions($cachedFilePath);
$cachedFileWebPath = PathUtility::getAbsoluteWebPath($cachedFilePath);
} else {
$cachedFileWebPath = PathUtility::getAbsoluteWebPath($cachedImage[0]);
}
return $cachedFileWebPath;
}
}
......@@ -65,7 +65,13 @@
<a class="sg-youtube-item sg-card-shadow" href="{feedItem.url}&list={settings.id}" data-disable-lightbox="{settings.disableLightbox}" data-disable-lightbox-mobile="{settings.disableLightboxMobile}">
<f:if condition="{feedItem.thumbnail}">
<div class="sg-youtube-image">
<img src="{feedItem.thumbnail}" alt="{feedItem.title}" loading="lazy" />
<picture>
<source media="(max-width: 600px)"
srcset="{f:uri.image(src: '{feedItem.thumbnail}', width: '600c')}">
<source media="(max-width: 992px)"
srcset="{f:uri.image(src: '{feedItem.thumbnail}', width: '992c')}">
<f:image src="{feedItem.thumbnail}" alt="{feedItem.title}" class="sg-video__image" width="1140" loading="lazy"/>
</picture>
</div>
</f:if>
<f:if condition="{showTitle} && {feedItem.title}">
......
......@@ -44,7 +44,11 @@ export default class SgYoutubeLightbox {
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
iframe.src = `https://www.youtube-nocookie.com/embed/${videoId}`;
item.replaceChild(iframe, videoImage);
if (videoImage.parentElement.nodeName.toLowerCase() === 'picture') {
item.replaceChild(iframe, videoImage.parentElement);
} else {
item.replaceChild(iframe, videoImage);
}
}
/**
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment