2021-11-01 14:38:15 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2021-11-28 10:29:54 +01:00
|
|
|
#include <glib.h>
|
2021-11-01 14:38:15 +01:00
|
|
|
#include <gtk/gtk.h>
|
2021-11-28 10:29:54 +01:00
|
|
|
#include <sqlite3.h>
|
2021-11-01 14:38:15 +01:00
|
|
|
|
|
|
|
#include <openmg/util/soup.h>
|
|
|
|
#include <openmg/util/gobject_utility_extensions.h>
|
|
|
|
|
2021-11-28 10:29:54 +01:00
|
|
|
#include <openmg/database.h>
|
|
|
|
#include <openmg/database/statement.h>
|
|
|
|
|
|
|
|
const char *const IMAGE_CACHE_FORMAT_STRING =
|
|
|
|
"%s/.cache/openmg/%s";
|
|
|
|
|
2022-01-16 22:39:01 +01:00
|
|
|
typedef struct {
|
|
|
|
char *url;
|
|
|
|
gint picture_size;
|
2022-01-18 21:00:39 +01:00
|
|
|
GFile *image;
|
2022-01-16 22:39:01 +01:00
|
|
|
} PictureThreadAttributes;
|
|
|
|
|
2021-11-28 10:29:54 +01:00
|
|
|
static char *
|
|
|
|
generate_cache_file_name (void);
|
|
|
|
GFile *
|
|
|
|
get_image_for_url (const char *url);
|
|
|
|
|
2022-01-16 22:39:01 +01:00
|
|
|
static void
|
|
|
|
threaded_picture_recover (GTask *task, gpointer source_object,
|
|
|
|
gpointer task_data, GCancellable *cancellable) {
|
|
|
|
PictureThreadAttributes *attrs = (PictureThreadAttributes *) task_data;
|
|
|
|
const char *url = attrs->url;
|
|
|
|
gint picture_size = attrs->picture_size;
|
2021-11-28 10:29:54 +01:00
|
|
|
GFileIOStream *iostream = NULL;
|
2022-01-18 21:00:39 +01:00
|
|
|
GFile *image = attrs->image;
|
2021-11-01 14:38:15 +01:00
|
|
|
GError *error = NULL;
|
2021-11-28 10:29:54 +01:00
|
|
|
GdkTexture *texture = NULL;
|
2022-01-16 22:39:01 +01:00
|
|
|
GtkPicture *picture = NULL;
|
2021-11-22 22:24:06 +01:00
|
|
|
|
2021-11-01 14:38:15 +01:00
|
|
|
size_t size_downloaded_image = 0;
|
2021-11-28 10:29:54 +01:00
|
|
|
char *downloaded_image = NULL;
|
2021-11-01 14:38:15 +01:00
|
|
|
|
|
|
|
MgUtilSoup *util_soup = mg_util_soup_new ();
|
2022-01-18 21:00:39 +01:00
|
|
|
downloaded_image = mg_util_soup_get_request (util_soup,
|
|
|
|
url, &size_downloaded_image);
|
2022-01-16 22:39:01 +01:00
|
|
|
static GMutex mutex;
|
|
|
|
g_mutex_lock (&mutex);
|
2021-11-28 10:29:54 +01:00
|
|
|
if (!g_file_query_exists (image, NULL)) {
|
|
|
|
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;
|
|
|
|
}
|
2021-11-01 14:38:15 +01:00
|
|
|
}
|
2022-01-18 21:00:39 +01:00
|
|
|
g_mutex_unlock (&mutex);
|
2021-11-28 10:29:54 +01:00
|
|
|
texture = gdk_texture_new_from_file (image, &error);
|
2021-11-22 22:24:06 +01:00
|
|
|
if (error) {
|
2022-01-18 21:00:39 +01:00
|
|
|
g_warning ("Texture malformed.");
|
|
|
|
g_clear_error (&error);
|
2021-11-22 22:24:06 +01:00
|
|
|
goto cleanup_create_picture_from_url;
|
|
|
|
}
|
|
|
|
picture = GTK_PICTURE (gtk_picture_new_for_paintable (GDK_PAINTABLE (texture)));
|
2022-01-16 22:39:01 +01:00
|
|
|
if (GTK_IS_WIDGET (picture)) {
|
|
|
|
g_object_set_property_int (G_OBJECT(picture), "height-request", picture_size);
|
|
|
|
g_object_set_property_int (G_OBJECT(picture), "width-request", picture_size);
|
|
|
|
}
|
2021-11-07 17:59:43 +01:00
|
|
|
|
|
|
|
cleanup_create_picture_from_url:
|
2021-11-28 10:29:54 +01:00
|
|
|
if (downloaded_image) {
|
|
|
|
g_free (downloaded_image);
|
|
|
|
}
|
2021-11-20 17:24:24 +01:00
|
|
|
g_clear_object (&util_soup);
|
2021-11-28 10:29:54 +01:00
|
|
|
if (iostream) {
|
|
|
|
g_clear_object (&iostream);
|
|
|
|
}
|
|
|
|
g_clear_object (&image);
|
2022-01-16 22:39:01 +01:00
|
|
|
g_task_return_pointer (task, picture, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_picture_thread_attributes (gpointer user_data) {
|
|
|
|
PictureThreadAttributes *attrs = (PictureThreadAttributes *) user_data;
|
|
|
|
g_free (attrs->url);
|
|
|
|
g_free (attrs);
|
|
|
|
}
|
|
|
|
|
2022-01-18 21:00:39 +01:00
|
|
|
GtkPicture *
|
2022-01-16 22:39:01 +01:00
|
|
|
create_picture_from_url (const char *const url, gint picture_size,
|
|
|
|
GAsyncReadyCallback ready, gpointer source_object,
|
|
|
|
gpointer callback_data) {
|
2022-01-18 21:00:39 +01:00
|
|
|
GtkPicture *picture = NULL;
|
|
|
|
GFile *image = NULL;
|
|
|
|
GdkTexture *texture = NULL;
|
|
|
|
GError *error = NULL;
|
2022-01-16 22:39:01 +01:00
|
|
|
|
2022-01-18 21:00:39 +01:00
|
|
|
image = get_image_for_url (url);
|
|
|
|
size_t url_len = strlen (url) + 1;
|
2022-01-16 22:39:01 +01:00
|
|
|
|
2022-01-18 21:00:39 +01:00
|
|
|
if (g_file_query_exists (image, NULL)) {
|
|
|
|
texture = gdk_texture_new_from_file (image, &error);
|
|
|
|
if (error) {
|
|
|
|
g_warning ("Texture malformed.");
|
|
|
|
g_clear_error (&error);
|
|
|
|
goto cleanup_create_picture_from_url;
|
|
|
|
}
|
|
|
|
picture = GTK_PICTURE (gtk_picture_new_for_paintable (GDK_PAINTABLE (texture)));
|
|
|
|
if (GTK_IS_WIDGET (picture)) {
|
|
|
|
g_object_set_property_int (G_OBJECT(picture), "height-request", picture_size);
|
|
|
|
g_object_set_property_int (G_OBJECT(picture), "width-request", picture_size);
|
|
|
|
}
|
2022-01-16 22:39:01 +01:00
|
|
|
|
2022-01-18 21:00:39 +01:00
|
|
|
} else {
|
|
|
|
GTask *task = g_task_new (source_object, NULL, ready, callback_data);
|
|
|
|
PictureThreadAttributes *attrs = g_malloc (sizeof *attrs);
|
|
|
|
attrs->url = g_malloc (url_len * sizeof *url);
|
|
|
|
snprintf (attrs->url, url_len, "%s", url);
|
|
|
|
attrs->image = image;
|
|
|
|
attrs->picture_size = picture_size;
|
|
|
|
g_task_set_task_data (task, attrs, free_picture_thread_attributes);
|
|
|
|
g_task_run_in_thread (task, threaded_picture_recover);
|
|
|
|
}
|
|
|
|
cleanup_create_picture_from_url:
|
|
|
|
return picture;
|
2021-11-01 14:38:15 +01:00
|
|
|
}
|
2021-11-28 10:29:54 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|