From 7c4bd0136d3e63e8c90de59f5b6c23f6baa7f9c2 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Tue, 8 Oct 2013 23:02:31 +0200 Subject: [PATCH] Thumbnail generator --- config.ini | 8 ++ public_html/media/css/upload.css | 2 +- public_html/media/img/thumb-swf.png | Bin 0 -> 1820 bytes ...thumb-unavailable.png => thumb-upload.png} | Bin public_html/media/img/thumb.png | Bin 0 -> 948 bytes src/Controllers/PostController.php | 103 ++++++++++++++++-- src/Helpers/ThumbnailHelper.php | 47 ++++++++ src/Views/post-list.phtml | 2 +- thumbs/.gitignore | 2 + 9 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 public_html/media/img/thumb-swf.png rename public_html/media/img/{thumb-unavailable.png => thumb-upload.png} (100%) create mode 100644 public_html/media/img/thumb.png create mode 100644 src/Helpers/ThumbnailHelper.php create mode 100644 thumbs/.gitignore diff --git a/config.ini b/config.ini index 3f7f7be9..831968ed 100644 --- a/config.ini +++ b/config.ini @@ -5,8 +5,16 @@ prettyPrint=1 [main] dbPath=./db.sqlite filesPath=./files/ +thumbsPath=./files/ +mediaPath=./public_html/media/ title=szurubooru +[browsing] +postsPerPage=20 +thumbWidth=150 +thumbHeight=150 +thumbStyle=outside + [registration] emailActivation = 0 adminActivation = 0 diff --git a/public_html/media/css/upload.css b/public_html/media/css/upload.css index 40a106c7..1629206c 100644 --- a/public_html/media/css/upload.css +++ b/public_html/media/css/upload.css @@ -31,7 +31,7 @@ width: 100px; height: 100px; line-height: 100px; - background-image: url('../img/thumb-unavailable.png'); + background-image: url('../img/thumb-upload.png'); border: 1px solid black; vertical-align: middle; text-align: center; diff --git a/public_html/media/img/thumb-swf.png b/public_html/media/img/thumb-swf.png new file mode 100644 index 0000000000000000000000000000000000000000..3100aa53a901660ab1ea1713b386f39704ba8e5c GIT binary patch literal 1820 zcmZ{lX*ipS8iqe=@1m3*Vu}3_+DSvmq)KCHL=sDUv7Xo(duxeggy|y0R%2=HnxQR% zuj*8osZv|f2CXinscLJR5_BvV9kiX!$(euW$NBNR&-J|T_58ixJTDLYK^2$^000LG zZY~t5m;YBva(~bJF;bI@!jW+R&{nknYnZ$=%Oq3qPC)Cp?y@u(oOPp+0U*r`0MfYt z@L9S_mjD0@2>>gh0D!FofPWm>xgB^jB!hi9c#)HlMH~FLKTcI;-mi#jGwf$ZERcGOu+L zJu6&>h4_8N(CoxYb{=9$*FgRRtLG6WsVjT+DBQ!Z&n$6{>59Io*cgXUj1i+FzS*o+ z8~zx`dRlq1pleacv!x|3l1p0U>E!%Y`46PbR{6*dnmA)?s>PCX?$;G}^NNBl^(XY} zt=z5G^+czmK}^DESmyCOZIaluYKBfzc}*5ID%nHC=z z%MF*Ujl^`d$K=*qR}}hVQ88EeIz^Az*tY1xru{iGl~|-!2K0GXVGhQ3Hx+OoFiZz> zm+u;Y=nFcn?vZl)!I3Lrc?fN9D@qwtcMLSu9&w@)h6Ny1{e_k9FB0}kY6M-MqywWsrkcDAN6L`x&^dDKjb&H-;TMI3&uS?&N0~scB-?)9-;p!=$oG1WOPp zLPPj7+Sn1}+a>7y)}&0k0ew~gW2r@U$K0pixSm*|@Dy{&W=r?3BeSUK6R$4^JVif( zF?syC%vf&n+F-@~rfItMC5C`*A;6XRISp7ugtFYq@yh9%_76D0EX8z%5V!gY#Hx&{ zj3g0vy4)x=H0$4NLK*C!zRcJzck`mVQ+zhMW(Zhtm<5 z`F%iKIOGUdXQrO&Z+y7c-*M@CrG|{g`w3*A{(zAQWTqQA6gL9@ zdQLSYuI;s$u?k~0t9a~cRH-?`D1P1B44-*yt6Z*tf&3RKK8qtF?B&g*Wi-XHoa#WxG#Io0{{joM(dqjraK(}sSL9T;vw3p{<5 zWncQqGF4z?q2sqGJm5OZ&{Qb?V0e;yPF*nQ`!!bY;864!y{E-EtzUh{D|h-ruZb#G z_b~+7|I?)Ux@_>!Qui%Rr%MON7Zkh-(0jk7WxjcejOAM8{b}^^iipkZMVK50$#25* z^>S?)_gT}IBxfN!d}8@D)R2?$-2w7=--?=Y{BMh-Q%$gk`kM} zEn$osu5-C_J2G`!wTeth-?Pd(xovjJohU+3)Ux-z9<_wMC>Xz)0Agr9F^OG#N-={r z@yN5SHQ-hwC~j%LL564d93QO4NCL%}gNMArTFwP>{w z6m9KGcA#sUOU0MMj&ijF;t!X^xHzUW>;BBG6S7lC8k9tZ&mciPtpg7yvjz{l{Oo_A zf1&`NY@=7TJ^b=2(bI6Tw@JS4aKD*WdNx@l7C09BA4)GIbL02?Ku@MTn*O>@q$Ni+*xRUm6cBmOfGk$=f z+KDyoJVd*&jRo*s@WZP<=zxf+Z}5=j2pV&}IYas>Oqs6!OnMkI0(+JbAvFMnJYjw8 z1o9XX1ENq^YilgZ#uACbB9UF2YB~Q?IG;d|iA??fg|U|AlhOh}aP@F$bqdY+54_V!Z literal 0 HcmV?d00001 diff --git a/public_html/media/img/thumb-unavailable.png b/public_html/media/img/thumb-upload.png similarity index 100% rename from public_html/media/img/thumb-unavailable.png rename to public_html/media/img/thumb-upload.png diff --git a/public_html/media/img/thumb.png b/public_html/media/img/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..d079c1e4546c3c08593a3e460af41cd6ac37b94f GIT binary patch literal 948 zcmeAS@N?(olHy`uVBq!ia0vp^(?FPm1x)VD1X3*Nj=qiz3>*8o|0J>k`HaOu?oJHr z&dIz4aukw1f_xbms?-=58d?|_egTCVUNA6}8Za=tN?>5Hn!&&zUNC1@pbb!hDaqU2 zg@IvJud)%4$64SJSq!8-z}W3%wjEH#T2B|pkcwMx?|4o-&E znc54iwZjAQ-PRpAmlJcacIFO-T}&YfuQ-F6mnsK{w$&w+Uf9hT=Jc*Lf_tNIhUpf& z179cXY6y3E=@=lqklFLgm1jMio*JqO zUVUMCa)q0pmxpOqSkNlL%R<3l=S`op>iH3-kiBVQ3tt&-6}WnJ=g}4GeDpj@%gO>* z8DHiOmYo|v=M_ieiZeWmew~+VU2$*IQN>m3*G%llGW6D26m?d;cZJ#KQ_8FE+H|<2 zp1Sg^On9p4_HUQ-E~;8A3ZG_Kb>7H*e%rGb7N=!o56)iv9>`qNY-u$=weQ@NS&NH5 zJbQ6nw)o5LhZkPD?kri`eELaJ%r3jrrCEY{vB6SChL+RUHS^D%HpTnWLl|uva!&WuCI}>v5@6DAm z<|zw`ZcS`GmFTUu>R|PzK;Ao3CT3k&w1txO>ZFbG}B+l-context->user, Privilege::ListPosts, PostSafety::toString($safety)); + }); //todo safety [user choice] - //todo safety [user privileges] + + $whereSql = 'WHERE safety IN (' . R::genSlots($allowedSafety) . ')'; + $params = array_merge($params, $allowedSafety); + //todo construct WHERE based on filters - $whereSql = ''; //todo construct ORDER based on filers $orderSql = 'ORDER BY upload_date DESC'; - $limitSql = 'LIMIT :limit OFFSET :offset'; + $limitSql = 'LIMIT ? OFFSET ?'; + $postsPerPage = intval($this->config->browsing->postsPerPage); + $params[] = $postsPerPage; + $params[] = ($page - 1) * $postsPerPage; $posts = R::findAll('post', sprintf('%s %s %s', $whereSql, $orderSql, $limitSql), $params); $this->context->transport->posts = $posts; } + + /** * @route /post/upload */ @@ -123,12 +134,12 @@ class PostController move_uploaded_file($suppliedFile['tmp_name'], $path); R::store($dbPost); - //todo: generate thumbnail - $this->context->transport->success = true; } } + + /** * Action that decorates the page containing the post. * @route /post/{id} @@ -146,6 +157,82 @@ class PostController $this->context->transport->post = $post; } + + + /** + * Action that renders the thumbnail of the requested file and sends it to user. + * @route /post/thumb/{id} + */ + public function thumbAction($id) + { + $this->context->layoutName = 'layout-file'; + + $post = R::findOne('post', 'id = ?', [$id]); + if (!$post) + throw new SimpleException('Invalid post ID "' . $id . '"'); + + PrivilegesHelper::confirmWithException($this->context->user, Privilege::ViewPost); + PrivilegesHelper::confirmWithException($this->context->user, Privilege::ViewPost, PostSafety::toString($post->safety)); + + $path = $this->config->main->thumbsPath . DIRECTORY_SEPARATOR . $post->name . '.png'; + if (!file_exists($path)) + { + $srcPath = $this->config->main->thumbsPath . DIRECTORY_SEPARATOR . $post->name; + $dstPath = $path; + $dstWidth = $this->config->browsing->thumbWidth; + $dstHeight = $this->config->browsing->thumbHeight; + + switch($post->mime_type) + { + case 'image/jpeg': + $srcImage = imagecreatefromjpeg($srcPath); + break; + case 'image/png': + $srcImage = imagecreatefrompng($srcPath); + break; + case 'image/gif': + $srcImage = imagecreatefromgif($srcPath); + break; + case 'application/x-shockwave-flash': + $path = $this->config->main->mediaPath . DIRECTORY_SEPARATOR . 'img' . DIRECTORY_SEPARATOR . 'thumb-swf.png'; + break; + default: + $path = $this->config->main->mediaPath . DIRECTORY_SEPARATOR . 'img' . DIRECTORY_SEPARATOR . 'thumb.png'; + break; + } + + if (isset($srcImage)) + { + switch ($this->config->browsing->thumbStyle) + { + case 'outside': + $dstImage = ThumbnailHelper::cropOutside($srcImage, $dstWidth, $dstHeight); + break; + case 'inside': + $dstImage = ThumbnailHelper::cropInside($srcImage, $dstWidth, $dstHeight); + break; + default: + throw new SimpleException('Unknown thumbnail crop style'); + } + + imagepng($dstImage, $dstPath); + imagedestroy($srcImage); + imagedestroy($dstImage); + } + } + if (!is_readable($path)) + throw new SimpleException('Thumbnail file is not readable'); + + \Chibi\HeadersHelper::set('Pragma', 'public'); + \Chibi\HeadersHelper::set('Cache-Control', 'max-age=86400'); + \Chibi\HeadersHelper::set('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + 86400)); + + $this->context->transport->mimeType = 'image/png'; + $this->context->transport->filePath = $path; + } + + + /** * Action that renders the requested file itself and sends it to user. * @route /post/retrieve/{name} @@ -171,6 +258,8 @@ class PostController $this->context->transport->filePath = $path; } + + /** * @route /favorites */ diff --git a/src/Helpers/ThumbnailHelper.php b/src/Helpers/ThumbnailHelper.php new file mode 100644 index 00000000..4f9d1ccf --- /dev/null +++ b/src/Helpers/ThumbnailHelper.php @@ -0,0 +1,47 @@ + ($srcHeight / $srcWidth)) + { + $h = $srcHeight; + $w = $h * $dstWidth / $dstHeight; + } + else + { + $w = $srcWidth; + $h = $w * $dstHeight / $dstWidth; + } + $x = ($srcWidth - $w) / 2; + $y = ($srcHeight - $h) / 2; + + $dstImage = imagecreatetruecolor($dstWidth, $dstHeight); + imagecopyresampled($dstImage, $srcImage, 0, 0, $x, $y, $dstWidth, $dstHeight, $w, $h); + return $dstImage; + } + + public static function cropInside($srcImage, $dstWidth, $dstHeight) + { + $srcWidth = imagesx($srcImage); + $srcHeight = imagesy($srcImage); + + if (($dstHeight / $dstWidth) < ($srcHeight / $srcWidth)) + { + $h = $dstHeight; + $w = $h * $srcWidth / $srcHeight; + } + else + { + $w = $dstWidth; + $h = $w * $srcHeight / $srcWidth; + } + + $dstImage = imagecreatetruecolor($w, $h); + imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $w, $h, $srcWidth, $srcHeight); + return $dstImage; + } +} diff --git a/src/Views/post-list.phtml b/src/Views/post-list.phtml index bfec607c..207e1fba 100644 --- a/src/Views/post-list.phtml +++ b/src/Views/post-list.phtml @@ -3,7 +3,7 @@ context->transport->posts as $post): ?> - Post id; ?> + @<?php echo $post->id ?> diff --git a/thumbs/.gitignore b/thumbs/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/thumbs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore