diff --git a/public_html/css/post-list.css b/public_html/css/post-list.css index f383d304..b501669d 100644 --- a/public_html/css/post-list.css +++ b/public_html/css/post-list.css @@ -158,6 +158,9 @@ .post-small.post-type-flash .link::after { content: 'flash'; } +.post-small.post-type-animation .link::after { + content: 'anim'; +} .post-small .action { display: none; diff --git a/public_html/templates/post-content.tpl b/public_html/templates/post-content.tpl index b71908c3..4e8caf33 100644 --- a/public_html/templates/post-content.tpl +++ b/public_html/templates/post-content.tpl @@ -2,7 +2,7 @@ var postContentUrl = '/data/posts/' + post.name + '?' + Math.round(Math.random() * 1000) /* reset gif animations */ var width; var height; - if (post.contentType === 'image' || post.contentType === 'flash') { + if (post.contentType === 'image' || post.contentType === 'animation' || post.contentType === 'flash') { width = post.imageWidth; height = post.imageHeight; } else { @@ -21,7 +21,7 @@ data-height="<%= height %>" style="max-width: <%= width %>px"> - <% if (post.contentType === 'image') { %> + <% if (post.contentType === 'image' || post.contentType === 'animation') { %> <%= post.name %> diff --git a/src/Entities/Post.php b/src/Entities/Post.php index bc6d548b..b54d2bd1 100644 --- a/src/Entities/Post.php +++ b/src/Entities/Post.php @@ -12,6 +12,7 @@ final class Post extends Entity const POST_TYPE_FLASH = 2; const POST_TYPE_VIDEO = 3; const POST_TYPE_YOUTUBE = 4; + const POST_TYPE_ANIMATED_IMAGE = 5; const FLAG_LOOP = 1; diff --git a/src/Helpers/EnumHelper.php b/src/Helpers/EnumHelper.php index 2fb4bd69..89a5c715 100644 --- a/src/Helpers/EnumHelper.php +++ b/src/Helpers/EnumHelper.php @@ -37,6 +37,7 @@ class EnumHelper 'video' => Post::POST_TYPE_VIDEO, 'flash' => Post::POST_TYPE_FLASH, 'youtube' => Post::POST_TYPE_YOUTUBE, + 'animation' => Post::POST_TYPE_ANIMATED_IMAGE, ]; private static $snapshotTypeMap = diff --git a/src/Helpers/MimeHelper.php b/src/Helpers/MimeHelper.php index 8d9166a2..a771aa15 100644 --- a/src/Helpers/MimeHelper.php +++ b/src/Helpers/MimeHelper.php @@ -18,6 +18,12 @@ class MimeHelper return self::getMimeTypeFrom16Bytes(substr($buffer, 0, 16)); } + public static function isBufferAnimatedGif($buffer) + { + return strtolower(self::getMimeTypeFromBuffer($buffer)) === 'image/gif' + and preg_match_all('#\x21\xf9\x04.{4}\x00[\x2c\x21]#s', $buffer) > 1; + } + public static function isFlash($mime) { return strtolower($mime) === 'application/x-shockwave-flash'; diff --git a/src/Services/PostService.php b/src/Services/PostService.php index 4edbf48b..7837bdc5 100644 --- a/src/Services/PostService.php +++ b/src/Services/PostService.php @@ -206,13 +206,24 @@ class PostService $post->setContentMimeType($mime); if (MimeHelper::isFlash($mime)) + { $post->setContentType(Post::POST_TYPE_FLASH); + } elseif (MimeHelper::isImage($mime)) - $post->setContentType(Post::POST_TYPE_IMAGE); + { + $post->setContentType( + MimeHelper::isBufferAnimatedGif($content) + ? Post::POST_TYPE_ANIMATED_IMAGE + : Post::POST_TYPE_IMAGE); + } elseif (MimeHelper::isVideo($mime)) + { $post->setContentType(Post::POST_TYPE_VIDEO); + } else + { throw new \DomainException('Unhandled file type: "' . $mime . '"'); + } $post->setContentChecksum(sha1($content)); $this->assertNoPostWithThisContentChecksum($post); diff --git a/src/Services/UpgradeService.php b/src/Services/UpgradeService.php index 27662d45..0f340c3c 100644 --- a/src/Services/UpgradeService.php +++ b/src/Services/UpgradeService.php @@ -41,13 +41,10 @@ final class UpgradeService if ($this->isUpgradeNeeded($upgrade)) { if ($verbose) - { - echo 'Running ' . get_class($upgrade) . PHP_EOL; - if (ob_get_level()) - ob_flush(); - flush(); - } + $this->log('Running ' . get_class($upgrade)); $this->runUpgrade($upgrade); + if ($verbose) + $this->log(PHP_EOL); } } } @@ -87,4 +84,12 @@ final class UpgradeService preg_match('/(\d+)/', $className, $matches); return intval($matches[1]); } + + private function log($message) + { + echo $message; + if (ob_get_level()) + ob_flush(); + flush(); + } } diff --git a/src/Upgrades/Upgrade38.php b/src/Upgrades/Upgrade38.php new file mode 100644 index 00000000..da648493 --- /dev/null +++ b/src/Upgrades/Upgrade38.php @@ -0,0 +1,52 @@ +postDao = $postDao; + $this->fileDao = $fileDao; + } + + public function run(DatabaseConnection $databaseConnection) + { + $posts = $this->postDao->findAll(); + $progress = 0; + foreach ($posts as $post) + { + if ($post->getContentType() === Post::POST_TYPE_IMAGE) + { + $fullPath = $this->fileDao->getFullPath($post->getContentPath()); + try + { + $contents = file_get_contents($fullPath); + } + catch (\Exception $e) + { + continue; + } + if (MimeHelper::isBufferAnimatedGif($contents)) + { + $post->setContentType(Post::POST_TYPE_ANIMATED_IMAGE); + $this->postDao->save($post); + } + if (++ $progress == 100) + { + echo '.'; + $progress = 0; + } + } + } + } +} diff --git a/src/di.php b/src/di.php index 3d3aebc2..35cb09e8 100644 --- a/src/di.php +++ b/src/di.php @@ -54,6 +54,7 @@ return [ $container->get(\Szurubooru\Upgrades\Upgrade35::class), $container->get(\Szurubooru\Upgrades\Upgrade36::class), $container->get(\Szurubooru\Upgrades\Upgrade37::class), + $container->get(\Szurubooru\Upgrades\Upgrade38::class), ]; }), diff --git a/tests/Helpers/MimeHelperTest.php b/tests/Helpers/MimeHelperTest.php index 8362dc06..7e1cddef 100644 --- a/tests/Helpers/MimeHelperTest.php +++ b/tests/Helpers/MimeHelperTest.php @@ -5,6 +5,19 @@ use Szurubooru\Tests\AbstractTestCase; final class MimeHelperTest extends AbstractTestCase { + public static function animatedGifProvider() + { + return + [ + ['test_files/video.mp4', false], + ['test_files/static.gif', false], + ['test_files/animated.gif', true], + ['test_files/animated2.gif', true], + ['test_files/animated3.gif', true], + ['test_files/animated4.gif', true], + ]; + } + public function testGettingMime() { $expected = 'image/jpeg'; @@ -38,4 +51,16 @@ final class MimeHelperTest extends AbstractTestCase $this->assertTrue(MimeHelper::isVideo('application/ogg')); $this->assertFalse(MimeHelper::isVideo('something else')); } + + /** + * @dataProvider animatedGifProvider + */ + public function testIsAnimatedGif($path, $expected) + { + $fullPath = __DIR__ + . DIRECTORY_SEPARATOR . '..' + . DIRECTORY_SEPARATOR . $path; + $actual = MimeHelper::isBufferAnimatedGif(file_get_contents($fullPath)); + $this->assertEquals($expected, $actual); + } } diff --git a/tests/test_files/animated.gif b/tests/test_files/animated.gif new file mode 100644 index 00000000..ac99b82a Binary files /dev/null and b/tests/test_files/animated.gif differ diff --git a/tests/test_files/animated2.gif b/tests/test_files/animated2.gif new file mode 100644 index 00000000..cda5d338 Binary files /dev/null and b/tests/test_files/animated2.gif differ diff --git a/tests/test_files/animated3.gif b/tests/test_files/animated3.gif new file mode 100644 index 00000000..6015ff1b Binary files /dev/null and b/tests/test_files/animated3.gif differ diff --git a/tests/test_files/animated4.gif b/tests/test_files/animated4.gif new file mode 100644 index 00000000..a133d26f Binary files /dev/null and b/tests/test_files/animated4.gif differ diff --git a/tests/test_files/static.gif b/tests/test_files/static.gif new file mode 100644 index 00000000..547037c9 Binary files /dev/null and b/tests/test_files/static.gif differ