mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-18 05:58:01 +00:00
extended mp4 support and rendered in html like imgur does with "gifv"
This commit is contained in:
@@ -42,5 +42,7 @@ else if($_REQUEST['base64'])
|
|||||||
}
|
}
|
||||||
else if($_REQUEST['geturlinfo'])
|
else if($_REQUEST['geturlinfo'])
|
||||||
echo json_encode($pm->getURLInfo($_REQUEST['geturlinfo']));
|
echo json_encode($pm->getURLInfo($_REQUEST['geturlinfo']));
|
||||||
|
else if($_REQUEST['a']=='oembed')
|
||||||
|
echo json_encode($pm->oembed($_REQUEST['url'],$_REQUEST['t']));
|
||||||
else
|
else
|
||||||
echo json_encode(array('status'=>'ERR'));
|
echo json_encode(array('status'=>'ERR'));
|
||||||
|
|||||||
114
inc/core.php
114
inc/core.php
@@ -165,15 +165,27 @@ function renderImage($data)
|
|||||||
case 'mp4':
|
case 'mp4':
|
||||||
if(!$cached)
|
if(!$cached)
|
||||||
{
|
{
|
||||||
$path = $pm->resizeMP4($data,$cachepath);
|
$pm->resizeMP4($data,$cachepath);
|
||||||
|
$path = $cachepath;
|
||||||
}
|
}
|
||||||
header('Content-type: video/mp4');
|
|
||||||
//header('Content-type: video/mpeg');
|
|
||||||
header('Content-disposition: inline');
|
|
||||||
header("Content-Transfer-Encoding: binary");
|
|
||||||
header("Content-Length: ".filesize($path));
|
|
||||||
|
|
||||||
readfile($path);
|
if(filesize($path)==0) //if there was an error and the file is 0 bytes, use the original
|
||||||
|
$path = ROOT.DS.'upload'.DS.$hash.DS.$hash;
|
||||||
|
|
||||||
|
if($data['raw'])
|
||||||
|
{
|
||||||
|
serveFile($path, '/raw/'.$hash,'video/mp4');
|
||||||
|
}
|
||||||
|
else if($data['preview'])
|
||||||
|
{
|
||||||
|
$file = $path.'.jpg';
|
||||||
|
if(!file_exists($file))
|
||||||
|
$pm->saveFirstFrameOfMP4($path);
|
||||||
|
header ("Content-type: image/jpeg");
|
||||||
|
readfile($file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
renderMP4($path,$data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,3 +214,91 @@ function render($variables=null)
|
|||||||
extract($variables);
|
extract($variables);
|
||||||
include (ROOT . DS . 'template.php');
|
include (ROOT . DS . 'template.php');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderMP4($path,$data)
|
||||||
|
{
|
||||||
|
$pm = new PictshareModel;
|
||||||
|
$hash = $data['hash'];
|
||||||
|
if($data['size'])
|
||||||
|
$hash = $data['size'].'/'.$hash;
|
||||||
|
$info = $pm->getSizeOfMP4($path);
|
||||||
|
$width = $info['width'];
|
||||||
|
$height = $info['height'];
|
||||||
|
include (ROOT . DS . 'template_mp4.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// from: https://stackoverflow.com/questions/25975943/php-serve-mp4-chrome-provisional-headers-are-shown-request-is-not-finished-ye
|
||||||
|
//
|
||||||
|
function serveFile($filename, $filename_output = false, $mime = 'application/octet-stream')
|
||||||
|
{
|
||||||
|
$buffer_size = 8192;
|
||||||
|
$expiry = 90; //days
|
||||||
|
|
||||||
|
if(!file_exists($filename))
|
||||||
|
{
|
||||||
|
throw new Exception('File not found: ' . $filename);
|
||||||
|
}
|
||||||
|
if(!is_readable($filename))
|
||||||
|
{
|
||||||
|
throw new Exception('File not readable: ' . $filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
header_remove('Cache-Control');
|
||||||
|
header_remove('Pragma');
|
||||||
|
|
||||||
|
$byte_offset = 0;
|
||||||
|
$filesize_bytes = $filesize_original = filesize($filename);
|
||||||
|
|
||||||
|
header('Accept-Ranges: bytes', true);
|
||||||
|
header('Content-Type: ' . $mime, true);
|
||||||
|
|
||||||
|
if($filename_output)
|
||||||
|
{
|
||||||
|
header('Content-Disposition: attachment; filename="' . $filename_output . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content-Range header for byte offsets
|
||||||
|
if (isset($_SERVER['HTTP_RANGE']) && preg_match('%bytes=(\d+)-(\d+)?%i', $_SERVER['HTTP_RANGE'], $match))
|
||||||
|
{
|
||||||
|
$byte_offset = (int) $match[1];//Offset signifies where we should begin to read the file
|
||||||
|
if (isset($match[2]))//Length is for how long we should read the file according to the browser, and can never go beyond the file size
|
||||||
|
{
|
||||||
|
$filesize_bytes = min((int) $match[2], $filesize_bytes - $byte_offset);
|
||||||
|
}
|
||||||
|
header("HTTP/1.1 206 Partial content");
|
||||||
|
header(sprintf('Content-Range: bytes %d-%d/%d', $byte_offset, $filesize_bytes - 1, $filesize_original)); ### Decrease by 1 on byte-length since this definition is zero-based index of bytes being sent
|
||||||
|
}
|
||||||
|
|
||||||
|
$byte_range = $filesize_bytes - $byte_offset;
|
||||||
|
|
||||||
|
header('Content-Length: ' . $byte_range);
|
||||||
|
header('Expires: ' . date('D, d M Y H:i:s', time() + 60 * 60 * 24 * $expiry) . ' GMT');
|
||||||
|
|
||||||
|
$buffer = '';
|
||||||
|
$bytes_remaining = $byte_range;
|
||||||
|
|
||||||
|
$handle = fopen($filename, 'r');
|
||||||
|
if(!$handle)
|
||||||
|
{
|
||||||
|
throw new Exception("Could not get handle for file: " . $filename);
|
||||||
|
}
|
||||||
|
if (fseek($handle, $byte_offset, SEEK_SET) == -1)
|
||||||
|
{
|
||||||
|
throw new Exception("Could not seek to byte offset %d", $byte_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($bytes_remaining > 0)
|
||||||
|
{
|
||||||
|
$chunksize_requested = min($buffer_size, $bytes_remaining);
|
||||||
|
$buffer = fread($handle, $chunksize_requested);
|
||||||
|
$chunksize_real = strlen($buffer);
|
||||||
|
if ($chunksize_real == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$bytes_remaining -= $chunksize_real;
|
||||||
|
echo $buffer;
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,8 +21,19 @@ class PictshareModel extends Model
|
|||||||
$path = ROOT.DS.'upload'.DS.$hash.DS.$file;
|
$path = ROOT.DS.'upload'.DS.$hash.DS.$file;
|
||||||
if(file_exists($path))
|
if(file_exists($path))
|
||||||
{
|
{
|
||||||
|
$type = $this->getTypeOfHash($hash);
|
||||||
$byte = filesize($path);
|
$byte = filesize($path);
|
||||||
return array('status'=>'ok','hash'=>$hash,'cachename'=>$file,'size'=>$byte,'humansize'=>$html->renderSize($byte));
|
if($type=='mp4')
|
||||||
|
{
|
||||||
|
$info = $this->getSizeOfMP4($path);
|
||||||
|
$width = intval($info['width']);
|
||||||
|
$height = intval($info['height']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list($width, $height) = getimagesize($path);
|
||||||
|
}
|
||||||
|
return array('status'=>'ok','hash'=>$hash,'cachename'=>$file,'size'=>$byte,'humansize'=>$html->renderSize($byte),'width'=>$width,'height'=>$height,'type'=>$type);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -44,8 +55,8 @@ class PictshareModel extends Model
|
|||||||
|
|
||||||
if($this->isImage($el))
|
if($this->isImage($el))
|
||||||
$data['hash']=$el;
|
$data['hash']=$el;
|
||||||
else if($el=='mp4')
|
else if($el=='mp4' || $el=='raw' || $el=='preview' || $el=='webm')
|
||||||
$data['mp4'] = 1;
|
$data[$el] = 1;
|
||||||
else if($this->isSize($el))
|
else if($this->isSize($el))
|
||||||
$data['size'] = $el;
|
$data['size'] = $el;
|
||||||
else if($this->isRotation($el))
|
else if($this->isRotation($el))
|
||||||
@@ -179,6 +190,9 @@ class PictshareModel extends Model
|
|||||||
function getCacheName($data)
|
function getCacheName($data)
|
||||||
{
|
{
|
||||||
ksort($data);
|
ksort($data);
|
||||||
|
unset($data['raw']);
|
||||||
|
unset($data['preview']);
|
||||||
|
unset($data['raw']);
|
||||||
$name = false;
|
$name = false;
|
||||||
foreach($data as $key=>$val)
|
foreach($data as $key=>$val)
|
||||||
{
|
{
|
||||||
@@ -190,7 +204,6 @@ class PictshareModel extends Model
|
|||||||
foreach($val as $valdata)
|
foreach($val as $valdata)
|
||||||
$name[] = $valdata;
|
$name[] = $valdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(is_array($name))
|
if(is_array($name))
|
||||||
@@ -332,7 +345,14 @@ class PictshareModel extends Model
|
|||||||
|
|
||||||
mkdir(ROOT.DS.'upload'.DS.$hash);
|
mkdir(ROOT.DS.'upload'.DS.$hash);
|
||||||
$file = ROOT.DS.'upload'.DS.$hash.DS.$hash;
|
$file = ROOT.DS.'upload'.DS.$hash.DS.$hash;
|
||||||
$status = file_put_contents($file, file_get_contents($url));
|
|
||||||
|
|
||||||
|
if($type=='mp4')
|
||||||
|
{
|
||||||
|
$this->saveFirstFrameOfMP4($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($file, file_get_contents($url));
|
||||||
|
|
||||||
//remove all exif data from jpeg
|
//remove all exif data from jpeg
|
||||||
if($type=='jpg')
|
if($type=='jpg')
|
||||||
@@ -679,4 +699,89 @@ class PictshareModel extends Model
|
|||||||
|
|
||||||
return $mp4file;
|
return $mp4file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveAsMP4($source,$target)
|
||||||
|
{
|
||||||
|
$bin = escapeshellcmd(ROOT.DS.'bin'.DS.'ffmpeg');
|
||||||
|
$source = escapeshellarg($source);
|
||||||
|
$target = escapeshellarg($target);
|
||||||
|
$cmd = "$bin -y -i $source -c:v libx264 $target";
|
||||||
|
|
||||||
|
system($cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveFirstFrameOfMP4($path)
|
||||||
|
{
|
||||||
|
//$path = ROOT.DS.'upload'.DS.$hash.DS.$hash;
|
||||||
|
$jpgpath = $path.'.jpg';
|
||||||
|
$bin = escapeshellcmd(ROOT.DS.'bin'.DS.'ffmpeg');
|
||||||
|
$file = escapeshellarg($path);
|
||||||
|
$cmd = "$bin -y -i $file -vframes 1 -f image2 $jpgpath";
|
||||||
|
|
||||||
|
system($cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
//from https://stackoverflow.com/questions/4847752/how-to-get-video-duration-dimension-and-size-in-php
|
||||||
|
function getSizeOfMP4($video)
|
||||||
|
{
|
||||||
|
$video = escapeshellarg($video);
|
||||||
|
$bin = escapeshellcmd(ROOT.DS.'bin'.DS.'ffmpeg');
|
||||||
|
$command = $bin . ' -i ' . $video . ' -vstats 2>&1';
|
||||||
|
$output = shell_exec($command);
|
||||||
|
|
||||||
|
$regex_sizes = "/Video: ([^,]*), ([^,]*), ([0-9]{1,4})x([0-9]{1,4})/";
|
||||||
|
if (preg_match($regex_sizes, $output, $regs)) {
|
||||||
|
$codec = $regs [1] ? $regs [1] : null;
|
||||||
|
$width = $regs [3] ? $regs [3] : null;
|
||||||
|
$height = $regs [4] ? $regs [4] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$regex_duration = "/Duration: ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}).([0-9]{1,2})/";
|
||||||
|
if (preg_match($regex_duration, $output, $regs)) {
|
||||||
|
$hours = $regs [1] ? $regs [1] : null;
|
||||||
|
$mins = $regs [2] ? $regs [2] : null;
|
||||||
|
$secs = $regs [3] ? $regs [3] : null;
|
||||||
|
$ms = $regs [4] ? $regs [4] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array ('codec' => $codec,
|
||||||
|
'width' => $width,
|
||||||
|
'height' => $height,
|
||||||
|
'hours' => $hours,
|
||||||
|
'mins' => $mins,
|
||||||
|
'secs' => $secs,
|
||||||
|
'ms' => $ms
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function oembed($url,$type)
|
||||||
|
{
|
||||||
|
$data = $this->getURLInfo($url);
|
||||||
|
$rawurl = $url.'/raw';
|
||||||
|
switch($type)
|
||||||
|
{
|
||||||
|
case 'json':
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
return array( "version"=> "1.0",
|
||||||
|
"type"=> "video",
|
||||||
|
"thumbnail_url"=>$url.'/preview',
|
||||||
|
"thumbnail_width"=>$data['width'],
|
||||||
|
"thumbnail_height"=>$data['height'],
|
||||||
|
"width"=> $data['width'],
|
||||||
|
"height"=> $data['height'],
|
||||||
|
"title"=> "PictShare",
|
||||||
|
//"url"=> $url.'/raw',
|
||||||
|
"provider_name"=> "PictShare",
|
||||||
|
"provider_url"=> DOMAINPATH,
|
||||||
|
"html"=> '<video id="video" poster="'.$url.'/preview'.'" preload="auto" autoplay="autoplay" muted="muted" loop="loop" webkit-playsinline>
|
||||||
|
<source src="'.$rawurl.'" type="video/mp4">
|
||||||
|
</video>');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'xml':
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
template_mp4.php
Normal file
47
template_mp4.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>PictShare</title>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
|
||||||
|
<meta name="copyright" content="Copyright <?php echo date("Y"); ?> PictShare" />
|
||||||
|
<meta id="viewport" name="viewport" content="width=<?php echo $width ?>, user-scalable=yes" />
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
#content, #video {
|
||||||
|
width: <?php echo $width ?>px;
|
||||||
|
height: <?php echo $height ?>px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<link rel="alternate" type="application/json+oembed" href="<?php echo DOMAINPATH; ?>backend.php?a=oembed&t=json&url=<?php echo rawurlencode(DOMAINPATH.$hash); ?>" title="PictShare" />
|
||||||
|
|
||||||
|
<link rel="canonical" href="<?php echo DOMAINPATH.$hash; ?>" />
|
||||||
|
|
||||||
|
<meta property="og:site_name" content="Imgur" />
|
||||||
|
<meta property="og:url" content="<?php echo DOMAINPATH.$hash; ?>" />
|
||||||
|
<meta property="og:title" content="PictShare MP4" />
|
||||||
|
<meta property="og:type" content="video.other" />
|
||||||
|
|
||||||
|
<meta property="og:image" content="<?php echo DOMAINPATH.'preview/'.$hash; ?>" />
|
||||||
|
<meta property="og:image:width" content="<?php echo $width ?>" />
|
||||||
|
<meta property="og:image:height" content="<?php echo $height ?>" />
|
||||||
|
<meta property="og:description" content="PictShare MP4 Video" />
|
||||||
|
<meta property="og:video" content="<?php echo DOMAINPATH.$hash; ?>" />
|
||||||
|
<meta property="og:video:secure_url" content="<?php echo DOMAINPATH.$hash; ?>" />
|
||||||
|
<meta property="og:video:type" content="application/x-shockwave-flash" />
|
||||||
|
<meta property="og:video:width" content="<?php echo $width ?>" />
|
||||||
|
<meta property="og:video:height" content="<?php echo $height ?>" />
|
||||||
|
<meta property="og:video:type" content="video/mp4" />
|
||||||
|
<meta property="og:video:width" content="<?php echo $width ?>" />
|
||||||
|
<meta property="og:video:height" content="<?php echo $height ?>" />
|
||||||
|
</head>
|
||||||
|
<body id="body">
|
||||||
|
<div id="content">
|
||||||
|
<video id="video" poster="<?php echo DOMAINPATH.'preview/'.$hash; ?>" preload="auto" autoplay="autoplay" muted="muted" loop="loop" webkit-playsinline>
|
||||||
|
<source src="<?php echo DOMAINPATH.'raw/'.$hash; ?>" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user