preparations for rewrite
This commit is contained in:
3
src/config.inc.php
Normal file
3
src/config.inc.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
define('URL','http://localhost:8081');
|
||||
16
src/helpers.php
Normal file
16
src/helpers.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
function renderTemplate($templatename,$variables=[],$basepath=ROOT.'/src')
|
||||
{
|
||||
ob_start();
|
||||
if(is_array($variables))
|
||||
extract($variables);
|
||||
if(file_exists($basepath.DS.'templates'.DS.$templatename.'.php'))
|
||||
include($basepath.DS.'templates'.DS.$templatename.'.php');
|
||||
else if(file_exists($basepath.DS.'templates'.DS.$templatename))
|
||||
include($basepath.DS.'templates'.DS.$templatename);
|
||||
$rendered = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $rendered;
|
||||
}
|
||||
339
src/http2pic.class.php
Normal file
339
src/http2pic.class.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
/**
|
||||
* http2pic by Christian Haschek (https://haschek.solutions)
|
||||
*
|
||||
* For more info and up2date version of this file visit https://github.com/chrisiaut/http2pic
|
||||
* -------------
|
||||
*
|
||||
* @category Website rendering API
|
||||
* @author Christian Haschek <christian@haschek.at>
|
||||
* @copyright 2015 by HASCHEK SOLUTIONS
|
||||
* @link https://http2pic.haschek.at
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// Stuff you can edit
|
||||
//
|
||||
|
||||
// if true, will save all cmd queries
|
||||
define('DEBUG',false);
|
||||
|
||||
define('MAXTIMEOUT',30);
|
||||
define('ONFAILIMAGE', __DIR__.'/img/pagefailed.jpg');
|
||||
define('ONDOMAINFAILIMAGE', __DIR__.'/img/domainfailed.jpg');
|
||||
|
||||
//rendering engine: wkhtmltoimage or phantomjs
|
||||
define('RENDERINGENGINE','wkhtmltoimage');
|
||||
|
||||
//location of wkhtmltoimage
|
||||
define('WKHTMLTOIMAGEPATH','/usr/sbin/wkhtmltoimage');
|
||||
|
||||
//location of phantomJS
|
||||
define('PHANTOMJSPATH',__DIR__.'/phantomjs');
|
||||
|
||||
//where shoud we store cached images
|
||||
define('CACHEDIR',ROOT.DS.'cache'.DS);
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Only edit from here if you know what you are doing
|
||||
//
|
||||
class http2pic
|
||||
{
|
||||
private $params = array();
|
||||
function __construct($params)
|
||||
{
|
||||
//try to create the cache folder if not exists
|
||||
if (!is_dir(CACHEDIR)) {
|
||||
mkdir(CACHEDIR);
|
||||
}
|
||||
|
||||
$this->params = $params;
|
||||
return $this->paramsPrepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and validate params
|
||||
**/
|
||||
function paramsPrepare()
|
||||
{
|
||||
//validate file type of rendered image
|
||||
switch($this->params['type'])
|
||||
{
|
||||
case 'png': $this->params['type'] = 'png'; break;
|
||||
case 'jpg': $this->params['type'] = 'jpg'; break;
|
||||
default: $this->params['type'] = 'png';
|
||||
}
|
||||
|
||||
//validate timeout
|
||||
if (!$this->params['timeout'] || !is_numeric($this->params['timeout']) || ($this->params['timeout'] > MAXTIMEOUT || $this->params['timeout'] < 1))
|
||||
$this->params['timeout'] = 10;
|
||||
|
||||
//validate viewport
|
||||
if ($this->params['viewport'])
|
||||
{
|
||||
$a = explode('x', $this->params['viewport']);
|
||||
$w = $a[0];
|
||||
$h = $a[1];
|
||||
if (is_numeric($w))
|
||||
$this->params['vp_w'] = $w;
|
||||
if (is_numeric($h))
|
||||
$this->params['vp_h'] = $h;
|
||||
}
|
||||
|
||||
//validate resize width
|
||||
if($this->params['resizewidth'])
|
||||
{
|
||||
if(!is_numeric($this->params['resizewidth']) || $this->params['resizewidth']<1 || $this->params['resizewidth']>8000)
|
||||
unset($this->params['resizewidth']);
|
||||
}
|
||||
|
||||
if(!$this->params['onfail'])
|
||||
$this->params['onfail'] = ONFAILIMAGE;
|
||||
else
|
||||
$this->params['onfail'] = rawurldecode($this->params['onfail']);
|
||||
|
||||
if(!$this->params['ondomainfail'])
|
||||
$this->params['ondomainfail'] = ONDOMAINFAILIMAGE;
|
||||
else
|
||||
$this->params['ondomainfail'] = rawurldecode($this->params['ondomainfail']);
|
||||
|
||||
|
||||
//validate URL and check if exists
|
||||
if ($this->isBase64($this->params['url']))
|
||||
$this->params['url'] = base64_decode($url);
|
||||
else
|
||||
$this->params['url'] = rawurldecode($_GET['url']);
|
||||
|
||||
//if the url is not valid or not responding, show onfail image and leave
|
||||
if(!$this->isURLValid($this->params['url']) || !(($reachableResult = $this->isURLReachable($this->params['url'])) == 0))
|
||||
{
|
||||
header('Content-Type: image/jpeg');
|
||||
header('Content-Disposition: inline; filename="http2png.jpg"');
|
||||
switch ($reachableResult) {
|
||||
case 1:
|
||||
header('HTTP/1.0 404 File Not Found');
|
||||
$result = imagecreatefromjpeg($this->params['onfail']);
|
||||
break;
|
||||
case 2:
|
||||
header('HTTP/1.0 404 Server Not Found');
|
||||
$result = imagecreatefromjpeg($this->params['ondomainfail']);
|
||||
break;
|
||||
}
|
||||
imagejpeg($result, NULL, 100);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//prepare file name
|
||||
$this->params['cache'] = $this->trimToAlphaNumeric($this->params['cache']);
|
||||
$hash = $this->params['cache'].'-'.preg_replace("/[^A-Za-z0-9 ]/", '', $this->params['url']).'.'.$this->params['type'];
|
||||
if (!$this->params['cache'])
|
||||
$hash = md5(time().rand(1,2000)).$hash;
|
||||
$this->params['file'] = CACHEDIR.$hash;
|
||||
|
||||
$this->render();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function render()
|
||||
{
|
||||
//if phantomjs is selected and installed
|
||||
if(RENDERINGENGINE=='phantomjs' && file_exists(PHANTOMJSPATH))
|
||||
return $this->renderPagePHANTOMJS();
|
||||
|
||||
//no? well ok how about WKHTMLToImage?
|
||||
else if(RENDERINGENGINE=='wkhtmltoimage' && file_exists(WKHTMLTOIMAGEPATH))
|
||||
return $this->renderPageWKHTMLTOIMAGE();
|
||||
|
||||
//you're fucked
|
||||
else
|
||||
throw new Exception('No valid rendering engine found');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render using PhantomJS
|
||||
**/
|
||||
function renderPagePHANTOMJS()
|
||||
{
|
||||
$cmd = 'timeout '.$this->params['timeout'].' '.PHANTOMJSPATH;
|
||||
$cmd.= ' --ignore-ssl-errors=yes --ssl-protocol=any '.__DIR__.'/phantom.js ';
|
||||
|
||||
$cmd.= ($this->params['url']);
|
||||
$cmd.= ','.($this->params['file']);
|
||||
$cmd.= ','.$this->params['vp_w'];
|
||||
$cmd.= ','.$this->params['vp_h'];
|
||||
$cmd.= ','.$this->params['js'];
|
||||
|
||||
$cmd = escapeshellcmd($cmd);
|
||||
shell_exec($cmd);
|
||||
$this->params['cmd'] = $cmd;
|
||||
|
||||
$this->postRender();
|
||||
if(DEBUG)
|
||||
{
|
||||
$fp = fopen('debug.log', 'a');
|
||||
fwrite($fp, $cmd."\n");
|
||||
fclose($fp);
|
||||
}
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render using WKHTMLToImage
|
||||
**/
|
||||
function renderPageWKHTMLTOIMAGE()
|
||||
{
|
||||
//escapeshellarg
|
||||
|
||||
//timeout
|
||||
$cmd = 'timeout '.$this->params['timeout'].' '.WKHTMLTOIMAGEPATH;
|
||||
|
||||
//viewport vp_w und vp_h
|
||||
if($this->params['vp_w'])
|
||||
$cmd.=' --width '.$this->params['vp_w'];
|
||||
if($this->params['vp_h'])
|
||||
$cmd.=' --height '.$this->params['vp_h'];
|
||||
|
||||
//js or no js
|
||||
if($this->params['js']=='no')
|
||||
$cmd.=' -n';
|
||||
|
||||
//png or jpg (default)
|
||||
if($this->params['type']=='png')
|
||||
$cmd.=' -f png';
|
||||
|
||||
//add url to cmd
|
||||
$cmd.=' \''.addslashes($this->params['url']).'\'';
|
||||
|
||||
//add storage path to cmd
|
||||
$cmd.=' '.escapeshellarg($this->params['file']);
|
||||
|
||||
$cmd = escapeshellcmd($cmd);
|
||||
shell_exec($cmd);
|
||||
$this->params['cmd'] = $cmd;
|
||||
|
||||
$this->postRender();
|
||||
|
||||
if(DEBUG)
|
||||
{
|
||||
$fp = fopen('debug.log', 'a');
|
||||
fwrite($fp, $cmd."\n");
|
||||
fclose($fp);
|
||||
}
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called after a render took place.
|
||||
* This method will print the image to the user, then
|
||||
* resizes or deletes it
|
||||
*/
|
||||
function postRender()
|
||||
{
|
||||
// resize if necessary
|
||||
if($this->params['resizewidth'])
|
||||
$this->resizeImage($this->params['file']);
|
||||
|
||||
|
||||
//print image to user
|
||||
if ($this->params['type'] === 'png') {
|
||||
|
||||
header('Content-Type: image/png');
|
||||
header('Content-Disposition: inline; filename="'.$this->trimToAlphaNumeric($this->params['url']).'.png"');
|
||||
$result = imagecreatefrompng($this->params['file']);
|
||||
imagepng($result, NULL, 9);
|
||||
}
|
||||
else {
|
||||
header('Content-Type: image/jpeg');
|
||||
header('Content-Disposition: inline; filename="'.$this->trimToAlphaNumeric($this->params['url']).'.jpg"');
|
||||
$result = imagecreatefromjpeg($this->params['file']);
|
||||
imagejpeg($result, NULL, 100);
|
||||
}
|
||||
|
||||
|
||||
//if no cache value specified: delete the image
|
||||
if(!$this->params['cache']) unlink($this->params['file']);
|
||||
}
|
||||
|
||||
function resizeImage($file)
|
||||
{
|
||||
list($width_orig, $height_orig) = getimagesize($file);
|
||||
|
||||
if ($width_orig != $this->params['resizewidth'])
|
||||
{
|
||||
$ratio_orig = $width_orig/$height_orig;
|
||||
$height = $this->params['resizewidth']/$ratio_orig;
|
||||
|
||||
// resample
|
||||
$image_p = imagecreatetruecolor($this->params['resizewidth'], $height);
|
||||
if ($this->params['type'] === 'png')
|
||||
$image = imagecreatefrompng($file);
|
||||
else
|
||||
$image = imagecreatefromjpeg($file);
|
||||
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $this->params['resizewidth'], $height, $width_orig, $height_orig);
|
||||
|
||||
if ($this->params['type'] === 'png')
|
||||
imagepng($image_p, $file, 9);
|
||||
else
|
||||
imagejpeg($image_p, $file, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function isURLValid($url)
|
||||
{
|
||||
if(!$this->startsWith($url,'http://') && !$this->startsWith($url,'https://') && !$this->startsWith($url,'ftp://'))
|
||||
return false;
|
||||
return filter_var($url, FILTER_VALIDATE_URL);
|
||||
}
|
||||
|
||||
function startsWith($haystack,$needle)
|
||||
{
|
||||
$length = strlen($needle);
|
||||
return (substr($haystack,0,$length) === $needle);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/7684771/how-check-if-file-exists-from-the-url
|
||||
*/
|
||||
function isURLReachable($url)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
if(curl_exec($ch) != false){
|
||||
//We were able to connect to a webserver, what did it return?
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if($code < 400) //status code updated so redirects will also work
|
||||
$status = 0;
|
||||
else
|
||||
$status = 1;
|
||||
curl_close($ch);
|
||||
return $status;
|
||||
} else {
|
||||
//We were not able to connect to any webserver so we didn't get a status code
|
||||
//to compare against. There must be a problem with the domain that was supplied.
|
||||
curl_close($ch);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
function trimToAlphaNumeric($string)
|
||||
{
|
||||
return preg_replace("/[^A-Za-z0-9 ]/", '', $string);
|
||||
}
|
||||
|
||||
function isBase64($data)
|
||||
{
|
||||
if (base64_encode(base64_decode($data, true)) === $data)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
206
src/templates/index.html.php
Normal file
206
src/templates/index.html.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Christian Haschek">
|
||||
|
||||
<!-- Open Graph data -->
|
||||
<meta property="og:title" content="HTTP2PIC | Website screenshot API" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="<?=URL?>" />
|
||||
<meta property="og:image" content="<?=URL?>/api.php?url=<?php echo rawurlencode(URL); ?>&cache=1&viewport=1200x630"/>
|
||||
<meta property="og:description" content="An open source website renderer" />
|
||||
<meta property="og:site_name" content="http2pic" />
|
||||
<meta property="article:published_time" content="2015-09-28 00:14:58" />
|
||||
<meta property="article:modified_time" content="2015-09-28 22:08:16" />
|
||||
<meta property="article:tag" content="selfhosted,opensource,screenshot" />
|
||||
|
||||
|
||||
<title>http2pic</title>
|
||||
|
||||
<!-- Bootstrap Core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link href="css/clean-blog.min.css" rel="stylesheet">
|
||||
<link href="css/http2pic.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom Fonts -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||
<link href='//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
|
||||
<link href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
||||
<script src="//oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a href="https://github.com/chrisiaut/http2pic"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
|
||||
|
||||
<!-- Page Header -->
|
||||
<!-- Set your background image for this header on the line below. -->
|
||||
<header id="intro-header" class="intro-header" style="background-image: url('img/home-bg.jpg')">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<div class="site-heading">
|
||||
<h1 style="text-shadow: 0px 0px 12px #000000;">http2pic</h1>
|
||||
<h2 id="loading"><img src="img/loading.gif" /><br/>Loading..</h2>
|
||||
<hr class="small">
|
||||
<span style="text-shadow: 0px 0px 12px #000000;" class="subheading">Give it a try! <input id="showcase_url" type="url" placeholder="eg. http://xkcd.com" />
|
||||
<input id="showcase_button" type="button" value="GO" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<img id="preloader" src="" />
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="container">
|
||||
<h2>How the API works</h2>
|
||||
<div class="well"><h2 ><?=URL?>/api.php?<span style="color:#C73C49">[OPTIONS]</span>&url=<span style="color:#1e90ff">[WEBSITE_URL]</span></h2></div><hr/><br/>
|
||||
<div >
|
||||
<div>
|
||||
<section>
|
||||
<h2>Options</h2>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Values</th>
|
||||
<th>Description</th>
|
||||
<th>Example</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>url</td>
|
||||
<td>http://..</td>
|
||||
<td>The URL of the webpage you'd like to take a screenshot of. Make sure to encode the URL!</td>
|
||||
<td>url=http://xkcd.com</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>width</td>
|
||||
<td>WIDTH</td>
|
||||
<td>Resizes the screenshot to a specified maximum width. Default value is the original size</td>
|
||||
<td>width=400</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>viewport</td>
|
||||
<td>WIDTHxHEIGHT</td>
|
||||
<td>Sets the size of the virtual screen rendering the page. Default: smart width, full height</td>
|
||||
<td>viewport=1980x1080</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>js</td>
|
||||
<td>yes|no</td>
|
||||
<td>Allows you to enable/disable JavaScript in the rendered Website. Default value: yes</td>
|
||||
<td>js=yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>jpg|png</td>
|
||||
<td>Sets the output file format of the rendered screenshot. Default value: jpg</td>
|
||||
<td>type=png</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>onfail</td>
|
||||
<td>[url of .jpg]</td>
|
||||
<td>If the page can't be reached, this image will be displayed instead</td>
|
||||
<td><?=URL?>/img/pagefailed.jpg</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ondomainfail</td>
|
||||
<td>[url of .jpg]</td>
|
||||
<td>If the web server can't be reached, this image will be displayed instead</td>
|
||||
<td><?=URL?>/img/domainfailed.jpg</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>cache</td>
|
||||
<td>[any alphanumeric string]</td>
|
||||
<td>If provided, caches the rendered image (based on the URL) so it loads faster on next request. The same cache id with the same url will return the cached image. Change cache id to re-render</td>
|
||||
<td>f01d0</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
<div class="6u">
|
||||
<section>
|
||||
<h1>Examples</h1>
|
||||
|
||||
<h3>Simple link via img tag</h3>
|
||||
<p class="margin-bottom"></p>
|
||||
<p>
|
||||
<pre><code class="php">
|
||||
<?php
|
||||
$url = 'http://www.xkcd.com';
|
||||
$query = 'type=jpg&viewport=1200x330&url='.rawurlencode($url);
|
||||
$img="<?=URL?>/api.php?$query";
|
||||
|
||||
echo "<img src='$img' />";
|
||||
?>
|
||||
</code></pre>
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Proxy script to download the image via curl</h3>
|
||||
<p class="margin-bottom"></p>
|
||||
<p>
|
||||
<pre><code class="php">
|
||||
<?php
|
||||
$targeturl = 'http://www.xkcd.com';
|
||||
$url = '<?=URL?>/api.php?url='.rawurlencode($targeturl);
|
||||
|
||||
$ch = curl_init($url);
|
||||
$fp = fopen('xkcd.jpg', 'wb');
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 0);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
?>
|
||||
</code></pre>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<p class="copyright text-muted">Copyright © Haschek Solutions <br/><a href="https://www.haschek-solutions.com"><img src="img/hs_logo.png" /></a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="js/jquery.js"></script>
|
||||
|
||||
<!-- Bootstrap Core JavaScript -->
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
|
||||
<!-- Custom Theme JavaScript -->
|
||||
<script src="js/clean-blog.min.js"></script>
|
||||
<script src="js/http2pic.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user