diff options
| author | ilotterytea <iltsu@alright.party> | 2025-04-19 16:47:23 +0500 |
|---|---|---|
| committer | ilotterytea <iltsu@alright.party> | 2025-04-19 16:47:23 +0500 |
| commit | 9c2f42e423096bdf56b7f96e07221f6420b0e337 (patch) | |
| tree | 5cbcc6054f0727b4777590d6c25f6c66c68b0075 | |
| parent | ad9ed65599cdcbd90fd6862caae23f5141cdfbf7 (diff) | |
feat: upload emotes
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | database.sql | 5 | ||||
| -rw-r--r-- | public/emotes/upload.php | 112 | ||||
| -rw-r--r-- | src/images.php | 48 | ||||
| -rw-r--r-- | src/utils.php | 33 |
5 files changed, 199 insertions, 1 deletions
@@ -1,3 +1,3 @@ /.vscode -/userdata +userdata/ *.db
\ No newline at end of file diff --git a/database.sql b/database.sql new file mode 100644 index 0000000..b4da2c1 --- /dev/null +++ b/database.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS "emotes" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "code" TEXT NOT NULL, + "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +);
\ No newline at end of file diff --git a/public/emotes/upload.php b/public/emotes/upload.php new file mode 100644 index 0000000..e68aea4 --- /dev/null +++ b/public/emotes/upload.php @@ -0,0 +1,112 @@ +<?php +function abort_upload(string $path, SQLite3 $db, string $id, string $response_text, int $response_code = 400) +{ + $stmt = $db->prepare("DELETE FROM emotes WHERE id = :id"); + $stmt->bindValue(":id", $id, SQLITE3_INTEGER); + $stmt->execute(); + $db->close(); + + array_map("unlink", glob("$path/*.*")); + rmdir($path); + http_response_code($response_code); + exit($response_text); +} + +include "../../src/utils.php"; +include "../../src/images.php"; + +if ($_SERVER['REQUEST_METHOD'] != "POST") { + echo 'imagine there is a page'; + exit; +} + +if (!isset($_FILES["file"])) { + http_response_code(400); + echo json_encode([ + "status_code" => 400, + "message" => "No file set", + "data" => null + ]); + exit; +} + +$code = str_safe($_POST["code"] ?? "", 500); + +if ($code == "") { + http_response_code(400); + echo json_encode([ + "status_code" => 400, + "message" => "Invalid code", + "data" => null + ]); + exit; +} + +// creating a new emote record +$db = new SQLite3("../../database.db"); + +$stmt = $db->prepare("INSERT INTO emotes(code) VALUES (:code)"); +$stmt->bindValue(":code", $code); +$results = $stmt->execute(); + +$id = $db->lastInsertRowID(); + +if ($id == 0) { + $db->close(); + http_response_code(500); + echo json_encode([ + "status_code" => 500, + "message" => "Failed to create an emote record", + "data" => null + ]); + exit; +} + +$path = "../static/userdata/emotes/$id"; + +if (!is_dir($path)) { + mkdir($path, 0777, true); +} + +$image = $_FILES["file"]; + +// resizing the image + +// TODO: make it configurable later +$max_width = max(128, 1); +$max_height = max(128, 1); + +// 3x image +$resized_image = resize_image($image["tmp_name"], "$path/3x", $max_width, $max_height); +if ($resized_image) { + abort_upload($path, $db, $id, $resized_image); +} + +// 2x image +$resized_image = resize_image($image["tmp_name"], "$path/2x", $max_width / 2, $max_height / 2); +if ($resized_image) { + abort_upload($path, $db, $id, $resized_image); +} + +// 1x image +$resized_image = resize_image($image["tmp_name"], "$path/1x", $max_width / 4, $max_height / 4); +if ($resized_image) { + abort_upload($path, $db, $id, $resized_image); +} + +$db->close(); + +if (isset($_SERVER["HTTP_ACCEPT"]) && $_SERVER["HTTP_ACCEPT"] == "application/json") { + http_response_code(201); + echo json_encode([ + "status_code" => 201, + "message" => null, + "data" => [ + "id" => $id, + "code" => $code + ] + ]); + exit; +} + +header("Location: /emotes/$id", true, 307);
\ No newline at end of file diff --git a/src/images.php b/src/images.php new file mode 100644 index 0000000..0e09814 --- /dev/null +++ b/src/images.php @@ -0,0 +1,48 @@ +<?php +function resize_image(string $src_path, string $dst_path, int $max_width, int $max_height): string|null +{ + if ($src_path == "" || !getimagesize($src_path)) { + return json_encode([ + "status_code" => 400, + "message" => "Not an image", + "data" => null + ]); + } + + $imagick = new Imagick(); + + $imagick->readImage($src_path); + $format = strtolower($imagick->getImageFormat()); + + if ($imagick->getNumberImages() > 1) { + $imagick = $imagick->coalesceImages(); + + foreach ($imagick as $frame) { + $width = $frame->getImageWidth(); + $height = $frame->getImageHeight(); + $ratio = min($max_width / $width, $max_height / $height); + $new_width = (int) ($width * $ratio); + $new_height = (int) ($height * $ratio); + + $frame->resizeImage($new_width, $new_height, Imagick::FILTER_TRIANGLE, 1); + $frame->setImagePage($new_width, $new_height, 0, 0); + } + + $imagick = $imagick->deconstructImages(); + $imagick->writeImages("$dst_path.$format", true); + } else { + $width = $imagick->getImageWidth(); + $height = $imagick->getImageHeight(); + $ratio = min($max_width / $width, $max_height / $height); + $new_width = (int) ($width * $ratio); + $new_height = (int) ($height * $ratio); + + $imagick->resizeImage($new_width, $new_height, Imagick::FILTER_TRIANGLE, 1); + $imagick->writeImage("$dst_path.$format"); + } + + $imagick->clear(); + $imagick->destroy(); + + return null; +}
\ No newline at end of file diff --git a/src/utils.php b/src/utils.php new file mode 100644 index 0000000..59ecad4 --- /dev/null +++ b/src/utils.php @@ -0,0 +1,33 @@ +<?php +function generate_random_string(int $length): string +{ + $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + $output = ""; + + for ($i = 0; $i < $length; $i++) { + $charindex = random_int(0, strlen($chars) - 1); + $output .= $chars[$charindex]; + } + + return $output; +} + +function str_safe(string $s, int|null $max_length, bool $remove_new_lines = true): string +{ + $output = $s; + + if ($remove_new_lines) { + $output = str_replace(PHP_EOL, "", $output); + } + + $output = htmlspecialchars($output); + $output = strip_tags($output); + + if ($max_length) { + $output = substr($output, 0, $max_length); + } + + $output = trim($output); + + return $output; +}
\ No newline at end of file |
