Implemented ID3 tag parsing in our KotoIndexedFile leveraging taglib, refactored our mimetype code.

Implemented KotoIndexedArtist as well as KotoIndexedAlbum, including moving the file indexing capabilities into the KotoIndexedAlbum (subject to change).

Added g_debug messages instead of using g_message for debug purposes. Re-introduced Koto Utils with new koto_utils_get_filename_without_extension, since that is used to get the extensionless file name across both album fetching and base file name fetching.
This commit is contained in:
Joshua Strobl 2021-02-16 17:15:10 +02:00
parent 9b6fa4593a
commit 304c7635da
11 changed files with 815 additions and 125 deletions

289
src/indexer/album.c Normal file
View file

@ -0,0 +1,289 @@
/* album.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 <glib-2.0/glib.h>
#include <magic.h>
#include <stdio.h>
#include "album.h"
#include "file.h"
#include "koto-utils.h"
struct _KotoIndexedAlbum {
GObject parent_instance;
gchar *path;
gchar *name;
gchar *art_path;
GHashTable *files;
gboolean has_album_art;
};
G_DEFINE_TYPE(KotoIndexedAlbum, koto_indexed_album, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_PATH,
PROP_ALBUM_NAME,
PROP_ART_PATH,
N_PROPERTIES
};
static GParamSpec *props[N_PROPERTIES] = { NULL, };
static void koto_indexed_album_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_indexed_album_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_indexed_album_class_init(KotoIndexedAlbumClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_indexed_album_set_property;
gobject_class->get_property = koto_indexed_album_get_property;
props[PROP_PATH] = g_param_spec_string(
"path",
"Path",
"Path to Album",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_ALBUM_NAME] = g_param_spec_string(
"name",
"Name",
"Name of Album",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_ART_PATH] = g_param_spec_string(
"art-path",
"Path to Artwork",
"Path to Artwork",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
}
static void koto_indexed_album_init(KotoIndexedAlbum *self) {
self->has_album_art = FALSE;
}
void koto_indexed_album_add_file(KotoIndexedAlbum *self, KotoIndexedFile *file) {
if (file == NULL) { // Not a file
return;
}
if (self->files == NULL) { // No HashTable yet
self->files = g_hash_table_new(g_str_hash, g_str_equal); // Create a new HashTable
}
gchar *file_name;
g_object_get(file, "parsed-name", &file_name, NULL);
if (g_hash_table_contains(self->files, file_name)) { // If we have this file already
g_free(file_name);
return;
}
g_hash_table_insert(self->files, file_name, file); // Add the file
}
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_PATH:
g_value_set_string(val, self->path);
break;
case PROP_ALBUM_NAME:
g_value_set_string(val, self->name);
break;
case PROP_ART_PATH:
g_value_set_string(val, koto_indexed_album_get_album_art(self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
gchar* koto_indexed_album_get_album_art(KotoIndexedAlbum *self) {
return g_strdup((self->has_album_art) ? self->art_path : "");
}
GList* koto_indexed_album_get_files(KotoIndexedAlbum *self) {
if (self->files == NULL) { // No HashTable yet
self->files = g_hash_table_new(g_str_hash, g_str_equal); // Create a new HashTable
}
return g_hash_table_get_values(self->files);
}
void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album_art) {
if (album_art == NULL) { // Not valid album art
return;
}
if (self->art_path != NULL) {
g_free(self->art_path);
}
self->art_path = g_strdup(album_art);
self->has_album_art = TRUE;
}
void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedFile *file) {
if (file == NULL) { // Not a file
return;
}
if (self->files == NULL) { // No HashTable yet
self->files = g_hash_table_new(g_str_hash, g_str_equal); // Create a new HashTable
}
gchar *file_name;
g_object_get(file, "parsed-name", &file_name, NULL);
g_hash_table_remove(self->files, file_name);
}
void koto_indexed_album_remove_file_by_name(KotoIndexedAlbum *self, const gchar *file_name) {
if (file_name == NULL) {
return;
}
KotoIndexedFile *file = g_hash_table_lookup(self->files, file_name); // Get the files
koto_indexed_album_remove_file(self, file);
}
void koto_indexed_album_set_album_name(KotoIndexedAlbum *self, const gchar *album_name) {
if (album_name == NULL) { // Not valid album name
return;
}
if (self->name != NULL) {
g_free(self->name);
}
self->name = g_strdup(album_name);
}
static void koto_indexed_album_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec){
KotoIndexedAlbum *self = KOTO_INDEXED_ALBUM(obj);
switch (prop_id) {
case PROP_PATH: // Path to the album
koto_indexed_album_update_path(self, g_value_get_string(val));
break;
case PROP_ALBUM_NAME: // Name of album
koto_indexed_album_set_album_name(self, g_value_get_string(val));
break;
case PROP_ART_PATH: // Path to art
koto_indexed_album_set_album_art(self, g_value_get_string(val));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
void koto_indexed_album_update_path(KotoIndexedAlbum *self, const gchar* new_path) {
if (new_path == NULL) {
return;
}
DIR *dir = opendir(new_path); // Attempt to open our directory
if (dir == NULL) {
return;
}
if (self->path != NULL) {
g_free(self->path);
}
self->path = g_strdup(new_path);
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) {
return;
}
if (magic_load(magic_cookie, NULL) != 0) {
magic_close(magic_cookie);
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
}
if (entry->d_type != DT_REG) { // If this is not a regular file
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);
} else if (g_str_has_prefix(mime_type, "audio/")) { // Is an audio file
KotoIndexedFile *file = koto_indexed_file_new(full_path);
if (file != NULL) { // Is a file
koto_indexed_album_add_file(self, file); // Add our file
}
}
g_free(full_path);
}
magic_close(magic_cookie);
}
KotoIndexedAlbum* koto_indexed_album_new(const gchar *path) {
return g_object_new(KOTO_TYPE_INDEXED_ALBUM,
"path", path,
NULL
);
}

View file

@ -17,17 +17,22 @@
#pragma once
#include <glib-2.0/glib-object.h>
#include "file.h"
G_BEGIN_DECLS
#define KOTO_INDEXED_ALBUM_TYPE koto_indexed_album_get_type()
#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 add_song(KotoIndexedAlbum *self, KotoIndexedFile *file);
KotoIndexedFile** get_songs(KotoIndexedAlbum *self);
void remove_song(KotoIndexedAlbum *self, KotoIndexedFile *file);
void remove_song_by_name(KotoIndexedAlbum *self, gchar *file_name);
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

196
src/indexer/artist.c Normal file
View file

@ -0,0 +1,196 @@
/* artist.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 <glib-2.0/glib.h>
#include "album.h"
#include "artist.h"
struct _KotoIndexedArtist {
GObject parent_instance;
gchar *path;
gboolean has_artist_art;
gchar *artist_name;
GHashTable *albums;
};
G_DEFINE_TYPE(KotoIndexedArtist, koto_indexed_artist, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_PATH,
PROP_ARTIST_NAME,
N_PROPERTIES
};
static GParamSpec *props[N_PROPERTIES] = { NULL, };
static void koto_indexed_artist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_indexed_artist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_indexed_artist_class_init(KotoIndexedArtistClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_indexed_artist_set_property;
gobject_class->get_property = koto_indexed_artist_get_property;
props[PROP_PATH] = g_param_spec_string(
"path",
"Path",
"Path to Artist",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_ARTIST_NAME] = g_param_spec_string(
"name",
"Name",
"Name of Artist",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
}
static void koto_indexed_artist_init(KotoIndexedArtist *self) {
self->has_artist_art = FALSE;
self->albums = NULL; // Set to null initially maybe
}
static void koto_indexed_artist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj);
switch (prop_id) {
case PROP_PATH:
g_value_set_string(val, self->path);
break;
case PROP_ARTIST_NAME:
g_value_set_string(val, self->artist_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
static void koto_indexed_artist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec){
KotoIndexedArtist *self = KOTO_INDEXED_ARTIST(obj);
switch (prop_id) {
case PROP_PATH:
koto_indexed_artist_update_path(self, g_value_get_string(val));
break;
case PROP_ARTIST_NAME:
koto_indexed_artist_set_artist_name(self, g_value_get_string(val));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
void koto_indexed_artist_add_album(KotoIndexedArtist *self, KotoIndexedAlbum *album) {
if (album == NULL) { // No album really defined
return;
}
if (self->albums == NULL) { // No HashTable yet
self->albums = g_hash_table_new(g_str_hash, g_str_equal); // Create a new HashTable
}
gchar *album_name;
g_object_get(album, "name", &album_name, NULL);
if (album_name == NULL) {
g_free(album_name);
return;
}
if (g_hash_table_contains(self->albums, album_name)) { // If we have this album already
g_free(album_name);
return;
}
g_hash_table_insert(self->albums, album_name, album); // Add the album
}
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self) {
if (self->albums == NULL) { // No HashTable yet
self->albums = g_hash_table_new(g_str_hash, g_str_equal); // Create a new HashTable
}
return g_hash_table_get_values(self->albums);
}
void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album) {
if (album == NULL) { // No album defined
return;
}
if (self->albums == NULL) { // No HashTable yet
self->albums = g_hash_table_new(g_str_hash, g_str_equal); // Create a new HashTable
}
gchar *album_name;
g_object_get(album, "name", &album_name, NULL);
g_hash_table_remove(self->albums, album_name);
}
void koto_indexed_artist_remove_album_by_name(KotoIndexedArtist *self, gchar *album_name) {
if (album_name == NULL) {
return;
}
KotoIndexedAlbum *album = g_hash_table_lookup(self->albums, album_name); // Get the album
koto_indexed_artist_remove_album(self, album);
}
void koto_indexed_artist_update_path(KotoIndexedArtist *self, const gchar *new_path) {
if (new_path == NULL) { // No path really
return;
}
if (self->path != NULL) { // Already have a path set
g_free(self->path); // Free
}
self->path = g_strdup(new_path);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]);
}
void koto_indexed_artist_set_artist_name(KotoIndexedArtist *self, const gchar *artist_name) {
if (artist_name == NULL) { // No artist name
return;
}
if (self->artist_name != NULL) { // Has artist name
g_free(self->artist_name);
}
self->artist_name = g_strdup(artist_name);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_NAME]);
}
KotoIndexedArtist* koto_indexed_artist_new(const gchar *path) {
return g_object_new(KOTO_TYPE_INDEXED_ARTIST,
"path", path,
"name", g_path_get_basename(path),
NULL
);
}

View file

@ -17,17 +17,22 @@
#pragma once
#include <glib-2.0/glib-object.h>
#include "album.h"
G_BEGIN_DECLS
#define KOTO_INDEXED_ARTIST_TYPE koto_indexed_artist_get_type()
#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 add_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
KotoIndexedAlbum** get_albums(KotoIndexedArtist *self);
void remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
void remove_album_by_name(KotoIndexedArtist *self, gchar *album_name);
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

View file

@ -17,27 +17,14 @@
#include <dirent.h>
#include <magic.h>
#include <stdio.h>
#include <sys/stat.h>
#include <taglib/tag_c.h>
#include "album.h"
#include "artist.h"
#include "file.h"
#include "file-indexer.h"
struct KotoIndexedAlbum {
GObject parent_instance;
gboolean has_album_art;
gchar *album_name;
gchar *art_path;
gchar *path;
GHashTable *songs;
};
struct _KotoIndexedArtist {
GObject parent_instance;
gboolean has_artist_art;
gchar *artist_name;
GHashTable *albums;
gchar *path;
};
struct _KotoIndexedLibrary {
GObject parent_instance;
gchar *path;
@ -73,12 +60,60 @@ static void koto_indexed_library_class_init(KotoIndexedLibraryClass *c) {
);
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_indexed_library_init(KotoIndexedLibrary *self) {
self->music_artists = g_hash_table_new(g_str_hash, g_str_equal);
}
void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist) {
if (artist == NULL) { // No artist
return;
}
if (self->music_artists == NULL) { // Not a HashTable
self->music_artists = g_hash_table_new(g_str_hash, g_str_equal);
}
gchar *artist_name;
g_object_get(artist, "name", &artist_name, NULL);
if (g_hash_table_contains(self->music_artists, artist_name)) { // Already have the artist
g_free(artist_name);
return;
}
g_hash_table_insert(self->music_artists, artist_name, artist); // Add the artist
}
KotoIndexedArtist* koto_indexed_library_get_artist(KotoIndexedLibrary *self, gchar *artist_name) {
if (artist_name == NULL) {
return NULL;
}
if (self->music_artists == NULL) { // Not a HashTable
return NULL;
}
return g_hash_table_lookup(self->music_artists, (KotoIndexedArtist*) artist_name);
}
void koto_indexed_library_remove_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist) {
if (artist == NULL) {
return;
}
if (self->music_artists == NULL) { // Not a HashTable
return;
}
gchar *artist_name;
g_object_get(artist, "name", &artist_name, NULL);
g_hash_table_remove(self->music_artists, artist_name); // Remove the artist
}
static void koto_indexed_library_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
KotoIndexedLibrary *self = KOTO_INDEXED_LIBRARY(obj);
@ -98,7 +133,6 @@ static void koto_indexed_library_set_property(GObject *obj, guint prop_id, const
switch (prop_id) {
case PROP_PATH:
self->path = g_strdup(g_value_get_string(val));
g_message("Set to %s", self->path);
start_indexing(self);
break;
default:
@ -131,11 +165,14 @@ void start_indexing(KotoIndexedLibrary *self) {
return;
}
index_folder(self, self->path);
index_folder(self, self->path, 0);
magic_close(self->magic_cookie);
g_hash_table_foreach(self->music_artists, output_artists, NULL);
}
void index_folder(KotoIndexedLibrary *self, gchar *path) {
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth) {
depth++;
DIR *dir = opendir(path); // Attempt to open our directory
if (dir == NULL) {
@ -145,54 +182,88 @@ void index_folder(KotoIndexedLibrary *self, gchar *path) {
struct dirent *entry;
while ((entry = readdir(dir))) {
if (!g_str_has_prefix(entry->d_name, ".")) { // Not a reference to parent dir, self, or a hidden item
gchar *full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name);
if (entry->d_type == DT_DIR) { // Directory
index_folder(self, full_path); // Index this directory
} else if (entry->d_type == DT_REG) { // Regular file
index_file(self, full_path); // Index the file
}
g_free(full_path);
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
KotoIndexedArtist *artist = koto_indexed_artist_new(full_path); // Attempt to get the artist
gchar *artist_name;
g_object_get(artist,
"name", &artist_name,
NULL
);
koto_indexed_library_add_artist(self, artist); // Add the artist
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
KotoIndexedAlbum *album = koto_indexed_album_new(full_path);
KotoIndexedArtist *artist = koto_indexed_library_get_artist(self, artist_name); // Get the artist
if (artist == NULL) {
continue;
}
koto_indexed_artist_add_album(artist, album); // Add the album
g_free(artist_name);
}
}
g_free(full_path);
}
closedir(dir); // Close the directory
}
void index_file(KotoIndexedLibrary *self, gchar *path) {
const char *mime_type = magic_file(self->magic_cookie, path);
void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data) {
KotoIndexedArtist *artist = (KotoIndexedArtist*) artist_ptr;
gchar *artist_name;
g_object_get(artist, "name", &artist_name, NULL);
if (mime_type == NULL) { // Failed to get the mimetype
return;
g_debug("Artist: %s", artist_name);
GList *albums = koto_indexed_artist_get_albums(artist); // Get the albums for this artist
if (albums != NULL) {
g_debug("Length of Albums: %d", g_list_length(albums));
}
gchar** mime_info = g_strsplit(mime_type, ";", 2); // Only care about our first item
GList *a;
for (a = albums; a != NULL; a = a->next) {
KotoIndexedAlbum *album = (KotoIndexedAlbum*) a->data;
gchar *artwork = koto_indexed_album_get_album_art(album);
gchar *album_name;
g_object_get(album, "name", &album_name, NULL);
g_debug("Album Art: %s", artwork);
g_debug("Album Name: %s", album_name);
if (
g_str_has_prefix(mime_info[0], "audio/") || // Is audio
g_str_has_prefix(mime_info[0], "image/") // Is image
) {
//g_message("File Name: %s", path);
GList *files = koto_indexed_album_get_files(album); // Get the files for the album
GList *f;
for (f = files; f != NULL; f = f->next) {
KotoIndexedFile *file = (KotoIndexedFile*) f->data;
gchar *filepath;
gchar *parsed_name;
guint *pos;
g_object_get(file,
"path", &filepath,
"parsed-name", &parsed_name,
"position", &pos,
NULL);
g_debug("File Path: %s", filepath);
g_debug("Parsed Name: %s", parsed_name);
g_debug("Position: %d", GPOINTER_TO_INT(pos));
g_free(filepath);
g_free(parsed_name);
}
}
if (g_str_has_prefix(mime_info[0], "audio/")) { // Is an audio file
KotoIndexedFile *file = koto_indexed_file_new(path);
gchar *filepath;
gchar *parsed_name;
g_object_get(file,
"path", &filepath,
"parsed-name", &parsed_name,
NULL);
g_free(filepath);
g_free(parsed_name);
g_object_unref(file);
}
g_strfreev(mime_info); // Free our mimeinfo
}
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) {

View file

@ -17,6 +17,7 @@
#pragma once
#include <glib-2.0/glib-object.h>
#include "artist.h"
G_BEGIN_DECLS
@ -25,8 +26,10 @@ G_DECLARE_FINAL_TYPE(KotoIndexedLibrary, koto_indexed_library, KOTO, INDEXED_LIB
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);
void koto_indexed_library_remove_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist);
void start_indexing(KotoIndexedLibrary *self);
void index_folder(KotoIndexedLibrary *self, gchar *path);
void index_file(KotoIndexedLibrary *self, gchar *path);
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth);
G_END_DECLS

View file

@ -16,17 +16,19 @@
*/
#include <glib-2.0/glib.h>
#include <taglib/tag_c.h>
#include "file.h"
#include "koto-utils.h"
struct _KotoIndexedFile {
GObject parent_instance;
gchar *path;
gchar *file_name;
gchar *parsed_name;
gchar *artist;
gchar *album;
guint position;
guint *position;
gboolean acquired_metadata_from_id3;
};
@ -131,7 +133,7 @@ static void koto_indexed_file_get_property(GObject *obj, guint prop_id, GValue *
g_value_set_string(val, self->parsed_name);
break;
case PROP_POSITION:
g_value_set_uint(val, self->position);
g_value_set_uint(val, GPOINTER_TO_UINT(self->position));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
@ -144,8 +146,7 @@ static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GV
switch (prop_id) {
case PROP_PATH:
self->path = g_strdup(g_value_get_string(val)); // Update our path
koto_indexed_file_set_file_name(self, g_path_get_basename(self->path)); // Update our file name
koto_indexed_file_update_path(self, g_value_get_string(val)); // Update the path
break;
case PROP_ARTIST:
self->artist = g_strdup(g_value_get_string(val));
@ -160,7 +161,7 @@ static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GV
koto_indexed_file_set_parsed_name(self, g_strdup(g_value_get_string(val)));
break;
case PROP_POSITION:
self->position = g_value_get_uint(val);
koto_indexed_file_set_position(self, g_value_get_uint(val));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
@ -168,6 +169,65 @@ static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GV
}
}
void koto_indexed_file_parse_name(KotoIndexedFile *self) {
gchar *copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters
if (self->artist != NULL && strcmp(self->artist, "") != 0) { // If we have artist set
gchar **split = g_strsplit(copied_file_name, self->artist, -1); // Split whenever we encounter the artist
copied_file_name = g_strjoinv("", split); // Remove the artist
g_strfreev(split);
}
if (self->artist != NULL && strcmp(self->album, "") != 0) { // If we have album set
gchar **split = g_strsplit(copied_file_name, self->album, -1); // Split whenever we encounter the album
copied_file_name = g_strjoinv("", split); // Remove the album
g_strfreev(split);
split = g_strsplit(copied_file_name, g_utf8_strdown(self->album, g_utf8_strlen(self->album, -1)), -1); // Split whenever we encounter the album lowercased
copied_file_name = g_strjoin("", split, NULL); // Remove the lowercased album
g_strfreev(split);
}
if (g_str_match_string(copied_file_name, "-", FALSE)) { // Contains - like a heathen
gchar **split = g_strsplit(copied_file_name, " - ", -1); // Split whenever we encounter " - "
copied_file_name = g_strjoin("", split, NULL); // Remove entirely
g_strfreev(split);
if (g_str_match_string(copied_file_name, "-", FALSE)) { // If we have any stragglers
split = g_strsplit(copied_file_name, "-", -1); // Split whenever we encounter -
copied_file_name = g_strjoin("", split, NULL); // Remove entirely
g_strfreev(split);
}
}
gchar *file_without_ext = koto_utils_get_filename_without_extension(copied_file_name);
g_free(copied_file_name);
gchar **split = g_regex_split_simple("^([\\d]+)\\s", file_without_ext, G_REGEX_JAVASCRIPT_COMPAT, 0);
if (g_strv_length(split) > 1) { // Has positional info at the beginning of the file
gchar *num = split[1];
g_free(file_without_ext); // Free the prior name
file_without_ext = g_strdup(split[2]); // Set to our second item which is the rest of the song name without the prefixed numbers
if ((strcmp(num, "0") == 0) || (strcmp(num, "00") == 0)) { // Is exactly zero
koto_indexed_file_set_position(self, 0); // Set position to 0
} else { // Either starts with 0 (like 09) or doesn't start with it at all
guint64 potential_pos = g_ascii_strtoull(num, NULL, 10); // Attempt to convert
if (potential_pos != 0) { // Got a legitimate position
koto_indexed_file_set_position(self, potential_pos);
}
}
}
g_strfreev(split);
koto_indexed_file_set_parsed_name(self, file_without_ext);
g_free(file_without_ext);
}
void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name) {
if (new_file_name == NULL) {
return;
@ -206,64 +266,45 @@ void koto_indexed_file_set_parsed_name(KotoIndexedFile *self, gchar *new_parsed_
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PARSED_NAME]);
}
void koto_indexed_file_parse_name(KotoIndexedFile *self) {
gchar *copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters
if (self->artist != NULL && strcmp(self->artist, "") != 0) { // If we have artist set
gchar **split = g_strsplit(copied_file_name, self->artist, -1); // Split whenever we encounter the artist
copied_file_name = g_strjoinv("", split); // Remove the artist
g_strfreev(split);
void koto_indexed_file_set_position(KotoIndexedFile *self, guint pos) {
if (pos == 0) { // No position change really
return;
}
if (self->artist != NULL && strcmp(self->album, "") != 0) { // If we have album set
gchar **split = g_strsplit(copied_file_name, self->album, -1); // Split whenever we encounter the album
copied_file_name = g_strjoinv("", split); // Remove the album
g_strfreev(split);
self->position = GUINT_TO_POINTER(pos);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_POSITION]);
}
split = g_strsplit(copied_file_name, g_utf8_strdown(self->album, g_utf8_strlen(self->album, -1)), -1); // Split whenever we encounter the album lowercased
copied_file_name = g_strjoin("", split, NULL); // Remove the lowercased album
g_strfreev(split);
}
void koto_indexed_file_update_metadata(KotoIndexedFile *self) {
TagLib_File *t_file = taglib_file_new(self->path); // Get a taglib file for this file
if (g_str_match_string(copied_file_name, "-", FALSE)) { // Contains - like a heathen
gchar **split = g_strsplit(copied_file_name, " - ", -1); // Split whenever we encounter " - "
copied_file_name = g_strjoin("", split, NULL); // Remove entirely
g_strfreev(split);
if (g_str_match_string(copied_file_name, "-", FALSE)) { // If we have any stragglers
split = g_strsplit(copied_file_name, "-", -1); // Split whenever we encounter -
copied_file_name = g_strjoin("", split, NULL); // Remove entirely
g_strfreev(split);
}
}
gchar **split = g_strsplit(copied_file_name, ".", -1); // Split every time we see .
g_free(copied_file_name);
guint len_of_extension_split = g_strv_length(split);
if (len_of_extension_split == 2) { // Only have two elements
copied_file_name = g_strdup(split[0]); // Get the first element
} else {
gchar *new_parsed_name = "";
for (guint i = 0; i < len_of_extension_split - 1; i++) { // Iterate over everything except the last item
if (g_strcmp0(new_parsed_name, "") == 0) { // Currently empty
new_parsed_name = g_strdup(split[i]); // Just duplicate this string
} else {
gchar *tmp_copy = g_strdup(new_parsed_name);
g_free(new_parsed_name); // Free the old
new_parsed_name = g_strdup(g_strjoin(".", tmp_copy, split[i], NULL)); // Join the two strings with a . again and duplicate it, setting it to our new_parsed_name
g_free(tmp_copy); // Free our temporary copy
}
if ((t_file != NULL) && taglib_file_is_valid(t_file)) { // If we got the taglib file and it is valid
self->acquired_metadata_from_id3 = TRUE;
TagLib_Tag *tag = taglib_file_tag(t_file); // Get our tag
koto_indexed_file_set_parsed_name(self, taglib_tag_title(tag)); // Set the title of the file
self->artist = g_strdup(taglib_tag_artist((tag))); // Set the artist
self->album = g_strdup(taglib_tag_album(tag)); // Set the album
koto_indexed_file_set_position(self, (uint) taglib_tag_track(tag)); // Get the track, convert to uint and cast as a pointer
}
copied_file_name = g_strdup(new_parsed_name);
g_free(new_parsed_name);
taglib_tag_free_strings(); // Free strings
taglib_file_free(t_file); // Free the file
}
void koto_indexed_file_update_path(KotoIndexedFile *self, const gchar *new_path) {
if (new_path == NULL) {
return;
}
g_strfreev(split);
if (self->path != NULL) { // Already have a path
g_free(self->path); // Free it
}
koto_indexed_file_set_parsed_name(self, copied_file_name);
g_free(copied_file_name);
self->path = g_strdup(new_path); // Duplicate the path and set it
koto_indexed_file_update_metadata(self); // Attempt to get ID3 info
koto_indexed_file_set_file_name(self, g_path_get_basename(self->path)); // Update our file name
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]);
}
KotoIndexedFile* koto_indexed_file_new(const gchar *path) {

View file

@ -25,8 +25,11 @@ G_DECLARE_FINAL_TYPE(KotoIndexedFile, koto_indexed_file, KOTO, INDEXED_FILE, GOb
KotoIndexedFile* koto_indexed_file_new(const gchar *path);
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_parsed_name(KotoIndexedFile *self, gchar *new_parsed_name);
void koto_indexed_file_parse_name(KotoIndexedFile *self);
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