Making possible to see the list of available chapters.

This commit is contained in:
sergiotarxz 2021-11-06 01:36:07 +01:00
parent aad920661e
commit ba2c4ac9f4
9 changed files with 389 additions and 2 deletions

16
include/openmg/chapter.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS;
#define MG_TYPE_MANGA_CHAPTER mg_manga_chapter_get_type()
G_DECLARE_FINAL_TYPE (MgMangaChapter, mg_manga_chapter, MG, MANGA_CHAPTER, GObject)
MgMangaChapter *
mg_manga_chapter_new (const char *const title,
const char *const published_text,
const char *const url);
char *
mg_manga_chapter_get_title (MgMangaChapter *self);
G_END_DECLS

View File

@ -22,6 +22,10 @@ int
mg_manga_has_details (MgManga *self); mg_manga_has_details (MgManga *self);
void void
mg_manga_details_recovered (MgManga *self); mg_manga_details_recovered (MgManga *self);
void
mg_manga_set_chapter_list (MgManga *self, GListStore *chapter_list);
GListStore *
mg_manga_get_chapter_list (MgManga *self);
MgManga *mg_manga_new (const char *const image_url, const char *const title, const char *const id); MgManga *mg_manga_new (const char *const image_url, const char *const title, const char *const id);

View File

@ -0,0 +1,5 @@
#include <gtk/gtk.h>
#include <openmg/manga.h>
GtkListView *
create_list_view_chapters (MgManga *manga);

View File

@ -12,6 +12,7 @@ openmgdeps = [
] ]
sources = [ sources = [
'src/view/list_view_chapter.c',
'src/view/picture.c', 'src/view/picture.c',
'src/util/gobject_utility_extensions.c', 'src/util/gobject_utility_extensions.c',
'src/view/detail_manga.c', 'src/view/detail_manga.c',
@ -22,6 +23,7 @@ sources = [
'src/view/list_view_manga.c', 'src/view/list_view_manga.c',
'src/view/main_view.c', 'src/view/main_view.c',
'src/manga.c', 'src/manga.c',
'src/chapter.c',
'src/backend/readmng.c', 'src/backend/readmng.c',
'src/main.c', 'src/main.c',
] ]

View File

@ -8,6 +8,7 @@
#include <openmg/util/string.h> #include <openmg/util/string.h>
#include <openmg/manga.h> #include <openmg/manga.h>
#include <openmg/chapter.h>
typedef enum { typedef enum {
MG_BACKEND_READMNG_BASE_URL = 1, MG_BACKEND_READMNG_BASE_URL = 1,
@ -44,6 +45,17 @@ mg_backend_readmng_class_init (MgBackendReadmngClass *class) {
mg_backend_readmng_properties); mg_backend_readmng_properties);
} }
static xmlNodePtr
mg_backend_readmng_get_a_for_chapter (
MgBackendReadmng *self,
xmlNodePtr li);
static MgMangaChapter *
mg_backend_readmng_loop_li_chapter (
MgBackendReadmng *self,
xmlNodePtr li);
static GListStore *
mg_backend_readmng_recover_chapter_list (MgBackendReadmng *self,
xmlDocPtr html_document_details);
static xmlDocPtr static xmlDocPtr
mg_backend_readmng_fetch_xml_details (MgBackendReadmng *self, mg_backend_readmng_fetch_xml_details (MgBackendReadmng *self,
MgManga *manga); MgManga *manga);
@ -140,13 +152,15 @@ mg_backend_readmng_retrieve_manga_details (MgBackendReadmng *self,
xmlNodePtr *movie_detail = NULL; xmlNodePtr *movie_detail = NULL;
xmlXPathObjectPtr xpath_result = NULL; xmlXPathObjectPtr xpath_result = NULL;
xmlNodeSetPtr node_set = NULL; xmlNodeSetPtr node_set = NULL;
GListStore *manga_chapters = NULL;
size_t movie_detail_len = 0; size_t movie_detail_len = 0;
if (mg_manga_has_details (manga)) { if (mg_manga_has_details (manga)) {
return; return;
} }
xml_utils = mg_util_xml_new ();
xml_utils = self->xml_utils;
html_document = mg_backend_readmng_fetch_xml_details (self, html_document = mg_backend_readmng_fetch_xml_details (self,
manga); manga);
xpath_result = mg_util_xml_get_nodes_xpath_expression (xml_utils, xpath_result = mg_util_xml_get_nodes_xpath_expression (xml_utils,
@ -165,9 +179,87 @@ mg_backend_readmng_retrieve_manga_details (MgBackendReadmng *self,
mg_manga_set_description (manga, mg_manga_set_description (manga,
(char *) xmlNodeGetContent (movie_detail[0])); (char *) xmlNodeGetContent (movie_detail[0]));
} }
manga_chapters = mg_backend_readmng_recover_chapter_list (self, html_document);
mg_manga_set_chapter_list (manga, manga_chapters);
mg_manga_details_recovered (manga); mg_manga_details_recovered (manga);
} }
static GListStore *
mg_backend_readmng_recover_chapter_list (MgBackendReadmng *self,
xmlDocPtr html_document_details) {
MgUtilXML *xml_utils = self->xml_utils;
xmlXPathObjectPtr xpath_result = NULL;
xmlNodeSetPtr node_set = NULL;
xmlNodePtr *uls = NULL;
xmlNodePtr ul;
GListStore *return_value = g_list_store_new (
MG_TYPE_MANGA_CHAPTER);
size_t ul_len = 0;
xpath_result = mg_util_xml_get_nodes_xpath_expression (xml_utils,
html_document_details, "//ul[@class]");
node_set = xpath_result->nodesetval;
if (!node_set) {
fprintf(stderr, "No matching ul\n");
return return_value;
}
for (int i = 0; i < node_set->nodeNr; i++) {
xmlNodePtr node = node_set->nodeTab[i];
uls = mg_util_xml_loop_search_class (xml_utils,
node, uls, "chp_lst", &ul_len);
}
if (!ul_len) {
fprintf(stderr, "No matching chp_lst\n");
return return_value;
}
ul = uls[0];
for (xmlNodePtr li = ul->children; li; li = li->next) {
if (!strcmp ((char *) li->name, "li")) {
MgMangaChapter *chapter = mg_backend_readmng_loop_li_chapter (self, li);
if (chapter) {
g_list_store_append (return_value, chapter);
}
}
}
return return_value;
}
static MgMangaChapter *
mg_backend_readmng_loop_li_chapter (
MgBackendReadmng *self,
xmlNodePtr li) {
MgUtilXML *xml_utils = self->xml_utils;
xmlNodePtr a = mg_backend_readmng_get_a_for_chapter (
self, li);
if (!a) return NULL;
char *url = mg_util_xml_get_attr (xml_utils, a, "href");
size_t val_len = 0;
size_t dte_len = 0;
xmlNodePtr *val = mg_util_xml_find_class (xml_utils, a, "val", &val_len, NULL, 1);
xmlNodePtr *dte = mg_util_xml_find_class (xml_utils, a, "dte", &dte_len, NULL, 1);
if (val_len && dte_len) {
return mg_manga_chapter_new (
(char *) xmlNodeGetContent (val[0]),
(char *) xmlNodeGetContent(dte[0]),
url);
}
return NULL;
}
static xmlNodePtr
mg_backend_readmng_get_a_for_chapter (
MgBackendReadmng *self,
xmlNodePtr li) {
for (xmlNodePtr child = li->children; child; child = child->next) {
if (!strcmp((char *) child->name, "a")) {
return child;
}
}
return NULL;
}
static xmlDocPtr static xmlDocPtr
mg_backend_readmng_fetch_xml_details (MgBackendReadmng *self, mg_backend_readmng_fetch_xml_details (MgBackendReadmng *self,
MgManga *manga) { MgManga *manga) {

162
src/chapter.c Normal file
View File

@ -0,0 +1,162 @@
#include <glib-object.h>
#include <openmg/chapter.h>
#include <openmg/util/string.h>
struct _MgMangaChapter {
GObject parent_instance;
char *title;
char *url;
char *published_text;
};
static void
mg_manga_chapter_class_init (MgMangaChapterClass *class);
static void
mg_manga_chapter_class_init (MgMangaChapterClass *class);
static void
mg_manga_chapter_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void
mg_manga_chapter_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (MgMangaChapter, mg_manga_chapter,
G_TYPE_OBJECT)
typedef enum {
MG_MANGA_CHAPTER_TITLE = 1,
MG_MANGA_CHAPTER_URL,
MG_MANGA_CHAPTER_PUBLISHED_TEXT,
MG_MANGA_CHAPTER_N_PROPERTIES
} MgMangaChapterProperties;
static GParamSpec *manga_chapter_properties[MG_MANGA_CHAPTER_N_PROPERTIES] = { NULL, };
MgMangaChapter *
mg_manga_chapter_new (const char *const title,
const char *const published_text,
const char *const url) {
MgMangaChapter *self = NULL;
MgUtilString *string_util = mg_util_string_new ();
self = MG_MANGA_CHAPTER (
g_object_new (MG_TYPE_MANGA_CHAPTER, NULL));
self->title = mg_util_string_alloc_string (
string_util, strlen (title));
self->url = mg_util_string_alloc_string (
string_util, strlen (url));
self->published_text = mg_util_string_alloc_string (
string_util, strlen (published_text));
mg_util_string_copy_substring (string_util,
title, self->title,
strlen(title) + 1, 0, strlen (title));
mg_util_string_copy_substring (string_util,
url, self->url,
strlen(url) + 1, 0, strlen (url));
mg_util_string_copy_substring (string_util,
published_text, self->published_text,
strlen(published_text) + 1, 0,
strlen (published_text));
return self;
}
static void
mg_manga_chapter_class_init (MgMangaChapterClass *class) {
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->set_property = mg_manga_chapter_set_property;
object_class->get_property = mg_manga_chapter_get_property;
manga_chapter_properties[MG_MANGA_CHAPTER_TITLE] =
g_param_spec_string ("title",
"Title",
"Title of the chapter.",
NULL,
G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE);
manga_chapter_properties[MG_MANGA_CHAPTER_URL] =
g_param_spec_string ("url",
"URL",
"URL of the chapter.",
NULL,
G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE);
manga_chapter_properties[MG_MANGA_CHAPTER_PUBLISHED_TEXT] =
g_param_spec_string ("published_text",
"PublishedText",
"Text of publication.",
NULL,
G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE);
g_object_class_install_properties (object_class,
MG_MANGA_CHAPTER_N_PROPERTIES,
manga_chapter_properties);
}
static void
mg_manga_chapter_init (MgMangaChapter *self) {
}
static void
mg_manga_chapter_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec) {
MgMangaChapter *self = MG_MANGA_CHAPTER (object);
switch ((MgMangaChapterProperties) property_id) {
case MG_MANGA_CHAPTER_TITLE:
g_value_set_string (value, self->title);
break;
case MG_MANGA_CHAPTER_URL:
g_value_set_string (value, self->url);
break;
case MG_MANGA_CHAPTER_PUBLISHED_TEXT:
g_value_set_string (value, self->published_text);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
mg_manga_chapter_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec) {
MgMangaChapter *self = MG_MANGA_CHAPTER (object);
switch ((MgMangaChapterProperties) property_id) {
case MG_MANGA_CHAPTER_TITLE:
g_free (self->title);
self->title = g_value_dup_string (value);
break;
case MG_MANGA_CHAPTER_URL:
g_free (self->url);
self->url = g_value_dup_string (value);
break;
case MG_MANGA_CHAPTER_PUBLISHED_TEXT:
g_free (self->published_text);
self->published_text = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
char *
mg_manga_chapter_get_title (MgMangaChapter *self) {
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_STRING);
g_object_get_property (G_OBJECT (self), "title", &value);
return g_value_dup_string (&value);
}

View File

@ -1,7 +1,11 @@
#include <stdio.h>
#include <glib-object.h> #include <glib-object.h>
#include <gio/gio.h>
#include <openmg/util/string.h> #include <openmg/util/string.h>
#include <openmg/manga.h> #include <openmg/manga.h>
#include <openmg/chapter.h>
struct _MgManga { struct _MgManga {
GObject parent_instance; GObject parent_instance;
@ -9,6 +13,7 @@ struct _MgManga {
char *title; char *title;
char *id; char *id;
char *description; char *description;
GListStore *chapter_list;
int has_details; int has_details;
}; };
@ -19,6 +24,7 @@ typedef enum {
MG_MANGA_TITLE, MG_MANGA_TITLE,
MG_MANGA_ID, MG_MANGA_ID,
MG_MANGA_DESCRIPTION, MG_MANGA_DESCRIPTION,
MG_MANGA_CHAPTER_LIST,
MG_MANGA_N_PROPERTIES MG_MANGA_N_PROPERTIES
} MgMangaProperties; } MgMangaProperties;
@ -62,6 +68,13 @@ mg_manga_class_init (MgMangaClass *class) {
"Description of the manga.", "Description of the manga.",
NULL, NULL,
G_PARAM_READWRITE); G_PARAM_READWRITE);
manga_properties[MG_MANGA_CHAPTER_LIST] = g_param_spec_object (
"chapter_list",
"ChapterList",
"List of chapters.",
G_TYPE_LIST_STORE,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, g_object_class_install_properties (object_class,
MG_MANGA_N_PROPERTIES, MG_MANGA_N_PROPERTIES,
@ -123,6 +136,24 @@ mg_manga_get_description (MgManga *self) {
return g_value_dup_string (&value); return g_value_dup_string (&value);
} }
GListStore *
mg_manga_get_chapter_list (MgManga *self) {
if (!mg_manga_has_details (self)) {
fprintf(stderr, "Manga has still not details\n");
return NULL;
}
GValue value = G_VALUE_INIT;
GListStore *return_value;
g_value_init (&value, G_TYPE_LIST_STORE);
g_object_get_property (G_OBJECT (self),
"chapter_list",
&value);
return_value = G_LIST_STORE (g_value_peek_pointer (&value));
return return_value;
}
void void
mg_manga_set_description (MgManga *self, const char *description) { mg_manga_set_description (MgManga *self, const char *description) {
GValue value = G_VALUE_INIT; GValue value = G_VALUE_INIT;
@ -131,12 +162,21 @@ mg_manga_set_description (MgManga *self, const char *description) {
g_object_set_property (G_OBJECT (self), "description", &value); g_object_set_property (G_OBJECT (self), "description", &value);
} }
void
mg_manga_set_chapter_list (MgManga *self, GListStore *chapter_list) {
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_LIST_STORE);
g_value_set_instance (&value, chapter_list);
g_object_set_property (G_OBJECT (self), "chapter_list", &value);
}
static void static void
mg_manga_set_property (GObject *object, mg_manga_set_property (GObject *object,
guint property_id, guint property_id,
const GValue *value, const GValue *value,
GParamSpec *pspec) { GParamSpec *pspec) {
MgManga *self = MG_MANGA (object); MgManga *self = MG_MANGA (object);
GListStore *chapter_list;
switch ((MgMangaProperties) property_id) { switch ((MgMangaProperties) property_id) {
case MG_MANGA_IMAGE_URL: case MG_MANGA_IMAGE_URL:
g_free (self->image_url); g_free (self->image_url);
@ -154,6 +194,11 @@ mg_manga_set_property (GObject *object,
g_free (self->description); g_free (self->description);
self->description = g_value_dup_string (value); self->description = g_value_dup_string (value);
break; break;
case MG_MANGA_CHAPTER_LIST:
g_free (self->chapter_list);
chapter_list = g_value_peek_pointer (value);
self->chapter_list = chapter_list;
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@ -179,6 +224,9 @@ mg_manga_get_property (GObject *object,
case MG_MANGA_DESCRIPTION: case MG_MANGA_DESCRIPTION:
g_value_set_string (value, self->description); g_value_set_string (value, self->description);
break; break;
case MG_MANGA_CHAPTER_LIST:
g_value_set_instance (value, self->chapter_list);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;

View File

@ -6,26 +6,32 @@
#include <openmg/backend/readmng.h> #include <openmg/backend/readmng.h>
#include <openmg/util/xml.h> #include <openmg/util/xml.h>
#include <openmg/util/gobject_utility_extensions.h>
#include <openmg/view/picture.h> #include <openmg/view/picture.h>
#include <openmg/view/detail_manga.h> #include <openmg/view/detail_manga.h>
#include <openmg/view/list_view_chapter.h>
GtkBox * GtkBox *
create_detail_view (MgManga *manga) { create_detail_view (MgManga *manga) {
MgBackendReadmng *readmng = mg_backend_readmng_new (); MgBackendReadmng *readmng = mg_backend_readmng_new ();
GtkWidget *scroll;
GtkBox *detail_view = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0)); GtkBox *detail_view = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
GtkBox *avatar_title_box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0)); GtkBox *avatar_title_box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0));
MgUtilXML *xml_util = mg_util_xml_new (); MgUtilXML *xml_util = mg_util_xml_new ();
GtkLabel *manga_title = NULL; GtkLabel *manga_title = NULL;
GtkLabel *manga_description = NULL; GtkLabel *manga_description = NULL;
GtkListView *chapter_list = NULL;
GtkPicture *manga_image = create_picture_from_url ( GtkPicture *manga_image = create_picture_from_url (
mg_manga_get_image_url(manga), 200); mg_manga_get_image_url(manga), 200);
char *title_text = mg_util_xml_get_title_text ( char *title_text = mg_util_xml_get_title_text (
xml_util, mg_manga_get_title (manga)); xml_util, mg_manga_get_title (manga));
char *description_text; char *description_text;
mg_backend_readmng_retrieve_manga_details (readmng, manga); scroll = gtk_scrolled_window_new ();
mg_backend_readmng_retrieve_manga_details (readmng, manga);
chapter_list = create_list_view_chapters (manga);
description_text = mg_manga_get_description (manga); description_text = mg_manga_get_description (manga);
manga_title = GTK_LABEL (gtk_label_new (title_text)); manga_title = GTK_LABEL (gtk_label_new (title_text));
@ -39,6 +45,9 @@ create_detail_view (MgManga *manga) {
gtk_box_append (avatar_title_box, GTK_WIDGET (manga_title)); gtk_box_append (avatar_title_box, GTK_WIDGET (manga_title));
gtk_box_append (detail_view, GTK_WIDGET (avatar_title_box)); gtk_box_append (detail_view, GTK_WIDGET (avatar_title_box));
gtk_box_append (detail_view, GTK_WIDGET (manga_description)); gtk_box_append (detail_view, GTK_WIDGET (manga_description));
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scroll), GTK_WIDGET (chapter_list));
g_object_set_property_int (G_OBJECT (scroll), "vexpand", 1);
gtk_box_append (detail_view, GTK_WIDGET (scroll));
return detail_view; return detail_view;
} }

View File

@ -0,0 +1,49 @@
#include <gtk/gtk.h>
#include <openmg/manga.h>
#include <openmg/chapter.h>
#include <openmg/backend/readmng.h>
#include <openmg/view/list_view_chapter.h>
static void
setup_list_view_chapter_list (GtkSignalListItemFactory *factory,
GtkListItem *list_item,
gpointer user_data);
GtkListView *
create_list_view_chapters (MgManga *manga) {
GListStore *manga_chapter_list = NULL;
GtkListView *list_view_chapters = NULL;
GtkSingleSelection *selection = NULL;
GtkListItemFactory *factory =
gtk_signal_list_item_factory_new ();
manga_chapter_list = mg_manga_get_chapter_list (manga);
selection = gtk_single_selection_new
(G_LIST_MODEL (manga_chapter_list));
g_signal_connect (G_OBJECT (factory), "bind",
G_CALLBACK (setup_list_view_chapter_list),
NULL);
list_view_chapters = GTK_LIST_VIEW (gtk_list_view_new (GTK_SELECTION_MODEL (selection),
factory));
return list_view_chapters;
}
static void
setup_list_view_chapter_list (GtkSignalListItemFactory *factory,
GtkListItem *list_item,
gpointer user_data) {
MgMangaChapter *manga_chapter = gtk_list_item_get_item (list_item);
GtkBox *box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0));
GtkLabel *title = GTK_LABEL (gtk_label_new (mg_manga_chapter_get_title (manga_chapter)));
GtkImage *icon = GTK_IMAGE (gtk_image_new_from_icon_name (
"weather-clear-night-symbolic"));
gtk_image_set_icon_size (icon, GTK_ICON_SIZE_LARGE);
gtk_label_set_wrap (title, 1);
gtk_box_append (box, GTK_WIDGET (icon));
gtk_box_append (box, GTK_WIDGET (title));
gtk_list_item_set_child (list_item, GTK_WIDGET (box));
}