diff mrjunejune/main.c @ 202:b9b184b3303c

[Notes] Images get processed and it is properly fetched. Thank you.
author MrJuneJune <me@mrjunejune.com>
date Sun, 15 Feb 2026 09:12:57 -0800
parents 6cdee35a7ba9
children e5aed6c36672
line wrap: on
line diff
--- a/mrjunejune/main.c	Sun Feb 15 07:07:50 2026 -0800
+++ b/mrjunejune/main.c	Sun Feb 15 09:12:57 2026 -0800
@@ -21,6 +21,7 @@
   char     s3_key_processed[512];
   char     content_type[128];
   char     access_token[256];
+  char     db_path[256];
   S3_Config s3_config;
 } Media_Processing_Context;
 
@@ -668,15 +669,6 @@
   return resp;
 }
 
-Seobeo_Request_Entry *GetEditor(Seobeo_Request_Entry *req, Dowa_Arena *arena)
-{
-  Seobeo_Request_Entry *resp = NULL;
-  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
-  Seobeo_Render_Html_FilePath(final_body, "/editor/index.html", arena);
-  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
-  return resp;
-}
-
 Seobeo_Request_Entry *GetNotesLogin(Seobeo_Request_Entry *req, Dowa_Arena *arena)
 {
   Seobeo_Request_Entry *resp = NULL;
@@ -1268,11 +1260,16 @@
 {
   Media_Processing_Context *ctx = (Media_Processing_Context *)arg;
 
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Background thread started for media_id=%lld\n", (long long)ctx->media_id);
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] S3 key original: %s\n", ctx->s3_key_original);
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] S3 key processed: %s\n", ctx->s3_key_processed);
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] DB path: %s\n", ctx->db_path);
+
   // Open thread-local DB connection
-  Deita_Connection *db_conn = Deita_Connection_Create(DEITA_DATABASE_TYPE_SQLITE3, g_db_path);
+  Deita_Connection *db_conn = Deita_Connection_Create(DEITA_DATABASE_TYPE_SQLITE3, ctx->db_path);
   if (!db_conn || !Deita_Connection_Is_Open(db_conn))
   {
-    printf("[MEDIA] Thread ERROR: Failed to open database for media_id=%lld\n", (long long)ctx->media_id);
+    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] Thread ERROR: Failed to open database for media_id=%lld\n", (long long)ctx->media_id);
     free(ctx);
     return NULL;
   }
@@ -1283,24 +1280,27 @@
   char media_id_str[32];
   snprintf(media_id_str, sizeof(media_id_str), "%lld", (long long)ctx->media_id);
   const char *params[] = { media_id_str };
-  Deita_Query_Execute_Update_Prepared(db_conn, update_processing, 1, params);
-
-  printf("[MEDIA] Processing media_id=%lld\n", (long long)ctx->media_id);
+  int32 update_result = Deita_Query_Execute_Update_Prepared(db_conn, update_processing, 1, params);
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Updated status to 'processing' for media_id=%lld (result=%d)\n", (long long)ctx->media_id, update_result);
 
   // Generate presigned GET URL for download (10 min expiry)
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Generating presigned GET URL for media_id=%lld\n", (long long)ctx->media_id);
   S3_Presigned_URL download_url = S3_Presign_Get(&ctx->s3_config, ctx->s3_key_original, 600);
   if (!download_url.success)
   {
+    const char *error_msg = download_url.error_message ? download_url.error_message : "Failed to generate download URL";
+    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to generate download URL for media_id=%lld: %s\n",
+               (long long)ctx->media_id, error_msg);
     const char *update_error =
       "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
-    const char *error_params[] = { "Failed to generate download URL", media_id_str };
+    const char *error_params[] = { error_msg, media_id_str };
     Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
-    printf("[MEDIA] ERROR: Failed to generate download URL for media_id=%lld\n", (long long)ctx->media_id);
     S3_Presigned_URL_Destroy(&download_url);
     Deita_Connection_Close(db_conn);
     free(ctx);
     return NULL;
   }
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Generated presigned URL: %.100s...\n", download_url.url);
 
   // Generate temp file paths
   char tmp_input[256];
@@ -1317,6 +1317,7 @@
   free(uuid_output);
 
   // Download from S3
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Downloading from S3 to %s for media_id=%lld\n", tmp_input, (long long)ctx->media_id);
   Seobeo_Client_Request *download_req = Seobeo_Client_Request_Create(download_url.url);
   Seobeo_Client_Request_Set_Download_Path(download_req, tmp_input);
   Seobeo_Client_Response *download_resp = Seobeo_Client_Request_Execute(download_req);
@@ -1325,11 +1326,13 @@
 
   if (!download_resp || download_resp->status_code != 200)
   {
+    int status = download_resp ? download_resp->status_code : 0;
+    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to download from S3 for media_id=%lld (status=%d)\n",
+               (long long)ctx->media_id, status);
     const char *update_error =
       "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
     const char *error_params[] = { "Failed to download from S3", media_id_str };
     Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
-    printf("[MEDIA] ERROR: Failed to download from S3 for media_id=%lld\n", (long long)ctx->media_id);
     if (download_req) Seobeo_Client_Request_Destroy(download_req);
     if (download_resp) Seobeo_Client_Response_Destroy(download_resp);
     unlink(tmp_input);
@@ -1338,6 +1341,7 @@
     return NULL;
   }
 
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully downloaded file to %s\n", tmp_input);
   Seobeo_Client_Request_Destroy(download_req);
   Seobeo_Client_Response_Destroy(download_resp);
 
@@ -1348,14 +1352,40 @@
   snprintf(cmd, sizeof(cmd), "ffmpeg -y -i %s -quality 80 %s 2>%s",
            tmp_input, tmp_output, log_file);
 
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Running FFmpeg: %s\n", cmd);
   int ffmpeg_result = system(cmd);
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] FFmpeg result: %d for media_id=%lld\n", ffmpeg_result, (long long)ctx->media_id);
+
   if (ffmpeg_result != 0)
   {
+    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: FFmpeg conversion failed for media_id=%lld (exit code %d). Check log: %s\n",
+               (long long)ctx->media_id, ffmpeg_result, log_file);
     const char *update_error =
       "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
     const char *error_params[] = { "Image conversion failed", media_id_str };
     Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
-    printf("[MEDIA] ERROR: FFmpeg conversion failed for media_id=%lld\n", (long long)ctx->media_id);
+    unlink(tmp_input);
+    unlink(tmp_output);
+    Deita_Connection_Close(db_conn);
+    free(ctx);
+    return NULL;
+  }
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully converted to webp: %s\n", tmp_output);
+
+  // Upload processed file to S3
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Uploading processed file to S3: %s -> %s\n", tmp_output, ctx->s3_key_processed);
+  S3_Result upload_result = S3_Upload_File_With_Content_Type(
+    &ctx->s3_config, tmp_output, ctx->s3_key_processed, "image/webp");
+
+  if (!upload_result.success)
+  {
+    const char *error_msg = upload_result.error_message ? upload_result.error_message : "Failed to upload processed file";
+    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to upload processed file for media_id=%lld: %s\n",
+               (long long)ctx->media_id, error_msg);
+    const char *update_error =
+      "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
+    const char *error_params[] = { error_msg, media_id_str };
+    Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
     unlink(tmp_input);
     unlink(tmp_output);
     Deita_Connection_Close(db_conn);
@@ -1363,30 +1393,14 @@
     return NULL;
   }
 
-  // Upload processed file to S3
-  S3_Result upload_result = S3_Upload_File_With_Content_Type(
-    &ctx->s3_config, tmp_output, ctx->s3_key_processed, "image/webp");
-
-  if (!upload_result.success)
-  {
-    const char *update_error =
-      "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
-    const char *error_params[] = { "Failed to upload processed file", media_id_str };
-    Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
-    printf("[MEDIA] ERROR: Failed to upload processed file for media_id=%lld\n", (long long)ctx->media_id);
-    unlink(tmp_input);
-    unlink(tmp_output);
-    Deita_Connection_Close(db_conn);
-    free(ctx);
-    return NULL;
-  }
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully uploaded processed file to S3\n");
 
   // Update status to 'finished'
   const char *update_finished =
     "UPDATE media_uploads SET status='finished', updated_at=strftime('%s','now') WHERE id=?";
   Deita_Query_Execute_Update_Prepared(db_conn, update_finished, 1, params);
 
