Video support
This commit is contained in:
4
files/db/update_v5_to_v6.sql
Normal file
4
files/db/update_v5_to_v6.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
RENAME TABLE pictures TO medias;
|
||||||
|
ALTER TABLE medias CHANGE COLUMN id_picture id_media INT(10) UNSIGNED NOT NULL auto_increment;
|
||||||
|
ALTER TABLE medias ADD COLUMN type VARCHAR(20) AFTER filename;
|
||||||
|
UPDATE medias SET type = 'image';
|
||||||
222
inc/media.php
Normal file
222
inc/media.php
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Media extends PhpObject {
|
||||||
|
|
||||||
|
//DB Tables
|
||||||
|
const MEDIA_TABLE = 'medias';
|
||||||
|
|
||||||
|
//Media folders
|
||||||
|
const MEDIA_FOLDER = 'files/';
|
||||||
|
const THUMB_FOLDER = self::MEDIA_FOLDER.'thumbs/';
|
||||||
|
|
||||||
|
const THUMB_MAX_WIDTH = 400;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database Handle
|
||||||
|
* @var Db
|
||||||
|
*/
|
||||||
|
private $oDb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media Project
|
||||||
|
* @var Project
|
||||||
|
*/
|
||||||
|
private $oProject;
|
||||||
|
private $asMedias;
|
||||||
|
private $sSystemType;
|
||||||
|
|
||||||
|
public function __construct(Db &$oDb, &$oProject) {
|
||||||
|
parent::__construct(__CLASS__, Settings::DEBUG);
|
||||||
|
$this->oDb = &$oDb;
|
||||||
|
$this->oProject = &$oProject;
|
||||||
|
$this->sSystemType = (substr(php_uname(), 0, 7) == "Windows")?'win':'unix';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMediasInfo() {
|
||||||
|
if(empty($this->asMedias)) {
|
||||||
|
if($this->oProject->getProjectId()) {
|
||||||
|
$asMedias = $this->oDb->selectRows(array(
|
||||||
|
'select' => array(Db::getId(self::MEDIA_TABLE), 'filename', 'taken_on', 'posted_on', 'rotate'),
|
||||||
|
'from' => self::MEDIA_TABLE,
|
||||||
|
'constraint'=> array(Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId())
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach($asMedias as &$asMedia) {
|
||||||
|
$asMedia['pic_path'] = self::getMediaPath($asMedia['filename']);
|
||||||
|
$asMedia['thumb_path'] = $this->getMediaThumbnail($asMedia['filename']);
|
||||||
|
}
|
||||||
|
$this->asMedias = $asMedias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->asMedias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isProjectModeValid() {
|
||||||
|
return ($this->oProject->getMode() == Project::MODE_BLOG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMedia($sMediaName, $sMethod='upload') {
|
||||||
|
$sError = '';
|
||||||
|
if(!$this->isProjectModeValid() && $sMethod!='sync') $sError = 'Le projet (id='.$this->oProject->getProjectId().') n\'est pas en mode "blog"';
|
||||||
|
elseif($this->oDb->pingValue(self::MEDIA_TABLE, array('filename'=>$sMediaName)) && $sMethod!='sync') $sError = 'l\'image existe déjà';
|
||||||
|
else {
|
||||||
|
//Add media to DB
|
||||||
|
$asMediaInfo = $this->getMediaInfoFromFile($sMediaName);
|
||||||
|
$asDbInfo = array(
|
||||||
|
Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId(),
|
||||||
|
'filename' => $sMediaName,
|
||||||
|
'taken_on' => ($asMediaInfo['taken_ts'] > 0)?date(Db::TIMESTAMP_FORMAT, $asMediaInfo['taken_ts']):0, //Site Time (Settings::TIMEZONE)
|
||||||
|
'posted_on' => date(Db::TIMESTAMP_FORMAT, $asMediaInfo['file_ts']), //Site Time
|
||||||
|
'rotate' => $asMediaInfo['rotate'],
|
||||||
|
'type' => $asMediaInfo['type']
|
||||||
|
);
|
||||||
|
|
||||||
|
if($sMethod=='sync') $iMediaId = $this->oDb->insertUpdateRow(self::MEDIA_TABLE, $asDbInfo, array(Db::getId(Project::PROJ_TABLE), 'filename'));
|
||||||
|
else $iMediaId = $this->oDb->insertRow(self::MEDIA_TABLE, $asDbInfo);
|
||||||
|
|
||||||
|
if(!$iMediaId) $sError = 'l\'image n\'a pas pu être entrée en base';
|
||||||
|
else {
|
||||||
|
//Create thumbnail
|
||||||
|
$this->getMediaThumbnail($sMediaName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($sError!='') {
|
||||||
|
$sError = 'Erreur lors de l\'ajout de "'.$sMediaName.'" : '.$sError;
|
||||||
|
$this->addError($sError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-shot function to initialize DB with existing images
|
||||||
|
*/
|
||||||
|
public function syncFileFolder() {
|
||||||
|
$asMediaPaths = glob(self::getMediaPath('*.{jpg,JPG,jpeg,JPEG,png,PNG,mov,MOV}'), GLOB_BRACE);
|
||||||
|
foreach($asMediaPaths as $sMediaPath)
|
||||||
|
{
|
||||||
|
$sMediaName = pathinfo($sMediaPath, PATHINFO_BASENAME);
|
||||||
|
$this->addMedia($sMediaName, 'sync');
|
||||||
|
}
|
||||||
|
$this->setExtractMode(PhpObject::MODE_HTML);
|
||||||
|
return $this->getCleanMessageStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMediaInfoFromFile($sMediaName)
|
||||||
|
{
|
||||||
|
$sMediaPath = self::getMediaPath($sMediaName);
|
||||||
|
$sType = self::getMediaType($sMediaName);
|
||||||
|
|
||||||
|
$iTimeStamp = $iTakenOn = $iPostedOn = 0;
|
||||||
|
$sRotate = '0';
|
||||||
|
$sTakenOn = '';
|
||||||
|
switch($sType) {
|
||||||
|
case 'video':
|
||||||
|
$asResult = array();
|
||||||
|
$sParams = implode(' ', array(
|
||||||
|
'-loglevel error', //Remove comments
|
||||||
|
'-select_streams v:0', //First video channel
|
||||||
|
'-show_entries stream_tags=rotate,creation_time', //filter tags :rotation & creation time only
|
||||||
|
'-print_format json', //output format: json
|
||||||
|
'-i' //input file
|
||||||
|
));
|
||||||
|
exec('ffprobe '.$sParams.' "'.$sMediaPath.'"', $asResult);
|
||||||
|
$asResult = json_decode(implode('', $asResult), true);
|
||||||
|
|
||||||
|
//Timestamps
|
||||||
|
$sTakenOn = date(Db::TIMESTAMP_FORMAT, strtotime($asResult['streams'][0]['tags']['creation_time']));
|
||||||
|
$iPostedOn = filemtime($sMediaPath);
|
||||||
|
|
||||||
|
//Orientation
|
||||||
|
if(isset($asResult['streams'][0]['tags']['rotate'])) $sRotate = $asResult['streams'][0]['tags']['rotate'];
|
||||||
|
break;
|
||||||
|
case 'image':
|
||||||
|
$asExif = @exif_read_data($sMediaPath, 0, true);
|
||||||
|
if(!$asExif) $asExif['FILE']['FileDateTime'] = filemtime($sMediaPath);
|
||||||
|
|
||||||
|
//Timestamps
|
||||||
|
if(array_key_exists('EXIF', $asExif) && array_key_exists('DateTimeOriginal', $asExif['EXIF'])) $sTakenOn = $asExif['EXIF']['DateTimeOriginal'];
|
||||||
|
if(array_key_exists('FILE', $asExif) && array_key_exists('FileDateTime', $asExif['FILE'])) $iPostedOn = $asExif['FILE']['FileDateTime'];
|
||||||
|
|
||||||
|
//Orientation
|
||||||
|
if(array_key_exists('IFD0', $asExif) && array_key_exists('Orientation', $asExif['IFD0'])) {
|
||||||
|
switch($asExif['IFD0']['Orientation'])
|
||||||
|
{
|
||||||
|
case 1: $sRotate = '0'; break; //None
|
||||||
|
case 3: $sRotate = '180'; break; //Flip over
|
||||||
|
case 6: $sRotate = '90'; break; //Clockwise
|
||||||
|
case 8: $sRotate = '-90'; break; //Trigo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Media info do not have any TZ: Interpreting date time using project timezone (assuming all medias have been taken in this time zone)
|
||||||
|
if($sTakenOn != '') {
|
||||||
|
$oTakenOn = new DateTime($sTakenOn, new DateTimeZone($this->oProject->getTimeZone()));
|
||||||
|
$iTakenOn = $oTakenOn->format('U');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Merge timestamps
|
||||||
|
$iTimeStamp = ($iTakenOn > 0)?$iTakenOn:$iPostedOn;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'timestamp' => $iTimeStamp,
|
||||||
|
'taken_ts' => $iTakenOn,
|
||||||
|
'file_ts' => $iPostedOn,
|
||||||
|
'rotate' => $sRotate,
|
||||||
|
'type' => $sType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMediaThumbnail($sMediaName)
|
||||||
|
{
|
||||||
|
$sMediaPath = self::getMediaPath($sMediaName);
|
||||||
|
$sThumbPath = self::getMediaPath($sMediaName, 'thumbnail');
|
||||||
|
$sType = self::getMediaType($sMediaName);
|
||||||
|
|
||||||
|
if(!file_exists($sThumbPath)) {
|
||||||
|
switch($sType) {
|
||||||
|
case 'image':
|
||||||
|
$asThumbInfo = ToolBox::createThumbnail($sMediaPath, self::THUMB_MAX_WIDTH, 0, $sThumbPath, false, array('jpg', 'jpeg', 'gif', 'png'), false, true);
|
||||||
|
break;
|
||||||
|
case 'video':
|
||||||
|
//Get a screenshot of the video 1 second in
|
||||||
|
$sTempPath = self::getMediaPath(uniqid('temp_').'.png');
|
||||||
|
$asResult = array();
|
||||||
|
$sParams = implode(' ', array(
|
||||||
|
'-i "'.$sMediaPath.'"', //input file
|
||||||
|
'-ss 00:00:01.000', //Image taken after x seconds
|
||||||
|
'-vframes 1', //number of video frames to output
|
||||||
|
'"'.$sTempPath.'"', //output file
|
||||||
|
));
|
||||||
|
exec('ffmpeg '.$sParams, $asResult);
|
||||||
|
|
||||||
|
//Resize
|
||||||
|
$asThumbInfo = ToolBox::createThumbnail($sTempPath, self::THUMB_MAX_WIDTH, 0, $sThumbPath, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else $asThumbInfo = array('error'=>'', 'out'=>$sThumbPath);
|
||||||
|
|
||||||
|
return ($asThumbInfo['error']=='')?$asThumbInfo['out']:$sMediaPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getMediaPath($sMediaName, $sFileType='media') {
|
||||||
|
if($sFileType=='thumbnail') return self::THUMB_FOLDER.$sMediaName.(self::getMediaType($sMediaName)=='video'?'.png':'');
|
||||||
|
else return self::MEDIA_FOLDER.$sMediaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getMediaType($sMediaName) {
|
||||||
|
$sMediaPath = self::getMediaPath($sMediaName);
|
||||||
|
$sMediaMime = mime_content_type($sMediaPath);
|
||||||
|
switch($sMediaMime) {
|
||||||
|
case 'video/quicktime': $sType = 'video'; break;
|
||||||
|
default: $sType = 'image'; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sType;
|
||||||
|
}
|
||||||
|
}
|
||||||
157
inc/picture.php
157
inc/picture.php
@@ -1,157 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class Picture extends PhpObject {
|
|
||||||
|
|
||||||
//DB Tables
|
|
||||||
const PIC_TABLE = 'pictures';
|
|
||||||
|
|
||||||
//Picture folders
|
|
||||||
const PIC_FOLDER = 'files/';
|
|
||||||
const THUMB_FOLDER = self::PIC_FOLDER.'thumbs/';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Database Handle
|
|
||||||
* @var Db
|
|
||||||
*/
|
|
||||||
private $oDb;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Picture Project
|
|
||||||
* @var Project
|
|
||||||
*/
|
|
||||||
private $oProject;
|
|
||||||
private $asPics;
|
|
||||||
|
|
||||||
public function __construct(Db &$oDb, &$oProject) {
|
|
||||||
parent::__construct(__CLASS__, Settings::DEBUG);
|
|
||||||
$this->oDb = &$oDb;
|
|
||||||
$this->oProject = &$oProject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPicsInfo() {
|
|
||||||
if(empty($this->asPics)) {
|
|
||||||
if($this->oProject->getProjectId()) {
|
|
||||||
$asPics = $this->oDb->selectRows(array(
|
|
||||||
'select' => array(Db::getId(self::PIC_TABLE), 'filename', 'taken_on', 'posted_on', 'rotate'),
|
|
||||||
'from' => self::PIC_TABLE,
|
|
||||||
'constraint'=> array(Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId())
|
|
||||||
));
|
|
||||||
|
|
||||||
foreach($asPics as &$asPic) {
|
|
||||||
$asPic['pic_path'] = self::getPicPath($asPic['filename']);
|
|
||||||
$asPic['thumb_path'] = self::getPicThumbnail($asPic['filename']);
|
|
||||||
}
|
|
||||||
$this->asPics = $asPics;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->asPics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isProjectModeValid() {
|
|
||||||
return ($this->oProject->getMode() == Project::MODE_BLOG);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addPic($sPicName, $sMethod='upload') {
|
|
||||||
$sError = '';
|
|
||||||
if(!$this->isProjectModeValid() && $sMethod!='sync') $sError = 'Le projet (id='.$this->oProject->getProjectId().') n\'est pas en mode "blog"';
|
|
||||||
elseif($this->oDb->pingValue(self::PIC_TABLE, array('filename'=>$sPicName)) && $sMethod!='sync') $sError = 'l\'image existe déjà';
|
|
||||||
else {
|
|
||||||
//Add picture to DB
|
|
||||||
$asPicInfo = $this->getPicInfoFromFile($sPicName);
|
|
||||||
$asDbInfo = array(
|
|
||||||
Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId(),
|
|
||||||
'filename' => $sPicName,
|
|
||||||
'taken_on' => ($asPicInfo['taken_ts'] > 0)?date(Db::TIMESTAMP_FORMAT, $asPicInfo['taken_ts']):0, //Site Time (Settings::TIMEZONE)
|
|
||||||
'posted_on' => date(Db::TIMESTAMP_FORMAT, $asPicInfo['file_ts']), //Site Time
|
|
||||||
'rotate' => $asPicInfo['rotate']
|
|
||||||
);
|
|
||||||
|
|
||||||
if($sMethod=='sync') $iPicId = $this->oDb->insertUpdateRow(self::PIC_TABLE, $asDbInfo, array(Db::getId(Project::PROJ_TABLE), 'filename'));
|
|
||||||
else $iPicId = $this->oDb->insertRow(self::PIC_TABLE, $asDbInfo);
|
|
||||||
|
|
||||||
if(!$iPicId) $sError = 'l\'image n\'a pas pu être entrée en base';
|
|
||||||
else {
|
|
||||||
//Create thumbnail
|
|
||||||
self::getPicThumbnail($sPicName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($sError!='') {
|
|
||||||
$sError = 'Erreur lors de l\'ajout de "'.$sPicName.'" : '.$sError;
|
|
||||||
$this->addError($sError);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One-shot function to initialize DB with existing images
|
|
||||||
*/
|
|
||||||
public function syncFileFolder() {
|
|
||||||
$asPicPaths = glob(self::getPicPath('*.{jpg,JPG,jpeg,JPEG,png,PNG}'), GLOB_BRACE);
|
|
||||||
foreach($asPicPaths as $sPicPath)
|
|
||||||
{
|
|
||||||
$sPicName = pathinfo($sPicPath, PATHINFO_BASENAME);
|
|
||||||
$this->addPic($sPicName, 'sync');
|
|
||||||
}
|
|
||||||
$this->setExtractMode(PhpObject::MODE_HTML);
|
|
||||||
return $this->getCleanMessageStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getPicInfoFromFile($sPicName)
|
|
||||||
{
|
|
||||||
$sPicPath = self::getPicPath($sPicName);
|
|
||||||
$iTimeStamp = $iTakenOn = $iPostedOn = 0;
|
|
||||||
$sTakenOn = '';
|
|
||||||
$asExif = @exif_read_data($sPicPath, 0, true);
|
|
||||||
if(!$asExif) $asExif['FILE']['FileDateTime'] = filemtime($sPicPath);
|
|
||||||
|
|
||||||
//Timestamps
|
|
||||||
if(array_key_exists('EXIF', $asExif) && array_key_exists('DateTimeOriginal', $asExif['EXIF'])) $sTakenOn = $asExif['EXIF']['DateTimeOriginal'];
|
|
||||||
if(array_key_exists('FILE', $asExif) && array_key_exists('FileDateTime', $asExif['FILE'])) $iPostedOn = $asExif['FILE']['FileDateTime'];
|
|
||||||
|
|
||||||
//Picture info do not have any TZ: Interpreting date time using project timezone (assuming all pictures have been taken in this time zone)
|
|
||||||
if($sTakenOn != '') {
|
|
||||||
$oTakenOn = new DateTime($sTakenOn, new DateTimeZone($this->oProject->getTimeZone()));
|
|
||||||
$iTakenOn = $oTakenOn->format('U');
|
|
||||||
}
|
|
||||||
|
|
||||||
//Merge timestamps
|
|
||||||
$iTimeStamp = ($iTakenOn > 0)?$iTakenOn:$iPostedOn;
|
|
||||||
|
|
||||||
//Orientation
|
|
||||||
if(array_key_exists('IFD0', $asExif) && array_key_exists('Orientation', $asExif['IFD0'])) {
|
|
||||||
switch($asExif['IFD0']['Orientation'])
|
|
||||||
{
|
|
||||||
case 1: $sRotate = '0'; break; //None
|
|
||||||
case 3: $sRotate = '180'; break; //Flip over
|
|
||||||
case 6: $sRotate = '90'; break; //Clockwise
|
|
||||||
case 8: $sRotate = '-90'; break; //Trigo
|
|
||||||
default: $sRotate = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else $sRotate = '0';
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'timestamp' => $iTimeStamp,
|
|
||||||
'taken_ts' => $iTakenOn,
|
|
||||||
'file_ts' => $iPostedOn,
|
|
||||||
'rotate' => $sRotate
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function getPicThumbnail($sPicName)
|
|
||||||
{
|
|
||||||
$sPicPath = self::getPicPath($sPicName);
|
|
||||||
$sThumbPath = self::getPicPath($sPicName, 'thumbnail');
|
|
||||||
|
|
||||||
if(!file_exists($sThumbPath)) $asThumbInfo = ToolBox::createThumbnail($sPicPath, 400, 0, $sThumbPath, false, array('jpg', 'jpeg', 'gif', 'png'), false, true);
|
|
||||||
else $asThumbInfo = array('error'=>'', 'out'=>$sThumbPath);
|
|
||||||
|
|
||||||
return ($asThumbInfo['error']=='')?$asThumbInfo['out']:$sPicPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function getPicPath($sPicName, $sFileType='picture') {
|
|
||||||
return ($sFileType=='thumbnail'?self::THUMB_FOLDER:self::PIC_FOLDER).$sPicName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
72
inc/spot.php
72
inc/spot.php
@@ -7,7 +7,7 @@
|
|||||||
* - unix_time: UNIX (int) in UTC
|
* - unix_time: UNIX (int) in UTC
|
||||||
* - site_time: timestamp in site time (see Settings::TIMEZONE)
|
* - site_time: timestamp in site time (see Settings::TIMEZONE)
|
||||||
* - iso_time: raw ISO 8601 in local timezone
|
* - iso_time: raw ISO 8601 in local timezone
|
||||||
* - Pictures (table `pictures`):
|
* - Medias (table `medias`):
|
||||||
* - posted_on: timestamp in site time (see Settings::TIMEZONE)
|
* - posted_on: timestamp in site time (see Settings::TIMEZONE)
|
||||||
* - taken_on: timestamp in site time (see Settings::TIMEZONE)
|
* - taken_on: timestamp in site time (see Settings::TIMEZONE)
|
||||||
* - Posts (table `posts`):
|
* - Posts (table `posts`):
|
||||||
@@ -30,23 +30,23 @@ class Spot extends Main
|
|||||||
private $oProject;
|
private $oProject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Picture Class
|
* Media Class
|
||||||
* @var Picture
|
* @var Media
|
||||||
*/
|
*/
|
||||||
private $oPicture;
|
private $oMedia;
|
||||||
|
|
||||||
public function __construct($oClassManagement, $sProcessPage)
|
public function __construct($oClassManagement, $sProcessPage)
|
||||||
{
|
{
|
||||||
$asClasses = array(
|
$asClasses = array(
|
||||||
array('name'=>'feed', 'project'=>true),
|
array('name'=>'feed', 'project'=>true),
|
||||||
array('name'=>'project', 'project'=>true),
|
array('name'=>'project', 'project'=>true),
|
||||||
array('name'=>'picture', 'project'=>true),
|
array('name'=>'media', 'project'=>true),
|
||||||
array('name'=>'converter', 'project'=>true)
|
array('name'=>'converter', 'project'=>true)
|
||||||
);
|
);
|
||||||
parent::__construct($oClassManagement, $sProcessPage, $asClasses);
|
parent::__construct($oClassManagement, $sProcessPage, $asClasses);
|
||||||
|
|
||||||
$this->oProject = new Project($this->oDb);
|
$this->oProject = new Project($this->oDb);
|
||||||
$this->oPicture = new Picture($this->oDb, $this->oProject);
|
$this->oMedia = new Media($this->oDb, $this->oProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function install()
|
protected function install()
|
||||||
@@ -66,7 +66,7 @@ class Spot extends Main
|
|||||||
Feed::SPOT_TABLE => array('ref_spot_id', 'name', 'model'),
|
Feed::SPOT_TABLE => array('ref_spot_id', 'name', 'model'),
|
||||||
Project::PROJ_TABLE => array('name', 'codename', 'active_from', 'active_to', 'geofile', 'timezone'),
|
Project::PROJ_TABLE => array('name', 'codename', 'active_from', 'active_to', 'geofile', 'timezone'),
|
||||||
self::POST_TABLE => array(Db::getId(Project::PROJ_TABLE), 'name', 'content', 'site_time'),
|
self::POST_TABLE => array(Db::getId(Project::PROJ_TABLE), 'name', 'content', 'site_time'),
|
||||||
Picture::PIC_TABLE => array(Db::getId(Project::PROJ_TABLE), 'filename', 'taken_on', 'posted_on', 'rotate')
|
Media::MEDIA_TABLE => array(Db::getId(Project::PROJ_TABLE), 'filename', 'type', 'taken_on', 'posted_on', 'rotate')
|
||||||
),
|
),
|
||||||
'types' => array
|
'types' => array
|
||||||
(
|
(
|
||||||
@@ -105,7 +105,7 @@ class Spot extends Main
|
|||||||
Feed::FEED_TABLE => "INDEX(`ref_feed_id`)",
|
Feed::FEED_TABLE => "INDEX(`ref_feed_id`)",
|
||||||
Feed::SPOT_TABLE => "INDEX(`ref_spot_id`)",
|
Feed::SPOT_TABLE => "INDEX(`ref_spot_id`)",
|
||||||
Project::PROJ_TABLE => "UNIQUE KEY `uni_proj_name` (`codename`)",
|
Project::PROJ_TABLE => "UNIQUE KEY `uni_proj_name` (`codename`)",
|
||||||
Picture::PIC_TABLE => "UNIQUE KEY `uni_file_name` (`filename`)"
|
Media::MEDIA_TABLE=> "UNIQUE KEY `uni_file_name` (`filename`)"
|
||||||
),
|
),
|
||||||
'cascading_delete' => array
|
'cascading_delete' => array
|
||||||
(
|
(
|
||||||
@@ -162,25 +162,25 @@ class Spot extends Main
|
|||||||
$bSuccess = !empty($asMessages);
|
$bSuccess = !empty($asMessages);
|
||||||
$sDesc = $bSuccess?'':self::NO_DATA;
|
$sDesc = $bSuccess?'':self::NO_DATA;
|
||||||
|
|
||||||
//Add pictures
|
//Add medias
|
||||||
if($bSuccess) {
|
if($bSuccess) {
|
||||||
$asPics = $this->getPictures('taken_on');
|
$asMedias = $this->getMedias('taken_on');
|
||||||
|
|
||||||
//Assign pictures to closest message
|
//Assign medias to closest message
|
||||||
$iIndex = 0;
|
$iIndex = 0;
|
||||||
$iMaxIndex = count($asMessages) - 1;
|
$iMaxIndex = count($asMessages) - 1;
|
||||||
foreach($asPics as $asPic) {
|
foreach($asMedias as $asMedia) {
|
||||||
while($iIndex <= $iMaxIndex && $asPic['unix_time'] > $asMessages[$iIndex]['unix_time']) {
|
while($iIndex <= $iMaxIndex && $asMedia['unix_time'] > $asMessages[$iIndex]['unix_time']) {
|
||||||
$iIndex++;
|
$iIndex++;
|
||||||
}
|
}
|
||||||
if($iIndex == 0) $iMsgIndex = $iIndex;
|
if($iIndex == 0) $iMsgIndex = $iIndex;
|
||||||
elseif($iIndex > $iMaxIndex) $iMsgIndex = $iMaxIndex;
|
elseif($iIndex > $iMaxIndex) $iMsgIndex = $iMaxIndex;
|
||||||
else {
|
else {
|
||||||
$iHalfWayPoint = ($asMessages[$iIndex - 1]['unix_time'] + $asMessages[$iIndex]['unix_time'])/2;
|
$iHalfWayPoint = ($asMessages[$iIndex - 1]['unix_time'] + $asMessages[$iIndex]['unix_time'])/2;
|
||||||
$iMsgIndex = ($asPic['unix_time'] >= $iHalfWayPoint)?$iIndex:($iIndex - 1);
|
$iMsgIndex = ($asMedia['unix_time'] >= $iHalfWayPoint)?$iIndex:($iIndex - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$asMessages[$iMsgIndex]['pics'][] = $asPic;
|
$asMessages[$iMsgIndex]['medias'][] = $asMedia;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,29 +211,29 @@ class Spot extends Main
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get valid pictures based on $sTimeRefField (both are on site time):
|
* Get valid medias based on $sTimeRefField (both are on site time):
|
||||||
* - taken_on: Date/time on which the picture was taken
|
* - taken_on: Date/time on which the media was taken
|
||||||
* - posted_on: Date/time on which the picture was uploaded
|
* - posted_on: Date/time on which the media was uploaded
|
||||||
* @param String $sTimeRefField Field to calculate relative times
|
* @param String $sTimeRefField Field to calculate relative times
|
||||||
* @return Array Pictures info
|
* @return Array Medias info
|
||||||
*/
|
*/
|
||||||
private function getPictures($sTimeRefField)
|
private function getMedias($sTimeRefField)
|
||||||
{
|
{
|
||||||
$asPics = $this->oPicture->getPicsInfo();
|
$asMedias = $this->oMedia->getMediasInfo();
|
||||||
$asValidPics = array();
|
$asValidMedias = array();
|
||||||
foreach($asPics as $iIndex=>$asPic) {
|
foreach($asMedias as $iIndex=>$asMedia) {
|
||||||
$sTimeRef = $asPic[$sTimeRefField];
|
$sTimeRef = $asMedia[$sTimeRefField];
|
||||||
if($sTimeRef >= $this->oProject->getActivePeriod('from') && $sTimeRef <= $this->oProject->getActivePeriod('to')) {
|
if($sTimeRef >= $this->oProject->getActivePeriod('from') && $sTimeRef <= $this->oProject->getActivePeriod('to')) {
|
||||||
$asPic['taken_on_formatted'] = date(self::FORMAT_TIME, strtotime($asPic['taken_on']));
|
$asMedia['taken_on_formatted'] = date(self::FORMAT_TIME, strtotime($asMedia['taken_on']));
|
||||||
$asPic['displayed_id'] = 'N°'.($iIndex + 1);
|
$asMedia['displayed_id'] = 'N°'.($iIndex + 1);
|
||||||
|
|
||||||
$this->addTimeStamp($asPic, strtotime($sTimeRef));
|
$this->addTimeStamp($asMedia, strtotime($sTimeRef));
|
||||||
$asValidPics[] = $asPic;
|
$asValidMedias[] = $asMedia;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
usort($asValidPics, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
|
usort($asValidMedias, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
|
||||||
|
|
||||||
return $asValidPics;
|
return $asValidMedias;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPosts()
|
private function getPosts()
|
||||||
@@ -276,9 +276,9 @@ class Spot extends Main
|
|||||||
'feed' => $this->getSpotMessages(),
|
'feed' => $this->getSpotMessages(),
|
||||||
'priority' => 0
|
'priority' => 0
|
||||||
),
|
),
|
||||||
'picture' => array(
|
'media' => array(
|
||||||
'table' => Picture::PIC_TABLE,
|
'table' => Media::MEDIA_TABLE,
|
||||||
'feed' => $this->getPictures('posted_on'),
|
'feed' => $this->getMedias('posted_on'),
|
||||||
'priority' => 1
|
'priority' => 1
|
||||||
),
|
),
|
||||||
'post' => array(
|
'post' => array(
|
||||||
@@ -308,8 +308,8 @@ class Spot extends Main
|
|||||||
return self::getJsonResult(true, '', $asFeeds);
|
return self::getJsonResult(true, '', $asFeeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncPics() {
|
public function syncMedias() {
|
||||||
return $this->oPicture->syncFileFolder();
|
return $this->oMedia->syncFileFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPost($sName, $sPost)
|
public function addPost($sName, $sPost)
|
||||||
@@ -327,7 +327,7 @@ class Spot extends Main
|
|||||||
public function upload()
|
public function upload()
|
||||||
{
|
{
|
||||||
$this->oClassManagement->incClass('uploader', true);
|
$this->oClassManagement->incClass('uploader', true);
|
||||||
$oUploader = new Uploader($this->oPicture);
|
$oUploader = new Uploader($this->oMedia);
|
||||||
|
|
||||||
return $oUploader->sBody;
|
return $oUploader->sBody;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
class Uploader extends UploadHandler
|
class Uploader extends UploadHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Pictures Management
|
* Medias Management
|
||||||
* @var Picture
|
* @var Media
|
||||||
*/
|
*/
|
||||||
private $oPicture;
|
private $oMedia;
|
||||||
|
|
||||||
public $sBody;
|
public $sBody;
|
||||||
|
|
||||||
function __construct(&$oPicture)
|
function __construct(&$oMedia)
|
||||||
{
|
{
|
||||||
$this->error_messages['wrong_project_mode'] = 'Le projet n\'est pas en mode "blog".';
|
$this->error_messages['wrong_project_mode'] = 'Le projet n\'est pas en mode "blog".';
|
||||||
$this->oPicture = &$oPicture;
|
$this->oMedia = &$oMedia;
|
||||||
$this->sBody = '';
|
$this->sBody = '';
|
||||||
parent::__construct(array('image_versions'=>array(), 'accept_file_types'=>'/\.(gif|jpe?g|png|mov)$/i'));
|
parent::__construct(array('image_versions'=>array(), 'accept_file_types'=>'/\.(gif|jpe?g|png|mov)$/i'));
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ class Uploader extends UploadHandler
|
|||||||
$bResult = parent::validate($uploaded_file, $file, $error, $index);
|
$bResult = parent::validate($uploaded_file, $file, $error, $index);
|
||||||
|
|
||||||
//Check project mode
|
//Check project mode
|
||||||
if(!$this->oPicture->isProjectModeValid()) {
|
if(!$this->oMedia->isProjectModeValid()) {
|
||||||
$file->error = $this->get_error_message('wrong_project_mode');
|
$file->error = $this->get_error_message('wrong_project_mode');
|
||||||
$bResult = false;
|
$bResult = false;
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ class Uploader extends UploadHandler
|
|||||||
$file = parent::handle_file_upload($uploaded_file, $name, $size, $type, $error, $index, $content_range);
|
$file = parent::handle_file_upload($uploaded_file, $name, $size, $type, $error, $index, $content_range);
|
||||||
|
|
||||||
if(empty($file->error)) {
|
if(empty($file->error)) {
|
||||||
$sError = $this->oPicture->addPic($file->name);
|
$sError = $this->oMedia->addMedia($file->name);
|
||||||
if($sError!='') {
|
if($sError!='') {
|
||||||
$file->error = $this->get_error_message($sError);
|
$file->error = $this->get_error_message($sError);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ function initSpotMessages(aoMessages, aoTracks) {
|
|||||||
.append('Lat : '+oMsg.latitude+', Lng : '+oMsg.longitude));
|
.append('Lat : '+oMsg.latitude+', Lng : '+oMsg.longitude));
|
||||||
|
|
||||||
//Tooltip pictures
|
//Tooltip pictures
|
||||||
if(oMsg.pics) {
|
if(oMsg.medias) {
|
||||||
var $Pics = $('<div>', {'class':'pics'});
|
var $Pics = $('<div>', {'class':'pics'});
|
||||||
$.each(oMsg.pics, function(iKey, asPic){
|
$.each(oMsg.pics, function(iKey, asPic){
|
||||||
$Pics.append($('<a>', {href: asPic.pic_path, 'data-lightbox': 'marker-pictures', 'data-title': 'Photo prise le '+asPic.formatted_time+self.tmp('site_tz_notice'), 'data-orientation': asPic.rotate})
|
$Pics.append($('<a>', {href: asPic.pic_path, 'data-lightbox': 'marker-pictures', 'data-title': 'Photo prise le '+asPic.formatted_time+self.tmp('site_tz_notice'), 'data-orientation': asPic.rotate})
|
||||||
@@ -501,7 +501,7 @@ function getPost(asPost) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'picture':
|
case 'media':
|
||||||
var sTakenOn = (asPost.taken_on == '0000-00-00 00:00:00')?'':' et prise le '+asPost.taken_on_formatted+self.tmp('site_tz_notice');
|
var sTakenOn = (asPost.taken_on == '0000-00-00 00:00:00')?'':' et prise le '+asPost.taken_on_formatted+self.tmp('site_tz_notice');
|
||||||
var $Image = $('<img>', {'src': asPost.thumb_path, title: 'Click pour zoomer'});
|
var $Image = $('<img>', {'src': asPost.thumb_path, title: 'Click pour zoomer'});
|
||||||
var sVideo = asPost.pic_path.toLowerCase().split('.').pop()=='mov'?'true':'false';
|
var sVideo = asPost.pic_path.toLowerCase().split('.').pop()=='mov'?'true':'false';
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
## Dependencies
|
## Dependencies
|
||||||
* php-mbstring
|
* php-mbstring
|
||||||
* php-imagick
|
* php-imagick
|
||||||
|
* ffprobe (ffmpeg)
|
||||||
## To Do List
|
## To Do List
|
||||||
* require js
|
* require js
|
||||||
* Video support
|
* Video support on markers
|
||||||
* Thumbnail
|
|
||||||
* Preloader
|
|
||||||
* Resize
|
|
||||||
* Navigation
|
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
|
|
||||||
//ADDED-START
|
//ADDED-START
|
||||||
if(self.options.hasVideo) {
|
if(self.options.hasVideo) {
|
||||||
this.$video = $('<video class="lb-video" width="560" height="315" src="" controls autoplay></video>');
|
this.$video = $('<video class="lb-video" src="" controls autoplay></video>');
|
||||||
this.$image.after(this.$video);
|
this.$image.after(this.$video);
|
||||||
this.videoBorderWidth = {
|
this.videoBorderWidth = {
|
||||||
top: parseInt(this.$video.css('border-top-width'), 10),
|
top: parseInt(this.$video.css('border-top-width'), 10),
|
||||||
@@ -306,11 +306,43 @@
|
|||||||
var $hasVideoNav = $lbContainer.hasClass('lb-video-nav');
|
var $hasVideoNav = $lbContainer.hasClass('lb-video-nav');
|
||||||
|
|
||||||
if(self.album[imageNumber].video) {
|
if(self.album[imageNumber].video) {
|
||||||
$video.attr('src', self.album[imageNumber].link);
|
$video.on('loadedmetadata', function(){
|
||||||
var $videoWidth = parseInt($video.attr('width'));
|
var $This = $(this);
|
||||||
var $videoHeight = parseInt($video.attr('height'));
|
|
||||||
|
//TODO merge with image
|
||||||
|
if(self.options.fitImagesInViewport) {
|
||||||
|
windowWidth = $(window).width();
|
||||||
|
windowHeight = $(window).height();
|
||||||
|
maxVideoWidth = windowWidth - self.containerPadding.left - self.containerPadding.right - self.videoBorderWidth.left - self.videoBorderWidth.right - 20;
|
||||||
|
maxVideoHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.videoBorderWidth.top - self.videoBorderWidth.bottom - 120;
|
||||||
|
|
||||||
|
//Check if image size is larger then maxWidth|maxHeight in settings
|
||||||
|
if(self.options.maxWidth && self.options.maxWidth < maxVideoWidth) maxVideoWidth = self.options.maxWidth;
|
||||||
|
if(self.options.maxHeight && self.options.maxHeight < maxVideoWidth) maxVideoHeight = self.options.maxHeight;
|
||||||
|
|
||||||
|
//Is the current image's width or height is greater than the maxImageWidth or maxImageHeight
|
||||||
|
//option than we need to size down while maintaining the aspect ratio.
|
||||||
|
if((this.videoWidth > maxVideoWidth) || (this.videoHeight > maxVideoHeight)) {
|
||||||
|
if ((this.videoWidth / maxVideoWidth) > (this.videoHeight / maxVideoHeight)) {
|
||||||
|
videoWidth = maxVideoWidth;
|
||||||
|
videoHeight = parseInt(this.videoHeight / (this.videoWidth / videoWidth), 10);
|
||||||
|
$This.width(videoWidth);
|
||||||
|
$This.height(videoHeight);
|
||||||
|
} else {
|
||||||
|
videoHeight = maxVideoHeight;
|
||||||
|
videoWidth = parseInt(this.videoWidth / (this.videoHeight / videoHeight), 10);
|
||||||
|
$This.width(videoWidth);
|
||||||
|
$This.height(videoHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sizeContainer($This.width(), $This.height(), 'video');
|
||||||
|
$This.off('loadedmetadata');
|
||||||
|
});
|
||||||
|
|
||||||
this.currentImageIndex = imageNumber;
|
this.currentImageIndex = imageNumber;
|
||||||
self.sizeContainer($videoWidth, $videoHeight, 'video');
|
$video.attr('src', self.album[imageNumber].link);
|
||||||
|
|
||||||
if(!$hasVideoNav) $lbContainer.addClass('lb-video-nav');
|
if(!$hasVideoNav) $lbContainer.addClass('lb-video-nav');
|
||||||
return;
|
return;
|
||||||
@@ -414,10 +446,9 @@
|
|||||||
//ADDED-START
|
//ADDED-START
|
||||||
//var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right;
|
//var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right;
|
||||||
//var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom;
|
//var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom;
|
||||||
var mediaBorderWidth = (media=='image')?(this.imageBorderWidth.left + this.imageBorderWidth.right):(this.videoBorderWidth.left + this.videoBorderWidth.right);
|
var mediaBorderWidth = (media=='image')?this.imageBorderWidth:this.videoBorderWidth;
|
||||||
var mediaBorderHeight = (media=='image')?(this.imageBorderWidth.top + this.imageBorderWidth.bottom):(this.videoBorderWidth.top + this.videoBorderWidth.bottom);
|
var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + mediaBorderWidth.left + mediaBorderWidth.right;
|
||||||
var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + mediaBorderWidth;
|
var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + mediaBorderWidth.top + mediaBorderWidth.bottom;
|
||||||
var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + mediaBorderHeight;
|
|
||||||
//ADDED-END
|
//ADDED-END
|
||||||
|
|
||||||
function postResize() {
|
function postResize() {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ $post-color: #323268;
|
|||||||
$post-bg: #B4BDFF;
|
$post-bg: #B4BDFF;
|
||||||
$message-color: #326526;
|
$message-color: #326526;
|
||||||
$message-bg: #6DFF58;
|
$message-bg: #6DFF58;
|
||||||
$picture-color: #635C28;
|
$media-color: #635C28;
|
||||||
$picture-bg: #F3EC9F;
|
$media-bg: #F3EC9F;
|
||||||
|
|
||||||
//Legend colors
|
//Legend colors
|
||||||
$track-main-color: #00ff78;
|
$track-main-color: #00ff78;
|
||||||
@@ -302,9 +302,9 @@ $legend-color: #222;
|
|||||||
padding-top: 0.5em;
|
padding-top: 0.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.picture {
|
&.media {
|
||||||
background: $picture-bg;
|
background: $media-bg;
|
||||||
color: $picture-color;
|
color: $media-color;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user