Adding initial search view.

This commit is contained in:
sergiotarxz 2022-01-16 01:32:46 +01:00
parent c96742dcf4
commit 31850456aa
11 changed files with 286 additions and 10 deletions

View File

@ -30,6 +30,10 @@ mg_backend_readmng_new (void);
GListStore * GListStore *
mg_backend_readmng_get_featured_manga (MgBackendReadmng *self); mg_backend_readmng_get_featured_manga (MgBackendReadmng *self);
GListStore *
mg_backend_readmng_search (MgBackendReadmng *self,
const char *search_query);
void void
mg_backend_readmng_retrieve_manga_details (MgBackendReadmng *self, mg_backend_readmng_retrieve_manga_details (MgBackendReadmng *self,
MgManga *manga); MgManga *manga);

View File

@ -27,6 +27,6 @@ mg_util_regex_splitted_string_free (MgUtilRegex *self,
struct SplittedString *splitted_string); struct SplittedString *splitted_string);
char * char *
mg_util_regex_match_1 (MgUtilRegex *self, mg_util_regex_match_1 (MgUtilRegex *self,
char *re_str, char *subject); const char *re_str, const char *subject);
G_END_DECLS G_END_DECLS

View File

@ -9,7 +9,17 @@ G_DECLARE_FINAL_TYPE (MgUtilSoup, mg_util_soup, MG, UTIL_SOUP, GObject)
MgUtilSoup * MgUtilSoup *
mg_util_soup_new (); mg_util_soup_new ();
typedef struct {
char *key;
char *value;
} SoupParam;
char * char *
mg_util_soup_get_request (MgUtilSoup *self, const char *const url, gsize *size_response_text); mg_util_soup_get_request (MgUtilSoup *self, const char *const url, gsize *size_response_text);
char *
mg_util_soup_post_request_url_encoded (MgUtilSoup *self,
const char *url, SoupParam *body, gsize body_len,
SoupParam *headers, gsize headers_len,
gsize *size_response_text);
G_END_DECLS G_END_DECLS

View File

