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
|
@ -17,13 +17,17 @@
|
|||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <magic.h>
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include "album.h"
|
||||
#include "file.h"
|
||||
#include "structs.h"
|
||||
#include "koto-utils.h"
|
||||
|
||||
extern sqlite3 *koto_db;
|
||||
|
||||
struct _KotoIndexedAlbum {
|
||||
GObject parent_instance;
|
||||
KotoIndexedArtist *artist;
|
||||
gchar *uuid;
|
||||
gchar *path;
|
||||
|
||||
gchar *name;
|
||||
|
@ -31,12 +35,16 @@ struct _KotoIndexedAlbum {
|
|||
GHashTable *files;
|
||||
|
||||
gboolean has_album_art;
|
||||
gboolean do_initial_index;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(KotoIndexedAlbum, koto_indexed_album, G_TYPE_OBJECT);
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ARTIST,
|
||||
PROP_UUID,
|
||||
PROP_DO_INITIAL_INDEX,
|
||||
PROP_PATH,
|
||||
PROP_ALBUM_NAME,
|
||||
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->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(
|
||||
"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
|
||||
}
|
||||
|
||||
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) {
|
||||
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj);
|
||||
|
||||
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:
|
||||
g_value_set_string(val, self->path);
|
||||
break;
|
||||
|
@ -148,93 +371,11 @@ void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album
|
|||
}
|
||||
|
||||
self->art_path = g_strdup(album_art);
|
||||
|
||||
g_message("Set album art to %s", album_art);
|
||||
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) {
|
||||
if (file == NULL) { // Not a file
|
||||
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);
|
||||
|
||||
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
|
||||
koto_indexed_album_update_path(self, g_value_get_string(val));
|
||||
break;
|
||||
|
@ -294,12 +445,6 @@ void koto_indexed_album_update_path(KotoIndexedAlbum *self, const gchar* new_pat
|
|||
return;
|
||||
}
|
||||
|
||||
DIR *dir = opendir(new_path); // Attempt to open our directory
|
||||
|
||||
if (dir == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->path != NULL) {
|
||||
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
|
||||
|
||||
magic_t magic_cookie = magic_open(MAGIC_MIME);
|
||||
|
||||
if (magic_cookie == NULL) {
|
||||
if (!self->do_initial_index) { // Not doing our initial index
|
||||
return;
|
||||
}
|
||||
|
||||
if (magic_load(magic_cookie, NULL) != 0) {
|
||||
magic_close(magic_cookie);
|
||||
return;
|
||||
}
|
||||
|
||||
koto_indexed_album_read_path(self, magic_cookie, self->path);
|
||||
|
||||
magic_close(magic_cookie);
|
||||
koto_indexed_album_find_album_art(self); // Update our path for the album art
|
||||
}
|
||||
|
||||
KotoIndexedAlbum* koto_indexed_album_new(const gchar *path) {
|
||||
return g_object_new(KOTO_TYPE_INDEXED_ALBUM,
|
||||
KotoIndexedAlbum* koto_indexed_album_new(KotoIndexedArtist *artist, const gchar *path) {
|
||||
KotoIndexedAlbum* album = g_object_new(KOTO_TYPE_INDEXED_ALBUM,
|
||||
"artist", artist,
|
||||
"uuid", g_strdup(g_uuid_string_random()),
|
||||
"do-initial-index", TRUE,
|
||||
"path", path,
|
||||
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
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue