From 3050ac823015be94adced8ba897a115d6807c05b Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sun, 28 Nov 2021 10:29:54 +0100 Subject: [PATCH] Adding image cache. --- include/openmg/database.h | 2 +- include/openmg/database/migrations.h | 2 +- include/openmg/database/statement.h | 15 +++- src/database/statement.c | 43 ++++++++-- src/view/picture.c | 121 ++++++++++++++++++++++----- 5 files changed, 151 insertions(+), 32 deletions(-) diff --git a/include/openmg/database.h b/include/openmg/database.h index cbd2fca..bf6bcd4 100644 --- a/include/openmg/database.h +++ b/include/openmg/database.h @@ -16,7 +16,7 @@ mg_database_prepare (MgDatabase *self, char *z_sql, const char **pz_tail); int mg_database_get_affected_rows (MgDatabase *self); -char * +const char * mg_database_get_error_string (MgDatabase *self); G_END_DECLS diff --git a/include/openmg/database/migrations.h b/include/openmg/database/migrations.h index f74f496..73cc938 100644 --- a/include/openmg/database/migrations.h +++ b/include/openmg/database/migrations.h @@ -3,7 +3,7 @@ const char *const MIGRATIONS[] = { "key TEXT PRIMARY KEY,\n" "value TEXT\n" ");\n"), - ("CREATE TABLE image (\n" + ("CREATE TABLE images (\n" "url TEXT PRIMARY KEY,\n" "file TEXT\n" ");\n"), diff --git a/include/openmg/database/statement.h b/include/openmg/database/statement.h index ce5f9a6..09703a9 100644 --- a/include/openmg/database/statement.h +++ b/include/openmg/database/statement.h @@ -8,8 +8,19 @@ G_DECLARE_FINAL_TYPE (MgDatabaseStatement, mg_database_statement, MG, DATABASE_S MgDatabaseStatement *mg_database_statement_new (); -void -mg_database_statement_bind_text (MgDatabaseStatement *self, int index, char *value); +int +mg_database_statement_bind_text (MgDatabaseStatement *self, + int index, const char *value); +int +mg_database_statement_step (MgDatabaseStatement *self); + +const unsigned char * +mg_database_statement_column_text (MgDatabaseStatement *self, + int i_col); + +int +mg_database_statement_column_int (MgDatabaseStatement *self, + int i_col); G_END_DECLS diff --git a/src/database/statement.c b/src/database/statement.c index ab98fa7..1599354 100644 --- a/src/database/statement.c +++ b/src/database/statement.c @@ -13,7 +13,7 @@ struct _MgDatabaseStatement { G_DEFINE_TYPE (MgDatabaseStatement, mg_database_statement, G_TYPE_OBJECT) typedef enum { - MG_DATABASE_STATEMENT_OWNER, + MG_DATABASE_STATEMENT_OWNER = 1, MG_DATABASE_STATEMENT_STMT, MG_DATABASE_STATEMENT_N_PROPERTIES } MgDatabaseStatementProperties; @@ -38,7 +38,7 @@ MgDatabaseStatement * mg_database_statement_new (MgDatabase *owner, sqlite3_stmt *statement) { MgDatabaseStatement *self = NULL; self = MG_DATABASE_STATEMENT ((g_object_new (MG_TYPE_DATABASE_STATEMENT, - "owner", owner, "stmt", statement))); + "owner", owner, "stmt", statement, NULL))); return self; } @@ -52,7 +52,7 @@ mg_database_statement_class_init (MgDatabaseStatementClass *class) { "owner", "Owner", "Owner MgDatabase.", - MG_TYPE_DATABASE_STATEMENT, + MG_TYPE_DATABASE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); database_statement_properties[MG_DATABASE_STATEMENT_STMT] = g_param_spec_pointer ( "stmt", @@ -95,6 +95,7 @@ mg_database_statement_set_property (GObject *object, g_clear_object (&(self->owner)); } self->owner = g_value_peek_pointer (value); + g_object_ref (self->owner); break; case MG_DATABASE_STATEMENT_STMT: if (self->stmt) { @@ -124,17 +125,47 @@ mg_database_statement_get_stmt (MgDatabaseStatement *self) { static void mg_database_statement_dispose (GObject *object) { MgDatabaseStatement *self = MG_DATABASE_STATEMENT (object); - g_clear_object (&(self->owner)); + if (self->owner) { + g_clear_object (&(self->owner)); + } sqlite3_finalize (self->stmt); } -void -mg_database_statement_bind_text (MgDatabaseStatement *self, int index, char *value) { +int +mg_database_statement_bind_text (MgDatabaseStatement *self, + int index, const char *value) { sqlite3_stmt *stmt = mg_database_statement_get_stmt (self); int error = sqlite3_bind_text (stmt, index, value, -1, SQLITE_TRANSIENT); if ( error != SQLITE_OK ) { g_error (mg_database_get_error_string (self->owner)); } + return error; +} + +int +mg_database_statement_step (MgDatabaseStatement *self) { + sqlite3_stmt *stmt = mg_database_statement_get_stmt (self); + int error; + while ((error = sqlite3_step (stmt)) == SQLITE_BUSY) { + } + if (error != SQLITE_DONE && error != SQLITE_ROW) { + g_error (mg_database_get_error_string (self->owner)); + } + return error; +} + +const unsigned char * +mg_database_statement_column_text (MgDatabaseStatement *self, + int i_col) { + sqlite3_stmt *stmt = mg_database_statement_get_stmt (self); + return sqlite3_column_text (stmt, i_col); +} + +int +mg_database_statement_column_int (MgDatabaseStatement *self, + int i_col) { + sqlite3_stmt *stmt = mg_database_statement_get_stmt (self); + return sqlite3_column_int (stmt, i_col); } static void diff --git a/src/view/picture.c b/src/view/picture.c index d3f898c..76fd986 100644 --- a/src/view/picture.c +++ b/src/view/picture.c @@ -1,38 +1,57 @@ #include +#include #include +#include #include #include +#include +#include + +const char *const IMAGE_CACHE_FORMAT_STRING = +"%s/.cache/openmg/%s"; + +static char * +generate_cache_file_name (void); +GFile * +get_image_for_url (const char *url); + GtkPicture * create_picture_from_url (const char *const url, gint picture_size) { GtkPicture *picture = NULL; - GFileIOStream *iostream; - GFile *tmp_image; + GFileIOStream *iostream = NULL; + GFile *image = NULL; GError *error = NULL; - GdkTexture *texture; + GdkTexture *texture = NULL; size_t size_downloaded_image = 0; - char *downloaded_image; + char *downloaded_image = NULL; MgUtilSoup *util_soup = mg_util_soup_new (); - downloaded_image = mg_util_soup_get_request (util_soup, - url, &size_downloaded_image); - tmp_image = g_file_new_tmp ("mangareadertmpfileXXXXXX", &iostream, &error); - if (error) { - fprintf (stderr, "Unable to read file: %s\n", error->message); - g_clear_error (&error); - goto cleanup_create_picture_from_url; + image = get_image_for_url (url); + if (!g_file_query_exists (image, NULL)) { + downloaded_image = + mg_util_soup_get_request + (util_soup, + url, &size_downloaded_image); + iostream = g_file_create_readwrite (image, G_FILE_CREATE_NONE, + NULL, &error); + if (error) { + fprintf (stderr, "Unable to read file: %s\n", error->message); + g_clear_error (&error); + goto cleanup_create_picture_from_url; + } + g_output_stream_write (g_io_stream_get_output_stream (G_IO_STREAM (iostream)), + downloaded_image, size_downloaded_image, NULL, &error); + if (error) { + fprintf (stderr, "Unable to write file: %s\n", error->message); + g_clear_error (&error); + goto cleanup_create_picture_from_url; + } } - g_output_stream_write (g_io_stream_get_output_stream (G_IO_STREAM (iostream)), - downloaded_image, size_downloaded_image, NULL, &error); - if (error) { - fprintf (stderr, "Unable to write file: %s\n", error->message); - g_clear_error (&error); - goto cleanup_create_picture_from_url; - } - texture = gdk_texture_new_from_file (tmp_image, &error); + texture = gdk_texture_new_from_file (image, &error); if (error) { fprintf (stderr, "Texture malformed."); goto cleanup_create_picture_from_url; @@ -42,9 +61,67 @@ create_picture_from_url (const char *const url, gint picture_size) { g_object_set_property_int (G_OBJECT(picture), "width-request", picture_size); cleanup_create_picture_from_url: - g_free (downloaded_image); + if (downloaded_image) { + g_free (downloaded_image); + } g_clear_object (&util_soup); - g_clear_object (&iostream); - g_clear_object (&tmp_image); + if (iostream) { + g_clear_object (&iostream); + } + g_clear_object (&image); return picture; } + +GFile * +get_image_for_url (const char *url) { + GFile *image; + MgDatabase *db = mg_database_new (); + MgDatabaseStatement *statement = mg_database_prepare (db, + "select file from images where url = ?;", NULL); + char *file_name = NULL; + mg_database_statement_bind_text (statement, 1, url); + if (mg_database_statement_step (statement) == SQLITE_ROW) { + const char *file_name = (const char *) mg_database_statement_column_text + (statement, 0); + image = g_file_new_for_path (file_name); + goto cleanup_get_image_for_url; + } + g_clear_object (&statement); + file_name = generate_cache_file_name (); + statement = mg_database_prepare (db, + "insert into images (url, file) values (?, ?);", NULL); + mg_database_statement_bind_text (statement, 1, url); + mg_database_statement_bind_text (statement, 2, file_name); + mg_database_statement_step (statement); + image = g_file_new_for_path (file_name); +cleanup_get_image_for_url: + if (file_name) { + g_free (file_name); + } + g_clear_object (&statement); + g_clear_object (&db); + return image; +} + +static char * +generate_cache_file_name (void) { + const char *home_dir = g_get_home_dir(); + char *file_basename = g_uuid_string_random (); + char *file_path; + char *file_path_directory; + size_t file_path_len; + file_path_len = snprintf + (NULL, 0, IMAGE_CACHE_FORMAT_STRING, + home_dir, file_basename); + file_path = g_malloc + (sizeof *file_path + * (file_path_len + 1)); + snprintf (file_path, file_path_len, + IMAGE_CACHE_FORMAT_STRING, + home_dir, file_basename); + file_path_directory = g_path_get_dirname + (file_path); + g_mkdir_with_parents + (file_path_directory, 00755); + return file_path; +}