/* 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 #include #include #include #include #include "../db/cartographer.h" #include "../db/db.h" #include "../db/loaders.h" #include "../playlist/playlist.h" #include "structs.h" extern KotoCartographer * koto_maps; extern sqlite3 * koto_db; struct _KotoLibrary { GObject parent_instance; gchar * path; // Compat gchar * directory; gchar * name; KotoLibraryType type; gchar * uuid; gboolean override_builtin; magic_t magic_cookie; }; G_DEFINE_TYPE(KotoLibrary, koto_library, G_TYPE_OBJECT); enum { PROP_0, PROP_PATH, N_PROPERTIES }; static GParamSpec * props[N_PROPERTIES] = { NULL, }; static void koto_library_get_property( GObject * obj, guint prop_id, GValue * val, GParamSpec * spec ); static void koto_library_set_property( GObject * obj, guint prop_id, const GValue * val, GParamSpec * spec ); static void koto_library_class_init(KotoLibraryClass * c) { GObjectClass * gobject_class; gobject_class = G_OBJECT_CLASS(c); gobject_class->set_property = koto_library_set_property; gobject_class->get_property = koto_library_get_property; props[PROP_PATH] = g_param_spec_string( "path", "Path", "Path to Music", NULL, G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE ); g_object_class_install_properties(gobject_class, N_PROPERTIES, props); taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_UTF8); // Ensure our id3v2 text encoding is UTF-8 } static void koto_library_init(KotoLibrary * self) { (void) self; } static void koto_library_get_property( GObject * obj, guint prop_id, GValue * val, GParamSpec * spec ) { KotoLibrary * self = KOTO_LIBRARY(obj); 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; } } static void koto_library_set_property( GObject * obj, guint prop_id, const GValue * val, GParamSpec * spec ) { KotoLibrary * self = KOTO_LIBRARY(obj); switch (prop_id) { case PROP_PATH: koto_library_set_path(self, g_strdup(g_value_get_string(val))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec); break; } } void koto_library_set_path( KotoLibrary * self, gchar * path ) { 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 read_from_db(NULL); // Read from the database } } void start_indexing(KotoLibrary * self) { 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; } index_folder(self, self->path, 0); magic_close(self->magic_cookie); } KotoLibrary * koto_library_new(const gchar * path) { return g_object_new( KOTO_TYPE_LIBRARY, "path", path, NULL ); } void index_folder( KotoLibrary * self, gchar * path, guint depth ) { depth++; 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, ".")) { // A reference to parent dir, self, or a hidden item continue; } gchar * full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name); if (entry->d_type == DT_DIR) { // Directory if (depth == 1) { // If we are following FOLDER/ARTIST/ALBUM then this would be artist KotoArtist * artist = koto_artist_new(full_path); // Attempt to get the artist gchar * artist_name; g_object_get( artist, "name", &artist_name, NULL ); koto_cartographer_add_artist(koto_maps, artist); // Add the artist to cartographer 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 gchar * artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist KotoArtist * artist = koto_cartographer_get_artist_by_name(koto_maps, artist_name); if (!KOTO_IS_ARTIST(artist)) { // Not an artist continue; } KotoAlbum * album = koto_album_new(artist, full_path); koto_cartographer_add_album(koto_maps, album); // Add our album to the cartographer gchar * album_uuid = NULL; g_object_get(album, "uuid", &album_uuid, NULL); koto_artist_add_album(artist, album_uuid); // Add the album g_free(artist_name); } } g_free(full_path); } closedir(dir); // Close the directory }