Merge remote-tracking branch 'origin/master' into robo/og-tags
This commit is contained in:
commit
2637a4c207
11 changed files with 78 additions and 42 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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]");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue