This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Exception;
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Exception;
class FileNotFoundException extends \Exception implements ExceptionInterface
{
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus;
use MediaVorus\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\File as SymfonyFile;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException as SFFileNotFoundException;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class File extends SymfonyFile
{
public function __construct($path)
{
try {
parent::__construct($path, true);
} catch (SFFileNotFoundException $e) {
throw new FileNotFoundException(sprintf('File %s not found', $path));
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Filter;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
interface FilterInterface
{
/**
* Return a \Closure with {$key, Media $media} parameters and returns boolean
*/
public function apply();
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Filter;
use MediaVorus\Media\MediaInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class MediaType implements FilterInterface
{
protected $type;
/**
* Filter on Media Type
*
* @param string $type One of the \MediaVorus\Media\Media::TYPE_* constants
*/
public function __construct($type)
{
$this->type = $type;
}
/**
* {@inheritdoc}
*/
public function apply()
{
$type = $this->type;
return function($key, MediaInterface $media) use ($type) {
return $media->getType() === $type;
};
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Media;
use JMS\Serializer\Annotation\VirtualProperty;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class Audio extends DefaultMedia
{
/**
* @VirtualProperty
*
* @return string
*/
public function getType()
{
return self::TYPE_AUDIO;
}
/**
* Get the duration of the audio in seconds, null if unavailable
*
* @VirtualProperty
*
* @return float
*/
public function getDuration()
{
$sources = array('Composite:Duration');
if (null !== $value = $this->findInSources($sources)) {
return (float) $value;
}
return null;
}
}

View File

@@ -0,0 +1,301 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Media;
use FFMpeg\FFProbe;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\VirtualProperty;
use MediaVorus\File;
use MediaVorus\Exception\InvalidArgumentException;
use PHPExiftool\Driver\Metadata\MetadataBag;
use PHPExiftool\Exception\ExceptionInterface as PHPExiftoolExceptionInterface;
use PHPExiftool\Writer;
use PHPExiftool\FileEntity;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*
* @ExclusionPolicy("all")
* @todo declarations of custom filters/getters
*/
class DefaultMedia implements MediaInterface
{
const GPSREF_LONGITUDE_WEST = 'W';
const GPSREF_LONGITUDE_EAST = 'E';
const GPSREF_LATITUDE_NORTH = 'N';
const GPSREF_LATITUDE_SOUTH = 'S';
/**
* @var File
*/
protected $file;
/**
* @var FileEntity
*/
protected $entity;
/**
* @var Writer
*/
protected $writer;
/**
* @var type
*/
protected $temporaryFiles = array();
/**
* @var FFProbe
*/
protected $ffprobe;
/**
* Constructor for Medias
*
* @param File $file
* @param FileEntity $entity
* @return MediaInterface
*/
public function __construct(File $file, FileEntity $entity, Writer $writer, FFProbe $ffprobe = null)
{
$this->file = $file;
$this->entity = $entity;
$this->writer = $writer;
$this->ffprobe = $ffprobe;
return $this;
}
/**
* Destructor
*/
public function __destruct()
{
foreach ($this->temporaryFiles as $file) {
$this->removeTemporaryFile($file);
}
$this->file = $this->entity = $this->temporaryFiles = null;
}
/**
* Return the hash of the empty file (without metadatas)
*
* @param string $algo The algorithm to use, available ones are returned by hash_algos()
* @return string The computed hash
*
* @throws InvalidArgumentException If the algorithm is not supported
*/
public function getHash($algo)
{
if ( ! in_array($algo, hash_algos())) {
throw new InvalidArgumentException(sprintf('Hash %s not supported', $algo));
}
$tmpFile = $this->getTemporaryEmptyFile();
$hash = hash_file($algo, $tmpFile);
$this->removeTemporaryFile($tmpFile);
return $hash;
}
/**
* @VirtualProperty
*
* @return string
*/
public function getType()
{
return 'DefaultMedia';
}
/**
*
* @return \MediaVorus\File
*/
public function getFile()
{
return $this->file;
}
/**
* Get Longitude value
*
* @VirtualProperty
*
* @return float
*/
public function getLongitude()
{
if ($this->getMetadatas()->containsKey('Composite:GPSLongitude')) {
return (float) $this->getMetadatas()->get('Composite:GPSLongitude')->getValue()->asString();
}
return null;
}
/**
* Get Longitude Reference value, one of the GPSREF_LONGITUDE_*
*
* @VirtualProperty
*
* @return string|null
*/
public function getLongitudeRef()
{
if ($this->getMetadatas()->containsKey('Composite:GPSLongitudeRef')) {
switch (strtolower($this->getMetadatas()->get('Composite:GPSLongitudeRef')->getValue()->asString())) {
case 'w':
return self::GPSREF_LONGITUDE_WEST;
break;
case 'e':
return self::GPSREF_LONGITUDE_EAST;
break;
}
}
return null;
}
/**
* Get Latitude value
*
* @VirtualProperty
*
* @return float
*/
public function getLatitude()
{
if ($this->getMetadatas()->containsKey('Composite:GPSLatitude')) {
return (float) $this->getMetadatas()->get('Composite:GPSLatitude')->getValue()->asString();
}
return null;
}
/**
* Get Latitude Reference value, one of the GPSREF_LATITUDE_*
*
* @VirtualProperty
*
* @return string|null
*/
public function getLatitudeRef()
{
if ($this->getMetadatas()->containsKey('Composite:GPSLatitudeRef')) {
switch (strtolower($this->getMetadatas()->get('Composite:GPSLatitudeRef')->getValue()->asString())) {
case 'n':
return self::GPSREF_LATITUDE_NORTH;
break;
case 's':
return self::GPSREF_LATITUDE_SOUTH;
break;
}
}
return null;
}
/**
*
* @return MetadataBag
*/
public function getMetadatas()
{
return $this->entity->getMetadatas();
}
protected function findInSources(Array $sources)
{
foreach ($sources as $source) {
if ($this->getMetadatas()->containsKey($source)) {
return $this->getMetadatas()->get($source)->getValue()->asString();
}
}
return null;
}
protected function castValue($value, $type)
{
if (is_null($value)) {
return null;
}
switch ($type) {
case 'int':
case 'integer':
return (int) $value;
break;
case 'float':
return (float) $value;
break;
case 'string':
return (string) $value;
break;
default:
return $value;
break;
}
}
/**
* Generates a metadatas-free version of the file in the temporary directory
*
* @return string the path file to the temporary file
*/
private function getTemporaryEmptyFile()
{
$tmpFile = tempnam(sys_get_temp_dir(), 'hash');
unlink($tmpFile);
try {
$this->writer->reset();
$this->writer->erase(true);
$this->writer->write($this->file->getPathname(), new MetadataBag(), $tmpFile);
} catch (PHPExiftoolExceptionInterface $e) {
/**
* Some files can not be written by exiftool
*/
copy($this->file->getPathname(), $tmpFile);
}
$this->temporaryFiles[] = $tmpFile;
return $tmpFile;
}
/**
* Remove a file generated by ::getTemporaryEmptyFile
*
* @param String $pathfile The path to the file
* @return DefaultMedia
*/
private function removeTemporaryFile($pathfile)
{
if (false !== $offset = array_search($pathfile, $this->temporaryFiles, true)) {
$file = $this->temporaryFiles[$offset];
if (file_exists($file) && is_writable($file)) {
unlink($file);
array_splice($this->temporaryFiles, $offset, 1);
}
}
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Media;
class Document extends Image
{
/**
*
* @return string
*/
public function getType()
{
return self::TYPE_DOCUMENT;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Media;
class Flash extends Image
{
/**
*
* @return string
*/
public function getType()
{
return self::TYPE_FLASH;
}
}

View File

@@ -0,0 +1,420 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Media;
use JMS\Serializer\Annotation\SerializedName;
use JMS\Serializer\Annotation\VirtualProperty;
use MediaVorus\Utils\RawImageMimeTypeGuesser;
/**
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*
* @todo refactor Meta resolver to an independant object
*/
class Image extends DefaultMedia
{
/**
* Orientation constant Horizontal (normal)
*/
const ORIENTATION_0 = 0;
/**
* Orientation constant Vertical (90 CW)
*/
const ORIENTATION_90 = 90;
/**
* Orientation constant Vertical (270 CW)
*/
const ORIENTATION_270 = 270;
/**
* Orientation constant Horizontal (reversed)
*/
const ORIENTATION_180 = 180;
/**
* Colorspace constant CMYK
*/
const COLORSPACE_CMYK = 'CMYK';
/**
* Colorspace constant RGB
*/
const COLORSPACE_RGB = 'RGB';
/**
* Colorspace constant sRGB
*/
const COLORSPACE_SRGB = 'sRGB';
/**
* Colorspace constant Grayscale
*/
const COLORSPACE_GRAYSCALE = 'Grayscale';
/**
* Colorspace constant RGBA
*/
const COLORSPACE_RGBA = 'RGBA';
/**
* @VirtualProperty
*
* @return string
*/
public function getType()
{
return self::TYPE_IMAGE;
}
/**
* Returns true if the document is a "Raw" image
*
* @VirtualProperty
* @SerializedName("raw_image")
*
* @return boolean
*/
public function isRawImage()
{
return in_array($this->getFile()->getMimeType(), RawImageMimeTypeGuesser::$rawMimeTypes);
}
/**
* Returns true if the document has multiple layers.
* This method is supposed to be used to extract layer 0 with ImageMagick
*
* @VirtualProperty
* @SerializedName("multiple_layers")
*
* @return type
*/
public function hasMultipleLayers()
{
return in_array($this->getFile()->getMimeType(), array(
'image/tiff',
'application/pdf',
'image/psd',
'image/vnd.adobe.photoshop',
'image/photoshop',
'image/ai',
'image/illustrator',
'image/vnd.adobe.illustrator'
));
}
/**
* Return the width, null on error
*
* @VirtualProperty
*
* @return int
*/
public function getWidth()
{
if ($this->getMetadatas()->containsKey('File:ImageWidth')) {
return (int) $this->getMetadatas()->get('File:ImageWidth')->getValue()->asString();
}
if ($this->getMetadatas()->containsKey('Composite:ImageSize')) {
$dimensions = $this->extractFromDimensions(
$this->getMetadatas()->get('Composite:ImageSize')->getValue()->asString()
);
if ($dimensions) {
return (int) $dimensions['width'];
}
}
$sources = array('SubIFD:ImageWidth', 'IFD0:ImageWidth', 'ExifIFD:ExifImageWidth');
return $this->castValue($this->findInSources($sources), 'int');
}
/**
* Return the height, null on error
*
* @VirtualProperty
*
* @return int
*/
public function getHeight()
{
if ($this->getMetadatas()->containsKey('File:ImageHeight')) {
return (int) $this->getMetadatas()->get('File:ImageHeight')->getValue()->asString();
}
if ($this->getMetadatas()->containsKey('Composite:ImageSize')) {
$dimensions = $this->extractFromDimensions(
$this->getMetadatas()->get('Composite:ImageSize')->getValue()->asString()
);
if ($dimensions) {
return (int) $dimensions['height'];
}
}
$sources = array('SubIFD:ImageHeight', 'IFD0:ImageHeight', 'ExifIFD:ExifImageHeight');
return $this->castValue($this->findInSources($sources), 'int');
}
/**
* Return the number of channels (samples per pixel), null on error
*
* @VirtualProperty
*
* @return int
*/
public function getChannels()
{
$sources = array('File:ColorComponents', 'IFD0:SamplesPerPixel');
return $this->castValue($this->findInSources($sources), 'int');
}
/**
* Return the focal length used by the camera in mm, null on error
*
* @VirtualProperty
*
* @return float
*/
public function getFocalLength()
{
$sources = array('ExifIFD:FocalLength', 'XMP-exif:FocalLength');
return $this->castValue($this->findInSources($sources), 'float');
}
/**
* Return the color depth (bits per sample), null on error
*
* @VirtualProperty
*
* @return int
*/
public function getColorDepth()
{
$sources = array('File:BitsPerSample', 'IFD0:BitsPerSample');
return $this->castValue($this->findInSources($sources), 'int');
}
/**
* Return the camera model, null on error
*
* @VirtualProperty
*
* @return string
*/
public function getCameraModel()
{
$sources = array('IFD0:Model', 'IFD0:UniqueCameraModel');
return $this->findInSources($sources);
}
/**
* Return true if the Flash has been fired, false if it has not been
* fired, null if does not know
*
* @VirtualProperty
*
* @return boolean
*/
public function getFlashFired()
{
if (null !== $value = $this->findInSources(array('ExifIFD:Flash', 'Composite:Flash'))) {
switch ($value % 2) {
case 0: // not triggered
return false;
break;
case 1: // triggered
return true;
break;
}
}
return null;
}
/**
* Get Aperture value
*
* @VirtualProperty
*
* @return float
*/
public function getAperture()
{
return $this->castValue($this->findInSources(array('Composite:Aperture')), 'float');
}
/**
* Get ShutterSpeed value in seconds
*
* @VirtualProperty
*
* @return float
*/
public function getShutterSpeed()
{
return $this->castValue($this->findInSources(array('Composite:ShutterSpeed')), 'float');
}
/**
* Returns one one the ORIENTATION_* constants, the degrees value of Orientation
*
* @VirtualProperty
*
* @return int
*/
public function getOrientation()
{
switch ($this->findInSources(array('IFD0:Orientation'))) {
case 6:
return self::ORIENTATION_90;
break;
case 8:
return self::ORIENTATION_270;
break;
case 1:
return self::ORIENTATION_0;
break;
case 3:
return self::ORIENTATION_180;
break;
}
return null;
}
/**
* Returns the Creation Date
*
* @todo rename in getDateTaken to avoid conflicts with the original file
* properties, return a DateTime object
*
* @VirtualProperty
*
* @return string
*/
public function getCreationDate()
{
$sources = array('IPTC:DateCreated', 'ExifIFD:DateTimeOriginal');
return $this->findInSources($sources);
}
/**
* Return the Hyperfocal Distance in meters
*
* @VirtualProperty
*
* @return float
*/
public function getHyperfocalDistance()
{
return $this->castValue($this->findInSources(array('Composite:HyperfocalDistance')), 'float');
}
/**
* Return the ISO value
*
* @VirtualProperty
* @SerializedName("ISO")
*
* @return int
*/
public function getISO()
{
$sources = array('ExifIFD:ISO', 'IFD0:ISO');
return $this->castValue($this->findInSources($sources), 'int');
}
/**
* Return the Light Value
*
* @VirtualProperty
*
* @return float
*/
public function getLightValue()
{
return $this->castValue($this->findInSources(array('Composite:LightValue')), 'float');
}
/**
* Returns the colorspace as one of the COLORSPACE_* constants
*
* @VirtualProperty
*
* @return string
*/
public function getColorSpace()
{
$regexp = '/.*:(colorspace|colormode|colorspacedata|colortype)/i';
foreach ($this->getMetadatas()->filterKeysByRegExp($regexp) as $meta) {
switch (strtolower(trim($meta->getValue()->asString()))) {
case 'cmyk':
return self::COLORSPACE_CMYK;
break;
case 'srgb':
return self::COLORSPACE_SRGB;
break;
case 'rgb':
case '2':
return self::COLORSPACE_RGB;
break;
case 'grayscale':
case '0':
return self::COLORSPACE_GRAYSCALE;
break;
case 'rgba':
case '6':
return self::COLORSPACE_RGBA;
break;
}
}
switch ($this->findInSources(array('File:ColorComponents'))) {
case 1:
return self::COLORSPACE_GRAYSCALE;
break;
case 3:
return self::COLORSPACE_RGB;
break;
case 4:
return self::COLORSPACE_CMYK;
break;
}
return null;
}
/**
* Extract the width and height from a widthXheight serialized value
* Returns an array with width and height keys, null on error
*
* @param type $WidthXHeight
* @return array
*/
protected function extractFromDimensions($WidthXHeight)
{
preg_match("/(\d+)\D+(\d+)/", $WidthXHeight, $values);
if (count($values) === 3 && ctype_digit($values[1]) && ctype_digit($values[2])) {
return array('width' => $values[1], 'height' => $values[2]);
}
return null;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Media;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
interface MediaInterface
{
const TYPE_AUDIO = 'Audio';
const TYPE_IMAGE = 'Image';
const TYPE_VIDEO = 'Video';
const TYPE_FLASH = 'Flash';
const TYPE_DOCUMENT = 'Document';
/**
* Return the hash of the `nude` file ; the file is first empty of all its
* metadatas, then the hash is computed.
*
* @see hash_algos()
*
* @param type $algo A valid hash algorithm, see hash_algos function for valid hash names
*
* @return string
*/
public function getHash($algo);
/**
* Return the type
*
* @return string
*/
public function getType();
public function getFile();
public function getMetadatas();
public function getLongitude();
public function getLongitudeRef();
public function getLatitude();
public function getLatitudeRef();
}

View File

@@ -0,0 +1,270 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Media;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\VirtualProperty;
use JMS\Serializer\Annotation\Exclude;
use FFMpeg\Exception\ExceptionInterface as FFMpegException;
use FFMpeg\FFProbe;
use MediaVorus\File;
use PHPExiftool\Writer;
use PHPExiftool\FileEntity;
/**
* @ExclusionPolicy("all")
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class Video extends Image
{
/**
* @VirtualProperty
*
* @return string
*/
public function getType()
{
return self::TYPE_VIDEO;
}
/**
* @VirtualProperty
*
* @return Integer
*/
public function getWidth()
{
$width = parent::getWidth();
if (null === $this->ffprobe) {
return $width;
}
try {
$video = $this->ffprobe
->streams($this->file->getPathname())
->videos()
->first();
return $video->getDimensions()->getWidth();
} catch (FFMpegException $e) {
}
return $width;
}
/**
* @VirtualProperty
*
* @return Integer
*/
public function getHeight()
{
$height = parent::getHeight();
if (null === $this->ffprobe) {
return $height;
}
try {
$video = $this->ffprobe
->streams($this->file->getPathname())
->videos()
->first();
return $video->getDimensions()->getHeight();
} catch (FFMpegException $e) {
}
return $height;
}
/**
* Returns one one the ORIENTATION_* constants, the degrees value of Orientation
*
* @VirtualProperty
*
* @return int
*/
public function getOrientation()
{
switch ($this->findInSources(array('Composite:Rotation'))) {
case 90:
return self::ORIENTATION_90;
break;
case 270:
return self::ORIENTATION_270;
break;
case 0:
return self::ORIENTATION_0;
break;
case 180:
return self::ORIENTATION_180;
break;
}
return null;
}
/**
* Get the duration of the video in seconds, null if unavailable
*
* @VirtualProperty
*
* @return float
*/
public function getDuration()
{
$sources = array('Composite:Duration', 'Flash:Duration', 'QuickTime:Duration', 'Real-PROP:Duration');
if (null !== $value = $this->findInSources($sources)) {
return $this->castValue($value, 'float');
}
if (null === $this->ffprobe) {
return null;
}
$format = $this->ffprobe->format($this->file->getPathname());
if ($format->has('duration')) {
return $this->castValue($format->get('duration'), 'float');
}
return null;
}
/**
* Returns the value of video frame rate, null if not available
*
* @VirtualProperty
*
* @return string
*/
public function getFrameRate()
{
$sources = array('RIFF:FrameRate', 'RIFF:VideoFrameRate', 'Flash:FrameRate');
if (null !== $value = $this->findInSources($sources)) {
return $this->castValue($value, 'float');
}
if (null !== $value = $this->entity->executeQuery('Track1:VideoFrameRate')) {
return $this->castValue($value->asString(), 'float');
}
if (null !== $value = $this->entity->executeQuery('Track2:VideoFrameRate')) {
return $this->castValue($value->asString(), 'float');
}
return null;
}
/**
* Returns the value of audio samplerate, null if not available
*
* @VirtualProperty
*
* @return string
*/
public function getAudioSampleRate()
{
$sources = array('RIFF:AudioSampleRate', 'Flash:AudioSampleRate');
if (null !== $value = $this->findInSources($sources)) {
return $this->castValue($value, 'int');
}
if (null !== $value = $this->entity->executeQuery('Track1:AudioSampleRate')) {
return $this->castValue($value->asString(), 'int');
}
if (null !== $value = $this->entity->executeQuery('Track2:AudioSampleRate')) {
return $this->castValue($value->asString(), 'int');
}
return null;
}
/**
* Returns the name of video codec, null if not available
*
* @VirtualProperty
*
* @return string
*/
public function getVideoCodec()
{
$sources = array('RIFF:AudioSampleRate', 'Flash:VideoEncoding');
if (null !== $value = $this->findInSources($sources)) {
return $this->castValue($value, 'string');
}
if (null !== $value = $this->entity->executeQuery('QuickTime:ComAppleProappsOriginalFormat')) {
return $this->castValue($value->asString(), 'string');
}
if (null !== $value = $this->entity->executeQuery('Track1:CompressorName')) {
return $this->castValue($value->asString(), 'string');
}
if (null !== $value = $this->entity->executeQuery('Track2:CompressorName')) {
return $this->castValue($value->asString(), 'string');
}
if (null !== $value = $this->entity->executeQuery('Track1:CompressorID')) {
return $this->castValue($value->asString(), 'string');
}
if (null !== $value = $this->entity->executeQuery('Track2:CompressorID')) {
return $this->castValue($value->asString(), 'string');
}
return null;
}
/**
* Returns the name of audio codec, null if not available
*
* @VirtualProperty
*
* @return string
*/
public function getAudioCodec()
{
if ($this->getMetadatas()->containsKey('RIFF:AudioCodec')
&& $this->getMetadatas()->containsKey('RIFF:Encoding')
&& $this->getMetadatas()->get('RIFF:AudioCodec')->getValue()->asString() === '') {
return $this->getMetadatas()->get('RIFF:Encoding')->getValue()->asString();
}
if (null !== $value = $this->findInSources(array('Flash:AudioEncoding'))) {
return $this->castValue($value, 'string');
}
if (null !== $VideoCodec = $this->entity->executeQuery('Track1:AudioFormat')) {
return $this->castValue($VideoCodec->asString(), 'string');
}
if (null !== $VideoCodec = $this->entity->executeQuery('Track2:AudioFormat')) {
return $this->castValue($VideoCodec->asString(), 'string');
}
return null;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus;
use Doctrine\Common\Collections\ArrayCollection;
use MediaVorus\Filter\FilterInterface;
class MediaCollection extends ArrayCollection
{
/**
* Filters a MediaCollection with Filters
*
* @param FilterInterface $filter
* @param Boolean $invert_match
* @return type
*/
public function match(FilterInterface $filter, $invert_match = false)
{
list($with, $without) = $this->partition($filter->apply());
return $invert_match ? $without : $with;
}
}

View File

@@ -0,0 +1,192 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus;
use FFMpeg\FFProbe;
use MediaVorus\MediaCollection;
use MediaVorus\Exception\FileNotFoundException;
use MediaVorus\Media\MediaInterface;
use Monolog\Logger;
use Monolog\Handler\NullHandler;
use PHPExiftool\Reader;
use PHPExiftool\Writer;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class MediaVorus
{
private $reader;
private $writer;
private $ffprobe;
public function __construct(Reader $reader, Writer $writer, FFProbe $ffprobe = null)
{
$this->reader = $reader;
$this->writer = $writer;
$this->ffprobe = $ffprobe;
}
/**
* @return Reader
*/
public function getReader()
{
return $this->reader;
}
/**
* @return Writer
*/
public function getWriter()
{
return $this->writer;
}
/**
* @return FFProbe
*/
public function getFFProbe()
{
return $this->ffprobe;
}
/**
* Build a Media Object given a file
*
* @param string $file
* @return MediaInterface
* @throws FileNotFoundException
*/
public function guess($file)
{
$fileObj = new File($file);
$classname = $this->guessFromMimeType($fileObj->getMimeType());
return new $classname($fileObj, $this->reader->reset()->files($file)->first(), $this->writer, $this->ffprobe);
}
/**
*
* @param \SplFileInfo $dir
* @param type $recursive
*
* @return MediaCollection
*/
public function inspectDirectory($dir, $recursive = false)
{
$this->reader
->reset()
->in($dir)
->followSymLinks();
if ( ! $recursive) {
$this->reader->notRecursive();
}
$files = new MediaCollection();
foreach ($this->reader as $entity) {
$file = new File($entity->getFile());
$classname = $this->guessFromMimeType($file->getMimeType());
$files[] = new $classname($file, $entity, $this->writer, $this->ffprobe);
}
return $files;
}
/**
* Create MediaVorus
*
* @return MediaVorus
*/
public static function create()
{
$logger = new Logger('MediaVorus');
$logger->pushHandler(new NullHandler());
return new static(Reader::create($logger), Writer::create($logger), FFProbe::create(array(), $logger));
}
/**
* Return the corresponding \MediaVorus\Media\* class corresponding to a
* mimetype
*
* @param string $mime
* @return string The name of the MediaType class to use
*/
protected function guessFromMimeType($mime)
{
$mime = strtolower($mime);
switch (true) {
case strpos($mime, 'image/') === 0:
case $mime === 'application/postscript':
case $mime === 'application/illustrator':
return 'MediaVorus\Media\Image';
break;
case strpos($mime, 'video/') === 0:
case $mime === 'application/vnd.rn-realmedia':
case $mime === 'application/mxf':
return 'MediaVorus\Media\Video';
break;
case strpos($mime, 'audio/') === 0:
return 'MediaVorus\Media\Audio';
break;
/**
* @todo Implements Documents
*/
case strpos($mime, 'text/') === 0:
case $mime === 'application/msword':
case $mime === 'application/access':
case $mime === 'application/pdf':
case $mime === 'application/excel':
case $mime === 'application/powerpoint':
case $mime === 'application/vnd.ms-powerpoint':
case $mime === 'application/vnd.ms-excel':
case $mime === 'application/vnd.ms-office':
case $mime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
case $mime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.template':
case $mime === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
case $mime === 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
case $mime === 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
case $mime === 'application/vnd.openxmlformats-officedocument.presentationml.template':
case $mime === 'application/vnd.openxmlformats-officedocument.presentationml.slideshow':
case $mime === 'application/vnd.oasis.opendocument.formula':
case $mime === 'application/vnd.oasis.opendocument.text-master':
case $mime === 'application/vnd.oasis.opendocument.database':
case $mime === 'application/vnd.oasis.opendocument.formula':
case $mime === 'application/vnd.oasis.opendocument.chart':
case $mime === 'application/vnd.oasis.opendocument.graphics':
case $mime === 'application/vnd.oasis.opendocument.presentation':
case $mime === 'application/vnd.oasis.opendocument.spreadsheet':
case $mime === 'application/vnd.oasis.opendocument.text':
case $mime === 'application/x-indesign':
return 'MediaVorus\Media\Document';
break;
case $mime === 'application/x-shockwave-flash':
return 'MediaVorus\Media\Flash';
break;
default:
break;
}
return 'MediaVorus\Media\DefaultMedia';
}
}

View File

@@ -0,0 +1,49 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus;
use FFMpeg\Exception\ExecutableNotFoundException;
use MediaVorus\Exception\RuntimeException;
use Silex\Application;
use Silex\ServiceProviderInterface;
class MediaVorusServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*/
public function register(Application $app)
{
$app['mediavorus'] = $app->share(function(Application $app) {
$ffprobe = null;
if (isset($app['ffmpeg.ffprobe'])) {
try {
$ffprobe = $app['ffmpeg.ffprobe'];
} catch (ExecutableNotFoundException $e) {
}
}
return new MediaVorus($app['exiftool.reader'], $app['exiftool.writer'], $ffprobe);
});
}
/**
* {@inheritdoc}
*/
public function boot(Application $app)
{
if (!isset($app['exiftool.reader']) || ! isset($app['exiftool.writer'])) {
throw new RuntimeException('MediaVorus Service Provider requires Exiftool Service Provider');
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Utils;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class AudioMimeTypeGuesser implements MimeTypeGuesserInterface
{
public static $videoMimeTypes = array(
'ape' => 'audio/x-monkeys-audio',
'mp3' => 'audio/mpeg',
);
/**
* {@inheritdoc}
*/
public function guess($path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (array_key_exists($extension, static::$videoMimeTypes)) {
return static::$videoMimeTypes[$extension];
}
return null;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Utils;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class DocumentMimeTypeGuesser implements MimeTypeGuesserInterface
{
public static $documentMimeTypes = array(
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
);
/**
* {@inheritdoc}
*/
public function guess($path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (array_key_exists($extension, static::$documentMimeTypes)) {
return static::$documentMimeTypes[$extension];
}
return null;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Utils;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class FlashMimeTypeGuesser implements MimeTypeGuesserInterface
{
public static $flashMimeTypes = array(
'swf' => 'application/x-shockwave-flash',
);
/**
* {@inheritdoc}
*/
public function guess($path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (array_key_exists($extension, static::$flashMimeTypes)) {
return static::$flashMimeTypes[$extension];
}
return null;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Utils;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class ImageMimeTypeGuesser implements MimeTypeGuesserInterface
{
public static $imageMimeTypes = array(
'heic'=> 'image/heic',
'heif'=> 'image/heif',
);
/**
* {@inheritdoc}
*/
public function guess($path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (array_key_exists($extension, static::$imageMimeTypes)) {
return static::$imageMimeTypes[$extension];
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Utils;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class PostScriptMimeTypeGuesser implements MimeTypeGuesserInterface
{
public static $postscriptMimeTypes = array(
'eps' => 'application/postscript',
'ai' => 'application/illustrator',
'indd' => 'application/x-indesign',
);
/**
* {@inheritdoc}
*/
public function guess($path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (array_key_exists($extension, static::$postscriptMimeTypes)) {
return static::$postscriptMimeTypes[$extension];
}
return null;
}
}

View File

@@ -0,0 +1,72 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Utils;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class RawImageMimeTypeGuesser implements MimeTypeGuesserInterface
{
public static $rawMimeTypes = array(
'3fr' => 'image/x-tika-hasselblad',
'arw' => 'image/x-tika-sony',
'bay' => 'image/x-tika-casio',
'cap' => 'image/x-tika-phaseone',
'cr2' => 'image/x-tika-canon',
'crw' => 'image/x-tika-canon',
'dcs' => 'image/x-tika-kodak',
'dcr' => 'image/x-tika-kodak',
'dng' => 'image/x-tika-dng',
'drf' => 'image/x-tika-kodak',
'erf' => 'image/x-tika-epson',
'fff' => 'image/x-tika-imacon',
'iiq' => 'image/x-tika-phaseone',
'kdc' => 'image/x-tika-kodak',
'k25' => 'image/x-tika-kodak',
'mef' => 'image/x-tika-mamiya',
'mos' => 'image/x-tika-leaf',
'mrw' => 'image/x-tika-minolta',
'nef' => 'image/x-tika-nikon',
'nrw' => 'image/x-tika-nikon',
'orf' => 'image/x-tika-olympus',
'pef' => 'image/x-tika-pentax',
'ppm' => 'image/x-portable-pixmap',
'ptx' => 'image/x-tika-pentax',
'pxn' => 'image/x-tika-logitech',
'raf' => 'image/x-tika-fuji',
'raw' => 'image/x-tika-panasonic',
'r3d' => 'image/x-tika-red',
'rw2' => 'image/x-tika-panasonic',
'rwz' => 'image/x-tika-rawzor',
'sr2' => 'image/x-tika-sony',
'srf' => 'image/x-tika-sony',
'x3f' => 'image/x-tika-sigma',
);
/**
* {@inheritdoc}
*/
public function guess($path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (array_key_exists($extension, static::$rawMimeTypes)) {
return static::$rawMimeTypes[$extension];
}
return null;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of MediaVorus.
*
* (c) 2012 Romain Neutron <imprec@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MediaVorus\Utils;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class VideoMimeTypeGuesser implements MimeTypeGuesserInterface
{
public static $videoMimeTypes = array(
'webm' => 'video/webm',
'ogv' => 'video/ogg',
'mts' => 'video/m2ts',
'mov' => 'video/quicktime',
'm4v' => 'video/m4v'
);
/**
* {@inheritdoc}
*/
public function guess($path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (array_key_exists($extension, static::$videoMimeTypes)) {
return static::$videoMimeTypes[$extension];
}
return null;
}
}