2021-02-09 17:37:26 +02:00
|
|
|
/* file-indexer.c
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <magic.h>
|
2021-02-16 17:15:10 +02:00
|
|
|
#include <stdio.h>
|
2021-02-09 17:37:26 +02:00
|
|
|
#include <sys/stat.h>
|
2021-02-16 17:15:10 +02:00
|
|
|
#include <taglib/tag_c.h>
|
2021-03-23 19:50:09 +02:00
|
|
|
#include "../db/cartographer.h"
|
2021-03-09 11:45:44 +02:00
|
|
|
#include "../db/db.h"
|
2021-06-01 13:10:18 +03:00
|
|
|
#include "../db/loaders.h"
|
2021-05-07 16:45:57 +03:00
|
|
|
#include "../playlist/playlist.h"
|
2021-03-23 19:50:09 +02:00
|
|
|
#include "structs.h"
|
2021-03-09 11:45:44 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
extern KotoCartographer * koto_maps;
|
|
|
|
extern sqlite3 * koto_db;
|
2021-02-09 17:37:26 +02:00
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
struct _KotoLibrary {
|
2021-02-09 17:37:26 +02:00
|
|
|
GObject parent_instance;
|
2021-05-27 16:58:28 +03:00
|
|
|
|
|
|
|
gchar * path; // Compat
|
|
|
|
|
|
|
|
gchar * directory;
|
|
|
|
gchar * name;
|
|
|
|
KotoLibraryType type;
|
|
|
|
gchar * uuid;
|
|
|
|
|
|
|
|
gboolean override_builtin;
|
|
|
|
|
2021-02-09 17:37:26 +02:00
|
|
|
magic_t magic_cookie;
|
|
|
|
};
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
G_DEFINE_TYPE(KotoLibrary, koto_library, G_TYPE_OBJECT);
|
2021-02-09 17:37:26 +02:00
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_0,
|
|
|
|
PROP_PATH,
|
|
|
|
N_PROPERTIES
|
|
|
|
};
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
static GParamSpec * props[N_PROPERTIES] = {
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
static void koto_library_get_property(
|
2021-05-11 20:05:04 +03:00
|
|
|
GObject * obj,
|
|
|
|
guint prop_id,
|
|
|
|
GValue * val,
|
|
|
|
GParamSpec * spec
|
|
|
|
);
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
static void koto_library_set_property(
|
2021-05-11 20:05:04 +03:00
|
|
|
GObject * obj,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue * val,
|
|
|
|
GParamSpec * spec
|
|
|
|
);
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
static void koto_library_class_init(KotoLibraryClass * c) {
|
2021-05-11 20:05:04 +03:00
|
|
|
GObjectClass * gobject_class;
|
2021-02-09 17:37:26 +02:00
|
|
|
|
|
|
|
gobject_class = G_OBJECT_CLASS(c);
|
2021-05-11 20:08:39 +03:00
|
|
|
gobject_class->set_property = koto_library_set_property;
|
|
|
|
gobject_class->get_property = koto_library_get_property;
|
2021-02-09 17:37:26 +02:00
|
|
|
|
|
|
|
props[PROP_PATH] = g_param_spec_string(
|
|
|
|
"path",
|
|
|
|
"Path",
|
|
|
|
"Path to Music",
|
|
|
|
NULL,
|
2021-05-11 20:05:04 +03:00
|
|
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
2021-02-09 17:37:26 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
2021-02-16 17:15:10 +02:00
|
|
|
taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_UTF8); // Ensure our id3v2 text encoding is UTF-8
|
2021-02-09 17:37:26 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
static void koto_library_init(KotoLibrary * self) {
|
2021-05-27 16:58:28 +03:00
|
|
|
(void) self;
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
static void koto_library_get_property(
|
2021-05-11 20:05:04 +03:00
|
|
|
GObject * obj,
|
|
|
|
guint prop_id,
|
|
|
|
GValue * val,
|
|
|
|
GParamSpec * spec
|
|
|
|
) {
|
2021-05-11 20:08:39 +03:00
|
|
|
KotoLibrary * self = KOTO_LIBRARY(obj);
|
2021-05-11 20:05:04 +03:00
|
|
|
|
2021-02-09 17:37:26 +02:00
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_PATH:
|
|
|
|
g_value_set_string(val, self->path);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
static void koto_library_set_property(
|
2021-05-11 20:05:04 +03:00
|
|
|
GObject * obj,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue * val,
|
|
|
|
GParamSpec * spec
|
|
|
|
) {
|
2021-05-11 20:08:39 +03:00
|
|
|
KotoLibrary * self = KOTO_LIBRARY(obj);
|
2021-05-11 20:05:04 +03:00
|
|
|
|
2021-02-09 17:37:26 +02:00
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_PATH:
|
2021-05-11 20:08:39 +03:00
|
|
|
koto_library_set_path(self, g_strdup(g_value_get_string(val)));
|
2021-02-09 17:37:26 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
void koto_library_set_path(
|
|
|
|
KotoLibrary * self,
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * path
|
|
|
|
) {
|
2021-03-09 11:45:44 +02:00
|
|
|
if (path == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->path != NULL) {
|
|
|
|
g_free(self->path);
|
|
|
|
}
|
|
|
|
|
|
|
|
self->path = path;
|
|
|
|
|
|
|
|
if (created_new_db) { // Created new database
|
|
|
|
start_indexing(self); // Start index operation
|
|
|
|
} else { // Have existing database
|
2021-06-01 13:10:18 +03:00
|
|
|
read_from_db(NULL); // Read from the database
|
2021-05-07 16:45:57 +03:00
|
|
|
}
|
2021-03-09 11:45:44 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
void start_indexing(KotoLibrary * self) {
|
2021-02-09 17:37:26 +02:00
|
|
|
struct stat library_stat;
|
|
|
|
int success = stat(self->path, &library_stat);
|
|
|
|
|
|
|
|
if (success != 0) { // Failed to read the library path
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISDIR(library_stat.st_mode)) { // Is not a directory
|
|
|
|
g_warning("%s is not a directory", self->path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->magic_cookie = magic_open(MAGIC_MIME);
|
|
|
|
|
|
|
|
if (self->magic_cookie == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (magic_load(self->magic_cookie, NULL) != 0) {
|
|
|
|
magic_close(self->magic_cookie);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
index_folder(self, self->path, 0);
|
2021-02-09 17:37:26 +02:00
|
|
|
magic_close(self->magic_cookie);
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
KotoLibrary * koto_library_new(const gchar * path) {
|
2021-05-11 20:05:04 +03:00
|
|
|
return g_object_new(
|
2021-05-11 20:08:39 +03:00
|
|
|
KOTO_TYPE_LIBRARY,
|
2021-05-11 20:05:04 +03:00
|
|
|
"path",
|
|
|
|
path,
|
2021-03-09 11:45:44 +02:00
|
|
|
NULL
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void index_folder(
|
2021-05-11 20:08:39 +03:00
|
|
|
KotoLibrary * self,
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * path,
|
|
|
|
guint depth
|
|
|
|
) {
|
2021-02-16 17:15:10 +02:00
|
|
|
depth++;
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
DIR * dir = opendir(path); // Attempt to open our directory
|
|
|
|
|
2021-02-09 17:37:26 +02:00
|
|
|
if (dir == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
struct dirent * entry;
|
|
|
|
|
2021-02-09 17:37:26 +02:00
|
|
|
while ((entry = readdir(dir))) {
|
2021-02-16 17:15:10 +02:00
|
|
|
if (g_str_has_prefix(entry->d_name, ".")) { // A reference to parent dir, self, or a hidden item
|
|
|
|
continue;
|
|
|
|
}
|
2021-02-09 17:37:26 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name);
|
2021-02-09 17:37:26 +02:00
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
if (entry->d_type == DT_DIR) { // Directory
|
|
|
|
if (depth == 1) { // If we are following FOLDER/ARTIST/ALBUM then this would be artist
|
2021-05-11 20:08:39 +03:00
|
|
|
KotoArtist * artist = koto_artist_new(full_path); // Attempt to get the artist
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * artist_name;
|
2021-02-09 17:37:26 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
g_object_get(
|
|
|
|
artist,
|
|
|
|
"name",
|
|
|
|
&artist_name,
|
2021-02-16 17:15:10 +02:00
|
|
|
NULL
|
|
|
|
);
|
2021-02-09 17:37:26 +02:00
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
koto_cartographer_add_artist(koto_maps, artist); // Add the artist to cartographer
|
2021-02-16 17:15:10 +02:00
|
|
|
index_folder(self, full_path, depth); // Index this directory
|
|
|
|
g_free(artist_name);
|
|
|
|
} else if (depth == 2) { // If we are following FOLDER/ARTIST/ALBUM then this would be album
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist
|
2021-05-27 16:58:28 +03:00
|
|
|
KotoArtist * artist = koto_cartographer_get_artist_by_name(koto_maps, artist_name);
|
2021-02-09 17:37:26 +02:00
|
|
|
|
2021-05-27 16:58:28 +03:00
|
|
|
if (!KOTO_IS_ARTIST(artist)) { // Not an artist
|
2021-02-16 17:15:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:08:39 +03:00
|
|
|
KotoAlbum * album = koto_album_new(artist, full_path);
|
2021-03-23 19:50:09 +02:00
|
|
|
koto_cartographer_add_album(koto_maps, album); // Add our album to the cartographer
|
2021-03-02 19:12:12 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * album_uuid = NULL;
|
2021-03-23 19:50:09 +02:00
|
|
|
g_object_get(album, "uuid", &album_uuid, NULL);
|
2021-05-11 20:08:39 +03:00
|
|
|
koto_artist_add_album(artist, album_uuid); // Add the album
|
2021-02-16 17:15:10 +02:00
|
|
|
g_free(artist_name);
|
|
|
|
}
|
|
|
|
}
|
2021-02-09 17:37:26 +02:00
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
g_free(full_path);
|
2021-02-09 17:37:26 +02:00
|
|
|
}
|
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
closedir(dir); // Close the directory
|
2021-05-27 16:58:28 +03:00
|
|
|
}
|