Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add service for fetching metadata from online media providers (Youtube + Vimeo) #87

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions Classes/Service/Api/ApiService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace Causal\Extractor\Service\Api;

use Causal\Extractor\Service\AbstractService;
// use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
// use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry;
use TYPO3\CMS\Core\Resource\File;
use Causal\Extractor\Resource\Event\AfterMetadataExtractedEvent;

/**
* A ApiService service implementation.
*
* @author Martin Kristensen <[email protected]> + Daniel Damm <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html
*/
class ApiService extends AbstractService
{
/** @var array */
protected $onlineExtensions = [
'youtube', // VIDEOTYPE_YOUTUBE
'vimeo', // VIDEOTYPE_VIMEO
];


/**
* Returns a list of supported file types.
*
* @return array
*/
public function getSupportedFileExtensions()
{
return array_merge(
$this->onlineExtensions
);
}


/**
* Takes a file reference and extracts its metadata.
*
* @param \TYPO3\CMS\Core\Resource\File $file Path to the file
* @return array
* @see \Causal\ImageAutoresize\Utility\ImageUtility::getMetadata()
*/
public function extractMetadataFromLocalFile($file)
{
$fileName = $file->getForLocalProcessing(false);
$extension = strtolower(substr($fileName, strrpos($fileName, '.') + 1));

try {
switch (true) {
case $extension === 'youtube':
$metadata['youtube'] = $this->extractMetadataFromYoutube($file);
break;
case $extension === 'vimeo':
$metadata['vimeo'] = $this->extractMetadataFromVimeo($file);
break;
default:
$metadata = [];
break;
}
$this->cleanupTempFile($fileName, $file);

static::getLogger()->debug('Metadata extracted', $metadata);
} catch (\Exception $e) {
static::getLogger()->error('Error while extracting metadata from file', ['exception' => $e]);
$metadata = [];
}
return $metadata;
}


/**
* Takes a file reference and extracts its metadata.
*
* @param File $file
* @return array
*/
public function extractMetadata(File $file)
{
static::getLogger()->debug(
'Extracting metadata',
[
'file' => $file->getUid(),
'identifier' => $file->getCombinedIdentifier(),
]
);
$metadata = $this->extractMetadataFromLocalFile($file);

// Emit Signal after metadata has been extracted
if ($this->eventDispatcher !== null) {
$event = new AfterMetadataExtractedEvent($file, $metadata);
$this->eventDispatcher->dispatch($event);
$metadata = $event->getMetadata();
} else {
$this->getSignalSlotDispatcher()->dispatch(
self::class,
'postMetaDataExtraction',
[
$file,
&$metadata
]
);
}

return $metadata;
}


/**
* Fetch metadata from a youtube video.
*
* @param \TYPO3\CMS\Core\Resource\File $file File object
* @return array
*/
protected function extractMetadataFromYoutube($file)
{
static::getLogger()->debug('Fetching metadata from Youtube API');
$metadata = [];

$params = array(
'part' => 'contentDetails,snippet',
'id' => $this->getMediaId($file),
'key' => $this->settings['youtube_api_key'],
);

$api_url = $this->settings['youtube_api_base'] . '?' . http_build_query($params);
$result = json_decode(@file_get_contents($api_url), true);

if(empty($result['items'][0])) {
return [];
}
$metadata = $result['items'][0];

return $metadata;
}


/**
* Fetch metadata from a Vimeo video.
*
* @param \TYPO3\CMS\Core\Resource\File $file File object
* @return array
*/
protected function extractMetadataFromVimeo($file)
{
static::getLogger()->debug('Fetching metadata from Vimeo API');
$metadata = [];

// Create a stream - setting headers for authentication
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"cache-control: no-cache\r\n" .
"authorization: Bearer ". $this->settings['vimeo_api_key'] ."\r\n"
)
);

// Build URI with Params and open the file using the HTTP headers set above
$api_url = $this->settings['vimeo_api_base'] . $this->getMediaId($file);
$result = json_decode(@file_get_contents($api_url, false, stream_context_create($opts)), true);

if(empty($result)) {
return [];
}
$metadata = $result;

return $metadata;
}


/**
* Fetch online media id from file.
*
* @param \TYPO3\CMS\Core\Resource\File $file File object
* @return string
*/
protected function getMediaId($file)
{
$onlineMediaHelper = OnlineMediaHelperRegistry::getInstance()->getOnlineMediaHelper($file);
$mediaId = $onlineMediaHelper->getOnlineMediaId($file);

return $mediaId;
}
}
113 changes: 113 additions & 0 deletions Classes/Service/Extraction/ApiMetadataExtraction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace Causal\Extractor\Service\Extraction;

use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
* A service to extract metadata from files using API
*
* @author Martin Kristensen <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html
*/
class ApiMetadataExtraction extends AbstractExtractionService
{
/**
* @var integer
*/
protected $priority = 50;

/**
* @var string
*/
protected $serviceName = 'Api';

/**
* ApiMetadataExtraction constructor.
*/
public function __construct()
{
parent::__construct();

$apiService = $this->getApiService();
$this->supportedFileExtensions = $apiService->getSupportedFileExtensions();
}

/**
* Checks if the given file can be processed by this extractor.
*
* @param File $file
* @return boolean
*/
protected function _canProcess(File $file): bool
{
$fileExtension = strtolower($file->getProperty('extension'));
return in_array($fileExtension, $this->supportedFileExtensions);
}

/**
* The actual processing task.
*
* Should return an array with database properties for sys_file_metadata to write.
*
* @param File $file
* @param array $previousExtractedData optional, contains the array of already extracted data
* @return array
*/
public function extractMetaData(File $file, array $previousExtractedData = [])
{
$metadata = [];

$extractedMetadata = $this->getApiService()->extractMetadata($file);

if (!empty($extractedMetadata)) {
$dataMapping = $this->getDataMapping($file);
$metadata = $this->remapServiceOutput($extractedMetadata, $dataMapping);
$this->processCategories($file, $metadata);
}

return $metadata;
}

/**
* Returns a API service.
*
* @return \Causal\Extractor\Service\Api\ApiService
*/
protected function getApiService()
{
/** @var \Causal\Extractor\Service\Api\ApiService $apiService */
static $apiService = null;

if ($apiService === null) {
$apiService = GeneralUtility::makeInstance(\Causal\Extractor\Service\Api\ApiService::class);
}

return $apiService;
}

/**
* Returns a logger.
*
* @return \TYPO3\CMS\Core\Log\Logger
*/
protected static function getLogger()
{
/** @var \TYPO3\CMS\Core\Log\Logger $logger */
$logger = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
return $logger;
}
}
3 changes: 3 additions & 0 deletions Classes/Utility/ExtensionHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ public static function getExtensionCategory(string $extension): string
case 'wmv':
case 'yuv':
return 'video';
case 'youtube':
case 'vimeo':
return 'onlinemedia';
}

return 'other';
Expand Down
37 changes: 37 additions & 0 deletions Classes/Utility/YoutubeTime.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace Causal\Extractor\Utility;

/**
* Youtube time utility class.
*
* @author Daniel Alexander Damm <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html
*/
class YoutubeTime
{
/**
* Convert Youtube time
*
* @param string $youtubeTime
* @return int
*/
public static function toSeconds($youtubeTime) : int
{
$start = new \DateTime('@0'); // Unix epoch
$start->add(new \DateInterval($youtubeTime));
return (int)$start->format('U');
}
}
22 changes: 22 additions & 0 deletions Configuration/Services/Api/onlinemedia.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"FAL": "duration",
"DATA": [
"youtube|contentDetails|duration->Causal\\Extractor\\Utility\\YoutubeTime::toSeconds",
"vimeo|duration->Causal\\Extractor\\Utility\\Number::castInteger"
]
},
{
"FAL": "description",
"DATA": [
"youtube|snippet|description->Causal\\Extractor\\Utility\\SimpleString::trim",
"vimeo|description->Causal\\Extractor\\Utility\\SimpleString::trim"
]
},
{
"FAL": "publisher",
"DATA": [
"vimeo|user|name->Causal\\Extractor\\Utility\\SimpleString::trim"
]
}
]
Loading