Slon/Api/V2/Media: Add support for Catbox API
This commit is contained in:
parent
6ff3a74914
commit
27fa0f8aad
4 changed files with 135 additions and 24 deletions
|
@ -9,6 +9,8 @@ Slon is developed to run from a standard TempleOS installation, or directly from
|
|||
|
||||
Slon includes its own Virtio net and block device drivers, TCP/IPv4 network stack, and various supplemental libraries, all loaded at runtime, making it suitable for QEMU/KVM based cloud environments.
|
||||
|
||||
Image uploads powered by [Catbox](https://catbox.moe) (Support Catbox: [Patreon](https://patreon.com/catbox) | [Ko-fi](https://ko-fi.com/catboxmoe) | [Merch](https://store.catbox.moe))
|
||||
|
||||
Slon utilizes the following third-party libraries:
|
||||
|
||||
- [`stb_image.h`](https://github.com/nothings/stb/blob/master/stb_image.h) and [`stb_image_write.h`](https://github.com/nothings/stb/blob/master/stb_image_write.h) (for optional image processing)
|
||||
|
|
|
@ -1,6 +1,64 @@
|
|||
U0 (*@slon_api_status_create_fedi)(JsonObject* status) = NULL;
|
||||
U0 (*@slon_api_status_delete_fedi)(JsonObject* status) = NULL;
|
||||
|
||||
U0 @slon_api_v1_statuses_delete_file_from_catbox(SlonHttpSession* session, U8* filename)
|
||||
{
|
||||
if (!session || !filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
// build the multipart/form-data payload
|
||||
|
||||
U8* payload = @slon_calloc(session, 4096);
|
||||
I64 payload_size = 0;
|
||||
|
||||
U8* boundary = "----------SlonFormBoundary00";
|
||||
StrPrint(payload, "--%s\r\n", boundary);
|
||||
|
||||
String.Append(payload, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n--%s\r\n", "reqtype", "deletefiles", boundary);
|
||||
if (db->o("settings")->@("catbox_userhash")) {
|
||||
String.Append(payload, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n--%s\r\n", "userhash", db->o("settings")->@("catbox_userhash"), boundary);
|
||||
}
|
||||
String.Append(payload, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n--%s\r\n", "files", filename, boundary);
|
||||
payload_size = StrLen(payload);
|
||||
|
||||
// build the http headers
|
||||
U8* headers = @slon_calloc(session, 4096);
|
||||
String.Append(headers, "POST /user/api.php HTTP/1.1\r\n");
|
||||
String.Append(headers, "Host: catbox.moe\r\n");
|
||||
String.Append(headers, "User-Agent: slon/1.0\r\n");
|
||||
String.Append(headers, "Content-Length: %d\r\n", payload_size);
|
||||
String.Append(headers, "Content-Type: multipart/form-data; boundary=%s\r\n\r\n", boundary);
|
||||
|
||||
I64 send_buffer_size = StrLen(headers) + payload_size;
|
||||
U8* send_buffer = @slon_calloc(session, send_buffer_size);
|
||||
|
||||
MemCpy(send_buffer, headers, StrLen(headers));
|
||||
MemCpy(send_buffer + StrLen(headers), payload, payload_size);
|
||||
|
||||
TlsSocket* s = @tls_socket_create("catbox.moe", 443);
|
||||
while (!@tls_established(s->ctx))
|
||||
Sleep(1);
|
||||
|
||||
s->send(send_buffer, send_buffer_size);
|
||||
|
||||
I64 bytes_received = 0;
|
||||
I64 response_buffer_size = 0;
|
||||
U8* response_buffer = @slon_calloc(session, 4096);
|
||||
|
||||
while (!bytes_received) {
|
||||
bytes_received = s->receive(response_buffer + response_buffer_size, 4096);
|
||||
response_buffer_size += bytes_received;
|
||||
}
|
||||
|
||||
s->close();
|
||||
|
||||
@slon_free(session, response_buffer);
|
||||
@slon_free(session, send_buffer);
|
||||
@slon_free(session, payload);
|
||||
@slon_free(session, headers);
|
||||
}
|
||||
|
||||
JsonObject* @slon_api_v1_statuses_lookup_by_id(U8* id, JsonArray* statuses)
|
||||
{
|
||||
if (!id || !statuses) {
|
||||
|
@ -131,13 +189,31 @@ U0 @slon_api_v1_statuses_delete(SlonHttpSession* session)
|
|||
U8* id = session->path(3);
|
||||
JsonObject* status;
|
||||
JsonObject* fedi_status;
|
||||
JsonArray* media_attachments = NULL;
|
||||
JsonObject* attachment = NULL;
|
||||
U8* attachment_url_ptr = NULL;
|
||||
|
||||
I64 i;
|
||||
I64 j;
|
||||
for (i = 0; i < statuses->length; i++) {
|
||||
status = statuses->@(i);
|
||||
if (!StrICmp(status->@("id"), id)) {
|
||||
fedi_status = Json.Clone(status);
|
||||
status->set("deleted", TRUE, JSON_BOOLEAN);
|
||||
media_attachments = status->a("media_attachments");
|
||||
if (db->o("settings")->@("catbox_userhash") && media_attachments && media_attachments->length) {
|
||||
for (j = 0; j < media_attachments->length; j++) {
|
||||
attachment = media_attachments->@(j);
|
||||
attachment_url_ptr = attachment->@("url");
|
||||
if (attachment_url_ptr) {
|
||||
attachment_url_ptr += StrLen(attachment_url_ptr) - 1;
|
||||
while (*(attachment_url_ptr - 1) != '/') {
|
||||
--attachment_url_ptr;
|
||||
}
|
||||
@slon_api_v1_statuses_delete_file_from_catbox(session, attachment_url_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@slon_db_save_statuses_to_disk;
|
||||
@slon_db_instance_decrement_status_count;
|
||||
@slon_db_save_instance_to_disk;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
U8* @slon_api_v2_media_upload(SlonHttpSession* session, U8* filepath)
|
||||
U8* @slon_api_v2_media_upload_to_catbox(SlonHttpSession* session, U8* filepath)
|
||||
{
|
||||
if (!session || !filepath) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
I64 data_size = 0;
|
||||
U8* data = FileRead(filepath, &data_size);
|
||||
U8* image_url = NULL;
|
||||
|
||||
// build the multipart/form-data payload
|
||||
|
||||
|
@ -10,9 +15,17 @@ U8* @slon_api_v2_media_upload(SlonHttpSession* session, U8* filepath)
|
|||
|
||||
U8* boundary = "----------SlonFormBoundary00";
|
||||
StrPrint(payload, "--%s\r\n", boundary);
|
||||
String.Append(payload, "Content-Disposition: form-data; name=\"file\"; filename=\"file\"\r\n");
|
||||
String.Append(payload, "Content-Type: image/%s\r\n\r\n", StrFind(".", filepath) + 1);
|
||||
|
||||
String.Append(payload, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n--%s\r\n", "reqtype", "fileupload", boundary);
|
||||
if (db->o("settings")->@("catbox_userhash")) {
|
||||
String.Append(payload, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n--%s\r\n", "userhash", db->o("settings")->@("catbox_userhash"), boundary);
|
||||
}
|
||||
U8* random_filename = @slon_api_generate_unique_id(session);
|
||||
U8* ext = StrFind(".", filepath) + 1;
|
||||
String.Append(payload, "Content-Disposition: form-data; name=\"fileToUpload\"; filename=\"%s.%s\"\r\n", random_filename, ext);
|
||||
String.Append(payload, "Content-Type: image/%s\r\n\r\n", ext);
|
||||
payload_size = StrLen(payload);
|
||||
@slon_free(session, random_filename);
|
||||
|
||||
MemCpy(payload + payload_size, data, data_size);
|
||||
payload_size += data_size;
|
||||
|
@ -22,35 +35,56 @@ U8* @slon_api_v2_media_upload(SlonHttpSession* session, U8* filepath)
|
|||
|
||||
// build the http headers
|
||||
U8* headers = @slon_calloc(session, 4096);
|
||||
String.Append(headers, "POST / HTTP/1.0\r\n");
|
||||
String.Append(headers, "Content-Type: multipart/form-data; boundary=%s\r\n", boundary);
|
||||
String.Append(headers, "Content-Length: %d\r\n\r\n", payload_size);
|
||||
String.Append(headers, "POST /user/api.php HTTP/1.1\r\n");
|
||||
String.Append(headers, "Host: catbox.moe\r\n");
|
||||
String.Append(headers, "User-Agent: slon/1.0\r\n");
|
||||
String.Append(headers, "Content-Length: %d\r\n", payload_size);
|
||||
String.Append(headers, "Content-Type: multipart/form-data; boundary=%s\r\n\r\n", boundary);
|
||||
|
||||
I64 send_buffer_size = StrLen(headers) + payload_size;
|
||||
I64 response_buffer_size = 0;
|
||||
|
||||
U8* send_buffer = @slon_calloc(session, send_buffer_size);
|
||||
U8* response_buffer = @slon_calloc(session, 16384);
|
||||
|
||||
MemCpy(send_buffer, headers, StrLen(headers));
|
||||
MemCpy(send_buffer + StrLen(headers), payload, payload_size);
|
||||
|
||||
TlsSocket* s = @tcp_socket_create("10.20.0.254", 5558);
|
||||
while (s->state != TCP_SOCKET_STATE_ESTABLISHED)
|
||||
TlsSocket* s = @tls_socket_create("catbox.moe", 443);
|
||||
while (!@tls_established(s->ctx))
|
||||
Sleep(1);
|
||||
|
||||
s->send(send_buffer, send_buffer_size);
|
||||
I64 left = send_buffer_size;
|
||||
I64 sent = 0;
|
||||
I64 chunk_size = 0;
|
||||
while (left) {
|
||||
chunk_size = 2048;
|
||||
if (chunk_size > left)
|
||||
chunk_size = left;
|
||||
s->send(send_buffer + sent, chunk_size);
|
||||
left -= chunk_size;
|
||||
sent += chunk_size;
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
I64 bytes_received = -1;
|
||||
while (bytes_received != 0) {
|
||||
bytes_received = s->receive(response_buffer + response_buffer_size, 16384);
|
||||
I64 bytes_received = 0;
|
||||
I64 response_buffer_size = 0;
|
||||
U8* response_buffer = @slon_calloc(session, 4096);
|
||||
|
||||
while (!bytes_received) {
|
||||
bytes_received = s->receive(response_buffer + response_buffer_size, 4096);
|
||||
response_buffer_size += bytes_received;
|
||||
}
|
||||
|
||||
response_buffer[response_buffer_size] = NULL;
|
||||
|
||||
s->close();
|
||||
JsonObject* obj = Json.Parse(StrFind("\r\n\r\n", response_buffer) + 4);
|
||||
|
||||
U8* url_ptr = StrFind("\r\n\r\n", response_buffer) + 4;
|
||||
if (url_ptr < 0x10) {
|
||||
goto slon_api_v2_media_upload_to_catbox_failed;
|
||||
}
|
||||
url_ptr = StrFind("\r\n", url_ptr) + 2;
|
||||
StrFind("\r\n", url_ptr)[0] = NULL;
|
||||
|
||||
image_url = @slon_strnew(session, url_ptr);
|
||||
|
||||
slon_api_v2_media_upload_to_catbox_failed:
|
||||
|
||||
@slon_free(session, response_buffer);
|
||||
@slon_free(session, send_buffer);
|
||||
|
@ -58,7 +92,7 @@ U8* @slon_api_v2_media_upload(SlonHttpSession* session, U8* filepath)
|
|||
@slon_free(session, payload);
|
||||
Free(data);
|
||||
|
||||
return obj->@("url");
|
||||
return image_url;
|
||||
}
|
||||
|
||||
U0 @slon_api_v2_media_post(SlonHttpSession* session)
|
||||
|
@ -143,10 +177,8 @@ U0 @slon_api_v2_media_post(SlonHttpSession* session)
|
|||
StrPrint(scratch_buffer, "%s/%s.%s", SLON_MEDIA_PATH, media_id, media_file_ext);
|
||||
FileWrite(scratch_buffer, file_ptr, file_size);
|
||||
|
||||
// Then, upload to image host
|
||||
// NOTE: Replace @slon_api_v2_media_upload(session, filepath) with a function that uploads to your desired image host.
|
||||
// An example upload function is provided.
|
||||
U8* media_url = @slon_api_v2_media_upload(session, scratch_buffer);
|
||||
// Then, upload to Catbox
|
||||
U8* media_url = @slon_api_v2_media_upload_to_catbox(session, scratch_buffer);
|
||||
if (media_url) {
|
||||
JsonObject* media_object = Json.CreateObject();
|
||||
media_object->set("id", media_id, JSON_STRING);
|
||||
|
@ -162,6 +194,7 @@ U0 @slon_api_v2_media_post(SlonHttpSession* session)
|
|||
media_object->set("blurhash", NULL, JSON_NULL);
|
||||
db->o("media")->set(media_id, media_object, JSON_OBJECT);
|
||||
session->send(media_object);
|
||||
@slon_free(session, media_url);
|
||||
} else {
|
||||
session->status(400);
|
||||
}
|
||||
|
|
|
@ -493,7 +493,7 @@ U0 @slon_activitypub_async_create_status_to(JsonObject* status, U8* dest)
|
|||
for (i = 0; i < media_attachments->length; i++) {
|
||||
masto_attachment = media_attachments->@(i);
|
||||
ap_attachment = Json.CreateObject();
|
||||
StrPrint(scratch_buffer, "image/%s", StrFind(".", StrFind("/images/", masto_attachment->@("url")) + 8) + 1);
|
||||
StrPrint(scratch_buffer, "image/%s", StrFind(".", StrFind("catbox.moe/", masto_attachment->@("url")) + 11) + 1);
|
||||
ap_attachment->set("mediaType", scratch_buffer, JSON_STRING);
|
||||
ap_attachment->set("url", masto_attachment->@("url"), JSON_STRING);
|
||||
ap_attachment->set("width", masto_attachment->o("meta")->o("original")->@("width"), JSON_NUMBER);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue