Merge branch 'main' into main

This commit is contained in:
germedeb 2022-02-10 18:27:06 +01:00
commit 5c00c7e09c
18 changed files with 252 additions and 110 deletions

View File

@ -1,2 +1,5 @@
Sergiotarxz sergiotarxz@posteo.net Project leader and developer [Gitea](https://gitea.sergiotarxz.freemyip.com/sergiotarxz)
Germe.db FOSSgerme.deb@tuta.io Author of the icon openmg.svg. [sr.ht](https://sr.ht/~germe-fur/)
Sergiotarxz sergiotarxz@posteo.net Project leader and developer. [Gitea](https://gitea.sergiotarxz.freemyip.com/sergiotarxz)
Germe.db FOSSgerme.deb@tuta.io Designer and icons creator. [sr.ht](https://sr.ht/~germe-fur/)
Endes endes@disroot.org Developer. [Github](https://github.com/endes0)

View File

@ -2,6 +2,10 @@
## Installing the app.
First fine tune the options in `me.sergiotarxz.openmg.json` for
meson you want to have, for example preview images, complete list is
on `meson_options.txt`
```shell
flatpak --user remote-add --if-not-exists gnome-nightly https://nightly.gnome.org/gnome-nightly.flatpakrepo
flatpak install org.gnome.Sdk//master
@ -15,3 +19,7 @@ flatpak-builder --install --user build me.sergiotarxz.openmg.yml me.sergiotarxz.
```shell
flatpak run me.sergiotarxz.openmg
```
## Donations welcome:
btc: `bc1q0apxdedrm5vjn3zr0hxswnruk2x2uecwqrusmj`

View File

@ -9,6 +9,9 @@ typedef struct {
AdwHeaderBar *header;
AdwLeaflet *views_leaflet;
AdwViewStack *view_stack;
GCancellable **image_threads;
size_t image_threads_len;
bool avoid_list_image_downloads;
GtkButton *previous;
gboolean is_set_previous;
} ControlsAdwaita;

View File

@ -1,6 +1,6 @@
#pragma once
#include <gtk/gtk.h>
void
GtkPicture *
create_picture_from_url (const char *const url, gint picture_size,
GAsyncReadyCallback ready, gpointer source_object,
gpointer callback_data);
gpointer callback_data, bool do_not_download);

View File

@ -0,0 +1,30 @@
{
"app-id": "me.sergiotarxz.openmg",
"runtime": "org.gnome.Platform",
"runtime-version": "master",
"sdk": "org.gnome.Sdk",
"command": "openmg",
"finish-args": [
"--share=ipc",
"--socket=x11",
"--socket=wayland",
"--socket=session-bus",
"--share=network",
"--device=dri"
],
"modules": [
{
"name": "openmg",
"buildsystem": "meson",
"config-opts": [
"-Dimages=true"
],
"sources": [
{
"type": "dir",
"path": "."
}
]
}
]
}

View File

@ -1,20 +0,0 @@
app-id: me.sergiotarxz.openmg
runtime: org.gnome.Platform
runtime-version: master
sdk: org.gnome.Sdk
sdk-version: master
command: openmg
finish-args:
- "--share=ipc"
- "--socket=x11"
- "--socket=wayland"
- "--socket=session-bus"
- "--share=network"
- "--device=dri"
modules:
- name: openmg
buildsystem: meson
sources:
- type: dir
path: .

View File

@ -36,14 +36,26 @@ sources = [
]
link_arguments = [
'-ldl',
'-lm'
]
images_on_lists = get_option('images')
is_windows = get_option('windows')
if not is_windows
link_arguments += ['-ldl']
endif
cArgs = []
if images_on_lists
cArgs += ['-DLIST_IMAGES']
endif
executable('openmg',
sources,
dependencies : openmgdeps,
include_directories : inc,
install : true,
link_args : link_arguments
link_args : link_arguments,
c_args: cArgs
)

2
meson_options.txt Normal file
View File

@ -0,0 +1,2 @@
option('images', type : 'boolean', value : true)
option('windows', type : 'boolean', value : false)

36
openmg.supp Normal file
View File

@ -0,0 +1,36 @@
#
# Valgrind suppression file for mangareader.
# Warning: This file is very generic, so a real memory leak might be suppressed.
# Also, some false positives are still captured
#
# Format specification:
# http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress
#
{
gtk4
Memcheck:Leak
...
obj:/usr/lib/x86_64-linux-gnu/libgtk-4.so.1.600.0
...
}
{
glib
Memcheck:Leak
...
obj:/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7100.0
...
}
{
fontconfig
Memcheck:Leak
...
obj:/usr/lib/x86_64-linux-gnu/libfontconfig.so.1.12.0
...
}
{
gallium
Memcheck:Leak
...
obj:/usr/lib/x86_64-linux-gnu/GL/default/lib/dri/libgallium_dri.so
...
}

1
sqlite

@ -1 +0,0 @@
Subproject commit 855a165fecc25da50cb20525ddf5b9e60a67d18f

View File

@ -66,7 +66,8 @@ mg_backend_readmng_loop_li_chapter (
MgBackendReadmng *self,
xmlNodePtr li);
static char *
mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_query);
mg_backend_readmng_fetch_search (MgBackendReadmng *self,
const char *search_query, size_t *response_len);
static GListModel *
mg_backend_readmng_parse_page (MgBackendReadmng *self,
xmlDocPtr html_document);
@ -235,15 +236,21 @@ mg_backend_readmng_fetch_page_url (MgBackendReadmng *self,
GListStore *
mg_backend_readmng_search (MgBackendReadmng *self,
const char *search_query) {
char *response = mg_backend_readmng_fetch_search (self, search_query);
size_t response_len = 0;
char *response = mg_backend_readmng_fetch_search (self, search_query,
&response_len);
JsonParser *parser = json_parser_new ();
GListStore *mangas = g_list_store_new(MG_TYPE_MANGA);
GListStore *mangas = g_list_store_new (MG_TYPE_MANGA);
GError *error = NULL;
JsonNode *root = NULL;
JsonArray *mangas_json_array = NULL;
guint mangas_json_array_len = 0;
json_parser_load_from_data (parser, response, -1, &error);
if (!response) {
g_warning ("Json search response is null.");
goto cleanup_mg_backend_readmng_search;
}
json_parser_load_from_data (parser, response, response_len, &error);
if (error) {
g_warning ("Unable to parse json: %s.", error->message);
g_clear_error (&error);
@ -256,7 +263,7 @@ mg_backend_readmng_search (MgBackendReadmng *self,
mangas_json_array = json_node_get_array (root);
mangas_json_array_len = json_array_get_length (
mangas_json_array);
for (guint i = 0; i < mangas_json_array_len; i++) {
for (guint i = 0; i < mangas_json_array_len && i < 19; i++) {
JsonObject *manga_json_object =
json_array_get_object_element (mangas_json_array, i);
char *id_manga = NULL;
@ -280,14 +287,14 @@ cleanup_mg_backend_readmng_search:
}
static char *
mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_query) {
mg_backend_readmng_fetch_search (MgBackendReadmng *self,
const char *search_query, size_t *response_len) {
MgUtilSoup *util_soup;
MgUtilString *string_util;
char *request_url;
size_t request_url_len;
size_t response_len = 0;
util_soup = mg_util_soup_new ();
string_util = mg_util_string_new ();
@ -328,7 +335,7 @@ mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_quer
size_t body_len = sizeof body / sizeof *body;
char *text_response = mg_util_soup_post_request_url_encoded (util_soup,
request_url, body, body_len, headers, headers_len, &response_len);
request_url, body, body_len, headers, headers_len, response_len);
g_free (request_url);
g_free (phrase);
@ -357,7 +364,7 @@ mg_backend_readmng_retrieve_manga_details (MgBackendReadmng *self,
MgManga *manga) {
MgUtilXML *xml_utils;
xmlDocPtr html_document;
xmlDocPtr html_document = NULL;
xmlNodePtr *movie_detail = NULL;
xmlXPathObjectPtr xpath_result = NULL;
xmlNodeSetPtr node_set = NULL;
@ -402,6 +409,9 @@ cleanup_mg_backend_readmng_retrieve_manga_details:
if (movie_detail) {
g_free (movie_detail);
}
if (html_document) {
xmlFreeDoc(html_document);
}
}
static GListStore *
@ -446,6 +456,9 @@ cleanup_mg_backend_readmng_recover_chapter_list:
xmlXPathFreeObject(xpath_result);
}
if (uls) {
for (size_t i = 0; i < ul_len; i++) {
xmlFreeNode(uls[i]);
}
g_free (uls);
}
return return_value;
@ -581,6 +594,7 @@ mg_backend_readmng_parse_main_page (MgBackendReadmng *self, const xmlDocPtr html
xmlFreeNode (current_li);
li[i] = NULL;
}
xmlFreeNode(slides);
g_free (li);
return mangas;
}
@ -636,6 +650,11 @@ cleanup_mg_backend_readmng_retrieve_slides:
xmlXPathFreeObject(xpath_result);
}
if (nodes) {
for (size_t i = 1; i < matching_classes_len; i++)
{
xmlFreeNode(nodes[i]);
}
g_free (nodes);
}
return slides;

View File

@ -54,7 +54,6 @@ mg_util_soup_get_request (MgUtilSoup *self, const char *url, gsize *size_respons
g_value_unset (&response);
g_clear_object (&soup_session);
g_clear_object (&msg);
return return_value;
}

View File

@ -167,8 +167,14 @@ set_image_zoomable_picture_container (ChapterVisorData *chapter_visor_data) {
strlen(url_image_not_owned) + 1, 0,
strlen (url_image_not_owned));
create_picture_from_url (url_image, 0, picture_ready_manga_page,
zoomable_picture_container, chapter_visor_data);
GtkPicture *picture = create_picture_from_url (url_image, 0, picture_ready_manga_page,
zoomable_picture_container, chapter_visor_data, false);
if (picture) {
chapter_visor_data->current_picture = GTK_PICTURE (picture);
g_signal_connect (G_OBJECT (picture), "map",
G_CALLBACK (image_page_show), chapter_visor_data);
gtk_scrolled_window_set_child (zoomable_picture_container, GTK_WIDGET (picture));
}
g_free (url_image);
g_clear_object (&string_util);
}

View File

@ -89,8 +89,8 @@ create_detail_view (MgManga *manga, ControlsAdwaita *controls) {
("network-transmit-receive-symbolic"));
GtkListView *chapter_list = NULL;
char *url_image = mg_manga_get_image_url(manga);
create_picture_from_url (url_image, 200,
picture_ready_manga_detail, avatar_title_box, NULL);
GtkPicture *picture = create_picture_from_url (url_image, 200,
picture_ready_manga_detail, avatar_title_box, NULL, false);
char *manga_title_text = mg_manga_get_title (manga);
char *title_text = mg_util_xml_get_title_text (
xml_util, manga_title_text);
@ -116,6 +116,9 @@ create_detail_view (MgManga *manga, ControlsAdwaita *controls) {
gtk_widget_set_size_request (GTK_WIDGET (manga_description), 200, -1);
gtk_label_set_use_markup (GTK_LABEL (manga_title), 1);
if (picture) {
gtk_box_append (avatar_title_box, GTK_WIDGET (picture));
}
gtk_box_append (avatar_title_box, GTK_WIDGET (manga_title));
gtk_box_append (foldable_manga_data, GTK_WIDGET (avatar_title_box));

View File

@ -26,6 +26,15 @@ manga_selected (GtkListView *list_view,
guint position,
gpointer user_data) {
ControlsAdwaita *controls = (ControlsAdwaita *) user_data;
controls->avoid_list_image_downloads = true;;
for (size_t i = 0; i < controls->image_threads_len; i++) {
g_cancellable_cancel (controls->image_threads[i]);
}
if (controls->image_threads) {
g_free (controls->image_threads);
}
controls->image_threads = NULL;
controls->image_threads_len = 0;
AdwLeaflet *views_leaflet = controls->views_leaflet;
GtkSingleSelection *selection = GTK_SINGLE_SELECTION
(gtk_list_view_get_model (list_view));
@ -44,8 +53,10 @@ manga_selected (GtkListView *list_view,
GtkBox *detail_view = create_detail_view (manga, controls);
adw_leaflet_append (views_leaflet, GTK_WIDGET (detail_view));
adw_leaflet_navigate (views_leaflet, ADW_NAVIGATION_DIRECTION_FORWARD);
controls->avoid_list_image_downloads = false;
}
#ifdef LIST_IMAGES
static void
picture_ready_manga_preview (GObject *source_object,
GAsyncResult *res,
@ -58,21 +69,37 @@ picture_ready_manga_preview (GObject *source_object,
gtk_box_prepend (box, picture);
}
}
#endif
static void
setup_list_view_mangas (GtkSignalListItemFactory *factory,
GtkListItem *list_item,
gpointer user_data) {
ControlsAdwaita *controls = (ControlsAdwaita *) user_data;
MgManga *manga = gtk_list_item_get_item (list_item);
GtkBox *box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0));
char *manga_title = mg_manga_get_title (manga);
char *image_url = mg_manga_get_image_url (manga);
GtkWidget *label = gtk_label_new (manga_title);
create_picture_from_url (image_url, 100,
picture_ready_manga_preview, box, NULL);
GtkPicture *picture = NULL;
#ifdef LIST_IMAGES
GCancellable *cancellable = g_cancellable_new ();
picture = create_picture_from_url (image_url, 100,
picture_ready_manga_preview, box, cancellable,
controls->avoid_list_image_downloads);
controls->image_threads_len++;
controls->image_threads = g_realloc (controls->image_threads,
controls->image_threads_len * sizeof *(controls->image_threads));
controls->image_threads[controls->image_threads_len-1] = cancellable;
#endif
g_object_set_property_int (G_OBJECT(box), "height-request", 100);
#ifdef LIST_IMAGES
if (picture) {
gtk_box_append (box, GTK_WIDGET (picture));
}
#endif
gtk_box_append (box, label);
gtk_list_item_set_child (list_item, GTK_WIDGET (box));
@ -82,14 +109,13 @@ setup_list_view_mangas (GtkSignalListItemFactory *factory,
GtkListView *
create_list_view_mangas (GListStore *mangas, ControlsAdwaita *controls) {
AdwLeaflet *views_leaflet = controls->views_leaflet;
GtkSingleSelection *selection = gtk_single_selection_new (G_LIST_MODEL (mangas));
GtkListItemFactory *factory = gtk_signal_list_item_factory_new ();
GtkListView *list_view_manga = NULL;
g_signal_connect (G_OBJECT (factory), "bind",
G_CALLBACK (setup_list_view_mangas),
views_leaflet);
controls);
list_view_manga = GTK_LIST_VIEW (gtk_list_view_new (GTK_SELECTION_MODEL (selection),
factory));

View File

@ -1,4 +1,6 @@
#ifndef _WIN32
#include <dlfcn.h>
#endif
#include <gtk/gtk.h>
#include <adwaita.h>
@ -37,6 +39,7 @@ activate (AdwApplication *app,
AdwLeaflet *views_leaflet_search;
AdwHeaderBar *header_bar;
#ifndef _WIN32
swipe_back_t swipe_back = (swipe_back_t) dlsym
(NULL, "adw_leaflet_set_can_navigate_back");
@ -44,11 +47,16 @@ activate (AdwApplication *app,
swipe_back = (swipe_back_t) dlsym
(NULL, "adw_leaflet_set_can_swipe_back");
}
#else
swipe_back_t swipe_back = adw_leaflet_set_can_navigate_back;
#endif
controls->is_set_previous = 0;
controls->header = NULL;
controls->view_stack = view_stack;
controls->image_threads_len = 0;
controls->image_threads = NULL;
controls->avoid_list_image_downloads = false;
views_leaflet_explore = create_explore_leaflet (controls, swipe_back);
views_leaflet_search = create_search_leaflet (controls, swipe_back);

View File

@ -16,6 +16,7 @@ const char *const IMAGE_CACHE_FORMAT_STRING =
typedef struct {
char *url;
gint picture_size;
GFile *image;
} PictureThreadAttributes;
static char *
@ -30,7 +31,7 @@ threaded_picture_recover (GTask *task, gpointer source_object,
const char *url = attrs->url;
gint picture_size = attrs->picture_size;
GFileIOStream *iostream = NULL;
GFile *image = NULL;
GFile *image = attrs->image;
GError *error = NULL;
GdkTexture *texture = NULL;
GtkPicture *picture = NULL;
@ -39,15 +40,12 @@ threaded_picture_recover (GTask *task, gpointer source_object,
char *downloaded_image = NULL;
MgUtilSoup *util_soup = mg_util_soup_new ();
downloaded_image = mg_util_soup_get_request (util_soup,
url, &size_downloaded_image);
static GMutex mutex;
g_mutex_lock (&mutex);
image = get_image_for_url (url);
g_mutex_unlock (&mutex);
if (!g_file_query_exists (image, NULL)) {
downloaded_image =
mg_util_soup_get_request
(util_soup,
url, &size_downloaded_image);
g_info ("Storing %s", url);
iostream = g_file_create_readwrite (image, G_FILE_CREATE_NONE,
NULL, &error);
if (error) {
@ -63,9 +61,11 @@ threaded_picture_recover (GTask *task, gpointer source_object,
goto cleanup_create_picture_from_url;
}
}
g_mutex_unlock (&mutex);
texture = gdk_texture_new_from_file (image, &error);
if (error) {
fprintf (stderr, "Texture malformed.");
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)));
@ -93,21 +93,44 @@ free_picture_thread_attributes (gpointer user_data) {
g_free (attrs);
}
void
GtkPicture *
create_picture_from_url (const char *const url, gint picture_size,
GAsyncReadyCallback ready, gpointer source_object,
gpointer callback_data) {
GTask *task = g_task_new (source_object, NULL, ready, callback_data);
gpointer callback_data, bool do_not_download) {
GtkPicture *picture = NULL;
GFile *image = NULL;
GdkTexture *texture = NULL;
GError *error = NULL;
image = get_image_for_url (url);
size_t url_len = strlen (url) + 1;
PictureThreadAttributes *attrs = g_malloc (sizeof *attrs);
attrs->url = g_malloc (url_len * sizeof *url);
snprintf (attrs->url, url_len, "%s", url);
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);
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);
}
} else if (!do_not_download) {
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_set_return_on_cancel (task, true);
g_task_run_in_thread (task, threaded_picture_recover);
}
cleanup_create_picture_from_url:
return picture;
}
GFile *

View File

@ -6,18 +6,14 @@
#include <openmg/view/list_view_manga.h>
static void
search_text_changed (GtkEditable *self,
gpointer user_data);
static void
async_search_manga (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable);
static void
set_mangas_to_list_view (GObject *source_object,
GAsyncResult *res,
search_text_changed (GtkEntry *entry,
gpointer user_data);
typedef struct {
GtkListView *list_view_mangas;
ControlsAdwaita *controls;
} SearchTextData;
GtkWidget *
create_search_view (ControlsAdwaita *controls) {
GtkWidget *search_view = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
@ -26,12 +22,15 @@ create_search_view (ControlsAdwaita *controls) {
GtkWidget *scroll = gtk_scrolled_window_new ();
GListStore *mangas = g_list_store_new(MG_TYPE_MANGA);
GtkListView *list_view_mangas;
SearchTextData *search_text_data = g_malloc (sizeof *search_text_data);
gtk_box_append (GTK_BOX (search_view), search_entry);
list_view_mangas = create_list_view_mangas (mangas, controls);
g_signal_connect (search_entry, "changed",
G_CALLBACK (search_text_changed), list_view_mangas);
search_text_data->list_view_mangas = list_view_mangas;
search_text_data ->controls = controls;
g_signal_connect (search_entry, "activate",
G_CALLBACK (search_text_changed), search_text_data);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scroll),
GTK_WIDGET (list_view_mangas));
@ -42,43 +41,29 @@ create_search_view (ControlsAdwaita *controls) {
return search_view;
}
static GCancellable *cancellable = NULL;
static void
search_text_changed (GtkEditable *editable,
search_text_changed (GtkEntry *entry,
gpointer user_data) {
if (cancellable) {
g_cancellable_cancel (cancellable);
}
cancellable = g_cancellable_new ();
GtkListView *list_view_mangas = GTK_LIST_VIEW (user_data);
GTask *task = g_task_new (editable, cancellable, set_mangas_to_list_view,list_view_mangas);
g_task_set_return_on_cancel (task, true);
g_task_run_in_thread (task, async_search_manga);
}
static void
set_mangas_to_list_view (GObject *source_object,
GAsyncResult *res,
gpointer user_data) {
GListStore *mangas = G_LIST_STORE (g_task_propagate_pointer (G_TASK (res), NULL));
if (!mangas) return;
GtkListView *list_view_mangas = GTK_LIST_VIEW (user_data);
GtkSingleSelection *selection = GTK_SINGLE_SELECTION (
gtk_list_view_get_model (list_view_mangas));
gtk_single_selection_set_model (selection,
G_LIST_MODEL (mangas));
}
static void
async_search_manga (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable) {
GtkEntry *entry = GTK_ENTRY (source_object);
SearchTextData *search_text_data = (SearchTextData *) user_data;
ControlsAdwaita *controls = search_text_data->controls;
GtkListView *list_view_mangas = search_text_data->list_view_mangas;
GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry);
MgBackendReadmng *readmng = mg_backend_readmng_new ();
const char *search_string = gtk_entry_buffer_get_text (buffer);
GListStore *mangas = mg_backend_readmng_search (readmng, search_string);
g_task_return_pointer (task, mangas, g_object_unref);
for (size_t i = 0; i < controls->image_threads_len; i++) {
g_cancellable_cancel (controls->image_threads[i]);
}
if (controls->image_threads) {
g_free (controls->image_threads);
}
controls->image_threads = NULL;
controls->image_threads_len = 0;
controls->avoid_list_image_downloads = true;
if (!mangas) return;
GtkSingleSelection *selection = GTK_SINGLE_SELECTION (
gtk_list_view_get_model (list_view_mangas));
controls->avoid_list_image_downloads = false;
gtk_single_selection_set_model (selection,
G_LIST_MODEL (mangas));
}