Adding image cache.

This commit is contained in:
sergiotarxz 2021-11-28 10:29:54 +01:00
parent 0b3c459482
commit 3050ac8230
5 changed files with 151 additions and 32 deletions

View File

@ -16,7 +16,7 @@ mg_database_prepare (MgDatabase *self, char *z_sql, const char **pz_tail);
int int
mg_database_get_affected_rows (MgDatabase *self); mg_database_get_affected_rows (MgDatabase *self);
char * const char *
mg_database_get_error_string (MgDatabase *self); mg_database_get_error_string (MgDatabase *self);
G_END_DECLS G_END_DECLS

View File

@ -3,7 +3,7 @@ const char *const MIGRATIONS[] = {
"key TEXT PRIMARY KEY,\n" "key TEXT PRIMARY KEY,\n"
"value TEXT\n" "value TEXT\n"
");\n"), ");\n"),
("CREATE TABLE image (\n" ("CREATE TABLE images (\n"
"url TEXT PRIMARY KEY,\n" "url TEXT PRIMARY KEY,\n"
"file TEXT\n" "file TEXT\n"
");\n"), ");\n"),

View File

@ -8,8 +8,19 @@ G_DECLARE_FINAL_TYPE (MgDatabaseStatement, mg_database_statement, MG, DATABASE_S
MgDatabaseStatement *mg_database_statement_new (); MgDatabaseStatement *mg_database_statement_new ();
void int
mg_database_statement_bind_text (MgDatabaseStatement *self, int index, char *value); 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 G_END_DECLS

View File

@ -13,7 +13,7 @@ struct _MgDatabaseStatement {
G_DEFINE_TYPE (MgDatabaseStatement, mg_database_statement, G_TYPE_OBJECT) G_DEFINE_TYPE (MgDatabaseStatement, mg_database_statement, G_TYPE_OBJECT)
typedef enum { typedef enum {
MG_DATABASE_STATEMENT_OWNER, MG_DATABASE_STATEMENT_OWNER = 1,
MG_DATABASE_STATEMENT_STMT, MG_DATABASE_STATEMENT_STMT,
MG_DATABASE_STATEMENT_N_PROPERTIES MG_DATABASE_STATEMENT_N_PROPERTIES
} MgDatabaseStatementProperties; } MgDatabaseStatementProperties;
@ -38,7 +38,7 @@ MgDatabaseStatement *
mg_database_statement_new (MgDatabase *owner, sqlite3_stmt *statement) { mg_database_statement_new (MgDatabase *owner, sqlite3_stmt *statement) {
MgDatabaseStatement *self = NULL; MgDatabaseStatement *self = NULL;
self = MG_DATABASE_STATEMENT ((g_object_new (MG_TYPE_DATABASE_STATEMENT, self = MG_DATABASE_STATEMENT ((g_object_new (MG_TYPE_DATABASE_STATEMENT,
"owner", owner, "stmt", statement))); "owner", owner, "stmt", statement, NULL)));
return self; return self;
} }
@ -52,7 +52,7 @@ mg_database_statement_class_init (MgDatabaseStatementClass *class) {
"owner", "owner",
"Owner", "Owner",
"Owner MgDatabase.", "Owner MgDatabase.",
MG_TYPE_DATABASE_STATEMENT, MG_TYPE_DATABASE,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
database_statement_properties[MG_DATABASE_STATEMENT_STMT] = g_param_spec_pointer ( database_statement_properties[MG_DATABASE_STATEMENT_STMT] = g_param_spec_pointer (
"stmt", "stmt",
@ -95,6 +95,7 @@ mg_database_statement_set_property (GObject *object,
g_clear_object (&(self->owner)); g_clear_object (&(self->owner));
} }
self->owner = g_value_peek_pointer (value); self->owner = g_value_peek_pointer (value);
g_object_ref (self->owner);
break; break;
case MG_DATABASE_STATEMENT_STMT: case MG_DATABASE_STATEMENT_STMT:
if (self->stmt) { if (self->stmt) {
@ -124,17 +125,47 @@ mg_database_statement_get_stmt (MgDatabaseStatement *self) {
static void static void
mg_database_statement_dispose (GObject *object) { mg_database_statement_dispose (GObject *object) {
MgDatabaseStatement *self = MG_DATABASE_STATEMENT (object); MgDatabaseStatement *self = MG_DATABASE_STATEMENT (object);
g_clear_object (&(self->owner)); if (self->owner) {
g_clear_object (&(self->owner));
}
sqlite3_finalize (self->stmt); sqlite3_finalize (self->stmt);
} }
void int
mg_database_statement_bind_text (MgDatabaseStatement *self, int index, char *value) { mg_database_statement_bind_text (MgDatabaseStatement *self,
int index, const char *value) {
sqlite3_stmt *stmt = mg_database_statement_get_stmt (self); sqlite3_stmt *stmt = mg_database_statement_get_stmt (self);
int error = sqlite3_bind_text (stmt, index, value, -1, SQLITE_TRANSIENT); int error = sqlite3_bind_text (stmt, index, value, -1, SQLITE_TRANSIENT);
if ( error != SQLITE_OK ) { if ( error != SQLITE_OK ) {
g_error (mg_database_get_error_string (self->owner)); 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 static void

View File

@ -1,38 +1,57 @@
#include <stdio.h> #include <stdio.h>
#include <glib.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <sqlite3.h>
#include <openmg/util/soup.h> #include <openmg/util/soup.h>
#include <openmg/util/gobject_utility_extensions.h> #include <openmg/util/gobject_utility_extensions.h>
#include <openmg/database.h>
#include <openmg/database/statement.h>
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 * GtkPicture *
create_picture_from_url (const char *const url, gint picture_size) { create_picture_from_url (const char *const url, gint picture_size) {
GtkPicture *picture = NULL; GtkPicture *picture = NULL;
GFileIOStream *iostream; GFileIOStream *iostream = NULL;
GFile *tmp_image; GFile *image = NULL;
GError *error = NULL; GError *error = NULL;
GdkTexture *texture; GdkTexture *texture = NULL;
size_t size_downloaded_image = 0; size_t size_downloaded_image = 0;
char *downloaded_image; char *downloaded_image = NULL;
MgUtilSoup *util_soup = mg_util_soup_new (); MgUtilSoup *util_soup = mg_util_soup_new ();
downloaded_image = mg_util_soup_get_request (util_soup, image = get_image_for_url (url);
url, &size_downloaded_image); if (!g_file_query_exists (image, NULL)) {
tmp_image = g_file_new_tmp ("mangareadertmpfileXXXXXX", &iostream, &error); downloaded_image =
if (error) { mg_util_soup_get_request
fprintf (stderr, "Unable to read file: %s\n", error->message); (util_soup,
g_clear_error (&error); url, &size_downloaded_image);
goto cleanup_create_picture_from_url; 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)), texture = gdk_texture_new_from_file (image, &error);
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);
if (error) { if (error) {
fprintf (stderr, "Texture malformed."); fprintf (stderr, "Texture malformed.");
goto cleanup_create_picture_from_url; 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); g_object_set_property_int (G_OBJECT(picture), "width-request", picture_size);
cleanup_create_picture_from_url: cleanup_create_picture_from_url:
g_free (downloaded_image); if (downloaded_image) {
g_free (downloaded_image);
}
g_clear_object (&util_soup); g_clear_object (&util_soup);
g_clear_object (&iostream); if (iostream) {
g_clear_object (&tmp_image); g_clear_object (&iostream);
}
g_clear_object (&image);
return picture; 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;
}