summaryrefslogtreecommitdiff
path: root/server/szurubooru/api/post_api.py
diff options
context:
space:
mode:
Diffstat (limited to 'server/szurubooru/api/post_api.py')
-rw-r--r--server/szurubooru/api/post_api.py82
1 files changed, 81 insertions, 1 deletions
diff --git a/server/szurubooru/api/post_api.py b/server/szurubooru/api/post_api.py
index 7883f5e..4e89f54 100644
--- a/server/szurubooru/api/post_api.py
+++ b/server/szurubooru/api/post_api.py
@@ -1,3 +1,4 @@
+from math import ceil
from datetime import datetime
from typing import Dict, List, Optional
@@ -5,13 +6,15 @@ from szurubooru import db, errors, model, rest, search
from szurubooru.func import (
auth,
favorites,
+ metrics,
mime,
posts,
scores,
serialization,
+ similar,
snapshots,
tags,
- versions,
+ versions, image_hash,
)
_search_executor_config = search.configs.PostSearchConfig()
@@ -167,6 +170,14 @@ def update_post(ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
if ctx.has_file("thumbnail"):
auth.verify_privilege(ctx.user, "posts:edit:thumbnail")
posts.update_post_thumbnail(post, ctx.get_file("thumbnail"))
+ if ctx.has_param("metrics"):
+ auth.verify_privilege(ctx.user, "metrics:edit:posts")
+ metrics.update_or_create_post_metrics(
+ post, ctx.get_param_as_list("metrics"))
+ if ctx.has_param("metricRanges"):
+ auth.verify_privilege(ctx.user, "metrics:edit:posts")
+ metrics.update_or_create_post_metric_ranges(
+ post, ctx.get_param_as_list("metricRanges"))
post.last_edit_time = datetime.utcnow()
ctx.session.flush()
snapshots.modify(post, ctx.user)
@@ -310,3 +321,72 @@ def get_posts_by_image(
for distance, post in lookalikes
],
}
+
+
+@rest.routes.get("/post/(?P<post_id>[^/]+)/reverse-search/?")
+def get_posts_lookalikes(
+ ctx: rest.Context, params: Dict[str, str] = {}
+) -> rest.Response:
+ auth.verify_privilege(ctx.user, "posts:reverse_search")
+ limit = ctx.get_param_as_int("limit", default=10, min=1, max=100)
+ threshold = ctx.get_param_as_float("threshold", default=1, min=0, max=100)
+ query_text = ctx.get_param_as_string("query", default="")
+ post_id = _get_post_id(params)
+ post = posts.get_post_by_id(post_id)
+ if post.signature is None:
+ return {"similarPosts": []}
+
+ sig = image_hash.unpack_signature(post.signature.signature)
+ # limit + 1 because the original post will be excluded
+ lookalikes = posts.search_by_signature(sig, limit + 1, threshold, query_text)
+ # exclude the original post:
+ lookalikes = filter(lambda la: la[1].post_id != post_id, lookalikes)
+ lookalikes = sorted(lookalikes, key=lambda la: la[0])
+ return {
+ "similarPosts": [
+ {
+ "distance": distance,
+ "post": _serialize_post(ctx, post),
+ }
+ for distance, post in lookalikes
+ ],
+ }
+
+
+@rest.routes.get("/posts/median/?")
+def get_posts_median(
+ ctx: rest.Context, _params: Dict[str, str] = {}
+) -> rest.Response:
+ auth.verify_privilege(ctx.user, "posts:list")
+ _search_executor_config.user = ctx.user
+ query_text = ctx.get_param_as_string("query", default="")
+ total_count = _search_executor.count(query_text)
+ offset = ceil(total_count / 2) - 1
+ _, results = _search_executor.execute(query_text, offset, 1)
+ return {
+ "query": query_text,
+ "offset": offset,
+ "limit": 1,
+ "total": len(results),
+ "results": list([_serialize_post(ctx, post) for post in results])
+ }
+
+
+@rest.routes.get("/post/(?P<post_id>[^/]+)/similar-by-tags/?")
+def get_posts_similar_by_tags(
+ ctx: rest.Context, params: Dict[str, str]
+) -> rest.Response:
+ auth.verify_privilege(ctx.user, "posts:view:similar")
+ _search_executor_config.user = ctx.user
+ query_text = ctx.get_param_as_string("query", default="")
+ post_id = _get_post_id(params)
+ post = posts.get_post_by_id(post_id)
+ limit = ctx.get_param_as_int("limit", default=10, min=1, max=100)
+ results = similar.find_similar_posts(post, limit, query_text)
+ return {
+ "query": query_text,
+ "limit": limit,
+ "results": list([
+ posts.serialize_micro_post(result, ctx.user) for result in results
+ ])
+ }