first working version
authorChristian Weiske <cweiske@cweiske.de>
Mon, 31 Mar 2014 12:53:00 +0000 (14:53 +0200)
committerChristian Weiske <cweiske@cweiske.de>
Mon, 31 Mar 2014 12:53:00 +0000 (14:53 +0200)
.gitignore [new file with mode: 0644]
src/phancap/Adapter/Cutycapt.php [new file with mode: 0644]
src/phancap/Config.php [new file with mode: 0644]
src/phancap/Executor.php [new file with mode: 0644]
src/phancap/Image.php [new file with mode: 0644]
src/phancap/Options.php
src/phancap/Repository.php [new file with mode: 0644]
www/get.php

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..7426688
--- /dev/null
@@ -0,0 +1 @@
+/www/imgcache
diff --git a/src/phancap/Adapter/Cutycapt.php b/src/phancap/Adapter/Cutycapt.php
new file mode 100644 (file)
index 0000000..173a86c
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+namespace phancap;
+
+class Adapter_Cutycapt
+{
+    public function isAvailable()
+    {
+        //FIXME: setup check for xvfbrun, cutycapt, convert
+    }
+
+    public function render(Image $img, Options $options)
+    {
+        $tmpPath = $img->getPath() . '-tmp';
+        $cmd = 'cutycapt'
+            . ' --url=' . escapeshellarg($options->values['url'])
+            . ' --out-format=' . escapeshellarg($options->values['sformat'])
+            . ' --out=' . escapeshellarg($tmpPath)
+            . ' --max-wait=10000'
+            . ' --min-width=' . $options->values['bwidth'];
+        if ($options->values['bheight'] !== null) {
+            $cmd .= ' --min-height=' . $options->values['bheight'];
+        }
+
+        $xvfbcmd = 'xvfb-run'
+            . ' -e /dev/stdout'
+            . ' --server-args="-screen 0, 1024x768x24"';
+        Executor::run($xvfbcmd . ' ' . $cmd);
+
+        $this->resize($tmpPath, $img, $options);
+    }
+
+    protected function resize($tmpPath, $img, $options)
+    {
+        if ($options->values['sformat'] == 'pdf') {
+            //nothing to resize.
+            rename($tmpPath, $img->getPath());
+            return;
+        }
+
+        $crop = '';
+        if ($options->values['smode'] == 'screen') {
+            $crop = ' -crop ' . $options->values['swidth']
+                . 'x' . $options->values['sheight']
+                . '+0x0';
+        }
+
+        $convertcmd = 'convert'
+            . ' ' . escapeshellarg($tmpPath)
+            . ' -resize ' . $options->values['swidth']
+            . $crop
+            . ' ' . escapeshellarg($img->getPath());
+        Executor::run($convertcmd);
+        //var_dump($convertcmd);die();
+        unlink($tmpPath);
+    }
+}
+?>
diff --git a/src/phancap/Config.php b/src/phancap/Config.php
new file mode 100644 (file)
index 0000000..d69bd04
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+namespace phancap;
+
+class Config
+{
+    /**
+     * Full file system path to cache directory
+     * @var string
+     */
+    public $cacheDir;
+
+    /**
+     * Full URL to cache directory
+     * @var string
+     */
+    public $cacheDirUrl;
+
+
+    public function __construct()
+    {
+        $this->cacheDir    = getcwd() . '/imgcache/';
+        $this->cacheDirUrl = $this->getCurrentUrlDir() . '/imgcache/';
+    }
+
+    public function setupCheck()
+    {
+        if (!is_dir($this->cacheDir)) {
+            throw new \Exception('Cache directory does not exist: ' . $this->cacheDir);
+        }
+        if (!is_writable($this->cacheDir)) {
+            throw new \Exception('Cache directory is not writable: ' . $this->cacheDir);
+        }
+    }
+
+    protected function getCurrentUrl()
+    {
+        if (!isset($_SERVER['REQUEST_SCHEME'])) {
+            $_SERVER['REQUEST_SCHEME'] = 'http';
+        }
+        return $_SERVER['REQUEST_SCHEME'] . '://'
+            . $_SERVER['HTTP_HOST']
+            . preg_replace('/#.*$/', '', $_SERVER['REQUEST_URI']);
+    }
+
+    protected function getCurrentUrlDir()
+    {
+        $url = $this->getCurrentUrl();
+        $url = preg_replace('/\?.*$/', '', $url);
+        if (substr($url, -1) == '/') {
+            return $url;
+        }
+
+        return substr($url, 0, -strlen(basename($url)) - 1);
+    }
+}
+?>
diff --git a/src/phancap/Executor.php b/src/phancap/Executor.php
new file mode 100644 (file)
index 0000000..12a34c3
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+namespace phancap;
+
+class Executor
+{
+    public static function run($cmd)
+    {
+        exec($cmd . ' 2>&1', $arOutput, $exitcode);
+        if ($exitcode != 0) {
+            //FIXME: do something with $arOutput
+            echo implode("\n", $arOutput) . "\n";
+            throw new \Exception('Error running cutycapt', $exitcode);
+        }
+    }
+}
+
+?>
diff --git a/src/phancap/Image.php b/src/phancap/Image.php
new file mode 100644 (file)
index 0000000..6750cfc
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+namespace phancap;
+
+class Image
+{
+    protected $config;
+    public $name;
+
+
+    public function __construct($name)
+    {
+        $this->name = $name;
+    }
+
+    public function getPath()
+    {
+        return $this->config->cacheDir . $this->name;
+    }
+
+    public function getUrl()
+    {
+        return $this->config->cacheDirUrl . $this->name;
+    }
+
+    public function setConfig(Config $config)
+    {
+        $this->config = $config;
+    }
+}
+?>
\ No newline at end of file
index b5c96d9..ee129e8 100644 (file)
@@ -11,6 +11,7 @@ class Options
             'title'     => 'Website URL',
             'default'   => null,
             'type'      => 'url',
+            'required'  => true,
         ),
         'bwidth' => array(
             'title'   => 'Browser width',
@@ -31,7 +32,7 @@ class Options
          */
         'swidth' => array(
             'title'   => 'Screenshot width',
-            'default' => 300,
+            'default' => null,
             'type'    => 'int',
             'min'     => 16,
             'max'     => 8192,
@@ -69,8 +70,14 @@ class Options
         foreach (static::$options as $name => $arOption) {
             $this->values[$name] = $arOption['default'];
             if (!isset($arValues[$name])) {
+                if (isset($arOption['required'])) {
+                    throw new \InvalidArgumentException(
+                        $name . ' parameter missing'
+                    );
+                }
                 continue;
             }
+
             if ($arOption['type'] == 'url') {
                 $this->values[$name] = $this->validateUrl($arValues[$name]);
             } else if ($arOption['type'] == 'int') {
@@ -100,6 +107,9 @@ class Options
 
     protected function calcPageSize()
     {
+        if ($this->values['swidth'] === null) {
+            $this->values['swidth'] = $this->values['bwidth'];
+        }
         if ($this->values['smode'] == 'page') {
             return;
         }
diff --git a/src/phancap/Repository.php b/src/phancap/Repository.php
new file mode 100644 (file)
index 0000000..2058bb6
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+namespace phancap;
+
+class Repository
+{
+    public function setConfig(Config $config)
+    {
+        $this->config = $config;
+    }
+
+    public function getImage(Options $options)
+    {
+        $name = $this->getFilename($options);
+        $img = new Image($name);
+        $img->setConfig($this->config);
+        if (!$this->isAvailable($img)) {
+            $this->render($img, $options);
+        }
+        return $img;
+    }
+
+    public function getFilename(Options $options)
+    {
+        return parse_url($options->values['url'], PHP_URL_HOST)
+            . '-' . md5(\serialize($options->values))
+            . '.' . $options->values['sformat'];
+    }
+
+    public function isAvailable(Image $img)
+    {
+        $path = $img->getPath();
+        if (!file_exists($path)) {
+            return false;
+        }
+        //FIXME: add cache lifetime check
+
+        return true;
+    }
+
+    protected function render(Image $img, Options $options)
+    {
+        $adapter = new Adapter_Cutycapt();
+        $adapter->render($img, $options);
+    }
+}
+?>
index 5212e99..6bb5c7f 100644 (file)
@@ -3,6 +3,8 @@ namespace phancap;
 /**
  * Get a screenshot for a website.
  */
+header('HTTP/1.0 500 Internal Server Error');
+
 if (file_exists(__DIR__ . '/../src/phancap/Autoloader.php')) {
     include_once __DIR__ . '/../src/phancap/Autoloader.php';
     Autoloader::register();
@@ -10,15 +12,30 @@ if (file_exists(__DIR__ . '/../src/phancap/Autoloader.php')) {
     include_once 'phancap/Autoloader.php';
 }
 
+$config = new Config();
+$config->setupCheck();
+
 $options = new Options();
 try {
     $options->parse($_GET);
 } catch (\InvalidArgumentException $e) {
     header('HTTP/1.0 400 Bad Request');
     header('Content-type: text/plain');
-    echo $e->getMessage();
+    echo $e->getMessage() . "\n";
     exit(1);
 }
 
-var_dump($options->values);
+$rep = new Repository();
+$rep->setConfig($config);
+try {
+    $img = $rep->getImage($options);
+    header('HTTP/1.0 302 Found');
+    header('Location: ' . $img->getUrl());
+} catch (\Exception $e) {
+    //FIXME: handle 404s and so properly
+    header('HTTP/1.0 500 Internal Server error');
+    header('Content-type: text/plain');
+    echo $e->getMessage() . "\n";
+    exit(2);
+}
 ?>