@ -2,6 +2,9 @@
#include <glib.h> #include <glib.h>
#include <gtk/gtk.h>
#include <adwaita.h>
typedef struct { typedef struct {
AdwHeaderBar *header; AdwHeaderBar *header;
AdwLeaflet *views_leaflet; AdwLeaflet *views_leaflet;

View File

@ -0,0 +1,6 @@
#include <gtk/gtk.h>
#include <openmg/view/controls.h>
GtkWidget *
create_search_view (ControlsAdwaita *controls);

View File

@ -9,7 +9,8 @@ openmgdeps = [
dependency('libxml-2.0'), dependency('libxml-2.0'),
dependency('libpcre2-8'), dependency('libpcre2-8'),
dependency('gio-2.0'), dependency('gio-2.0'),
dependency('sqlite3') dependency('sqlite3'),
dependency('json-glib-1.0'),
] ]
sources = [ sources = [
@ -30,6 +31,7 @@ sources = [
'src/database.c', 'src/database.c',
'src/database/statement.c', 'src/database/statement.c',
'src/view/explore.c', 'src/view/explore.c',
'src/view/search.c',
'src/main.c', 'src/main.c',
] ]

View File

@ -1,4 +1,6 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <json-glib/json-glib.h>
#include <libxml/HTMLparser.h> #include <libxml/HTMLparser.h>
#ifndef PCRE2_CODE_UNIT_WIDTH #ifndef PCRE2_CODE_UNIT_WIDTH
@ -63,6 +65,8 @@ static MgMangaChapter *
mg_backend_readmng_loop_li_chapter ( mg_backend_readmng_loop_li_chapter (
MgBackendReadmng *self, MgBackendReadmng *self,
xmlNodePtr li); xmlNodePtr li);
static char *
mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_query);
static GListModel * static GListModel *
mg_backend_readmng_parse_page (MgBackendReadmng *self, mg_backend_readmng_parse_page (MgBackendReadmng *self,
xmlDocPtr html_document); xmlDocPtr html_document);
@ -92,6 +96,8 @@ static GListStore *
mg_backend_readmng_parse_main_page (MgBackendReadmng *self, const xmlDocPtr html_document); mg_backend_readmng_parse_main_page (MgBackendReadmng *self, const xmlDocPtr html_document);
static xmlDocPtr static xmlDocPtr
mg_backend_readmng_fetch_xml_main_page (MgBackendReadmng *self); mg_backend_readmng_fetch_xml_main_page (MgBackendReadmng *self);
static char *
mg_backend_readmng_get_id_manga_link_from_string (MgBackendReadmng *self, const char *url);
MgBackendReadmng * MgBackendReadmng *
mg_backend_readmng_new(void) { mg_backend_readmng_new(void) {
@ -226,6 +232,113 @@ mg_backend_readmng_fetch_page_url (MgBackendReadmng *self,
return document; return document;
} }
GListStore *
mg_backend_readmng_search (MgBackendReadmng *self,
const char *search_query) {
char *response = mg_backend_readmng_fetch_search (self, search_query);
JsonParser *parser = json_parser_new ();
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 (error) {
g_warning ("Unable to parse json: %s.", error->message);
g_clear_error (&error);
goto cleanup_mg_backend_readmng_search;
}
root = json_parser_get_root (parser);
if (json_node_get_node_type (root) != JSON_NODE_ARRAY) {
goto cleanup_mg_backend_readmng_search;
}
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++) {
JsonObject *manga_json_object =
json_array_get_object_element (mangas_json_array, i);
char *id_manga = NULL;
const char *url = json_object_get_string_member
(manga_json_object, "url");
const char *title = json_object_get_string_member
(manga_json_object, "title");
const char *image = json_object_get_string_member
(manga_json_object, "image");
id_manga = mg_backend_readmng_get_id_manga_link_from_string (self, url);
g_list_store_append (mangas, mg_manga_new (image, title, id_manga));
pcre2_substring_free ((PCRE2_UCHAR8 *) id_manga);
}
cleanup_mg_backend_readmng_search:
g_clear_object (&parser);
g_free (response);
response = NULL;
return mangas;
}
static char *
mg_backend_readmng_fetch_search (MgBackendReadmng *self, const char *search_query) {
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 ();
request_url_len = snprintf ( NULL, 0, "%s/%s/", self->base_url, "service/search");
request_url = mg_util_string_alloc_string (string_util, request_url_len);
snprintf ( request_url, request_url_len+1, "%s/%s/", self->base_url, "service/search");
SoupParam headers[] = {
{
.key = "Accept",
.value = "application/json, text/javascript, */*; q=0.01"
},
{
.key = "Content-Type",
.value = "application/x-www-form-urlencoded; charset=UTF-8"
},
{
.key = "X-Requested-With",
.value = "XMLHttpRequest"
}
};
char *phrase = g_malloc (strlen (search_query) + 1);
snprintf ( phrase, strlen (search_query) + 1, "%s", search_query);
SoupParam body[] = {
{
.key = "dataType",
.value = "json"
},
{
.key = "phrase",
.value = phrase
}
};
size_t headers_len = sizeof headers / sizeof *headers;
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);
g_free (request_url);
g_free (phrase);
request_url = NULL;
g_clear_object (&util_soup);
g_clear_object (&string_util);
return text_response;
}
GListStore * GListStore *
mg_backend_readmng_get_featured_manga (MgBackendReadmng *self) { mg_backend_readmng_get_featured_manga (MgBackendReadmng *self) {
@ -402,7 +515,6 @@ mg_backend_readmng_fetch_xml_details (MgBackendReadmng *self,
size_t request_url_len; size_t request_url_len;
size_t response_len = 0; size_t response_len = 0;
util_soup = mg_util_soup_new (); util_soup = mg_util_soup_new ();
string_util = mg_util_string_new (); string_util = mg_util_string_new ();
manga_id = mg_manga_get_id (manga); manga_id = mg_manga_get_id (manga);
@ -573,13 +685,18 @@ mg_backend_readmng_find_a_link_chapter (MgBackendReadmng *self,
static char * static char *
mg_backend_readmng_get_id_manga_link (MgBackendReadmng *self, xmlNodePtr a) { mg_backend_readmng_get_id_manga_link (MgBackendReadmng *self, xmlNodePtr a) {
char *re_str = "readmng\\.com/([^/]+)";
MgUtilXML *xml_utils = self->xml_utils; MgUtilXML *xml_utils = self->xml_utils;
MgUtilRegex *regex_util = mg_util_regex_new ();
char *href = mg_util_xml_get_attr (xml_utils, a, "href"); char *href = mg_util_xml_get_attr (xml_utils, a, "href");
char *result = mg_util_regex_match_1 (regex_util, re_str, href); char *result = mg_backend_readmng_get_id_manga_link_from_string (self, href);
g_free (href); g_free (href);
return result;
}
static char *
mg_backend_readmng_get_id_manga_link_from_string (MgBackendReadmng *self, const char *url) {
MgUtilRegex *regex_util = mg_util_regex_new ();
char *re_str = "readmng\\.com/([^/]+)";
char *result = mg_util_regex_match_1 (regex_util, re_str, url);
g_clear_object (&regex_util); g_clear_object (&regex_util);
return result; return result;
} }

View File

@ -131,7 +131,7 @@ cleanup_iterate_string_to_split:
char * char *
mg_util_regex_match_1 (MgUtilRegex *self, mg_util_regex_match_1 (MgUtilRegex *self,
char *re_str, char *subject) { const char *re_str, const char *subject) {
pcre2_code *re; pcre2_code *re;
pcre2_match_data *match_data; pcre2_match_data *match_data;

View File

@ -1,3 +1,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <libsoup/soup.h> #include <libsoup/soup.h>
#include <openmg/util/soup.h> #include <openmg/util/soup.h>
@ -23,6 +26,7 @@ mg_util_soup_class_init (MgUtilSoupClass *class) {
static void static void
mg_util_soup_init (MgUtilSoup *self) { mg_util_soup_init (MgUtilSoup *self) {
} }
char * char *
mg_util_soup_get_request (MgUtilSoup *self, const char *url, gsize *size_response_text) { mg_util_soup_get_request (MgUtilSoup *self, const char *url, gsize *size_response_text) {
SoupSession *soup_session; SoupSession *soup_session;
@ -53,6 +57,101 @@ mg_util_soup_get_request (MgUtilSoup *self, const char *url, gsize *size_respons
return return_value; return return_value;
} }
char *
mg_util_soup_post_request_url_encoded (MgUtilSoup *self,
const char *url, SoupParam *body, gsize body_len,
SoupParam *headers, gsize headers_len,
gsize *size_response_text) {
SoupSession *soup_session;
SoupMessage *msg;
SoupMessageBody *request_body;
SoupMessageHeaders *request_headers;
GValue response = G_VALUE_INIT;
GValue request = G_VALUE_INIT;
GValue request_headers_value = G_VALUE_INIT;
*size_response_text = 0;
g_value_init (&response, G_TYPE_BYTES);
g_value_init (&request, SOUP_TYPE_MESSAGE_BODY);
g_value_init (&request_headers_value,
SOUP_TYPE_MESSAGE_HEADERS);
soup_session = soup_session_new ();
msg = soup_message_new ("POST", url);
g_object_get_property (
G_OBJECT (msg),
"request-body",
&request);
g_object_get_property (
G_OBJECT (msg),
"request-headers",
&request_headers_value);
soup_message_set_request (msg,
"application/x-www-form-urlencoded; charset=UTF-8",
SOUP_MEMORY_COPY, "", 1);
request_body = g_value_peek_pointer (&request);
request_headers = g_value_peek_pointer (
&request_headers_value);
for (int i = 0; i < body_len; i++) {
char *key = g_uri_escape_string (body[i].key,
NULL, false);
size_t key_len = strlen (key) + 1;
char *value = g_uri_escape_string (body[i].value,
NULL, false);
size_t value_len = strlen (value) + 1;
if (body_len) {
soup_message_body_append (request_body,
SOUP_MEMORY_COPY, "&", 1);
}
soup_message_body_append (request_body,
SOUP_MEMORY_COPY, key, key_len);
soup_message_body_append (request_body,
SOUP_MEMORY_COPY, "=", 1);
soup_message_body_append (request_body,
SOUP_MEMORY_COPY, value, value_len);
g_free (key);
g_free (value);
}
soup_message_body_append (request_body,
SOUP_MEMORY_COPY, "", 1);
for (int i = 0; i < headers_len; i++) {
soup_message_headers_append (request_headers,
headers[i].key,
headers[i].value);
}
soup_session_send_message (soup_session, msg);
g_object_get_property(
G_OBJECT (msg),
"response-body-data",
&response);
const char *html_response = g_bytes_get_data ((GBytes *)
g_value_peek_pointer (&response),
size_response_text);
char *return_value = mg_util_soup_copy_binary_data(self, html_response, *size_response_text);
g_value_unset (&response);
g_value_unset (&request);
g_clear_object (&soup_session);
g_clear_object (&msg);
return return_value;
}
static char * static char *
mg_util_soup_copy_binary_data (MgUtilSoup *self, const char *input, size_t size) { mg_util_soup_copy_binary_data (MgUtilSoup *self, const char *input, size_t size) {
char *response = NULL; char *response = NULL;

View File

@ -5,6 +5,7 @@
#include <openmg/view/controls.h> #include <openmg/view/controls.h>
#include <openmg/view/explore.h> #include <openmg/view/explore.h>
#include <openmg/view/search.h>
static AdwHeaderBar * static AdwHeaderBar *
create_headerbar (GtkBox *box, ControlsAdwaita *controls, GtkButton **out_previous); create_headerbar (GtkBox *box, ControlsAdwaita *controls, GtkButton **out_previous);
@ -15,6 +16,8 @@ go_back_view (GtkButton *previous, gpointer user_data);
typedef void (*swipe_back_t)(AdwLeaflet *, gboolean); typedef void (*swipe_back_t)(AdwLeaflet *, gboolean);
static AdwLeaflet * static AdwLeaflet *
create_explore_leaflet (ControlsAdwaita *controls, swipe_back_t swipe_back); create_explore_leaflet (ControlsAdwaita *controls, swipe_back_t swipe_back);
static AdwLeaflet *
create_search_leaflet (ControlsAdwaita *controls, swipe_back_t swipe_back);
static void static void
activate (AdwApplication *app, activate (AdwApplication *app,
@ -29,6 +32,7 @@ activate (AdwApplication *app,
ControlsAdwaita *controls = g_malloc (sizeof *controls); ControlsAdwaita *controls = g_malloc (sizeof *controls);
GtkButton *previous = NULL; GtkButton *previous = NULL;
AdwLeaflet *views_leaflet_explore; AdwLeaflet *views_leaflet_explore;
AdwLeaflet *views_leaflet_search;
AdwHeaderBar *header_bar; AdwHeaderBar *header_bar;
swipe_back_t swipe_back = (swipe_back_t) dlsym swipe_back_t swipe_back = (swipe_back_t) dlsym
@ -45,6 +49,7 @@ activate (AdwApplication *app,
views_leaflet_explore = create_explore_leaflet (controls, swipe_back); views_leaflet_explore = create_explore_leaflet (controls, swipe_back);
views_leaflet_search = create_search_leaflet (controls, swipe_back);
header_bar = create_headerbar (box, controls, &previous); header_bar = create_headerbar (box, controls, &previous);
controls->header = header_bar; controls->header = header_bar;
controls->previous = previous; controls->previous = previous;
@ -52,12 +57,32 @@ activate (AdwApplication *app,
AdwViewStackPage *explore_page = adw_view_stack_add_titled (view_stack, GTK_WIDGET (views_leaflet_explore), AdwViewStackPage *explore_page = adw_view_stack_add_titled (view_stack, GTK_WIDGET (views_leaflet_explore),
"explore", "explore",
"Explore"); "Explore");
AdwViewStackPage *search_page = adw_view_stack_add_titled (view_stack, GTK_WIDGET (views_leaflet_search),
"search",
"Search");
adw_view_stack_page_set_icon_name (explore_page, "view-list-symbolic"); adw_view_stack_page_set_icon_name (explore_page, "view-list-symbolic");
adw_view_stack_page_set_icon_name (search_page, "system-search-symbolic");
gtk_box_append (box, GTK_WIDGET (view_stack)); gtk_box_append (box, GTK_WIDGET (view_stack));
gtk_widget_show (window); gtk_widget_show (window);
} }
static AdwLeaflet *
create_search_leaflet (ControlsAdwaita *controls, swipe_back_t swipe_back) {
AdwLeaflet *views_leaflet = ADW_LEAFLET (adw_leaflet_new ());
GtkWidget *search_view;
swipe_back (views_leaflet, 1);
search_view = create_search_view (controls);
adw_leaflet_append (views_leaflet, search_view);
adw_leaflet_set_can_unfold (views_leaflet, false);
return views_leaflet;
}
static AdwLeaflet * static AdwLeaflet *
create_explore_leaflet (ControlsAdwaita *controls, swipe_back_t swipe_back) { create_explore_leaflet (ControlsAdwaita *controls, swipe_back_t swipe_back) {
AdwLeaflet *views_leaflet_explore = ADW_LEAFLET (adw_leaflet_new ()); AdwLeaflet *views_leaflet_explore = ADW_LEAFLET (adw_leaflet_new ());
@ -75,8 +100,7 @@ create_explore_leaflet (ControlsAdwaita *controls, swipe_back_t swipe_back) {
static GtkBox * static GtkBox *
create_main_box (AdwApplicationWindow *window) { create_main_box (AdwApplicationWindow *window) {
GtkWidget *box = gtk_box_new( GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL,
GTK_ORIENTATION_VERTICAL,
10); 10);
adw_application_window_set_content( adw_application_window_set_content(
window, window,

11
src/view/search.c Normal file
View File

@ -0,0 +1,11 @@
#include <openmg/view/search.h>
GtkWidget *
create_search_view (ControlsAdwaita *controls) {
GtkWidget *search_view = gtk_box_new (
GTK_ORIENTATION_VERTICAL, 10);
GtkWidget *search_entry = gtk_entry_new ();
gtk_box_append (GTK_BOX (search_view), search_entry);;
return search_view;
}