Added ability to loop video posts

This commit is contained in:
Marcin Kurczewski 2014-10-09 09:45:06 +02:00
parent 4d20e1bfc4
commit 93291e5164
16 changed files with 95 additions and 14 deletions

4
TODO
View file

@ -2,10 +2,6 @@ This is transient file that lists functionalities to be implemented before
first major release. first major release.
everything related to posts: everything related to posts:
- single post view
- editing
- ability to loop video posts
- ability to paste many urls in post upload - ability to paste many urls in post upload
- post notes - post notes

View file

@ -53,6 +53,7 @@ changePostTags = regularUser, powerUser, moderator, administrator
changePostContent = regularUser, powerUser, moderator, administrator changePostContent = regularUser, powerUser, moderator, administrator
changePostThumbnail = powerUser, moderator, administrator changePostThumbnail = powerUser, moderator, administrator
changePostRelations = regularUser, powerUser, moderator, administrator changePostRelations = regularUser, powerUser, moderator, administrator
changePostFlags = regularUser, powerUser, moderator, administrator
listTags = anonymous, regularUser, powerUser, moderator, administrator listTags = anonymous, regularUser, powerUser, moderator, administrator

View file

@ -32,6 +32,7 @@ App.Auth = function(_, jQuery, util, api, appState, promise) {
changePostContent: 'changePostContent', changePostContent: 'changePostContent',
changePostThumbnail: 'changePostThumbnail', changePostThumbnail: 'changePostThumbnail',
changePostRelations: 'changePostRelations', changePostRelations: 'changePostRelations',
changePostFlags: 'changePostFlags',
listComments: 'listComments', listComments: 'listComments',
addComments: 'addComments', addComments: 'addComments',

View file

@ -47,6 +47,7 @@ App.Presenters.PostPresenter = function(
editPrivileges.canChangeContent = auth.hasPrivilege(auth.privileges.changePostContent); editPrivileges.canChangeContent = auth.hasPrivilege(auth.privileges.changePostContent);
editPrivileges.canChangeThumbnail = auth.hasPrivilege(auth.privileges.changePostThumbnail); editPrivileges.canChangeThumbnail = auth.hasPrivilege(auth.privileges.changePostThumbnail);
editPrivileges.canChangeRelations = auth.hasPrivilege(auth.privileges.changePostRelations); editPrivileges.canChangeRelations = auth.hasPrivilege(auth.privileges.changePostRelations);
editPrivileges.canChangeFlags = auth.hasPrivilege(auth.privileges.changePostFlags);
promise.wait( promise.wait(
util.promiseTemplate('post'), util.promiseTemplate('post'),
@ -162,6 +163,11 @@ App.Presenters.PostPresenter = function(
attachLinksToPostsAround(); attachLinksToPostsAround();
} }
function softRender() {
renderSidebar();
$el.find('video').prop('loop', post.flags.loop);
}
function renderSidebar() { function renderSidebar() {
$el.find('#sidebar').html(jQuery(renderPostTemplate()).find('#sidebar').html()); $el.find('#sidebar').html(jQuery(renderPostTemplate()).find('#sidebar').html());
attachSidebarEvents(); attachSidebarEvents();
@ -262,6 +268,7 @@ App.Presenters.PostPresenter = function(
var $form = $el.find('form'); var $form = $el.find('form');
var formData = {}; var formData = {};
formData.seenEditTime = post.lastEditTime; formData.seenEditTime = post.lastEditTime;
formData.flags = {};
if (editPrivileges.canChangeContent && postContent) { if (editPrivileges.canChangeContent && postContent) {
formData.content = postContent; formData.content = postContent;
@ -287,6 +294,12 @@ App.Presenters.PostPresenter = function(
formData.relations = $form.find('[name=relations]').val(); formData.relations = $form.find('[name=relations]').val();
} }
if (editPrivileges.canChangeFlags) {
if (post.contentType === 'video') {
formData.flags.loop = $form.find('[name=loop]').is(':checked') ? 1 : 0;
}
}
if (post.tags.length === 0) { if (post.tags.length === 0) {
showEditError('No tags set.'); showEditError('No tags set.');
return; return;
@ -296,7 +309,7 @@ App.Presenters.PostPresenter = function(
.then(function(response) { .then(function(response) {
post = response.json; post = response.json;
hideEditForm(); hideEditForm();
renderSidebar(); softRender();
}).fail(function(response) { }).fail(function(response) {
showEditError(response); showEditError(response);
}); });
@ -344,14 +357,14 @@ App.Presenters.PostPresenter = function(
function addFavorite() { function addFavorite() {
promise.wait(api.post('/posts/' + post.id + '/favorites')) promise.wait(api.post('/posts/' + post.id + '/favorites'))
.then(function(response) { .then(function(response) {
promise.wait(refreshPost()).then(renderSidebar); promise.wait(refreshPost()).then(softRender);
}).fail(showGenericError); }).fail(showGenericError);
} }
function deleteFavorite() { function deleteFavorite() {
promise.wait(api.delete('/posts/' + post.id + '/favorites')) promise.wait(api.delete('/posts/' + post.id + '/favorites'))
.then(function(response) { .then(function(response) {
promise.wait(refreshPost()).then(renderSidebar); promise.wait(refreshPost()).then(softRender);
}).fail(showGenericError); }).fail(showGenericError);
} }
@ -370,7 +383,7 @@ App.Presenters.PostPresenter = function(
function score(scoreValue) { function score(scoreValue) {
promise.wait(api.post('/posts/' + post.id + '/score', {score: scoreValue})) promise.wait(api.post('/posts/' + post.id + '/score', {score: scoreValue}))
.then(function() { .then(function() {
promise.wait(refreshPost()).then(renderSidebar); promise.wait(refreshPost()).then(softRender);
}).fail(showGenericError); }).fail(showGenericError);
} }

View file

@ -2,15 +2,15 @@
<div class="post-type-<%= post.contentType %>"> <div class="post-type-<%= post.contentType %>">
<% if (post.contentType == 'image') { %> <% if (post.contentType === 'image') { %>
<img alt="<%= post.name %>" src="<%= postContentUrl %>"/> <img alt="<%= post.name %>" src="<%= postContentUrl %>"/>
<% } else if (post.contentType == 'youtube') { %> <% } else if (post.contentType === 'youtube') { %>
<iframe src="//www.youtube.com/embed/<%= post.contentChecksum %>?wmode=opaque" allowfullscreen></iframe> <iframe src="//www.youtube.com/embed/<%= post.contentChecksum %>?wmode=opaque" allowfullscreen></iframe>
<% } else if (post.contentType == 'flash') { %> <% } else if (post.contentType === 'flash') { %>
<object <object
type="<%= post.contentMimeType %>" type="<%= post.contentMimeType %>"
@ -21,9 +21,13 @@
<param name="movie" value="<%= postContentUrl %>"/> <param name="movie" value="<%= postContentUrl %>"/>
</object> </object>
<% } else if (post.contentType == 'video') { %> <% } else if (post.contentType === 'video') { %>
<% if (post.flags.loop) { %>
<video id="video" controls loop="loop">
<% } else { %>
<video id="video" controls>
<% } %>
<video controls>
<source type="<%= post.contentMimeType %>" src="<%= postContentUrl %>"/> <source type="<%= post.contentMimeType %>" src="<%= postContentUrl %>"/>
Your browser doesn't support HTML5 videos. Your browser doesn't support HTML5 videos.

View file

@ -48,6 +48,18 @@
</div> </div>
<% } %> <% } %>
<% if (privileges.canChangeFlags && post.contentType === 'video') { %>
<div class="form-row">
<label class="form-label">Loop:</label>
<div class="form-input">
<input type="checkbox" id="post-loop" name="loop" value="loop" <%= post.flags.loop ? 'checked="checked"' : '' %>/>
<label for="post-loop">
Automatically repeat video after playback
</label>
</div>
</div>
<% } %>
<% if (privileges.canChangeContent) { %> <% if (privileges.canChangeContent) { %>
<div class="form-row"> <div class="form-row">
<label class="form-label" for="post-content">Content:</label> <label class="form-label" for="post-content">Content:</label>

View file

@ -8,6 +8,7 @@ use Szurubooru\Services\FavoritesService;
use Szurubooru\Services\HistoryService; use Szurubooru\Services\HistoryService;
use Szurubooru\Services\PrivilegeService; use Szurubooru\Services\PrivilegeService;
use Szurubooru\Services\ScoreService; use Szurubooru\Services\ScoreService;
use Szurubooru\Entities\Post;
class PostViewProxy extends AbstractViewProxy class PostViewProxy extends AbstractViewProxy
{ {
@ -72,6 +73,8 @@ class PostViewProxy extends AbstractViewProxy
$result->favoriteCount = $post->getFavoriteCount(); $result->favoriteCount = $post->getFavoriteCount();
$result->score = $post->getScore(); $result->score = $post->getScore();
$result->commentCount = $post->getCommentCount(); $result->commentCount = $post->getCommentCount();
$result->flags = new \StdClass;
$result->flags->loop = ($post->getFlags() & Post::FLAG_LOOP);
if (!empty($config[self::FETCH_TAGS])) if (!empty($config[self::FETCH_TAGS]))
$result->tags = $this->tagViewProxy->fromArray($post->getTags()); $result->tags = $this->tagViewProxy->fromArray($post->getTags());

View file

@ -25,6 +25,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv
'originalFileName' => $entity->getOriginalFileName(), 'originalFileName' => $entity->getOriginalFileName(),
'featureCount' => $entity->getFeatureCount(), 'featureCount' => $entity->getFeatureCount(),
'lastFeatureTime' => $this->entityTimeToDbTime($entity->getLastFeatureTime()), 'lastFeatureTime' => $this->entityTimeToDbTime($entity->getLastFeatureTime()),
'flags' => $entity->getFlags(),
]; ];
} }
@ -46,6 +47,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv
$entity->setOriginalFileName($array['originalFileName']); $entity->setOriginalFileName($array['originalFileName']);
$entity->setFeatureCount(intval($array['featureCount'])); $entity->setFeatureCount(intval($array['featureCount']));
$entity->setLastFeatureTime($this->dbTimeToEntityTime($array['lastFeatureTime'])); $entity->setLastFeatureTime($this->dbTimeToEntityTime($array['lastFeatureTime']));
$entity->setFlags(intval($array['flags']));
$entity->setMeta(Post::META_TAG_COUNT, intval($array['tagCount'])); $entity->setMeta(Post::META_TAG_COUNT, intval($array['tagCount']));
$entity->setMeta(Post::META_FAV_COUNT, intval($array['favCount'])); $entity->setMeta(Post::META_FAV_COUNT, intval($array['favCount']));
$entity->setMeta(Post::META_COMMENT_COUNT, intval($array['commentCount'])); $entity->setMeta(Post::META_COMMENT_COUNT, intval($array['commentCount']));

View file

@ -13,6 +13,8 @@ final class Post extends Entity
const POST_TYPE_VIDEO = 3; const POST_TYPE_VIDEO = 3;
const POST_TYPE_YOUTUBE = 4; const POST_TYPE_YOUTUBE = 4;
const FLAG_LOOP = 1;
const LAZY_LOADER_USER = 'user'; const LAZY_LOADER_USER = 'user';
const LAZY_LOADER_TAGS = 'tags'; const LAZY_LOADER_TAGS = 'tags';
const LAZY_LOADER_CONTENT = 'content'; const LAZY_LOADER_CONTENT = 'content';
@ -39,6 +41,7 @@ final class Post extends Entity
private $originalFileName; private $originalFileName;
private $featureCount = 0; private $featureCount = 0;
private $lastFeatureTime; private $lastFeatureTime;
private $flags = 0;
public function getIdMarkdown() public function getIdMarkdown()
{ {
@ -195,6 +198,16 @@ final class Post extends Entity
$this->lastFeatureTime = $lastFeatureTime; $this->lastFeatureTime = $lastFeatureTime;
} }
public function getFlags()
{
return $this->flags;
}
public function setFlags($flags)
{
$this->flags = $flags;
}
public function getTags() public function getTags()
{ {
return $this->lazyLoad(self::LAZY_LOADER_TAGS, []); return $this->lazyLoad(self::LAZY_LOADER_TAGS, []);

View file

@ -12,6 +12,7 @@ class PostEditFormData implements IValidatable
public $source; public $source;
public $tags; public $tags;
public $relations; public $relations;
public $flags;
public $seenEditTime; public $seenEditTime;
@ -26,6 +27,8 @@ class PostEditFormData implements IValidatable
$this->tags = preg_split('/[\s+]/', $inputReader->tags); $this->tags = preg_split('/[\s+]/', $inputReader->tags);
$this->relations = array_filter(preg_split('/[\s+]/', $inputReader->relations)); $this->relations = array_filter(preg_split('/[\s+]/', $inputReader->relations));
$this->seenEditTime = $inputReader->seenEditTime; $this->seenEditTime = $inputReader->seenEditTime;
$this->flags = new \StdClass;
$this->flags->loop = !empty($inputReader->flags['loop']);
} }
} }

View file

@ -32,6 +32,7 @@ class Privilege
const CHANGE_POST_CONTENT = 'changePostContent'; const CHANGE_POST_CONTENT = 'changePostContent';
const CHANGE_POST_THUMBNAIL = 'changePostThumbnail'; const CHANGE_POST_THUMBNAIL = 'changePostThumbnail';
const CHANGE_POST_RELATIONS = 'changePostRelations'; const CHANGE_POST_RELATIONS = 'changePostRelations';
const CHANGE_POST_FLAGS = 'changePostFlags';
const LIST_TAGS = 'listTags'; const LIST_TAGS = 'listTags';

View file

@ -105,6 +105,10 @@ class HistoryService
$featuredPostParam = $this->globalParamDao->findByKey(GlobalParam::KEY_FEATURED_POST); $featuredPostParam = $this->globalParamDao->findByKey(GlobalParam::KEY_FEATURED_POST);
$isFeatured = ($featuredPostParam and intval($featuredPostParam->getValue()) === $post->getId()); $isFeatured = ($featuredPostParam and intval($featuredPostParam->getValue()) === $post->getId());
$flags = [];
if ($post->getFlags() & Post::FLAG_LOOP)
$flags []= 'loop';
$data = $data =
[ [
'source' => $post->getSource(), 'source' => $post->getSource(),
@ -128,6 +132,7 @@ class HistoryService
}, },
$post->getRelatedPosts()), $post->getRelatedPosts()),
'flags' => $flags,
]; ];
$snapshot = $this->getPostSnapshot($post); $snapshot = $this->getPostSnapshot($post);

View file

@ -188,6 +188,9 @@ class PostService
if ($formData->relations !== null) if ($formData->relations !== null)
$this->updatePostRelations($post, $formData->relations); $this->updatePostRelations($post, $formData->relations);
if (count($formData->flags) > 0)
$this->updatePostFlags($post, $formData->flags);
$this->historyService->saveSnapshot($this->historyService->getPostChangeSnapshot($post)); $this->historyService->saveSnapshot($this->historyService->getPostChangeSnapshot($post));
return $this->postDao->save($post); return $this->postDao->save($post);
}; };
@ -313,6 +316,14 @@ class PostService
$post->setRelatedPosts($relatedPosts); $post->setRelatedPosts($relatedPosts);
} }
private function updatePostFlags(Post $post, \StdClass $flags)
{
$value = 0;
if (!empty($flags->loop))
$value |= Post::FLAG_LOOP;
$post->setFlags($value);
}
public function deletePost(Post $post) public function deletePost(Post $post)
{ {
$transactionFunc = function() use ($post) $transactionFunc = function() use ($post)

View file

@ -0,0 +1,13 @@
<?php
namespace Szurubooru\Upgrades;
use Szurubooru\DatabaseConnection;
class Upgrade19 implements IUpgrade
{
public function run(DatabaseConnection $databaseConnection)
{
$pdo = $databaseConnection->getPDO();
$pdo->exec('ALTER TABLE posts ADD COLUMN flags INT(8) NOT NULL DEFAULT 0');
}
}

View file

@ -34,6 +34,7 @@ return [
$container->get(\Szurubooru\Upgrades\Upgrade16::class), $container->get(\Szurubooru\Upgrades\Upgrade16::class),
$container->get(\Szurubooru\Upgrades\Upgrade17::class), $container->get(\Szurubooru\Upgrades\Upgrade17::class),
$container->get(\Szurubooru\Upgrades\Upgrade18::class), $container->get(\Szurubooru\Upgrades\Upgrade18::class),
$container->get(\Szurubooru\Upgrades\Upgrade19::class),
]; ];
}), }),

View file

@ -135,6 +135,7 @@ final class HistoryServiceTest extends AbstractTestCase
$post->setContentChecksum('checksum'); $post->setContentChecksum('checksum');
$post->setSafety(Post::POST_SAFETY_SKETCHY); $post->setSafety(Post::POST_SAFETY_SKETCHY);
$post->setSource('amazing source'); $post->setSource('amazing source');
$post->setFlags(Post::FLAG_LOOP);
$historyService = $this->getHistoryService(); $historyService = $this->getHistoryService();
$snapshot = $historyService->getPostChangeSnapshot($post); $snapshot = $historyService->getPostChangeSnapshot($post);
@ -145,7 +146,8 @@ final class HistoryServiceTest extends AbstractTestCase
'contentChecksum' => 'checksum', 'contentChecksum' => 'checksum',
'featured' => false, 'featured' => false,
'tags' => ['tag1', 'tag2'], 'tags' => ['tag1', 'tag2'],
'relations' => [1, 2] 'relations' => [1, 2],
'flags' => ['loop'],
], $snapshot->getData()); ], $snapshot->getData());
$this->assertEquals(Snapshot::TYPE_POST, $snapshot->getType()); $this->assertEquals(Snapshot::TYPE_POST, $snapshot->getType());