-  printf("[MEDIA] Successfully processed media_id=%lld\n", (long long)ctx->media_id);
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully processed media_id=%lld - COMPLETE\n", (long long)ctx->media_id);
 
   // Cleanup
   unlink(tmp_input);
@@ -1500,9 +1514,13 @@
     return resp;
   }
 
+  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Content type for media_id=%lld: '%s'\n", (long long)media_id, content_type_copy);
+
   // If content_type starts with "image/", spawn background processing thread
   if (strncmp(content_type_copy, "image/", 6) == 0)
   {
+    Seobeo_Log(SEOBEO_INFO, "[MEDIA] Detected image type, preparing to spawn background thread for media_id=%lld\n", (long long)media_id);
+
     // Create context for background thread (heap allocated)
     Media_Processing_Context *ctx = malloc(sizeof(Media_Processing_Context));
     ctx->media_id = media_id;
@@ -1510,28 +1528,36 @@
     strncpy(ctx->s3_key_processed, s3_key_processed_copy, sizeof(ctx->s3_key_processed) - 1);
     strncpy(ctx->content_type, content_type_copy, sizeof(ctx->content_type) - 1);
     strncpy(ctx->access_token, token, sizeof(ctx->access_token) - 1);
+    strncpy(ctx->db_path, g_db_path, sizeof(ctx->db_path) - 1);
     ctx->s3_key_original[sizeof(ctx->s3_key_original) - 1] = '\0';
     ctx->s3_key_processed[sizeof(ctx->s3_key_processed) - 1] = '\0';
     ctx->content_type[sizeof(ctx->content_type) - 1] = '\0';
     ctx->access_token[sizeof(ctx->access_token) - 1] = '\0';
+    ctx->db_path[sizeof(ctx->db_path) - 1] = '\0';
     ctx->s3_config = g_s3_config;
 
+    Seobeo_Log(SEOBEO_INFO, "[MEDIA] Creating pthread for media_id=%lld\n", (long long)media_id);
+
     // Spawn detached thread
     pthread_t thread_id;
     int thread_result = pthread_create(&thread_id, NULL, Media_Process_Background, ctx);
 
     if (thread_result != 0)
     {
-      Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to spawn processing thread for media_id=%lld\n", (long long)media_id);
+      Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: pthread_create failed with result=%d for media_id=%lld\n", thread_result, (long long)media_id);
       free(ctx);
     }
     else
     {
       // Detach thread so it cleans up automatically when done
       pthread_detach(thread_id);
-      Seobeo_Log(SEOBEO_INFO, "[MEDIA] Spawned processing thread for media_id=%lld\n", (long long)media_id);
+      Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully spawned and detached thread for media_id=%lld\n", (long long)media_id);
     }
   }
+  else
+  {
+    Seobeo_Log(SEOBEO_INFO, "[MEDIA] Non-image file, skipping background processing for media_id=%lld\n", (long long)media_id);
+  }
 
   Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
   Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
@@ -1600,7 +1626,7 @@
 
   // Query media status
   const char *select_query =
-    "SELECT id, status, s3_key_processed, error_message FROM media_uploads WHERE id = ? AND access_token = ?";
+    "SELECT id, status, s3_key_original, s3_key_processed, error_message FROM media_uploads WHERE id = ? AND access_token = ?";
   const char *select_params[] = { media_id_str, token };
 
   Deita_Result_Set *p_result = Deita_Query_Execute_Prepared(g_db_connection, select_query, 2, select_params, arena);
@@ -1616,10 +1642,11 @@
 
   int64 id = Deita_Result_Set_Get_Integer(p_result, 0);
   const char *status = Deita_Result_Set_Get_Text(p_result, 1);
-  const char *s3_key_processed = Deita_Result_Set_Get_Text(p_result, 2);
-  const char *error_message = Deita_Result_Set_Get_Text(p_result, 3);
+  const char *s3_key_original = Deita_Result_Set_Get_Text(p_result, 2);
+  const char *s3_key_processed = Deita_Result_Set_Get_Text(p_result, 3);
+  const char *error_message = Deita_Result_Set_Get_Text(p_result, 4);
 
-  // Build CloudFront URL if status is 'finished' and s3_key_processed exists
+  // Build CloudFront URL for processed file if status is 'finished'
   char processed_url[1024] = {0};
   if (strcmp(status, "finished") == 0 && s3_key_processed && strlen(s3_key_processed) > 0)
   {
@@ -1634,32 +1661,63 @@
     }
   }
 
