~haowenl/vorg

ddcb35fa42e15e713276045ac3b0dc0d6ad44f5a — Haowen Liu a month ago ea3f411
Add DB query
M src/meson.build => src/meson.build +2 -0
@@ 7,6 7,7 @@ vorg_sources = [
  'vorg-multi-entry.c',
  'vorg-repo.c',
  'vorg-browse.c',
  'vorg-video.c',
  'utils/vorg-utils.c',
]



@@ 19,6 20,7 @@ vorg_headers = [
  'vorg-multi-entry.h',
  'vorg-repo.h',
  'vorg-browse.h',
  'vorg-video.h',
  'utils/vorg-utils.h',
]


M src/vorg-application.c => src/vorg-application.c +3 -0
@@ 272,6 272,9 @@ vorg_application_window_mode_action (GSimpleAction *action,
  if (g_strcmp0 (name, "browse") == 0)
    {
      vorg_window_set_state (self->window, VORG_WINDOW_BROWSE);
      VorgBrowse *browse = vorg_window_get_browse (self->window);
      g_autoptr (GListStore) videos = vorg_repo_get_videos (self->repo);
      vorg_browse_set_videos (browse, videos);
    }
  else if (g_strcmp0 (name, "import") == 0)
    {

M src/vorg-browse.c => src/vorg-browse.c +90 -2
@@ 17,18 17,33 @@
 */

#include "vorg-browse.h"
#include "vorg-video.h"

struct _VorgBrowse
{
  GtkBox parent_instance;

  GtkFlowBox *browse_flowbox;
  GListStore *videos;
};

enum
{
  PROP_0,
  PROP_VIDEOS,
  NUM_PROPERTIES,
};

static GParamSpec *browse_props[NUM_PROPERTIES] = {
  NULL,
};

G_DEFINE_TYPE (VorgBrowse, vorg_browse, GTK_TYPE_BOX)

VorgBrowse *
vorg_browse_new ()
vorg_browse_new_with_videos (GListStore *videos)
{
  return VORG_BROWSE (g_object_new (VORG_TYPE_BROWSE, NULL));
  return VORG_BROWSE (g_object_new (VORG_TYPE_BROWSE, "videos", videos, NULL));
}

static void


@@ 44,6 59,31 @@ vorg_browse_finalize (GObject *object)
}

static void
vorg_browse_get_property (GObject *object, guint prop_id,
                          __attribute__ ((unused)) GValue *value,
                          GParamSpec *pspec)
{
  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}

static void
vorg_browse_set_property (GObject *object, guint prop_id, const GValue *value,
                          GParamSpec *pspec)
{
  VorgBrowse *browse = VORG_BROWSE (object);

  switch (prop_id)
    {
    case PROP_VIDEOS:
      vorg_browse_set_videos (browse, g_value_get_object (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
vorg_browse_class_init (VorgBrowseClass *klass)
{
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);


@@ 52,5 92,53 @@ vorg_browse_class_init (VorgBrowseClass *klass)
  gtk_widget_class_set_template_from_resource (
      widget_class, "/com/lunacd/vorg/vorg-browse.ui");

  gtk_widget_class_bind_template_child (widget_class, VorgBrowse,
                                        browse_flowbox);

  gobject_class->finalize = vorg_browse_finalize;
  gobject_class->get_property = vorg_browse_get_property;
  gobject_class->set_property = vorg_browse_set_property;

  /**
   * VorgBrowse:videos: (attributes org.gtk.Property.set=vorg_browse_set_videos) 
   * 
   * A list of videos to display.
   */
  browse_props[PROP_VIDEOS]
      = g_param_spec_object ("videos", NULL, NULL, G_TYPE_LIST_STORE,
                             G_PARAM_CONSTRUCT | G_PARAM_WRITABLE);

  g_object_class_install_properties (gobject_class, NUM_PROPERTIES,
                                     browse_props);
}

static GtkWidget *
vorg_browse_widget_from_video (GObject *item,
                               __attribute__ ((unused)) gpointer user_data)
{
  VorgVideo *video = VORG_VIDEO (item);
  return gtk_label_new (vorg_video_get_path (video));
}

/**
 * vorg_browse_set_videos: (attributes org.gtk.Method.set_property=videos) 
 * @videos: (transfer full) (nullable): The list of videos to display.
 * 
 * Sets videos to display.
 */
void
vorg_browse_set_videos (VorgBrowse *self, GListStore *videos)
{
  if (videos == NULL)
    {
      self->videos = g_list_store_new (VORG_TYPE_VIDEO);
    }
  else
    {
      self->videos = videos;
      g_object_ref (self->videos);
    }
  gtk_flow_box_bind_model (
      self->browse_flowbox, G_LIST_MODEL (self->videos),
      (GtkFlowBoxCreateWidgetFunc)&vorg_browse_widget_from_video, NULL, NULL);
}

M src/vorg-browse.h => src/vorg-browse.h +4 -0
@@ 23,3 23,7 @@
#define VORG_TYPE_BROWSE (vorg_browse_get_type ())

G_DECLARE_FINAL_TYPE (VorgBrowse, vorg_browse, VORG, BROWSE, GtkBox)

VorgBrowse *vorg_browse_new_with_videos (GListStore *videos);

void vorg_browse_set_videos (VorgBrowse *self, GListStore *videos);

M src/vorg-browse.ui => src/vorg-browse.ui +5 -1
@@ 2,5 2,9 @@
<interface>
  <requires lib="gtk" version="4.0" />
  <requires lib="adwaita" version="1.1" />
  <template class="VorgBrowse" parent="GtkBox"></template>
  <template class="VorgBrowse" parent="GtkBox">
    <child>
      <object class="GtkFlowBox" id="browse_flowbox"></object>
    </child>
  </template>
</interface>
\ No newline at end of file

M src/vorg-db.c => src/vorg-db.c +35 -1
@@ 463,7 463,8 @@ vorg_db_import_file (VorgDB *self, const gchar *sha, const gchar *title,
      = g_list_model_get_n_items (G_LIST_MODEL ((GtkStringList *)tags));
  g_autofree gint64 *actor_ids
      = vorg_db_get_list_id (self->db, actors, n_actors, "actor");
  g_autofree gint64 *tag_ids = vorg_db_get_list_id (self->db, tags, n_tags, "tag");
  g_autofree gint64 *tag_ids
      = vorg_db_get_list_id (self->db, tags, n_tags, "tag");
  gint64 studio_id = vorg_db_get_studio_id (self->db, studio);
  gint64 item_id;
  sqlite3_stmt *stmt;


@@ 489,3 490,36 @@ vorg_db_import_file (VorgDB *self, const gchar *sha, const gchar *title,
  vorg_db_insert_list_elems (self->db, item_id, actor_ids, n_actors, "actor");
  vorg_db_insert_list_elems (self->db, item_id, tag_ids, n_tags, "tag");
}

/**
 * vorg_db_get_videos: 
 * 
 * Gets a list of filtered videos.
 * 
 * Filters are control through `filter-title`, `filter-studio`, `filter-actors`,
 * `filter-tags` properties.
 * When all of those are empty, returns a full list of videos in the repo.
 * 
 * Returns: (transfer full): A list of `VorgVideo`s.
 */
GtkListStore *
vorg_db_get_videos (VorgDB *self)
{
  GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
  const gchar *sql = "SELECT hash, ext FROM items";
  sqlite3_stmt *stmt;
  gint rc = sqlite3_prepare (self->db, sql, 30, &stmt, NULL);
  SQLITE3_CHECK_ERROR (rc, "Prepare get videos")
  rc = sqlite3_step (stmt);
  while (rc == SQLITE_ROW)
    {
      const gchar *sha = (gchar *)sqlite3_column_text (stmt, 0);
      const gchar *ext = (gchar *)sqlite3_column_text (stmt, 1);
      GtkTreeIter iter;
      gtk_list_store_append (store, &iter);
      gtk_list_store_set (store, &iter, VORG_DB_COL_SHA, sha, VORG_DB_COL_EXT,
                          ext, -1);
      rc = sqlite3_step (stmt);
    }
  return store;
}

M src/vorg-db.h => src/vorg-db.h +9 -0
@@ 39,6 39,13 @@ typedef enum

G_DECLARE_FINAL_TYPE (VorgDB, vorg_db, VORG, DB, GObject)

enum
{
  VORG_DB_COL_SHA,
  VORG_DB_COL_EXT,
  VORG_DB_NUM_COLS,
};

VorgDB *vorg_db_new ();

gboolean vorg_db_get_initialized (VorgDB *self);


@@ 54,4 61,6 @@ void vorg_db_import_file (VorgDB *self, const gchar *sha, const gchar *title,
                          const GtkStringList *actors,
                          const GtkStringList *tags);

GtkListStore *vorg_db_get_videos (VorgDB *self);

G_END_DECLS

M src/vorg-multi-entry.c => src/vorg-multi-entry.c +9 -9
@@ 81,15 81,6 @@ vorg_multi_entry_set_button_add (GtkButton *button)
}

static void
vorg_multi_entry_finalize (GObject *object)
{
  VorgMultiEntry *self = VORG_MULTI_ENTRY (object);
  g_object_unref (self->store);

  G_OBJECT_CLASS (vorg_multi_entry_parent_class)->finalize (object);
}

static void
vorg_multi_entry_set_button_remove (GtkButton *button)
{
  if (g_strcmp0 ("Remove", gtk_button_get_label (button)) != 0)


@@ 205,6 196,15 @@ vorg_multi_entry_init (VorgMultiEntry *self)
}

static void
vorg_multi_entry_finalize (GObject *object)
{
  VorgMultiEntry *self = VORG_MULTI_ENTRY (object);
  g_object_unref (self->store);

  G_OBJECT_CLASS (vorg_multi_entry_parent_class)->finalize (object);
}

static void
vorg_multi_entry_get_property (GObject *obj, guint prop_id, GValue *value,
                               GParamSpec *pspec)
{

M src/vorg-repo.c => src/vorg-repo.c +33 -0
@@ 21,6 21,7 @@
#include "utils/vorg-utils.h"
#include "vorg-db.h"
#include "vorg-store.h"
#include "vorg-video.h"

struct _VorgRepo
{


@@ 292,3 293,35 @@ vorg_repo_import_file (VorgRepo *self, const gchar *title, const gchar *studio,
  g_clear_pointer (&self->current_file, g_object_unref);
  g_clear_pointer (&self->current_sha, g_free);
}

/**
 * vorg_repo_get_videos: 
 * 
 * Returns: (transfer full): A list of `VorgVideo`s. 
 */
GListStore *
vorg_repo_get_videos (VorgRepo *self)
{
  GListStore *result = g_list_store_new (VORG_TYPE_VIDEO);
  g_autoptr (GtkTreeModel) db_result
      = GTK_TREE_MODEL (vorg_db_get_videos (self->db));
  GtkTreeIter iter;
  if (!gtk_tree_model_get_iter_first (db_result, &iter))
    {
      return result;
    }
  do
    {
      g_autofree gchar *sha = NULL;
      g_autofree gchar *ext = NULL;
      gtk_tree_model_get (db_result, &iter, VORG_DB_COL_SHA, &sha,
                          VORG_DB_COL_EXT, &ext, -1);
      g_autofree gchar *path
          = vorg_store_get_path_from_sha (self->store, sha, ext);
      g_autofree gchar *thumbnail = vorg_store_get_thumbnail_from_sha (sha);
      g_autoptr (VorgVideo) video = vorg_video_new (path, thumbnail);
      g_list_store_append (result, video);
    }
  while (gtk_tree_model_iter_next (db_result, &iter));
  return result;
}

M src/vorg-repo.h => src/vorg-repo.h +2 -0
@@ 56,4 56,6 @@ void vorg_repo_import_file (VorgRepo *self, const gchar *title,
                            const gchar *studio, const GtkStringList *actors,
                            const GtkStringList *tags, GError **error);

GListStore *vorg_repo_get_videos (VorgRepo *self);

G_END_DECLS

M src/vorg-store.c => src/vorg-store.c +46 -0
@@ 210,6 210,52 @@ vorg_store_get_cache_home ()
}

/**
 * vorg_store_get_thumbnail_from_sha: 
 * @sha: SHA1 digest of the video.
 * @ext: File extension of the video.
 * 
 * Gets path to video.
 * 
 * Returns: (transfer full): The video path.
 */
gchar *
vorg_store_get_path_from_sha (VorgStore *self, const gchar *sha,
                             const gchar *ext)
{
  gchar lead[3] = { sha[0], sha[1], '\0' };
  g_autofree gchar *store_path = g_file_get_path (self->store);
  gsize ext_len = strlen (ext);
  g_autofree gchar *dest_filename
      = g_malloc (sizeof (gchar) * (38 + 1 + ext_len + 1));
  memcpy (dest_filename, sha + 2, 38);
  dest_filename[38] = '.';
  memcpy (dest_filename + 39, ext, ext_len + 1);
  return g_build_path ("/", store_path, lead, dest_filename, NULL);
}

/**
 * vorg_store_get_thumbnail_from_sha: 
 * @sha: SHA1 digest of the video.
 * 
 * Gets path to thumbnail.
 * 
 * Returns: (transfer full): The thumbnail path.
 */
gchar *
vorg_store_get_thumbnail_from_sha (const gchar *sha)
{
  gchar lead[3] = { sha[0], sha[1], '\0' };
  g_autoptr (GFile) cache_dir = vorg_store_get_cache_home ();
  g_autofree gchar *cache_path = g_file_get_path (cache_dir);
  g_autofree gchar *dest_filename
      = g_malloc (sizeof (gchar) * (38 + 1 + 3 + 1));
  memcpy (dest_filename, sha + 2, 38);
  dest_filename[38] = '.';
  memcpy (dest_filename + 39, "png", 4);
  return g_build_path ("/", cache_path, lead, dest_filename, NULL);
}

/**
 * vorg_store_import_file:
 * @file: (transfer none): A `GFile` of a video file to be exported.
 * @ext: (transfer none): The extension of the file to import.

M src/vorg-store.h => src/vorg-store.h +5 -0
@@ 43,6 43,11 @@ void vorg_store_create (VorgStore *self, GFile *repo, GError **error);

void vorg_store_open (VorgStore *self, GFile *repo, GError **error);

gchar *vorg_store_get_path_from_sha (VorgStore *self, const gchar *sha,
                                     const gchar *ext);

gchar *vorg_store_get_thumbnail_from_sha (const gchar *sha);

void vorg_store_import_file (VorgStore *self, GFile *file, const gchar *ext,
                             const gchar *sha);


A src/vorg-video.c => src/vorg-video.c +168 -0
@@ 0,0 1,168 @@
/* vorg-video.c
 *
 * Copyright 2022 Haowen Liu
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "vorg-video.h"

struct _VorgVideo
{
  GObject parent_instance;

  gchar *path;
  gchar *thumbnail;
};

enum
{
  PROP_0,
  PROP_PATH,
  PROP_THUMBNAIL,
  NUM_PROPERTIES,
};

static GParamSpec *video_props[NUM_PROPERTIES] = {
  NULL,
};

G_DEFINE_TYPE (VorgVideo, vorg_video, G_TYPE_OBJECT)

/**
 * vorg_video_new: 
 * @path: Path of the video.
 * @thumbnail: Path of the video thumbnail.
 * 
 * Returns: The created `VorgVideo`. 
 */
VorgVideo *
vorg_video_new (const gchar *path, const gchar *thumbnail)
{
  return g_object_new (VORG_TYPE_VIDEO, "path", path, "thumbnail", thumbnail,
                       NULL);
}

static void
vorg_video_init (VorgVideo *self)
{
  self->path = NULL;
  self->thumbnail = NULL;
}

static void
vorg_video_finalize (GObject *object)
{
  VorgVideo *self = VORG_VIDEO (object);

  g_clear_pointer (&self->path, g_free);
  g_clear_pointer (&self->thumbnail, g_free);

  G_OBJECT_CLASS (vorg_video_parent_class)->finalize (object);
}

static void
vorg_video_get_property (GObject *object, guint prop_id, GValue *value,
                         GParamSpec *pspec)
{
  VorgVideo *self = VORG_VIDEO (object);

  switch (prop_id)
    {
    case PROP_PATH:
      g_value_set_string (value, vorg_video_get_path (self));
      break;
    case PROP_THUMBNAIL:
      g_value_set_string (value, vorg_video_get_thumbnail (self));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
vorg_video_set_property (GObject *object, guint prop_id, const GValue *value,
                         GParamSpec *pspec)
{
  VorgVideo *self = VORG_VIDEO (object);

  switch (prop_id)
    {
    case PROP_PATH:
      self->path = g_strdup (g_value_get_string (value));
      break;
    case PROP_THUMBNAIL:
      self->thumbnail = g_strdup (g_value_get_string (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
vorg_video_class_init (VorgVideoClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->finalize = vorg_video_finalize;
  gobject_class->get_property = vorg_video_get_property;
  gobject_class->set_property = vorg_video_set_property;

  /**
   * VorgVideo:path: (attributes org.gtk.Property.get=vorg_video_get_path)
   * 
   * Path to the video file.
   */
  video_props[PROP_PATH] = g_param_spec_string (
      "path", NULL, NULL, "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);

  /**
   * VorgVideo:thumbnail: (attributes org.gtk.Property.get=vorg_video_get_thumbnail)
   * 
   * Path to the thumbnail image.
   */
  video_props[PROP_THUMBNAIL] = g_param_spec_string (
      "thumbnail", NULL, NULL, "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);

  g_object_class_install_properties (gobject_class, NUM_PROPERTIES,
                                     video_props);
}

/**
 * vorg_video_get_path: (attributes org.gtk.Method.get_property=path) 
 * 
 * Gets video path.
 * 
 * Returns: (transfer full): The video path.
 */
const gchar *
vorg_video_get_path (VorgVideo *self)
{
  return self->path;
}

/**
 * vorg_video_get_thumbnail: (attributes org.gtk.Method.get_property=thumbnail) 
 * 
 * Gets thumbnail path.
 * 
 * Returns: (transfer full): The thumbnail path.
 */
const gchar *
vorg_video_get_thumbnail (VorgVideo *self)
{
  return self->thumbnail;
}

A src/vorg-video.h => src/vorg-video.h +31 -0
@@ 0,0 1,31 @@
/* vorg-video.h
 *
 * Copyright 2022 Haowen Liu
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <gtk/gtk.h>

#define VORG_TYPE_VIDEO (vorg_video_get_type ())

G_DECLARE_FINAL_TYPE (VorgVideo, vorg_video, VORG, VIDEO, GObject)

VorgVideo *vorg_video_new (const gchar *path, const gchar *thumbnail);

const gchar *vorg_video_get_path (VorgVideo *self);

const gchar *vorg_video_get_thumbnail (VorgVideo *self);

M src/vorg-window.c => src/vorg-window.c +26 -1
@@ 18,7 18,6 @@

#include "vorg-window.h"
#include "utils/vorg-gtk-utils.h"
#include "vorg-browse.h"
#include "vorg-config.h"
#include "vorg-enums.h"



@@ 44,6 43,7 @@ enum
  PROP_0,
  PROP_WINDOW_STATE,
  PROP_EDIT,
  PROP_BROWSE,
  NUM_PROPERTIES,
};



@@ 82,6 82,9 @@ vorg_window_get_property (GObject *obj, guint prop_id, GValue *value,
    case PROP_EDIT:
      g_value_set_object (value, vorg_window_get_edit (window));
      break;
    case PROP_BROWSE:
      g_value_set_object (value, vorg_window_get_browse (window));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
      break;


@@ 141,6 144,15 @@ vorg_window_class_init (VorgWindowClass *klass)
  window_props[PROP_EDIT] = g_param_spec_object (
      "edit", NULL, NULL, VORG_TYPE_EDIT, G_PARAM_READABLE);

  /**
   * VorgWindow:browse: (attributes org.gtk.Property.get=vorg_window_get_browse)
   * 
   * Get the embedded `VorgBrowse`.
   * Only relavent if `window_state` is `VORG_WINDOW_BROWSE`.
   */
  window_props[PROP_BROWSE] = g_param_spec_object (
      "browse", NULL, NULL, VORG_TYPE_BROWSE, G_PARAM_READABLE);

  g_object_class_install_properties (gobject_class, NUM_PROPERTIES,
                                     window_props);
}


@@ 216,6 228,19 @@ vorg_window_get_edit (VorgWindow *self)
}

/**
 * vorg_window_get_browse:
 *
 * Gets the `VorgBrowse` of the current window.
 *
 * Returns: (transfer none): The `VorgBrowse`.
 */
VorgBrowse *
vorg_window_get_browse (VorgWindow *self)
{
  return self->browse_box;
}

/**
 * vorg_window_show_toast:
 * @toast: A `AdwToast` to be displayed.
 *

M src/vorg-window.h => src/vorg-window.h +3 -0
@@ 18,6 18,7 @@

#pragma once

#include "vorg-browse.h"
#include "vorg-edit.h"

#include <adwaita.h>


@@ 49,6 50,8 @@ void vorg_window_set_state (VorgWindow *self, VorgWindowState state);

VorgEdit *vorg_window_get_edit (VorgWindow *self);

VorgBrowse *vorg_window_get_browse (VorgWindow *self);

void vorg_window_show_toast (VorgWindow *self, AdwToast *toast);

G_END_DECLS