diff --git a/app/blueprints/api/endpoints.py b/app/blueprints/api/endpoints.py
index 8b968930..94aeecd3 100644
--- a/app/blueprints/api/endpoints.py
+++ b/app/blueprints/api/endpoints.py
@@ -14,18 +14,19 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
-from flask import *
+from flask import request, jsonify, current_app, abort
from flask_login import current_user, login_required
+from sqlalchemy.sql.expression import func
+
+from app import csrf
+from app.markdown import render_markdown
+from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Tags, Permission, ForumTopic, MinetestRelease, APIToken
+from app.querybuilder import QueryBuilder
+from app.utils import is_package_page
from . import bp
from .auth import is_api_authd
-from .support import error, api_create_vcs_release, api_create_zip_release
-from app import csrf
-from app.models import *
-from app.utils import is_package_page
-from app.markdown import render_markdown
-from app.querybuilder import QueryBuilder
-from sqlalchemy.sql.expression import func
+from .support import error, api_create_vcs_release, api_create_zip_release, api_create_screenshot
+
@bp.route("/api/packages/")
def packages():
@@ -230,3 +231,25 @@ def create_release(token, package):
error(400, "Missing 'file' in multipart body")
return api_create_zip_release(token, package, data["title"], file)
+
+
+@bp.route("/api/packages///screenshots/new/", methods=["POST"])
+@csrf.exempt
+@is_package_page
+@is_api_authd
+def create_screenshot(token: APIToken, package: Package):
+ if not token:
+ error(401, "Authentication needed")
+
+ if not package.checkPerm(token.owner, Permission.ADD_SCREENSHOTS):
+ error(403, "You do not have the permission to create screenshots")
+
+ data = request.form
+ if "title" not in data:
+ error(400, "Title is required in the POST data")
+
+ file = request.files.get("file")
+ if file is None:
+ error(400, "Missing 'file' in multipart body")
+
+ return api_create_screenshot(token, package, data["title"], file)
diff --git a/app/blueprints/api/support.py b/app/blueprints/api/support.py
index f5b115bc..0e0c6c72 100644
--- a/app/blueprints/api/support.py
+++ b/app/blueprints/api/support.py
@@ -17,7 +17,8 @@
from flask import jsonify, abort, make_response, url_for
from app.logic.releases import LogicError, do_create_vcs_release, do_create_zip_release
-from app.models import APIToken, Package, MinetestRelease
+from app.logic.screenshots import do_create_screenshot
+from app.models import APIToken, Package, MinetestRelease, PackageScreenshot
def error(code: int, msg: str):
@@ -56,3 +57,15 @@ def api_create_zip_release(token: APIToken, package: Package, title: str, file,
"task": url_for("tasks.check", id=rel.task_id),
"release": rel.getAsDictionary()
})
+
+
+def api_create_screenshot(token: APIToken, package: Package, title: str, file):
+ if not token.canOperateOnPackage(package):
+ error(403, "API token does not have access to the package")
+
+ ss : PackageScreenshot = run_safe(do_create_screenshot, token.owner, package, title, file)
+
+ return jsonify({
+ "success": True,
+ "screenshot": ss.getAsDictionary()
+ })
diff --git a/app/flatpages/help/api.md b/app/flatpages/help/api.md
index e69d349d..4ad3f5f1 100644
--- a/app/flatpages/help/api.md
+++ b/app/flatpages/help/api.md
@@ -69,6 +69,22 @@ curl https://content.minetest.net/api/packages/username/name/releases/new/ \
-F title="My Release" -F file=@path/to/file.zip
```
+### Screenshots
+
+* POST `/api/packages///screenshots/new/` (Create)
+ * Requires authentication.
+ * Body is multipart form data.
+ * `title`: human-readable name for the screenshot, shown as a caption and alt text.
+ * `file`: multipart file to upload, like ``.
+
+Example:
+
+```bash
+curl https://content.minetest.net/api/packages/username/name/screenshots/new/ \
+ -H "Authorization: Bearer YOURTOKEN" \
+ -F title="My Release" -F file=@path/to/screnshot.png
+```
+
### Topics
* GET `/api/topics/` - Supports [Package Queries](#package-queries), and the following two options: