mirror of
https://github.com/rr-/szurubooru.git
synced 2025-07-17 08:26:24 +00:00
Merge 14a4386561
into ee7e9ef2a3
This commit is contained in:
@ -106,6 +106,11 @@ form .fa-question-circle-o
|
|||||||
background-color: $scrollbar-bg-color
|
background-color: $scrollbar-bg-color
|
||||||
&::-webkit-scrollbar-thumb
|
&::-webkit-scrollbar-thumb
|
||||||
background-color: $scrollbar-thumb-color
|
background-color: $scrollbar-thumb-color
|
||||||
|
li[data-name=view]
|
||||||
|
background: $button-enabled-background-color
|
||||||
|
margin-right: 1em
|
||||||
|
a
|
||||||
|
color: $button-enabled-text-color
|
||||||
>.content-wrapper:not(.transparent)
|
>.content-wrapper:not(.transparent)
|
||||||
background: $top-navigation-color
|
background: $top-navigation-color
|
||||||
padding: 1.8em
|
padding: 1.8em
|
||||||
@ -214,8 +219,6 @@ nav
|
|||||||
ul li[data-name=settings],
|
ul li[data-name=settings],
|
||||||
ul li[data-name=help]
|
ul li[data-name=help]
|
||||||
float: none
|
float: none
|
||||||
.access-key
|
|
||||||
text-decoration: underline
|
|
||||||
.thumbnail
|
.thumbnail
|
||||||
width: 1.5em
|
width: 1.5em
|
||||||
height: 1.5em
|
height: 1.5em
|
||||||
@ -244,9 +247,6 @@ nav
|
|||||||
#mobile-navigation-toggle
|
#mobile-navigation-toggle
|
||||||
color: $text-color-darktheme
|
color: $text-color-darktheme
|
||||||
|
|
||||||
a .access-key
|
|
||||||
text-decoration: underline
|
|
||||||
|
|
||||||
.messages
|
.messages
|
||||||
margin: 0 auto
|
margin: 0 auto
|
||||||
text-align: left
|
text-align: left
|
||||||
@ -287,6 +287,7 @@ a .access-key
|
|||||||
background-size: cover
|
background-size: cover
|
||||||
background-position: center
|
background-position: center
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
overflow: hidden
|
||||||
width: 20px
|
width: 20px
|
||||||
height: 20px
|
height: 20px
|
||||||
&.empty
|
&.empty
|
||||||
@ -298,13 +299,12 @@ a .access-key
|
|||||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0px
|
background-position: 0 0, 0 10px, 10px -10px, -10px 0px
|
||||||
background-repeat: repeat
|
background-repeat: repeat
|
||||||
background-size: 20px 20px
|
background-size: 20px 20px
|
||||||
img
|
img, video
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
object-fit: cover
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 100%
|
height: 100%
|
||||||
video
|
display: block
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
.flexbox-dummy
|
.flexbox-dummy
|
||||||
height: 0 !important
|
height: 0 !important
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
@import colors
|
@import colors
|
||||||
|
|
||||||
.post-container
|
.post-container
|
||||||
.post-content.transparency-grid img
|
.post-content
|
||||||
|
&.transparency-grid, &.post-error
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(45deg, $transparency-grid-square-color 25%, transparent 25%),
|
linear-gradient(45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
linear-gradient(-45deg, $transparency-grid-square-color 25%, transparent 25%),
|
linear-gradient(-45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
linear-gradient(45deg, transparent 75%, $transparency-grid-square-color 75%),
|
linear-gradient(45deg, transparent 75%, $transparency-grid-square-color 75%),
|
||||||
linear-gradient(-45deg, transparent 75%, $transparency-grid-square-color 75%)
|
linear-gradient(-45deg, transparent 75%, $transparency-grid-square-color 75%)
|
||||||
background-size: 20px 20px
|
|
||||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0px
|
background-position: 0 0, 0 10px, 10px -10px, -10px 0px
|
||||||
|
background-repeat: repeat
|
||||||
|
background-size: 20px 20px
|
||||||
|
|
||||||
text-align: center
|
text-align: center
|
||||||
.post-content
|
.post-content
|
||||||
@ -17,6 +19,8 @@
|
|||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
.resize-listener
|
.resize-listener
|
||||||
|
background-repeat: no-repeat
|
||||||
|
background-size: cover
|
||||||
position: absolute
|
position: absolute
|
||||||
left: 0
|
left: 0
|
||||||
right: 0
|
right: 0
|
||||||
@ -27,3 +31,14 @@
|
|||||||
|
|
||||||
img
|
img
|
||||||
image-orientation: from-image
|
image-orientation: from-image
|
||||||
|
|
||||||
|
.darktheme .post-container .post-content
|
||||||
|
&.transparency-grid, &.post-error
|
||||||
|
background-image:
|
||||||
|
linear-gradient(45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
|
linear-gradient(-45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
|
linear-gradient(45deg, transparent 75%, $transparency-grid-square-color 75%),
|
||||||
|
linear-gradient(-45deg, transparent 75%, $transparency-grid-square-color 75%)
|
||||||
|
background-position: 0 0, 0 10px, 10px -10px, -10px 0px
|
||||||
|
background-repeat: repeat
|
||||||
|
background-size: 20px 20px
|
||||||
|
@ -124,6 +124,8 @@
|
|||||||
li
|
li
|
||||||
margin: 0 0.3em 0.3em 0
|
margin: 0 0.3em 0.3em 0
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
a
|
||||||
|
box-shadow: 0 0 0 1px rgba(0,0,0,0.2)
|
||||||
|
|
||||||
.tags
|
.tags
|
||||||
margin-top: 2em
|
margin-top: 2em
|
||||||
|
@ -62,22 +62,22 @@ $cancel-button-color = tomato
|
|||||||
margin: 0 0 1.2em 0
|
margin: 0 0 1.2em 0
|
||||||
padding-left: 13em
|
padding-left: 13em
|
||||||
|
|
||||||
img
|
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
video
|
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
&>.thumbnail-wrapper
|
&>.thumbnail-wrapper
|
||||||
float: left
|
float: left
|
||||||
width: 12em
|
width: 12em
|
||||||
height: 8em
|
height: 8em
|
||||||
margin: 0 0 0 -13em
|
margin: 0 0 0 -13em
|
||||||
|
a
|
||||||
|
display: block
|
||||||
|
height: 100%
|
||||||
|
width: 100%
|
||||||
.thumbnail
|
.thumbnail
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 100%
|
height: 100%
|
||||||
|
video
|
||||||
|
opacity: 1
|
||||||
|
img, video
|
||||||
|
object-fit: contain
|
||||||
|
|
||||||
.uploadable
|
.uploadable
|
||||||
border: 1px solid $upload-border-color
|
border: 1px solid $upload-border-color
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
<div class='post-content post-type-<%- ctx.post.type %>'>
|
<div class='post-content post-type-<%- ctx.post.type %>'>
|
||||||
<% if (['image', 'animation'].includes(ctx.post.type)) { %>
|
<% if (['image', 'animation'].includes(ctx.post.type)) { %>
|
||||||
|
|
||||||
<img class='resize-listener' alt='' src='<%- ctx.post.contentUrl %>'/>
|
<img class='resize-listener' alt='' src='<%- ctx.post.contentUrl %>' draggable='false' fetchPriority='high'/>
|
||||||
|
|
||||||
<% } else if (ctx.post.type === 'flash') { %>
|
<% } else if (ctx.post.type === 'flash') { %>
|
||||||
|
|
||||||
<object class='resize-listener' width='<%- ctx.post.canvasWidth %>' height='<%- ctx.post.canvasHeight %>' data='<%- ctx.post.contentUrl %>'>
|
<object class='resize-listener' width='<%- ctx.post.canvasWidth %>' height='<%- ctx.post.canvasHeight %>' data='<%- ctx.post.contentUrl %>'>
|
||||||
<param name='wmode' value='opaque'/>
|
<param name='wmode' value='transparent'/>
|
||||||
<param name='movie' value='<%- ctx.post.contentUrl %>'/>
|
<param name='movie' value='<%- ctx.post.contentUrl %>'/>
|
||||||
|
<div class='messages'><div class='message-wrapper'><div class='message error'>Your browser does not support Flash.</div></div></div>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
<% } else if (ctx.post.type === 'video') { %>
|
<% } else if (ctx.post.type === 'video') { %>
|
||||||
@ -19,6 +20,8 @@
|
|||||||
loop: (ctx.post.flags || []).includes('loop'),
|
loop: (ctx.post.flags || []).includes('loop'),
|
||||||
playsinline: true,
|
playsinline: true,
|
||||||
autoplay: ctx.autoplay,
|
autoplay: ctx.autoplay,
|
||||||
|
preload: 'auto',
|
||||||
|
poster: ctx.post.originalThumbnailUrl,
|
||||||
},
|
},
|
||||||
ctx.makeElement('source', {
|
ctx.makeElement('source', {
|
||||||
type: ctx.post.mimeType,
|
type: ctx.post.mimeType,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<div class='thumbnail'>
|
<div class='thumbnail'>
|
||||||
<a href='<%= ctx.uploadable.previewUrl %>'>
|
<a href='<%= ctx.uploadable.previewUrl %>'>
|
||||||
<video id='video' nocontrols muted>
|
<video nocontrols muted>
|
||||||
<source type='<%- ctx.uploadable.mimeType %>' src='<%- ctx.uploadable.previewUrl %>'/>
|
<source type='<%- ctx.uploadable.mimeType %>' src='<%- ctx.uploadable.previewUrl %>'/>
|
||||||
</video>
|
</video>
|
||||||
</a>
|
</a>
|
||||||
|
@ -294,11 +294,16 @@ class Api extends events.EventTarget {
|
|||||||
// transform the request: upload each file, then make the request use
|
// transform the request: upload each file, then make the request use
|
||||||
// its tokens.
|
// its tokens.
|
||||||
data = Object.assign({}, data);
|
data = Object.assign({}, data);
|
||||||
|
let fileData = {};
|
||||||
let abortFunction = () => {};
|
let abortFunction = () => {};
|
||||||
let promise = Promise.resolve();
|
let promise = Promise.resolve();
|
||||||
if (files) {
|
if (files) {
|
||||||
for (let key of Object.keys(files)) {
|
for (let key of Object.keys(files)) {
|
||||||
const file = files[key];
|
const file = files[key];
|
||||||
|
if (file === null) {
|
||||||
|
fileData[key] = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const fileId = this._getFileId(file);
|
const fileId = this._getFileId(file);
|
||||||
if (fileTokens[fileId]) {
|
if (fileTokens[fileId]) {
|
||||||
data[key + "Token"] = fileTokens[fileId];
|
data[key + "Token"] = fileTokens[fileId];
|
||||||
@ -324,7 +329,7 @@ class Api extends events.EventTarget {
|
|||||||
url,
|
url,
|
||||||
requestFactory,
|
requestFactory,
|
||||||
data,
|
data,
|
||||||
{},
|
fileData,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
abortFunction = () => requestPromise.abort();
|
abortFunction = () => requestPromise.abort();
|
||||||
@ -388,7 +393,7 @@ class Api extends events.EventTarget {
|
|||||||
if (files) {
|
if (files) {
|
||||||
for (let key of Object.keys(files)) {
|
for (let key of Object.keys(files)) {
|
||||||
const value = files[key];
|
const value = files[key];
|
||||||
if (value.constructor === String) {
|
if (value !== null && value.constructor === String) {
|
||||||
data[key + "Url"] = value;
|
data[key + "Url"] = value;
|
||||||
} else {
|
} else {
|
||||||
req.attach(key, value || new Blob());
|
req.attach(key, value || new Blob());
|
||||||
|
@ -8,7 +8,7 @@ const PageController = require("../controllers/page_controller.js");
|
|||||||
const CommentsPageView = require("../views/comments_page_view.js");
|
const CommentsPageView = require("../views/comments_page_view.js");
|
||||||
const EmptyView = require("../views/empty_view.js");
|
const EmptyView = require("../views/empty_view.js");
|
||||||
|
|
||||||
const fields = ["id", "comments", "commentCount", "thumbnailUrl"];
|
const fields = ["id", "comments", "commentCount", "thumbnailUrl", "customThumbnailUrl"];
|
||||||
|
|
||||||
class CommentsController {
|
class CommentsController {
|
||||||
constructor(ctx) {
|
constructor(ctx) {
|
||||||
|
@ -14,6 +14,7 @@ const EmptyView = require("../views/empty_view.js");
|
|||||||
const fields = [
|
const fields = [
|
||||||
"id",
|
"id",
|
||||||
"thumbnailUrl",
|
"thumbnailUrl",
|
||||||
|
"customThumbnailUrl",
|
||||||
"type",
|
"type",
|
||||||
"safety",
|
"safety",
|
||||||
"score",
|
"score",
|
||||||
|
@ -178,15 +178,11 @@ class PostMainController extends BasePostController {
|
|||||||
if (e.detail.relations !== undefined && e.detail.relations !== null) {
|
if (e.detail.relations !== undefined && e.detail.relations !== null) {
|
||||||
post.relations = e.detail.relations;
|
post.relations = e.detail.relations;
|
||||||
}
|
}
|
||||||
if (e.detail.content !== undefined && e.detail.content !== null) {
|
|
||||||
post.newContent = e.detail.content;
|
|
||||||
}
|
|
||||||
if (e.detail.thumbnail !== undefined && e.detail.thumbnail !== null) {
|
|
||||||
post.newThumbnail = e.detail.thumbnail;
|
|
||||||
}
|
|
||||||
if (e.detail.source !== undefined && e.detail.source !== null) {
|
if (e.detail.source !== undefined && e.detail.source !== null) {
|
||||||
post.source = e.detail.source;
|
post.source = e.detail.source;
|
||||||
}
|
}
|
||||||
|
post.newContent = e.detail.content;
|
||||||
|
post.newThumbnail = e.detail.thumbnail;
|
||||||
post.save().then(
|
post.save().then(
|
||||||
() => {
|
() => {
|
||||||
this._view.sidebarControl.showSuccess("Post saved.");
|
this._view.sidebarControl.showSuccess("Post saved.");
|
||||||
|
@ -5,11 +5,12 @@ const views = require("../util/views.js");
|
|||||||
const optimizedResize = require("../util/optimized_resize.js");
|
const optimizedResize = require("../util/optimized_resize.js");
|
||||||
|
|
||||||
class PostContentControl {
|
class PostContentControl {
|
||||||
constructor(hostNode, post, viewportSizeCalculator, fitFunctionOverride) {
|
constructor(hostNode, post, isMediaCached, viewportSizeCalculator, fitFunctionOverride) {
|
||||||
this._post = post;
|
this._post = post;
|
||||||
this._viewportSizeCalculator = viewportSizeCalculator;
|
this._viewportSizeCalculator = viewportSizeCalculator;
|
||||||
this._hostNode = hostNode;
|
this._hostNode = hostNode;
|
||||||
this._template = views.getTemplate("post-content");
|
this._template = views.getTemplate("post-content");
|
||||||
|
this._isMediaCached = isMediaCached;
|
||||||
|
|
||||||
let fitMode = settings.get().fitMode;
|
let fitMode = settings.get().fitMode;
|
||||||
if (typeof fitFunctionOverride !== "undefined") {
|
if (typeof fitFunctionOverride !== "undefined") {
|
||||||
@ -143,9 +144,30 @@ class PostContentControl {
|
|||||||
post: this._post,
|
post: this._post,
|
||||||
autoplay: settings.get().autoplayVideos,
|
autoplay: settings.get().autoplayVideos,
|
||||||
});
|
});
|
||||||
|
function load(argument) {
|
||||||
if (settings.get().transparencyGrid) {
|
if (settings.get().transparencyGrid) {
|
||||||
newNode.classList.add("transparency-grid");
|
newNode.classList.add("transparency-grid");
|
||||||
}
|
}
|
||||||
|
newNode.firstElementChild.style.backgroundImage = "";
|
||||||
|
}
|
||||||
|
if (["image", "flash"].includes(this._post.type)) {
|
||||||
|
if (this._post.type !== "image" || !this._isMediaCached) {
|
||||||
|
newNode.firstElementChild.style.backgroundImage = "url("+this._post.originalThumbnailUrl+")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this._post.type == "image" && !this._isMediaCached) {
|
||||||
|
newNode.firstElementChild.addEventListener("load", load);
|
||||||
|
} else if (settings.get().transparencyGrid) {
|
||||||
|
newNode.classList.add("transparency-grid");
|
||||||
|
}
|
||||||
|
newNode.firstElementChild.addEventListener("error", (e) => {
|
||||||
|
newNode.classList.add("post-error");
|
||||||
|
if (["image", "animation"].includes(this._post.type)) {
|
||||||
|
newNode.firstElementChild.removeEventListener("load", load);
|
||||||
|
newNode.firstElementChild.style.backgroundImage = "url("+this._post.originalThumbnailUrl+")";
|
||||||
|
newNode.firstElementChild.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
|
}
|
||||||
|
});
|
||||||
if (this._postContentNode) {
|
if (this._postContentNode) {
|
||||||
this._hostNode.replaceChild(newNode, this._postContentNode);
|
this._hostNode.replaceChild(newNode, this._postContentNode);
|
||||||
} else {
|
} else {
|
||||||
|
@ -138,10 +138,7 @@ class PostEditSidebarControl extends events.EventTarget {
|
|||||||
this._thumbnailRemovalLinkNode.addEventListener("click", (e) =>
|
this._thumbnailRemovalLinkNode.addEventListener("click", (e) =>
|
||||||
this._evtRemoveThumbnailClick(e)
|
this._evtRemoveThumbnailClick(e)
|
||||||
);
|
);
|
||||||
this._thumbnailRemovalLinkNode.style.display = this._post
|
this._thumbnailRemovalLinkUpdate(this._post);
|
||||||
.hasCustomThumbnail
|
|
||||||
? "block"
|
|
||||||
: "none";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._addNoteLinkNode) {
|
if (this._addNoteLinkNode) {
|
||||||
@ -249,12 +246,25 @@ class PostEditSidebarControl extends events.EventTarget {
|
|||||||
this._poolsExpander.title = `Pools (${this._post.pools.length})`;
|
this._poolsExpander.title = `Pools (${this._post.pools.length})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_thumbnailRemovalLinkUpdate(post) {
|
||||||
|
if (this._thumbnailRemovalLinkNode) {
|
||||||
|
this._thumbnailRemovalLinkNode.style.display = post
|
||||||
|
.customThumbnailUrl
|
||||||
|
? "block"
|
||||||
|
: "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_evtPostContentChange(e) {
|
_evtPostContentChange(e) {
|
||||||
this._contentFileDropper.reset();
|
this._contentFileDropper.reset();
|
||||||
|
this._thumbnailRemovalLinkUpdate(e.detail.post);
|
||||||
|
this._newPostContent = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_evtPostThumbnailChange(e) {
|
_evtPostThumbnailChange(e) {
|
||||||
this._thumbnailFileDropper.reset();
|
this._thumbnailFileDropper.reset();
|
||||||
|
this._thumbnailRemovalLinkUpdate(e.detail.post);
|
||||||
|
this._newPostThumbnail = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
_evtRemoveThumbnailClick(e) {
|
_evtRemoveThumbnailClick(e) {
|
||||||
@ -427,9 +437,7 @@ class PostEditSidebarControl extends events.EventTarget {
|
|||||||
: undefined,
|
: undefined,
|
||||||
|
|
||||||
thumbnail:
|
thumbnail:
|
||||||
this._newPostThumbnail !== undefined && this._newPostThumbnail !== null
|
this._newPostThumbnail,
|
||||||
? this._newPostThumbnail
|
|
||||||
: undefined,
|
|
||||||
|
|
||||||
source: this._sourceInputNode
|
source: this._sourceInputNode
|
||||||
? this._sourceInputNode.value
|
? this._sourceInputNode.value
|
||||||
|
@ -70,6 +70,14 @@ class Post extends events.EventTarget {
|
|||||||
return this._thumbnailUrl;
|
return this._thumbnailUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get customThumbnailUrl() {
|
||||||
|
return this._customThumbnailUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
get originalThumbnailUrl() {
|
||||||
|
return this._originalThumbnailUrl;
|
||||||
|
}
|
||||||
|
|
||||||
get source() {
|
get source() {
|
||||||
return this._source;
|
return this._source;
|
||||||
}
|
}
|
||||||
@ -146,10 +154,6 @@ class Post extends events.EventTarget {
|
|||||||
return this._ownScore;
|
return this._ownScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasCustomThumbnail() {
|
|
||||||
return this._hasCustomThumbnail;
|
|
||||||
}
|
|
||||||
|
|
||||||
set flags(value) {
|
set flags(value) {
|
||||||
this._flags = value;
|
this._flags = value;
|
||||||
}
|
}
|
||||||
@ -477,7 +481,9 @@ class Post extends events.EventTarget {
|
|||||||
response.contentUrl,
|
response.contentUrl,
|
||||||
document.getElementsByTagName("base")[0].href
|
document.getElementsByTagName("base")[0].href
|
||||||
).href,
|
).href,
|
||||||
_thumbnailUrl: response.thumbnailUrl,
|
_thumbnailUrl: response.customThumbnailUrl ? response.customThumbnailUrl : response.thumbnailUrl,
|
||||||
|
_customThumbnailUrl: response.customThumbnailUrl,
|
||||||
|
_originalThumbnailUrl: response.thumbnailUrl,
|
||||||
_source: response.source,
|
_source: response.source,
|
||||||
_canvasWidth: response.canvasWidth,
|
_canvasWidth: response.canvasWidth,
|
||||||
_canvasHeight: response.canvasHeight,
|
_canvasHeight: response.canvasHeight,
|
||||||
@ -491,7 +497,6 @@ class Post extends events.EventTarget {
|
|||||||
_favoriteCount: response.favoriteCount,
|
_favoriteCount: response.favoriteCount,
|
||||||
_ownScore: response.ownScore,
|
_ownScore: response.ownScore,
|
||||||
_ownFavorite: response.ownFavorite,
|
_ownFavorite: response.ownFavorite,
|
||||||
_hasCustomThumbnail: response.hasCustomThumbnail,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let obj of [this, this._orig]) {
|
for (let obj of [this, this._orig]) {
|
||||||
|
@ -211,6 +211,15 @@ function getPrettyName(tag) {
|
|||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMediaCached(post) {
|
||||||
|
if (post.type !== "image") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const img = new Image()
|
||||||
|
img.src = post.contentUrl;
|
||||||
|
return img.complete;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
range: range,
|
range: range,
|
||||||
formatRelativeTime: formatRelativeTime,
|
formatRelativeTime: formatRelativeTime,
|
||||||
@ -229,4 +238,5 @@ module.exports = {
|
|||||||
escapeSearchTerm: escapeSearchTerm,
|
escapeSearchTerm: escapeSearchTerm,
|
||||||
dataURItoBlob: dataURItoBlob,
|
dataURItoBlob: dataURItoBlob,
|
||||||
getPrettyName: getPrettyName,
|
getPrettyName: getPrettyName,
|
||||||
|
isMediaCached: isMediaCached,
|
||||||
};
|
};
|
||||||
|
@ -49,7 +49,7 @@ function makeThumbnail(url) {
|
|||||||
style: `background-image: url(\'${url}\')`,
|
style: `background-image: url(\'${url}\')`,
|
||||||
}
|
}
|
||||||
: { class: "thumbnail empty" },
|
: { class: "thumbnail empty" },
|
||||||
makeElement("img", { alt: "thumbnail", src: url })
|
makeElement("img", { alt: "thumbnail", src: url, draggable: "false" })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ class HomeView {
|
|||||||
this._postContentControl = new PostContentControl(
|
this._postContentControl = new PostContentControl(
|
||||||
this._postContainerNode,
|
this._postContainerNode,
|
||||||
postInfo.featuredPost,
|
postInfo.featuredPost,
|
||||||
|
misc.isMediaCached(postInfo.featuredPost),
|
||||||
() => {
|
() => {
|
||||||
return [window.innerWidth * 0.8, window.innerHeight * 0.7];
|
return [window.innerWidth * 0.8, window.innerHeight * 0.7];
|
||||||
},
|
},
|
||||||
|
@ -31,6 +31,7 @@ class PostMainView {
|
|||||||
this._postContentControl = new PostContentControl(
|
this._postContentControl = new PostContentControl(
|
||||||
postContainerNode,
|
postContainerNode,
|
||||||
ctx.post,
|
ctx.post,
|
||||||
|
misc.isMediaCached(ctx.post),
|
||||||
() => {
|
() => {
|
||||||
const margin = sidebarNode.getBoundingClientRect().left;
|
const margin = sidebarNode.getBoundingClientRect().left;
|
||||||
|
|
||||||
|
@ -401,6 +401,14 @@ class PostUploadView extends events.EventTarget {
|
|||||||
.addEventListener("click", (e) =>
|
.addEventListener("click", (e) =>
|
||||||
this._evtMoveClick(e, uploadable, 1)
|
this._evtMoveClick(e, uploadable, 1)
|
||||||
);
|
);
|
||||||
|
if (uploadable.type == "video") {
|
||||||
|
const video = rowNode.querySelector("video");
|
||||||
|
if (video) {
|
||||||
|
video.addEventListener("loadedmetadata", (e) => {
|
||||||
|
if (!isNaN(video.duration)) video.currentTime = Math.floor(video.duration * 0.3)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateThumbnailNode(uploadable) {
|
_updateThumbnailNode(uploadable) {
|
||||||
|
@ -33,6 +33,21 @@ RUN pip3 install --no-cache-dir --disable-pip-version-check \
|
|||||||
"pillow-avif-plugin~=1.1.0"
|
"pillow-avif-plugin~=1.1.0"
|
||||||
RUN apk --no-cache del py3-pip
|
RUN apk --no-cache del py3-pip
|
||||||
|
|
||||||
|
# build and install mozjpeg
|
||||||
|
RUN apk --no-cache add automake cmake nasm libpng-dev libpng curl
|
||||||
|
RUN curl -fL "https://github.com/mozilla/mozjpeg/archive/refs/tags/v4.1.1.tar.gz" -o mozjpeg.tar.gz
|
||||||
|
RUN tar xzf mozjpeg.tar.gz && cd mozjpeg-* \
|
||||||
|
&& cmake -DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DCMAKE_INSTALL_LIBDIR=/usr/lib \
|
||||||
|
-DBUILD_SHARED_LIBS=True \
|
||||||
|
-DCMAKE_BUILD_TYPE=None \
|
||||||
|
-DCMAKE_C_FLAGS="$CFLAGS" \
|
||||||
|
-DWITH_JPEG8=1 \
|
||||||
|
-DWITH_TURBOJPEG=1 \
|
||||||
|
-DENABLE_STATIC=0 \
|
||||||
|
&& make install && cd
|
||||||
|
RUN apk --no-cache del automake nasm cmake curl
|
||||||
|
|
||||||
COPY ./ /opt/app/
|
COPY ./ /opt/app/
|
||||||
RUN rm -rf /opt/app/szurubooru/tests
|
RUN rm -rf /opt/app/szurubooru/tests
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ from getpass import getpass
|
|||||||
from sys import stderr
|
from sys import stderr
|
||||||
|
|
||||||
from szurubooru import config, db, errors, model
|
from szurubooru import config, db, errors, model
|
||||||
from szurubooru.func import files, images
|
from szurubooru.func import files, images, mime
|
||||||
from szurubooru.func import posts as postfuncs
|
from szurubooru.func import posts as postfuncs
|
||||||
from szurubooru.func import users as userfuncs
|
from szurubooru.func import users as userfuncs
|
||||||
|
|
||||||
@ -95,7 +95,14 @@ def regenerate_thumbnails() -> None:
|
|||||||
for post in db.session.query(model.Post).all():
|
for post in db.session.query(model.Post).all():
|
||||||
print("Generating tumbnail for post %d ..." % post.post_id, end="\r")
|
print("Generating tumbnail for post %d ..." % post.post_id, end="\r")
|
||||||
try:
|
try:
|
||||||
postfuncs.generate_post_thumbnail(post)
|
content = files.get(postfuncs.get_post_content_path(post))
|
||||||
|
postfuncs.generate_post_thumbnail(postfuncs.get_post_thumbnail_path(post), content, seek=False)
|
||||||
|
|
||||||
|
custom_content = files.get(postfuncs.get_post_custom_content_path(post))
|
||||||
|
if custom_content:
|
||||||
|
generate_post_thumbnail(get_post_custom_thumbnail_path(post), custom_content, seek=True)
|
||||||
|
elif mime.is_video(post.mime_type):
|
||||||
|
generate_post_thumbnail(get_post_custom_thumbnail_path(post), content, seek=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import glob
|
||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from szurubooru import config
|
from szurubooru import config
|
||||||
@ -24,6 +25,10 @@ def scan(path: str) -> List[Any]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def find(path: str, pattern: str, recursive: bool = False) -> List[Any]:
|
||||||
|
return glob.glob(glob.escape(_get_full_path(path) + "/") + pattern, recursive=recursive)
|
||||||
|
|
||||||
|
|
||||||
def move(source_path: str, target_path: str) -> None:
|
def move(source_path: str, target_path: str) -> None:
|
||||||
os.rename(_get_full_path(source_path), _get_full_path(target_path))
|
os.rename(_get_full_path(source_path), _get_full_path(target_path))
|
||||||
|
|
||||||
|
@ -24,6 +24,11 @@ def convert_heif_to_png(content: bytes) -> bytes:
|
|||||||
return img_byte_arr.getvalue()
|
return img_byte_arr.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_loop(content: bytes) -> bytes:
|
||||||
|
img = PILImage.open(BytesIO(content))
|
||||||
|
return "loop" in img.info
|
||||||
|
|
||||||
|
|
||||||
class Image:
|
class Image:
|
||||||
def __init__(self, content: bytes) -> None:
|
def __init__(self, content: bytes) -> None:
|
||||||
self.content = content
|
self.content = content
|
||||||
@ -41,7 +46,7 @@ class Image:
|
|||||||
def frames(self) -> int:
|
def frames(self) -> int:
|
||||||
return self.info["streams"][0]["nb_read_frames"]
|
return self.info["streams"][0]["nb_read_frames"]
|
||||||
|
|
||||||
def resize_fill(self, width: int, height: int) -> None:
|
def resize_fill(self, width: int, height: int, keep_transparency: bool = True, seek=True) -> None:
|
||||||
width_greater = self.width > self.height
|
width_greater = self.width > self.height
|
||||||
width, height = (-1, height) if width_greater else (width, -1)
|
width, height = (-1, height) if width_greater else (width, -1)
|
||||||
|
|
||||||
@ -50,8 +55,12 @@ class Image:
|
|||||||
"{path}",
|
"{path}",
|
||||||
"-f",
|
"-f",
|
||||||
"image2",
|
"image2",
|
||||||
"-filter:v",
|
"-filter_complex",
|
||||||
"scale='{width}:{height}'".format(width=width, height=height),
|
(
|
||||||
|
"format=rgb32,scale={width}:{height}:flags=bicubic"
|
||||||
|
if keep_transparency else
|
||||||
|
"[0:v]format=rgb32,scale={width}:{height}:flags=bicubic[a];color=white[b];[b][a]scale2ref[b][a];[b][a]overlay"
|
||||||
|
).format(width=width, height=height),
|
||||||
"-map",
|
"-map",
|
||||||
"0:v:0",
|
"0:v:0",
|
||||||
"-vframes",
|
"-vframes",
|
||||||
@ -61,7 +70,8 @@ class Image:
|
|||||||
"-",
|
"-",
|
||||||
]
|
]
|
||||||
if (
|
if (
|
||||||
"duration" in self.info["format"]
|
seek
|
||||||
|
and "duration" in self.info["format"]
|
||||||
and self.info["format"]["format_name"] != "swf"
|
and self.info["format"]["format_name"] != "swf"
|
||||||
):
|
):
|
||||||
duration = float(self.info["format"]["duration"])
|
duration = float(self.info["format"]["duration"])
|
||||||
@ -96,24 +106,13 @@ class Image:
|
|||||||
def to_jpeg(self) -> bytes:
|
def to_jpeg(self) -> bytes:
|
||||||
return self._execute(
|
return self._execute(
|
||||||
[
|
[
|
||||||
"-f",
|
"-quality",
|
||||||
"lavfi",
|
"85",
|
||||||
"-i",
|
"-sample",
|
||||||
"color=white:s=%dx%d" % (self.width, self.height),
|
"1x1",
|
||||||
"-i",
|
|
||||||
"{path}",
|
"{path}",
|
||||||
"-f",
|
],
|
||||||
"image2",
|
program="cjpeg",
|
||||||
"-filter_complex",
|
|
||||||
"overlay",
|
|
||||||
"-map",
|
|
||||||
"0:v:0",
|
|
||||||
"-vframes",
|
|
||||||
"1",
|
|
||||||
"-vcodec",
|
|
||||||
"mjpeg",
|
|
||||||
"-",
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_webm(self) -> bytes:
|
def to_webm(self) -> bytes:
|
||||||
@ -274,7 +273,10 @@ class Image:
|
|||||||
with util.create_temp_file(suffix="." + extension) as handle:
|
with util.create_temp_file(suffix="." + extension) as handle:
|
||||||
handle.write(self.content)
|
handle.write(self.content)
|
||||||
handle.flush()
|
handle.flush()
|
||||||
|
if program in ["ffmpeg", "ffprobe"]:
|
||||||
cli = [program, "-loglevel", "32" if get_logs else "24"] + cli
|
cli = [program, "-loglevel", "32" if get_logs else "24"] + cli
|
||||||
|
else:
|
||||||
|
cli = [program] + cli
|
||||||
cli = [part.format(path=handle.name) for part in cli]
|
cli = [part.format(path=handle.name) for part in cli]
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
cli,
|
cli,
|
||||||
@ -285,7 +287,7 @@ class Image:
|
|||||||
out, err = proc.communicate()
|
out, err = proc.communicate()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Failed to execute ffmpeg command (cli=%r, err=%r)",
|
"Failed to execute {program} command (cli=%r, err=%r)".format(program=program),
|
||||||
" ".join(shlex.quote(arg) for arg in cli),
|
" ".join(shlex.quote(arg) for arg in cli),
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
|
@ -117,7 +117,16 @@ def get_post_content_url(post: model.Post) -> str:
|
|||||||
|
|
||||||
def get_post_thumbnail_url(post: model.Post) -> str:
|
def get_post_thumbnail_url(post: model.Post) -> str:
|
||||||
assert post
|
assert post
|
||||||
return "%s/generated-thumbnails/%d_%s.jpg" % (
|
return "%s/generated-thumbnails/sample_%d_%s.jpg" % (
|
||||||
|
config.config["data_url"].rstrip("/"),
|
||||||
|
post.post_id,
|
||||||
|
get_post_security_hash(post.post_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_post_custom_thumbnail_url(post: model.Post) -> str:
|
||||||
|
assert post
|
||||||
|
return "%s/generated-thumbnails/custom-thumbnails/sample_%d_%s.jpg" % (
|
||||||
config.config["data_url"].rstrip("/"),
|
config.config["data_url"].rstrip("/"),
|
||||||
post.post_id,
|
post.post_id,
|
||||||
get_post_security_hash(post.post_id),
|
get_post_security_hash(post.post_id),
|
||||||
@ -134,17 +143,26 @@ def get_post_content_path(post: model.Post) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_post_thumbnail_path(post: model.Post) -> str:
|
def get_post_custom_content_path(post: model.Post) -> str:
|
||||||
assert post
|
assert post
|
||||||
return "generated-thumbnails/%d_%s.jpg" % (
|
assert post.post_id
|
||||||
|
return "posts/custom-thumbnails/%d_%s.dat" % (
|
||||||
post.post_id,
|
post.post_id,
|
||||||
get_post_security_hash(post.post_id),
|
get_post_security_hash(post.post_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_post_thumbnail_backup_path(post: model.Post) -> str:
|
def get_post_thumbnail_path(post: model.Post) -> str:
|
||||||
assert post
|
assert post
|
||||||
return "posts/custom-thumbnails/%d_%s.dat" % (
|
return "generated-thumbnails/sample_%d_%s.jpg" % (
|
||||||
|
post.post_id,
|
||||||
|
get_post_security_hash(post.post_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_post_custom_thumbnail_path(post: model.Post) -> str:
|
||||||
|
assert post
|
||||||
|
return "generated-thumbnails/custom-thumbnails/sample_%d_%s.jpg" % (
|
||||||
post.post_id,
|
post.post_id,
|
||||||
get_post_security_hash(post.post_id),
|
get_post_security_hash(post.post_id),
|
||||||
)
|
)
|
||||||
@ -180,6 +198,7 @@ class PostSerializer(serialization.BaseSerializer):
|
|||||||
"canvasHeight": self.serialize_canvas_height,
|
"canvasHeight": self.serialize_canvas_height,
|
||||||
"contentUrl": self.serialize_content_url,
|
"contentUrl": self.serialize_content_url,
|
||||||
"thumbnailUrl": self.serialize_thumbnail_url,
|
"thumbnailUrl": self.serialize_thumbnail_url,
|
||||||
|
"customThumbnailUrl": self.serialize_custom_thumbnail_url,
|
||||||
"flags": self.serialize_flags,
|
"flags": self.serialize_flags,
|
||||||
"tags": self.serialize_tags,
|
"tags": self.serialize_tags,
|
||||||
"relations": self.serialize_relations,
|
"relations": self.serialize_relations,
|
||||||
@ -195,7 +214,6 @@ class PostSerializer(serialization.BaseSerializer):
|
|||||||
"featureCount": self.serialize_feature_count,
|
"featureCount": self.serialize_feature_count,
|
||||||
"lastFeatureTime": self.serialize_last_feature_time,
|
"lastFeatureTime": self.serialize_last_feature_time,
|
||||||
"favoritedBy": self.serialize_favorited_by,
|
"favoritedBy": self.serialize_favorited_by,
|
||||||
"hasCustomThumbnail": self.serialize_has_custom_thumbnail,
|
|
||||||
"notes": self.serialize_notes,
|
"notes": self.serialize_notes,
|
||||||
"comments": self.serialize_comments,
|
"comments": self.serialize_comments,
|
||||||
"pools": self.serialize_pools,
|
"pools": self.serialize_pools,
|
||||||
@ -319,8 +337,9 @@ class PostSerializer(serialization.BaseSerializer):
|
|||||||
for rel in self.post.favorited_by
|
for rel in self.post.favorited_by
|
||||||
]
|
]
|
||||||
|
|
||||||
def serialize_has_custom_thumbnail(self) -> Any:
|
def serialize_custom_thumbnail_url(self) -> Any:
|
||||||
return files.has(get_post_thumbnail_backup_path(self.post))
|
if files.has(get_post_custom_thumbnail_path(self.post)):
|
||||||
|
return get_post_custom_thumbnail_url(self.post)
|
||||||
|
|
||||||
def serialize_notes(self) -> Any:
|
def serialize_notes(self) -> Any:
|
||||||
return sorted(
|
return sorted(
|
||||||
@ -357,7 +376,7 @@ def serialize_micro_post(
|
|||||||
post: model.Post, auth_user: model.User
|
post: model.Post, auth_user: model.User
|
||||||
) -> Optional[rest.Response]:
|
) -> Optional[rest.Response]:
|
||||||
return serialize_post(
|
return serialize_post(
|
||||||
post, auth_user=auth_user, options=["id", "thumbnailUrl"]
|
post, auth_user=auth_user, options=["id", "thumbnailUrl", "customThumbnailUrl"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -462,32 +481,28 @@ def _before_post_delete(
|
|||||||
) -> None:
|
) -> None:
|
||||||
if post.post_id:
|
if post.post_id:
|
||||||
if config.config["delete_source_files"]:
|
if config.config["delete_source_files"]:
|
||||||
files.delete(get_post_content_path(post))
|
pattern = f"{post.post_id}_*"
|
||||||
files.delete(get_post_thumbnail_path(post))
|
for file in files.find("posts", "**/" + pattern, recursive=True) + files.find("generated-thumbnails", "**/sample_" + pattern, recursive=True):
|
||||||
|
files.delete(file)
|
||||||
|
|
||||||
|
|
||||||
def _sync_post_content(post: model.Post) -> None:
|
def _sync_post_content(post: model.Post) -> None:
|
||||||
regenerate_thumb = False
|
|
||||||
|
|
||||||
if hasattr(post, "__content"):
|
if hasattr(post, "__content"):
|
||||||
content = getattr(post, "__content")
|
content = getattr(post, "__content")
|
||||||
files.save(get_post_content_path(post), content)
|
files.save(get_post_content_path(post), content)
|
||||||
|
generate_post_thumbnail(get_post_thumbnail_path(post), content, seek=False)
|
||||||
|
if mime.is_video(post.mime_type):
|
||||||
|
generate_post_thumbnail(get_post_custom_thumbnail_path(post), content, seek=True)
|
||||||
delattr(post, "__content")
|
delattr(post, "__content")
|
||||||
regenerate_thumb = True
|
|
||||||
|
|
||||||
if hasattr(post, "__thumbnail"):
|
if hasattr(post, "__thumbnail"):
|
||||||
if getattr(post, "__thumbnail"):
|
if getattr(post, "__thumbnail"):
|
||||||
files.save(
|
thumbnail = getattr(post, "__thumbnail")
|
||||||
get_post_thumbnail_backup_path(post),
|
files.save(get_post_custom_content_path(post), thumbnail)
|
||||||
getattr(post, "__thumbnail"),
|
generate_post_thumbnail(get_post_custom_thumbnail_path(post), thumbnail, seek=True)
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
files.delete(get_post_thumbnail_backup_path(post))
|
files.delete(get_post_custom_thumbnail_path(post))
|
||||||
delattr(post, "__thumbnail")
|
delattr(post, "__thumbnail")
|
||||||
regenerate_thumb = True
|
|
||||||
|
|
||||||
if regenerate_thumb:
|
|
||||||
generate_post_thumbnail(post)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_alternate_formats(
|
def generate_alternate_formats(
|
||||||
@ -677,22 +692,19 @@ def update_post_thumbnail(
|
|||||||
setattr(post, "__thumbnail", content)
|
setattr(post, "__thumbnail", content)
|
||||||
|
|
||||||
|
|
||||||
def generate_post_thumbnail(post: model.Post) -> None:
|
def generate_post_thumbnail(path: str, content: bytes, seek=True) -> None:
|
||||||
assert post
|
|
||||||
if files.has(get_post_thumbnail_backup_path(post)):
|
|
||||||
content = files.get(get_post_thumbnail_backup_path(post))
|
|
||||||
else:
|
|
||||||
content = files.get(get_post_content_path(post))
|
|
||||||
try:
|
try:
|
||||||
assert content
|
assert content
|
||||||
image = images.Image(content)
|
image = images.Image(content)
|
||||||
image.resize_fill(
|
image.resize_fill(
|
||||||
int(config.config["thumbnails"]["post_width"]),
|
int(config.config["thumbnails"]["post_width"]),
|
||||||
int(config.config["thumbnails"]["post_height"]),
|
int(config.config["thumbnails"]["post_height"]),
|
||||||
|
keep_transparency=False,
|
||||||
|
seek=seek,
|
||||||
)
|
)
|
||||||
files.save(get_post_thumbnail_path(post), image.to_jpeg())
|
files.save(path, image.to_jpeg())
|
||||||
except errors.ProcessingError:
|
except errors.ProcessingError:
|
||||||
files.save(get_post_thumbnail_path(post), EMPTY_PIXEL)
|
files.save(path, EMPTY_PIXEL)
|
||||||
|
|
||||||
|
|
||||||
def update_post_tags(
|
def update_post_tags(
|
||||||
|
@ -311,6 +311,8 @@ def update_user_avatar(
|
|||||||
image.resize_fill(
|
image.resize_fill(
|
||||||
int(config.config["thumbnails"]["avatar_width"]),
|
int(config.config["thumbnails"]["avatar_width"]),
|
||||||
int(config.config["thumbnails"]["avatar_height"]),
|
int(config.config["thumbnails"]["avatar_height"]),
|
||||||
|
keep_transparency=False,
|
||||||
|
seek=False,
|
||||||
)
|
)
|
||||||
files.save(avatar_path, image.to_png())
|
files.save(avatar_path, image.to_png())
|
||||||
else:
|
else:
|
||||||
|
@ -51,7 +51,7 @@ class Context:
|
|||||||
use_video_downloader: bool = False,
|
use_video_downloader: bool = False,
|
||||||
allow_tokens: bool = True,
|
allow_tokens: bool = True,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
if name in self._files and self._files[name]:
|
if name in self._files:
|
||||||
return self._files[name]
|
return self._files[name]
|
||||||
|
|
||||||
if name + "Url" in self._params:
|
if name + "Url" in self._params:
|
||||||
|
@ -41,7 +41,7 @@ def test_get_post_thumbnail_url(input_mime_type, config_injector):
|
|||||||
post.mime_type = input_mime_type
|
post.mime_type = input_mime_type
|
||||||
assert (
|
assert (
|
||||||
posts.get_post_thumbnail_url(post)
|
posts.get_post_thumbnail_url(post)
|
||||||
== "http://example.com/generated-thumbnails/1_244c8840887984c4.jpg"
|
== "http://example.com/generated-thumbnails/sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -67,18 +67,18 @@ def test_get_post_thumbnail_path(input_mime_type):
|
|||||||
post.mime_type = input_mime_type
|
post.mime_type = input_mime_type
|
||||||
assert (
|
assert (
|
||||||
posts.get_post_thumbnail_path(post)
|
posts.get_post_thumbnail_path(post)
|
||||||
== "generated-thumbnails/1_244c8840887984c4.jpg"
|
== "generated-thumbnails/sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("input_mime_type", ["image/jpeg", "image/gif"])
|
@pytest.mark.parametrize("input_mime_type", ["image/jpeg", "image/gif"])
|
||||||
def test_get_post_thumbnail_backup_path(input_mime_type):
|
def test_get_post_custom_thumbnail_path(input_mime_type):
|
||||||
post = model.Post()
|
post = model.Post()
|
||||||
post.post_id = 1
|
post.post_id = 1
|
||||||
post.mime_type = input_mime_type
|
post.mime_type = input_mime_type
|
||||||
assert (
|
assert (
|
||||||
posts.get_post_thumbnail_backup_path(post)
|
posts.get_post_custom_thumbnail_path(post)
|
||||||
== "posts/custom-thumbnails/1_244c8840887984c4.dat"
|
== "generated-thumbnails/custom-thumbnails/sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -226,7 +226,9 @@ def test_serialize_post(
|
|||||||
"canvasHeight": 300,
|
"canvasHeight": 300,
|
||||||
"contentUrl": "http://example.com/posts/1_244c8840887984c4.jpg",
|
"contentUrl": "http://example.com/posts/1_244c8840887984c4.jpg",
|
||||||
"thumbnailUrl": "http://example.com/"
|
"thumbnailUrl": "http://example.com/"
|
||||||
"generated-thumbnails/1_244c8840887984c4.jpg",
|
"generated-thumbnails/sample_1_244c8840887984c4.jpg",
|
||||||
|
"customThumbnailUrl": "http://example.com/"
|
||||||
|
"generated-thumbnails/custom-thumbnails/sample_1_244c8840887984c4.jpg",
|
||||||
"flags": ["loop"],
|
"flags": ["loop"],
|
||||||
"tags": [
|
"tags": [
|
||||||
{
|
{
|
||||||
@ -270,17 +272,27 @@ def test_serialize_post(
|
|||||||
"relationCount": 0,
|
"relationCount": 0,
|
||||||
"lastFeatureTime": datetime(1999, 1, 1),
|
"lastFeatureTime": datetime(1999, 1, 1),
|
||||||
"favoritedBy": ["fav1"],
|
"favoritedBy": ["fav1"],
|
||||||
"hasCustomThumbnail": True,
|
|
||||||
"mimeType": "image/jpeg",
|
"mimeType": "image/jpeg",
|
||||||
"comments": ["commenter1", "commenter2"],
|
"comments": ["commenter1", "commenter2"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_serialize_micro_post(post_factory, user_factory):
|
def test_serialize_micro_post(tmpdir, config_injector, post_factory, user_factory):
|
||||||
with patch("szurubooru.func.posts.get_post_thumbnail_url"):
|
with patch("szurubooru.func.posts.get_post_thumbnail_url"):
|
||||||
posts.get_post_thumbnail_url.return_value = (
|
posts.get_post_thumbnail_url.return_value = (
|
||||||
"https://example.com/thumb.png"
|
"https://example.com/thumb.png"
|
||||||
)
|
)
|
||||||
|
config_injector(
|
||||||
|
{
|
||||||
|
"data_dir": str(tmpdir.mkdir("data")),
|
||||||
|
"thumbnails": {
|
||||||
|
"post_width": 300,
|
||||||
|
"post_height": 300,
|
||||||
|
},
|
||||||
|
"secret": "test",
|
||||||
|
"allow_broken_uploads": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
auth_user = user_factory()
|
auth_user = user_factory()
|
||||||
post = post_factory()
|
post = post_factory()
|
||||||
db.session.add(post)
|
db.session.add(post)
|
||||||
@ -288,6 +300,7 @@ def test_serialize_micro_post(post_factory, user_factory):
|
|||||||
assert posts.serialize_micro_post(post, auth_user) == {
|
assert posts.serialize_micro_post(post, auth_user) == {
|
||||||
"id": post.post_id,
|
"id": post.post_id,
|
||||||
"thumbnailUrl": "https://example.com/thumb.png",
|
"thumbnailUrl": "https://example.com/thumb.png",
|
||||||
|
"customThumbnailUrl": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -605,7 +618,7 @@ def test_update_post_thumbnail_to_new_one(
|
|||||||
assert post.post_id
|
assert post.post_id
|
||||||
generated_path = (
|
generated_path = (
|
||||||
"{}/data/generated-thumbnails/".format(tmpdir)
|
"{}/data/generated-thumbnails/".format(tmpdir)
|
||||||
+ "1_244c8840887984c4.jpg"
|
+ "sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
source_path = (
|
source_path = (
|
||||||
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
||||||
@ -646,7 +659,7 @@ def test_update_post_thumbnail_to_default(
|
|||||||
assert post.post_id
|
assert post.post_id
|
||||||
generated_path = (
|
generated_path = (
|
||||||
"{}/data/generated-thumbnails/".format(tmpdir)
|
"{}/data/generated-thumbnails/".format(tmpdir)
|
||||||
+ "1_244c8840887984c4.jpg"
|
+ "sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
source_path = (
|
source_path = (
|
||||||
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
||||||
@ -686,7 +699,7 @@ def test_update_post_thumbnail_with_broken_thumbnail(
|
|||||||
assert post.post_id
|
assert post.post_id
|
||||||
generated_path = (
|
generated_path = (
|
||||||
"{}/data/generated-thumbnails/".format(tmpdir)
|
"{}/data/generated-thumbnails/".format(tmpdir)
|
||||||
+ "1_244c8840887984c4.jpg"
|
+ "sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
source_path = (
|
source_path = (
|
||||||
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
||||||
@ -705,8 +718,8 @@ def test_update_post_thumbnail_with_broken_thumbnail(
|
|||||||
assert handle.read() == read_asset("png-broken.png")
|
assert handle.read() == read_asset("png-broken.png")
|
||||||
with open(generated_path, "rb") as handle:
|
with open(generated_path, "rb") as handle:
|
||||||
image = images.Image(handle.read())
|
image = images.Image(handle.read())
|
||||||
assert image.width == 1
|
assert image.width == 300
|
||||||
assert image.height == 1
|
assert image.height == 300
|
||||||
|
|
||||||
|
|
||||||
def test_update_post_content_leaving_custom_thumbnail(
|
def test_update_post_content_leaving_custom_thumbnail(
|
||||||
@ -731,7 +744,7 @@ def test_update_post_content_leaving_custom_thumbnail(
|
|||||||
db.session.flush()
|
db.session.flush()
|
||||||
generated_path = (
|
generated_path = (
|
||||||
"{}/data/generated-thumbnails/".format(tmpdir)
|
"{}/data/generated-thumbnails/".format(tmpdir)
|
||||||
+ "1_244c8840887984c4.jpg"
|
+ "sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
source_path = (
|
source_path = (
|
||||||
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
||||||
@ -763,7 +776,7 @@ def test_update_post_content_convert_heif_to_png_when_processing(
|
|||||||
db.session.flush()
|
db.session.flush()
|
||||||
generated_path = (
|
generated_path = (
|
||||||
"{}/data/generated-thumbnails/".format(tmpdir)
|
"{}/data/generated-thumbnails/".format(tmpdir)
|
||||||
+ "1_244c8840887984c4.jpg"
|
+ "sample_1_244c8840887984c4.jpg"
|
||||||
)
|
)
|
||||||
source_path = (
|
source_path = (
|
||||||
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
"{}/data/posts/custom-thumbnails/".format(tmpdir)
|
||||||
|
Reference in New Issue
Block a user