-  // Build JSON response
-  char *response_body = Dowa_Arena_Allocate(arena, 2048);
+  // Build CloudFront URL for original file (for non-images or before processing completes)
+  char original_url[1024] = {0};
+  if (s3_key_original && strlen(s3_key_original) > 0)
+  {
+    if (g_s3_cloudfront_url[0])
+    {
+      snprintf(original_url, sizeof(original_url), "%s/%s", g_s3_cloudfront_url, s3_key_original);
+    }
+    else
+    {
+      snprintf(original_url, sizeof(original_url), "https://%s.s3.%s.amazonaws.com/%s",
+               g_s3_bucket, g_s3_region, s3_key_original);
+    }
+  }
+
+  // Build JSON response with both processed_url and original_url
+  char *response_body = Dowa_Arena_Allocate(arena, 3072);
+
+  // Build the base response
+  int offset = snprintf(response_body, 3072,
+                        "{\"id\":%lld,\"status\":\"%s\",",
+                        (long long)id, status);
+
+  // Add processed_url
   if (strlen(processed_url) > 0)
   {
-    snprintf(response_body, 2048,
-             "{\"id\":%lld,\"status\":\"%s\",\"processed_url\":\"%s\",\"error_message\":%s}",
-             (long long)id, status, processed_url,
-             error_message ? "\"" : "null");
-    if (error_message)
-    {
-      // Append error message if exists
-      size_t len = strlen(response_body);
-      snprintf(response_body + len - 1, 2048 - len + 1, "%s\"}", error_message);
-    }
+    offset += snprintf(response_body + offset, 3072 - offset,
+                       "\"processed_url\":\"%s\",", processed_url);
   }
   else
   {
-    snprintf(response_body, 2048,
-             "{\"id\":%lld,\"status\":\"%s\",\"processed_url\":null,\"error_message\":%s}",
-             (long long)id, status,
-             error_message ? "\"" : "null");
-    if (error_message)
-    {
-      size_t len = strlen(response_body);
-      snprintf(response_body + len - 1, 2048 - len + 1, "%s\"}", error_message);
-    }
+    offset += snprintf(response_body + offset, 3072 - offset,
+                       "\"processed_url\":null,");
+  }
+
+  // Add original_url
+  if (strlen(original_url) > 0)
+  {
+    offset += snprintf(response_body + offset, 3072 - offset,
+                       "\"original_url\":\"%s\",", original_url);
+  }
+  else
+  {
+    offset += snprintf(response_body + offset, 3072 - offset,
+                       "\"original_url\":null,");
+  }
+
+  // Add error_message
+  if (error_message && strlen(error_message) > 0)
+  {
+    snprintf(response_body + offset, 3072 - offset,
+             "\"error_message\":\"%s\"}", error_message);
+  }
+  else
+  {
+    snprintf(response_body + offset, 3072 - offset,
+             "\"error_message\":null}");
   }
 
   Deita_Result_Set_Free(p_result);
@@ -1715,6 +1773,14 @@
   printf("[S3] Configured: region=%s, bucket=%s, key=%s...\n",
          g_s3_region, g_s3_bucket, s3_access_key[0] ? "***" : "(missing)");
 
+  // Show current working directory
+  char cwd[1024];
+  if (getcwd(cwd, sizeof(cwd)) != NULL)
+  {
+    printf("[STARTUP] Current working directory: %s\n", cwd);
+    printf("[STARTUP] Database path (relative): %s\n", g_db_path);
+  }
+
   // Initialize database
   init_database();
 
@@ -1760,10 +1826,6 @@
   Seobeo_Router_Register("GET", "/talk", GetTalk);
   Seobeo_Router_Register("GET", "/talk/index.html", GetRedirectTalk);
 
-  // -- Editor (legacy) --/
-  Seobeo_Router_Register("GET", "/editor", GetEditor);
-  Seobeo_Router_Register("GET", "/editor/index.html", GetRedirectEditor);
-
   // -- Notes --/
   Seobeo_Router_Register("GET", "/notes", GetNotes);
   Seobeo_Router_Register("GET", "/notes/", GetNotes);