Merge remote-tracking branch 'origin/master' into robo/og-tags

This commit is contained in:
Ben Klein 2021-09-17 22:28:08 -04:00
commit 2637a4c207
11 changed files with 78 additions and 42 deletions

View file

@ -13,8 +13,10 @@ $cancel-button-color = tomato
&.inactive input[type=submit], &.inactive input[type=submit],
&.inactive .skip-duplicates &.inactive .skip-duplicates
&.inactive .always-upload-similar
&.uploading input[type=submit], &.uploading input[type=submit],
&.uploading .skip-duplicates, &.uploading .skip-duplicates,
&.uploading .always-upload-similar
&:not(.uploading) .cancel &:not(.uploading) .cancel
display: none display: none
@ -39,6 +41,9 @@ $cancel-button-color = tomato
.skip-duplicates .skip-duplicates
margin-left: 1em margin-left: 1em
.always-upload-similar
margin-left: 1em
form>.messages form>.messages
margin-top: 1em margin-top: 1em

View file

@ -13,6 +13,14 @@
}) %> }) %>
</span> </span>
<span class='always-upload-similar'>
<%= ctx.makeCheckbox({
text: 'Always upload similar',
name: 'always-upload-similar',
checked: false,
}) %>
</span>
<input type='button' value='Cancel' class='cancel'/> <input type='button' value='Cancel' class='cancel'/>
</div> </div>

View file

@ -61,6 +61,7 @@
text: 'Upload anonymously', text: 'Upload anonymously',
name: 'anonymous', name: 'anonymous',
checked: ctx.uploadable.anonymous, checked: ctx.uploadable.anonymous,
readonly: ctx.uploadable.forceAnonymous,
}) %> }) %>
</div> </div>
<% } %> <% } %>

View file

@ -17,7 +17,7 @@ class HomeController {
buildDate: config.meta.buildDate, buildDate: config.meta.buildDate,
canListSnapshots: api.hasPrivilege("snapshots:list"), canListSnapshots: api.hasPrivilege("snapshots:list"),
canListPosts: api.hasPrivilege("posts:list"), canListPosts: api.hasPrivilege("posts:list"),
isDevelopmentMode: config.environment == "development" isDevelopmentMode: config.environment == "development",
}); });
Info.get().then( Info.get().then(

View file

@ -12,7 +12,7 @@ const PostUploadView = require("../views/post_upload_view.js");
const EmptyView = require("../views/empty_view.js"); const EmptyView = require("../views/empty_view.js");
const genericErrorMessage = const genericErrorMessage =
"One of the posts needs your attention; " + "One or more posts needs your attention; " +
'click "resume upload" when you\'re ready.'; 'click "resume upload" when you\'re ready.';
class PostUploadController { class PostUploadController {
@ -55,6 +55,7 @@ class PostUploadController {
_evtSubmit(e) { _evtSubmit(e) {
this._view.disableForm(); this._view.disableForm();
this._view.clearMessages(); this._view.clearMessages();
let anyFailures = false;
e.detail.uploadables e.detail.uploadables
.reduce( .reduce(
@ -62,44 +63,51 @@ class PostUploadController {
promise.then(() => promise.then(() =>
this._uploadSinglePost( this._uploadSinglePost(
uploadable, uploadable,
e.detail.skipDuplicates e.detail.skipDuplicates,
) e.detail.alwaysUploadSimilar
), ).catch((error) => {
Promise.resolve() anyFailures = true;
)
.then(
() => {
this._view.clearMessages();
misc.disableExitConfirmation();
const ctx = router.show(uri.formatClientLink("posts"));
ctx.controller.showSuccess("Posts uploaded.");
},
(error) => {
if (error.uploadable) { if (error.uploadable) {
if (error.similarPosts) { if (error.similarPosts) {
error.uploadable.lookalikes = error.similarPosts; error.uploadable.lookalikes =
this._view.updateUploadable(error.uploadable); error.similarPosts;
this._view.showInfo(genericErrorMessage); this._view.updateUploadable(
error.uploadable
);
this._view.showInfo( this._view.showInfo(
error.message, error.message,
error.uploadable error.uploadable
); );
} else { } else {
this._view.showError(genericErrorMessage);
this._view.showError( this._view.showError(
error.message, error.message,
error.uploadable error.uploadable
); );
} }
} else { } else {
this._view.showError(error.message); this._view.showError(
} error.message,
this._view.enableForm(); uploadable
}
); );
} }
})
),
Promise.resolve()
)
.then(() => {
if (anyFailures) {
this._view.showError(genericErrorMessage);
this._view.enableForm();
} else {
this._view.clearMessages();
misc.disableExitConfirmation();
const ctx = router.show(uri.formatClientLink("posts"));
ctx.controller.showSuccess("Posts uploaded.");
}
});
}
_uploadSinglePost(uploadable, skipDuplicates) { _uploadSinglePost(uploadable, skipDuplicates, alwaysUploadSimilar) {
progress.start(); progress.start();
let reverseSearchPromise = Promise.resolve(); let reverseSearchPromise = Promise.resolve();
if (!uploadable.lookalikesConfirmed) { if (!uploadable.lookalikesConfirmed) {
@ -128,7 +136,10 @@ class PostUploadController {
} }
// notify about similar posts // notify about similar posts
if (searchResult.similarPosts.length) { if (
searchResult.similarPosts.length &&
!alwaysUploadSimilar
) {
let error = new Error( let error = new Error(
`Found ${searchResult.similarPosts.length} similar ` + `Found ${searchResult.similarPosts.length} similar ` +
"posts.\nYou can resume or discard this upload." "posts.\nYou can resume or discard this upload."

View file

@ -196,11 +196,9 @@ class TagInputControl extends events.EventTarget {
const listItemNode = this._createListItemNode(tag); const listItemNode = this._createListItemNode(tag);
if (!tag.category) { if (!tag.category) {
listItemNode.classList.add("new"); listItemNode.classList.add("new");
} } else if (source === SOURCE_IMPLICATION) {
else if (source === SOURCE_IMPLICATION) {
listItemNode.classList.add("implication"); listItemNode.classList.add("implication");
} } else {
else {
listItemNode.classList.add("added"); listItemNode.classList.add("added");
} }
this._tagListNode.prependChild(listItemNode); this._tagListNode.prependChild(listItemNode);

View file

@ -4,12 +4,12 @@ const config = require("./config.js");
if (config.environment == "development") { if (config.environment == "development") {
var ws = new WebSocket("ws://" + location.hostname + ":8080"); var ws = new WebSocket("ws://" + location.hostname + ":8080");
ws.addEventListener('open', function (event) { ws.addEventListener("open", function (event) {
console.log("Live-reloading websocket connected."); console.log("Live-reloading websocket connected.");
}); });
ws.addEventListener('message', (event) => { ws.addEventListener("message", (event) => {
console.log(event); console.log(event);
if (event.data == 'reload'){ if (event.data == "reload") {
location.reload(); location.reload();
} }
}); });

View file

@ -1,6 +1,7 @@
"use strict"; "use strict";
const events = require("../events.js"); const events = require("../events.js");
const api = require("../api.js");
const views = require("../util/views.js"); const views = require("../util/views.js");
const FileDropperControl = require("../controls/file_dropper_control.js"); const FileDropperControl = require("../controls/file_dropper_control.js");
@ -34,7 +35,8 @@ class Uploadable extends events.EventTarget {
this.flags = []; this.flags = [];
this.tags = []; this.tags = [];
this.relations = []; this.relations = [];
this.anonymous = false; this.anonymous = !api.isLoggedIn();
this.forceAnonymous = !api.isLoggedIn();
} }
destroy() {} destroy() {}
@ -358,6 +360,8 @@ class PostUploadView extends events.EventTarget {
detail: { detail: {
uploadables: this._uploadables, uploadables: this._uploadables,
skipDuplicates: this._skipDuplicatesCheckboxNode.checked, skipDuplicates: this._skipDuplicatesCheckboxNode.checked,
alwaysUploadSimilar: this._alwaysUploadSimilarCheckboxNode
.checked,
}, },
}) })
); );
@ -421,6 +425,12 @@ class PostUploadView extends events.EventTarget {
return this._hostNode.querySelector("form [name=skip-duplicates]"); return this._hostNode.querySelector("form [name=skip-duplicates]");
} }
get _alwaysUploadSimilarCheckboxNode() {
return this._hostNode.querySelector(
"form [name=always-upload-similar]"
);
}
get _submitButtonNode() { get _submitButtonNode() {
return this._hostNode.querySelector("form [type=submit]"); return this._hostNode.querySelector("form [type=submit]");
} }

View file

@ -5,14 +5,15 @@ from io import BytesIO
from typing import Any, Callable, List, Optional, Set, Tuple from typing import Any, Callable, List, Optional, Set, Tuple
import numpy as np import numpy as np
from PIL import Image
import pillow_avif import pillow_avif
import pyheif import pyheif
from PIL import Image
from pyheif_pillow_opener import register_heif_opener from pyheif_pillow_opener import register_heif_opener
register_heif_opener()
from szurubooru import config, errors from szurubooru import config, errors
register_heif_opener()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Math based on paper from H. Chi Wong, Marshall Bern and David Goldberg # Math based on paper from H. Chi Wong, Marshall Bern and David Goldberg

View file

@ -6,6 +6,7 @@ import shlex
import subprocess import subprocess
from io import BytesIO from io import BytesIO
from typing import List from typing import List
from PIL import Image as PILImage from PIL import Image as PILImage
from szurubooru import errors from szurubooru import errors
@ -17,7 +18,7 @@ logger = logging.getLogger(__name__)
def convert_heif_to_png(content: bytes) -> bytes: def convert_heif_to_png(content: bytes) -> bytes:
img = PILImage.open(BytesIO(content)) img = PILImage.open(BytesIO(content))
img_byte_arr = BytesIO() img_byte_arr = BytesIO()
img.save(img_byte_arr, format='PNG') img.save(img_byte_arr, format="PNG")
return img_byte_arr.getvalue() return img_byte_arr.getvalue()

View file

@ -88,6 +88,7 @@ def is_animated_gif(content: bytes) -> bool:
and len(re.findall(pattern, content)) > 1 and len(re.findall(pattern, content)) > 1
) )
def is_heif(mime_type: str) -> bool: def is_heif(mime_type: str) -> bool:
return mime_type.lower() in ( return mime_type.lower() in (
"image/heif", "image/heif",