Consolidate indexer struct header definitions, implement artist, album, and file inserts.
Tweaked database schema. Ensure we enable FOREIGN KEYS. Refactored how and when we are doing cover art and track fetching for an album.
This commit is contained in:
parent
8f50d388bd
commit
7566fb39f9
21 changed files with 585 additions and 282 deletions
19
src/db/db.c
19
src/db/db.c
|
@ -29,9 +29,9 @@ void close_db() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_db_tables() {
|
void create_db_tables() {
|
||||||
char *tables_creation_queries = "CREATE TABLE IF NOT EXISTS artists(id string UNIQUE, path string UNIQUE, type int, name string, art_path string);"
|
char *tables_creation_queries = "CREATE TABLE IF NOT EXISTS artists(id string UNIQUE PRIMARY KEY, path string, type int, name string, art_path string);"
|
||||||
"CREATE TABLE IF NOT EXISTS albums(id string UNIQUE, path string UNIQUE, artist_id int, name string, art_path string);"
|
"CREATE TABLE IF NOT EXISTS albums(id string UNIQUE PRIMARY KEY, path string, artist_id string, name string, art_path string, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
||||||
"CREATE TABLE IF NOT EXISTS tracks(id string UNIQUE, path string UNIQUE, type int, artist_id int, album_id int, file_name string, name string, disc int, position int);";
|
"CREATE TABLE IF NOT EXISTS tracks(id string UNIQUE PRIMARY KEY, path string, type int, artist_id string, album_id string, file_name string, name string, disc int, position int, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);";
|
||||||
|
|
||||||
gchar *create_tables_errmsg = NULL;
|
gchar *create_tables_errmsg = NULL;
|
||||||
int rc = sqlite3_exec(koto_db, tables_creation_queries, 0,0, &create_tables_errmsg);
|
int rc = sqlite3_exec(koto_db, tables_creation_queries, 0,0, &create_tables_errmsg);
|
||||||
|
@ -41,6 +41,17 @@ void create_db_tables() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enable_foreign_keys() {
|
||||||
|
gchar *enable_foreign_keys_err = NULL;
|
||||||
|
int rc = sqlite3_exec(koto_db, "PRAGMA foreign_keys = ON;", 0, 0, &enable_foreign_keys_err);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
g_critical("Failed to enable foreign key support. Ensure your sqlite3 is compiled with neither SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined: %s", enable_foreign_keys_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(enable_foreign_keys_err);
|
||||||
|
}
|
||||||
|
|
||||||
void open_db() {
|
void open_db() {
|
||||||
const gchar *data_home = g_get_user_data_dir();
|
const gchar *data_home = g_get_user_data_dir();
|
||||||
gchar *data_dir = g_build_path(G_DIR_SEPARATOR_S, data_home, "com.github.joshstrobl.koto", NULL);
|
gchar *data_dir = g_build_path(G_DIR_SEPARATOR_S, data_home, "com.github.joshstrobl.koto", NULL);
|
||||||
|
@ -56,5 +67,7 @@ void open_db() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enable_foreign_keys(); // Enable FOREIGN KEY support
|
||||||
|
|
||||||
create_db_tables(); // Attempt to create our database tables
|
create_db_tables(); // Attempt to create our database tables
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,5 +18,6 @@
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
void close_db();
|
void close_db();
|
||||||
|
void enable_foreign_keys();
|
||||||
void open_db();
|
void open_db();
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,17 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "album.h"
|
#include "structs.h"
|
||||||
#include "file.h"
|
|
||||||
#include "koto-utils.h"
|
#include "koto-utils.h"
|
||||||
|
|
||||||
|
extern sqlite3 *koto_db;
|
||||||
|
|
||||||
struct _KotoIndexedAlbum {
|
struct _KotoIndexedAlbum {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
KotoIndexedArtist *artist;
|
||||||
|
gchar *uuid;
|
||||||
gchar *path;
|
gchar *path;
|
||||||
|
|
||||||
gchar *name;
|
gchar *name;
|
||||||
|
@ -31,12 +35,16 @@ struct _KotoIndexedAlbum {
|
||||||
GHashTable *files;
|
GHashTable *files;
|
||||||
|
|
||||||
gboolean has_album_art;
|
gboolean has_album_art;
|
||||||
|
gboolean do_initial_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE(KotoIndexedAlbum, koto_indexed_album, G_TYPE_OBJECT);
|
G_DEFINE_TYPE(KotoIndexedAlbum, koto_indexed_album, G_TYPE_OBJECT);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
|
PROP_ARTIST,
|
||||||
|
PROP_UUID,
|
||||||
|
PROP_DO_INITIAL_INDEX,
|
||||||
PROP_PATH,
|
PROP_PATH,
|
||||||
PROP_ALBUM_NAME,
|
PROP_ALBUM_NAME,
|
||||||
PROP_ART_PATH,
|
PROP_ART_PATH,
|
||||||
|
@ -54,6 +62,30 @@ static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
|
||||||
gobject_class->set_property = koto_indexed_album_set_property;
|
gobject_class->set_property = koto_indexed_album_set_property;
|
||||||
gobject_class->get_property = koto_indexed_album_get_property;
|
gobject_class->get_property = koto_indexed_album_get_property;
|
||||||
|
|
||||||
|
props[PROP_ARTIST] = g_param_spec_object(
|
||||||
|
"artist",
|
||||||
|
"Artist associated with Album",
|
||||||
|
"Artist associated with Album",
|
||||||
|
KOTO_TYPE_INDEXED_ARTIST,
|
||||||
|
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
props[PROP_UUID] = g_param_spec_string(
|
||||||
|
"uuid",
|
||||||
|
"UUID to Album in database",
|
||||||
|
"UUID to Album in database",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
props[PROP_DO_INITIAL_INDEX] = g_param_spec_boolean(
|
||||||
|
"do-initial-index",
|
||||||
|
"Do an initial indexing operating instead of pulling from the database",
|
||||||
|
"Do an initial indexing operating instead of pulling from the database",
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
props[PROP_PATH] = g_param_spec_string(
|
props[PROP_PATH] = g_param_spec_string(
|
||||||
"path",
|
"path",
|
||||||
"Path",
|
"Path",
|
||||||
|
@ -105,12 +137,203 @@ void koto_indexed_album_add_file(KotoIndexedAlbum *self, KotoIndexedFile *file)
|
||||||
g_hash_table_insert(self->files, file_name, file); // Add the file
|
g_hash_table_insert(self->files, file_name, file); // Add the file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_indexed_album_commit(KotoIndexedAlbum *self) {
|
||||||
|
gchar *artist_uuid;
|
||||||
|
g_object_get(self->artist, "uuid", &artist_uuid, NULL); // Get the artist UUID
|
||||||
|
|
||||||
|
if (self->art_path == NULL) { // If art_path isn't defined when committing
|
||||||
|
koto_indexed_album_set_album_art(self, ""); // Set to an empty string
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *commit_op = g_strdup_printf(
|
||||||
|
"INSERT INTO albums(id, path, artist_id, name, art_path)"
|
||||||
|
"VALUES('%s', quote(\"%s\"), '%s', quote(\"%s\"), quote(\"%s\"))"
|
||||||
|
"ON CONFLICT(id) DO UPDATE SET path=excluded.path, name=excluded.name, art_path=excluded.art_path;",
|
||||||
|
self->uuid,
|
||||||
|
self->path,
|
||||||
|
artist_uuid,
|
||||||
|
self->name,
|
||||||
|
self->art_path
|
||||||
|
);
|
||||||
|
|
||||||
|
g_debug("INSERT query for album: %s", commit_op);
|
||||||
|
|
||||||
|
gchar *commit_op_errmsg = NULL;
|
||||||
|
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
g_warning("Failed to write our album to the database: %s", commit_op_errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(commit_op);
|
||||||
|
g_free(commit_op_errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_indexed_album_find_album_art(KotoIndexedAlbum *self) {
|
||||||
|
magic_t magic_cookie = magic_open(MAGIC_MIME);
|
||||||
|
|
||||||
|
if (magic_cookie == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magic_load(magic_cookie, NULL) != 0) {
|
||||||
|
magic_close(magic_cookie);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIR *dir = opendir(self->path); // Attempt to open our directory
|
||||||
|
|
||||||
|
if (dir == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
while ((entry = readdir(dir))) {
|
||||||
|
if (entry->d_type != DT_REG) { // Not a regular file
|
||||||
|
continue; // SKIP
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item
|
||||||
|
continue; // Skip
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *full_path = g_strdup_printf("%s%s%s", self->path, G_DIR_SEPARATOR_S, entry->d_name);
|
||||||
|
|
||||||
|
const char *mime_type = magic_file(magic_cookie, full_path);
|
||||||
|
|
||||||
|
if (mime_type == NULL) { // Failed to get the mimetype
|
||||||
|
g_free(full_path);
|
||||||
|
continue; // Skip
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_str_has_prefix(mime_type, "image/") && !self->has_album_art) { // Is an image file and doesn't have album art yet
|
||||||
|
gchar *album_art_no_ext = g_strdup(koto_utils_get_filename_without_extension(entry->d_name)); // Get the name of the file without the extension
|
||||||
|
gchar *lower_art = g_strdup(g_utf8_strdown(album_art_no_ext, -1)); // Lowercase
|
||||||
|
|
||||||
|
if (
|
||||||
|
(g_strrstr(lower_art, "Small") == NULL) && // Not Small
|
||||||
|
(g_strrstr(lower_art, "back") == NULL) // Not back
|
||||||
|
) {
|
||||||
|
koto_indexed_album_set_album_art(self, full_path);
|
||||||
|
g_free(album_art_no_ext);
|
||||||
|
g_free(lower_art);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(album_art_no_ext);
|
||||||
|
g_free(lower_art);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
magic_close(magic_cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie, const gchar *path) {
|
||||||
|
if (magic_cookie == NULL) { // No cookie provided
|
||||||
|
magic_cookie = magic_open(MAGIC_MIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magic_cookie == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == NULL) {
|
||||||
|
path = self->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magic_load(magic_cookie, NULL) != 0) {
|
||||||
|
magic_close(magic_cookie);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIR *dir = opendir(path); // Attempt to open our directory
|
||||||
|
|
||||||
|
if (dir == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
while ((entry = readdir(dir))) {
|
||||||
|
if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item
|
||||||
|
continue; // Skip
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name);
|
||||||
|
|
||||||
|
if (entry->d_type == DT_DIR) { // If this is a directory
|
||||||
|
koto_indexed_album_find_tracks(self, magic_cookie, full_path); // Recursively find tracks
|
||||||
|
g_free(full_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->d_type != DT_REG) { // Not a regular file
|
||||||
|
continue; // SKIP
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mime_type = magic_file(magic_cookie, full_path);
|
||||||
|
|
||||||
|
if (mime_type == NULL) { // Failed to get the mimetype
|
||||||
|
g_free(full_path);
|
||||||
|
continue; // Skip
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_str_has_prefix(mime_type, "audio/") || g_str_has_prefix(mime_type, "video/ogg")) { // Is an audio file or ogg because it is special
|
||||||
|
gchar *appended_slash_to_path = g_strdup_printf("%s%s", g_strdup(self->path), G_DIR_SEPARATOR_S);
|
||||||
|
gchar **possible_cd_split = g_strsplit(full_path, appended_slash_to_path, -1); // Split based on the album path
|
||||||
|
guint *cd = (guint*) 1;
|
||||||
|
|
||||||
|
gchar *file_with_cd_sep = g_strdup(possible_cd_split[1]); // Duplicate
|
||||||
|
gchar **split_on_cd = g_strsplit(file_with_cd_sep, G_DIR_SEPARATOR_S, -1); // Split based on separator (e.g. / )
|
||||||
|
|
||||||
|
if (g_strv_length(split_on_cd) > 1) {
|
||||||
|
gchar *cdd = g_strdup(split_on_cd[0]);
|
||||||
|
gchar **cd_sep = g_strsplit(g_utf8_strdown(cdd, -1), "cd", -1);
|
||||||
|
|
||||||
|
if (g_strv_length(cd_sep) > 1) {
|
||||||
|
gchar *pos_str = g_strdup(cd_sep[1]);
|
||||||
|
cd = (guint*) g_ascii_strtoull(pos_str, NULL, 10); // Attempt to convert
|
||||||
|
g_free(pos_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(cd_sep);
|
||||||
|
g_free(cdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(split_on_cd);
|
||||||
|
g_free(file_with_cd_sep);
|
||||||
|
|
||||||
|
g_strfreev(possible_cd_split);
|
||||||
|
g_free(appended_slash_to_path);
|
||||||
|
|
||||||
|
KotoIndexedFile *file = koto_indexed_file_new(self, full_path, cd);
|
||||||
|
|
||||||
|
if (file != NULL) { // Is a file
|
||||||
|
koto_indexed_album_add_file(self, file); // Add our file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(full_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void koto_indexed_album_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
|
static void koto_indexed_album_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
|
||||||
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj);
|
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
|
case PROP_ARTIST:
|
||||||
|
g_value_set_object(val, self->artist);
|
||||||
|
break;
|
||||||
|
case PROP_UUID:
|
||||||
|
g_value_set_string(val, self->uuid);
|
||||||
|
break;
|
||||||
|
case PROP_DO_INITIAL_INDEX:
|
||||||
|
g_value_set_boolean(val, self->do_initial_index);
|
||||||
|
break;
|
||||||
case PROP_PATH:
|
case PROP_PATH:
|
||||||
g_value_set_string(val, self->path);
|
g_value_set_string(val, self->path);
|
||||||
break;
|
break;
|
||||||
|
@ -148,93 +371,11 @@ void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album
|
||||||
}
|
}
|
||||||
|
|
||||||
self->art_path = g_strdup(album_art);
|
self->art_path = g_strdup(album_art);
|
||||||
|
|
||||||
|
g_message("Set album art to %s", album_art);
|
||||||
self->has_album_art = TRUE;
|
self->has_album_art = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_indexed_album_read_path(KotoIndexedAlbum *self, magic_t cookie, const gchar* path) {
|
|
||||||
DIR *dir = opendir(path); // Attempt to open our directory
|
|
||||||
|
|
||||||
if (dir == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dirent *entry;
|
|
||||||
|
|
||||||
while ((entry = readdir(dir))) {
|
|
||||||
if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item
|
|
||||||
continue; // Skip
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar *full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name);
|
|
||||||
|
|
||||||
if (entry->d_type == DT_DIR) { // If this is a directory
|
|
||||||
koto_indexed_album_read_path(self, cookie, full_path); // Read this directory as well
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->d_type != DT_REG) { // If this is not a regular file
|
|
||||||
continue; // Skip
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mime_type = magic_file(cookie, full_path);
|
|
||||||
|
|
||||||
if (mime_type == NULL) { // Failed to get the mimetype
|
|
||||||
g_free(full_path);
|
|
||||||
continue; // Skip
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_str_has_prefix(mime_type, "image/") && !self->has_album_art) { // Is an image file and doesn't have album art yet
|
|
||||||
gchar *album_art_no_ext = g_strdup(koto_utils_get_filename_without_extension(entry->d_name)); // Get the name of the file without the extension
|
|
||||||
gchar *lower_art = g_strdup(g_utf8_strdown(album_art_no_ext, -1)); // Lowercase
|
|
||||||
|
|
||||||
if (
|
|
||||||
(g_strrstr(lower_art, "Small") == NULL) && // Not Small
|
|
||||||
(g_strrstr(lower_art, "back") == NULL) // Not back
|
|
||||||
) {
|
|
||||||
koto_indexed_album_set_album_art(self, full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(album_art_no_ext);
|
|
||||||
g_free(lower_art);
|
|
||||||
} else if (g_str_has_prefix(mime_type, "audio/") || g_str_has_prefix(mime_type, "video/ogg")) { // Is an audio file or ogg because it is special
|
|
||||||
gchar *appended_slash_to_path = g_strdup_printf("%s%s", g_strdup(self->path), G_DIR_SEPARATOR_S);
|
|
||||||
gchar **possible_cd_split = g_strsplit(full_path, appended_slash_to_path, -1); // Split based on the album path
|
|
||||||
guint *cd = (guint*) 1;
|
|
||||||
|
|
||||||
gchar *file_with_cd_sep = g_strdup(possible_cd_split[1]); // Duplicate
|
|
||||||
gchar **split_on_cd = g_strsplit(file_with_cd_sep, G_DIR_SEPARATOR_S, -1); // Split based on separator (e.g. / )
|
|
||||||
|
|
||||||
if (g_strv_length(split_on_cd) > 1) {
|
|
||||||
gchar *cdd = g_strdup(split_on_cd[0]);
|
|
||||||
gchar **cd_sep = g_strsplit(g_utf8_strdown(cdd, -1), "cd", -1);
|
|
||||||
|
|
||||||
if (g_strv_length(cd_sep) > 1) {
|
|
||||||
gchar *pos_str = g_strdup(cd_sep[1]);
|
|
||||||
cd = (guint*) g_ascii_strtoull(pos_str, NULL, 10); // Attempt to convert
|
|
||||||
g_free(pos_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev(cd_sep);
|
|
||||||
g_free(cdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev(split_on_cd);
|
|
||||||
g_free(file_with_cd_sep);
|
|
||||||
|
|
||||||
g_strfreev(possible_cd_split);
|
|
||||||
g_free(appended_slash_to_path);
|
|
||||||
|
|
||||||
KotoIndexedFile *file = koto_indexed_file_new(full_path, cd);
|
|
||||||
|
|
||||||
if (file != NULL) { // Is a file
|
|
||||||
koto_indexed_album_add_file(self, file); // Add our file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(full_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedFile *file) {
|
void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedFile *file) {
|
||||||
if (file == NULL) { // Not a file
|
if (file == NULL) { // Not a file
|
||||||
return;
|
return;
|
||||||
|
@ -274,6 +415,16 @@ static void koto_indexed_album_set_property(GObject *obj, guint prop_id, const G
|
||||||
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj);
|
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
|
case PROP_ARTIST:
|
||||||
|
self->artist = (KotoIndexedArtist*) g_value_get_object(val);
|
||||||
|
break;
|
||||||
|
case PROP_UUID:
|
||||||
|
self->uuid = g_strdup(g_value_get_string(val));
|
||||||
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_UUID]);
|
||||||
|
break;
|
||||||
|
case PROP_DO_INITIAL_INDEX:
|
||||||
|
self->do_initial_index = g_value_get_boolean(val);
|
||||||
|
break;
|
||||||
case PROP_PATH: // Path to the album
|
case PROP_PATH: // Path to the album
|
||||||
koto_indexed_album_update_path(self, g_value_get_string(val));
|
koto_indexed_album_update_path(self, g_value_get_string(val));
|
||||||
break;
|
break;
|
||||||
|
@ -294,12 +445,6 @@ void koto_indexed_album_update_path(KotoIndexedAlbum *self, const gchar* new_pat
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DIR *dir = opendir(new_path); // Attempt to open our directory
|
|
||||||
|
|
||||||
if (dir == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->path != NULL) {
|
if (self->path != NULL) {
|
||||||
g_free(self->path);
|
g_free(self->path);
|
||||||
}
|
}
|
||||||
|
@ -308,25 +453,33 @@ void koto_indexed_album_update_path(KotoIndexedAlbum *self, const gchar* new_pat
|
||||||
|
|
||||||
koto_indexed_album_set_album_name(self, g_path_get_basename(self->path)); // Update our album name based on the base name
|
koto_indexed_album_set_album_name(self, g_path_get_basename(self->path)); // Update our album name based on the base name
|
||||||
|
|
||||||
magic_t magic_cookie = magic_open(MAGIC_MIME);
|
if (!self->do_initial_index) { // Not doing our initial index
|
||||||
|
|
||||||
if (magic_cookie == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (magic_load(magic_cookie, NULL) != 0) {
|
koto_indexed_album_find_album_art(self); // Update our path for the album art
|
||||||
magic_close(magic_cookie);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
koto_indexed_album_read_path(self, magic_cookie, self->path);
|
KotoIndexedAlbum* koto_indexed_album_new(KotoIndexedArtist *artist, const gchar *path) {
|
||||||
|
KotoIndexedAlbum* album = g_object_new(KOTO_TYPE_INDEXED_ALBUM,
|
||||||
magic_close(magic_cookie);
|
"artist", artist,
|
||||||
}
|
"uuid", g_strdup(g_uuid_string_random()),
|
||||||
|
"do-initial-index", TRUE,
|
||||||
KotoIndexedAlbum* koto_indexed_album_new(const gchar *path) {
|
|
||||||
return g_object_new(KOTO_TYPE_INDEXED_ALBUM,
|
|
||||||
"path", path,
|
"path", path,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
koto_indexed_album_commit(album);
|
||||||
|
koto_indexed_album_find_tracks(album, NULL, NULL); // Scan for tracks now that we committed to the database (hopefully)
|
||||||
|
|
||||||
|
return album;
|
||||||
|
}
|
||||||
|
|
||||||
|
KotoIndexedAlbum* koto_indexed_album_new_with_uuid(KotoIndexedArtist *artist, const gchar *uuid) {
|
||||||
|
return g_object_new(KOTO_TYPE_INDEXED_ALBUM,
|
||||||
|
"artist", artist,
|
||||||
|
"uuid", uuid,
|
||||||
|
"do-initial-index", FALSE,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/* album.h
|
|
||||||
*
|
|
||||||
* Copyright 2021 Joshua Strobl
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <glib-2.0/glib-object.h>
|
|
||||||
#include "file.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define KOTO_TYPE_INDEXED_ALBUM koto_indexed_album_get_type()
|
|
||||||
G_DECLARE_FINAL_TYPE (KotoIndexedAlbum, koto_indexed_album, KOTO, INDEXED_ALBUM, GObject);
|
|
||||||
|
|
||||||
KotoIndexedAlbum* koto_indexed_album_new(const gchar *path);
|
|
||||||
|
|
||||||
void koto_indexed_album_add_file(KotoIndexedAlbum *self, KotoIndexedFile *file);
|
|
||||||
gchar* koto_indexed_album_get_album_art(KotoIndexedAlbum *self);
|
|
||||||
GList* koto_indexed_album_get_files(KotoIndexedAlbum *self);
|
|
||||||
void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedFile *file);
|
|
||||||
void koto_indexed_album_remove_file_by_name(KotoIndexedAlbum *self, const gchar *file_name);
|
|
||||||
void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album_art);
|
|
||||||
void koto_indexed_album_set_album_name(KotoIndexedAlbum *self, const gchar *album_name);
|
|
||||||
void koto_indexed_album_update_path(KotoIndexedAlbum *self, const gchar *path);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
|
@ -16,11 +16,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
#include "album.h"
|
#include <sqlite3.h>
|
||||||
#include "artist.h"
|
#include "structs.h"
|
||||||
|
#include "../db/db.h"
|
||||||
|
|
||||||
|
extern sqlite3 *koto_db;
|
||||||
|
|
||||||
struct _KotoIndexedArtist {
|
struct _KotoIndexedArtist {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
gchar *uuid;
|
||||||
gchar *path;
|
gchar *path;
|
||||||
|
|
||||||
gboolean has_artist_art;
|
gboolean has_artist_art;
|
||||||
|
@ -32,6 +36,7 @@ G_DEFINE_TYPE(KotoIndexedArtist, koto_indexed_artist, G_TYPE_OBJECT);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
|
PROP_UUID,
|
||||||
PROP_PATH,
|
PROP_PATH,
|
||||||
PROP_ARTIST_NAME,
|
PROP_ARTIST_NAME,
|
||||||
N_PROPERTIES
|
N_PROPERTIES
|
||||||
|
@ -48,6 +53,14 @@ static void koto_indexed_artist_class_init(KotoIndexedArtistClass *c) {
|
||||||
gobject_class->set_property = koto_indexed_artist_set_property;
|
gobject_class->set_property = koto_indexed_artist_set_property;
|
||||||
gobject_class->get_property = koto_indexed_artist_get_property;
|
gobject_class->get_property = koto_indexed_artist_get_property;
|
||||||
|
|
||||||
|
props[PROP_UUID] = g_param_spec_string(
|
||||||
|
"uuid",
|
||||||
|
"UUID to Artist in database",
|
||||||
|
"UUID to Artist in database",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
props[PROP_PATH] = g_param_spec_string(
|
props[PROP_PATH] = g_param_spec_string(
|
||||||
"path",
|
"path",
|
||||||
"Path",
|
"Path",
|
||||||
|
@ -67,6 +80,34 @@ static void koto_indexed_artist_class_init(KotoIndexedArtistClass *c) {
|
||||||
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_indexed_artist_commit(KotoIndexedArtist *self) {
|
||||||
|
if ((self->uuid == NULL) || strcmp(self->uuid, "")) { // UUID not set
|
||||||
|
self->uuid = g_strdup(g_uuid_string_random());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Support multiple types instead of just local music artist
|
||||||
|
gchar *commit_op = g_strdup_printf(
|
||||||
|
"INSERT INTO artists(id,path,type,name,art_path)"
|
||||||
|
"VALUES ('%s', quote(\"%s\"), 0, quote(\"%s\"), NULL)"
|
||||||
|
"ON CONFLICT(id) DO UPDATE SET path=excluded.path, type=excluded.type, name=excluded.name, art_path=excluded.art_path;",
|
||||||
|
self->uuid,
|
||||||
|
self->path,
|
||||||
|
self->artist_name
|
||||||
|
);
|
||||||
|
|
||||||
|
g_debug("INSERT query for artist: %s", commit_op);
|
||||||
|
|
||||||
|
gchar *commit_opt_errmsg = NULL;
|
||||||
|
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_opt_errmsg);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
g_warning("Failed to write our artist to the database: %s", commit_opt_errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(commit_op);
|
||||||
|
g_free(commit_opt_errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
static void koto_indexed_artist_init(KotoIndexedArtist *self) {
|
static void koto_indexed_artist_init(KotoIndexedArtist *self) {
|
||||||
self->has_artist_art = FALSE;
|
self->has_artist_art = FALSE;
|
||||||
self->albums = NULL; // Set to null initially maybe
|
self->albums = NULL; // Set to null initially maybe
|
||||||
|
@ -76,6 +117,9 @@ static void koto_indexed_artist_get_property(GObject *obj, guint prop_id, GValue
|
||||||
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj);
|
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
|
case PROP_UUID:
|
||||||
|
g_value_set_string(val, self->uuid);
|
||||||
|
break;
|
||||||
case PROP_PATH:
|
case PROP_PATH:
|
||||||
g_value_set_string(val, self->path);
|
g_value_set_string(val, self->path);
|
||||||
break;
|
break;
|
||||||
|
@ -92,6 +136,10 @@ static void koto_indexed_artist_set_property(GObject *obj, guint prop_id, const
|
||||||
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj);
|
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
|
case PROP_UUID:
|
||||||
|
self->uuid = g_strdup(g_value_get_string(val));
|
||||||
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_UUID]);
|
||||||
|
break;
|
||||||
case PROP_PATH:
|
case PROP_PATH:
|
||||||
koto_indexed_artist_update_path(self, g_value_get_string(val));
|
koto_indexed_artist_update_path(self, g_value_get_string(val));
|
||||||
break;
|
break;
|
||||||
|
@ -188,9 +236,20 @@ void koto_indexed_artist_set_artist_name(KotoIndexedArtist *self, const gchar *a
|
||||||
}
|
}
|
||||||
|
|
||||||
KotoIndexedArtist* koto_indexed_artist_new(const gchar *path) {
|
KotoIndexedArtist* koto_indexed_artist_new(const gchar *path) {
|
||||||
return g_object_new(KOTO_TYPE_INDEXED_ARTIST,
|
KotoIndexedArtist* artist = g_object_new(KOTO_TYPE_INDEXED_ARTIST,
|
||||||
|
"uuid", g_uuid_string_random(),
|
||||||
"path", path,
|
"path", path,
|
||||||
"name", g_path_get_basename(path),
|
"name", g_path_get_basename(path),
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
koto_indexed_artist_commit(artist); // Commit the artist immediately to the database
|
||||||
|
return artist;
|
||||||
|
}
|
||||||
|
|
||||||
|
KotoIndexedArtist* koto_indexed_artist_new_with_uuid(const gchar *uuid) {
|
||||||
|
return g_object_new(KOTO_TYPE_INDEXED_ARTIST,
|
||||||
|
"uuid", uuid,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/* artist.h
|
|
||||||
*
|
|
||||||
* Copyright 2021 Joshua Strobl
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <glib-2.0/glib-object.h>
|
|
||||||
#include "album.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define KOTO_TYPE_INDEXED_ARTIST koto_indexed_artist_get_type()
|
|
||||||
G_DECLARE_FINAL_TYPE (KotoIndexedArtist, koto_indexed_artist, KOTO, INDEXED_ARTIST, GObject);
|
|
||||||
|
|
||||||
KotoIndexedArtist* koto_indexed_artist_new(const gchar *path);
|
|
||||||
|
|
||||||
void koto_indexed_artist_add_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
|
|
||||||
guint koto_indexed_artist_find_album_with_name(gconstpointer *album_data, gconstpointer *album_name_data);
|
|
||||||
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self);
|
|
||||||
void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
|
|
||||||
void koto_indexed_artist_remove_album_by_name(KotoIndexedArtist *self, gchar *album_name);
|
|
||||||
void koto_indexed_artist_set_artist_name(KotoIndexedArtist *self, const gchar *artist_name);
|
|
||||||
void koto_indexed_artist_update_path(KotoIndexedArtist *self, const gchar *new_path);
|
|
||||||
void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
|
@ -20,10 +20,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <taglib/tag_c.h>
|
#include <taglib/tag_c.h>
|
||||||
#include "album.h"
|
#include "structs.h"
|
||||||
#include "artist.h"
|
|
||||||
#include "file.h"
|
|
||||||
#include "file-indexer.h"
|
|
||||||
|
|
||||||
struct _KotoIndexedLibrary {
|
struct _KotoIndexedLibrary {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
@ -205,7 +202,6 @@ void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth) {
|
||||||
g_free(artist_name);
|
g_free(artist_name);
|
||||||
} else if (depth == 2) { // If we are following FOLDER/ARTIST/ALBUM then this would be album
|
} else if (depth == 2) { // If we are following FOLDER/ARTIST/ALBUM then this would be album
|
||||||
gchar *artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist
|
gchar *artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist
|
||||||
KotoIndexedAlbum *album = koto_indexed_album_new(full_path);
|
|
||||||
|
|
||||||
KotoIndexedArtist *artist = koto_indexed_library_get_artist(self, artist_name); // Get the artist
|
KotoIndexedArtist *artist = koto_indexed_library_get_artist(self, artist_name); // Get the artist
|
||||||
|
|
||||||
|
@ -213,6 +209,8 @@ void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KotoIndexedAlbum *album = koto_indexed_album_new(artist, full_path);
|
||||||
|
|
||||||
koto_indexed_artist_add_album(artist, album); // Add the album
|
koto_indexed_artist_add_album(artist, album); // Add the album
|
||||||
g_free(artist_name);
|
g_free(artist_name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
/* file-indexer.h
|
|
||||||
*
|
|
||||||
* Copyright 2021 Joshua Strobl
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <glib-2.0/glib-object.h>
|
|
||||||
#include "artist.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define KOTO_TYPE_INDEXED_LIBRARY koto_indexed_library_get_type()
|
|
||||||
G_DECLARE_FINAL_TYPE(KotoIndexedLibrary, koto_indexed_library, KOTO, INDEXED_LIBRARY, GObject);
|
|
||||||
|
|
||||||
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path);
|
|
||||||
|
|
||||||
void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist);
|
|
||||||
KotoIndexedArtist* koto_indexed_library_get_artist(KotoIndexedLibrary *self, gchar* artist_name);
|
|
||||||
GHashTable* koto_indexed_library_get_artists(KotoIndexedLibrary *self);
|
|
||||||
void koto_indexed_library_remove_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist);
|
|
||||||
void start_indexing(KotoIndexedLibrary *self);
|
|
||||||
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
|
@ -16,12 +16,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
#include <taglib/tag_c.h>
|
#include <taglib/tag_c.h>
|
||||||
#include "file.h"
|
#include "structs.h"
|
||||||
#include "koto-utils.h"
|
#include "koto-utils.h"
|
||||||
|
|
||||||
|
extern sqlite3 *koto_db;
|
||||||
|
|
||||||
struct _KotoIndexedFile {
|
struct _KotoIndexedFile {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
gchar *artist_uuid;
|
||||||
|
gchar *album_uuid;
|
||||||
|
gchar *uuid;
|
||||||
gchar *path;
|
gchar *path;
|
||||||
|
|
||||||
gchar *file_name;
|
gchar *file_name;
|
||||||
|
@ -30,13 +36,19 @@ struct _KotoIndexedFile {
|
||||||
gchar *album;
|
gchar *album;
|
||||||
guint *cd;
|
guint *cd;
|
||||||
guint *position;
|
guint *position;
|
||||||
|
|
||||||
gboolean acquired_metadata_from_id3;
|
gboolean acquired_metadata_from_id3;
|
||||||
|
gboolean do_initial_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE(KotoIndexedFile, koto_indexed_file, G_TYPE_OBJECT);
|
G_DEFINE_TYPE(KotoIndexedFile, koto_indexed_file, G_TYPE_OBJECT);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
|
PROP_ARTIST_UUID,
|
||||||
|
PROP_ALBUM_UUID,
|
||||||
|
PROP_UUID,
|
||||||
|
PROP_DO_INITIAL_INDEX,
|
||||||
PROP_PATH,
|
PROP_PATH,
|
||||||
PROP_ARTIST,
|
PROP_ARTIST,
|
||||||
PROP_ALBUM,
|
PROP_ALBUM,
|
||||||
|
@ -58,6 +70,38 @@ static void koto_indexed_file_class_init(KotoIndexedFileClass *c) {
|
||||||
gobject_class->set_property = koto_indexed_file_set_property;
|
gobject_class->set_property = koto_indexed_file_set_property;
|
||||||
gobject_class->get_property = koto_indexed_file_get_property;
|
gobject_class->get_property = koto_indexed_file_get_property;
|
||||||
|
|
||||||
|
props[PROP_ARTIST_UUID] = g_param_spec_string(
|
||||||
|
"artist-uuid",
|
||||||
|
"UUID to Artist associated with the File",
|
||||||
|
"UUID to Artist associated with the File",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
props[PROP_ALBUM_UUID] = g_param_spec_string(
|
||||||
|
"album-uuid",
|
||||||
|
"UUID to Album associated with the File",
|
||||||
|
"UUID to Album associated with the File",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
props[PROP_UUID] = g_param_spec_string(
|
||||||
|
"uuid",
|
||||||
|
"UUID to File in database",
|
||||||
|
"UUID to File in database",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
props[PROP_DO_INITIAL_INDEX] = g_param_spec_boolean(
|
||||||
|
"do-initial-index",
|
||||||
|
"Do an initial indexing operating instead of pulling from the database",
|
||||||
|
"Do an initial indexing operating instead of pulling from the database",
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
props[PROP_PATH] = g_param_spec_string(
|
props[PROP_PATH] = g_param_spec_string(
|
||||||
"path",
|
"path",
|
||||||
"Path",
|
"Path",
|
||||||
|
@ -129,6 +173,15 @@ static void koto_indexed_file_get_property(GObject *obj, guint prop_id, GValue *
|
||||||
KotoIndexedFile *self = KOTO_INDEXED_FILE(obj);
|
KotoIndexedFile *self = KOTO_INDEXED_FILE(obj);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
|
case PROP_ARTIST_UUID:
|
||||||
|
g_value_set_string(val, self->artist_uuid);
|
||||||
|
break;
|
||||||
|
case PROP_ALBUM_UUID:
|
||||||
|
g_value_set_string(val, self->album_uuid);
|
||||||
|
break;
|
||||||
|
case PROP_UUID:
|
||||||
|
g_value_set_string(val, self->uuid);
|
||||||
|
break;
|
||||||
case PROP_PATH:
|
case PROP_PATH:
|
||||||
g_value_set_string(val, self->path);
|
g_value_set_string(val, self->path);
|
||||||
break;
|
break;
|
||||||
|
@ -160,6 +213,21 @@ static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GV
|
||||||
KotoIndexedFile *self = KOTO_INDEXED_FILE(obj);
|
KotoIndexedFile *self = KOTO_INDEXED_FILE(obj);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
|
case PROP_ARTIST_UUID:
|
||||||
|
self->artist_uuid = g_strdup(g_value_get_string(val));
|
||||||
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_UUID]);
|
||||||
|
break;
|
||||||
|
case PROP_ALBUM_UUID:
|
||||||
|
self->album_uuid = g_strdup(g_value_get_string(val));
|
||||||
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ALBUM_UUID]);
|
||||||
|
break;
|
||||||
|
case PROP_UUID:
|
||||||
|
self->uuid = g_strdup(g_value_get_string(val));
|
||||||
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_UUID]);
|
||||||
|
break;
|
||||||
|
case PROP_DO_INITIAL_INDEX:
|
||||||
|
self->do_initial_index = g_value_get_boolean(val);
|
||||||
|
break;
|
||||||
case PROP_PATH:
|
case PROP_PATH:
|
||||||
koto_indexed_file_update_path(self, g_value_get_string(val)); // Update the path
|
koto_indexed_file_update_path(self, g_value_get_string(val)); // Update the path
|
||||||
break;
|
break;
|
||||||
|
@ -187,6 +255,43 @@ static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GV
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void koto_indexed_file_commit(KotoIndexedFile *self) {
|
||||||
|
if ((self->artist_uuid == NULL) || (strcmp(self->artist_uuid, "") == 0)) { // No valid required artist UUID
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->album_uuid == NULL) {
|
||||||
|
g_object_set(self, "album-uuid", "", NULL); // Set to an empty string
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *commit_op = g_strdup_printf(
|
||||||
|
"INSERT INTO tracks(id, path, type, artist_id, album_id, file_name, name, disc, position)"
|
||||||
|
"VALUES('%s', quote(\"%s\"), 0, '%s', '%s', quote(\"%s\"), quote(\"%s\"), %d, %d)"
|
||||||
|
"ON CONFLICT(id) DO UPDATE SET path=excluded.path, type=excluded.type, album_id=excluded.album_id, file_name=excluded.file_name, name=excluded.file_name, disc=excluded.disc, position=excluded.position;",
|
||||||
|
self->uuid,
|
||||||
|
self->path,
|
||||||
|
self->artist_uuid,
|
||||||
|
self->album_uuid,
|
||||||
|
self->file_name,
|
||||||
|
self->parsed_name,
|
||||||
|
GPOINTER_TO_INT((int*) self->cd),
|
||||||
|
GPOINTER_TO_INT((int*) self->position)
|
||||||
|
);
|
||||||
|
|
||||||
|
g_debug("INSERT query for file: %s", commit_op);
|
||||||
|
|
||||||
|
gchar *commit_op_errmsg = NULL;
|
||||||
|
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
g_warning("Failed to write our file to the database: %s", commit_op_errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(commit_op);
|
||||||
|
g_free(commit_op_errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
void koto_indexed_file_parse_name(KotoIndexedFile *self) {
|
void koto_indexed_file_parse_name(KotoIndexedFile *self) {
|
||||||
gchar *copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters
|
gchar *copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters
|
||||||
|
|
||||||
|
@ -334,10 +439,32 @@ void koto_indexed_file_update_path(KotoIndexedFile *self, const gchar *new_path)
|
||||||
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]);
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]);
|
||||||
}
|
}
|
||||||
|
|
||||||
KotoIndexedFile* koto_indexed_file_new(const gchar *path, guint *cd) {
|
KotoIndexedFile* koto_indexed_file_new(KotoIndexedAlbum *album, const gchar *path, guint *cd) {
|
||||||
return g_object_new(KOTO_TYPE_INDEXED_FILE,
|
KotoIndexedArtist *artist;
|
||||||
|
|
||||||
|
gchar *artist_uuid;
|
||||||
|
gchar *album_uuid;
|
||||||
|
|
||||||
|
g_object_get(album, "artist", &artist, NULL); // Get the artist from our Album
|
||||||
|
g_object_get(artist, "uuid", &artist_uuid, NULL); // Get the artist UUID from the artist
|
||||||
|
g_object_get(album, "uuid", &album_uuid, NULL); // Get the album UUID from the album
|
||||||
|
|
||||||
|
KotoIndexedFile *file = g_object_new(KOTO_TYPE_INDEXED_FILE,
|
||||||
|
"artist-uuid", artist_uuid,
|
||||||
|
"album-uuid", album_uuid,
|
||||||
|
"uuid", g_uuid_string_random(),
|
||||||
"path", path,
|
"path", path,
|
||||||
"cd", cd,
|
"cd", cd,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
koto_indexed_file_commit(file); // Immediately commit to the database
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
KotoIndexedFile* koto_indexed_file_new_with_uuid(const gchar *uuid) {
|
||||||
|
return g_object_new(KOTO_TYPE_INDEXED_FILE,
|
||||||
|
"uuid", uuid,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
/* file.h
|
|
||||||
*
|
|
||||||
* Copyright 2021 Joshua Strobl
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <glib-2.0/glib-object.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define KOTO_TYPE_INDEXED_FILE koto_indexed_file_get_type()
|
|
||||||
G_DECLARE_FINAL_TYPE(KotoIndexedFile, koto_indexed_file, KOTO, INDEXED_FILE, GObject);
|
|
||||||
|
|
||||||
KotoIndexedFile* koto_indexed_file_new(const gchar *path, guint *cd);
|
|
||||||
|
|
||||||
void koto_indexed_file_parse_name(KotoIndexedFile *self);
|
|
||||||
void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name);
|
|
||||||
void koto_indexed_file_set_cd(KotoIndexedFile *self, guint cd);
|
|
||||||
void koto_indexed_file_set_parsed_name(KotoIndexedFile *self, gchar *new_parsed_name);
|
|
||||||
void koto_indexed_file_set_position(KotoIndexedFile *self, guint pos);
|
|
||||||
void koto_indexed_file_update_metadata(KotoIndexedFile *self);
|
|
||||||
void koto_indexed_file_update_path(KotoIndexedFile *self, const gchar *new_path);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
105
src/indexer/structs.h
Normal file
105
src/indexer/structs.h
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/* structs.h
|
||||||
|
*
|
||||||
|
* Copyright 2021 Joshua Strobl
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <glib-2.0/glib-object.h>
|
||||||
|
#include <magic.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type Definition
|
||||||
|
**/
|
||||||
|
|
||||||
|
#define KOTO_TYPE_INDEXED_LIBRARY koto_indexed_library_get_type()
|
||||||
|
G_DECLARE_FINAL_TYPE(KotoIndexedLibrary, koto_indexed_library, KOTO, INDEXED_LIBRARY, GObject);
|
||||||
|
|
||||||
|
#define KOTO_TYPE_INDEXED_ARTIST koto_indexed_artist_get_type()
|
||||||
|
G_DECLARE_FINAL_TYPE (KotoIndexedArtist, koto_indexed_artist, KOTO, INDEXED_ARTIST, GObject);
|
||||||
|
|
||||||
|
#define KOTO_TYPE_INDEXED_ALBUM koto_indexed_album_get_type()
|
||||||
|
G_DECLARE_FINAL_TYPE (KotoIndexedAlbum, koto_indexed_album, KOTO, INDEXED_ALBUM, GObject);
|
||||||
|
|
||||||
|
#define KOTO_TYPE_INDEXED_FILE koto_indexed_file_get_type()
|
||||||
|
G_DECLARE_FINAL_TYPE(KotoIndexedFile, koto_indexed_file, KOTO, INDEXED_FILE, GObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Library Functions
|
||||||
|
**/
|
||||||
|
|
||||||
|
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path);
|
||||||
|
|
||||||
|
void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist);
|
||||||
|
KotoIndexedArtist* koto_indexed_library_get_artist(KotoIndexedLibrary *self, gchar* artist_name);
|
||||||
|
GHashTable* koto_indexed_library_get_artists(KotoIndexedLibrary *self);
|
||||||
|
void koto_indexed_library_remove_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist);
|
||||||
|
void start_indexing(KotoIndexedLibrary *self);
|
||||||
|
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Artist Functions
|
||||||
|
**/
|
||||||
|
|
||||||
|
KotoIndexedArtist* koto_indexed_artist_new(const gchar *path);
|
||||||
|
KotoIndexedArtist* koto_indexed_artist_new_with_uuid(const gchar *uuid);
|
||||||
|
|
||||||
|
void koto_indexed_artist_add_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
|
||||||
|
void koto_indexed_artist_commit(KotoIndexedArtist *self);
|
||||||
|
guint koto_indexed_artist_find_album_with_name(gconstpointer *album_data, gconstpointer *album_name_data);
|
||||||
|
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self);
|
||||||
|
void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
|
||||||
|
void koto_indexed_artist_remove_album_by_name(KotoIndexedArtist *self, gchar *album_name);
|
||||||
|
void koto_indexed_artist_set_artist_name(KotoIndexedArtist *self, const gchar *artist_name);
|
||||||
|
void koto_indexed_artist_update_path(KotoIndexedArtist *self, const gchar *new_path);
|
||||||
|
void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Album Functions
|
||||||
|
**/
|
||||||
|
|
||||||
|
KotoIndexedAlbum* koto_indexed_album_new(KotoIndexedArtist *artist, const gchar *path);
|
||||||
|
KotoIndexedAlbum* koto_indexed_album_new_with_uuid(KotoIndexedArtist *artist, const gchar *uuid);
|
||||||
|
|
||||||
|
void koto_indexed_album_add_file(KotoIndexedAlbum *self, KotoIndexedFile *file);
|
||||||
|
void koto_indexed_album_commit(KotoIndexedAlbum *self);
|
||||||
|
void koto_indexed_album_find_album_art(KotoIndexedAlbum *self);
|
||||||
|
void koto_indexed_album_find_tracks(KotoIndexedAlbum *self, magic_t magic_cookie, const gchar *path);
|
||||||
|
gchar* koto_indexed_album_get_album_art(KotoIndexedAlbum *self);
|
||||||
|
GList* koto_indexed_album_get_files(KotoIndexedAlbum *self);
|
||||||
|
void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedFile *file);
|
||||||
|
void koto_indexed_album_remove_file_by_name(KotoIndexedAlbum *self, const gchar *file_name);
|
||||||
|
void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album_art);
|
||||||
|
void koto_indexed_album_set_album_name(KotoIndexedAlbum *self, const gchar *album_name);
|
||||||
|
void koto_indexed_album_update_path(KotoIndexedAlbum *self, const gchar *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File / Track Functions
|
||||||
|
**/
|
||||||
|
|
||||||
|
KotoIndexedFile* koto_indexed_file_new(KotoIndexedAlbum *album, const gchar *path, guint *cd);
|
||||||
|
KotoIndexedFile* koto_indexed_file_new_with_uuid(const gchar *uuid);
|
||||||
|
|
||||||
|
void koto_indexed_file_commit(KotoIndexedFile *self);
|
||||||
|
void koto_indexed_file_parse_name(KotoIndexedFile *self);
|
||||||
|
void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name);
|
||||||
|
void koto_indexed_file_set_cd(KotoIndexedFile *self, guint cd);
|
||||||
|
void koto_indexed_file_set_parsed_name(KotoIndexedFile *self, gchar *new_parsed_name);
|
||||||
|
void koto_indexed_file_set_position(KotoIndexedFile *self, guint pos);
|
||||||
|
void koto_indexed_file_update_metadata(KotoIndexedFile *self);
|
||||||
|
void koto_indexed_file_update_path(KotoIndexedFile *self, const gchar *new_path);
|
||||||
|
|
||||||
|
G_END_DECLS
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "indexer/file.h"
|
#include "indexer/structs.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtk-4.0/gdk/x11/gdkx.h>
|
#include <gtk-4.0/gdk/x11/gdkx.h>
|
||||||
#include "indexer/file-indexer.h"
|
#include "indexer/structs.h"
|
||||||
#include "pages/music/music-local.h"
|
#include "pages/music/music-local.h"
|
||||||
#include "koto-config.h"
|
#include "koto-config.h"
|
||||||
#include "koto-nav.h"
|
#include "koto-nav.h"
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/album.h"
|
#include "../../indexer/structs.h"
|
||||||
#include "../../indexer/artist.h"
|
|
||||||
#include "album-view.h"
|
#include "album-view.h"
|
||||||
#include "disc-view.h"
|
#include "disc-view.h"
|
||||||
#include "koto-config.h"
|
#include "koto-config.h"
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/album.h"
|
#include "../../indexer/structs.h"
|
||||||
#include "../../indexer/artist.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/album.h"
|
#include "../../indexer/structs.h"
|
||||||
#include "../../indexer/artist.h"
|
|
||||||
#include "album-view.h"
|
#include "album-view.h"
|
||||||
#include "artist-view.h"
|
#include "artist-view.h"
|
||||||
#include "koto-config.h"
|
#include "koto-config.h"
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/album.h"
|
#include "../../indexer/structs.h"
|
||||||
#include "../../indexer/artist.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/album.h"
|
#include "../../indexer/structs.h"
|
||||||
#include "../../koto-track-item.h"
|
#include "../../koto-track-item.h"
|
||||||
#include "disc-view.h"
|
#include "disc-view.h"
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/album.h"
|
#include "../../indexer/structs.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/file-indexer.h"
|
#include "../../indexer/structs.h"
|
||||||
#include "koto-button.h"
|
#include "koto-button.h"
|
||||||
#include "koto-config.h"
|
#include "koto-config.h"
|
||||||
#include "music-local.h"
|
#include "music-local.h"
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../indexer/artist.h"
|
#include "../../indexer/structs.h"
|
||||||
#include "../../indexer/file-indexer.h"
|
|
||||||
#include "artist-view.h"
|
#include "artist-view.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue