Added ability to loop video posts
This commit is contained in:
parent
4d20e1bfc4
commit
93291e5164
16 changed files with 95 additions and 14 deletions
4
TODO
4
TODO
|
@ -2,10 +2,6 @@ This is transient file that lists functionalities to be implemented before
|
|||
first major release.
|
||||
|
||||
everything related to posts:
|
||||
- single post view
|
||||
- editing
|
||||
- ability to loop video posts
|
||||
|
||||
- ability to paste many urls in post upload
|
||||
|
||||
- post notes
|
||||
|
|
|
@ -53,6 +53,7 @@ changePostTags = regularUser, powerUser, moderator, administrator
|
|||
changePostContent = regularUser, powerUser, moderator, administrator
|
||||
changePostThumbnail = powerUser, moderator, administrator
|
||||
changePostRelations = regularUser, powerUser, moderator, administrator
|
||||
changePostFlags = regularUser, powerUser, moderator, administrator
|
||||
|
||||
listTags = anonymous, regularUser, powerUser, moderator, administrator
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ App.Auth = function(_, jQuery, util, api, appState, promise) {
|
|||
changePostContent: 'changePostContent',
|
||||
changePostThumbnail: 'changePostThumbnail',
|
||||
changePostRelations: 'changePostRelations',
|
||||
changePostFlags: 'changePostFlags',
|
||||
|
||||
listComments: 'listComments',
|
||||
addComments: 'addComments',
|
||||
|
|
|
@ -47,6 +47,7 @@ App.Presenters.PostPresenter = function(
|
|||
editPrivileges.canChangeContent = auth.hasPrivilege(auth.privileges.changePostContent);
|
||||
editPrivileges.canChangeThumbnail = auth.hasPrivilege(auth.privileges.changePostThumbnail);
|
||||
editPrivileges.canChangeRelations = auth.hasPrivilege(auth.privileges.changePostRelations);
|
||||
editPrivileges.canChangeFlags = auth.hasPrivilege(auth.privileges.changePostFlags);
|
||||
|
||||
promise.wait(
|
||||
util.promiseTemplate('post'),
|
||||
|
@ -162,6 +163,11 @@ App.Presenters.PostPresenter = function(
|
|||
attachLinksToPostsAround();
|
||||
}
|
||||
|
||||
function softRender() {
|
||||
renderSidebar();
|
||||
$el.find('video').prop('loop', post.flags.loop);
|
||||
}
|
||||
|
||||
function renderSidebar() {
|
||||
$el.find('#sidebar').html(jQuery(renderPostTemplate()).find('#sidebar').html());
|
||||
attachSidebarEvents();
|
||||
|
@ -262,6 +268,7 @@ App.Presenters.PostPresenter = function(
|
|||
var $form = $el.find('form');
|
||||
var formData = {};
|
||||
formData.seenEditTime = post.lastEditTime;
|
||||
formData.flags = {};
|
||||
|
||||
if (editPrivileges.canChangeContent && postContent) {
|
||||
formData.content = postContent;
|
||||
|
@ -287,6 +294,12 @@ App.Presenters.PostPresenter = function(
|
|||
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) {
|
||||
showEditError('No tags set.');
|
||||
return;
|
||||
|
@ -296,7 +309,7 @@ App.Presenters.PostPresenter = function(
|
|||
.then(function(response) {
|
||||
post = response.json;
|
||||
hideEditForm();
|
||||
renderSidebar();
|
||||
softRender();
|
||||
}).fail(function(response) {
|
||||
showEditError(response);
|
||||
});
|
||||
|
@ -344,14 +357,14 @@ App.Presenters.PostPresenter = function(
|
|||
function addFavorite() {
|
||||
promise.wait(api.post('/posts/' + post.id + '/favorites'))
|
||||
.then(function(response) {
|
||||
promise.wait(refreshPost()).then(renderSidebar);
|
||||
promise.wait(refreshPost()).then(softRender);
|
||||
}).fail(showGenericError);
|
||||
}
|
||||
|
||||
function deleteFavorite() {
|
||||
promise.wait(api.delete('/posts/' + post.id + '/favorites'))
|
||||
.then(function(response) {
|
||||
promise.wait(refreshPost()).then(renderSidebar);
|
||||
promise.wait(refreshPost()).then(softRender);
|
||||
}).fail(showGenericError);
|
||||
}
|
||||
|
||||
|
@ -370,7 +383,7 @@ App.Presenters.PostPresenter = function(
|
|||
function score(scoreValue) {
|
||||
promise.wait(api.post('/posts/' + post.id + '/score', {score: scoreValue}))
|
||||
.then(function() {
|
||||
promise.wait(refreshPost()).then(renderSidebar);
|
||||
promise.wait(refreshPost()).then(softRender);
|
||||
}).fail(showGenericError);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
<div class="post-type-<%= post.contentType %>">
|
||||
|
||||
<% if (post.contentType == 'image') { %>
|
||||
<% if (post.contentType === 'image') { %>
|
||||
|
||||
<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>
|
||||
|
||||
<% } else if (post.contentType == 'flash') { %>
|
||||
<% } else if (post.contentType === 'flash') { %>
|
||||
|
||||
<object
|
||||
type="<%= post.contentMimeType %>"
|
||||
|
@ -21,9 +21,13 @@
|
|||
<param name="movie" value="<%= postContentUrl %>"/>
|
||||
</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 %>"/>
|
||||
|
||||
Your browser doesn't support HTML5 videos.
|
||||
|
|
|
@ -48,6 +48,18 @@
|
|||
</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) { %>
|
||||
<div class="form-row">
|
||||
<label class="form-label" for="post-content">Content:</label>
|
||||
|
|
|
@ -8,6 +8,7 @@ use Szurubooru\Services\FavoritesService;
|
|||
use Szurubooru\Services\HistoryService;
|
||||
use Szurubooru\Services\PrivilegeService;
|
||||
use Szurubooru\Services\ScoreService;
|
||||
use Szurubooru\Entities\Post;
|
||||
|
||||
class PostViewProxy extends AbstractViewProxy
|
||||
{
|
||||
|
@ -72,6 +73,8 @@ class PostViewProxy extends AbstractViewProxy
|
|||
$result->favoriteCount = $post->getFavoriteCount();
|
||||
$result->score = $post->getScore();
|
||||
$result->commentCount = $post->getCommentCount();
|
||||
$result->flags = new \StdClass;
|
||||
$result->flags->loop = ($post->getFlags() & Post::FLAG_LOOP);
|
||||
|
||||
if (!empty($config[self::FETCH_TAGS]))
|
||||
$result->tags = $this->tagViewProxy->fromArray($post->getTags());
|
||||
|
|
|
@ -25,6 +25,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv
|
|||
'originalFileName' => $entity->getOriginalFileName(),
|
||||
'featureCount' => $entity->getFeatureCount(),
|
||||
'lastFeatureTime' => $this->entityTimeToDbTime($entity->getLastFeatureTime()),
|
||||
'flags' => $entity->getFlags(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -46,6 +47,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv
|
|||
$entity->setOriginalFileName($array['originalFileName']);
|
||||
$entity->setFeatureCount(intval($array['featureCount']));
|
||||
$entity->setLastFeatureTime($this->dbTimeToEntityTime($array['lastFeatureTime']));
|
||||
$entity->setFlags(intval($array['flags']));
|
||||
$entity->setMeta(Post::META_TAG_COUNT, intval($array['tagCount']));
|
||||
$entity->setMeta(Post::META_FAV_COUNT, intval($array['favCount']));
|
||||
$entity->setMeta(Post::META_COMMENT_COUNT, intval($array['commentCount']));
|
||||
|
|
|
@ -13,6 +13,8 @@ final class Post extends Entity
|
|||
const POST_TYPE_VIDEO = 3;
|
||||
const POST_TYPE_YOUTUBE = 4;
|
||||
|
||||
const FLAG_LOOP = 1;
|
||||
|
||||
const LAZY_LOADER_USER = 'user';
|
||||
const LAZY_LOADER_TAGS = 'tags';
|
||||
const LAZY_LOADER_CONTENT = 'content';
|
||||
|
@ -39,6 +41,7 @@ final class Post extends Entity
|
|||
private $originalFileName;
|
||||
private $featureCount = 0;
|
||||
private $lastFeatureTime;
|
||||
private $flags = 0;
|
||||
|
||||
public function getIdMarkdown()
|
||||
{
|
||||
|
@ -195,6 +198,16 @@ final class Post extends Entity
|
|||
$this->lastFeatureTime = $lastFeatureTime;
|
||||
}
|
||||
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
|
||||
public function setFlags($flags)
|
||||
{
|
||||
$this->flags = $flags;
|
||||
}
|
||||
|
||||
public function getTags()
|
||||
{
|
||||
return $this->lazyLoad(self::LAZY_LOADER_TAGS, []);
|
||||
|
|
|
@ -12,6 +12,7 @@ class PostEditFormData implements IValidatable
|
|||
public $source;
|
||||
public $tags;
|
||||
public $relations;
|
||||
public $flags;
|
||||
|
||||
public $seenEditTime;
|
||||
|
||||
|
@ -26,6 +27,8 @@ class PostEditFormData implements IValidatable
|
|||
$this->tags = preg_split('/[\s+]/', $inputReader->tags);
|
||||
$this->relations = array_filter(preg_split('/[\s+]/', $inputReader->relations));
|
||||
$this->seenEditTime = $inputReader->seenEditTime;
|
||||
$this->flags = new \StdClass;
|
||||
$this->flags->loop = !empty($inputReader->flags['loop']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ class Privilege
|
|||
const CHANGE_POST_CONTENT = 'changePostContent';
|
||||
const CHANGE_POST_THUMBNAIL = 'changePostThumbnail';
|
||||
const CHANGE_POST_RELATIONS = 'changePostRelations';
|
||||
const CHANGE_POST_FLAGS = 'changePostFlags';
|
||||
|
||||
const LIST_TAGS = 'listTags';
|
||||
|
||||
|
|
|
@ -105,6 +105,10 @@ class HistoryService
|
|||
$featuredPostParam = $this->globalParamDao->findByKey(GlobalParam::KEY_FEATURED_POST);
|
||||
$isFeatured = ($featuredPostParam and intval($featuredPostParam->getValue()) === $post->getId());
|
||||
|
||||
$flags = [];
|
||||
if ($post->getFlags() & Post::FLAG_LOOP)
|
||||
$flags []= 'loop';
|
||||
|
||||
$data =
|
||||
[
|
||||
'source' => $post->getSource(),
|
||||
|
@ -128,6 +132,7 @@ class HistoryService
|
|||
},
|
||||
$post->getRelatedPosts()),
|
||||
|
||||
'flags' => $flags,
|
||||
];
|
||||
|
||||
$snapshot = $this->getPostSnapshot($post);
|
||||
|
|
|
@ -188,6 +188,9 @@ class PostService
|
|||
if ($formData->relations !== null)
|
||||
$this->updatePostRelations($post, $formData->relations);
|
||||
|
||||
if (count($formData->flags) > 0)
|
||||
$this->updatePostFlags($post, $formData->flags);
|
||||
|
||||
$this->historyService->saveSnapshot($this->historyService->getPostChangeSnapshot($post));
|
||||
return $this->postDao->save($post);
|
||||
};
|
||||
|
@ -313,6 +316,14 @@ class PostService
|
|||
$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)
|
||||
{
|
||||
$transactionFunc = function() use ($post)
|
||||
|
|
13
src/Upgrades/Upgrade19.php
Normal file
13
src/Upgrades/Upgrade19.php
Normal 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');
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ return [
|
|||
$container->get(\Szurubooru\Upgrades\Upgrade16::class),
|
||||
$container->get(\Szurubooru\Upgrades\Upgrade17::class),
|
||||
$container->get(\Szurubooru\Upgrades\Upgrade18::class),
|
||||
$container->get(\Szurubooru\Upgrades\Upgrade19::class),
|
||||
];
|
||||
}),
|
||||
|
||||
|
|
|
@ -135,6 +135,7 @@ final class HistoryServiceTest extends AbstractTestCase
|
|||
$post->setContentChecksum('checksum');
|
||||
$post->setSafety(Post::POST_SAFETY_SKETCHY);
|
||||
$post->setSource('amazing source');
|
||||
$post->setFlags(Post::FLAG_LOOP);
|
||||
|
||||
$historyService = $this->getHistoryService();
|
||||
$snapshot = $historyService->getPostChangeSnapshot($post);
|
||||
|
@ -145,7 +146,8 @@ final class HistoryServiceTest extends AbstractTestCase
|
|||
'contentChecksum' => 'checksum',
|
||||
'featured' => false,
|
||||
'tags' => ['tag1', 'tag2'],
|
||||
'relations' => [1, 2]
|
||||
'relations' => [1, 2],
|
||||
'flags' => ['loop'],
|
||||
], $snapshot->getData());
|
||||
|
||||
$this->assertEquals(Snapshot::TYPE_POST, $snapshot->getType());
|
||||
|
|
Loading…
Reference in a new issue