mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-17 21:48:00 +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'])
|
||||
echo json_encode($pm->getURLInfo($_REQUEST['geturlinfo']));
|
||||
else if($_REQUEST['a']=='oembed')
|
||||
echo json_encode($pm->oembed($_REQUEST['url'],$_REQUEST['t']));
|
||||
else
|
||||
echo json_encode(array('status'=>'ERR'));
|
||||
|
||||
116
inc/core.php
116
inc/core.php
@@ -165,15 +165,27 @@ function renderImage($data)
|
||||
case 'mp4':
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -201,4 +213,92 @@ function render($variables=null)
|
||||
if(is_array($variables))
|
||||
extract($variables);
|
||||
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;
|
||||
if(file_exists($path))
|
||||
{
|
||||
$type = $this->getTypeOfHash($hash);
|
||||
$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
|
||||
@@ -44,8 +55,8 @@ class PictshareModel extends Model
|
||||
|
||||
if($this->isImage($el))
|
||||
$data['hash']=$el;
|
||||
else if($el=='mp4')
|
||||
$data['mp4'] = 1;
|
||||
else if($el=='mp4' || $el=='raw' || $el=='preview' || $el=='webm')
|
||||
$data[$el] = 1;
|
||||
else if($this->isSize($el))
|
||||
$data['size'] = $el;
|
||||
else if($this->isRotation($el))
|
||||
@@ -179,6 +190,9 @@ class PictshareModel extends Model
|
||||
function getCacheName($data)
|
||||
{
|
||||
ksort($data);
|
||||
unset($data['raw']);
|
||||
unset($data['preview']);
|
||||
unset($data['raw']);
|
||||
$name = false;
|
||||
foreach($data as $key=>$val)
|
||||
{
|
||||
@@ -190,7 +204,6 @@ class PictshareModel extends Model
|
||||
foreach($val as $valdata)
|
||||
$name[] = $valdata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(is_array($name))
|
||||
@@ -332,7 +345,14 @@ class PictshareModel extends Model
|
||||
|
||||
mkdir(ROOT.DS.'upload'.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
|
||||
if($type=='jpg')
|
||||
@@ -679,4 +699,89 @@ class PictshareModel extends Model
|
||||
|
||||
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